ripple-down-rules 0.6.13__py3-none-any.whl → 0.6.15__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.
- ripple_down_rules/__init__.py +1 -1
- ripple_down_rules/datastructures/callable_expression.py +8 -0
- ripple_down_rules/helpers.py +1 -1
- ripple_down_rules/rdr.py +33 -3
- ripple_down_rules/rdr_decorators.py +6 -6
- ripple_down_rules/rules.py +49 -1
- ripple_down_rules/user_interface/gui.py +3 -0
- ripple_down_rules/utils.py +46 -11
- {ripple_down_rules-0.6.13.dist-info → ripple_down_rules-0.6.15.dist-info}/METADATA +2 -1
- {ripple_down_rules-0.6.13.dist-info → ripple_down_rules-0.6.15.dist-info}/RECORD +13 -13
- {ripple_down_rules-0.6.13.dist-info → ripple_down_rules-0.6.15.dist-info}/WHEEL +0 -0
- {ripple_down_rules-0.6.13.dist-info → ripple_down_rules-0.6.15.dist-info}/licenses/LICENSE +0 -0
- {ripple_down_rules-0.6.13.dist-info → ripple_down_rules-0.6.15.dist-info}/top_level.txt +0 -0
ripple_down_rules/__init__.py
CHANGED
@@ -116,6 +116,10 @@ class CallableExpression(SubclassJSONSerializer):
|
|
116
116
|
if user_input is None:
|
117
117
|
user_input = build_user_input_from_conclusion(conclusion)
|
118
118
|
self.conclusion: Optional[Any] = conclusion
|
119
|
+
if "def " in user_input:
|
120
|
+
self.user_defined_name = user_input.split('(')[0].replace('def ', '')
|
121
|
+
else:
|
122
|
+
self.user_defined_name = user_input
|
119
123
|
self._user_input: str = encapsulate_user_input(user_input, self.get_encapsulating_function())
|
120
124
|
if conclusion_type is not None:
|
121
125
|
if is_iterable(conclusion_type):
|
@@ -214,6 +218,10 @@ class CallableExpression(SubclassJSONSerializer):
|
|
214
218
|
Set the user input.
|
215
219
|
"""
|
216
220
|
if value is not None:
|
221
|
+
if "def " in value:
|
222
|
+
self.user_defined_name = value.split('(')[0].replace('def ', '')
|
223
|
+
else:
|
224
|
+
self.user_defined_name = value
|
217
225
|
self._user_input = encapsulate_user_input(value, self.get_encapsulating_function())
|
218
226
|
self.scope = get_used_scope(self.user_input, self.scope)
|
219
227
|
self.expression_tree = parse_string_to_expression(self.user_input)
|
ripple_down_rules/helpers.py
CHANGED
@@ -55,7 +55,7 @@ def general_rdr_classify(classifiers_dict: Dict[str, Union[ModuleType, RippleDow
|
|
55
55
|
if attribute_name in new_conclusions:
|
56
56
|
temp_case_query = CaseQuery(case_cp, attribute_name, rdr.conclusion_type, rdr.mutually_exclusive)
|
57
57
|
update_case(temp_case_query, new_conclusions)
|
58
|
-
if len(new_conclusions) == 0 or len(classifiers_dict) == 1:
|
58
|
+
if len(new_conclusions) == 0 or len(classifiers_dict) == 1 and list(classifiers_dict.values())[0].mutually_exclusive:
|
59
59
|
break
|
60
60
|
return conclusions
|
61
61
|
|
ripple_down_rules/rdr.py
CHANGED
@@ -36,7 +36,7 @@ except ImportError as e:
|
|
36
36
|
RDRCaseViewer = None
|
37
37
|
from .utils import draw_tree, make_set, SubclassJSONSerializer, make_list, get_type_from_string, \
|
38
38
|
is_conflicting, extract_function_source, extract_imports, get_full_class_name, \
|
39
|
-
is_iterable, str_to_snake_case, get_import_path_from_path, get_imports_from_types
|
39
|
+
is_iterable, str_to_snake_case, get_import_path_from_path, get_imports_from_types, render_tree
|
40
40
|
|
41
41
|
|
42
42
|
class RippleDownRules(SubclassJSONSerializer, ABC):
|
@@ -92,8 +92,38 @@ class RippleDownRules(SubclassJSONSerializer, ABC):
|
|
92
92
|
self.start_rule = start_rule
|
93
93
|
self.fig: Optional[Figure] = None
|
94
94
|
self.viewer: Optional[RDRCaseViewer] = viewer
|
95
|
-
if
|
96
|
-
|
95
|
+
if viewer is None:
|
96
|
+
if len(RDRCaseViewer.instances) > 0:
|
97
|
+
self.viewer = RDRCaseViewer.instances[0]
|
98
|
+
logger.error("No viewer was provided, but there is already an existing viewer. "
|
99
|
+
"Using the existing viewer.")
|
100
|
+
|
101
|
+
@property
|
102
|
+
def viewer(self):
|
103
|
+
return self._viewer
|
104
|
+
|
105
|
+
@viewer.setter
|
106
|
+
def viewer(self, value):
|
107
|
+
self._viewer = value
|
108
|
+
if value is not None:
|
109
|
+
value.set_save_function(self.save)
|
110
|
+
|
111
|
+
def render_evaluated_rule_tree(self, filename: str) -> None:
|
112
|
+
evaluated_rules = self.get_evaluated_rule_tree()
|
113
|
+
if len(evaluated_rules) > 0:
|
114
|
+
render_tree(evaluated_rules[0], use_dot_exporter=True, filename=filename,
|
115
|
+
only_nodes=evaluated_rules)
|
116
|
+
|
117
|
+
def get_evaluated_rule_tree(self) -> List[Rule]:
|
118
|
+
"""
|
119
|
+
Get the evaluated rule tree of the classifier.
|
120
|
+
|
121
|
+
:return: The evaluated rule tree.
|
122
|
+
"""
|
123
|
+
if self.start_rule is None:
|
124
|
+
raise ValueError("The start rule is not set. Please set the start rule before getting the evaluated rule tree.")
|
125
|
+
evaluated_rule_tree = [r for r in [self.start_rule] + list(self.start_rule.descendants) if r.evaluated]
|
126
|
+
return evaluated_rule_tree
|
97
127
|
|
98
128
|
def save(self, save_dir: Optional[str] = None, model_name: Optional[str] = None,
|
99
129
|
package_name: Optional[str] = None) -> str:
|
@@ -33,7 +33,7 @@ class RDRDecorator:
|
|
33
33
|
viewer: Optional[RDRCaseViewer] = None,
|
34
34
|
package_name: Optional[str] = None,
|
35
35
|
use_generated_classifier: bool = False,
|
36
|
-
ask_now: Callable[[
|
36
|
+
ask_now: Callable[Dict[str, Any], bool] = lambda _: True,
|
37
37
|
fitting_decorator: Optional[Callable] = None):
|
38
38
|
"""
|
39
39
|
:param models_dir: The directory to save/load the RDR models.
|
@@ -67,7 +67,8 @@ class RDRDecorator:
|
|
67
67
|
self.use_generated_classifier = use_generated_classifier
|
68
68
|
self.generated_classifier: Optional[Callable] = None
|
69
69
|
self.ask_now = ask_now
|
70
|
-
self.fitting_decorator = fitting_decorator
|
70
|
+
self.fitting_decorator = fitting_decorator if fitting_decorator is not None else \
|
71
|
+
lambda f: f # Default to no fitting decorator
|
71
72
|
self.load()
|
72
73
|
|
73
74
|
def decorator(self, func: Callable) -> Callable:
|
@@ -99,7 +100,7 @@ class RDRDecorator:
|
|
99
100
|
viewer=self.viewer)
|
100
101
|
return output
|
101
102
|
|
102
|
-
if self.fit and not self.use_generated_classifier and self.ask_now(case_dict
|
103
|
+
if self.fit and not self.use_generated_classifier and self.ask_now(case_dict):
|
103
104
|
output = fit()
|
104
105
|
else:
|
105
106
|
if self.use_generated_classifier:
|
@@ -167,6 +168,7 @@ class RDRDecorator:
|
|
167
168
|
return Case(dict, id(case_dict), case_name, case_dict, **case_dict), case_dict
|
168
169
|
|
169
170
|
def initialize_rdr_model_name_and_load(self, func: Callable) -> None:
|
171
|
+
self.viewer = RDRCaseViewer.instances[0] if len(RDRCaseViewer.instances) > 0 else self.viewer
|
170
172
|
model_file_name = get_func_rdr_model_name(func, include_file_name=True)
|
171
173
|
self.model_name = str_to_snake_case(model_file_name)
|
172
174
|
self.load()
|
@@ -201,10 +203,8 @@ class RDRDecorator:
|
|
201
203
|
model_path = os.path.join(self.rdr_models_dir, self.model_name + f"/rdr_metadata/{self.model_name}.json")
|
202
204
|
if os.path.exists(os.path.join(self.rdr_models_dir, model_path)):
|
203
205
|
self.rdr = GeneralRDR.load(self.rdr_models_dir, self.model_name, package_name=self.package_name)
|
204
|
-
self.rdr.set_viewer(self.viewer)
|
205
206
|
if self.rdr is None:
|
206
|
-
self.rdr = GeneralRDR(save_dir=self.rdr_models_dir, model_name=self.model_name
|
207
|
-
viewer=self.viewer)
|
207
|
+
self.rdr = GeneralRDR(save_dir=self.rdr_models_dir, model_name=self.model_name)
|
208
208
|
|
209
209
|
def update_from_python(self):
|
210
210
|
"""
|
ripple_down_rules/rules.py
CHANGED
@@ -57,6 +57,21 @@ class Rule(NodeMixin, SubclassJSONSerializer, ABC):
|
|
57
57
|
self._name: Optional[str] = None
|
58
58
|
# generate a unique id for the rule using uuid4
|
59
59
|
self.uid: str = uid if uid else str(uuid4().int)
|
60
|
+
self.evaluated: bool = False
|
61
|
+
self._user_defined_name: Optional[str] = None
|
62
|
+
|
63
|
+
@property
|
64
|
+
def user_defined_name(self) -> Optional[str]:
|
65
|
+
"""
|
66
|
+
Get the user defined name of the rule, if it exists.
|
67
|
+
"""
|
68
|
+
if self._user_defined_name is None:
|
69
|
+
if self.conditions and self.conditions.user_input and "def " in self.conditions.user_input:
|
70
|
+
# If the conditions have a user input, use it as the name
|
71
|
+
self._user_defined_name = self.conditions.user_input.split('(')[0].replace('def ', '').strip()
|
72
|
+
else:
|
73
|
+
self._user_defined_name = f"Rule_{self.uid}"
|
74
|
+
return self._user_defined_name
|
60
75
|
|
61
76
|
@classmethod
|
62
77
|
def from_case_query(cls, case_query: CaseQuery) -> Rule:
|
@@ -91,6 +106,10 @@ class Rule(NodeMixin, SubclassJSONSerializer, ABC):
|
|
91
106
|
:param x: The case to evaluate the rule on.
|
92
107
|
:return: The rule that fired or the last evaluated rule if no rule fired.
|
93
108
|
"""
|
109
|
+
if self.root is self:
|
110
|
+
for descendant in self.descendants:
|
111
|
+
descendant.evaluated = False
|
112
|
+
self.evaluated = True
|
94
113
|
if not self.conditions:
|
95
114
|
raise ValueError("Rule has no conditions")
|
96
115
|
self.fired = self.conditions(x)
|
@@ -261,11 +280,40 @@ class Rule(NodeMixin, SubclassJSONSerializer, ABC):
|
|
261
280
|
"""
|
262
281
|
self._name = new_name
|
263
282
|
|
283
|
+
@property
|
284
|
+
def semantic_condition_name(self) -> Optional[str]:
|
285
|
+
"""
|
286
|
+
Get the name of the conditions of the rule, which is the user input of the conditions.
|
287
|
+
"""
|
288
|
+
return self.expression_name(self.conditions)
|
289
|
+
|
290
|
+
@property
|
291
|
+
def semantic_conclusion_name(self) -> Optional[str]:
|
292
|
+
"""
|
293
|
+
Get the name of the conclusion of the rule, which is the user input of the conclusion.
|
294
|
+
"""
|
295
|
+
return self.expression_name(self.conclusion)
|
296
|
+
|
297
|
+
@staticmethod
|
298
|
+
def expression_name(expression: CallableExpression) -> str:
|
299
|
+
"""
|
300
|
+
Get the name of the expression, which is the user input of the expression if it exists,
|
301
|
+
otherwise it is the conclusion or conditions of the rule.
|
302
|
+
"""
|
303
|
+
if expression.user_defined_name is not None:
|
304
|
+
return expression.user_defined_name.strip()
|
305
|
+
elif expression.user_input and "def " in expression.user_input:
|
306
|
+
return expression.user_input.split('(')[0].replace('def ', '').strip()
|
307
|
+
elif expression.user_input:
|
308
|
+
return expression.user_input.strip()
|
309
|
+
else:
|
310
|
+
return str(expression)
|
311
|
+
|
264
312
|
def __str__(self, sep="\n"):
|
265
313
|
"""
|
266
314
|
Get the string representation of the rule, which is the conditions and the conclusion.
|
267
315
|
"""
|
268
|
-
return f"{self.
|
316
|
+
return f"{self.semantic_condition_name}{sep}=> {self.semantic_conclusion_name}"
|
269
317
|
|
270
318
|
def __repr__(self):
|
271
319
|
return self.__str__()
|
@@ -282,11 +282,14 @@ class RDRCaseViewer(QMainWindow):
|
|
282
282
|
user_input: Optional[str] = None
|
283
283
|
attributes_widget: Optional[QWidget] = None
|
284
284
|
save_function: Optional[Callable[str, str], None] = None
|
285
|
+
instances: List[RDRCaseViewer] = []
|
285
286
|
|
286
287
|
def __init__(self, parent=None,
|
287
288
|
save_dir: Optional[str] = None,
|
288
289
|
save_model_name: Optional[str] = None):
|
289
290
|
super().__init__(parent)
|
291
|
+
self.instances.clear()
|
292
|
+
self.instances.append(self)
|
290
293
|
self.save_dir = save_dir
|
291
294
|
self.save_model_name = save_model_name
|
292
295
|
|
ripple_down_rules/utils.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
import ast
|
4
3
|
import builtins
|
5
4
|
import copyreg
|
6
5
|
import importlib
|
@@ -22,10 +21,10 @@ from types import NoneType
|
|
22
21
|
|
23
22
|
from sqlalchemy.exc import NoInspectionAvailable
|
24
23
|
|
25
|
-
|
26
24
|
try:
|
27
25
|
import matplotlib
|
28
26
|
from matplotlib import pyplot as plt
|
27
|
+
|
29
28
|
Figure = plt.Figure
|
30
29
|
except ImportError as e:
|
31
30
|
matplotlib = None
|
@@ -40,13 +39,13 @@ except ImportError as e:
|
|
40
39
|
logging.debug(f"{e}: networkx is not installed")
|
41
40
|
|
42
41
|
import requests
|
43
|
-
from anytree import Node, RenderTree
|
42
|
+
from anytree import Node, RenderTree, PreOrderIter
|
44
43
|
from anytree.exporter import DotExporter
|
45
44
|
from sqlalchemy import MetaData, inspect
|
46
45
|
from sqlalchemy.orm import Mapped, registry, class_mapper, DeclarativeBase as SQLTable, Session
|
47
46
|
from tabulate import tabulate
|
48
47
|
from typing_extensions import Callable, Set, Any, Type, Dict, TYPE_CHECKING, get_type_hints, \
|
49
|
-
get_origin, get_args, Tuple, Optional, List, Union, Self, ForwardRef,
|
48
|
+
get_origin, get_args, Tuple, Optional, List, Union, Self, ForwardRef, Iterable
|
50
49
|
|
51
50
|
if TYPE_CHECKING:
|
52
51
|
from .datastructures.case import Case
|
@@ -127,7 +126,7 @@ def extract_imports(file_path: Optional[str] = None, tree: Optional[ast.AST] = N
|
|
127
126
|
name = alias.name
|
128
127
|
asname = alias.asname or name
|
129
128
|
try:
|
130
|
-
if package_name is not None and node.level > 0:
|
129
|
+
if package_name is not None and node.level > 0: # Handle relative imports
|
131
130
|
module_rel_path = Path(os.path.join(file_path, *['..'] * node.level, module_name)).resolve()
|
132
131
|
idx = str(module_rel_path).rfind(package_name)
|
133
132
|
if idx != -1:
|
@@ -681,6 +680,7 @@ def is_builtin_type(tp):
|
|
681
680
|
def is_typing_type(tp):
|
682
681
|
return tp.__module__ == "typing"
|
683
682
|
|
683
|
+
|
684
684
|
origin_type_to_hint = {
|
685
685
|
list: List,
|
686
686
|
set: Set,
|
@@ -688,6 +688,7 @@ origin_type_to_hint = {
|
|
688
688
|
tuple: Tuple,
|
689
689
|
}
|
690
690
|
|
691
|
+
|
691
692
|
def extract_types(tp, seen: Set = None) -> Set[type]:
|
692
693
|
"""Recursively extract all base types from a type hint."""
|
693
694
|
if seen is None:
|
@@ -1400,7 +1401,8 @@ def table_rows_as_str(row_dicts: List[Dict[str, Any]], columns_per_row: int = 20
|
|
1400
1401
|
keys_values = [list(r[0]) + list(r[1]) if len(r) > 1 else r[0] for r in keys_values]
|
1401
1402
|
all_table_rows = []
|
1402
1403
|
row_values = [list(map(lambda v: str(v) if v is not None else "", row)) for row in keys_values]
|
1403
|
-
row_values = [list(map(lambda v: v[:max_line_sze] + '...' if len(v) > max_line_sze else v, row)) for row in
|
1404
|
+
row_values = [list(map(lambda v: v[:max_line_sze] + '...' if len(v) > max_line_sze else v, row)) for row in
|
1405
|
+
row_values]
|
1404
1406
|
row_values = [list(map(lambda v: v.lower() if v in ["True", "False"] else v, row)) for row in row_values]
|
1405
1407
|
table = tabulate(row_values, tablefmt='simple_grid', maxcolwidths=[max_line_sze] * 2)
|
1406
1408
|
all_table_rows.append(table)
|
@@ -1627,14 +1629,46 @@ def edge_attr_setter(parent, child):
|
|
1627
1629
|
return ""
|
1628
1630
|
|
1629
1631
|
|
1632
|
+
class FilteredDotExporter(DotExporter):
|
1633
|
+
def __init__(self, root, include_nodes=None, **kwargs):
|
1634
|
+
self.include_nodes = include_nodes
|
1635
|
+
node_name_func = get_unique_node_names_func(root)
|
1636
|
+
self.include_node_names = [node_name_func(n) for n in self.include_nodes] if include_nodes else None
|
1637
|
+
super().__init__(root, **kwargs)
|
1638
|
+
|
1639
|
+
def __iter_nodes(self, indent, nodenamefunc, nodeattrfunc):
|
1640
|
+
for node in PreOrderIter(self.node, maxlevel=self.maxlevel):
|
1641
|
+
nodename = nodenamefunc(node)
|
1642
|
+
if self.include_nodes is not None and nodename not in self.include_node_names:
|
1643
|
+
continue
|
1644
|
+
nodeattr = nodeattrfunc(node)
|
1645
|
+
nodeattr = " [%s]" % nodeattr if nodeattr is not None else ""
|
1646
|
+
yield '%s"%s"%s;' % (indent, DotExporter.esc(nodename), nodeattr)
|
1647
|
+
|
1648
|
+
def __iter_edges(self, indent, nodenamefunc, edgeattrfunc, edgetypefunc):
|
1649
|
+
maxlevel = self.maxlevel - 1 if self.maxlevel else None
|
1650
|
+
for node in PreOrderIter(self.node, maxlevel=maxlevel):
|
1651
|
+
nodename = nodenamefunc(node)
|
1652
|
+
if self.include_nodes is not None and nodename not in self.include_node_names:
|
1653
|
+
continue
|
1654
|
+
for child in node.children:
|
1655
|
+
childname = nodenamefunc(child)
|
1656
|
+
edgeattr = edgeattrfunc(node, child)
|
1657
|
+
edgetype = edgetypefunc(node, child)
|
1658
|
+
edgeattr = " [%s]" % edgeattr if edgeattr is not None else ""
|
1659
|
+
yield '%s"%s" %s "%s"%s;' % (indent, DotExporter.esc(nodename), edgetype,
|
1660
|
+
DotExporter.esc(childname), edgeattr)
|
1661
|
+
|
1662
|
+
|
1630
1663
|
def render_tree(root: Node, use_dot_exporter: bool = False,
|
1631
|
-
filename: str = "scrdr"):
|
1664
|
+
filename: str = "scrdr", only_nodes: List[Node] = None):
|
1632
1665
|
"""
|
1633
1666
|
Render the tree using the console and optionally export it to a dot file.
|
1634
1667
|
|
1635
1668
|
:param root: The root node of the tree.
|
1636
1669
|
:param use_dot_exporter: Whether to export the tree to a dot file.
|
1637
1670
|
:param filename: The name of the file to export the tree to.
|
1671
|
+
:param only_nodes: A list of nodes to include in the dot export.
|
1638
1672
|
"""
|
1639
1673
|
if not root:
|
1640
1674
|
logging.warning("No rules to render")
|
@@ -1644,10 +1678,11 @@ def render_tree(root: Node, use_dot_exporter: bool = False,
|
|
1644
1678
|
if use_dot_exporter:
|
1645
1679
|
unique_node_names = get_unique_node_names_func(root)
|
1646
1680
|
|
1647
|
-
de =
|
1648
|
-
|
1649
|
-
|
1650
|
-
|
1681
|
+
de = FilteredDotExporter(root,
|
1682
|
+
include_nodes=only_nodes,
|
1683
|
+
nodenamefunc=unique_node_names,
|
1684
|
+
edgeattrfunc=edge_attr_setter
|
1685
|
+
)
|
1651
1686
|
de.to_dotfile(f"{filename}{'.dot'}")
|
1652
1687
|
de.to_picture(f"{filename}{'.png'}")
|
1653
1688
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: ripple_down_rules
|
3
|
-
Version: 0.6.
|
3
|
+
Version: 0.6.15
|
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
|
@@ -677,6 +677,7 @@ License: GNU GENERAL PUBLIC LICENSE
|
|
677
677
|
the library. If this is what you want to do, use the GNU Lesser General
|
678
678
|
Public License instead of this License. But first, please read
|
679
679
|
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
680
|
+
Project-URL: Homepage, https://github.com/AbdelrhmanBassiouny/ripple_down_rules
|
680
681
|
Keywords: robotics,knowledge,reasoning,representation
|
681
682
|
Classifier: Programming Language :: Python :: 3
|
682
683
|
Description-Content-Type: text/markdown
|
@@ -1,24 +1,24 @@
|
|
1
|
-
ripple_down_rules/__init__.py,sha256=
|
1
|
+
ripple_down_rules/__init__.py,sha256=lX1XrdZ7llw3tZk6e_RhQszLZMG9KS8H7GA6V7n4DlI,100
|
2
2
|
ripple_down_rules/experts.py,sha256=4-dMIVeMzFXCLYl_XBG_P7_Xs4sZih9-vZxCIPri6dA,12958
|
3
|
-
ripple_down_rules/helpers.py,sha256=
|
4
|
-
ripple_down_rules/rdr.py,sha256=
|
5
|
-
ripple_down_rules/rdr_decorators.py,sha256=
|
6
|
-
ripple_down_rules/rules.py,sha256=
|
3
|
+
ripple_down_rules/helpers.py,sha256=RUdfiSWMZjGwCxuCy44TcEJf2UNAFlPJusgHzuAs6qI,4583
|
4
|
+
ripple_down_rules/rdr.py,sha256=mtY_kBOjUXRZYz4QFaKN6C2O7liFpbMdnjUnFFnXbdU,57550
|
5
|
+
ripple_down_rules/rdr_decorators.py,sha256=j_bIv30nonWz-D5em7dYnV3htlklar2QwFRa5o48XYM,10707
|
6
|
+
ripple_down_rules/rules.py,sha256=ULIHRbVfKd2L3zzPnssmaeqInWHifPvwwa16MyzakPc,23548
|
7
7
|
ripple_down_rules/start-code-server.sh,sha256=otClk7VmDgBOX2TS_cjws6K0UwvgAUJhoA0ugkPCLqQ,949
|
8
|
-
ripple_down_rules/utils.py,sha256=
|
8
|
+
ripple_down_rules/utils.py,sha256=uOa_b3FoDnoTLp84RnMbeSyiTMgREtUOkOImQj8B5HQ,64204
|
9
9
|
ripple_down_rules/datastructures/__init__.py,sha256=V2aNgf5C96Y5-IGghra3n9uiefpoIm_QdT7cc_C8cxQ,111
|
10
|
-
ripple_down_rules/datastructures/callable_expression.py,sha256=
|
10
|
+
ripple_down_rules/datastructures/callable_expression.py,sha256=ysK-4JmZ4oSUTJC7zpo_o77g4ONxPDEcIpSWggsnx3c,13320
|
11
11
|
ripple_down_rules/datastructures/case.py,sha256=PJ7_-AdxYic6BO5z816piFODj6nU5J6Jt1YzTFH-dds,15510
|
12
12
|
ripple_down_rules/datastructures/dataclasses.py,sha256=D-nrVEW_27njmDGkyiHRnq5lmqEdO8RHKnLb1mdnwrA,11054
|
13
13
|
ripple_down_rules/datastructures/enums.py,sha256=ce7tqS0otfSTNAOwsnXlhsvIn4iW_Y_N3TNebF3YoZs,5700
|
14
14
|
ripple_down_rules/user_interface/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
15
|
-
ripple_down_rules/user_interface/gui.py,sha256=
|
15
|
+
ripple_down_rules/user_interface/gui.py,sha256=druufu9cVeVUajPW-RqGW3ZiEbgdgNBQD2CLhadQo18,27486
|
16
16
|
ripple_down_rules/user_interface/ipython_custom_shell.py,sha256=yp-F8YRWGhj1PLB33HE6vJkdYWFN5Zn2244S2DUWRTM,6576
|
17
17
|
ripple_down_rules/user_interface/object_diagram.py,sha256=FEa2HaYR9QmTE6NsOwBvZ0jqmu3DKyg6mig2VE5ZP4Y,4956
|
18
18
|
ripple_down_rules/user_interface/prompt.py,sha256=JceEUGYsd0lIvd-v2y3D3swoo96_C0lxfp3CxM7Vfts,8900
|
19
19
|
ripple_down_rules/user_interface/template_file_creator.py,sha256=kwBbFLyN6Yx2NTIHPSwOoytWgbJDYhgrUOVFw_jkDQ4,13522
|
20
|
-
ripple_down_rules-0.6.
|
21
|
-
ripple_down_rules-0.6.
|
22
|
-
ripple_down_rules-0.6.
|
23
|
-
ripple_down_rules-0.6.
|
24
|
-
ripple_down_rules-0.6.
|
20
|
+
ripple_down_rules-0.6.15.dist-info/licenses/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
|
21
|
+
ripple_down_rules-0.6.15.dist-info/METADATA,sha256=2x_pdEry-CrkjQ1lgQXmfGhjdK5HScMZOSf16a_yUfE,48294
|
22
|
+
ripple_down_rules-0.6.15.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
23
|
+
ripple_down_rules-0.6.15.dist-info/top_level.txt,sha256=VeoLhEhyK46M1OHwoPbCQLI1EifLjChqGzhQ6WEUqeM,18
|
24
|
+
ripple_down_rules-0.6.15.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|