ripple-down-rules 0.6.49__py3-none-any.whl → 0.6.50__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.49"
1
+ __version__ = "0.6.50"
2
2
 
3
3
  import logging
4
4
  import sys
@@ -4,17 +4,18 @@ that can be used with any python function such that this function can benefit fr
4
4
  of the RDRs.
5
5
  """
6
6
  import os.path
7
+ from dataclasses import dataclass, field
7
8
  from functools import wraps
8
9
  from typing import get_origin
9
10
 
10
11
  from typing_extensions import Callable, Optional, Type, Tuple, Dict, Any, Self, get_type_hints, List, Union, Sequence
11
12
 
12
- from .utils import get_type_from_type_hint
13
13
  from .datastructures.case import Case
14
14
  from .datastructures.dataclasses import CaseQuery
15
15
  from .experts import Expert, Human
16
16
  from .rdr import GeneralRDR
17
- from . import logger
17
+ from .utils import get_type_from_type_hint
18
+
18
19
  try:
19
20
  from .user_interface.gui import RDRCaseViewer
20
21
  except ImportError:
@@ -23,63 +24,92 @@ from .utils import get_method_args_as_dict, get_func_rdr_model_name, make_set, \
23
24
  get_method_class_if_exists, str_to_snake_case, make_list
24
25
 
25
26
 
27
+ @dataclass(unsafe_hash=True)
26
28
  class RDRDecorator:
27
- rdr: GeneralRDR
28
-
29
- def __init__(self, models_dir: str,
30
- output_type: Tuple[Type],
31
- mutual_exclusive: bool,
32
- output_name: str = "output_",
33
- fit: bool = True,
34
- expert: Optional[Expert] = None,
35
- update_existing_rules: bool = True,
36
- viewer: Optional[RDRCaseViewer] = None,
37
- package_name: Optional[str] = None,
38
- use_generated_classifier: bool = False,
39
- ask_now: Callable[Dict[str, Any], bool] = lambda _: True,
40
- fitting_decorator: Optional[Callable] = None,
41
- generate_dot_file: bool = False) -> None:
42
- """
43
- :param models_dir: The directory to save/load the RDR models.
44
- :param output_type: The type of the output. This is used to create the RDR model.
45
- :param mutual_exclusive: If True, the output types are mutually exclusive.
46
- If None, the RDR model will not be saved as a python file.
47
- :param output_name: The name of the output. This is used to create the RDR model.
48
- :param fit: If True, the function will be in fit mode. This means that the RDR will prompt the user for the
49
- correct output if the function's output is not in the RDR model. If False, the function will be in
50
- classification mode. This means that the RDR will classify the function's output based on the RDR model.
51
- :param expert: The expert that will be used to prompt the user for the correct output. If None, a Human
52
- expert will be used.
53
- :param update_existing_rules: If True, the function will update the existing RDR rules
54
- even if they gave an output.
55
- :param viewer: The viewer to use for the RDR model. If None, no viewer will be used.
56
- :param package_name: The package name to use for relative imports in the RDR model.
57
- :param use_generated_classifier: If True, the function will use the generated classifier instead of the RDR model.
58
- :param ask_now: A callable that takes the case dictionary and returns True if the user should be asked for
59
- the output, or False if the function should return the output without asking.
60
- :param fitting_decorator: A decorator to use for the fitting function. If None, no decorator will be used.
61
- :param generate_dot_file: If True, the RDR model will generate a dot file for visualization.
62
- :return: A decorator to use a GeneralRDR as a classifier that monitors and modifies the function's output.
63
- """
64
- self.rdr_models_dir = models_dir
65
- self.model_name: Optional[str] = None
66
- self.output_type = output_type
67
- self.parsed_output_type: List[Type] = []
68
- self.mutual_exclusive = mutual_exclusive
69
- self.output_name = output_name
70
- self.fit: bool = fit
71
- self.expert: Optional[Expert] = expert
72
- self.update_existing_rules = update_existing_rules
73
- self.viewer = viewer
74
- self.package_name = package_name
75
- self.use_generated_classifier = use_generated_classifier
76
- self.generated_classifier: Optional[Callable] = None
77
- self.ask_now = ask_now
78
- self.fitting_decorator = fitting_decorator if fitting_decorator is not None else \
79
- lambda f: f # Default to no fitting decorator
80
- self.generate_dot_file = generate_dot_file
81
- self.not_none_output_found: bool = False
82
- self.origin_type: Optional[Type] = None
29
+ models_dir: str
30
+ """
31
+ The directory to save the RDR models in.
32
+ """
33
+ output_type: Tuple[Type, ...]
34
+ """
35
+ The type(s) of the output produced by the RDR model (The type(s) of the queried attribute).
36
+ """
37
+ mutual_exclusive: bool
38
+ """
39
+ Whether the queried attribute is mutually exclusive (i.e. allows for only one possible value) or not.
40
+ """
41
+ fit: bool = field(default=True)
42
+ """
43
+ Whether to run in fitting mode and prompt the expert or just classify using existing rules.
44
+ """
45
+ expert: Optional[Expert] = field(default=None)
46
+ """
47
+ The expert instance, this is used by the rdr to prompt for answers.
48
+ """
49
+ update_existing_rules: bool = field(default=True)
50
+ """
51
+ When in fitting mode, whether to ask for answers for existing rules as well or not.
52
+ """
53
+ package_name: Optional[str] = field(default=None)
54
+ """
55
+ The name of the user python package where the RDR model will be saved, this is useful for generating relative
56
+ imports in the generated rdr model files.
57
+ """
58
+ use_generated_classifier: bool = field(default=False)
59
+ """
60
+ Whether to use the generated classifier files of the rdr model instead of the RDR instance itself, this is useful
61
+ when you want to debug inside the rules.
62
+ """
63
+ ask_now: Callable[Dict[str, Any], bool] = field(default=lambda _: True)
64
+ """
65
+ A user provided callable function that outputs a boolean indicating when to ask the expert for an answer. The input
66
+ to the `ask_now` function is a dictionary with the original function arguments, while arguments like `self` and
67
+ `cls` are passed as a special key `self_` or `cls_` respectively.
68
+ """
69
+ fitting_decorator: Optional[Callable] = field(default=lambda f: f)
70
+ """
71
+ A user provided decorator that wraps the `py:meth:ripple_down_rules.rdr.RippleDownRules.fit_case` method which is
72
+ used when in fitting mode, this is useful when you want special logic pre and post the fitting operation, you can
73
+ for example freeze your system during fitting such that you have a stable state that you can query and use while
74
+ writing and testing your answer/rule.
75
+ """
76
+ generate_dot_file: bool = field(default=False)
77
+ """
78
+ Whether to generate a dynamic dot file representing the state of the rule tree each time the rdr is queried, showing
79
+ which rules fired and which rules didn't get evaluated, ...etc.
80
+ """
81
+ model_name: Optional[str] = field(default=None)
82
+ """
83
+ The name of the rdr model, this gets auto generated from the function signature and the class/file it is contained
84
+ in.
85
+ """
86
+ rdr: GeneralRDR = field(init=False)
87
+ """
88
+ The ripple down rules instance of the decorator class.
89
+ """
90
+ parsed_output_type: List[Type] = field(init=False, default_factory=list)
91
+ """
92
+ The output of a post processing done on the output types, for example converting typing module types
93
+ (i.e. type hints) to python types.
94
+ """
95
+ origin_type: Optional[Type] = field(init=False, default=None)
96
+ """
97
+ The origin of the type hint of the attribute, useful in the case of not mutually exclusive attributes to map the
98
+ result to the specified container type (e.g. a list instead of a set which is the default container type for rdr
99
+ output).
100
+ """
101
+ output_name: str = field(init=False, default='output_')
102
+ """
103
+ This is used to refer to the output value of the decorated function, this is used as part of the case as input to
104
+ the rdr model, but is never used in the rule logic to prevent cycles from happening. The correct way to use the
105
+ output of an rdr in the rules logic is in the case when mutually exclusive is False, or through refinements of
106
+ existing rules when mutually exclusive is True, which happens automatically by the rdr prompting for refinements.
107
+ """
108
+ _not_none_output_found: bool = field(init=False, default=False)
109
+ """
110
+ This is a flag that indicates that a not None output for the rdr has been inferred, this is used to update the
111
+ generated dot file if it is set to `True`.
112
+ """
83
113
 
84
114
  def decorator(self, func: Callable) -> Callable:
85
115
 
@@ -102,33 +132,33 @@ class RDRDecorator:
102
132
  if len(self.parsed_output_type) == 0:
103
133
  self.parsed_output_type = self.parse_output_type(func, self.output_type, *args)
104
134
  if self.expert is None:
105
- self.expert = Human(answers_save_path=self.rdr_models_dir + f'/{self.model_name}/expert_answers')
135
+ self.expert = Human(answers_save_path=self.models_dir + f'/{self.model_name}/expert_answers')
106
136
  case_query = self.create_case_query_from_method(func, func_output,
107
137
  self.parsed_output_type,
108
138
  self.mutual_exclusive,
109
139
  case, case_dict,
110
140
  *args, **kwargs)
111
141
  output = self.rdr.fit_case(case_query, expert=self.expert,
112
- update_existing_rules=self.update_existing_rules,
113
- viewer=self.viewer)
142
+ update_existing_rules=self.update_existing_rules)
114
143
  return output
115
-
144
+
116
145
  if self.fit and not self.use_generated_classifier and self.ask_now(case_dict):
117
146
  output = fit()
118
147
  else:
119
148
  if self.use_generated_classifier:
120
149
  if self.generated_classifier is None:
121
- model_path = os.path.join(self.rdr_models_dir, self.model_name)
150
+ model_path = os.path.join(self.models_dir, self.model_name)
122
151
  self.generated_classifier = self.rdr.get_rdr_classifier_from_python_file(model_path)
123
152
  output = self.generated_classifier(case)
124
153
  else:
125
154
  output = self.rdr.classify(case)
126
155
  if self.generate_dot_file:
127
156
  eval_rule_tree = self.rdr.get_evaluated_rule_tree()
128
- if not self.not_none_output_found or (eval_rule_tree and len(eval_rule_tree) > 1):
129
- self.rdr.render_evaluated_rule_tree(self.rdr_models_dir + f'/{self.model_name}', show_full_tree=True)
157
+ if not self._not_none_output_found or (eval_rule_tree and len(eval_rule_tree) > 1):
158
+ self.rdr.render_evaluated_rule_tree(self.models_dir + f'/{self.model_name}',
159
+ show_full_tree=True)
130
160
  if eval_rule_tree and len(eval_rule_tree) > 1:
131
- self.not_none_output_found = True
161
+ self._not_none_output_found = True
132
162
 
133
163
  if self.output_name in output:
134
164
  if self.origin_type == list:
@@ -189,7 +219,6 @@ class RDRDecorator:
189
219
  return Case(dict, id(case_dict), case_name, case_dict, **case_dict), case_dict
190
220
 
191
221
  def initialize_rdr_model_name_and_load(self, func: Callable) -> None:
192
- self.viewer = RDRCaseViewer.instances[0] if RDRCaseViewer is not None and len(RDRCaseViewer.instances) > 0 else self.viewer
193
222
  model_file_name = get_func_rdr_model_name(func, include_file_name=True)
194
223
  self.model_name = str_to_snake_case(model_file_name)
195
224
  self.load()
@@ -209,20 +238,8 @@ class RDRDecorator:
209
238
  parsed_output_type.append(ot)
210
239
  return parsed_output_type
211
240
 
212
- def save(self):
213
- """
214
- Save the RDR model to the specified directory.
215
- """
216
- self.rdr.save(self.rdr_models_dir, self.model_name, package_name=self.package_name)
217
-
218
241
  def load(self):
219
242
  """
220
- Load the RDR model from the specified directory.
221
- """
222
- self.rdr = GeneralRDR(save_dir=self.rdr_models_dir, model_name=self.model_name)
223
-
224
- def update_from_python(self):
225
- """
226
- Update the RDR model from a python file.
243
+ Load the RDR model from the specified directory, otherwise create a new one.
227
244
  """
228
- self.rdr.update_from_python(self.rdr_models_dir, parent_package_name=self.package_name)
245
+ self.rdr = GeneralRDR(save_dir=self.models_dir, model_name=self.model_name)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ripple_down_rules
3
- Version: 0.6.49
3
+ Version: 0.6.50
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,9 +1,9 @@
1
- ripple_down_rules/__init__.py,sha256=GyypMGVzsHCBimVk-W61kWH2zma_ldearwOUdhYFOG4,375
1
+ ripple_down_rules/__init__.py,sha256=6EQLRCLTgXJBkkqQ-9rcFfDRgNFPMVqlW-ofOWDjfAY,375
2
2
  ripple_down_rules/experts.py,sha256=KXwWCmDrCffu9HW3yNewqWc1e5rnPI5Rnc981w_5M7U,17896
3
3
  ripple_down_rules/failures.py,sha256=ZPypPwKjyVcJkY9YG6p538ld8ZzpdW6CrC1RmJi44Ek,42
4
4
  ripple_down_rules/helpers.py,sha256=sVU1Q7HPbMh57X8zDYP6vj1maDoaVAvnXHpUYIbr_As,5715
5
5
  ripple_down_rules/rdr.py,sha256=M7CEjHIegJWiDZtn-B8Qf9CpMvplAkrFX3HiiyGQC7M,84292
6
- ripple_down_rules/rdr_decorators.py,sha256=sbrbo5qfhnY9dIge4sFlkn4p8yPNBWWQ1Nq-NvMDx20,11756
6
+ ripple_down_rules/rdr_decorators.py,sha256=H3ONvgTSgL5LS_Nv_hKAYY2nkqvn7KZ1zmIzIDi07dA,11870
7
7
  ripple_down_rules/rules.py,sha256=L4Ws-x3g5ljE0GrDt4LAib2qtR1C1_Ra4cRcIB-IQDI,28702
8
8
  ripple_down_rules/start-code-server.sh,sha256=otClk7VmDgBOX2TS_cjws6K0UwvgAUJhoA0ugkPCLqQ,949
9
9
  ripple_down_rules/utils.py,sha256=5UL4h-m5Q4l7RyCYqmUmDDxUAkFqPYpFa3QldDYoJUE,80752
@@ -18,8 +18,8 @@ ripple_down_rules/user_interface/ipython_custom_shell.py,sha256=GQf5Je5szkPH17g4
18
18
  ripple_down_rules/user_interface/object_diagram.py,sha256=FEa2HaYR9QmTE6NsOwBvZ0jqmu3DKyg6mig2VE5ZP4Y,4956
19
19
  ripple_down_rules/user_interface/prompt.py,sha256=WPbw_8_-8SpF2ISyRZRuFwPKBEuGC4HaX3lbCPFHhh8,10314
20
20
  ripple_down_rules/user_interface/template_file_creator.py,sha256=uSbosZS15MOR3Nv7M3MrFuoiKXyP4cBId-EK3I6stHM,13660
21
- ripple_down_rules-0.6.49.dist-info/licenses/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
22
- ripple_down_rules-0.6.49.dist-info/METADATA,sha256=-xhdW2zRfqAdIa_deC_DHETUVV5_8PLWVe0YTDN4VXg,48294
23
- ripple_down_rules-0.6.49.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
24
- ripple_down_rules-0.6.49.dist-info/top_level.txt,sha256=VeoLhEhyK46M1OHwoPbCQLI1EifLjChqGzhQ6WEUqeM,18
25
- ripple_down_rules-0.6.49.dist-info/RECORD,,
21
+ ripple_down_rules-0.6.50.dist-info/licenses/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
22
+ ripple_down_rules-0.6.50.dist-info/METADATA,sha256=LVOjG94lFcXLJD80mILahLyK1sYrsHEsDogeKg-PBSU,48294
23
+ ripple_down_rules-0.6.50.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
24
+ ripple_down_rules-0.6.50.dist-info/top_level.txt,sha256=VeoLhEhyK46M1OHwoPbCQLI1EifLjChqGzhQ6WEUqeM,18
25
+ ripple_down_rules-0.6.50.dist-info/RECORD,,