mirror of
https://github.com/python/cpython.git
synced 2026-10-28 10:25:05 +08:00
gh-157473: Add structured version info for pyexpat (GH-157474)
Add constant pyexpat.VERSION_INFO which provides information about the version of the Expat library that was used for building the module. Make pyexpat.version_info a named tuple. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
3b0c39cb8b
commit
cbecd5edd9
6 files changed
+135
-12
No files matched your search
+21
-2
@@ -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
|
||||
|
||||
@@ -825,6 +825,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
|
||||
-------
|
||||
|
||||
|
||||
@@ -748,7 +748,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)
|
||||
|
||||
|
||||
|
||||
@@ -1393,5 +1393,34 @@ class MemoryProtectionTest(AttackProtectionTestBase, unittest.TestCase):
|
||||
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()
|
||||
@@ -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.
|
||||
+74
-9
@@ -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)
|
||||
{
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user