tg-guard 0.1.0.5__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.
Files changed (40) hide show
  1. tg_guard-0.1.0.5/LICENSE.txt +21 -0
  2. tg_guard-0.1.0.5/PKG-INFO +365 -0
  3. tg_guard-0.1.0.5/README.md +344 -0
  4. tg_guard-0.1.0.5/pyproject.toml +35 -0
  5. tg_guard-0.1.0.5/setup.cfg +4 -0
  6. tg_guard-0.1.0.5/tg_guard.egg-info/PKG-INFO +365 -0
  7. tg_guard-0.1.0.5/tg_guard.egg-info/SOURCES.txt +38 -0
  8. tg_guard-0.1.0.5/tg_guard.egg-info/dependency_links.txt +1 -0
  9. tg_guard-0.1.0.5/tg_guard.egg-info/requires.txt +1 -0
  10. tg_guard-0.1.0.5/tg_guard.egg-info/top_level.txt +1 -0
  11. tg_guard-0.1.0.5/tokenguard/__init__.py +42 -0
  12. tg_guard-0.1.0.5/tokenguard/admission_gate.py +175 -0
  13. tg_guard-0.1.0.5/tokenguard/convergence_engine.py +727 -0
  14. tg_guard-0.1.0.5/tokenguard/core_affinity_queue.py +160 -0
  15. tg_guard-0.1.0.5/tokenguard/core_pinned_staggered_queue.py +704 -0
  16. tg_guard-0.1.0.5/tokenguard/demo/__init__.py +0 -0
  17. tg_guard-0.1.0.5/tokenguard/guard_house.py +415 -0
  18. tg_guard-0.1.0.5/tokenguard/hash_conductor.py +328 -0
  19. tg_guard-0.1.0.5/tokenguard/operations_coordinator.py +460 -0
  20. tg_guard-0.1.0.5/tokenguard/overflow_guard.py +232 -0
  21. tg_guard-0.1.0.5/tokenguard/setup.py +65 -0
  22. tg_guard-0.1.0.5/tokenguard/sticky_token.py +139 -0
  23. tg_guard-0.1.0.5/tokenguard/storage_throttle.py +293 -0
  24. tg_guard-0.1.0.5/tokenguard/tests/__init__.py +0 -0
  25. tg_guard-0.1.0.5/tokenguard/tests/max_concurrency_test.py +348 -0
  26. tg_guard-0.1.0.5/tokenguard/tests/ops/__init__.py +0 -0
  27. tg_guard-0.1.0.5/tokenguard/tests/ops/cache_storm.py +247 -0
  28. tg_guard-0.1.0.5/tokenguard/tests/ops/cache_storm_ops.py +36 -0
  29. tg_guard-0.1.0.5/tokenguard/tests/ops/chain_ops.py +180 -0
  30. tg_guard-0.1.0.5/tokenguard/tests/ops/cpu_ops.py +101 -0
  31. tg_guard-0.1.0.5/tokenguard/tests/ops/hash_conductor_ops.py +58 -0
  32. tg_guard-0.1.0.5/tokenguard/tests/ops/hash_conductor_test.py +181 -0
  33. tg_guard-0.1.0.5/tokenguard/tests/ops/io_ops.py +50 -0
  34. tg_guard-0.1.0.5/tokenguard/tests/test_runner.py +369 -0
  35. tg_guard-0.1.0.5/tokenguard/tg_print.py +202 -0
  36. tg_guard-0.1.0.5/tokenguard/threading_metrics.py +258 -0
  37. tg_guard-0.1.0.5/tokenguard/token_options.py +217 -0
  38. tg_guard-0.1.0.5/tokenguard/token_system.py +844 -0
  39. tg_guard-0.1.0.5/tokenguard/topology_detector.py +149 -0
  40. tg_guard-0.1.0.5/tokenguard/unhashable_checker.py +794 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Tavari
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.
@@ -0,0 +1,365 @@
1
+ Metadata-Version: 2.4
2
+ Name: tg-guard
3
+ Version: 0.1.0.5
4
+ Summary: Lightweight, decorator-first task routing for Python.
5
+ Author: Tavari
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/TavariAgent/tg-guard
8
+ Project-URL: Repository, https://github.com/TavariAgent/tg-guard
9
+ Project-URL: Issues, https://github.com/TavariAgent/tg-guard/issues
10
+ Keywords: async,threading,task-queue,concurrency,decorator,worker-pool,cpu-pinning
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
15
+ Classifier: Topic :: System :: Distributed Computing
16
+ Requires-Python: >=3.12
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE.txt
19
+ Requires-Dist: psutil>=5.9
20
+ Dynamic: license-file
21
+
22
+ # TokenGuard
23
+
24
+ > Lightweight, decorator-first task routing for Python.
25
+ > A focused branch of [TokenGate](https://github.com/TavariAgent/Py-TokenGate) — same core dispatch model, no extras.
26
+
27
+ ---
28
+
29
+ ## What It Does
30
+
31
+ TokenGuard routes your functions to dedicated CPU-pinned workers automatically. Decorate a function, call it, and it dispatches to the right worker without blocking the caller — no manual thread management, no boilerplate, no complexity creep between threaded and async contexts.
32
+
33
+ The routing is weight-aware and cache-conscious: heavy tasks stay isolated on core 1, lighter work spreads across the rest, and the convergence engine adjusts active worker counts live under load. You get the performance characteristics of a well-tuned thread pool without having to build or maintain one.
34
+
35
+
36
+ ---
37
+
38
+ ## How It Works
39
+
40
+ When you decorate a function with `@task_token_guard`, calling it no longer executes it directly. Instead, a `TaskToken` is created and handed to the coordinator's admission queue. The coordinator routes the token to a pinned mailbox worker based on its weight class and current core load, executes it there, and delivers the result back through the token. The caller keeps moving immediately — no blocking, no manual thread management.
41
+
42
+ The staggered position system ensures tokens are spread across workers in a predictable, thread-safe sequence. Each core tracks its own monotonic counter, and position arithmetic naturally shuffles assignments across the active worker slots without locks on the hot path. The stride stays globally consistent even when convergence changes the active worker count at runtime — assignments never break mid-flight.
43
+
44
+ ---
45
+
46
+ ## Installation
47
+
48
+ ```bash
49
+ pip install tg-guard
50
+ ```
51
+
52
+ ---
53
+
54
+ ## Quick Start
55
+
56
+ ### Run the tests
57
+
58
+ ```bash
59
+ python -m tokenguard.tests.test_runner
60
+
61
+ OR
62
+
63
+ python -m tokenguard.tests.max_concurrency_test
64
+ ```
65
+
66
+ ### Then give it a try (in your own code)
67
+
68
+ ```python
69
+ from tokenguard import task_token_guard, OperationsCoordinator
70
+
71
+ coordinator = OperationsCoordinator()
72
+ coordinator.start()
73
+
74
+ @task_token_guard(operation_type='resize_image', tags={'weight': 'heavy'})
75
+ def resize_image(path, size):
76
+ ...
77
+
78
+ # Caller is not blocked — resize_image runs on a worker
79
+ token = resize_image('photo.jpg', (1920, 1080))
80
+
81
+ # Result available when ready
82
+ result = token.get(timeout=30.0)
83
+
84
+ # Or awaitable
85
+ result = await token
86
+
87
+ # Always clean up the coordinator on shutdown
88
+ coordinator.stop()
89
+ ```
90
+
91
+ ---
92
+
93
+ ## Configuration
94
+
95
+ TokenGuard ships with sensible defaults. Use `option` and `tg_option` at module
96
+ level to override them before starting the coordinator.
97
+
98
+ ```python
99
+ from tokenguard import option, tg_option
100
+
101
+ # Example Coordinator settings (I suggest testing these for yourself.)
102
+ option.enable_convergence(False) # default: True
103
+ option.num_executors(12) # default: 6
104
+ option.mailbox_max(500) # default: 100 — max tokens per worker mailbox
105
+ option.recent_executions_max(50) # default: 50 — history buffer size
106
+
107
+ # Logging
108
+ tg_option.enable('coordinator', 'worker')
109
+ tg_option.debug('coordinator', on=True)
110
+ tg_option.silence('convergence')
111
+ tg_option.silence_all()
112
+ tg_option.enable_all()
113
+ ```
114
+
115
+ > `workers_per_core` is intentionally not exposed here. The convergence engine
116
+ > handles live scaling — this value is a ceiling, not a target.
117
+
118
+ ---
119
+
120
+ ## Decorator Reference
121
+
122
+ ### `@task_token_guard(operation_type, tags)`
123
+
124
+ | Parameter | Type | Required | Description |
125
+ |------------------|--------|-----------|--------------------------------------------------------------|
126
+ | `operation_type` | `str` | Yes | Stable label used for routing, metrics, and admin operations |
127
+ | `tags` | `dict` | No | Routing and policy hints — see Tag Reference below |
128
+
129
+ The decorated function returns a `TaskToken` instead of executing. The caller is not blocked.
130
+
131
+ ---
132
+
133
+ ## Tag Reference
134
+
135
+ | Tag | Values | Effect |
136
+ |------------------|-----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|
137
+ | `weight` | `'heavy'` `'medium'` `'light'` | Routes token to a specific core range. Heavy → core 1 only. Medium → core 2+. Light → core 3+. Defaults to `medium` |
138
+ | `storage_speed` | `'FAST'` `'SLOW'` `'MODERATE'` `'INSANE'` | Wraps the function with storage throttling. Mutually exclusive with `process_pool` |
139
+ | `process_pool` | `True` | Routes to `ProcessPoolExecutor` instead of thread pool. Args must be picklable. Falls back to thread executor if pickling fails |
140
+ | `sticky_anchor` | any `str` | Pins all tokens sharing this key to the same core. Useful when a group of operations must stay cache-local |
141
+ | `hash_policy` | `HashPolicy.STANDARD` `HashPolicy.FAST` `HashPolicy.NONE` | Controls how args are hashed for sticky routing. See Domain Hashing |
142
+ | `external_calls` | `list[str]` | Marks this token as a lead token that will dispatch child tokens. Opens a conductor seed domain — all children route to the same core automatically |
143
+
144
+ ---
145
+
146
+ ## Working With Tokens
147
+
148
+ Decorated functions return a `TaskToken`. The most common pattern is fire-and-forget:
149
+
150
+ ```python
151
+ process_token_file(path) # dispatched, caller continues
152
+ ```
153
+
154
+ When you need the result:
155
+
156
+ ```python
157
+ # Block until resolved (sync context only)
158
+ result = token.get(timeout=30.0)
159
+
160
+ # Await in async context
161
+ result = await token
162
+
163
+ # Gather a batch
164
+ results = await asyncio.gather(token_a, token_b, token_c)
165
+ ```
166
+
167
+ `TaskToken` proxies arithmetic, comparison, iteration, and type conversion directly to its resolved value. This means tokens can often stand in for their return values without an explicit `.get()`:
168
+
169
+ ```python
170
+ total = token_a + token_b # both block-and-resolve automatically
171
+ if token > 0: # same
172
+ for item in token: # same
173
+ ```
174
+
175
+ Inspect a token at any point:
176
+
177
+ ```python
178
+ print(token.get_status())
179
+ # {'state': 'executing', 'operation_type': 'resize_image', 'age': 0.42, ...}
180
+ ```
181
+
182
+ ### Token Lifecycle
183
+
184
+ ```
185
+ CREATED → WAITING → ADMITTED → EXECUTING → COMPLETED
186
+ → FAILED
187
+ ↓ ↓ ↓ ↓
188
+ KILLED / TIMEOUT (valid from any non-terminal state)
189
+ ```
190
+
191
+ Terminal states are permanent unless failed. A killed or completed token cannot be re-queued unless failed and re-admitted. Failed tokens can be re-queued or killed.
192
+
193
+ ---
194
+
195
+ ## Domain Hashing and Sticky Routing
196
+
197
+ Two mechanisms prevent concurrent access to the same data from different cores.
198
+
199
+ **Sticky anchor** pins a group of tokens to one core by a shared key:
200
+
201
+ ```python
202
+ @task_token_guard(
203
+ operation_type='write_user_record',
204
+ tags={
205
+ 'weight': 'medium',
206
+ 'sticky_anchor': 'user_records',
207
+ }
208
+ )
209
+ def write_user_record(user_id, data):
210
+ ...
211
+ ```
212
+
213
+ All tokens with `sticky_anchor='user_records'` land on the same core until the inflight token completes. The pin releases automatically on completion.
214
+
215
+ **Conductor domains** handle coordinated fan-out — a lead token dispatches children and all of them pin to the same core automatically:
216
+
217
+ ```python
218
+ @task_token_guard(
219
+ operation_type='orchestrate_pipeline',
220
+ tags={
221
+ 'external_calls': ['stage_a', 'stage_b', 'stage_c'],
222
+ }
223
+ )
224
+ def orchestrate_pipeline(data):
225
+ stage_a(data)
226
+ stage_b(data)
227
+ stage_c(data)
228
+ ```
229
+
230
+ The lead token charges a conductor seed domain. Any token emitted inside that execution inherits the domain and is routed to the same core, regardless of weight class.
231
+
232
+ **Hash policy** controls how args are hashed for sticky key resolution:
233
+
234
+ | Value | Behavior |
235
+ |-----------------------|-----------------------------------------------|
236
+ | `HashPolicy.STANDARD` | Default. Uses token args directly |
237
+ | `HashPolicy.FAST` | Converts args to hashable form before hashing |
238
+ | `HashPolicy.NONE` | Skips arg-based hashing — uses key name only |
239
+
240
+ ---
241
+
242
+ ## OperationsCoordinator
243
+
244
+ One instance per process. Start it before any decorated functions are called.
245
+
246
+ ```python
247
+ from tokenguard import OperationsCoordinator
248
+
249
+ coordinator = OperationsCoordinator()
250
+ coordinator.start()
251
+
252
+ # ... application main runs ...
253
+
254
+ coordinator.stop() # Close the event loop and worker threads cleanly on close
255
+ ```
256
+
257
+ ### Convergence Engine
258
+
259
+ When `enable_convergence=True`, TokenGuard monitors per-core utilization and adjusts active worker counts every 5 seconds. Leave these at their defaults unless you have a specific reason to change them:
260
+
261
+ | Setting | Default | Description |
262
+ |------------------------|---------|-------------------------------------------------|
263
+ | `utilization_high` | `80.0` | Utilization % above which workers scale up |
264
+ | `utilization_low` | `10.0` | Utilization % below which workers scale down |
265
+ | `queue_wait_threshold` | `4.0` | Queue wait time (seconds) that triggers scaling |
266
+ | `queue_depth_factor` | `3` | Multiplier applied to queue depth pressure |
267
+
268
+ These are set in `OperationsCoordinator.__init__()` until a future release exposes them through `option`.
269
+
270
+ ---
271
+
272
+ ## Admin API
273
+
274
+ Pool-level controls for interfaces, dashboards, or operational tooling. No WebSocket layer is included — these are the clean primitives to build on.
275
+
276
+ ```python
277
+ # Kill individual or grouped tokens
278
+ coordinator.kill_token(token_id)
279
+ coordinator.kill_all_by_operation('resize_image')
280
+
281
+ # Pause and resume admission (tokens still accept, just queue)
282
+ coordinator.pause_admission()
283
+ coordinator.resume_admission()
284
+
285
+ # Per-operation pause / resume
286
+ coordinator.pause_operation('resize_image')
287
+ coordinator.resume_operation('resize_image')
288
+
289
+ # Drain waiting tokens
290
+ coordinator.drain_pool()
291
+ coordinator.drain_operation('resize_image')
292
+
293
+ # Observability
294
+ stats = coordinator.get_stats()
295
+ coordinator.print_guard_house_dashboard()
296
+ coordinator.dump_execution_history('history.json')
297
+ coordinator.get_affinity_report()
298
+ ```
299
+
300
+ `get_stats()` returns a composite snapshot covering topology, token pool state, admission gate, worker queue, and convergence status.
301
+
302
+ ---
303
+
304
+ ## Logging
305
+
306
+ All TokenGuard output goes through `tg_print`. Every channel is off by default.
307
+
308
+ ```python
309
+ from tokenguard import tg_option
310
+
311
+ tg_option.enable('coordinator', 'worker') # turn on specific channels
312
+ tg_option.debug('coordinator', on=True) # enable debug level for a channel
313
+ tg_option.silence('convergence') # turn off a channel
314
+ tg_option.silence_all() # quiet everything
315
+ tg_option.enable_all() # turn everything on
316
+ ```
317
+
318
+ Available channels: `gate` `pool` `token` `coordinator` `convergence` `worker`
319
+ `storage` `guard` `overflow` `affinity` `sticky` `conductor`
320
+
321
+ > **Note:** Do not enable `convergence` in a REPL. It produces continuous output
322
+ > on a fast poll interval.
323
+
324
+ ---
325
+
326
+ ## TokenGate vs TokenGuard
327
+
328
+ TokenGuard is a stable, focused branch. TokenGate is the experimental surface
329
+ where new subsystems are developed and tested before being considered for a branch.
330
+
331
+ | Feature | TokenGate | TokenGuard |
332
+ |-------------------------------------|------------|-------------|
333
+ | `@task_token_guard` decorator | ✓ | ✓ |
334
+ | Pinned staggered queue | ✓ | ✓ |
335
+ | Convergence engine | ✓ | ✓ |
336
+ | Storage throttle | ✓ | ✓ |
337
+ | Process pool routing | ✓ | ✓ |
338
+ | Domain hashing / sticky routing | ✓ | ✓ |
339
+ | Admin API (pause/resume/drain/kill) | ✓ | ✓ |
340
+ | WebSocket orchestration | ✓ | — |
341
+ | Allocation optimizer | ✓ | — |
342
+ | Code inspector | ✓ | — |
343
+
344
+ ---
345
+
346
+ ## Stability & Releases
347
+
348
+ ```
349
+ Versioning:
350
+ 0 . 1 . 0 . 0
351
+ │ │ │ └── Patch — bug fixes, typo corrections
352
+ │ │ └──────── Minor — small improvements, non-breaking additions
353
+ │ └────────────── Update — meaningful feature additions or tuning
354
+ └──────────────────── Major — architectural changes, breaking API shifts
355
+ ```
356
+
357
+ TokenGuard follows a slow, deliberate release cadence by design. The core routing and execution model is stable — updates here are fixes and minor improvements, not architectural experiments.
358
+
359
+ New subsystems and experimental features are developed in TokenGate first. If something proves solid there, it may eventually be branched into TokenGuard. This means you can build on TokenGuard without worrying about unexpected API changes while you're still learning the performance characteristics and boundaries of the system.
360
+
361
+ ## Requirements
362
+ - Python 3.10+
363
+ - Windows, macOS, Linux
364
+
365
+ [LICENSE](LICENSE.txt)