Skip to content

Preserve legacy deque, timezone and type signatures - #9773

Draft
youknowdot wants to merge 1 commit into
RustPython:mainfrom
youknowdot:fix/legacy-native-signatures-20261110
Draft

youknowdot wants to merge 1 commit into
RustPython:mainfrom
youknowdot:fix/legacy-native-signatures-20261110

Conversation

@youknowdot

Copy link
Copy Markdown
Contributor

Summary

Match four remaining CPython 3.15 raw text signatures in one batch:

  • collections.deque and deque.index: preserve their legacy optional-group spelling.
  • datetime.timezone: expose its unrepresentable name default instead of hiding the entire class signature.
  • type.__prepare__: use its $cls receiver spelling, including unbound inspection.

Use the existing static internal-doc path. The const renderer now preserves an explicitly supplied, correctly framed Clinic header instead of prepending a second generated header. Ordinary prose and incomplete/wrong-name headers retain generated signatures. No new macro attribute, registration path, argument conversion, binding pass, or call-time work is added. Existing public doc bodies remain identical.

The class representability filter is unchanged, and no RustPython-only signature is removed. This is an independent main-based patch, not a replacement for the optional-group or wrapping work in #9756/#9758. The deque and timezone signatures intentionally remain unrepresentable by inspect.signature, as in CPython.

References

CPython 3.15.1: deque, timezone, and type.prepare.

Validation

  • Actual CPython 3.15.1 and RustPython release: complete signature snippet passes, including raw strings, unchanged docs, bound/unbound inspection, and expected inspection failures.
  • 65 ordinary/malformed calls are unchanged from the main-based Match BytesIO readlines and certificate byte format bindings #9772 binary, whose only source changes are in IO/SSL. Existing CPython error-text differences are not claimed fixed.
  • Inventory comparison retains all 16,227 observed paths and changes only these four metadata entries, apart from the two known IO/SSL fixes present only in Match BytesIO readlines and certificate byte format bindings #9772.
  • Workspace tests, CAPI tests (149 passed, 2 ignored), both strict all-target Clippy runs, the pytest snippet wrapper, and normal commit hooks pass.
  • test_deque test_datetime test_descr test_inspect: 1,790 tests run, 165 skipped; all four files pass.
  • Cross-platform CI is pending publication. The final commit has exactly the tested source tree.

AI assistance: OpenAI Codex prepared the implementation and tests, with a separate read-only review.

@coderabbitai

coderabbitai Bot commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share


// timezone

/// timezone(offset, name=<unrepresentable>)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

dont' manually add docs

@youknowone youknowone left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The mechanism looks right: has_internal_doc_signature follows find_signature + skip_signature in Objects/typeobject.c (the doc must start with name(, the scan looks for )\n--\n\n, and it gives up at a blank line). These four objects get their text signatures from hand-written docstring headers in CPython as well. A few things should change before merging, though.

Observed before → after

Before: a build of 45a012cda (main at the time). After: the values asserted in this PR's snippet, which match CPython 3.15.

object __text_signature__ before after (= CPython) inspect.signature
deque None '([iterable[, maxlen]])' ValueError both
deque.index '($self, needle, start=0, stop=<unrepresentable>, /)' '($self, value, [start, [stop]])' ValueError both
timezone None '(offset, name=<unrepresentable>)' ValueError both
type.__dict__['__prepare__'] '($type, name, bases, /, **kwds)' '($cls, name, bases, /, **kwds)' (type, …) → (cls, …)

In practice this changes raw metadata only. The only inspect-visible difference is the receiver name of the unbound __prepare__.

1. Drop "legacy" from the naming; one comment is factually wrong

The feature is "an explicit internal-doc signature in the docstring is used as-is", and has_internal_doc_signature already says that. "Legacy" in the title, branch, code comment, test name and snippet comment does not describe it:

  • The comment in signature.rs, "Explicit Clinic headers cover legacy signatures that are not Python parameter lists", is wrong for __prepare__: ($cls, name, bases, /, **kwds) is an ordinary parameter list. Only the deque headers use optional groups. Something like: "A doc that starts with a complete name(...)\n--\n\n header supplies its own text signature."
  • explicit_internal_doc_preserves_legacy_signatures → e.g. explicit_internal_doc_signature_is_kept.
  • Snippet comment "Legacy Clinic headers …" and the legacy signature should remain unrepresentable message → describe them as explicit internal-doc signatures.
  • PR title → e.g. "Honor explicit internal-doc signatures for deque, timezone and type.prepare".

#9757 went through the same rename (legacy_signature → signature_from_flags) for the same reason.

2. The header is used implicitly and never checked against the Rust binding

Any /// doc that happens to be header-shaped now silently replaces the generated signature, and nothing checks the hand-written header against the real arguments. The drift is already there: deque.index binds needle in Rust, while the header says value. The displayed text is correct today, but a later change to the Rust signature would go unnoticed.

#9757 takes the opposite approach: an explicit #[pyfunction(signature_from_flags)] opt-in, plus const assertions that reject a mismatched arity at compile time. CPython does not validate hand-written headers either, so this is not a parity bug. It is still inconsistent within RustPython. Please consider at least a const check that the header's positional arity agrees with the binding metadata, or an explicit opt-in so the override cannot happen by accident.

3. Docs are now split between two sources

Because a /// doc overrides the crates/doc DB entry, the bodies of these four docstrings are now hard-coded in Rust, and regenerating crates/doc/src/data.inc.rs will no longer update them. crates/doc/generate.py stores only __doc__, which has the header stripped. A broader fix would be to have the generator also capture __text_signature__. That can be a follow-up, but it should be acknowledged here.

4. Conflict with #9757

Both PRs append to crates/vm/src/function/signature.rs and to the end of extra_tests/snippets/builtin_signature.py, so whichever lands second needs a rebase.

— commented by Claude

Expose the deque and timezone class headers, deque.index optional groups, and type.__prepare__ receiver spelling through existing static documentation. Preserve typed argument binding, documentation bodies and generated defaults for ordinary prose.

Assisted-by: OpenAI Codex:model version not exposed
@youknowdot
youknowdot force-pushed the fix/legacy-native-signatures-20261110 branch from 80e7f45 to 0e02866 Compare November 10, 2026 15:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants