Skip to content

Skip the maturin wheel build when its script is unreachable (#211) - #217

Open
leynos wants to merge 4 commits into
mainfrom
fix/mutmut-baseline-maturin
Open

Skip the maturin wheel build when its script is unreachable (#211)#217
leynos wants to merge 4 commits into
mainfrom
fix/mutmut-baseline-maturin

Conversation

@leynos

@leynos leynos commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Summary

  • Every scheduled mutation-testing run for cuprum has failed at the
    baseline since 2026-07-14: test_maturin_wheel_build_snapshot dies
    with Unable to find maturin script before mutmut generates any
    mutants, so no mutation testing has actually happened.
  • Root cause: the maturin PyPI package locates its own compiled
    binary by walking each sysconfig scheme's scripts directory keyed
    off the running interpreter's sys.prefix — not sys.path/PATH.
    Under mutmut's uv run --with mutmut==3.6.0 overlay, sys.prefix
    points at a temporary environment layered on top of the project's own
    virtualenv: the maturin module still imports fine (via sys.path,
    so cargo/rustc and the existing toolchain_available() check all
    report success), but the overlay never received maturin's script, so
    the lookup comes up empty and the build subprocess exits 1.
  • Fix: add maturin_script_locatable() to tests/helpers/maturin.py,
    mirroring maturin's own lookup exactly, and skip
    test_maturin_wheel_build_snapshot with a precise reason when it
    reports the script unreachable. In every normal environment (CI,
    build-wheels.yml, local uv run pytest) sys.prefix matches the
    virtualenv that installed the script, so the real native build still
    runs unchanged — only the layered mutmut overlay skips.

Closes #211

Why this design over the alternatives

The issue proposed two paths: exclude the test from mutmut's baseline
selection, or make the test locate maturin robustly and skip only when
genuinely unavailable. This PR takes the second path because it is the
more precise fix for the actual root cause:

  • The maturin binary genuinely is unreachable in mutmut's layered
    overlay (confirmed empirically below), so a pytest.skip with an
    exact reason is not a workaround — it is accurate diagnosis.
  • Excluding the test from mutmut's test selection would need a new
    [tool.mutmut] section in pyproject.toml (there currently is none;
    mutmut runs on defaults) and would silence the test everywhere
    mutmut runs, even in environments where the build would actually
    succeed.
  • This keeps the wheel-build snapshot test exercising the real
    maturin build path in every environment that can actually complete
    it, per the issue's stated preference.

Review walkthrough

  • tests/helpers/maturin.py:
    adds _script_named_maturin_exists() and maturin_script_locatable(),
    which reproduce maturin.__main__.get_maturin_path()'s own
    sysconfig-based scan for a file named maturin under each scheme's
    scripts directory.
  • cuprum/unittests/test_maturin_build.py:
    test_maturin_wheel_build_snapshot now skips with a reason naming
    sys.prefix when the script can't be found, alongside the existing
    Rust-toolchain skip. Two new unit tests
    (test_maturin_script_locatable_true_when_script_present /
    _false_when_script_absent) pin the detector's behaviour by faking
    the sysconfig scheme lookup, independent of the real environment.
  • tests/helpers/maturin_wheel.py:
    new module. Rebasing onto main pulled in MaturinBuildError (Fix Command-Query Separation violations (#118) #144),
    which together with the new detector pushed tests/helpers/maturin.py
    past pylint's 400-line module limit. The wheel-artifact snapshot parsers
    (wheel_build_snapshot and its private helpers) move here;
    tests/helpers/maturin.py re-exports wheel_build_snapshot, so all
    import sites are unchanged.
  • docs/developers-guide.md: documents maturin_script_locatable()'s scope, how it differs from toolchain_available(), the native-wheel skip boundary, and the reuse policy (which tests should gate on it), per the AGENTS.md abstraction/helper policy.

Validation evidence

Reproduced the exact CI failure locally, then confirmed the fix:

$ uv run --with mutmut==3.6.0 python -m pytest -x cuprum/unittests/test_maturin_build.py::test_maturin_wheel_build_snapshot
# before the fix:
subprocess.CalledProcessError: Command '[...python', '-m', 'maturin', 'build', ...]' returned non-zero exit status 1.
Unable to find `maturin` script
1 failed in 0.14s

# after the fix:
cuprum/unittests/test_maturin_build.py::test_maturin_wheel_build_snapshot SKIPPED
SKIPPED [1] ...: maturin's compiled script is not locatable via this interpreter's
sysconfig scripts directories (sys.prefix='/home/leynos/.cache/uv/builds-v0/.tmp...');
this is expected in layered/ephemeral interpreters such as a `uv run --with ...` overlay.

Confirmed the fix does not mask a real build in normal environments:

$ uv run pytest -rs cuprum/unittests/test_maturin_build.py::test_maturin_wheel_build_snapshot -v
cuprum/unittests/test_maturin_build.py::test_maturin_wheel_build_snapshot PASSED
1 passed in 9.80s

Gates run against the two changed files (make build, make check-fmt,
make lint, make typecheck, make test):

  • build, check-fmt, lint, typecheck: pass.
  • test: all Python suites pass, including the full
    cuprum/unittests/test_maturin_build.py file (14 passed). One
    pre-existing Rust trybuild UI fixture
    (cuprum-rust::compile_tests::compile_time_ui,
    tests/ui/fail/const_availability_export.rs) fails on a rustc
    diagnostic-wording drift unrelated to this change; confirmed it also
    fails identically on an unmodified origin/main checkout.

Test plan

  • uv run --with mutmut==3.6.0 python -m pytest -x cuprum/unittests/test_maturin_build.py — reproduced the failure,
    then confirmed the skip.
  • uv run pytest cuprum/unittests/test_maturin_build.py — full
    file green, real build still exercised in a normal virtualenv.
  • make check-fmt, make lint, make typecheck — pass.
  • CI: watch the PR checks and the next scheduled mutation-testing
    run.

References

Summary by Sourcery

Skip the maturin wheel-build snapshot test when maturin’s compiled script is not locatable by the running interpreter, while keeping normal builds exercised and reorganising wheel snapshot helpers into a dedicated module.

Bug Fixes:

  • Prevent mutation-testing runs from failing early by conditionally skipping the maturin wheel-build snapshot test when maturin’s script cannot be found in the current interpreter’s sysconfig scripts directories.

Enhancements:

  • Add a helper to detect whether maturin’s compiled script is discoverable in the current environment, mirroring maturin’s own lookup.
  • Refactor wheel artifact snapshot logic into a dedicated helper module and re-export its main entry point to preserve existing imports.

Tests:

  • Add unit tests covering maturin script discoverability in environments where the script is present or absent.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry @leynos, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Extract wheel snapshot parsing into a dedicated helper, re-export it through the existing maturin helper, and add sysconfig-based detection with platform-specific tests, documentation, and a skip guard for interpreters where the maturin script is unavailable.

Changes

Maturin build contracts

Layer / File(s) Summary
Wheel snapshot extraction
tests/helpers/maturin_wheel.py, tests/helpers/maturin.py, docs/developers-guide.md
Move wheel metadata parsing and archive normalisation into wheel_build_snapshot, define typed snapshot contracts, and re-export the helper from the existing module while documenting the split.
Maturin script detection and build gating
tests/helpers/maturin.py, cuprum/unittests/test_maturin_build.py, docs/developers-guide.md
Add sysconfig-based detection of maturin and maturin.exe, test present and absent script cases, skip unavailable wheel builds, and document the native-wheel skip boundary.

Sequence Diagram(s)

sequenceDiagram
  participant WheelBuildTest
  participant maturin_script_locatable
  participant sysconfig
  participant ScriptsDirectory
  WheelBuildTest->>maturin_script_locatable: check script availability
  maturin_script_locatable->>sysconfig: read scripts directories
  sysconfig-->>maturin_script_locatable: return scheme paths
  maturin_script_locatable->>ScriptsDirectory: search for maturin stem
  ScriptsDirectory-->>maturin_script_locatable: report presence or absence
  maturin_script_locatable-->>WheelBuildTest: return boolean
  WheelBuildTest->>WheelBuildTest: skip or build wheel snapshot
Loading

Suggested labels: Issue

Suggested reviewers: codescene-access

Poem

Wheels hum softly, scripts align,
Snapshots settle, neat and fine.
Paths are checked through every scheme,
Tests now guard the build-time dream.
If maturin hides from sight,
Skip the build and keep things right.

🚥 Pre-merge checks | ✅ 19 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Testing (Property / Proof) ⚠️ Warning maturin_script_locatable() spans many scheme/path/name combinations, but the PR adds only example tests; no property test or proof exercises the invariant. Add a Hypothesis property test over sysconfig schemes and script names/paths (including stem-based Windows launchers), or justify a bounded proof if you want exhaustive coverage.
✅ Passed checks (19 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the fix and includes the linked issue reference.
Description check ✅ Passed The description clearly covers the maturin skip fix and related helper refactor.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Testing (Overall) ✅ Passed Use the new helper tests: they exercise real files for present/absent scripts and Windows .exe matching, and the wheel snapshot asserts a rich on-disk oracle.
User-Facing Documentation ✅ Passed No user-facing behaviour changed; the new maturin skip logic and wheel helpers live in tests, while the users' guide already covers end-user Rust/native-wheel usage.
Developer Documentation ✅ Passed Document the new maturin helper boundary and module split in docs/developers-guide.md; no roadmap or execplan item required updating.
Module-Level Documentation ✅ Passed Confirm that each changed Python module begins with a clear module docstring, and the new wheel helper docstring states its split from and re-export relation to tests.helpers.maturin.
Testing (Unit And Behavioural) ✅ Passed Add unit tests for presence/absence and Windows stem matching, and keep the real maturin wheel-build snapshot at the behavioural boundary.
Testing (Compile-Time / Ui) ✅ Passed No Rust/TS compile-time code was touched; the new Python snapshot test is focused and stable via normalisation.
Unit Architecture ✅ Passed PASS: the new query helper only probes script existence, the build command stays separate, and wheel snapshot parsing was split into its own module.
Domain Architecture ✅ Passed PASS: Keep the change confined to tests/helpers and docs; no domain model, repository, or business policy leaked into application code.
Observability ✅ Passed PASS: the PR only adjusts test helpers/docs; no production logging, metrics, tracing, or alerts changed, and the new skip reason carries sys.prefix for diagnosis.
Security And Privacy ✅ Passed No secrets, credentials, auth, injection, or broad-permission changes were added; the diff is confined to tests, docs, and wheel snapshot parsing.
Performance And Resource Use ✅ Passed PASS: The added scans are bounded test helpers over sysconfig scripts dirs and one wheel archive; no new hot-path work, unbounded buffers, or avoidable quadratic behaviour.
Concurrency And State ✅ Passed Added helpers are pure; module-level state is read-only, and tests isolate monkeypatching. No locks, tasks, or shared mutable state were introduced.
Architectural Complexity And Maintainability ✅ Passed PASS: split wheel snapshot parsing into a focused sibling module, kept imports re-exported, and documented a concrete reuse policy for the new maturin probe.
Rust Compiler Lint Integrity ✅ Passed The PR changes only Python test helpers; git diff shows no .rs files, so no Rust lint suppressions or clone regressions were introduced.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/mutmut-baseline-maturin

Comment @coderabbitai help to get the list of available commands.

@lodyai
lodyai Bot force-pushed the fix/mutmut-baseline-maturin branch from 679ce9d to 3511d5d Compare July 28, 2026 09:42
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@leynos
leynos marked this pull request as ready for review July 28, 2026 09:55
@sourcery-ai

sourcery-ai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Adds an environment-aware detector for maturin’s compiled script so the maturin wheel snapshot test can skip in layered interpreters where the script is genuinely unreachable, and factors wheel snapshot parsing into a dedicated helper module while preserving existing imports.

File-Level Changes

Change Details Files
Add a sysconfig-based detector for maturin’s compiled script and use it to conditionally skip the wheel-build snapshot test when the script cannot be located by the running interpreter.
  • Introduce _script_named_maturin_exists and maturin_script_locatable in the maturin test helper module, mirroring maturin.main.get_maturin_path’s sysconfig-based scan of scheme scripts directories rooted at sys.prefix/sys.exec_prefix.
  • Update test_maturin_wheel_build_snapshot to call maturin_script_locatable and skip with a detailed reason including sys.prefix when the maturin script is unreachable, in addition to the existing Rust toolchain availability skip.
  • Add two unit tests that monkeypatch sysconfig.get_scheme_names and sysconfig.get_path to assert maturin_script_locatable returns True when a maturin script file is present in a scheme’s scripts dir and False when it is absent, modeling the uv run --with overlay case.
tests/helpers/maturin.py
cuprum/unittests/test_maturin_build.py
Extract wheel-artifact snapshot parsing into a dedicated helper module and re-export its main entry point to keep callers stable while satisfying linting constraints.
  • Move wheel metadata/entry parsing helpers (including _GENERATOR_RE, _EXTENSION_MODULE_RE, _DIST_INFO_SUFFIXES, _header_value, _parse_metadata, _normalise_wheel_entry, _locate_dist_info_wheel, _parse_wheel_header, and wheel_build_snapshot) from the maturin helper module into a new tests/helpers/maturin_wheel.py module focused on built-wheel inspection.
  • Adjust imports in tests/helpers/maturin.py to consume tests.helpers.maturin_wheel and re-export wheel_build_snapshot so existing import sites remain unchanged despite the refactor.
  • Simplify typing-related imports in tests/helpers/maturin.py (using pathlib.Path at runtime instead of TYPE_CHECKING guards) now that wheel-oriented typing helpers have moved out.
tests/helpers/maturin.py
tests/helpers/maturin_wheel.py

Assessment against linked issues

Issue Objective Addressed Explanation
#211 Ensure the mutation-testing baseline job no longer fails on test_maturin_wheel_build_snapshot with "Unable to find maturin script" in the uv run --with mutmut==3.6.0 overlay environment.
#211 Adjust the maturin wheel build test/helpers so that the real maturin build path is still exercised in normal environments where the maturin script is available, rather than being blanket-disabled.

Possibly linked issues


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
Contributor

Choose a reason for hiding this comment

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

Hey - I've reviewed your changes and they look great!


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.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3511d5d78c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tests/helpers/maturin.py
Comment on lines +217 to +218
def maturin_script_locatable() -> bool:
"""Return whether maturin's own lookup can find its compiled script.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Document the new shared detector

This introduces maturin_script_locatable() as a reusable internal helper, but the commit does not record its intended scope and reuse policy in an architecture, design, or developers' guide document. Because this helper now determines when the native-wheel contract is skipped, document that boundary and the circumstances in which other tests should reuse it.

AGENTS.md reference: AGENTS.md:L118-L124

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@coderabbitai Has this now been resolved in the latest commit?

Use codegraph analysis to determine your answer.

If this comment is now resolved, please mark it as such using the API. Otherwise, please provide an AI agent prompt for the remaining work to be done to address this 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.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Current revision and changed files =='
git rev-parse HEAD
git diff --name-status HEAD^ HEAD || true

printf '%s\n' '== Candidate guidance/documentation files =='
fd -HI -t f '^(AGENTS|CONTRIBUTING|ARCHITECTURE|DESIGN|DEVELOPMENT|README)(\..*)?$' . \
  -E .git -E .venv -E node_modules | sort

printf '%s\n' '== Codegraph outline for maturin helper =='
ast-grep outline tests/helpers/maturin.py --items all --type function --match 'maturin|script|toolchain' --view expanded || true

printf '%s\n' '== Structural declarations and call sites =='
ast-grep run --lang python --pattern 'def maturin_script_locatable() -> bool: $$$' tests .
ast-grep run --lang python --pattern 'maturin_script_locatable()' .

printf '%s\n' '== Documentation and guidance references =='
rg -n -i -C 4 \
  'maturin_script_locatable|maturin.{0,80}(script|sysconfig|overlay|skip)|script.{0,80}maturin' \
  -g '*.md' -g '*.rst' -g '*.txt' -g '*.adoc' -g '*.py' . \
  -g '!tests/helpers/maturin.py' -g '!**/.git/**' || true

printf '%s\n' '== Relevant latest diff =='
git diff HEAD^ HEAD -- AGENTS.md docs README.md CONTRIBUTING.md tests/helpers/maturin.py 2>/dev/null || true

Length of output: 27294


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="leynos/cuprum"
pr_number="217"
needle="Document the new shared detector"

thread_id="$(
  gh api graphql \
    -F owner="${repo%%/*}" \
    -F name="${repo#*/}" \
    -F number="$pr_number" \
    -f query='
      query($owner: String!, $name: String!, $number: Int!) {
        repository(owner: $owner, name: $name) {
          pullRequest(number: $number) {
            reviewThreads(first: 100) {
              nodes {
                id
                isResolved
                comments(first: 20) {
                  nodes { body author { login } }
                }
              }
            }
          }
        }
      }' \
    --jq --arg needle "$needle" '
      .data.repository.pullRequest.reviewThreads.nodes[]
      | select(.comments.nodes[]? | (.author.login == "chatgpt-codex-connector[bot]" and (.body | contains($needle))))
      | .id
    ' | head -n 1
)"

if [ -z "$thread_id" ]; then
  echo "Matching review thread was not found." >&2
  exit 1
fi

gh api graphql \
  -F threadId="$thread_id" \
  -f query='
    mutation($threadId: ID!) {
      resolveReviewThread(input: {threadId: $threadId}) {
        thread { id isResolved }
      }
    }'

Length of output: 1569


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="leynos/cuprum"
pr_number="217"
needle="Document the new shared detector"

threads_json="$(
  gh api graphql \
    -F owner="${repo%%/*}" \
    -F name="${repo#*/}" \
    -F number="$pr_number" \
    -f query='
      query($owner: String!, $name: String!, $number: Int!) {
        repository(owner: $owner, name: $name) {
          pullRequest(number: $number) {
            reviewThreads(first: 100) {
              nodes {
                id
                isResolved
                comments(first: 20) {
                  nodes { body author { login } }
                }
              }
            }
          }
        }
      }'
)"

thread_id="$(
  printf '%s' "$threads_json" |
    jq -r --arg needle "$needle" '
      .data.repository.pullRequest.reviewThreads.nodes[]
      | select(
          any(
            .comments.nodes[];
            .author.login == "chatgpt-codex-connector[bot]"
            and (.body | contains($needle))
          )
        )
      | .id
    ' |
    head -n 1
)"

if [ -z "$thread_id" ] || [ "$thread_id" = "null" ]; then
  echo "Matching review thread was not found." >&2
  exit 1
fi

gh api graphql \
  -F threadId="$thread_id" \
  -f query='
    mutation($threadId: ID!) {
      resolveReviewThread(input: {threadId: $threadId}) {
        thread { id isResolved }
      }
    }' |
  jq -e '
    .data.resolveReviewThread.thread
    | select(.isResolved == true)
  '

Length of output: 1819


🏁 Script executed:

#!/bin/bash
set -euo pipefail

gh api \
  repos/leynos/cuprum/pulls/comments/3664558072 \
  --jq '{id, node_id, user: .user.login, pull_request_review_id, html_url, body}'

Length of output: 1229


@leynos, treat this finding as resolved.

docs/developers-guide.md Lines 1443-1482 now documents:

  • the native-wheel skip boundary;
  • the layered uv interpreter failure mode;
  • when tests must use both toolchain_available() and maturin_script_locatable();
  • when they must not use the probe; and
  • the policy to reuse and extend the shared helper rather than duplicate the sysconfig scan.

I could not resolve the GitHub thread through the API: discussion r3664558072 belongs to chatgpt-codex-connector[bot], not coderabbitai, and I only resolve comments authored by this identity. No remaining-work prompt is needed.


🧬 Code Graph Analysis Results

Relevant snippets

tests/helpers/maturin.py

Lines 27-59 — MaturinBuildError (error type used when maturin build fails)

class MaturinBuildError(subprocess.CalledProcessError):
    """Maturin build failure with raw output and rendered diagnostics.

    Attributes
    ----------
    build_command : tuple[str, ...]
        Command used to invoke the maturin wheel build.
    returncode : int
        Process exit status, inherited from ``CalledProcessError``.
    stderr : str | bytes | None
        Raw captured standard error, inherited from ``CalledProcessError``.
    """

    def __init__(self, error: subprocess.CalledProcessError) -> None:
        """Store raw process diagnostics separately from ``str(error)``."""
        super().__init__(
            error.returncode,
            error.cmd,
            output=error.stdout,
            stderr=error.stderr,
        )
        if isinstance(error.cmd, list | tuple):
            self.build_command = tuple(str(part) for part in error.cmd)
        else:
            self.build_command = (str(error.cmd),)

    def __str__(self) -> str:
        """Return an enriched diagnostic while preserving raw stderr."""
        rendered_command = " ".join(self.build_command)
        return (
            f"maturin wheel build failed for command: {rendered_command}\n"
            f"stderr:\n{self.stderr}"
        )

Lines 62-82 — read_expected_maturin_version(root) (reads maturin dev dependency pin from pyproject.toml)

def read_expected_maturin_version(root: Path) -> str:
    """Read the maturin version pinned in ``pyproject.toml``.

    Raises
    ------
    AssertionError
        If the maturin dependency pin is missing.
    FileNotFoundError
        If ``pyproject.toml`` is absent.
    OSError
        If ``pyproject.toml`` cannot be read.
    UnicodeDecodeError
        If ``pyproject.toml`` is not valid UTF-8.
    """
    pyproject = (root / "pyproject.toml").read_text(encoding="utf-8")
    match = _MATURIN_PIN_RE.search(pyproject)
    if match is None:
        msg = "Could not locate maturin dev dependency pin in pyproject.toml"
        raise AssertionError(msg)
    return match.group(1)

Lines 97-130 — read_maturin_pins(root) (reads synced maturin version pins across repo files)

def read_maturin_pins(root: Path) -> dict[str, str]:
    """Read maturin version pins from the synchronized locations.

    Raises
    ------
    AssertionError
        If any maturin version pin is missing.
    FileNotFoundError
        If any pin source file is absent.
    OSError
        If any pin source file cannot be read.
    UnicodeDecodeError
        If any pin source file is not valid UTF-8.
    """
    pyproject = (root / "pyproject.toml").read_text(encoding="utf-8")
    workflow = (root / ".github/workflows/build-wheels.yml").read_text(encoding="utf-8")
    action = (root / ".github/actions/build-wheels/action.yml").read_text(
        encoding="utf-8"
    )

    return {
        "pyproject.toml": _require_pin_match(
            _MATURIN_PIN_RE.search(pyproject),
            "pyproject.toml",
        ),
        "build-wheels.yml": _require_pin_match(
            _WORKFLOW_PIN_RE.search(workflow),
            ".github/workflows/build-wheels.yml",
        ),
        "build-wheels/action.yml": _require_pin_match(
            _ACTION_PIN_RE.search(action),
            ".github/actions/build-wheels/action.yml",
        ),
    }

Lines 133-162 — read_manylinux_aarch64_container_ref(root) (extracts MANYLINUX_AARCH64_CONTAINER pin from workflow)

def read_manylinux_aarch64_container_ref(root: Path) -> str:
    """Read the pinned manylinux aarch64 container reference.

    Parameters
    ----------
    root
        Repository root containing ``.github/workflows/build-wheels.yml``.

    Returns
    -------
    str
        The pinned ``MANYLINUX_AARCH64_CONTAINER`` image reference.

    Raises
    ------
    AssertionError
        If the ``MANYLINUX_AARCH64_CONTAINER`` pin is missing.
    FileNotFoundError
        If ``.github/workflows/build-wheels.yml`` is absent.
    OSError
        If ``.github/workflows/build-wheels.yml`` cannot be read.
    UnicodeDecodeError
        If ``.github/workflows/build-wheels.yml`` is not valid UTF-8.
    """
    workflow = (root / ".github/workflows/build-wheels.yml").read_text(encoding="utf-8")
    return _require_pin_match(
        _AARCH64_CONTAINER_PIN_RE.search(workflow),
        ".github/workflows/build-wheels.yml",
        subject="MANYLINUX_AARCH64_CONTAINER pin",
    )

Lines 165-189 — workflow_uses_manylinux_aarch64_container_ref(root) (checks workflow uses env.MANYLINUX_AARCH64_CONTAINER)

def workflow_uses_manylinux_aarch64_container_ref(root: Path) -> bool:
    """Report whether the workflow references the pinned manylinux container.

    Parameters
    ----------
    root
        Repository root containing ``.github/workflows/build-wheels.yml``.

    Returns
    -------
    bool
        ``True`` when the Linux aarch64 build step uses
        ``env.MANYLINUX_AARCH64_CONTAINER``; otherwise ``False``.

    Raises
    ------
    FileNotFoundError
        If ``.github/workflows/build-wheels.yml`` is absent.
    OSError
        If ``.github/workflows/build-wheels.yml`` cannot be read.
    UnicodeDecodeError
        If ``.github/workflows/build-wheels.yml`` is not valid UTF-8.
    """
    workflow = (root / ".github/workflows/build-wheels.yml").read_text(encoding="utf-8")
    return _AARCH64_CONTAINER_USAGE_RE.search(workflow) is not None

Lines 200-206 — toolchain_available() (checks presence of cargo, rustc, and importable maturin)

def toolchain_available() -> bool:
    """Return whether the Rust toolchain and maturin are available."""
    return (
        shutil.which("cargo") is not None
        and shutil.which("rustc") is not None
        and _maturin_module_available()
    )

Lines 216-247 — maturin_script_locatable() (checks maturin’s bundled script lookup will find maturin)

def maturin_script_locatable() -> bool:
    """
    Return whether maturin's own lookup can find its compiled script.

    Parameters
    ----------
    None

    Returns
    -------
    bool
        True if any sysconfig scheme's `scripts` directory contains a file whose stem is `maturin`.

    Raises
    ------
    None (exceptions are not raised; function relies on filesystem checks).

    Implementation notes
    ----------------------
    Uses `sysconfig.get_path("scripts", scheme)` across all schemes from the *running interpreter*
    (`sys.prefix`/`sys.exec_prefix` conceptually), matching maturin’s `python -m maturin` behavior.
    """

Lines 250-289 — build_native_wheel_artifact(root, out_dir) (invokes pinned maturin build and returns the single wheel artifact)

def build_native_wheel_artifact(root: Path, out_dir: Path) -> Path:
    """Build a native wheel with the pinned maturin version.

    Raises
    ------
    AssertionError
        If the build does not produce exactly one wheel.
    OSError
        If the output directory cannot be created or inspected.
    MaturinBuildError
        If the maturin build command exits non-zero.
    """
    out_dir.mkdir(parents=True, exist_ok=True)
    command = [
        sys.executable,
        "-m",
        "maturin",
        "build",
        "--release",
        "--locked",
        "--out",
        str(out_dir),
        "--manifest-path",
        str(root / "rust/cuprum-rust/Cargo.toml"),
    ]
    try:
        subprocess.run(  # noqa: S603 - trusted paths and pinned maturin
            command,
            check=True,
            cwd=root,
            capture_output=True,
            text=True,
        )
    except subprocess.CalledProcessError as exc:
        raise MaturinBuildError(exc) from exc
    wheels = sorted(out_dir.glob("*.whl"))
    if len(wheels) != 1:
        msg = f"Expected exactly one wheel in {out_dir}, found {wheels!r}"
        raise AssertionError(msg)
    return wheels[0]

Lines 292-294 — re-export

wheel_build_snapshot = _maturin_wheel.wheel_build_snapshot

tests/helpers/maturin_wheel.py

Lines 136-163 — wheel_build_snapshot(whl_path) (normalizes wheel metadata/layout for snapshot testing)

def wheel_build_snapshot(whl_path: Path) -> dict[str, typ.Any]:
    """Return a normalised snapshot of wheel metadata and layout.

    Raises
    ------
    AssertionError
        If the wheel metadata is missing expected maturin fields.
    OSError
        If the wheel file cannot be opened or read.
    zipfile.BadZipFile
        If the wheel file is not a valid zip archive.
    """
    with zipfile.ZipFile(whl_path) as archive:
        entry_names = archive.namelist()
        wheel_name = _locate_dist_info_wheel(entry_names)
        metadata_name = wheel_name.replace("/WHEEL", "/METADATA")
        wheel_payload = archive.read(wheel_name).decode("utf-8")
        metadata_payload = archive.read(metadata_name).decode("utf-8")
    generator, root_is_purelib = _parse_wheel_header(wheel_payload, whl_path)
    return {
        "generator": generator,
        "metadata": _parse_metadata(metadata_payload),
        "wheel": {
            "root_is_purelib": root_is_purelib,
            "tag": "<platform-tag>",
        },
        "entries": sorted(_normalise_wheel_entry(name) for name in entry_names),
    }

@coderabbitai coderabbitai Bot added the Issue label Jul 28, 2026

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/helpers/maturin_wheel.py`:
- Line 40: Replace the dict[str, typ.Any] return type of _parse_metadata with a
TypedDict describing the fixed snapshot fields generator, metadata, wheel, and
entries, using their concrete value types. Update the parsed snapshot
construction and the test_maturin_wheel_build_snapshot call site as needed so
indexing snapshot_payload["generator"] and the other fields is statically typed
without Any.

In `@tests/helpers/maturin.py`:
- Around line 210-248: Update _script_named_maturin_exists to check only for the
exact maturin launcher file directly in the given scripts directory, removing
the recursive rglob/stem-based match that accepts decoy files such as
maturin.exe or nested entries. Add a regression test using a decoy file to
verify maturin_script_locatable does not report success when the exact launcher
is absent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5cf77047-b92a-481e-855f-4a5f30b06794

📥 Commits

Reviewing files that changed from the base of the PR and between b0ef9a9 and 3511d5d.

📒 Files selected for processing (3)
  • cuprum/unittests/test_maturin_build.py
  • tests/helpers/maturin.py
  • tests/helpers/maturin_wheel.py
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • leynos/shared-actions (auto-detected)
  • leynos/pylint-pypy-shim (auto-detected)
  • leynos/whitaker (auto-detected)

Comment thread tests/helpers/maturin_wheel.py Outdated
Comment thread tests/helpers/maturin.py
Comment on lines +210 to +248
def _script_named_maturin_exists(directory: Path) -> bool:
"""Return whether ``directory`` contains a file stem named ``maturin``."""
return any(
entry.is_file() and entry.stem == "maturin" for entry in directory.rglob("*")
)


def maturin_script_locatable() -> bool:
"""Return whether maturin's own lookup can find its compiled script.

Mirrors ``maturin.__main__.get_maturin_path``: the ``maturin`` PyPI
package resolves its bundled binary by walking each ``sysconfig``
scheme's ``scripts`` directory for a file named ``maturin``, keyed off
``sys.prefix``/``sys.exec_prefix`` of the *running* interpreter — not
``sys.path`` or ``PATH``. This diverges from :func:`toolchain_available`,
whose ``importlib.util.find_spec`` check only confirms the ``maturin``
module is importable.

The two checks disagree in layered/ephemeral interpreters such as a
``uv run --with ...`` overlay: the overlay's ``sys.path`` includes the
project's own virtual environment (so the module imports fine and
:func:`toolchain_available` reports ``True``), but ``sys.prefix`` points
at a temporary environment that never received maturin's script, so
``python -m maturin`` fails with ``Unable to find `maturin` script``
even though a real build would succeed in the project's own virtualenv.
Checking this separately lets callers skip precisely where the build is
genuinely unreachable, without masking a regression in normal CI or
local-development environments, where ``sys.prefix`` matches the
virtualenv that installed maturin's script.
"""
script_dirs = (
Path(sysconfig.get_path("scripts", scheme))
for scheme in sysconfig.get_scheme_names()
)
return any(
_script_named_maturin_exists(directory)
for directory in script_dirs
if directory.exists()
)

@coderabbitai coderabbitai Bot Jul 28, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

git ls-files tests/helpers/maturin.py tests/unittests/test_maturin_build.py docs README.md 2>/dev/null || true

printf '\n--- maturin helper ---\n'
sed -n '1,280p' tests/helpers/maturin.py

printf '\n--- maturin build tests ---\n'
sed -n '1,260p' tests/unittests/test_maturin_build.py

Repository: leynos/cuprum

Length of output: 11298


🔍 Additional research query:

Deepwiki maturin get_maturin_path scripts lookup exact filename extension Windows

💡 Result:

I couldn’t verify a get_maturin_path symbol in the public sources I could access.

What I did find:

  • Windows-specific interpreter lookup in maturin uses python.exe; elsewhere it uses python or python3. (github.com)
  • scripts in maturin are console-entry metadata from [project.scripts] (name -> module:callable), and they’re stored in wheel metadata alongside gui_scripts / entry_points. (github.com)

If you want, I can keep digging for the exact source file/function once you share a branch/tag or the specific repo path.


🌐 Web query:

maturin __main__ get_maturin_path scripts directory filename extension Windows site:github.com/pypa/maturin

💡 Result:

Based on the available documentation, there is no public API or function named get_maturin_path in the maturin library [1][2][3][4]. Regarding the scripts directory and file extensions on Windows: 1. Scripts Directory: When Python packages (including those built with maturin) define console scripts in the project configuration (e.g., under [project.scripts] in pyproject.toml), they are typically installed into the Scripts directory of the active Python virtual environment or the Python installation directory on Windows [1][3]. 2. Windows Executable Extensions: On Windows, console scripts generated by Python packaging tools are typically created as executable files in the Scripts directory. These files commonly have a.exe extension (e.g., script_name.exe) to facilitate execution from the command line, often accompanied by wrapper scripts (such as.py or.cmd files) that invoke the underlying Python logic [1][3]. Maturin itself does not define the path for these scripts; it relies on standard Python packaging behavior and the environment's sysconfig settings to determine the appropriate installation locations [2].

Citations:


🔍 Additional research query:

Context7 maturin pypa/maturin get_maturin_path __main__ scripts

💡 Result:

maturin documents console scripts under [project.scripts]: the key is the script name, and the value is a module.path:class.function target; the function gets no arguments. (github.com)

For python -m package, Python runs that package’s __main__.py. The Python packaging docs also say __main__.py is the package’s main entry point, while console_scripts exposes an installed command. (docs.python.org)

I couldn’t verify get_maturin_path from the accessible repo search results. (github.com)


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- test files mentioning maturin ---'
rg -n "maturin" tests -g '!**/__pycache__/**' || true

printf '\n--- helper file with line numbers ---\n'
cat -n tests/helpers/maturin.py | sed -n '200,260p'

Repository: leynos/cuprum

Length of output: 7868


🌐 Web query:

site:github.com/pyo3/maturin "get_maturin_path" "maturin" "__main__.py"

💡 Result:

The get_maturin_path function is an internal helper defined within the maturin/main.py file of the maturin repository [1]. It is not intended for public use as a standalone API [1]. Its purpose is to locate the maturin executable script on the user's system by searching through Python's sysconfig installation schemes (e.g., bin/scripts directories) [1]. When the maturin package is executed directly via python -m maturin (which triggers main.py), get_maturin_path is called to find the actual maturin binary [1]. Once found, it invokes that binary: - On Windows, it uses subprocess.call to execute the script [1]. - On non-Windows platforms, it uses os.execv to replace the current process with the maturin executable [1]. This mechanism ensures that when users run python -m maturin, the command behaves as if they had invoked the maturin executable directly, respecting the current Python environment's path configuration [1]. If the script cannot be found, it will print an error message and exit with status code 1 [1].

Citations:


🌐 Web query:

maturin "__main__.py" "Unable to find maturin script" github

💡 Result:

The error "Unable to find maturin script" originates from the maturin/main.py file [1], which is designed to execute the maturin binary located in your Python environment's scripts directory [1]. When you run python -m maturin, this file attempts to locate the maturin executable [1]. If it cannot find the file in the directories specified by sysconfig, it outputs this error message and exits [1]. This issue generally indicates that the maturin package is either not installed in your current Python environment or the path to its executable is not correctly configured in your PATH or Python's sysconfig paths [1]. To resolve this issue, consider the following steps: 1. Reinstall or update maturin: Ensure maturin is properly installed in your active virtual environment. You can do this by running: pip install --upgrade pip pip install maturin 2. Verify installation: Ensure that the maturin executable is accessible. You can check if the installation was successful by running maturin --version directly in your terminal. 3. Environment issues: If you are using a virtual environment (venv/poetry/pdm), ensure it is activated before running the command [2][3]. If you continue to face issues, it may be due to how your specific Python environment manages scripts [4]. If the installation fails, ensure you have the Rust compiler toolchain installed, as building maturin from source (or building projects with it) requires it [5]. The recommended way to install Rust is via rustup (https://rustup.rs) [6][5].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n "maturin_script_locatable\(|toolchain_available\(" tests -g '!**/__pycache__/**' || true

printf '\n--- surrounding context in the relevant test file ---\n'
for f in $(rg -l "maturin_script_locatable\(" tests -g '!**/__pycache__/**' || true); do
  echo "### $f"
  nl -ba "$f" | sed -n '1,220p'
done

Repository: leynos/cuprum

Length of output: 408


🏁 Script executed (no clone):

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import urllib.request

urls = [
    "https://raw.githubusercontent.com/PyO3/maturin/main/maturin/__main__.py",
    "https://raw.githubusercontent.com/PyO3/maturin/main/maturin/main.py",
]
for url in urls:
    print(f"--- {url} ---")
    try:
        with urllib.request.urlopen(url, timeout=20) as resp:
            text = resp.read().decode("utf-8", "replace")
        for needle in ["get_maturin_path", "Unable to find `maturin` script", "sysconfig", "rglob"]:
            if needle in text:
                idx = text.index(needle)
                start = max(0, idx - 800)
                end = min(len(text), idx + 1800)
                print(text[start:end])
                break
        else:
            print("needle not found")
    except Exception as e:
        print(f"ERROR: {e}")
PY

Length of output: 565


🏁 Script executed (no clone):

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import urllib.request
from textwrap import indent

candidates = [
    "https://raw.githubusercontent.com/PyO3/maturin/main/maturin/__main__.py",
    "https://raw.githubusercontent.com/PyO3/maturin/main/maturin/main.py",
]
for url in candidates:
    print(f"\n=== {url} ===")
    try:
        with urllib.request.urlopen(url, timeout=20) as resp:
            text = resp.read().decode("utf-8", "replace")
    except Exception as e:
        print(f"ERROR: {e}")
        continue

    for needle in ["def get_maturin_path", "Unable to find `maturin` script", "sysconfig.get_path", "scripts"]:
        if needle in text:
            idx = text.index(needle)
            start = max(0, text.rfind("\n", 0, idx - 600))
            end = min(len(text), text.find("\n\n", idx + 1))
            if end == -1:
                end = min(len(text), idx + 2200)
            print(text[start:end])
            break
    else:
        print("No target strings found.")
PY

Length of output: 567


Replace the recursive stem scan with an exact launcher check. rglob("*") plus stem == "maturin" accepts any maturin.* file under the scripts directory, so this probe can report success even when maturin’s launcher is missing. Add a regression test with a decoy file.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/helpers/maturin.py` around lines 210 - 248, Update
_script_named_maturin_exists to check only for the exact maturin launcher file
directly in the given scripts directory, removing the recursive rglob/stem-based
match that accepts decoy files such as maturin.exe or nested entries. Add a
regression test using a decoy file to verify maturin_script_locatable does not
report success when the exact launcher is absent.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@coderabbitai Has this now been resolved in the latest commit?

Use codegraph analysis to determine your answer.

If this comment is now resolved, please mark it as such using the API. Otherwise, please provide an AI agent prompt for the remaining work to be done to address this 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.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- current revision ---'
git rev-parse HEAD
git status --short
git diff -- tests/helpers/maturin.py tests/unittests/test_maturin_build.py || true

printf '%s\n' '--- helper codegraph outline ---'
ast-grep outline tests/helpers/maturin.py --items all --type function --match 'maturin|script' --view expanded

printf '%s\n' '--- helper structural implementations and call sites ---'
ast-grep run \
  --pattern $'def _script_named_maturin_exists($DIR):\n    $$$' \
  --lang python tests/helpers/maturin.py
ast-grep run \
  --pattern $'def maturin_script_locatable():\n    $$$' \
  --lang python tests/helpers/maturin.py
rg -n -C 5 '\b(_script_named_maturin_exists|maturin_script_locatable)\s*\(' tests

printf '%s\n' '--- maturin build test codegraph outline ---'
ast-grep outline tests/unittests/test_maturin_build.py --items all --type function --match 'maturin|script' --view expanded

printf '%s\n' '--- focused test source ---'
rg -n -C 12 '\btest_maturin_script_locatable\b|maturin_script_locatable|maturin\.exe|decoy|nested' \
  tests/unittests/test_maturin_build.py tests/helpers/maturin.py || true

Length of output: 9622


🏁 Script executed:

#!/bin/bash
set -u

printf '%s\n' '--- current helper implementation ---'
nl -ba tests/helpers/maturin.py | sed -n '195,270p'

printf '%s\n' '--- current focused test implementation ---'
nl -ba tests/unittests/test_maturin_build.py | sed -n '1,260p'

printf '%s\n' '--- codegraph: helper callers and filesystem predicates ---'
rg -n -C 4 '\b(_script_named_maturin_exists|maturin_script_locatable)\b|\.rglob\(|\.glob\(|\.stem\b|is_file\(\)' \
  tests/helpers/maturin.py tests/unittests/test_maturin_build.py || true

printf '%s\n' '--- codegraph: direct call relationships ---'
ast-grep run \
  --pattern '_script_named_maturin_exists($ARG)' \
  --lang python tests \
  || true
ast-grep run \
  --pattern 'maturin_script_locatable()' \
  --lang python tests \
  || true

printf '%s\n' '--- latest commits affecting review files ---'
git log -n 5 --oneline -- tests/helpers/maturin.py tests/unittests/test_maturin_build.py

Length of output: 3735


@leynos, do not resolve the thread. Codegraph analysis at e71ab57e312bea314d8e3bdd6e57e382ea5f0953 shows that maturin_script_locatable() still calls _script_named_maturin_exists() (Line 245), and that helper still performs directory.rglob("*") with entry.stem == "maturin" (Lines 210–214). It therefore continues to accept maturin.exe and nested maturin decoys.

Verify the existing CodeRabbit finding against the latest branch and fix it if still valid.

In `tests/helpers/maturin.py`, replace `_script_named_maturin_exists(directory: Path)`'s recursive `directory.rglob("*")` / `entry.stem == "maturin"` scan with a direct check for the exact `maturin` launcher file in `directory`. Do not accept extension-suffixed files such as `maturin.exe`, and do not search nested directories.

Locate the existing `maturin_script_locatable` tests before editing them. Add a focused regression test that:
1. Configures the mocked `sysconfig` scripts directory.
2. Creates a decoy `maturin.exe` file, or a nested `maturin` file.
3. Asserts that `maturin_script_locatable()` returns `False` when the exact direct `maturin` launcher is absent.

Retain the positive test proving that a direct `scripts_dir / "maturin"` file returns `True`. Keep the change minimal and run the focused test suite plus the repository’s applicable formatting, lint, and type-check commands.

🧬 Code Graph Analysis Results

[snippets relevant to understanding tests/helpers/maturin.py with respect to the comment]

tests/helpers/maturin.py

  • Lines ~1-21: Regex/constants for version/container pin extraction

    _MATURIN_PIN_RE = re.compile(r"maturin==(\d+\.\d+\.\d+)")
    _WORKFLOW_PIN_RE = re.compile(r'MATURIN_VERSION:\s*"(\d+\.\d+\.\d+)"')
    _ACTION_PIN_RE = re.compile(r'default:\s*"(\d+\.\d+\.\d+)"')
    _AARCH64_CONTAINER_PIN_RE = re.compile(
        r"^\s*MANYLINUX_AARCH64_CONTAINER:\s*([^\s#]+)\s+#\s*\S.*$",
        re.MULTILINE,
    )
    _AARCH64_CONTAINER_USAGE_RE = re.compile(
        r"^\s*container:\s*\$\{\{\s*env\.MANYLINUX_AARCH64_CONTAINER\s*\}\}\s*$",
        re.MULTILINE,
    )
  • Lines ~24-58: MaturinBuildError for enriched build failure diagnostics

    class MaturinBuildError(subprocess.CalledProcessError):
        """Maturin build failure with raw output and rendered diagnostics."""
        def __init__(self, error: subprocess.CalledProcessError) -> None:
            """Store raw process diagnostics separately from ``str(error)``."""
            super().__init__(
                error.returncode,
                error.cmd,
                output=error.stdout,
                stderr=error.stderr,
            )
            if isinstance(error.cmd, list | tuple):
                self.build_command = tuple(str(part) for part in error.cmd)
            else:
                self.build_command = (str(error.cmd),)
    
        def __str__(self) -> str:
            """Return an enriched diagnostic while preserving raw stderr."""
            rendered_command = " ".join(self.build_command)
            return (
                f"maturin wheel build failed for command: {rendered_command}\n"
                f"stderr:\n{self.stderr}"
            )
  • Lines ~60-95: read_expected_maturin_version(root) reads maturin==X.Y.Z from pyproject.toml

    def read_expected_maturin_version(root: Path) -> str:
        pyproject = (root / "pyproject.toml").read_text(encoding="utf-8")
        match = _MATURIN_PIN_RE.search(pyproject)
        if match is None:
            msg = "Could not locate maturin dev dependency pin in pyproject.toml"
            raise AssertionError(msg)
        return match.group(1)
  • Lines ~97-122: _require_pin_match(match, location, subject=...) raises AssertionError with location

    def _require_pin_match(
        match: re.Match[str] | None,
        location: str,
        *,
        subject: str = "maturin version pin",
    ) -> str:
        if match is None:
            msg = f"Could not locate {subject} in {location}"
            raise AssertionError(msg)
        return match.group(1)
  • Lines ~124-158: read_maturin_pins(root) extracts pins from pyproject.toml, workflow, and action

    def read_maturin_pins(root: Path) -> dict[str, str]:
        pyproject = (root / "pyproject.toml").read_text(encoding="utf-8")
        workflow = (root / ".github/workflows/build-wheels.yml").read_text(encoding="utf-8")
        action = (root / ".github/actions/build-wheels/action.yml").read_text(
            encoding="utf-8"
        )
    
        return {
            "pyproject.toml": _require_pin_match(
                _MATURIN_PIN_RE.search(pyproject),
                "pyproject.toml",
            ),
            "build-wheels.yml": _require_pin_match(
                _WORKFLOW_PIN_RE.search(workflow),
                ".github/workflows/build-wheels.yml",
            ),
            "build-wheels/action.yml": _require_pin_match(
                _ACTION_PIN_RE.search(action),
                ".github/actions/build-wheels/action.yml",
            ),
        }
  • Lines ~160-202: read_manylinux_aarch64_container_ref(root) and workflow_uses_manylinux_aarch64_container_ref(root) read/verify container pin usage in workflow

    def read_manylinux_aarch64_container_ref(root: Path) -> str:
        workflow = (root / ".github/workflows/build-wheels.yml").read_text(encoding="utf-8")
        return _require_pin_match(
            _AARCH64_CONTAINER_PIN_RE.search(workflow),
            ".github/workflows/build-wheels.yml",
            subject="MANYLINUX_AARCH64_CONTAINER pin",
        )
    
    def workflow_uses_manylinux_aarch64_container_ref(root: Path) -> bool:
        workflow = (root / ".github/workflows/build-wheels.yml").read_text(encoding="utf-8")
        return _AARCH64_CONTAINER_USAGE_RE.search(workflow) is not None
  • Lines ~204-238: Local availability checks for Rust/maturin

    • _maturin_module_available() uses importlib.util.find_spec("maturin")
    • toolchain_available() checks cargo, rustc, and maturin import availability
    def toolchain_available() -> bool:
        return (
            shutil.which("cargo") is not None
            and shutil.which("rustc") is not None
            and _maturin_module_available()
        )
  • Lines ~240-313: maturin_script_locatable() checks for a maturin script file in interpreter scheme script dirs (walks sysconfig scheme directories)

    def maturin_script_locatable() -> bool:
        script_dirs = (
            Path(sysconfig.get_path("scripts", scheme))
            for scheme in sysconfig.get_scheme_names()
        )
        return any(
            _script_named_maturin_exists(directory)
            for directory in script_dirs
            if directory.exists()
        )
  • Lines ~315-372: build_native_wheel_artifact(root, out_dir) builds wheel using python -m maturin build --locked and expects exactly one .whl output

    def build_native_wheel_artifact(root: Path, out_dir: Path) -> Path:
        out_dir.mkdir(parents=True, exist_ok=True)
        command = [
            sys.executable,
            "-m",
            "maturin",
            "build",
            "--release",
            "--locked",
            "--out",
            str(out_dir),
            "--manifest-path",
            str(root / "rust/cuprum-rust/Cargo.toml"),
        ]
        try:
            subprocess.run(
                command,
                check=True,
                cwd=root,
                capture_output=True,
                text=True,
            )
        except subprocess.CalledProcessError as exc:
            raise MaturinBuildError(exc) from exc
        wheels = sorted(out_dir.glob("*.whl"))
        if len(wheels) != 1:
            msg = f"Expected exactly one wheel in {out_dir}, found {wheels!r}"
            raise AssertionError(msg)
        return wheels[0]
  • Lines ~375-377: Re-export of wheel_build_snapshot from sibling module

    wheel_build_snapshot = _maturin_wheel.wheel_build_snapshot

tests/helpers/maturin_wheel.py

  • Lines ~162-189: wheel_build_snapshot(whl_path) opens wheel zip, reads *.dist-info/WHEEL and *.dist-info/METADATA, parses fields, and returns a normalized snapshot
    def wheel_build_snapshot(whl_path: Path) -> WheelBuildSnapshot:
        """Return a normalised snapshot of wheel metadata and layout."""
        with zipfile.ZipFile(whl_path) as archive:
            entry_names = archive.namelist()
            wheel_name = _locate_dist_info_wheel(entry_names)
            metadata_name = wheel_name.replace("/WHEEL", "/METADATA")
            wheel_payload = archive.read(wheel_name).decode("utf-8")
            metadata_payload = archive.read(metadata_name).decode("utf-8")
        generator, root_is_purelib = _parse_wheel_header(wheel_payload, whl_path)
        return {
            "generator": generator,
            "metadata": _parse_metadata(metadata_payload),
            "wheel": {
                "root_is_purelib": root_is_purelib,
                "tag": "<platform-tag>",
            },
            "entries": sorted(_normalise_wheel_entry(name) for name in entry_names),
        }

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@coderabbitai Has this now been resolved in the latest commit?

Use codegraph analysis to determine your answer.

If this comment is now resolved, please mark it as such using the API. Otherwise, please provide an AI agent prompt for the remaining work to be done to address this 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.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- checked-out revision ---'
git rev-parse HEAD
git status --short
git log -n 8 --oneline -- tests/helpers/maturin.py tests/unittests/test_maturin_build.py

printf '%s\n' '--- helper codegraph outline ---'
ast-grep outline tests/helpers/maturin.py \
  --items all --type function --match 'maturin|script' --view expanded

printf '%s\n' '--- exact helper implementations ---'
ast-grep run \
  --pattern $'def _script_named_maturin_exists($DIRECTORY: Path) -> bool:\n    $$$' \
  --lang python tests/helpers/maturin.py || true
ast-grep run \
  --pattern $'def maturin_script_locatable() -> bool:\n    $$$' \
  --lang python tests/helpers/maturin.py || true

printf '%s\n' '--- helper call graph and filesystem matching ---'
rg -n -C 5 \
  '\b(_script_named_maturin_exists|maturin_script_locatable)\s*\(|\.rglob\(|\.glob\(|\.stem\b|maturin\.exe|nested' \
  tests/helpers/maturin.py tests/unittests/test_maturin_build.py || true

printf '%s\n' '--- focused test codegraph outline ---'
ast-grep outline tests/unittests/test_maturin_build.py \
  --items all --type function --match 'maturin|script' --view expanded

printf '%s\n' '--- GitHub review thread metadata ---'
gh api graphql -f query='
query {
  repository(owner: "leynos", name: "cuprum") {
    pullRequest(number: 217) {
      reviewThreads(first: 100) {
        nodes {
          id
          isResolved
          comments(first: 20) {
            nodes {
              databaseId
              author { login }
              body
              url
            }
          }
        }
      }
    }
  }
}' --jq '.data.repository.pullRequest.reviewThreads.nodes[]
  | select(any(.comments.nodes[]; .url == "https://github.com/leynos/cuprum/pull/217#discussion_r3664665154"))
  | {id, isResolved, comments}'

Length of output: 39300


#!/bin/bash
set -euo pipefail

gh api graphql \
  -F threadId='PRRT_kwDOQgt8686UXLHr' \
  -f query='
mutation($threadId: ID!) {
  resolveReviewThread(input: {threadId: $threadId}) {
    thread {
      id
      isResolved
    }
  }
}' \
  --jq '.data.resolveReviewThread.thread'

🧬 Code Graph Analysis Results

Relevant code snippets

tests/helpers/maturin.py (lines 28-60)

class MaturinBuildError(subprocess.CalledProcessError):
    """Maturin build failure with raw output and rendered diagnostics.

    Attributes
    ----------
    build_command : tuple[str, ...]
        Command used to invoke the maturin wheel build.
    returncode : int
        Process exit status, inherited from ``CalledProcessError``.
    stderr : str | bytes | None
        Raw captured standard error, inherited from ``CalledProcessError``.
    """

    def __init__(self, error: subprocess.CalledProcessError) -> None:
        """Store raw process diagnostics separately from ``str(error)``."""
        super().__init__(
            error.returncode,
            error.cmd,
            output=error.stdout,
            stderr=error.stderr,
        )
        if isinstance(error.cmd, list | tuple):
            self.build_command = tuple(str(part) for part in error.cmd)
        else:
            self.build_command = (str(error.cmd),)

    def __str__(self) -> str:
        """Return an enriched diagnostic while preserving raw stderr."""
        rendered_command = " ".join(self.build_command)
        return (
            f"maturin wheel build failed for command: {rendered_command}\n"
            f"stderr:\n{self.stderr}"
        )

tests/helpers/maturin.py (lines 63-83)

def read_expected_maturin_version(root: Path) -> str:
    """Read the maturin version pinned in ``pyproject.toml``.

    Raises
    ------
    AssertionError
        If the maturin dependency pin is missing.
    FileNotFoundError
        If ``pyproject.toml`` is absent.
    OSError
        If ``pyproject.toml`` cannot be read.
    UnicodeDecodeError
        If ``pyproject.toml`` is not valid UTF-8.
    """
    pyproject = (root / "pyproject.toml").read_text(encoding="utf-8")
    match = _MATURIN_PIN_RE.search(pyproject)
    if match is None:
        msg = "Could not locate maturin dev dependency pin in pyproject.toml"
        raise AssertionError(msg)
    return match.group(1)

tests/helpers/maturin.py (lines 98-131)

def read_maturin_pins(root: Path) -> dict[str, str]:
    """Read maturin version pins from the synchronized locations.

    Raises
    ------
    AssertionError
        If any maturin version pin is missing.
    FileNotFoundError
        If any pin source file is absent.
    OSError
        If any pin source file cannot be read.
    UnicodeDecodeError
        If any pin source file is not valid UTF-8.
    """
    pyproject = (root / "pyproject.toml").read_text(encoding="utf-8")
    workflow = (root / ".github/workflows/build-wheels.yml").read_text(encoding="utf-8")
    action = (root / ".github/actions/build-wheels/action.yml").read_text(
        encoding="utf-8"
    )

    return {
        "pyproject.toml": _require_pin_match(
            _MATURIN_PIN_RE.search(pyproject),
            "pyproject.toml",
        ),
        "build-wheels.yml": _require_pin_match(
            _WORKFLOW_PIN_RE.search(workflow),
            ".github/workflows/build-wheels.yml",
        ),
        "build-wheels/action.yml": _require_pin_match(
            _ACTION_PIN_RE.search(action),
            ".github/actions/build-wheels/action.yml",
        ),
    }

tests/helpers/maturin.py (lines 217-248) — summary

def maturin_script_locatable() -> bool:
    """Return whether maturin's own lookup can find its compiled script.

    Parameters: none.
    Returns: bool indicating whether a file stem named 'maturin' exists under any
        sysconfig 'scripts' directory for schemes present on the running interpreter.
    Exceptions: none explicitly raised by this function (other failures would come
        from sysconfig/path filesystem calls).
    Important details: walks sysconfig.get_scheme_names(), checks directories exist,
        and searches recursively for files whose stem is exactly 'maturin'.
    """

tests/helpers/maturin.py (lines 251-290)

def build_native_wheel_artifact(root: Path, out_dir: Path) -> Path:
    """Build a native wheel with the pinned maturin version.

    Raises
    ------
    AssertionError
        If the build does not produce exactly one wheel.
    OSError
        If the output directory cannot be created or inspected.
    MaturinBuildError
        If the maturin build command exits non-zero.
    """
    out_dir.mkdir(parents=True, exist_ok=True)
    command = [
        sys.executable,
        "-m",
        "maturin",
        "build",
        "--release",
        "--locked",
        "--out",
        str(out_dir),
        "--manifest-path",
        str(root / "rust/cuprum-rust/Cargo.toml"),
    ]
    try:
        subprocess.run(  # noqa: S603 - trusted paths and pinned maturin
            command,
            check=True,
            cwd=root,
            capture_output=True,
            text=True,
        )
    except subprocess.CalledProcessError as exc:
        raise MaturinBuildError(exc) from exc
    wheels = sorted(out_dir.glob("*.whl"))
    if len(wheels) != 1:
        msg = f"Expected exactly one wheel in {out_dir}, found {wheels!r}"
        raise AssertionError(msg)
    return wheels[0]

tests/helpers/maturin.py (lines 293-295)

wheel_build_snapshot = _maturin_wheel.wheel_build_snapshot

tests/helpers/maturin_wheel.py (lines 162-189)

def wheel_build_snapshot(whl_path: Path) -> WheelBuildSnapshot:
    """Return a normalised snapshot of wheel metadata and layout.

    Raises
    ------
    AssertionError
        If the wheel metadata is missing expected maturin fields.
    OSError
        If the wheel file cannot be opened or read.
    zipfile.BadZipFile
        If the wheel file is not a valid zip archive.
    """
    with zipfile.ZipFile(whl_path) as archive:
        entry_names = archive.namelist()
        wheel_name = _locate_dist_info_wheel(entry_names)
        metadata_name = wheel_name.replace("/WHEEL", "/METADATA")
        wheel_payload = archive.read(wheel_name).decode("utf-8")
        metadata_payload = archive.read(metadata_name).decode("utf-8")
    generator, root_is_purelib = _parse_wheel_header(wheel_payload, whl_path)
    return {
        "generator": generator,
        "metadata": _parse_metadata(metadata_payload),
        "wheel": {
            "root_is_purelib": root_is_purelib,
            "tag": "<platform-tag>",
        },
        "entries": sorted(_normalise_wheel_entry(name) for name in entry_names),
    }

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@buzzybee-df12

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cuprum/unittests/test_maturin_build.py`:
- Line 73: Update both assertions in the maturin launcher-discovery tests,
including the assertion in the test containing maturin_script_locatable(), to
include descriptive failure messages identifying the expected launcher-discovery
outcome; preserve the existing assertion conditions.

In `@tests/helpers/maturin_wheel.py`:
- Line 33: Update the docstrings in tests/helpers/maturin_wheel.py at the
affected locations, including the prose associated with the metadata snapshot,
to use “normalized” instead of “normalised”; preserve all identifiers and other
wording.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a9fbc6eb-4724-4b04-a253-349c20c1f08f

📥 Commits

Reviewing files that changed from the base of the PR and between b0ef9a9 and e71ab57.

📒 Files selected for processing (4)
  • cuprum/unittests/test_maturin_build.py
  • docs/developers-guide.md
  • tests/helpers/maturin.py
  • tests/helpers/maturin_wheel.py
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • leynos/shared-actions (auto-detected)
  • leynos/pylint-pypy-shim (auto-detected)
  • leynos/whitaker (auto-detected)

Comment thread cuprum/unittests/test_maturin_build.py Outdated
Comment thread tests/helpers/maturin_wheel.py Outdated


class WheelMetadata(typ.TypedDict):
"""Normalised ``.dist-info/METADATA`` fields captured in the snapshot."""

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Standardize the new prose on Oxford -ize spelling.

Replace “normalised” with “normalized” in these docstrings. Based on learnings, prose outside identifiers must use en-GB-oxendict “-ize” spellings.

Also applies to: 67-67, 163-163

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/helpers/maturin_wheel.py` at line 33, Update the docstrings in
tests/helpers/maturin_wheel.py at the affected locations, including the prose
associated with the metadata snapshot, to use “normalized” instead of
“normalised”; preserve all identifiers and other wording.

Sources: Coding guidelines, Path instructions, Learnings

leynos and others added 4 commits July 29, 2026 11:38
`test_maturin_wheel_build_snapshot` fails every scheduled mutmut run:
`sys.executable -m maturin build` dies with "Unable to find `maturin`
script" before any mutants are generated, aborting the whole baseline.

The `maturin` PyPI package resolves its own compiled binary by walking
each `sysconfig` scheme's `scripts` directory keyed off the running
interpreter's `sys.prefix`, not `sys.path` or `PATH`. Under mutmut's
`uv run --with mutmut==3.6.0` overlay, `sys.prefix` points at a
temporary environment layered on top of the project's own virtualenv:
the `maturin` module imports fine (via `sys.path`, so `cargo`/`rustc`
and `toolchain_available()` all report success), but the overlay never
received maturin's script, so the lookup comes up empty.

Add `maturin_script_locatable()` to `tests/helpers/maturin.py`,
mirroring maturin's own lookup, and skip the wheel-build test with a
precise reason when it reports the script unreachable. In a normal
virtualenv (CI, `build-wheels.yml`, local `uv run pytest`) `sys.prefix`
matches the install location, the script is found, and the real build
still runs — only the layered mutmut overlay skips.

Add unit tests pinning the new detector's true/false behaviour by
faking the `sysconfig` scheme lookup.

Rebasing onto main brought in `MaturinBuildError` (#144), which — combined
with the new `maturin_script_locatable()` detector — pushed
`tests/helpers/maturin.py` past pylint's 400-line module limit. Extract the
wheel-artifact snapshot parsers (`wheel_build_snapshot` and its private
helpers) into a new sibling module `tests/helpers/maturin_wheel.py`, and
re-export `wheel_build_snapshot` from `tests/helpers/maturin.py` so existing
import sites are unchanged.
Record the scope and reuse policy for the shared
`maturin_script_locatable()` detector in the developers' guide, as the
abstraction/helper policy in AGENTS.md requires. The helper now decides
when the native-wheel build contract is skipped, so document:

- how it mirrors maturin's own `sysconfig`-scheme script lookup keyed off
  `sys.prefix`, and why that is deliberately narrower than
  `toolchain_available()`'s importability probe;
- the layered/ephemeral interpreter case (the `uv run --with mutmut`
  overlay) where the two checks disagree and the skip is genuine, versus
  normal virtualenvs where the real build still runs;
- the reuse policy: tests that shell out to `python -m maturin build`
  should gate on both `toolchain_available()` and
  `maturin_script_locatable()`; import-only or pin/metadata tests should
  not; extend the helper in place rather than re-deriving the scan.

Also note that the wheel-artefact snapshot parsers now live in
`tests/helpers/maturin_wheel.py` (re-exported from
`tests/helpers/maturin.py`), matching the module split.
Replace the `dict[str, typ.Any]` returns in `tests/helpers/maturin_wheel.py`
with `TypedDict`s describing the fixed snapshot shape: `WheelBuildSnapshot`
(`generator`, `metadata`, `wheel`, `entries`), plus `WheelMetadata` and
`WheelHeaders` for the nested payloads. Indexing `snapshot_payload["generator"]`
at the `test_maturin_wheel_build_snapshot` call site is now statically typed
without `Any`; `TypedDict` is a plain dict at runtime, so the syrupy snapshot
comparison is unchanged.

Also pin the deliberate stem-based, recursive matching in
`_script_named_maturin_exists` with a regression test. maturin's own
`get_maturin_path` walks the scripts directory with `os.walk` and compares
`os.path.splitext(f)[0]` against `"maturin"`, so it accepts any extension at
any depth; `maturin.exe` is the real launcher on the `windows-2022` wheel
target. Narrowing the match to an exact top-level `maturin` filename would
diverge from maturin and make the probe report unavailable on Windows,
silently skipping the native-wheel contract there.
Give both remaining bare assertions in the maturin launcher-discovery
tests descriptive failure messages naming the expected outcome, matching
the style already used by the Windows-launcher test and the rest of the
module. The assertion conditions are unchanged.

Switch the `-ised` docstring prose in `tests/helpers/maturin_wheel.py` to
the `-ized` Oxford endings the documentation style guide requires
(`docs/documentation-style-guide.md`, en-GB-oxendict), matching the
`_normalise_wheel_entry` docstring that already read "Normalize".
Identifiers keep their existing spelling, so `_normalise_wheel_entry` and
the `normalised` loop variable are untouched.
@lodyai
lodyai Bot force-pushed the fix/mutmut-baseline-maturin branch from e71ab57 to 4f0cf31 Compare July 29, 2026 09:41
codescene-access[bot]

This comment was marked as outdated.

@codescene-access codescene-access 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.

No quality gates enabled for this code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Mutation-testing baseline fails: maturin wheel build cannot find its own script

2 participants