From 0f0b144e4715f3b9754b449d21871b5117492823 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Mon, 12 Oct 2026 18:23:45 +0100 Subject: [PATCH 1/3] Deprecate `__author__`, `__credits__` and `__copyright__` attributes in stdlib modules --- Doc/deprecations/pending-removal-in-3.20.rst | 21 ++++ Doc/whatsnew/3.16.rst | 23 ++++ Lib/bz2.py | 10 +- Lib/concurrent/futures/__init__.py | 12 ++- Lib/concurrent/futures/_base.py | 2 +- Lib/concurrent/futures/process.py | 10 +- Lib/concurrent/futures/thread.py | 10 +- Lib/ctypes/_aix.py | 2 +- Lib/inspect.py | 13 ++- Lib/io.py | 25 +++-- Lib/json/__init__.py | 14 +-- Lib/logging/__init__.py | 9 +- Lib/optparse.py | 100 ++++++++++++------ Lib/platform.py | 66 +++++++----- Lib/pydoc.py | 67 +++++++----- Lib/tarfile.py | 17 +-- Lib/test/test_bz2.py | 10 ++ .../test_concurrent_futures/test_future.py | 12 +++ Lib/test/test_inspect/test_inspect.py | 10 ++ Lib/test/test_io/test_general.py | 10 ++ Lib/test/test_json/__init__.py | 8 ++ Lib/test/test_logging.py | 4 +- Lib/test/test_optparse.py | 8 ++ Lib/test/test_platform.py | 10 ++ Lib/test/test_pydoc/test_pydoc.py | 18 +++- Lib/test/test_tarfile.py | 8 ++ Lib/test/test_tokenize.py | 10 ++ Lib/test/test_ttk/__init__.py | 8 ++ Lib/test/test_uuid.py | 10 ++ Lib/tkinter/ttk.py | 14 +-- Lib/tokenize.py | 21 +++- Lib/uuid.py | 11 +- ...-10-12-18-21-06.gh-issue-159240.a1b2c3.rst | 6 ++ 33 files changed, 443 insertions(+), 136 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-10-12-18-21-06.gh-issue-159240.a1b2c3.rst diff --git a/Doc/deprecations/pending-removal-in-3.20.rst b/Doc/deprecations/pending-removal-in-3.20.rst index d771764502c9103..fbb1bc00171eaa9 100644 --- a/Doc/deprecations/pending-removal-in-3.20.rst +++ b/Doc/deprecations/pending-removal-in-3.20.rst @@ -39,6 +39,27 @@ Pending removal in Python 3.20 (Contributed by Hugo van Kemenade and Stan Ulbrych in :gh:`76007`.) +* The ``__author__``, ``__credits__``, ``__copyright__`` and ``__date__`` + attributes have been deprecated in these standard library modules and will + be removed in Python 3.20. Attribution is preserved in the source code + and the version control history. + + - :mod:`bz2` + - :mod:`concurrent.futures` + - :mod:`inspect` + - :mod:`io` + - :mod:`json` + - :mod:`logging` + - :mod:`optparse` + - :mod:`platform` + - :mod:`pydoc` + - :mod:`tarfile` + - :mod:`tkinter.ttk` + - :mod:`tokenize` + - :mod:`uuid` + + (Contributed by Stan Ulbrych in :gh:`159240`.) + * Deprecations defined by :pep:`829`: * Warnings are produced for ``import`` lines found in :file:`{name}.pth` diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst index 759651d61359812..417eb746958a4d3 100644 --- a/Doc/whatsnew/3.16.rst +++ b/Doc/whatsnew/3.16.rst @@ -1037,6 +1037,29 @@ New deprecations 3.9, now issues a deprecation warning on use. This property is slated for removal in 3.21. Use ``ast.Tuple.elts`` instead. +* ``__author__``, ``__credits__``, ``__copyright__`` and ``__date__``: + + * The ``__author__``, ``__credits__``, ``__copyright__`` and ``__date__`` + attributes have been deprecated in these standard library modules and will + be removed in Python 3.20. Attribution is preserved in the source code + and the version control history. + + - :mod:`bz2` + - :mod:`concurrent.futures` + - :mod:`inspect` + - :mod:`io` + - :mod:`json` + - :mod:`logging` + - :mod:`optparse` + - :mod:`platform` + - :mod:`pydoc` + - :mod:`tarfile` + - :mod:`tkinter.ttk` + - :mod:`tokenize` + - :mod:`uuid` + + (Contributed by Stan Ulbrych in :gh:`159240`.) + * :mod:`struct`: * The ``'F'`` and ``'D'`` type codes, soft-deprecated since Python 3.15, diff --git a/Lib/bz2.py b/Lib/bz2.py index 79305669efc38c9..bcfc5ceb6dc3458 100644 --- a/Lib/bz2.py +++ b/Lib/bz2.py @@ -8,12 +8,13 @@ "open", "compress", "decompress", "bzlib_version", "bzlib_version_info"] -__author__ = "Nadeem Vawda " +# Original author: Nadeem Vawda from builtins import open as _builtin_open from compression._common import _streams import io import os +lazy import warnings from _bz2 import (BZ2Compressor, BZ2Decompressor, bzlib_version, bzlib_version_info) @@ -352,3 +353,10 @@ def decompress(data): "end-of-stream marker was reached") data = decomp.unused_data return b"".join(results) + + +def __getattr__(name): + if name == "__author__": + warnings._deprecated("__author__", remove=(3, 20)) + return "Nadeem Vawda " # Do not change + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/Lib/concurrent/futures/__init__.py b/Lib/concurrent/futures/__init__.py index 5d53a5ac50931d3..6b0536cc3c2bb16 100644 --- a/Lib/concurrent/futures/__init__.py +++ b/Lib/concurrent/futures/__init__.py @@ -3,7 +3,7 @@ """Execute computations asynchronously using threads or processes.""" -__author__ = 'Brian Quinlan (brian@sweetapp.com)' +# Original author: Brian Quinlan (brian@sweetapp.com) from concurrent.futures._base import (FIRST_COMPLETED, FIRST_EXCEPTION, @@ -17,6 +17,7 @@ wait, as_completed) +lazy import warnings lazy from .process import ProcessPoolExecutor lazy from .thread import ThreadPoolExecutor @@ -48,4 +49,11 @@ def __dir__(): - return __all__ + ['__author__', '__doc__'] + return __all__ + ['__doc__'] + + +def __getattr__(name): + if name == "__author__": + warnings._deprecated("__author__", remove=(3, 20)) + return 'Brian Quinlan (brian@sweetapp.com)' # Do not change + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/Lib/concurrent/futures/_base.py b/Lib/concurrent/futures/_base.py index e728b8e0a91f744..01dfd09deef2b34 100644 --- a/Lib/concurrent/futures/_base.py +++ b/Lib/concurrent/futures/_base.py @@ -1,7 +1,7 @@ # Copyright 2009 Brian Quinlan. All Rights Reserved. # Licensed to PSF under a Contributor Agreement. -__author__ = 'Brian Quinlan (brian@sweetapp.com)' +# Original author: Brian Quinlan (brian@sweetapp.com) import collections import logging diff --git a/Lib/concurrent/futures/process.py b/Lib/concurrent/futures/process.py index 7f4f225c0ad4fbd..5d267481041e907 100644 --- a/Lib/concurrent/futures/process.py +++ b/Lib/concurrent/futures/process.py @@ -43,7 +43,7 @@ _ResultItems in "Result Q" """ -__author__ = 'Brian Quinlan (brian@sweetapp.com)' +# Original author: Brian Quinlan (brian@sweetapp.com) import os from concurrent.futures import _base @@ -58,6 +58,7 @@ from functools import partial import itertools import sys +lazy import warnings from traceback import format_exception @@ -1065,3 +1066,10 @@ def kill_workers(self): submitted). """ return self._force_shutdown(operation=_KILL) + + +def __getattr__(name): + if name == "__author__": + warnings._deprecated("__author__", remove=(3, 20)) + return 'Brian Quinlan (brian@sweetapp.com)' # Do not change + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/Lib/concurrent/futures/thread.py b/Lib/concurrent/futures/thread.py index 909359b648709fe..ae187be2eb23881 100644 --- a/Lib/concurrent/futures/thread.py +++ b/Lib/concurrent/futures/thread.py @@ -3,7 +3,7 @@ """Implements ThreadPoolExecutor.""" -__author__ = 'Brian Quinlan (brian@sweetapp.com)' +# Original author: Brian Quinlan (brian@sweetapp.com) from concurrent.futures import _base import itertools @@ -12,6 +12,7 @@ import types import weakref import os +lazy import warnings _threads_queues = weakref.WeakKeyDictionary() @@ -272,3 +273,10 @@ def shutdown(self, wait=True, *, cancel_futures=False): for t in self._threads: t.join() shutdown.__doc__ = _base.Executor.shutdown.__doc__ + + +def __getattr__(name): + if name == "__author__": + warnings._deprecated("__author__", remove=(3, 20)) + return 'Brian Quinlan (brian@sweetapp.com)' # Do not change + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/Lib/ctypes/_aix.py b/Lib/ctypes/_aix.py index ee790f713a9ee3a..ed4b57ed92d6b05 100644 --- a/Lib/ctypes/_aix.py +++ b/Lib/ctypes/_aix.py @@ -44,7 +44,7 @@ The mode bit RTLD_MEMBER tells initAndLoad() that it needs to use the AIX (SVR3) naming style. """ -__author__ = "Michael Felt " +# Original author: Michael Felt import re from os import environ, path diff --git a/Lib/inspect.py b/Lib/inspect.py index bceed35a53abba1..bd88c95598c9bba 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -28,8 +28,8 @@ # This module is in the public domain. No warranties. -__author__ = ('Ka-Ping Yee ', - 'Yury Selivanov ') +# Original authors: Ka-Ping Yee, +# Yury Selivanov __all__ = [ "AGEN_CLOSED", @@ -160,6 +160,7 @@ import types import functools import builtins +lazy import warnings from keyword import iskeyword from operator import attrgetter from collections import namedtuple, OrderedDict @@ -3526,5 +3527,13 @@ def _main(): print(getsource(obj)) +def __getattr__(name): + if name == "__author__": + warnings._deprecated("__author__", remove=(3, 20)) + return ('Ka-Ping Yee', + 'Yury Selivanov ') # Do not change + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + if __name__ == "__main__": _main() diff --git a/Lib/io.py b/Lib/io.py index 63ffadb1d385f99..78d6f87f68be2f2 100644 --- a/Lib/io.py +++ b/Lib/io.py @@ -34,12 +34,12 @@ """ # New I/O library conforming to PEP 3116. -__author__ = ("Guido van Rossum , " - "Mike Verdone , " - "Mark Russell , " - "Antoine Pitrou , " - "Amaury Forgeot d'Arc , " - "Benjamin Peterson ") +# Original authors: Guido van Rossum , +# Mike Verdone , +# Mark Russell , +# Antoine Pitrou , +# Amaury Forgeot d'Arc , +# Benjamin Peterson __all__ = ["BlockingIOError", "open", "open_code", "IOBase", "RawIOBase", "FileIO", "BytesIO", "StringIO", "BufferedIOBase", @@ -52,6 +52,7 @@ import _io import abc +lazy import warnings from _collections_abc import _check_methods from _io import (DEFAULT_BUFFER_SIZE, BlockingIOError, UnsupportedOperation, @@ -148,3 +149,15 @@ def __subclasshook__(cls, C): return NotImplemented __class_getitem__ = classmethod(GenericAlias) + + +def __getattr__(name): + if name == "__author__": + warnings._deprecated("__author__", remove=(3, 20)) + return ("Guido van Rossum , " + "Mike Verdone , " + "Mark Russell , " + "Antoine Pitrou , " + "Amaury Forgeot d'Arc , " + "Benjamin Peterson ") # Do not change + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/Lib/json/__init__.py b/Lib/json/__init__.py index 37a86831ff94838..f74b55ed0dd4f53 100644 --- a/Lib/json/__init__.py +++ b/Lib/json/__init__.py @@ -100,11 +100,12 @@ 'JSONDecoder', 'JSONDecodeError', 'JSONEncoder', ] -__author__ = 'Bob Ippolito ' +# Original author: Bob Ippolito from .decoder import JSONDecoder, JSONDecodeError from .encoder import JSONEncoder import codecs +lazy import warnings _default_encoder = JSONEncoder( skipkeys=False, @@ -387,9 +388,10 @@ def loads(s, *, cls=None, object_hook=None, parse_float=None, def __getattr__(name): - if name == "__version__": - from warnings import _deprecated - - _deprecated("__version__", remove=(3, 20)) - return "2.0.9" # Do not change + if name in ("__version__", "__author__"): + warnings._deprecated(name, remove=(3, 20)) + return { # Do not change + "__version__": "2.0.9", + "__author__": "Bob Ippolito ", + }[name] raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 549a6b3c457b2c6..f0ef935fd9ec40b 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -43,7 +43,7 @@ import threading -__author__ = "Vinay Sajip " +# Original author: Vinay Sajip __status__ = "production" #--------------------------------------------------------------------------- @@ -2369,13 +2369,12 @@ def captureWarnings(capture): def __getattr__(name): - if name in ("__version__", "__date__"): - from warnings import _deprecated - - _deprecated(name, remove=(3, 20)) + if name in ("__version__", "__date__", "__author__"): + warnings._deprecated(name, remove=(3, 20)) return { # Do not change "__version__": "0.5.1.2", "__date__": "07 February 2010", + "__author__": "Vinay Sajip ", }[name] raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/Lib/optparse.py b/Lib/optparse.py index 70ca3b38b021a58..bd26909ed14e36d 100644 --- a/Lib/optparse.py +++ b/Lib/optparse.py @@ -32,39 +32,38 @@ 'BadOptionError', 'check_choice'] -__copyright__ = """ -Copyright (c) 2001-2006 Gregory P. Ward. All rights reserved. -Copyright (c) 2002 Python Software Foundation. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -""" +# Copyright (c) 2001-2006 Gregory P. Ward. All rights reserved. +# Copyright (c) 2002 Python Software Foundation. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the name of the author nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import sys, os +lazy import warnings from gettext import gettext as _, ngettext @@ -1671,9 +1670,40 @@ def _match_abbrev(s, wordmap): def __getattr__(name): - if name == "__version__": - from warnings import _deprecated + if name in ("__version__", "__copyright__"): + warnings._deprecated(name, remove=(3, 20)) + return { # Do not change + "__version__": "1.5.3", + "__copyright__": """ +Copyright (c) 2001-2006 Gregory P. Ward. All rights reserved. +Copyright (c) 2002 Python Software Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: - _deprecated("__version__", remove=(3, 20)) - return "1.5.3" # Do not change + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +""", + }[name] raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/Lib/platform.py b/Lib/platform.py index c4ef1c3eb20edf4..2a40e325f1ef254 100644 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -89,26 +89,23 @@ # # If that URL should fail, try contacting the author. -__copyright__ = """ - Copyright (c) 1999-2000, Marc-Andre Lemburg; mailto:mal@lemburg.com - Copyright (c) 2000-2010, eGenix.com Software GmbH; mailto:info@egenix.com - - Permission to use, copy, modify, and distribute this software and its - documentation for any purpose and without fee or royalty is hereby granted, - provided that the above copyright notice appear in all copies and that - both that copyright notice and this permission notice appear in - supporting documentation or portions thereof, including modifications, - that you make. - - EGENIX.COM SOFTWARE GMBH DISCLAIMS ALL WARRANTIES WITH REGARD TO - THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, - INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING - FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - WITH THE USE OR PERFORMANCE OF THIS SOFTWARE ! - -""" +# Copyright (c) 1999-2000, Marc-Andre Lemburg; mailto:mal@lemburg.com +# Copyright (c) 2000-2010, eGenix.com Software GmbH; mailto:info@egenix.com +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee or royalty is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation or portions thereof, including modifications, +# that you make. +# +# EGENIX.COM SOFTWARE GMBH DISCLAIMS ALL WARRANTIES WITH REGARD TO +# THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +# FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, +# INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE ! import collections import os @@ -116,6 +113,7 @@ import sys import functools import itertools +lazy import warnings try: import _wmi except ImportError: @@ -1443,11 +1441,31 @@ def _main(args: list[str] | None = None): def __getattr__(name): - if name == "__version__": - from warnings import _deprecated + if name in ("__version__", "__copyright__"): + warnings._deprecated(name, remove=(3, 20)) + return { # Do not change + "__version__": "1.1.0", + "__copyright__": """ + Copyright (c) 1999-2000, Marc-Andre Lemburg; mailto:mal@lemburg.com + Copyright (c) 2000-2010, eGenix.com Software GmbH; mailto:info@egenix.com + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose and without fee or royalty is hereby granted, + provided that the above copyright notice appear in all copies and that + both that copyright notice and this permission notice appear in + supporting documentation or portions thereof, including modifications, + that you make. + + EGENIX.COM SOFTWARE GMBH DISCLAIMS ALL WARRANTIES WITH REGARD TO + THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, + INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + WITH THE USE OR PERFORMANCE OF THIS SOFTWARE ! - _deprecated("__version__", remove=(3, 20)) - return "1.1.0" # Do not change +""", + }[name] raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/Lib/pydoc.py b/Lib/pydoc.py index 6f3dab24d5bda33..a3607e030c9dcb8 100644 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -37,14 +37,12 @@ class or function within a module or module in a package. If the Reference Manual pages. """ __all__ = ['help'] -__author__ = "Ka-Ping Yee " -__date__ = "26 February 2001" -__credits__ = """Guido van Rossum, for an excellent programming language. -Tommy Burnette, the original creator of manpy. -Paul Prescod, for all his work on onlinehelp. -Richard Chamberlain, for the first implementation of textdoc. -""" +# Original author: Ka-Ping Yee +# Credits: Guido van Rossum, for an excellent programming language. +# Tommy Burnette, the original creator of manpy. +# Paul Prescod, for all his work on onlinehelp. +# Richard Chamberlain, for the first implementation of textdoc. # Known bugs that can't be fixed here: # - synopsis() cannot be prevented from clobbering existing @@ -524,14 +522,14 @@ def getdocloc(self, object, basedir=None): docloc = None return docloc - def _get_version(self, object): + def _get_metadata(self, object, name): if self._is_stdlib_module(object): with warnings.catch_warnings(): warnings.simplefilter("ignore", DeprecationWarning) - version = getattr(object, '__version__', None) + value = getattr(object, name, None) else: - version = getattr(object, '__version__', None) - return '' if version is None else str(version) + value = getattr(object, name, None) + return '' if value is None else str(value) def _is_stdlib_module(self, object, basedir=None): basedir = self.STDLIB_DIR if basedir is None else basedir @@ -801,12 +799,12 @@ def docmodule(self, object, name=None, mod=None, *ignored): filelink = '(built-in)' info = [] - if version := self._get_version(object): + if version := self._get_metadata(object, '__version__'): if version[:11] == '$' + 'Revision: ' and version[-1:] == '$': version = version[11:-1].strip() info.append('version %s' % self.escape(version)) - if hasattr(object, '__date__'): - info.append(self.escape(str(object.__date__))) + if date := self._get_metadata(object, '__date__'): + info.append(self.escape(date)) if info: head = head + ' (%s)' % ', '.join(info) docloc = self.getdocloc(object) @@ -888,11 +886,11 @@ def docmodule(self, object, name=None, mod=None, *ignored): f'
{self.document(value, key)}
') result = result + self.bigsection( 'Data', 'data', '\n'.join(contents)) - if hasattr(object, '__author__'): - contents = self.markup(str(object.__author__), self.preformat) + if author := self._get_metadata(object, '__author__'): + contents = self.markup(author, self.preformat) result = result + self.bigsection('Author', 'author', contents) - if hasattr(object, '__credits__'): - contents = self.markup(str(object.__credits__), self.preformat) + if credits := self._get_metadata(object, '__credits__'): + contents = self.markup(credits, self.preformat) result = result + self.bigsection('Credits', 'credits', contents) return result @@ -1368,16 +1366,16 @@ def docmodule(self, object, name=None, mod=None, *ignored): contents.append(self.docother(value, key, name, maxlen=76)) result = result + self.section('DATA', '\n'.join(contents)) - if version := self._get_version(object): + if version := self._get_metadata(object, '__version__'): if version[:11] == '$' + 'Revision: ' and version[-1:] == '$': version = version[11:-1].strip() result = result + self.section('VERSION', version) - if hasattr(object, '__date__'): - result = result + self.section('DATE', str(object.__date__)) - if hasattr(object, '__author__'): - result = result + self.section('AUTHOR', str(object.__author__)) - if hasattr(object, '__credits__'): - result = result + self.section('CREDITS', str(object.__credits__)) + if date := self._get_metadata(object, '__date__'): + result = result + self.section('DATE', date) + if author := self._get_metadata(object, '__author__'): + result = result + self.section('AUTHOR', author) + if credits := self._get_metadata(object, '__credits__'): + result = result + self.section('CREDITS', credits) try: file = inspect.getabsfile(object) except TypeError: @@ -2541,8 +2539,7 @@ def bltinlink(name): contents.append(html.index(dir, seen)) contents.append( - '
pydoc by Ka-Ping Yee' - ' <ping@lfw.org>
') + '
pydoc by Ka-Ping Yee
') return 'Index of Modules', ''.join(contents) def html_search(key): @@ -2851,5 +2848,21 @@ class BadUsage(Exception): pass it names a directory, documentation is written for all the contents. """.format(cmd=cmd, sep=os.sep)) + +def __getattr__(name): + if name in ("__author__", "__date__", "__credits__"): + warnings._deprecated(name, remove=(3, 20)) + return { # Do not change + "__author__": "Ka-Ping Yee", + "__date__": "26 February 2001", + "__credits__": """Guido van Rossum, for an excellent programming language. +Tommy Burnette, the original creator of manpy. +Paul Prescod, for all his work on onlinehelp. +Richard Chamberlain, for the first implementation of textdoc. +""", + }[name] + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + if __name__ == '__main__': cli() diff --git a/Lib/tarfile.py b/Lib/tarfile.py index c0b9f3f429c10e2..bc565a7b95ea0ac 100644 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -28,8 +28,8 @@ """Read from and write to tar format archives. """ -__author__ = "Lars Gust\u00e4bel (lars@gustaebel.de)" -__credits__ = "Gustavo Niemeyer, Niels Gust\u00e4bel, Richard Townsend." +# Original author: Lars Gustäbel (lars@gustaebel.de) +# Credits: Gustavo Niemeyer, Niels Gustäbel, Richard Townsend. #--------- # Imports @@ -44,6 +44,7 @@ import struct import copy import re +lazy import warnings try: import pwd @@ -3236,11 +3237,13 @@ def main(): def __getattr__(name): - if name == "version": - from warnings import _deprecated - - _deprecated("version", remove=(3, 20)) - return "0.9.0" # Do not change + if name in ("version", "__author__", "__credits__"): + warnings._deprecated(name, remove=(3, 20)) + return { # Do not change + "version": "0.9.0", + "__author__": "Lars Gust\u00e4bel (lars@gustaebel.de)", + "__credits__": "Gustavo Niemeyer, Niels Gust\u00e4bel, Richard Townsend.", + }[name] raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/Lib/test/test_bz2.py b/Lib/test/test_bz2.py index 2c95a8eec510b0a..01af10f5e56ab8c 100644 --- a/Lib/test/test_bz2.py +++ b/Lib/test/test_bz2.py @@ -1249,6 +1249,16 @@ def test_bzlib_version(self): self.assertStartsWith(bz2.bzlib_version, '%d.%d.%d' % v) +class TestModule(unittest.TestCase): + def test_deprecated__author__(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'__author__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(bz2, "__author__") + self.assertEqual(cm.filename, __file__) + + def tearDownModule(): support.reap_children() diff --git a/Lib/test/test_concurrent_futures/test_future.py b/Lib/test/test_concurrent_futures/test_future.py index 06b11a3bacf13a3..73e803c4a68cc0b 100644 --- a/Lib/test/test_concurrent_futures/test_future.py +++ b/Lib/test/test_concurrent_futures/test_future.py @@ -340,6 +340,18 @@ def get_snapshot(): self.assertEqual(result, expected) +class TestModule(unittest.TestCase): + def test_deprecated__author__(self): + for mod in (futures, futures.process, futures.thread): + with self.subTest(mod=mod.__name__): + with self.assertWarnsRegex( + DeprecationWarning, + "'__author__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(mod, "__author__") + self.assertEqual(cm.filename, __file__) + + def setUpModule(): setup_module() diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 3f14a9e66226eaa..32cbac3dff131a9 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -6973,5 +6973,15 @@ def f(): self.assertIn(expected, output) +class TestModule(unittest.TestCase): + def test_deprecated__author__(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'__author__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(inspect, "__author__") + self.assertEqual(cm.filename, __file__) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_io/test_general.py b/Lib/test/test_io/test_general.py index 085ed3ea6a95ee1..0dbe06e99a09a6d 100644 --- a/Lib/test/test_io/test_general.py +++ b/Lib/test/test_io/test_general.py @@ -1427,5 +1427,15 @@ def test_writer_subclass(self): self.assertNotIsSubclass(str, io.Writer) +class TestModule(unittest.TestCase): + def test_deprecated__author__(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'__author__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(io, "__author__") + self.assertEqual(cm.filename, __file__) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_json/__init__.py b/Lib/test/test_json/__init__.py index e58fbee41af3e6c..26b8f10325f06a2 100644 --- a/Lib/test/test_json/__init__.py +++ b/Lib/test/test_json/__init__.py @@ -56,6 +56,14 @@ def test_deprecated__version__(self): getattr(json, "__version__") self.assertEqual(cm.filename, __file__) + def test_deprecated__author__(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'__author__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(json, "__author__") + self.assertEqual(cm.filename, __file__) + def load_tests(loader, _, pattern): suite = unittest.TestSuite() diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index b1852ee43b594fa..5e773ab9235cc51 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -7587,9 +7587,9 @@ def test__all__(self): class TestModule(unittest.TestCase): - def test_deprecated__version__and__date__(self): + def test_deprecated__version__and__date__and__author__(self): msg = "is deprecated and slated for removal in Python 3.20" - for attr in ("__version__", "__date__"): + for attr in ("__version__", "__date__", "__author__"): with self.subTest(attr=attr): with self.assertWarnsRegex(DeprecationWarning, msg) as cm: getattr(logging, attr) diff --git a/Lib/test/test_optparse.py b/Lib/test/test_optparse.py index 9fca2dafd5c99e2..d5e69b01dfbdee9 100644 --- a/Lib/test/test_optparse.py +++ b/Lib/test/test_optparse.py @@ -1682,6 +1682,14 @@ def test_deprecated__version__(self): getattr(optparse, "__version__") self.assertEqual(cm.filename, __file__) + def test_deprecated__copyright__(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'__copyright__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(optparse, "__copyright__") + self.assertEqual(cm.filename, __file__) + if __name__ == '__main__': # To regenerate translation snapshots diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py index 3503eaefaf8e9e7..0631bcde705a1fa 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -840,5 +840,15 @@ def test_help(self): self.assertStartsWith(output.getvalue(), "usage:") +class TestModule(unittest.TestCase): + def test_deprecated__copyright__(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'__copyright__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(platform, "__copyright__") + self.assertEqual(cm.filename, __file__) + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_pydoc/test_pydoc.py b/Lib/test/test_pydoc/test_pydoc.py index 006fbc8b6968f3b..548cbe2faab5061 100644 --- a/Lib/test/test_pydoc/test_pydoc.py +++ b/Lib/test/test_pydoc/test_pydoc.py @@ -2461,7 +2461,7 @@ def test_sys_path_adjustment_when_curdir_already_included(self): trailing_argv0dir = trailing_curdir + [self.argv0dir] self.assertIsNone(self._get_revised_path(trailing_argv0dir)) - def test__get_version(self): + def test__get_metadata(self): import json import warnings @@ -2477,17 +2477,29 @@ def __version__(self): doc = pydoc.Doc() with warnings.catch_warnings(record=True) as w: # TODO: remove in 3.20 warnings.simplefilter("always") - version = doc._get_version(json) + version = doc._get_metadata(json, '__version__') self.assertEqual(version, "2.0.9") + author = doc._get_metadata(json, '__author__') + self.assertEqual(author, "Bob Ippolito ") self.assertEqual(len(w), 0) with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") - version = doc._get_version(module) + version = doc._get_metadata(module, '__version__') self.assertEqual(version, "1.2.3") self.assertEqual(len(w), 1) +class TestModule(unittest.TestCase): + def test_deprecated__author__and__date__and__credits__(self): + msg = "is deprecated and slated for removal in Python 3.20" + for attr in ("__author__", "__date__", "__credits__"): + with self.subTest(attr=attr): + with self.assertWarnsRegex(DeprecationWarning, msg) as cm: + getattr(pydoc, attr) + self.assertEqual(cm.filename, __file__) + + def setUpModule(): thread_info = threading_helper.threading_setup() unittest.addModuleCleanup(threading_helper.threading_cleanup, *thread_info) diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index 2b4cf748dffc1a9..0fa2ed8d2a995b2 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -5459,6 +5459,14 @@ def test_deprecated_version(self): getattr(tarfile, "version") self.assertEqual(cm.filename, __file__) + def test_deprecated__author__and__credits__(self): + msg = "is deprecated and slated for removal in Python 3.20" + for attr in ("__author__", "__credits__"): + with self.subTest(attr=attr): + with self.assertWarnsRegex(DeprecationWarning, msg) as cm: + getattr(tarfile, attr) + self.assertEqual(cm.filename, __file__) + def setUpModule(): os_helper.unlink(TEMPDIR) diff --git a/Lib/test/test_tokenize.py b/Lib/test/test_tokenize.py index 9fa294aa3618845..788a6bcba7d17f5 100644 --- a/Lib/test/test_tokenize.py +++ b/Lib/test/test_tokenize.py @@ -3896,5 +3896,15 @@ def test_prefixes(self): self.assertEqual(defined_prefixes, self.determine_valid_prefixes()) +class TestModule(unittest.TestCase): + def test_deprecated__author__and__credits__(self): + msg = "is deprecated and slated for removal in Python 3.20" + for attr in ("__author__", "__credits__"): + with self.subTest(attr=attr): + with self.assertWarnsRegex(DeprecationWarning, msg) as cm: + getattr(tokenize, attr) + self.assertEqual(cm.filename, __file__) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_ttk/__init__.py b/Lib/test/test_ttk/__init__.py index 7a47f44ca382218..c980dc32fcbd83b 100644 --- a/Lib/test/test_ttk/__init__.py +++ b/Lib/test/test_ttk/__init__.py @@ -28,6 +28,14 @@ def test_deprecated__version__(self): getattr(ttk, "__version__") self.assertEqual(cm.filename, __file__) + def test_deprecated__author__(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'__author__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(ttk, "__author__") + self.assertEqual(cm.filename, __file__) + def setUpModule(): wantobjects = support.get_resource_value('wantobjects') diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py index 055be2994bf435e..43bcecb44d97d32 100755 --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py @@ -1553,5 +1553,15 @@ def test_windll_getnode(self): self.check_node(node) +class TestModule(unittest.TestCase): + def test_deprecated__author__(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'__author__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(py_uuid, "__author__") + self.assertEqual(cm.filename, __file__) + + if __name__ == '__main__': unittest.main() diff --git a/Lib/tkinter/ttk.py b/Lib/tkinter/ttk.py index 57953d90b672a2c..73d2b63ad445f87 100644 --- a/Lib/tkinter/ttk.py +++ b/Lib/tkinter/ttk.py @@ -12,7 +12,7 @@ of the widgets appearance lies at Themes. """ -__author__ = "Guilherme Polo " +# Original author: Guilherme Polo __all__ = ["Button", "Checkbutton", "Combobox", "Entry", "Frame", "Label", "Labelframe", "LabelFrame", "Menubutton", "Notebook", "Panedwindow", @@ -24,6 +24,7 @@ "tclobjs_to_py", "setup_master"] import tkinter +lazy import warnings from tkinter import _flatten, _join, _stringify, _splitdict @@ -2210,9 +2211,10 @@ def destroy(self): def __getattr__(name): - if name == "__version__": - from warnings import _deprecated - - _deprecated("__version__", remove=(3, 20)) - return "0.3.1" # Do not change + if name in ("__version__", "__author__"): + warnings._deprecated(name, remove=(3, 20)) + return { # Do not change + "__version__": "0.3.1", + "__author__": "Guilherme Polo ", + }[name] raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/Lib/tokenize.py b/Lib/tokenize.py index 3545d92c4f5d7ff..6f69fc76c3911e1 100644 --- a/Lib/tokenize.py +++ b/Lib/tokenize.py @@ -20,10 +20,10 @@ which tells you which encoding was used to decode the bytes stream. """ -__author__ = 'Ka-Ping Yee ' -__credits__ = ('GvR, ESR, Tim Peters, Thomas Wouters, Fred Drake, ' - 'Skip Montanaro, Raymond Hettinger, Trent Nelson, ' - 'Michael Foord') +# Original author: Ka-Ping Yee +# Credits: GvR, ESR, Tim Peters, Thomas Wouters, Fred Drake, +# Skip Montanaro, Raymond Hettinger, Trent Nelson, Michael Foord + from builtins import open as _builtin_open from codecs import lookup, BOM_UTF8 import collections @@ -36,6 +36,7 @@ from token import EXACT_TOKEN_TYPES import _tokenize lazy import _colorize +lazy import warnings cookie_re = re.compile(br'^[ \t\f]*#.*?coding[:=][ \t]*([-\w.]+)', re.ASCII) blank_re = re.compile(br'^[ \t\f]*(?:[#\r\n]|$)', re.ASCII) @@ -640,5 +641,17 @@ def _generate_tokens_from_c_tokenizer(source, encoding=None, extra_tokens=False) raise TokenError(msg, (e.lineno, e.offset)) from None +def __getattr__(name): + if name in ("__author__", "__credits__"): + warnings._deprecated(name, remove=(3, 20)) + return { # Do not change + "__author__": 'Ka-Ping Yee', + "__credits__": ('GvR, ESR, Tim Peters, Thomas Wouters, Fred Drake, ' + 'Skip Montanaro, Raymond Hettinger, Trent Nelson, ' + 'Michael Foord'), + }[name] + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + if __name__ == "__main__": _main() diff --git a/Lib/uuid.py b/Lib/uuid.py index 3f10fed81c1bf9c..be681d8861f427f 100644 --- a/Lib/uuid.py +++ b/Lib/uuid.py @@ -59,11 +59,11 @@ import os import sys import time +lazy import warnings from enum import Enum, _simple_enum - -__author__ = 'Ka-Ping Yee ' +# Original author: Ka-Ping Yee # The recognized platforms - known behaviors if sys.platform in {'win32', 'darwin', 'emscripten', 'wasi'}: @@ -1009,5 +1009,12 @@ def main(): NIL = UUID('00000000-0000-0000-0000-000000000000') MAX = UUID('ffffffff-ffff-ffff-ffff-ffffffffffff') +def __getattr__(name): + if name == "__author__": + warnings._deprecated("__author__", remove=(3, 20)) + return 'Ka-Ping Yee' # Do not change + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + if __name__ == "__main__": main() diff --git a/Misc/NEWS.d/next/Library/2026-10-12-18-21-06.gh-issue-159240.a1b2c3.rst b/Misc/NEWS.d/next/Library/2026-10-12-18-21-06.gh-issue-159240.a1b2c3.rst new file mode 100644 index 000000000000000..85439b77aaa8da4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-10-12-18-21-06.gh-issue-159240.a1b2c3.rst @@ -0,0 +1,6 @@ +Deprecate, and schedule for removal in Python 3.20, the ``__author__``, +``__credits__``, ``__copyright__`` and ``__date__`` attributes of :mod:`bz2`, +:mod:`concurrent.futures`, +:mod:`inspect`, :mod:`io`, :mod:`json`, :mod:`logging`, :mod:`optparse`, +:mod:`platform`, :mod:`pydoc`, :mod:`tarfile`, :mod:`tkinter.ttk`, +:mod:`tokenize` and :mod:`uuid`. From e76e80930ee013cbc485453a9eb82ed60d147097 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Mon, 12 Oct 2026 20:13:41 +0100 Subject: [PATCH 2/3] =?UTF-8?q?Remove=20Guido=20from=20attribution=20comme?= =?UTF-8?q?nts=20=F0=9F=AB=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Lib/io.py | 3 +-- Lib/pydoc.py | 3 +-- Lib/tokenize.py | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Lib/io.py b/Lib/io.py index 78d6f87f68be2f2..aff42604479b2c1 100644 --- a/Lib/io.py +++ b/Lib/io.py @@ -34,8 +34,7 @@ """ # New I/O library conforming to PEP 3116. -# Original authors: Guido van Rossum , -# Mike Verdone , +# Original authors: Mike Verdone , # Mark Russell , # Antoine Pitrou , # Amaury Forgeot d'Arc , diff --git a/Lib/pydoc.py b/Lib/pydoc.py index a3607e030c9dcb8..e8a3fe1c4908f78 100644 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -39,8 +39,7 @@ class or function within a module or module in a package. If the __all__ = ['help'] # Original author: Ka-Ping Yee -# Credits: Guido van Rossum, for an excellent programming language. -# Tommy Burnette, the original creator of manpy. +# Credits: Tommy Burnette, the original creator of manpy. # Paul Prescod, for all his work on onlinehelp. # Richard Chamberlain, for the first implementation of textdoc. diff --git a/Lib/tokenize.py b/Lib/tokenize.py index 6f69fc76c3911e1..3cde65d0a707ae9 100644 --- a/Lib/tokenize.py +++ b/Lib/tokenize.py @@ -21,8 +21,8 @@ """ # Original author: Ka-Ping Yee -# Credits: GvR, ESR, Tim Peters, Thomas Wouters, Fred Drake, -# Skip Montanaro, Raymond Hettinger, Trent Nelson, Michael Foord +# Credits: ESR, Tim Peters, Thomas Wouters, Fred Drake, Skip Montanaro, +# Raymond Hettinger, Trent Nelson, Michael Foord from builtins import open as _builtin_open from codecs import lookup, BOM_UTF8 From 855a2b649e269921bca42d55f2a6efac32737191 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Thu, 22 Oct 2026 16:05:14 +0100 Subject: [PATCH 3/3] Refactor testing --- Doc/library/test.rst | 8 ++++++++ Lib/test/support/__init__.py | 18 +++++++++++++++++ Lib/test/test_argparse.py | 12 +++++------ Lib/test/test_bz2.py | 11 ++++------ .../test_concurrent_futures/test_future.py | 11 ++++------ Lib/test/test_csv.py | 11 ++++------ Lib/test/test_ctypes/__init__.py | 11 ++++------ Lib/test/test_ctypes/test_macholib.py | 12 +++++------ Lib/test/test_decimal.py | 11 ++++------ Lib/test/test_httpservers.py | 11 ++++------ Lib/test/test_imaplib.py | 11 ++++------ Lib/test/test_inspect/test_inspect.py | 13 +++++------- Lib/test/test_io/test_general.py | 11 ++++------ Lib/test/test_ipaddress.py | 12 +++++------ Lib/test/test_json/__init__.py | 20 +++++-------------- Lib/test/test_logging.py | 12 +++++------ Lib/test/test_optparse.py | 19 ++++-------------- Lib/test/test_platform.py | 11 ++++------ Lib/test/test_pydoc/test_pydoc.py | 12 +++++------ Lib/test/test_re.py | 12 +++++------ Lib/test/test_sax.py | 12 +++++------ Lib/test/test_socketserver.py | 11 ++++------ Lib/test/test_tabnanny.py | 12 +++++------ Lib/test/test_tarfile.py | 20 +++++-------------- Lib/test/test_tkinter/test_font.py | 12 +++++------ Lib/test/test_tokenize.py | 11 ++++------ Lib/test/test_ttk/__init__.py | 19 ++++-------------- Lib/test/test_uuid.py | 11 ++++------ Lib/test/test_wsgiref.py | 11 ++++------ Lib/test/test_xml_etree.py | 11 ++++------ Lib/test/test_zlib.py | 11 ++++------ 31 files changed, 154 insertions(+), 236 deletions(-) diff --git a/Doc/library/test.rst b/Doc/library/test.rst index 765811eba32fd40..84c6dc852ce6bfe 100644 --- a/Doc/library/test.rst +++ b/Doc/library/test.rst @@ -1082,6 +1082,14 @@ The :mod:`!test.support` module defines the following functions: .. versionadded:: 3.6 + +.. function:: check_deprecated_module_attributes(test_case, module, *names, remove) + + Assert that accessing each attribute in *names* on *module* emits a + :exc:`DeprecationWarning` naming the attribute and the Python version + *remove*, given as a ``(major, minor)`` tuple, in which it will be removed. + + .. function:: skip_if_broken_multiprocessing_synchronize() Skip tests if the :mod:`multiprocessing.synchronize` module is missing, if diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 5331615b1b1587a..2bd7441b5500184 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -1943,6 +1943,24 @@ def test__all__(self): test_case.assertCountEqual(module.__all__, expected) +def check_deprecated_module_attributes(test_case, module, *names, remove): + """Assert that accessing each of 'names' on 'module' emits a + DeprecationWarning naming the attribute and the Python version 'remove', + given as a (major, minor) tuple, in which it will be removed. + """ + major, minor = remove + for name in names: + with test_case.subTest(name=name): + with test_case.assertWarns(DeprecationWarning) as cm: + getattr(module, name) + test_case.assertEqual( + str(cm.warning), + f"{name!r} is deprecated and slated for removal " + f"in Python {major}.{minor}", + ) + test_case.assertEqual(cm.filename, __file__) + + def suppress_msvcrt_asserts(verbose=False): try: import msvcrt diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 3abdb3244570a51..6b2b147cbeac6e9 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -18,6 +18,7 @@ import warnings from enum import StrEnum +from test import support from test.support import ( captured_stderr, force_not_colorized, @@ -8070,13 +8071,10 @@ def test_fake_color_theme_raises(self): class TestModule(unittest.TestCase): - def test_deprecated__version__(self): - with self.assertWarnsRegex( - DeprecationWarning, - "'__version__' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(argparse, "__version__") - self.assertEqual(cm.filename, __file__) + def test_deprecated_module_attributes(self): + support.check_deprecated_module_attributes( + self, argparse, "__version__", remove=(3, 20), + ) def tearDownModule(): diff --git a/Lib/test/test_bz2.py b/Lib/test/test_bz2.py index 01af10f5e56ab8c..2f59f337abbe320 100644 --- a/Lib/test/test_bz2.py +++ b/Lib/test/test_bz2.py @@ -1250,13 +1250,10 @@ def test_bzlib_version(self): class TestModule(unittest.TestCase): - def test_deprecated__author__(self): - with self.assertWarnsRegex( - DeprecationWarning, - "'__author__' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(bz2, "__author__") - self.assertEqual(cm.filename, __file__) + def test_deprecated_module_attributes(self): + support.check_deprecated_module_attributes( + self, bz2, "__author__", remove=(3, 20), + ) def tearDownModule(): diff --git a/Lib/test/test_concurrent_futures/test_future.py b/Lib/test/test_concurrent_futures/test_future.py index 73e803c4a68cc0b..f2ffef65843b34c 100644 --- a/Lib/test/test_concurrent_futures/test_future.py +++ b/Lib/test/test_concurrent_futures/test_future.py @@ -341,15 +341,12 @@ def get_snapshot(): class TestModule(unittest.TestCase): - def test_deprecated__author__(self): + def test_deprecated_module_attributes(self): for mod in (futures, futures.process, futures.thread): with self.subTest(mod=mod.__name__): - with self.assertWarnsRegex( - DeprecationWarning, - "'__author__' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(mod, "__author__") - self.assertEqual(cm.filename, __file__) + support.check_deprecated_module_attributes( + self, mod, "__author__", remove=(3, 20), + ) def setUpModule(): diff --git a/Lib/test/test_csv.py b/Lib/test/test_csv.py index 36fa7e3572a5ac2..056d7551cfb8e5f 100644 --- a/Lib/test/test_csv.py +++ b/Lib/test/test_csv.py @@ -2095,13 +2095,10 @@ def test_disallow_instantiation(self): class TestModule(unittest.TestCase): - def test_deprecated__version__(self): - with self.assertWarnsRegex( - DeprecationWarning, - "'__version__' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(csv, "__version__") - self.assertEqual(cm.filename, __file__) + def test_deprecated_module_attributes(self): + support.check_deprecated_module_attributes( + self, csv, "__version__", remove=(3, 20), + ) if __name__ == '__main__': diff --git a/Lib/test/test_ctypes/__init__.py b/Lib/test/test_ctypes/__init__.py index d848beb1ff18571..ce69c4c8dadbb1f 100644 --- a/Lib/test/test_ctypes/__init__.py +++ b/Lib/test/test_ctypes/__init__.py @@ -9,18 +9,15 @@ class TestModule(unittest.TestCase): - def test_deprecated__version__(self): + def test_deprecated_module_attributes(self): import ctypes import _ctypes for mod in (ctypes, _ctypes): with self.subTest(mod=mod): - with self.assertWarnsRegex( - DeprecationWarning, - "'__version__' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(mod, "__version__") - self.assertEqual(cm.filename, __file__) + support.check_deprecated_module_attributes( + self, mod, "__version__", remove=(3, 20), + ) def load_tests(*args): diff --git a/Lib/test/test_ctypes/test_macholib.py b/Lib/test/test_ctypes/test_macholib.py index 9a5e18ed589d44b..f2464d92a012ba6 100644 --- a/Lib/test/test_ctypes/test_macholib.py +++ b/Lib/test/test_ctypes/test_macholib.py @@ -30,6 +30,7 @@ import os import sys import unittest +from test import support from ctypes.macholib.dyld import dyld_find from ctypes.macholib.dylib import dylib_info @@ -109,15 +110,12 @@ def test_framework_info(self): class TestModule(unittest.TestCase): - def test_deprecated__version__(self): + def test_deprecated_module_attributes(self): import ctypes.macholib - with self.assertWarnsRegex( - DeprecationWarning, - "'__version__' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(ctypes.macholib, "__version__") - self.assertEqual(cm.filename, __file__) + support.check_deprecated_module_attributes( + self, ctypes.macholib, "__version__", remove=(3, 20), + ) if __name__ == "__main__": diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index 65bca438078634a..d6f07ffcd746350 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -6032,13 +6032,10 @@ def doit(ty): class TestModule: - def test_deprecated__version__(self): - with self.assertWarnsRegex( - DeprecationWarning, - "'__version__' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(self.decimal, "__version__") - self.assertEqual(cm.filename, __file__) + def test_deprecated_module_attributes(self): + support.check_deprecated_module_attributes( + self, self.decimal, "__version__", remove=(3, 20), + ) @requires_cdecimal diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 63f65a9c5cf47a9..c82084ce299d77c 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1742,13 +1742,10 @@ def test_https_client(self): class TestModule(unittest.TestCase): - def test_deprecated__version__(self): - with self.assertWarnsRegex( - DeprecationWarning, - "'__version__' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(http.server, "__version__") - self.assertEqual(cm.filename, __file__) + def test_deprecated_module_attributes(self): + support.check_deprecated_module_attributes( + self, http.server, "__version__", remove=(3, 20), + ) def setUpModule(): diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py index da63da54e8ef733..daf5b0ee496c03c 100644 --- a/Lib/test/test_imaplib.py +++ b/Lib/test/test_imaplib.py @@ -2796,13 +2796,10 @@ def test_ssl_verified(self): class TestModule(unittest.TestCase): - def test_deprecated__version__(self): - with self.assertWarnsRegex( - DeprecationWarning, - "'__version__' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(imaplib, "__version__") - self.assertEqual(cm.filename, __file__) + def test_deprecated_module_attributes(self): + support.check_deprecated_module_attributes( + self, imaplib, "__version__", remove=(3, 20), + ) if __name__ == "__main__": diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 32cbac3dff131a9..6d9d066ad34e7f1 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -36,6 +36,7 @@ except ImportError: ThreadPoolExecutor = None +from test import support from test.support import cpython_only, import_helper from test.support import MISSING_C_DOCSTRINGS, ALWAYS_EQ from test.support import run_no_yield_async_fn, EqualToForwardRef @@ -43,7 +44,6 @@ from test.support.os_helper import TESTFN, TESTFN_UNDECODABLE, temp_cwd from test.support.script_helper import assert_python_ok, assert_python_failure, kill_python from test.support import has_subprocess_support -from test import support from test.test_inspect import inspect_fodder as mod from test.test_inspect import inspect_fodder2 as mod2 @@ -6974,13 +6974,10 @@ def f(): class TestModule(unittest.TestCase): - def test_deprecated__author__(self): - with self.assertWarnsRegex( - DeprecationWarning, - "'__author__' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(inspect, "__author__") - self.assertEqual(cm.filename, __file__) + def test_deprecated_module_attributes(self): + support.check_deprecated_module_attributes( + self, inspect, "__author__", remove=(3, 20), + ) if __name__ == "__main__": diff --git a/Lib/test/test_io/test_general.py b/Lib/test/test_io/test_general.py index 0dbe06e99a09a6d..4f50885e45b942e 100644 --- a/Lib/test/test_io/test_general.py +++ b/Lib/test/test_io/test_general.py @@ -1428,13 +1428,10 @@ def test_writer_subclass(self): class TestModule(unittest.TestCase): - def test_deprecated__author__(self): - with self.assertWarnsRegex( - DeprecationWarning, - "'__author__' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(io, "__author__") - self.assertEqual(cm.filename, __file__) + def test_deprecated_module_attributes(self): + support.check_deprecated_module_attributes( + self, io, "__author__", remove=(3, 20), + ) if __name__ == "__main__": diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py index 600e1f19a9cdc28..758774effc183c0 100644 --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -13,6 +13,7 @@ import ipaddress import weakref from collections.abc import Iterator +from test import support from test.support import LARGEST, SMALLEST @@ -2913,13 +2914,10 @@ def testNetworkV6HashCollisions(self): class TestModule(unittest.TestCase): - def test_deprecated__version__(self): - with self.assertWarnsRegex( - DeprecationWarning, - "'__version__' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(ipaddress, "__version__") - self.assertEqual(cm.filename, __file__) + def test_deprecated_module_attributes(self): + support.check_deprecated_module_attributes( + self, ipaddress, "__version__", remove=(3, 20), + ) if __name__ == '__main__': diff --git a/Lib/test/test_json/__init__.py b/Lib/test/test_json/__init__.py index 26b8f10325f06a2..d14938eef18f9e0 100644 --- a/Lib/test/test_json/__init__.py +++ b/Lib/test/test_json/__init__.py @@ -48,21 +48,10 @@ def test_cjson(self): class TestModule(unittest.TestCase): - def test_deprecated__version__(self): - with self.assertWarnsRegex( - DeprecationWarning, - "'__version__' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(json, "__version__") - self.assertEqual(cm.filename, __file__) - - def test_deprecated__author__(self): - with self.assertWarnsRegex( - DeprecationWarning, - "'__author__' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(json, "__author__") - self.assertEqual(cm.filename, __file__) + def test_deprecated_module_attributes(self): + support.check_deprecated_module_attributes( + self, json, "__version__", "__author__", remove=(3, 20), + ) def load_tests(loader, _, pattern): @@ -71,6 +60,7 @@ def load_tests(loader, _, pattern): suite.addTest(doctest.DocTestSuite(mod)) suite.addTest(TestPyTest('test_pyjson')) suite.addTest(TestCTest('test_cjson')) + suite.addTests(loader.loadTestsFromTestCase(TestModule)) pkg_dir = os.path.dirname(__file__) return support.load_package_tests(pkg_dir, loader, suite, pattern) diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 5e773ab9235cc51..0595a5e72b06183 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -7587,13 +7587,11 @@ def test__all__(self): class TestModule(unittest.TestCase): - def test_deprecated__version__and__date__and__author__(self): - msg = "is deprecated and slated for removal in Python 3.20" - for attr in ("__version__", "__date__", "__author__"): - with self.subTest(attr=attr): - with self.assertWarnsRegex(DeprecationWarning, msg) as cm: - getattr(logging, attr) - self.assertEqual(cm.filename, __file__) + def test_deprecated_module_attributes(self): + support.check_deprecated_module_attributes( + self, logging, "__version__", "__date__", "__author__", + remove=(3, 20), + ) # Set the locale to the platform-dependent default. I have no idea diff --git a/Lib/test/test_optparse.py b/Lib/test/test_optparse.py index d5e69b01dfbdee9..10ecdd3bccc23f7 100644 --- a/Lib/test/test_optparse.py +++ b/Lib/test/test_optparse.py @@ -1674,21 +1674,10 @@ def test_translations(self): class TestModule(unittest.TestCase): - def test_deprecated__version__(self): - with self.assertWarnsRegex( - DeprecationWarning, - "'__version__' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(optparse, "__version__") - self.assertEqual(cm.filename, __file__) - - def test_deprecated__copyright__(self): - with self.assertWarnsRegex( - DeprecationWarning, - "'__copyright__' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(optparse, "__copyright__") - self.assertEqual(cm.filename, __file__) + def test_deprecated_module_attributes(self): + support.check_deprecated_module_attributes( + self, optparse, "__version__", "__copyright__", remove=(3, 20), + ) if __name__ == '__main__': diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py index 0631bcde705a1fa..6435b64eec73c83 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -841,13 +841,10 @@ def test_help(self): class TestModule(unittest.TestCase): - def test_deprecated__copyright__(self): - with self.assertWarnsRegex( - DeprecationWarning, - "'__copyright__' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(platform, "__copyright__") - self.assertEqual(cm.filename, __file__) + def test_deprecated_module_attributes(self): + support.check_deprecated_module_attributes( + self, platform, "__copyright__", remove=(3, 20), + ) if __name__ == '__main__': diff --git a/Lib/test/test_pydoc/test_pydoc.py b/Lib/test/test_pydoc/test_pydoc.py index 548cbe2faab5061..f57e596d67cf7de 100644 --- a/Lib/test/test_pydoc/test_pydoc.py +++ b/Lib/test/test_pydoc/test_pydoc.py @@ -2491,13 +2491,11 @@ def __version__(self): class TestModule(unittest.TestCase): - def test_deprecated__author__and__date__and__credits__(self): - msg = "is deprecated and slated for removal in Python 3.20" - for attr in ("__author__", "__date__", "__credits__"): - with self.subTest(attr=attr): - with self.assertWarnsRegex(DeprecationWarning, msg) as cm: - getattr(pydoc, attr) - self.assertEqual(cm.filename, __file__) + def test_deprecated_module_attributes(self): + support.check_deprecated_module_attributes( + self, pydoc, "__author__", "__date__", "__credits__", + remove=(3, 20), + ) def setUpModule(): diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index 8e7611344b9150d..0be8d2864219f3d 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -1,3 +1,4 @@ +from test import support from test.support import (gc_collect, bigmemtest, _2G, cpython_only, captured_stdout, check_disallow_instantiation, linked_to_musl, @@ -3378,13 +3379,10 @@ def test_re_tests(self): class TestModule(unittest.TestCase): - def test_deprecated__version__(self): - with self.assertWarnsRegex( - DeprecationWarning, - "'__version__' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(re, "__version__") - self.assertEqual(cm.filename, __file__) + def test_deprecated_module_attributes(self): + support.check_deprecated_module_attributes( + self, re, "__version__", remove=(3, 20), + ) if __name__ == "__main__": diff --git a/Lib/test/test_sax.py b/Lib/test/test_sax.py index 46b29650d9383b5..cc80a141ed949a3 100644 --- a/Lib/test/test_sax.py +++ b/Lib/test/test_sax.py @@ -24,6 +24,7 @@ import sys from urllib.error import URLError import urllib.request +from test import support from test.support import os_helper from test.support import findfile, check__all__ from test.support.os_helper import FakePath, TESTFN @@ -1590,15 +1591,12 @@ def test_all(self): class TestModule(unittest.TestCase): - def test_deprecated__version__and__date__(self): + def test_deprecated_module_attributes(self): for module in (sax.expatreader, sax.handler): with self.subTest(module=module): - with self.assertWarnsRegex( - DeprecationWarning, - "'version' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(module, "version") - self.assertEqual(cm.filename, __file__) + support.check_deprecated_module_attributes( + self, module, "version", remove=(3, 20), + ) if __name__ == "__main__": diff --git a/Lib/test/test_socketserver.py b/Lib/test/test_socketserver.py index ca33a9a6dac8d34..4f2ed7140a749b3 100644 --- a/Lib/test/test_socketserver.py +++ b/Lib/test/test_socketserver.py @@ -516,13 +516,10 @@ class MyServer(socketserver.ThreadingMixIn, socketserver.TCPServer): class TestModule(unittest.TestCase): - def test_deprecated__version__(self): - with self.assertWarnsRegex( - DeprecationWarning, - "'__version__' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(socketserver, "__version__") - self.assertEqual(cm.filename, __file__) + def test_deprecated_module_attributes(self): + test.support.check_deprecated_module_attributes( + self, socketserver, "__version__", remove=(3, 20), + ) if __name__ == "__main__": diff --git a/Lib/test/test_tabnanny.py b/Lib/test/test_tabnanny.py index 85e3e76e9007ca8..e41616423b546dc 100644 --- a/Lib/test/test_tabnanny.py +++ b/Lib/test/test_tabnanny.py @@ -12,6 +12,7 @@ import tokenize import tempfile import textwrap +from test import support from test.support import captured_stderr, captured_stdout, script_helper from test.support.os_helper import unlink @@ -355,13 +356,10 @@ def test_double_verbose_mode(self): class TestModule(TestCase): - def test_deprecated__version__(self): - with self.assertWarnsRegex( - DeprecationWarning, - "'__version__' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(tabnanny, "__version__") - self.assertEqual(cm.filename, __file__) + def test_deprecated_module_attributes(self): + support.check_deprecated_module_attributes( + self, tabnanny, "__version__", remove=(3, 20), + ) if __name__ == "__main__": diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index 0fa2ed8d2a995b2..c317b9124d14848 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -5451,21 +5451,11 @@ def test_ignore_invalid_offset_headers(self): class TestModule(unittest.TestCase): - def test_deprecated_version(self): - with self.assertWarnsRegex( - DeprecationWarning, - "'version' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(tarfile, "version") - self.assertEqual(cm.filename, __file__) - - def test_deprecated__author__and__credits__(self): - msg = "is deprecated and slated for removal in Python 3.20" - for attr in ("__author__", "__credits__"): - with self.subTest(attr=attr): - with self.assertWarnsRegex(DeprecationWarning, msg) as cm: - getattr(tarfile, attr) - self.assertEqual(cm.filename, __file__) + def test_deprecated_module_attributes(self): + support.check_deprecated_module_attributes( + self, tarfile, "version", "__author__", "__credits__", + remove=(3, 20), + ) def setUpModule(): diff --git a/Lib/test/test_tkinter/test_font.py b/Lib/test/test_tkinter/test_font.py index 16c9c1dc22bbca7..c80098271039d19 100644 --- a/Lib/test/test_tkinter/test_font.py +++ b/Lib/test/test_tkinter/test_font.py @@ -2,6 +2,7 @@ import unittest import tkinter from tkinter import font +from test import support from test.support import requires, gc_collect, ALWAYS_EQ from test.test_tkinter.support import setUpModule # noqa: F401 from test.test_tkinter.support import AbstractTkTest, AbstractDefaultRootTest @@ -364,13 +365,10 @@ def test_nametofont(self): class TestModule(unittest.TestCase): - def test_deprecated__version__(self): - with self.assertWarnsRegex( - DeprecationWarning, - "'__version__' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(font, "__version__") - self.assertEqual(cm.filename, __file__) + def test_deprecated_module_attributes(self): + support.check_deprecated_module_attributes( + self, font, "__version__", remove=(3, 20), + ) if __name__ == "__main__": diff --git a/Lib/test/test_tokenize.py b/Lib/test/test_tokenize.py index 788a6bcba7d17f5..241951364e4ecb9 100644 --- a/Lib/test/test_tokenize.py +++ b/Lib/test/test_tokenize.py @@ -3897,13 +3897,10 @@ def test_prefixes(self): class TestModule(unittest.TestCase): - def test_deprecated__author__and__credits__(self): - msg = "is deprecated and slated for removal in Python 3.20" - for attr in ("__author__", "__credits__"): - with self.subTest(attr=attr): - with self.assertWarnsRegex(DeprecationWarning, msg) as cm: - getattr(tokenize, attr) - self.assertEqual(cm.filename, __file__) + def test_deprecated_module_attributes(self): + support.check_deprecated_module_attributes( + self, tokenize, "__author__", "__credits__", remove=(3, 20), + ) if __name__ == "__main__": diff --git a/Lib/test/test_ttk/__init__.py b/Lib/test/test_ttk/__init__.py index c980dc32fcbd83b..d066b70873c4392 100644 --- a/Lib/test/test_ttk/__init__.py +++ b/Lib/test/test_ttk/__init__.py @@ -20,21 +20,10 @@ class TestModule(unittest.TestCase): - def test_deprecated__version__(self): - with self.assertWarnsRegex( - DeprecationWarning, - "'__version__' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(ttk, "__version__") - self.assertEqual(cm.filename, __file__) - - def test_deprecated__author__(self): - with self.assertWarnsRegex( - DeprecationWarning, - "'__author__' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(ttk, "__author__") - self.assertEqual(cm.filename, __file__) + def test_deprecated_module_attributes(self): + support.check_deprecated_module_attributes( + self, ttk, "__version__", "__author__", remove=(3, 20), + ) def setUpModule(): diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py index 43bcecb44d97d32..8ad3f0af0895c5d 100755 --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py @@ -1554,13 +1554,10 @@ def test_windll_getnode(self): class TestModule(unittest.TestCase): - def test_deprecated__author__(self): - with self.assertWarnsRegex( - DeprecationWarning, - "'__author__' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(py_uuid, "__author__") - self.assertEqual(cm.filename, __file__) + def test_deprecated_module_attributes(self): + support.check_deprecated_module_attributes( + self, py_uuid, "__author__", remove=(3, 20), + ) if __name__ == '__main__': diff --git a/Lib/test/test_wsgiref.py b/Lib/test/test_wsgiref.py index 484a2db1637861c..564385224096e07 100644 --- a/Lib/test/test_wsgiref.py +++ b/Lib/test/test_wsgiref.py @@ -986,15 +986,12 @@ def testRaisesControlCharacters(self): class TestModule(unittest.TestCase): - def test_deprecated__version__(self): + def test_deprecated_module_attributes(self): from wsgiref import simple_server - with self.assertWarnsRegex( - DeprecationWarning, - "'__version__' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(simple_server, "__version__") - self.assertEqual(cm.filename, __file__) + support.check_deprecated_module_attributes( + self, simple_server, "__version__", remove=(3, 20), + ) if __name__ == "__main__": diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py index 899947c1f8e0d74..f0cf7dffab0e8a9 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -5323,13 +5323,10 @@ def get_option(config, option_name, default=None): class TestModule(unittest.TestCase): - def test_deprecated_version(self): - with self.assertWarnsRegex( - DeprecationWarning, - "'VERSION' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(ET, "VERSION") - self.assertEqual(cm.filename, __file__) + def test_deprecated_module_attributes(self): + support.check_deprecated_module_attributes( + self, ET, "VERSION", remove=(3, 20), + ) # -------------------------------------------------------------------- diff --git a/Lib/test/test_zlib.py b/Lib/test/test_zlib.py index dcb4eac1b779520..3243448043b5c21 100644 --- a/Lib/test/test_zlib.py +++ b/Lib/test/test_zlib.py @@ -1312,13 +1312,10 @@ def __index__(self): class TestModule(unittest.TestCase): - def test_deprecated__version__(self): - with self.assertWarnsRegex( - DeprecationWarning, - "'__version__' is deprecated and slated for removal in Python 3.20", - ) as cm: - getattr(zlib, "__version__") - self.assertEqual(cm.filename, __file__) + def test_deprecated_module_attributes(self): + support.check_deprecated_module_attributes( + self, zlib, "__version__", remove=(3, 20), + ) if __name__ == "__main__":