pyreact-framework 1.0.0__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 (41) hide show
  1. pyreact/__init__.py +144 -0
  2. pyreact/cli/__init__.py +3 -0
  3. pyreact/cli/main.py +512 -0
  4. pyreact/core/__init__.py +80 -0
  5. pyreact/core/component.py +372 -0
  6. pyreact/core/context.py +173 -0
  7. pyreact/core/element.py +208 -0
  8. pyreact/core/error_boundary.py +145 -0
  9. pyreact/core/hooks.py +550 -0
  10. pyreact/core/memo.py +221 -0
  11. pyreact/core/portal.py +159 -0
  12. pyreact/core/reconciler.py +399 -0
  13. pyreact/core/refs.py +213 -0
  14. pyreact/core/renderer.py +112 -0
  15. pyreact/core/scheduler.py +304 -0
  16. pyreact/devtools/__init__.py +18 -0
  17. pyreact/devtools/debugger.py +314 -0
  18. pyreact/devtools/profiler.py +288 -0
  19. pyreact/dom/__init__.py +64 -0
  20. pyreact/dom/attributes.py +317 -0
  21. pyreact/dom/dom_operations.py +333 -0
  22. pyreact/dom/events.py +349 -0
  23. pyreact/server/__init__.py +34 -0
  24. pyreact/server/hydration.py +216 -0
  25. pyreact/server/ssr.py +344 -0
  26. pyreact/styles/__init__.py +19 -0
  27. pyreact/styles/css_module.py +231 -0
  28. pyreact/styles/styled.py +303 -0
  29. pyreact/testing/__init__.py +71 -0
  30. pyreact/testing/fire_event.py +355 -0
  31. pyreact/testing/screen.py +267 -0
  32. pyreact/testing/test_renderer.py +232 -0
  33. pyreact/utils/__init__.py +17 -0
  34. pyreact/utils/diff.py +182 -0
  35. pyreact/utils/object_pool.py +216 -0
  36. pyreact_framework-1.0.0.dist-info/METADATA +363 -0
  37. pyreact_framework-1.0.0.dist-info/RECORD +41 -0
  38. pyreact_framework-1.0.0.dist-info/WHEEL +5 -0
  39. pyreact_framework-1.0.0.dist-info/entry_points.txt +2 -0
  40. pyreact_framework-1.0.0.dist-info/licenses/LICENSE +21 -0
  41. pyreact_framework-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,314 @@
1
+ """
2
+ Debugger Module
3
+ ===============
4
+
5
+ Debugging tools for PyReact components.
6
+ """
7
+
8
+ from typing import Any, Dict, List, Optional
9
+ import time
10
+
11
+
12
+ class Debugger:
13
+ """
14
+ Debugger for PyReact components
15
+
16
+ Provides tools for inspecting component trees,
17
+ state, props, and lifecycle events.
18
+
19
+ Example:
20
+ debugger = Debugger()
21
+ debugger.enable()
22
+
23
+ # Inspect component
24
+ debugger.inspect_component(component)
25
+
26
+ # Get component tree
27
+ tree = debugger.get_tree()
28
+ """
29
+
30
+ def __init__(self):
31
+ self._enabled: bool = False
32
+ self._components: Dict[int, Dict] = {}
33
+ self._logs: List[Dict] = []
34
+ self._breakpoints: Dict[str, List[str]] = {}
35
+ self._watch_list: List[str] = []
36
+
37
+ def enable(self) -> None:
38
+ """Enable debugging"""
39
+ self._enabled = True
40
+
41
+ def disable(self) -> None:
42
+ """Disable debugging"""
43
+ self._enabled = False
44
+
45
+ def is_enabled(self) -> bool:
46
+ """Check if debugging is enabled"""
47
+ return self._enabled
48
+
49
+ def log(self, message: str, data: Optional[Dict] = None) -> None:
50
+ """
51
+ Log a debug message
52
+
53
+ Args:
54
+ message: Log message
55
+ data: Optional data
56
+ """
57
+ if not self._enabled:
58
+ return
59
+
60
+ entry = {
61
+ 'timestamp': time.time(),
62
+ 'message': message,
63
+ 'data': data or {}
64
+ }
65
+ self._logs.append(entry)
66
+
67
+ # Print to console
68
+ print(f"[PyReact Debug] {message}")
69
+ if data:
70
+ print(f" Data: {data}")
71
+
72
+ def register_component(self, component: Any) -> None:
73
+ """
74
+ Register a component for debugging
75
+
76
+ Args:
77
+ component: Component instance
78
+ """
79
+ if not self._enabled:
80
+ return
81
+
82
+ component_id = id(component)
83
+ self._components[component_id] = {
84
+ 'component': component,
85
+ 'name': component.__class__.__name__,
86
+ 'props': {},
87
+ 'state': {},
88
+ 'lifecycle_events': [],
89
+ 'render_count': 0,
90
+ 'created_at': time.time()
91
+ }
92
+
93
+ self.log(f"Component registered: {component.__class__.__name__}")
94
+
95
+ def unregister_component(self, component: Any) -> None:
96
+ """
97
+ Unregister a component
98
+
99
+ Args:
100
+ component: Component instance
101
+ """
102
+ component_id = id(component)
103
+ if component_id in self._components:
104
+ del self._components[component_id]
105
+ self.log(f"Component unregistered: {component.__class__.__name__}")
106
+
107
+ def update_component_state(
108
+ self,
109
+ component: Any,
110
+ state: Dict[str, Any]
111
+ ) -> None:
112
+ """
113
+ Update component state in debug view
114
+
115
+ Args:
116
+ component: Component instance
117
+ state: Current state
118
+ """
119
+ if not self._enabled:
120
+ return
121
+
122
+ component_id = id(component)
123
+ if component_id in self._components:
124
+ self._components[component_id]['state'] = state.copy()
125
+
126
+ def update_component_props(
127
+ self,
128
+ component: Any,
129
+ props: Dict[str, Any]
130
+ ) -> None:
131
+ """
132
+ Update component props in debug view
133
+
134
+ Args:
135
+ component: Component instance
136
+ props: Current props
137
+ """
138
+ if not self._enabled:
139
+ return
140
+
141
+ component_id = id(component)
142
+ if component_id in self._components:
143
+ self._components[component_id]['props'] = props.copy()
144
+
145
+ def log_lifecycle_event(
146
+ self,
147
+ component: Any,
148
+ event: str,
149
+ data: Optional[Dict] = None
150
+ ) -> None:
151
+ """
152
+ Log a lifecycle event
153
+
154
+ Args:
155
+ component: Component instance
156
+ event: Event name
157
+ data: Optional data
158
+ """
159
+ if not self._enabled:
160
+ return
161
+
162
+ component_id = id(component)
163
+ if component_id in self._components:
164
+ self._components[component_id]['lifecycle_events'].append({
165
+ 'event': event,
166
+ 'timestamp': time.time(),
167
+ 'data': data
168
+ })
169
+
170
+ # Increment render count
171
+ if event == 'render':
172
+ self._components[component_id]['render_count'] += 1
173
+
174
+ self.log(f"Lifecycle: {component.__class__.__name__}.{event}", data)
175
+
176
+ def get_component_info(self, component: Any) -> Optional[Dict]:
177
+ """
178
+ Get debug info for a component
179
+
180
+ Args:
181
+ component: Component instance
182
+
183
+ Returns:
184
+ dict or None: Component debug info
185
+ """
186
+ component_id = id(component)
187
+ return self._components.get(component_id)
188
+
189
+ def get_tree(self) -> Dict:
190
+ """
191
+ Get the component tree
192
+
193
+ Returns:
194
+ dict: Component tree structure
195
+ """
196
+ tree = {'name': 'root', 'children': []}
197
+
198
+ for info in self._components.values():
199
+ tree['children'].append({
200
+ 'name': info['name'],
201
+ 'props': info['props'],
202
+ 'state': info['state'],
203
+ 'render_count': info['render_count']
204
+ })
205
+
206
+ return tree
207
+
208
+ def get_logs(self, limit: int = 100) -> List[Dict]:
209
+ """
210
+ Get debug logs
211
+
212
+ Args:
213
+ limit: Maximum number of logs
214
+
215
+ Returns:
216
+ list: Recent logs
217
+ """
218
+ return self._logs[-limit:]
219
+
220
+ def clear_logs(self) -> None:
221
+ """Clear debug logs"""
222
+ self._logs.clear()
223
+
224
+ def add_breakpoint(self, component_name: str, event: str) -> None:
225
+ """
226
+ Add a breakpoint
227
+
228
+ Args:
229
+ component_name: Component class name
230
+ event: Event to break on
231
+ """
232
+ if component_name not in self._breakpoints:
233
+ self._breakpoints[component_name] = []
234
+ self._breakpoints[component_name].append(event)
235
+
236
+ def remove_breakpoint(self, component_name: str, event: str) -> None:
237
+ """
238
+ Remove a breakpoint
239
+
240
+ Args:
241
+ component_name: Component class name
242
+ event: Event
243
+ """
244
+ if component_name in self._breakpoints:
245
+ if event in self._breakpoints[component_name]:
246
+ self._breakpoints[component_name].remove(event)
247
+
248
+ def should_break(self, component_name: str, event: str) -> bool:
249
+ """
250
+ Check if should break
251
+
252
+ Args:
253
+ component_name: Component class name
254
+ event: Event
255
+
256
+ Returns:
257
+ bool: True if should break
258
+ """
259
+ return (
260
+ component_name in self._breakpoints and
261
+ event in self._breakpoints[component_name]
262
+ )
263
+
264
+ def watch(self, expression: str) -> None:
265
+ """
266
+ Add expression to watch list
267
+
268
+ Args:
269
+ expression: Expression to watch
270
+ """
271
+ if expression not in self._watch_list:
272
+ self._watch_list.append(expression)
273
+
274
+ def unwatch(self, expression: str) -> None:
275
+ """
276
+ Remove expression from watch list
277
+
278
+ Args:
279
+ expression: Expression to remove
280
+ """
281
+ if expression in self._watch_list:
282
+ self._watch_list.remove(expression)
283
+
284
+
285
+ # Global debugger instance
286
+ _debugger = Debugger()
287
+
288
+
289
+ def get_debugger() -> Debugger:
290
+ """Get the global debugger"""
291
+ return _debugger
292
+
293
+
294
+ def debug_component(component: Any) -> Dict:
295
+ """
296
+ Get debug info for a component
297
+
298
+ Args:
299
+ component: Component instance
300
+
301
+ Returns:
302
+ dict: Component debug info
303
+ """
304
+ return _debugger.get_component_info(component) or {}
305
+
306
+
307
+ def get_component_tree() -> Dict:
308
+ """
309
+ Get the component tree
310
+
311
+ Returns:
312
+ dict: Component tree
313
+ """
314
+ return _debugger.get_tree()
@@ -0,0 +1,288 @@
1
+ """
2
+ Profiler Module
3
+ ===============
4
+
5
+ Performance profiling for PyReact components.
6
+ """
7
+
8
+ from typing import Any, Dict, List, Optional
9
+ import time
10
+
11
+
12
+ class Profiler:
13
+ """
14
+ Profiler for measuring component performance
15
+
16
+ Tracks render times, commit times, and identifies
17
+ performance bottlenecks.
18
+
19
+ Example:
20
+ profiler = Profiler()
21
+ profiler.start()
22
+
23
+ # ... render components ...
24
+
25
+ profiler.stop()
26
+ report = profiler.get_report()
27
+ """
28
+
29
+ def __init__(self):
30
+ self._enabled: bool = False
31
+ self._current_phase: Optional[str] = None
32
+ self._measurements: List[Dict] = []
33
+ self._component_times: Dict[str, List[float]] = {}
34
+ self._render_times: List[float] = []
35
+ self._commit_times: List[float] = []
36
+ self._start_time: float = 0
37
+ self._phase_start: float = 0
38
+
39
+ def start(self) -> None:
40
+ """Start profiling"""
41
+ self._enabled = True
42
+ self._start_time = time.time()
43
+ self.log("Profiler started")
44
+
45
+ def stop(self) -> None:
46
+ """Stop profiling"""
47
+ self._enabled = False
48
+ self.log("Profiler stopped")
49
+
50
+ def is_enabled(self) -> bool:
51
+ """Check if profiling is enabled"""
52
+ return self._enabled
53
+
54
+ def begin_render(self, component_name: str) -> None:
55
+ """
56
+ Begin measuring render phase
57
+
58
+ Args:
59
+ component_name: Name of component being rendered
60
+ """
61
+ if not self._enabled:
62
+ return
63
+
64
+ self._current_phase = 'render'
65
+ self._phase_start = time.time()
66
+
67
+ self._measurements.append({
68
+ 'type': 'render_begin',
69
+ 'component': component_name,
70
+ 'timestamp': self._phase_start
71
+ })
72
+
73
+ def end_render(self, component_name: str) -> float:
74
+ """
75
+ End measuring render phase
76
+
77
+ Args:
78
+ component_name: Name of component
79
+
80
+ Returns:
81
+ float: Render time in milliseconds
82
+ """
83
+ if not self._enabled:
84
+ return 0
85
+
86
+ end_time = time.time()
87
+ render_time = (end_time - self._phase_start) * 1000 # Convert to ms
88
+
89
+ self._render_times.append(render_time)
90
+
91
+ if component_name not in self._component_times:
92
+ self._component_times[component_name] = []
93
+ self._component_times[component_name].append(render_time)
94
+
95
+ self._measurements.append({
96
+ 'type': 'render_end',
97
+ 'component': component_name,
98
+ 'timestamp': end_time,
99
+ 'duration': render_time
100
+ })
101
+
102
+ self._current_phase = None
103
+ return render_time
104
+
105
+ def begin_commit(self) -> None:
106
+ """Begin measuring commit phase"""
107
+ if not self._enabled:
108
+ return
109
+
110
+ self._current_phase = 'commit'
111
+ self._phase_start = time.time()
112
+
113
+ def end_commit(self) -> float:
114
+ """
115
+ End measuring commit phase
116
+
117
+ Returns:
118
+ float: Commit time in milliseconds
119
+ """
120
+ if not self._enabled:
121
+ return 0
122
+
123
+ end_time = time.time()
124
+ commit_time = (end_time - self._phase_start) * 1000
125
+
126
+ self._commit_times.append(commit_time)
127
+
128
+ self._measurements.append({
129
+ 'type': 'commit',
130
+ 'timestamp': end_time,
131
+ 'duration': commit_time
132
+ })
133
+
134
+ self._current_phase = None
135
+ return commit_time
136
+
137
+ def log(self, message: str, data: Optional[Dict] = None) -> None:
138
+ """
139
+ Log a profiling message
140
+
141
+ Args:
142
+ message: Log message
143
+ data: Optional data
144
+ """
145
+ if not self._enabled:
146
+ return
147
+
148
+ self._measurements.append({
149
+ 'type': 'log',
150
+ 'message': message,
151
+ 'data': data or {},
152
+ 'timestamp': time.time()
153
+ })
154
+
155
+ def get_report(self) -> Dict:
156
+ """
157
+ Get profiling report
158
+
159
+ Returns:
160
+ dict: Profiling report
161
+ """
162
+ total_render_time = sum(self._render_times)
163
+ total_commit_time = sum(self._commit_times)
164
+
165
+ # Calculate component statistics
166
+ component_stats = {}
167
+ for name, times in self._component_times.items():
168
+ if times:
169
+ component_stats[name] = {
170
+ 'count': len(times),
171
+ 'total': sum(times),
172
+ 'average': sum(times) / len(times),
173
+ 'min': min(times),
174
+ 'max': max(times)
175
+ }
176
+
177
+ return {
178
+ 'total_time': (time.time() - self._start_time) * 1000,
179
+ 'render_time': total_render_time,
180
+ 'commit_time': total_commit_time,
181
+ 'render_count': len(self._render_times),
182
+ 'commit_count': len(self._commit_times),
183
+ 'average_render_time': total_render_time / len(self._render_times) if self._render_times else 0,
184
+ 'average_commit_time': total_commit_time / len(self._commit_times) if self._commit_times else 0,
185
+ 'component_stats': component_stats,
186
+ 'measurements': self._measurements[-100:] # Last 100 measurements
187
+ }
188
+
189
+ def get_slow_components(self, threshold: float = 16.0) -> List[Dict]:
190
+ """
191
+ Get components that exceed threshold
192
+
193
+ Args:
194
+ threshold: Threshold in milliseconds (default: 16ms for 60fps)
195
+
196
+ Returns:
197
+ list: Slow components
198
+ """
199
+ slow = []
200
+ for name, times in self._component_times.items():
201
+ avg_time = sum(times) / len(times) if times else 0
202
+ if avg_time > threshold:
203
+ slow.append({
204
+ 'name': name,
205
+ 'average_time': avg_time,
206
+ 'count': len(times),
207
+ 'max_time': max(times)
208
+ })
209
+
210
+ return sorted(slow, key=lambda x: x['average_time'], reverse=True)
211
+
212
+ def clear(self) -> None:
213
+ """Clear profiling data"""
214
+ self._measurements.clear()
215
+ self._component_times.clear()
216
+ self._render_times.clear()
217
+ self._commit_times.clear()
218
+
219
+ def export(self) -> str:
220
+ """
221
+ Export profiling data as JSON
222
+
223
+ Returns:
224
+ str: JSON string
225
+ """
226
+ import json
227
+ return json.dumps(self.get_report(), indent=2)
228
+
229
+
230
+ # Global profiler instance
231
+ _profiler = Profiler()
232
+
233
+
234
+ def get_profiler() -> Profiler:
235
+ """Get the global profiler"""
236
+ return _profiler
237
+
238
+
239
+ def profile_component(component: Any) -> Dict:
240
+ """
241
+ Profile a single component
242
+
243
+ Args:
244
+ component: Component instance
245
+
246
+ Returns:
247
+ dict: Component profile data
248
+ """
249
+ name = component.__class__.__name__
250
+ times = _profiler._component_times.get(name, [])
251
+
252
+ if times:
253
+ return {
254
+ 'name': name,
255
+ 'count': len(times),
256
+ 'total': sum(times),
257
+ 'average': sum(times) / len(times),
258
+ 'min': min(times),
259
+ 'max': max(times)
260
+ }
261
+
262
+ return {'name': name, 'count': 0}
263
+
264
+
265
+ def get_profile_data() -> Dict:
266
+ """
267
+ Get all profile data
268
+
269
+ Returns:
270
+ dict: Profile data
271
+ """
272
+ return _profiler.get_report()
273
+
274
+
275
+ def start_profiling() -> None:
276
+ """Start profiling"""
277
+ _profiler.start()
278
+
279
+
280
+ def stop_profiling() -> Dict:
281
+ """
282
+ Stop profiling and get report
283
+
284
+ Returns:
285
+ dict: Profiling report
286
+ """
287
+ _profiler.stop()
288
+ return _profiler.get_report()
@@ -0,0 +1,64 @@
1
+ """
2
+ PyReact DOM Module
3
+ ==================
4
+
5
+ This module provides DOM-specific functionality:
6
+ - DOM operations (create, update, remove elements)
7
+ - Synthetic events (cross-browser event handling)
8
+ - Attribute mapping (HTML attributes vs DOM properties)
9
+ """
10
+
11
+ from .dom_operations import (
12
+ create_element,
13
+ create_text_node,
14
+ append_child,
15
+ remove_child,
16
+ insert_before,
17
+ set_attribute,
18
+ remove_attribute,
19
+ set_style,
20
+ add_event_listener,
21
+ remove_event_listener,
22
+ )
23
+
24
+ from .events import (
25
+ SyntheticEvent,
26
+ SyntheticMouseEvent,
27
+ SyntheticKeyboardEvent,
28
+ SyntheticFocusEvent,
29
+ SyntheticFormEvent,
30
+ EVENT_TYPES,
31
+ )
32
+
33
+ from .attributes import (
34
+ is_custom_attribute,
35
+ should_set_attribute,
36
+ get_attribute_name,
37
+ PROPERTY_NAMES,
38
+ )
39
+
40
+ __all__ = [
41
+ # DOM Operations
42
+ 'create_element',
43
+ 'create_text_node',
44
+ 'append_child',
45
+ 'remove_child',
46
+ 'insert_before',
47
+ 'set_attribute',
48
+ 'remove_attribute',
49
+ 'set_style',
50
+ 'add_event_listener',
51
+ 'remove_event_listener',
52
+ # Events
53
+ 'SyntheticEvent',
54
+ 'SyntheticMouseEvent',
55
+ 'SyntheticKeyboardEvent',
56
+ 'SyntheticFocusEvent',
57
+ 'SyntheticFormEvent',
58
+ 'EVENT_TYPES',
59
+ # Attributes
60
+ 'is_custom_attribute',
61
+ 'should_set_attribute',
62
+ 'get_attribute_name',
63
+ 'PROPERTY_NAMES',
64
+ ]