pythagoras 0.24.4__py3-none-any.whl → 0.24.7__py3-none-any.whl
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.
- pythagoras/_060_autonomous_code_portals/autonomous_decorators.py +31 -4
- pythagoras/_060_autonomous_code_portals/autonomous_portal_core_classes.py +94 -14
- pythagoras/_060_autonomous_code_portals/names_usage_analyzer.py +133 -4
- pythagoras/_070_protected_code_portals/basic_pre_validators.py +130 -15
- pythagoras/_070_protected_code_portals/fn_arg_names_checker.py +20 -18
- pythagoras/_070_protected_code_portals/list_flattener.py +45 -7
- pythagoras/_070_protected_code_portals/package_manager.py +99 -24
- pythagoras/_070_protected_code_portals/protected_decorators.py +59 -1
- pythagoras/_070_protected_code_portals/protected_portal_core_classes.py +239 -4
- pythagoras/_070_protected_code_portals/system_utils.py +85 -12
- pythagoras/_070_protected_code_portals/validation_succesful_const.py +12 -7
- pythagoras/_080_pure_code_portals/pure_core_classes.py +178 -25
- pythagoras/_080_pure_code_portals/pure_decorator.py +37 -0
- pythagoras/_080_pure_code_portals/recursion_pre_validator.py +39 -0
- pythagoras/_090_swarming_portals/output_suppressor.py +32 -3
- pythagoras/_090_swarming_portals/swarming_portals.py +165 -19
- pythagoras/_100_top_level_API/__init__.py +11 -0
- pythagoras/_800_signatures_and_converters/__init__.py +17 -0
- pythagoras/_800_signatures_and_converters/base_16_32_convertors.py +55 -20
- pythagoras/_800_signatures_and_converters/current_date_gmt_str.py +20 -5
- pythagoras/_800_signatures_and_converters/hash_signatures.py +46 -10
- pythagoras/_800_signatures_and_converters/node_signature.py +27 -12
- pythagoras/_800_signatures_and_converters/random_signatures.py +14 -3
- pythagoras/core/__init__.py +54 -0
- {pythagoras-0.24.4.dist-info → pythagoras-0.24.7.dist-info}/METADATA +1 -1
- {pythagoras-0.24.4.dist-info → pythagoras-0.24.7.dist-info}/RECORD +27 -27
- {pythagoras-0.24.4.dist-info → pythagoras-0.24.7.dist-info}/WHEEL +0 -0
|
@@ -30,18 +30,54 @@ from .._060_autonomous_code_portals import *
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
class ProtectedCodePortal(AutonomousCodePortal):
|
|
33
|
+
"""Portal for protected code execution.
|
|
34
|
+
|
|
35
|
+
This portal specializes the AutonomousCodePortal to coordinate execution of
|
|
36
|
+
ProtectedFn instances. It carries configuration and storage
|
|
37
|
+
required by validators (e.g., retry throttling) and by protected function
|
|
38
|
+
orchestration.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
root_dict (PersiDict | str | None): Optional persistent dictionary or a
|
|
42
|
+
path/identifier to initialize the portal's storage. If None, a
|
|
43
|
+
default in-memory storage may be used.
|
|
44
|
+
p_consistency_checks (float | Joker): Probability or flag controlling
|
|
45
|
+
internal consistency checks performed by the portal. Use
|
|
46
|
+
KEEP_CURRENT to inherit the current setting.
|
|
47
|
+
excessive_logging (bool | Joker): Enables verbose logging of portal and
|
|
48
|
+
function operations. Use KEEP_CURRENT to inherit the current
|
|
49
|
+
setting.
|
|
50
|
+
"""
|
|
33
51
|
|
|
34
52
|
def __init__(self
|
|
35
53
|
, root_dict: PersiDict|str|None = None
|
|
36
54
|
, p_consistency_checks: float|Joker = KEEP_CURRENT
|
|
37
55
|
, excessive_logging: bool|Joker = KEEP_CURRENT
|
|
38
56
|
):
|
|
57
|
+
"""Initialize the portal.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
root_dict (PersiDict | str | None): Backing storage or its path.
|
|
61
|
+
If None, use default.
|
|
62
|
+
p_consistency_checks (float | Joker): Probability for internal
|
|
63
|
+
consistency checks (or KEEP_CURRENT to inherit).
|
|
64
|
+
excessive_logging (bool | Joker): Verbose logging flag
|
|
65
|
+
(KEEP_CURRENT to inherit).
|
|
66
|
+
"""
|
|
39
67
|
super().__init__(root_dict=root_dict
|
|
40
68
|
, p_consistency_checks=p_consistency_checks
|
|
41
69
|
, excessive_logging=excessive_logging)
|
|
42
70
|
|
|
43
71
|
|
|
44
72
|
class ProtectedFn(AutonomousFn):
|
|
73
|
+
"""Function wrapper that enforces pre/post validation around execution.
|
|
74
|
+
|
|
75
|
+
A ProtectedFn evaluates a sequence of pre-validators before executing the
|
|
76
|
+
underlying function and a sequence of post-validators after execution. If a
|
|
77
|
+
pre-validator returns a ProtectedFnCallSignature, that signature will be
|
|
78
|
+
executed first (allowing validators to perform prerequisite actions) before
|
|
79
|
+
re-attempting the validation/execution loop.
|
|
80
|
+
"""
|
|
45
81
|
|
|
46
82
|
_pre_validators_cache: list[ValidatorFn] | None
|
|
47
83
|
_post_validators_cache: list[ValidatorFn] | None
|
|
@@ -57,6 +93,25 @@ class ProtectedFn(AutonomousFn):
|
|
|
57
93
|
, excessive_logging: bool | Joker = KEEP_CURRENT
|
|
58
94
|
, fixed_kwargs: dict[str,Any] | None = None
|
|
59
95
|
, portal: ProtectedCodePortal | None = None):
|
|
96
|
+
"""Construct a ProtectedFn.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
fn (Callable | str): The underlying Python function or its source
|
|
100
|
+
code string.
|
|
101
|
+
pre_validators (list[ValidatorFn] | list[Callable] | ValidatorFn | Callable | None):
|
|
102
|
+
Pre-execution validators. Callables are wrapped into
|
|
103
|
+
PreValidatorFn. Lists can be nested and will
|
|
104
|
+
be flattened.
|
|
105
|
+
post_validators (list[ValidatorFn] | list[Callable] | ValidatorFn | Callable | None):
|
|
106
|
+
Post-execution validators. Callables are wrapped into
|
|
107
|
+
PostValidatorFn. Lists can be nested and will be flattened.
|
|
108
|
+
excessive_logging (bool | Joker): Enable verbose logging or inherit
|
|
109
|
+
current setting with KEEP_CURRENT.
|
|
110
|
+
fixed_kwargs (dict[str, Any] | None): Keyword arguments to be fixed
|
|
111
|
+
(bound) for every execution of the function.
|
|
112
|
+
portal (ProtectedCodePortal | None): Portal instance to bind the
|
|
113
|
+
function to.
|
|
114
|
+
"""
|
|
60
115
|
super().__init__(fn=fn
|
|
61
116
|
, portal = portal
|
|
62
117
|
, fixed_kwargs=fixed_kwargs
|
|
@@ -114,6 +169,11 @@ class ProtectedFn(AutonomousFn):
|
|
|
114
169
|
|
|
115
170
|
@property
|
|
116
171
|
def pre_validators(self) -> list[AutonomousFn]:
|
|
172
|
+
"""List of pre-validator functions for this protected function.
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
list[AutonomousFn]: A cached list of PreValidatorFn instances.
|
|
176
|
+
"""
|
|
117
177
|
if not hasattr(self, "_pre_validators_cache"):
|
|
118
178
|
self._pre_validators_cache = [
|
|
119
179
|
addr.get() for addr in self._pre_validators_addrs]
|
|
@@ -122,6 +182,11 @@ class ProtectedFn(AutonomousFn):
|
|
|
122
182
|
|
|
123
183
|
@property
|
|
124
184
|
def post_validators(self) -> list[AutonomousFn]:
|
|
185
|
+
"""List of post-validator functions for this protected function.
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
list[AutonomousFn]: A cached list of PostValidatorFn instances.
|
|
189
|
+
"""
|
|
125
190
|
if not hasattr(self, "_post_validators_cache"):
|
|
126
191
|
self._post_validators_cache = [
|
|
127
192
|
addr.get() for addr in self._post_validators_addrs]
|
|
@@ -131,6 +196,21 @@ class ProtectedFn(AutonomousFn):
|
|
|
131
196
|
def can_be_executed(self
|
|
132
197
|
, kw_args: KwArgs
|
|
133
198
|
) -> ProtectedFnCallSignature|ValidationSuccessFlag|None:
|
|
199
|
+
"""Run pre-validators to determine if execution can proceed.
|
|
200
|
+
|
|
201
|
+
The portal will shuffle the order of pre-validators. If any validator
|
|
202
|
+
returns a ProtectedFnCallSignature, that signature should be executed by
|
|
203
|
+
the caller prior to executing the protected function (this method simply
|
|
204
|
+
returns it). If any validator fails, None is returned. If all succeed,
|
|
205
|
+
VALIDATION_SUCCESSFUL is returned.
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
kw_args (KwArgs): Arguments intended for the wrapped function.
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
ProtectedFnCallSignature | ValidationSuccessFlag | None: Either a
|
|
212
|
+
signature to execute first, the success flag, or None on failure.
|
|
213
|
+
"""
|
|
134
214
|
with self.portal as portal:
|
|
135
215
|
kw_args = kw_args.pack()
|
|
136
216
|
pre_validators = copy(self.pre_validators)
|
|
@@ -150,6 +230,16 @@ class ProtectedFn(AutonomousFn):
|
|
|
150
230
|
def validate_execution_result(self
|
|
151
231
|
, kw_args: KwArgs
|
|
152
232
|
, result: Any) -> ValidationSuccessFlag|None:
|
|
233
|
+
"""Run post-validators to confirm the execution result is acceptable.
|
|
234
|
+
|
|
235
|
+
Args:
|
|
236
|
+
kw_args (KwArgs): Arguments that were passed to the protected function.
|
|
237
|
+
result (Any): The value returned by the protected function.
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
ValidationSuccessFlag | None: VALIDATION_SUCCESSFUL if all
|
|
241
|
+
post-validators pass, otherwise None.
|
|
242
|
+
"""
|
|
153
243
|
with self.portal as portal:
|
|
154
244
|
kw_args = kw_args.pack()
|
|
155
245
|
post_validators = copy(self.post_validators)
|
|
@@ -162,6 +252,24 @@ class ProtectedFn(AutonomousFn):
|
|
|
162
252
|
|
|
163
253
|
|
|
164
254
|
def execute(self, **kwargs) -> Any:
|
|
255
|
+
"""Execute the protected function with validation.
|
|
256
|
+
|
|
257
|
+
This method performs the following loop:
|
|
258
|
+
- Runs pre-validators. If a pre-validator returns a
|
|
259
|
+
ProtectedFnCallSignature, that signature is executed and validation is
|
|
260
|
+
reattempted. If any pre-validator fails, an AssertionError is raised.
|
|
261
|
+
- Executes the wrapped function.
|
|
262
|
+
- Runs post-validators and asserts they all succeed.
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
**kwargs: Keyword arguments to pass to the wrapped function.
|
|
266
|
+
|
|
267
|
+
Returns:
|
|
268
|
+
Any: The result returned by the wrapped function.
|
|
269
|
+
|
|
270
|
+
Raises:
|
|
271
|
+
AssertionError: If pre- or post-validation fails.
|
|
272
|
+
"""
|
|
165
273
|
with (self.portal):
|
|
166
274
|
kw_args = KwArgs(**kwargs)
|
|
167
275
|
while True:
|
|
@@ -182,8 +290,21 @@ class ProtectedFn(AutonomousFn):
|
|
|
182
290
|
) -> list[ValidatorFn]:
|
|
183
291
|
"""Return list of validators in a normalized form.
|
|
184
292
|
|
|
185
|
-
|
|
186
|
-
|
|
293
|
+
- Wraps plain callables/strings into appropriate ValidatorFn subclasses.
|
|
294
|
+
- Flattens nested lists.
|
|
295
|
+
- Removes duplicates while inforcing deterministic
|
|
296
|
+
order via sort_dict_by_keys.
|
|
297
|
+
|
|
298
|
+
Args:
|
|
299
|
+
validators (list[ValidatorFn] | ValidatorFn | None): Validators in
|
|
300
|
+
any supported representation (single, list, nested lists, etc.).
|
|
301
|
+
validator_type (type): Either PreValidatorFn or PostValidatorFn.
|
|
302
|
+
|
|
303
|
+
Returns:
|
|
304
|
+
list[ValidatorFn]: A sorted list of validator instances.
|
|
305
|
+
|
|
306
|
+
Raises:
|
|
307
|
+
TypeError: If an unexpected validator_type is provided.
|
|
187
308
|
"""
|
|
188
309
|
assert validator_type in {PreValidatorFn, PostValidatorFn}
|
|
189
310
|
if validators is None:
|
|
@@ -216,6 +337,12 @@ class ProtectedFn(AutonomousFn):
|
|
|
216
337
|
|
|
217
338
|
@property
|
|
218
339
|
def portal(self) -> ProtectedCodePortal:
|
|
340
|
+
"""Return the bound ProtectedCodePortal.
|
|
341
|
+
|
|
342
|
+
Returns:
|
|
343
|
+
ProtectedCodePortal: The portal controlling execution context and
|
|
344
|
+
storage for this protected function.
|
|
345
|
+
"""
|
|
219
346
|
return super().portal
|
|
220
347
|
|
|
221
348
|
|
|
@@ -240,14 +367,33 @@ class ProtectedFn(AutonomousFn):
|
|
|
240
367
|
|
|
241
368
|
|
|
242
369
|
def get_signature(self, arguments:dict) -> ProtectedFnCallSignature:
|
|
370
|
+
"""Create a call signature for this protected function.
|
|
371
|
+
|
|
372
|
+
Args:
|
|
373
|
+
arguments (dict): Arguments to bind into the call signature.
|
|
374
|
+
|
|
375
|
+
Returns:
|
|
376
|
+
ProtectedFnCallSignature: Signature object representing a
|
|
377
|
+
particular call to this function.
|
|
378
|
+
"""
|
|
243
379
|
return ProtectedFnCallSignature(self, arguments)
|
|
244
380
|
|
|
245
381
|
|
|
246
382
|
class ProtectedFnCallSignature(AutonomousFnCallSignature):
|
|
247
|
-
"""
|
|
383
|
+
"""Invocation signature for a protected function.
|
|
384
|
+
|
|
385
|
+
Encapsulates a function reference and bound arguments that can be executed
|
|
386
|
+
later via execute().
|
|
387
|
+
"""
|
|
248
388
|
_fn_cache: ProtectedFn | None
|
|
249
389
|
|
|
250
390
|
def __init__(self, fn: ProtectedFn, arguments: dict):
|
|
391
|
+
"""Initialize the signature.
|
|
392
|
+
|
|
393
|
+
Args:
|
|
394
|
+
fn (ProtectedFn): The protected function to call.
|
|
395
|
+
arguments (dict): Keyword arguments to be passed at execution time.
|
|
396
|
+
"""
|
|
251
397
|
assert isinstance(fn, ProtectedFn)
|
|
252
398
|
assert isinstance(arguments, dict)
|
|
253
399
|
super().__init__(fn, arguments)
|
|
@@ -259,10 +405,26 @@ class ProtectedFnCallSignature(AutonomousFnCallSignature):
|
|
|
259
405
|
|
|
260
406
|
|
|
261
407
|
class ValidatorFn(AutonomousFn):
|
|
408
|
+
"""Base class for validator wrappers.
|
|
409
|
+
|
|
410
|
+
A ValidatorFn ensures the wrapped callable accepts exactly the keyword
|
|
411
|
+
arguments declared by get_allowed_kwargs_names(). Subclasses define the
|
|
412
|
+
specific interface for pre/post validation phases.
|
|
413
|
+
"""
|
|
262
414
|
def __init__(self, fn: Callable | str | AutonomousFn
|
|
263
415
|
, fixed_kwargs: dict | None = None
|
|
264
416
|
, excessive_logging: bool | Joker = KEEP_CURRENT
|
|
265
417
|
, portal: AutonomousCodePortal | None = None):
|
|
418
|
+
"""Initialize a validator function wrapper.
|
|
419
|
+
|
|
420
|
+
Args:
|
|
421
|
+
fn (Callable | str | AutonomousFn): The validator implementation or
|
|
422
|
+
its source code.
|
|
423
|
+
fixed_kwargs (dict | None): Keyword arguments fixed for every
|
|
424
|
+
validation call.
|
|
425
|
+
excessive_logging (bool | Joker): Controls verbose logging.
|
|
426
|
+
portal (AutonomousCodePortal | None): Optional portal binding.
|
|
427
|
+
"""
|
|
266
428
|
super().__init__(
|
|
267
429
|
fn=fn
|
|
268
430
|
, fixed_kwargs=fixed_kwargs
|
|
@@ -274,20 +436,52 @@ class ValidatorFn(AutonomousFn):
|
|
|
274
436
|
|
|
275
437
|
@classmethod
|
|
276
438
|
def get_allowed_kwargs_names(cls)->set[str]:
|
|
439
|
+
"""Return the exact set of allowed keyword argument names.
|
|
440
|
+
|
|
441
|
+
Subclasses must override to declare their interface.
|
|
442
|
+
|
|
443
|
+
Returns:
|
|
444
|
+
set[str]: Names of keyword arguments accepted by execute().
|
|
445
|
+
"""
|
|
277
446
|
raise NotImplementedError("This method must be overridden")
|
|
278
447
|
|
|
279
448
|
|
|
280
449
|
def execute(self,**kwargs) \
|
|
281
450
|
-> ProtectedFnCallSignature | ValidationSuccessFlag | None:
|
|
451
|
+
"""Execute the validator after verifying keyword arguments.
|
|
452
|
+
|
|
453
|
+
Args:
|
|
454
|
+
**kwargs: Must exactly match get_allowed_kwargs_names().
|
|
455
|
+
|
|
456
|
+
Returns:
|
|
457
|
+
ProtectedFnCallSignature | ValidationSuccessFlag | None: Depending
|
|
458
|
+
on the validator type and outcome.
|
|
459
|
+
"""
|
|
282
460
|
assert set(kwargs) == self.get_allowed_kwargs_names()
|
|
283
461
|
return super().execute(**kwargs)
|
|
284
462
|
|
|
285
463
|
|
|
286
464
|
class PreValidatorFn(ValidatorFn):
|
|
465
|
+
"""Base class for pre-execution validators.
|
|
466
|
+
|
|
467
|
+
Pre-validators are executed before the protected function. They may return:
|
|
468
|
+
- VALIDATION_SUCCESSFUL to indicate execution can proceed;
|
|
469
|
+
- ProtectedFnCallSignature to request execution of an auxiliary action
|
|
470
|
+
prior to re-validating;
|
|
471
|
+
- None to indicate failure.
|
|
472
|
+
"""
|
|
287
473
|
def __init__(self, fn: Callable | str | AutonomousFn
|
|
288
474
|
, fixed_kwargs: dict | None = None
|
|
289
475
|
, excessive_logging: bool | Joker = KEEP_CURRENT
|
|
290
476
|
, portal: AutonomousCodePortal | None = None):
|
|
477
|
+
"""Initialize a pre-execution validator wrapper.
|
|
478
|
+
|
|
479
|
+
Args:
|
|
480
|
+
fn (Callable | str | AutonomousFn): The pre-validator implementation.
|
|
481
|
+
fixed_kwargs (dict | None): Keyword arguments fixed for every call.
|
|
482
|
+
excessive_logging (bool | Joker): Controls verbose logging.
|
|
483
|
+
portal (AutonomousCodePortal | None): Optional portal binding.
|
|
484
|
+
"""
|
|
291
485
|
super().__init__(
|
|
292
486
|
fn=fn
|
|
293
487
|
, fixed_kwargs=fixed_kwargs
|
|
@@ -296,10 +490,22 @@ class PreValidatorFn(ValidatorFn):
|
|
|
296
490
|
|
|
297
491
|
|
|
298
492
|
class SimplePreValidatorFn(PreValidatorFn):
|
|
493
|
+
"""A pre-validator that takes no runtime inputs.
|
|
494
|
+
|
|
495
|
+
The wrapped callable must accept no parameters; use fixed_kwargs only.
|
|
496
|
+
"""
|
|
299
497
|
def __init__(self, fn: Callable | str | AutonomousFn
|
|
300
498
|
, fixed_kwargs: dict | None = None
|
|
301
499
|
, excessive_logging: bool | Joker = KEEP_CURRENT
|
|
302
500
|
, portal: AutonomousCodePortal | None = None):
|
|
501
|
+
"""Initialize a simple pre-validator.
|
|
502
|
+
|
|
503
|
+
Args:
|
|
504
|
+
fn (Callable | str | AutonomousFn): The implementation.
|
|
505
|
+
fixed_kwargs (dict | None): Fixed keyword arguments, if any.
|
|
506
|
+
excessive_logging (bool | Joker): Controls verbose logging.
|
|
507
|
+
portal (AutonomousCodePortal | None): Optional portal binding.
|
|
508
|
+
"""
|
|
303
509
|
super().__init__(
|
|
304
510
|
fn=fn
|
|
305
511
|
, fixed_kwargs=fixed_kwargs
|
|
@@ -314,10 +520,23 @@ class SimplePreValidatorFn(PreValidatorFn):
|
|
|
314
520
|
|
|
315
521
|
|
|
316
522
|
class ComplexPreValidatorFn(PreValidatorFn):
|
|
523
|
+
"""A pre-validator that can inspect inputs and the function address.
|
|
524
|
+
|
|
525
|
+
The callable must accept the keyword arguments named
|
|
526
|
+
packed_kwargs and fn_addr.
|
|
527
|
+
"""
|
|
317
528
|
def __init__(self, fn: Callable | str | AutonomousFn
|
|
318
529
|
, fixed_kwargs: dict | None = None
|
|
319
530
|
, excessive_logging: bool | Joker = KEEP_CURRENT
|
|
320
531
|
, portal: AutonomousCodePortal | None = None):
|
|
532
|
+
"""Initialize a complex pre-validator.
|
|
533
|
+
|
|
534
|
+
Args:
|
|
535
|
+
fn (Callable | str | AutonomousFn): The implementation.
|
|
536
|
+
fixed_kwargs (dict | None): Fixed keyword arguments, if any.
|
|
537
|
+
excessive_logging (bool | Joker): Controls verbose logging.
|
|
538
|
+
portal (AutonomousCodePortal | None): Optional portal binding.
|
|
539
|
+
"""
|
|
321
540
|
super().__init__(
|
|
322
541
|
fn=fn
|
|
323
542
|
, fixed_kwargs=fixed_kwargs
|
|
@@ -332,10 +551,22 @@ class ComplexPreValidatorFn(PreValidatorFn):
|
|
|
332
551
|
|
|
333
552
|
|
|
334
553
|
class PostValidatorFn(ValidatorFn):
|
|
554
|
+
"""Post-execution validator wrapper.
|
|
555
|
+
|
|
556
|
+
The callable must accept packed_kwargs, fn_addr, and result.
|
|
557
|
+
"""
|
|
335
558
|
def __init__(self, fn: Callable | str | AutonomousFn
|
|
336
559
|
, fixed_kwargs: dict | None = None
|
|
337
560
|
, excessive_logging: bool | Joker = KEEP_CURRENT
|
|
338
561
|
, portal: AutonomousCodePortal | None = None):
|
|
562
|
+
"""Initialize a post-execution validator.
|
|
563
|
+
|
|
564
|
+
Args:
|
|
565
|
+
fn (Callable | str | AutonomousFn): The implementation.
|
|
566
|
+
fixed_kwargs (dict | None): Fixed keyword arguments, if any.
|
|
567
|
+
excessive_logging (bool | Joker): Controls verbose logging.
|
|
568
|
+
portal (AutonomousCodePortal | None): Optional portal binding.
|
|
569
|
+
"""
|
|
339
570
|
super().__init__(
|
|
340
571
|
fn=fn
|
|
341
572
|
, fixed_kwargs=fixed_kwargs
|
|
@@ -344,5 +575,9 @@ class PostValidatorFn(ValidatorFn):
|
|
|
344
575
|
|
|
345
576
|
@classmethod
|
|
346
577
|
def get_allowed_kwargs_names(cls) -> set[str]:
|
|
347
|
-
"""Post-validators use
|
|
578
|
+
"""Post-validators use function metadata, inputs, and the result.
|
|
579
|
+
|
|
580
|
+
Returns:
|
|
581
|
+
set[str]: {"packed_kwargs", "fn_addr", "result"}
|
|
582
|
+
"""
|
|
348
583
|
return {"packed_kwargs", "fn_addr", "result" }
|
|
@@ -3,13 +3,41 @@ import psutil
|
|
|
3
3
|
import pynvml
|
|
4
4
|
|
|
5
5
|
def get_unused_ram_mb() -> int:
|
|
6
|
-
"""
|
|
6
|
+
"""Get the currently available RAM on the system in megabytes (MB).
|
|
7
|
+
|
|
8
|
+
Returns:
|
|
9
|
+
int: Integer number of megabytes of RAM that are currently available
|
|
10
|
+
to user processes as reported by psutil.virtual_memory().available.
|
|
11
|
+
|
|
12
|
+
Notes:
|
|
13
|
+
- The value is rounded down to the nearest integer.
|
|
14
|
+
- Uses powers-of-two conversion (1 MB = 1024^2 bytes).
|
|
15
|
+
- On systems with memory compression or overcommit, this value is an
|
|
16
|
+
approximation provided by the OS.
|
|
17
|
+
"""
|
|
7
18
|
free_ram = psutil.virtual_memory().available / (1024 * 1024)
|
|
8
19
|
return int(free_ram)
|
|
9
20
|
|
|
10
21
|
|
|
11
22
|
def get_unused_cpu_cores() -> float:
|
|
12
|
-
"""
|
|
23
|
+
"""Estimate currently unused logical CPU capacity in units of CPU cores.
|
|
24
|
+
|
|
25
|
+
On POSIX systems with load average support, this uses the 1-minute load
|
|
26
|
+
average to estimate remaining capacity: max(logical_cores - load1, 0).
|
|
27
|
+
On other systems, it falls back to instantaneous CPU percent usage as
|
|
28
|
+
reported by psutil and computes: logical_cores * (1 - usage/100).
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
float: A non-negative float representing approximate available logical
|
|
32
|
+
CPU cores. For example, 2.5 means roughly two and a half cores free.
|
|
33
|
+
|
|
34
|
+
Notes:
|
|
35
|
+
- The number of logical cores (with SMT/Hyper-Threading) is used.
|
|
36
|
+
- If psutil reports near-zero usage, a small default (0.5%) is assumed
|
|
37
|
+
to avoid transient 0.0 readings.
|
|
38
|
+
- This is a heuristic; short spikes and scheduling nuances may cause
|
|
39
|
+
deviations from actual availability.
|
|
40
|
+
"""
|
|
13
41
|
|
|
14
42
|
cnt = psutil.cpu_count(logical=True) or 1
|
|
15
43
|
|
|
@@ -24,7 +52,20 @@ def get_unused_cpu_cores() -> float:
|
|
|
24
52
|
|
|
25
53
|
|
|
26
54
|
def process_is_active(pid: int) -> bool:
|
|
27
|
-
"""
|
|
55
|
+
"""Check whether a process with the given PID is currently active.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
pid (int): Operating system process identifier (PID).
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
bool: True if the process exists and is running; False if it does not
|
|
62
|
+
exist, has exited, or cannot be inspected due to permissions or other
|
|
63
|
+
errors.
|
|
64
|
+
|
|
65
|
+
Notes:
|
|
66
|
+
- Any exception from psutil (e.g., NoSuchProcess, AccessDenied) results
|
|
67
|
+
in a False return value for safety.
|
|
68
|
+
"""
|
|
28
69
|
try:
|
|
29
70
|
process = psutil.Process(pid)
|
|
30
71
|
return process.is_running()
|
|
@@ -33,7 +74,19 @@ def process_is_active(pid: int) -> bool:
|
|
|
33
74
|
|
|
34
75
|
|
|
35
76
|
def get_process_start_time(pid: int) -> int:
|
|
36
|
-
"""
|
|
77
|
+
"""Get the UNIX timestamp of when a process started.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
pid (int): Operating system process identifier (PID).
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
int: Start time as a UNIX timestamp (seconds since epoch). Returns 0 if
|
|
84
|
+
the process does not exist or cannot be accessed.
|
|
85
|
+
|
|
86
|
+
Notes:
|
|
87
|
+
- Any exception from psutil (e.g., NoSuchProcess, AccessDenied) results
|
|
88
|
+
in a 0 return value for safety.
|
|
89
|
+
"""
|
|
37
90
|
try:
|
|
38
91
|
process = psutil.Process(pid)
|
|
39
92
|
return int(process.create_time())
|
|
@@ -42,21 +95,41 @@ def get_process_start_time(pid: int) -> int:
|
|
|
42
95
|
|
|
43
96
|
|
|
44
97
|
def get_current_process_id() -> int:
|
|
45
|
-
"""
|
|
98
|
+
"""Get the current process ID (PID).
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
int: The PID of the running Python process.
|
|
102
|
+
"""
|
|
46
103
|
return psutil.Process().pid
|
|
47
104
|
|
|
48
105
|
|
|
49
106
|
def get_current_process_start_time() -> int:
|
|
50
|
-
"""
|
|
107
|
+
"""Get the UNIX timestamp for when the current Python process started.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
int: Start time as a UNIX timestamp (seconds since epoch). Returns 0 on
|
|
111
|
+
unexpected error.
|
|
112
|
+
"""
|
|
51
113
|
return get_process_start_time(get_current_process_id())
|
|
52
114
|
|
|
53
115
|
|
|
54
116
|
def get_unused_nvidia_gpus() -> float:
|
|
55
|
-
"""
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
For example, 2.0 means
|
|
59
|
-
|
|
117
|
+
"""Estimate the total unused NVIDIA GPU capacity across all devices.
|
|
118
|
+
|
|
119
|
+
This aggregates the per-GPU unused utilization percentage (100 - gpu%) and
|
|
120
|
+
returns the sum in "GPU units". For example, 2.0 means capacity equivalent
|
|
121
|
+
to two fully idle GPUs. If no NVIDIA GPUs are present or NVML is unavailable,
|
|
122
|
+
the function returns 0.0.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
float: Sum of unused GPU capacity across all NVIDIA GPUs in GPU units.
|
|
126
|
+
|
|
127
|
+
Notes:
|
|
128
|
+
- Requires NVIDIA Management Library (pynvml) to be installed and the
|
|
129
|
+
NVIDIA driver to be available.
|
|
130
|
+
- Utilization is based on instantaneous NVML readings and may fluctuate.
|
|
131
|
+
- Any NVML error (e.g., no devices, driver issues) results in 0.0 for
|
|
132
|
+
safety.
|
|
60
133
|
"""
|
|
61
134
|
try:
|
|
62
135
|
pynvml.nvmlInit()
|
|
@@ -70,7 +143,7 @@ def get_unused_nvidia_gpus() -> float:
|
|
|
70
143
|
|
|
71
144
|
return unused_capacity / 100.0
|
|
72
145
|
|
|
73
|
-
except pynvml.NVMLError
|
|
146
|
+
except pynvml.NVMLError:
|
|
74
147
|
# Return 0.0 on any NVML error (no GPUs, driver issues, etc.)
|
|
75
148
|
return 0.0
|
|
76
149
|
finally:
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
"""Singleton class to represent a successful validation."""
|
|
1
|
+
from persidict.singletons import Singleton
|
|
3
2
|
|
|
4
|
-
_instance = None
|
|
5
3
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
cls._instance = super().__new__(cls)
|
|
9
|
-
return cls._instance
|
|
4
|
+
class ValidationSuccessFlag(Singleton):
|
|
5
|
+
"""Marker singleton indicating that validation has succeeded.
|
|
10
6
|
|
|
7
|
+
This lightweight class is used as a unique sentinel object that signals a
|
|
8
|
+
successful validation outcome in protected code portals. Using a singleton
|
|
9
|
+
avoids ambiguity with other truthy values.
|
|
10
|
+
"""
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# A canonical, importable singleton value representing a successful validation.
|
|
15
|
+
# Use identity checks (``is VALIDATION_SUCCESSFUL``) rather than equality.
|
|
11
16
|
VALIDATION_SUCCESSFUL = ValidationSuccessFlag()
|