88from __future__ import annotations
99
1010import re
11+ import sys
1112from inspect import Parameter , Signature
1213from types import ModuleType
1314from typing import (TYPE_CHECKING , Any , Callable , Iterator , List , Sequence , Tuple , TypeVar ,
@@ -1420,6 +1421,11 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
14201421 'class-doc-from' : class_doc_from_option ,
14211422 }
14221423
1424+ # Must be higher than FunctionDocumenter, ClassDocumenter, and
1425+ # AttributeDocumenter as NewType can be an attribute and is a class
1426+ # after Python 3.10. Before 3.10 it is a kind of function object
1427+ priority = 15
1428+
14231429 _signature_class : Any = None
14241430 _signature_method_name : str = None
14251431
@@ -1441,7 +1447,8 @@ def __init__(self, *args: Any) -> None:
14411447 @classmethod
14421448 def can_document_member (cls , member : Any , membername : str , isattr : bool , parent : Any
14431449 ) -> bool :
1444- return isinstance (member , type )
1450+ return isinstance (member , type ) or (
1451+ isattr and (inspect .isNewType (member ) or isinstance (member , TypeVar )))
14451452
14461453 def import_object (self , raiseerror : bool = False ) -> bool :
14471454 ret = super ().import_object (raiseerror )
@@ -1452,9 +1459,19 @@ def import_object(self, raiseerror: bool = False) -> bool:
14521459 self .doc_as_attr = (self .objpath [- 1 ] != self .object .__name__ )
14531460 else :
14541461 self .doc_as_attr = True
1462+ if inspect .isNewType (self .object ) or isinstance (self .object , TypeVar ):
1463+ modname = getattr (self .object , '__module__' , self .modname )
1464+ if modname != self .modname and self .modname .startswith (modname ):
1465+ bases = self .modname [len (modname ):].strip ('.' ).split ('.' )
1466+ self .objpath = bases + self .objpath
1467+ self .modname = modname
14551468 return ret
14561469
14571470 def _get_signature (self ) -> tuple [Any | None , str | None , Signature | None ]:
1471+ if inspect .isNewType (self .object ) or isinstance (self .object , TypeVar ):
1472+ # Supress signature
1473+ return None , None , None
1474+
14581475 def get_user_defined_function_or_method (obj : Any , attr : str ) -> Any :
14591476 """ Get the `attr` function or method from `obj`, if it is user-defined. """
14601477 if inspect .is_builtin_class_method (obj , attr ):
@@ -1635,11 +1652,15 @@ def add_directive_header(self, sig: str) -> None:
16351652 self .directivetype = 'attribute'
16361653 super ().add_directive_header (sig )
16371654
1655+ if inspect .isNewType (self .object ) or isinstance (self .object , TypeVar ):
1656+ return
1657+
16381658 if self .analyzer and '.' .join (self .objpath ) in self .analyzer .finals :
16391659 self .add_line (' :final:' , sourcename )
16401660
16411661 canonical_fullname = self .get_canonical_fullname ()
1642- if not self .doc_as_attr and canonical_fullname and self .fullname != canonical_fullname :
1662+ if (not self .doc_as_attr and not inspect .isNewType (self .object )
1663+ and canonical_fullname and self .fullname != canonical_fullname ):
16431664 self .add_line (' :canonical: %s' % canonical_fullname , sourcename )
16441665
16451666 # add inheritance info, if wanted
@@ -1687,6 +1708,27 @@ def get_object_members(self, want_all: bool) -> tuple[bool, ObjectMembers]:
16871708 return False , [m for m in members .values () if m .class_ == self .object ]
16881709
16891710 def get_doc (self ) -> list [list [str ]] | None :
1711+ if isinstance (self .object , TypeVar ):
1712+ if self .object .__doc__ == TypeVar .__doc__ :
1713+ return []
1714+ if sys .version_info [:2 ] < (3 , 10 ):
1715+ if inspect .isNewType (self .object ) or isinstance (self .object , TypeVar ):
1716+ parts = self .modname .strip ('.' ).split ('.' )
1717+ orig_objpath = self .objpath
1718+ for i in range (len (parts )):
1719+ new_modname = '.' .join (parts [:len (parts ) - i ])
1720+ new_objpath = parts [len (parts ) - i :] + orig_objpath
1721+ try :
1722+ analyzer = ModuleAnalyzer .for_module (new_modname )
1723+ analyzer .analyze ()
1724+ key = ('' , new_objpath [- 1 ])
1725+ comment = list (analyzer .attr_docs .get (key , []))
1726+ if comment :
1727+ self .objpath = new_objpath
1728+ self .modname = new_modname
1729+ return [comment ]
1730+ except PycodeError :
1731+ pass
16901732 if self .doc_as_attr :
16911733 # Don't show the docstring of the class when it is an alias.
16921734 comment = self .get_variable_comment ()
@@ -1751,6 +1793,35 @@ def get_variable_comment(self) -> list[str] | None:
17511793 return None
17521794
17531795 def add_content (self , more_content : StringList | None ) -> None :
1796+ if inspect .isNewType (self .object ):
1797+ if self .config .autodoc_typehints_format == "short" :
1798+ supertype = restify (self .object .__supertype__ , "smart" )
1799+ else :
1800+ supertype = restify (self .object .__supertype__ )
1801+
1802+ more_content = StringList ([_ ('alias of %s' ) % supertype , '' ], source = '' )
1803+ if isinstance (self .object , TypeVar ):
1804+ attrs = [repr (self .object .__name__ )]
1805+ for constraint in self .object .__constraints__ :
1806+ if self .config .autodoc_typehints_format == "short" :
1807+ attrs .append (stringify_annotation (constraint , "smart" ))
1808+ else :
1809+ attrs .append (stringify_annotation (constraint ))
1810+ if self .object .__bound__ :
1811+ if self .config .autodoc_typehints_format == "short" :
1812+ bound = restify (self .object .__bound__ , "smart" )
1813+ else :
1814+ bound = restify (self .object .__bound__ )
1815+ attrs .append (r"bound=\ " + bound )
1816+ if self .object .__covariant__ :
1817+ attrs .append ("covariant=True" )
1818+ if self .object .__contravariant__ :
1819+ attrs .append ("contravariant=True" )
1820+
1821+ more_content = StringList (
1822+ [_ ('alias of TypeVar(%s)' ) % ", " .join (attrs ), '' ],
1823+ source = ''
1824+ )
17541825 if self .doc_as_attr and self .modname != self .get_real_modname ():
17551826 try :
17561827 # override analyzer to obtain doccomment around its definition.
@@ -1801,7 +1872,7 @@ class ExceptionDocumenter(ClassDocumenter):
18011872 member_order = 10
18021873
18031874 # needs a higher priority than ClassDocumenter
1804- priority = 10
1875+ priority = ClassDocumenter . priority + 5
18051876
18061877 @classmethod
18071878 def can_document_member (cls , member : Any , membername : str , isattr : bool , parent : Any
@@ -1827,7 +1898,7 @@ def should_suppress_value_header(self) -> bool:
18271898 return False
18281899
18291900 def update_content (self , more_content : StringList ) -> None :
1830- """Update docstring for the NewType object ."""
1901+ """Update docstring, for example with TypeVar variance ."""
18311902 pass
18321903
18331904
@@ -1854,74 +1925,6 @@ def update_content(self, more_content: StringList) -> None:
18541925 super ().update_content (more_content )
18551926
18561927
1857- class NewTypeMixin (DataDocumenterMixinBase ):
1858- """
1859- Mixin for DataDocumenter and AttributeDocumenter to provide the feature for
1860- supporting NewTypes.
1861- """
1862-
1863- def should_suppress_directive_header (self ) -> bool :
1864- return (inspect .isNewType (self .object ) or
1865- super ().should_suppress_directive_header ())
1866-
1867- def update_content (self , more_content : StringList ) -> None :
1868- if inspect .isNewType (self .object ):
1869- if self .config .autodoc_typehints_format == "short" :
1870- supertype = restify (self .object .__supertype__ , "smart" )
1871- else :
1872- supertype = restify (self .object .__supertype__ )
1873-
1874- more_content .append (_ ('alias of %s' ) % supertype , '' )
1875- more_content .append ('' , '' )
1876-
1877- super ().update_content (more_content )
1878-
1879-
1880- class TypeVarMixin (DataDocumenterMixinBase ):
1881- """
1882- Mixin for DataDocumenter and AttributeDocumenter to provide the feature for
1883- supporting TypeVars.
1884- """
1885-
1886- def should_suppress_directive_header (self ) -> bool :
1887- return (isinstance (self .object , TypeVar ) or
1888- super ().should_suppress_directive_header ())
1889-
1890- def get_doc (self ) -> list [list [str ]] | None :
1891- if isinstance (self .object , TypeVar ):
1892- if self .object .__doc__ != TypeVar .__doc__ :
1893- return super ().get_doc () # type: ignore
1894- else :
1895- return []
1896- else :
1897- return super ().get_doc () # type: ignore
1898-
1899- def update_content (self , more_content : StringList ) -> None :
1900- if isinstance (self .object , TypeVar ):
1901- attrs = [repr (self .object .__name__ )]
1902- for constraint in self .object .__constraints__ :
1903- if self .config .autodoc_typehints_format == "short" :
1904- attrs .append (stringify_annotation (constraint , "smart" ))
1905- else :
1906- attrs .append (stringify_annotation (constraint ,
1907- "fully-qualified-except-typing" ))
1908- if self .object .__bound__ :
1909- if self .config .autodoc_typehints_format == "short" :
1910- bound = restify (self .object .__bound__ , "smart" )
1911- else :
1912- bound = restify (self .object .__bound__ )
1913- attrs .append (r"bound=\ " + bound )
1914- if self .object .__covariant__ :
1915- attrs .append ("covariant=True" )
1916- if self .object .__contravariant__ :
1917- attrs .append ("contravariant=True" )
1918-
1919- more_content .append (_ ('alias of TypeVar(%s)' ) % ", " .join (attrs ), '' )
1920- more_content .append ('' , '' )
1921-
1922- super ().update_content (more_content )
1923-
1924-
19251928class UninitializedGlobalVariableMixin (DataDocumenterMixinBase ):
19261929 """
19271930 Mixin for DataDocumenter to provide the feature for supporting uninitialized
@@ -1963,7 +1966,7 @@ def get_doc(self) -> list[list[str]] | None:
19631966 return super ().get_doc () # type: ignore
19641967
19651968
1966- class DataDocumenter (GenericAliasMixin , NewTypeMixin , TypeVarMixin ,
1969+ class DataDocumenter (GenericAliasMixin ,
19671970 UninitializedGlobalVariableMixin , ModuleLevelDocumenter ):
19681971 """
19691972 Specialized Documenter subclass for data items.
@@ -2083,24 +2086,6 @@ def add_content(self, more_content: StringList | None) -> None:
20832086 super ().add_content (more_content )
20842087
20852088
2086- class NewTypeDataDocumenter (DataDocumenter ):
2087- """
2088- Specialized Documenter subclass for NewTypes.
2089-
2090- Note: This must be invoked before FunctionDocumenter because NewType is a kind of
2091- function object.
2092- """
2093-
2094- objtype = 'newtypedata'
2095- directivetype = 'data'
2096- priority = FunctionDocumenter .priority + 1
2097-
2098- @classmethod
2099- def can_document_member (cls , member : Any , membername : str , isattr : bool , parent : Any
2100- ) -> bool :
2101- return inspect .isNewType (member ) and isattr
2102-
2103-
21042089class MethodDocumenter (DocstringSignatureMixin , ClassLevelDocumenter ): # type: ignore
21052090 """
21062091 Specialized Documenter subclass for methods (normal, static and class).
@@ -2520,8 +2505,8 @@ def get_doc(self) -> list[list[str]] | None:
25202505 return super ().get_doc () # type: ignore
25212506
25222507
2523- class AttributeDocumenter (GenericAliasMixin , NewTypeMixin , SlotsMixin , # type: ignore
2524- TypeVarMixin , RuntimeInstanceAttributeMixin ,
2508+ class AttributeDocumenter (GenericAliasMixin , SlotsMixin , # type: ignore
2509+ RuntimeInstanceAttributeMixin ,
25252510 UninitializedInstanceAttributeMixin , NonDataDescriptorMixin ,
25262511 DocstringStripSignatureMixin , ClassLevelDocumenter ):
25272512 """
@@ -2759,24 +2744,6 @@ def add_directive_header(self, sig: str) -> None:
27592744 return None
27602745
27612746
2762- class NewTypeAttributeDocumenter (AttributeDocumenter ):
2763- """
2764- Specialized Documenter subclass for NewTypes.
2765-
2766- Note: This must be invoked before MethodDocumenter because NewType is a kind of
2767- function object.
2768- """
2769-
2770- objtype = 'newvarattribute'
2771- directivetype = 'attribute'
2772- priority = MethodDocumenter .priority + 1
2773-
2774- @classmethod
2775- def can_document_member (cls , member : Any , membername : str , isattr : bool , parent : Any
2776- ) -> bool :
2777- return not isinstance (parent , ModuleDocumenter ) and inspect .isNewType (member )
2778-
2779-
27802747def autodoc_attrgetter (app : Sphinx , obj : Any , name : str , * defargs : Any ) -> Any :
27812748 """Alternative getattr() for types"""
27822749 for typ , func in app .registry .autodoc_attrgettrs .items ():
@@ -2791,13 +2758,11 @@ def setup(app: Sphinx) -> dict[str, Any]:
27912758 app .add_autodocumenter (ClassDocumenter )
27922759 app .add_autodocumenter (ExceptionDocumenter )
27932760 app .add_autodocumenter (DataDocumenter )
2794- app .add_autodocumenter (NewTypeDataDocumenter )
27952761 app .add_autodocumenter (FunctionDocumenter )
27962762 app .add_autodocumenter (DecoratorDocumenter )
27972763 app .add_autodocumenter (MethodDocumenter )
27982764 app .add_autodocumenter (AttributeDocumenter )
27992765 app .add_autodocumenter (PropertyDocumenter )
2800- app .add_autodocumenter (NewTypeAttributeDocumenter )
28012766
28022767 app .add_config_value ('autoclass_content' , 'class' , True , ENUM ('both' , 'class' , 'init' ))
28032768 app .add_config_value ('autodoc_member_order' , 'alphabetical' , True ,
0 commit comments