Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Doc/deprecations/pending-removal-in-3.20.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Pending removal in Python 3.20
- :mod:`argparse`
- :mod:`csv`
- :mod:`!ctypes.macholib`
- :mod:`decimal` (use :data:`decimal.SPEC_VERSION` instead)
- :mod:`imaplib`
- :mod:`ipaddress`
- :mod:`json`
Expand Down
11 changes: 10 additions & 1 deletion Doc/library/decimal.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1569,7 +1569,16 @@ In addition to the three supplied contexts, new contexts can be created with the
Constants
---------

The constants in this section are only relevant for the C module. They
.. data:: SPEC_VERSION

The highest version of the General Decimal Arithmetic
Specification that this implementation complies with.
See https://speleotrove.com/decimal/decarith.html for the specification.

.. versionadded:: next


The following constants are only relevant for the C module. They
are also included in the pure Python version for compatibility.

+---------------------------------+---------------------+-------------------------------+
Expand Down
1 change: 1 addition & 0 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,7 @@ New deprecations
- :mod:`argparse`
- :mod:`csv`
- :mod:`!ctypes.macholib`
- :mod:`decimal` (use :data:`decimal.SPEC_VERSION` instead)
- :mod:`imaplib`
- :mod:`ipaddress`
- :mod:`json`
Expand Down
17 changes: 14 additions & 3 deletions Lib/_pydecimal.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,16 @@
'HAVE_THREADS',

# C version: compile time choice that enables the coroutine local context
'HAVE_CONTEXTVAR'
'HAVE_CONTEXTVAR',

# Highest version of the spec this module complies with
'SPEC_VERSION',
]

__xname__ = __name__ # sys.modules lookup (--without-threads)
__name__ = 'decimal' # For pickling
__version__ = '1.70' # Highest version of the spec this complies with
# See http://speleotrove.com/decimal/
SPEC_VERSION = '1.70' # Highest version of the spec this complies with
# See https://speleotrove.com/decimal/decarith.html
__libmpdec_version__ = "2.4.2" # compatible libmpdec version

import math as _math
Expand Down Expand Up @@ -6399,3 +6402,11 @@ def _format_number(is_negative, intpart, fracpart, exp, spec):
# _PyHASH_10INV is the inverse of 10 modulo the prime _PyHASH_MODULUS
_PyHASH_10INV = pow(10, _PyHASH_MODULUS - 2, _PyHASH_MODULUS)
del sys

def __getattr__(name):
if name == "__version__":
from warnings import _deprecated

_deprecated("__version__", remove=(3, 20))
return SPEC_VERSION
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
2 changes: 1 addition & 1 deletion Lib/decimal.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@

try:
from _decimal import *
from _decimal import __version__ # noqa: F401
from _decimal import __libmpdec_version__ # noqa: F401
from _decimal import __getattr__ # noqa: F401
except ImportError:
import _pydecimal
import sys
Expand Down
19 changes: 18 additions & 1 deletion Lib/test/test_decimal.py
Original file line number Diff line number Diff line change
Expand Up @@ -4474,7 +4474,7 @@ def test_module_attributes(self):
self.assertTrue(C.HAVE_THREADS is True or C.HAVE_THREADS is False)
self.assertTrue(P.HAVE_THREADS is True or P.HAVE_THREADS is False)

self.assertEqual(C.__version__, P.__version__)
self.assertEqual(C.SPEC_VERSION, P.SPEC_VERSION)

self.assertLessEqual(set(dir(C)), set(dir(P)))
self.assertEqual([n for n in dir(C) if n[:2] != '__'], sorted(P.__all__))
Expand Down Expand Up @@ -5929,6 +5929,23 @@ def doit(ty):
doit('Context')


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__)


@requires_cdecimal
class CTestModule(TestModule, unittest.TestCase):
decimal = C
class PyTestModule(TestModule, unittest.TestCase):
decimal = P


def load_tests(loader, tests, pattern):
if TODO_TESTS is not None:
# Run only Arithmetic tests
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
:mod:`decimal`: Deprecate ``__version__`` and replace with
:data:`decimal.SPEC_VERSION`.
28 changes: 27 additions & 1 deletion Modules/_decimal/_decimal.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@

#include "clinic/_decimal.c.h"

#define MPD_SPEC_VERSION "1.70" // Highest version of the spec this complies with
// See https://speleotrove.com/decimal/decarith.html

/*[clinic input]
module _decimal
class _decimal.Decimal "PyObject *" "&dec_spec"
Expand Down Expand Up @@ -7566,12 +7569,35 @@ static PyType_Spec context_spec = {
};


static PyObject *
decimal_getattr(PyObject *self, PyObject *args)
{
PyObject *name;
if (!PyArg_UnpackTuple(args, "__getattr__", 1, 1, &name)) {
return NULL;
}

if (PyUnicode_Check(name) && PyUnicode_EqualToUTF8(name, "__version__")) {
if (PyErr_WarnEx(PyExc_DeprecationWarning,
"'__version__' is deprecated and slated for removal in Python 3.20",
1) < 0) {
return NULL;
}
return PyUnicode_FromString(MPD_SPEC_VERSION);
}

PyErr_Format(PyExc_AttributeError, "module 'decimal' has no attribute %R", name);
return NULL;
}


static PyMethodDef _decimal_methods [] =
{
_DECIMAL_GETCONTEXT_METHODDEF
_DECIMAL_SETCONTEXT_METHODDEF
_DECIMAL_LOCALCONTEXT_METHODDEF
_DECIMAL_IEEECONTEXT_METHODDEF
{"__getattr__", decimal_getattr, METH_VARARGS, "Module __getattr__"},
{ NULL, NULL, 1, NULL }
};

Expand Down Expand Up @@ -7891,7 +7917,7 @@ _decimal_exec(PyObject *m)
}

/* Add specification version number */
CHECK_INT(PyModule_AddStringConstant(m, "__version__", "1.70"));
CHECK_INT(PyModule_AddStringConstant(m, "SPEC_VERSION", MPD_SPEC_VERSION));
CHECK_INT(PyModule_AddStringConstant(m, "__libmpdec_version__", mpd_version()));

return 0;
Expand Down
Loading