ripple-down-rules 0.6.41__py3-none-any.whl → 0.6.43__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.
@@ -1,4 +1,4 @@
1
- __version__ = "0.6.41"
1
+ __version__ = "0.6.43"
2
2
 
3
3
  import logging
4
4
  logger = logging.Logger("rdr")
ripple_down_rules/rdr.py CHANGED
@@ -4,10 +4,12 @@ import ast
4
4
  import importlib
5
5
  import json
6
6
  import os
7
+ import sys
7
8
  from abc import ABC, abstractmethod
8
9
  from copy import copy
9
10
  from dataclasses import is_dataclass
10
11
  from io import TextIOWrapper
12
+ from os.path import dirname
11
13
  from pathlib import Path
12
14
  from types import NoneType, ModuleType
13
15
 
@@ -42,7 +44,8 @@ except ImportError as e:
42
44
  from .utils import draw_tree, make_set, SubclassJSONSerializer, make_list, get_type_from_string, \
43
45
  is_value_conflicting, extract_function_source, extract_imports, get_full_class_name, \
44
46
  is_iterable, str_to_snake_case, get_import_path_from_path, get_imports_from_types, render_tree, \
45
- get_types_to_import_from_func_type_hints, get_function_return_type, get_file_that_ends_with
47
+ get_types_to_import_from_func_type_hints, get_function_return_type, get_file_that_ends_with, \
48
+ get_and_import_python_module, get_and_import_python_modules_in_a_package
46
49
 
47
50
 
48
51
  class RippleDownRules(SubclassJSONSerializer, ABC):
@@ -524,7 +527,10 @@ class RippleDownRules(SubclassJSONSerializer, ABC):
524
527
  :param model_path: The path to the model directory.
525
528
  :return: The name of the model.
526
529
  """
527
- return Path(model_path).name
530
+ file_name = get_file_that_ends_with(model_path, f"_{cls.get_acronym().lower()}.py")
531
+ if file_name is None:
532
+ raise FileNotFoundError(f"Could not find the python file for the model in the given path: {model_path}.")
533
+ return file_name.replace('.py', '')
528
534
 
529
535
  @property
530
536
  def generated_python_file_name(self) -> str:
@@ -871,52 +877,27 @@ class RDRWithCodeWriter(RippleDownRules, ABC):
871
877
  if not isinstance(rule, MultiClassStopRule)]
872
878
  all_func_names = condition_func_names + conclusion_func_names
873
879
 
874
- if python_file_path is None:
875
- main_file_name = get_file_that_ends_with(model_dir, f"_{self.get_acronym().lower()}.py")
876
- main_file_path = os.path.join(model_dir, main_file_name)
877
- else:
878
- main_file_path = python_file_path
879
- if not os.path.exists(main_file_path):
880
- raise ModuleNotFoundError(main_file_path)
881
- self.generated_python_file_name = Path(main_file_path).name.replace(".py", "")
882
-
883
- defs_file_path = main_file_path.replace(".py", "_defs.py")
884
- defs_file_name = Path(defs_file_path).name.replace(".py", "")
880
+ main_module, defs_module, cases_module = self.get_and_import_model_python_modules(
881
+ model_dir,
882
+ python_file_path=python_file_path,
883
+ parent_package_name=package_name)
884
+ self.generated_python_file_name = Path(main_module.__file__).name.replace(".py", "")
885
885
 
886
- cases_path = main_file_path.replace(".py", "_cases.py")
887
- cases_file_name = Path(cases_path).name.replace(".py", "")
888
- model_import_path = get_import_path_from_path(model_dir)
889
- cases_import_path = f"{model_import_path}.{cases_file_name}" if model_import_path \
890
- else cases_file_name
891
- if os.path.exists(cases_path):
892
- cases_module = importlib.import_module(cases_import_path, package=package_name)
893
- importlib.reload(cases_module)
894
- else:
895
- cases_module = None
896
-
897
- defs_import_path = f"{model_import_path}.{defs_file_name}" if model_import_path \
898
- else defs_file_name
899
- defs_module = importlib.import_module(defs_import_path, package=package_name)
900
- importlib.reload(defs_module)
886
+ self.update_rdr_metadata_from_python(main_module)
901
887
 
902
- main_file_name = Path(main_file_path).name.replace(".py", "")
903
- main_import_path = f"{model_import_path}.{main_file_name}" if model_import_path \
904
- else main_file_name
905
- main_module = importlib.import_module(main_import_path, package=package_name)
906
- importlib.reload(main_module)
888
+ functions_source = extract_function_source(defs_module.__file__,
889
+ all_func_names, include_signature=False)
890
+ scope = extract_imports(defs_module.__file__, package_name=package_name)
907
891
 
908
- self.start_rule.conclusion_name = main_module.attribute_name
909
- self.update_rdr_metadata_from_python(main_module)
910
- functions_source = extract_function_source(defs_file_path, all_func_names, include_signature=False)
911
- scope = extract_imports(defs_file_path, package_name=package_name)
912
892
  for rule in all_rules:
913
893
  if rule.conditions is not None:
914
894
  conditions_wrapper_func_name = rule.generated_conditions_function_name
915
895
  user_input = functions_source[conditions_wrapper_func_name]
916
896
  rule.conditions = CallableExpression(user_input, (bool,), scope=scope)
917
- if os.path.exists(cases_path):
897
+ if cases_module:
918
898
  rule.corner_case_metadata = cases_module.__dict__.get(rule.generated_corner_case_object_name, None)
919
899
  if not isinstance(rule, MultiClassStopRule):
900
+ rule.conclusion_name = main_module.attribute_name
920
901
  conclusion_wrapper_func_name = rule.generated_conclusion_function_name
921
902
  user_input = functions_source[conclusion_wrapper_func_name]
922
903
  conclusion_func = defs_module.__dict__.get(rule.generated_conclusion_function_name)
@@ -924,6 +905,34 @@ class RDRWithCodeWriter(RippleDownRules, ABC):
924
905
  rule.conclusion = CallableExpression(user_input, conclusion_type, scope=scope,
925
906
  mutually_exclusive=self.mutually_exclusive)
926
907
 
908
+ @classmethod
909
+ def get_and_import_model_python_modules(cls, model_dir: str,
910
+ python_file_path: Optional[str] = None,
911
+ parent_package_name: Optional[str] = None)\
912
+ -> Tuple[ModuleType, ModuleType, ModuleType]:
913
+ """
914
+ Get and import the python modules that contain the RDR classifier function, definitions, and corner cases.
915
+
916
+ :param model_dir: The path to the directory where the generated python files are located.
917
+ :param python_file_path: The path to the generated python file that contains the RDR classifier function.
918
+ :param parent_package_name: The name of the package that contains the RDR classifier function, this
919
+ is required in case of relative imports in the generated python file.
920
+ :return: A tuple containing the main module, defs module, and cases module.
921
+ """
922
+ if python_file_path is None:
923
+ main_file_path = cls.get_generated_python_file_path(model_dir)
924
+ else:
925
+ main_file_path = python_file_path
926
+ if not os.path.exists(main_file_path):
927
+ raise ModuleNotFoundError(main_file_path)
928
+
929
+ defs_file_path = main_file_path.replace(".py", "_defs.py")
930
+ cases_path = main_file_path.replace(".py", "_cases.py")
931
+
932
+ main_module, defs_module, cases_module = get_and_import_python_modules_in_a_package(
933
+ [main_file_path, defs_file_path, cases_path], parent_package_name=parent_package_name)
934
+ return main_module, defs_module, cases_module
935
+
927
936
  @abstractmethod
928
937
  def write_rules_as_source_code_to_file(self, rule: Rule, file, parent_indent: str = "",
929
938
  defs_file: Optional[str] = None, cases_file: Optional[str] = None,
@@ -1530,21 +1539,14 @@ class GeneralRDR(RippleDownRules):
1530
1539
  :return: An instance of the class.
1531
1540
  """
1532
1541
  if python_file_path is None:
1533
- file_name = get_file_that_ends_with(model_dir, f"_{cls.get_acronym().lower()}.py",)
1534
- main_python_file_path = os.path.join(model_dir, file_name)
1542
+ main_python_file_path = cls.get_generated_python_file_path(model_dir)
1535
1543
  else:
1536
1544
  main_python_file_path = python_file_path
1537
- main_python_file_name = Path(main_python_file_path).name.replace('.py', '')
1538
- main_module_import_path = get_import_path_from_path(model_dir)
1539
- main_module_import_path = f"{main_module_import_path}.{main_python_file_name}" \
1540
- if main_module_import_path else main_python_file_name
1541
- main_module = importlib.import_module(main_module_import_path)
1542
- importlib.reload(main_module)
1545
+ main_module = get_and_import_python_module(main_python_file_path, parent_package_name=parent_package_name)
1543
1546
  classifiers_dict = main_module.classifiers_dict
1544
1547
  start_rules_dict = {}
1545
1548
  for rdr_name, rdr_module in classifiers_dict.items():
1546
- rdr_module_name = rdr_module.__name__
1547
- rdr_acronym = rdr_module_name.split('_')[-1]
1549
+ rdr_acronym = rdr_module.__name__.split('_')[-1]
1548
1550
  rdr_type = cls.get_rdr_type_from_acronym(rdr_acronym)
1549
1551
  rdr_model_path = main_python_file_path.replace('_rdr.py', f'_{rdr_name}_{rdr_acronym}.py')
1550
1552
  rdr = rdr_type.from_python(model_dir, python_file_path=rdr_model_path, parent_package_name=parent_package_name)
@@ -286,16 +286,10 @@ class Rule(NodeMixin, SubclassJSONSerializer, ABC):
286
286
  pass
287
287
 
288
288
  def _to_json(self) -> Dict[str, Any]:
289
- # try:
290
- # corner_case = SubclassJSONSerializer.to_json_static(self.corner_case) if self.corner_case else None
291
- # except Exception as e:
292
- # logging.debug("Failed to serialize corner case to json, setting it to None. Error: %s", e)
293
- # corner_case = None
294
289
  json_serialization = {"_type": get_full_class_name(type(self)),
295
290
  "conditions": self.conditions.to_json(),
296
291
  "conclusion": conclusion_to_json(self.conclusion),
297
292
  "parent": self.parent.json_serialization if self.parent else None,
298
- # "corner_case": corner_case,
299
293
  "conclusion_name": self.conclusion_name,
300
294
  "weight": self.weight,
301
295
  "uid": self.uid}
@@ -303,15 +297,9 @@ class Rule(NodeMixin, SubclassJSONSerializer, ABC):
303
297
 
304
298
  @classmethod
305
299
  def _from_json(cls, data: Dict[str, Any]) -> Rule:
306
- # try:
307
- # corner_case = Case.from_json(data["corner_case"])
308
- # except Exception as e:
309
- # logging.debug("Failed to load corner case from json, setting it to None.")
310
- # corner_case = None
311
300
  loaded_rule = cls(conditions=CallableExpression.from_json(data["conditions"]),
312
301
  conclusion=CallableExpression.from_json(data["conclusion"]),
313
302
  parent=cls.from_json(data["parent"]),
314
- # corner_case=corner_case,
315
303
  conclusion_name=data["conclusion_name"],
316
304
  weight=data["weight"],
317
305
  uid=data["uid"])
@@ -20,7 +20,7 @@ from pathlib import Path
20
20
  from subprocess import check_call
21
21
  from tempfile import NamedTemporaryFile
22
22
  from textwrap import dedent
23
- from types import NoneType
23
+ from types import NoneType, ModuleType
24
24
  import inspect
25
25
 
26
26
  import six
@@ -60,6 +60,49 @@ if TYPE_CHECKING:
60
60
  import ast
61
61
 
62
62
 
63
+ def get_and_import_python_modules_in_a_package(file_paths: List[str],
64
+ parent_package_name: Optional[str] = None) -> List[Optional[ModuleType]]:
65
+ """
66
+ :param file_paths: The paths to the python files to import.
67
+ :param parent_package_name: The name of the parent package to use for relative imports.
68
+ :return: The imported modules.
69
+ """
70
+ package_path = dirname(file_paths[0])
71
+ package_import_path = get_import_path_from_path(package_path)
72
+ file_names = [Path(file_path).name.replace(".py", "") for file_path in file_paths]
73
+ module_import_paths = [
74
+ f"{package_import_path}.{file_name}" if package_import_path else file_name
75
+ for file_name in file_names
76
+ ]
77
+ modules = [
78
+ importlib.import_module(module_import_path, package=parent_package_name)
79
+ if os.path.exists(file_paths[i]) else None
80
+ for i, module_import_path in enumerate(module_import_paths)
81
+ ]
82
+ for module in modules:
83
+ if module is not None:
84
+ importlib.reload(module)
85
+ return modules
86
+
87
+
88
+ def get_and_import_python_module(python_file_path: str, package_import_path: Optional[str] = None,
89
+ parent_package_name: Optional[str] = None) -> ModuleType:
90
+ """
91
+ :param python_file_path: The path to the python file to import.
92
+ :param package_import_path: The import path of the package that contains the python file.
93
+ :param parent_package_name: The name of the parent package to use for relative imports.
94
+ :return: The imported module.
95
+ """
96
+ if package_import_path is None:
97
+ package_path = dirname(python_file_path)
98
+ package_import_path = get_import_path_from_path(package_path)
99
+ file_name = Path(python_file_path).name.replace(".py", "")
100
+ module_import_path = f"{package_import_path}.{file_name}" if package_import_path else file_name
101
+ module = importlib.import_module(module_import_path, package=parent_package_name)
102
+ importlib.reload(module)
103
+ return module
104
+
105
+
63
106
  def str_to_snake_case(snake_str: str) -> str:
64
107
  """
65
108
  Convert a string to snake case.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ripple_down_rules
3
- Version: 0.6.41
3
+ Version: 0.6.43
4
4
  Summary: Implements the various versions of Ripple Down Rules (RDR) for knowledge representation and reasoning.
5
5
  Author-email: Abdelrhman Bassiouny <abassiou@uni-bremen.de>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -1,11 +1,11 @@
1
- ripple_down_rules/__init__.py,sha256=nYtH-We95fPZmgC7xF4K09TKVWFy7gOrO7Hv4zidPiI,99
1
+ ripple_down_rules/__init__.py,sha256=NAOJdOIPnZXrnWLryxT1ZsFVSXmALEqXeTLcaySBVa8,99
2
2
  ripple_down_rules/experts.py,sha256=KXwWCmDrCffu9HW3yNewqWc1e5rnPI5Rnc981w_5M7U,17896
3
3
  ripple_down_rules/helpers.py,sha256=X1psHOqrb4_xYN4ssQNB8S9aRKKsqgihAyWJurN0dqk,5499
4
- ripple_down_rules/rdr.py,sha256=s9y3ImomYOw3WKHggljfIaNFRxid3qtM0h-yso9oOdk,81327
4
+ ripple_down_rules/rdr.py,sha256=wK6Altc7YNP9JR_mUrKignTkojq2kUy9L9g0g9_c7nM,81626
5
5
  ripple_down_rules/rdr_decorators.py,sha256=xoBGsIJMkJYUdsrsEaPZqoAsGuXkuVZAKCoP-xD2Iv8,11668
6
- ripple_down_rules/rules.py,sha256=32apFTxtWXKRQ2MJDnqc1URjRJDnNBe_t5A_gGfKNd0,29349
6
+ ripple_down_rules/rules.py,sha256=L4Ws-x3g5ljE0GrDt4LAib2qtR1C1_Ra4cRcIB-IQDI,28702
7
7
  ripple_down_rules/start-code-server.sh,sha256=otClk7VmDgBOX2TS_cjws6K0UwvgAUJhoA0ugkPCLqQ,949
8
- ripple_down_rules/utils.py,sha256=SuZKBnGZvfDqlry7PY_N8AzLBOhEeuev57AVS-fJy78,77992
8
+ ripple_down_rules/utils.py,sha256=lMRJoCtrbP6YSMqmgnqB6hY9UyQtn-Db-syUMoLzpUU,80035
9
9
  ripple_down_rules/datastructures/__init__.py,sha256=V2aNgf5C96Y5-IGghra3n9uiefpoIm_QdT7cc_C8cxQ,111
10
10
  ripple_down_rules/datastructures/callable_expression.py,sha256=rzMrpD5oztaCRlt3hQ2B_xZ09cSuJNkYOCePndfQJRA,13684
11
11
  ripple_down_rules/datastructures/case.py,sha256=dfLnrjsHIVF2bgbz-4ID7OdQvw68V71btCeTK372P-g,15667
@@ -17,8 +17,8 @@ ripple_down_rules/user_interface/ipython_custom_shell.py,sha256=RLdPqPxx-a0Sh74U
17
17
  ripple_down_rules/user_interface/object_diagram.py,sha256=FEa2HaYR9QmTE6NsOwBvZ0jqmu3DKyg6mig2VE5ZP4Y,4956
18
18
  ripple_down_rules/user_interface/prompt.py,sha256=WPbw_8_-8SpF2ISyRZRuFwPKBEuGC4HaX3lbCPFHhh8,10314
19
19
  ripple_down_rules/user_interface/template_file_creator.py,sha256=uSbosZS15MOR3Nv7M3MrFuoiKXyP4cBId-EK3I6stHM,13660
20
- ripple_down_rules-0.6.41.dist-info/licenses/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
21
- ripple_down_rules-0.6.41.dist-info/METADATA,sha256=DkE-Aey_rdGNqR0UzZM4uT7BMaCcSB0vobs2I1sYYRo,48294
22
- ripple_down_rules-0.6.41.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
- ripple_down_rules-0.6.41.dist-info/top_level.txt,sha256=VeoLhEhyK46M1OHwoPbCQLI1EifLjChqGzhQ6WEUqeM,18
24
- ripple_down_rules-0.6.41.dist-info/RECORD,,
20
+ ripple_down_rules-0.6.43.dist-info/licenses/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
21
+ ripple_down_rules-0.6.43.dist-info/METADATA,sha256=H9Y0gibG6FcFb2LLUiZVwiGWFdKDlXJVY4GYkvZoBeA,48294
22
+ ripple_down_rules-0.6.43.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
+ ripple_down_rules-0.6.43.dist-info/top_level.txt,sha256=VeoLhEhyK46M1OHwoPbCQLI1EifLjChqGzhQ6WEUqeM,18
24
+ ripple_down_rules-0.6.43.dist-info/RECORD,,