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
40 changes: 40 additions & 0 deletions Doc/library/decimal.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1632,6 +1632,46 @@ are also included in the pure Python version for compatibility.

.. versionadded:: 3.8.3

The following constants are only available in the C module.

.. data:: LIBMPDEC_VERSION

The version string of the libmpdec library that was used for building
the module.
This may be different from the libmpdec library actually used at runtime,
which is available as :const:`libmpdec_version`.

.. versionadded:: next

.. data:: libmpdec_version

The version string of the libmpdec library actually loaded by the
interpreter.

.. versionadded:: next

.. data:: LIBMPDEC_VERSION_INFO

A named tuple containing the three components of the libmpdec library
version that was used for building the module:
*major*, *minor*, and *micro*.
All values are integers.
The components can also be accessed by name,
so ``decimal.LIBMPDEC_VERSION_INFO[0]`` is equivalent to
``decimal.LIBMPDEC_VERSION_INFO.major`` and so on.
This may be different from the libmpdec library actually used at runtime,
which is available as :const:`libmpdec_version_info`.

.. versionadded:: next

.. data:: libmpdec_version_info

A named tuple containing the version of the libmpdec library
actually loaded by the interpreter,
with the same fields as :const:`LIBMPDEC_VERSION_INFO`.

.. versionadded:: next


Rounding modes
--------------
Expand Down
10 changes: 10 additions & 0 deletions Doc/whatsnew/3.16.rst
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,16 @@ concurrent.futures
(Contributed by xzmeng and Serhiy Storchaka in :gh:`108518`.)


decimal
-------

* Added constants :const:`~decimal.LIBMPDEC_VERSION`,
:const:`~decimal.libmpdec_version`, :const:`~decimal.LIBMPDEC_VERSION_INFO`,
and :const:`~decimal.libmpdec_version_info` in the C implementation,
which provide information about the version of the libmpdec library in use.
(Contributed by Serhiy Storchaka in :gh:`157475`.)


difflib
-------

Expand Down
2 changes: 1 addition & 1 deletion Lib/test/pythoninfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -761,7 +761,7 @@ def collect_decimal(info_add):
except ImportError:
return

attributes = ('__libmpdec_version__',)
attributes = ('LIBMPDEC_VERSION', 'libmpdec_version')
copy_attributes(info_add, _decimal, '_decimal.%s', attributes)


Expand Down
41 changes: 39 additions & 2 deletions Lib/test/test_decimal.py
Original file line number Diff line number Diff line change
Expand Up @@ -4534,8 +4534,13 @@ def test_module_attributes(self):

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__))
# Information about the libmpdec library, specific to the C module.
libmpdec_names = {'LIBMPDEC_VERSION', 'LIBMPDEC_VERSION_INFO',
'libmpdec_version', 'libmpdec_version_info'}
self.assertLessEqual(set(dir(C)) - libmpdec_names, set(dir(P)))
self.assertEqual([n for n in dir(C)
if n[:2] != '__' and n not in libmpdec_names],
sorted(P.__all__))

def test_context_attributes(self):

Expand Down Expand Up @@ -5059,6 +5064,38 @@ def test_constants(self):
self.assertEqual(C.DecTraps,
C.DecErrors|C.DecOverflow|C.DecUnderflow)

@requires_cdecimal
class CVersion(unittest.TestCase):
"""Information about the libmpdec library in _decimal"""

def _test_libmpdec_version(self, v, string):
self.assertIsInstance(v[:], tuple)
self.assertEqual(len(v), 3)
self.assertIsInstance(v[0], int)
self.assertIsInstance(v[1], int)
self.assertIsInstance(v[2], int)
self.assertIsInstance(v.major, int)
self.assertIsInstance(v.minor, int)
self.assertIsInstance(v.micro, int)
self.assertEqual(v[0], v.major)
self.assertEqual(v[1], v.minor)
self.assertEqual(v[2], v.micro)
self.assertGreaterEqual(v.major, 2)
self.assertGreaterEqual(v.minor, 0)
self.assertGreaterEqual(v.micro, 0)
self.assertEqual(string, '%d.%d.%d' % v)

def test_libmpdec_version(self):
if support.verbose:
print(f'LIBMPDEC_VERSION = {C.LIBMPDEC_VERSION}', flush=True)
print(f'libmpdec_version = {C.libmpdec_version}', flush=True)
print(f'LIBMPDEC_VERSION_INFO = {C.LIBMPDEC_VERSION_INFO}', flush=True)
print(f'libmpdec_version_info = {C.libmpdec_version_info}', flush=True)
self._test_libmpdec_version(C.LIBMPDEC_VERSION_INFO, C.LIBMPDEC_VERSION)
self._test_libmpdec_version(C.libmpdec_version_info, C.libmpdec_version)
self.assertEqual(C.LIBMPDEC_VERSION_INFO[0], C.libmpdec_version_info[0])
self.assertIs(C.libmpdec_version, C.__libmpdec_version__)

@requires_cdecimal
class CWhitebox(unittest.TestCase):
"""Whitebox testing for _decimal"""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Add constants :const:`decimal.LIBMPDEC_VERSION`,
:const:`decimal.libmpdec_version`, :const:`decimal.LIBMPDEC_VERSION_INFO`,
and :const:`decimal.libmpdec_version_info` in the C implementation, which
provide information about the version of the libmpdec library in use.
100 changes: 99 additions & 1 deletion Modules/_decimal/_decimal.c
Original file line number Diff line number Diff line change
Expand Up @@ -7744,6 +7744,104 @@ cfunc_noargs(PyTypeObject *t, const char *name)
return NULL;
}

PyDoc_STRVAR(libmpdec_version_info__doc__,
"decimal.libmpdec_version_info\n\
\n\
libmpdec version information as a named tuple.");

static PyStructSequence_Field libmpdec_version_info_fields[] = {
{"major", "Major release number"},
{"minor", "Minor release number"},
{"micro", "Micro release number"},
{0}
};

static PyStructSequence_Desc libmpdec_version_info_desc = {
"decimal.libmpdec_version_info", /* name */
libmpdec_version_info__doc__, /* doc */
libmpdec_version_info_fields, /* fields */
3
};

static PyObject *
make_libmpdec_version_info(PyTypeObject *type, int major, int minor, int micro)
{
PyObject *version;
int pos = 0;

version = PyStructSequence_New(type);
if (version == NULL) {
return NULL;
}

#define SetItem(VALUE) \
PyStructSequence_SET_ITEM(version, pos++, VALUE); \
if (PyErr_Occurred()) { \
Py_DECREF(version); \
return NULL; \
}

SetItem(PyLong_FromLong(major))
SetItem(PyLong_FromLong(minor))
SetItem(PyLong_FromLong(micro))
#undef SetItem

return version;
}

static PyObject *
parse_libmpdec_version_info(PyTypeObject *type, const char *version)
{
int major, minor, micro;
if (sscanf(version, "%d.%d.%d", &major, &minor, &micro) != 3) {
PyErr_Format(PyExc_RuntimeError,
"unexpected libmpdec version string %s", version);
return NULL;
}
return make_libmpdec_version_info(type, major, minor, micro);
}

static int
add_version_constants(PyObject *m)
{
const char *version = mpd_version();
if (PyModule_AddStringConstant(m, "LIBMPDEC_VERSION", MPD_VERSION) < 0) {
return -1;
}
PyObject *obj = PyUnicode_FromString(version);
if (obj == NULL) {
return -1;
}
if (PyModule_AddObjectRef(m, "libmpdec_version", obj) < 0 ||
PyModule_AddObjectRef(m, "__libmpdec_version__", obj) < 0)
{
Py_DECREF(obj);
return -1;
}
Py_DECREF(obj);
PyTypeObject *version_type;
version_type = PyStructSequence_NewType(&libmpdec_version_info_desc);
if (version_type == NULL) {
return -1;
}
if (PyModule_Add(m, "LIBMPDEC_VERSION_INFO",
make_libmpdec_version_info(version_type, MPD_MAJOR_VERSION,
MPD_MINOR_VERSION,
MPD_MICRO_VERSION)) < 0)
{
Py_DECREF(version_type);
return -1;
}
if (PyModule_Add(m, "libmpdec_version_info",
parse_libmpdec_version_info(version_type, version)) < 0)
{
Py_DECREF(version_type);
return -1;
}
Py_DECREF(version_type);
return 0;
}

static int minalloc_is_set = 0;

static int
Expand Down Expand Up @@ -7992,7 +8090,7 @@ _decimal_exec(PyObject *m)

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

return 0;

Expand Down
Loading