mirror of
https://github.com/python/cpython.git
synced 2025-11-25 04:34:37 +00:00
gh-67795: Accept any real numbers as timestamp and timeout (GH-139224)
Some checks are pending
Tests / (push) Blocked by required conditions
Tests / Windows MSI (push) Blocked by required conditions
Tests / Change detection (push) Waiting to run
Tests / Docs (push) Blocked by required conditions
Tests / Check if Autoconf files are up to date (push) Blocked by required conditions
Tests / Check if generated files are up to date (push) Blocked by required conditions
Tests / Ubuntu SSL tests with OpenSSL (push) Blocked by required conditions
Tests / Ubuntu SSL tests with AWS-LC (push) Blocked by required conditions
Tests / Android (aarch64) (push) Blocked by required conditions
Tests / Android (x86_64) (push) Blocked by required conditions
Tests / WASI (push) Blocked by required conditions
Tests / Hypothesis tests on Ubuntu (push) Blocked by required conditions
Tests / Address sanitizer (push) Blocked by required conditions
Tests / Sanitizers (push) Blocked by required conditions
Tests / Cross build Linux (push) Blocked by required conditions
Tests / CIFuzz (push) Blocked by required conditions
Tests / All required checks pass (push) Blocked by required conditions
Lint / lint (push) Waiting to run
mypy / Run mypy on Lib/_pyrepl (push) Waiting to run
mypy / Run mypy on Lib/test/libregrtest (push) Waiting to run
mypy / Run mypy on Lib/tomllib (push) Waiting to run
mypy / Run mypy on Tools/build (push) Waiting to run
mypy / Run mypy on Tools/cases_generator (push) Waiting to run
mypy / Run mypy on Tools/clinic (push) Waiting to run
mypy / Run mypy on Tools/jit (push) Waiting to run
mypy / Run mypy on Tools/peg_generator (push) Waiting to run
Some checks are pending
Tests / (push) Blocked by required conditions
Tests / Windows MSI (push) Blocked by required conditions
Tests / Change detection (push) Waiting to run
Tests / Docs (push) Blocked by required conditions
Tests / Check if Autoconf files are up to date (push) Blocked by required conditions
Tests / Check if generated files are up to date (push) Blocked by required conditions
Tests / Ubuntu SSL tests with OpenSSL (push) Blocked by required conditions
Tests / Ubuntu SSL tests with AWS-LC (push) Blocked by required conditions
Tests / Android (aarch64) (push) Blocked by required conditions
Tests / Android (x86_64) (push) Blocked by required conditions
Tests / WASI (push) Blocked by required conditions
Tests / Hypothesis tests on Ubuntu (push) Blocked by required conditions
Tests / Address sanitizer (push) Blocked by required conditions
Tests / Sanitizers (push) Blocked by required conditions
Tests / Cross build Linux (push) Blocked by required conditions
Tests / CIFuzz (push) Blocked by required conditions
Tests / All required checks pass (push) Blocked by required conditions
Lint / lint (push) Waiting to run
mypy / Run mypy on Lib/_pyrepl (push) Waiting to run
mypy / Run mypy on Lib/test/libregrtest (push) Waiting to run
mypy / Run mypy on Lib/tomllib (push) Waiting to run
mypy / Run mypy on Tools/build (push) Waiting to run
mypy / Run mypy on Tools/cases_generator (push) Waiting to run
mypy / Run mypy on Tools/clinic (push) Waiting to run
mypy / Run mypy on Tools/jit (push) Waiting to run
mypy / Run mypy on Tools/peg_generator (push) Waiting to run
Functions that take timestamp or timeout arguments now accept any real numbers (such as Decimal and Fraction), not only integers or floats, although this does not improve precision.
This commit is contained in:
parent
6ec058a1f7
commit
1a2e00c97a
20 changed files with 342 additions and 97 deletions
|
|
@ -535,6 +535,9 @@ Other constructors, all class methods:
|
|||
:c:func:`localtime` function. Raise :exc:`OSError` instead of
|
||||
:exc:`ValueError` on :c:func:`localtime` failure.
|
||||
|
||||
.. versionchanged:: next
|
||||
Accepts any real number as *timestamp*, not only integer or float.
|
||||
|
||||
|
||||
.. classmethod:: date.fromordinal(ordinal)
|
||||
|
||||
|
|
@ -1020,6 +1023,10 @@ Other constructors, all class methods:
|
|||
.. versionchanged:: 3.6
|
||||
:meth:`fromtimestamp` may return instances with :attr:`.fold` set to 1.
|
||||
|
||||
.. versionchanged:: next
|
||||
Accepts any real number as *timestamp*, not only integer or float.
|
||||
|
||||
|
||||
.. classmethod:: datetime.utcfromtimestamp(timestamp)
|
||||
|
||||
Return the UTC :class:`.datetime` corresponding to the POSIX timestamp, with
|
||||
|
|
@ -1060,6 +1067,9 @@ Other constructors, all class methods:
|
|||
|
||||
Use :meth:`datetime.fromtimestamp` with :const:`UTC` instead.
|
||||
|
||||
.. versionchanged:: next
|
||||
Accepts any real number as *timestamp*, not only integer or float.
|
||||
|
||||
|
||||
.. classmethod:: datetime.fromordinal(ordinal)
|
||||
|
||||
|
|
|
|||
|
|
@ -3618,7 +3618,8 @@ features:
|
|||
where each member is an int expressing nanoseconds.
|
||||
- If *times* is not ``None``,
|
||||
it must be a 2-tuple of the form ``(atime, mtime)``
|
||||
where each member is an int or float expressing seconds.
|
||||
where each member is a real number expressing seconds,
|
||||
rounded down to nanoseconds.
|
||||
- If *times* is ``None`` and *ns* is unspecified,
|
||||
this is equivalent to specifying ``ns=(atime_ns, mtime_ns)``
|
||||
where both times are the current time.
|
||||
|
|
@ -3645,6 +3646,9 @@ features:
|
|||
.. versionchanged:: 3.6
|
||||
Accepts a :term:`path-like object`.
|
||||
|
||||
.. versionchanged:: next
|
||||
Accepts any real numbers as *times*, not only integers or floats.
|
||||
|
||||
|
||||
.. function:: walk(top, topdown=True, onerror=None, followlinks=False)
|
||||
|
||||
|
|
@ -4050,7 +4054,7 @@ Naturally, they are all only available on Linux.
|
|||
the timer will fire when the timer's clock
|
||||
(set by *clockid* in :func:`timerfd_create`) reaches *initial* seconds.
|
||||
|
||||
The timer's interval is set by the *interval* :py:class:`float`.
|
||||
The timer's interval is set by the *interval* real number.
|
||||
If *interval* is zero, the timer only fires once, on the initial expiration.
|
||||
If *interval* is greater than zero, the timer fires every time *interval*
|
||||
seconds have elapsed since the previous expiration.
|
||||
|
|
|
|||
|
|
@ -129,8 +129,9 @@ The module defines the following:
|
|||
|
||||
Empty iterables are allowed, but acceptance of three empty iterables is
|
||||
platform-dependent. (It is known to work on Unix but not on Windows.) The
|
||||
optional *timeout* argument specifies a time-out as a floating-point number
|
||||
in seconds. When the *timeout* argument is omitted the function blocks until
|
||||
optional *timeout* argument specifies a time-out in seconds; it may be
|
||||
a non-integer to specify fractions of seconds.
|
||||
When the *timeout* argument is omitted the function blocks until
|
||||
at least one file descriptor is ready. A time-out value of zero specifies a
|
||||
poll and never blocks.
|
||||
|
||||
|
|
@ -164,6 +165,9 @@ The module defines the following:
|
|||
:pep:`475` for the rationale), instead of raising
|
||||
:exc:`InterruptedError`.
|
||||
|
||||
.. versionchanged:: next
|
||||
Accepts any real number as *timeout*, not only integer or float.
|
||||
|
||||
|
||||
.. data:: PIPE_BUF
|
||||
|
||||
|
|
@ -270,6 +274,9 @@ object.
|
|||
:pep:`475` for the rationale), instead of raising
|
||||
:exc:`InterruptedError`.
|
||||
|
||||
.. versionchanged:: next
|
||||
Accepts any real number as *timeout*, not only integer or float.
|
||||
|
||||
|
||||
.. _epoll-objects:
|
||||
|
||||
|
|
@ -368,7 +375,9 @@ Edge and Level Trigger Polling (epoll) Objects
|
|||
|
||||
.. method:: epoll.poll(timeout=None, maxevents=-1)
|
||||
|
||||
Wait for events. timeout in seconds (float)
|
||||
Wait for events.
|
||||
If *timeout* is given, it specifies the length of time in seconds
|
||||
(may be non-integer) which the system will wait for events before returning.
|
||||
|
||||
.. versionchanged:: 3.5
|
||||
The function is now retried with a recomputed timeout when interrupted by
|
||||
|
|
@ -376,6 +385,9 @@ Edge and Level Trigger Polling (epoll) Objects
|
|||
:pep:`475` for the rationale), instead of raising
|
||||
:exc:`InterruptedError`.
|
||||
|
||||
.. versionchanged:: next
|
||||
Accepts any real number as *timeout*, not only integer or float.
|
||||
|
||||
|
||||
.. _poll-objects:
|
||||
|
||||
|
|
@ -464,6 +476,9 @@ linearly scanned again. :c:func:`!select` is *O*\ (*highest file descriptor*), w
|
|||
:pep:`475` for the rationale), instead of raising
|
||||
:exc:`InterruptedError`.
|
||||
|
||||
.. versionchanged:: next
|
||||
Accepts any real number as *timeout*, not only integer or float.
|
||||
|
||||
|
||||
.. _kqueue-objects:
|
||||
|
||||
|
|
@ -496,7 +511,7 @@ Kqueue Objects
|
|||
|
||||
- changelist must be an iterable of kevent objects or ``None``
|
||||
- max_events must be 0 or a positive integer
|
||||
- timeout in seconds (floats possible); the default is ``None``,
|
||||
- timeout in seconds (non-integers are possible); the default is ``None``,
|
||||
to wait forever
|
||||
|
||||
.. versionchanged:: 3.5
|
||||
|
|
@ -505,6 +520,9 @@ Kqueue Objects
|
|||
:pep:`475` for the rationale), instead of raising
|
||||
:exc:`InterruptedError`.
|
||||
|
||||
.. versionchanged:: next
|
||||
Accepts any real number as *timeout*, not only integer or float.
|
||||
|
||||
|
||||
.. _kevent-objects:
|
||||
|
||||
|
|
|
|||
|
|
@ -478,11 +478,11 @@ The :mod:`signal` module defines the following functions:
|
|||
.. versionadded:: 3.3
|
||||
|
||||
|
||||
.. function:: setitimer(which, seconds, interval=0.0)
|
||||
.. function:: setitimer(which, seconds, interval=0)
|
||||
|
||||
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 *seconds* (rounded up to microseconds, different from
|
||||
:func:`alarm`) and after that every *interval* seconds (if *interval*
|
||||
is non-zero). The interval timer specified by *which* can be cleared by
|
||||
setting *seconds* to zero.
|
||||
|
|
@ -493,13 +493,18 @@ The :mod:`signal` module defines the following functions:
|
|||
: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 two-tuple of floats:
|
||||
(``delay``, ``interval``).
|
||||
|
||||
Attempting to pass an invalid interval timer will cause an
|
||||
:exc:`ItimerError`.
|
||||
|
||||
.. availability:: Unix.
|
||||
|
||||
.. versionchanged:: next
|
||||
Accepts any real numbers as *seconds* and *interval*, not only integers
|
||||
or floats.
|
||||
|
||||
|
||||
.. function:: getitimer(which)
|
||||
|
||||
|
|
@ -676,6 +681,9 @@ The :mod:`signal` module defines the following functions:
|
|||
by a signal not in *sigset* and the signal handler does not raise an
|
||||
exception (see :pep:`475` for the rationale).
|
||||
|
||||
.. versionchanged:: next
|
||||
Accepts any real number as *timeout*, not only integer or float.
|
||||
|
||||
|
||||
.. _signal-example:
|
||||
|
||||
|
|
|
|||
|
|
@ -1407,11 +1407,14 @@ The :mod:`socket` module also offers various network-related services:
|
|||
|
||||
.. function:: setdefaulttimeout(timeout)
|
||||
|
||||
Set the default timeout in seconds (float) for new socket objects. When
|
||||
Set the default timeout in seconds (real number) for new socket objects. When
|
||||
the socket module is first imported, the default is ``None``. See
|
||||
:meth:`~socket.settimeout` for possible values and their respective
|
||||
meanings.
|
||||
|
||||
.. versionchanged:: next
|
||||
Accepts any real number, not only integer or float.
|
||||
|
||||
|
||||
.. function:: sethostname(name)
|
||||
|
||||
|
|
@ -2073,7 +2076,7 @@ to sockets.
|
|||
.. method:: socket.settimeout(value)
|
||||
|
||||
Set a timeout on blocking socket operations. The *value* argument can be a
|
||||
nonnegative floating-point number expressing seconds, or ``None``.
|
||||
nonnegative real number expressing seconds, or ``None``.
|
||||
If a non-zero value is given, subsequent socket operations will raise a
|
||||
:exc:`timeout` exception if the timeout period *value* has elapsed before
|
||||
the operation has completed. If zero is given, the socket is put in
|
||||
|
|
@ -2085,6 +2088,9 @@ to sockets.
|
|||
The method no longer toggles :const:`SOCK_NONBLOCK` flag on
|
||||
:attr:`socket.type`.
|
||||
|
||||
.. versionchanged:: next
|
||||
Accepts any real number, not only integer or float.
|
||||
|
||||
|
||||
.. method:: socket.setsockopt(level, optname, value: int)
|
||||
.. method:: socket.setsockopt(level, optname, value: buffer)
|
||||
|
|
|
|||
|
|
@ -608,7 +608,7 @@ since it is impossible to detect the termination of alien threads.
|
|||
timeout occurs.
|
||||
|
||||
When the *timeout* argument is present and not ``None``, it should be a
|
||||
floating-point number specifying a timeout for the operation in seconds
|
||||
real number specifying a timeout for the operation in seconds
|
||||
(or fractions thereof). As :meth:`~Thread.join` always returns ``None``,
|
||||
you must call :meth:`~Thread.is_alive` after :meth:`~Thread.join` to
|
||||
decide whether a timeout happened -- if the thread is still alive, the
|
||||
|
|
@ -632,6 +632,9 @@ since it is impossible to detect the termination of alien threads.
|
|||
|
||||
May raise :exc:`PythonFinalizationError`.
|
||||
|
||||
.. versionchanged:: next
|
||||
Accepts any real number as *timeout*, not only integer or float.
|
||||
|
||||
.. attribute:: name
|
||||
|
||||
A string used for identification purposes only. It has no semantics.
|
||||
|
|
@ -764,7 +767,7 @@ All methods are executed atomically.
|
|||
If a call with *blocking* set to ``True`` would block, return ``False``
|
||||
immediately; otherwise, set the lock to locked and return ``True``.
|
||||
|
||||
When invoked with the floating-point *timeout* argument set to a positive
|
||||
When invoked with the *timeout* argument set to a positive
|
||||
value, block for at most the number of seconds specified by *timeout*
|
||||
and as long as the lock cannot be acquired. A *timeout* argument of ``-1``
|
||||
specifies an unbounded wait. It is forbidden to specify a *timeout*
|
||||
|
|
@ -783,6 +786,9 @@ All methods are executed atomically.
|
|||
.. versionchanged:: 3.14
|
||||
Lock acquisition can now be interrupted by signals on Windows.
|
||||
|
||||
.. versionchanged:: next
|
||||
Accepts any real number as *timeout*, not only integer or float.
|
||||
|
||||
|
||||
.. method:: release()
|
||||
|
||||
|
|
@ -863,7 +869,7 @@ call release as many times the lock has been acquired can lead to deadlock.
|
|||
* If no thread owns the lock, acquire the lock and return immediately.
|
||||
|
||||
* If another thread owns the lock, block until we are able to acquire
|
||||
lock, or *timeout*, if set to a positive float value.
|
||||
lock, or *timeout*, if set to a positive value.
|
||||
|
||||
* If the same thread owns the lock, acquire the lock again, and
|
||||
return immediately. This is the difference between :class:`Lock` and
|
||||
|
|
@ -890,6 +896,9 @@ call release as many times the lock has been acquired can lead to deadlock.
|
|||
.. versionchanged:: 3.2
|
||||
The *timeout* parameter is new.
|
||||
|
||||
.. versionchanged:: next
|
||||
Accepts any real number as *timeout*, not only integer or float.
|
||||
|
||||
|
||||
.. method:: release()
|
||||
|
||||
|
|
@ -1023,7 +1032,7 @@ item to the buffer only needs to wake up one consumer thread.
|
|||
occurs. Once awakened or timed out, it re-acquires the lock and returns.
|
||||
|
||||
When the *timeout* argument is present and not ``None``, it should be a
|
||||
floating-point number specifying a timeout for the operation in seconds
|
||||
real number specifying a timeout for the operation in seconds
|
||||
(or fractions thereof).
|
||||
|
||||
When the underlying lock is an :class:`RLock`, it is not released using
|
||||
|
|
@ -1150,6 +1159,9 @@ Semaphores also support the :ref:`context management protocol <with-locks>`.
|
|||
.. versionchanged:: 3.2
|
||||
The *timeout* parameter is new.
|
||||
|
||||
.. versionchanged:: next
|
||||
Accepts any real number as *timeout*, not only integer or float.
|
||||
|
||||
.. method:: release(n=1)
|
||||
|
||||
Release a semaphore, incrementing the internal counter by *n*. When it
|
||||
|
|
@ -1250,7 +1262,7 @@ method. The :meth:`~Event.wait` method blocks until the flag is true.
|
|||
the internal flag did not become true within the given wait time.
|
||||
|
||||
When the timeout argument is present and not ``None``, it should be a
|
||||
floating-point number specifying a timeout for the operation in seconds,
|
||||
real number specifying a timeout for the operation in seconds,
|
||||
or fractions thereof.
|
||||
|
||||
.. versionchanged:: 3.1
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ Functions
|
|||
.. versionadded:: 3.7
|
||||
|
||||
|
||||
.. function:: clock_settime(clk_id, time: float)
|
||||
.. function:: clock_settime(clk_id, time)
|
||||
|
||||
Set the time of the specified clock *clk_id*. Currently,
|
||||
:data:`CLOCK_REALTIME` is the only accepted value for *clk_id*.
|
||||
|
|
@ -201,6 +201,9 @@ Functions
|
|||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
.. versionchanged:: next
|
||||
Accepts any real number as *time*, not only integer or float.
|
||||
|
||||
|
||||
.. function:: clock_settime_ns(clk_id, time: int)
|
||||
|
||||
|
|
@ -223,6 +226,9 @@ Functions
|
|||
``asctime(localtime(secs))``. Locale information is not used by
|
||||
:func:`ctime`.
|
||||
|
||||
.. versionchanged:: next
|
||||
Accepts any real number, not only integer or float.
|
||||
|
||||
|
||||
.. function:: get_clock_info(name)
|
||||
|
||||
|
|
@ -258,6 +264,9 @@ Functions
|
|||
:class:`struct_time` object. See :func:`calendar.timegm` for the inverse of this
|
||||
function.
|
||||
|
||||
.. versionchanged:: next
|
||||
Accepts any real number, not only integer or float.
|
||||
|
||||
|
||||
.. function:: localtime([secs])
|
||||
|
||||
|
|
@ -271,6 +280,9 @@ Functions
|
|||
:c:func:`gmtime` failure. It's common for this to be restricted to years
|
||||
between 1970 and 2038.
|
||||
|
||||
.. versionchanged:: next
|
||||
Accepts any real number, not only integer or float.
|
||||
|
||||
|
||||
.. function:: mktime(t)
|
||||
|
||||
|
|
@ -382,8 +394,7 @@ Functions
|
|||
.. function:: sleep(secs)
|
||||
|
||||
Suspend execution of the calling thread for the given number of seconds.
|
||||
The argument may be a floating-point number to indicate a more precise sleep
|
||||
time.
|
||||
The argument may be a non-integer to indicate a more precise sleep time.
|
||||
|
||||
If the sleep is interrupted by a signal and no exception is raised by the
|
||||
signal handler, the sleep is restarted with a recomputed timeout.
|
||||
|
|
@ -428,6 +439,9 @@ Functions
|
|||
.. versionchanged:: 3.13
|
||||
Raises an auditing event.
|
||||
|
||||
.. versionchanged:: next
|
||||
Accepts any real number, not only integer or float.
|
||||
|
||||
.. index::
|
||||
single: % (percent); datetime format
|
||||
|
||||
|
|
|
|||
|
|
@ -279,6 +279,11 @@ Other language changes
|
|||
and ends with a forward slash (``/``).
|
||||
(Contributed by Serhiy Storchaka in :gh:`134716`.)
|
||||
|
||||
* Functions that take timestamp or timeout arguments now accept any real
|
||||
numbers (such as :class:`~decimal.Decimal` and :class:`~fractions.Fraction`),
|
||||
not only integers or floats, although this does not improve precision.
|
||||
(Contributed by Serhiy Storchaka in :gh:`67795`.)
|
||||
|
||||
|
||||
New modules
|
||||
===========
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import bisect
|
|||
import contextlib
|
||||
import copy
|
||||
import decimal
|
||||
import fractions
|
||||
import io
|
||||
import itertools
|
||||
import os
|
||||
|
|
@ -2626,6 +2627,10 @@ class TestDateTime(TestDate):
|
|||
expected = time.localtime(ts)
|
||||
got = self.theclass.fromtimestamp(ts)
|
||||
self.verify_field_equality(expected, got)
|
||||
got = self.theclass.fromtimestamp(decimal.Decimal(ts))
|
||||
self.verify_field_equality(expected, got)
|
||||
got = self.theclass.fromtimestamp(fractions.Fraction(ts))
|
||||
self.verify_field_equality(expected, got)
|
||||
|
||||
def test_fromtimestamp_keyword_arg(self):
|
||||
import time
|
||||
|
|
@ -2641,6 +2646,12 @@ class TestDateTime(TestDate):
|
|||
with self.assertWarns(DeprecationWarning):
|
||||
got = self.theclass.utcfromtimestamp(ts)
|
||||
self.verify_field_equality(expected, got)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
got = self.theclass.utcfromtimestamp(decimal.Decimal(ts))
|
||||
self.verify_field_equality(expected, got)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
got = self.theclass.utcfromtimestamp(fractions.Fraction(ts))
|
||||
self.verify_field_equality(expected, got)
|
||||
|
||||
# Run with US-style DST rules: DST begins 2 a.m. on second Sunday in
|
||||
# March (M3.2.0) and ends 2 a.m. on first Sunday in November (M11.1.0).
|
||||
|
|
@ -2728,6 +2739,108 @@ class TestDateTime(TestDate):
|
|||
self.assertEqual(t.second, 0)
|
||||
self.assertEqual(t.microsecond, 7812)
|
||||
|
||||
@support.run_with_tz('MSK-03') # Something east of Greenwich
|
||||
def test_microsecond_rounding_decimal(self):
|
||||
D = decimal.Decimal
|
||||
def utcfromtimestamp(*args, **kwargs):
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
return self.theclass.utcfromtimestamp(*args, **kwargs)
|
||||
|
||||
for fts in [self.theclass.fromtimestamp,
|
||||
utcfromtimestamp]:
|
||||
zero = fts(D(0))
|
||||
self.assertEqual(zero.second, 0)
|
||||
self.assertEqual(zero.microsecond, 0)
|
||||
one = fts(D('0.000_001'))
|
||||
try:
|
||||
minus_one = fts(D('-0.000_001'))
|
||||
except OSError:
|
||||
# localtime(-1) and gmtime(-1) is not supported on Windows
|
||||
pass
|
||||
else:
|
||||
self.assertEqual(minus_one.second, 59)
|
||||
self.assertEqual(minus_one.microsecond, 999_999)
|
||||
|
||||
t = fts(D('-0.000_000_1'))
|
||||
self.assertEqual(t, zero)
|
||||
t = fts(D('-0.000_000_9'))
|
||||
self.assertEqual(t, minus_one)
|
||||
t = fts(D(-1)/2**7)
|
||||
self.assertEqual(t.second, 59)
|
||||
self.assertEqual(t.microsecond, 992188)
|
||||
|
||||
t = fts(D('0.000_000_1'))
|
||||
self.assertEqual(t, zero)
|
||||
t = fts(D('0.000_000_5'))
|
||||
self.assertEqual(t, zero)
|
||||
t = fts(D('0.000_000_500_000_000_000_000_1'))
|
||||
self.assertEqual(t, one)
|
||||
t = fts(D('0.000_000_9'))
|
||||
self.assertEqual(t, one)
|
||||
t = fts(D('0.999_999_499_999_999_9'))
|
||||
self.assertEqual(t.second, 0)
|
||||
self.assertEqual(t.microsecond, 999_999)
|
||||
t = fts(D('0.999_999_5'))
|
||||
self.assertEqual(t.second, 1)
|
||||
self.assertEqual(t.microsecond, 0)
|
||||
t = fts(D('0.999_999_9'))
|
||||
self.assertEqual(t.second, 1)
|
||||
self.assertEqual(t.microsecond, 0)
|
||||
t = fts(D(1)/2**7)
|
||||
self.assertEqual(t.second, 0)
|
||||
self.assertEqual(t.microsecond, 7812)
|
||||
|
||||
@support.run_with_tz('MSK-03') # Something east of Greenwich
|
||||
def test_microsecond_rounding_fraction(self):
|
||||
F = fractions.Fraction
|
||||
def utcfromtimestamp(*args, **kwargs):
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
return self.theclass.utcfromtimestamp(*args, **kwargs)
|
||||
|
||||
for fts in [self.theclass.fromtimestamp,
|
||||
utcfromtimestamp]:
|
||||
zero = fts(F(0))
|
||||
self.assertEqual(zero.second, 0)
|
||||
self.assertEqual(zero.microsecond, 0)
|
||||
one = fts(F(1, 1_000_000))
|
||||
try:
|
||||
minus_one = fts(F(-1, 1_000_000))
|
||||
except OSError:
|
||||
# localtime(-1) and gmtime(-1) is not supported on Windows
|
||||
pass
|
||||
else:
|
||||
self.assertEqual(minus_one.second, 59)
|
||||
self.assertEqual(minus_one.microsecond, 999_999)
|
||||
|
||||
t = fts(F(-1, 10_000_000))
|
||||
self.assertEqual(t, zero)
|
||||
t = fts(F(-9, 10_000_000))
|
||||
self.assertEqual(t, minus_one)
|
||||
t = fts(F(-1, 2**7))
|
||||
self.assertEqual(t.second, 59)
|
||||
self.assertEqual(t.microsecond, 992188)
|
||||
|
||||
t = fts(F(1, 10_000_000))
|
||||
self.assertEqual(t, zero)
|
||||
t = fts(F(5, 10_000_000))
|
||||
self.assertEqual(t, zero)
|
||||
t = fts(F(5_000_000_000, 9_999_999_999_999_999))
|
||||
self.assertEqual(t, one)
|
||||
t = fts(F(9, 10_000_000))
|
||||
self.assertEqual(t, one)
|
||||
t = fts(F(9_999_995_000_000_000, 10_000_000_000_000_001))
|
||||
self.assertEqual(t.second, 0)
|
||||
self.assertEqual(t.microsecond, 999_999)
|
||||
t = fts(F(9_999_995, 10_000_000))
|
||||
self.assertEqual(t.second, 1)
|
||||
self.assertEqual(t.microsecond, 0)
|
||||
t = fts(F(9_999_999, 10_000_000))
|
||||
self.assertEqual(t.second, 1)
|
||||
self.assertEqual(t.microsecond, 0)
|
||||
t = fts(F(1, 2**7))
|
||||
self.assertEqual(t.second, 0)
|
||||
self.assertEqual(t.microsecond, 7812)
|
||||
|
||||
def test_timestamp_limits(self):
|
||||
with self.subTest("minimum UTC"):
|
||||
min_dt = self.theclass.min.replace(tzinfo=timezone.utc)
|
||||
|
|
|
|||
|
|
@ -948,6 +948,20 @@ class UtimeTests(unittest.TestCase):
|
|||
# issue, os.utime() rounds towards minus infinity.
|
||||
return (ns * 1e-9) + 0.5e-9
|
||||
|
||||
@staticmethod
|
||||
def ns_to_sec_decimal(ns):
|
||||
# Convert a number of nanosecond (int) to a number of seconds (Decimal).
|
||||
# Round towards infinity by adding 0.5 nanosecond to avoid rounding
|
||||
# issue, os.utime() rounds towards minus infinity.
|
||||
return decimal.Decimal('1e-9') * ns + decimal.Decimal('0.5e-9')
|
||||
|
||||
@staticmethod
|
||||
def ns_to_sec_fraction(ns):
|
||||
# Convert a number of nanosecond (int) to a number of seconds (Fraction).
|
||||
# Round towards infinity by adding 0.5 nanosecond to avoid rounding
|
||||
# issue, os.utime() rounds towards minus infinity.
|
||||
return fractions.Fraction(ns, 10**9) + fractions.Fraction(1, 2*10**9)
|
||||
|
||||
def test_utime_by_indexed(self):
|
||||
# pass times as floating-point seconds as the second indexed parameter
|
||||
def set_time(filename, ns):
|
||||
|
|
@ -968,6 +982,24 @@ class UtimeTests(unittest.TestCase):
|
|||
os.utime(filename, times=(atime, mtime))
|
||||
self._test_utime(set_time)
|
||||
|
||||
def test_utime_decimal(self):
|
||||
# pass times as Decimal seconds
|
||||
def set_time(filename, ns):
|
||||
atime_ns, mtime_ns = ns
|
||||
atime = self.ns_to_sec_decimal(atime_ns)
|
||||
mtime = self.ns_to_sec_decimal(mtime_ns)
|
||||
os.utime(filename, (atime, mtime))
|
||||
self._test_utime(set_time)
|
||||
|
||||
def test_utime_fraction(self):
|
||||
# pass times as Fraction seconds
|
||||
def set_time(filename, ns):
|
||||
atime_ns, mtime_ns = ns
|
||||
atime = self.ns_to_sec_fraction(atime_ns)
|
||||
mtime = self.ns_to_sec_fraction(mtime_ns)
|
||||
os.utime(filename, (atime, mtime))
|
||||
self._test_utime(set_time)
|
||||
|
||||
@unittest.skipUnless(os.utime in os.supports_follow_symlinks,
|
||||
"follow_symlinks support for utime required "
|
||||
"for this test.")
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@ from test.support.import_helper import ensure_lazy_imports
|
|||
import _thread as thread
|
||||
import array
|
||||
import contextlib
|
||||
import decimal
|
||||
import errno
|
||||
import fractions
|
||||
import gc
|
||||
import io
|
||||
import itertools
|
||||
|
|
@ -1310,10 +1312,20 @@ class GeneralModuleTests(unittest.TestCase):
|
|||
self.assertEqual(s.gettimeout(), None)
|
||||
|
||||
# Set the default timeout to 10, and see if it propagates
|
||||
with socket_setdefaulttimeout(10):
|
||||
self.assertEqual(socket.getdefaulttimeout(), 10)
|
||||
with socket_setdefaulttimeout(10.125):
|
||||
self.assertEqual(socket.getdefaulttimeout(), 10.125)
|
||||
with socket.socket() as sock:
|
||||
self.assertEqual(sock.gettimeout(), 10)
|
||||
self.assertEqual(sock.gettimeout(), 10.125)
|
||||
|
||||
socket.setdefaulttimeout(decimal.Decimal('11.125'))
|
||||
self.assertEqual(socket.getdefaulttimeout(), 11.125)
|
||||
with socket.socket() as sock:
|
||||
self.assertEqual(sock.gettimeout(), 11.125)
|
||||
|
||||
socket.setdefaulttimeout(fractions.Fraction(97, 8))
|
||||
self.assertEqual(socket.getdefaulttimeout(), 12.125)
|
||||
with socket.socket() as sock:
|
||||
self.assertEqual(sock.gettimeout(), 12.125)
|
||||
|
||||
# Reset the default timeout to None, and see if it propagates
|
||||
socket.setdefaulttimeout(None)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ from test import support
|
|||
from test.support import warnings_helper
|
||||
import decimal
|
||||
import enum
|
||||
import fractions
|
||||
import math
|
||||
import platform
|
||||
import sys
|
||||
|
|
@ -170,10 +171,12 @@ class TimeTestCase(unittest.TestCase):
|
|||
# Improved exception #81267
|
||||
with self.assertRaises(TypeError) as errmsg:
|
||||
time.sleep([])
|
||||
self.assertIn("integer or float", str(errmsg.exception))
|
||||
self.assertIn("real number", str(errmsg.exception))
|
||||
|
||||
def test_sleep(self):
|
||||
for value in [-0.0, 0, 0.0, 1e-100, 1e-9, 1e-6, 1, 1.2]:
|
||||
for value in [-0.0, 0, 0.0, 1e-100, 1e-9, 1e-6, 1, 1.2,
|
||||
decimal.Decimal('0.02'),
|
||||
fractions.Fraction(1, 50)]:
|
||||
with self.subTest(value=value):
|
||||
time.sleep(value)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
Functions that take timestamp or timeout arguments now accept any real
|
||||
numbers (such as :class:`~decimal.Decimal` and :class:`~fractions.Fraction`),
|
||||
not only integers or floats, although this does not improve precision.
|
||||
8
Modules/clinic/selectmodule.c.h
generated
8
Modules/clinic/selectmodule.c.h
generated
|
|
@ -26,7 +26,7 @@ PyDoc_STRVAR(select_select__doc__,
|
|||
"gotten from a fileno() method call on one of those.\n"
|
||||
"\n"
|
||||
"The optional 4th argument specifies a timeout in seconds; it may be\n"
|
||||
"a floating-point number to specify fractions of seconds. If it is absent\n"
|
||||
"a non-integer to specify fractions of seconds. If it is absent\n"
|
||||
"or None, the call will never time out.\n"
|
||||
"\n"
|
||||
"The return value is a tuple of three lists corresponding to the first three\n"
|
||||
|
|
@ -973,7 +973,7 @@ PyDoc_STRVAR(select_epoll_poll__doc__,
|
|||
"Wait for events on the epoll file descriptor.\n"
|
||||
"\n"
|
||||
" timeout\n"
|
||||
" the maximum time to wait in seconds (as float);\n"
|
||||
" the maximum time to wait in seconds (with fractions);\n"
|
||||
" a timeout of None or -1 makes poll wait indefinitely\n"
|
||||
" maxevents\n"
|
||||
" the maximum number of events returned; -1 means no limit\n"
|
||||
|
|
@ -1262,7 +1262,7 @@ PyDoc_STRVAR(select_kqueue_control__doc__,
|
|||
" The maximum number of events that the kernel will return.\n"
|
||||
" timeout\n"
|
||||
" The maximum time to wait in seconds, or else None to wait forever.\n"
|
||||
" This accepts floats for smaller timeouts, too.");
|
||||
" This accepts non-integers for smaller timeouts, too.");
|
||||
|
||||
#define SELECT_KQUEUE_CONTROL_METHODDEF \
|
||||
{"control", _PyCFunction_CAST(select_kqueue_control), METH_FASTCALL, select_kqueue_control__doc__},
|
||||
|
|
@ -1399,4 +1399,4 @@ exit:
|
|||
#ifndef SELECT_KQUEUE_CONTROL_METHODDEF
|
||||
#define SELECT_KQUEUE_CONTROL_METHODDEF
|
||||
#endif /* !defined(SELECT_KQUEUE_CONTROL_METHODDEF) */
|
||||
/*[clinic end generated code: output=2a66dd831f22c696 input=a9049054013a1b77]*/
|
||||
/*[clinic end generated code: output=ae54d65938513132 input=a9049054013a1b77]*/
|
||||
|
|
|
|||
4
Modules/clinic/signalmodule.c.h
generated
4
Modules/clinic/signalmodule.c.h
generated
|
|
@ -600,7 +600,7 @@ PyDoc_STRVAR(signal_sigtimedwait__doc__,
|
|||
"\n"
|
||||
"Like sigwaitinfo(), but with a timeout.\n"
|
||||
"\n"
|
||||
"The timeout is specified in seconds, with floating-point numbers allowed.");
|
||||
"The timeout is specified in seconds, rounded up to nanoseconds.");
|
||||
|
||||
#define SIGNAL_SIGTIMEDWAIT_METHODDEF \
|
||||
{"sigtimedwait", _PyCFunction_CAST(signal_sigtimedwait), METH_FASTCALL, signal_sigtimedwait__doc__},
|
||||
|
|
@ -794,4 +794,4 @@ exit:
|
|||
#ifndef SIGNAL_PIDFD_SEND_SIGNAL_METHODDEF
|
||||
#define SIGNAL_PIDFD_SEND_SIGNAL_METHODDEF
|
||||
#endif /* !defined(SIGNAL_PIDFD_SEND_SIGNAL_METHODDEF) */
|
||||
/*[clinic end generated code: output=37ae8ebeae4178fa input=a9049054013a1b77]*/
|
||||
/*[clinic end generated code: output=42e20d118435d7fa input=a9049054013a1b77]*/
|
||||
|
|
|
|||
|
|
@ -6678,7 +6678,7 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns,
|
|||
if (!PyTuple_CheckExact(times) || (PyTuple_Size(times) != 2)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"utime: 'times' must be either"
|
||||
" a tuple of two ints or None");
|
||||
" a tuple of two numbers or None");
|
||||
return NULL;
|
||||
}
|
||||
utime.now = 0;
|
||||
|
|
|
|||
|
|
@ -262,7 +262,7 @@ A file descriptor is either a socket or file object, or a small integer
|
|||
gotten from a fileno() method call on one of those.
|
||||
|
||||
The optional 4th argument specifies a timeout in seconds; it may be
|
||||
a floating-point number to specify fractions of seconds. If it is absent
|
||||
a non-integer to specify fractions of seconds. If it is absent
|
||||
or None, the call will never time out.
|
||||
|
||||
The return value is a tuple of three lists corresponding to the first three
|
||||
|
|
@ -277,7 +277,7 @@ descriptors can be used.
|
|||
static PyObject *
|
||||
select_select_impl(PyObject *module, PyObject *rlist, PyObject *wlist,
|
||||
PyObject *xlist, PyObject *timeout_obj)
|
||||
/*[clinic end generated code: output=2b3cfa824f7ae4cf input=df20779a9c2f5c1e]*/
|
||||
/*[clinic end generated code: output=2b3cfa824f7ae4cf input=b0403de75cd11cc1]*/
|
||||
{
|
||||
#ifdef SELECT_USES_HEAP
|
||||
pylist *rfd2obj, *wfd2obj, *efd2obj;
|
||||
|
|
@ -305,8 +305,9 @@ select_select_impl(PyObject *module, PyObject *rlist, PyObject *wlist,
|
|||
if (_PyTime_FromSecondsObject(&timeout, timeout_obj,
|
||||
_PyTime_ROUND_TIMEOUT) < 0) {
|
||||
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"timeout must be a float or None");
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"timeout must be a real number or None, not %T",
|
||||
timeout_obj);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -632,8 +633,9 @@ select_poll_poll_impl(pollObject *self, PyObject *timeout_obj)
|
|||
if (_PyTime_FromMillisecondsObject(&timeout, timeout_obj,
|
||||
_PyTime_ROUND_TIMEOUT) < 0) {
|
||||
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"timeout must be an integer or None");
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"timeout must be a real number or None, not %T",
|
||||
timeout_obj);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -974,8 +976,9 @@ select_devpoll_poll_impl(devpollObject *self, PyObject *timeout_obj)
|
|||
if (_PyTime_FromMillisecondsObject(&timeout, timeout_obj,
|
||||
_PyTime_ROUND_TIMEOUT) < 0) {
|
||||
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"timeout must be an integer or None");
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"timeout must be a real number or None, not %T",
|
||||
timeout_obj);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -1565,7 +1568,7 @@ select_epoll_unregister_impl(pyEpoll_Object *self, int fd)
|
|||
select.epoll.poll
|
||||
|
||||
timeout as timeout_obj: object = None
|
||||
the maximum time to wait in seconds (as float);
|
||||
the maximum time to wait in seconds (with fractions);
|
||||
a timeout of None or -1 makes poll wait indefinitely
|
||||
maxevents: int = -1
|
||||
the maximum number of events returned; -1 means no limit
|
||||
|
|
@ -1579,7 +1582,7 @@ as a list of (fd, events) 2-tuples.
|
|||
static PyObject *
|
||||
select_epoll_poll_impl(pyEpoll_Object *self, PyObject *timeout_obj,
|
||||
int maxevents)
|
||||
/*[clinic end generated code: output=e02d121a20246c6c input=33d34a5ea430fd5b]*/
|
||||
/*[clinic end generated code: output=e02d121a20246c6c input=deafa7f04a60ebe0]*/
|
||||
{
|
||||
int nfds, i;
|
||||
PyObject *elist = NULL, *etuple = NULL;
|
||||
|
|
@ -1595,8 +1598,9 @@ select_epoll_poll_impl(pyEpoll_Object *self, PyObject *timeout_obj,
|
|||
if (_PyTime_FromSecondsObject(&timeout, timeout_obj,
|
||||
_PyTime_ROUND_TIMEOUT) < 0) {
|
||||
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"timeout must be an integer or None");
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"timeout must be a real number or None, not %T",
|
||||
timeout_obj);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -2291,7 +2295,7 @@ select.kqueue.control
|
|||
The maximum number of events that the kernel will return.
|
||||
timeout as otimeout: object = None
|
||||
The maximum time to wait in seconds, or else None to wait forever.
|
||||
This accepts floats for smaller timeouts, too.
|
||||
This accepts non-integers for smaller timeouts, too.
|
||||
/
|
||||
|
||||
Calls the kernel kevent function.
|
||||
|
|
@ -2300,7 +2304,7 @@ Calls the kernel kevent function.
|
|||
static PyObject *
|
||||
select_kqueue_control_impl(kqueue_queue_Object *self, PyObject *changelist,
|
||||
int maxevents, PyObject *otimeout)
|
||||
/*[clinic end generated code: output=81324ff5130db7ae input=59c4e30811209c47]*/
|
||||
/*[clinic end generated code: output=81324ff5130db7ae input=be969d2bc6f84205]*/
|
||||
{
|
||||
int gotevents = 0;
|
||||
int nchanges = 0;
|
||||
|
|
@ -2331,9 +2335,8 @@ select_kqueue_control_impl(kqueue_queue_Object *self, PyObject *changelist,
|
|||
if (_PyTime_FromSecondsObject(&timeout,
|
||||
otimeout, _PyTime_ROUND_TIMEOUT) < 0) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"timeout argument must be a number "
|
||||
"or None, got %.200s",
|
||||
_PyType_Name(Py_TYPE(otimeout)));
|
||||
"timeout must be a real number or None, not %T",
|
||||
otimeout);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1210,13 +1210,13 @@ signal.sigtimedwait
|
|||
|
||||
Like sigwaitinfo(), but with a timeout.
|
||||
|
||||
The timeout is specified in seconds, with floating-point numbers allowed.
|
||||
The timeout is specified in seconds, rounded up to nanoseconds.
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
signal_sigtimedwait_impl(PyObject *module, sigset_t sigset,
|
||||
PyObject *timeout_obj)
|
||||
/*[clinic end generated code: output=59c8971e8ae18a64 input=955773219c1596cd]*/
|
||||
/*[clinic end generated code: output=59c8971e8ae18a64 input=f89af57d645e48e0]*/
|
||||
{
|
||||
PyTime_t timeout;
|
||||
if (_PyTime_FromSecondsObject(&timeout,
|
||||
|
|
|
|||
|
|
@ -7182,7 +7182,7 @@ socket_setdefaulttimeout(PyObject *self, PyObject *arg)
|
|||
PyDoc_STRVAR(setdefaulttimeout_doc,
|
||||
"setdefaulttimeout(timeout)\n\
|
||||
\n\
|
||||
Set the default timeout in seconds (float) for new socket objects.\n\
|
||||
Set the default timeout in seconds (real number) for new socket objects.\n\
|
||||
A value of None indicates that new socket objects have no timeout.\n\
|
||||
When the socket module is first imported, the default is None.");
|
||||
|
||||
|
|
|
|||
|
|
@ -368,8 +368,20 @@ pytime_object_to_denominator(PyObject *obj, time_t *sec, long *numerator,
|
|||
{
|
||||
assert(denominator >= 1);
|
||||
|
||||
if (PyFloat_Check(obj)) {
|
||||
if (PyIndex_Check(obj)) {
|
||||
*sec = _PyLong_AsTime_t(obj);
|
||||
*numerator = 0;
|
||||
if (*sec == (time_t)-1 && PyErr_Occurred()) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
double d = PyFloat_AsDouble(obj);
|
||||
if (d == -1 && PyErr_Occurred()) {
|
||||
*numerator = 0;
|
||||
return -1;
|
||||
}
|
||||
if (isnan(d)) {
|
||||
*numerator = 0;
|
||||
PyErr_SetString(PyExc_ValueError, "Invalid value NaN (not a number)");
|
||||
|
|
@ -378,30 +390,28 @@ pytime_object_to_denominator(PyObject *obj, time_t *sec, long *numerator,
|
|||
return pytime_double_to_denominator(d, sec, numerator,
|
||||
denominator, round);
|
||||
}
|
||||
else {
|
||||
*sec = _PyLong_AsTime_t(obj);
|
||||
*numerator = 0;
|
||||
if (*sec == (time_t)-1 && PyErr_Occurred()) {
|
||||
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"argument must be int or float, not %T", obj);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
_PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round)
|
||||
{
|
||||
if (PyFloat_Check(obj)) {
|
||||
if (PyIndex_Check(obj)) {
|
||||
*sec = _PyLong_AsTime_t(obj);
|
||||
if (*sec == (time_t)-1 && PyErr_Occurred()) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
double intpart;
|
||||
/* volatile avoids optimization changing how numbers are rounded */
|
||||
volatile double d;
|
||||
|
||||
d = PyFloat_AsDouble(obj);
|
||||
if (d == -1 && PyErr_Occurred()) {
|
||||
return -1;
|
||||
}
|
||||
if (isnan(d)) {
|
||||
PyErr_SetString(PyExc_ValueError, "Invalid value NaN (not a number)");
|
||||
return -1;
|
||||
|
|
@ -418,13 +428,6 @@ _PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round)
|
|||
*sec = (time_t)intpart;
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
*sec = _PyLong_AsTime_t(obj);
|
||||
if (*sec == (time_t)-1 && PyErr_Occurred()) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -586,39 +589,38 @@ static int
|
|||
pytime_from_object(PyTime_t *tp, PyObject *obj, _PyTime_round_t round,
|
||||
long unit_to_ns)
|
||||
{
|
||||
if (PyFloat_Check(obj)) {
|
||||
if (PyIndex_Check(obj)) {
|
||||
long long sec = PyLong_AsLongLong(obj);
|
||||
if (sec == -1 && PyErr_Occurred()) {
|
||||
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
|
||||
pytime_overflow();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static_assert(sizeof(long long) <= sizeof(PyTime_t),
|
||||
"PyTime_t is smaller than long long");
|
||||
PyTime_t ns = (PyTime_t)sec;
|
||||
if (pytime_mul(&ns, unit_to_ns) < 0) {
|
||||
pytime_overflow();
|
||||
return -1;
|
||||
}
|
||||
|
||||
*tp = ns;
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
double d;
|
||||
d = PyFloat_AsDouble(obj);
|
||||
if (d == -1 && PyErr_Occurred()) {
|
||||
return -1;
|
||||
}
|
||||
if (isnan(d)) {
|
||||
PyErr_SetString(PyExc_ValueError, "Invalid value NaN (not a number)");
|
||||
return -1;
|
||||
}
|
||||
return pytime_from_double(tp, d, round, unit_to_ns);
|
||||
}
|
||||
|
||||
long long sec = PyLong_AsLongLong(obj);
|
||||
if (sec == -1 && PyErr_Occurred()) {
|
||||
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
|
||||
pytime_overflow();
|
||||
}
|
||||
else if (PyErr_ExceptionMatches(PyExc_TypeError)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"'%T' object cannot be interpreted as an integer or float",
|
||||
obj);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static_assert(sizeof(long long) <= sizeof(PyTime_t),
|
||||
"PyTime_t is smaller than long long");
|
||||
PyTime_t ns = (PyTime_t)sec;
|
||||
if (pytime_mul(&ns, unit_to_ns) < 0) {
|
||||
pytime_overflow();
|
||||
return -1;
|
||||
}
|
||||
|
||||
*tp = ns;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue