Changeset 265942 in webkit


Ignore:
Timestamp:
Aug 20, 2020, 7:33:58 AM (4 years ago)
Author:
Jonathan Bedard
Message:

[webkitcorepy] Move Timeout to webkitcorepy (Part 1)
https://bugs.webkit.org/show_bug.cgi?id=215584
<rdar://problem/67270713>

Reviewed by Darin Adler and Dewei Zhu.

The Timeout class is a generally useful Python utility, it should not live inside webkitpy.

  • Scripts/libraries/webkitcorepy/README.md: Document Timeout object.
  • Scripts/libraries/webkitcorepy/webkitcorepy/init.py: Expose Timeout object API, increment version.
  • Scripts/libraries/webkitcorepy/webkitcorepy/mocks/time_.py:

(_MetaTime.enter): Replace timeout.py's ORIGINAL_SLEEP variable to speed up testing.

  • Scripts/libraries/webkitcorepy/webkitcorepy/tests/timeout_unittest.py: Added.

(TimeoutTests): Added.

  • Scripts/libraries/webkitcorepy/webkitcorepy/timeout.py: Added.

(Timeout): Class representing a stackable Timeout context.
(Timeout.Data): Class containing information about a specific deadline.
(Timeout.Exception): Exception belonging to the Timeout object.
(Timeout.DisableAlarm): For some block of code, disable Timeout alarms. This is useful because many APIs (such as
subprocess.run) define their own timeouts which do better cleanup than an interrupt would.
(Timeout.default_handler): Default Timeout handler which raises an exception.
(Timeout.current): Return the current Timeout.Data class, if one exists.
(Timeout.deadline): Return the closest deadline, if one exists.
(Timeout.difference): Return the number of seconds between the current time and the closest deadline.
(Timeout.check): Check if we have surpassed the closest deadline., raise an exception if we have.
(Timeout.bind): Create a signal based on the closest deadline.
(Timeout.sleep): Override the sleep function because if we ever request a sleep that is greater than the closest deadline,
we should imiediately trigger the timeout logic without waiting for the timeout to actually expire.
(Timeout.init): Create a Timeout context, 1 second by default.
(Timeout.enter):
(Timeout.exit):

  • Scripts/webkitpy/common/timeout_context.py:

(Timeout): Moved to webkitcorepy.

Location:
trunk/Tools
Files:
2 added
1 deleted
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/Tools/ChangeLog

    r265916 r265942  
     12020-08-20  Jonathan Bedard  <jbedard@apple.com>
     2
     3        [webkitcorepy] Move Timeout to webkitcorepy (Part 1)
     4        https://bugs.webkit.org/show_bug.cgi?id=215584
     5        <rdar://problem/67270713>
     6
     7        Reviewed by Darin Adler and Dewei Zhu.
     8
     9        The Timeout class is a generally useful Python utility, it should not live inside webkitpy.
     10
     11        * Scripts/libraries/webkitcorepy/README.md: Document Timeout object.
     12        * Scripts/libraries/webkitcorepy/webkitcorepy/__init__.py: Expose Timeout object API, increment version.
     13        * Scripts/libraries/webkitcorepy/webkitcorepy/mocks/time_.py:
     14        (_MetaTime.__enter__): Replace timeout.py's ORIGINAL_SLEEP variable to speed up testing.
     15        * Scripts/libraries/webkitcorepy/webkitcorepy/tests/timeout_unittest.py: Added.
     16        (TimeoutTests): Added.
     17        * Scripts/libraries/webkitcorepy/webkitcorepy/timeout.py: Added.
     18        (Timeout): Class representing a stackable Timeout context.
     19        (Timeout.Data): Class containing information about a specific deadline.
     20        (Timeout.Exception): Exception belonging to the Timeout object.
     21        (Timeout.DisableAlarm): For some block of code, disable Timeout alarms. This is useful because many APIs (such as
     22        subprocess.run) define their own timeouts which do better cleanup than an interrupt would.
     23        (Timeout.default_handler): Default Timeout handler which raises an exception.
     24        (Timeout.current): Return the current Timeout.Data class, if one exists.
     25        (Timeout.deadline): Return the closest deadline, if one exists.
     26        (Timeout.difference): Return the number of seconds between the current time and the closest deadline.
     27        (Timeout.check): Check if we have surpassed the closest deadline., raise an exception if we have.
     28        (Timeout.bind): Create a signal based on the closest deadline.
     29        (Timeout.sleep): Override the sleep function because if we ever request a sleep that is greater than the closest deadline,
     30        we should imiediately trigger the timeout logic without waiting for the timeout to actually expire.
     31        (Timeout.__init__): Create a Timeout context, 1 second by default.
     32        (Timeout.__enter__):
     33        (Timeout.__exit__):
     34        * Scripts/webkitpy/common/timeout_context.py:
     35        (Timeout): Moved to webkitcorepy.
     36
    1372020-08-19  Kate Cheney  <katherine_cheney@apple.com>
    238
  • trunk/Tools/Scripts/libraries/webkitcorepy/README.md

    r265769 r265942  
    5858assert capturer.stdout.getvalue() == 'data\n'
    5959```
     60
     61Timeout context:
     62```
     63import time
     64
     65from webkitcorepy import Timeout
     66
     67with Timeout(5, handler=RuntimeError('Exceeded 5 second timeout')):
     68    time.sleep(4)
     69```
  • trunk/Tools/Scripts/libraries/webkitcorepy/webkitcorepy/__init__.py

    r265885 r265942  
    3333from webkitcorepy.string_utils import BytesIO, StringIO, UnicodeIO, unicode
    3434from webkitcorepy.output_capture import LoggerCapture, OutputCapture, OutputDuplicate
     35from webkitcorepy.timeout import Timeout
    3536
    36 version = Version(0, 2, 9)
     37version = Version(0, 2, 10)
    3738
    3839from webkitcorepy.autoinstall import Package, AutoInstall
  • trunk/Tools/Scripts/libraries/webkitcorepy/webkitcorepy/mocks/time_.py

    r265386 r265942  
    5555            patch('time.sleep', new=sleep_func),
    5656            patch('datetime.datetime', new=FakeDateTime),
     57            patch('webkitcorepy.timeout.ORIGINAL_SLEEP', new=sleep_func),
    5758        ])
    5859
  • trunk/Tools/Scripts/webkitpy/common/timeout_context.py

    r251955 r265942  
    1 # Copyright (C) 2017 Apple Inc. All rights reserved.
     1# Copyright (C) 2020 Apple Inc. All rights reserved.
    22#
    33# Redistribution and use in source and binary forms, with or without
     
    2121# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    2222
    23 import logging
    24 import math
    25 import os
    26 import signal
    27 import time
    28 import threading
    29 
    30 _log = logging.getLogger(__name__)
    31 
    32 
    33 class Timeout(object):
    34 
    35     thread_exception = RuntimeError('Timeout originates from a different thread')
    36     _process_to_timeout_map = {}
    37 
    38     class TimeoutData(object):
    39 
    40         def __init__(self, alarm_time, handler):
    41             self.alarm_time = alarm_time
    42             self.handler = handler
    43             self.thread_id = threading.current_thread().ident
    44 
    45     @staticmethod
    46     def default_handler(signum, frame):
    47         raise RuntimeError('Timeout alarm was triggered')
    48 
    49     @staticmethod
    50     def current():
    51         result = Timeout._process_to_timeout_map.get(os.getpid(), [])
    52         if not result:
    53             return None
    54         if result[0].thread_id != threading.current_thread().ident:
    55             _log.critical('Using both alarms and threading in the same process, this is unsupported')
    56             raise Timeout.thread_exception
    57         return result[0]
    58 
    59     def __init__(self, seconds=1, handler=None):
    60         if seconds == 0:
    61             raise RuntimeError('Cannot have a timeout of 0 seconds')
    62 
    63         if isinstance(handler, BaseException):
    64             exception = handler
    65 
    66             def exception_handler(signum, frame):
    67                 raise exception
    68 
    69             handler = exception_handler
    70 
    71         self._timeout = seconds
    72         self._handler = handler if handler else Timeout.default_handler
    73         self.data = None
    74 
    75     @staticmethod
    76     def _bind_timeout_data_to_alarm(data):
    77         def handler(signum, frame):
    78             assert signum == signal.SIGALRM
    79             if data.thread_id != threading.current_thread().ident:
    80                 raise Timeout.thread_exception
    81             data.handler(signum, frame)
    82 
    83         current_time = time.time()
    84         if data.alarm_time <= current_time:
    85             handler(signal.SIGALRM, None)
    86 
    87         signal.signal(signal.SIGALRM, handler)
    88         signal.alarm(int(math.ceil(data.alarm_time - current_time)))
    89 
    90     def __enter__(self):
    91         signal.alarm(0)  # Imiediatly disable the alarm so we aren't interupted.
    92         self.data = Timeout.TimeoutData(time.time() + self._timeout, self._handler)
    93         current_timeout = Timeout.current()
    94 
    95         # Another timeout is more urgent.
    96         if current_timeout and current_timeout.alarm_time < self.data.alarm_time:
    97             for i in range(len(Timeout._process_to_timeout_map[os.getpid()]) - 1):
    98                 if self.data.alarm_time < Timeout._process_to_timeout_map[os.getpid()][i + 1].alarm_time:
    99                     Timeout._process_to_timeout_map[os.getpid()].insert(i, self.data)
    100                     break
    101             Timeout._process_to_timeout_map[os.getpid()].append(self.data)
    102 
    103         # This is the most urgent timeout
    104         else:
    105             Timeout._process_to_timeout_map[os.getpid()] = [self.data] + Timeout._process_to_timeout_map.get(os.getpid(), [])
    106 
    107         Timeout._bind_timeout_data_to_alarm(Timeout.current())
    108         return self
    109 
    110     def __exit__(self, exc_type, exc_value, traceback):
    111         signal.alarm(0)  # Imiediatly disable the alarm so we aren't interupted.
    112 
    113         if not Timeout._process_to_timeout_map[os.getpid()]:
    114             raise RuntimeError('No timeout registered')
    115         Timeout._process_to_timeout_map[os.getpid()].remove(self.data)
    116         self.data = None
    117 
    118         if Timeout._process_to_timeout_map[os.getpid()]:
    119             Timeout._bind_timeout_data_to_alarm(Timeout.current())
     23from webkitcorepy import Timeout
Note: See TracChangeset for help on using the changeset viewer.