1"""
2from: https://bitbucket.org/haypo/misc/src/tip/python/pep418.py
3
4Implementation of the PEP 418 in pure Python using ctypes.
5
6Functions:
7
8 - clock()
9 - get_clock_info(name)
10 - monotonic(): not always available
11 - perf_frequency()
12 - process_time()
13 - sleep()
14 - time()
15
16Constants:
17
18 - has_monotonic (bool): True if time.monotonic() is available
19"""
20# flake8: noqa
21# TODO: gethrtime() for Solaris/OpenIndiana
22# TODO: call GetSystemTimeAdjustment() to get the resolution
23# TODO: other FIXME
24
25import os
26import sys
27import time as python_time
28
29has_mach_absolute_time = False
30has_clock_gettime = False
31has_gettimeofday = False
32has_ftime = False
33has_delay = False
34has_libc_time = False
35has_libc_clock = False
36has_libc_sleep = False
37has_GetTickCount64 = False
38CLOCK_REALTIME = None
39CLOCK_MONOTONIC = None
40CLOCK_PROCESS_CPUTIME_ID = None
41CLOCK_HIGHRES = None
42CLOCK_PROF = None
43try:
44    import ctypes
45    import ctypes.util
46    from ctypes import POINTER
47    from ctypes import byref
48except ImportError as err:
49    pass
50else:
51    def ctypes_oserror():
52        errno = ctypes.get_errno()
53        message = os.strerror(errno)
54        return OSError(errno, message)
55
56    time_t = ctypes.c_long
57
58    if os.name == "nt":
59        from ctypes import windll
60        from ctypes.wintypes import BOOL
61        from ctypes.wintypes import DWORD
62        from ctypes.wintypes import FILETIME
63        from ctypes.wintypes import HANDLE
64        LARGEINTEGER = ctypes.c_int64
65        LARGEINTEGER_p = POINTER(LARGEINTEGER)
66        FILETIME_p = POINTER(FILETIME)
67        ULONGLONG = ctypes.c_uint64
68
69        def ctypes_winerror():
70            errno = ctypes.get_errno()
71            message = os.strerror(errno)
72            return WindowsError(errno, message)
73
74        _QueryPerformanceFrequency = windll.kernel32.QueryPerformanceFrequency
75        _QueryPerformanceFrequency.restype = BOOL
76        _QueryPerformanceFrequency.argtypes = (LARGEINTEGER_p,)
77        def QueryPerformanceFrequency():
78            frequency = LARGEINTEGER()
79            ok = _QueryPerformanceFrequency(byref(frequency))
80            if not ok:
81                raise ctypes_winerror()
82            return int(frequency.value)
83
84        _QueryPerformanceCounter = windll.kernel32.QueryPerformanceCounter
85        _QueryPerformanceCounter.restype = BOOL
86        _QueryPerformanceCounter.argtypes = (LARGEINTEGER_p,)
87        def QueryPerformanceCounter():
88            frequency = LARGEINTEGER()
89            ok = _QueryPerformanceCounter(byref(frequency))
90            if not ok:
91                raise ctypes_winerror()
92            return int(frequency.value)
93
94        GetTickCount = windll.kernel32.GetTickCount
95        GetTickCount.restype = DWORD
96        GetTickCount.argtypes = ()
97
98        if hasattr(windll.kernel32, 'GetTickCount64'):
99            GetTickCount64 = windll.kernel32.GetTickCount64
100            GetTickCount64.restype = ULONGLONG
101            GetTickCount64.argtypes = ()
102            has_GetTickCount64 = True
103
104        GetCurrentProcess = windll.kernel32.GetCurrentProcess
105        GetCurrentProcess.argtypes = ()
106        GetCurrentProcess.restype = HANDLE
107
108        _GetProcessTimes = windll.kernel32.GetProcessTimes
109        _GetProcessTimes.argtypes = (HANDLE, FILETIME_p, FILETIME_p, FILETIME_p, FILETIME_p)
110        _GetProcessTimes.restype = BOOL
111
112        def filetime2py(obj):
113            return (obj.dwHighDateTime << 32) + obj.dwLowDateTime
114
115        def GetProcessTimes(handle):
116            creation_time = FILETIME()
117            exit_time = FILETIME()
118            kernel_time = FILETIME()
119            user_time = FILETIME()
120            ok = _GetProcessTimes(handle,
121                        byref(creation_time), byref(exit_time),
122                        byref(kernel_time), byref(user_time))
123            if not ok:
124                raise ctypes_winerror()
125            return (filetime2py(creation_time), filetime2py(exit_time),
126                    filetime2py(kernel_time), filetime2py(user_time))
127
128        _GetSystemTimeAsFileTime = windll.kernel32.GetSystemTimeAsFileTime
129        _GetSystemTimeAsFileTime.argtypes = (FILETIME_p,)
130        _GetSystemTimeAsFileTime.restype = None
131
132        def GetSystemTimeAsFileTime():
133            system_time = FILETIME()
134            _GetSystemTimeAsFileTime(byref(system_time))
135            return filetime2py(system_time)
136
137    libc_name = ctypes.util.find_library('c')
138    if libc_name:
139        libc = ctypes.CDLL(libc_name, use_errno=True)
140        clock_t = ctypes.c_ulong
141
142        if sys.platform == 'darwin':
143            mach_absolute_time = libc.mach_absolute_time
144            mach_absolute_time.argtypes = ()
145            mach_absolute_time.restype = ctypes.c_uint64
146            has_mach_absolute_time = True
147
148            class mach_timebase_info_data_t(ctypes.Structure):
149                _fields_ = (
150                    ('numer', ctypes.c_uint32),
151                    ('denom', ctypes.c_uint32),
152                )
153            mach_timebase_info_data_p = POINTER(mach_timebase_info_data_t)
154
155            _mach_timebase_info = libc.mach_timebase_info
156            _mach_timebase_info.argtypes = (mach_timebase_info_data_p,)
157            _mach_timebase_info.restype = ctypes.c_int
158            def mach_timebase_info():
159                timebase = mach_timebase_info_data_t()
160                _mach_timebase_info(byref(timebase))
161                return (timebase.numer, timebase.denom)
162
163        _libc_clock = libc.clock
164        _libc_clock.argtypes = ()
165        _libc_clock.restype = clock_t
166        has_libc_clock = True
167
168        if hasattr(libc, 'sleep'):
169            _libc_sleep = libc.sleep
170            _libc_sleep.argtypes = (ctypes.c_uint,)
171            _libc_sleep.restype = ctypes.c_uint
172            has_libc_sleep = True
173
174        if hasattr(libc, 'gettimeofday'):
175            class timeval(ctypes.Structure):
176                _fields_ = (
177                    ('tv_sec', time_t),
178                    ('tv_usec', ctypes.c_long),
179                )
180            timeval_p = POINTER(timeval)
181            timezone_p = ctypes.c_void_p
182
183            _gettimeofday = libc.gettimeofday
184            # FIXME: some platforms only expect one argument
185            _gettimeofday.argtypes = (timeval_p, timezone_p)
186            _gettimeofday.restype = ctypes.c_int
187            def gettimeofday():
188                tv = timeval()
189                err = _gettimeofday(byref(tv), None)
190                if err:
191                    raise ctypes_oserror()
192                return tv
193            has_gettimeofday = True
194
195        time_t_p = POINTER(time_t)
196        if hasattr(libc, 'time'):
197            _libc__time = libc.time
198            _libc__time.argtypes = (time_t_p,)
199            _libc__time.restype = time_t
200            def _libc_time():
201                return _libc__time(None)
202            has_libc_time = True
203
204    if sys.platform.startswith(("freebsd", "openbsd")):
205        librt_name = libc_name
206    else:
207        librt_name = ctypes.util.find_library('rt')
208    if librt_name:
209        librt = ctypes.CDLL(librt_name, use_errno=True)
210        if hasattr(librt, 'clock_gettime'):
211            clockid_t = ctypes.c_int
212            class timespec(ctypes.Structure):
213                _fields_ = (
214                    ('tv_sec', time_t),
215                    ('tv_nsec', ctypes.c_long),
216                )
217            timespec_p = POINTER(timespec)
218
219            _clock_gettime = librt.clock_gettime
220            _clock_gettime.argtypes = (clockid_t, timespec_p)
221            _clock_gettime.restype = ctypes.c_int
222            def clock_gettime(clk_id):
223                ts = timespec()
224                err = _clock_gettime(clk_id, byref(ts))
225                if err:
226                    raise ctypes_oserror()
227                return ts.tv_sec + ts.tv_nsec * 1e-9
228            has_clock_gettime = True
229
230            _clock_settime = librt.clock_settime
231            _clock_settime.argtypes = (clockid_t, timespec_p)
232            _clock_settime.restype = ctypes.c_int
233            def clock_settime(clk_id, value):
234                ts = timespec()
235                ts.tv_sec = int(value)
236                ts.tv_nsec = int(float(abs(value)) % 1.0 * 1e9)
237                err = _clock_settime(clk_id, byref(ts))
238                if err:
239                    raise ctypes_oserror()
240                return ts.tv_sec + ts.tv_nsec * 1e-9
241
242            _clock_getres = librt.clock_getres
243            _clock_getres.argtypes = (clockid_t, timespec_p)
244            _clock_getres.restype = ctypes.c_int
245            def clock_getres(clk_id):
246                ts = timespec()
247                err = _clock_getres(clk_id, byref(ts))
248                if err:
249                    raise ctypes_oserror()
250                return ts.tv_sec + ts.tv_nsec * 1e-9
251
252            if sys.platform.startswith("linux"):
253                CLOCK_REALTIME = 0
254                CLOCK_MONOTONIC = 1
255                CLOCK_PROCESS_CPUTIME_ID = 2
256            elif sys.platform.startswith("freebsd"):
257                CLOCK_REALTIME = 0
258                CLOCK_PROF = 2
259                CLOCK_MONOTONIC = 4
260            elif sys.platform.startswith("openbsd"):
261                CLOCK_REALTIME = 0
262                CLOCK_MONOTONIC = 3
263            elif sys.platform.startswith("sunos"):
264                CLOCK_REALTIME = 3
265                CLOCK_HIGHRES = 4
266                # clock_gettime(CLOCK_PROCESS_CPUTIME_ID) fails with errno 22
267                # on OpenSolaris
268                # CLOCK_PROCESS_CPUTIME_ID = 5
269
270def _clock_gettime_info(use_info, clk_id):
271    value = clock_gettime(clk_id)
272    if use_info:
273        name = {
274            CLOCK_MONOTONIC: 'CLOCK_MONOTONIC',
275            CLOCK_PROF: 'CLOCK_PROF',
276            CLOCK_HIGHRES: 'CLOCK_HIGHRES',
277            CLOCK_PROCESS_CPUTIME_ID: 'CLOCK_PROCESS_CPUTIME_ID',
278            CLOCK_REALTIME: 'CLOCK_REALTIME',
279        }[clk_id]
280        try:
281            resolution = clock_getres(clk_id)
282        except OSError:
283            resolution = 1e-9
284        info = {
285            'implementation': 'clock_gettime(%s)' % name,
286            'resolution': resolution,
287        }
288        if clk_id in (CLOCK_MONOTONIC, CLOCK_PROF, CLOCK_HIGHRES, CLOCK_PROCESS_CPUTIME_ID):
289            info['monotonic'] = True
290            info['adjustable'] = False
291        elif clk_id in (CLOCK_REALTIME,):
292            info['monotonic'] = False
293            info['adjustable'] = True
294    else:
295        info = None
296    return (value, info)
297
298has_monotonic = False
299if os.name == 'nt':
300    # GetTickCount64() requires Windows Vista, Server 2008 or later
301    if has_GetTickCount64:
302        def _monotonic(use_info):
303            value = GetTickCount64() * 1e-3
304            if use_info:
305                info = {
306                    'implementation': "GetTickCount64()",
307                    "monotonic": True,
308                    "resolution": 1e-3,
309                    "adjustable": False,
310                }
311                # FIXME: call GetSystemTimeAdjustment() to get the resolution
312            else:
313                info = None
314            return (value, info)
315        has_monotonic = True
316    else:
317        def _monotonic(use_info):
318            ticks = GetTickCount()
319            if ticks < _monotonic.last:
320                # Integer overflow detected
321                _monotonic.delta += 2**32
322            _monotonic.last = ticks
323            value = (ticks + _monotonic.delta) * 1e-3
324
325            if use_info:
326                info = {
327                    'implementation': "GetTickCount()",
328                    "monotonic": True,
329                    "resolution": 1e-3,
330                    "adjustable": False,
331                }
332                # FIXME: call GetSystemTimeAdjustment() to get the resolution
333            else:
334                info = None
335            return (value, info)
336        _monotonic.last = 0
337        _monotonic.delta = 0
338    has_monotonic = True
339
340elif has_mach_absolute_time:
341    def _monotonic(use_info):
342        if _monotonic.factor is None:
343            timebase = mach_timebase_info()
344            _monotonic.factor = timebase[0] / timebase[1] * 1e-9
345        value = mach_absolute_time() * _monotonic.factor
346        if use_info:
347            info = {
348                'implementation': "mach_absolute_time()",
349                'resolution': _monotonic.factor,
350                'monotonic': True,
351                'adjustable': False,
352            }
353        else:
354            info = None
355        return (value, info)
356    _monotonic.factor = None
357    has_monotonic = True
358
359elif has_clock_gettime and CLOCK_HIGHRES is not None:
360    def _monotonic(use_info):
361        return _clock_gettime_info(use_info, CLOCK_HIGHRES)
362    has_monotonic = True
363
364elif has_clock_gettime and CLOCK_MONOTONIC is not None:
365    def _monotonic(use_info):
366        return _clock_gettime_info(use_info, CLOCK_MONOTONIC)
367    has_monotonic = True
368
369if has_monotonic:
370    def monotonic():
371        return _monotonic(False)[0]
372
373
374def _perf_counter(use_info):
375    info = None
376    if _perf_counter.use_performance_counter:
377        if _perf_counter.performance_frequency is None:
378            value, info = _win_perf_counter(use_info)
379            if value is not None:
380                return (value, info)
381    if _perf_counter.use_monotonic:
382        # The monotonic clock is preferred over the system time
383        try:
384            return _monotonic(use_info)
385        except (OSError, WindowsError):
386            _perf_counter.use_monotonic = False
387    return _time(use_info)
388_perf_counter.use_performance_counter = (os.name == 'nt')
389if _perf_counter.use_performance_counter:
390    _perf_counter.performance_frequency = None
391_perf_counter.use_monotonic = has_monotonic
392
393def perf_counter():
394    return _perf_counter(False)[0]
395
396
397if os.name == 'nt':
398    def _process_time(use_info):
399        handle = GetCurrentProcess()
400        process_times = GetProcessTimes(handle)
401        value = (process_times[2] + process_times[3]) * 1e-7
402        if use_info:
403            info = {
404                "implementation": "GetProcessTimes()",
405                "resolution": 1e-7,
406                "monotonic": True,
407                "adjustable": False,
408                # FIXME: call GetSystemTimeAdjustment() to get the resolution
409            }
410        else:
411            info = None
412        return (value, info)
413else:
414    import os
415    try:
416        import resource
417    except ImportError:
418        has_resource = False
419    else:
420        has_resource = True
421
422    def _process_time(use_info):
423        info = None
424        if _process_time.clock_id is not None:
425            try:
426                return _clock_gettime_info(use_info, _process_time.clock_id)
427            except OSError:
428                _process_time.clock_id = None
429        if _process_time.use_getrusage:
430            try:
431                usage = resource.getrusage(resource.RUSAGE_SELF)
432                value = usage[0] + usage[1]
433            except OSError:
434                _process_time.use_getrusage = False
435            else:
436                if use_info:
437                    info = {
438                        "implementation": "getrusage(RUSAGE_SELF)",
439                        "resolution": 1e-6,
440                        "monotonic": True,
441                        "adjustable": False,
442                    }
443                return (value, info)
444        if _process_time.use_times:
445            try:
446                times = os.times()
447                value = times[0] + times[1]
448            except OSError:
449                _process_time.use_getrusage = False
450            else:
451                if use_info:
452                    try:
453                        ticks_per_second = os.sysconf("SC_CLK_TCK")
454                    except ValueError:
455                        ticks_per_second = 60 # FIXME: get HZ constant
456                    info = {
457                        "implementation": "times()",
458                        "resolution": 1.0 / ticks_per_second,
459                        "monotonic": True,
460                        "adjustable": False,
461                    }
462                return (value, info)
463        return _libc_clock_info(use_info)
464    if has_clock_gettime and CLOCK_PROCESS_CPUTIME_ID is not None:
465        _process_time.clock_id = CLOCK_PROCESS_CPUTIME_ID
466    elif has_clock_gettime and CLOCK_PROF is not None:
467        _process_time.clock_id = CLOCK_PROF
468    else:
469        _process_time.clock_id = None
470    _process_time.use_getrusage = has_resource
471    # On OS/2, only the 5th field of os.times() is set, others are zeros
472    _process_time.use_times = (hasattr(os, 'times') and os.name != 'os2')
473
474def process_time():
475    return _process_time(False)[0]
476
477
478if os.name == "nt":
479    def _time(use_info):
480        value = GetSystemTimeAsFileTime() * 1e-7
481        if use_info:
482            info = {
483                'implementation': 'GetSystemTimeAsFileTime',
484                'resolution': 1e-7,
485                'monotonic': False,
486                # FIXME: call GetSystemTimeAdjustment() to get the resolution
487                # and adjustable
488            }
489        else:
490            info = None
491        return (value, info)
492else:
493    def _time(use_info):
494        info = None
495        if has_clock_gettime and CLOCK_REALTIME is not None:
496            try:
497                return _clock_gettime_info(use_info, CLOCK_REALTIME)
498            except OSError:
499                # CLOCK_REALTIME is not supported (unlikely)
500                pass
501        if has_gettimeofday:
502            try:
503                tv = gettimeofday()
504            except OSError:
505                # gettimeofday() should not fail
506                pass
507            else:
508                if use_info:
509                    info = {
510                        'monotonic': False,
511                        "implementation": "gettimeofday()",
512                        "resolution": 1e-6,
513                        'monotonic': False,
514                        'adjustable': True,
515                    }
516                value = tv.tv_sec + tv.tv_usec * 1e-6
517                return (value, info)
518        # FIXME: implement ftime()
519        if has_ftime:
520            if use_info:
521                info = {
522                    "implementation": "ftime()",
523                    "resolution": 1e-3,
524                    'monotonic': False,
525                    'adjustable': True,
526                }
527            value = ftime()
528        elif has_libc_time:
529            if use_info:
530                info = {
531                    "implementation": "time()",
532                    "resolution": 1.0,
533                    'monotonic': False,
534                    'adjustable': True,
535                }
536            value = float(_libc_time())
537        else:
538            if use_info:
539                info = {
540                    "implementation": "time.time()",
541                    'monotonic': False,
542                    'adjustable': True,
543                }
544                if os.name == "nt":
545                    # On Windows, time.time() uses ftime()
546                    info["resolution"] = 1e-3
547                else:
548                    # guess that time.time() uses gettimeofday()
549                    info["resolution"] = 1e-6
550            value = python_time.time()
551        return (value, info)
552
553def time():
554    return _time(False)[0]
555
556
557try:
558    import select
559except ImportError:
560    has_select = False
561else:
562    # FIXME: On Windows, select.select([], [], [], seconds) fails with
563    #        select.error(10093)
564    has_select = (hasattr(select, "select") and os.name != "nt")
565
566if has_select:
567    def _sleep(seconds):
568        return select.select([], [], [], seconds)
569
570elif has_delay:
571    def _sleep(seconds):
572        milliseconds = int(seconds * 1000)
573        # FIXME
574        delay(milliseconds)
575
576#elif os.name == "nt":
577#    def _sleep(seconds):
578#        milliseconds = int(seconds * 1000)
579#        # FIXME: use ctypes
580#        win32api.ResetEvent(hInterruptEvent);
581#        win32api.WaitForSingleObject(sleep.sigint_event, milliseconds)
582#
583#    sleep.sigint_event = win32api.CreateEvent(NULL, TRUE, FALSE, FALSE)
584#    # SetEvent(sleep.sigint_event) will be called by the signal handler of SIGINT
585
586elif os.name == "os2":
587    def _sleep(seconds):
588        milliseconds = int(seconds * 1000)
589        # FIXME
590        DosSleep(milliseconds)
591
592elif has_libc_sleep:
593    def _sleep(seconds):
594        seconds = int(seconds)
595        _libc_sleep(seconds)
596
597else:
598    def _sleep(seconds):
599        python_time.sleep(seconds)
600
601def sleep(seconds):
602    if seconds < 0:
603        raise ValueError("sleep length must be non-negative")
604    _sleep(seconds)
605
606def _libc_clock_info(use_info):
607    if use_info:
608        info = {
609            'implementation': 'clock()',
610            'resolution': 1.0,
611            # FIXME: 'resolution': 1.0 / CLOCKS_PER_SEC,
612            'monotonic': True,
613            'adjustable': False,
614        }
615        if os.name != "nt":
616            info['monotonic'] = True
617    else:
618        info = None
619    if has_libc_clock:
620        value = _libc_clock()
621        if use_info:
622            info['implementation'] = 'clock()'
623    else:
624        value = python_time.clock()
625        if use_info:
626            info['implementation'] = 'time.clock()'
627    return (value, info)
628
629def _win_perf_counter(use_info):
630    if _win_perf_counter.perf_frequency is None:
631        try:
632            _win_perf_counter.perf_frequency = float(QueryPerformanceFrequency())
633        except WindowsError:
634            # QueryPerformanceFrequency() fails if the installed
635            # hardware does not support a high-resolution performance
636            # counter
637            return (None, None)
638
639    value = QueryPerformanceCounter() / _win_perf_counter.perf_frequency
640    if use_info:
641        info = {
642            'implementation': 'QueryPerformanceCounter',
643            'resolution': 1.0 / _win_perf_counter.perf_frequency,
644            'monotonic': True,
645            'adjustable': False,
646        }
647    else:
648        info = None
649    return (value, info)
650_win_perf_counter.perf_frequency = None
651
652if os.name == 'nt':
653    def _clock(use_info):
654        info = None
655        if _clock.use_performance_counter:
656            value, info = _win_perf_counter(use_info)
657            if value is not None:
658                return (value, info)
659        return _libc_clock_info(use_info)
660    _clock.use_performance_counter = True
661else:
662    def _clock(use_info):
663        return _libc_clock_info(use_info)
664
665def clock():
666    return _clock(False)[0]
667
668
669class clock_info(object):
670    def __init__(self, implementation, monotonic, adjustable, resolution):
671        self.implementation = implementation
672        self.monotonic = monotonic
673        self.adjustable = adjustable
674        self.resolution = resolution
675
676    def __repr__(self):
677        return (
678            'clockinfo(adjustable=%s, implementation=%r, monotonic=%s, resolution=%s'
679            % (self.adjustable, self.implementation, self.monotonic, self.resolution))
680
681def get_clock_info(name):
682    if name == 'clock':
683        info = _clock(True)[1]
684    elif name == 'perf_counter':
685        info = _perf_counter(True)[1]
686    elif name == 'process_time':
687        info = _process_time(True)[1]
688    elif name == 'time':
689        info = _time(True)[1]
690    elif has_monotonic and name == 'monotonic':
691        info = _monotonic(True)[1]
692    else:
693        raise ValueError("unknown clock: %s" % name)
694    return clock_info(**info)
695
696if __name__ == "__main__":
697    import threading
698    import unittest
699    from errno import EPERM
700
701    class TestPEP418(unittest.TestCase):
702        if not hasattr(unittest.TestCase, 'assertIsInstance'):
703            # Python < 2.7 or Python < 3.2
704            def assertIsInstance(self, obj, klass):
705                self.assertTrue(isinstance(obj, klass))
706            def assertGreater(self, a, b):
707                self.assertTrue(a > b)
708            def assertLess(self, a, b):
709                self.assertTrue(a < b)
710            def assertLessEqual(self, a, b):
711                self.assertTrue(a <= b)
712            def assertAlmostEqual(self, first, second, delta):
713                self.assertTrue(abs(first - second) <= delta)
714
715        def test_clock(self):
716            clock()
717
718            info = get_clock_info('clock')
719            self.assertEqual(info.monotonic, True)
720            self.assertEqual(info.adjustable, False)
721
722        def test_get_clock_info(self):
723            clocks = ['clock', 'perf_counter', 'process_time', 'time']
724            if has_monotonic:
725                clocks.append('monotonic')
726
727            for name in clocks:
728                info = get_clock_info(name)
729                self.assertIsInstance(info.implementation, str)
730                self.assertNotEqual(info.implementation, '')
731                self.assertIsInstance(info.monotonic, bool)
732                self.assertIsInstance(info.resolution, float)
733                # 0 < resolution <= 1.0
734                self.assertGreater(info.resolution, 0)
735                self.assertLessEqual(info.resolution, 1)
736                self.assertIsInstance(info.adjustable, bool)
737
738            self.assertRaises(ValueError, get_clock_info, 'xxx')
739
740        if not has_monotonic:
741            print("Skip test_monotonic: need time.monotonic")
742        else:
743            def test_monotonic(self):
744                t1 = monotonic()
745                python_time.sleep(0.1)
746                t2 = monotonic()
747                dt = t2 - t1
748                self.assertGreater(t2, t1)
749                self.assertAlmostEqual(dt, 0.1, delta=0.2)
750
751                info = get_clock_info('monotonic')
752                self.assertEqual(info.monotonic, True)
753                self.assertEqual(info.adjustable, False)
754
755        if not has_monotonic or not has_clock_gettime:
756            if not has_monotonic:
757                print('Skip test_monotonic_settime: need time.monotonic')
758            elif not has_clock_gettime:
759                print('Skip test_monotonic_settime: need time.clock_settime')
760        else:
761            def test_monotonic_settime(self):
762                t1 = monotonic()
763                realtime = clock_gettime(CLOCK_REALTIME)
764                # jump backward with an offset of 1 hour
765                try:
766                    clock_settime(CLOCK_REALTIME, realtime - 3600)
767                except OSError as err:
768                    if err.errno == EPERM:
769                        if hasattr(unittest, 'SkipTest'):
770                            raise unittest.SkipTest(str(err))
771                        else:
772                            print("Skip test_monotonic_settime: %s" % err)
773                            return
774                    else:
775                        raise
776                t2 = monotonic()
777                clock_settime(CLOCK_REALTIME, realtime)
778                # monotonic must not be affected by system clock updates
779                self.assertGreaterEqual(t2, t1)
780
781        def test_perf_counter(self):
782            perf_counter()
783
784        def test_process_time(self):
785            start = process_time()
786            python_time.sleep(0.1)
787            stop = process_time()
788            self.assertLess(stop - start, 0.01)
789
790            info = get_clock_info('process_time')
791            self.assertEqual(info.monotonic, True)
792            self.assertEqual(info.adjustable, False)
793
794
795        def test_process_time_threads(self):
796            class BusyThread(threading.Thread):
797                def run(self):
798                    while not self.stop:
799                        pass
800
801            thread = BusyThread()
802            thread.stop = False
803            t1 = process_time()
804            thread.start()
805            sleep(0.2)
806            t2 = process_time()
807            thread.stop = True
808            thread.join()
809            self.assertGreater(t2 - t1, 0.1)
810
811        def test_sleep(self):
812            self.assertRaises(ValueError, sleep, -2)
813            self.assertRaises(ValueError, sleep, -1)
814            sleep(1.2)
815
816        def test_time(self):
817            value = time()
818            self.assertIsInstance(value, float)
819
820            info = get_clock_info('time')
821            self.assertEqual(info.monotonic, False)
822            self.assertEqual(info.adjustable, True)
823
824
825    if True:
826        from pprint import pprint
827
828        print("clock: %s" % clock())
829        if has_monotonic:
830            print("monotonic: %s" % monotonic())
831        else:
832            print("monotonic: <not available>")
833        print("perf_counter: %s" % perf_counter())
834        print("process_time: %s" % process_time())
835        print("time: %s" % time())
836
837        clocks = ['clock', 'perf_counter', 'process_time', 'time']
838        if has_monotonic:
839            clocks.append('monotonic')
840        pprint(dict((name, get_clock_info(name)) for name in clocks))
841
842    unittest.main()
843