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
23 changes: 21 additions & 2 deletions Doc/library/pyexpat.rst
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,29 @@ This module provides the following exception, type object and data items:
like ``'expat_2.8.4'``.


.. data:: VERSION_INFO

A named tuple containing the three components of the Expat 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 ``xml.parsers.expat.VERSION_INFO[0]`` is equivalent to
``xml.parsers.expat.VERSION_INFO.major`` and so on.
This may be different from the Expat library actually used at runtime,
which is available as :const:`version_info`.

.. versionadded:: next


.. data:: version_info

The version of the Expat library loaded by the interpreter,
as a tuple of three integers: major, minor and micro version.
A named tuple containing the version of the Expat library
loaded by the interpreter,
with the same fields as :const:`VERSION_INFO`.

.. versionchanged:: next
It is now a named tuple.


.. data:: features
Expand Down
6 changes: 6 additions & 0 deletions Doc/whatsnew/3.16.rst
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,12 @@ xml
building a tree for it.
(Contributed by Serhiy Storchaka in :gh:`63102`.)

* Added constant :const:`~xml.parsers.expat.VERSION_INFO` in the
:mod:`XML parser <xml.parsers.expat>` module, which provides information
about the version of the Expat library that was used for building the module.
:const:`~xml.parsers.expat.version_info` is now a named tuple.
(Contributed by Serhiy Storchaka in :gh:`157473`.)

zipfile
-------

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

attributes = ('EXPAT_VERSION',)
attributes = ('EXPAT_VERSION', 'VERSION_INFO', 'version_info')
copy_attributes(info_add, expat, 'expat.%s', attributes)


Expand Down
29 changes: 29 additions & 0 deletions Lib/test/test_pyexpat.py
Original file line number Diff line number Diff line change
Expand Up @@ -1402,5 +1402,34 @@ def test_set_maximum_amplification__amplification_not_exceeded(self):
self.assertIsNotNone(parser.Parse(payload, True))


class VersionTest(unittest.TestCase):

def _test_version_info(self, v):
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)

def test_version_info(self):
if support.verbose:
print(f'EXPAT_VERSION = {expat.EXPAT_VERSION}', flush=True)
print(f'VERSION_INFO = {expat.VERSION_INFO}', flush=True)
print(f'version_info = {expat.version_info}', flush=True)
self._test_version_info(expat.VERSION_INFO)
self._test_version_info(expat.version_info)
self.assertEqual(expat.EXPAT_VERSION, 'expat_%d.%d.%d' % expat.version_info)
self.assertEqual(expat.VERSION_INFO[0], expat.version_info[0])


if __name__ == "__main__":
unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Add constant :const:`xml.parsers.expat.VERSION_INFO` which provides
information about the version of the Expat library that was used for
building the module.
:const:`xml.parsers.expat.version_info` is now a named tuple.
83 changes: 74 additions & 9 deletions Modules/pyexpat.c
Original file line number Diff line number Diff line change
Expand Up @@ -2436,6 +2436,78 @@ pyexpat_capsule_destructor(PyObject *capsule)
}


PyDoc_STRVAR(version_info__doc__,
"pyexpat.version_info\n\
\n\
Expat version information as a named tuple.");

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

static PyStructSequence_Desc version_info_desc = {
"pyexpat.version_info", /* name */
version_info__doc__, /* doc */
version_info_fields, /* fields */
3
};

static PyObject *
make_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 int
add_version_info(PyObject *mod)
{
PyTypeObject *version_type;
version_type = PyStructSequence_NewType(&version_info_desc);
if (version_type == NULL) {
return -1;
}
if (PyModule_Add(mod, "VERSION_INFO",
make_version_info(version_type, XML_MAJOR_VERSION,
XML_MINOR_VERSION, XML_MICRO_VERSION)) < 0)
{
Py_DECREF(version_type);
return -1;
}
XML_Expat_Version info = XML_ExpatVersionInfo();
if (PyModule_Add(mod, "version_info",
make_version_info(version_type, info.major,
info.minor, info.micro)) < 0)
{
Py_DECREF(version_type);
return -1;
}
Py_DECREF(version_type);
return 0;
}

static int
pyexpat_exec(PyObject *mod)
{
Expand Down Expand Up @@ -2479,15 +2551,8 @@ pyexpat_exec(PyObject *mod)
XML_ExpatVersion()) < 0) {
return -1;
}
{
XML_Expat_Version info = XML_ExpatVersionInfo();
PyObject *versionInfo = Py_BuildValue("(iii)",
info.major,
info.minor,
info.micro);
if (PyModule_Add(mod, "version_info", versionInfo) < 0) {
return -1;
}
if (add_version_info(mod) < 0) {
return -1;
}
/* XXX When Expat supports some way of figuring out how it was
compiled, this should check and set native_encoding
Expand Down
Loading