@@ -225,6 +225,19 @@ def classname(object, modname):
225225 name = object .__module__ + '.' + name
226226 return name
227227
228+ def parentname (object , modname ):
229+ """Get a name of the enclosing class (qualified it with a module name
230+ if necessary) or module."""
231+ if '.' in object .__qualname__ :
232+ name = object .__qualname__ .rpartition ('.' )[0 ]
233+ if object .__module__ != modname :
234+ return object .__module__ + '.' + name
235+ else :
236+ return name
237+ else :
238+ if object .__module__ != modname :
239+ return object .__module__
240+
228241def isdata (object ):
229242 """Check if an object is of a type that probably means it's data."""
230243 return not (inspect .ismodule (object ) or inspect .isclass (object ) or
@@ -319,13 +332,15 @@ def visiblename(name, all=None, obj=None):
319332 return not name .startswith ('_' )
320333
321334def classify_class_attrs (object ):
322- """Wrap inspect.classify_class_attrs, with fixup for data descriptors."""
335+ """Wrap inspect.classify_class_attrs, with fixup for data descriptors and bound methods ."""
323336 results = []
324337 for (name , kind , cls , value ) in inspect .classify_class_attrs (object ):
325338 if inspect .isdatadescriptor (value ):
326339 kind = 'data descriptor'
327340 if isinstance (value , property ) and value .fset is None :
328341 kind = 'readonly property'
342+ elif kind == 'method' and _is_bound_method (value ):
343+ kind = 'static method'
329344 results .append ((name , kind , cls , value ))
330345 return results
331346
@@ -681,6 +696,25 @@ def classlink(self, object, modname):
681696 module .__name__ , name , classname (object , modname ))
682697 return classname (object , modname )
683698
699+ def parentlink (self , object , modname ):
700+ """Make a link for the enclosing class or module."""
701+ link = None
702+ name , module = object .__name__ , sys .modules .get (object .__module__ )
703+ if hasattr (module , name ) and getattr (module , name ) is object :
704+ if '.' in object .__qualname__ :
705+ name = object .__qualname__ .rpartition ('.' )[0 ]
706+ if object .__module__ != modname :
707+ link = '%s.html#%s' % (module .__name__ , name )
708+ else :
709+ link = '#%s' % name
710+ else :
711+ if object .__module__ != modname :
712+ link = '%s.html' % module .__name__
713+ if link :
714+ return '<a href="%s">%s</a>' % (link , parentname (object , modname ))
715+ else :
716+ return parentname (object , modname )
717+
684718 def modulelink (self , object ):
685719 """Make a link for a module."""
686720 return '<a href="%s.html">%s</a>' % (object .__name__ , object .__name__ )
@@ -925,7 +959,7 @@ def spill(msg, attrs, predicate):
925959 push (self .docdata (value , name , mod ))
926960 else :
927961 push (self .document (value , name , mod ,
928- funcs , classes , mdict , object ))
962+ funcs , classes , mdict , object , homecls ))
929963 push ('\n ' )
930964 return attrs
931965
@@ -1043,24 +1077,44 @@ def formatvalue(self, object):
10431077 return self .grey ('=' + self .repr (object ))
10441078
10451079 def docroutine (self , object , name = None , mod = None ,
1046- funcs = {}, classes = {}, methods = {}, cl = None ):
1080+ funcs = {}, classes = {}, methods = {}, cl = None , homecls = None ):
10471081 """Produce HTML documentation for a function or method object."""
10481082 realname = object .__name__
10491083 name = name or realname
1050- anchor = (cl and cl .__name__ or '' ) + '-' + name
1084+ if homecls is None :
1085+ homecls = cl
1086+ anchor = ('' if cl is None else cl .__name__ ) + '-' + name
10511087 note = ''
1052- skipdocs = 0
1088+ skipdocs = False
1089+ imfunc = None
10531090 if _is_bound_method (object ):
1054- imclass = object .__self__ .__class__
1055- if cl :
1056- if imclass is not cl :
1057- note = ' from ' + self .classlink (imclass , mod )
1091+ imself = object .__self__
1092+ if imself is cl :
1093+ imfunc = getattr (object , '__func__' , None )
1094+ elif inspect .isclass (imself ):
1095+ note = ' class method of %s' % self .classlink (imself , mod )
10581096 else :
1059- if object .__self__ is not None :
1060- note = ' method of %s instance' % self .classlink (
1061- object .__self__ .__class__ , mod )
1062- else :
1063- note = ' unbound %s method' % self .classlink (imclass ,mod )
1097+ note = ' method of %s instance' % self .classlink (
1098+ imself .__class__ , mod )
1099+ elif (inspect .ismethoddescriptor (object ) or
1100+ inspect .ismethodwrapper (object )):
1101+ try :
1102+ objclass = object .__objclass__
1103+ except AttributeError :
1104+ pass
1105+ else :
1106+ if cl is None :
1107+ note = ' unbound %s method' % self .classlink (objclass , mod )
1108+ elif objclass is not homecls :
1109+ note = ' from ' + self .classlink (objclass , mod )
1110+ else :
1111+ imfunc = object
1112+ if inspect .isfunction (imfunc ) and homecls is not None and (
1113+ imfunc .__module__ != homecls .__module__ or
1114+ imfunc .__qualname__ != homecls .__qualname__ + '.' + realname ):
1115+ pname = self .parentlink (imfunc , mod )
1116+ if pname :
1117+ note = ' from %s' % pname
10641118
10651119 if (inspect .iscoroutinefunction (object ) or
10661120 inspect .isasyncgenfunction (object )):
@@ -1071,10 +1125,13 @@ def docroutine(self, object, name=None, mod=None,
10711125 if name == realname :
10721126 title = '<a name="%s"><strong>%s</strong></a>' % (anchor , realname )
10731127 else :
1074- if cl and inspect .getattr_static (cl , realname , []) is object :
1128+ if (cl is not None and
1129+ inspect .getattr_static (cl , realname , []) is object ):
10751130 reallink = '<a href="#%s">%s</a>' % (
10761131 cl .__name__ + '-' + realname , realname )
1077- skipdocs = 1
1132+ skipdocs = True
1133+ if note .startswith (' from ' ):
1134+ note = ''
10781135 else :
10791136 reallink = realname
10801137 title = '<a name="%s"><strong>%s</strong></a> = %s' % (
@@ -1102,7 +1159,7 @@ def docroutine(self, object, name=None, mod=None,
11021159 doc = doc and '<dd><span class="code">%s</span></dd>' % doc
11031160 return '<dl><dt>%s</dt>%s</dl>\n ' % (decl , doc )
11041161
1105- def docdata (self , object , name = None , mod = None , cl = None ):
1162+ def docdata (self , object , name = None , mod = None , cl = None , * ignored ):
11061163 """Produce html documentation for a data descriptor."""
11071164 results = []
11081165 push = results .append
@@ -1213,7 +1270,7 @@ def formattree(self, tree, modname, parent=None, prefix=''):
12131270 entry , modname , c , prefix + ' ' )
12141271 return result
12151272
1216- def docmodule (self , object , name = None , mod = None ):
1273+ def docmodule (self , object , name = None , mod = None , * ignored ):
12171274 """Produce text documentation for a given module object."""
12181275 name = object .__name__ # ignore the passed-in name
12191276 synop , desc = splitdoc (getdoc (object ))
@@ -1392,7 +1449,7 @@ def spill(msg, attrs, predicate):
13921449 push (self .docdata (value , name , mod ))
13931450 else :
13941451 push (self .document (value ,
1395- name , mod , object ))
1452+ name , mod , object , homecls ))
13961453 return attrs
13971454
13981455 def spilldescriptors (msg , attrs , predicate ):
@@ -1467,23 +1524,43 @@ def formatvalue(self, object):
14671524 """Format an argument default value as text."""
14681525 return '=' + self .repr (object )
14691526
1470- def docroutine (self , object , name = None , mod = None , cl = None ):
1527+ def docroutine (self , object , name = None , mod = None , cl = None , homecls = None ):
14711528 """Produce text documentation for a function or method object."""
14721529 realname = object .__name__
14731530 name = name or realname
1531+ if homecls is None :
1532+ homecls = cl
14741533 note = ''
1475- skipdocs = 0
1534+ skipdocs = False
1535+ imfunc = None
14761536 if _is_bound_method (object ):
1477- imclass = object .__self__ .__class__
1478- if cl :
1479- if imclass is not cl :
1480- note = ' from ' + classname (imclass , mod )
1537+ imself = object .__self__
1538+ if imself is cl :
1539+ imfunc = getattr (object , '__func__' , None )
1540+ elif inspect .isclass (imself ):
1541+ note = ' class method of %s' % classname (imself , mod )
14811542 else :
1482- if object .__self__ is not None :
1483- note = ' method of %s instance' % classname (
1484- object .__self__ .__class__ , mod )
1485- else :
1486- note = ' unbound %s method' % classname (imclass ,mod )
1543+ note = ' method of %s instance' % classname (
1544+ imself .__class__ , mod )
1545+ elif (inspect .ismethoddescriptor (object ) or
1546+ inspect .ismethodwrapper (object )):
1547+ try :
1548+ objclass = object .__objclass__
1549+ except AttributeError :
1550+ pass
1551+ else :
1552+ if cl is None :
1553+ note = ' unbound %s method' % classname (objclass , mod )
1554+ elif objclass is not homecls :
1555+ note = ' from ' + classname (objclass , mod )
1556+ else :
1557+ imfunc = object
1558+ if inspect .isfunction (imfunc ) and homecls is not None and (
1559+ imfunc .__module__ != homecls .__module__ or
1560+ imfunc .__qualname__ != homecls .__qualname__ + '.' + realname ):
1561+ pname = parentname (imfunc , mod )
1562+ if pname :
1563+ note = ' from %s' % pname
14871564
14881565 if (inspect .iscoroutinefunction (object ) or
14891566 inspect .isasyncgenfunction (object )):
@@ -1494,8 +1571,11 @@ def docroutine(self, object, name=None, mod=None, cl=None):
14941571 if name == realname :
14951572 title = self .bold (realname )
14961573 else :
1497- if cl and inspect .getattr_static (cl , realname , []) is object :
1498- skipdocs = 1
1574+ if (cl is not None and
1575+ inspect .getattr_static (cl , realname , []) is object ):
1576+ skipdocs = True
1577+ if note .startswith (' from ' ):
1578+ note = ''
14991579 title = self .bold (name ) + ' = ' + realname
15001580 argspec = None
15011581
@@ -1517,7 +1597,7 @@ def docroutine(self, object, name=None, mod=None, cl=None):
15171597 doc = getdoc (object ) or ''
15181598 return decl + '\n ' + (doc and self .indent (doc ).rstrip () + '\n ' )
15191599
1520- def docdata (self , object , name = None , mod = None , cl = None ):
1600+ def docdata (self , object , name = None , mod = None , cl = None , * ignored ):
15211601 """Produce text documentation for a data descriptor."""
15221602 results = []
15231603 push = results .append
@@ -1533,7 +1613,8 @@ def docdata(self, object, name=None, mod=None, cl=None):
15331613
15341614 docproperty = docdata
15351615
1536- def docother (self , object , name = None , mod = None , parent = None , maxlen = None , doc = None ):
1616+ def docother (self , object , name = None , mod = None , parent = None , * ignored ,
1617+ maxlen = None , doc = None ):
15371618 """Produce text documentation for a data object."""
15381619 repr = self .repr (object )
15391620 if maxlen :
0 commit comments