Skip to content

Unify the Rust-availability probes behind one resolver (#128) - #143

Merged
leynos merged 22 commits into
mainfrom
issue-128-unify-rust-availability-probes
Jul 18, 2026
Merged

Unify the Rust-availability probes behind one resolver (#128)#143
leynos merged 22 commits into
mainfrom
issue-128-unify-rust-availability-probes

Conversation

@leynos

@leynos leynos commented Jun 11, 2026

Copy link
Copy Markdown
Owner

Summary

This branch unifies the three divergent Rust-availability probes behind the single cached, override-aware resolver that governs stream-backend dispatch.

Closes #128.

The cached _backend._check_rust_available() governs dispatch and honours the set_rust_availability_for_testing override, but the public rust.is_rust_available() bypassed both the cache and the override by calling _rust_backend.is_available() directly. A caller using the documented public API could therefore observe a different availability than the dispatcher actually used. The public probe now delegates to the same resolver, so there is one source of truth; the raw uncached import probe is documented as such.

Review walkthrough

  • Start with cuprum/rust.py: is_rust_available() now delegates to _backend._check_rust_available().
  • cuprum/_rust_backend.py documents is_available() as the raw, uncached probe that does not honour the cache or test override.
  • Finish with cuprum/unittests/test_backend.py for the new tests asserting the public probe equals the dispatch resolver and reflects the test override.

Validation

  • make check-fmt: pass
  • make lint: pass
  • make typecheck: pass
  • make test: pass (598 passed, 44 skipped; Rust suite 4 passed)
  • coderabbit review --agent: 0 findings

Summary by Sourcery

Unify Rust extension availability checks so the public probe and backend dispatch share a single cached, override-aware resolver.

Bug Fixes:

  • Ensure the public is_rust_available API returns the same availability value used by stream-backend dispatch, including honoring the testing override.

Enhancements:

  • Document _rust_backend.is_available as the raw, uncached import probe and clarify its relationship to the public resolver.
  • Refine developer documentation formatting for environment overlay resolution and pipeline benchmark configuration.
  • Clarify concurrency helper docstrings in tee profile worker tests and increase CrossHair per-condition timeout to reduce test flakiness.

Tests:

  • Add tests asserting the public Rust-availability probe matches the dispatch resolver and respects set_rust_availability_for_testing.

References

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 46 minutes

Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b82efea2-3629-4004-9e56-f23988f5f624

📥 Commits

Reviewing files that changed from the base of the PR and between a4be9ea and f150437.

📒 Files selected for processing (14)
  • cuprum/_backend.py
  • cuprum/_rust_backend.py
  • cuprum/rust.py
  • cuprum/unittests/test_backend.py
  • cuprum/unittests/test_rust_extension.py
  • docs/adr-005-unified-rust-availability-probe.md
  • docs/cuprum-design.md
  • docs/developers-guide.md
  • docs/roadmap.md
  • docs/users-guide.md
  • rust/cuprum-rust/Cargo.toml
  • rust/cuprum-rust/src/lib.rs
  • rust/cuprum-rust/tests/ui/fail/const_availability_export.rs
  • rust/cuprum-rust/tests/ui/fail/const_availability_export.stderr

Walkthrough

Unify public Rust availability checks with cached backend resolution, preserve a separate uncached native probe, add structured logging and tests, update Rust bindings, and document resolver, fallback, testing, and dispatch semantics.

Changes

Rust availability resolution

Layer / File(s) Summary
Raw native availability probe
cuprum/_rust_backend.py, rust/cuprum-rust/src/lib.rs, cuprum/unittests/test_rust_extension.py
Document the uncached probe, log missing or unexpected native-module failures, and keep the Rust binding runtime-based with matching tests.
Backend resolution and dispatch
cuprum/_backend.py, cuprum/unittests/test_backend.py
Apply overrides, log resolution outcomes, and route Python, Rust, and AUTO modes through dedicated resolver helpers.
Public API and validation
cuprum/rust.py, cuprum/unittests/test_backend.py
Route is_rust_available() through the cached resolver, restrict exports, and test alignment, overrides, cache clearing, and logging.
Probe stack documentation
docs/adr-005-unified-rust-availability-probe.md, docs/cuprum-design.md, docs/developers-guide.md, docs/roadmap.md, docs/users-guide.md
Document caching, raw probing, fallback logging, dispatch integration, testing guidance, and related documentation updates.

Sequence Diagram(s)

sequenceDiagram
  participant Caller as cuprum.rust.is_rust_available()
  participant Backend as get_stream_backend()
  participant Resolver as _check_rust_available()
  participant Probe as _rust_backend.is_available()
  Caller->>Resolver: request cached availability
  Backend->>Resolver: request dispatch availability
  Resolver->>Probe: probe native module when uncached
  Probe-->>Resolver: return availability or raise ImportError
  Resolver-->>Caller: return resolved boolean
  Resolver-->>Backend: provide selection result
Loading

Possibly related PRs

  • leynos/cuprum#25: Introduces the Rust availability probe and public helper that this PR reroutes through the cached resolver.

Suggested reviewers: codescene-delta-analysis, codescene-access

Poem

Rust knocks softly at the door,
The resolver checks, then checks no more.
Logs glow bright where probes have flown,
Python waits when Rust is gone.
One cached truth now guides the way.

🚥 Pre-merge checks | ✅ 18 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The developer guide also removes unrelated token-registration documentation, which falls outside #128. Move the unrelated documentation removal into a separate PR and keep this branch focused on Rust-availability probe unification.
Testing (Compile-Time / Ui) ⚠️ Warning The crate has trybuild UI tests, but none cover the new runtime/non-const is_available contract; only runtime tests and a clippy expect were added. Add a trybuild fixture for the PyO3 export contract, proving is_available stays a runtime fn/non-const item or equivalent compile-time UI coverage.
✅ Passed checks (18 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the core refactor and includes the linked issue number.
Description check ✅ Passed The description directly explains the probe unification and related tests.
Linked Issues check ✅ Passed The changes route the public probe through the cached resolver and add the required parity tests for #128.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Testing (Overall) ✅ Passed Tests cover delegation, override short-circuiting, cache refresh, and raw-probe error handling; plausible regressions would fail.
User-Facing Documentation ✅ Passed Keep the user guide in sync: it documents cuprum.is_rust_available(), the override-aware resolver, the raw probe caveat, and the auto fallback log.
Developer Documentation ✅ Passed Developer guide, ADR/design docs, and roadmap are updated; issue #128 is checked off and no locale-sync or relevant execplan gap is present.
Module-Level Documentation ✅ Passed Every touched module has a top-level docstring or crate doc comment, and each explains its role and relationship to the resolver/dispatch stack.
Testing (Unit And Behavioural) ✅ Passed PASS: The PR adds targeted unit tests for probe errors, cache/override invariants, and public-probe parity; existing behavioural pipeline tests exercise the dispatch boundary.
Testing (Property / Proof) ✅ Passed PASS: Hypothesis property test covers availability/dispatch alignment across override transitions, matching the introduced state invariant.
Unit Architecture ✅ Passed Keep the raw probe, cached resolver, and test override split into distinct seams; the public helper now delegates cleanly and tests cover the boundary.
Domain Architecture ✅ Passed Changes stay in backend/Rust bridge, docs, and tests; no domain entities, repositories, or business rules were introduced or coupled to infrastructure.
Observability ✅ Passed PASS: Resolver and raw probe add structured debug/warning logs at cache-clear, resolution, and import-failure boundaries, with bounded fields and caplog coverage.
Security And Privacy ✅ Passed PASS: Changes only adjust Rust-availability resolution and structured logging with non-sensitive fields; no secrets, auth, permission, or injection surfaces are added.
Performance And Resource Use ✅ Passed PASS: keep the raw probe uncached by design, while public dispatch now uses a maxsize=1 cached resolver; no new unbounded loops or I/O paths appear.
Concurrency And State ✅ Passed Shared state is module-private, cache-backed, and reset via set_rust_availability_for_testing; ADR/docs state the process-lifetime model, and tests cover override transitions.
Architectural Complexity And Maintainability ✅ Passed Keep the refactor narrow: it reuses the existing resolver, adds only private branch helpers, and avoids new layers or hidden indirection.
Rust Compiler Lint Integrity ✅ Passed Only a narrow #[expect(clippy::missing_const_for_fn)] was added on is_available() with an issue link; no dead_code/unused suppressions or clone() additions were found.
📋 Issue Planner

Built with CodeRabbit's Coding Plans for faster development and fewer bugs.

View plan used: #128

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-128-unify-rust-availability-probes

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

@sourcery-ai

sourcery-ai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Unifies all Rust extension availability checks behind the cached, override-aware backend resolver, aligns the public rust availability API with backend dispatch behavior, and adds tests and documentation clarifications to keep these invariants enforced and well-documented.

Sequence diagram for unified Rust availability resolution

sequenceDiagram
    actor Caller
    participant rust as cuprum.rust
    participant backend as cuprum._backend
    participant rust_backend as cuprum._rust_backend

    Caller->>rust: is_rust_available()
    rust->>backend: _check_rust_available()

    alt [override set via set_rust_availability_for_testing]
        backend-->>Caller: cached_override_value
    else [no override]
        alt [cache hit]
            backend-->>Caller: cached_probe_value
        else [cache miss]
            backend->>rust_backend: is_available()
            rust_backend-->>backend: raw_import_probe_result
            backend-->>Caller: resolved_probe_value
        end
    end
Loading

File-Level Changes

Change Details Files
Public Rust-availability probe now delegates to the cached, override-aware backend resolver used for stream-backend dispatch.
  • Change is_rust_available to import _check_rust_available from cuprum._backend instead of calling _rust_backend.is_available directly.
  • Update is_rust_available docstring to describe it as the single public entry point that uses the dispatch resolver and honours set_rust_availability_for_testing.
  • Document the raw uncached import probe location and recommend preferring the public wrapper for dispatch-governing checks.
cuprum/rust.py
Document the raw _rust_backend import probe as uncached and override-insensitive to clarify its semantics relative to the public probe.
  • Extend is_available docstring to explicitly mark it as the raw import probe that does not use the cache or test override.
  • Point callers that care about dispatch-governing availability to cuprum.rust.is_rust_available or cuprum._backend._check_rust_available.
cuprum/_rust_backend.py
Add tests ensuring the public Rust-availability probe matches the dispatch resolver and respects the test override.
  • Import is_rust_available and set_rust_availability_for_testing into backend tests.
  • Add parametrized test that monkeypatches the raw probe and asserts is_rust_available equals _check_rust_available for both available True/False.
  • Add test verifying is_rust_available reflects set_rust_availability_for_testing for True, False, and reset (None).
cuprum/unittests/test_backend.py
Improve documentation and comments for environment overlays, lock protocol tests, and concurrency/profile tests without changing core behavior.
  • Reflow and clarify text in developers-guide.md around env overlay helpers, benchmark configuration, and model-checking notes without semantic changes.
  • Add docstrings to _RLockLike protocol methods and _SignallingRLock context manager methods to clarify behavior in concurrency tests.
  • Increase CrossHair per_condition_timeout in line-splitting tests with a comment explaining flakiness under parallel pytest workers.
docs/developers-guide.md
cuprum/unittests/test_tee_profile_worker_concurrency.py
cuprum/unittests/test_line_splitting.py

Assessment against linked issues

Issue Objective Addressed Explanation
#128 Route the public rust.is_rust_available() (and any consumers of _rust_backend.is_available()) through the same cached, override-aware resolver _backend._check_rust_available so there is a single source of truth for Rust extension availability.
#128 Document that the raw uncached import probe does not honour the backend cache or the test override, while clarifying that is_rust_available() uses the cached, override-aware resolver.
#128 Add unit tests asserting that the public probe (is_rust_available) and the dispatch probe (_check_rust_available) always agree, including when the set_rust_availability_for_testing override is applied.

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

codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos

leynos commented Jun 11, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response.

cuprum/unittests/test_tee_profile_worker_concurrency.py

Comment on file

    """Structural type for the ``RLock`` operations this test instruments."""

    def acquire(self, *, blocking: bool = True, timeout: float = -1) -> bool: ...
    def acquire(self, *, blocking: bool = True, timeout: float = -1) -> bool:

❌ New issue: Low Cohesion
This module has at least 7 different responsibilities amongst its 35 functions, threshold = 4

@coderabbitai

This comment was marked as resolved.

@lodyai
lodyai Bot force-pushed the issue-128-unify-rust-availability-probes branch from 8a6a5c8 to 1232c49 Compare June 11, 2026 10:59
codescene-delta-analysis[bot]

This comment was marked as outdated.

@lodyai
lodyai Bot force-pushed the issue-128-unify-rust-availability-probes branch from 1232c49 to e72bfce Compare June 11, 2026 13:07
codescene-delta-analysis[bot]

This comment was marked as outdated.

@lodyai
lodyai Bot force-pushed the issue-128-unify-rust-availability-probes branch from e72bfce to c80fc31 Compare June 11, 2026 21:31
codescene-delta-analysis[bot]

This comment was marked as outdated.

@lodyai
lodyai Bot force-pushed the issue-128-unify-rust-availability-probes branch from c80fc31 to 3d55365 Compare June 11, 2026 22:22
codescene-delta-analysis[bot]

This comment was marked as outdated.

@lodyai
lodyai Bot force-pushed the issue-128-unify-rust-availability-probes branch from 3d55365 to efaa0e1 Compare June 12, 2026 11:56
codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos
leynos force-pushed the issue-128-unify-rust-availability-probes branch from efaa0e1 to b42b657 Compare June 14, 2026 01:52
codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos
leynos marked this pull request as ready for review June 16, 2026 00:21

@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

@pandalump

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 16, 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 added the Issue label Jun 16, 2026
coderabbitai[bot]

This comment was marked as resolved.

@leynos

leynos commented Jun 16, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown
✅ Action performed

Reviews resumed.

codescene-delta-analysis[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as resolved.

codescene-delta-analysis[bot]

This comment was marked as outdated.

codescene-delta-analysis[bot]

This comment was marked as outdated.

leynos added 9 commits July 17, 2026 22:02
Log import failures only at the resolution boundary and use module-specific loggers so callers can configure each boundary independently.
Make the missing warning assertion actionable and document why Whitaker runs with one Cargo worker during the lint gate.
Retain the runtime PyO3 availability export while restoring mainline Unix descriptor conversion and ownership semantics.
Describe the cached resolver in the public and developer-facing docs.
Note that testing overrides short-circuit the resolver, clear both
backend caches, and only drift across out-of-band wheel swaps.
Remove branch-only `_TokenRegistration` and Whitaker notes that are not
part of `#128`.
Exercise public availability and backend dispatch through randomized
override and cache transitions, including raw-probe short-circuiting.
Add a compile-fail UI case that verifies the PyO3 availability function
cannot be evaluated in a Rust const context. This complements the existing
compile-pass registration case and the lint guard on the production export.
Clarify that cached availability can become stale after wheel swaps and
other out-of-band import-path or installation-state changes. Keep the public
API and developer and user guidance consistent.
Make the existing PyO3 availability function linkable to Rust UI tests
without changing its Python export. Replace the lookalike trybuild fixture
with a const-context call to the production symbol so a const regression
changes the expected compile-fail outcome.
@leynos
leynos force-pushed the issue-128-unify-rust-availability-probes branch from f86d456 to 00a08bd Compare July 17, 2026 20:02
codescene-access[bot]

This comment was marked as outdated.

@leynos

This comment was marked as resolved.

@coderabbitai

This comment was marked as resolved.

Regenerate the compile-fail diagnostic with the Rust 1.92.0 CI toolchain.
Retain the production-coupled `E0015` check while removing diagnostic detail
that newer compilers render differently.
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@coderabbitai

This comment was marked as resolved.

@leynos

leynos commented Jul 18, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Have the following failed checks now been resolved?

If further work is required, please provide an AI agent prompt for the remaining work to be done to address these failures.

Do not treat warnings as optional or aspirational. Where a change is out of scope for this PR, propose a GitHub issue unless one exists already. (Treat documentation and validation coverage as in scope).

Testing (Compile-Time / Ui) ⚠️ Warning The crate has trybuild UI tests, but none cover the new runtime/non-const is_available contract; only runtime tests and a clippy expect were added. Add a trybuild fixture for the PyO3 export contract, proving is_available stays a runtime fn/non-const item or equivalent compile-time UI coverage.

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== UI harness =="
cat -n rust/cuprum-rust/tests/compile_tests.rs

echo
echo "== UI fixture and expected diagnostic =="
for file in \
  rust/cuprum-rust/tests/ui/fail/const_availability_export.rs \
  rust/cuprum-rust/tests/ui/fail/const_availability_export.stderr
do
  echo "--- ${file}"
  cat -n "$file"
done

echo
echo "== Production export context =="
rg -n -C 14 \
  '#\[pyfunction\]|missing_const_for_fn|(?:pub\s+)?(?:const\s+)?fn\s+is_available' \
  rust/cuprum-rust/src/lib.rs

echo
echo "== Rust test configuration and relevant PR diff =="
cat -n rust/cuprum-rust/Cargo.toml
git diff --find-renames origin/main...HEAD -- \
  rust/cuprum-rust/src/lib.rs \
  rust/cuprum-rust/tests/compile_tests.rs \
  rust/cuprum-rust/tests/ui/fail/const_availability_export.rs \
  rust/cuprum-rust/tests/ui/fail/const_availability_export.stderr

Length of output: 6012


@leynos

Files: rust/cuprum-rust/src/lib.rs:is_available; rust/cuprum-rust/tests/compile_tests.rs:compile_time_ui; rust/cuprum-rust/tests/ui/fail/const_availability_export.rs; rust/cuprum-rust/tests/ui/fail/const_availability_export.stderr

Resolved. Mark this warning as passed.

The current codegraph contains production-coupled trybuild coverage for the runtime/non-const contract:

  • compile_time_ui discovers all failure fixtures with compile_fail("tests/ui/fail/*.rs").

  • const_availability_export.rs calls the actual compiled crate export in a constant context:

    const RUST_AVAILABLE: bool = _rust_backend_native::is_available();
  • Production is_available remains the PyO3 export and is deliberately non-const:

    #[expect(clippy::missing_const_for_fn, ...)]
    #[doc(hidden)]
    #[pyfunction]
    pub fn is_available() -> bool {
        true
    }
  • The checked-in trybuild snapshot expects E0015, confirming that a non-const function cannot be called by the fixture’s constant initializer.

Keep this fixture and snapshot. Do not add another local-function fixture or alter is_available merely to satisfy the warning. Changing the production export back to const fn would cause this compile-fail fixture to compile, which makes trybuild fail and therefore detects exactly the regression in question.

No further agent work or follow-up issue is required.

@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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Refactor: unify the three divergent Rust-availability probes behind one resolver

2 participants