Skip to content
Open
Show file tree
Hide file tree
Changes from 8 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:`ipaddress`
- :mod:`json`
- :mod:`logging` (``__date__`` also deprecated)
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:`ipaddress`
- :mod:`json`
- :mod:`logging` (``__date__`` also deprecated)
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