ripple-down-rules 0.6.42__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.42"
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,54 +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
- if not main_file_name:
877
- raise FileNotFoundError(f"Could not find the main python file for the model {self.model_name} in {model_dir}.")
878
- main_file_path = os.path.join(model_dir, main_file_name)
879
- else:
880
- main_file_path = python_file_path
881
- if not os.path.exists(main_file_path):
882
- raise ModuleNotFoundError(main_file_path)
883
- self.generated_python_file_name = Path(main_file_path).name.replace(".py", "")
884
-
885
- defs_file_path = main_file_path.replace(".py", "_defs.py")
886
- 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", "")
887
885
 
888
- cases_path = main_file_path.replace(".py", "_cases.py")
889
- cases_file_name = Path(cases_path).name.replace(".py", "")
890
- model_import_path = get_import_path_from_path(model_dir)
891
- cases_import_path = f"{model_import_path}.{cases_file_name}" if model_import_path \
892
- else cases_file_name
893
- if os.path.exists(cases_path):
894
- cases_module = importlib.import_module(cases_import_path, package=package_name)
895
- importlib.reload(cases_module)
896
- else:
897
- cases_module = None
898
-
899
- defs_import_path = f"{model_import_path}.{defs_file_name}" if model_import_path \
900
- else defs_file_name
901
- defs_module = importlib.import_module(defs_import_path, package=package_name)
902
- importlib.reload(defs_module)
886
+ self.update_rdr_metadata_from_python(main_module)
903
887
 
904
- main_file_name = Path(main_file_path).name.replace(".py", "")
905
- main_import_path = f"{model_import_path}.{main_file_name}" if model_import_path \
906
- else main_file_name
907
- main_module = importlib.import_module(main_import_path, package=package_name)
908
- 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)
909
891
 
910
- self.start_rule.conclusion_name = main_module.attribute_name
911
- self.update_rdr_metadata_from_python(main_module)
912
- functions_source = extract_function_source(defs_file_path, all_func_names, include_signature=False)
913
- scope = extract_imports(defs_file_path, package_name=package_name)
914
892
  for rule in all_rules:
915
893
  if rule.conditions is not None:
916
894
  conditions_wrapper_func_name = rule.generated_conditions_function_name
917
895
  user_input = functions_source[conditions_wrapper_func_name]
918
896
  rule.conditions = CallableExpression(user_input, (bool,), scope=scope)
919
- if os.path.exists(cases_path):
897
+ if cases_module:
920
898
  rule.corner_case_metadata = cases_module.__dict__.get(rule.generated_corner_case_object_name, None)
921
899
  if not isinstance(rule, MultiClassStopRule):
900
+ rule.conclusion_name = main_module.attribute_name
922
901
  conclusion_wrapper_func_name = rule.generated_conclusion_function_name
923
902
  user_input = functions_source[conclusion_wrapper_func_name]
924
903
  conclusion_func = defs_module.__dict__.get(rule.generated_conclusion_function_name)
@@ -926,6 +905,34 @@ class RDRWithCodeWriter(RippleDownRules, ABC):
926
905
  rule.conclusion = CallableExpression(user_input, conclusion_type, scope=scope,
927
906
  mutually_exclusive=self.mutually_exclusive)
928
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
+
929
936
  @abstractmethod
930
937
  def write_rules_as_source_code_to_file(self, rule: Rule, file, parent_indent: str = "",
931
938
  defs_file: Optional[str] = None, cases_file: Optional[str] = None,
@@ -1532,21 +1539,14 @@ class GeneralRDR(RippleDownRules):
1532
1539
  :return: An instance of the class.
1533
1540
  """
1534
1541
  if python_file_path is None:
1535
- file_name = get_file_that_ends_with(model_dir, f"_{cls.get_acronym().lower()}.py",)
1536
- main_python_file_path = os.path.join(model_dir, file_name)
1542
+ main_python_file_path = cls.get_generated_python_file_path(model_dir)
1537
1543
  else:
1538
1544
  main_python_file_path = python_file_path
1539
- main_python_file_name = Path(main_python_file_path).name.replace('.py', '')
1540
- main_module_import_path = get_import_path_from_path(model_dir)
1541
- main_module_import_path = f"{main_module_import_path}.{main_python_file_name}" \
1542
- if main_module_import_path else main_python_file_name
1543
- main_module = importlib.import_module(main_module_import_path)
1544
- importlib.reload(main_module)
1545
+ main_module = get_and_import_python_module(main_python_file_path, parent_package_name=parent_package_name)
1545
1546
  classifiers_dict = main_module.classifiers_dict
1546
1547
  start_rules_dict = {}
1547
1548
  for rdr_name, rdr_module in classifiers_dict.items():
1548
- rdr_module_name = rdr_module.__name__
1549
- rdr_acronym = rdr_module_name.split('_')[-1]
1549
+ rdr_acronym = rdr_module.__name__.split('_')[-1]
1550
1550
  rdr_type = cls.get_rdr_type_from_acronym(rdr_acronym)
1551
1551
  rdr_model_path = main_python_file_path.replace('_rdr.py', f'_{rdr_name}_{rdr_acronym}.py')
1552
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.42
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=Cz1RyE7DY-e05V8keWdWjsykmUF-cCRomJdyuWL81l0,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=8qiuaoJ56j-F0b3Ob0sTMlizyYS1YCkWu0bPr5_0TZo,81490
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.42.dist-info/licenses/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
21
- ripple_down_rules-0.6.42.dist-info/METADATA,sha256=0Cl2r-t_u-UDFbanHIbLnUnJ5vwTghzmZraqDlwxTAA,48294
22
- ripple_down_rules-0.6.42.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
- ripple_down_rules-0.6.42.dist-info/top_level.txt,sha256=VeoLhEhyK46M1OHwoPbCQLI1EifLjChqGzhQ6WEUqeM,18
24
- ripple_down_rules-0.6.42.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,,