Skip to content

feat(install): add --exclude-paths install option#152

Merged
stanislavlevin merged 1 commit into
mainfrom
issue_151
May 15, 2026
Merged

feat(install): add --exclude-paths install option#152
stanislavlevin merged 1 commit into
mainfrom
issue_151

Conversation

@stanislavlevin
Copy link
Copy Markdown
Owner

@stanislavlevin stanislavlevin commented May 15, 2026

Opt-in install --exclude-paths PATTERN [PATTERN ...] that drops wheel members matching one or more fnmatch globs before extraction. Intended for RPM packaging to strip test files (pkg/tests/, test_*.py, *_test.py) and similar artifacts shipped inside wheels but unwanted in installed form. Default empty -- byte-identical when the flag isn't set. nargs="+", matching the existing deps eval --exclude convention.

See for details:
docs/designs/exclude_paths_option.md

Fixes: #151

Summary by Sourcery

Add an opt-in install-time path exclusion mechanism to filter wheel contents based on glob patterns before extraction and document its behavior.

New Features:

  • Introduce an --exclude-paths install CLI option that accepts one or more fnmatch glob patterns to omit matching wheel members from installation.
  • Extend the install_wheel API with an exclude_paths parameter to support programmatic path-based exclusion of wheel contents.

Enhancements:

  • Ensure excluded wheel members are never written to disk or RPM filelists, and their empty parent directories are not created.
  • Document the design, usage, and packaging-oriented behavior of the exclude-paths option in a dedicated design document and README section.

Tests:

  • Add install tests verifying default behavior without exclusions, correct dropping of matching members including dist-info files, absence of empty parent directories, and interaction with RPM filelists.
  • Add CLI tests covering parsing and propagation of --exclude-paths for single and multiple patterns and enforcing that at least one pattern is provided.

Opt-in `install --exclude-paths PATTERN [PATTERN ...]` that drops
wheel members matching one or more fnmatch globs before extraction.
Intended for RPM packaging to strip test files (pkg/tests/,
test_*.py, *_test.py) and similar artifacts shipped inside wheels
but unwanted in installed form. Default empty -- byte-identical
when the flag isn't set. nargs="+", matching the existing
`deps eval --exclude` convention.

See for details:
docs/designs/exclude_paths_option.md

Fixes: #151
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Stanislav Levin <slev@altlinux.org>
Copy link
Copy Markdown

@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 2 issues, and left some high level feedback:

  • In install_wheel, the local variable name chain for the iterator returned by filter_dist_info/filter_exclude_paths can be confusing given the common use of itertools.chain; consider renaming it to something more domain-specific (e.g. member_iter) to improve readability.
  • The new --exclude-paths flag uses nargs='+', which is inherently greedy and can consume the wheel path if it appears after the option; if this proves surprising in practice, you might consider adding an argparse-level constraint or helper (e.g. a custom action or clearer usage error) to defensively guide users toward the intended argument ordering.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `install_wheel`, the local variable name `chain` for the iterator returned by `filter_dist_info`/`filter_exclude_paths` can be confusing given the common use of `itertools.chain`; consider renaming it to something more domain-specific (e.g. `member_iter`) to improve readability.
- The new `--exclude-paths` flag uses `nargs='+'`, which is inherently greedy and can consume the wheel path if it appears after the option; if this proves surprising in practice, you might consider adding an argparse-level constraint or helper (e.g. a custom action or clearer usage error) to defensively guide users toward the intended argument ordering.

## Individual Comments

### Comment 1
<location path="tests/unit/test_main.py" line_range="616-627" />
<code_context>
+    mock_install_wheel.assert_called_once_with(*i_args, **i_kwargs)
+
+
+@pytest.mark.usefixtures("mock_read_tracker")
+def test_install_cli_exclude_paths_requires_value(mock_install_wheel):
+    """
+    --exclude-paths requires at least one value (nargs='+').
+    """
+    install_args = ["install", "--exclude-paths"]
+
+    with pytest.raises(SystemExit):
+        project_main.main(install_args)
+    # actual error message is controlled by cpython,
+    # so it's unreliable to check stderr
+    mock_install_wheel.assert_not_called()
+
+
</code_context>
<issue_to_address>
**suggestion (testing):** Strengthen the CLI error test by asserting the SystemExit code

In `test_install_cli_exclude_paths_requires_value`, you can make the test more robust by capturing the exception (`with pytest.raises(SystemExit) as excinfo:`) and asserting `excinfo.value.code == 2`. This keeps the test independent of the error message while still verifying the CLI is treated as an argument error by argparse.

```suggestion
@pytest.mark.usefixtures("mock_read_tracker")
def test_install_cli_exclude_paths_requires_value(mock_install_wheel):
    """
    --exclude-paths requires at least one value (nargs='+').
    """
    install_args = ["install", "--exclude-paths"]

    with pytest.raises(SystemExit) as excinfo:
        project_main.main(install_args)
    # argparse uses exit code 2 for CLI argument errors
    assert excinfo.value.code == 2
    # actual error message is controlled by cpython,
    # so it's unreliable to check stderr
    mock_install_wheel.assert_not_called()
```
</issue_to_address>

### Comment 2
<location path="docs/designs/exclude_paths_option.md" line_range="10-12" />
<code_context>
+but that aren't useful in installed form.
+
+## Motivation
+Some Python projects ship test files (e.g. `pkg/tests/`,
+`test_*.py`, `*_test.py`) inside their wheel. For RPM packaging
+those files:
+
</code_context>
<issue_to_address>
**issue (typo):** Add a comma after "For RPM packaging" for grammatical correctness.

This will read more naturally as: "For RPM packaging, those files:".

```suggestion
Some Python projects ship test files (e.g. `pkg/tests/`,
`test_*.py`, `*_test.py`) inside their wheel. For RPM packaging, 
those files:
```
</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/unit/test_main.py
Comment thread docs/designs/exclude_paths_option.md
@stanislavlevin stanislavlevin merged commit e3bfebc into main May 15, 2026
22 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.

[installer] add possibility to skip installation of wheel members

1 participant