gh-76007: Deprecate __version__ attribute (#138675)

Co-authored-by: AN Long <aisk@users.noreply.github.com>
Co-authored-by: Petr Viktorin <encukou@gmail.com>
This commit is contained in:
Hugo van Kemenade
2025-09-29 11:03:23 +02:00
committed by GitHub
parent 408154d64a
commit 872eafd2b0
34 changed files with 320 additions and 29 deletions

View File

@@ -9,6 +9,8 @@ Deprecations
.. include:: pending-removal-in-3.19.rst
.. include:: pending-removal-in-3.20.rst
.. include:: pending-removal-in-future.rst
C API deprecations

View File

@@ -0,0 +1,23 @@
Pending removal in Python 3.20
------------------------------
* The ``__version__`` attribute has been deprecated in these standard library
modules and will be removed in Python 3.20.
Use :py:data:`sys.version_info` instead.
- :mod:`argparse`
- :mod:`csv`
- :mod:`!ctypes.macholib`
- :mod:`ipaddress`
- :mod:`json`
- :mod:`logging` (``__date__`` also deprecated)
- :mod:`optparse`
- :mod:`pickle`
- :mod:`platform`
- :mod:`re`
- :mod:`socketserver`
- :mod:`tabnanny`
- :mod:`tkinter.font`
- :mod:`tkinter.ttk`
(Contributed by Hugo van Kemenade in :gh:`76007`.)

View File

@@ -1363,6 +1363,10 @@ Deprecated
.. include:: ../deprecations/pending-removal-in-3.17.rst
.. include:: ../deprecations/pending-removal-in-3.19.rst
.. include:: ../deprecations/pending-removal-in-3.20.rst
.. include:: ../deprecations/pending-removal-in-future.rst
.. _whatsnew312-removed:

View File

@@ -2026,6 +2026,10 @@ New Deprecations
.. include:: ../deprecations/pending-removal-in-3.17.rst
.. include:: ../deprecations/pending-removal-in-3.19.rst
.. include:: ../deprecations/pending-removal-in-3.20.rst
.. include:: ../deprecations/pending-removal-in-future.rst
CPython Bytecode Changes

View File

@@ -2794,6 +2794,8 @@ Deprecated
.. include:: ../deprecations/pending-removal-in-3.19.rst
.. include:: ../deprecations/pending-removal-in-3.20.rst
.. include:: ../deprecations/pending-removal-in-future.rst

View File

@@ -631,9 +631,42 @@ hashlib
(Contributed by Bénédikt Tran in :gh:`134978`.)
__version__
-----------
* The ``__version__`` attribute has been deprecated in these standard library
modules and will be removed in Python 3.20.
Use :py:data:`sys.version_info` instead.
- :mod:`argparse`
- :mod:`csv`
- :mod:`!ctypes.macholib`
- :mod:`ipaddress`
- :mod:`json`
- :mod:`logging` (``__date__`` also deprecated)
- :mod:`optparse`
- :mod:`pickle`
- :mod:`platform`
- :mod:`re`
- :mod:`socketserver`
- :mod:`tabnanny`
- :mod:`tkinter.font`
- :mod:`tkinter.ttk`
(Contributed by Hugo van Kemenade in :gh:`76007`.)
.. Add deprecations above alphabetically, not here at the end.
.. include:: ../deprecations/pending-removal-in-3.16.rst
.. include:: ../deprecations/pending-removal-in-3.17.rst
.. include:: ../deprecations/pending-removal-in-3.19.rst
.. include:: ../deprecations/pending-removal-in-3.20.rst
.. include:: ../deprecations/pending-removal-in-future.rst
Removed
=======

View File

@@ -64,7 +64,6 @@ considered public as object names -- the API of the formatter objects is
still considered an implementation detail.)
"""
__version__ = '1.1'
__all__ = [
'ArgumentParser',
'ArgumentError',
@@ -2773,3 +2772,12 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
def _warning(self, message):
args = {'prog': self.prog, 'message': message}
self._print_message(_('%(prog)s: warning: %(message)s\n') % args, _sys.stderr)
def __getattr__(name):
if name == "__version__":
from warnings import _deprecated
_deprecated("__version__", remove=(3, 20))
return "1.1" # Do not change
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

View File

@@ -81,8 +81,6 @@ __all__ = ["QUOTE_MINIMAL", "QUOTE_ALL", "QUOTE_NONNUMERIC", "QUOTE_NONE",
"unregister_dialect", "DictReader", "DictWriter",
"unix_dialect"]
__version__ = "1.0"
class Dialect:
"""Describe a CSV dialect.
@@ -511,3 +509,12 @@ class Sniffer:
hasHeader -= 1
return hasHeader > 0
def __getattr__(name):
if name == "__version__":
from warnings import _deprecated
_deprecated("__version__", remove=(3, 20))
return "1.0" # Do not change
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

View File

@@ -6,4 +6,10 @@ See the relevant header files in /usr/include/mach-o
And also Apple's documentation.
"""
__version__ = '1.0'
def __getattr__(name):
if name == "__version__":
from warnings import _deprecated
_deprecated("__version__", remove=(3, 20))
return "1.0" # Do not change
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

View File

@@ -8,9 +8,6 @@ and networks.
"""
__version__ = '1.0'
import functools
IPV4LENGTH = 32
@@ -2419,3 +2416,12 @@ class _IPv6Constants:
IPv6Address._constants = _IPv6Constants
IPv6Network._constants = _IPv6Constants
def __getattr__(name):
if name == "__version__":
from warnings import _deprecated
_deprecated("__version__", remove=(3, 20))
return "1.0" # Do not change
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

View File

@@ -95,7 +95,6 @@ Using json from the shell to validate and pretty-print::
$ echo '{ 1.2:3.4}' | python -m json
Expecting property name enclosed in double quotes: line 1 column 3 (char 2)
"""
__version__ = '2.0.9'
__all__ = [
'dump', 'dumps', 'load', 'loads',
'JSONDecoder', 'JSONDecodeError', 'JSONEncoder',
@@ -357,3 +356,12 @@ def loads(s, *, cls=None, object_hook=None, parse_float=None,
if parse_constant is not None:
kw['parse_constant'] = parse_constant
return cls(**kw).decode(s)
def __getattr__(name):
if name == "__version__":
from warnings import _deprecated
_deprecated("__version__", remove=(3, 20))
return "2.0.9" # Do not change
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

View File

@@ -45,9 +45,6 @@ import threading
__author__ = "Vinay Sajip <vinay_sajip@red-dove.com>"
__status__ = "production"
# The following module attributes are no longer updated.
__version__ = "0.5.1.2"
__date__ = "07 February 2010"
#---------------------------------------------------------------------------
# Miscellaneous module data
@@ -2341,3 +2338,16 @@ def captureWarnings(capture):
if _warnings_showwarning is not None:
warnings.showwarning = _warnings_showwarning
_warnings_showwarning = None
def __getattr__(name):
if name in ("__version__", "__date__"):
from warnings import _deprecated
_deprecated(name, remove=(3, 20))
return { # Do not change
"__version__": "0.5.1.2",
"__date__": "07 February 2010",
}[name]
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

View File

@@ -21,8 +21,6 @@ Simple usage example:
(options, args) = parser.parse_args()
"""
__version__ = "1.5.3"
__all__ = ['Option',
'make_option',
'SUPPRESS_HELP',
@@ -1669,3 +1667,12 @@ def _match_abbrev(s, wordmap):
# which will become a factory function when there are many Option
# classes.
make_option = Option
def __getattr__(name):
if name == "__version__":
from warnings import _deprecated
_deprecated("__version__", remove=(3, 20))
return "1.5.3" # Do not change
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

View File

@@ -17,7 +17,6 @@ Functions:
Misc variables:
__version__
format_version
compatible_formats

View File

@@ -110,8 +110,6 @@ __copyright__ = """
"""
__version__ = '1.1.0'
import collections
import os
import re
@@ -1436,5 +1434,14 @@ def _main(args: list[str] | None = None):
print(platform(aliased, terse))
def __getattr__(name):
if name == "__version__":
from warnings import _deprecated
_deprecated("__version__", remove=(3, 20))
return "1.1.0" # Do not change
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
if __name__ == "__main__":
_main()

View File

@@ -137,8 +137,6 @@ __all__ = [
"UNICODE", "NOFLAG", "RegexFlag", "PatternError"
]
__version__ = "2.2.1"
@enum.global_enum
@enum._simple_enum(enum.IntFlag, boundary=enum.KEEP)
class RegexFlag:
@@ -426,3 +424,12 @@ class Scanner:
append(action)
i = j
return result, string[i:]
def __getattr__(name):
if name == "__version__":
from warnings import _deprecated
_deprecated("__version__", remove=(3, 20))
return "2.2.1" # Do not change
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

View File

@@ -120,9 +120,6 @@ BaseServer:
# Author of the BaseServer patch: Luke Kenneth Casson Leighton
__version__ = "0.4"
import socket
import selectors
import os
@@ -861,3 +858,12 @@ class DatagramRequestHandler(BaseRequestHandler):
def finish(self):
self.socket.sendto(self.wfile.getvalue(), self.client_address)
def __getattr__(name):
if name == "__version__":
from warnings import _deprecated
_deprecated("__version__", remove=(3, 20))
return "0.4" # Do not change
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

View File

@@ -16,8 +16,6 @@ releases; such changes may not be backward compatible.
# XXX The API needs to undergo changes however; the current code is too
# XXX script-like. This will be addressed later.
__version__ = "6"
import os
import sys
import tokenize
@@ -334,5 +332,14 @@ def _process_tokens(tokens):
raise NannyNag(start[0], msg, line)
def __getattr__(name):
if name == "__version__":
from warnings import _deprecated
_deprecated("__version__", remove=(3, 20))
return "6" # Do not change
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
if __name__ == '__main__':
main()

View File

@@ -7356,6 +7356,16 @@ class TestColorized(TestCase):
'''))
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 tearDownModule():
# Remove global references to avoid looking like we have refleaks.
RFile.seen = {}

View File

@@ -1603,5 +1603,16 @@ class MiscTestCase(unittest.TestCase):
with self.subTest(tp=tp):
check_disallow_instantiation(self, tp)
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__)
if __name__ == '__main__':
unittest.main()

View File

@@ -108,5 +108,17 @@ class MachOTest(unittest.TestCase):
d('P', 'F.framework/Versions/A/F_debug', 'F', 'A', 'debug'))
class TestModule(unittest.TestCase):
def test_deprecated__version__(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__)
if __name__ == "__main__":
unittest.main()

View File

@@ -2821,5 +2821,15 @@ class IpaddrUnitTest(unittest.TestCase):
)
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__)
if __name__ == '__main__':
unittest.main()

View File

@@ -47,6 +47,16 @@ class TestCTest(CTest):
'_json')
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 load_tests(loader, _, pattern):
suite = unittest.TestSuite()
for mod in (json, json.encoder, json.decoder):

View File

@@ -7236,6 +7236,16 @@ class MiscTestCase(unittest.TestCase):
support.check__all__(self, logging, not_exported=not_exported)
class TestModule(unittest.TestCase):
def test_deprecated__version__and__date__(self):
msg = "is deprecated and slated for removal in Python 3.20"
for attr in ("__version__", "__date__"):
with self.subTest(attr=attr):
with self.assertWarnsRegex(DeprecationWarning, msg) as cm:
getattr(logging, attr)
self.assertEqual(cm.filename, __file__)
# Set the locale to the platform-dependent default. I have no idea
# why the test does this, but in any case we save the current locale
# first and restore it at the end.

View File

@@ -1666,6 +1666,16 @@ class TestTranslations(TestTranslationsBase):
self.assertMsgidsEqual(optparse)
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__)
if __name__ == '__main__':
# To regenerate translation snapshots
if len(sys.argv) > 1 and sys.argv[1] == '--snapshot-update':

View File

@@ -3116,5 +3116,15 @@ class ExternalTests(unittest.TestCase):
self.assertTrue(obj.search(s))
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__)
if __name__ == "__main__":
unittest.main()

View File

@@ -511,5 +511,15 @@ class MiscTestCase(unittest.TestCase):
server.server_close()
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__)
if __name__ == "__main__":
unittest.main()

View File

@@ -3,7 +3,8 @@
Glossary:
* errored : Whitespace related problems present in file.
"""
from unittest import TestCase, mock
from unittest import TestCase, main, mock
import errno
import os
import tabnanny
@@ -352,3 +353,17 @@ class TestCommandLine(TestCase):
"offending line: '\\tprint(\"world\")'"
).strip()
self.validate_cmd("-vv", path, stdout=stdout, partial=True)
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__)
if __name__ == "__main__":
main()

View File

@@ -159,5 +159,15 @@ class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase):
self.assertRaises(RuntimeError, font.nametofont, fontname)
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__)
if __name__ == "__main__":
unittest.main()

View File

@@ -19,6 +19,16 @@ from _tkinter import TclError
from tkinter import ttk
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 setUpModule():
root = None
try:

View File

@@ -6,7 +6,6 @@
import itertools
import tkinter
__version__ = "0.9"
__all__ = ["NORMAL", "ROMAN", "BOLD", "ITALIC",
"nametofont", "Font", "families", "names"]
@@ -198,6 +197,15 @@ def names(root=None):
return root.tk.splitlist(root.tk.call("font", "names"))
def __getattr__(name):
if name == "__version__":
from warnings import _deprecated
_deprecated("__version__", remove=(3, 20))
return "0.9" # Do not change
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
# --------------------------------------------------------------------
# test stuff

View File

@@ -12,8 +12,6 @@ maintaining the widget state and invoking callbacks, all aspects
of the widgets appearance lies at Themes.
"""
__version__ = "0.3.1"
__author__ = "Guilherme Polo <ggpolo@gmail.com>"
__all__ = ["Button", "Checkbutton", "Combobox", "Entry", "Frame", "Label",
@@ -1648,3 +1646,12 @@ class OptionMenu(Menubutton):
except AttributeError:
pass
super().destroy()
def __getattr__(name):
if name == "__version__":
from warnings import _deprecated
_deprecated("__version__", remove=(3, 20))
return "0.3.1" # Do not change
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

View File

@@ -0,0 +1,2 @@
Deprecate ``__version__`` from a number of standard library modules. Patch
by Hugo van Kemenade.

4
Misc/sbom.spdx.json generated
View File

@@ -944,11 +944,11 @@
"checksums": [
{
"algorithm": "SHA1",
"checksumValue": "0fbc026a9771d9675e7094790b5b945334d3cb53"
"checksumValue": "0d83ed429ede0a2c6617cd2f24490be16d8393f4"
},
{
"algorithm": "SHA256",
"checksumValue": "1e77c01eec8f167ed10b754f153c0c743c8e5196ae9c81dffc08f129ab56dbfd"
"checksumValue": "876f4486d08d3eac3581a8681d7fb3cec2fe5b823d1bef27cca15e755510365c"
}
],
"fileName": "Lib/ctypes/macholib/__init__.py"