File tree Expand file tree Collapse file tree 19 files changed +706
-239
lines changed
Filter options
Expand file tree Collapse file tree 19 files changed +706
-239
lines changed
Original file line number Diff line number Diff line change @@ -38,6 +38,8 @@ Unreleased
38
38
- :pull: `1113 ` - Renamed ``reactpy.config.REACTPY_DEBUG_MODE `` to ``reactpy.config.REACTPY_DEBUG ``.
39
39
- :pull: `1113 ` - ``@reactpy/client `` now exports ``React `` and ``ReactDOM ``.
40
40
- :pull: `1263 ` - ReactPy no longer auto-converts ``snake_case `` props to ``camelCase ``. It is now the responsibility of the user to ensure that props are in the correct format.
41
+ - :pull: `1278 ` - ``reactpy.utils.reactpy_to_string `` will now retain the user's original casing for ``data-* `` and ``aria-* `` attributes.
42
+ - :pull: `1278 ` - ``reactpy.utils.string_to_reactpy `` has been upgraded to handle more complex scenarios without causing ReactJS rendering errors.
41
43
42
44
**Removed **
43
45
@@ -48,6 +50,8 @@ Unreleased
48
50
- :pull: `1113 ` - Removed ``reactpy.run ``. See the documentation for the new method to run ReactPy applications.
49
51
- :pull: `1113 ` - Removed ``reactpy.backend.* ``. See the documentation for the new method to run ReactPy applications.
50
52
- :pull: `1113 ` - Removed ``reactpy.core.types `` module. Use ``reactpy.types `` instead.
53
+ - :pull: `1278 ` - Removed ``reactpy.utils.html_to_vdom ``. Use ``reactpy.utils.string_to_reactpy `` instead.
54
+ - :pull: `1278 ` - Removed ``reactpy.utils.vdom_to_html ``. Use ``reactpy.utils.reactpy_to_string `` instead.
51
55
- :pull: `1113 ` - All backend related installation extras (such as ``pip install reactpy[starlette] ``) have been removed.
52
56
- :pull: `1113 ` - Removed deprecated function ``module_from_template ``.
53
57
- :pull: `1113 ` - Removed support for Python 3.9.
Original file line number Diff line number Diff line change 21
21
from reactpy .core .layout import Layout
22
22
from reactpy .core .vdom import vdom
23
23
from reactpy .pyscript .components import pyscript_component
24
- from reactpy .utils import Ref , html_to_vdom , vdom_to_html
24
+ from reactpy .utils import Ref , reactpy_to_string , string_to_reactpy
25
25
26
26
__author__ = "The Reactive Python Team"
27
27
__version__ = "2.0.0a1"
35
35
"event" ,
36
36
"hooks" ,
37
37
"html" ,
38
- "html_to_vdom" ,
39
38
"logging" ,
40
39
"pyscript_component" ,
40
+ "reactpy_to_string" ,
41
+ "string_to_reactpy" ,
41
42
"types" ,
42
43
"use_async_effect" ,
43
44
"use_callback" ,
52
53
"use_scope" ,
53
54
"use_state" ,
54
55
"vdom" ,
55
- "vdom_to_html" ,
56
56
"web" ,
57
57
"widgets" ,
58
58
]
Original file line number Diff line number Diff line change @@ -22,7 +22,7 @@ async def __call__(self, stop: Event) -> None: ...
22
22
logger = logging .getLogger (__name__ )
23
23
24
24
25
- class _HookStack (Singleton ): # pragma: no cover
25
+ class _HookStack (Singleton ): # nocov
26
26
"""A singleton object which manages the current component tree's hooks.
27
27
Life cycle hooks can be stored in a thread local or context variable depending
28
28
on the platform."""
Original file line number Diff line number Diff line change 5
5
_StateType = TypeVar ("_StateType" )
6
6
7
7
8
- class ThreadLocal (Generic [_StateType ]): # pragma: no cover
8
+ class ThreadLocal (Generic [_StateType ]): # nocov
9
9
"""Utility for managing per-thread state information. This is only used in
10
10
environments where ContextVars are not available, such as the `pyodide`
11
11
executor."""
Original file line number Diff line number Diff line change @@ -613,7 +613,7 @@ def strictly_equal(x: Any, y: Any) -> bool:
613
613
return x == y # type: ignore
614
614
615
615
# Fallback to identity check
616
- return x is y # pragma: no cover
616
+ return x is y # nocov
617
617
618
618
619
619
def run_effect_cleanup (cleanup_func : Ref [_EffectCleanFunc | None ]) -> None :
Original file line number Diff line number Diff line change @@ -166,7 +166,7 @@ async def __call__(
166
166
msg : dict [str , str ] = orjson .loads (event ["text" ])
167
167
if msg .get ("type" ) == "layout-event" :
168
168
await ws .rendering_queue .put (msg )
169
- else : # pragma: no cover
169
+ else : # nocov
170
170
await asyncio .to_thread (
171
171
_logger .warning , f"Unknown message type: { msg .get ('type' )} "
172
172
)
@@ -205,7 +205,7 @@ async def run_dispatcher(self) -> None:
205
205
# Determine component to serve by analyzing the URL and/or class parameters.
206
206
if self .parent .multiple_root_components :
207
207
url_match = re .match (self .parent .dispatcher_pattern , self .scope ["path" ])
208
- if not url_match : # pragma: no cover
208
+ if not url_match : # nocov
209
209
raise RuntimeError ("Could not find component in URL path." )
210
210
dotted_path = url_match ["dotted_path" ]
211
211
if dotted_path not in self .parent .root_components :
@@ -215,7 +215,7 @@ async def run_dispatcher(self) -> None:
215
215
component = self .parent .root_components [dotted_path ]
216
216
elif self .parent .root_component :
217
217
component = self .parent .root_component
218
- else : # pragma: no cover
218
+ else : # nocov
219
219
raise RuntimeError ("No root component provided." )
220
220
221
221
# Create a connection object by analyzing the websocket's query string.
Original file line number Diff line number Diff line change @@ -79,9 +79,7 @@ def __init__(
79
79
self .html_head = html_head or html .head ()
80
80
self .html_lang = html_lang
81
81
82
- def match_dispatch_path (
83
- self , scope : AsgiWebsocketScope
84
- ) -> bool : # pragma: no cover
82
+ def match_dispatch_path (self , scope : AsgiWebsocketScope ) -> bool : # nocov
85
83
"""We do not use a WebSocket dispatcher for Client-Side Rendering (CSR)."""
86
84
return False
87
85
Original file line number Diff line number Diff line change 31
31
RootComponentConstructor ,
32
32
VdomDict ,
33
33
)
34
- from reactpy .utils import html_to_vdom , import_dotted_path
34
+ from reactpy .utils import import_dotted_path , string_to_reactpy
35
35
36
36
_logger = getLogger (__name__ )
37
37
@@ -74,7 +74,7 @@ def __init__(
74
74
extra_py = pyscript_options .get ("extra_py" , [])
75
75
extra_js = pyscript_options .get ("extra_js" , {})
76
76
config = pyscript_options .get ("config" , {})
77
- pyscript_head_vdom = html_to_vdom (
77
+ pyscript_head_vdom = string_to_reactpy (
78
78
pyscript_setup_html (extra_py , extra_js , config )
79
79
)
80
80
pyscript_head_vdom ["tagName" ] = ""
@@ -182,7 +182,7 @@ class ReactPyApp:
182
182
async def __call__ (
183
183
self , scope : AsgiScope , receive : AsgiReceive , send : AsgiSend
184
184
) -> None :
185
- if scope ["type" ] != "http" : # pragma: no cover
185
+ if scope ["type" ] != "http" : # nocov
186
186
if scope ["type" ] != "lifespan" :
187
187
msg = (
188
188
"ReactPy app received unsupported request of type '%s' at path '%s'" ,
Original file line number Diff line number Diff line change 13
13
REACTPY_RECONNECT_MAX_RETRIES ,
14
14
)
15
15
from reactpy .types import ReactPyConfig , VdomDict
16
- from reactpy .utils import import_dotted_path , vdom_to_html
16
+ from reactpy .utils import import_dotted_path , reactpy_to_string
17
17
18
18
logger = logging .getLogger (__name__ )
19
19
@@ -25,7 +25,7 @@ def import_components(dotted_paths: Iterable[str]) -> dict[str, Any]:
25
25
}
26
26
27
27
28
- def check_path (url_path : str ) -> str : # pragma: no cover
28
+ def check_path (url_path : str ) -> str : # nocov
29
29
"""Check that a path is valid URL path."""
30
30
if not url_path :
31
31
return "URL path must not be empty."
@@ -41,7 +41,7 @@ def check_path(url_path: str) -> str: # pragma: no cover
41
41
42
42
def vdom_head_to_html (head : VdomDict ) -> str :
43
43
if isinstance (head , dict ) and head .get ("tagName" ) == "head" :
44
- return vdom_to_html (head )
44
+ return reactpy_to_string (head )
45
45
46
46
raise ValueError (
47
47
"Invalid head element! Element must be either `html.head` or a string."
Original file line number Diff line number Diff line change 6
6
from reactpy import component , hooks
7
7
from reactpy .pyscript .utils import pyscript_component_html
8
8
from reactpy .types import ComponentType , Key
9
- from reactpy .utils import html_to_vdom
9
+ from reactpy .utils import string_to_reactpy
10
10
11
11
if TYPE_CHECKING :
12
12
from reactpy .types import VdomDict
@@ -22,15 +22,15 @@ def _pyscript_component(
22
22
raise ValueError ("At least one file path must be provided." )
23
23
24
24
rendered , set_rendered = hooks .use_state (False )
25
- initial = html_to_vdom (initial ) if isinstance (initial , str ) else initial
25
+ initial = string_to_reactpy (initial ) if isinstance (initial , str ) else initial
26
26
27
27
if not rendered :
28
28
# FIXME: This is needed to properly re-render PyScript during a WebSocket
29
29
# disconnection / reconnection. There may be a better way to do this in the future.
30
30
set_rendered (True )
31
31
return None
32
32
33
- component_vdom = html_to_vdom (
33
+ component_vdom = string_to_reactpy (
34
34
pyscript_component_html (tuple (str (fp ) for fp in file_paths ), initial , root )
35
35
)
36
36
component_vdom ["tagName" ] = ""
Original file line number Diff line number Diff line change 18
18
import reactpy
19
19
from reactpy .config import REACTPY_DEBUG , REACTPY_PATH_PREFIX , REACTPY_WEB_MODULES_DIR
20
20
from reactpy .types import VdomDict
21
- from reactpy .utils import vdom_to_html
21
+ from reactpy .utils import reactpy_to_string
22
22
23
23
if TYPE_CHECKING :
24
24
from collections .abc import Sequence
@@ -77,7 +77,7 @@ def pyscript_component_html(
77
77
file_paths : Sequence [str ], initial : str | VdomDict , root : str
78
78
) -> str :
79
79
"""Renders a PyScript component with the user's code."""
80
- _initial = initial if isinstance (initial , str ) else vdom_to_html (initial )
80
+ _initial = initial if isinstance (initial , str ) else reactpy_to_string (initial )
81
81
uuid = uuid4 ().hex
82
82
executor_code = pyscript_executor_html (file_paths = file_paths , uuid = uuid , root = root )
83
83
@@ -144,7 +144,7 @@ def extend_pyscript_config(
144
144
return orjson .dumps (pyscript_config ).decode ("utf-8" )
145
145
146
146
147
- def reactpy_version_string () -> str : # pragma: no cover
147
+ def reactpy_version_string () -> str : # nocov
148
148
from reactpy .testing .common import GITHUB_ACTIONS
149
149
150
150
local_version = reactpy .__version__
Original file line number Diff line number Diff line change @@ -22,7 +22,7 @@ def render(self, *args: str, **kwargs: str) -> str:
22
22
return pyscript_setup (* args , ** kwargs )
23
23
24
24
# This should never happen, but we validate it for safety.
25
- raise ValueError (f"Unknown tag: { self .tag_name } " ) # pragma: no cover
25
+ raise ValueError (f"Unknown tag: { self .tag_name } " ) # nocov
26
26
27
27
28
28
def component (dotted_path : str , ** kwargs : str ) -> str :
Original file line number Diff line number Diff line change 16
16
from reactpy .config import REACTPY_TESTS_DEFAULT_TIMEOUT , REACTPY_WEB_MODULES_DIR
17
17
from reactpy .core ._life_cycle_hook import HOOK_STACK , LifeCycleHook
18
18
from reactpy .core .events import EventHandler , to_event_handler_function
19
+ from reactpy .utils import str_to_bool
19
20
20
21
21
22
def clear_reactpy_web_modules_dir () -> None :
@@ -29,14 +30,7 @@ def clear_reactpy_web_modules_dir() -> None:
29
30
30
31
31
32
_DEFAULT_POLL_DELAY = 0.1
32
- GITHUB_ACTIONS = os .getenv ("GITHUB_ACTIONS" , "False" ) in {
33
- "y" ,
34
- "yes" ,
35
- "t" ,
36
- "true" ,
37
- "on" ,
38
- "1" ,
39
- }
33
+ GITHUB_ACTIONS = str_to_bool (os .getenv ("GITHUB_ACTIONS" , "" ))
40
34
41
35
42
36
class poll (Generic [_R ]): # noqa: N801
Original file line number Diff line number Diff line change @@ -58,7 +58,7 @@ async def __aenter__(self) -> DisplayFixture:
58
58
59
59
self .page .set_default_timeout (REACTPY_TESTS_DEFAULT_TIMEOUT .current * 1000 )
60
60
61
- if not hasattr (self , "backend" ): # pragma: no cover
61
+ if not hasattr (self , "backend" ): # nocov
62
62
self .backend = BackendFixture ()
63
63
await es .enter_async_context (self .backend )
64
64
Original file line number Diff line number Diff line change 7
7
8
8
def find_available_port (
9
9
host : str , port_min : int = 8000 , port_max : int = 9000
10
- ) -> int : # pragma: no cover
10
+ ) -> int : # nocov
11
11
"""Get a port that's available for the given host and port range"""
12
12
for port in range (port_min , port_max ):
13
13
with closing (socket .socket ()) as sock :
You can’t perform that action at this time.
0 commit comments