diff -r 0fc10a33eb4c -r 4a6bf825b6cf Doc/library/os.rst --- a/Doc/library/os.rst Thu Feb 09 21:09:03 2012 +0200 +++ b/Doc/library/os.rst Fri Feb 10 02:46:06 2012 +0100 @@ -808,13 +808,16 @@ as internal buffering of data. Availability: Unix. -.. function:: fstat(fd) +.. function:: fstat(fd, timestamp=None) Return status for file descriptor *fd*, like :func:`~os.stat`. Availability: Unix, Windows. -.. function:: fstatat(dirfd, path, flags=0) + .. versionchanged:: 3.3 + Added the *timestamp* argument. + +.. function:: fstatat(dirfd, path, flags=0, timestamp="float") Like :func:`stat` but if *path* is relative, it is taken as relative to *dirfd*. *flags* is optional and may be 0 or :data:`AT_SYMLINK_NOFOLLOW`. @@ -1733,7 +1736,7 @@ Files and Directories .. versionadded:: 3.3 -.. function:: lstat(path) +.. function:: lstat(path, timestamp=None) Perform the equivalent of an :c:func:`lstat` system call on the given path. Similar to :func:`~os.stat`, but does not follow symbolic links. On @@ -1743,6 +1746,9 @@ Files and Directories .. versionchanged:: 3.2 Added support for Windows 6.0 (Vista) symbolic links. + .. versionchanged:: 3.3 + The *timestamp* argument was added. + .. function:: lutimes(path[, times]) @@ -2006,7 +2012,7 @@ Files and Directories .. versionadded:: 3.3 -.. function:: stat(path) +.. function:: stat(path, timestamp=None) Perform the equivalent of a :c:func:`stat` system call on the given path. (This function follows symlinks; to stat a symlink use :func:`lstat`.) @@ -2026,6 +2032,11 @@ Files and Directories * :attr:`st_ctime` - platform dependent; time of most recent metadata change on Unix, or the time of creation on Windows) + :attr:`st_atime`, :attr:`st_mtime` and :attr:`st_ctime` are :class:`float` + by default, or :class:`int` if :func:`os.stat_float_times` is ``False``. Set + the *timestamp* argument to get another :ref:`timestamp type + `. + On some Unix systems (such as Linux), the following attributes may also be available: @@ -2081,6 +2092,9 @@ Files and Directories Availability: Unix, Windows. + .. versionchanged:: 3.3 + Added the *timestamp* argument. + .. function:: stat_float_times([newvalue]) @@ -2106,6 +2120,9 @@ Files and Directories are processed, this application should turn the feature off until the library has been corrected. + .. deprecated:: 3.3 + Use *timestamp* argument of stat functions instead. + .. function:: statvfs(path) @@ -2896,27 +2913,39 @@ written in Python, such as a mail server with :const:`P_NOWAIT` return suitable process handles. -.. function:: wait3([options]) +.. function:: wait3(options[, timestamp=float]) Similar to :func:`waitpid`, except no process id argument is given and a 3-element tuple containing the child's process id, exit status indication, and resource usage information is returned. Refer to :mod:`resource`.\ :func:`getrusage` for details on resource usage information. The option argument is the same as that provided to :func:`waitpid` and :func:`wait4`. + :attr:`ru_utime` and :attr:`ru_stime` attributes of the resource usage are + :class:`float` by default, set the *timestamp* argument to get another + :ref:`timestamp type `. Availability: Unix. - -.. function:: wait4(pid, options) + .. versionchanged:: 3.3 + Added the *timestamp* argument. + + +.. function:: wait4(pid, options[, timestamp=float]) Similar to :func:`waitpid`, except a 3-element tuple, containing the child's process id, exit status indication, and resource usage information is returned. Refer to :mod:`resource`.\ :func:`getrusage` for details on resource usage information. The arguments to :func:`wait4` are the same as those provided to :func:`waitpid`. + :attr:`ru_utime` and :attr:`ru_stime` attributes of the resource usage are + :class:`float` by default, set the *timestamp* argument to get another + :ref:`timestamp type `. Availability: Unix. + .. versionchanged:: 3.3 + Added the *timestamp* argument. + .. data:: WNOHANG @@ -3103,10 +3132,12 @@ operating system. process with PID *pid*. A *pid* of 0 means the calling process. -.. function:: sched_rr_get_interval(pid) - - Return the round-robin quantum in seconds for the process with PID *pid*. A - *pid* of 0 means the calling process. +.. function:: sched_rr_get_interval(pid, timestamp=float) + + Return the round-robin quantum in seconds for the process with PID *pid*. + Return a floating point number by default, set the *timestamp* argument + to get another :ref:`timestamp type `. + A *pid* of 0 means the calling process. .. function:: sched_yield() diff -r 0fc10a33eb4c -r 4a6bf825b6cf Doc/library/resource.rst --- a/Doc/library/resource.rst Thu Feb 09 21:09:03 2012 +0200 +++ b/Doc/library/resource.rst Fri Feb 10 02:46:06 2012 +0100 @@ -144,7 +144,7 @@ Resource Usage These functions are used to retrieve resource usage information: -.. function:: getrusage(who) +.. function:: getrusage(who, timestamp=float) This function returns an object that describes the resources consumed by either the current process or its children, as specified by the *who* parameter. The @@ -168,9 +168,9 @@ These functions are used to retrieve res +--------+---------------------+-------------------------------+ | Index | Field | Resource | +========+=====================+===============================+ - | ``0`` | :attr:`ru_utime` | time in user mode (float) | + | ``0`` | :attr:`ru_utime` | time in user mode (1) | +--------+---------------------+-------------------------------+ - | ``1`` | :attr:`ru_stime` | time in system mode (float) | + | ``1`` | :attr:`ru_stime` | time in system mode (1) | +--------+---------------------+-------------------------------+ | ``2`` | :attr:`ru_maxrss` | maximum resident set size | +--------+---------------------+-------------------------------+ @@ -201,9 +201,16 @@ These functions are used to retrieve res | ``15`` | :attr:`ru_nivcsw` | involuntary context switches | +--------+---------------------+-------------------------------+ + (1): :attr:`ru_utime` and :attr:`ru_stime` are floating point numbers by + default, set the *timestamp* argument to get another :ref:`timestamp type + `. + This function will raise a :exc:`ValueError` if an invalid *who* parameter is specified. It may also raise :exc:`error` exception in unusual circumstances. + .. versionchanged:: 3.3 + Added the *timestamp* argument. + .. function:: getpagesize() diff -r 0fc10a33eb4c -r 4a6bf825b6cf Doc/library/signal.rst --- a/Doc/library/signal.rst Thu Feb 09 21:09:03 2012 +0200 +++ b/Doc/library/signal.rst Fri Feb 10 02:46:06 2012 +0100 @@ -238,11 +238,11 @@ The :mod:`signal` module defines the fol .. versionadded:: 3.3 -.. function:: setitimer(which, seconds[, interval]) +.. function:: setitimer(which, value[, interval, timestamp=float]) Sets given interval timer (one of :const:`signal.ITIMER_REAL`, :const:`signal.ITIMER_VIRTUAL` or :const:`signal.ITIMER_PROF`) specified - by *which* to fire after *seconds* (float is accepted, different from + by *which* to fire after *value* (float is accepted, different from :func:`alarm`) and after that every *interval* seconds. The interval timer specified by *which* can be cleared by setting seconds to zero. @@ -252,17 +252,29 @@ The :mod:`signal` module defines the fol :const:`signal.ITIMER_VIRTUAL` sends :const:`SIGVTALRM`, and :const:`signal.ITIMER_PROF` will deliver :const:`SIGPROF`. - The old values are returned as a tuple: (delay, interval). + The old values are returned as a tuple: (value, interval). value and + interval are floating point numbers by default, set the *timestamp* argument + to get another :ref:`timestamp type `. Attempting to pass an invalid interval timer will cause an :exc:`ItimerError`. Availability: Unix. + .. versionchanged:: 3.3 + Added the *timestamp* argument. -.. function:: getitimer(which) - Returns current value of a given interval timer specified by *which*. +.. function:: getitimer(which, timestamp=float) + + Returns current value of a given interval timer specified by *which* as a + tuple: (value, interval). delay and interval are floating point numbers by + default, set the *timestamp* argument to get another :ref:`timestamp type + `. + Availability: Unix. + .. versionchanged:: 3.3 + Added the *timestamp* argument. + .. function:: set_wakeup_fd(fd) @@ -369,11 +381,10 @@ The :mod:`signal` module defines the fol .. versionadded:: 3.3 -.. function:: sigtimedwait(sigset, (timeout_sec, timeout_nsec)) +.. function:: sigtimedwait(sigset, timeout) - Like :func:`sigtimedwait`, but takes a tuple of ``(seconds, nanoseconds)`` - as an additional argument specifying a timeout. If both *timeout_sec* and - *timeout_nsec* are specified as :const:`0`, a poll is performed. Returns + Like :func:`sigtimedwait`, but takes an additional timeout argument. If + *timeout* is specified as :const:`0`, a poll is performed. Returns :const:`None` if a timeout occurs. Availability: Unix (see the man page :manpage:`sigtimedwait(2)` for further diff -r 0fc10a33eb4c -r 4a6bf825b6cf Doc/library/time.rst --- a/Doc/library/time.rst Thu Feb 09 21:09:03 2012 +0200 +++ b/Doc/library/time.rst Fri Feb 10 02:46:06 2012 +0100 @@ -95,6 +95,14 @@ An explanation of some terminology and c | local time | | | +-------------------------+-------------------------+-------------------------+ +.. _timestamp-types: + +* Python supports the following timestamp types: + + * :class:`int` + * :class:`float` + * :class:`decimal.Decimal` + The module defines the following functions and data items: @@ -119,7 +127,7 @@ The module defines the following functio trailing newline. -.. function:: clock() +.. function:: clock(timestamp=float) .. index:: single: CPU time @@ -136,16 +144,27 @@ The module defines the following functio :c:func:`QueryPerformanceCounter`. The resolution is typically better than one microsecond. + Return a floating point number by default, set the *timestamp* argument + to get another :ref:`timestamp type `. -.. function:: clock_getres(clk_id) + .. versionchanged:: 3.3 + Added the *timestamp* argument. + + +.. function:: clock_getres(clk_id, timestamp=float) Return the resolution (precision) of the specified clock *clk_id*. + Return a floating point number by default, set the *timestamp* argument to + get another :ref:`timestamp type `. + .. versionadded:: 3.3 -.. function:: clock_gettime(clk_id) +.. function:: clock_gettime(clk_id, timestamp=float) Return the time of the specified clock *clk_id*. + Return a floating point number by default, set the *timestamp* argument to + get another :ref:`timestamp type `. .. versionadded:: 3.3 @@ -214,19 +233,22 @@ The module defines the following functio flag is set to ``1`` when DST applies to the given time. -.. function:: mktime(t) +.. function:: mktime(t, timestamp=float) This is the inverse function of :func:`localtime`. Its argument is the :class:`struct_time` or full 9-tuple (since the dst flag is needed; use ``-1`` as the dst flag if it is unknown) which expresses the time in *local* time, not - UTC. It returns a floating point number, for compatibility with :func:`time`. + It returns a floating point number by default, for compatibility with + :func:`time`, set the *timestamp* argument to get another :ref:`timestamp + type `. + If the input value cannot be represented as a valid time, either :exc:`OverflowError` or :exc:`ValueError` will be raised (which depends on whether the invalid value is caught by Python or the underlying C libraries). The earliest date for which it can generate a time is platform-dependent. -.. function:: monotonic() +.. function:: monotonic(timestamp=float) Monotonic clock. The reference point of the returned value is undefined so only the difference of consecutive calls is valid. @@ -440,15 +462,20 @@ The module defines the following functio :exc:`TypeError` is raised. -.. function:: time() +.. function:: time(timestamp=float) - Return the time as a floating point number expressed in seconds since the epoch, - in UTC. Note that even though the time is always returned as a floating point + Return the time expressed in seconds since the epoch in UTC. Return a + floating point number by default, set the *timestamp* argument to get + another :ref:`timestamp type `. + Note that even though the time is always returned as a floating point number, not all systems provide time with a better precision than 1 second. While this function normally returns non-decreasing values, it can return a lower value than a previous call if the system clock has been set back between the two calls. + .. versionchanged:: 3.3 + Added the *timestamp* argument. + .. data:: timezone @@ -546,13 +573,16 @@ The module defines the following functio ('EET', 'EEST') -.. function:: wallclock() +.. function:: wallclock(timestamp=float) .. index:: single: Wallclock single: benchmarking Return the current time in fractions of a second to the system's best ability. + Return a floating point number by default, set the *timestamp* argument to + get another :ref:`timestamp type `. + Use this when the most accurate representation of wall-clock is required, i.e. when "processor time" is inappropriate. The reference point of the returned value is undefined so only the difference of consecutive calls is valid. diff -r 0fc10a33eb4c -r 4a6bf825b6cf Doc/whatsnew/3.3.rst --- a/Doc/whatsnew/3.3.rst Thu Feb 09 21:09:03 2012 +0200 +++ b/Doc/whatsnew/3.3.rst Fri Feb 10 02:46:06 2012 +0100 @@ -270,6 +270,42 @@ new, more precise information:: '' +PEP 410: Use decimal.Decimal type for timestamps +================================================ + +:pep:`410` - Use decimal.Decimal type for timestamps + PEP written and implemented by Victor Stinner. + +The following functions have a new optional *timestamp* argument to get a +timestamp as a :class:`decimal.Decimal` instead of :class:`int` or +:class:`float`: + + * :mod:`time` module: :func:`~time.clock`, :func:`~time.clock_gettime`, + :func:`~time.clock_getres`, :func:`~time.monotonic`, :func:`~time.time` and + :func:`~time.wallclock` + * :mod:`os` module: :func:`~os.fstat`, :func:`~os.fstatat`, :func:`~os.lstat`, + :func:`~os.stat` (``st_atime``, ``st_ctime`` and ``st_mtime`` fields of the + stat structure), :func:`~os.wait3` and :func:`~os.wait4` + +:class:`decimal.Decimal` supports a resolution of a nanosecond (10\ :sup:`-9`) +resolution, whereas :class:`float` has only a resolution of a microsecond +(10\ :sup:`-6`) in common cases. See the list of available :ref:`timestamp +types `. + +Example:: + + >>> import decimal, time + >>> time.time() + 1328006975.681211 + >>> time.time(timestamp=int) + 1328006979 + >>> time.time(timestamp=decimal.Decimal) + Decimal('1328006983.761119') + +:func:`os.stat_float_times` has been deprecated, use *timestamp* argument of +`os.stat` instead. + + Other Language Changes ====================== diff -r 0fc10a33eb4c -r 4a6bf825b6cf Include/pytime.h --- a/Include/pytime.h Thu Feb 09 21:09:03 2012 +0200 +++ b/Include/pytime.h Fri Feb 10 02:46:06 2012 +0100 @@ -2,7 +2,8 @@ #ifndef Py_PYTIME_H #define Py_PYTIME_H -#include "pyconfig.h" /* include for defines */ +#include "pyport.h" +#include "object.h" /************************************************************************** Symbols and macros to supply platform-independent interfaces to time related @@ -37,6 +38,90 @@ do { \ ((tv_end.tv_sec - tv_start.tv_sec) + \ (tv_end.tv_usec - tv_start.tv_usec) * 0.000001) + +#if defined(HAVE_LONG_LONG) +typedef unsigned PY_LONG_LONG _PyTime_fraction_t; +#else +typedef size_t _PyTime_fraction_t; +#endif + +PyAPI_FUNC(PyObject*) _PyLong_FromTime_t(time_t value); + +#if defined(HAVE_LONG_LONG) +# define _PyLong_FromTimeFraction_t PyLong_FromLongLong +#else +# define _PyLong_FromTimeFraction_t PyLong_FromSize_t +#endif + +typedef struct +{ + /* timestamp = seconds + numerator / denominator */ + time_t seconds; + /* numerator can be bigger than denominator */ + _PyTime_fraction_t numerator; + /* denominator cannot be zero */ + _PyTime_fraction_t denominator; + /* the timestamp resolution is 1/divisor */ +} _PyTime_t; + +/* Change the denominator of a timestamp. + Return 0 on success, raise a OverflowError and return -1 if the number of + seconds doesn't fit into time_t. */ +PyAPI_FUNC(int) _PyTime_SetDenominator( + _PyTime_t *ts, + _PyTime_fraction_t new_denominator); + +PyAPI_FUNC(void) _PyTime_FromTime_t(_PyTime_t *ts, time_t seconds); + +/* Convert a timestamp to time_t (number of seconds). + Raise a OverflowError and return (time_t)-1 if the number of seconds doesn't + fit into time_t. */ +PyAPI_FUNC(time_t) _PyTime_AsTime_t(const _PyTime_t *ts); + +/* Convert a timestamp to a number of milliseconds (10^-3). + Raise a OverflowError and return -1 if the number of + milliseconds doesn't fit into int. */ +PyAPI_FUNC(int) _PyTime_AsMillisec(_PyTime_t *ts); + +#if defined(HAVE_GETTIMEOFDAY) || defined(MS_WINDOWS) +/* Set a timestamp from a timeval structure (microsecond resolution) */ +PyAPI_FUNC(void) _PyTime_FromTimeval(_PyTime_t *ts, struct timeval *tv); + +/* Convert a timestamp to a timeval structure (microsecond resolution). + Return 0 on success, raise a OverflowError and return -1 if the number of + seconds doesn't fit into time_t. */ +PyAPI_FUNC(int) _PyTime_AsTimeval( + const _PyTime_t *ts, + struct timeval *tv); +#endif + +#if defined(HAVE_CLOCK_GETTIME) +/* Set a timestamp from a timespec structure (nanosecond resolution) */ +PyAPI_FUNC(void) _PyTime_FromTimespec(_PyTime_t *ts, struct timespec *tv); + +/* Convert a timestamp to a timespec structure (nanosecond resolution). + Return 0 on success, raise a OverflowError and return -1 if the number of + seconds doesn't fit into time_t. */ +PyAPI_FUNC(int) _PyTime_AsTimespec( + const _PyTime_t *ts, + struct timespec *tv); +#endif + +/* Set a timestamp from a Python object: support float and int. + Return 0 on success, raise an exception and return -1 on error. */ +PyAPI_FUNC(int) _PyTime_FromObject(_PyTime_t *ts, PyObject *obj); + +/* Similar to POSIX gettimeofday. If system gettimeofday + fails or is not available, fall back to lower resolution clocks. */ +PyAPI_FUNC(void) _PyTime_Get(_PyTime_t *tp); + +/* Convert a timestamp structure to the specified timestamp type. + + Raise a ValueError if the timestamp type is unknown. */ +PyAPI_FUNC(PyObject*) _PyTime_AsType( + const _PyTime_t *ts, + PyObject *timestamp); + /* Dummy to force linking. */ PyAPI_FUNC(void) _PyTime_Init(void); diff -r 0fc10a33eb4c -r 4a6bf825b6cf Lib/test/test_os.py --- a/Lib/test/test_os.py Thu Feb 09 21:09:03 2012 +0200 +++ b/Lib/test/test_os.py Fri Feb 10 02:46:06 2012 +0100 @@ -2,6 +2,7 @@ # does add tests for a few functions which have been determined to be more # portable than they had been thought to be. +import decimal import os import errno import unittest @@ -238,6 +239,36 @@ class StatAttributeTests(unittest.TestCa warnings.simplefilter("ignore", DeprecationWarning) self.check_stat_attributes(fname) + def test_stat_timestamp(self): + # test deprecation + with warnings.catch_warnings(): + warnings.simplefilter("error", DeprecationWarning) + self.assertRaises(DeprecationWarning, os.stat_float_times, False) + + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + old_value = os.stat_float_times() + try: + # test invalid timestamp types + self.assertRaises(ValueError, os.stat, self.fname, + timestamp="abc") + self.assertRaises(ValueError, os.stat, self.fname, + timestamp=decimal.Context) + + for float_times in (False, True): + os.stat_float_times(float_times) + t = os.stat(self.fname).st_mtime + if float_times: + self.assertIsInstance(t, float) + else: + self.assertIsInstance(t, int) + + for type in (int, float, decimal.Decimal): + t = os.stat(self.fname, timestamp=type).st_mtime + self.assertIsInstance(t, type) + finally: + os.stat_float_times(old_value) + def test_statvfs_attributes(self): if not hasattr(os, "statvfs"): return diff -r 0fc10a33eb4c -r 4a6bf825b6cf Lib/test/test_resource.py --- a/Lib/test/test_resource.py Thu Feb 09 21:09:03 2012 +0200 +++ b/Lib/test/test_resource.py Fri Feb 10 02:46:06 2012 +0100 @@ -94,7 +94,6 @@ class ResourceTest(unittest.TestCase): def test_getrusage(self): self.assertRaises(TypeError, resource.getrusage) - self.assertRaises(TypeError, resource.getrusage, 42, 42) usageself = resource.getrusage(resource.RUSAGE_SELF) usagechildren = resource.getrusage(resource.RUSAGE_CHILDREN) # May not be available on all systems. diff -r 0fc10a33eb4c -r 4a6bf825b6cf Lib/test/test_signal.py --- a/Lib/test/test_signal.py Thu Feb 09 21:09:03 2012 +0200 +++ b/Lib/test/test_signal.py Fri Feb 10 02:46:06 2012 +0100 @@ -662,7 +662,7 @@ class PendingSignalsTests(unittest.TestC self.wait_helper(signal.SIGALRM, ''' def test(signum): signal.alarm(1) - info = signal.sigtimedwait([signum], (10, 1000)) + info = signal.sigtimedwait([signum], 10.3) if info.si_signo != signum: raise Exception('info.si_signo != %s' % signum) ''') @@ -675,7 +675,7 @@ class PendingSignalsTests(unittest.TestC def test(signum): import os os.kill(os.getpid(), signum) - info = signal.sigtimedwait([signum], (0, 0)) + info = signal.sigtimedwait([signum], 0) if info.si_signo != signum: raise Exception('info.si_signo != %s' % signum) ''') @@ -685,7 +685,7 @@ class PendingSignalsTests(unittest.TestC def test_sigtimedwait_timeout(self): self.wait_helper(signal.SIGALRM, ''' def test(signum): - received = signal.sigtimedwait([signum], (1, 0)) + received = signal.sigtimedwait([signum], 1.0) if received is not None: raise Exception("received=%r" % (received,)) ''') @@ -694,9 +694,7 @@ class PendingSignalsTests(unittest.TestC 'need signal.sigtimedwait()') def test_sigtimedwait_negative_timeout(self): signum = signal.SIGALRM - self.assertRaises(ValueError, signal.sigtimedwait, [signum], (-1, -1)) - self.assertRaises(ValueError, signal.sigtimedwait, [signum], (0, -1)) - self.assertRaises(ValueError, signal.sigtimedwait, [signum], (-1, 0)) + self.assertRaises(ValueError, signal.sigtimedwait, [signum], -1.0) @unittest.skipUnless(hasattr(signal, 'sigwaitinfo'), 'need signal.sigwaitinfo()') diff -r 0fc10a33eb4c -r 4a6bf825b6cf Lib/test/test_time.py --- a/Lib/test/test_time.py Thu Feb 09 21:09:03 2012 +0200 +++ b/Lib/test/test_time.py Fri Feb 10 02:46:06 2012 +0100 @@ -1,10 +1,12 @@ +import decimal +import locale +import platform +import sys +import sysconfig from test import support +import _testcapi import time import unittest -import locale -import sysconfig -import sys -import platform # Max year is only limited by the size of C int. SIZEOF_INT = sysconfig.get_config_var('SIZEOF_INT') or 4 @@ -345,6 +347,30 @@ class TimeTestCase(unittest.TestCase): self.assertGreater(t2, t1) self.assertAlmostEqual(dt, 0.1, delta=0.2) + def test_timestamp(self): + calls = [ + (time.time,), + (time.mktime, time.localtime()), + ] + if hasattr(time, 'monotonic'): + calls.append((time.monotonic,)) + if hasattr(time, 'wallclock'): + calls.append((time.wallclock,)) + if hasattr(time, 'CLOCK_REALTIME'): + if hasattr(time, 'clock_gettime'): + calls.append((time.clock_gettime, time.CLOCK_REALTIME)) + if hasattr(time, 'clock_getres'): + calls.append((time.clock_getres, time.CLOCK_REALTIME)) + for call in calls: + func, *args = call + + # test invalid timestamp + for invalid in ("int", decimal.Context): + self.assertRaises(ValueError, func, *args, timestamp=invalid) + + for type in (int, float, decimal.Decimal): + self.assertIsInstance(func(*args, timestamp=type), type) + def test_wallclock(self): t1 = time.wallclock() t2 = time.wallclock() @@ -496,13 +522,82 @@ class TestAsctime4dyear(_TestAsctimeYear class TestStrftime4dyear(_TestStrftimeYear, _Test4dYear): pass +class TestPytime(unittest.TestCase): + def setUp(self): + self.decode = _testcapi.pytime_fromobject + self.encode = _testcapi.pytime_astype + + def test_int(self): + self.assertEqual(self.decode(-42), + (-42, 0, 1)) + self.assertEqual(self.decode(42), + (42, 0, 1)) + self.assertEqual(self.encode((3, 14, 100), int), + 3) + self.assertEqual(self.encode((8, 4, 10), int), + 8) + self.assertEqual(self.encode((8, 5, 10), int), + 8) + self.assertEqual(self.encode((8, 7, 10), int), + 8) + self.assertEqual(self.encode((-2, 0, 100), int), + -2) + self.assertEqual(self.encode((-2, 3, 10), int), + -2) + self.assertEqual(self.encode((-2, 5, 10), int), + -2) + self.assertEqual(self.encode((-2, 7, 10), int), + -2) + for value in (-5, 5): + ts = self.decode(value) + self.assertEqual(self.encode(ts, int), value) + + def test_float(self): + self.assertEqual(self.decode(-3.14), + (-4, 1936547839769313, 2251799813685248)) + self.assertEqual(self.decode(3.14), + (3, 315251973915935, 2251799813685248)) + self.assertEqual(self.decode(2+1e-9), + (2, 281475, 281474976710656)) + self.assertEqual(self.encode((3, 14, 100), float), + 3.14) + for value in (-1.9, 1.1, 1.9, 3.14): + ts = self.decode(value) + self.assertEqual(self.encode(ts, type(value)), value) + + def test_decimal(self): + self.assertEqual(self.decode(decimal.Decimal('12300')), + (12300, 0, 1)) + self.assertEqual(self.decode(decimal.Decimal('3.14')), + (3, 7, 50)) + self.assertEqual(self.decode(decimal.Decimal('-3.14')), + (-4, 43, 50)) + self.assertEqual(self.decode(decimal.Decimal('2.000000001')), + (2, 1, 1000000000)) + self.assertEqual(str(self.encode((5, 0, 1), decimal.Decimal)), + '5') + self.assertEqual(str(self.encode((1, 0, 1000), decimal.Decimal)), + '1.000') + self.assertEqual(self.encode((3, 14, 100), decimal.Decimal), + decimal.Decimal('3.14')) + for value in (decimal.Decimal('12300'), decimal.Decimal('3.14'), decimal.Decimal('-1.9')): + ts = self.decode(value) + self.assertEqual(self.encode(ts, type(value)), value) + + def test_overflow(self): + self.assertRaises(OverflowError, self.decode, 2 ** 100) + self.assertRaises(OverflowError, self.decode, 2.0 ** 100) + self.assertRaises(OverflowError, self.decode, decimal.Decimal('2') ** decimal.Decimal('100')) + def test_main(): support.run_unittest( TimeTestCase, TestLocale, TestAsctime4dyear, - TestStrftime4dyear) + TestStrftime4dyear, + TestPytime + ) if __name__ == "__main__": test_main() diff -r 0fc10a33eb4c -r 4a6bf825b6cf Modules/_datetimemodule.c --- a/Modules/_datetimemodule.c Thu Feb 09 21:09:03 2012 +0200 +++ b/Modules/_datetimemodule.c Fri Feb 10 02:46:06 2012 +0100 @@ -4131,9 +4131,12 @@ datetime_from_timestamp(PyObject *cls, T static PyObject * datetime_best_possible(PyObject *cls, TM_FUNC f, PyObject *tzinfo) { - _PyTime_timeval t; - _PyTime_gettimeofday(&t); - return datetime_from_timet_and_us(cls, f, t.tv_sec, (int)t.tv_usec, + _PyTime_t t; + _PyTime_Get(&t); + if (_PyTime_SetDenominator(&t, 1000000) == -1) + return NULL; + return datetime_from_timet_and_us(cls, f, + t.seconds, (int)t.numerator, tzinfo); } diff -r 0fc10a33eb4c -r 4a6bf825b6cf Modules/_testcapimodule.c --- a/Modules/_testcapimodule.c Thu Feb 09 21:09:03 2012 +0200 +++ b/Modules/_testcapimodule.c Fri Feb 10 02:46:06 2012 +0100 @@ -2412,6 +2412,50 @@ run_in_subinterp(PyObject *self, PyObjec return PyLong_FromLong(r); } +static PyObject* +test_pytime_from_object(PyObject *self, PyObject *obj) +{ + _PyTime_t ts; + PyObject *seconds, *numerator, *denominator; + + if (_PyTime_FromObject(&ts, obj) == -1) + return NULL; + + seconds = _PyLong_FromTime_t(ts.seconds); + numerator = _PyLong_FromTimeFraction_t(ts.numerator); + denominator = _PyLong_FromTimeFraction_t(ts.denominator); + if (PyErr_Occurred()) { + Py_XDECREF(seconds); + Py_XDECREF(numerator); + Py_XDECREF(denominator); + return NULL; + } + return Py_BuildValue("(NNN)", seconds, numerator, denominator); +} + +static PyObject* +test_pytime_astype(PyObject *self, PyObject *args) +{ + _PyTime_t ts; + long seconds; + unsigned PY_LONG_LONG numerator, denominator; + PyObject *timestamp; + + if (!PyArg_ParseTuple(args, "(lKK)O:pytime_astype", + &seconds, &numerator, &denominator, ×tamp)) + return NULL; + + if (denominator == 0) { + PyErr_SetString(PyExc_ValueError, "denominator cannot be zero"); + return NULL; + } + + ts.seconds = (time_t)seconds; + ts.numerator = numerator; + ts.denominator = denominator; + return _PyTime_AsType(&ts, timestamp); +} + static PyMethodDef TestMethods[] = { {"raise_exception", raise_exception, METH_VARARGS}, @@ -2502,6 +2546,8 @@ static PyMethodDef TestMethods[] = { METH_NOARGS}, {"crash_no_current_thread", (PyCFunction)crash_no_current_thread, METH_NOARGS}, {"run_in_subinterp", run_in_subinterp, METH_VARARGS}, + {"pytime_fromobject", test_pytime_from_object, METH_O}, + {"pytime_astype", test_pytime_astype, METH_VARARGS}, {NULL, NULL} /* sentinel */ }; diff -r 0fc10a33eb4c -r 4a6bf825b6cf Modules/posixmodule.c --- a/Modules/posixmodule.c Thu Feb 09 21:09:03 2012 +0200 +++ b/Modules/posixmodule.c Fri Feb 10 02:46:06 2012 +0100 @@ -1060,16 +1060,6 @@ FILE_TIME_to_time_t_nsec(FILETIME *in_pt *time_out = Py_SAFE_DOWNCAST((in / 10000000) - secs_between_epochs, __int64, time_t); } -static void -time_t_to_FILE_TIME(time_t time_in, int nsec_in, FILETIME *out_ptr) -{ - /* XXX endianness */ - __int64 out; - out = time_in + secs_between_epochs; - out = out * 10000000 + nsec_in / 100; - memcpy(out_ptr, &out, sizeof(out)); -} - /* Below, we *know* that ugo+r is 0444 */ #if _S_IREAD != 0400 #error Unsupported C library @@ -1714,6 +1704,12 @@ stat_float_times(PyObject* self, PyObjec int newval = -1; if (!PyArg_ParseTuple(args, "|i:stat_float_times", &newval)) return NULL; + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "os.stat_float_times() has been deprecated, " + "use timestamp argument of os.stat() instead", + 1)) + return NULL; + if (newval == -1) /* Return old value */ return PyBool_FromLong(_stat_float_times); @@ -1723,19 +1719,29 @@ stat_float_times(PyObject* self, PyObjec } static void -fill_time(PyObject *v, int index, time_t sec, unsigned long nsec) +fill_time(PyObject *v, int index, time_t sec, unsigned long nsec, + int has_nsec, PyObject *timestamp) { PyObject *fval,*ival; -#if SIZEOF_TIME_T > SIZEOF_LONG - ival = PyLong_FromLongLong((PY_LONG_LONG)sec); -#else - ival = PyLong_FromLong((long)sec); -#endif + _PyTime_t ts; + + ival = _PyLong_FromTime_t(sec); if (!ival) return; - if (_stat_float_times) { - fval = PyFloat_FromDouble(sec + 1e-9*nsec); - } else { + if (timestamp == NULL && _stat_float_times) + timestamp = (PyObject*)&PyFloat_Type; + if (timestamp != NULL) { + if (has_nsec) { + ts.seconds = sec; + ts.numerator = nsec; + ts.denominator = 1000000000; + } + else { + _PyTime_FromTime_t(&ts, sec); + } + fval = _PyTime_AsType(&ts, timestamp); + } + else { fval = ival; Py_INCREF(fval); } @@ -1746,9 +1752,14 @@ fill_time(PyObject *v, int index, time_t /* pack a system stat C structure into the Python stat tuple (used by posix_stat() and posix_fstat()) */ static PyObject* -_pystat_fromstructstat(STRUCT_STAT *st) +_pystat_fromstructstat(STRUCT_STAT *st, PyObject *timestamp) { unsigned long ansec, mnsec, cnsec; + int has_nsec; +#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME + _PyTime_t ts; +#endif + PyObject *v = PyStructSequence_New(&StatResultType); if (v == NULL) return NULL; @@ -1780,20 +1791,24 @@ _pystat_fromstructstat(STRUCT_STAT *st) ansec = st->st_atim.tv_nsec; mnsec = st->st_mtim.tv_nsec; cnsec = st->st_ctim.tv_nsec; + has_nsec = 1; #elif defined(HAVE_STAT_TV_NSEC2) ansec = st->st_atimespec.tv_nsec; mnsec = st->st_mtimespec.tv_nsec; cnsec = st->st_ctimespec.tv_nsec; + has_nsec = 1; #elif defined(HAVE_STAT_NSEC) ansec = st->st_atime_nsec; mnsec = st->st_mtime_nsec; cnsec = st->st_ctime_nsec; + has_nsec = 1; #else ansec = mnsec = cnsec = 0; -#endif - fill_time(v, 7, st->st_atime, ansec); - fill_time(v, 8, st->st_mtime, mnsec); - fill_time(v, 9, st->st_ctime, cnsec); + has_nsec = 0; +#endif + fill_time(v, 7, st->st_atime, ansec, has_nsec, timestamp); + fill_time(v, 8, st->st_mtime, mnsec, has_nsec, timestamp); + fill_time(v, 9, st->st_ctime, cnsec, has_nsec, timestamp); #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE PyStructSequence_SET_ITEM(v, ST_BLKSIZE_IDX, @@ -1813,21 +1828,26 @@ _pystat_fromstructstat(STRUCT_STAT *st) #endif #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME { - PyObject *val; - unsigned long bsec,bnsec; - bsec = (long)st->st_birthtime; + PyObject *val; + ts.seconds = (long)st->st_birthtime; #ifdef HAVE_STAT_TV_NSEC2 - bnsec = st->st_birthtimespec.tv_nsec; + ts.numerator = st->st_birthtimespec.tv_nsec; + ts.denominator = 1000000000; #else - bnsec = 0; -#endif - if (_stat_float_times) { - val = PyFloat_FromDouble(bsec + 1e-9*bnsec); - } else { - val = PyLong_FromLong((long)bsec); - } - PyStructSequence_SET_ITEM(v, ST_BIRTHTIME_IDX, - val); + ts.numerator = 0; + ts.denominator = 1; +#endif + if (timestamp == NULL) { + if (_stat_float_times) + val = _PyTime_AsType(&ts, (PyObject*)&PyFloat_Type); + else + val = _PyTime_AsType(&ts, (PyObject*)&PyLong_Type); + } + else { + val = _PyTime_AsType(&ts, timestamp); + } + PyStructSequence_SET_ITEM(v, ST_BIRTHTIME_IDX, + val); } #endif #ifdef HAVE_STRUCT_STAT_ST_FLAGS @@ -1844,7 +1864,7 @@ _pystat_fromstructstat(STRUCT_STAT *st) } static PyObject * -posix_do_stat(PyObject *self, PyObject *args, +posix_do_stat(PyObject *self, PyObject *args, PyObject *kw, char *format, #ifdef __VMS int (*statfunc)(const char *, STRUCT_STAT *, ...), @@ -1854,15 +1874,18 @@ posix_do_stat(PyObject *self, PyObject * char *wformat, int (*wstatfunc)(const wchar_t *, STRUCT_STAT *)) { + static char *kwlist[] = {"path", "timestamp", NULL}; STRUCT_STAT st; PyObject *opath; char *path; int res; PyObject *result; + PyObject *timestamp = NULL; #ifdef MS_WINDOWS PyObject *po; - if (PyArg_ParseTuple(args, wformat, &po)) { + if (PyArg_ParseTupleAndKeywords(args, kw, wformat, kwlist, + &po, ×tamp)) { wchar_t *wpath = PyUnicode_AsUnicode(po); if (wpath == NULL) return NULL; @@ -1873,15 +1896,17 @@ posix_do_stat(PyObject *self, PyObject * if (res != 0) return win32_error_object("stat", po); - return _pystat_fromstructstat(&st); + return _pystat_fromstructstat(&st, timestamp); } /* Drop the argument parsing error as narrow strings are also valid. */ PyErr_Clear(); -#endif - - if (!PyArg_ParseTuple(args, format, - PyUnicode_FSConverter, &opath)) + timestamp = NULL; +#endif + + if (!PyArg_ParseTupleAndKeywords(args, kw, format, kwlist, + PyUnicode_FSConverter, &opath, + ×tamp)) return NULL; #ifdef MS_WINDOWS if (win32_warn_bytes_api()) { @@ -1902,7 +1927,7 @@ posix_do_stat(PyObject *self, PyObject * #endif } else - result = _pystat_fromstructstat(&st); + result = _pystat_fromstructstat(&st, timestamp); Py_DECREF(opath); return result; @@ -3393,16 +3418,16 @@ posix_rmdir(PyObject *self, PyObject *ar PyDoc_STRVAR(posix_stat__doc__, -"stat(path) -> stat result\n\n\ +"stat(path, timestamp=None) -> stat result\n\n\ Perform a stat system call on the given path."); static PyObject * -posix_stat(PyObject *self, PyObject *args) +posix_stat(PyObject *self, PyObject *args, PyObject *kw) { #ifdef MS_WINDOWS - return posix_do_stat(self, args, "O&:stat", STAT, "U:stat", win32_stat_w); + return posix_do_stat(self, args, kw, "O&|O:stat", STAT, "U|O:stat", win32_stat_w); #else - return posix_do_stat(self, args, "O&:stat", STAT, NULL, NULL); + return posix_do_stat(self, args, kw, "O&|O:stat", STAT, NULL, NULL); #endif } @@ -3540,29 +3565,44 @@ posix_uname(PyObject *self, PyObject *no #endif /* HAVE_UNAME */ +#ifdef MS_WINDOWS +int +_PyTime_AsFILE_TIME(const _PyTime_t *ts, FILETIME *filetime) +{ + _PyTime_t ts_win; + ULONGLONG value; + ULARGE_INTEGER large; + + ts_win = *ts; + if (_PyTime_SetDenominator(&ts_win, 10000000) == -1) + return -1; + + value = ts_win.seconds + secs_between_epochs; + /* FIXME: raise OverflowError and return -1 on integer overflow */ + value *= 10000000; /* 100 ns */ + value += ts_win.numerator; + + large.QuadPart = value; + filetime->dwLowDateTime = large.u.LowPart; + filetime->dwHighDateTime = large.u.HighPart; + return 0; +} +#endif + static int -extract_time(PyObject *t, time_t* sec, long* nsec) -{ - time_t intval; - if (PyFloat_Check(t)) { - double d = PyFloat_AsDouble(t); - double mod; - *sec = (time_t)d; - mod = fmod(d, 1.0); - mod *= 1e9; - *nsec = (long)mod; +parse_timestamp_tuple(PyObject *arg, + _PyTime_t *a, _PyTime_t *b, + const char *errmsg) +{ + if (!PyTuple_Check(arg) || PyTuple_Size(arg) != 2) { + PyErr_SetString(PyExc_TypeError, errmsg); return 0; } -#if SIZEOF_TIME_T > SIZEOF_LONG - intval = PyLong_AsUnsignedLongLongMask(t); -#else - intval = PyLong_AsLong(t); -#endif - if (intval == -1 && PyErr_Occurred()) - return -1; - *sec = intval; - *nsec = 0; - return 0; + if (_PyTime_FromObject(a, PyTuple_GET_ITEM(arg, 0)) == -1) + return 0; + if (_PyTime_FromObject(b, PyTuple_GET_ITEM(arg, 1)) == -1) + return 0; + return 1; } PyDoc_STRVAR(posix_utime__doc__, @@ -3580,8 +3620,7 @@ posix_utime(PyObject *self, PyObject *ar wchar_t *wpath = NULL; const char *apath; HANDLE hFile; - time_t atimesec, mtimesec; - long ansec, mnsec; + _PyTime_t atime_ts, mtime_ts; FILETIME atime, mtime; PyObject *result = NULL; @@ -3627,20 +3666,16 @@ posix_utime(PyObject *self, PyObject *ar goto done; } } - else if (!PyTuple_Check(arg) || PyTuple_Size(arg) != 2) { - PyErr_SetString(PyExc_TypeError, - "utime() arg 2 must be a tuple (atime, mtime)"); - goto done; - } else { - if (extract_time(PyTuple_GET_ITEM(arg, 0), - &atimesec, &ansec) == -1) - goto done; - time_t_to_FILE_TIME(atimesec, ansec, &atime); - if (extract_time(PyTuple_GET_ITEM(arg, 1), - &mtimesec, &mnsec) == -1) - goto done; - time_t_to_FILE_TIME(mtimesec, mnsec, &mtime); + if (!parse_timestamp_tuple(arg, &atime_ts, &mtime_ts, + "utime() arg 2 must be " + "a tuple (atime, mtime)")) + return NULL; + + if (_PyTime_AsFILE_TIME(&atime_ts, &atime) == -1) + return NULL; + if (_PyTime_AsFILE_TIME(&mtime_ts, &mtime) == -1) + return NULL; } if (!SetFileTime(hFile, NULL, &atime, &mtime)) { /* Avoid putting the file name into the error here, @@ -3659,76 +3694,78 @@ done: PyObject *opath; char *path; - time_t atime, mtime; - long ansec, mnsec; + _PyTime_t atime, mtime; int res; - PyObject* arg = Py_None; + PyObject *arg = Py_None; if (!PyArg_ParseTuple(args, "O&|O:utime", PyUnicode_FSConverter, &opath, &arg)) return NULL; + path = PyBytes_AsString(opath); if (arg == Py_None) { /* optional time values not given */ - Py_BEGIN_ALLOW_THREADS - res = utime(path, NULL); - Py_END_ALLOW_THREADS - } - else if (!PyTuple_Check(arg) || PyTuple_Size(arg) != 2) { - PyErr_SetString(PyExc_TypeError, - "utime() arg 2 must be a tuple (atime, mtime)"); - Py_DECREF(opath); - return NULL; + _PyTime_Get(&atime); + mtime = atime; } else { - if (extract_time(PyTuple_GET_ITEM(arg, 0), - &atime, &ansec) == -1) { - Py_DECREF(opath); + if (!parse_timestamp_tuple(arg, &atime, &mtime, + "utime() arg 2 must be " + "a tuple (atime, mtime)")) return NULL; - } - if (extract_time(PyTuple_GET_ITEM(arg, 1), - &mtime, &mnsec) == -1) { - Py_DECREF(opath); - return NULL; - } - - Py_BEGIN_ALLOW_THREADS - { + } + + { #ifdef HAVE_UTIMENSAT - struct timespec buf[2]; - buf[0].tv_sec = atime; - buf[0].tv_nsec = ansec; - buf[1].tv_sec = mtime; - buf[1].tv_nsec = mnsec; - res = utimensat(AT_FDCWD, path, buf, 0); + struct timespec buf[2]; + if (_PyTime_AsTimespec(&atime, &buf[0]) == -1) + goto error; + if (_PyTime_AsTimespec(&mtime, &buf[1]) == -1) + goto error; + Py_BEGIN_ALLOW_THREADS + res = utimensat(AT_FDCWD, path, buf, 0); + Py_END_ALLOW_THREADS #elif defined(HAVE_UTIMES) - struct timeval buf[2]; - buf[0].tv_sec = atime; - buf[0].tv_usec = ansec / 1000; - buf[1].tv_sec = mtime; - buf[1].tv_usec = mnsec / 1000; - res = utimes(path, buf); + struct timeval buf[2]; + if (_PyTime_AsTimeval(&atime, &buf[0]) == -1) + goto error; + if (_PyTime_AsTimeval(&mtime, &buf[1]) == -1) + goto error; + Py_BEGIN_ALLOW_THREADS + res = utimes(path, buf); + Py_END_ALLOW_THREADS #elif defined(HAVE_UTIME_H) - /* XXX should define struct utimbuf instead, above */ - struct utimbuf buf; - buf.actime = atime; - buf.modtime = mtime; - res = utime(path, &buf); + /* XXX should define struct utimbuf instead, above */ + struct utimbuf buf; + buf.actime = _PyTime_AsTime_t(&atime); + buf.modtime = _PyTime_AsTime_t(&mtime); + if (PyErr_Occurred()) + goto error; + Py_BEGIN_ALLOW_THREADS + res = utime(path, &buf); + Py_END_ALLOW_THREADS #else - time_t buf[2]; - buf[0] = atime; - buf[1] = mtime; - res = utime(path, buf); -#endif - } - Py_END_ALLOW_THREADS - } + time_t buf[2]; + buf[0] = _PyTime_AsTime_t(&atime); + buf[1] = _PyTime_AsTime_t(&mtime); + if (PyErr_Occurred()) + goto error; + Py_BEGIN_ALLOW_THREADS + res = utime(path, buf); + Py_END_ALLOW_THREADS +#endif + } + if (res < 0) { return posix_error_with_allocated_filename(opath); } Py_DECREF(opath); Py_INCREF(Py_None); return Py_None; + +error: + Py_DECREF(opath); + return NULL; #undef UTIME_EXTRACT #endif /* MS_WINDOWS */ } @@ -3745,8 +3782,7 @@ posix_futimes(PyObject *self, PyObject * { int res, fd; PyObject* arg = Py_None; - time_t atime, mtime; - long ansec, mnsec; + _PyTime_t atime, mtime; if (!PyArg_ParseTuple(args, "i|O:futimes", &fd, &arg)) return NULL; @@ -3757,39 +3793,33 @@ posix_futimes(PyObject *self, PyObject * res = futimes(fd, NULL); Py_END_ALLOW_THREADS } - else if (!PyTuple_Check(arg) || PyTuple_Size(arg) != 2) { - PyErr_SetString(PyExc_TypeError, - "futimes() arg 2 must be a tuple (atime, mtime)"); - return NULL; - } else { - if (extract_time(PyTuple_GET_ITEM(arg, 0), - &atime, &ansec) == -1) { + if (!parse_timestamp_tuple(arg, &atime, &mtime, + "futimes() arg 2 must be " + "a tuple (atime, mtime)")) return NULL; - } - if (extract_time(PyTuple_GET_ITEM(arg, 1), - &mtime, &mnsec) == -1) { - return NULL; - } - Py_BEGIN_ALLOW_THREADS + { #ifdef HAVE_FUTIMENS struct timespec buf[2]; - buf[0].tv_sec = atime; - buf[0].tv_nsec = ansec; - buf[1].tv_sec = mtime; - buf[1].tv_nsec = mnsec; + if (_PyTime_AsTimespec(&atime, &buf[0]) == -1) + return NULL; + if (_PyTime_AsTimespec(&mtime, &buf[1]) == -1) + return NULL; + Py_BEGIN_ALLOW_THREADS res = futimens(fd, buf); + Py_END_ALLOW_THREADS #else struct timeval buf[2]; - buf[0].tv_sec = atime; - buf[0].tv_usec = ansec / 1000; - buf[1].tv_sec = mtime; - buf[1].tv_usec = mnsec / 1000; + if (_PyTime_AsTimeval(&atime, &buf[0]) == -1) + return NULL; + if (_PyTime_AsTimeval(&mtime, &buf[1]) == -1) + return NULL; + Py_BEGIN_ALLOW_THREADS res = futimes(fd, buf); + Py_END_ALLOW_THREADS #endif } - Py_END_ALLOW_THREADS } if (res < 0) return posix_error(); @@ -3809,8 +3839,7 @@ posix_lutimes(PyObject *self, PyObject * PyObject *arg = Py_None; const char *path; int res; - time_t atime, mtime; - long ansec, mnsec; + _PyTime_t atime, mtime; if (!PyArg_ParseTuple(args, "O&|O:lutimes", PyUnicode_FSConverter, &opath, &arg)) @@ -3822,47 +3851,42 @@ posix_lutimes(PyObject *self, PyObject * res = lutimes(path, NULL); Py_END_ALLOW_THREADS } - else if (!PyTuple_Check(arg) || PyTuple_Size(arg) != 2) { - PyErr_SetString(PyExc_TypeError, - "lutimes() arg 2 must be a tuple (atime, mtime)"); - Py_DECREF(opath); - return NULL; - } else { - if (extract_time(PyTuple_GET_ITEM(arg, 0), - &atime, &ansec) == -1) { - Py_DECREF(opath); + if (!parse_timestamp_tuple(arg, &atime, &mtime, + "lutimes() arg 2 must be " + "a tuple (atime, mtime)")) return NULL; - } - if (extract_time(PyTuple_GET_ITEM(arg, 1), - &mtime, &mnsec) == -1) { - Py_DECREF(opath); - return NULL; - } - Py_BEGIN_ALLOW_THREADS + { #ifdef HAVE_UTIMENSAT struct timespec buf[2]; - buf[0].tv_sec = atime; - buf[0].tv_nsec = ansec; - buf[1].tv_sec = mtime; - buf[1].tv_nsec = mnsec; + if (_PyTime_AsTimespec(&atime, &buf[0]) == -1) + goto error; + if (_PyTime_AsTimespec(&mtime, &buf[1]) == -1) + goto error; + Py_BEGIN_ALLOW_THREADS res = utimensat(AT_FDCWD, path, buf, AT_SYMLINK_NOFOLLOW); + Py_END_ALLOW_THREADS #else struct timeval buf[2]; - buf[0].tv_sec = atime; - buf[0].tv_usec = ansec / 1000; - buf[1].tv_sec = mtime; - buf[1].tv_usec = mnsec / 1000; + if (_PyTime_AsTimeval(&atime, &buf[0]) == -1) + goto error; + if (_PyTime_AsTimeval(&mtime, &buf[1]) == -1) + goto error; + Py_BEGIN_ALLOW_THREADS res = lutimes(path, buf); + Py_END_ALLOW_THREADS #endif } - Py_END_ALLOW_THREADS } Py_DECREF(opath); if (res < 0) return posix_error(); Py_RETURN_NONE; + +error: + Py_DECREF(opath); + return NULL; } #endif @@ -4893,20 +4917,27 @@ posix_sched_setparam(PyObject *self, PyO #ifdef HAVE_SCHED_RR_GET_INTERVAL PyDoc_STRVAR(posix_sched_rr_get_interval__doc__, -"sched_rr_get_interval(pid) -> float\n\n\ +"sched_rr_get_interval(pid, timestamp=float) -> float\n\n\ Return the round-robin quantum for the process with PID *pid* in seconds."); static PyObject * -posix_sched_rr_get_interval(PyObject *self, PyObject *args) -{ +posix_sched_rr_get_interval(PyObject *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"pid", "timestamp", NULL}; pid_t pid; + PyObject *timestamp = NULL; struct timespec interval; - - if (!PyArg_ParseTuple(args, _Py_PARSE_PID ":sched_rr_get_interval", &pid)) + _PyTime_t ts; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + _Py_PARSE_PID "|O:sched_rr_get_interval", + kwlist, + &pid, ×tamp)) return NULL; if (sched_rr_get_interval(pid, &interval)) return posix_error(); - return PyFloat_FromDouble((double)interval.tv_sec + 1e-9*interval.tv_nsec); + _PyTime_FromTimespec(&ts, &interval); + return _PyTime_AsType(&ts, timestamp); } #endif @@ -6130,11 +6161,12 @@ posix_setgroups(PyObject *self, PyObject #if defined(HAVE_WAIT3) || defined(HAVE_WAIT4) static PyObject * -wait_helper(pid_t pid, int status, struct rusage *ru) +wait_helper(pid_t pid, int status, struct rusage *ru, PyObject *timestamp) { PyObject *result; static PyObject *struct_rusage; _Py_IDENTIFIER(struct_rusage); + _PyTime_t ts; if (pid == -1) return posix_error(); @@ -6154,14 +6186,13 @@ wait_helper(pid_t pid, int status, struc if (!result) return NULL; -#ifndef doubletime -#define doubletime(TV) ((double)(TV).tv_sec + (TV).tv_usec * 0.000001) -#endif - + _PyTime_FromTimeval(&ts, &ru->ru_utime); PyStructSequence_SET_ITEM(result, 0, - PyFloat_FromDouble(doubletime(ru->ru_utime))); + _PyTime_AsType(&ts, timestamp)); + + _PyTime_FromTimeval(&ts, &ru->ru_stime); PyStructSequence_SET_ITEM(result, 1, - PyFloat_FromDouble(doubletime(ru->ru_stime))); + _PyTime_AsType(&ts, timestamp)); #define SET_INT(result, index, value)\ PyStructSequence_SET_ITEM(result, index, PyLong_FromLong(value)) SET_INT(result, 2, ru->ru_maxrss); @@ -6191,51 +6222,55 @@ wait_helper(pid_t pid, int status, struc #ifdef HAVE_WAIT3 PyDoc_STRVAR(posix_wait3__doc__, -"wait3(options) -> (pid, status, rusage)\n\n\ +"wait3(options[, timestamp=float]) -> (pid, status, rusage)\n\n\ Wait for completion of a child process."); static PyObject * -posix_wait3(PyObject *self, PyObject *args) -{ +posix_wait3(PyObject *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"options", "timestamp", NULL}; pid_t pid; int options; struct rusage ru; WAIT_TYPE status; WAIT_STATUS_INT(status) = 0; - - if (!PyArg_ParseTuple(args, "i:wait3", &options)) + PyObject *timestamp = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|O:wait3", kwlist, &options, ×tamp)) return NULL; Py_BEGIN_ALLOW_THREADS pid = wait3(&status, options, &ru); Py_END_ALLOW_THREADS - return wait_helper(pid, WAIT_STATUS_INT(status), &ru); + return wait_helper(pid, WAIT_STATUS_INT(status), &ru, timestamp); } #endif /* HAVE_WAIT3 */ #ifdef HAVE_WAIT4 PyDoc_STRVAR(posix_wait4__doc__, -"wait4(pid, options) -> (pid, status, rusage)\n\n\ +"wait4(pid, options[, timestamp=float]) -> (pid, status, rusage)\n\n\ Wait for completion of a given child process."); static PyObject * -posix_wait4(PyObject *self, PyObject *args) -{ +posix_wait4(PyObject *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"pid", "options", "timestamp", NULL}; pid_t pid; int options; struct rusage ru; WAIT_TYPE status; WAIT_STATUS_INT(status) = 0; - - if (!PyArg_ParseTuple(args, _Py_PARSE_PID "i:wait4", &pid, &options)) + PyObject *timestamp = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, _Py_PARSE_PID "i|O:wait4", kwlist, &pid, &options, ×tamp)) return NULL; Py_BEGIN_ALLOW_THREADS pid = wait4(pid, &status, options, &ru); Py_END_ALLOW_THREADS - return wait_helper(pid, WAIT_STATUS_INT(status), &ru); + return wait_helper(pid, WAIT_STATUS_INT(status), &ru, timestamp); } #endif /* HAVE_WAIT4 */ @@ -6362,20 +6397,20 @@ posix_wait(PyObject *self, PyObject *noa PyDoc_STRVAR(posix_lstat__doc__, -"lstat(path) -> stat result\n\n\ +"lstat(path, timestamp=None) -> stat result\n\n\ Like stat(path), but do not follow symbolic links."); static PyObject * -posix_lstat(PyObject *self, PyObject *args) +posix_lstat(PyObject *self, PyObject *args, PyObject *kw) { #ifdef HAVE_LSTAT - return posix_do_stat(self, args, "O&:lstat", lstat, NULL, NULL); + return posix_do_stat(self, args, kw, "O&|O:lstat", lstat, NULL, NULL); #else /* !HAVE_LSTAT */ #ifdef MS_WINDOWS - return posix_do_stat(self, args, "O&:lstat", win32_lstat, "U:lstat", + return posix_do_stat(self, args, kw, "O&|O:lstat", win32_lstat, "U|O:lstat", win32_lstat_w); #else - return posix_do_stat(self, args, "O&:lstat", STAT, NULL, NULL); + return posix_do_stat(self, args, "kw, O&|O:lstat", STAT, NULL, NULL); #endif #endif /* !HAVE_LSTAT */ } @@ -7334,16 +7369,19 @@ done: #endif PyDoc_STRVAR(posix_fstat__doc__, -"fstat(fd) -> stat result\n\n\ +"fstat(fd, timestamp=None) -> stat result\n\n\ Like stat(), but for an open file descriptor."); static PyObject * -posix_fstat(PyObject *self, PyObject *args) -{ +posix_fstat(PyObject *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"fd", "timestamp", NULL}; int fd; STRUCT_STAT st; int res; - if (!PyArg_ParseTuple(args, "i:fstat", &fd)) + PyObject *timestamp = NULL; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|O:fstat", kwlist, + &fd, ×tamp)) return NULL; #ifdef __VMS /* on OpenVMS we must ensure that all bytes are written to the file */ @@ -7362,7 +7400,7 @@ posix_fstat(PyObject *self, PyObject *ar #endif } - return _pystat_fromstructstat(&st); + return _pystat_fromstructstat(&st, timestamp); } PyDoc_STRVAR(posix_isatty__doc__, @@ -9646,22 +9684,25 @@ posix_fchownat(PyObject *self, PyObject #ifdef HAVE_FSTATAT PyDoc_STRVAR(posix_fstatat__doc__, -"fstatat(dirfd, path, flags=0) -> stat result\n\n\ +"fstatat(dirfd, path, flags=0, timestamp=None) -> stat result\n\n\ Like stat() but if path is relative, it is taken as relative to dirfd.\n\ flags is optional and may be 0 or AT_SYMLINK_NOFOLLOW.\n\ If path is relative and dirfd is the special value AT_FDCWD, then path\n\ is interpreted relative to the current working directory."); static PyObject * -posix_fstatat(PyObject *self, PyObject *args) -{ +posix_fstatat(PyObject *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"dirfd", "path", "flags", "timestamp", NULL}; PyObject *opath; char *path; STRUCT_STAT st; int dirfd, res, flags = 0; - - if (!PyArg_ParseTuple(args, "iO&|i:fstatat", - &dirfd, PyUnicode_FSConverter, &opath, &flags)) + PyObject *timestamp = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iO&|iO:fstatat", kwlist, + &dirfd, PyUnicode_FSConverter, &opath, + &flags, ×tamp)) return NULL; path = PyBytes_AsString(opath); @@ -9672,7 +9713,7 @@ posix_fstatat(PyObject *self, PyObject * if (res != 0) return posix_error(); - return _pystat_fromstructstat(&st); + return _pystat_fromstructstat(&st, timestamp); } #endif @@ -9690,8 +9731,7 @@ posix_futimesat(PyObject *self, PyObject char *path; int res, dirfd; PyObject* arg = Py_None; - time_t atime, mtime; - long ansec, mnsec; + _PyTime_t atime, mtime; if (!PyArg_ParseTuple(args, "iO&|O:futimesat", &dirfd, PyUnicode_FSConverter, &opath, &arg)) @@ -9703,49 +9743,43 @@ posix_futimesat(PyObject *self, PyObject res = futimesat(dirfd, path, NULL); Py_END_ALLOW_THREADS } - else if (!PyTuple_Check(arg) || PyTuple_Size(arg) != 2) { - PyErr_SetString(PyExc_TypeError, - "futimesat() arg 3 must be a tuple (atime, mtime)"); - Py_DECREF(opath); - return NULL; - } else { - if (extract_time(PyTuple_GET_ITEM(arg, 0), - &atime, &ansec) == -1) { - Py_DECREF(opath); + if (!parse_timestamp_tuple(arg, &atime, &mtime, + "futimesat() arg 3 must be " + "a tuple (atime, mtime)")) return NULL; - } - if (extract_time(PyTuple_GET_ITEM(arg, 1), - &mtime, &mnsec) == -1) { - Py_DECREF(opath); - return NULL; - } - - Py_BEGIN_ALLOW_THREADS + { #ifdef HAVE_UTIMENSAT struct timespec buf[2]; - buf[0].tv_sec = atime; - buf[0].tv_nsec = ansec; - buf[1].tv_sec = mtime; - buf[1].tv_nsec = mnsec; + if (_PyTime_AsTimespec(&atime, &buf[0]) == -1) + goto error; + if (_PyTime_AsTimespec(&mtime, &buf[1]) == -1) + goto error; + Py_BEGIN_ALLOW_THREADS res = utimensat(dirfd, path, buf, 0); + Py_END_ALLOW_THREADS #else struct timeval buf[2]; - buf[0].tv_sec = atime; - buf[0].tv_usec = ansec / 1000; - buf[1].tv_sec = mtime; - buf[1].tv_usec = mnsec / 1000; + if (_PyTime_AsTimeval(&atime, &buf[0]) == -1) + goto error; + if (_PyTime_AsTimeval(&mtime, &buf[1]) == -1) + goto error; + Py_BEGIN_ALLOW_THREADS res = futimesat(dirfd, path, buf); + Py_END_ALLOW_THREADS #endif } - Py_END_ALLOW_THREADS } Py_DECREF(opath); if (res < 0) { return posix_error(); } Py_RETURN_NONE; + +error: + Py_DECREF(opath); + return NULL; } #endif @@ -10644,7 +10678,7 @@ static PyMethodDef posix_methods[] = { #ifdef HAVE_FDOPENDIR {"flistdir", posix_flistdir, METH_VARARGS, posix_flistdir__doc__}, #endif - {"lstat", posix_lstat, METH_VARARGS, posix_lstat__doc__}, + {"lstat", (PyCFunction)posix_lstat, METH_VARARGS | METH_KEYWORDS, posix_lstat__doc__}, {"mkdir", posix_mkdir, METH_VARARGS, posix_mkdir__doc__}, #ifdef HAVE_NICE {"nice", posix_nice, METH_VARARGS, posix_nice__doc__}, @@ -10664,7 +10698,8 @@ static PyMethodDef posix_methods[] = { {"rename", posix_rename, METH_VARARGS, posix_rename__doc__}, {"replace", posix_replace, METH_VARARGS, posix_replace__doc__}, {"rmdir", posix_rmdir, METH_VARARGS, posix_rmdir__doc__}, - {"stat", posix_stat, METH_VARARGS, posix_stat__doc__}, + {"stat", (PyCFunction)posix_stat, + METH_VARARGS | METH_KEYWORDS, posix_stat__doc__}, {"stat_float_times", stat_float_times, METH_VARARGS, stat_float_times__doc__}, #if defined(HAVE_SYMLINK) && !defined(MS_WINDOWS) {"symlink", posix_symlink, METH_VARARGS, posix_symlink__doc__}, @@ -10729,7 +10764,9 @@ static PyMethodDef posix_methods[] = { {"sched_getscheduler", posix_sched_getscheduler, METH_VARARGS, posix_sched_getscheduler__doc__}, #endif #ifdef HAVE_SCHED_RR_GET_INTERVAL - {"sched_rr_get_interval", posix_sched_rr_get_interval, METH_VARARGS, posix_sched_rr_get_interval__doc__}, + {"sched_rr_get_interval", (PyCFunction)posix_sched_rr_get_interval, + METH_VARARGS | METH_KEYWORDS, + posix_sched_rr_get_interval__doc__}, #endif #ifdef HAVE_SCHED_SETPARAM {"sched_setparam", posix_sched_setparam, METH_VARARGS, posix_sched_setparam__doc__}, @@ -10825,10 +10862,12 @@ static PyMethodDef posix_methods[] = { {"wait", posix_wait, METH_NOARGS, posix_wait__doc__}, #endif /* HAVE_WAIT */ #ifdef HAVE_WAIT3 - {"wait3", posix_wait3, METH_VARARGS, posix_wait3__doc__}, + {"wait3", (PyCFunction)posix_wait3, + METH_VARARGS | METH_KEYWORDS, posix_wait3__doc__}, #endif /* HAVE_WAIT3 */ #ifdef HAVE_WAIT4 - {"wait4", posix_wait4, METH_VARARGS, posix_wait4__doc__}, + {"wait4", (PyCFunction)posix_wait4, + METH_VARARGS | METH_KEYWORDS, posix_wait4__doc__}, #endif /* HAVE_WAIT4 */ #if defined(HAVE_WAITID) && !defined(__APPLE__) {"waitid", posix_waitid, METH_VARARGS, posix_waitid__doc__}, @@ -10879,7 +10918,8 @@ static PyMethodDef posix_methods[] = { {"sendfile", (PyCFunction)posix_sendfile, METH_VARARGS | METH_KEYWORDS, posix_sendfile__doc__}, #endif - {"fstat", posix_fstat, METH_VARARGS, posix_fstat__doc__}, + {"fstat", (PyCFunction)posix_fstat, METH_VARARGS | METH_KEYWORDS, + posix_fstat__doc__}, {"isatty", posix_isatty, METH_VARARGS, posix_isatty__doc__}, #ifdef HAVE_PIPE {"pipe", posix_pipe, METH_NOARGS, posix_pipe__doc__}, @@ -11014,7 +11054,8 @@ static PyMethodDef posix_methods[] = { {"fchownat", posix_fchownat, METH_VARARGS, posix_fchownat__doc__}, #endif /* HAVE_FCHOWNAT */ #ifdef HAVE_FSTATAT - {"fstatat", posix_fstatat, METH_VARARGS, posix_fstatat__doc__}, + {"fstatat", (PyCFunction)posix_fstatat, METH_VARARGS | METH_KEYWORDS, + posix_fstatat__doc__}, #endif #ifdef HAVE_FUTIMESAT {"futimesat", posix_futimesat, METH_VARARGS, posix_futimesat__doc__}, diff -r 0fc10a33eb4c -r 4a6bf825b6cf Modules/resource.c --- a/Modules/resource.c Thu Feb 09 21:09:03 2012 +0200 +++ b/Modules/resource.c Fri Feb 10 02:46:06 2012 +0100 @@ -56,13 +56,17 @@ static int initialized; static PyTypeObject StructRUsageType; static PyObject * -resource_getrusage(PyObject *self, PyObject *args) +resource_getrusage(PyObject *self, PyObject *args, PyObject *kwargs) { + static char *kwlist[] = {"who", "timestamp", NULL}; int who; + PyObject *timestamp = NULL; struct rusage ru; PyObject *result; + _PyTime_t ts; - if (!PyArg_ParseTuple(args, "i:getrusage", &who)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|O:getrusage", kwlist, + &who, ×tamp)) return NULL; if (getrusage(who, &ru) == -1) { @@ -79,10 +83,12 @@ resource_getrusage(PyObject *self, PyObj if (!result) return NULL; + _PyTime_FromTimeval(&ts, &ru.ru_utime); PyStructSequence_SET_ITEM(result, 0, - PyFloat_FromDouble(doubletime(ru.ru_utime))); + _PyTime_AsType(&ts, timestamp)); + _PyTime_FromTimeval(&ts, &ru.ru_stime); PyStructSequence_SET_ITEM(result, 1, - PyFloat_FromDouble(doubletime(ru.ru_stime))); + _PyTime_AsType(&ts, timestamp)); PyStructSequence_SET_ITEM(result, 2, PyLong_FromLong(ru.ru_maxrss)); PyStructSequence_SET_ITEM(result, 3, PyLong_FromLong(ru.ru_ixrss)); PyStructSequence_SET_ITEM(result, 4, PyLong_FromLong(ru.ru_idrss)); @@ -210,7 +216,8 @@ resource_getpagesize(PyObject *self, PyO static struct PyMethodDef resource_methods[] = { - {"getrusage", resource_getrusage, METH_VARARGS}, + {"getrusage", (PyCFunction)resource_getrusage, + METH_VARARGS | METH_KEYWORDS}, {"getrlimit", resource_getrlimit, METH_VARARGS}, {"setrlimit", resource_setrlimit, METH_VARARGS}, {"getpagesize", resource_getpagesize, METH_NOARGS}, diff -r 0fc10a33eb4c -r 4a6bf825b6cf Modules/selectmodule.c --- a/Modules/selectmodule.c Thu Feb 09 21:09:03 2012 +0200 +++ b/Modules/selectmodule.c Fri Feb 10 02:46:06 2012 +0100 @@ -204,44 +204,32 @@ select_select(PyObject *self, PyObject * #endif /* SELECT_USES_HEAP */ PyObject *ifdlist, *ofdlist, *efdlist; PyObject *ret = NULL; - PyObject *tout = Py_None; fd_set ifdset, ofdset, efdset; - double timeout; + PyObject *timeout_obj = Py_None; + _PyTime_t timeout; struct timeval tv, *tvp; - long seconds; int imax, omax, emax, max; int n; /* convert arguments */ if (!PyArg_UnpackTuple(args, "select", 3, 4, - &ifdlist, &ofdlist, &efdlist, &tout)) + &ifdlist, &ofdlist, &efdlist, &timeout_obj)) return NULL; - if (tout == Py_None) + if (timeout_obj == Py_None) tvp = (struct timeval *)0; - else if (!PyNumber_Check(tout)) { - PyErr_SetString(PyExc_TypeError, - "timeout must be a float or None"); - return NULL; - } else { - timeout = PyFloat_AsDouble(tout); - if (timeout == -1 && PyErr_Occurred()) + if (_PyTime_FromObject(&timeout, timeout_obj) == -1) return NULL; - if (timeout > (double)LONG_MAX) { - PyErr_SetString(PyExc_OverflowError, - "timeout period too long"); + + if (_PyTime_AsTimeval(&timeout, &tv) == -1) return NULL; - } - if (timeout < 0) { + + if (tv.tv_sec < 0) { PyErr_SetString(PyExc_ValueError, "timeout must be non-negative"); return NULL; } - seconds = (long)timeout; - timeout = timeout - (double)seconds; - tv.tv_sec = seconds; - tv.tv_usec = (long)(timeout * 1E6); tvp = &tv; } @@ -493,29 +481,23 @@ any descriptors that have events or erro static PyObject * poll_poll(pollObject *self, PyObject *args) { - PyObject *result_list = NULL, *tout = NULL; + PyObject *result_list = NULL, *timeout_obj = NULL; + _PyTime_t timeout_ts; int timeout = 0, poll_result, i, j; PyObject *value = NULL, *num = NULL; - if (!PyArg_UnpackTuple(args, "poll", 0, 1, &tout)) { + if (!PyArg_UnpackTuple(args, "poll", 0, 1, &timeout_obj)) { return NULL; } /* Check values for timeout */ - if (tout == NULL || tout == Py_None) + if (timeout_obj == NULL || timeout_obj == Py_None) timeout = -1; - else if (!PyNumber_Check(tout)) { - PyErr_SetString(PyExc_TypeError, - "timeout must be an integer or None"); - return NULL; - } else { - tout = PyNumber_Long(tout); - if (!tout) + if (_PyTime_FromObject(&timeout_ts, timeout_obj) == -1) return NULL; - timeout = PyLong_AsLong(tout); - Py_DECREF(tout); - if (timeout == -1 && PyErr_Occurred()) + timeout = _PyTime_AsMillisec(&timeout_ts); + if (PyErr_Occurred()) return NULL; } @@ -1315,7 +1297,8 @@ fd is the target file descriptor of the static PyObject * pyepoll_poll(pyEpoll_Object *self, PyObject *args, PyObject *kwds) { - double dtimeout = -1.; + PyObject *timeout_obj = NULL; + _PyTime_t timeout_ts; int timeout; int maxevents = -1; int nfds, i; @@ -1326,21 +1309,20 @@ pyepoll_poll(pyEpoll_Object *self, PyObj if (self->epfd < 0) return pyepoll_err_closed(); - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|di:poll", kwlist, - &dtimeout, &maxevents)) { + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oi:poll", kwlist, + &timeout_obj, &maxevents)) { return NULL; } - if (dtimeout < 0) { + if (timeout_obj == NULL) { timeout = -1; } - else if (dtimeout * 1000.0 > INT_MAX) { - PyErr_SetString(PyExc_OverflowError, - "timeout is too large"); - return NULL; - } else { - timeout = (int)(dtimeout * 1000.0); + if (_PyTime_FromObject(&timeout_ts, timeout_obj) == -1) + return NULL; + timeout = _PyTime_AsMillisec(&timeout_ts); + if (PyErr_Occurred()) + return NULL; } if (maxevents == -1) { diff -r 0fc10a33eb4c -r 4a6bf825b6cf Modules/signalmodule.c --- a/Modules/signalmodule.c Thu Feb 09 21:09:03 2012 +0200 +++ b/Modules/signalmodule.c Fri Feb 10 02:46:06 2012 +0100 @@ -116,41 +116,30 @@ static HANDLE sigint_event = NULL; #ifdef HAVE_GETITIMER static PyObject *ItimerError; -/* auxiliary functions for setitimer/getitimer */ -static void -timeval_from_double(double d, struct timeval *tv) -{ - tv->tv_sec = floor(d); - tv->tv_usec = fmod(d, 1.0) * 1000000.0; -} - -Py_LOCAL_INLINE(double) -double_from_timeval(struct timeval *tv) -{ - return tv->tv_sec + (double)(tv->tv_usec / 1000000.0); -} - static PyObject * -itimer_retval(struct itimerval *iv) +itimer_retval(struct itimerval *iv, PyObject *timestamp) { PyObject *r, *v; + _PyTime_t ts; r = PyTuple_New(2); if (r == NULL) - return NULL; + return NULL; - if(!(v = PyFloat_FromDouble(double_from_timeval(&iv->it_value)))) { - Py_DECREF(r); - return NULL; + _PyTime_FromTimeval(&ts, &iv->it_value); + v = _PyTime_AsType(&ts, timestamp); + if(!v) { + Py_DECREF(r); + return NULL; } - PyTuple_SET_ITEM(r, 0, v); - if(!(v = PyFloat_FromDouble(double_from_timeval(&iv->it_interval)))) { - Py_DECREF(r); - return NULL; + _PyTime_FromTimeval(&ts, &iv->it_interval); + v = _PyTime_AsType(&ts, timestamp); + if(!v) { + Py_DECREF(r); + return NULL; } - PyTuple_SET_ITEM(r, 1, v); return r; @@ -459,29 +448,46 @@ PySignal_SetWakeupFd(int fd) #ifdef HAVE_SETITIMER static PyObject * -signal_setitimer(PyObject *self, PyObject *args) +signal_setitimer(PyObject *self, PyObject *args, PyObject *kwargs) { - double first; - double interval = 0; + static char *kwlist[] = {"which", "value", "interval", "timestamp", NULL}; + PyObject *value_obj, *interval_obj = NULL, *timestamp = NULL; + _PyTime_t value, interval; int which; struct itimerval new, old; - if(!PyArg_ParseTuple(args, "id|d:setitimer", &which, &first, &interval)) - return NULL; + if(!PyArg_ParseTupleAndKeywords(args, kwargs, "iO|OO:setitimer", kwlist, + &which, &value_obj, + &interval_obj, ×tamp)) + return NULL; - timeval_from_double(first, &new.it_value); - timeval_from_double(interval, &new.it_interval); + if (_PyTime_FromObject(&value, value_obj) == -1) + return NULL; + if (_PyTime_AsTimeval(&value, &new.it_value) == -1) + return NULL; + + if (interval_obj != NULL) { + if (_PyTime_FromObject(&interval, interval_obj) == -1) + return NULL; + if (_PyTime_AsTimeval(&interval, &new.it_interval) == -1) + return NULL; + } + else { + new.it_interval.tv_sec = 0; + new.it_interval.tv_usec = 0; + } + /* Let OS check "which" value */ if (setitimer(which, &new, &old) != 0) { - PyErr_SetFromErrno(ItimerError); - return NULL; + PyErr_SetFromErrno(ItimerError); + return NULL; } - return itimer_retval(&old); + return itimer_retval(&old, timestamp); } PyDoc_STRVAR(setitimer_doc, -"setitimer(which, seconds[, interval])\n\ +"setitimer(which, value[, interval, timestamp=float])\n\ \n\ Sets given itimer (one of ITIMER_REAL, ITIMER_VIRTUAL\n\ or ITIMER_PROF) to fire after value seconds and after\n\ @@ -494,24 +500,27 @@ Returns old values as a tuple: (delay, i #ifdef HAVE_GETITIMER static PyObject * -signal_getitimer(PyObject *self, PyObject *args) +signal_getitimer(PyObject *self, PyObject *args, PyObject *kwargs) { + static char *kwlist[] = {"which", "timestamp", NULL}; int which; + PyObject *timestamp = NULL; struct itimerval old; - if (!PyArg_ParseTuple(args, "i:getitimer", &which)) - return NULL; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|O:getitimer", kwlist, + &which, ×tamp)) + return NULL; if (getitimer(which, &old) != 0) { - PyErr_SetFromErrno(ItimerError); - return NULL; + PyErr_SetFromErrno(ItimerError); + return NULL; } - return itimer_retval(&old); + return itimer_retval(&old, timestamp); } PyDoc_STRVAR(getitimer_doc, -"getitimer(which)\n\ +"getitimer(which, timestamp=float)\n\ \n\ Returns current value of given itimer."); #endif @@ -777,23 +786,19 @@ Returns a struct_siginfo containing info static PyObject * signal_sigtimedwait(PyObject *self, PyObject *args) { - PyObject *signals, *timeout; + PyObject *signals, *timeout_obj; + _PyTime_t timeout; struct timespec buf; sigset_t set; siginfo_t si; int err; - if (!PyArg_ParseTuple(args, "OO:sigtimedwait", &signals, &timeout)) + if (!PyArg_ParseTuple(args, "OO:sigtimedwait", &signals, &timeout_obj)) return NULL; - if (!PyTuple_Check(timeout) || PyTuple_Size(timeout) != 2) { - PyErr_SetString(PyExc_TypeError, - "sigtimedwait() arg 2 must be a tuple " - "(timeout_sec, timeout_nsec)"); + if (_PyTime_FromObject(&timeout, timeout_obj) == -1) return NULL; - } else if (!PyArg_ParseTuple(timeout, "ll:sigtimedwait", - &(buf.tv_sec), &(buf.tv_nsec))) - return NULL; + _PyTime_AsTimespec(&timeout, &buf); if (buf.tv_sec < 0 || buf.tv_nsec < 0) { PyErr_SetString(PyExc_ValueError, "timeout must be non-negative"); @@ -817,7 +822,7 @@ signal_sigtimedwait(PyObject *self, PyOb } PyDoc_STRVAR(signal_sigtimedwait_doc, -"sigtimedwait(sigset, (timeout_sec, timeout_nsec)) -> struct_siginfo\n\ +"sigtimedwait(sigset, timeout) -> struct_siginfo\n\ \n\ Like sigwaitinfo(), but with a timeout specified as a tuple of (seconds,\n\ nanoseconds)."); @@ -863,10 +868,12 @@ static PyMethodDef signal_methods[] = { {"alarm", signal_alarm, METH_VARARGS, alarm_doc}, #endif #ifdef HAVE_SETITIMER - {"setitimer", signal_setitimer, METH_VARARGS, setitimer_doc}, + {"setitimer", (PyCFunction)signal_setitimer, + METH_VARARGS | METH_KEYWORDS, setitimer_doc}, #endif #ifdef HAVE_GETITIMER - {"getitimer", signal_getitimer, METH_VARARGS, getitimer_doc}, + {"getitimer", (PyCFunction)signal_getitimer, + METH_VARARGS | METH_KEYWORDS, getitimer_doc}, #endif {"signal", signal_signal, METH_VARARGS, signal_doc}, {"getsignal", signal_getsignal, METH_VARARGS, getsignal_doc}, diff -r 0fc10a33eb4c -r 4a6bf825b6cf Modules/timemodule.c --- a/Modules/timemodule.c Thu Feb 09 21:09:03 2012 +0200 +++ b/Modules/timemodule.c Fri Feb 10 02:46:06 2012 +0100 @@ -40,24 +40,30 @@ #include #endif +#if (defined(MS_WINDOWS) && !defined(__BORLANDC__)) || defined(HAVE_CLOCK) +# define HAVE_PYCLOCK +#endif + /* Forward declarations */ -static int floatsleep(double); -static double floattime(void); +static int pysleep(_PyTime_t *ts); static PyObject * -time_time(PyObject *self, PyObject *unused) +time_time(PyObject *self, PyObject *args, PyObject *kwargs) { - double secs; - secs = floattime(); - if (secs == 0.0) { - PyErr_SetFromErrno(PyExc_IOError); + static char *kwlist[] = {"timestamp", NULL}; + PyObject *timestamp = NULL; + _PyTime_t ts; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O:time", kwlist, + ×tamp)) return NULL; - } - return PyFloat_FromDouble(secs); + + _PyTime_Get(&ts); + return _PyTime_AsType(&ts, timestamp); } PyDoc_STRVAR(time_doc, -"time() -> floating point number\n\ +"time(timestamp=float) -> floating point number\n\ \n\ Return the current time in seconds since the Epoch.\n\ Fractions of a second may be present if the system clock provides them."); @@ -72,65 +78,91 @@ Fractions of a second may be present if #endif #endif -static PyObject * -pyclock(void) +static int +pyclock(_PyTime_t *ts) { - clock_t value; - value = clock(); - if (value == (clock_t)-1) { + clock_t processor_time; + processor_time = clock(); + if (processor_time == (clock_t)-1) { PyErr_SetString(PyExc_RuntimeError, "the processor time used is not available " "or its value cannot be represented"); - return NULL; + return -1; } - return PyFloat_FromDouble((double)value / CLOCKS_PER_SEC); + ts->seconds = 0; + assert(sizeof(clock_t) <= sizeof(_PyTime_fraction_t)); + ts->numerator = Py_SAFE_DOWNCAST(processor_time, + clock_t, _PyTime_fraction_t); + ts->denominator = CLOCKS_PER_SEC; + return 0; } #endif /* HAVE_CLOCK */ #if defined(MS_WINDOWS) && !defined(__BORLANDC__) /* Win32 has better clock replacement; we have our own version, due to Mark Hammond and Tim Peters */ -static PyObject * -win32_clock(int fallback) +static int +win32_clock(_PyTime_t *ts, int fallback) { static LONGLONG cpu_frequency = 0; - static LONGLONG ctrStart; + static LONGLONG start; LARGE_INTEGER now; - double diff; + LONGLONG dt; if (cpu_frequency == 0) { LARGE_INTEGER freq; QueryPerformanceCounter(&now); - ctrStart = now.QuadPart; + start = now.QuadPart; if (!QueryPerformanceFrequency(&freq) || freq.QuadPart == 0) { /* Unlikely to happen - this works on all intel machines at least! Revert to clock() */ - if (fallback) - return pyclock(); - else - return PyErr_SetFromWindowsErr(0); + if (fallback) { + return pyclock(ts); + } + else { + PyErr_SetFromWindowsErr(0); + return -1; + } } cpu_frequency = freq.QuadPart; } QueryPerformanceCounter(&now); - diff = (double)(now.QuadPart - ctrStart); - return PyFloat_FromDouble(diff / (double)cpu_frequency); + dt = now.QuadPart - start; + + ts->seconds = 0; + assert(sizeof(LONGLONG) <= sizeof(_PyTime_fraction_t)); + ts->numerator = Py_SAFE_DOWNCAST(dt, + LONGLONG, _PyTime_fraction_t); + ts->denominator = Py_SAFE_DOWNCAST(cpu_frequency, + LONGLONG, _PyTime_fraction_t); + return 0; } #endif #if (defined(MS_WINDOWS) && !defined(__BORLANDC__)) || defined(HAVE_CLOCK) static PyObject * -time_clock(PyObject *self, PyObject *unused) +time_clock(PyObject *self, PyObject *args, PyObject *kwargs) { + static char *kwlist[] = {"timestamp", NULL}; + _PyTime_t ts; + PyObject *timestamp = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O:clock", kwlist, + ×tamp)) + return NULL; + #if defined(MS_WINDOWS) && !defined(__BORLANDC__) - return win32_clock(1); + if (win32_clock(&ts, 1) == -1) + return NULL; #else - return pyclock(); + if (pyclock(&ts) == -1) + return NULL; #endif + return _PyTime_AsType(&ts, timestamp); } PyDoc_STRVAR(clock_doc, -"clock() -> floating point number\n\ +"clock(timestamp=float) -> floating point number\n\ \n\ Return the CPU time or real time since the start of the process or since\n\ the first call to clock(). This has as much precision as the system\n\ @@ -139,13 +171,17 @@ records."); #ifdef HAVE_CLOCK_GETTIME static PyObject * -time_clock_gettime(PyObject *self, PyObject *args) +time_clock_gettime(PyObject *self, PyObject *args, PyObject *kwargs) { + static char *kwlist[] = {"clk_id", "timestamp", NULL}; + PyObject *timestamp = NULL; int ret; clockid_t clk_id; struct timespec tp; + _PyTime_t ts; - if (!PyArg_ParseTuple(args, "i:clock_gettime", &clk_id)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|O:clock_gettime", kwlist, + &clk_id, ×tamp)) return NULL; ret = clock_gettime((clockid_t)clk_id, &tp); @@ -153,25 +189,29 @@ time_clock_gettime(PyObject *self, PyObj PyErr_SetFromErrno(PyExc_IOError); return NULL; } - - return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9); + _PyTime_FromTimespec(&ts, &tp); + return _PyTime_AsType(&ts, timestamp); } PyDoc_STRVAR(clock_gettime_doc, -"clock_gettime(clk_id) -> floating point number\n\ +"clock_gettime(clk_id, timestamp=float) -> floating point number\n\ \n\ Return the time of the specified clock clk_id."); #endif #ifdef HAVE_CLOCK_GETRES static PyObject * -time_clock_getres(PyObject *self, PyObject *args) +time_clock_getres(PyObject *self, PyObject *args, PyObject *kwargs) { + static char *kwlist[] = {"clk_id", "timestamp", NULL}; + PyObject *timestamp = NULL; int ret; clockid_t clk_id; struct timespec tp; + _PyTime_t ts; - if (!PyArg_ParseTuple(args, "i:clock_getres", &clk_id)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|O:clock_getres", kwlist, + &clk_id, ×tamp)) return NULL; ret = clock_getres((clockid_t)clk_id, &tp); @@ -179,28 +219,28 @@ time_clock_getres(PyObject *self, PyObje PyErr_SetFromErrno(PyExc_IOError); return NULL; } - - return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9); + _PyTime_FromTimespec(&ts, &tp); + return _PyTime_AsType(&ts, timestamp); } PyDoc_STRVAR(clock_getres_doc, -"clock_getres(clk_id) -> floating point number\n\ +"clock_getres(clk_id, timestamp=float) -> floating point number\n\ \n\ Return the resolution (precision) of the specified clock clk_id."); #endif static PyObject * -time_sleep(PyObject *self, PyObject *args) +time_sleep(PyObject *self, PyObject *arg) { - double secs; - if (!PyArg_ParseTuple(args, "d:sleep", &secs)) + _PyTime_t ts; + if (_PyTime_FromObject(&ts, arg) == -1) return NULL; - if (secs < 0) { + if (ts.seconds < 0) { PyErr_SetString(PyExc_ValueError, "sleep length must be non-negative"); return NULL; } - if (floatsleep(secs) != 0) + if (pysleep(&ts) != 0) return NULL; Py_INCREF(Py_None); return Py_None; @@ -707,10 +747,19 @@ not present, current time as returned by #ifdef HAVE_MKTIME static PyObject * -time_mktime(PyObject *self, PyObject *tup) +time_mktime(PyObject *self, PyObject *args, PyObject *kwargs) { + static char *kwlist[] = {"t", "timestamp", NULL}; + PyObject *timestamp = NULL; + PyObject *tup; struct tm buf; time_t tt; + _PyTime_t ts; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:mktime", kwlist, + &tup, ×tamp)) + return NULL; + if (!gettmarg(tup, &buf)) return NULL; buf.tm_wday = -1; /* sentinel; original value ignored */ @@ -722,7 +771,8 @@ time_mktime(PyObject *self, PyObject *tu "mktime argument out of range"); return NULL; } - return PyFloat_FromDouble((double)tt); + _PyTime_FromTime_t(&ts, tt); + return _PyTime_AsType(&ts, timestamp); } PyDoc_STRVAR(mktime_doc, @@ -768,12 +818,14 @@ the local timezone used by methods such should not be relied on."); #endif /* HAVE_WORKING_TZSET */ -static PyObject * -time_wallclock(PyObject *self, PyObject *unused) +static int +pywallclock(_PyTime_t *ts) { #if defined(MS_WINDOWS) && !defined(__BORLANDC__) - return win32_clock(1); -#elif defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) + return win32_clock(ts, 1); +#else + +#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) static int clk_index = 0; clockid_t clk_ids[] = { #ifdef CLOCK_MONOTONIC_RAW @@ -793,20 +845,39 @@ time_wallclock(PyObject *self, PyObject clockid_t clk_id = clk_ids[clk_index]; ret = clock_gettime(clk_id, &tp); if (ret == 0) - return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9); + { + _PyTime_FromTimespec(ts, &tp); + return 0; + } clk_index++; if (Py_ARRAY_LENGTH(clk_ids) <= clk_index) clk_index = -1; } - return time_time(self, NULL); -#else - return time_time(self, NULL); +#endif /* defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) */ + + _PyTime_Get(ts); + return 0; #endif } +static PyObject * +time_wallclock(PyObject *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"timestamp", NULL}; + PyObject *timestamp = NULL; + _PyTime_t ts; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O:wallclock", kwlist, + ×tamp)) + return NULL; + if (pywallclock(&ts)) + return NULL; + return _PyTime_AsType(&ts, timestamp); +} + PyDoc_STRVAR(wallclock_doc, -"wallclock() -> float\n\ +"wallclock(timestamp=float)\n\ \n\ Return the current time in fractions of a second to the system's best\n\ ability. Use this when the most accurate representation of wall-clock is\n\ @@ -821,11 +892,11 @@ calls is valid."); #ifdef HAVE_PYTIME_MONOTONIC static PyObject * -time_monotonic(PyObject *self, PyObject *unused) +time_monotonic(PyObject *self, PyObject *args, PyObject *kwargs) { -#if defined(MS_WINDOWS) && !defined(__BORLANDC__) - return win32_clock(0); -#else + static char *kwlist[] = {"timestamp", NULL}; + PyObject *timestamp = NULL; +#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) static int clk_index = 0; clockid_t clk_ids[] = { #ifdef CLOCK_MONOTONIC_RAW @@ -835,12 +906,26 @@ time_monotonic(PyObject *self, PyObject }; int ret; struct timespec tp; +#endif + _PyTime_t ts; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O:monotonic", kwlist, + ×tamp)) + return NULL; + +#if defined(MS_WINDOWS) && !defined(__BORLANDC__) + if (win32_clock(&ts, 0) == -1) + return NULL; + return _PyTime_AsType(&ts, timestamp); +#else while (0 <= clk_index) { clockid_t clk_id = clk_ids[clk_index]; ret = clock_gettime(clk_id, &tp); if (ret == 0) - return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9); + { + _PyTime_FromTimespec(&ts, &tp); + return _PyTime_AsType(&ts, timestamp); + } clk_index++; if (Py_ARRAY_LENGTH(clk_ids) <= clk_index) @@ -968,26 +1053,32 @@ PyInit_timezone(PyObject *m) { static PyMethodDef time_methods[] = { - {"time", time_time, METH_NOARGS, time_doc}, -#if (defined(MS_WINDOWS) && !defined(__BORLANDC__)) || defined(HAVE_CLOCK) - {"clock", time_clock, METH_NOARGS, clock_doc}, + {"time", (PyCFunction)time_time, + METH_VARARGS | METH_KEYWORDS, time_doc}, +#ifdef HAVE_PYCLOCK + {"clock", (PyCFunction)time_clock, + METH_VARARGS | METH_KEYWORDS, clock_doc}, #endif #ifdef HAVE_CLOCK_GETTIME - {"clock_gettime", time_clock_gettime, METH_VARARGS, clock_gettime_doc}, + {"clock_gettime", (PyCFunction)time_clock_gettime, + METH_VARARGS | METH_KEYWORDS, clock_gettime_doc}, #endif #ifdef HAVE_CLOCK_GETRES - {"clock_getres", time_clock_getres, METH_VARARGS, clock_getres_doc}, + {"clock_getres", (PyCFunction)time_clock_getres, + METH_VARARGS | METH_KEYWORDS, clock_getres_doc}, #endif - {"sleep", time_sleep, METH_VARARGS, sleep_doc}, + {"sleep", time_sleep, METH_O, sleep_doc}, {"gmtime", time_gmtime, METH_VARARGS, gmtime_doc}, {"localtime", time_localtime, METH_VARARGS, localtime_doc}, {"asctime", time_asctime, METH_VARARGS, asctime_doc}, {"ctime", time_ctime, METH_VARARGS, ctime_doc}, #ifdef HAVE_MKTIME - {"mktime", time_mktime, METH_O, mktime_doc}, + {"mktime", (PyCFunction)time_mktime, + METH_VARARGS | METH_KEYWORDS, mktime_doc}, #endif #ifdef HAVE_PYTIME_MONOTONIC - {"monotonic", time_monotonic, METH_NOARGS, monotonic_doc}, + {"monotonic", (PyCFunction)time_monotonic, + METH_VARARGS | METH_KEYWORDS, monotonic_doc}, #endif #ifdef HAVE_STRFTIME {"strftime", time_strftime, METH_VARARGS, strftime_doc}, @@ -996,7 +1087,8 @@ static PyMethodDef time_methods[] = { #ifdef HAVE_WORKING_TZSET {"tzset", time_tzset, METH_NOARGS, tzset_doc}, #endif - {"wallclock", time_wallclock, METH_NOARGS, wallclock_doc}, + {"wallclock", (PyCFunction)time_wallclock, + METH_VARARGS | METH_KEYWORDS, wallclock_doc}, {NULL, NULL} /* sentinel */ }; @@ -1081,32 +1173,22 @@ PyInit_time(void) return m; } -static double -floattime(void) -{ - _PyTime_timeval t; - _PyTime_gettimeofday(&t); - return (double)t.tv_sec + t.tv_usec*0.000001; -} - - -/* Implement floatsleep() for various platforms. +/* Implement sleep() for various platforms. When interrupted (or when another error occurs), return -1 and set an exception; else return 0. */ +#if (defined(__WATCOMC__) && !defined(__QNX__)) || defined(MS_WINDOWS) || defined(PYOS_OS2) +#endif + static int -floatsleep(double secs) +pysleep(_PyTime_t *ts) { /* XXX Should test for MS_WINDOWS first! */ #if defined(HAVE_SELECT) && !defined(__EMX__) struct timeval t; - double frac; int err; - frac = fmod(secs, 1.0); - secs = floor(secs); - t.tv_sec = (long)secs; - t.tv_usec = (long)(frac*1000000.0); + _PyTime_AsTimeval(ts, &t); Py_BEGIN_ALLOW_THREADS err = select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &t); Py_END_ALLOW_THREADS @@ -1124,54 +1206,64 @@ floatsleep(double secs) } } #elif defined(__WATCOMC__) && !defined(__QNX__) + int ms = _PyTime_AsMillisec(ts); + if (PyErr_Occurred()) + return -1; + /* XXX Can't interrupt this sleep */ Py_BEGIN_ALLOW_THREADS - delay((int)(secs * 1000 + 0.5)); /* delay() uses milliseconds */ + delay(ms); /* delay() uses milliseconds */ Py_END_ALLOW_THREADS #elif defined(MS_WINDOWS) - { - double millisecs = secs * 1000.0; - unsigned long ul_millis; + int millisecs; + unsigned long ul_millis; - if (millisecs > (double)ULONG_MAX) { - PyErr_SetString(PyExc_OverflowError, - "sleep length is too large"); + millisecs = _PyTime_AsMillisec(ts); + if (PyErr_Occurred()) + return -1; + assert(millisecs >= 0); + ul_millis = (unsigned long)millisecs; + + Py_BEGIN_ALLOW_THREADS + /* Allow sleep(0) to maintain win32 semantics, and as decreed + * by Guido, only the main thread can be interrupted. + */ + if (ul_millis == 0 || !_PyOS_IsMainThread()) + Sleep(ul_millis); + else { + DWORD rc; + HANDLE hInterruptEvent = _PyOS_SigintEvent(); + ResetEvent(hInterruptEvent); + rc = WaitForSingleObject(hInterruptEvent, ul_millis); + if (rc == WAIT_OBJECT_0) { + Py_BLOCK_THREADS + errno = EINTR; + PyErr_SetFromErrno(PyExc_IOError); return -1; } - Py_BEGIN_ALLOW_THREADS - /* Allow sleep(0) to maintain win32 semantics, and as decreed - * by Guido, only the main thread can be interrupted. - */ - ul_millis = (unsigned long)millisecs; - if (ul_millis == 0 || !_PyOS_IsMainThread()) - Sleep(ul_millis); - else { - DWORD rc; - HANDLE hInterruptEvent = _PyOS_SigintEvent(); - ResetEvent(hInterruptEvent); - rc = WaitForSingleObject(hInterruptEvent, ul_millis); - if (rc == WAIT_OBJECT_0) { - Py_BLOCK_THREADS - errno = EINTR; - PyErr_SetFromErrno(PyExc_IOError); - return -1; - } - } - Py_END_ALLOW_THREADS } + Py_END_ALLOW_THREADS #elif defined(PYOS_OS2) + int ms = _PyTime_AsMillisec(ts); + if (PyErr_Occurred()) + return -1; + /* This Sleep *IS* Interruptable by Exceptions */ Py_BEGIN_ALLOW_THREADS - if (DosSleep(secs * 1000) != NO_ERROR) { + if (DosSleep(ms) != NO_ERROR) { Py_BLOCK_THREADS PyErr_SetFromErrno(PyExc_IOError); return -1; } Py_END_ALLOW_THREADS #else + time_t seconds = _PyTime_AsTime_t(ts); + if (PyErr_Occurred()) + return -1; + /* XXX Can't interrupt this sleep */ Py_BEGIN_ALLOW_THREADS - sleep((int)secs); + sleep((int)seconds); Py_END_ALLOW_THREADS #endif diff -r 0fc10a33eb4c -r 4a6bf825b6cf Python/pytime.c --- a/Python/pytime.c Thu Feb 09 21:09:03 2012 +0200 +++ b/Python/pytime.c Fri Feb 10 02:46:06 2012 +0100 @@ -1,4 +1,5 @@ #include "Python.h" +#include "float.h" #ifdef MS_WINDOWS #include #endif @@ -18,24 +19,214 @@ extern int ftime(struct timeb *); #endif +#ifdef MS_WINDOWS +/* Seconds between 1.1.1601 and 1.1.1970 */ +static __int64 secs_between_epochs = 11644473600; +#endif + +#if SIZEOF_TIME_T == SIZEOF_LONG +# define PY_TIME_T_MAX LONG_MAX +#elif SIZEOF_TIME_T == SIZEOF_LONG_LONG +# define PY_TIME_T_MAX PY_LLONG_MAX +#else +# error "Unknown size_t size" +#endif + + +#define MICROSECONDS 1000000 +#define NANOSECONDS 1000000000 + +int +_PyTime_SetDenominator(_PyTime_t *ts, + _PyTime_fraction_t new_denominator) +{ + _PyTime_fraction_t k; + + assert(new_denominator != 0); + + if (ts->denominator == new_denominator) + return 0; + + if (ts->numerator) { + if (ts->numerator > ts->denominator) { + k = ts->numerator / ts->denominator; + if (k > (_PyTime_fraction_t)(PY_TIME_T_MAX - ts->seconds)) { + PyErr_SetString(PyExc_OverflowError, + "number of seconds doesn't fit in time_t"); + return -1; + } + ts->seconds += (time_t)k; + ts->numerator %= ts->denominator; + } + if (new_denominator >= ts->denominator) { + k = new_denominator / ts->denominator; + ts->numerator *= k; + } + else { + k = ts->denominator / new_denominator; + ts->numerator /= k; + } + } + ts->denominator = new_denominator; + return 0; +} + void -_PyTime_gettimeofday(_PyTime_timeval *tp) +_PyTime_SimplifyFraction(_PyTime_t *ts) +{ + _PyTime_fraction_t a, b, gcd; + + a = ts->numerator; + b = ts->denominator; + while (b) { + gcd = b; + b = a % b; + a = gcd; + } + gcd = a; + ts->numerator /= gcd; + ts->denominator /= gcd; +} + +time_t +_PyTime_AsTime_t(const _PyTime_t *ts) +{ + _PyTime_fraction_t k; + time_t seconds; + + assert(ts->denominator != 0); + seconds = ts->seconds; + if (ts->numerator > ts->denominator) { + k = ts->numerator / ts->denominator; + if (k > (_PyTime_fraction_t)(PY_TIME_T_MAX - seconds)) { + PyErr_SetString(PyExc_OverflowError, + "number of seconds doesn't fit in time_t"); + return (time_t)-1; + } + seconds += (time_t)k; + } + return seconds; +} + +void +_PyTime_FromTime_t(_PyTime_t *ts, time_t seconds) +{ + ts->seconds = seconds; + ts->numerator = 0; + ts->denominator = 1; +} + +int +_PyTime_AsMillisec(_PyTime_t *ts) +{ + _PyTime_t millisec; + int result; + + assert(ts->denominator != 0); + millisec = *ts; + _PyTime_SetDenominator(&millisec, 1000); + if (millisec.seconds > INT_MAX) + goto overflow; + result = (int)millisec.seconds; + if (result > INT_MAX / 1000) + goto overflow; + result *= 1000; + if (millisec.numerator > (_PyTime_fraction_t)(INT_MAX - result)) + goto overflow; + result += (int)millisec.numerator; + return result; + +overflow: + PyErr_SetString(PyExc_OverflowError, + "number of milliseconds doesn't fit in int"); + return -1; +} + +#if defined(HAVE_GETTIMEOFDAY) || defined(MS_WINDOWS) +void +_PyTime_FromTimeval(_PyTime_t *ts, struct timeval *tv) +{ + ts->seconds = (time_t)tv->tv_sec; + ts->numerator = tv->tv_usec; + ts->denominator = MICROSECONDS; +} + +int +_PyTime_AsTimeval(const _PyTime_t *ts, struct timeval *tv) +{ + _PyTime_t microsec; + + assert(ts->denominator != 0); + microsec = *ts; + if (_PyTime_SetDenominator(µsec, MICROSECONDS) == -1) + return -1; +#ifdef MS_WINDOWS + if (microsec.seconds > LONG_MAX) { + PyErr_SetString(PyExc_OverflowError, + "number of seconds doesn't fit in time_t"); + return -1; + } + tv->tv_sec = (long)microsec.seconds; +#else + tv->tv_sec = microsec.seconds; +#endif + tv->tv_usec = (long)microsec.numerator; + return 0; +} +#endif + +#if defined(HAVE_CLOCK_GETTIME) +void +_PyTime_FromTimespec(_PyTime_t *ts, struct timespec *tv) +{ + ts->seconds = tv->tv_sec; + ts->numerator = tv->tv_nsec; + ts->denominator = NANOSECONDS; +} + +int +_PyTime_AsTimespec(const _PyTime_t *ts, struct timespec *tv) +{ + _PyTime_t nanosec; + + assert(ts->denominator != 0); + nanosec = *ts; + if (_PyTime_SetDenominator(&nanosec, NANOSECONDS) == -1) + return -1; + tv->tv_sec = nanosec.seconds; + tv->tv_nsec = (long)nanosec.numerator; + return 0; +} +#endif + +void +_PyTime_Get(_PyTime_t *ts) { #ifdef MS_WINDOWS FILETIME system_time; ULARGE_INTEGER large; - ULONGLONG microseconds; + ULONGLONG value; GetSystemTimeAsFileTime(&system_time); large.u.LowPart = system_time.dwLowDateTime; large.u.HighPart = system_time.dwHighDateTime; - /* 11,644,473,600,000,000: number of microseconds between + /* 116,444,736,000,000,000: number of 100 ns between the 1st january 1601 and the 1st january 1970 (369 years + 89 leap days). */ - microseconds = large.QuadPart / 10 - 11644473600000000; - tp->tv_sec = microseconds / 1000000; - tp->tv_usec = microseconds % 1000000; + value = large.QuadPart - 116444736000000000; + ts->seconds = 0; + ts->numerator = value; + ts->denominator = 10000000; #else + +#ifdef HAVE_GETTIMEOFDAY + struct timeval tv; + int err; +#endif +#if defined(HAVE_FTIME) + struct timeb t; +#endif + /* There are three ways to get the time: (1) gettimeofday() -- resolution in microseconds (2) ftime() -- resolution in milliseconds @@ -47,30 +238,400 @@ _PyTime_gettimeofday(_PyTime_timeval *tp #ifdef HAVE_GETTIMEOFDAY #ifdef GETTIMEOFDAY_NO_TZ - if (gettimeofday(tp) == 0) + err = gettimeofday(&tv); +#else /* !GETTIMEOFDAY_NO_TZ */ + err = gettimeofday(&tv, (struct timezone *)NULL); +#endif /* !GETTIMEOFDAY_NO_TZ */ + if (err == 0) { + _PyTime_FromTimeval(ts, &tv); return; -#else /* !GETTIMEOFDAY_NO_TZ */ - if (gettimeofday(tp, (struct timezone *)NULL) == 0) - return; -#endif /* !GETTIMEOFDAY_NO_TZ */ + } #endif /* !HAVE_GETTIMEOFDAY */ #if defined(HAVE_FTIME) - { - struct timeb t; - ftime(&t); - tp->tv_sec = t.time; - tp->tv_usec = t.millitm * 1000; - } + ftime(&t); + ts->seconds = t.time; + ts->numerator = t.millitm; + ts->denominator = 1000; #else /* !HAVE_FTIME */ - tp->tv_sec = time(NULL); - tp->tv_usec = 0; + _PyTime_FromTime_t(ts, time(NULL)); #endif /* !HAVE_FTIME */ #endif /* MS_WINDOWS */ } void +_PyTime_gettimeofday(_PyTime_timeval *tv) +{ + _PyTime_t ts; + _PyTime_Get(&ts); + if (_PyTime_SetDenominator(&ts, MICROSECONDS) == -1) { + /* integer overflow */ + PyErr_Clear(); + tv->tv_sec = PY_TIME_T_MAX; + tv->tv_usec = 0; + } + else { + tv->tv_sec = ts.seconds; + tv->tv_usec = (long)ts.numerator; + } +} + +PyObject* +_PyLong_FromTime_t(time_t value) +{ +#if SIZEOF_TIME_T <= SIZEOF_LONG + return PyLong_FromLong((long)value); +#else + assert(sizeof(time_t) <= sizeof(PY_LONG_LONG)); + return PyLong_FromLongLong((PY_LONG_LONG)value); +#endif +} + +static time_t +_PyLong_AsTime_t(PyObject *obj) +{ +#if SIZEOF_TIME_T <= SIZEOF_LONG + return (time_t)PyLong_AsLong(obj); +#else + assert(sizeof(time_t) <= sizeof(PY_LONG_LONG)); + return (time_t)PyLong_AsLongLong(obj); +#endif +} + +/* Convert a timestamp to a PyFloat object */ +static PyObject* +_PyTime_AsFloat(const _PyTime_t *ts) +{ + double d; + assert(ts->denominator != 0); + d = (double)ts->seconds; + d += (double)ts->numerator / (double)ts->denominator; + return PyFloat_FromDouble(d); +} + +/* Convert a timestamp to a PyLong object */ +static PyObject* +_PyTime_AsLong(const _PyTime_t *ts) +{ + PyObject *a, *b, *c; + + assert(ts->denominator != 0); + a = _PyLong_FromTime_t(ts->seconds); + if (a == NULL) + return NULL; + /* round to nearest with ties going to nearest even integer */ + b = _PyLong_FromTimeFraction_t(ts->numerator / ts->denominator); + if (b == NULL) { + Py_DECREF(a); + return NULL; + } + c = PyNumber_Add(a, b); + Py_DECREF(a); + Py_DECREF(b); + return c; +} + +/* Convert a timestamp to a decimal.Decimal object */ +static PyObject* +_PyTime_AsDecimal(const _PyTime_t *ts) +{ + static PyObject* module = NULL; + static PyObject* decimal = NULL; + static PyObject* exponent_context = NULL; + static PyObject* context = NULL; + /* exponent cache, dictionary of: + int (denominator) => Decimal (1/denominator) */ + static PyObject* exponent_cache = NULL; + PyObject *t = NULL; + PyObject *key, *exponent, *quantized; + _Py_IDENTIFIER(quantize); + _Py_IDENTIFIER(__truediv__); + + assert(ts->denominator != 0); + + if (!module) { + module = PyImport_ImportModuleNoBlock("decimal"); + if (module == NULL) + return NULL; + } + + if (!decimal) { + decimal = PyObject_GetAttrString(module, "Decimal"); + if (decimal == NULL) + return NULL; + } + + if (context == NULL) { + /* Use 12 decimal digits to store 10,000 years in seconds + 9 + decimal digits for the floating part in nanoseconds + 1 decimal + digit to round correctly. + + context = decimal.Context(22, rounding=decimal.ROUND_HALF_EVEN) + exponent_context = decimal.Context(1, rounding=decimal.ROUND_HALF_EVEN) + */ + PyObject *context_class, *rounding; + context_class = PyObject_GetAttrString(module, "Context"); + if (context_class == NULL) + return NULL; + rounding = PyObject_GetAttrString(module, "ROUND_HALF_EVEN"); + if (rounding == NULL) { + Py_DECREF(context_class); + return NULL; + } + context = PyObject_CallFunction(context_class, "iO", 22, rounding); + if (context == NULL) { + Py_DECREF(context_class); + Py_DECREF(rounding); + return NULL; + } + + exponent_context = PyObject_CallFunction(context_class, "iO", 1, rounding); + Py_DECREF(context_class); + Py_DECREF(rounding); + if (exponent_context == NULL) { + Py_CLEAR(context); + return NULL; + } + } + + /* t = decimal.Decimal(value) */ + if (ts->seconds) { + PyObject *f = _PyLong_FromTime_t(ts->seconds); + t = PyObject_CallFunction(decimal, "O", f); + Py_CLEAR(f); + } + else { + t = PyObject_CallFunction(decimal, "iO", 0, context); + } + if (t == NULL) + return NULL; + + if (ts->numerator) { + /* t += decimal.Decimal(numerator, ctx) / decimal.Decimal(denominator, ctx) */ + PyObject *a, *b, *c, *d, *x; + + x = _PyLong_FromTimeFraction_t(ts->numerator); + if (x == NULL) + goto error; + a = PyObject_CallFunction(decimal, "OO", x, context); + Py_CLEAR(x); + if (a == NULL) + goto error; + + x = _PyLong_FromTimeFraction_t(ts->denominator); + if (x == NULL) { + Py_DECREF(a); + goto error; + } + b = PyObject_CallFunction(decimal, "OO", x, context); + Py_CLEAR(x); + if (b == NULL) { + Py_DECREF(a); + goto error; + } + + c = _PyObject_CallMethodId(a, &PyId___truediv__, "OO", + b, context); + Py_DECREF(a); + Py_DECREF(b); + if (c == NULL) + goto error; + + d = PyNumber_Add(t, c); + Py_DECREF(c); + if (d == NULL) + goto error; + Py_DECREF(t); + t = d; + } + + if (exponent_cache == NULL) { + exponent_cache = PyDict_New(); + if (exponent_cache == NULL) + goto error; + } + + key = _PyLong_FromTimeFraction_t(ts->denominator); + if (key == NULL) + goto error; + exponent = PyDict_GetItem(exponent_cache, key); + if (exponent == NULL) { + /* exponent = decimal.Decimal(1) / decimal.Decimal(resolution) */ + PyObject *one, *denominator; + + one = PyObject_CallFunction(decimal, "i", 1); + if (one == NULL) { + Py_DECREF(key); + goto error; + } + + denominator = PyObject_CallFunction(decimal, "O", key); + if (denominator == NULL) { + Py_DECREF(key); + Py_DECREF(one); + goto error; + } + + exponent = _PyObject_CallMethodId(one, &PyId___truediv__, "OO", + denominator, exponent_context); + Py_DECREF(one); + Py_DECREF(denominator); + if (exponent == NULL) { + Py_DECREF(key); + goto error; + } + + if (PyDict_SetItem(exponent_cache, key, exponent) < 0) { + Py_DECREF(key); + Py_DECREF(exponent); + goto error; + } + Py_DECREF(key); + } + + /* t = t.quantize(exponent, None, context) */ + quantized = _PyObject_CallMethodId(t, &PyId_quantize, "OOO", + exponent, Py_None, context); + if (quantized == NULL) + goto error; + Py_DECREF(t); + t = quantized; + + return t; + +error: + Py_XDECREF(t); + return NULL; +} + +int +is_decimal_type(PyObject *obj) +{ + PyObject *module, *name; + _Py_IDENTIFIER(__name__); + _Py_IDENTIFIER(__module__); + + if (!PyType_Check(obj)) + return 0; + + module = _PyObject_GetAttrId(obj, &PyId___module__); + name = _PyObject_GetAttrId(obj, &PyId___name__); + if (module != NULL && PyUnicode_Check(module) + && name != NULL && PyUnicode_Check(name)) { + if (PyUnicode_CompareWithASCIIString(module, "decimal") == 0 + && PyUnicode_CompareWithASCIIString(name, "Decimal") == 0) + return 1; + } + else + PyErr_Clear(); + return 0; +} + +PyObject* +_PyTime_AsType(const _PyTime_t *ts, PyObject *format) +{ + assert(ts->denominator != 0); + + if (format == NULL || (PyTypeObject *)format == &PyFloat_Type) + return _PyTime_AsFloat(ts); + + if ((PyTypeObject *)format == &PyLong_Type) + return _PyTime_AsLong(ts); + + if (is_decimal_type(format)) + return _PyTime_AsDecimal(ts); + + PyErr_Format(PyExc_ValueError, "Unknown timestamp format: %R", format); + return NULL; +} + +static int +_PyTime_FromDouble(_PyTime_t *ts, PyObject *obj) +{ + double d, mod, intpart; + int exp; + + d = PyFloat_AsDouble(obj); + if (PyErr_Occurred()) + return -1; + + mod = modf(d, &intpart); + ts->seconds = (time_t)intpart; + if ((double)ts->seconds != intpart) { + PyErr_SetString(PyExc_OverflowError, + "number of seconds doesn't fit in time_t"); + return -1; + } + frexp(d, &exp); + if (mod < 0.0 && mod != 0.0) { + mod += 1.0; + ts->seconds -= 1; + } + + exp = DBL_MANT_DIG; +#if defined(HAVE_LONG_LONG) + exp = Py_MIN(exp, SIZEOF_LONG_LONG * 8); +#else + exp = Py_MIN(exp, SIZEOF_LONG * 8); +#endif + mod = ldexp(mod, exp); + ts->numerator = (_PyTime_fraction_t)mod; + ts->denominator = (_PyTime_fraction_t)1 << exp; + + _PyTime_SimplifyFraction(ts); + return 0; +} + +static int +_PyTime_FromDecimal(_PyTime_t *ts, PyObject *obj) +{ + double d, mod, intpart; + + d = PyFloat_AsDouble(obj); + if (PyErr_Occurred()) + return -1; + mod = modf(d, &intpart); + ts->seconds = (time_t)intpart; + if ((double)ts->seconds != intpart) { + PyErr_SetString(PyExc_OverflowError, + "number of seconds doesn't fit in time_t"); + return -1; + } + if (mod < 0.0 && mod != 0.0) { + mod += 1.0; + ts->seconds -= 1; + } + mod *= (double)NANOSECONDS; + ts->numerator = (long)(mod + 0.5); + ts->denominator = NANOSECONDS; + _PyTime_SimplifyFraction(ts); + return 0; +} + +static int +_PyTime_FromLong(_PyTime_t *ts, PyObject *obj) +{ + time_t seconds = _PyLong_AsTime_t(obj); + if (PyErr_Occurred()) + return -1; + _PyTime_FromTime_t(ts, seconds); + return 0; +} + + +int +_PyTime_FromObject(_PyTime_t *ts, PyObject *obj) +{ + if (PyLong_Check(obj)) + return _PyTime_FromLong(ts, obj); + else if (!PyFloat_Check(obj) && is_decimal_type((PyObject*)Py_TYPE(obj))) + return _PyTime_FromDecimal(ts, obj); + else + return _PyTime_FromDouble(ts, obj); +} + +void _PyTime_Init() { /* Do nothing. Needed to force linking. */