1+ """Override Python Domain types to pull signature information from typeshed."""
2+ from __future__ import annotations
3+ import ast
4+ from typing import TYPE_CHECKING
5+ import pathlib
6+
7+ import typeshed_client
8+ from sphinx .domains .python import (
9+ PyFunction ,
10+ PyDecoratorFunction ,
11+ PyClasslike ,
12+ PyMethod ,
13+ PyClassMethod ,
14+ PyStaticMethod ,
15+ PyDecoratorMethod ,
16+ PyAttribute ,
17+ PyProperty ,
18+ PythonDomain ,
19+ )
20+
21+ if TYPE_CHECKING :
22+ from sphinx .application import Sphinx
23+ from sphinx .util .typing import ExtensionMetadata
24+ from sphinx .addnodes import desc_signature
25+
26+ ignored_modules = (
27+ "test" ,
28+ )
29+
30+ search_context = typeshed_client .get_search_context (
31+ typeshed = pathlib .Path ().home () / "typeshed" / "stdlib" ,
32+ version = (3 , 14 ),
33+ platform = "linux" ,
34+ )
35+
36+ def ast_from_resolved_name (resolved_name : typeshed_client .resolver .ResolvedName ) -> ast .AST :
37+ if isinstance (resolved_name , typeshed_client .NameInfo ):
38+ return resolved_name .ast
39+ elif isinstance (resolved_name , typeshed_client .ImportedInfo ):
40+ return resolved_name .info .ast
41+ else :
42+ return None
43+
44+ class TypedPyFunction (PyFunction ):
45+
46+ def handle_signature (self , sig : str , signode : desc_signature ) -> tuple [str , str ]:
47+ fullname , clsname = super ().handle_signature (sig , signode )
48+ modname = self .options .get ('module' , self .env .ref_context .get ('py:module' ))
49+ resolver = typeshed_client .Resolver (search_context )
50+ if modname and not any ((modname .startswith (name ) for name in ignored_modules )):
51+ try :
52+ if modname is not None :
53+ qualname = f"{ modname } .{ fullname } "
54+ else :
55+ qualname = fullname
56+ resolved_name = resolver .get_fully_qualified_name (qualname )
57+ resolved_ast = ast_from_resolved_name (resolved_name )
58+ if resolved_ast :
59+ # update signode with the proper signature from resolved_ast
60+ pass
61+ except Exception :
62+ print (f"Exception with: { modname } .{ fullname } " )
63+ raise
64+
65+ return fullname , clsname
66+
67+ overriden_directives = {
68+ "function" : TypedPyFunction ,
69+ "class" : PyClasslike ,
70+ "method" : PyMethod ,
71+ "classmethod" : PyClassMethod ,
72+ "staticmethod" : PyStaticMethod ,
73+ "attribute" : PyAttribute ,
74+ "property" : PyProperty ,
75+ "decorator" : PyDecoratorFunction ,
76+ "decoratormethod" : PyDecoratorMethod ,
77+ }
78+
79+
80+ def setup (app : Sphinx ) -> ExtensionMetadata :
81+ for name , type_ in overriden_directives .items ():
82+ app .add_directive_to_domain ("py" , name , type_ , override = True )
83+ PythonDomain .directives .update (** overriden_directives )
84+ app .add_domain (PythonDomain , override = True )
85+ return {
86+ "version" : "0.1.0" ,
87+ "env_version" : 4 ,
88+ "parallel_read_safe" : True ,
89+ "parallel_write_safe" : True ,
90+ }
0 commit comments