From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Sun, 14 Dec 2025 12:59:05 +0000 (+0000) Subject: gh-76007: `pydoc`: Catch `DeprecationWarning` for stdlib module `__version__` attribu... X-Git-Tag: v3.15.0a3~47 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=78a50ee10ef73296eba98899d496bff3915ddc41;p=thirdparty%2FPython%2Fcpython.git gh-76007: `pydoc`: Catch `DeprecationWarning` for stdlib module `__version__` attributes (#139997) --- diff --git a/Lib/pydoc.py b/Lib/pydoc.py index ee4457d9d3a9..c8d4aa7a8832 100644 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -450,6 +450,7 @@ class Doc: PYTHONDOCS = os.environ.get("PYTHONDOCS", "https://docs.python.org/%d.%d/library" % sys.version_info[:2]) + STDLIB_DIR = sysconfig.get_path('stdlib') def document(self, object, name=None, *args): """Generate documentation for an object.""" @@ -475,23 +476,12 @@ class Doc: docmodule = docclass = docroutine = docother = docproperty = docdata = fail - def getdocloc(self, object, basedir=sysconfig.get_path('stdlib')): + def getdocloc(self, object, basedir=None): """Return the location of module docs or None""" - - try: - file = inspect.getabsfile(object) - except TypeError: - file = '(built-in)' - + basedir = self.STDLIB_DIR if basedir is None else basedir docloc = os.environ.get("PYTHONDOCS", self.PYTHONDOCS) - basedir = os.path.normcase(basedir) - if (isinstance(object, type(os)) and - (object.__name__ in ('errno', 'exceptions', 'gc', - 'marshal', 'posix', 'signal', 'sys', - '_thread', 'zipimport') or - (file.startswith(basedir) and - not file.startswith(os.path.join(basedir, 'site-packages')))) and + if (self._is_stdlib_module(object, basedir) and object.__name__ not in ('xml.etree', 'test.test_pydoc.pydoc_mod')): if docloc.startswith(("http://", "https://")): docloc = "{}/{}.html".format(docloc.rstrip("/"), object.__name__.lower()) @@ -501,6 +491,36 @@ class Doc: docloc = None return docloc + def _get_version(self, object): + if self._is_stdlib_module(object): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + version = getattr(object, '__version__', None) + else: + version = getattr(object, '__version__', None) + return '' if version is None else str(version) + + def _is_stdlib_module(self, object, basedir=None): + basedir = self.STDLIB_DIR if basedir is None else basedir + + try: + file = inspect.getabsfile(object) + except TypeError: + file = '(built-in)' + + if sysconfig.is_python_build(): + srcdir = sysconfig.get_config_var('srcdir') + if srcdir: + basedir = os.path.join(srcdir, 'Lib') + + basedir = os.path.normcase(basedir) + return (isinstance(object, type(os)) and + (object.__name__ in ('errno', 'exceptions', 'gc', + 'marshal', 'posix', 'signal', 'sys', + '_thread', 'zipimport') + or (file.startswith(basedir) and + not file.startswith(os.path.join(basedir, 'site-packages'))))) + # -------------------------------------------- HTML documentation generator class HTMLRepr(Repr): @@ -760,8 +780,8 @@ class HTMLDoc(Doc): except TypeError: filelink = '(built-in)' info = [] - if hasattr(object, '__version__'): - version = str(object.__version__) + + if version := self._get_version(object): if version[:11] == '$' + 'Revision: ' and version[-1:] == '$': version = version[11:-1].strip() info.append('version %s' % self.escape(version)) @@ -1296,8 +1316,7 @@ location listed above. contents.append(self.docother(value, key, name, maxlen=70)) result = result + self.section('DATA', '\n'.join(contents)) - if hasattr(object, '__version__'): - version = str(object.__version__) + if version := self._get_version(object): if version[:11] == '$' + 'Revision: ' and version[-1:] == '$': version = version[11:-1].strip() result = result + self.section('VERSION', version) diff --git a/Lib/test/test_pydoc/test_pydoc.py b/Lib/test/test_pydoc/test_pydoc.py index c640416327ef..89480423d32b 100644 --- a/Lib/test/test_pydoc/test_pydoc.py +++ b/Lib/test/test_pydoc/test_pydoc.py @@ -2305,6 +2305,32 @@ class TestInternalUtilities(unittest.TestCase): trailing_argv0dir = trailing_curdir + [self.argv0dir] self.assertIsNone(self._get_revised_path(trailing_argv0dir)) + def test__get_version(self): + import json + import warnings + + class MyModule: + __name__ = 'my_module' + + @property + def __version__(self): + warnings._deprecated("__version__", remove=(3, 20)) + return "1.2.3" + + module = MyModule() + doc = pydoc.Doc() + with warnings.catch_warnings(record=True) as w: # TODO: remove in 3.20 + warnings.simplefilter("always") + version = doc._get_version(json) + self.assertEqual(version, "2.0.9") + self.assertEqual(len(w), 0) + + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + version = doc._get_version(module) + self.assertEqual(version, "1.2.3") + self.assertEqual(len(w), 1) + def setUpModule(): thread_info = threading_helper.threading_setup() diff --git a/Misc/NEWS.d/next/Library/2025-10-12-12-43-56.gh-issue-76007.PyGM14.rst b/Misc/NEWS.d/next/Library/2025-10-12-12-43-56.gh-issue-76007.PyGM14.rst new file mode 100644 index 000000000000..3a0914f38228 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-12-12-43-56.gh-issue-76007.PyGM14.rst @@ -0,0 +1,2 @@ +:mod:`pydoc`: Fix :exc:`DeprecationWarning` being raised when generating doc for +:term:`stdlib` modules.