Skip to content

Commit

Permalink
Configuration reference interpolation (#448)
Browse files Browse the repository at this point in the history
* Configuration reference interpolation

Constants are defined in a `[constants]` section, and unused constants will
only warn instead of erroring.

My intended usage looks like this:
```
[constants]
run_current_ab: 1.9

[tmc5160 stepper_x]
run_current: ${constants.run_current_ab}
[tmc5160 stepper_y]
run_current: ${constants.run_current_ab}
```

`${option}` references the current section
`${section.option}` looks up anywhere

interpolation occurs at most 10 times right now to avoid infinite loops

* Add Danger_Features note
  • Loading branch information
kageurufu authored Dec 11, 2024
1 parent 136149b commit 86d1f55
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 11 deletions.
24 changes: 24 additions & 0 deletions docs/Config_Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,30 @@ A collection of Kalico-specific system options
#log_webhook_method_register_messages: False
```

## ⚠️ Configuration references

In your configuration, you can reference other values to share
configuration between multiple sections. References take the form of
`${option}` to copy a value in the current section, or
`${section.option}` to look up a value elsewhere in your configuration.

Optionally, a `[constants]` section may be used specifically to store
these values. Unlike the rest of your configuration, unused constants
will show a warning instead of causing an error.

```
[constants]
run_current_ab: 1.0
i_am_not_used: True # Will show "Constant 'i_am_not_used' is unused"
[tmc5160 stepper_x]
run_current: ${constants.run_current_ab}
[tmc5160 stepper_y]
run_current: ${tmc5160 stepper_x.run_current}
# Nested references work, but are not advised
```

## Common kinematic settings

### [printer]
Expand Down
1 change: 1 addition & 0 deletions docs/Danger_Features.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
- `--rotate-log-at-restart` can be added to your Kalico start script or service to force log rotation every restart.
- [`[virtual_sdcard] with_subdirs`](./Config_Reference.md#virtual_sdcard) enables scanning of subdirectories for .gcode files, for the menu and M20/M23 commands
- [`[firmware_retraction] z_hop_height`](./Config_Reference.md#firmware_retraction) adds an automatic z hop when using firmware retraction
- [`[constants]` and `${constants.value}`](./Config_Reference.md#configuration-references) allow re-using values in your configuration

## Enhanced behavior

Expand Down
58 changes: 48 additions & 10 deletions klippy/configfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,38 @@ class sentinel:
pass


class SectionInterpolation(configparser.Interpolation):
"""
variable interpolation replacing ${[section.]option}
"""

_KEYCRE = re.compile(
r"\$\{(?:(?P<section>[^.:${}]+)[.:])?(?P<option>[^${}]+)\}"
)

def __init__(self, access_tracking):
self.access_tracking = access_tracking

def before_get(self, parser, section, option, value, defaults):
depth = configparser.MAX_INTERPOLATION_DEPTH
while depth:
depth -= 1

match = self._KEYCRE.search(value)
if not match:
break

sect = match.group("section") or section
opt = match.group("option")

const = parser.get(sect, opt)
self.access_tracking.setdefault((sect, opt), const)

value = value[: match.start()] + const + value[match.end() :]

return value


class ConfigWrapper:
error = configparser.Error

Expand Down Expand Up @@ -416,14 +448,17 @@ def _parse_config(self, data, filename, fileconfig, visited):
visited.remove(path)

def _build_config_wrapper(self, data, filename):
if sys.version_info.major >= 3:
fileconfig = configparser.RawConfigParser(
strict=False, inline_comment_prefixes=(";", "#")
)
else:
fileconfig = configparser.RawConfigParser()
access_tracking = {}
fileconfig = configparser.RawConfigParser(
strict=False,
inline_comment_prefixes=(";", "#"),
interpolation=SectionInterpolation(access_tracking),
)

self._parse_config(data, filename, fileconfig, set())
return ConfigWrapper(self.printer, fileconfig, {}, "printer")
return ConfigWrapper(
self.printer, fileconfig, access_tracking, "printer"
)

def _build_config_string(self, config):
sfile = io.StringIO()
Expand Down Expand Up @@ -454,7 +489,7 @@ def check_unused_options(self, config, error_on_unused):
for option in self.autosave.fileconfig.options(section):
access_tracking[(section.lower(), option.lower())] = 1
# Validate that there are no undefined parameters in the config file
valid_sections = {s: 1 for s, o in access_tracking}
valid_sections = {s for s, o in access_tracking}
for section_name in fileconfig.sections():
section = section_name.lower()
if section not in valid_sections and section not in objects:
Expand All @@ -468,7 +503,7 @@ def check_unused_options(self, config, error_on_unused):
for option in fileconfig.options(section_name):
option = option.lower()
if (section, option) not in access_tracking:
if error_on_unused:
if error_on_unused and section != "constants":
raise error(
"Option '%s' is not valid in section '%s'"
% (option, section)
Expand Down Expand Up @@ -524,7 +559,10 @@ def _build_status(self, config):

for section, option in self.unused_options:
_type = "unused_option"
msg = f"Option '{option}' in section '{section}' is invalid"
if section == "constants":
msg = f"Constant '{option}' is unused"
else:
msg = f"Option '{option}' in section '{section}' is invalid"
self.warn(_type, msg, section, option)
for section in self.unused_sections:
_type = "unused_section"
Expand Down
4 changes: 3 additions & 1 deletion test/klippy/danger_options.cfg
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
[constants]
false: False

[poopybutt]
[danger_options]
log_statistics: False
log_config_file_at_startup: False
error_on_unused_config_options: False
error_on_unused_config_options: ${constants.false}
log_bed_mesh_at_startup: False
log_shutdown_info: False
allow_plugin_override: True
Expand Down

0 comments on commit 86d1f55

Please sign in to comment.