Skip to content

Conversation

@jackdewinter
Copy link
Owner

@jackdewinter jackdewinter commented Jan 24, 2026

Summary by Sourcery

Update TOML loading to support keys containing periods when quoted, ensure explicit configuration files respect TOML section headers, and add tooling and documentation updates for the new release.

New Features:

  • Allow ApplicationProperties to load dictionaries with keys containing periods when explicitly enabled.
  • Enable MultisourceConfigurationLoader to specify an optional TOML section header when loading configuration files.
  • Introduce reusable patch utilities for mocking builtins.open in tests.

Bug Fixes:

  • Fix TOML key parsing to accept quoted keys containing separator characters.
  • Ensure configuration files loaded via the config flag honor the TOML section header similarly to implicit pyproject.toml loading.

Enhancements:

  • Extend test coverage for TOML key edge cases and configuration loading from implicit and explicit pyproject.toml files.
  • Add an option to skip dependency upgrade checks in the clean.sh helper script.

Documentation:

  • Update changelog with version 0.9.1 entry and historical release notes.

@sourcery-ai
Copy link

sourcery-ai bot commented Jan 24, 2026

Reviewer's Guide

Adds TOML key-handling improvements (support for quoted keys containing periods and slashes), wires section-header handling into the multisource configuration loader for TOML files, introduces reusable open() patching utilities for tests, extends TOML loader tests, updates CI tooling script flags, bumps version to 0.9.1, and documents fixes in the changelog.

Sequence diagram for TOML configuration loading with section headers and quoted keys

sequenceDiagram
    participant Client
    participant Loader as MultisourceConfigurationLoader
    participant Options as MultisourceConfigurationLoaderOptions
    participant AppProps as ApplicationProperties
    participant TomlLoader as ApplicationPropertiesTomlLoader

    Client->>Loader: _load_config(file_name, options, AppProps, handle_error_fn)
    Loader->>Loader: detect ConfigurationFileType TOML
    Loader->>Loader: __load_as_toml(file_name, options, AppProps, handle_error_fn)
    Loader->>TomlLoader: load_and_set(AppProps, file_name, section_header=Options.section_header_if_toml, handle_error_fn, clear_property_map=False, check_for_file_presence=True, is_configuration_required=True)
    TomlLoader->>AppProps: load_from_dict(configuration_map, clear_map=False, allow_periods_in_keys=True)
    AppProps->>AppProps: __scan_map(config_map, current_prefix="", allow_periods_in_keys=True)
    AppProps-->>TomlLoader: properties updated with quoted period keys preserved
    TomlLoader-->>Loader: did_apply_map, did_have_one_error
    Loader-->>Client: did_apply_map, did_have_one_error
Loading

Class diagram for updated TOML loading and key parsing

classDiagram
    class ApplicationProperties {
        +load_from_dict(config_map, clear_map, allow_periods_in_keys)
        -__scan_map(config_map, current_prefix, allow_periods_in_keys)
    }

    class ApplicationPropertiesTomlLoader {
        +load_and_set(properties_object, configuration_filename, section_header, handle_error_fn, clear_property_map, check_for_file_presence, is_configuration_required) Tuple~bool,bool~
    }

    class MultisourceConfigurationLoaderOptions {
        +bool parse_json_as_json5
        +Optional~string~ section_header_if_toml
    }

    class MultisourceConfigurationLoader {
        -__load_as_toml(file_name, options, application_properties, handle_error_fn) Tuple~bool,bool~
        -_load_config(file_name, options, application_properties, handle_error_fn) Tuple~bool,bool~
    }

    MultisourceConfigurationLoader --> MultisourceConfigurationLoaderOptions : uses
    MultisourceConfigurationLoader --> ApplicationPropertiesTomlLoader : calls
    ApplicationPropertiesTomlLoader --> ApplicationProperties : config_target
Loading

File-Level Changes

Change Details Files
Allow TOML-loaded configuration keys to contain periods by relaxing validation and quoting such keys internally when scanning config maps.
  • Extended ApplicationProperties.load_from_dict to accept an allow_periods_in_keys flag and pass it into the internal scan method.
  • Updated __scan_map to conditionally allow the separator character in keys when allowed, to auto-quote keys containing the separator, and to propagate the flag during recursive scanning.
  • Enabled allow_periods_in_keys when loading TOML maps in ApplicationPropertiesTomlLoader.load_and_set so TOML keys with quoted periods are supported.
application_properties/application_properties.py
application_properties/application_properties_toml_loader.py
Ensure TOML configuration loaded via MultisourceConfigurationLoader honors a section header consistently, including for explicit config files.
  • Added section_header_if_toml option to MultisourceConfigurationLoaderOptions for specifying an optional TOML section header.
  • Updated __load_as_toml and _load_config to pass options into TOML loading and to forward section_header_if_toml to ApplicationPropertiesTomlLoader.load_and_set.
  • Clarified add_specified_configuration_file docstring to mention section_header_if_toml.
application_properties/multisource_configuration_loader.py
Add reusable patch utilities for builtins.open to support test scenarios that need in-memory file content or exceptions, and use them to test implicit pyproject.toml loading.
  • Introduced PatchBase helper for managing unittest.mock.patch lifecycles and action logging.
  • Implemented PatchBuiltinOpen to register per-path text content, binary content, or exceptions, and provide context managers for binary-content and exception-based open() patches.
  • Used path_builtin_open_with_binary_content in tests to simulate a local pyproject.toml without touching the filesystem.
test/patches/patch_base.py
test/patches/patch_builtin_open.py
test/test_application_properties_toml_loader.py
Expand TOML loader tests to cover quoted/unquoted keys with separators, unsupported value types, and TOML loading via pyproject and explicit configuration files.
  • Added tests to verify TOML keys with quoted separators and slashes are loaded correctly and mapped to the expected property names.
  • Added tests asserting that unquoted keys with separators/slashes in TOML cause parse errors and that unsupported TOML value types fall back to default values when converted.
  • Added tests that exercise MultisourceConfigurationLoader with implicit pyproject.toml and explicit config files, validating section-header-based TOML loading.
  • Inserted explicit '# Assert' comments in several existing TOML loader tests for readability.
test/test_application_properties_toml_loader.py
Update project documentation, versioning, and tooling script behavior to reflect the new release and provide opt-out for dependency upgrade checks.
  • Bumped library version from 0.9.0 to 0.9.1.
  • Extended changelog with entries for versions 0.9.1 through 0.5.0, documenting TOML parsing and config flag fixes plus historical changes.
  • Added a --no-upgrades/-nu flag to clean.sh that disables automatic dependency upgrade checks in look_for_upgrades.
  • Updated usage text and option parsing in clean.sh to handle NO_UPGRADE_MODE.
application_properties/version.py
newdocs/src/changelog.md
clean.sh
publish/coverage.json
publish/pylint_suppression.json
publish/test-results.json

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 4 issues, and left some high level feedback:

  • In PatchBuiltinOpen.__my_open, calling self.stop() and then self.start() around the passthrough open call means the side effect is only attached the first time; subsequent start() calls don’t re-register __my_open, so later opens will be handled by a plain mock instead of the custom logic—consider either avoiding stop/start in the side-effect or reapplying the side effect each time start() is called.
  • The key validation error raised in ApplicationProperties.__scan_map still claims keys cannot contain the separator character even when allow_periods_in_keys=True; consider adjusting the message (or branching it) so it accurately reflects the allowed characters in that mode.
  • The docstring for register_binary_content_for_file in PatchBuiltinOpen describes registering text content for text mode, but the implementation targets binary content for rb mode—updating this description would make the intent and usage clearer.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `PatchBuiltinOpen.__my_open`, calling `self.stop()` and then `self.start()` around the passthrough `open` call means the side effect is only attached the first time; subsequent `start()` calls don’t re-register `__my_open`, so later opens will be handled by a plain mock instead of the custom logic—consider either avoiding stop/start in the side-effect or reapplying the side effect each time `start()` is called.
- The key validation error raised in `ApplicationProperties.__scan_map` still claims keys cannot contain the separator character even when `allow_periods_in_keys=True`; consider adjusting the message (or branching it) so it accurately reflects the allowed characters in that mode.
- The docstring for `register_binary_content_for_file` in `PatchBuiltinOpen` describes registering text content for text mode, but the implementation targets binary content for `rb` mode—updating this description would make the intent and usage clearer.

## Individual Comments

### Comment 1
<location> `application_properties/application_properties.py:472-476` </location>
<code_context>
                 or "\t" in next_key
                 or "\n" in next_key
                 or ApplicationProperties.__assignment_operator in next_key
-                or ApplicationProperties.__separator in next_key
+                or (
+                    ApplicationProperties.__separator in next_key
+                    and not allow_periods_in_keys
</code_context>

<issue_to_address>
**suggestion:** Make the validation error message reflect the `allow_periods_in_keys` behavior.

The `ValueError` text still states that keys cannot contain the separator, even though the condition now allows it when `allow_periods_in_keys` is `True`. To avoid confusion when reading the code, please update the message to reflect that this restriction is conditional (e.g., branch the message on `allow_periods_in_keys` or reword it to note the configurability).

```suggestion
                raise ValueError(
                    "Key strings cannot contain a whitespace character, "
                    + f"a '{ApplicationProperties.__assignment_operator}' character, or "
                    + f"a '{ApplicationProperties.__separator}' character when periods in keys are disabled."
                )
```
</issue_to_address>

### Comment 2
<location> `newdocs/src/changelog.md:90-91` </location>
<code_context>
+    - added ability to translate from string to other forms
+    - controlled by flag, and only occurs on pure strings, not any strings with
+      any typing
+        - i.e. only manual set and "config" files are untyped, so only those are
+          affected
+- [Issue 145](https://github.com/jackdewinter/application_properties/issues/145)
</code_context>

<issue_to_address>
**issue (typo):** Minor grammar issue: "only manual set" should likely be "only manually set."

Suggest updating the parenthetical to: `i.e. only manually set and "config" files are untyped, so only those are affected`.

```suggestion
        - i.e. only manually set and "config" files are untyped, so only those are
          affected
```
</issue_to_address>

### Comment 3
<location> `newdocs/src/changelog.md:105` </location>
<code_context>
+<!-- pyml disable-next-line no-duplicate-heading-->
+### Fixed and Added
+
+- Added `py.typed` file to ensure that type hints are consumed by package.
+- Added `__all__` variable to `__init__.py` to resolve mypy error
+
</code_context>

<issue_to_address>
**suggestion (typo):** Minor grammar: add an article before "package."

Consider changing this to "consumed by the package" for more natural grammar.

```suggestion
- Added `py.typed` file to ensure that type hints are consumed by the package.
```
</issue_to_address>

### Comment 4
<location> `newdocs/src/changelog.md:106` </location>
<code_context>
+### Fixed and Added
+
+- Added `py.typed` file to ensure that type hints are consumed by package.
+- Added `__all__` variable to `__init__.py` to resolve mypy error
+
+## Version 0.5.1 - Date: 2022-04-01
</code_context>

<issue_to_address>
**suggestion (typo):** Minor grammar: consider adding an article before "mypy error."

Change this to “to resolve a mypy error” for smoother grammar.

```suggestion
- Added `__all__` variable to `__init__.py` to resolve a mypy error
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines 472 to 476
raise ValueError(
"Key strings cannot contain a whitespace character, "
+ f"a '{ApplicationProperties.__assignment_operator}' character, or "
+ f"a '{ApplicationProperties.__separator}' character."
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Make the validation error message reflect the allow_periods_in_keys behavior.

The ValueError text still states that keys cannot contain the separator, even though the condition now allows it when allow_periods_in_keys is True. To avoid confusion when reading the code, please update the message to reflect that this restriction is conditional (e.g., branch the message on allow_periods_in_keys or reword it to note the configurability).

Suggested change
raise ValueError(
"Key strings cannot contain a whitespace character, "
+ f"a '{ApplicationProperties.__assignment_operator}' character, or "
+ f"a '{ApplicationProperties.__separator}' character."
)
raise ValueError(
"Key strings cannot contain a whitespace character, "
+ f"a '{ApplicationProperties.__assignment_operator}' character, or "
+ f"a '{ApplicationProperties.__separator}' character when periods in keys are disabled."
)

Comment on lines 90 to 91
- i.e. only manual set and "config" files are untyped, so only those are
affected
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (typo): Minor grammar issue: "only manual set" should likely be "only manually set."

Suggest updating the parenthetical to: i.e. only manually set and "config" files are untyped, so only those are affected.

Suggested change
- i.e. only manual set and "config" files are untyped, so only those are
affected
- i.e. only manually set and "config" files are untyped, so only those are
affected

<!-- pyml disable-next-line no-duplicate-heading-->
### Fixed and Added

- Added `py.typed` file to ensure that type hints are consumed by package.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (typo): Minor grammar: add an article before "package."

Consider changing this to "consumed by the package" for more natural grammar.

Suggested change
- Added `py.typed` file to ensure that type hints are consumed by package.
- Added `py.typed` file to ensure that type hints are consumed by the package.

### Fixed and Added

- Added `py.typed` file to ensure that type hints are consumed by package.
- Added `__all__` variable to `__init__.py` to resolve mypy error
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (typo): Minor grammar: consider adding an article before "mypy error."

Change this to “to resolve a mypy error” for smoother grammar.

Suggested change
- Added `__all__` variable to `__init__.py` to resolve mypy error
- Added `__all__` variable to `__init__.py` to resolve a mypy error

@codecov
Copy link

codecov bot commented Jan 24, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (14141cb) to head (d7a1aa9).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main      #321   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           10        10           
  Lines          657       661    +4     
  Branches        89        90    +1     
=========================================
+ Hits           657       661    +4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jackdewinter jackdewinter merged commit 93b44ad into main Jan 24, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants