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,208 @@
1
+ """
2
+ Virtual DOM Element Module
3
+ ==========================
4
+
5
+ This module defines VNode (Virtual Node) and the h() function (hyperscript)
6
+ for creating virtual DOM elements in PyReact.
7
+ """
8
+
9
+ from typing import Any, Callable, Dict, List, Optional, Union, TYPE_CHECKING
10
+
11
+ if TYPE_CHECKING:
12
+ from .component import Component
13
+
14
+
15
+ class VNode:
16
+ """
17
+ Virtual DOM Node
18
+
19
+ Represents a node in the virtual DOM tree. Can be:
20
+ - An HTML element ('div', 'span', etc.)
21
+ - A Component (function or class)
22
+ - A text node (string)
23
+
24
+ Attributes:
25
+ type: Element type (string tag name or Component class/callable)
26
+ props: Dictionary of properties/attributes
27
+ children: List of child VNodes or strings
28
+ key: Optional key for list reconciliation
29
+ ref: Optional reference to DOM node or component instance
30
+ """
31
+
32
+ def __init__(
33
+ self,
34
+ type: Union[str, Callable, type],
35
+ props: Optional[Dict[str, Any]] = None,
36
+ children: Optional[List[Union['VNode', str]]] = None,
37
+ key: Optional[Union[str, int]] = None,
38
+ ref: Optional[Any] = None
39
+ ):
40
+ self.type = type
41
+ self.props = props or {}
42
+ self.children = children or []
43
+ self.key = key
44
+ self.ref = ref
45
+
46
+ # Internal properties (managed by renderer)
47
+ self._dom_node: Optional[Any] = None
48
+ self._parent: Optional['VNode'] = None
49
+ self._component_instance: Optional['Component'] = None
50
+ self._hooks: List[Any] = []
51
+ self._hook_index: int = 0
52
+
53
+ def __repr__(self) -> str:
54
+ type_name = self.type if isinstance(self.type, str) else self.type.__name__
55
+ return f"VNode(type={type_name!r}, key={self.key!r}, children={len(self.children)})"
56
+
57
+ def __eq__(self, other: Any) -> bool:
58
+ if not isinstance(other, VNode):
59
+ return False
60
+ return (
61
+ self.type == other.type and
62
+ self.key == other.key and
63
+ self.props == other.props
64
+ )
65
+
66
+ def clone(self) -> 'VNode':
67
+ """Create a shallow clone of this VNode"""
68
+ return VNode(
69
+ type=self.type,
70
+ props=self.props.copy(),
71
+ children=self.children.copy(),
72
+ key=self.key,
73
+ ref=self.ref
74
+ )
75
+
76
+
77
+ def h(
78
+ type: Union[str, Callable, type],
79
+ props: Optional[Dict[str, Any]] = None,
80
+ *children: Union['VNode', str, List[Union['VNode', str]]]
81
+ ) -> VNode:
82
+ """
83
+ Create a virtual DOM element (hyperscript function)
84
+
85
+ This is the primary way to create VNodes in PyReact, similar to
86
+ React.createElement().
87
+
88
+ Args:
89
+ type: Element type - HTML tag string ('div', 'span') or Component
90
+ props: Dictionary of properties/attributes (optional)
91
+ *children: Child elements (VNodes, strings, or lists)
92
+
93
+ Returns:
94
+ VNode: The created virtual node
95
+
96
+ Examples:
97
+ >>> h('div', {'id': 'app'}, 'Hello World')
98
+ VNode(type='div', key=None, children=1)
99
+
100
+ >>> h('div', None, h('span', None, 'text'))
101
+ VNode(type='div', key=None, children=1)
102
+
103
+ >>> h(Counter, {'initialCount': 0})
104
+ VNode(type='Counter', key=None, children=0)
105
+ """
106
+ # Flatten children (handle nested lists)
107
+ flat_children: List[Union[VNode, str]] = []
108
+ for child in children:
109
+ if isinstance(child, list):
110
+ flat_children.extend(_flatten_children(child))
111
+ elif child is not None:
112
+ flat_children.append(child)
113
+
114
+ # Extract special props
115
+ props = props.copy() if props else {}
116
+ key = props.pop('key', None)
117
+ ref = props.pop('ref', None)
118
+
119
+ return VNode(
120
+ type=type,
121
+ props=props,
122
+ children=flat_children,
123
+ key=key,
124
+ ref=ref
125
+ )
126
+
127
+
128
+ def _flatten_children(
129
+ children: List[Union[VNode, str, List]]
130
+ ) -> List[Union[VNode, str]]:
131
+ """Recursively flatten nested children lists"""
132
+ result: List[Union[VNode, str]] = []
133
+ for child in children:
134
+ if isinstance(child, list):
135
+ result.extend(_flatten_children(child))
136
+ elif child is not None:
137
+ result.append(child)
138
+ return result
139
+
140
+
141
+ def create_element(
142
+ type: Union[str, Callable, type],
143
+ props: Optional[Dict[str, Any]] = None,
144
+ *children: Union[VNode, str, List[Union[VNode, str]]]
145
+ ) -> VNode:
146
+ """
147
+ Alias for h() function
148
+
149
+ Provided for compatibility with React's createElement API.
150
+
151
+ Args:
152
+ type: Element type
153
+ props: Properties/attributes
154
+ *children: Child elements
155
+
156
+ Returns:
157
+ VNode: The created virtual node
158
+ """
159
+ return h(type, props, *children)
160
+
161
+
162
+ def is_valid_element(element: Any) -> bool:
163
+ """
164
+ Check if a value is a valid VNode
165
+
166
+ Args:
167
+ element: Value to check
168
+
169
+ Returns:
170
+ bool: True if element is a valid VNode
171
+ """
172
+ return isinstance(element, VNode)
173
+
174
+
175
+ def clone_element(
176
+ element: VNode,
177
+ props: Optional[Dict[str, Any]] = None,
178
+ *children: Union[VNode, str, List[Union[VNode, str]]]
179
+ ) -> VNode:
180
+ """
181
+ Clone and return a new VNode with optional new props and children
182
+
183
+ Args:
184
+ element: VNode to clone
185
+ props: New props to merge (optional)
186
+ *children: New children (optional)
187
+
188
+ Returns:
189
+ VNode: Cloned element
190
+ """
191
+ if not is_valid_element(element):
192
+ raise ValueError('clone_element requires a valid VNode')
193
+
194
+ # Merge props
195
+ new_props = element.props.copy()
196
+ if props:
197
+ new_props.update(props)
198
+
199
+ # Use new children if provided, otherwise keep original
200
+ new_children = list(children) if children else element.children.copy()
201
+
202
+ return VNode(
203
+ type=element.type,
204
+ props=new_props,
205
+ children=new_children,
206
+ key=props.get('key', element.key) if props else element.key,
207
+ ref=props.get('ref', element.ref) if props else element.ref
208
+ )
@@ -0,0 +1,145 @@
1
+ """
2
+ Error Boundary Module
3
+ =====================
4
+
5
+ This module implements Error Boundaries for catching errors
6
+ in child components.
7
+ """
8
+
9
+ from typing import Any, Callable, Dict, Optional
10
+ from .component import Component
11
+ from .element import VNode
12
+
13
+
14
+ class ErrorBoundary(Component):
15
+ """
16
+ Error Boundary component
17
+
18
+ Catches errors in child components and displays a fallback UI.
19
+
20
+ Example:
21
+ h(ErrorBoundary, {
22
+ 'fallback': h('div', None, 'Something went wrong!')
23
+ },
24
+ h(RiskyComponent, None)
25
+ )
26
+ """
27
+
28
+ def __init__(self, props: Optional[Dict[str, Any]] = None):
29
+ super().__init__(props)
30
+ self.state = {
31
+ 'has_error': False,
32
+ 'error': None,
33
+ 'error_info': None
34
+ }
35
+
36
+ @staticmethod
37
+ def get_derived_state_from_error(error: Exception) -> Dict[str, Any]:
38
+ """
39
+ Update state when an error is thrown
40
+
41
+ Args:
42
+ error: The error that was thrown
43
+
44
+ Returns:
45
+ dict: State updates
46
+ """
47
+ return {
48
+ 'has_error': True,
49
+ 'error': error
50
+ }
51
+
52
+ def component_did_catch(self, error: Exception, info: Dict[str, Any]) -> None:
53
+ """
54
+ Log error information
55
+
56
+ Args:
57
+ error: The error that was thrown
58
+ info: Object with componentStack key
59
+ """
60
+ # Log error (could be sent to error reporting service)
61
+ print(f"ErrorBoundary caught an error: {error}")
62
+ if 'componentStack' in info:
63
+ print(f"Component stack: {info['componentStack']}")
64
+
65
+ def render(self) -> VNode:
66
+ """Render fallback or children"""
67
+ if self.state['has_error']:
68
+ fallback = self.props.get('fallback')
69
+ if fallback:
70
+ return fallback
71
+
72
+ # Default fallback
73
+ return VNode('div', {'className': 'error-boundary'}, [
74
+ VNode('h2', None, ['Something went wrong.']),
75
+ VNode('p', None, [str(self.state['error'])])
76
+ ])
77
+
78
+ return self.props.get('children', VNode('div', None, []))
79
+
80
+
81
+ def with_error_boundary(
82
+ component: Callable,
83
+ fallback: Optional[VNode] = None,
84
+ on_error: Optional[Callable[[Exception, Dict], None]] = None
85
+ ) -> Callable:
86
+ """
87
+ Higher-order component that wraps a component with an ErrorBoundary
88
+
89
+ Args:
90
+ component: Component to wrap
91
+ fallback: Fallback UI to show on error
92
+ on_error: Callback when error occurs
93
+
94
+ Returns:
95
+ Wrapped component
96
+
97
+ Example:
98
+ @with_error_boundary(fallback=h('div', None, 'Error!'))
99
+ def RiskyComponent(props):
100
+ # May throw an error
101
+ return h('div', None, risky_operation())
102
+ """
103
+ class WrappedErrorBoundary(ErrorBoundary):
104
+ def component_did_catch(self, error, info):
105
+ super().component_did_catch(error, info)
106
+ if on_error:
107
+ on_error(error, info)
108
+
109
+ def wrapper(props):
110
+ return VNode(WrappedErrorBoundary, {
111
+ 'fallback': fallback
112
+ }, [VNode(component, props)])
113
+
114
+ return wrapper
115
+
116
+
117
+ class ErrorInfo:
118
+ """
119
+ Error information object passed to component_did_catch
120
+ """
121
+
122
+ def __init__(self, component_stack: str = ''):
123
+ self.component_stack = component_stack
124
+
125
+ def to_dict(self) -> Dict[str, Any]:
126
+ return {
127
+ 'componentStack': self.component_stack
128
+ }
129
+
130
+
131
+ def capture_error(error: Exception, component_stack: str = '') -> Dict[str, Any]:
132
+ """
133
+ Capture error information
134
+
135
+ Args:
136
+ error: The error that was thrown
137
+ component_stack: Component stack trace
138
+
139
+ Returns:
140
+ dict: Error information
141
+ """
142
+ return {
143
+ 'error': error,
144
+ 'errorInfo': ErrorInfo(component_stack).to_dict()
145
+ }