🐛 fix(stubs): resolve stub type hints for C/Rust extensions#655
🐛 fix(stubs): resolve stub type hints for C/Rust extensions#655gaborbernat merged 4 commits intotox-dev:mainfrom
Conversation
|
Right, I still had explicit type hints in my docstring for |
|
I removed all the explicit type hints now. |
|
Ah, I see: |
|
As for the failing resolution of |
|
I also noticed that EDIT: It would seem like this function was not updated for type stub support. |
|
@agronholm can you try again? Pushed some more fixes. |
|
Now it seems like all type hints are gone completely. Did this work for you locally? |
|
My bad, I installed the wrong branch. With the correct branch, type hints are there but the resolution of local names still doesn't work. |
|
Could you share a specific example of which local names aren't resolving? For instance, is it that type aliases like On my end, building the cbor2 rust branch docs with this PR produces zero warnings under |
I already showed screenshots here, did I not? I also shared my analysis on why that is happening. |
|
I had local changes that interfered with the name resolution. With those gone, I'm seeing the same as you are, so that's one issue down. The classes are still missing their type hints and the type alias expansion is still happening though. |
|
I tested your latest changes. Here are my findings:
So I would call this good progress, but there is still some work left to be done. |
6a8f3db to
abe4e8c
Compare
|
If you fixed something with those changes, I can't say what. If you keep force-pushing, it's hard for me to see what actually changed. |
Should be good now 🤔 (at least in my local tests seems sane). |
|
I can confirm that the links to type aliases work properly now. The only remaining problem is then the missing type hints in the initialization parameters of |
) C/Rust extension functions re-exported from parent packages (e.g., cbor2._cbor2.dumps re-exported as cbor2.dumps) were missing stub type annotations because stub discovery only checked the direct module. Additionally, TypeAlias names were being expanded to their full definitions (Callable[...]) instead of appearing as cross-referenced names, and constructor annotations from __new__ methods were being ignored for C extension classes that inherit __init__ from object. Implemented parent package stub fallback walk-up to find stubs that document re-exported APIs (e.g., cbor2/__init__.pyi for cbor2._cbor2 functions). Added stub-owning module vars to localns so TypeAlias values and class objects are available during eval. Introduced crossref flag on MyTypeAliasForwardRef to distinguish stub-derived aliases (which emit :py:type: roles for linking) from autodoc_type_aliases display names. Extended process_docstring to use __new__ when __init__ is inherited from object, enabling constructor parameter documentation for C extension classes that define constructors via __new__ in stubs.
|
As a data point, changing |
Add Encoder class to c_ext_mod to test __new__ stub resolution for C extension classes where __module__ is None and __self__ points to the owning class.
|
@agronholm any remaining feature gaps? 🤔 |
You mean other than the initialization parameters in the classes missing their type hints? |
|
Strange we're getting different results from the same code. What specific function should I be looking at to debug? |
|
Let me double check 🤔 can you post the exact command sequence you ran and cwd? |
|
I ran |
|
I'm debugging this, and I can confirm it's picking up the type hints from the stub's |
Yeah but how you've created envs, how did you install the tool? 🤔 |
|
The env: |
|
Debugging 👍 |
|
I'm stepping through |
|
Okay, so I figured it was going wrong when |
|
I tried on 3.10 and 3.14 too and they too give a signature of |
|
Are you getting a different result when inspecting the signature of |
|
If I get the signature from the |
|
I am not entirely sure. I'll have another look at it tomorrow. |
Strip inline :param type name: formats, remove preexisting :type: directives (including multi-line continuations), and replace preexisting :rtype: with annotation-based return types. Use class signature for __new__ methods on C extension classes where inspect.signature(__new__) returns (*args, **kwargs).
|
Check with latest commit 🤔 but I'm truly off now until tomorrow 😆 |
MyTypeAliasForwardRef.__or__ returns typing.Union on 3.13, while 3.14+ produces types.UnionType natively.
|
Seems to work now! Thank you for your quick response to this. |
|
Cool 🆒 |
|
See #656, I think this breaks NamedTuple. |





C/Rust extension functions re-exported from parent packages — like
cbor2._cbor2.dumpsexposed ascbor2.dumps— had no working stub resolution. Sphinx's built-in_find_type_stub_spec()only looks for.pyifiles adjacent to the.sowith the same filename, so stubs at the parent package level (e.g.cbor2/__init__.pyi) were never found. This meant type aliases likeEncoderHookwere expanded to rawCallable[...]signatures, annotations on C extension functions were lost entirely, and cross-references were missing from rendered docs. Additionally, C extension classes that define constructors via__new__(with__init__inherited fromobject) had no constructor parameter documentation becauseprocess_docstringonly examined__init__. 🐛The fix introduces a parent-package walk-up that follows the PEP 561 re-export pattern: when no stub is found next to the extension module, it traverses
sys.modulesupward to find the parent whose__init__.pyidocuments the re-exported public API. A unified_get_stub_context()call retrieves the stub's local namespace,TypeAliasnames (detected via AST includingtyping.TypeAlias,typing_extensions.TypeAlias, andtypestatements), and the owning module name in a single lookup — avoiding duplicate path discovery. The owning module name is then used asglobalnsduringeval()-based annotation resolution, ensuring the correct namespace is used when the function's__module__points at the C extension child rather than the stub-owning parent. ✨A
crossrefflag onMyTypeAliasForwardRefdistinguishes stub-derived aliases (which emit:py:type:roles for Sphinx cross-referencing) fromautodoc_type_aliasesdisplay names (which must not be wrapped to avoid double-quoting in text output). Thecollect_documented_type_aliasesfunction no longer matches unqualified type names from other modules, eliminating a cross-contamination risk in multi-module projects. Constructor logic inprocess_docstringnow falls back to__new__when__init__isobject.__init__and__new__is overridden, and usesinspect.signature(cls)instead ofinspect.signature(cls.__new__)to get the correct parameter list for C extensions where the latter returns(*args, **kwargs).When injecting annotation-based types, preexisting
:type:directives in the docstring (including multi-line continuations) are now removed and replaced rather than duplicated. Inline:param type name:formats are stripped to:param name:before injection, ensuring all parameter types use consistent CSS styling via thesphinx_autodoc_typehints-typeclass. Preexisting:rtype:directives are also replaced with the annotation-derived return type, fixing a bug where stale docstring return types would shadow the actual function signature.