diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000000000000000000000000000000000..f98ec8829af7ee3b469d2109d0eb74672b3a308c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +.git/ +.idea/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 89399752c872dbca97a4d9a1d9067aea6bf41d30..a18ea39ceb967ee1d445987e62be6611c9c421b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,3 +5,5 @@ within given timeout * added power support to `AtomicNumber` * slight refactor in `AtomicNumber` +* removed `end_on_keyboard_interrupt` from `hang_until_sig` +* unit test for `hang_until_sig` at last diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..6f77129e3980d42184bf5c5d633678c362f85d11 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM python:3.8 + +ADD requirements.txt /app/requirements.txt +RUN pip install -r /app/requirements.txt +RUN pip install nose2 mock coverage + +ADD satella /app/satella +ADD tests /app/tests + +WORKDIR /app + +CMD ["coverage", "run", "-m", "nose2", "-vv"] diff --git a/README.md b/README.md index b534f2e4153e5ef6f6090d03a570368a107168f3..563bff7852033c940d23d8ab505db30cd00877c8 100644 --- a/README.md +++ b/README.md @@ -34,3 +34,9 @@ is available for the brave souls that do decide to use this library. See [LICENSE](LICENSE) for text of the license. This library may contain code taken from elsewhere on the internets, so this is copyright (c) respective authors. + +# Running unit tests + +Just build and run the attached +[Dockerfile](Dockerfile). +These tests run on Python 3.8 \ No newline at end of file diff --git a/docs/time.rst b/docs/time.rst index 16edecc7a1a9d466b7bc1b00f2c9f4de04635979..1b32ef73e8d04937c761384541a4f0f11803855a 100644 --- a/docs/time.rst +++ b/docs/time.rst @@ -30,3 +30,8 @@ time_us Syntactic sugar for `int(time.time()*1000000)` .. autofunction:: satella.time.time_us + +sleep +----- + +.. autofunction:: satella.time.sleep diff --git a/satella/__init__.py b/satella/__init__.py index b883a199d45fc6a298fbeeb44cbf2d068841424f..cb9f77006b0a7d9ae3005053701bb2c0a7a2a2d2 100644 --- a/satella/__init__.py +++ b/satella/__init__.py @@ -1 +1 @@ -__version__ = '2.8.13_a6' +__version__ = '2.8.13_a7' diff --git a/satella/os/signals.py b/satella/os/signals.py index d0f5b628f87f6c7f67e31be86e56f60f97cba85c..1d216a7fc9377e0ff92f1272bd04a11c1067061b 100644 --- a/satella/os/signals.py +++ b/satella/os/signals.py @@ -1,29 +1,29 @@ import signal import time +import logging import typing as tp +logger = logging.getLogger(__name__) end = False def __sighandler(a, b): + logger.warning('Handling a signal') global end end = True -def hang_until_sig(extra_signals: tp.Optional[tp.List[int]] = None, - end_on_keyboard_interrupt: bool = True) -> None: +def hang_until_sig(extra_signals: tp.Optional[tp.List[int]] = None) -> None: """ Will hang until this process receives SIGTERM or SIGINT. If you pass extra signal IDs (signal.SIG*) with extra_signals, then also on those signals this call will release. :param extra_signals: a list of extra signals to listen to - :param end_on_keyboard_interrupt: whether to consider receiving a KeyboardInterrupt as - a signal to finish """ global end - extra_signals = extra_signals or [] + extra_signals = extra_signals or () signal.signal(signal.SIGTERM, __sighandler) signal.signal(signal.SIGINT, __sighandler) @@ -31,10 +31,6 @@ def hang_until_sig(extra_signals: tp.Optional[tp.List[int]] = None, signal.signal(s, __sighandler) while not end: - try: - time.sleep(0.5) - except KeyboardInterrupt: - if end_on_keyboard_interrupt: - end = True + time.sleep(0.5) end = False # reset for next use diff --git a/satella/time.py b/satella/time.py index 4469d562d183d757bbd68da3a232e4f3b447877b..29fa754d4df704f3c8a53c597cfe0f810578c322 100644 --- a/satella/time.py +++ b/satella/time.py @@ -3,9 +3,35 @@ import inspect import time import typing as tp from concurrent.futures import Future -from functools import wraps +from satella.coding.decorators import wraps -__all__ = ['measure', 'time_as_int', 'time_ms'] +__all__ = ['measure', 'time_as_int', 'time_ms', 'sleep'] + + +def sleep(x: float, abort_on_interrupt: bool = False) -> bool: + """ + Sleep for given interval. + + This won't be interrupted by KeyboardInterrupted, and always will sleep for given time interval. + This will return at once if x is negative + + :param x: the interval to wait + :param abort_on_interrupt: whether to abort at once when KeyboardInterrupt is seen + :returns: whether the function has completed its sleep naturally. False is seen on + aborts thanks to KeyboardInterrupt only if abort_on_interrupt is True + """ + if x < 0: + return + + with measure() as measurement: + while measurement() < x: + try: + time.sleep(x - measurement()) + except KeyboardInterrupt: + if abort_on_interrupt: + return False + pass + return True def time_as_int() -> int: diff --git a/tests/test_os/test_hang_until_sig.py b/tests/test_os/test_hang_until_sig.py index e5162f197cf6282b72bbc8ec32a36599c477fb37..5710cfa3d098b76b74f8f71d8e6955b1c7c9b066 100644 --- a/tests/test_os/test_hang_until_sig.py +++ b/tests/test_os/test_hang_until_sig.py @@ -3,8 +3,8 @@ import signal import sys import threading import unittest +import time -from satella import time from satella.os import hang_until_sig @@ -12,10 +12,10 @@ class TestHangUntilSig(unittest.TestCase): @unittest.skipIf('win' in sys.platform, 'Cannot test on Windows') def test_hang_until_sig(self): + def send_sig(): time.sleep(1) - os.kill(os.getpid(), signal.SIGINT) + os.kill(0, signal.SIGTERM) threading.Thread(target=send_sig).start() hang_until_sig() - self.assertEqual(True, False)