pythagoras 0.24.7__tar.gz → 0.24.10__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.
- {pythagoras-0.24.7 → pythagoras-0.24.10}/PKG-INFO +1 -1
- {pythagoras-0.24.7 → pythagoras-0.24.10}/pyproject.toml +1 -1
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_010_basic_portals/__init__.py +0 -1
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_010_basic_portals/basic_portal_core_classes.py +21 -15
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_010_basic_portals/portal_tester.py +3 -2
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_020_ordinary_code_portals/__init__.py +1 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_020_ordinary_code_portals/code_normalizer.py +14 -14
- pythagoras-0.24.10/src/pythagoras/_020_ordinary_code_portals/exceptions.py +11 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_020_ordinary_code_portals/function_processing.py +10 -10
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_020_ordinary_code_portals/ordinary_decorator.py +2 -1
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_020_ordinary_code_portals/ordinary_portal_core_classes.py +11 -9
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_030_data_portals/data_portal_core_classes.py +13 -11
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_030_data_portals/storable_decorator.py +3 -1
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_040_logging_code_portals/logging_decorator.py +4 -2
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_040_logging_code_portals/logging_portal_core_classes.py +17 -13
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_050_safe_code_portals/safe_decorator.py +2 -1
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_050_safe_code_portals/safe_portal_core_classes.py +4 -2
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_060_autonomous_code_portals/autonomous_decorators.py +8 -3
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_060_autonomous_code_portals/autonomous_portal_core_classes.py +23 -17
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_060_autonomous_code_portals/names_usage_analyzer.py +18 -15
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_070_protected_code_portals/protected_decorators.py +4 -2
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_070_protected_code_portals/protected_portal_core_classes.py +15 -8
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_080_pure_code_portals/__init__.py +5 -1
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_080_pure_code_portals/pure_core_classes.py +22 -15
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_080_pure_code_portals/recursion_pre_validator.py +8 -4
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_090_swarming_portals/swarming_portals.py +17 -13
- pythagoras-0.24.7/src/pythagoras/_010_basic_portals/exceptions.py +0 -36
- {pythagoras-0.24.7 → pythagoras-0.24.10}/README.md +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/.DS_Store +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_010_basic_portals/long_infoname.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_010_basic_portals/post_init_metaclass.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_030_data_portals/__init__.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_030_data_portals/ready_and_get.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_040_logging_code_portals/__init__.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_040_logging_code_portals/exception_processing_tracking.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_040_logging_code_portals/execution_environment_summary.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_040_logging_code_portals/kw_args.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_040_logging_code_portals/notebook_checker.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_040_logging_code_portals/output_capturer.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_040_logging_code_portals/uncaught_exceptions.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_050_safe_code_portals/__init__.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_060_autonomous_code_portals/__init__.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_070_protected_code_portals/__init__.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_070_protected_code_portals/basic_pre_validators.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_070_protected_code_portals/fn_arg_names_checker.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_070_protected_code_portals/list_flattener.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_070_protected_code_portals/package_manager.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_070_protected_code_portals/system_utils.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_070_protected_code_portals/validation_succesful_const.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_080_pure_code_portals/pure_decorator.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_090_swarming_portals/__init__.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_090_swarming_portals/output_suppressor.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_100_top_level_API/__init__.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_100_top_level_API/default_local_portal.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_100_top_level_API/top_level_API.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_800_signatures_and_converters/__init__.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_800_signatures_and_converters/base_16_32_convertors.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_800_signatures_and_converters/current_date_gmt_str.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_800_signatures_and_converters/hash_signatures.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_800_signatures_and_converters/node_signature.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_800_signatures_and_converters/random_signatures.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_900_project_stats_collector/__init__.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_900_project_stats_collector/project_analyzer.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/__init__.py +0 -0
- {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/core/__init__.py +0 -0
|
@@ -65,8 +65,8 @@ def _describe_runtime_characteristic(name, value) -> pd.DataFrame:
|
|
|
65
65
|
return pd.DataFrame(d)
|
|
66
66
|
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
_BASE_DIRECTORY_TXT = "Base directory"
|
|
69
|
+
_BACKEND_TYPE_TXT = "Backend type"
|
|
70
70
|
|
|
71
71
|
|
|
72
72
|
def _get_description_value_by_key(dataframe:pd.DataFrame, key:str) -> Any:
|
|
@@ -200,7 +200,8 @@ def get_default_portal_base_dir() -> str:
|
|
|
200
200
|
target_directory = home_directory / ".pythagoras" / ".default_portal"
|
|
201
201
|
target_directory.mkdir(parents=True, exist_ok=True)
|
|
202
202
|
target_directory_str = str(target_directory.resolve())
|
|
203
|
-
|
|
203
|
+
if not isinstance(target_directory_str, str):
|
|
204
|
+
raise TypeError(f"Expected target_directory_str to be str, got {type(target_directory_str).__name__}")
|
|
204
205
|
return target_directory_str
|
|
205
206
|
|
|
206
207
|
|
|
@@ -280,8 +281,10 @@ class BasicPortal(NotPicklableClass,ParameterizableClass, metaclass = PostInitMe
|
|
|
280
281
|
global _all_links_from_objects_to_portals
|
|
281
282
|
result = set()
|
|
282
283
|
for obj_str_id, portal_str_id in _all_links_from_objects_to_portals.items():
|
|
283
|
-
|
|
284
|
-
|
|
284
|
+
if not isinstance(obj_str_id, str):
|
|
285
|
+
raise TypeError(f"Expected obj_str_id to be str, got {type(obj_str_id).__name__}")
|
|
286
|
+
if not isinstance(portal_str_id, str):
|
|
287
|
+
raise TypeError(f"Expected portal_str_id to be str, got {type(portal_str_id).__name__}")
|
|
285
288
|
if portal_str_id == self._str_id:
|
|
286
289
|
if target_class is None:
|
|
287
290
|
result.add(obj_str_id)
|
|
@@ -386,9 +389,9 @@ class BasicPortal(NotPicklableClass,ParameterizableClass, metaclass = PostInitMe
|
|
|
386
389
|
all_params = []
|
|
387
390
|
|
|
388
391
|
all_params.append(_describe_persistent_characteristic(
|
|
389
|
-
|
|
392
|
+
_BASE_DIRECTORY_TXT, self._root_dict.base_dir))
|
|
390
393
|
all_params.append(_describe_persistent_characteristic(
|
|
391
|
-
|
|
394
|
+
_BACKEND_TYPE_TXT, self._root_dict.__class__.__name__))
|
|
392
395
|
|
|
393
396
|
result = pd.concat(all_params)
|
|
394
397
|
result.reset_index(drop=True, inplace=True)
|
|
@@ -411,10 +414,9 @@ class BasicPortal(NotPicklableClass,ParameterizableClass, metaclass = PostInitMe
|
|
|
411
414
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
412
415
|
"""Pop the portal from the stack of active ones"""
|
|
413
416
|
global _active_portals_stack, _active_portals_counters_stack
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
+ "within a 'with' statement with another portal.")
|
|
417
|
+
if _active_portals_stack[-1] != self:
|
|
418
|
+
raise RuntimeError(
|
|
419
|
+
"Inconsistent state of the portal stack. Most probably, portal.__enter__() method was called explicitly within a 'with' statement with another portal.")
|
|
418
420
|
if _active_portals_counters_stack[-1] == 1:
|
|
419
421
|
_active_portals_stack.pop()
|
|
420
422
|
_active_portals_counters_stack.pop()
|
|
@@ -462,7 +464,8 @@ class PortalAwareClass(metaclass = PostInitMeta):
|
|
|
462
464
|
portal: The portal to link this object to, or None to use the
|
|
463
465
|
currently active portal for operations.
|
|
464
466
|
"""
|
|
465
|
-
|
|
467
|
+
if not (portal is None or isinstance(portal, BasicPortal)):
|
|
468
|
+
raise TypeError(f"portal must be a BasicPortal or None, got {type(portal).__name__}")
|
|
466
469
|
self._linked_portal_at_init = portal
|
|
467
470
|
# self._hash_id_cache = None
|
|
468
471
|
self._visited_portals = set()
|
|
@@ -501,7 +504,8 @@ class PortalAwareClass(metaclass = PostInitMeta):
|
|
|
501
504
|
portal = self._linked_portal_at_init
|
|
502
505
|
elif self._str_id in _all_links_from_objects_to_portals:
|
|
503
506
|
portal_str_id = _all_links_from_objects_to_portals[self._str_id]
|
|
504
|
-
|
|
507
|
+
if not isinstance(portal_str_id, str):
|
|
508
|
+
raise TypeError(f"Expected portal_str_id to be str, got {type(portal_str_id).__name__}")
|
|
505
509
|
portal = _all_known_portals[portal_str_id]
|
|
506
510
|
return portal
|
|
507
511
|
|
|
@@ -595,7 +599,8 @@ class PortalAwareClass(metaclass = PostInitMeta):
|
|
|
595
599
|
"""Return True if the object has been registered in at least one of the portals."""
|
|
596
600
|
global _all_activated_portal_aware_objects
|
|
597
601
|
if len(self._visited_portals) >=1:
|
|
598
|
-
|
|
602
|
+
if self._str_id not in _all_activated_portal_aware_objects:
|
|
603
|
+
raise RuntimeError(f"Object with id {self._str_id} is expected to be in the activated objects registry")
|
|
599
604
|
return True
|
|
600
605
|
return False
|
|
601
606
|
|
|
@@ -606,7 +611,8 @@ class PortalAwareClass(metaclass = PostInitMeta):
|
|
|
606
611
|
Empty the list of portals it has been registered into.
|
|
607
612
|
"""
|
|
608
613
|
global _all_activated_portal_aware_objects
|
|
609
|
-
|
|
614
|
+
if not self.is_activated:
|
|
615
|
+
raise RuntimeError(f"Object with id {self._str_id} is not activated")
|
|
610
616
|
del _all_activated_portal_aware_objects[self._str_id]
|
|
611
617
|
self._invalidate_cache()
|
|
612
618
|
self._visited_portals = set()
|
|
@@ -25,14 +25,15 @@ class _PortalTester:
|
|
|
25
25
|
|
|
26
26
|
Raises:
|
|
27
27
|
Exception: If another _PortalTester instance is already active.
|
|
28
|
-
|
|
28
|
+
TypeError: If portal_class is not a subclass of BasicPortal.
|
|
29
29
|
"""
|
|
30
30
|
if _PortalTester._current_instance is not None:
|
|
31
31
|
raise Exception("_PortalTester can't be nested")
|
|
32
32
|
_PortalTester._current_instance = self
|
|
33
33
|
|
|
34
34
|
if portal_class is not None:
|
|
35
|
-
|
|
35
|
+
if not issubclass(portal_class, BasicPortal):
|
|
36
|
+
raise TypeError(f"portal_class must be a subclass of BasicPortal, got {portal_class}")
|
|
36
37
|
self.portal_class = portal_class
|
|
37
38
|
self.args = args
|
|
38
39
|
self.kwargs = kwargs
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import sys
|
|
2
|
-
|
|
3
|
-
from .._010_basic_portals.exceptions import NonCompliantFunction
|
|
4
1
|
import ast
|
|
5
2
|
import inspect
|
|
6
3
|
from typing import Callable
|
|
@@ -9,6 +6,7 @@ import autopep8
|
|
|
9
6
|
from .function_processing import get_function_name_from_source
|
|
10
7
|
from .._010_basic_portals.long_infoname import get_long_infoname
|
|
11
8
|
from .function_processing import assert_ordinarity
|
|
9
|
+
from .exceptions import FunctionError
|
|
12
10
|
|
|
13
11
|
_pythagoras_decorator_names = {
|
|
14
12
|
"ordinary"
|
|
@@ -52,12 +50,11 @@ def _get_normalized_function_source_impl(
|
|
|
52
50
|
A normalized source code string for the function.
|
|
53
51
|
|
|
54
52
|
Raises:
|
|
55
|
-
|
|
53
|
+
FunctionError: If the function has multiple decorators when
|
|
56
54
|
a callable or a string representing a single function is
|
|
57
55
|
expected; or if it fails ordinarity checks.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
when internal integrity checks do not hold.
|
|
56
|
+
TypeError | ValueError: If input types or parsing assumptions fail
|
|
57
|
+
(e.g., unexpected AST node types), or when integrity checks do not hold.
|
|
61
58
|
SyntaxError: If the provided source string cannot be parsed.
|
|
62
59
|
"""
|
|
63
60
|
|
|
@@ -71,7 +68,7 @@ def _get_normalized_function_source_impl(
|
|
|
71
68
|
code = a_func
|
|
72
69
|
a_func_name = get_function_name_from_source(code)
|
|
73
70
|
else:
|
|
74
|
-
|
|
71
|
+
raise TypeError(f"a_func must be a callable or a string, got {type(a_func).__name__}")
|
|
75
72
|
|
|
76
73
|
code_lines = code.splitlines()
|
|
77
74
|
|
|
@@ -88,7 +85,8 @@ def _get_normalized_function_source_impl(
|
|
|
88
85
|
chars_to_remove = code_no_empty_lines[0][:n_chars_to_remove]
|
|
89
86
|
code_clean_version = []
|
|
90
87
|
for line in code_no_empty_lines:
|
|
91
|
-
|
|
88
|
+
if not line.startswith(chars_to_remove):
|
|
89
|
+
raise ValueError(f"Inconsistent indentation detected while normalizing function {a_func_name}")
|
|
92
90
|
cleaned_line = line[n_chars_to_remove:]
|
|
93
91
|
code_clean_version.append(cleaned_line)
|
|
94
92
|
|
|
@@ -97,14 +95,15 @@ def _get_normalized_function_source_impl(
|
|
|
97
95
|
a_func_name = get_function_name_from_source(code_clean_version)
|
|
98
96
|
code_ast = ast.parse(code_clean_version)
|
|
99
97
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
98
|
+
if not isinstance(code_ast, ast.Module):
|
|
99
|
+
raise TypeError(f"Expected AST Module for {a_func_name}, got {type(code_ast).__name__}")
|
|
100
|
+
if not isinstance(code_ast.body[0], ast.FunctionDef):
|
|
101
|
+
raise ValueError(f"Top-level node is not a FunctionDef for {a_func_name}; got {type(code_ast.body[0]).__name__}")
|
|
103
102
|
|
|
104
103
|
# TODO: add support for multiple decorators???
|
|
105
104
|
decorator_list = code_ast.body[0].decorator_list
|
|
106
105
|
if len(decorator_list) > 1:
|
|
107
|
-
raise
|
|
106
|
+
raise FunctionError(
|
|
108
107
|
f"Function {a_func_name} can't have multiple decorators,"
|
|
109
108
|
+ " only one decorator is allowed.")
|
|
110
109
|
|
|
@@ -127,7 +126,8 @@ def _get_normalized_function_source_impl(
|
|
|
127
126
|
break
|
|
128
127
|
except:
|
|
129
128
|
pass
|
|
130
|
-
|
|
129
|
+
if pth_dec_counter != 1:
|
|
130
|
+
raise ValueError(f"Unexpected decorator configuration for {a_func_name}: unable to drop Pythagoras decorator")
|
|
131
131
|
code_ast.body[0].decorator_list = []
|
|
132
132
|
|
|
133
133
|
# Remove docstrings.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class FunctionError(Exception):
|
|
5
|
+
"""Exception raised when a function does not comply with Pythagoras requirements.
|
|
6
|
+
|
|
7
|
+
This exception is raised when a function (or its usage) fails to meet
|
|
8
|
+
the compliance requirements of the Pythagoras framework.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
pass
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import types, inspect
|
|
2
2
|
from typing import Callable
|
|
3
3
|
|
|
4
|
-
from
|
|
4
|
+
from .exceptions import FunctionError
|
|
5
5
|
from .._010_basic_portals.long_infoname import get_long_infoname
|
|
6
6
|
|
|
7
7
|
|
|
@@ -93,7 +93,7 @@ def assert_ordinarity(a_func: Callable) -> None:
|
|
|
93
93
|
a_func: The callable to validate.
|
|
94
94
|
|
|
95
95
|
Raises:
|
|
96
|
-
|
|
96
|
+
FunctionError: If the callable violates any of the ordinarity
|
|
97
97
|
constraints described above (e.g., not a function, is a method,
|
|
98
98
|
is a lambda/closure/async function, accepts *args, or has defaults).
|
|
99
99
|
"""
|
|
@@ -104,35 +104,35 @@ def assert_ordinarity(a_func: Callable) -> None:
|
|
|
104
104
|
name = get_long_infoname(a_func)
|
|
105
105
|
|
|
106
106
|
if not callable(a_func):
|
|
107
|
-
raise
|
|
107
|
+
raise FunctionError(f"{name} must be callable.")
|
|
108
108
|
|
|
109
109
|
if not inspect.isfunction(a_func):
|
|
110
|
-
raise
|
|
110
|
+
raise FunctionError(f"The function {name} is not ordinary."
|
|
111
111
|
"It must be a function, not a method, "
|
|
112
112
|
"a classmethod, or a lambda function.")
|
|
113
113
|
|
|
114
114
|
if isinstance(a_func, types.MethodType):
|
|
115
|
-
raise
|
|
115
|
+
raise FunctionError(f"The function {name} can't be "
|
|
116
116
|
"an instance or a class method, only "
|
|
117
117
|
"regular functions are allowed.")
|
|
118
118
|
|
|
119
119
|
if hasattr(a_func, "__closure__") and a_func.__closure__ is not None:
|
|
120
|
-
raise
|
|
120
|
+
raise FunctionError(f"The function {name} can't be a closure,"
|
|
121
121
|
" only regular functions are allowed.")
|
|
122
122
|
|
|
123
123
|
if a_func.__name__ == "<lambda>":
|
|
124
|
-
raise
|
|
124
|
+
raise FunctionError(f"The function {name} can't be lambda,"
|
|
125
125
|
" only regular functions are allowed.")
|
|
126
126
|
|
|
127
127
|
if accepts_unlimited_positional_args(a_func):
|
|
128
|
-
raise
|
|
128
|
+
raise FunctionError("Pythagoras only allows functions "
|
|
129
129
|
f"with named arguments, but {name} accepts "
|
|
130
130
|
"unlimited (nameless) positional arguments.")
|
|
131
131
|
|
|
132
132
|
if inspect.iscoroutinefunction(a_func):
|
|
133
|
-
raise
|
|
133
|
+
raise FunctionError(f"The function {name} can't be "
|
|
134
134
|
"an async function, only regular functions are allowed.")
|
|
135
135
|
|
|
136
136
|
if count_parameters_with_defaults(a_func) > 0:
|
|
137
|
-
raise
|
|
137
|
+
raise FunctionError(f"The function {name} can't have "
|
|
138
138
|
"default values for its parameters.")
|
|
@@ -20,7 +20,8 @@ class ordinary:
|
|
|
20
20
|
portal: Optional OrdinaryCodePortal to link to the resulting
|
|
21
21
|
OrdinaryFn wrappers.
|
|
22
22
|
"""
|
|
23
|
-
|
|
23
|
+
if not (portal is None or isinstance(portal, OrdinaryCodePortal)):
|
|
24
|
+
raise TypeError(f"portal must be an OrdinaryCodePortal or None, got {type(portal).__name__}")
|
|
24
25
|
self._portal=portal
|
|
25
26
|
|
|
26
27
|
|
|
@@ -6,6 +6,7 @@ from typing import Callable, Any
|
|
|
6
6
|
import pandas as pd
|
|
7
7
|
from persidict import PersiDict
|
|
8
8
|
|
|
9
|
+
from .exceptions import FunctionError
|
|
9
10
|
from .._010_basic_portals import BasicPortal, PortalAwareClass
|
|
10
11
|
from .code_normalizer import _get_normalized_function_source_impl
|
|
11
12
|
from .function_processing import get_function_name_from_source
|
|
@@ -32,9 +33,9 @@ def get_normalized_function_source(a_func: OrdinaryFn | Callable | str) -> str:
|
|
|
32
33
|
The normalized source code string.
|
|
33
34
|
|
|
34
35
|
Raises:
|
|
35
|
-
|
|
36
|
+
FunctionError: If the function is not compliant with Pythagoras'
|
|
36
37
|
ordinarity rules or multiple decorators are present.
|
|
37
|
-
|
|
38
|
+
TypeError | ValueError: If input type is invalid or integrity checks fail.
|
|
38
39
|
SyntaxError: If the provided source cannot be parsed.
|
|
39
40
|
"""
|
|
40
41
|
|
|
@@ -45,7 +46,7 @@ def get_normalized_function_source(a_func: OrdinaryFn | Callable | str) -> str:
|
|
|
45
46
|
a_func, drop_pth_decorators=True)
|
|
46
47
|
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
_REGISTERED_FUNCTIONS_TXT = "Registered functions"
|
|
49
50
|
|
|
50
51
|
class OrdinaryCodePortal(BasicPortal):
|
|
51
52
|
"""Portal that manages OrdinaryFn instances and their runtime context.
|
|
@@ -136,7 +137,7 @@ class OrdinaryCodePortal(BasicPortal):
|
|
|
136
137
|
all_params = [super().describe()]
|
|
137
138
|
|
|
138
139
|
all_params.append(_describe_runtime_characteristic(
|
|
139
|
-
|
|
140
|
+
_REGISTERED_FUNCTIONS_TXT, self.get_number_of_linked_functions()))
|
|
140
141
|
|
|
141
142
|
result = pd.concat(all_params)
|
|
142
143
|
result.reset_index(drop=True, inplace=True)
|
|
@@ -182,7 +183,7 @@ class OrdinaryFn(PortalAwareClass):
|
|
|
182
183
|
|
|
183
184
|
Raises:
|
|
184
185
|
TypeError: If fn is neither callable, a string, nor an OrdinaryFn.
|
|
185
|
-
|
|
186
|
+
FunctionError: If the provided function source is not
|
|
186
187
|
compliant with Pythagoras ordinarity rules.
|
|
187
188
|
SyntaxError: If the provided source cannot be parsed.
|
|
188
189
|
"""
|
|
@@ -368,11 +369,12 @@ class OrdinaryFn(PortalAwareClass):
|
|
|
368
369
|
Any: The result of executing the function.
|
|
369
370
|
|
|
370
371
|
Raises:
|
|
371
|
-
|
|
372
|
+
TypeError: If positional arguments are supplied.
|
|
372
373
|
"""
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
374
|
+
if len(args) != 0:
|
|
375
|
+
raise FunctionError(f"Function {self.name} can't be called"
|
|
376
|
+
f" with positional arguments, only keyword arguments are allowed."
|
|
377
|
+
f" Got {len(args)} positional args.")
|
|
376
378
|
return self.execute(**kwargs)
|
|
377
379
|
|
|
378
380
|
|
|
@@ -17,8 +17,8 @@ from .._010_basic_portals.basic_portal_core_classes import (
|
|
|
17
17
|
from .._020_ordinary_code_portals import OrdinaryCodePortal ,OrdinaryFn
|
|
18
18
|
from persidict import WriteOnceDict
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
_TOTAL_VALUES_TXT = "Values, total"
|
|
21
|
+
_PROBABILITY_OF_CHECKS_TXT = "Probability of consistency checks"
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
def get_active_data_portal() -> DataPortal:
|
|
@@ -28,10 +28,11 @@ def get_active_data_portal() -> DataPortal:
|
|
|
28
28
|
DataPortal: The portal that is active in the current context ("with" block).
|
|
29
29
|
|
|
30
30
|
Raises:
|
|
31
|
-
|
|
31
|
+
TypeError: If the active portal is not an instance of DataPortal.
|
|
32
32
|
"""
|
|
33
33
|
portal = get_active_portal()
|
|
34
|
-
|
|
34
|
+
if not isinstance(portal, DataPortal):
|
|
35
|
+
raise TypeError(f"Active portal must be DataPortal, got {type(portal).__name__}")
|
|
35
36
|
return portal
|
|
36
37
|
|
|
37
38
|
|
|
@@ -43,10 +44,12 @@ def get_nonactive_data_portals() -> list[DataPortal]:
|
|
|
43
44
|
the runtime but are not the current active portal stack.
|
|
44
45
|
|
|
45
46
|
Raises:
|
|
46
|
-
|
|
47
|
+
TypeError: If any returned portal is not an instance of DataPortal.
|
|
47
48
|
"""
|
|
48
49
|
portals = get_nonactive_portals()
|
|
49
|
-
|
|
50
|
+
if not all(isinstance(p, DataPortal) for p in portals):
|
|
51
|
+
bad = [type(p).__name__ for p in portals if not isinstance(p, DataPortal)]
|
|
52
|
+
raise TypeError(f"Expected all nonactive portals to be DataPortal, got invalid types: {bad}")
|
|
50
53
|
return portals
|
|
51
54
|
|
|
52
55
|
|
|
@@ -211,9 +214,9 @@ class DataPortal(OrdinaryCodePortal):
|
|
|
211
214
|
all_params = [super().describe()]
|
|
212
215
|
|
|
213
216
|
all_params.append(_describe_persistent_characteristic(
|
|
214
|
-
|
|
217
|
+
_TOTAL_VALUES_TXT, len(self._value_store)))
|
|
215
218
|
all_params.append(_describe_runtime_characteristic(
|
|
216
|
-
|
|
219
|
+
_PROBABILITY_OF_CHECKS_TXT, self.p_consistency_checks))
|
|
217
220
|
|
|
218
221
|
result = pd.concat(all_params)
|
|
219
222
|
result.reset_index(drop=True, inplace=True)
|
|
@@ -493,9 +496,8 @@ class ValueAddr(HashAddr):
|
|
|
493
496
|
, hash_signature=hash_signature)
|
|
494
497
|
return
|
|
495
498
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
+ "convert HashAddr into ValueAddr")
|
|
499
|
+
if isinstance(data, HashAddr):
|
|
500
|
+
raise TypeError("get_ValueAddr is the only way to convert HashAddr into ValueAddr")
|
|
499
501
|
|
|
500
502
|
descriptor = self._build_descriptor(data)
|
|
501
503
|
hash_signature = self._build_hash_signature(data)
|
{pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_030_data_portals/storable_decorator.py
RENAMED
|
@@ -18,7 +18,9 @@ class storable(ordinary):
|
|
|
18
18
|
portal: The DataPortal to use as default when wrapping functions.
|
|
19
19
|
If None, the active portal at call time will be used.
|
|
20
20
|
"""
|
|
21
|
-
|
|
21
|
+
if not (isinstance(portal, DataPortal) or portal is None):
|
|
22
|
+
raise TypeError(f"portal must be a DataPortal or None, "
|
|
23
|
+
f"got {type(portal).__name__}")
|
|
22
24
|
ordinary.__init__(self=self, portal=portal)
|
|
23
25
|
|
|
24
26
|
def __call__(self,fn:Callable)->StorableFn:
|
|
@@ -27,8 +27,10 @@ class logging(storable):
|
|
|
27
27
|
portal: Optional LoggingCodePortal to bind the wrapped function to.
|
|
28
28
|
If None, the active portal at execution time is used.
|
|
29
29
|
"""
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
if not isinstance(excessive_logging, (bool, Joker)):
|
|
31
|
+
raise TypeError(f"excessive_logging must be bool or Joker, got {type(excessive_logging).__name__}")
|
|
32
|
+
if not (isinstance(portal, LoggingCodePortal) or portal is None):
|
|
33
|
+
raise TypeError(f"portal must be LoggingCodePortal or None, got {type(portal).__name__}")
|
|
32
34
|
storable.__init__(self=self, portal=portal)
|
|
33
35
|
self._excessive_logging = excessive_logging
|
|
34
36
|
|
|
@@ -39,6 +39,9 @@ class LoggingFn(StorableFn):
|
|
|
39
39
|
exceptions, and custom events. It also supports an excessive_logging mode
|
|
40
40
|
that enables storing rich per-call artifacts.
|
|
41
41
|
|
|
42
|
+
A logging function can only be called with keyword arguments.
|
|
43
|
+
It can't be called with positional arguments.
|
|
44
|
+
|
|
42
45
|
Attributes:
|
|
43
46
|
_auxiliary_config_params_at_init (dict): Internal configuration store
|
|
44
47
|
inherited from StorableFn. Includes the 'excessive_logging' flag
|
|
@@ -156,7 +159,8 @@ class LoggingFnCallSignature:
|
|
|
156
159
|
_addr_cache: ValueAddr | None
|
|
157
160
|
|
|
158
161
|
def __init__(self, fn:LoggingFn, arguments:dict):
|
|
159
|
-
|
|
162
|
+
if not isinstance(fn, LoggingFn):
|
|
163
|
+
raise TypeError(f"fn must be an instance of LoggingFn, got {type(fn).__name__}")
|
|
160
164
|
isinstance(arguments, dict)
|
|
161
165
|
arguments = KwArgs(**arguments)
|
|
162
166
|
with fn.portal:
|
|
@@ -726,13 +730,13 @@ class LoggingFnExecutionFrame(NotPicklableClass):
|
|
|
726
730
|
Returns:
|
|
727
731
|
LoggingFnExecutionFrame: This frame instance for use as a context var.
|
|
728
732
|
"""
|
|
729
|
-
|
|
730
|
-
"An instance of PureFnExecutionFrame can be used only once.")
|
|
733
|
+
if self.context_used:
|
|
734
|
+
raise RuntimeError(f"An instance of PureFnExecutionFrame can be used only once.")
|
|
731
735
|
self.context_used = True
|
|
732
|
-
|
|
733
|
-
"An instance of PureFnExecutionFrame can be used only once.")
|
|
734
|
-
|
|
735
|
-
"An instance of PureFnExecutionFrame can be used only once.")
|
|
736
|
+
if self.exception_counter != 0:
|
|
737
|
+
raise RuntimeError(f"An instance of PureFnExecutionFrame can be used only once.")
|
|
738
|
+
if self.event_counter != 0:
|
|
739
|
+
raise RuntimeError(f"An instance of PureFnExecutionFrame can be used only once.")
|
|
736
740
|
self.portal.__enter__()
|
|
737
741
|
if isinstance(self.output_capturer, OutputCapturer):
|
|
738
742
|
self.output_capturer.__enter__()
|
|
@@ -794,9 +798,9 @@ class LoggingFnExecutionFrame(NotPicklableClass):
|
|
|
794
798
|
LoggingFnExecutionFrame.call_stack.pop()
|
|
795
799
|
|
|
796
800
|
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
801
|
+
_EXCEPTIONS_TOTAL_TXT = "Exceptions, total"
|
|
802
|
+
_EXCEPTIONS_TODAY_TXT = "Exceptions, today"
|
|
803
|
+
_EXCESSIVE_LOGGING_TXT = "Excessive logging"
|
|
800
804
|
|
|
801
805
|
class LoggingCodePortal(DataPortal):
|
|
802
806
|
"""A portal that supports function-level logging for events and exceptions.
|
|
@@ -918,12 +922,12 @@ class LoggingCodePortal(DataPortal):
|
|
|
918
922
|
"""
|
|
919
923
|
all_params = [super().describe()]
|
|
920
924
|
all_params.append(_describe_persistent_characteristic(
|
|
921
|
-
|
|
925
|
+
_EXCEPTIONS_TOTAL_TXT, len(self._crash_history)))
|
|
922
926
|
all_params.append(_describe_persistent_characteristic(
|
|
923
|
-
|
|
927
|
+
_EXCEPTIONS_TODAY_TXT
|
|
924
928
|
, len(self._crash_history.get_subdict(current_date_gmt_string()))))
|
|
925
929
|
all_params.append(_describe_runtime_characteristic(
|
|
926
|
-
|
|
930
|
+
_EXCESSIVE_LOGGING_TXT, self.excessive_logging))
|
|
927
931
|
|
|
928
932
|
result = pd.concat(all_params)
|
|
929
933
|
result.reset_index(drop=True, inplace=True)
|
{pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_050_safe_code_portals/safe_decorator.py
RENAMED
|
@@ -30,7 +30,8 @@ class safe(logging):
|
|
|
30
30
|
portal: The SafeCodePortal to attach the resulting SafeFn to. If
|
|
31
31
|
None, the active portal (if any) may be used by lower layers.
|
|
32
32
|
"""
|
|
33
|
-
|
|
33
|
+
if not (isinstance(portal, SafeCodePortal) or portal is None):
|
|
34
|
+
raise TypeError(f"portal must be a SafeCodePortal or None, got {type(portal).__name__}")
|
|
34
35
|
logging.__init__(self=self
|
|
35
36
|
, portal=portal
|
|
36
37
|
, excessive_logging=excessive_logging)
|
|
@@ -72,8 +72,10 @@ class SafeFnCallSignature(LoggingFnCallSignature):
|
|
|
72
72
|
fn: The safe function object to be called.
|
|
73
73
|
arguments: The keyword arguments to use for the call.
|
|
74
74
|
"""
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
if not isinstance(fn, SafeFn):
|
|
76
|
+
raise TypeError(f"fn must be a SafeFn instance, got {type(fn).__name__}")
|
|
77
|
+
if not isinstance(arguments, dict):
|
|
78
|
+
raise TypeError(f"arguments must be a dict, got {type(arguments).__name__}")
|
|
77
79
|
super().__init__(fn, arguments)
|
|
78
80
|
|
|
79
81
|
@property
|
|
@@ -23,6 +23,9 @@ Autonomous functions can have nested functions and classes.
|
|
|
23
23
|
Only ordinary functions can be autonomous. Asynchronous functions, closures,
|
|
24
24
|
class methods, and lambda functions cannot be autonomous.
|
|
25
25
|
|
|
26
|
+
An autonomous function can only be called with keyword arguments.
|
|
27
|
+
It can't be called with positional arguments.
|
|
28
|
+
|
|
26
29
|
Autonomous functions support partial application of arguments:
|
|
27
30
|
the process of pre-filling some arguments of a function,
|
|
28
31
|
producing a new autonomous function that takes the remaining arguments.
|
|
@@ -71,11 +74,13 @@ class autonomous(safe):
|
|
|
71
74
|
portal: Portal instance to use for autonomy and safety checks.
|
|
72
75
|
|
|
73
76
|
Raises:
|
|
74
|
-
|
|
77
|
+
TypeError: If portal is not an AutonomousCodePortal or None, or
|
|
75
78
|
if fixed_kwargs is not a dict or None.
|
|
76
79
|
"""
|
|
77
|
-
|
|
78
|
-
|
|
80
|
+
if not (isinstance(portal, AutonomousCodePortal) or portal is None):
|
|
81
|
+
raise TypeError(f"portal must be an AutonomousCodePortal or None, got {type(portal).__name__}")
|
|
82
|
+
if not (isinstance(fixed_kwargs, dict) or fixed_kwargs is None):
|
|
83
|
+
raise TypeError(f"fixed_kwargs must be a dict or None, got {type(fixed_kwargs).__name__}")
|
|
79
84
|
safe.__init__(self=self
|
|
80
85
|
, portal=portal
|
|
81
86
|
, excessive_logging=excessive_logging)
|