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.
Files changed (65) hide show
  1. {pythagoras-0.24.7 → pythagoras-0.24.10}/PKG-INFO +1 -1
  2. {pythagoras-0.24.7 → pythagoras-0.24.10}/pyproject.toml +1 -1
  3. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_010_basic_portals/__init__.py +0 -1
  4. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_010_basic_portals/basic_portal_core_classes.py +21 -15
  5. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_010_basic_portals/portal_tester.py +3 -2
  6. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_020_ordinary_code_portals/__init__.py +1 -0
  7. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_020_ordinary_code_portals/code_normalizer.py +14 -14
  8. pythagoras-0.24.10/src/pythagoras/_020_ordinary_code_portals/exceptions.py +11 -0
  9. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_020_ordinary_code_portals/function_processing.py +10 -10
  10. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_020_ordinary_code_portals/ordinary_decorator.py +2 -1
  11. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_020_ordinary_code_portals/ordinary_portal_core_classes.py +11 -9
  12. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_030_data_portals/data_portal_core_classes.py +13 -11
  13. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_030_data_portals/storable_decorator.py +3 -1
  14. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_040_logging_code_portals/logging_decorator.py +4 -2
  15. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_040_logging_code_portals/logging_portal_core_classes.py +17 -13
  16. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_050_safe_code_portals/safe_decorator.py +2 -1
  17. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_050_safe_code_portals/safe_portal_core_classes.py +4 -2
  18. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_060_autonomous_code_portals/autonomous_decorators.py +8 -3
  19. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_060_autonomous_code_portals/autonomous_portal_core_classes.py +23 -17
  20. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_060_autonomous_code_portals/names_usage_analyzer.py +18 -15
  21. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_070_protected_code_portals/protected_decorators.py +4 -2
  22. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_070_protected_code_portals/protected_portal_core_classes.py +15 -8
  23. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_080_pure_code_portals/__init__.py +5 -1
  24. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_080_pure_code_portals/pure_core_classes.py +22 -15
  25. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_080_pure_code_portals/recursion_pre_validator.py +8 -4
  26. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_090_swarming_portals/swarming_portals.py +17 -13
  27. pythagoras-0.24.7/src/pythagoras/_010_basic_portals/exceptions.py +0 -36
  28. {pythagoras-0.24.7 → pythagoras-0.24.10}/README.md +0 -0
  29. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/.DS_Store +0 -0
  30. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_010_basic_portals/long_infoname.py +0 -0
  31. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_010_basic_portals/post_init_metaclass.py +0 -0
  32. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_030_data_portals/__init__.py +0 -0
  33. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_030_data_portals/ready_and_get.py +0 -0
  34. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_040_logging_code_portals/__init__.py +0 -0
  35. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_040_logging_code_portals/exception_processing_tracking.py +0 -0
  36. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_040_logging_code_portals/execution_environment_summary.py +0 -0
  37. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_040_logging_code_portals/kw_args.py +0 -0
  38. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_040_logging_code_portals/notebook_checker.py +0 -0
  39. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_040_logging_code_portals/output_capturer.py +0 -0
  40. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_040_logging_code_portals/uncaught_exceptions.py +0 -0
  41. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_050_safe_code_portals/__init__.py +0 -0
  42. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_060_autonomous_code_portals/__init__.py +0 -0
  43. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_070_protected_code_portals/__init__.py +0 -0
  44. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_070_protected_code_portals/basic_pre_validators.py +0 -0
  45. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_070_protected_code_portals/fn_arg_names_checker.py +0 -0
  46. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_070_protected_code_portals/list_flattener.py +0 -0
  47. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_070_protected_code_portals/package_manager.py +0 -0
  48. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_070_protected_code_portals/system_utils.py +0 -0
  49. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_070_protected_code_portals/validation_succesful_const.py +0 -0
  50. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_080_pure_code_portals/pure_decorator.py +0 -0
  51. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_090_swarming_portals/__init__.py +0 -0
  52. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_090_swarming_portals/output_suppressor.py +0 -0
  53. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_100_top_level_API/__init__.py +0 -0
  54. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_100_top_level_API/default_local_portal.py +0 -0
  55. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_100_top_level_API/top_level_API.py +0 -0
  56. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_800_signatures_and_converters/__init__.py +0 -0
  57. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_800_signatures_and_converters/base_16_32_convertors.py +0 -0
  58. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_800_signatures_and_converters/current_date_gmt_str.py +0 -0
  59. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_800_signatures_and_converters/hash_signatures.py +0 -0
  60. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_800_signatures_and_converters/node_signature.py +0 -0
  61. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_800_signatures_and_converters/random_signatures.py +0 -0
  62. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_900_project_stats_collector/__init__.py +0 -0
  63. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/_900_project_stats_collector/project_analyzer.py +0 -0
  64. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/__init__.py +0 -0
  65. {pythagoras-0.24.7 → pythagoras-0.24.10}/src/pythagoras/core/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pythagoras
3
- Version: 0.24.7
3
+ Version: 0.24.10
4
4
  Summary: Planet-scale distributed computing in Python.
5
5
  Keywords: cloud,ML,AI,serverless,distributed,parallel,machine-learning,deep-learning,pythagoras
6
6
  Author: Volodymyr (Vlad) Pavlov
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "pythagoras"
7
- version = "0.24.7"
7
+ version = "0.24.10"
8
8
  authors = [
9
9
  {name = "Volodymyr (Vlad) Pavlov", email = "vlpavlov@ieee.org"},
10
10
  ]
@@ -32,7 +32,6 @@ be subclassed to provide additional functionality.
32
32
  """
33
33
 
34
34
  from .post_init_metaclass import *
35
- from .exceptions import *
36
35
  from .basic_portal_core_classes import *
37
36
  from .portal_tester import _PortalTester
38
37
  from .long_infoname import *
@@ -65,8 +65,8 @@ def _describe_runtime_characteristic(name, value) -> pd.DataFrame:
65
65
  return pd.DataFrame(d)
66
66
 
67
67
 
68
- BASE_DIRECTORY_TXT = "Base directory"
69
- BACKEND_TYPE_TXT = "Backend type"
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
- assert isinstance(target_directory_str, str)
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
- assert isinstance(obj_str_id, str)
284
- assert isinstance(portal_str_id, str)
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
- BASE_DIRECTORY_TXT, self._root_dict.base_dir))
392
+ _BASE_DIRECTORY_TXT, self._root_dict.base_dir))
390
393
  all_params.append(_describe_persistent_characteristic(
391
- BACKEND_TYPE_TXT, self._root_dict.__class__.__name__))
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
- assert _active_portals_stack[-1] == self, (
415
- "Inconsistent state of the portal stack. "
416
- + "Most probably, portal.__enter__() method was called explicitly "
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
- assert portal is None or isinstance(portal, BasicPortal)
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
- assert isinstance(portal_str_id, str)
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
- assert self._str_id in _all_activated_portal_aware_objects
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
- assert self.is_activated
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
- AssertionError: If portal_class is not a subclass of BasicPortal.
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
- assert issubclass(portal_class, BasicPortal)
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
@@ -28,4 +28,5 @@ from .ordinary_portal_core_classes import *
28
28
  from .function_processing import *
29
29
  from .ordinary_decorator import *
30
30
  from .code_normalizer import *
31
+ from .exceptions import *
31
32
 
@@ -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
- NonCompliantFunction: If the function has multiple decorators when
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
- AssertionError: If input is neither a callable nor a string, if
59
- parsing assumptions fail (e.g., unexpected AST node types), or
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
- assert callable(a_func) or isinstance(a_func, str)
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
- assert line.startswith(chars_to_remove)
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
- assert isinstance(code_ast, ast.Module)
101
- assert isinstance(code_ast.body[0], ast.FunctionDef), (
102
- f"{type(code_ast.body[0])=}")
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 NonCompliantFunction(
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
- assert pth_dec_counter == 1
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 .._010_basic_portals.exceptions import NonCompliantFunction
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
- NonCompliantFunction: If the callable violates any of the ordinarity
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 NonCompliantFunction(f"{name} must be callable.")
107
+ raise FunctionError(f"{name} must be callable.")
108
108
 
109
109
  if not inspect.isfunction(a_func):
110
- raise NonCompliantFunction(f"The function {name} is not ordinary."
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 NonCompliantFunction(f"The function {name} can't be "
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 NonCompliantFunction(f"The function {name} can't be a closure,"
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 NonCompliantFunction(f"The function {name} can't be lambda,"
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 NonCompliantFunction("Pythagoras only allows functions "
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 NonCompliantFunction(f"The function {name} can't be "
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 NonCompliantFunction(f"The function {name} can't have "
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
- assert portal is None or isinstance(portal, OrdinaryCodePortal)
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
- NonCompliantFunction: If the function is not compliant with Pythagoras'
36
+ FunctionError: If the function is not compliant with Pythagoras'
36
37
  ordinarity rules or multiple decorators are present.
37
- AssertionError: If input type is invalid or integrity checks fail.
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
- REGISTERED_FUNCTIONS_TXT = "Registered functions"
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
- REGISTERED_FUNCTIONS_TXT, self.get_number_of_linked_functions()))
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
- NonCompliantFunction: If the provided function source is not
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
- AssertionError: If positional arguments are supplied.
372
+ TypeError: If positional arguments are supplied.
372
373
  """
373
- assert len(args) == 0, (f"Function {self.name} can't"
374
- + " be called with positional arguments,"
375
- + " only keyword arguments are allowed.")
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
- TOTAL_VALUES_TXT = "Values, total"
21
- PROBABILITY_OF_CHECKS_TXT = "Probability of consistency checks"
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
- AssertionError: If the active portal is not an instance of DataPortal.
31
+ TypeError: If the active portal is not an instance of DataPortal.
32
32
  """
33
33
  portal = get_active_portal()
34
- assert isinstance(portal, DataPortal)
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
- AssertionError: If any returned portal is not an instance of DataPortal.
47
+ TypeError: If any returned portal is not an instance of DataPortal.
47
48
  """
48
49
  portals = get_nonactive_portals()
49
- assert all(isinstance(p, DataPortal) for p in portals)
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
- TOTAL_VALUES_TXT, len(self._value_store)))
217
+ _TOTAL_VALUES_TXT, len(self._value_store)))
215
218
  all_params.append(_describe_runtime_characteristic(
216
- PROBABILITY_OF_CHECKS_TXT, self.p_consistency_checks))
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
- assert not isinstance(data, HashAddr), (
497
- "get_ValueAddr is the only way to "
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)
@@ -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
- assert isinstance(portal, DataPortal) or portal is None
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
- assert isinstance(excessive_logging, (bool,Joker))
31
- assert isinstance(portal, LoggingCodePortal) or portal is None
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
- assert isinstance(fn, LoggingFn)
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
- assert not self.context_used, (
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
- assert self.exception_counter == 0, (
733
- "An instance of PureFnExecutionFrame can be used only once.")
734
- assert self.event_counter == 0, (
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
- EXCEPTIONS_TOTAL_TXT = "Exceptions, total"
798
- EXCEPTIONS_TODAY_TXT = "Exceptions, today"
799
- EXCESSIVE_LOGGING_TXT = "Excessive logging"
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
- EXCEPTIONS_TOTAL_TXT, len(self._crash_history)))
925
+ _EXCEPTIONS_TOTAL_TXT, len(self._crash_history)))
922
926
  all_params.append(_describe_persistent_characteristic(
923
- EXCEPTIONS_TODAY_TXT
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
- EXCESSIVE_LOGGING_TXT, self.excessive_logging))
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)
@@ -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
- assert isinstance(portal, SafeCodePortal) or portal is None
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
- assert isinstance(fn, SafeFn)
76
- assert isinstance(arguments, dict)
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
- AssertionError: If portal is not an AutonomousCodePortal or None, or
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
- assert isinstance(portal, AutonomousCodePortal) or portal is None
78
- assert isinstance(fixed_kwargs, dict) or fixed_kwargs is None
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)