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.
- pyreact/__init__.py +144 -0
- pyreact/cli/__init__.py +3 -0
- pyreact/cli/main.py +512 -0
- pyreact/core/__init__.py +80 -0
- pyreact/core/component.py +372 -0
- pyreact/core/context.py +173 -0
- pyreact/core/element.py +208 -0
- pyreact/core/error_boundary.py +145 -0
- pyreact/core/hooks.py +550 -0
- pyreact/core/memo.py +221 -0
- pyreact/core/portal.py +159 -0
- pyreact/core/reconciler.py +399 -0
- pyreact/core/refs.py +213 -0
- pyreact/core/renderer.py +112 -0
- pyreact/core/scheduler.py +304 -0
- pyreact/devtools/__init__.py +18 -0
- pyreact/devtools/debugger.py +314 -0
- pyreact/devtools/profiler.py +288 -0
- pyreact/dom/__init__.py +64 -0
- pyreact/dom/attributes.py +317 -0
- pyreact/dom/dom_operations.py +333 -0
- pyreact/dom/events.py +349 -0
- pyreact/server/__init__.py +34 -0
- pyreact/server/hydration.py +216 -0
- pyreact/server/ssr.py +344 -0
- pyreact/styles/__init__.py +19 -0
- pyreact/styles/css_module.py +231 -0
- pyreact/styles/styled.py +303 -0
- pyreact/testing/__init__.py +71 -0
- pyreact/testing/fire_event.py +355 -0
- pyreact/testing/screen.py +267 -0
- pyreact/testing/test_renderer.py +232 -0
- pyreact/utils/__init__.py +17 -0
- pyreact/utils/diff.py +182 -0
- pyreact/utils/object_pool.py +216 -0
- pyreact_framework-1.0.0.dist-info/METADATA +363 -0
- pyreact_framework-1.0.0.dist-info/RECORD +41 -0
- pyreact_framework-1.0.0.dist-info/WHEEL +5 -0
- pyreact_framework-1.0.0.dist-info/entry_points.txt +2 -0
- pyreact_framework-1.0.0.dist-info/licenses/LICENSE +21 -0
- 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()
|
pyreact/dom/__init__.py
ADDED
|
@@ -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
|
+
]
|