From 39156d689cc3ddec96a93b3eda4e3763ac354aff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=91=D0=BB?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Tue, 31 Mar 2026 01:21:17 +0300 Subject: [PATCH 01/12] Add comprehensive docstrings for token cancellation methods --- cantok/tokens/abstract/abstract_token.py | 75 ++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/cantok/tokens/abstract/abstract_token.py b/cantok/tokens/abstract/abstract_token.py index 1b5d502..d0ab7c6 100644 --- a/cantok/tokens/abstract/abstract_token.py +++ b/cantok/tokens/abstract/abstract_token.py @@ -112,6 +112,20 @@ def __bool__(self) -> bool: @property def cancelled(self) -> bool: + """ + Whether the token is currently cancelled. + + Evaluated dynamically on each access, taking into account the token's own + cancellation rules and all embedded tokens. Setting to True cancels the token; + setting to False on an already cancelled token raises ValueError. + + >>> token = SimpleToken() + >>> token.cancelled + False + >>> token.cancel() + >>> token.cancelled + True + """ return self.is_cancelled() @cancelled.setter @@ -123,12 +137,50 @@ def cancelled(self, new_value: bool) -> None: raise ValueError('You cannot restore a cancelled token.') def keep_on(self) -> bool: + """ + Returns True if the token is not cancelled, False otherwise. + The opposite of is_cancelled(). + + >>> token = SimpleToken() + >>> token.keep_on() + True + >>> token.cancel() + >>> token.keep_on() + False + """ return not self.is_cancelled() def is_cancelled(self, direct: bool = True) -> bool: + """ + Returns True if the token is cancelled, False otherwise. + + :param direct: When False, tokens with rollback behaviour (e.g. CounterToken + with direct=True) do not apply their side effects while being + polled indirectly through a parent token. Defaults to True. + + >>> token = SimpleToken() + >>> token.is_cancelled() + False + >>> token.cancel() + >>> token.is_cancelled() + True + """ return self._get_report(direct=direct).cause != CancelCause.NOT_CANCELLED def wait(self, step: Union[int, float] = 0.0001, timeout: Optional[Union[int, float]] = None) -> Awaitable: # type: ignore[type-arg] + """ + Waits until the token is cancelled. + + When used with ``await``, runs non-blocking inside an asyncio event loop. + When called without ``await``, blocks the current thread. + + :param step: Interval between status checks, in seconds. Defaults to 0.0001. + :param timeout: Maximum time to wait, in seconds. If exceeded, + raises TimeoutCancellationError. Defaults to None (no limit). + + >>> token = TimeoutToken(5) + >>> token.wait() # blocks for ~5 seconds, then returns + """ if step < 0: raise ValueError('The token polling iteration time cannot be less than zero.') if timeout is not None and timeout < 0: @@ -146,10 +198,33 @@ def wait(self, step: Union[int, float] = 0.0001, timeout: Optional[Union[int, fl return WaitCoroutineWrapper(step, self + token, token) def cancel(self) -> 'AbstractToken': + """ + Cancels the token. Returns the token itself to allow method chaining. + + Cancellation is irreversible: once cancelled, the token cannot be restored. + + >>> token = SimpleToken() + >>> token.cancel() + >>> token.cancelled + True + """ self._cancelled = True return self def check(self) -> None: + """ + Raises an exception if the token is cancelled; does nothing otherwise. + + The exception type depends on the cancellation cause: + - Manual cancellation via cancel() raises CancellationError. + - Automatic cancellation by a specific token type raises the corresponding + subclass (e.g. TimeoutCancellationError for TimeoutToken). + + >>> token = SimpleToken() + >>> token.check() # nothing happens + >>> token.cancel() + >>> token.check() # raises CancellationError + """ with self._lock: report = self._get_report() From 5e2637dd35379f72d45cf819bf69550d31299f55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=91=D0=BB?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Tue, 31 Mar 2026 01:26:16 +0300 Subject: [PATCH 02/12] Bump version to 0.0.35 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index b84d0fe..b7276f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "cantok" -version = "0.0.34" +version = "0.0.35" authors = [{ name = "Evgeniy Blinov", email = "zheni-b@yandex.ru" }] description = 'Implementation of the "Cancellation Token" pattern' readme = "README.md" From 96eb60eafa82e9f86f8af02634dbdd2fe9a46a3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=91=D0=BB?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Tue, 31 Mar 2026 01:27:15 +0300 Subject: [PATCH 03/12] Refactor ConditionToken to use _run_function instead of run_function --- cantok/tokens/condition_token.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cantok/tokens/condition_token.py b/cantok/tokens/condition_token.py index f7c9e24..bcd749d 100644 --- a/cantok/tokens/condition_token.py +++ b/cantok/tokens/condition_token.py @@ -25,7 +25,7 @@ def _superpower(self) -> bool: if not self._suppress_exceptions: self._before() - result = self.run_function() + result = self._run_function() self._after() return result @@ -34,13 +34,13 @@ def _superpower(self) -> bool: with suppress(Exception): self._before() with suppress(Exception): - result = self.run_function() + result = self._run_function() with suppress(Exception): self._after() return result - def run_function(self) -> bool: + def _run_function(self) -> bool: result = self._function() if not isinstance(result, bool): From de6d8e0d107c4b4d791cfd81fa56fe79ae9dec4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=91=D0=BB?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Tue, 31 Mar 2026 01:30:40 +0300 Subject: [PATCH 04/12] Add CLAUDE.md to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a1c96fb..825653f 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ test.py html .qwen .claude +CLAUDE.md From 17f08fc149c0989544f476970470ccbccd4da889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=91=D0=BB?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Tue, 31 Mar 2026 01:48:08 +0300 Subject: [PATCH 05/12] Update TimeoutToken.wait docstring to include asyncio usage example --- cantok/tokens/abstract/abstract_token.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cantok/tokens/abstract/abstract_token.py b/cantok/tokens/abstract/abstract_token.py index d0ab7c6..f35de9f 100644 --- a/cantok/tokens/abstract/abstract_token.py +++ b/cantok/tokens/abstract/abstract_token.py @@ -178,8 +178,11 @@ def wait(self, step: Union[int, float] = 0.0001, timeout: Optional[Union[int, fl :param timeout: Maximum time to wait, in seconds. If exceeded, raises TimeoutCancellationError. Defaults to None (no limit). + >>> import asyncio + >>> >>> token = TimeoutToken(5) >>> token.wait() # blocks for ~5 seconds, then returns + >>> asyncio.run(token.wait()) # non-blocking, inside an asyncio event loop """ if step < 0: raise ValueError('The token polling iteration time cannot be less than zero.') From b2f6e7ce025a0d604d0ddaab1e5512a2ddd95b8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=91=D0=BB?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Tue, 31 Mar 2026 01:50:39 +0300 Subject: [PATCH 06/12] Add docstrings to all token classes --- cantok/tokens/abstract/abstract_token.py | 28 ++++++++++++++++++++++++ cantok/tokens/condition_token.py | 25 +++++++++++++++++++++ cantok/tokens/counter_token.py | 17 ++++++++++++++ cantok/tokens/default_token.py | 15 +++++++++++++ cantok/tokens/simple_token.py | 12 ++++++++++ cantok/tokens/timeout_token.py | 21 ++++++++++++++++++ 6 files changed, 118 insertions(+) diff --git a/cantok/tokens/abstract/abstract_token.py b/cantok/tokens/abstract/abstract_token.py index f35de9f..5a77aee 100644 --- a/cantok/tokens/abstract/abstract_token.py +++ b/cantok/tokens/abstract/abstract_token.py @@ -11,6 +11,34 @@ class AbstractToken(ABC): + """ + Abstract base class for all cancellation tokens. + + A cancellation token represents a signal that can be used to cooperatively + cancel a long-running operation. Most subclasses add an automatic cancellation + condition (superpower) evaluated on every check; SimpleToken and DefaultToken + rely on manual cancellation only. + + Tokens can be composed with the + operator: the resulting token is cancelled + when any of the combined tokens is cancelled. + + Use AbstractToken as a type hint when a function accepts any token type. + Pass DefaultToken() as the default to make the token optional: + + >>> def run(token: AbstractToken = DefaultToken()) -> bool: + ... return token.keep_on() + >>> run() # DefaultToken never cancels + True + >>> run(SimpleToken().cancel()) # cancelled token passed explicitly + False + + The idiomatic loop pattern: + + >>> token = SimpleToken() + >>> while token: + ... ... # loop exits when token is cancelled + """ + exception = CancellationError _rollback_if_nondirect_polling = False diff --git a/cantok/tokens/condition_token.py b/cantok/tokens/condition_token.py index bcd749d..c67c134 100644 --- a/cantok/tokens/condition_token.py +++ b/cantok/tokens/condition_token.py @@ -6,6 +6,31 @@ class ConditionToken(AbstractToken): + """ + A token that cancels automatically when a condition function returns True. + + The condition function is evaluated on every cancellation check. Once it + returns True, the result is cached by default and the token stays cancelled. + + :param function: A callable returning bool. Called on each cancellation check. + :param suppress_exceptions: If True (default), exceptions from the function + are swallowed and treated as the default value. + :param default: Value to use when the function raises and suppress_exceptions + is True. Defaults to False. + :param before: Callable invoked before the condition function on each check. + :param after: Callable invoked after the condition function on each check. + :param caching: If True (default), the token stays cancelled once the + condition has returned True, without re-evaluating it. + + >>> items = [] + >>> token = ConditionToken(lambda: len(items) >= 3) + >>> token.cancelled + False + >>> items += [1, 2, 3] + >>> token.cancelled + True + """ + exception = ConditionCancellationError def __init__(self, function: Callable[[], bool], *tokens: AbstractToken, cancelled: bool = False, suppress_exceptions: bool = True, default: bool = False, before: Callable[[], Any] = lambda: None, after: Callable[[], Any] = lambda: None, caching: bool = True): # noqa: PLR0913 diff --git a/cantok/tokens/counter_token.py b/cantok/tokens/counter_token.py index c31d5aa..aa4e9fd 100644 --- a/cantok/tokens/counter_token.py +++ b/cantok/tokens/counter_token.py @@ -5,6 +5,23 @@ class CounterToken(ConditionToken): + """ + A token that cancels automatically after a fixed number of iterations. + + The internal counter decrements on each direct cancellation check. When it + reaches zero the token is cancelled. Useful for limiting the number of + iterations of a loop without tracking state externally. + + :param counter: Number of iterations before cancellation. Must be >= 0. + :param direct: If True (default), counter decrements even when polled + indirectly through a parent token. If False, indirect polls + are rolled back, so only direct checks consume the counter. + + >>> token = CounterToken(3) + >>> while token: + ... ... # loop body executes exactly 3 times + """ + exception = CounterCancellationError def __init__(self, counter: int, *tokens: AbstractToken, cancelled: bool = False, direct: bool = True): diff --git a/cantok/tokens/default_token.py b/cantok/tokens/default_token.py index 52f7908..179624f 100644 --- a/cantok/tokens/default_token.py +++ b/cantok/tokens/default_token.py @@ -3,6 +3,21 @@ class DefaultToken(AbstractToken): + """ + An immutable token that never cancels. + + Useful as a neutral default argument: a function that accepts a token can + receive a DefaultToken when no real cancellation is needed, without + requiring None checks. Calling cancel() raises ImpossibleCancelError. + + >>> def run(token: AbstractToken = DefaultToken()) -> bool: + ... return token.keep_on() + >>> run() # True — DefaultToken never cancels + True + >>> run(SimpleToken()) # True — SimpleToken not yet cancelled + True + """ + exception = ImpossibleCancelError def __init__(self) -> None: diff --git a/cantok/tokens/simple_token.py b/cantok/tokens/simple_token.py index a743000..77ee2c8 100644 --- a/cantok/tokens/simple_token.py +++ b/cantok/tokens/simple_token.py @@ -3,6 +3,18 @@ class SimpleToken(AbstractToken): + """ + A basic cancellation token with no automatic cancellation condition. + + Can only be cancelled explicitly by calling cancel() or setting + cancelled = True. Useful as a manual stop signal passed between threads. + + >>> token = SimpleToken() + >>> token.cancel() + >>> token.cancelled + True + """ + exception = CancellationError def _superpower(self) -> bool: diff --git a/cantok/tokens/timeout_token.py b/cantok/tokens/timeout_token.py index f23b66d..ed5739e 100644 --- a/cantok/tokens/timeout_token.py +++ b/cantok/tokens/timeout_token.py @@ -6,6 +6,27 @@ class TimeoutToken(ConditionToken): + """ + A token that cancels automatically after a specified duration. + + The timeout is measured from the moment the token is created. When the + deadline is reached, any cancellation check will return True and subsequent + check() calls will raise TimeoutCancellationError. + + :param timeout: Duration in seconds before cancellation. Must be >= 0. + :param monotonic: If True, uses time.monotonic_ns() instead of + time.perf_counter(), which is unaffected by system + clock adjustments. Defaults to False. + + >>> import time + >>> token = TimeoutToken(0.1) + >>> token.cancelled + False + >>> time.sleep(0.2) + >>> token.cancelled + True + """ + exception = TimeoutCancellationError def __init__(self, timeout: Union[int, float], *tokens: AbstractToken, cancelled: bool = False, monotonic: bool = False): From 1b46a32e4966f9cfc112e72620c8317bbb2475d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=91=D0=BB?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Tue, 31 Mar 2026 01:53:06 +0300 Subject: [PATCH 07/12] Fix example usage for TimeoutToken --- cantok/tokens/timeout_token.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cantok/tokens/timeout_token.py b/cantok/tokens/timeout_token.py index ed5739e..b751b55 100644 --- a/cantok/tokens/timeout_token.py +++ b/cantok/tokens/timeout_token.py @@ -19,6 +19,7 @@ class TimeoutToken(ConditionToken): clock adjustments. Defaults to False. >>> import time + >>> >>> token = TimeoutToken(0.1) >>> token.cancelled False From e8a21c2292f0313bd5b1fee2959778a1a4406ded Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=91=D0=BB?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Tue, 31 Mar 2026 02:00:11 +0300 Subject: [PATCH 08/12] Fix typos and improve clarity in documentation --- docs/the_pattern.md | 4 ++-- docs/types_of_tokens/ConditionToken.md | 4 ++-- docs/types_of_tokens/CounterToken.md | 4 ++-- docs/what_are_tokens/in_general.md | 2 +- docs/what_are_tokens/summation.md | 4 ++-- docs/what_are_tokens/waiting.md | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/the_pattern.md b/docs/the_pattern.md index c9492e7..816b759 100644 --- a/docs/the_pattern.md +++ b/docs/the_pattern.md @@ -4,8 +4,8 @@ Cancellation Token is a pattern that allows us to refuse to continue calculation The essence of the pattern is that we pass special objects to functions and constructors, by which the executed code can understand whether it should continue its execution or not. When deciding whether to allow code execution to continue, this object can take into account both the restrictions specified to it, such as the maximum code execution time, and receive signals about the need to stop from the outside, for example from another thread or a coroutine. Thus, we do not nail down the logic associated with stopping code execution, for example, by directly tracking cycle counters, but implement [Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection) of this restriction. -In addition, the pattern assumes that various restrictions can be combined indefinitely with each other: if at least one of the restrictions is not met, code execution will be interrupted. It is assumed that each function in the call stack will call other functions, throwing its token directly to them, or wrapping it in another token, with a stricter restriction imposed on it. +In addition, the pattern assumes that various restrictions can be combined indefinitely with each other: if at least one of the restrictions is not met, code execution will be interrupted. It is assumed that each function in the call stack will call other functions, passing its token directly to them, or wrapping it in another token, with a stricter restriction imposed on it. Unlike other ways of interrupting code execution, tokens do not force the execution thread to be interrupted forcibly. The interruption occurs "gently", allowing the code to terminate correctly, return all occupied resources and restore consistency. -It is highly desirable for library developers to use this pattern for any long-term composite operations. Your function can accept a token as an optional argument, with a default value that imposes minimal restrictions or none at all. If the user wishes, he can transfer his token there, imposing stricter restrictions on the library code. In addition to a more convenient and extensible API, this will give the library an advantage in the form of better testability, because the restrictions are no longer sewn directly into the function, which means they can be made whatever you want for the test. In addition, the library developer no longer needs to think about all the numerous restrictions that can be imposed on his code - the user can take care of it himself if he needs to. +It is highly desirable for library developers to use this pattern for any long-term composite operations. Your function can accept a token as an optional argument, with a default value that imposes minimal restrictions or none at all. If the user wishes, they can transfer their token there, imposing stricter restrictions on the library code. In addition to a more convenient and extensible API, this will give the library an advantage in the form of better testability, because the restrictions are no longer hardcoded into the function, which means they can be made whatever you want for the test. In addition, the library developer no longer needs to think about all the numerous restrictions that can be imposed on their code - the user can take care of it themselves if they need to. diff --git a/docs/types_of_tokens/ConditionToken.md b/docs/types_of_tokens/ConditionToken.md index a3aef4f..04a643c 100644 --- a/docs/types_of_tokens/ConditionToken.md +++ b/docs/types_of_tokens/ConditionToken.md @@ -19,10 +19,10 @@ def function(): raise ValueError token = ConditionToken(function, suppress_exceptions=False) -token.cancelled # ValueError has risen. +token.cancelled # ValueError has been raised. ``` -If you still use exception suppression mode, by default, in case of an exception, the `canceled` attribute will contain `False`. If you want to change this, pass it there as the `default` parameter - `True`. +If you still use exception suppression mode, by default, in case of an exception, the `cancelled` attribute will contain `False`. If you want to change this, pass it there as the `default` parameter - `True`. ```python def function(): raise ValueError diff --git a/docs/types_of_tokens/CounterToken.md b/docs/types_of_tokens/CounterToken.md index 6e99e26..1e7972b 100644 --- a/docs/types_of_tokens/CounterToken.md +++ b/docs/types_of_tokens/CounterToken.md @@ -1,6 +1,6 @@ -`CounterToken` is the most ambiguous of the tokens presented by this library. Do not use it if you are not sure that you understand how it works correctly. However, it can be very useful in situations where you want to limit the number of attempts to perform an operation. +`CounterToken` is the least intuitive of the tokens presented by this library. Do not use it if you are not sure that you understand how it works correctly. However, it can be very useful in situations where you want to limit the number of attempts to perform an operation. -`CounterToken` is initialized with an integer greater than zero. At each calculation of the answer to the question whether it is canceled, this number is reduced by one. When this number becomes zero, the token is considered canceled: +`CounterToken` is initialized with an integer greater than or equal to zero. At each calculation of the answer to the question whether it is canceled, this number is reduced by one. When this number becomes zero, the token is considered canceled: ```python from cantok import CounterToken diff --git a/docs/what_are_tokens/in_general.md b/docs/what_are_tokens/in_general.md index 4c4e38b..ce6fd07 100644 --- a/docs/what_are_tokens/in_general.md +++ b/docs/what_are_tokens/in_general.md @@ -19,7 +19,7 @@ Each of them has its own characteristics, but they also have something in common - Token cancellation is a one-way operation. A token that has already been cancelled cannot be restored. -- All token classes are inherited from `AbstractToken` and have a single interface that defines how they can be canceled, how to find out their status, how to expect their cancellation and much more. If you are writing a function that accepts an unknown token type, you can use `AbstractToken` to hint types: +- All token classes are inherited from `AbstractToken` and have a single interface that defines how they can be canceled, how to find out their status, how to wait for their cancellation and much more. If you are writing a function that accepts an unknown token type, you can use `AbstractToken` to hint types: ```python from cantok import AbstractToken diff --git a/docs/what_are_tokens/summation.md b/docs/what_are_tokens/summation.md index 5e8c317..bfb8221 100644 --- a/docs/what_are_tokens/summation.md +++ b/docs/what_are_tokens/summation.md @@ -7,7 +7,7 @@ print(repr(first_token + second_token)) #> SimpleToken(TimeoutToken(5), ConditionToken(λ)) ``` -This feature is convenient to use if your function has received a token with certain restrictions and wants to throw it into other called functions, imposing additional restrictions: +This feature is convenient to use if your function has received a token with certain restrictions and wants to pass it to other called functions, imposing additional restrictions: ```python from cantok import AbstractToken, TimeoutToken @@ -29,7 +29,7 @@ print(repr(SimpleToken(cancelled=True) + TimeoutToken(5))) #> SimpleToken(cancelled=True) ``` -In addition, you can not be afraid to sum more than 2 tokens - this does not generate anything superfluous: +In addition, you can safely sum more than 2 tokens - this does not generate anything superfluous: ```python print(repr(TimeoutToken(5) + ConditionToken(lambda: False) + CounterToken(23))) diff --git a/docs/what_are_tokens/waiting.md b/docs/what_are_tokens/waiting.md index 13b567c..9901ab4 100644 --- a/docs/what_are_tokens/waiting.md +++ b/docs/what_are_tokens/waiting.md @@ -32,5 +32,5 @@ Yes, it looks like magic, it is magic. The method itself finds out how it was us In addition to the above, the `wait()` method has two optional arguments: -- **`timeout`** (`int` or `float`) - the maximum waiting time in seconds. If this time is exceeded, a [`TimeoutCancellationError` exception](../what_are_tokens/waiting.md) will be raised. By default, the `timeout` is not set. +- **`timeout`** (`int` or `float`) - the maximum waiting time in seconds. If this time is exceeded, a [`TimeoutCancellationError` exception](../what_are_tokens/exceptions.md) will be raised. By default, the `timeout` is not set. - **`step`** (`int` or `float`, by default `0.0001`) - the duration of the iteration, once in which the token state is polled, in seconds. For obvious reasons, you cannot set this value to a number that exceeds the `timeout`. From d2cf137ecce4fbeca7dd3a834365b722f494d694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=91=D0=BB?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Tue, 31 Mar 2026 02:03:37 +0300 Subject: [PATCH 09/12] =?UTF-8?q?Update=20ConditionToken=20documentation:?= =?UTF-8?q?=20"detonated"=20=E2=86=92=20"triggered"=20for=20consistency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/types_of_tokens/ConditionToken.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/types_of_tokens/ConditionToken.md b/docs/types_of_tokens/ConditionToken.md index 04a643c..ff7dd4b 100644 --- a/docs/types_of_tokens/ConditionToken.md +++ b/docs/types_of_tokens/ConditionToken.md @@ -54,7 +54,7 @@ token.check() #> 2 ``` -`ConditionToken` has another feature. If the condition has detonated at least once and canceled it, then the condition is no longer polled and the token is permanently considered canceled. You can change this by manipulating the `caching` parameter when creating a token. By setting it to `False`, you will make sure that the condition is polled every time. +`ConditionToken` has another feature. If the condition has triggered at least once and canceled it, then the condition is no longer polled and the token is permanently considered canceled. You can change this by manipulating the `caching` parameter when creating a token. By setting it to `False`, you will make sure that the condition is polled every time. ```python counter = 0 From d34592148cfa2dcf0c0880a2d731ad58a9ed3648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=91=D0=BB?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Tue, 31 Mar 2026 02:13:48 +0300 Subject: [PATCH 10/12] Update documentation for consistency and clarity across all token types and concepts --- .github/ISSUE_TEMPLATE/documentation.md | 4 ++-- cantok/tokens/counter_token.py | 2 +- docs/installation.md | 2 +- docs/quick_start.md | 4 ++-- docs/the_pattern.md | 8 ++++---- docs/types_of_tokens/ConditionToken.md | 6 +++--- docs/types_of_tokens/CounterToken.md | 6 +++--- docs/types_of_tokens/SimpleToken.md | 2 +- docs/types_of_tokens/TimeoutToken.md | 4 ++-- docs/what_are_tokens/cancel_and_read_the_status.md | 8 ++++---- docs/what_are_tokens/exceptions.md | 6 +++--- docs/what_are_tokens/in_general.md | 8 ++++---- docs/what_are_tokens/summation.md | 4 ++-- docs/what_are_tokens/waiting.md | 4 ++-- 14 files changed, 34 insertions(+), 34 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/documentation.md b/.github/ISSUE_TEMPLATE/documentation.md index 20f4742..19d9939 100644 --- a/.github/ISSUE_TEMPLATE/documentation.md +++ b/.github/ISSUE_TEMPLATE/documentation.md @@ -8,7 +8,7 @@ assignees: pomponchik ## It's cool that you're here! -Documentation is an important part of the project, we strive to make it high-quality and keep it up to date. Please adjust this template by outlining your proposal. +Documentation is an important part of the project; we strive to make it high-quality and keep it up to date. Please adjust this template by outlining your proposal. ## Type of action @@ -18,7 +18,7 @@ What do you want to do: remove something, add it, or change it? ## Where? -Specify which part of the documentation you want to make a change to? For example, the name of an existing documentation section or the line number in a file `README.md`. +Specify which part of the documentation you want to make a change to. For example, the name of an existing documentation section or the line number in a file `README.md`. ## The essence diff --git a/cantok/tokens/counter_token.py b/cantok/tokens/counter_token.py index aa4e9fd..9a814fb 100644 --- a/cantok/tokens/counter_token.py +++ b/cantok/tokens/counter_token.py @@ -9,7 +9,7 @@ class CounterToken(ConditionToken): A token that cancels automatically after a fixed number of iterations. The internal counter decrements on each direct cancellation check. When it - reaches zero the token is cancelled. Useful for limiting the number of + reaches zero, the token is cancelled. Useful for limiting the number of iterations of a loop without tracking state externally. :param counter: Number of iterations before cancellation. Must be >= 0. diff --git a/docs/installation.md b/docs/installation.md index cafcb05..bf114f0 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -1,4 +1,4 @@ -Install it from [Pypi](https://pypi.org/project/cantok/): +Install it from [PyPI](https://pypi.org/project/cantok/): ```bash pip install cantok diff --git a/docs/quick_start.md b/docs/quick_start.md index 1ca0911..8bd1e42 100644 --- a/docs/quick_start.md +++ b/docs/quick_start.md @@ -14,6 +14,6 @@ while token: print(counter) ``` -In this code, we use a token that describes several restrictions: on the [number of iterations](types_of_tokens/CounterToken.md) of the cycle, on [time](types_of_tokens/TimeoutToken.md), as well as on the [occurrence](types_of_tokens/ConditionToken.md) of a random unlikely event. When any of the indicated events occur, the cycle stops. +In this code, we use a token that describes several restrictions: on the [number of iterations](types_of_tokens/CounterToken.md) of the loop, on [time](types_of_tokens/TimeoutToken.md), as well as on the [occurrence](types_of_tokens/ConditionToken.md) of a random unlikely event. When any of the indicated events occur, the loop stops. -In fact, the library's capabilities are much broader, read the documentation below. +In fact, the library's capabilities are much broader. Read the documentation below. diff --git a/docs/the_pattern.md b/docs/the_pattern.md index 816b759..147cdf3 100644 --- a/docs/the_pattern.md +++ b/docs/the_pattern.md @@ -2,10 +2,10 @@ Cancellation Token is a pattern that allows us to refuse to continue calculations that we no longer need. It is implemented out of the box in many programming languages, for example in [C#](https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken) and in [Go](https://pkg.go.dev/context). However, there was still no sane implementation in Python, until the [cantok](https://github.com/pomponchik/cantok) library appeared. -The essence of the pattern is that we pass special objects to functions and constructors, by which the executed code can understand whether it should continue its execution or not. When deciding whether to allow code execution to continue, this object can take into account both the restrictions specified to it, such as the maximum code execution time, and receive signals about the need to stop from the outside, for example from another thread or a coroutine. Thus, we do not nail down the logic associated with stopping code execution, for example, by directly tracking cycle counters, but implement [Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection) of this restriction. +The essence of the pattern is that we pass special objects to functions and constructors, by which the executed code can understand whether it should continue its execution or not. When deciding whether to allow code execution to continue, this object can both take into account the restrictions specified to it, such as the maximum code execution time, and receive signals about the need to stop from the outside, for example from another thread or a coroutine. Thus, we do not nail down the logic associated with stopping code execution, for example, by directly tracking loop counters, but implement [Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection) of this restriction. -In addition, the pattern assumes that various restrictions can be combined indefinitely with each other: if at least one of the restrictions is not met, code execution will be interrupted. It is assumed that each function in the call stack will call other functions, passing its token directly to them, or wrapping it in another token, with a stricter restriction imposed on it. +In addition, the pattern assumes that various restrictions can be combined in unlimited combinations with each other: if at least one of the restrictions is not met, code execution will be interrupted. It is assumed that each function in the call stack will call other functions, passing its token directly to them, or wrapping it in another token, with a stricter restriction imposed on it. -Unlike other ways of interrupting code execution, tokens do not force the execution thread to be interrupted forcibly. The interruption occurs "gently", allowing the code to terminate correctly, return all occupied resources and restore consistency. +Unlike other ways of interrupting code execution, tokens do not force the execution thread to be interrupted. The interruption occurs "gently", allowing the code to terminate correctly, release all held resources and restore consistency. -It is highly desirable for library developers to use this pattern for any long-term composite operations. Your function can accept a token as an optional argument, with a default value that imposes minimal restrictions or none at all. If the user wishes, they can transfer their token there, imposing stricter restrictions on the library code. In addition to a more convenient and extensible API, this will give the library an advantage in the form of better testability, because the restrictions are no longer hardcoded into the function, which means they can be made whatever you want for the test. In addition, the library developer no longer needs to think about all the numerous restrictions that can be imposed on their code - the user can take care of it themselves if they need to. +It is highly desirable for library developers to use this pattern for any long-running operations. Your function can accept a token as an optional argument, with a default value that imposes minimal restrictions or none at all. If the user wishes, they can pass their token to it, imposing stricter restrictions on the library code. In addition to a more convenient and extensible API, this will give the library an advantage in the form of better testability, because the restrictions are no longer hardcoded into the function, which means they can be made whatever you want for the test. In addition, the library developer no longer needs to think about all the numerous restrictions that can be imposed on their code - the user can take care of it themselves if they need to. diff --git a/docs/types_of_tokens/ConditionToken.md b/docs/types_of_tokens/ConditionToken.md index ff7dd4b..0faf0c3 100644 --- a/docs/types_of_tokens/ConditionToken.md +++ b/docs/types_of_tokens/ConditionToken.md @@ -1,4 +1,4 @@ -`ConditionToken` has superpower: it can check arbitrary conditions. In addition to this, it can do all the same things as [`SimpleToken`](../types_of_tokens/SimpleToken.md). The condition is a function that returns an answer to the question "has the token been canceled" (`True`/`False`), it is passed to the token as the first required argument during initialization: +`ConditionToken` has a superpower: it can check arbitrary conditions. In addition to this, it can do all the same things as [`SimpleToken`](../types_of_tokens/SimpleToken.md). The condition is a function that returns an answer to the question "has the token been cancelled" (`True`/`False`), it is passed to the token as the first required argument during initialization: ```python from cantok import ConditionToken @@ -22,7 +22,7 @@ token = ConditionToken(function, suppress_exceptions=False) token.cancelled # ValueError has been raised. ``` -If you still use exception suppression mode, by default, in case of an exception, the `cancelled` attribute will contain `False`. If you want to change this, pass it there as the `default` parameter - `True`. +When using exception suppression mode, the `cancelled` attribute will contain `False` by default in case of an exception. If you want to change this, pass `default=True`. ```python def function(): raise ValueError @@ -54,7 +54,7 @@ token.check() #> 2 ``` -`ConditionToken` has another feature. If the condition has triggered at least once and canceled it, then the condition is no longer polled and the token is permanently considered canceled. You can change this by manipulating the `caching` parameter when creating a token. By setting it to `False`, you will make sure that the condition is polled every time. +`ConditionToken` has another feature. If the condition has triggered at least once and cancelled it, then the condition is no longer polled and the token is permanently considered cancelled. You can change this by manipulating the `caching` parameter when creating a token. By setting it to `False`, you will make sure that the condition is polled every time. ```python counter = 0 diff --git a/docs/types_of_tokens/CounterToken.md b/docs/types_of_tokens/CounterToken.md index 1e7972b..f5f40f6 100644 --- a/docs/types_of_tokens/CounterToken.md +++ b/docs/types_of_tokens/CounterToken.md @@ -1,6 +1,6 @@ `CounterToken` is the least intuitive of the tokens presented by this library. Do not use it if you are not sure that you understand how it works correctly. However, it can be very useful in situations where you want to limit the number of attempts to perform an operation. -`CounterToken` is initialized with an integer greater than or equal to zero. At each calculation of the answer to the question whether it is canceled, this number is reduced by one. When this number becomes zero, the token is considered canceled: +`CounterToken` is initialized with an integer greater than or equal to zero. Each time cancellation is checked, this number is decremented by one. When this number becomes zero, the token is considered cancelled: ```python from cantok import CounterToken @@ -14,13 +14,13 @@ while token: print(counter) #> 5 ``` -The counter inside the `CounterToken` is reduced under one of three conditions: +The counter inside the `CounterToken` is decremented under one of three conditions: - Access to the `cancelled` attribute. - Calling the `is_cancelled()` method. - Calling the `keep_on()` method. -If you use `CounterToken` inside other tokens, the wrapping token can specify the status of the `CounterToken`. For security reasons, this operation does not decrease the counter. However, if for some reason you need it to decrease, pass `direct` - `False` as an argument: +If you use `CounterToken` inside other tokens, the wrapping token can query the status of the `CounterToken`. To avoid unintended side effects, this operation does not decrease the counter. However, if for some reason you need it to decrease, pass `direct` - `False` as an argument: ```python from cantok import SimpleToken, CounterToken diff --git a/docs/types_of_tokens/SimpleToken.md b/docs/types_of_tokens/SimpleToken.md index 6e8f9da..0d1233b 100644 --- a/docs/types_of_tokens/SimpleToken.md +++ b/docs/types_of_tokens/SimpleToken.md @@ -9,4 +9,4 @@ token.cancel() print(token.cancelled) #> True ``` -There is not much more to tell about it if you have read [the story](../what_are_tokens/in_general.md) about tokens in general. +There is not much more to tell about it if you have read [the section](../what_are_tokens/in_general.md) about tokens in general. diff --git a/docs/types_of_tokens/TimeoutToken.md b/docs/types_of_tokens/TimeoutToken.md index 8f3cb59..d730fbd 100644 --- a/docs/types_of_tokens/TimeoutToken.md +++ b/docs/types_of_tokens/TimeoutToken.md @@ -1,4 +1,4 @@ -`TimeoutToken` is automatically canceled after the time specified in seconds in the class constructor: +`TimeoutToken` is automatically cancelled after the time specified in seconds in the class constructor: ```python from time import sleep @@ -16,7 +16,7 @@ Just like `ConditionToken`, `TimeoutToken` can include other tokens: token = TimeoutToken(45, SimpleToken(), TimeoutToken(5), CounterToken(20)) # Includes all additional restrictions of the passed tokens. ``` -By default, time is measured using [`perf_counter`](https://docs.python.org/3/library/time.html#time.perf_counter) as the most accurate way to measure time. In extremely rare cases, you may need to use [monotonic](https://docs.python.org/3/library/time.html#time.monotonic_ns)-time, for this use the appropriate initialization argument: +By default, time is measured using [`perf_counter`](https://docs.python.org/3/library/time.html#time.perf_counter) as the most accurate option. In extremely rare cases, you may need to use [monotonic](https://docs.python.org/3/library/time.html#time.monotonic_ns)-time; for this, use the appropriate initialization argument: ```python token = TimeoutToken(33, monotonic=True) diff --git a/docs/what_are_tokens/cancel_and_read_the_status.md b/docs/what_are_tokens/cancel_and_read_the_status.md index 3789751..5698e64 100644 --- a/docs/what_are_tokens/cancel_and_read_the_status.md +++ b/docs/what_are_tokens/cancel_and_read_the_status.md @@ -1,4 +1,4 @@ -Each token object has a `cancelled` attribute and a `cancel()` method. By the attribute, you can find out whether this token has been canceled: +Each token object has a `cancelled` attribute and a `cancel()` method. By the attribute, you can find out whether this token has been cancelled: ```python from cantok import SimpleToken @@ -34,9 +34,9 @@ print(token.cancelled) #> True print(token.is_cancelled()) #> True ``` -Choose what you like best. To the author of the library, the use of the attribute seems more beautiful, but the method call more clearly reflects the complexity of the work that is actually being done to answer the question "has the token been canceled?". +Choose what you like best. To the author of the library, the use of the attribute seems more elegant, but the method call more clearly reflects the complexity of the work that is actually being done to answer the question "has the token been cancelled?". -There is another method opposite to `is_cancelled()` - `keep_on()`. It answers the opposite question, and can be used in the same situations: +There is another method opposite to `is_cancelled()` — `keep_on()`. It answers the opposite question, and can be used in the same situations: ```python from cantok import SimpleToken @@ -62,7 +62,7 @@ print(bool(token)) #> False print(token.keep_on()) #> False ``` -There is another method that is close in meaning to `is_cancelled()` - `check()`. It does nothing if the token is not canceled, or raises an exception if canceled. If the token was canceled by calling the `cancel()` method, a `CancellationError` exception will be raised: +There is another method that is close in meaning to `is_cancelled()` — `check()`. It does nothing if the token is not cancelled, or raises an exception if cancelled. If the token was cancelled by calling the `cancel()` method, a `CancellationError` exception will be raised: ```python from cantok import SimpleToken diff --git a/docs/what_are_tokens/exceptions.md b/docs/what_are_tokens/exceptions.md index fe0d261..b1fdb28 100644 --- a/docs/what_are_tokens/exceptions.md +++ b/docs/what_are_tokens/exceptions.md @@ -1,4 +1,4 @@ -When a token is canceled, you can call the `check()` method from it and an exception will be raised: +When a token is cancelled, you can call the `check()` method from it and an exception will be raised: ```python from cantok import TimeoutToken @@ -17,7 +17,7 @@ Each type of token (except [`DefaultToken`](../types_of_tokens/DefaultToken.md)) - [`TimeoutToken`](../types_of_tokens/TimeoutToken.md) -> `TimeoutCancellationError` - [`CounterToken`](../types_of_tokens/CounterToken.md) -> `CounterCancellationError` -When you call the `check()` method on any token, one of two things will happen. If it (or any of the tokens nested in it) was canceled by calling the `cancel()` method, `CancellationError` will always be raised. But if the cancellation occurred as a result of the unique ability of the token, such as for `TimeoutToken` - timeout expiration, then an exception specific to this type of token will be raised. +When you call the `check()` method on any token, one of two things will happen. If it (or any of the tokens nested in it) was cancelled by calling the `cancel()` method, `CancellationError` will always be raised. But if the cancellation occurred as a result of the unique ability of the token, such as for `TimeoutToken` - timeout expiration, then an exception specific to this type of token will be raised. `ConditionCancellationError`, `TimeoutCancellationError` and `CounterCancellationError` are inherited from `CancellationError`, so if you're not sure which exception specifically you're catching, catch `CancellationError`. But also all the listed exceptions can always be imported separately: @@ -38,7 +38,7 @@ except CancellationError as e: print(type(e) is TimeoutToken.exception) #> True ``` -And each exception object has a `token` attribute indicating the specific token that was canceled. This can be useful in situations where several tokens are nested in one another and you want to find out which one has been canceled: +And each exception object has a `token` attribute indicating the specific token that was cancelled. This can be useful in situations where several tokens are nested in one another and you want to find out which one has been cancelled: ```python from cantok import SimpleToken, TimeoutToken, CancellationError diff --git a/docs/what_are_tokens/in_general.md b/docs/what_are_tokens/in_general.md index ce6fd07..bf18d49 100644 --- a/docs/what_are_tokens/in_general.md +++ b/docs/what_are_tokens/in_general.md @@ -1,4 +1,4 @@ -A token is an object that can tell you whether to continue the action you started, or whether it has already been canceled. +A token is an object that can tell you whether to continue the action you started, or whether it has already been cancelled. There are 4 main types of tokens in this library: @@ -13,13 +13,13 @@ Additionally, there is a 5th type that cannot be cancelled: Each of them has its own characteristics, but they also have something in common: -- Each token (except [`DefaultToken`](../types_of_tokens/DefaultToken.md)) can be canceled manually, and some types of tokens can cancel themselves when a condition or timeout occurs. It doesn't matter how the token was canceled, you work with it the same way. +- Each token (except [`DefaultToken`](../types_of_tokens/DefaultToken.md)) can be cancelled manually, and some types of tokens can cancel themselves when a condition or timeout occurs. It doesn't matter how the token was cancelled, you work with it the same way. -- All types of tokens are thread-safe and can be used from multiple threads/coroutines. However, they are not intended to be shared from multiple processes. +- All types of tokens are thread-safe and can be used from multiple threads/coroutines. However, they are not intended to be shared across multiple processes. - Token cancellation is a one-way operation. A token that has already been cancelled cannot be restored. -- All token classes are inherited from `AbstractToken` and have a single interface that defines how they can be canceled, how to find out their status, how to wait for their cancellation and much more. If you are writing a function that accepts an unknown token type, you can use `AbstractToken` to hint types: +- All token classes are inherited from `AbstractToken` and have a single interface that defines how they can be cancelled, how to find out their status, how to wait for their cancellation and much more. If you are writing a function that accepts an unknown token type, you can use `AbstractToken` to hint types: ```python from cantok import AbstractToken diff --git a/docs/what_are_tokens/summation.md b/docs/what_are_tokens/summation.md index bfb8221..193d373 100644 --- a/docs/what_are_tokens/summation.md +++ b/docs/what_are_tokens/summation.md @@ -18,7 +18,7 @@ def function(token: AbstractToken): ... ``` -The token summation operation always generates a new token. If at least one of the operand tokens is canceled, the sum will also be canceled. It is also guaranteed that the cancellation of this token does not lead to the cancellation of operands. That is, the sum of two tokens always behaves as if it were a [`SimpleToken`](../types_of_tokens/SimpleToken.md) in which both operands were [nested](embedding.md). However, it is difficult to say exactly which token will result from summation, since the library strives to minimize the generated graph of tokens for the sake of performance. +The token summation operation always generates a new token. If at least one of the operand tokens is cancelled, the sum will also be cancelled. It is also guaranteed that the cancellation of this token does not lead to the cancellation of operands. That is, the sum of two tokens always behaves as if it were a [`SimpleToken`](../types_of_tokens/SimpleToken.md) in which both operands were [nested](embedding.md). However, it is difficult to say exactly which token will result from summation, since the library strives to minimize the generated graph of tokens for performance reasons. You may notice that some tokens disappear altogether during summation: @@ -36,4 +36,4 @@ print(repr(TimeoutToken(5) + ConditionToken(lambda: False) + CounterToken(23))) #> TimeoutToken(5, ConditionToken(λ), CounterToken(23)) ``` -In fact, there are quite a few effective ways to optimize the token addition operation that are implemented in the library. This operation is pretty well optimized, so it is recommended in all cases when you need to combine the constraints of different tokens in one. +In fact, there are quite a few effective ways to optimize the token addition operation that are implemented in the library. This operation is well optimized, so it is recommended in all cases when you need to combine the constraints of different tokens into one. diff --git a/docs/what_are_tokens/waiting.md b/docs/what_are_tokens/waiting.md index 9901ab4..c74511a 100644 --- a/docs/what_are_tokens/waiting.md +++ b/docs/what_are_tokens/waiting.md @@ -1,4 +1,4 @@ -Each token has `wait()` method, which allows you to wait for its cancellation. +Each token has a `wait()` method, which allows you to wait for its cancellation. ```python from cantok import TimeoutToken @@ -28,7 +28,7 @@ async def main(): asyncio.run(main()) ``` -Yes, it looks like magic, it is magic. The method itself finds out how it was used: inside an expression with or without the `await` keyword. In the first case, it runs in CPU-saving mode, in the second - in non-blocking event-loop mode. +Yes, it looks like magic — it is magic. The method itself finds out how it was used: inside an expression with or without the `await` keyword. In the first case, it runs in CPU-saving mode, in the second - in non-blocking event-loop mode. In addition to the above, the `wait()` method has two optional arguments: From fc3d7ed5adcd72d8195d3d6185add766319ef848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=91=D0=BB?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Tue, 31 Mar 2026 02:22:39 +0300 Subject: [PATCH 11/12] Update documentation grammar and wording for consistency and clarity --- README.md | 2 +- docs/ecosystem/projects/regular_functions_calling.md | 2 +- docs/index.md | 2 +- docs/types_of_tokens/ConditionToken.md | 2 +- docs/types_of_tokens/CounterToken.md | 4 ++-- docs/types_of_tokens/TimeoutToken.md | 2 +- docs/what_are_tokens/cancel_and_read_the_status.md | 2 +- docs/what_are_tokens/embedding.md | 2 +- docs/what_are_tokens/exceptions.md | 2 +- docs/what_are_tokens/in_general.md | 2 +- docs/what_are_tokens/summation.md | 2 +- docs/what_are_tokens/waiting.md | 2 +- 12 files changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 71ab1fe..bfe7cc9 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ ![logo](https://raw.githubusercontent.com/pomponchik/cantok/main/docs/assets/logo_5.png) -Cancellation Token is a pattern that allows us to refuse to continue calculations that we no longer need. It is implemented out of the box in many programming languages, for example in [C#](https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken) and in [Go](https://pkg.go.dev/context). However, there was still no sane implementation in Python, until the [cantok](https://github.com/pomponchik/cantok) library. +Cancellation Token is a pattern that allows us to refuse to continue calculations that we no longer need. It is implemented out of the box in many programming languages, for example in [C#](https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken) and in [Go](https://pkg.go.dev/context). However, there was still no sane implementation in Python, until the [cantok](https://github.com/pomponchik/cantok) library appeared. ## Quick start diff --git a/docs/ecosystem/projects/regular_functions_calling.md b/docs/ecosystem/projects/regular_functions_calling.md index 544f89a..bcf1625 100644 --- a/docs/ecosystem/projects/regular_functions_calling.md +++ b/docs/ecosystem/projects/regular_functions_calling.md @@ -38,7 +38,7 @@ metronome = Metronome(0.2, lambda: None, token=TimeoutToken(1)) metronome.start() print(metronome.stopped) #> False -sleep(1.5) # Here I specify a little more time than in the constructor of the token itself, since a small margin is needed for operations related to the creation of the metronome object itself. +sleep(1.5) # A slightly longer time is specified here than in the token constructor, to allow a small margin for the overhead of creating the metronome object. print(metronome.stopped) #> True ``` diff --git a/docs/index.md b/docs/index.md index 8ec0893..f4f8c2a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,4 +1,4 @@ ![logo](https://raw.githubusercontent.com/pomponchik/cantok/main/docs/assets/logo_5.png) -Cancellation Token is a pattern that allows us to refuse to continue calculations that we no longer need. It is implemented out of the box in many programming languages, for example in [C#](https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken) and in [Go](https://pkg.go.dev/context). However, there was still no sane implementation in Python, until the [cantok](https://github.com/pomponchik/cantok) library. +Cancellation Token is a pattern that allows us to refuse to continue calculations that we no longer need. It is implemented out of the box in many programming languages, for example in [C#](https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken) and in [Go](https://pkg.go.dev/context). However, there was still no sane implementation in Python, until the [cantok](https://github.com/pomponchik/cantok) library appeared. diff --git a/docs/types_of_tokens/ConditionToken.md b/docs/types_of_tokens/ConditionToken.md index 0faf0c3..c11f57a 100644 --- a/docs/types_of_tokens/ConditionToken.md +++ b/docs/types_of_tokens/ConditionToken.md @@ -54,7 +54,7 @@ token.check() #> 2 ``` -`ConditionToken` has another feature. If the condition has triggered at least once and cancelled it, then the condition is no longer polled and the token is permanently considered cancelled. You can change this by manipulating the `caching` parameter when creating a token. By setting it to `False`, you will make sure that the condition is polled every time. +`ConditionToken` has another feature. If the condition has triggered at least once and cancelled the token, then the condition is no longer polled and the token is permanently considered cancelled. You can change this by manipulating the `caching` parameter when creating a token. By setting it to `False`, you will make sure that the condition is polled every time. ```python counter = 0 diff --git a/docs/types_of_tokens/CounterToken.md b/docs/types_of_tokens/CounterToken.md index f5f40f6..ff9c431 100644 --- a/docs/types_of_tokens/CounterToken.md +++ b/docs/types_of_tokens/CounterToken.md @@ -1,4 +1,4 @@ -`CounterToken` is the least intuitive of the tokens presented by this library. Do not use it if you are not sure that you understand how it works correctly. However, it can be very useful in situations where you want to limit the number of attempts to perform an operation. +`CounterToken` is the least intuitive of the tokens provided by this library. Do not use it if you are not sure that you understand how it works correctly. However, it can be very useful in situations where you want to limit the number of attempts to perform an operation. `CounterToken` is initialized with an integer greater than or equal to zero. Each time cancellation is checked, this number is decremented by one. When this number becomes zero, the token is considered cancelled: @@ -20,7 +20,7 @@ The counter inside the `CounterToken` is decremented under one of three conditio - Calling the `is_cancelled()` method. - Calling the `keep_on()` method. -If you use `CounterToken` inside other tokens, the wrapping token can query the status of the `CounterToken`. To avoid unintended side effects, this operation does not decrease the counter. However, if for some reason you need it to decrease, pass `direct` - `False` as an argument: +If you use `CounterToken` inside other tokens, the wrapping token can query the status of the `CounterToken`. To avoid unintended side effects, this operation does not decrease the counter. However, if for some reason you need it to decrease, pass `direct=False` as an argument: ```python from cantok import SimpleToken, CounterToken diff --git a/docs/types_of_tokens/TimeoutToken.md b/docs/types_of_tokens/TimeoutToken.md index d730fbd..edb1cc1 100644 --- a/docs/types_of_tokens/TimeoutToken.md +++ b/docs/types_of_tokens/TimeoutToken.md @@ -16,7 +16,7 @@ Just like `ConditionToken`, `TimeoutToken` can include other tokens: token = TimeoutToken(45, SimpleToken(), TimeoutToken(5), CounterToken(20)) # Includes all additional restrictions of the passed tokens. ``` -By default, time is measured using [`perf_counter`](https://docs.python.org/3/library/time.html#time.perf_counter) as the most accurate option. In extremely rare cases, you may need to use [monotonic](https://docs.python.org/3/library/time.html#time.monotonic_ns)-time; for this, use the appropriate initialization argument: +By default, time is measured using [`perf_counter`](https://docs.python.org/3/library/time.html#time.perf_counter) as the most accurate option. In extremely rare cases, you may need to use [monotonic](https://docs.python.org/3/library/time.html#time.monotonic_ns) time; for this, use the appropriate initialization argument: ```python token = TimeoutToken(33, monotonic=True) diff --git a/docs/what_are_tokens/cancel_and_read_the_status.md b/docs/what_are_tokens/cancel_and_read_the_status.md index 5698e64..c233021 100644 --- a/docs/what_are_tokens/cancel_and_read_the_status.md +++ b/docs/what_are_tokens/cancel_and_read_the_status.md @@ -34,7 +34,7 @@ print(token.cancelled) #> True print(token.is_cancelled()) #> True ``` -Choose what you like best. To the author of the library, the use of the attribute seems more elegant, but the method call more clearly reflects the complexity of the work that is actually being done to answer the question "has the token been cancelled?". +Choose what you like best. The attribute is considered more elegant, but the method call more clearly reflects the complexity of the work that is actually being done to answer the question "has the token been cancelled?". There is another method opposite to `is_cancelled()` — `keep_on()`. It answers the opposite question, and can be used in the same situations: diff --git a/docs/what_are_tokens/embedding.md b/docs/what_are_tokens/embedding.md index 83e3cbd..4320208 100644 --- a/docs/what_are_tokens/embedding.md +++ b/docs/what_are_tokens/embedding.md @@ -1,4 +1,4 @@ -An unlimited number of other tokens can be embedded in one token as arguments during initialization. Each time checking whether it has been canceled, the token first checks its cancellation rules, and if it has not been canceled itself, then it checks the tokens nested in it. Thus, one cancelled token nested in another non-cancelled token cancels it: +An unlimited number of other tokens can be embedded in one token as arguments during initialization. Each time checking whether it has been cancelled, the token first checks its cancellation rules, and if it has not been canceled itself, then it checks the tokens nested in it. Thus, one cancelled token nested in another non-cancelled token cancels it: ```python from cantok import SimpleToken diff --git a/docs/what_are_tokens/exceptions.md b/docs/what_are_tokens/exceptions.md index b1fdb28..7ed9a85 100644 --- a/docs/what_are_tokens/exceptions.md +++ b/docs/what_are_tokens/exceptions.md @@ -7,7 +7,7 @@ token = TimeoutToken(1) token.wait() token.check() #> ... -#> cantok.errors.TimeoutCancellationError: The timeout of 1 seconds has expired. +#> cantok.errors.TimeoutCancellationError: The timeout of 1 second has expired. ``` Each type of token (except [`DefaultToken`](../types_of_tokens/DefaultToken.md)) has a corresponding type of exception that can be raised in this case: diff --git a/docs/what_are_tokens/in_general.md b/docs/what_are_tokens/in_general.md index bf18d49..039c3b3 100644 --- a/docs/what_are_tokens/in_general.md +++ b/docs/what_are_tokens/in_general.md @@ -19,7 +19,7 @@ Each of them has its own characteristics, but they also have something in common - Token cancellation is a one-way operation. A token that has already been cancelled cannot be restored. -- All token classes are inherited from `AbstractToken` and have a single interface that defines how they can be cancelled, how to find out their status, how to wait for their cancellation and much more. If you are writing a function that accepts an unknown token type, you can use `AbstractToken` to hint types: +- All token classes inherit from `AbstractToken` and have a single interface that defines how they can be cancelled, how to find out their status, how to wait for their cancellation and much more. If you are writing a function that accepts an unknown token type, you can use `AbstractToken` to hint types: ```python from cantok import AbstractToken diff --git a/docs/what_are_tokens/summation.md b/docs/what_are_tokens/summation.md index 193d373..2504e9c 100644 --- a/docs/what_are_tokens/summation.md +++ b/docs/what_are_tokens/summation.md @@ -18,7 +18,7 @@ def function(token: AbstractToken): ... ``` -The token summation operation always generates a new token. If at least one of the operand tokens is cancelled, the sum will also be cancelled. It is also guaranteed that the cancellation of this token does not lead to the cancellation of operands. That is, the sum of two tokens always behaves as if it were a [`SimpleToken`](../types_of_tokens/SimpleToken.md) in which both operands were [nested](embedding.md). However, it is difficult to say exactly which token will result from summation, since the library strives to minimize the generated graph of tokens for performance reasons. +The token summation operation always generates a new token. If at least one of the operand tokens is cancelled, the sum will also be cancelled. It is also guaranteed that the cancellation of this token does not lead to the cancellation of the operands. That is, the sum of two tokens always behaves as if it were a [`SimpleToken`](../types_of_tokens/SimpleToken.md) in which both operands were [nested](embedding.md). However, it is difficult to say exactly which token will result from summation, since the library strives to minimize the generated graph of tokens for performance reasons. You may notice that some tokens disappear altogether during summation: diff --git a/docs/what_are_tokens/waiting.md b/docs/what_are_tokens/waiting.md index c74511a..0cd310a 100644 --- a/docs/what_are_tokens/waiting.md +++ b/docs/what_are_tokens/waiting.md @@ -33,4 +33,4 @@ Yes, it looks like magic — it is magic. The method itself finds out how it was In addition to the above, the `wait()` method has two optional arguments: - **`timeout`** (`int` or `float`) - the maximum waiting time in seconds. If this time is exceeded, a [`TimeoutCancellationError` exception](../what_are_tokens/exceptions.md) will be raised. By default, the `timeout` is not set. -- **`step`** (`int` or `float`, by default `0.0001`) - the duration of the iteration, once in which the token state is polled, in seconds. For obvious reasons, you cannot set this value to a number that exceeds the `timeout`. +- **`step`** (`int` or `float`, by default `0.0001`) - the duration of each iteration during which the token state is polled, in seconds. For obvious reasons, you cannot set this value to a number that exceeds the `timeout`. From 94216f0de823d3e27b84b0782192e193386767c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=91=D0=BB?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Tue, 31 Mar 2026 02:27:48 +0300 Subject: [PATCH 12/12] Fix grammatical errors and improve clarity in documentation --- README.md | 2 +- docs/ecosystem/projects/regular_functions_calling.md | 2 +- docs/the_pattern.md | 8 ++++---- docs/types_of_tokens/ConditionToken.md | 6 +++--- docs/types_of_tokens/CounterToken.md | 2 +- docs/what_are_tokens/cancel_and_read_the_status.md | 2 +- docs/what_are_tokens/embedding.md | 2 +- docs/what_are_tokens/exceptions.md | 4 ++-- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index bfe7cc9..d8dfb76 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ ![logo](https://raw.githubusercontent.com/pomponchik/cantok/main/docs/assets/logo_5.png) -Cancellation Token is a pattern that allows us to refuse to continue calculations that we no longer need. It is implemented out of the box in many programming languages, for example in [C#](https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken) and in [Go](https://pkg.go.dev/context). However, there was still no sane implementation in Python, until the [cantok](https://github.com/pomponchik/cantok) library appeared. +Cancellation Token is a pattern that allows us to cancel calculations that we no longer need. It is implemented out of the box in many programming languages, for example in [C#](https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken) and in [Go](https://pkg.go.dev/context). However, there was still no sane implementation in Python, until the [cantok](https://github.com/pomponchik/cantok) library appeared. ## Quick start diff --git a/docs/ecosystem/projects/regular_functions_calling.md b/docs/ecosystem/projects/regular_functions_calling.md index bcf1625..4855805 100644 --- a/docs/ecosystem/projects/regular_functions_calling.md +++ b/docs/ecosystem/projects/regular_functions_calling.md @@ -38,7 +38,7 @@ metronome = Metronome(0.2, lambda: None, token=TimeoutToken(1)) metronome.start() print(metronome.stopped) #> False -sleep(1.5) # A slightly longer time is specified here than in the token constructor, to allow a small margin for the overhead of creating the metronome object. +sleep(1.5) # We specify a slightly longer sleep time than the token timeout to allow for the overhead of creating the metronome object. print(metronome.stopped) #> True ``` diff --git a/docs/the_pattern.md b/docs/the_pattern.md index 147cdf3..8b26baa 100644 --- a/docs/the_pattern.md +++ b/docs/the_pattern.md @@ -1,11 +1,11 @@ # The pattern -Cancellation Token is a pattern that allows us to refuse to continue calculations that we no longer need. It is implemented out of the box in many programming languages, for example in [C#](https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken) and in [Go](https://pkg.go.dev/context). However, there was still no sane implementation in Python, until the [cantok](https://github.com/pomponchik/cantok) library appeared. +Cancellation Token is a pattern that allows us to cancel calculations that we no longer need. It is implemented out of the box in many programming languages, for example in [C#](https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken) and in [Go](https://pkg.go.dev/context). However, there was still no sane implementation in Python, until the [cantok](https://github.com/pomponchik/cantok) library appeared. -The essence of the pattern is that we pass special objects to functions and constructors, by which the executed code can understand whether it should continue its execution or not. When deciding whether to allow code execution to continue, this object can both take into account the restrictions specified to it, such as the maximum code execution time, and receive signals about the need to stop from the outside, for example from another thread or a coroutine. Thus, we do not nail down the logic associated with stopping code execution, for example, by directly tracking loop counters, but implement [Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection) of this restriction. +The essence of the pattern is that we pass special objects to functions and constructors, by which the executed code can understand whether it should continue its execution or not. When deciding whether to allow code execution to continue, this object can both take into account the restrictions imposed on it, such as the maximum code execution time, and receive signals about the need to stop from the outside, for example from another thread or a coroutine. Thus, we do not nail down the logic associated with stopping code execution, for example, by directly tracking loop counters, but implement [Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection) of this restriction. -In addition, the pattern assumes that various restrictions can be combined in unlimited combinations with each other: if at least one of the restrictions is not met, code execution will be interrupted. It is assumed that each function in the call stack will call other functions, passing its token directly to them, or wrapping it in another token, with a stricter restriction imposed on it. +In addition, the pattern assumes that various restrictions can be combined in unlimited combinations with each other: if at least one of the restrictions is not met, code execution will be interrupted. It is assumed that each function in the call stack will call other functions, passing its token directly to them, or wrapping it in another token with stricter restrictions. -Unlike other ways of interrupting code execution, tokens do not force the execution thread to be interrupted. The interruption occurs "gently", allowing the code to terminate correctly, release all held resources and restore consistency. +Unlike other ways of stopping code execution, tokens do not force the execution thread to be interrupted. The interruption occurs "gently", allowing the code to terminate correctly, release all held resources and restore consistency. It is highly desirable for library developers to use this pattern for any long-running operations. Your function can accept a token as an optional argument, with a default value that imposes minimal restrictions or none at all. If the user wishes, they can pass their token to it, imposing stricter restrictions on the library code. In addition to a more convenient and extensible API, this will give the library an advantage in the form of better testability, because the restrictions are no longer hardcoded into the function, which means they can be made whatever you want for the test. In addition, the library developer no longer needs to think about all the numerous restrictions that can be imposed on their code - the user can take care of it themselves if they need to. diff --git a/docs/types_of_tokens/ConditionToken.md b/docs/types_of_tokens/ConditionToken.md index c11f57a..0d0e64f 100644 --- a/docs/types_of_tokens/ConditionToken.md +++ b/docs/types_of_tokens/ConditionToken.md @@ -12,7 +12,7 @@ while token: print(counter) #> 5 ``` -By default, if the passed function raises an exception, it will be silently suppressed. However, you can make the raised exceptions explicit by setting the `suppress_exceptions` parameter to `False`: +By default, if the passed function raises an exception, the exception will be silently suppressed. However, you can make the raised exceptions explicit by setting the `suppress_exceptions` parameter to `False`: ```python def function(): raise ValueError @@ -54,7 +54,7 @@ token.check() #> 2 ``` -`ConditionToken` has another feature. If the condition has triggered at least once and cancelled the token, then the condition is no longer polled and the token is permanently considered cancelled. You can change this by manipulating the `caching` parameter when creating a token. By setting it to `False`, you will make sure that the condition is polled every time. +`ConditionToken` has another feature. If the condition has evaluated to True at least once and cancelled the token, then the condition is no longer polled and the token is permanently considered cancelled. You can change this by manipulating the `caching` parameter when creating a token. By setting it to `False`, you will make sure that the condition is polled every time. ```python counter = 0 @@ -75,4 +75,4 @@ print(token.cancelled) #> False ``` -However, we do not recommend doing this. In the vast majority of cases, you do not want your token to be able to roll back the fact of its cancellation. If the token has been cancelled once, it must remain cancelled. Manipulate the `caching` parameter only if you are sure that you understand what you are doing. +However, we do not recommend doing this. In the vast majority of cases, you do not want your token to be able to undo its cancellation. If the token has been cancelled once, it must remain cancelled. Manipulate the `caching` parameter only if you are sure that you understand what you are doing. diff --git a/docs/types_of_tokens/CounterToken.md b/docs/types_of_tokens/CounterToken.md index ff9c431..4d82893 100644 --- a/docs/types_of_tokens/CounterToken.md +++ b/docs/types_of_tokens/CounterToken.md @@ -20,7 +20,7 @@ The counter inside the `CounterToken` is decremented under one of three conditio - Calling the `is_cancelled()` method. - Calling the `keep_on()` method. -If you use `CounterToken` inside other tokens, the wrapping token can query the status of the `CounterToken`. To avoid unintended side effects, this operation does not decrease the counter. However, if for some reason you need it to decrease, pass `direct=False` as an argument: +If you use `CounterToken` inside other tokens, the wrapping token can query the status of the `CounterToken`. To avoid unintended side effects, querying the status does not decrease the counter. However, if for some reason you need it to decrease, pass `direct=False` as an argument: ```python from cantok import SimpleToken, CounterToken diff --git a/docs/what_are_tokens/cancel_and_read_the_status.md b/docs/what_are_tokens/cancel_and_read_the_status.md index c233021..256a0c9 100644 --- a/docs/what_are_tokens/cancel_and_read_the_status.md +++ b/docs/what_are_tokens/cancel_and_read_the_status.md @@ -34,7 +34,7 @@ print(token.cancelled) #> True print(token.is_cancelled()) #> True ``` -Choose what you like best. The attribute is considered more elegant, but the method call more clearly reflects the complexity of the work that is actually being done to answer the question "has the token been cancelled?". +Choose what you like best. The attribute is considered more elegant, but calling the method more clearly reflects the complexity of the work actually being done to answer the question "has the token been cancelled?". There is another method opposite to `is_cancelled()` — `keep_on()`. It answers the opposite question, and can be used in the same situations: diff --git a/docs/what_are_tokens/embedding.md b/docs/what_are_tokens/embedding.md index 4320208..5e14e7a 100644 --- a/docs/what_are_tokens/embedding.md +++ b/docs/what_are_tokens/embedding.md @@ -1,4 +1,4 @@ -An unlimited number of other tokens can be embedded in one token as arguments during initialization. Each time checking whether it has been cancelled, the token first checks its cancellation rules, and if it has not been canceled itself, then it checks the tokens nested in it. Thus, one cancelled token nested in another non-cancelled token cancels it: +You can embed an unlimited number of other tokens in one token by passing them as arguments during initialization. Each time checking whether it has been cancelled, the token first checks its cancellation rules, and if it has not been canceled itself, then it checks the tokens nested in it. Thus, one cancelled token nested in another non-cancelled token cancels it: ```python from cantok import SimpleToken diff --git a/docs/what_are_tokens/exceptions.md b/docs/what_are_tokens/exceptions.md index 7ed9a85..c874a37 100644 --- a/docs/what_are_tokens/exceptions.md +++ b/docs/what_are_tokens/exceptions.md @@ -17,7 +17,7 @@ Each type of token (except [`DefaultToken`](../types_of_tokens/DefaultToken.md)) - [`TimeoutToken`](../types_of_tokens/TimeoutToken.md) -> `TimeoutCancellationError` - [`CounterToken`](../types_of_tokens/CounterToken.md) -> `CounterCancellationError` -When you call the `check()` method on any token, one of two things will happen. If it (or any of the tokens nested in it) was cancelled by calling the `cancel()` method, `CancellationError` will always be raised. But if the cancellation occurred as a result of the unique ability of the token, such as for `TimeoutToken` - timeout expiration, then an exception specific to this type of token will be raised. +When you call the `check()` method on any token, one of two things will happen. If it (or any of the tokens nested in it) has been cancelled by calling the `cancel()` method, `CancellationError` will always be raised. But if the cancellation occurred as a result of the unique ability of the token, such as for `TimeoutToken` - timeout expiration, then an exception specific to this type of token will be raised. `ConditionCancellationError`, `TimeoutCancellationError` and `CounterCancellationError` are inherited from `CancellationError`, so if you're not sure which exception specifically you're catching, catch `CancellationError`. But also all the listed exceptions can always be imported separately: @@ -25,7 +25,7 @@ When you call the `check()` method on any token, one of two things will happen. from cantok import CancellationError, ConditionCancellationError, TimeoutCancellationError, CounterCancellationError ``` -You can also choose not to import these exceptions at all. For each token class, the corresponding exception class is located in the `exception` attribute: +You can also choose not to import these exceptions at all. For each token class, the corresponding exception class is accessible as the `exception` attribute: ```python from cantok import TimeoutToken, CancellationError