From 605a62ddb1c19978ee194a40a458f072e3242a31 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 24 Jun 2012 04:33:36 -0700 Subject: [PATCH] Issue #15118: Change return value of os.uname() and os.times() from plain tuples to immutable iterable objects with named attributes (structseq objects). --- Doc/library/os.rst | 48 +++++++++-- Lib/ctypes/__init__.py | 2 +- Lib/ctypes/util.py | 4 +- Lib/platform.py | 2 +- Lib/test/test__locale.py | 2 +- Lib/test/test_locale.py | 2 +- Lib/test/test_sysconfig.py | 5 +- Lib/uuid.py | 2 +- Misc/NEWS | 4 + Modules/posixmodule.c | 162 +++++++++++++++++++++++++++++++------ 10 files changed, 191 insertions(+), 42 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 59b1d10bafd..9556be6319a 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -570,15 +570,31 @@ process and user. single: gethostname() (in module socket) single: gethostbyaddr() (in module socket) - Return a 5-tuple containing information identifying the current operating - system. The tuple contains 5 strings: ``(sysname, nodename, release, version, - machine)``. Some systems truncate the nodename to 8 characters or to the + Returns information identifying the current operating system. + The return value is an object with five attributes: + + * :attr:`sysname` - operating system name + * :attr:`nodename` - name of machine on network (implementation-defined) + * :attr:`release` - operating system release + * :attr:`version` - operating system version + * :attr:`machine` - hardware identifier + + For backwards compatibility, this object is also iterable, behaving + like a five-tuple containing :attr:`sysname`, :attr:`nodename`, + :attr:`release`, :attr:`version`, and :attr:`machine` + in that order. + + Some systems truncate :attr:`nodename` to 8 characters or to the leading component; a better way to get the hostname is :func:`socket.gethostname` or even ``socket.gethostbyaddr(socket.gethostname())``. Availability: recent flavors of Unix. + .. versionchanged:: 3.3 + Return type changed from a tuple to a tuple-like object + with named attributes. + .. function:: unsetenv(key) @@ -2801,15 +2817,31 @@ written in Python, such as a mail server's external command delivery program. .. function:: times() - Return a 5-tuple of floating point numbers indicating accumulated (processor - or other) times, in seconds. The items are: user time, system time, - children's user time, children's system time, and elapsed real time since a - fixed point in the past, in that order. See the Unix manual page + Returns the current global process times. + The return value is an object with five attributes: + + * :attr:`user` - user time + * :attr:`system` - system time + * :attr:`children_user` - user time of all child processes + * :attr:`children_system` - system time of all child processes + * :attr:`elapsed` - elapsed real time since a fixed point in the past + + For backwards compatibility, this object also behaves like a five-tuple + containing :attr:`user`, :attr:`system`, :attr:`children_user`, + :attr:`children_system`, and :attr:`elapsed` in that order. + + See the Unix manual page :manpage:`times(2)` or the corresponding Windows Platform API documentation. - On Windows, only the first two items are filled, the others are zero. + On Windows, only :attr:`user` and :attr:`system` are known; the other + attributes are zero. + On OS/2, only :attr:`elapsed` is known; the other attributes are zero. Availability: Unix, Windows. + .. versionchanged:: 3.3 + Return type changed from a tuple to a tuple-like object + with named attributes. + .. function:: wait() diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index 111209a9535..f0bd66a17c8 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -26,7 +26,7 @@ if _os.name == "posix" and _sys.platform == "darwin": # libraries. OS X 10.3 is Darwin 7, so we check for # that. - if int(_os.uname()[2].split('.')[0]) < 8: + if int(_os.uname().release.split('.')[0]) < 8: DEFAULT_MODE = RTLD_GLOBAL from _ctypes import FUNCFLAG_CDECL as _FUNCFLAG_CDECL, \ diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py index 61eb09454b8..5555b2e41fe 100644 --- a/Lib/ctypes/util.py +++ b/Lib/ctypes/util.py @@ -171,9 +171,9 @@ elif os.name == "posix": def _findSoname_ldconfig(name): import struct if struct.calcsize('l') == 4: - machine = os.uname()[4] + '-32' + machine = os.uname().machine + '-32' else: - machine = os.uname()[4] + '-64' + machine = os.uname().machine + '-64' mach_map = { 'x86_64-64': 'libc6,x86-64', 'ppc64-64': 'libc6,64bit', diff --git a/Lib/platform.py b/Lib/platform.py index 45546599b4e..b7dbcca78aa 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -700,7 +700,7 @@ def _mac_ver_xml(): pl = plistlib.readPlist(fn) release = pl['ProductVersion'] versioninfo=('', '', '') - machine = os.uname()[4] + machine = os.uname().machine if machine in ('ppc', 'Power Macintosh'): # for compatibility with the gestalt based code machine = 'PowerPC' diff --git a/Lib/test/test__locale.py b/Lib/test/test__locale.py index f7f1abdab20..dab1565fbe2 100644 --- a/Lib/test/test__locale.py +++ b/Lib/test/test__locale.py @@ -12,7 +12,7 @@ from platform import uname from test.support import run_unittest if uname()[0] == "Darwin": - maj, min, mic = [int(part) for part in uname()[2].split(".")] + maj, min, mic = [int(part) for part in uname().release.split(".")] if (maj, min, mic) < (8, 0, 0): raise unittest.SkipTest("locale support broken for OS X < 10.4") diff --git a/Lib/test/test_locale.py b/Lib/test/test_locale.py index 7fdb6dafdd0..51a7bca4b62 100644 --- a/Lib/test/test_locale.py +++ b/Lib/test/test_locale.py @@ -11,7 +11,7 @@ def get_enUS_locale(): if sys.platform == 'darwin': import os tlocs = ("en_US.UTF-8", "en_US.ISO8859-1", "en_US") - if int(os.uname()[2].split('.')[0]) < 10: + if int(os.uname().release.split('.')[0]) < 10: # The locale test work fine on OSX 10.6, I (ronaldoussoren) # haven't had time yet to verify if tests work on OSX 10.5 # (10.4 is known to be bad) diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py index 3cb63ed8c4b..aa5d5828242 100644 --- a/Lib/test/test_sysconfig.py +++ b/Lib/test/test_sysconfig.py @@ -14,7 +14,6 @@ from sysconfig import (get_paths, get_platform, get_config_vars, _get_default_scheme, _expand_vars, get_scheme_names, get_config_var, _main) - class TestSysConfig(unittest.TestCase): def setUp(self): @@ -26,7 +25,7 @@ class TestSysConfig(unittest.TestCase): self._uname = os.uname() else: self.uname = None - self._uname = None + self._set_uname(('',)*5) os.uname = self._get_uname # saving the environment self.name = os.name @@ -70,7 +69,7 @@ class TestSysConfig(unittest.TestCase): super(TestSysConfig, self).tearDown() def _set_uname(self, uname): - self._uname = uname + self._uname = os.uname_result(uname) def _get_uname(self): return self._uname diff --git a/Lib/uuid.py b/Lib/uuid.py index 5684ad7acee..0df0743c4c5 100644 --- a/Lib/uuid.py +++ b/Lib/uuid.py @@ -440,7 +440,7 @@ try: import sys if sys.platform == 'darwin': import os - if int(os.uname()[2].split('.')[0]) >= 9: + if int(os.uname().release.split('.')[0]) >= 9: _uuid_generate_random = _uuid_generate_time = None # On Windows prior to 2000, UuidCreate gives a UUID containing the diff --git a/Misc/NEWS b/Misc/NEWS index 6f3b85d7633..d4edb95cf74 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -55,6 +55,10 @@ Core and Builtins Library ------- +- Issue #15118: Change return value of os.uname() and os.times() from + plain tuples to immutable iterable objects with named attributes + (structseq objects). + - Speed up _decimal by another 10-15% by caching the thread local context that was last accessed. In the pi benchmark (64-bit platform, prec=9), _decimal is now only 1.5x slower than float. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 6da030a371d..6cd8d883f6f 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -4299,28 +4299,76 @@ exit: } -#ifdef HAVE_UNAME PyDoc_STRVAR(posix_uname__doc__, -"uname() -> (sysname, nodename, release, version, machine)\n\n\ -Return a tuple identifying the current operating system."); +"uname() -> uname_result\n\n\ +Return an object identifying the current operating system.\n\ +The object behaves like a named tuple with the following fields:\n\ + (sysname, nodename, release, version, machine)"); +static PyStructSequence_Field uname_result_fields[] = { + {"sysname", "operating system name"}, + {"nodename", "name of machine on network (implementation-defined)"}, + {"release", "operating system release"}, + {"version", "operating system version"}, + {"machine", "hardware identifier"}, + {NULL} +}; + +PyDoc_STRVAR(uname_result__doc__, +"uname_result: Result from os.uname().\n\n\ +This object may be accessed either as a tuple of\n\ + (sysname, nodename, release, version, machine),\n\ +or via the attributes sysname, nodename, release, version, and machine.\n\ +\n\ +See os.uname for more information."); + +static PyStructSequence_Desc uname_result_desc = { + "uname_result", /* name */ + uname_result__doc__, /* doc */ + uname_result_fields, + 5 +}; + +static PyTypeObject UnameResultType; + + +#ifdef HAVE_UNAME static PyObject * posix_uname(PyObject *self, PyObject *noargs) { struct utsname u; int res; + PyObject *value; Py_BEGIN_ALLOW_THREADS res = uname(&u); Py_END_ALLOW_THREADS if (res < 0) return posix_error(); - return Py_BuildValue("(sssss)", - u.sysname, - u.nodename, - u.release, - u.version, - u.machine); + + value = PyStructSequence_New(&UnameResultType); + if (value == NULL) + return NULL; + +#define SET(i, field) \ + { \ + PyObject *o = PyUnicode_DecodeASCII(field, strlen(field), NULL); \ + if (!o) { \ + Py_DECREF(value); \ + return NULL; \ + } \ + PyStructSequence_SET_ITEM(value, i, o); \ + } \ + + SET(0, u.sysname); + SET(1, u.nodename); + SET(2, u.release); + SET(3, u.version); + SET(4, u.machine); + +#undef SET + + return value; } #endif /* HAVE_UNAME */ @@ -7366,6 +7414,75 @@ win_readlink(PyObject *self, PyObject *args, PyObject *kwargs) #endif /* !defined(HAVE_READLINK) && defined(MS_WINDOWS) */ +static PyStructSequence_Field times_result_fields[] = { + {"user", "user time"}, + {"system", "system time"}, + {"children_user", "user time of children"}, + {"children_system", "system time of children"}, + {"elapsed", "elapsed time since an arbitrary point in the past"}, + {NULL} +}; + +PyDoc_STRVAR(times_result__doc__, +"times_result: Result from os.times().\n\n\ +This object may be accessed either as a tuple of\n\ + (user, system, children_user, children_system, elapsed),\n\ +or via the attributes user, system, children_user, children_system,\n\ +and elapsed.\n\ +\n\ +See os.times for more information."); + +static PyStructSequence_Desc times_result_desc = { + "times_result", /* name */ + times_result__doc__, /* doc */ + times_result_fields, + 5 +}; + +static PyTypeObject TimesResultType; + + +#if defined(HAVE_TIMES) || defined(MS_WINDOWS) + +static PyObject * +build_times_result(double user, double system, + double children_user, double children_system, + double elapsed) +{ + PyObject *value = PyStructSequence_New(&TimesResultType); + if (value == NULL) + return NULL; + +#define SET(i, field) \ + { \ + PyObject *o = PyFloat_FromDouble(field); \ + if (!o) { \ + Py_DECREF(value); \ + return NULL; \ + } \ + PyStructSequence_SET_ITEM(value, i, o); \ + } \ + + SET(0, user); + SET(1, system); + SET(2, children_user); + SET(3, children_system); + SET(4, elapsed); + +#undef SET + + return value; +} + +PyDoc_STRVAR(posix_times__doc__, +"times() -> times_result\n\n\ +Return an object containing floating point numbers indicating process\n\ +times. The object behaves like a named tuple with these fields:\n\ + (utime, stime, cutime, cstime, elapsed_time)"); + +#endif + + #ifdef HAVE_TIMES #if defined(PYCC_VACPP) && defined(PYOS_OS2) static long @@ -7384,7 +7501,7 @@ static PyObject * posix_times(PyObject *self, PyObject *noargs) { /* Currently Only Uptime is Provided -- Others Later */ - return Py_BuildValue("ddddd", + return build_times_result( (double)0 /* t.tms_utime / HZ */, (double)0 /* t.tms_stime / HZ */, (double)0 /* t.tms_cutime / HZ */, @@ -7403,7 +7520,7 @@ posix_times(PyObject *self, PyObject *noargs) c = times(&t); if (c == (clock_t) -1) return posix_error(); - return Py_BuildValue("ddddd", + return build_times_result( (double)t.tms_utime / ticks_per_second, (double)t.tms_stime / ticks_per_second, (double)t.tms_cutime / ticks_per_second, @@ -7411,11 +7528,7 @@ posix_times(PyObject *self, PyObject *noargs) (double)c / ticks_per_second); } #endif /* not OS2 */ -#endif /* HAVE_TIMES */ - - -#ifdef MS_WINDOWS -#define HAVE_TIMES /* so the method table will pick it up */ +#elif defined(MS_WINDOWS) static PyObject * posix_times(PyObject *self, PyObject *noargs) { @@ -7428,8 +7541,7 @@ posix_times(PyObject *self, PyObject *noargs) 1e7 is one second in such units; 1e-7 the inverse. 429.4967296 is 2**32 / 1e7 or 2**32 * 1e-7. */ - return Py_BuildValue( - "ddddd", + return build_times_result( (double)(user.dwHighDateTime*429.4967296 + user.dwLowDateTime*1e-7), (double)(kernel.dwHighDateTime*429.4967296 + @@ -7438,12 +7550,6 @@ posix_times(PyObject *self, PyObject *noargs) (double)0, (double)0); } -#endif /* MS_WINDOWS */ - -#ifdef HAVE_TIMES -PyDoc_STRVAR(posix_times__doc__, -"times() -> (utime, stime, cutime, cstime, elapsed_time)\n\n\ -Return a tuple of floating point numbers indicating process times."); #endif @@ -11965,6 +12071,14 @@ INITFUNC(void) PyModule_AddObject(m, "sched_param", (PyObject *)&SchedParamType); #endif + times_result_desc.name = MODNAME ".times_result"; + PyStructSequence_InitType(&TimesResultType, ×_result_desc); + PyModule_AddObject(m, "times_result", (PyObject *)&TimesResultType); + + uname_result_desc.name = MODNAME ".uname_result"; + PyStructSequence_InitType(&UnameResultType, &uname_result_desc); + PyModule_AddObject(m, "uname_result", (PyObject *)&UnameResultType); + #ifdef __APPLE__ /* * Step 2 of weak-linking support on Mac OS X.