cantok 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
cantok-0.0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 pomponchik
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
cantok-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,339 @@
1
+ Metadata-Version: 2.1
2
+ Name: cantok
3
+ Version: 0.0.1
4
+ Summary: Implementation of the "Cancellation Token" pattern
5
+ Home-page: https://github.com/pomponchik/cantok
6
+ Author: Evgeniy Blinov
7
+ Author-email: zheni-b@yandex.ru
8
+ Classifier: Operating System :: MacOS :: MacOS X
9
+ Classifier: Operating System :: Microsoft :: Windows
10
+ Classifier: Operating System :: POSIX
11
+ Classifier: Operating System :: POSIX :: Linux
12
+ Classifier: Programming Language :: Python
13
+ Classifier: Programming Language :: Python :: 3.7
14
+ Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: License :: OSI Approved :: MIT License
19
+ Classifier: Intended Audience :: Developers
20
+ Classifier: Topic :: Software Development :: Libraries
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+
24
+ ![logo](https://raw.githubusercontent.com/pomponchik/cantok/develop/docs/assets/logo_2.png)
25
+
26
+ [![Downloads](https://static.pepy.tech/badge/cantok/month)](https://pepy.tech/project/cantok)
27
+ [![Downloads](https://static.pepy.tech/badge/cantok)](https://pepy.tech/project/cantok)
28
+ [![codecov](https://codecov.io/gh/pomponchik/cantok/graph/badge.svg?token=eZ4eK6fkmx)](https://codecov.io/gh/pomponchik/cantok)
29
+ [![Test-Package](https://github.com/pomponchik/cantok/actions/workflows/tests_and_coverage.yml/badge.svg)](https://github.com/pomponchik/cantok/actions/workflows/tests_and_coverage.yml)
30
+ [![Python versions](https://img.shields.io/pypi/pyversions/cantok.svg)](https://pypi.python.org/pypi/cantok)
31
+ [![PyPI version](https://badge.fury.io/py/cantok.svg)](https://badge.fury.io/py/cantok)
32
+ [![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/)
33
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
34
+
35
+
36
+ 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.
37
+
38
+
39
+ ## Table of contents
40
+
41
+ - [**Quick start**](#quick-start)
42
+ - [**The pattern**](#the-pattern)
43
+ - [**Tokens**](#tokens)
44
+ - [**Simple token**](#simple-token)
45
+ - [**Condition token**](#simple-token)
46
+ - [**Timeout token**](#timeout-token)
47
+ - [**Counter token**](#counter-token)
48
+
49
+
50
+ ## Quick start
51
+
52
+ Install [it](https://pypi.org/project/cantok/):
53
+
54
+ ```bash
55
+ pip install cantok
56
+ ```
57
+
58
+ And use:
59
+
60
+ ```python
61
+ from random import randint
62
+ from threading import Thread
63
+
64
+ from cantok import ConditionToken, CounterToken, TimeoutToken
65
+
66
+
67
+ counter = 0
68
+
69
+ def function(token):
70
+ global counter
71
+ while not token.cancelled:
72
+ counter += 1
73
+
74
+ token = ConditionToken(lambda: randint(1, 100_000) == 1984) + CounterToken(400_000, direct=False) + TimeoutToken(1)
75
+ thread = Thread(target=function, args=(token, ))
76
+ thread.start()
77
+ thread.join()
78
+
79
+ print(counter)
80
+ ```
81
+
82
+ In this example, we pass a token to the function that describes several restrictions: on the [number of iterations](#counter-token) of the cycle, on [time](#timeout-token), as well as on the [occurrence](#condition-token) of a random unlikely event. When any of the indicated events occur, the cycle stops.
83
+
84
+ Read more about the [possibilities of tokens](#tokens), as well as about the [pattern in general](#the-pattern).
85
+
86
+
87
+ ## The pattern
88
+
89
+ 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.
90
+
91
+ 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.
92
+
93
+ 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.
94
+
95
+ 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.
96
+
97
+
98
+ ## Tokens
99
+
100
+ All token classes presented in this library have a uniform interface. And they are all inherited from one class: `AbstractToken`. The only reason why you might want to import it is to use it for a type hint. This example illustrates a type hint suitable for any of the tokens:
101
+
102
+ ```python
103
+ from cantok import AbstractToken
104
+
105
+ def function(token: AbstractToken):
106
+ ...
107
+ ```
108
+
109
+ Each token object has a `cancelled` attribute and a `cancel()` method. By the attribute, you can find out whether this token has been canceled:
110
+
111
+ ```python
112
+ from cantok import SimpleToken
113
+
114
+ token = SimpleToken()
115
+ print(token.cancelled) # False
116
+ token.cancel()
117
+ print(token.cancelled) # True
118
+ ```
119
+
120
+ The cancelled attribute is dynamically calculated and takes into account, among other things, specific conditions that are checked by a specific token. Here is an example with a [token that measures time](#timeout-token):
121
+
122
+ ```python
123
+ from time import sleep
124
+ from cantok import TimeoutToken
125
+
126
+ token = TimeoutToken(5)
127
+ print(token.cancelled) # False
128
+ sleep(10)
129
+ print(token.cancelled) # True
130
+ ```
131
+
132
+ In addition to this attribute, each token implements the `is_cancelled()` method. It does exactly the same thing as the attribute:
133
+
134
+ ```python
135
+ from cantok import SimpleToken
136
+
137
+ token = SimpleToken()
138
+ print(token.cancelled) # False
139
+ print(token.is_cancelled()) # False
140
+ token.cancel()
141
+ print(token.cancelled) # True
142
+ print(token.is_cancelled()) # True
143
+ ```
144
+
145
+ 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?".
146
+
147
+ There is another method opposite to `is_cancelled()` - `keep_on()`. It answers the opposite question, and can be used in the same situations:
148
+
149
+ ```python
150
+ from cantok import SimpleToken
151
+
152
+ token = SimpleToken()
153
+ print(token.cancelled) # False
154
+ print(token.keep_on()) # True
155
+ token.cancel()
156
+ print(token.cancelled) # True
157
+ print(token.keep_on()) # False
158
+ ```
159
+
160
+ 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:
161
+
162
+ ```python
163
+ from cantok import SimpleToken
164
+
165
+ first_token = SimpleToken()
166
+ second_token = SimpleToken()
167
+ third_token = SimpleToken(first_token, second_token)
168
+
169
+ first_token.cancel()
170
+
171
+ print(first_token.cancelled) # True
172
+ print(second_token.cancelled) # False
173
+ print(third_token.cancelled) # True
174
+ ```
175
+
176
+ In addition, any tokens can be summed up among themselves. The summation operation generates another [`SimpleToken`](#simple-token) that includes the previous 2:
177
+
178
+ ```python
179
+ from cantok import SimpleToken, TimeoutToken
180
+
181
+ print(repr(SimpleToken() + TimeoutToken(5)))
182
+ # SimpleToken(SimpleToken(cancelled=False), TimeoutToken(5, cancelled=False, monotonic=False), cancelled=False)
183
+ ```
184
+
185
+ 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:
186
+
187
+ ```python
188
+ from cantok import AbstractToken, TimeoutToken
189
+
190
+ def function(token: AbstractToken):
191
+ ...
192
+ another_function(token + TimeoutToken(5)) # Imposes an additional restriction on the function being called: work for no more than 5 seconds. At the same time, it does not know anything about what restrictions were imposed earlier.
193
+ ...
194
+ ```
195
+
196
+ Read on about the features of each type of tokens in more detail.
197
+
198
+
199
+ ### Simple token
200
+
201
+ The base token is `SimpleToken`. It has no built-in automation that can cancel it. The only way to cancel `SimpleToken` is to explicitly call the `cancel()` method from it.
202
+
203
+ ```python
204
+ from cantok import SimpleToken
205
+
206
+ token = SimpleToken()
207
+ print(token.cancelled) # False
208
+ token.cancel()
209
+ print(token.cancelled) # True
210
+ ```
211
+
212
+ `SimpleToken` is also implicitly generated by the operation of summing two other tokens:
213
+
214
+ ```python
215
+ from cantok import CounterToken, TimeoutToken
216
+
217
+ print(repr(CounterToken(5) + TimeoutToken(5)))
218
+ # SimpleToken(CounterToken(5, cancelled=False, direct=True), TimeoutToken(5, cancelled=False, monotonic=False), cancelled=False)
219
+ ```
220
+
221
+ There is not much more to tell about it if you have read [the story](#tokens) about tokens in general.
222
+
223
+
224
+ ### Condition token
225
+
226
+ A slightly more complex type of token than [`SimpleToken`](#simple-token) is `ConditionToken`. In addition to everything that `SimpleToken` does, it also checks the condition passed to it as a first argument, answering the question whether it has been canceled.
227
+
228
+ To initialize `ConditionToken`, pass a function to it that does not accept arguments and returns a boolean value. If it returns `True`, it means that the operation has been canceled:
229
+
230
+ ```python
231
+ from cantok import ConditionToken
232
+
233
+ counter = 5
234
+ token = ConditionToken(lambda: counter >= 5)
235
+
236
+ while not token.cancelled:
237
+ counter += 1
238
+
239
+ print(counter) # 5
240
+ ```
241
+
242
+ 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`:
243
+
244
+ ```python
245
+ def function(): raise ValueError
246
+
247
+ token = ConditionToken(function, suppress_exceptions=False)
248
+
249
+ token.cancelled # ValueError has risen.
250
+ ```
251
+
252
+ 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`.
253
+
254
+ ```python
255
+ def function(): raise ValueError
256
+
257
+ print(ConditionToken(function).cancelled) # False
258
+ print(ConditionToken(function, default=False).cancelled) # False
259
+ print(ConditionToken(function, default=True).cancelled) # True
260
+ ```
261
+
262
+ `ConditionToken` may include other tokens during initialization:
263
+
264
+ ```python
265
+ token = ConditionToken(lambda: False, SimpleToken(), TimeoutToken(5), CounterToken(20)) # Includes all additional restrictions of the passed tokens.
266
+ ```
267
+
268
+ ### Timeout token
269
+
270
+ `TimeoutToken` is automatically canceled after the time specified in seconds in the class constructor:
271
+
272
+ ```python
273
+ from time import sleep
274
+ from cantok import TimeoutToken
275
+
276
+ token = TimeoutToken(5)
277
+ print(token.cancelled) # False
278
+ sleep(10)
279
+ print(token.cancelled) # True
280
+ ```
281
+
282
+ Just like `ConditionToken`, `TimeoutToken` can include other tokens:
283
+
284
+ ```python
285
+ token = TimeoutToken(45, SimpleToken(), TimeoutToken(5), CounterToken(20)) # Includes all additional restrictions of the passed tokens.
286
+ ```
287
+
288
+ 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:
289
+
290
+ ```python
291
+ token = TimeoutToken(33, monotonic=True)
292
+ ```
293
+
294
+ ### Counter token
295
+
296
+ `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.
297
+
298
+ `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:
299
+
300
+ ```python
301
+ from cantok import CounterToken
302
+
303
+ token = CounterToken(5)
304
+ counter = 0
305
+
306
+ while not token.cancelled:
307
+ counter += 1
308
+
309
+ print(counter) # 5
310
+ ```
311
+
312
+ The counter inside the `CounterToken` is reduced under one of three conditions:
313
+
314
+ - Access to the `cancelled` attribute.
315
+ - Calling the `is_cancelled()` method.
316
+ - Calling the `keep_on()` method.
317
+
318
+ 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:
319
+
320
+ ```python
321
+ from cantok import SimpleToken, CounterToken
322
+
323
+ first_counter_token = CounterToken(1, direct=False)
324
+ second_counter_token = CounterToken(1, direct=True)
325
+
326
+ print(SimpleToken(first_counter_token, second_counter_token).cancelled) # False
327
+ print(first_counter_token.cancelled) # True
328
+ print(second_counter_token.cancelled) # False
329
+ ```
330
+
331
+ Like all other tokens, `CounterToken` can accept other tokens as parameters during initialization:
332
+
333
+ ```python
334
+ from cantok import SimpleToken, CounterToken, TimeoutToken
335
+
336
+ token = CounterToken(15, SimpleToken(), TimeoutToken(5))
337
+ ```
338
+
339
+ `CounterToken` is thread-safe.
cantok-0.0.1/README.md ADDED
@@ -0,0 +1,316 @@
1
+ ![logo](https://raw.githubusercontent.com/pomponchik/cantok/develop/docs/assets/logo_2.png)
2
+
3
+ [![Downloads](https://static.pepy.tech/badge/cantok/month)](https://pepy.tech/project/cantok)
4
+ [![Downloads](https://static.pepy.tech/badge/cantok)](https://pepy.tech/project/cantok)
5
+ [![codecov](https://codecov.io/gh/pomponchik/cantok/graph/badge.svg?token=eZ4eK6fkmx)](https://codecov.io/gh/pomponchik/cantok)
6
+ [![Test-Package](https://github.com/pomponchik/cantok/actions/workflows/tests_and_coverage.yml/badge.svg)](https://github.com/pomponchik/cantok/actions/workflows/tests_and_coverage.yml)
7
+ [![Python versions](https://img.shields.io/pypi/pyversions/cantok.svg)](https://pypi.python.org/pypi/cantok)
8
+ [![PyPI version](https://badge.fury.io/py/cantok.svg)](https://badge.fury.io/py/cantok)
9
+ [![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/)
10
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
11
+
12
+
13
+ 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.
14
+
15
+
16
+ ## Table of contents
17
+
18
+ - [**Quick start**](#quick-start)
19
+ - [**The pattern**](#the-pattern)
20
+ - [**Tokens**](#tokens)
21
+ - [**Simple token**](#simple-token)
22
+ - [**Condition token**](#simple-token)
23
+ - [**Timeout token**](#timeout-token)
24
+ - [**Counter token**](#counter-token)
25
+
26
+
27
+ ## Quick start
28
+
29
+ Install [it](https://pypi.org/project/cantok/):
30
+
31
+ ```bash
32
+ pip install cantok
33
+ ```
34
+
35
+ And use:
36
+
37
+ ```python
38
+ from random import randint
39
+ from threading import Thread
40
+
41
+ from cantok import ConditionToken, CounterToken, TimeoutToken
42
+
43
+
44
+ counter = 0
45
+
46
+ def function(token):
47
+ global counter
48
+ while not token.cancelled:
49
+ counter += 1
50
+
51
+ token = ConditionToken(lambda: randint(1, 100_000) == 1984) + CounterToken(400_000, direct=False) + TimeoutToken(1)
52
+ thread = Thread(target=function, args=(token, ))
53
+ thread.start()
54
+ thread.join()
55
+
56
+ print(counter)
57
+ ```
58
+
59
+ In this example, we pass a token to the function that describes several restrictions: on the [number of iterations](#counter-token) of the cycle, on [time](#timeout-token), as well as on the [occurrence](#condition-token) of a random unlikely event. When any of the indicated events occur, the cycle stops.
60
+
61
+ Read more about the [possibilities of tokens](#tokens), as well as about the [pattern in general](#the-pattern).
62
+
63
+
64
+ ## The pattern
65
+
66
+ 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.
67
+
68
+ 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.
69
+
70
+ 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.
71
+
72
+ 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.
73
+
74
+
75
+ ## Tokens
76
+
77
+ All token classes presented in this library have a uniform interface. And they are all inherited from one class: `AbstractToken`. The only reason why you might want to import it is to use it for a type hint. This example illustrates a type hint suitable for any of the tokens:
78
+
79
+ ```python
80
+ from cantok import AbstractToken
81
+
82
+ def function(token: AbstractToken):
83
+ ...
84
+ ```
85
+
86
+ Each token object has a `cancelled` attribute and a `cancel()` method. By the attribute, you can find out whether this token has been canceled:
87
+
88
+ ```python
89
+ from cantok import SimpleToken
90
+
91
+ token = SimpleToken()
92
+ print(token.cancelled) # False
93
+ token.cancel()
94
+ print(token.cancelled) # True
95
+ ```
96
+
97
+ The cancelled attribute is dynamically calculated and takes into account, among other things, specific conditions that are checked by a specific token. Here is an example with a [token that measures time](#timeout-token):
98
+
99
+ ```python
100
+ from time import sleep
101
+ from cantok import TimeoutToken
102
+
103
+ token = TimeoutToken(5)
104
+ print(token.cancelled) # False
105
+ sleep(10)
106
+ print(token.cancelled) # True
107
+ ```
108
+
109
+ In addition to this attribute, each token implements the `is_cancelled()` method. It does exactly the same thing as the attribute:
110
+
111
+ ```python
112
+ from cantok import SimpleToken
113
+
114
+ token = SimpleToken()
115
+ print(token.cancelled) # False
116
+ print(token.is_cancelled()) # False
117
+ token.cancel()
118
+ print(token.cancelled) # True
119
+ print(token.is_cancelled()) # True
120
+ ```
121
+
122
+ 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?".
123
+
124
+ There is another method opposite to `is_cancelled()` - `keep_on()`. It answers the opposite question, and can be used in the same situations:
125
+
126
+ ```python
127
+ from cantok import SimpleToken
128
+
129
+ token = SimpleToken()
130
+ print(token.cancelled) # False
131
+ print(token.keep_on()) # True
132
+ token.cancel()
133
+ print(token.cancelled) # True
134
+ print(token.keep_on()) # False
135
+ ```
136
+
137
+ 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:
138
+
139
+ ```python
140
+ from cantok import SimpleToken
141
+
142
+ first_token = SimpleToken()
143
+ second_token = SimpleToken()
144
+ third_token = SimpleToken(first_token, second_token)
145
+
146
+ first_token.cancel()
147
+
148
+ print(first_token.cancelled) # True
149
+ print(second_token.cancelled) # False
150
+ print(third_token.cancelled) # True
151
+ ```
152
+
153
+ In addition, any tokens can be summed up among themselves. The summation operation generates another [`SimpleToken`](#simple-token) that includes the previous 2:
154
+
155
+ ```python
156
+ from cantok import SimpleToken, TimeoutToken
157
+
158
+ print(repr(SimpleToken() + TimeoutToken(5)))
159
+ # SimpleToken(SimpleToken(cancelled=False), TimeoutToken(5, cancelled=False, monotonic=False), cancelled=False)
160
+ ```
161
+
162
+ 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:
163
+
164
+ ```python
165
+ from cantok import AbstractToken, TimeoutToken
166
+
167
+ def function(token: AbstractToken):
168
+ ...
169
+ another_function(token + TimeoutToken(5)) # Imposes an additional restriction on the function being called: work for no more than 5 seconds. At the same time, it does not know anything about what restrictions were imposed earlier.
170
+ ...
171
+ ```
172
+
173
+ Read on about the features of each type of tokens in more detail.
174
+
175
+
176
+ ### Simple token
177
+
178
+ The base token is `SimpleToken`. It has no built-in automation that can cancel it. The only way to cancel `SimpleToken` is to explicitly call the `cancel()` method from it.
179
+
180
+ ```python
181
+ from cantok import SimpleToken
182
+
183
+ token = SimpleToken()
184
+ print(token.cancelled) # False
185
+ token.cancel()
186
+ print(token.cancelled) # True
187
+ ```
188
+
189
+ `SimpleToken` is also implicitly generated by the operation of summing two other tokens:
190
+
191
+ ```python
192
+ from cantok import CounterToken, TimeoutToken
193
+
194
+ print(repr(CounterToken(5) + TimeoutToken(5)))
195
+ # SimpleToken(CounterToken(5, cancelled=False, direct=True), TimeoutToken(5, cancelled=False, monotonic=False), cancelled=False)
196
+ ```
197
+
198
+ There is not much more to tell about it if you have read [the story](#tokens) about tokens in general.
199
+
200
+
201
+ ### Condition token
202
+
203
+ A slightly more complex type of token than [`SimpleToken`](#simple-token) is `ConditionToken`. In addition to everything that `SimpleToken` does, it also checks the condition passed to it as a first argument, answering the question whether it has been canceled.
204
+
205
+ To initialize `ConditionToken`, pass a function to it that does not accept arguments and returns a boolean value. If it returns `True`, it means that the operation has been canceled:
206
+
207
+ ```python
208
+ from cantok import ConditionToken
209
+
210
+ counter = 5
211
+ token = ConditionToken(lambda: counter >= 5)
212
+
213
+ while not token.cancelled:
214
+ counter += 1
215
+
216
+ print(counter) # 5
217
+ ```
218
+
219
+ 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`:
220
+
221
+ ```python
222
+ def function(): raise ValueError
223
+
224
+ token = ConditionToken(function, suppress_exceptions=False)
225
+
226
+ token.cancelled # ValueError has risen.
227
+ ```
228
+
229
+ 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`.
230
+
231
+ ```python
232
+ def function(): raise ValueError
233
+
234
+ print(ConditionToken(function).cancelled) # False
235
+ print(ConditionToken(function, default=False).cancelled) # False
236
+ print(ConditionToken(function, default=True).cancelled) # True
237
+ ```
238
+
239
+ `ConditionToken` may include other tokens during initialization:
240
+
241
+ ```python
242
+ token = ConditionToken(lambda: False, SimpleToken(), TimeoutToken(5), CounterToken(20)) # Includes all additional restrictions of the passed tokens.
243
+ ```
244
+
245
+ ### Timeout token
246
+
247
+ `TimeoutToken` is automatically canceled after the time specified in seconds in the class constructor:
248
+
249
+ ```python
250
+ from time import sleep
251
+ from cantok import TimeoutToken
252
+
253
+ token = TimeoutToken(5)
254
+ print(token.cancelled) # False
255
+ sleep(10)
256
+ print(token.cancelled) # True
257
+ ```
258
+
259
+ Just like `ConditionToken`, `TimeoutToken` can include other tokens:
260
+
261
+ ```python
262
+ token = TimeoutToken(45, SimpleToken(), TimeoutToken(5), CounterToken(20)) # Includes all additional restrictions of the passed tokens.
263
+ ```
264
+
265
+ 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:
266
+
267
+ ```python
268
+ token = TimeoutToken(33, monotonic=True)
269
+ ```
270
+
271
+ ### Counter token
272
+
273
+ `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.
274
+
275
+ `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:
276
+
277
+ ```python
278
+ from cantok import CounterToken
279
+
280
+ token = CounterToken(5)
281
+ counter = 0
282
+
283
+ while not token.cancelled:
284
+ counter += 1
285
+
286
+ print(counter) # 5
287
+ ```
288
+
289
+ The counter inside the `CounterToken` is reduced under one of three conditions:
290
+
291
+ - Access to the `cancelled` attribute.
292
+ - Calling the `is_cancelled()` method.
293
+ - Calling the `keep_on()` method.
294
+
295
+ 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:
296
+
297
+ ```python
298
+ from cantok import SimpleToken, CounterToken
299
+
300
+ first_counter_token = CounterToken(1, direct=False)
301
+ second_counter_token = CounterToken(1, direct=True)
302
+
303
+ print(SimpleToken(first_counter_token, second_counter_token).cancelled) # False
304
+ print(first_counter_token.cancelled) # True
305
+ print(second_counter_token.cancelled) # False
306
+ ```
307
+
308
+ Like all other tokens, `CounterToken` can accept other tokens as parameters during initialization:
309
+
310
+ ```python
311
+ from cantok import SimpleToken, CounterToken, TimeoutToken
312
+
313
+ token = CounterToken(15, SimpleToken(), TimeoutToken(5))
314
+ ```
315
+
316
+ `CounterToken` is thread-safe.
@@ -0,0 +1,8 @@
1
+ from cantok.tokens.abstract_token import AbstractToken # noqa: F401
2
+ from cantok.tokens.simple_token import SimpleToken # noqa: F401
3
+ from cantok.tokens.condition_token import ConditionToken # noqa: F401
4
+ from cantok.tokens.counter_token import CounterToken # noqa: F401
5
+ from cantok.tokens.timeout_token import TimeoutToken
6
+
7
+
8
+ TimeOutToken = TimeoutToken
File without changes