839 lines
28 KiB
Python
839 lines
28 KiB
Python
|
"""
|
||
|
from: https://bitbucket.org/haypo/misc/src/tip/python/pep418.py
|
||
|
|
||
|
Implementation of the PEP 418 in pure Python using ctypes.
|
||
|
|
||
|
Functions:
|
||
|
|
||
|
- clock()
|
||
|
- get_clock_info(name)
|
||
|
- monotonic(): not always available
|
||
|
- perf_frequency()
|
||
|
- process_time()
|
||
|
- sleep()
|
||
|
- time()
|
||
|
|
||
|
Constants:
|
||
|
|
||
|
- has_monotonic (bool): True if time.monotonic() is available
|
||
|
"""
|
||
|
# flake8: noqa
|
||
|
# TODO: gethrtime() for Solaris/OpenIndiana
|
||
|
# TODO: call GetSystemTimeAdjustment() to get the resolution
|
||
|
# TODO: other FIXME
|
||
|
|
||
|
import os
|
||
|
import sys
|
||
|
import time as python_time
|
||
|
|
||
|
has_mach_absolute_time = False
|
||
|
has_clock_gettime = False
|
||
|
has_gettimeofday = False
|
||
|
has_ftime = False
|
||
|
has_delay = False
|
||
|
has_libc_time = False
|
||
|
has_libc_clock = False
|
||
|
has_libc_sleep = False
|
||
|
has_GetTickCount64 = False
|
||
|
CLOCK_REALTIME = None
|
||
|
CLOCK_MONOTONIC = None
|
||
|
CLOCK_PROCESS_CPUTIME_ID = None
|
||
|
CLOCK_HIGHRES = None
|
||
|
CLOCK_PROF = None
|
||
|
try:
|
||
|
import ctypes
|
||
|
import ctypes.util
|
||
|
from ctypes import byref, POINTER
|
||
|
except ImportError as err:
|
||
|
pass
|
||
|
else:
|
||
|
def ctypes_oserror():
|
||
|
errno = ctypes.get_errno()
|
||
|
message = os.strerror(errno)
|
||
|
return OSError(errno, message)
|
||
|
|
||
|
time_t = ctypes.c_long
|
||
|
|
||
|
if os.name == "nt":
|
||
|
from ctypes.wintypes import BOOL, DWORD, HANDLE, FILETIME
|
||
|
from ctypes import windll
|
||
|
LARGEINTEGER = ctypes.c_int64
|
||
|
LARGEINTEGER_p = POINTER(LARGEINTEGER)
|
||
|
FILETIME_p = POINTER(FILETIME)
|
||
|
ULONGLONG = ctypes.c_uint64
|
||
|
|
||
|
def ctypes_winerror():
|
||
|
errno = ctypes.get_errno()
|
||
|
message = os.strerror(errno)
|
||
|
return WindowsError(errno, message)
|
||
|
|
||
|
_QueryPerformanceFrequency = windll.kernel32.QueryPerformanceFrequency
|
||
|
_QueryPerformanceFrequency.restype = BOOL
|
||
|
_QueryPerformanceFrequency.argtypes = (LARGEINTEGER_p,)
|
||
|
def QueryPerformanceFrequency():
|
||
|
frequency = LARGEINTEGER()
|
||
|
ok = _QueryPerformanceFrequency(byref(frequency))
|
||
|
if not ok:
|
||
|
raise ctypes_winerror()
|
||
|
return int(frequency.value)
|
||
|
|
||
|
_QueryPerformanceCounter = windll.kernel32.QueryPerformanceCounter
|
||
|
_QueryPerformanceCounter.restype = BOOL
|
||
|
_QueryPerformanceCounter.argtypes = (LARGEINTEGER_p,)
|
||
|
def QueryPerformanceCounter():
|
||
|
frequency = LARGEINTEGER()
|
||
|
ok = _QueryPerformanceCounter(byref(frequency))
|
||
|
if not ok:
|
||
|
raise ctypes_winerror()
|
||
|
return int(frequency.value)
|
||
|
|
||
|
GetTickCount = windll.kernel32.GetTickCount
|
||
|
GetTickCount.restype = DWORD
|
||
|
GetTickCount.argtypes = ()
|
||
|
|
||
|
if hasattr(windll.kernel32, 'GetTickCount64'):
|
||
|
GetTickCount64 = windll.kernel32.GetTickCount64
|
||
|
GetTickCount64.restype = ULONGLONG
|
||
|
GetTickCount64.argtypes = ()
|
||
|
has_GetTickCount64 = True
|
||
|
|
||
|
GetCurrentProcess = windll.kernel32.GetCurrentProcess
|
||
|
GetCurrentProcess.argtypes = ()
|
||
|
GetCurrentProcess.restype = HANDLE
|
||
|
|
||
|
_GetProcessTimes = windll.kernel32.GetProcessTimes
|
||
|
_GetProcessTimes.argtypes = (HANDLE, FILETIME_p, FILETIME_p, FILETIME_p, FILETIME_p)
|
||
|
_GetProcessTimes.restype = BOOL
|
||
|
|
||
|
def filetime2py(obj):
|
||
|
return (obj.dwHighDateTime << 32) + obj.dwLowDateTime
|
||
|
|
||
|
def GetProcessTimes(handle):
|
||
|
creation_time = FILETIME()
|
||
|
exit_time = FILETIME()
|
||
|
kernel_time = FILETIME()
|
||
|
user_time = FILETIME()
|
||
|
ok = _GetProcessTimes(handle,
|
||
|
byref(creation_time), byref(exit_time),
|
||
|
byref(kernel_time), byref(user_time))
|
||
|
if not ok:
|
||
|
raise ctypes_winerror()
|
||
|
return (filetime2py(creation_time), filetime2py(exit_time),
|
||
|
filetime2py(kernel_time), filetime2py(user_time))
|
||
|
|
||
|
_GetSystemTimeAsFileTime = windll.kernel32.GetSystemTimeAsFileTime
|
||
|
_GetSystemTimeAsFileTime.argtypes = (FILETIME_p,)
|
||
|
_GetSystemTimeAsFileTime.restype = None
|
||
|
|
||
|
def GetSystemTimeAsFileTime():
|
||
|
system_time = FILETIME()
|
||
|
_GetSystemTimeAsFileTime(byref(system_time))
|
||
|
return filetime2py(system_time)
|
||
|
|
||
|
libc_name = ctypes.util.find_library('c')
|
||
|
if libc_name:
|
||
|
libc = ctypes.CDLL(libc_name, use_errno=True)
|
||
|
clock_t = ctypes.c_ulong
|
||
|
|
||
|
if sys.platform == 'darwin':
|
||
|
mach_absolute_time = libc.mach_absolute_time
|
||
|
mach_absolute_time.argtypes = ()
|
||
|
mach_absolute_time.restype = ctypes.c_uint64
|
||
|
has_mach_absolute_time = True
|
||
|
|
||
|
class mach_timebase_info_data_t(ctypes.Structure):
|
||
|
_fields_ = (
|
||
|
('numer', ctypes.c_uint32),
|
||
|
('denom', ctypes.c_uint32),
|
||
|
)
|
||
|
mach_timebase_info_data_p = POINTER(mach_timebase_info_data_t)
|
||
|
|
||
|
_mach_timebase_info = libc.mach_timebase_info
|
||
|
_mach_timebase_info.argtypes = (mach_timebase_info_data_p,)
|
||
|
_mach_timebase_info.restype = ctypes.c_int
|
||
|
def mach_timebase_info():
|
||
|
timebase = mach_timebase_info_data_t()
|
||
|
_mach_timebase_info(byref(timebase))
|
||
|
return (timebase.numer, timebase.denom)
|
||
|
|
||
|
_libc_clock = libc.clock
|
||
|
_libc_clock.argtypes = ()
|
||
|
_libc_clock.restype = clock_t
|
||
|
has_libc_clock = True
|
||
|
|
||
|
if hasattr(libc, 'sleep'):
|
||
|
_libc_sleep = libc.sleep
|
||
|
_libc_sleep.argtypes = (ctypes.c_uint,)
|
||
|
_libc_sleep.restype = ctypes.c_uint
|
||
|
has_libc_sleep = True
|
||
|
|
||
|
if hasattr(libc, 'gettimeofday'):
|
||
|
class timeval(ctypes.Structure):
|
||
|
_fields_ = (
|
||
|
('tv_sec', time_t),
|
||
|
('tv_usec', ctypes.c_long),
|
||
|
)
|
||
|
timeval_p = POINTER(timeval)
|
||
|
timezone_p = ctypes.c_void_p
|
||
|
|
||
|
_gettimeofday = libc.gettimeofday
|
||
|
# FIXME: some platforms only expect one argument
|
||
|
_gettimeofday.argtypes = (timeval_p, timezone_p)
|
||
|
_gettimeofday.restype = ctypes.c_int
|
||
|
def gettimeofday():
|
||
|
tv = timeval()
|
||
|
err = _gettimeofday(byref(tv), None)
|
||
|
if err:
|
||
|
raise ctypes_oserror()
|
||
|
return tv
|
||
|
has_gettimeofday = True
|
||
|
|
||
|
time_t_p = POINTER(time_t)
|
||
|
if hasattr(libc, 'time'):
|
||
|
_libc__time = libc.time
|
||
|
_libc__time.argtypes = (time_t_p,)
|
||
|
_libc__time.restype = time_t
|
||
|
def _libc_time():
|
||
|
return _libc__time(None)
|
||
|
has_libc_time = True
|
||
|
|
||
|
if sys.platform.startswith(("freebsd", "openbsd")):
|
||
|
librt_name = libc_name
|
||
|
else:
|
||
|
librt_name = ctypes.util.find_library('rt')
|
||
|
if librt_name:
|
||
|
librt = ctypes.CDLL(librt_name, use_errno=True)
|
||
|
if hasattr(librt, 'clock_gettime'):
|
||
|
clockid_t = ctypes.c_int
|
||
|
class timespec(ctypes.Structure):
|
||
|
_fields_ = (
|
||
|
('tv_sec', time_t),
|
||
|
('tv_nsec', ctypes.c_long),
|
||
|
)
|
||
|
timespec_p = POINTER(timespec)
|
||
|
|
||
|
_clock_gettime = librt.clock_gettime
|
||
|
_clock_gettime.argtypes = (clockid_t, timespec_p)
|
||
|
_clock_gettime.restype = ctypes.c_int
|
||
|
def clock_gettime(clk_id):
|
||
|
ts = timespec()
|
||
|
err = _clock_gettime(clk_id, byref(ts))
|
||
|
if err:
|
||
|
raise ctypes_oserror()
|
||
|
return ts.tv_sec + ts.tv_nsec * 1e-9
|
||
|
has_clock_gettime = True
|
||
|
|
||
|
_clock_settime = librt.clock_settime
|
||
|
_clock_settime.argtypes = (clockid_t, timespec_p)
|
||
|
_clock_settime.restype = ctypes.c_int
|
||
|
def clock_settime(clk_id, value):
|
||
|
ts = timespec()
|
||
|
ts.tv_sec = int(value)
|
||
|
ts.tv_nsec = int(float(abs(value)) % 1.0 * 1e9)
|
||
|
err = _clock_settime(clk_id, byref(ts))
|
||
|
if err:
|
||
|
raise ctypes_oserror()
|
||
|
return ts.tv_sec + ts.tv_nsec * 1e-9
|
||
|
|
||
|
_clock_getres = librt.clock_getres
|
||
|
_clock_getres.argtypes = (clockid_t, timespec_p)
|
||
|
_clock_getres.restype = ctypes.c_int
|
||
|
def clock_getres(clk_id):
|
||
|
ts = timespec()
|
||
|
err = _clock_getres(clk_id, byref(ts))
|
||
|
if err:
|
||
|
raise ctypes_oserror()
|
||
|
return ts.tv_sec + ts.tv_nsec * 1e-9
|
||
|
|
||
|
if sys.platform.startswith("linux"):
|
||
|
CLOCK_REALTIME = 0
|
||
|
CLOCK_MONOTONIC = 1
|
||
|
CLOCK_PROCESS_CPUTIME_ID = 2
|
||
|
elif sys.platform.startswith("freebsd"):
|
||
|
CLOCK_REALTIME = 0
|
||
|
CLOCK_PROF = 2
|
||
|
CLOCK_MONOTONIC = 4
|
||
|
elif sys.platform.startswith("openbsd"):
|
||
|
CLOCK_REALTIME = 0
|
||
|
CLOCK_MONOTONIC = 3
|
||
|
elif sys.platform.startswith("sunos"):
|
||
|
CLOCK_REALTIME = 3
|
||
|
CLOCK_HIGHRES = 4
|
||
|
# clock_gettime(CLOCK_PROCESS_CPUTIME_ID) fails with errno 22
|
||
|
# on OpenSolaris
|
||
|
# CLOCK_PROCESS_CPUTIME_ID = 5
|
||
|
|
||
|
def _clock_gettime_info(use_info, clk_id):
|
||
|
value = clock_gettime(clk_id)
|
||
|
if use_info:
|
||
|
name = {
|
||
|
CLOCK_MONOTONIC: 'CLOCK_MONOTONIC',
|
||
|
CLOCK_PROF: 'CLOCK_PROF',
|
||
|
CLOCK_HIGHRES: 'CLOCK_HIGHRES',
|
||
|
CLOCK_PROCESS_CPUTIME_ID: 'CLOCK_PROCESS_CPUTIME_ID',
|
||
|
CLOCK_REALTIME: 'CLOCK_REALTIME',
|
||
|
}[clk_id]
|
||
|
try:
|
||
|
resolution = clock_getres(clk_id)
|
||
|
except OSError:
|
||
|
resolution = 1e-9
|
||
|
info = {
|
||
|
'implementation': 'clock_gettime(%s)' % name,
|
||
|
'resolution': resolution,
|
||
|
}
|
||
|
if clk_id in (CLOCK_MONOTONIC, CLOCK_PROF, CLOCK_HIGHRES, CLOCK_PROCESS_CPUTIME_ID):
|
||
|
info['monotonic'] = True
|
||
|
info['adjustable'] = False
|
||
|
elif clk_id in (CLOCK_REALTIME,):
|
||
|
info['monotonic'] = False
|
||
|
info['adjustable'] = True
|
||
|
else:
|
||
|
info = None
|
||
|
return (value, info)
|
||
|
|
||
|
has_monotonic = False
|
||
|
if os.name == 'nt':
|
||
|
# GetTickCount64() requires Windows Vista, Server 2008 or later
|
||
|
if has_GetTickCount64:
|
||
|
def _monotonic(use_info):
|
||
|
value = GetTickCount64() * 1e-3
|
||
|
if use_info:
|
||
|
info = {
|
||
|
'implementation': "GetTickCount64()",
|
||
|
"monotonic": True,
|
||
|
"resolution": 1e-3,
|
||
|
"adjustable": False,
|
||
|
}
|
||
|
# FIXME: call GetSystemTimeAdjustment() to get the resolution
|
||
|
else:
|
||
|
info = None
|
||
|
return (value, info)
|
||
|
has_monotonic = True
|
||
|
else:
|
||
|
def _monotonic(use_info):
|
||
|
ticks = GetTickCount()
|
||
|
if ticks < _monotonic.last:
|
||
|
# Integer overflow detected
|
||
|
_monotonic.delta += 2**32
|
||
|
_monotonic.last = ticks
|
||
|
value = (ticks + _monotonic.delta) * 1e-3
|
||
|
|
||
|
if use_info:
|
||
|
info = {
|
||
|
'implementation': "GetTickCount()",
|
||
|
"monotonic": True,
|
||
|
"resolution": 1e-3,
|
||
|
"adjustable": False,
|
||
|
}
|
||
|
# FIXME: call GetSystemTimeAdjustment() to get the resolution
|
||
|
else:
|
||
|
info = None
|
||
|
return (value, info)
|
||
|
_monotonic.last = 0
|
||
|
_monotonic.delta = 0
|
||
|
has_monotonic = True
|
||
|
|
||
|
elif has_mach_absolute_time:
|
||
|
def _monotonic(use_info):
|
||
|
if _monotonic.factor is None:
|
||
|
timebase = mach_timebase_info()
|
||
|
_monotonic.factor = timebase[0] / timebase[1] * 1e-9
|
||
|
value = mach_absolute_time() * _monotonic.factor
|
||
|
if use_info:
|
||
|
info = {
|
||
|
'implementation': "mach_absolute_time()",
|
||
|
'resolution': _monotonic.factor,
|
||
|
'monotonic': True,
|
||
|
'adjustable': False,
|
||
|
}
|
||
|
else:
|
||
|
info = None
|
||
|
return (value, info)
|
||
|
_monotonic.factor = None
|
||
|
has_monotonic = True
|
||
|
|
||
|
elif has_clock_gettime and CLOCK_HIGHRES is not None:
|
||
|
def _monotonic(use_info):
|
||
|
return _clock_gettime_info(use_info, CLOCK_HIGHRES)
|
||
|
has_monotonic = True
|
||
|
|
||
|
elif has_clock_gettime and CLOCK_MONOTONIC is not None:
|
||
|
def _monotonic(use_info):
|
||
|
return _clock_gettime_info(use_info, CLOCK_MONOTONIC)
|
||
|
has_monotonic = True
|
||
|
|
||
|
if has_monotonic:
|
||
|
def monotonic():
|
||
|
return _monotonic(False)[0]
|
||
|
|
||
|
|
||
|
def _perf_counter(use_info):
|
||
|
info = None
|
||
|
if _perf_counter.use_performance_counter:
|
||
|
if _perf_counter.performance_frequency is None:
|
||
|
value, info = _win_perf_counter(use_info)
|
||
|
if value is not None:
|
||
|
return (value, info)
|
||
|
if _perf_counter.use_monotonic:
|
||
|
# The monotonic clock is preferred over the system time
|
||
|
try:
|
||
|
return _monotonic(use_info)
|
||
|
except (OSError, WindowsError):
|
||
|
_perf_counter.use_monotonic = False
|
||
|
return _time(use_info)
|
||
|
_perf_counter.use_performance_counter = (os.name == 'nt')
|
||
|
if _perf_counter.use_performance_counter:
|
||
|
_perf_counter.performance_frequency = None
|
||
|
_perf_counter.use_monotonic = has_monotonic
|
||
|
|
||
|
def perf_counter():
|
||
|
return _perf_counter(False)[0]
|
||
|
|
||
|
|
||
|
if os.name == 'nt':
|
||
|
def _process_time(use_info):
|
||
|
handle = GetCurrentProcess()
|
||
|
process_times = GetProcessTimes(handle)
|
||
|
value = (process_times[2] + process_times[3]) * 1e-7
|
||
|
if use_info:
|
||
|
info = {
|
||
|
"implementation": "GetProcessTimes()",
|
||
|
"resolution": 1e-7,
|
||
|
"monotonic": True,
|
||
|
"adjustable": False,
|
||
|
# FIXME: call GetSystemTimeAdjustment() to get the resolution
|
||
|
}
|
||
|
else:
|
||
|
info = None
|
||
|
return (value, info)
|
||
|
else:
|
||
|
import os
|
||
|
try:
|
||
|
import resource
|
||
|
except ImportError:
|
||
|
has_resource = False
|
||
|
else:
|
||
|
has_resource = True
|
||
|
|
||
|
def _process_time(use_info):
|
||
|
info = None
|
||
|
if _process_time.clock_id is not None:
|
||
|
try:
|
||
|
return _clock_gettime_info(use_info, _process_time.clock_id)
|
||
|
except OSError:
|
||
|
_process_time.clock_id = None
|
||
|
if _process_time.use_getrusage:
|
||
|
try:
|
||
|
usage = resource.getrusage(resource.RUSAGE_SELF)
|
||
|
value = usage[0] + usage[1]
|
||
|
except OSError:
|
||
|
_process_time.use_getrusage = False
|
||
|
else:
|
||
|
if use_info:
|
||
|
info = {
|
||
|
"implementation": "getrusage(RUSAGE_SELF)",
|
||
|
"resolution": 1e-6,
|
||
|
"monotonic": True,
|
||
|
"adjustable": False,
|
||
|
}
|
||
|
return (value, info)
|
||
|
if _process_time.use_times:
|
||
|
try:
|
||
|
times = os.times()
|
||
|
value = times[0] + times[1]
|
||
|
except OSError:
|
||
|
_process_time.use_getrusage = False
|
||
|
else:
|
||
|
if use_info:
|
||
|
try:
|
||
|
ticks_per_second = os.sysconf("SC_CLK_TCK")
|
||
|
except ValueError:
|
||
|
ticks_per_second = 60 # FIXME: get HZ constant
|
||
|
info = {
|
||
|
"implementation": "times()",
|
||
|
"resolution": 1.0 / ticks_per_second,
|
||
|
"monotonic": True,
|
||
|
"adjustable": False,
|
||
|
}
|
||
|
return (value, info)
|
||
|
return _libc_clock_info(use_info)
|
||
|
if has_clock_gettime and CLOCK_PROCESS_CPUTIME_ID is not None:
|
||
|
_process_time.clock_id = CLOCK_PROCESS_CPUTIME_ID
|
||
|
elif has_clock_gettime and CLOCK_PROF is not None:
|
||
|
_process_time.clock_id = CLOCK_PROF
|
||
|
else:
|
||
|
_process_time.clock_id = None
|
||
|
_process_time.use_getrusage = has_resource
|
||
|
# On OS/2, only the 5th field of os.times() is set, others are zeros
|
||
|
_process_time.use_times = (hasattr(os, 'times') and os.name != 'os2')
|
||
|
|
||
|
def process_time():
|
||
|
return _process_time(False)[0]
|
||
|
|
||
|
|
||
|
if os.name == "nt":
|
||
|
def _time(use_info):
|
||
|
value = GetSystemTimeAsFileTime() * 1e-7
|
||
|
if use_info:
|
||
|
info = {
|
||
|
'implementation': 'GetSystemTimeAsFileTime',
|
||
|
'resolution': 1e-7,
|
||
|
'monotonic': False,
|
||
|
# FIXME: call GetSystemTimeAdjustment() to get the resolution
|
||
|
# and adjustable
|
||
|
}
|
||
|
else:
|
||
|
info = None
|
||
|
return (value, info)
|
||
|
else:
|
||
|
def _time(use_info):
|
||
|
info = None
|
||
|
if has_clock_gettime and CLOCK_REALTIME is not None:
|
||
|
try:
|
||
|
return _clock_gettime_info(use_info, CLOCK_REALTIME)
|
||
|
except OSError:
|
||
|
# CLOCK_REALTIME is not supported (unlikely)
|
||
|
pass
|
||
|
if has_gettimeofday:
|
||
|
try:
|
||
|
tv = gettimeofday()
|
||
|
except OSError:
|
||
|
# gettimeofday() should not fail
|
||
|
pass
|
||
|
else:
|
||
|
if use_info:
|
||
|
info = {
|
||
|
'monotonic': False,
|
||
|
"implementation": "gettimeofday()",
|
||
|
"resolution": 1e-6,
|
||
|
'monotonic': False,
|
||
|
'adjustable': True,
|
||
|
}
|
||
|
value = tv.tv_sec + tv.tv_usec * 1e-6
|
||
|
return (value, info)
|
||
|
# FIXME: implement ftime()
|
||
|
if has_ftime:
|
||
|
if use_info:
|
||
|
info = {
|
||
|
"implementation": "ftime()",
|
||
|
"resolution": 1e-3,
|
||
|
'monotonic': False,
|
||
|
'adjustable': True,
|
||
|
}
|
||
|
value = ftime()
|
||
|
elif has_libc_time:
|
||
|
if use_info:
|
||
|
info = {
|
||
|
"implementation": "time()",
|
||
|
"resolution": 1.0,
|
||
|
'monotonic': False,
|
||
|
'adjustable': True,
|
||
|
}
|
||
|
value = float(_libc_time())
|
||
|
else:
|
||
|
if use_info:
|
||
|
info = {
|
||
|
"implementation": "time.time()",
|
||
|
'monotonic': False,
|
||
|
'adjustable': True,
|
||
|
}
|
||
|
if os.name == "nt":
|
||
|
# On Windows, time.time() uses ftime()
|
||
|
info["resolution"] = 1e-3
|
||
|
else:
|
||
|
# guess that time.time() uses gettimeofday()
|
||
|
info["resolution"] = 1e-6
|
||
|
value = python_time.time()
|
||
|
return (value, info)
|
||
|
|
||
|
def time():
|
||
|
return _time(False)[0]
|
||
|
|
||
|
|
||
|
try:
|
||
|
import select
|
||
|
except ImportError:
|
||
|
has_select = False
|
||
|
else:
|
||
|
# FIXME: On Windows, select.select([], [], [], seconds) fails with
|
||
|
# select.error(10093)
|
||
|
has_select = (hasattr(select, "select") and os.name != "nt")
|
||
|
|
||
|
if has_select:
|
||
|
def _sleep(seconds):
|
||
|
return select.select([], [], [], seconds)
|
||
|
|
||
|
elif has_delay:
|
||
|
def _sleep(seconds):
|
||
|
milliseconds = int(seconds * 1000)
|
||
|
# FIXME
|
||
|
delay(milliseconds)
|
||
|
|
||
|
#elif os.name == "nt":
|
||
|
# def _sleep(seconds):
|
||
|
# milliseconds = int(seconds * 1000)
|
||
|
# # FIXME: use ctypes
|
||
|
# win32api.ResetEvent(hInterruptEvent);
|
||
|
# win32api.WaitForSingleObject(sleep.sigint_event, milliseconds)
|
||
|
#
|
||
|
# sleep.sigint_event = win32api.CreateEvent(NULL, TRUE, FALSE, FALSE)
|
||
|
# # SetEvent(sleep.sigint_event) will be called by the signal handler of SIGINT
|
||
|
|
||
|
elif os.name == "os2":
|
||
|
def _sleep(seconds):
|
||
|
milliseconds = int(seconds * 1000)
|
||
|
# FIXME
|
||
|
DosSleep(milliseconds)
|
||
|
|
||
|
elif has_libc_sleep:
|
||
|
def _sleep(seconds):
|
||
|
seconds = int(seconds)
|
||
|
_libc_sleep(seconds)
|
||
|
|
||
|
else:
|
||
|
def _sleep(seconds):
|
||
|
python_time.sleep(seconds)
|
||
|
|
||
|
def sleep(seconds):
|
||
|
if seconds < 0:
|
||
|
raise ValueError("sleep length must be non-negative")
|
||
|
_sleep(seconds)
|
||
|
|
||
|
def _libc_clock_info(use_info):
|
||
|
if use_info:
|
||
|
info = {
|
||
|
'implementation': 'clock()',
|
||
|
'resolution': 1.0,
|
||
|
# FIXME: 'resolution': 1.0 / CLOCKS_PER_SEC,
|
||
|
'monotonic': True,
|
||
|
'adjustable': False,
|
||
|
}
|
||
|
if os.name != "nt":
|
||
|
info['monotonic'] = True
|
||
|
else:
|
||
|
info = None
|
||
|
if has_libc_clock:
|
||
|
value = _libc_clock()
|
||
|
if use_info:
|
||
|
info['implementation'] = 'clock()'
|
||
|
else:
|
||
|
value = python_time.clock()
|
||
|
if use_info:
|
||
|
info['implementation'] = 'time.clock()'
|
||
|
return (value, info)
|
||
|
|
||
|
def _win_perf_counter(use_info):
|
||
|
if _win_perf_counter.perf_frequency is None:
|
||
|
try:
|
||
|
_win_perf_counter.perf_frequency = float(QueryPerformanceFrequency())
|
||
|
except WindowsError:
|
||
|
# QueryPerformanceFrequency() fails if the installed
|
||
|
# hardware does not support a high-resolution performance
|
||
|
# counter
|
||
|
return (None, None)
|
||
|
|
||
|
value = QueryPerformanceCounter() / _win_perf_counter.perf_frequency
|
||
|
if use_info:
|
||
|
info = {
|
||
|
'implementation': 'QueryPerformanceCounter',
|
||
|
'resolution': 1.0 / _win_perf_counter.perf_frequency,
|
||
|
'monotonic': True,
|
||
|
'adjustable': False,
|
||
|
}
|
||
|
else:
|
||
|
info = None
|
||
|
return (value, info)
|
||
|
_win_perf_counter.perf_frequency = None
|
||
|
|
||
|
if os.name == 'nt':
|
||
|
def _clock(use_info):
|
||
|
info = None
|
||
|
if _clock.use_performance_counter:
|
||
|
value, info = _win_perf_counter(use_info)
|
||
|
if value is not None:
|
||
|
return (value, info)
|
||
|
return _libc_clock_info(use_info)
|
||
|
_clock.use_performance_counter = True
|
||
|
else:
|
||
|
def _clock(use_info):
|
||
|
return _libc_clock_info(use_info)
|
||
|
|
||
|
def clock():
|
||
|
return _clock(False)[0]
|
||
|
|
||
|
|
||
|
class clock_info(object):
|
||
|
def __init__(self, implementation, monotonic, adjustable, resolution):
|
||
|
self.implementation = implementation
|
||
|
self.monotonic = monotonic
|
||
|
self.adjustable = adjustable
|
||
|
self.resolution = resolution
|
||
|
|
||
|
def __repr__(self):
|
||
|
return (
|
||
|
'clockinfo(adjustable=%s, implementation=%r, monotonic=%s, resolution=%s'
|
||
|
% (self.adjustable, self.implementation, self.monotonic, self.resolution))
|
||
|
|
||
|
def get_clock_info(name):
|
||
|
if name == 'clock':
|
||
|
info = _clock(True)[1]
|
||
|
elif name == 'perf_counter':
|
||
|
info = _perf_counter(True)[1]
|
||
|
elif name == 'process_time':
|
||
|
info = _process_time(True)[1]
|
||
|
elif name == 'time':
|
||
|
info = _time(True)[1]
|
||
|
elif has_monotonic and name == 'monotonic':
|
||
|
info = _monotonic(True)[1]
|
||
|
else:
|
||
|
raise ValueError("unknown clock: %s" % name)
|
||
|
return clock_info(**info)
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
from errno import EPERM
|
||
|
import threading
|
||
|
import unittest
|
||
|
|
||
|
class TestPEP418(unittest.TestCase):
|
||
|
if not hasattr(unittest.TestCase, 'assertIsInstance'):
|
||
|
# Python < 2.7 or Python < 3.2
|
||
|
def assertIsInstance(self, obj, klass):
|
||
|
self.assertTrue(isinstance(obj, klass))
|
||
|
def assertGreater(self, a, b):
|
||
|
self.assertTrue(a > b)
|
||
|
def assertLess(self, a, b):
|
||
|
self.assertTrue(a < b)
|
||
|
def assertLessEqual(self, a, b):
|
||
|
self.assertTrue(a <= b)
|
||
|
def assertAlmostEqual(self, first, second, delta):
|
||
|
self.assertTrue(abs(first - second) <= delta)
|
||
|
|
||
|
def test_clock(self):
|
||
|
clock()
|
||
|
|
||
|
info = get_clock_info('clock')
|
||
|
self.assertEqual(info.monotonic, True)
|
||
|
self.assertEqual(info.adjustable, False)
|
||
|
|
||
|
def test_get_clock_info(self):
|
||
|
clocks = ['clock', 'perf_counter', 'process_time', 'time']
|
||
|
if has_monotonic:
|
||
|
clocks.append('monotonic')
|
||
|
|
||
|
for name in clocks:
|
||
|
info = get_clock_info(name)
|
||
|
self.assertIsInstance(info.implementation, str)
|
||
|
self.assertNotEqual(info.implementation, '')
|
||
|
self.assertIsInstance(info.monotonic, bool)
|
||
|
self.assertIsInstance(info.resolution, float)
|
||
|
# 0 < resolution <= 1.0
|
||
|
self.assertGreater(info.resolution, 0)
|
||
|
self.assertLessEqual(info.resolution, 1)
|
||
|
self.assertIsInstance(info.adjustable, bool)
|
||
|
|
||
|
self.assertRaises(ValueError, get_clock_info, 'xxx')
|
||
|
|
||
|
if not has_monotonic:
|
||
|
print("Skip test_monotonic: need time.monotonic")
|
||
|
else:
|
||
|
def test_monotonic(self):
|
||
|
t1 = monotonic()
|
||
|
python_time.sleep(0.1)
|
||
|
t2 = monotonic()
|
||
|
dt = t2 - t1
|
||
|
self.assertGreater(t2, t1)
|
||
|
self.assertAlmostEqual(dt, 0.1, delta=0.2)
|
||
|
|
||
|
info = get_clock_info('monotonic')
|
||
|
self.assertEqual(info.monotonic, True)
|
||
|
self.assertEqual(info.adjustable, False)
|
||
|
|
||
|
if not has_monotonic or not has_clock_gettime:
|
||
|
if not has_monotonic:
|
||
|
print('Skip test_monotonic_settime: need time.monotonic')
|
||
|
elif not has_clock_gettime:
|
||
|
print('Skip test_monotonic_settime: need time.clock_settime')
|
||
|
else:
|
||
|
def test_monotonic_settime(self):
|
||
|
t1 = monotonic()
|
||
|
realtime = clock_gettime(CLOCK_REALTIME)
|
||
|
# jump backward with an offset of 1 hour
|
||
|
try:
|
||
|
clock_settime(CLOCK_REALTIME, realtime - 3600)
|
||
|
except OSError as err:
|
||
|
if err.errno == EPERM:
|
||
|
if hasattr(unittest, 'SkipTest'):
|
||
|
raise unittest.SkipTest(str(err))
|
||
|
else:
|
||
|
print("Skip test_monotonic_settime: %s" % err)
|
||
|
return
|
||
|
else:
|
||
|
raise
|
||
|
t2 = monotonic()
|
||
|
clock_settime(CLOCK_REALTIME, realtime)
|
||
|
# monotonic must not be affected by system clock updates
|
||
|
self.assertGreaterEqual(t2, t1)
|
||
|
|
||
|
def test_perf_counter(self):
|
||
|
perf_counter()
|
||
|
|
||
|
def test_process_time(self):
|
||
|
start = process_time()
|
||
|
python_time.sleep(0.1)
|
||
|
stop = process_time()
|
||
|
self.assertLess(stop - start, 0.01)
|
||
|
|
||
|
info = get_clock_info('process_time')
|
||
|
self.assertEqual(info.monotonic, True)
|
||
|
self.assertEqual(info.adjustable, False)
|
||
|
|
||
|
|
||
|
def test_process_time_threads(self):
|
||
|
class BusyThread(threading.Thread):
|
||
|
def run(self):
|
||
|
while not self.stop:
|
||
|
pass
|
||
|
|
||
|
thread = BusyThread()
|
||
|
thread.stop = False
|
||
|
t1 = process_time()
|
||
|
thread.start()
|
||
|
sleep(0.2)
|
||
|
t2 = process_time()
|
||
|
thread.stop = True
|
||
|
thread.join()
|
||
|
self.assertGreater(t2 - t1, 0.1)
|
||
|
|
||
|
def test_sleep(self):
|
||
|
self.assertRaises(ValueError, sleep, -2)
|
||
|
self.assertRaises(ValueError, sleep, -1)
|
||
|
sleep(1.2)
|
||
|
|
||
|
def test_time(self):
|
||
|
value = time()
|
||
|
self.assertIsInstance(value, float)
|
||
|
|
||
|
info = get_clock_info('time')
|
||
|
self.assertEqual(info.monotonic, False)
|
||
|
self.assertEqual(info.adjustable, True)
|
||
|
|
||
|
|
||
|
if True:
|
||
|
from pprint import pprint
|
||
|
|
||
|
print("clock: %s" % clock())
|
||
|
if has_monotonic:
|
||
|
print("monotonic: %s" % monotonic())
|
||
|
else:
|
||
|
print("monotonic: <not available>")
|
||
|
print("perf_counter: %s" % perf_counter())
|
||
|
print("process_time: %s" % process_time())
|
||
|
print("time: %s" % time())
|
||
|
|
||
|
clocks = ['clock', 'perf_counter', 'process_time', 'time']
|
||
|
if has_monotonic:
|
||
|
clocks.append('monotonic')
|
||
|
pprint(dict((name, get_clock_info(name)) for name in clocks))
|
||
|
|
||
|
unittest.main()
|