X-Git-Url: https://wannabe.guru.org/gitweb/?a=blobdiff_plain;f=thread_utils.py;h=c4a293794a99cb1f479da105a499cb2ce93b564e;hb=02302bbd9363facb59c4df2c1f4013087702cfa6;hp=22161275605d76a1199df8f18d536fd04e2fe17b;hpb=a4bf4d05230474ad14243d67ac7f8c938f670e58;p=python_utils.git diff --git a/thread_utils.py b/thread_utils.py index 2216127..c4a2937 100644 --- a/thread_utils.py +++ b/thread_utils.py @@ -1,10 +1,14 @@ #!/usr/bin/env python3 +# © Copyright 2021-2022, Scott Gasch + +"""Utilities for dealing with threads + threading.""" + import functools import logging import os import threading -from typing import Callable, Optional, Tuple +from typing import Any, Callable, Optional, Tuple # This module is commonly used by others in here and should avoid # taking any unnecessary dependencies back on them. @@ -13,10 +17,12 @@ logger = logging.getLogger(__name__) def current_thread_id() -> str: - """Returns a string composed of the parent process' id, the current - process' id and the current thread identifier. The former two are - numbers (pids) whereas the latter is a thread id passed during thread - creation time. + """ + Returns: + a string composed of the parent process' id, the current + process' id and the current thread identifier. The former two are + numbers (pids) whereas the latter is a thread id passed during thread + creation time. >>> ret = current_thread_id() >>> (ppid, pid, tid) = ret.split('/') @@ -33,8 +39,10 @@ def current_thread_id() -> str: def is_current_thread_main_thread() -> bool: - """Returns True is the current (calling) thread is the process' main - thread and False otherwise. + """ + Returns: + True is the current (calling) thread is the process' main + thread and False otherwise. >>> is_current_thread_main_thread() True @@ -60,15 +68,11 @@ def is_current_thread_main_thread() -> bool: def background_thread( - _funct: Optional[Callable], + _funct: Optional[Callable[..., Any]], ) -> Callable[..., Tuple[threading.Thread, threading.Event]]: """A function decorator to create a background thread. - *** Please note: the decorated function must take an shutdown *** - *** event as an input parameter and should periodically check *** - *** it and stop if the event is set. *** - - Usage: + Usage:: @background_thread def random(a: int, b: str, stop_event: threading.Event) -> None: @@ -78,7 +82,6 @@ def background_thread( if stop_event.is_set(): return - def main() -> None: (thread, event) = random(22, "dude") print("back!") @@ -86,10 +89,12 @@ def background_thread( event.set() thread.join() - Note: in addition to any other arguments the function has, it must - take a stop_event as the last unnamed argument which it should - periodically check. If the event is set, it means the thread has - been requested to terminate ASAP. + .. warning:: + + In addition to any other arguments the function has, it must + take a stop_event as the last unnamed argument which it should + periodically check. If the event is set, it means the thread has + been requested to terminate ASAP. """ def wrapper(funct: Callable): @@ -104,7 +109,7 @@ def background_thread( kwargs=kwa, ) thread.start() - logger.debug(f'Started thread {thread.name} tid={thread.ident}') + logger.debug('Started thread "%s" tid=%d', thread.name, thread.ident) return (thread, should_terminate) return inner_wrapper @@ -120,24 +125,31 @@ def periodically_invoke( stop_after: Optional[int], ): """ - Periodically invoke a decorated function. Stop after N invocations - (or, if stop_after is None, call forever). Delay period_sec between - invocations. + Periodically invoke the decorated function. + + Args: + period_sec: the delay period in seconds between invocations + stop_after: total number of invocations to make or, if None, + call forever + + Returns: + a :class:Thread object and an :class:Event that, when + signaled, will stop the invocations. - Returns a Thread object and an Event that, when signaled, will stop - the invocations. Note that it is possible to be invoked one time - after the Event is set. This event can be used to stop infinite - invocation style or finite invocation style decorations. + .. note:: + It is possible to be invoked one time after the :class:Event + is set. This event can be used to stop infinite + invocation style or finite invocation style decorations. + + Usage:: @periodically_invoke(period_sec=0.5, stop_after=None) def there(name: str, age: int) -> None: print(f" ...there {name}, {age}") - @periodically_invoke(period_sec=1.0, stop_after=3) def hello(name: str) -> None: print(f"Hello, {name}") - """ def decorator_repeat(func): @@ -163,7 +175,7 @@ def periodically_invoke( newargs = (should_terminate, *args) thread = threading.Thread(target=helper_thread, args=newargs, kwargs=kwargs) thread.start() - logger.debug(f'Started thread {thread.name} tid={thread.ident}') + logger.debug('Started thread "%s" tid=%d', thread.name, thread.ident) return (thread, should_terminate) return wrapper_repeat