diff --git a/Doc/deprecations/pending-removal-in-3.21.rst b/Doc/deprecations/pending-removal-in-3.21.rst index 5d0c56612094ff..18b89a20e4a208 100644 --- a/Doc/deprecations/pending-removal-in-3.21.rst +++ b/Doc/deprecations/pending-removal-in-3.21.rst @@ -1,6 +1,15 @@ Pending removal in Python 3.21 ------------------------------ +* :mod:`abc` + + * Soft-deprecated since Python 3.3 :class:`abc.abstractclassmethod`, + :class:`abc.abstractstaticmethod`, and :class:`abc.abstractproperty` + now raise a :exc:`DeprecationWarning`. + These classes will be removed in Python 3.21, instead + use :func:`abc.abstractmethod` with :func:`classmethod`, + :func:`staticmethod`, and :class:`property` respectively. + * :mod:`ast`: * Classes ``slice``, ``Index``, ``ExtSlice``, ``Suite``, ``Param``, diff --git a/Doc/library/abc.rst b/Doc/library/abc.rst index 8112cfee7d204d..904eff2e31e6ac 100644 --- a/Doc/library/abc.rst +++ b/Doc/library/abc.rst @@ -237,7 +237,7 @@ The :mod:`!abc` module also supports the following legacy decorators: .. decorator:: abstractclassmethod .. versionadded:: 3.2 - .. deprecated:: 3.3 + .. deprecated-removed:: 3.3 3.21 It is now possible to use :class:`classmethod` with :func:`abstractmethod`, making this decorator redundant. @@ -258,7 +258,7 @@ The :mod:`!abc` module also supports the following legacy decorators: .. decorator:: abstractstaticmethod .. versionadded:: 3.2 - .. deprecated:: 3.3 + .. deprecated-removed:: 3.3 3.21 It is now possible to use :class:`staticmethod` with :func:`abstractmethod`, making this decorator redundant. @@ -278,7 +278,7 @@ The :mod:`!abc` module also supports the following legacy decorators: .. decorator:: abstractproperty - .. deprecated:: 3.3 + .. deprecated-removed:: 3.3 3.21 It is now possible to use :class:`property`, :meth:`property.getter`, :meth:`property.setter` and :meth:`property.deleter` with :func:`abstractmethod`, making this decorator redundant. diff --git a/Doc/library/gettext.rst b/Doc/library/gettext.rst index 2de16fe40362b3..2ab7ba7df19cf1 100644 --- a/Doc/library/gettext.rst +++ b/Doc/library/gettext.rst @@ -51,19 +51,19 @@ class-based API instead. .. index:: single: _ (underscore); gettext -.. function:: gettext(message) +.. function:: gettext(message, /) Return the localized translation of *message*, based on the current global domain, language, and locale directory. This function is usually aliased as :func:`!_` in the local namespace (see examples below). -.. function:: dgettext(domain, message) +.. function:: dgettext(domain, message, /) Like :func:`.gettext`, but look the message up in the specified *domain*. -.. function:: ngettext(singular, plural, n) +.. function:: ngettext(singular, plural, n, /) Like :func:`.gettext`, but consider plural forms. If a translation is found, apply the plural formula to *n*, and return the resulting message (some @@ -78,15 +78,15 @@ class-based API instead. formulas for a variety of languages. -.. function:: dngettext(domain, singular, plural, n) +.. function:: dngettext(domain, singular, plural, n, /) Like :func:`ngettext`, but look the message up in the specified *domain*. -.. function:: pgettext(context, message) -.. function:: dpgettext(domain, context, message) -.. function:: npgettext(context, singular, plural, n) -.. function:: dnpgettext(domain, context, singular, plural, n) +.. function:: pgettext(context, message, /) +.. function:: dpgettext(domain, context, message, /) +.. function:: npgettext(context, singular, plural, n, /) +.. function:: dnpgettext(domain, context, singular, plural, n, /) Similar to the corresponding functions without the ``p`` in the prefix (that is, :func:`gettext`, :func:`dgettext`, :func:`ngettext`, :func:`dngettext`), @@ -223,20 +223,20 @@ are the methods of :class:`!NullTranslations`: translation for a given message. - .. method:: gettext(message) + .. method:: gettext(message, /) If a fallback has been set, forward :meth:`!gettext` to the fallback. Otherwise, return *message*. Overridden in derived classes. - .. method:: ngettext(singular, plural, n) + .. method:: ngettext(singular, plural, n, /) If a fallback has been set, forward :meth:`!ngettext` to the fallback. Otherwise, return *singular* if *n* is 1; return *plural* otherwise. Overridden in derived classes. - .. method:: pgettext(context, message) + .. method:: pgettext(context, message, /) If a fallback has been set, forward :meth:`pgettext` to the fallback. Otherwise, return the translated message. Overridden in derived classes. @@ -244,7 +244,7 @@ are the methods of :class:`!NullTranslations`: .. versionadded:: 3.8 - .. method:: npgettext(context, singular, plural, n) + .. method:: npgettext(context, singular, plural, n, /) If a fallback has been set, forward :meth:`npgettext` to the fallback. Otherwise, return the translated message. Overridden in derived classes. @@ -322,7 +322,7 @@ unexpected, or if other problems occur while reading the file, instantiating a The following methods are overridden from the base class implementation: - .. method:: gettext(message) + .. method:: gettext(message, /) Look up the *message* id in the catalog and return the corresponding message string, as a Unicode string. If there is no entry in the catalog for the @@ -331,7 +331,7 @@ unexpected, or if other problems occur while reading the file, instantiating a *message* id is returned. - .. method:: ngettext(singular, plural, n) + .. method:: ngettext(singular, plural, n, /) Do a plural-forms lookup of a message id. *singular* is used as the message id for purposes of lookup in the catalog, while *n* is used to determine which @@ -352,7 +352,7 @@ unexpected, or if other problems occur while reading the file, instantiating a n) % {'num': n} - .. method:: pgettext(context, message) + .. method:: pgettext(context, message, /) Look up the *context* and *message* id in the catalog and return the corresponding message string, as a Unicode string. If there is no @@ -363,7 +363,7 @@ unexpected, or if other problems occur while reading the file, instantiating a .. versionadded:: 3.8 - .. method:: npgettext(context, singular, plural, n) + .. method:: npgettext(context, singular, plural, n, /) Do a plural-forms lookup of a message id. *singular* is used as the message id for purposes of lookup in the catalog, while *n* is used to diff --git a/Doc/library/math.rst b/Doc/library/math.rst index 9cc8c5d6886324..41a9a0ab55d8fa 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -255,10 +255,12 @@ Floating point manipulation functions .. function:: frexp(x) - Return the mantissa and exponent of *x* as the pair ``(m, e)``. *m* is a float - and *e* is an integer such that ``x == m * 2**e`` exactly. If *x* is zero, - returns ``(0.0, 0)``, otherwise ``0.5 <= abs(m) < 1``. This is used to "pick - apart" the internal representation of a float in a portable way. + Return the mantissa and exponent of *x* as the pair ``(m, e)``. + If *x* is a finite nonzero number, then *m* is a float with + ``0.5 <= abs(m) < 1.0`` and an integer *e* is such that + ``x == m * 2**e`` exactly. Else, return ``(x, 0)``. + This is used to "pick apart" the internal representation of + a float in a portable way. Note that :func:`frexp` has a different call/return pattern than its C equivalents: it takes a single argument and return a pair of diff --git a/Doc/library/mimetypes.rst b/Doc/library/mimetypes.rst index 5103eacde1dd5b..f33098faf7d8a7 100644 --- a/Doc/library/mimetypes.rst +++ b/Doc/library/mimetypes.rst @@ -358,7 +358,7 @@ it converts file extensions to MIME types. For each ``type`` entry, the script writes a line into the standard output stream. If an unknown type occurs, it writes an error message into the -standard error stream and exits with the return code ``1``. +standard output stream and exits with the return code ``1``. .. mimetypes-cli-example: @@ -385,7 +385,7 @@ interface: $ # get a MIME type for a rare file extension $ python -m mimetypes filename.pict - error: unknown extension of filename.pict + error: media type unknown for filename.pict $ # now look in the extended database built into Python $ python -m mimetypes --lenient filename.pict @@ -407,7 +407,8 @@ interface: $ python -m mimetypes filename.sh filename.nc filename.xxx filename.txt type: application/x-sh encoding: None type: application/x-netcdf encoding: None - error: unknown extension of filename.xxx + error: media type unknown for filename.xxx + type: text/plain encoding: None $ # try to feed an unknown MIME type $ python -m mimetypes --extension audio/aac audio/opus audio/future audio/x-wav diff --git a/Doc/library/multiprocessing.rst b/Doc/library/multiprocessing.rst index 7b17df08f7dc71..2d13053915830b 100644 --- a/Doc/library/multiprocessing.rst +++ b/Doc/library/multiprocessing.rst @@ -1723,7 +1723,10 @@ inherited by child processes. Note that *lock* is a keyword only argument. Note that an array of :data:`ctypes.c_char` has *value* and *raw* - attributes which allow one to use it to store and retrieve strings. + attributes which can both be used to store and retrieve byte strings. + While *raw* allows interaction with a :class:`bytes` object the full size of + the array, reading *value* will terminate after a null byte, like most + programming languages handle strings. The :mod:`!multiprocessing.sharedctypes` module diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index df6cc98eaf1c90..0378ada72804c9 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -1367,6 +1367,8 @@ Deprecated .. include:: ../deprecations/pending-removal-in-3.20.rst +.. include:: ../deprecations/pending-removal-in-3.21.rst + .. include:: ../deprecations/pending-removal-in-future.rst .. _whatsnew312-removed: diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 4272c04d80868b..0db8ec55e5c9f8 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -2031,6 +2031,8 @@ New Deprecations .. include:: ../deprecations/pending-removal-in-3.20.rst +.. include:: ../deprecations/pending-removal-in-3.21.rst + .. include:: ../deprecations/pending-removal-in-future.rst CPython Bytecode Changes diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 39d1d8da5367e5..cd0d8b7cb006fe 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -2748,6 +2748,8 @@ New deprecations .. include:: ../deprecations/pending-removal-in-3.20.rst +.. include:: ../deprecations/pending-removal-in-3.21.rst + .. include:: ../deprecations/pending-removal-in-future.rst diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 8e6b1faa523f68..1d27baf38906e9 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -2335,6 +2335,8 @@ New deprecations .. include:: ../deprecations/pending-removal-in-3.20.rst +.. include:: ../deprecations/pending-removal-in-3.21.rst + .. include:: ../deprecations/pending-removal-in-future.rst .. include:: ../deprecations/soft-deprecations.rst diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst index a6911b68c2eb75..9a0a0d3d8831f5 100644 --- a/Doc/whatsnew/3.16.rst +++ b/Doc/whatsnew/3.16.rst @@ -215,6 +215,15 @@ tarfile Deprecated ========== +* :mod:`abc` + + * Soft-deprecated since Python 3.3 :class:`abc.abstractclassmethod`, + :class:`abc.abstractstaticmethod`, and :class:`abc.abstractproperty` + now raise a :exc:`DeprecationWarning`. + These classes will be removed in Python 3.21, instead + use :func:`abc.abstractmethod` with :func:`classmethod`, + :func:`staticmethod`, and :class:`property` respectively. + * :mod:`ast`: * Classes ``slice``, ``Index``, ``ExtSlice``, ``Suite``, ``Param``, @@ -228,6 +237,14 @@ Deprecated .. Add deprecations above alphabetically, not here at the end. +.. 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 + Porting to Python 3.16 ====================== diff --git a/Lib/abc.py b/Lib/abc.py index f8a4e11ce9c3b1..08f708d1b89208 100644 --- a/Lib/abc.py +++ b/Lib/abc.py @@ -36,11 +36,15 @@ class C(ABC): def my_abstract_classmethod(cls, ...): ... + .. deprecated-removed: 3.3 3.21 + """ __isabstractmethod__ = True def __init__(self, callable): + import warnings + warnings._deprecated('abc.abstractclassmethod', remove=(3, 21)) callable.__isabstractmethod__ = True super().__init__(callable) @@ -56,11 +60,15 @@ class C(ABC): def my_abstract_staticmethod(...): ... + .. deprecated-removed: 3.3 3.21 + """ __isabstractmethod__ = True def __init__(self, callable): + import warnings + warnings._deprecated('abc.abstractstaticmethod', remove=(3, 21)) callable.__isabstractmethod__ = True super().__init__(callable) @@ -76,10 +84,23 @@ class C(ABC): def my_abstract_property(self): ... + .. deprecated-removed: 3.3 3.21 + """ __isabstractmethod__ = True + def __init__( + self, + fget=None, + fset=None, + fdel=None, + doc=None, + ): + import warnings + warnings._deprecated('abc.abstractproperty', remove=(3, 21)) + super().__init__(fget, fset, fdel, doc) + try: from _abc import (get_cache_token, _abc_init, _abc_register, diff --git a/Lib/test/test_abc.py b/Lib/test/test_abc.py index 80ee9e0ba56e75..59a45a2eda07b0 100644 --- a/Lib/test/test_abc.py +++ b/Lib/test/test_abc.py @@ -11,20 +11,30 @@ import abc import _py_abc from inspect import isabstract +from test.support import warnings_helper def test_factory(abc_ABCMeta, abc_get_cache_token): class TestLegacyAPI(unittest.TestCase): def test_abstractproperty_basics(self): - @abc.abstractproperty - def foo(self): pass + with self.assertWarnsRegex( + DeprecationWarning, + 'abstractproperty', + ): + @abc.abstractproperty + def foo(self): pass + self.assertTrue(foo.__isabstractmethod__) def bar(self): pass self.assertNotHasAttr(bar, "__isabstractmethod__") - class C(metaclass=abc_ABCMeta): - @abc.abstractproperty - def foo(self): return 3 + with self.assertWarnsRegex( + DeprecationWarning, + 'abstractproperty', + ): + class C(metaclass=abc_ABCMeta): + @abc.abstractproperty + def foo(self): return 3 self.assertRaises(TypeError, C) class D(C): @property @@ -33,16 +43,26 @@ def foo(self): return super().foo self.assertFalse(getattr(D.foo, "__isabstractmethod__", False)) def test_abstractclassmethod_basics(self): - @abc.abstractclassmethod - def foo(cls): pass + with self.assertWarnsRegex( + DeprecationWarning, + 'abstractclassmethod', + ): + @abc.abstractclassmethod + def foo(cls): pass + self.assertTrue(foo.__isabstractmethod__) @classmethod def bar(cls): pass self.assertFalse(getattr(bar, "__isabstractmethod__", False)) - class C(metaclass=abc_ABCMeta): - @abc.abstractclassmethod - def foo(cls): return cls.__name__ + with self.assertWarnsRegex( + DeprecationWarning, + 'abstractclassmethod', + ): + class C(metaclass=abc_ABCMeta): + @abc.abstractclassmethod + def foo(cls): return cls.__name__ + self.assertRaises(TypeError, C) class D(C): @classmethod @@ -51,16 +71,26 @@ def foo(cls): return super().foo() self.assertEqual(D().foo(), 'D') def test_abstractstaticmethod_basics(self): - @abc.abstractstaticmethod - def foo(): pass + with self.assertWarnsRegex( + DeprecationWarning, + 'abstractstaticmethod', + ): + @abc.abstractstaticmethod + def foo(): pass + self.assertTrue(foo.__isabstractmethod__) @staticmethod def bar(): pass self.assertFalse(getattr(bar, "__isabstractmethod__", False)) - class C(metaclass=abc_ABCMeta): - @abc.abstractstaticmethod - def foo(): return 3 + with self.assertWarnsRegex( + DeprecationWarning, + 'abstractstaticmethod', + ): + class C(metaclass=abc_ABCMeta): + @abc.abstractstaticmethod + def foo(): return 3 + self.assertRaises(TypeError, C) class D(C): @staticmethod @@ -168,6 +198,7 @@ def method_two(self): msg = r"class C without an implementation for abstract methods 'method_one', 'method_two'" self.assertRaisesRegex(TypeError, msg, C) + @warnings_helper.ignore_warnings(category=DeprecationWarning) def test_abstractmethod_integration(self): for abstractthing in [abc.abstractmethod, abc.abstractproperty, abc.abstractclassmethod, diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index b1cdbe04765ed0..e0e8dd4eccfb1b 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -2700,10 +2700,6 @@ def iconcat(b, a): # MODIFIES! b.wait() a += c - def irepeat(b, a): # MODIFIES! - b.wait() - a *= 2 - def subscript(b, a): b.wait() try: assert a[0] != 0xdd @@ -2837,9 +2833,10 @@ def check(funcs, a=None, *args): check([clear] + [repeat] * 10) check([clear] + [iconcat] * 10) - check([clear] + [irepeat] * 10) check([clear] + [ass_subscript] * 10) check([clear] + [repr_] * 10) + # gh-148605: Do not test "a *= 2" since it allocates up to 4 GiB using + # 10 threads # value errors diff --git a/Lib/test/test_mimetypes.py b/Lib/test/test_mimetypes.py index 607aff8418edcf..eccdc5bdf70795 100644 --- a/Lib/test/test_mimetypes.py +++ b/Lib/test/test_mimetypes.py @@ -6,8 +6,9 @@ import unittest.mock from platform import win32_edition from test import support -from test.support import cpython_only, force_not_colorized, os_helper +from test.support import cpython_only, force_not_colorized, os_helper, requires_subprocess from test.support.import_helper import ensure_lazy_imports +from test.support.script_helper import assert_python_ok, assert_python_failure try: import _winapi @@ -511,5 +512,59 @@ def test_invocation_error(self): self.assertEqual(result, expected) +@requires_subprocess() +class CommandLineSubprocessTest(unittest.TestCase): + def test_help(self): + rc, stdout, stderr = assert_python_ok('-m', 'mimetypes', '--help') + self.assertIn(b'mimetypes', stdout) + self.assertIn(b'--extension', stdout) + self.assertIn(b'--lenient', stdout) + + def test_type_lookup(self): + rc, stdout, stderr = assert_python_ok('-m', 'mimetypes', 'foo.pdf') + self.assertEqual(stdout.strip(), b'type: application/pdf encoding: None') + self.assertEqual(stderr, b'') + + def test_type_lookup_unknown(self): + rc, stdout, stderr = assert_python_failure('-m', 'mimetypes', 'foo.unknownext12345') + self.assertEqual(stdout.strip(), b'error: media type unknown for foo.unknownext12345') + self.assertEqual(stderr, b'') + + def test_extension_flag(self): + rc, stdout, stderr = assert_python_ok('-m', 'mimetypes', '-e', 'image/jpeg') + self.assertEqual(stdout.strip(), b'.jpg') + self.assertEqual(stderr, b'') + + def test_extension_flag_unknown(self): + rc, stdout, stderr = assert_python_failure('-m', 'mimetypes', '-e', 'image/unknowntype12345') + self.assertEqual(stdout.strip(), b'error: unknown type image/unknowntype12345') + self.assertEqual(stderr, b'') + + def test_lenient_flag(self): + rc, stdout, stderr = assert_python_ok('-m', 'mimetypes', '-e', '--lenient', 'text/xul') + self.assertIn(b'.xul', stdout) + self.assertEqual(stderr, b'') + + def test_multiple_inputs(self): + rc, stdout, stderr = assert_python_ok('-m', 'mimetypes', 'foo.pdf', 'foo.png') + self.assertIn(b'type: application/pdf encoding: None', stdout) + self.assertIn(b'type: image/png encoding: None', stdout) + self.assertEqual(stderr, b'') + + def test_multiple_inputs_with_error(self): + rc, stdout, stderr = assert_python_failure( + '-m', 'mimetypes', 'foo.pdf', 'foo.unknownext12345' + ) + self.assertIn(b'type: application/pdf encoding: None', stdout) + self.assertIn(b'error: media type unknown for foo.unknownext12345', stdout) + self.assertEqual(stderr, b'') + + @force_not_colorized + def test_unknown_flag(self): + rc, stdout, stderr = assert_python_failure('-m', 'mimetypes', '--unknown-flag', 'foo.pdf') + self.assertEqual(stdout, b'') + self.assertIn(b'error: unrecognized arguments: --unknown-flag', stderr) + + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2026-05-10-16-06-27.gh-issue-149609.yLKPXo.rst b/Misc/NEWS.d/next/Library/2026-05-10-16-06-27.gh-issue-149609.yLKPXo.rst new file mode 100644 index 00000000000000..3acba7d25021b2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-10-16-06-27.gh-issue-149609.yLKPXo.rst @@ -0,0 +1,3 @@ +Raise :exc:`DeprecationWarning` on using :class:`abc.abstractclassmethod`, +:class:`abc.abstractstaticmethod`, and :class:`abc.abstractproperty`, +schedule its removal for Python 3.21. diff --git a/Modules/clinic/mathmodule.c.h b/Modules/clinic/mathmodule.c.h index 6a1b1a45d1d93a..a5e583c180defe 100644 --- a/Modules/clinic/mathmodule.c.h +++ b/Modules/clinic/mathmodule.c.h @@ -198,8 +198,9 @@ PyDoc_STRVAR(math_frexp__doc__, "\n" "Return the mantissa and exponent of x, as pair (m, e).\n" "\n" -"m is a float and e is an int, such that x = m * 2.**e.\n" -"If x is 0, m and e are both 0. Else 0.5 <= abs(m) < 1.0."); +"If x is a finite nonzero number, then m is a float with\n" +"0.5 <= abs(m) < 1.0 and an integer e is such that\n" +"x == m * 2**e exactly. Else, return (x, 0)."); #define MATH_FREXP_METHODDEF \ {"frexp", (PyCFunction)math_frexp, METH_O, math_frexp__doc__}, @@ -1163,4 +1164,4 @@ math_ulp(PyObject *module, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=80c666aef8d2df36 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=3452ce8caa2d1bd7 input=a9049054013a1b77]*/ diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index a7616ad70e4afe..5636a00afe10c4 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -1438,13 +1438,14 @@ math.frexp Return the mantissa and exponent of x, as pair (m, e). -m is a float and e is an int, such that x = m * 2.**e. -If x is 0, m and e are both 0. Else 0.5 <= abs(m) < 1.0. +If x is a finite nonzero number, then m is a float with +0.5 <= abs(m) < 1.0 and an integer e is such that +x == m * 2**e exactly. Else, return (x, 0). [clinic start generated code]*/ static PyObject * math_frexp_impl(PyObject *module, double x) -/*[clinic end generated code: output=03e30d252a15ad4a input=96251c9e208bc6e9]*/ +/*[clinic end generated code: output=03e30d252a15ad4a input=215cf8ea28a0959b]*/ { int i; /* deal with special cases directly, to sidestep platform