summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2020-05-18 17:25:07 (GMT)
committerGitHub <noreply@github.com>2020-05-18 17:25:07 (GMT)
commit08b47c367a08f571a986366aa33828d3951fa88d (patch)
treebe06ca168536a48a19f2a093192ad7137ff8aa3c /Lib
parent98e42d1f882b9b59f587d538c562dbc7d11c64c3 (diff)
downloadcpython-08b47c367a08f571a986366aa33828d3951fa88d.zip
cpython-08b47c367a08f571a986366aa33828d3951fa88d.tar.gz
cpython-08b47c367a08f571a986366aa33828d3951fa88d.tar.bz2
bpo-40257: Revert changes to inspect.getdoc() (GH-20073)
Diffstat (limited to 'Lib')
-rw-r--r--Lib/inspect.py33
-rwxr-xr-xLib/pydoc.py96
-rw-r--r--Lib/test/test_inspect.py15
3 files changed, 113 insertions, 31 deletions
diff --git a/Lib/inspect.py b/Lib/inspect.py
index ad7e8cb..887a342 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -543,6 +543,17 @@ def _findclass(func):
return cls
def _finddoc(obj):
+ if isclass(obj):
+ for base in obj.__mro__:
+ if base is not object:
+ try:
+ doc = base.__doc__
+ except AttributeError:
+ continue
+ if doc is not None:
+ return doc
+ return None
+
if ismethod(obj):
name = obj.__func__.__name__
self = obj.__self__
@@ -586,35 +597,23 @@ def _finddoc(obj):
return None
for base in cls.__mro__:
try:
- doc = _getowndoc(getattr(base, name))
+ doc = getattr(base, name).__doc__
except AttributeError:
continue
if doc is not None:
return doc
return None
-def _getowndoc(obj):
- """Get the documentation string for an object if it is not
- inherited from its class."""
- try:
- doc = object.__getattribute__(obj, '__doc__')
- if doc is None:
- return None
- if obj is not type:
- typedoc = type(obj).__doc__
- if isinstance(typedoc, str) and typedoc == doc:
- return None
- return doc
- except AttributeError:
- return None
-
def getdoc(object):
"""Get the documentation string for an object.
All tabs are expanded to spaces. To clean up docstrings that are
indented to line up with blocks of code, any whitespace than can be
uniformly removed from the second line onwards is removed."""
- doc = _getowndoc(object)
+ try:
+ doc = object.__doc__
+ except AttributeError:
+ return None
if doc is None:
try:
doc = _finddoc(object)
diff --git a/Lib/pydoc.py b/Lib/pydoc.py
index 898cc44..628f9fc 100755
--- a/Lib/pydoc.py
+++ b/Lib/pydoc.py
@@ -90,9 +90,101 @@ def pathdirs():
normdirs.append(normdir)
return dirs
+def _findclass(func):
+ cls = sys.modules.get(func.__module__)
+ if cls is None:
+ return None
+ for name in func.__qualname__.split('.')[:-1]:
+ cls = getattr(cls, name)
+ if not inspect.isclass(cls):
+ return None
+ return cls
+
+def _finddoc(obj):
+ if inspect.ismethod(obj):
+ name = obj.__func__.__name__
+ self = obj.__self__
+ if (inspect.isclass(self) and
+ getattr(getattr(self, name, None), '__func__') is obj.__func__):
+ # classmethod
+ cls = self
+ else:
+ cls = self.__class__
+ elif inspect.isfunction(obj):
+ name = obj.__name__
+ cls = _findclass(obj)
+ if cls is None or getattr(cls, name) is not obj:
+ return None
+ elif inspect.isbuiltin(obj):
+ name = obj.__name__
+ self = obj.__self__
+ if (inspect.isclass(self) and
+ self.__qualname__ + '.' + name == obj.__qualname__):
+ # classmethod
+ cls = self
+ else:
+ cls = self.__class__
+ # Should be tested before isdatadescriptor().
+ elif isinstance(obj, property):
+ func = obj.fget
+ name = func.__name__
+ cls = _findclass(func)
+ if cls is None or getattr(cls, name) is not obj:
+ return None
+ elif inspect.ismethoddescriptor(obj) or inspect.isdatadescriptor(obj):
+ name = obj.__name__
+ cls = obj.__objclass__
+ if getattr(cls, name) is not obj:
+ return None
+ if inspect.ismemberdescriptor(obj):
+ slots = getattr(cls, '__slots__', None)
+ if isinstance(slots, dict) and name in slots:
+ return slots[name]
+ else:
+ return None
+ for base in cls.__mro__:
+ try:
+ doc = _getowndoc(getattr(base, name))
+ except AttributeError:
+ continue
+ if doc is not None:
+ return doc
+ return None
+
+def _getowndoc(obj):
+ """Get the documentation string for an object if it is not
+ inherited from its class."""
+ try:
+ doc = object.__getattribute__(obj, '__doc__')
+ if doc is None:
+ return None
+ if obj is not type:
+ typedoc = type(obj).__doc__
+ if isinstance(typedoc, str) and typedoc == doc:
+ return None
+ return doc
+ except AttributeError:
+ return None
+
+def _getdoc(object):
+ """Get the documentation string for an object.
+
+ All tabs are expanded to spaces. To clean up docstrings that are
+ indented to line up with blocks of code, any whitespace than can be
+ uniformly removed from the second line onwards is removed."""
+ doc = _getowndoc(object)
+ if doc is None:
+ try:
+ doc = _finddoc(object)
+ except (AttributeError, TypeError):
+ return None
+ if not isinstance(doc, str):
+ return None
+ return inspect.cleandoc(doc)
+
def getdoc(object):
"""Get the doc string or comments for an object."""
- result = inspect.getdoc(object) or inspect.getcomments(object)
+ result = _getdoc(object) or inspect.getcomments(object)
return result and re.sub('^ *\n', '', result.rstrip()) or ''
def splitdoc(doc):
@@ -1669,7 +1761,7 @@ def render_doc(thing, title='Python Library Documentation: %s', forceload=0,
inspect.isclass(object) or
inspect.isroutine(object) or
inspect.isdatadescriptor(object) or
- inspect.getdoc(object)):
+ _getdoc(object)):
# If the passed object is a piece of data or an instance,
# document its available methods instead of its value.
if hasattr(object, '__origin__'):
diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py
index 98a9c0a..e3e2be5 100644
--- a/Lib/test/test_inspect.py
+++ b/Lib/test/test_inspect.py
@@ -439,7 +439,8 @@ class TestRetrievingSourceCode(GetSourceBase):
@unittest.skipIf(sys.flags.optimize >= 2,
"Docstrings are omitted with -O2 and above")
def test_getdoc_inherited(self):
- self.assertIsNone(inspect.getdoc(mod.FesteringGob))
+ self.assertEqual(inspect.getdoc(mod.FesteringGob),
+ 'A longer,\n\nindented\n\ndocstring.')
self.assertEqual(inspect.getdoc(mod.FesteringGob.abuse),
'Another\n\ndocstring\n\ncontaining\n\ntabs')
self.assertEqual(inspect.getdoc(mod.FesteringGob().abuse),
@@ -448,19 +449,9 @@ class TestRetrievingSourceCode(GetSourceBase):
'The automatic gainsaying.')
@unittest.skipIf(MISSING_C_DOCSTRINGS, "test requires docstrings")
- def test_getowndoc(self):
- getowndoc = inspect._getowndoc
- self.assertEqual(getowndoc(type), type.__doc__)
- self.assertEqual(getowndoc(int), int.__doc__)
- self.assertEqual(getowndoc(int.to_bytes), int.to_bytes.__doc__)
- self.assertEqual(getowndoc(int().to_bytes), int.to_bytes.__doc__)
- self.assertEqual(getowndoc(int.from_bytes), int.from_bytes.__doc__)
- self.assertEqual(getowndoc(int.real), int.real.__doc__)
-
- @unittest.skipIf(MISSING_C_DOCSTRINGS, "test requires docstrings")
def test_finddoc(self):
finddoc = inspect._finddoc
- self.assertIsNone(finddoc(int))
+ self.assertEqual(finddoc(int), int.__doc__)
self.assertEqual(finddoc(int.to_bytes), int.to_bytes.__doc__)
self.assertEqual(finddoc(int().to_bytes), int.to_bytes.__doc__)
self.assertEqual(finddoc(int.from_bytes), int.from_bytes.__doc__)