From 81fe09344cfa290efd6041269e70a2d0b629a7c8 Mon Sep 17 00:00:00 2001 From: Eric Smith Date: Fri, 6 Feb 2009 00:48:26 +0000 Subject: [PATCH] Implement issue #4285, convert sys.version_info to a named tuple. Patch by Ross Light. --- Doc/library/sys.rst | 6 ++- Lib/test/test_sys.py | 14 ++++++- Misc/NEWS | 3 ++ Python/sysmodule.c | 99 +++++++++++++++++++++++++++++++++++--------- 4 files changed, 100 insertions(+), 22 deletions(-) diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index afdf52c5524..8e6f3991e0f 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -853,9 +853,13 @@ 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. .. versionadded:: 2.0 + .. versionchanged:: 2.7 + Added named component attributes .. data:: warnoptions diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 8e10ec6a4bb..bd819c606ee 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -340,13 +340,25 @@ class SysModuleTest(unittest.TestCase): self.assert_(isinstance(sys.prefix, basestring)) self.assert_(isinstance(sys.version, basestring)) 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 cStringIO object when diff --git a/Misc/NEWS b/Misc/NEWS index 81115ed1d99..6ec126930b8 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -149,6 +149,9 @@ Core and Builtins Library ------- +- Issue #4285: Change sys.version_info to be a named tuple. Patch by + Ross Light. + - Issue #1276768: The verbose option was not used in the code of distutils.file_util and distutils.dir_util. diff --git a/Python/sysmodule.c b/Python/sysmodule.c index ff7157fc650..91f947d37e8 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1048,7 +1048,7 @@ maxsize -- the largest supported length of containers.\n\ maxunicode -- the largest supported character\n\ builtin_module_names -- tuple of module names built into this interpreter\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\ @@ -1279,6 +1279,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 = {0, 0, 0, 0, 0, 0}; + +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++, PyInt_FromLong(flag)) +#define SetStrItem(flag) \ + PyStructSequence_SET_ITEM(version_info, pos++, PyString_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; +} + PyObject * _PySys_Init(void) { @@ -1354,25 +1423,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("iiisi", PY_MAJOR_VERSION, - PY_MINOR_VERSION, - PY_MICRO_VERSION, s, - PY_RELEASE_SERIAL)); SET_SYS_FROM_STRING("api_version", PyInt_FromLong(PYTHON_API_VERSION)); SET_SYS_FROM_STRING("copyright", @@ -1429,6 +1479,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());