summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/sys.rst6
-rw-r--r--Lib/test/test_sys.py14
-rw-r--r--Misc/NEWS3
-rw-r--r--Python/sysmodule.c101
4 files changed, 100 insertions, 24 deletions
diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst
index aecef74..a00c516 100644
--- a/Doc/library/sys.rst
+++ b/Doc/library/sys.rst
@@ -769,8 +769,12 @@ always available.
*micro*, *releaselevel*, and *serial*. All values except *releaselevel* are
integers; the release level is ``'alpha'``, ``'beta'``, ``'candidate'``, or
``'final'``. The ``version_info`` value corresponding to the Python version 2.0
- is ``(2, 0, 0, 'final', 0)``.
+ is ``(2, 0, 0, 'final', 0)``. The components can also be accessed by name,
+ so ``sys.version_info[0]`` is equivalent to ``sys.version_info.major``
+ and so on.
+ .. versionchanged:: 2.7
+ Added named component attributes
.. data:: warnoptions
diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py
index 7655fbf..fad9939 100644
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -298,13 +298,25 @@ class SysModuleTest(unittest.TestCase):
self.assert_(isinstance(sys.prefix, str))
self.assert_(isinstance(sys.version, str))
vi = sys.version_info
- self.assert_(isinstance(vi, tuple))
+ self.assert_(isinstance(vi[:], tuple))
self.assertEqual(len(vi), 5)
self.assert_(isinstance(vi[0], int))
self.assert_(isinstance(vi[1], int))
self.assert_(isinstance(vi[2], int))
self.assert_(vi[3] in ("alpha", "beta", "candidate", "final"))
self.assert_(isinstance(vi[4], int))
+ self.assert_(isinstance(vi.major, int))
+ self.assert_(isinstance(vi.minor, int))
+ self.assert_(isinstance(vi.micro, int))
+ self.assert_(vi.releaselevel in
+ ("alpha", "beta", "candidate", "final"))
+ self.assert_(isinstance(vi.serial, int))
+ self.assertEqual(vi[0], vi.major)
+ self.assertEqual(vi[1], vi.minor)
+ self.assertEqual(vi[2], vi.micro)
+ self.assertEqual(vi[3], vi.releaselevel)
+ self.assertEqual(vi[4], vi.serial)
+ self.assert_(vi > (1,0,0))
def test_43581(self):
# Can't use sys.stdout, as this is a StringIO object when
diff --git a/Misc/NEWS b/Misc/NEWS
index 98a7cef..b6e06d7 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -155,6 +155,9 @@ Core and Builtins
Library
-------
+- Issue #4285: Change sys.version_info to be a named tuple. Patch by
+ Ross Light.
+
- Issue #1520877: Now distutils.sysconfig reads $AR from the
environment/Makefile. Patch by Douglas Greiman.
diff --git a/Python/sysmodule.c b/Python/sysmodule.c
index f703e09..b47a25b 100644
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -1017,7 +1017,7 @@ maxunicode -- the largest supported character\n\
builtin_module_names -- tuple of module names built into this interpreter\n\
subversion -- subversion information of the build as tuple\n\
version -- the version of this interpreter as a string\n\
-version_info -- version information as a tuple\n\
+version_info -- version information as a named tuple\n\
hexversion -- version information encoded as a single integer\n\
copyright -- copyright notice pertaining to this interpreter\n\
platform -- platform identifier\n\
@@ -1227,6 +1227,75 @@ make_flags(void)
return seq;
}
+PyDoc_STRVAR(version_info__doc__,
+"sys.version_info\n\
+\n\
+Version information as a named tuple.");
+
+static PyTypeObject VersionInfoType;
+
+static PyStructSequence_Field version_info_fields[] = {
+ {"major", "Major release number"},
+ {"minor", "Minor release number"},
+ {"micro", "Patch release number"},
+ {"releaselevel", "'alpha', 'beta', 'candidate', or 'release'"},
+ {"serial", "Serial release number"},
+ {0}
+};
+
+static PyStructSequence_Desc version_info_desc = {
+ "sys.version_info", /* name */
+ version_info__doc__, /* doc */
+ version_info_fields, /* fields */
+ 5
+};
+
+static PyObject *
+make_version_info(void)
+{
+ PyObject *version_info;
+ char *s;
+ int pos = 0;
+
+ version_info = PyStructSequence_New(&VersionInfoType);
+ if (version_info == NULL) {
+ return NULL;
+ }
+
+ /*
+ * These release level checks are mutually exclusive and cover
+ * the field, so don't get too fancy with the pre-processor!
+ */
+#if PY_RELEASE_LEVEL == PY_RELEASE_LEVEL_ALPHA
+ s = "alpha";
+#elif PY_RELEASE_LEVEL == PY_RELEASE_LEVEL_BETA
+ s = "beta";
+#elif PY_RELEASE_LEVEL == PY_RELEASE_LEVEL_GAMMA
+ s = "candidate";
+#elif PY_RELEASE_LEVEL == PY_RELEASE_LEVEL_FINAL
+ s = "final";
+#endif
+
+#define SetIntItem(flag) \
+ PyStructSequence_SET_ITEM(version_info, pos++, PyLong_FromLong(flag))
+#define SetStrItem(flag) \
+ PyStructSequence_SET_ITEM(version_info, pos++, PyUnicode_FromString(flag))
+
+ SetIntItem(PY_MAJOR_VERSION);
+ SetIntItem(PY_MINOR_VERSION);
+ SetIntItem(PY_MICRO_VERSION);
+ SetStrItem(s);
+ SetIntItem(PY_RELEASE_SERIAL);
+#undef SetIntItem
+#undef SetStrItem
+
+ if (PyErr_Occurred()) {
+ Py_CLEAR(version_info);
+ return NULL;
+ }
+ return version_info;
+}
+
static struct PyModuleDef sysmodule = {
PyModuleDef_HEAD_INIT,
"sys",
@@ -1239,8 +1308,6 @@ static struct PyModuleDef sysmodule = {
NULL
};
-
-
PyObject *
_PySys_Init(void)
{
@@ -1291,25 +1358,6 @@ _PySys_Init(void)
svn_revision));
SET_SYS_FROM_STRING("dont_write_bytecode",
PyBool_FromLong(Py_DontWriteBytecodeFlag));
- /*
- * These release level checks are mutually exclusive and cover
- * the field, so don't get too fancy with the pre-processor!
- */
-#if PY_RELEASE_LEVEL == PY_RELEASE_LEVEL_ALPHA
- s = "alpha";
-#elif PY_RELEASE_LEVEL == PY_RELEASE_LEVEL_BETA
- s = "beta";
-#elif PY_RELEASE_LEVEL == PY_RELEASE_LEVEL_GAMMA
- s = "candidate";
-#elif PY_RELEASE_LEVEL == PY_RELEASE_LEVEL_FINAL
- s = "final";
-#endif
-
- SET_SYS_FROM_STRING("version_info",
- Py_BuildValue("iiiUi", PY_MAJOR_VERSION,
- PY_MINOR_VERSION,
- PY_MICRO_VERSION, s,
- PY_RELEASE_SERIAL));
SET_SYS_FROM_STRING("api_version",
PyLong_FromLong(PYTHON_API_VERSION));
SET_SYS_FROM_STRING("copyright",
@@ -1361,6 +1409,15 @@ _PySys_Init(void)
PyDict_SetItemString(sysdict, "warnoptions", warnoptions);
}
+ /* version_info */
+ if (VersionInfoType.tp_name == 0)
+ PyStructSequence_InitType(&VersionInfoType, &version_info_desc);
+ SET_SYS_FROM_STRING("version_info", make_version_info());
+ /* prevent user from creating new instances */
+ VersionInfoType.tp_init = NULL;
+ VersionInfoType.tp_new = NULL;
+
+ /* flags */
if (FlagsType.tp_name == 0)
PyStructSequence_InitType(&FlagsType, &flags_desc);
SET_SYS_FROM_STRING("flags", make_flags());