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,112 @@
1
+ """DOM Renderer Module
2
+
3
+ This module handles rendering VNodes to actual DOM elements and managing the root container.
4
+ """
5
+
6
+ from typing import Any, Callable, Dict, List, Optional, Union
7
+ import weakref
8
+ from .element import VNode
9
+ from .component import Component
10
+ from .reconciler import Reconciler
11
+
12
+
13
+ class Root:
14
+ """
15
+ Root container for a PyReact application.
16
+
17
+ Manages the root DOM element and coordinates updates.
18
+ """
19
+
20
+ def __init__(self, container: Any, options: Optional[Dict[str, Any]] = None):
21
+ """Initialize root container"""
22
+ self.container = container
23
+ self.options = options or {}
24
+ self._reconciler = Reconciler()
25
+ self._current_vnode: Optional[VNode] = None
26
+ self._is_rendering: bool = False
27
+ self._update_scheduled: bool = False
28
+ self._callbacks: List[Callable] = []
29
+
30
+ def render(self, element: Optional[VNode]) -> None:
31
+ """Render an element to the container"""
32
+ if element is None:
33
+ self.unmount()
34
+ return
35
+
36
+ self._is_rendering = True
37
+ try:
38
+ if self._current_vnode is None:
39
+ if not self.options.get('hydrate'):
40
+ self._clear_container()
41
+
42
+ dom = self._reconciler.create_dom(element)
43
+ self.container.append_child(dom)
44
+ else:
45
+ self._reconciler.diff(
46
+ self._current_vnode,
47
+ element,
48
+ self.container
49
+ )
50
+
51
+ self._current_vnode = element
52
+ finally:
53
+ self._is_rendering = False
54
+
55
+ self._run_callbacks()
56
+
57
+ def unmount(self) -> None:
58
+ """Unmount the current tree"""
59
+ if self._current_vnode:
60
+ self._reconciler.unmount(self._current_vnode)
61
+ self._clear_container()
62
+ self._current_vnode = None
63
+
64
+ def _clear_container(self) -> None:
65
+ """Clear all children from container"""
66
+ while self.container.first_child:
67
+ self.container.remove_child(self.container.first_child)
68
+
69
+ def _run_callbacks(self) -> None:
70
+ """Run pending callbacks"""
71
+ callbacks = self._callbacks.copy()
72
+ self._callbacks.clear()
73
+ for callback in callbacks:
74
+ callback()
75
+
76
+ def _schedule_callback(self, callback: Callable) -> None:
77
+ """Schedule a callback to run after render"""
78
+ self._callbacks.append(callback)
79
+
80
+
81
+ def create_root(container: Any, options: Optional[Dict[str, Any]] = None) -> Root:
82
+ """Create a root for rendering"""
83
+ return Root(container, options)
84
+
85
+
86
+ def render(element: VNode, container: Any) -> Root:
87
+ """Render an element into a container (legacy API)"""
88
+ while container.first_child:
89
+ container.remove_child(container.first_child)
90
+
91
+ root = create_root(container)
92
+ root.render(element)
93
+ return root
94
+
95
+
96
+ def hydrate(element: VNode, container: Any) -> Root:
97
+ """Hydrate existing HTML with PyReact interactivity"""
98
+ root = create_root(container, {'hydrate': True})
99
+ root.render(element)
100
+ return root
101
+
102
+
103
+ def unmount_component_at_node(container: Any) -> bool:
104
+ """Unmount a PyReact component from a container"""
105
+ while container.first_child:
106
+ container.remove_child(container.first_child)
107
+ return True
108
+
109
+
110
+ def find_dom_node(component: Component) -> Optional[Any]:
111
+ """Find the DOM node associated with a component"""
112
+ return component._dom_node
@@ -0,0 +1,304 @@
1
+ """
2
+ Scheduler Module
3
+ ================
4
+
5
+ This module implements the scheduler for managing updates
6
+ with different priorities.
7
+ """
8
+
9
+ from typing import Any, Callable, Dict, List, Optional
10
+ from enum import IntEnum
11
+ import time
12
+
13
+
14
+ class Priority(IntEnum):
15
+ """Priority levels for scheduled updates"""
16
+ IMMEDIATE = 99 # Execute immediately
17
+ USER_BLOCKING = 98 # User interactions
18
+ NORMAL = 97 # Normal updates
19
+ LOW = 96 # Preloading, etc.
20
+ IDLE = 95 # Background tasks
21
+
22
+
23
+ def get_timeout_for_priority(priority: Priority) -> float:
24
+ """Get timeout for a priority level"""
25
+ timeouts = {
26
+ Priority.IMMEDIATE: 0,
27
+ Priority.USER_BLOCKING: 250,
28
+ Priority.NORMAL: 5000,
29
+ Priority.LOW: 10000,
30
+ Priority.IDLE: float('inf')
31
+ }
32
+ return timeouts.get(priority, 5000)
33
+
34
+
35
+ class Task:
36
+ """Scheduled task"""
37
+
38
+ def __init__(
39
+ self,
40
+ callback: Callable,
41
+ priority: Priority,
42
+ expiration_time: float
43
+ ):
44
+ self.callback = callback
45
+ self.priority = priority
46
+ self.expiration_time = expiration_time
47
+ self.completed = False
48
+
49
+ def __lt__(self, other: 'Task') -> bool:
50
+ """Compare tasks by priority (higher priority first)"""
51
+ return self.priority > other.priority
52
+
53
+ def __repr__(self) -> str:
54
+ return f"Task(priority={self.priority.name}, completed={self.completed})"
55
+
56
+
57
+ class Scheduler:
58
+ """
59
+ Scheduler for managing updates with priorities
60
+
61
+ Implements a priority queue for scheduling callbacks.
62
+ Higher priority tasks are executed first.
63
+
64
+ Example:
65
+ scheduler = Scheduler()
66
+
67
+ # Schedule high priority task
68
+ scheduler.schedule_callback(
69
+ lambda: print('User interaction'),
70
+ Priority.USER_BLOCKING
71
+ )
72
+
73
+ # Schedule low priority task
74
+ scheduler.schedule_callback(
75
+ lambda: print('Background task'),
76
+ Priority.IDLE
77
+ )
78
+ """
79
+
80
+ IMMEDIATE_PRIORITY = Priority.IMMEDIATE
81
+ USER_BLOCKING_PRIORITY = Priority.USER_BLOCKING
82
+ NORMAL_PRIORITY = Priority.NORMAL
83
+ LOW_PRIORITY = Priority.LOW
84
+ IDLE_PRIORITY = Priority.IDLE
85
+
86
+ def __init__(self):
87
+ self._queue: List[Task] = []
88
+ self._scheduled: bool = False
89
+ self._is_flushing: bool = False
90
+ self._current_task: Optional[Task] = None
91
+
92
+ def schedule_callback(
93
+ self,
94
+ callback: Callable,
95
+ priority: Priority = Priority.NORMAL
96
+ ) -> Task:
97
+ """
98
+ Schedule a callback with a priority
99
+
100
+ Args:
101
+ callback: Function to execute
102
+ priority: Priority level
103
+
104
+ Returns:
105
+ Task: The scheduled task
106
+ """
107
+ timeout = get_timeout_for_priority(priority)
108
+ expiration_time = time.time() + timeout
109
+
110
+ task = Task(callback, priority, expiration_time)
111
+
112
+ # Insert sorted by priority
113
+ self._insert_sorted(task)
114
+
115
+ # Ensure work loop is scheduled
116
+ self._ensure_scheduled()
117
+
118
+ return task
119
+
120
+ def _insert_sorted(self, task: Task) -> None:
121
+ """Insert task in sorted order"""
122
+ for i, existing in enumerate(self._queue):
123
+ if task < existing:
124
+ self._queue.insert(i, task)
125
+ return
126
+ self._queue.append(task)
127
+
128
+ def _ensure_scheduled(self) -> None:
129
+ """Ensure the work loop is scheduled"""
130
+ if not self._scheduled:
131
+ self._scheduled = True
132
+ self._schedule_work_loop()
133
+
134
+ def _schedule_work_loop(self) -> None:
135
+ """Schedule the work loop (override for different platforms)"""
136
+ # In a real implementation, this would use requestIdleCallback or similar
137
+ # For now, we'll execute immediately
138
+ self._work_loop()
139
+
140
+ def _work_loop(self) -> None:
141
+ """Process tasks until deadline or queue is empty"""
142
+ self._is_flushing = True
143
+
144
+ try:
145
+ while self._queue:
146
+ task = self._queue[0]
147
+
148
+ # Check if task has expired
149
+ if task.expiration_time < time.time():
150
+ # Task expired, execute it
151
+ self._queue.pop(0)
152
+ self._execute_task(task)
153
+ else:
154
+ # Check if we should yield
155
+ # In a real implementation, we'd check deadline
156
+ self._queue.pop(0)
157
+ self._execute_task(task)
158
+ finally:
159
+ self._is_flushing = False
160
+ self._scheduled = False
161
+
162
+ # Re-schedule if there are more tasks
163
+ if self._queue:
164
+ self._ensure_scheduled()
165
+
166
+ def _execute_task(self, task: Task) -> None:
167
+ """Execute a single task"""
168
+ self._current_task = task
169
+ try:
170
+ task.callback()
171
+ task.completed = True
172
+ except Exception as e:
173
+ print(f"Error executing scheduled task: {e}")
174
+ finally:
175
+ self._current_task = None
176
+
177
+ def cancel_task(self, task: Task) -> bool:
178
+ """
179
+ Cancel a scheduled task
180
+
181
+ Args:
182
+ task: Task to cancel
183
+
184
+ Returns:
185
+ bool: True if task was cancelled
186
+ """
187
+ if task in self._queue:
188
+ self._queue.remove(task)
189
+ return True
190
+ return False
191
+
192
+ def flush_all(self) -> None:
193
+ """Execute all pending tasks"""
194
+ while self._queue:
195
+ task = self._queue.pop(0)
196
+ self._execute_task(task)
197
+
198
+ def get_first_task(self) -> Optional[Task]:
199
+ """Get the highest priority task without removing it"""
200
+ return self._queue[0] if self._queue else None
201
+
202
+ def is_empty(self) -> bool:
203
+ """Check if queue is empty"""
204
+ return len(self._queue) == 0
205
+
206
+ def get_pending_count(self) -> int:
207
+ """Get number of pending tasks"""
208
+ return len(self._queue)
209
+
210
+
211
+ class UpdateScheduler:
212
+ """
213
+ Scheduler specifically for component updates
214
+
215
+ Manages batch updates and priorities for component re-renders.
216
+ """
217
+
218
+ def __init__(self):
219
+ self._pending_updates: Dict[int, Any] = {}
220
+ self._batch_depth: int = 0
221
+ self._is_batching: bool = False
222
+ self._scheduler = Scheduler()
223
+
224
+ def schedule_update(
225
+ self,
226
+ component: Any,
227
+ priority: Priority = Priority.NORMAL
228
+ ) -> None:
229
+ """
230
+ Schedule a component update
231
+
232
+ Args:
233
+ component: Component to update
234
+ priority: Update priority
235
+ """
236
+ component_id = id(component)
237
+ self._pending_updates[component_id] = component
238
+
239
+ if not self._is_batching:
240
+ self._flush_updates()
241
+
242
+ def batch_updates(self, callback: Callable) -> None:
243
+ """
244
+ Execute callback with batched updates
245
+
246
+ Updates are not flushed until callback completes.
247
+
248
+ Args:
249
+ callback: Function to execute
250
+ """
251
+ self._is_batching = True
252
+ self._batch_depth += 1
253
+
254
+ try:
255
+ callback()
256
+ finally:
257
+ self._batch_depth -= 1
258
+ if self._batch_depth == 0:
259
+ self._is_batching = False
260
+ self._flush_updates()
261
+
262
+ def _flush_updates(self) -> None:
263
+ """Flush all pending updates"""
264
+ updates = self._pending_updates.copy()
265
+ self._pending_updates.clear()
266
+
267
+ for component in updates.values():
268
+ if hasattr(component, '_apply_state'):
269
+ component._apply_state()
270
+ if hasattr(component, 'render'):
271
+ component.render()
272
+
273
+ def defer_update(self, component: Any) -> None:
274
+ """Defer an update to low priority"""
275
+ self.schedule_update(component, Priority.LOW)
276
+
277
+ def immediate_update(self, component: Any) -> None:
278
+ """Execute an immediate update"""
279
+ self.schedule_update(component, Priority.IMMEDIATE)
280
+
281
+
282
+ # Global scheduler instance
283
+ _scheduler = Scheduler()
284
+ _update_scheduler = UpdateScheduler()
285
+
286
+
287
+ def get_scheduler() -> Scheduler:
288
+ """Get the global scheduler"""
289
+ return _scheduler
290
+
291
+
292
+ def get_update_scheduler() -> UpdateScheduler:
293
+ """Get the global update scheduler"""
294
+ return _update_scheduler
295
+
296
+
297
+ def schedule_callback(callback: Callable, priority: Priority = Priority.NORMAL) -> Task:
298
+ """Schedule a callback with the global scheduler"""
299
+ return _scheduler.schedule_callback(callback, priority)
300
+
301
+
302
+ def batch_updates(callback: Callable) -> None:
303
+ """Batch multiple updates together"""
304
+ _update_scheduler.batch_updates(callback)
@@ -0,0 +1,18 @@
1
+ """
2
+ PyReact DevTools Module
3
+ =======================
4
+
5
+ Development tools for debugging and profiling.
6
+ """
7
+
8
+ from .debugger import Debugger, debug_component, get_component_tree
9
+ from .profiler import Profiler, profile_component, get_profile_data
10
+
11
+ __all__ = [
12
+ 'Debugger',
13
+ 'debug_component',
14
+ 'get_component_tree',
15
+ 'Profiler',
16
+ 'profile_component',
17
+ 'get_profile_data',
18
+ ]