Skip to content

fix: wrap string config setting values in list before iterating#10804

Merged
radoering merged 2 commits into
python-poetry:mainfrom
dimbleby:fix/executor-string-config
Mar 31, 2026
Merged

fix: wrap string config setting values in list before iterating#10804
radoering merged 2 commits into
python-poetry:mainfrom
dimbleby:fix/executor-string-config

Conversation

@dimbleby

@dimbleby dimbleby commented Mar 30, 2026

Copy link
Copy Markdown
Contributor

When a config setting value is a plain str (e.g. "CC": "gcc"), iterating it directly yields one character per iteration, producing garbled --config-settings flags in the error message pip command.

Pull Request Check List

Resolves: #issue-number-here

  • Added tests for changed code.
  • Updated documentation for changed code.

Summary by Sourcery

Ensure build backend error reporting correctly formats build config settings in generated pip commands.

Bug Fixes:

  • Prevent iteration over string-valued build config settings that previously produced malformed --config-settings flags in pip command error messages.

Tests:

  • Add regression test asserting that string-valued build config settings are rendered as whole values in the pip command within build backend error output.

@sourcery-ai

sourcery-ai Bot commented Mar 30, 2026

Copy link
Copy Markdown

Reviewer's Guide

Ensures build backend error reporting correctly formats pip --config-settings flags when config setting values are plain strings, and adds a regression test to cover this behavior.

File-Level Changes

Change Details Files
Normalize build-config-settings values to an iterable of strings before constructing pip --config-settings flags.
  • Retrieve config setting values into a local variable before iteration.
  • Detect when a config setting value is a plain string and wrap it in a single-element list.
  • Iterate over the normalized list of values to append correctly formatted --config-settings flags to the pip command string.
src/poetry/installation/executor.py
Add regression test ensuring string-valued build-config-settings are rendered correctly in pip command output on backend failure.
  • Configure a build-config-settings entry with a simple string value for a package (e.g. CC: gcc).
  • Trigger a BuildBackendException via a mocked ProjectBuilder.build call while executing an install operation.
  • Assert that the captured output contains a correctly formatted --config-settings='CC=gcc' fragment in the pip command.
tests/installation/test_executor.py

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

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • In _execute_operation, consider iterating over config_settings.items() (e.g. for setting, values in config_settings.items():) instead of indexing the dict twice to simplify the loop and avoid repeated lookups.
  • When normalizing values, you currently special-case only str; if other scalar types are ever passed (e.g. bytes), you may want to either handle them similarly or validate and raise early, so you don’t silently iterate over unexpected types.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `_execute_operation`, consider iterating over `config_settings.items()` (e.g. `for setting, values in config_settings.items():`) instead of indexing the dict twice to simplify the loop and avoid repeated lookups.
- When normalizing `values`, you currently special-case only `str`; if other scalar types are ever passed (e.g. bytes), you may want to either handle them similarly or validate and raise early, so you don’t silently iterate over unexpected types.

## Individual Comments

### Comment 1
<location path="tests/installation/test_executor.py" line_range="1575" />
<code_context>
     assert io.fetch_output() == expected_output


+def test_build_backend_error_includes_config_settings_in_pip_command(
+    mocker: MockerFixture,
+    config: Config,
</code_context>
<issue_to_address>
**suggestion (testing):** Add coverage for non-string/multiple config-setting values to ensure behavior for list inputs and multiple keys is preserved.

To better exercise `build-config-settings`, consider also covering:

- A setting whose value is already a list (e.g. `{"simple-project": {"CC": ["gcc", "clang"]}}`), asserting we get two `--config-settings` flags.
- Multiple keys for the same package (e.g. `{"CC": "gcc", "CFLAGS": "-O2"}`), asserting all appear in the pip command.

You can add a separate test for these or parametrize this one over string vs list vs multiple-key configurations to guard against regressions in value handling.

Suggested implementation:

```python
@pytest.mark.parametrize(
    "config_settings,expected_flags",
    [
        # A single string value for a single key
        (
            {"simple-project": {"CC": "gcc"}},
            ["--config-settings", "simple-project:CC=gcc"],
        ),
        # A list value for a single key should expand to multiple flags
        (
            {"simple-project": {"CC": ["gcc", "clang"]}},
            [
                "--config-settings",
                "simple-project:CC=gcc",
                "--config-settings",
                "simple-project:CC=clang",
            ],
        ),
        # Multiple keys for the same package should all appear
        (
            {"simple-project": {"CC": "gcc", "CFLAGS": "-O2"}},
            [
                "--config-settings",
                "simple-project:CC=gcc",
                "--config-settings",
                "simple-project:CFLAGS=-O2",
            ],
        ),
    ],
)
def test_build_backend_error_includes_config_settings_in_pip_command(
    mocker: MockerFixture,
    config: Config,
    pool: RepositoryPool,
    io: BufferedIO,
    env: MockEnv,
    fixture_dir: FixtureDirGetter,
    config_settings: dict[str, dict[str, object]],
    expected_flags: list[str],
) -> None:
    """Config setting values (strings, lists, multiple keys) must be expanded to full flags."""
    error = BuildBackendException(
        Exception("build failed"), description="build failed"
    )
    mocker.patch.object(ProjectBuilder, "build", side_effect=error)

    # Configure build-config-settings for this parameterized case
    # The concrete way to attach this to `config` should match how the executor
    # currently reads build configuration in this test file.
    config.set_build_config_settings(config_settings)  # type: ignore[attr-defined]

```

To fully implement the requested coverage, adjust the rest of `test_build_backend_error_includes_config_settings_in_pip_command` (the part not shown in the snippet) to:

1. Use the parameterized `config_settings` instead of any hard-coded build-config-settings:
   - If you are currently doing something like:
     `config.set_build_config_settings({"simple-project": {"CC": "gcc"}})`
     replace that with the already-added call or adapt it to your API.
   - If `Config` does not expose `set_build_config_settings`, use the correct mechanism already used in this file to inject build-config-settings (e.g. `config.merge()`, direct attribute assignment, or a dedicated helper). In that case, update the line I added to that mechanism instead of `set_build_config_settings`.

2. Assert that all `expected_flags` appear in the pip command constructed from the error:
   - Wherever you currently assert on the pip command (typically via `io.fetch_output()` or `io.fetch_error()` or by inspecting a `cmd` list), extend the assertion to verify:
     - Each `expected_flags[i]` appears in order.
     - The number of `--config-settings` occurrences matches the length of `expected_flags` // 2 (for flag/value pairs).
   - For example, if you have a `cmd` list:
     `cmd = <whatever builds the pip command>`
     add an assertion such as:
     `assert expected_flags == [arg for arg in cmd if arg in expected_flags]`
     or, if you capture a string:
     `output = io.fetch_output()`
     `for flag in expected_flags: assert flag in output`

3. Ensure `pytest` is imported in this file if it is not already:
   - At the top of `tests/installation/test_executor.py`, add:
     `import pytest`
   - If there is already a `pytest` import, no further changes are needed.

These adjustments will give the test real coverage of:
- A single string-valued config-setting.
- A list-valued config-setting expanding to multiple `--config-settings` flags.
- Multiple keys for the same package, all present in the pip command.
</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 thread tests/installation/test_executor.py
@dosubot

dosubot Bot commented Mar 30, 2026

Copy link
Copy Markdown

Documentation Updates

1 document(s) were updated by changes in this PR:

CHANGELOG
View Changes
@@ -11,6 +11,7 @@
 - Fix an issue where HTTP Basic Authentication credentials could be corrupted during request preparation, causing authentication failures with long tokens ([#10748](https://github.com/python-poetry/poetry/pull/10748)).
 - Fix an issue where `poetry publish --no-interaction --build` requested user interaction ([#10769](https://github.com/python-poetry/poetry/pull/10769)).
 - Fix an issue where `poetry init` and `poetry new` created a deprecated `project.license` format ([#10787](https://github.com/python-poetry/poetry/pull/10787)).
+- Fix an issue where string config setting values in `installer.build-config-settings` were iterated character-by-character instead of as whole values, producing garbled `--config-settings` flags in the pip fallback error message command ([#10804](https://github.com/python-poetry/poetry/pull/10804)).
 
 ### Docs
 

How did I do? Any feedback?  Join Discord

@dimbleby dimbleby force-pushed the fix/executor-string-config branch from 5a2713e to eca8626 Compare March 30, 2026 14:35
dimbleby and others added 2 commits March 31, 2026 17:53
When a config setting value is a plain str (e.g. "CC": "gcc"),
iterating it directly yields one character per iteration, producing
garbled --config-settings flags in the error message pip command.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@radoering radoering force-pushed the fix/executor-string-config branch from eca8626 to e6bd472 Compare March 31, 2026 15:54
@radoering radoering enabled auto-merge (squash) March 31, 2026 15:55
@radoering radoering merged commit 35eb502 into python-poetry:main Mar 31, 2026
54 checks passed
@dimbleby dimbleby deleted the fix/executor-string-config branch March 31, 2026 16:16
@github-actions

github-actions Bot commented May 1, 2026

Copy link
Copy Markdown

This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions Bot locked as resolved and limited conversation to collaborators May 1, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants