Skip to content

Config flag fails to apply pyproject TOML #1484

@TiepiNL

Description

@TiepiNL

Prerequisites

  • Are you running the latest version of this application?
  • Have you checked the Frequently Asked Questions document?
  • Have you simplified the bug report to the essential details?
    • Do you have a distinct command line to report?
    • Can you clearly state the configuration for this bug report?
    • Do you have a minimal document that highlights this bug?
    • Are any required files (configuration or Markdown document) attached to the issue?
  • Did you perform a cursory search of other issues to look for related issues?

Bug Report

Please replace any of [these areas] in the paragraphs below with the requested information.

Bug Type

  • Assertion Failure
  • Documentation
  • Scan/Rule not working as expected
  • Fix/Rule not working as expected
  • Other
    [Other reason]

Description

PyMarkdown's --config flag fails to apply pyproject TOML configuration due to a key mapping mismatch in plugin configuration lookup, causing the tool to fall back to default values instead of using the specified configuration.

Specifics

  • PyMarkdown version: 0.9.32
  • Python version: 3.13
  • application-properties: 0.9.0

Behavior

When using --config with TOML configuration files that use the pyproject.toml format (with [tool.pymarkdown] sections), pymarkdown ignores the specified configuration and uses default values instead. This occurs because pyproject.toml-style TOML configurations create keys with full dot-notation paths (tool.pymarkdown.plugins.md013.line_length), but the plugin lookup mechanism searches for shortened keys (plugins.md013.line_length), resulting in a key mismatch.

Note: TOML files using direct [plugins] sections work correctly - this bug only affects the pyproject.toml format with [tool.pymarkdown] sections.

Root Cause

The issue is in PluginManager.__find_configuration_for_plugin() method when processing pyproject.toml-format TOML files:

  1. Pyproject.toml-style TOML configs via --config: Create properties with full keys like tool.pymarkdown.plugins.md013.line_length (note: md013.line_length is used as an example here, but this affects all plugin settings)
  2. Plugin facade creation: Creates ApplicationPropertiesFacade with prefix plugins.md013.
  3. Property lookup: Searches for plugins.md013.line_length (prefix + property name)
  4. Key mismatch: Cannot find plugins.md013.line_length because only tool.pymarkdown.plugins.md013.line_length exists
  5. Fallback: Plugin uses hardcoded default values for all settings (e.g., line_length=80)

Comparison: TOML files with direct [plugins] sections create keys like plugins.md013.line_length which match the expected lookup pattern and work correctly.

Reproduction Steps

Step 1: Create pyproject.toml-style TOML configuration file

echo '[tool.pymarkdown]
plugins.md013.enabled = true
plugins.md013.line_length = 50' > pyproject.toml

Step 2: Create test markdown file (longer than 50 characters)

echo 'This line is definitely longer than 50 characters and should trigger MD013.' > test.md

Step 3: Run pymarkdown with pyproject.toml-style config

pymarkdown --config pyproject.toml scan test.md

Expected Result

test.md:1:1: MD013: Line length [Expected: 50, Actual: 78] (line-length)

Actual Result

test.md:1:1: MD013: Line length [Expected: 80, Actual: 78] (line-length)

Evidence: The violation shows Expected: 80 (default value) instead of Expected: 50 (configured value), proving the TOML configuration is ignored.

Technical Analysis

Configuration Loading Evidence

When debugging with ApplicationProperties, pyproject.toml-style TOML configs show:

Properties loaded: tool.pymarkdown.plugins.md013.line_length = 50
Plugin searches for: plugins.md013.line_length  
Result: Not found → falls back to default (80)

For comparison, direct [plugins] TOML configs work correctly:

Properties loaded: plugins.md013.line_length = 50
Plugin searches for: plugins.md013.line_length  
Result: Found → uses configured value (50)

Key Mapping Issue

The problem occurs in PluginManager.__find_configuration_for_plugin():

# Current code creates prefix: "plugins.md013."
plugin_section_title = (
    f"{PluginManager.__plugin_prefix}{properties.separator}"
    + f"{next_key_name}{properties.separator}"
)
# Results in: "plugins.md013."

# But pyproject.toml-style TOML configs have keys like: "tool.pymarkdown.plugins.md013.line_length"
# So lookup for "plugins.md013.line_length" fails

Impact

  • --config flag with pyproject.toml-style TOML files is completely non-functional
  • Users cannot use pyproject.toml format for pymarkdown configuration via --config
  • Affects pyproject.toml-based configuration workflows (the standard for modern Python projects)
  • No error message indicates the configuration is being ignored
  • TOML files using direct [plugins] sections continue to work correctly

Suggested Fix

Code Change Required

File: pymarkdown/plugin_manager/plugin_manager.py
Method: PluginManager.__find_configuration_for_plugin()
Location: Around line 918

@classmethod
def __find_configuration_for_plugin(
    cls,
    next_plugin: FoundPlugin,
    properties: ApplicationProperties,
    always_return_facade: bool = False,
) -> Optional[ApplicationPropertiesFacade]:
    plugin_specific_facade, first_facade = None, None
    for next_key_name in next_plugin.plugin_identifiers:
        # Try standard plugin prefix (for JSON configs and auto-discovered TOML)
        plugin_section_title = (
            f"{PluginManager.__plugin_prefix}{properties.separator}"
            + f"{next_key_name}{properties.separator}"
        )
        section_facade_candidate = ApplicationPropertiesFacade(
            properties, plugin_section_title
        )
        if not first_facade:
            first_facade = section_facade_candidate
        if section_facade_candidate.property_names:
            plugin_specific_facade = section_facade_candidate
            break

        # Try pyproject.toml-style prefix (for --config pyproject.toml files)
        toml_section_title = (
            f"tool{properties.separator}pymarkdown{properties.separator}"
            + f"{PluginManager.__plugin_prefix}{properties.separator}"
            + f"{next_key_name}{properties.separator}"
        )
        toml_section_facade_candidate = ApplicationPropertiesFacade(
            properties, toml_section_title
        )
        if toml_section_facade_candidate.property_names:
            plugin_specific_facade = toml_section_facade_candidate
            break

    if always_return_facade and not plugin_specific_facade:
        plugin_specific_facade = first_facade
    return plugin_specific_facade

(The code block at "Try pyproject.toml-style prefix" is the only new code.)

Fix Description

The fix adds a second lookup attempt with the pyproject.toml-style prefix (tool.pymarkdown.plugins.md013.) when the standard prefix fails. This maintains backward compatibility while enabling pyproject.toml configuration support via --config.

Workarounds

  1. Use JSON format for configuration (different code path, works correctly)
  2. Use auto-discovered TOML files (pyproject.toml in project root) instead of --config
  3. Use TOML files with direct [plugins] sections instead of [tool.pymarkdown] sections
  4. Use individual --set flags instead of configuration files

Verification

Test Case for Fix Validation

After applying the fix, the same reproduction steps should show:

pymarkdown --config pyproject.toml scan test.md

Expected Result (after fix):

test.md:1:1: MD013: Line length [Expected: 50, Actual: 78] (line-length)

Backward Compatibility

The fix maintains full backward compatibility:

  • JSON configurations continue to work unchanged
  • Auto-discovered TOML configurations (pyproject.toml in project root) continue to work unchanged
  • Direct [plugins] TOML configurations continue to work unchanged
  • Only adds support for previously broken --config pyproject.toml functionality

I'll create a PR based on the suggested fix later today.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions