chartflow 0.1__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.
Files changed (79) hide show
  1. chartflow/__init__.py +28 -0
  2. chartflow/_verify_importexport.py +169 -0
  3. chartflow/edit/__init__.py +119 -0
  4. chartflow/edit/code_editor.py +1007 -0
  5. chartflow/edit/code_folder.py +523 -0
  6. chartflow/edit/completer.py +2652 -0
  7. chartflow/edit/context_analyzer.py +521 -0
  8. chartflow/edit/demo.py +469 -0
  9. chartflow/edit/line_number_area.py +548 -0
  10. chartflow/edit/minimap.py +581 -0
  11. chartflow/edit/python_editor.py +509 -0
  12. chartflow/edit/syntax_highlighter.py +291 -0
  13. chartflow/edit/test_editor.py +190 -0
  14. chartflow/flow/__init__.py +31 -0
  15. chartflow/flow/_demo.py +98 -0
  16. chartflow/flow/chart.py +1101 -0
  17. chartflow/flow/editor.py +375 -0
  18. chartflow/flow/flow.py +6 -0
  19. chartflow/flow/qchart.py +1250 -0
  20. chartflow/flow/test/__init__.py +0 -0
  21. chartflow/flow/test/test_chart.py +537 -0
  22. chartflow/flow/test_decorator_sync.py +102 -0
  23. chartflow/fsm/__init__.py +84 -0
  24. chartflow/fsm/decorators.py +395 -0
  25. chartflow/fsm/demo.py +381 -0
  26. chartflow/fsm/demo_pyqt6.py +881 -0
  27. chartflow/fsm/demo_tree.py +46 -0
  28. chartflow/fsm/logic.py +447 -0
  29. chartflow/fsm/meta.py +569 -0
  30. chartflow/fsm/scheduler.py +427 -0
  31. chartflow/fsm/stage.py +2079 -0
  32. chartflow/fsm/stdio.py +66 -0
  33. chartflow/fsm/test/__init__.py +7 -0
  34. chartflow/fsm/test/test_decorators.py +210 -0
  35. chartflow/fsm/test/test_events.py +202 -0
  36. chartflow/fsm/test/test_integration.py +596 -0
  37. chartflow/fsm/test/test_logic.py +371 -0
  38. chartflow/fsm/test/test_meta.py +192 -0
  39. chartflow/fsm/test/test_runtime_param.py +336 -0
  40. chartflow/fsm/test/test_scheduler.py +280 -0
  41. chartflow/fsm/test/test_stage.py +314 -0
  42. chartflow/fsm/test_behavior_events.py +263 -0
  43. chartflow/fsm/test_nested_parallel.py +80 -0
  44. chartflow/fsm/test_stage_spec.py +448 -0
  45. chartflow/fsm/utils.py +34 -0
  46. chartflow/node/__init__.py +66 -0
  47. chartflow/node/_node_base.py +727 -0
  48. chartflow/node/_node_interaction.py +488 -0
  49. chartflow/node/_node_interface.py +426 -0
  50. chartflow/node/_node_markers.py +648 -0
  51. chartflow/node/_node_ports.py +536 -0
  52. chartflow/node/_port_interface.py +111 -0
  53. chartflow/node/arrow_item.py +287 -0
  54. chartflow/node/color_utils.py +113 -0
  55. chartflow/node/connection_item.py +939 -0
  56. chartflow/node/demo.py +258 -0
  57. chartflow/node/demo_arrow_drag.py +46 -0
  58. chartflow/node/demo_decorator.py +63 -0
  59. chartflow/node/demo_ports.py +69 -0
  60. chartflow/node/enums.py +35 -0
  61. chartflow/node/example.py +84 -0
  62. chartflow/node/layout_utils.py +307 -0
  63. chartflow/node/main_window.py +196 -0
  64. chartflow/node/menu_bar.py +240 -0
  65. chartflow/node/node_canvas.py +1730 -0
  66. chartflow/node/node_item.py +754 -0
  67. chartflow/node/popmenu.py +472 -0
  68. chartflow/node/port_item.py +591 -0
  69. chartflow/node/qnode_editor.py +73 -0
  70. chartflow/node/selection_rect.py +53 -0
  71. chartflow/node/style_panel.py +615 -0
  72. chartflow/node/styles.py +63 -0
  73. chartflow/node/test_qnode.py +2336 -0
  74. chartflow/showcase.py +528 -0
  75. chartflow/theme.py +508 -0
  76. chartflow-0.1.dist-info/METADATA +23 -0
  77. chartflow-0.1.dist-info/RECORD +79 -0
  78. chartflow-0.1.dist-info/WHEEL +5 -0
  79. chartflow-0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,371 @@
1
+ """Tests for Logic class"""
2
+
3
+ from chartflow.fsm.meta import StageMachineMeta
4
+
5
+
6
+ import pytest
7
+ from chartflow.fsm.logic import Logic
8
+ from chartflow.fsm.scheduler import Scheduler
9
+ from chartflow.fsm.meta import clear_prototypes
10
+
11
+
12
+ @pytest.fixture(autouse=True)
13
+ def reset_state():
14
+ """Reset state before each test"""
15
+ Scheduler._instance = None
16
+ clear_prototypes()
17
+ Logic._Logic__names__ = {}
18
+ Logic._Logic__types__ = {}
19
+ StageMachineMeta.__ID_PROTOS__ = {}
20
+ yield
21
+ Scheduler._instance = None
22
+
23
+
24
+ class MockKnowledge:
25
+ """Mock knowledge object"""
26
+ pass
27
+
28
+
29
+ class TestLogicInit:
30
+ """Test Logic initialization"""
31
+
32
+ def test_logic_auto_registers(self):
33
+ """Test that Logic auto-registers with Scheduler"""
34
+ scheduler = Scheduler()
35
+
36
+ class TestLogic(Logic):
37
+ NAME = "TestLogic"
38
+
39
+ def my_state(self, inst):
40
+ return None
41
+
42
+ logic = TestLogic()
43
+
44
+ assert logic in scheduler
45
+
46
+ def test_logic_tracks_instances(self):
47
+ """Test that Logic tracks instances by name"""
48
+
49
+ class TestLogic(Logic):
50
+ NAME = "TestLogic"
51
+
52
+ def my_state(self, inst):
53
+ return None
54
+
55
+ logic1 = TestLogic()
56
+ logic2 = TestLogic()
57
+
58
+ instances = Logic._Logic__names__.get("TestLogic", {})
59
+ assert logic1.uid in instances
60
+ assert logic2.uid in instances
61
+
62
+
63
+ class TestLogicLifecycle:
64
+ """Test Logic lifecycle methods"""
65
+
66
+ def test_check_method(self):
67
+ """Test _check method"""
68
+
69
+ class TestLogic(Logic):
70
+ NAME = "TestLogic"
71
+
72
+ def my_state(self, inst):
73
+ return None
74
+
75
+ logic = TestLogic()
76
+
77
+ assert logic._check(False) is True
78
+
79
+ def test_enter_method(self):
80
+ """Test _enter method calls onStart"""
81
+
82
+ class TestLogic(Logic):
83
+ NAME = "TestLogic"
84
+
85
+ def __init__(self):
86
+ self.started = False
87
+ super().__init__()
88
+
89
+ def my_state(self, inst):
90
+ return None
91
+
92
+ def onStart(self, it):
93
+ self.started = True
94
+
95
+ logic = TestLogic()
96
+ logic._enter()
97
+
98
+ assert logic.started
99
+ assert logic._isEntered
100
+
101
+ def test_step_method(self):
102
+ """Test _step method calls handleStage and onStep"""
103
+
104
+ class TestLogic(Logic):
105
+ NAME = "TestLogic"
106
+
107
+ def __init__(self):
108
+ self.stepped = False
109
+ super().__init__()
110
+
111
+ def my_state(self, inst):
112
+ return None
113
+
114
+ def onStep(self, it):
115
+ self.stepped = True
116
+
117
+ logic = TestLogic()
118
+ logic._current = "my_state"
119
+ logic._step()
120
+
121
+ assert logic.stepped
122
+
123
+ def test_exit_method(self):
124
+ """Test _exit method calls onStop"""
125
+
126
+ class TestLogic(Logic):
127
+ NAME = "TestLogic"
128
+
129
+ def __init__(self):
130
+ self.stopped = False
131
+ super().__init__()
132
+
133
+ def my_state(self, inst):
134
+ return None
135
+
136
+ def onStop(self, it):
137
+ self.stopped = True
138
+
139
+ logic = TestLogic()
140
+ logic._exit()
141
+
142
+ assert logic.stopped
143
+
144
+
145
+ class TestLogicProperties:
146
+ """Test Logic properties"""
147
+
148
+ def test_alive_property(self):
149
+ """Test alive property"""
150
+
151
+ class TestLogic(Logic):
152
+ NAME = "TestLogic"
153
+
154
+ def my_state(self, inst):
155
+ return None
156
+
157
+ logic = TestLogic()
158
+
159
+ assert logic.alive is True
160
+
161
+ logic.alive = False
162
+
163
+ assert logic.alive is False
164
+
165
+ def test_enabled_property(self):
166
+ """Test enabled property"""
167
+
168
+ class TestLogic(Logic):
169
+ NAME = "TestLogic"
170
+
171
+ def my_state(self, inst):
172
+ return None
173
+
174
+ logic = TestLogic()
175
+
176
+ assert logic.enabled is True
177
+
178
+ logic.enabled = False
179
+
180
+ assert logic.enabled is False
181
+
182
+ def test_uid_property(self):
183
+ """Test uid property"""
184
+
185
+ class TestLogic(Logic):
186
+ NAME = "TestLogic"
187
+
188
+ def my_state(self, inst):
189
+ return None
190
+
191
+ logic1 = TestLogic()
192
+ logic2 = TestLogic()
193
+
194
+ assert logic1.uid == 0
195
+ assert logic2.uid == 1
196
+
197
+ def test_priority_property(self):
198
+ """Test priority property"""
199
+
200
+ class TestLogic(Logic):
201
+ NAME = "TestLogic"
202
+ PRIORITY = 5
203
+
204
+ def my_state(self, inst):
205
+ return None
206
+
207
+ logic = TestLogic()
208
+
209
+ assert logic.priority == 5
210
+
211
+
212
+ class TestLogicRef:
213
+ """Test Logic reference methods"""
214
+
215
+ def test_ref_by_name(self):
216
+ """Test ref() by base name returns list"""
217
+
218
+ class TestLogic(Logic):
219
+ NAME = "TestLogic"
220
+
221
+ def my_state(self, inst):
222
+ return None
223
+
224
+ logic1 = TestLogic()
225
+ logic2 = TestLogic()
226
+
227
+ refs = logic1.ref("TestLogic")
228
+
229
+ assert isinstance(refs, list)
230
+ assert logic1 in refs
231
+ assert logic2 in refs
232
+
233
+ def test_ref_by_full_name(self):
234
+ """Test ref() by full name returns single instance"""
235
+
236
+ class TestLogic(Logic):
237
+ NAME = "TestLogic"
238
+
239
+ def my_state(self, inst):
240
+ return None
241
+
242
+ logic1 = TestLogic()
243
+ logic2 = TestLogic()
244
+
245
+ ref1 = logic1.ref("TestLogic0")
246
+ ref2 = logic2.ref("TestLogic1")
247
+
248
+ assert ref1 is logic1
249
+ assert ref2 is logic2
250
+
251
+ def test_ref_last(self):
252
+ """Test refLast() returns previous instance"""
253
+
254
+ class TestLogic(Logic):
255
+ NAME = "TestLogic"
256
+
257
+ def my_state(self, inst):
258
+ return None
259
+
260
+ logic1 = TestLogic()
261
+ logic2 = TestLogic()
262
+
263
+ assert logic2.refLast() is logic1
264
+ assert logic1.refLast() is None
265
+
266
+ def test_ref_next(self):
267
+ """Test refNext() returns next instance"""
268
+
269
+ class TestLogic(Logic):
270
+ NAME = "TestLogic"
271
+
272
+ def my_state(self, inst):
273
+ return None
274
+
275
+ logic1 = TestLogic()
276
+ logic2 = TestLogic()
277
+
278
+ assert logic1.refNext() is logic2
279
+ assert logic2.refNext() is None
280
+
281
+ def test_ref_adjacent(self):
282
+ """Test refAdjacent() returns prev and next"""
283
+
284
+ class TestLogic(Logic):
285
+ NAME = "TestLogic"
286
+
287
+ def my_state(self, inst):
288
+ return None
289
+
290
+ logic1 = TestLogic()
291
+ logic2 = TestLogic()
292
+ logic3 = TestLogic()
293
+
294
+ prev, next = logic2.refAdjacent()
295
+
296
+ assert prev is logic1
297
+ assert next is logic3
298
+
299
+ def test_ref_head(self):
300
+ """Test refHead() returns first instance"""
301
+
302
+ class TestLogic(Logic):
303
+ NAME = "TestLogic"
304
+
305
+ def my_state(self, inst):
306
+ return None
307
+
308
+ logic1 = TestLogic()
309
+ logic2 = TestLogic()
310
+
311
+ assert logic1.refHead() is logic1
312
+
313
+ def test_ref_end(self):
314
+ """Test refEnd() returns last instance"""
315
+
316
+ class TestLogic(Logic):
317
+ NAME = "TestLogic"
318
+
319
+ def my_state(self, inst):
320
+ return None
321
+
322
+ logic1 = TestLogic()
323
+ logic2 = TestLogic()
324
+
325
+ assert logic1.refEnd() is logic2
326
+
327
+
328
+ class TestLogicKill:
329
+ """Test Logic kill functionality"""
330
+
331
+ def test_kill_sets_alive_false(self):
332
+ """Test kill() sets alive to False"""
333
+
334
+ class TestLogic(Logic):
335
+ NAME = "TestLogic"
336
+
337
+ def my_state(self, inst):
338
+ return None
339
+
340
+ logic = TestLogic()
341
+
342
+ assert logic.alive is True
343
+
344
+ logic.kill()
345
+
346
+ assert logic.alive is False
347
+
348
+
349
+ class TestLogicControl:
350
+ """Test Logic control methods"""
351
+
352
+ def test_enable_disable(self):
353
+ """Test enable() and disable() methods"""
354
+
355
+ class TestLogic(Logic):
356
+ NAME = "TestLogic"
357
+
358
+ def my_state(self, inst):
359
+ return None
360
+
361
+ logic = TestLogic()
362
+
363
+ assert logic.enabled is True
364
+
365
+ logic.disable()
366
+
367
+ assert logic.enabled is False
368
+
369
+ logic.enable()
370
+
371
+ assert logic.enabled is True
@@ -0,0 +1,192 @@
1
+ """Tests for StageMachineMeta metaclass"""
2
+
3
+ import pytest
4
+ from chartflow.fsm.meta import StageMachineMeta, get_prototype, list_prototypes, clear_prototypes
5
+
6
+
7
+ class TestStateDiscovery:
8
+ """Test automatic state discovery"""
9
+
10
+ def test_basic_state_discovery(self):
11
+ """Test that methods become states"""
12
+ clear_prototypes()
13
+
14
+ class TestStage(metaclass=StageMachineMeta):
15
+ NAME = "TestStage"
16
+
17
+ def state_a(self, inst):
18
+ return "state_b"
19
+
20
+ def state_b(self, inst):
21
+ return "state_a"
22
+
23
+ assert "state_a" in TestStage.__states__
24
+ assert "state_b" in TestStage.__states__
25
+
26
+ def test_private_methods_not_states(self):
27
+ """Test that _private methods are not states"""
28
+ clear_prototypes()
29
+
30
+ class TestStage(metaclass=StageMachineMeta):
31
+ NAME = "TestStage"
32
+
33
+ def public_state(self, inst):
34
+ return None
35
+
36
+ def _private_method(self):
37
+ pass
38
+
39
+ assert "public_state" in TestStage.__states__
40
+ assert "_private_method" not in TestStage.__states__
41
+
42
+ def test_uppercase_methods_not_states(self):
43
+ """Test that UPPERCASE methods are not states"""
44
+ clear_prototypes()
45
+
46
+ class TestStage(metaclass=StageMachineMeta):
47
+ NAME = "TestStage"
48
+
49
+ def normal_state(self, inst):
50
+ return None
51
+
52
+ def CONSTANT(self):
53
+ pass
54
+
55
+ assert "normal_state" in TestStage.__states__
56
+ assert "CONSTANT" not in TestStage.__states__
57
+
58
+ def test_ignored_methods_not_states(self):
59
+ """Test that ignored methods are not states"""
60
+ clear_prototypes()
61
+
62
+ class TestStage(metaclass=StageMachineMeta):
63
+ NAME = "TestStage"
64
+
65
+ def normal_state(self, inst):
66
+ return None
67
+
68
+ def onLoading(self, it, k, *children):
69
+ pass
70
+
71
+ assert "normal_state" in TestStage.__states__
72
+ assert "onLoading" not in TestStage.__states__
73
+
74
+
75
+ class TestClassRegistration:
76
+ """Test automatic class registration"""
77
+
78
+ def test_class_registered(self):
79
+ """Test that classes with NAME are registered"""
80
+ clear_prototypes()
81
+
82
+ class TestStage(metaclass=StageMachineMeta):
83
+ NAME = "TestStage"
84
+
85
+ def state_a(self, inst):
86
+ return None
87
+
88
+ proto = get_prototype("TestStage")
89
+ assert proto is TestStage
90
+
91
+ def test_base_classes_not_registered(self):
92
+ """Test that base classes are not registered"""
93
+ clear_prototypes()
94
+
95
+ class Stage(metaclass=StageMachineMeta):
96
+ NAME = "Stage"
97
+
98
+ class Logic(Stage):
99
+ NAME = "Logic"
100
+
101
+ assert get_prototype("Stage") is None
102
+ assert get_prototype("Logic") is None
103
+
104
+ def test_name_validation(self):
105
+ """Test that NAME ending with digit is rejected"""
106
+ clear_prototypes()
107
+
108
+ with pytest.raises(ValueError):
109
+ class BadStage(metaclass=StageMachineMeta):
110
+ NAME = "BadStage1"
111
+
112
+
113
+ class TestInheritance:
114
+ """Test state and decorator inheritance"""
115
+
116
+ def test_states_inherited(self):
117
+ """Test that states are inherited from parent"""
118
+ clear_prototypes()
119
+
120
+ class BaseStage(metaclass=StageMachineMeta):
121
+ NAME = "BaseStage"
122
+
123
+ def base_state(self, inst):
124
+ return None
125
+
126
+ class DerivedStage(BaseStage):
127
+ NAME = "DerivedStage"
128
+
129
+ def derived_state(self, inst):
130
+ return None
131
+
132
+ assert "base_state" in DerivedStage.__states__
133
+ assert "derived_state" in DerivedStage.__states__
134
+
135
+ def test_recursive_inherited(self):
136
+ """Test that recursive markers are inherited"""
137
+ clear_prototypes()
138
+
139
+ class BaseStage(metaclass=StageMachineMeta):
140
+ NAME = "BaseStage"
141
+
142
+ def base_state(self, inst):
143
+ return None
144
+
145
+ BaseStage.__recursive__ = {"base_state"}
146
+
147
+ class DerivedStage(BaseStage):
148
+ NAME = "DerivedStage"
149
+
150
+ def derived_state(self, inst):
151
+ return None
152
+
153
+ assert "base_state" in DerivedStage.__recursive__
154
+
155
+
156
+ class TestPrototypeFunctions:
157
+ """Test prototype utility functions"""
158
+
159
+ def test_get_prototype(self):
160
+ """Test get_prototype function"""
161
+ clear_prototypes()
162
+
163
+ class TestStage(metaclass=StageMachineMeta):
164
+ NAME = "TestStage"
165
+
166
+ assert get_prototype("TestStage") is TestStage
167
+ assert get_prototype("NonExistent") is None
168
+
169
+ def test_list_prototypes(self):
170
+ """Test list_prototypes function"""
171
+ clear_prototypes()
172
+
173
+ class TestStageAlpha(metaclass=StageMachineMeta):
174
+ NAME = "TestStageAlpha"
175
+
176
+ class TestStageBeta(metaclass=StageMachineMeta):
177
+ NAME = "TestStageBeta"
178
+
179
+ protos = list_prototypes()
180
+ assert "TestStageAlpha" in protos
181
+ assert "TestStageBeta" in protos
182
+
183
+ def test_clear_prototypes(self):
184
+ """Test clear_prototypes function"""
185
+ clear_prototypes()
186
+
187
+ class TestStage(metaclass=StageMachineMeta):
188
+ NAME = "TestStage"
189
+
190
+ assert get_prototype("TestStage") is TestStage
191
+ clear_prototypes()
192
+ assert get_prototype("TestStage") is None