Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2835549

Browse files
committed
Part of a partial solution to SF bugs 463378, 463381, 463383, 463384.
This almost entirely replaces how pydoc pumps out class docs, but only in text mode (like help(whatever) from a Python shell), not in GUI mode. A class C's attrs are now grouped by the class in which they're defined, attrs defined by C first, then inherited attrs grouped by alphabetic order of the defining classes' names. Within each of those groups, the attrs are subgrouped according to whether they're plain methods, class methods, static methods, properties, or data. Note that pydoc never dumped class data attrs before. If a class data attr is implemented via a data descriptor, the data docstring (if any) is also displayed (e.g., file.softspace). Within a subgroup, the attrs are listed alphabetically. This is a friggin' mess, and there are bound to be glitches. Please beat on it and complain! Here are three glitches: 1. __new__ gets classifed as 'data', for some reason. This will have to get fixed in inspect.py, but since the latter is already looking for any clue that something is a method, pydoc will almost certainly not know what to do with it when its classification changes. 2. properties are special-cased to death. Unlike any other kind of function or method, they don't have a __name__ attr, so none of pydoc's usual code can deal with them. Worse, the getter and setter and del'er methods associated with a property don't appear to be discoverable from Python, so there's really nothing I can think of to do here beyond just listing their names. Note that a property can't be given a docstring, either (or at least I've been unable to sneak one in) -- perhaps the property() constructor could take an optional doc argument? 3. In a nested-scopes world, pydoc still doesn't know anything about nesting, so e.g. classes nested in functions are effectively invisible.
1 parent 894258c commit 2835549

1 file changed

Lines changed: 87 additions & 8 deletions

File tree

Lib/pydoc.py

Lines changed: 87 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,16 @@ def allmethods(cl):
128128
methods[key] = getattr(cl, key)
129129
return methods
130130

131+
def _split_class_attrs(attrs, predicate):
132+
yes = []
133+
no = []
134+
for tuple in attrs:
135+
if predicate(tuple):
136+
yes.append(tuple)
137+
else:
138+
no.append(tuple)
139+
return yes, no
140+
131141
# ----------------------------------------------------- module manipulation
132142

133143
def ispackage(path):
@@ -876,13 +886,80 @@ def makename(c, m=object.__module__): return classname(c, m)
876886
title = title + '(%s)' % join(parents, ', ')
877887

878888
doc = getdoc(object)
879-
contents = doc and doc + '\n'
880-
methods = allmethods(object).items()
881-
methods.sort()
882-
for key, value in methods:
883-
contents = contents + '\n' + self.document(value, key, mod, object)
884-
885-
if not contents: return title + '\n'
889+
contents = doc and [doc + '\n'] or []
890+
push = contents.append
891+
892+
def spill(msg, attrs, predicate):
893+
ok, attrs = _split_class_attrs(attrs, predicate)
894+
if ok:
895+
push(msg)
896+
for name, kind, homecls, value in ok:
897+
push(self.document(getattr(object, name),
898+
name, mod, object))
899+
return attrs
900+
901+
# pydoc can't make any reasonable sense of properties on its own,
902+
# and it doesn't appear that the getter, setter and del'er methods
903+
# are discoverable. For now, just pump out their names.
904+
def spillproperties(msg, attrs):
905+
ok, attrs = _split_class_attrs(attrs, lambda t: t[1] == 'property')
906+
if ok:
907+
push(msg)
908+
for name, kind, homecls, value in ok:
909+
push(name + '\n')
910+
return attrs
911+
912+
def spilldata(msg, attrs):
913+
ok, attrs = _split_class_attrs(attrs, lambda t: t[1] == 'data')
914+
if ok:
915+
push(msg)
916+
for name, kind, homecls, value in ok:
917+
doc = getattr(value, "__doc__", None)
918+
push(self.docother(getattr(object, name),
919+
name, mod, 70, doc) + '\n')
920+
return attrs
921+
922+
attrs = inspect.classify_class_attrs(object)
923+
924+
# All attrs defined in this class come first.
925+
attrs, inherited = _split_class_attrs(attrs,
926+
lambda t: t[2] is object)
927+
# Sort inherited attrs by name of defining class.
928+
inherited.sort(lambda t1, t2: cmp(t1[2].__name__, t2[2].__name__))
929+
930+
thisclass = object
931+
while attrs or inherited:
932+
if thisclass is object:
933+
tag = "defined here"
934+
else:
935+
tag = "inherited from class %s" % classname(thisclass,
936+
object.__module__)
937+
938+
# Sort attrs by name.
939+
attrs.sort(lambda t1, t2: cmp(t1[0], t2[0]))
940+
941+
# Pump out the attrs, segregated by kind.
942+
attrs = spill("Methods %s:\n" % tag, attrs,
943+
lambda t: t[1] == 'method')
944+
attrs = spill("Class methods %s:\n" % tag, attrs,
945+
lambda t: t[1] == 'class method')
946+
attrs = spill("Static methods %s:\n" % tag, attrs,
947+
lambda t: t[1] == 'static method')
948+
attrs = spillproperties("Properties %s:\n" % tag, attrs)
949+
attrs = spilldata("Data %s:\n" % tag, attrs)
950+
assert attrs == []
951+
952+
# Split off the attributes inherited from the next class (note
953+
# that inherited remains sorted by class name).
954+
if inherited:
955+
attrs = inherited
956+
thisclass = attrs[0][2]
957+
attrs, inherited = _split_class_attrs(attrs,
958+
lambda t: t[2] is thisclass)
959+
960+
contents = '\n'.join(contents)
961+
if not contents:
962+
return title + '\n'
886963
return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n'
887964

888965
def formatvalue(self, object):
@@ -933,14 +1010,16 @@ def docroutine(self, object, name=None, mod=None, cl=None):
9331010
doc = getdoc(object) or ''
9341011
return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n')
9351012

936-
def docother(self, object, name=None, mod=None, maxlen=None):
1013+
def docother(self, object, name=None, mod=None, maxlen=None, doc=None):
9371014
"""Produce text documentation for a data object."""
9381015
repr = self.repr(object)
9391016
if maxlen:
9401017
line = (name and name + ' = ' or '') + repr
9411018
chop = maxlen - len(line)
9421019
if chop < 0: repr = repr[:chop] + '...'
9431020
line = (name and self.bold(name) + ' = ' or '') + repr
1021+
if doc is not None:
1022+
line += '\n' + self.indent(str(doc))
9441023
return line
9451024

9461025
# --------------------------------------------------------- user interfaces

0 commit comments

Comments
 (0)