Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 3c6abe6

Browse filesBrowse files
authored
Improve and rename vdom_to_html -> reactpy_to_html (#1278)
- Renamed `reactpy.utils.html_to_vdom` to `reactpy.utils.string_to_reactpy`. - Renamed `reactpy.utils.vdom_to_html` to `reactpy.utils.reactpy_to_string`. - `reactpy.utils.string_to_reactpy` has been upgraded to handle more complex scenarios without causing ReactJS rendering errors. - `reactpy.utils.reactpy_to_string` will now retain the user's original casing for element `data-*` and `aria-*` attributes. - Convert `pragma: no cover` comments to `nocov`
1 parent e5e2661 commit 3c6abe6
Copy full SHA for 3c6abe6

File tree

Expand file treeCollapse file tree

19 files changed

+706
-239
lines changed
Filter options
Expand file treeCollapse file tree

19 files changed

+706
-239
lines changed

‎docs/source/about/changelog.rst

Copy file name to clipboardExpand all lines: docs/source/about/changelog.rst
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ Unreleased
3838
- :pull:`1113` - Renamed ``reactpy.config.REACTPY_DEBUG_MODE`` to ``reactpy.config.REACTPY_DEBUG``.
3939
- :pull:`1113` - ``@reactpy/client`` now exports ``React`` and ``ReactDOM``.
4040
- :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.
4143

4244
**Removed**
4345

@@ -48,6 +50,8 @@ Unreleased
4850
- :pull:`1113` - Removed ``reactpy.run``. See the documentation for the new method to run ReactPy applications.
4951
- :pull:`1113` - Removed ``reactpy.backend.*``. See the documentation for the new method to run ReactPy applications.
5052
- :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.
5155
- :pull:`1113` - All backend related installation extras (such as ``pip install reactpy[starlette]``) have been removed.
5256
- :pull:`1113` - Removed deprecated function ``module_from_template``.
5357
- :pull:`1113` - Removed support for Python 3.9.

‎src/reactpy/__init__.py

Copy file name to clipboardExpand all lines: src/reactpy/__init__.py
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from reactpy.core.layout import Layout
2222
from reactpy.core.vdom import vdom
2323
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
2525

2626
__author__ = "The Reactive Python Team"
2727
__version__ = "2.0.0a1"
@@ -35,9 +35,10 @@
3535
"event",
3636
"hooks",
3737
"html",
38-
"html_to_vdom",
3938
"logging",
4039
"pyscript_component",
40+
"reactpy_to_string",
41+
"string_to_reactpy",
4142
"types",
4243
"use_async_effect",
4344
"use_callback",
@@ -52,7 +53,6 @@
5253
"use_scope",
5354
"use_state",
5455
"vdom",
55-
"vdom_to_html",
5656
"web",
5757
"widgets",
5858
]

‎src/reactpy/core/_life_cycle_hook.py

Copy file name to clipboardExpand all lines: src/reactpy/core/_life_cycle_hook.py
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ async def __call__(self, stop: Event) -> None: ...
2222
logger = logging.getLogger(__name__)
2323

2424

25-
class _HookStack(Singleton): # pragma: no cover
25+
class _HookStack(Singleton): # nocov
2626
"""A singleton object which manages the current component tree's hooks.
2727
Life cycle hooks can be stored in a thread local or context variable depending
2828
on the platform."""

‎src/reactpy/core/_thread_local.py

Copy file name to clipboardExpand all lines: src/reactpy/core/_thread_local.py
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
_StateType = TypeVar("_StateType")
66

77

8-
class ThreadLocal(Generic[_StateType]): # pragma: no cover
8+
class ThreadLocal(Generic[_StateType]): # nocov
99
"""Utility for managing per-thread state information. This is only used in
1010
environments where ContextVars are not available, such as the `pyodide`
1111
executor."""

‎src/reactpy/core/hooks.py

Copy file name to clipboardExpand all lines: src/reactpy/core/hooks.py
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -613,7 +613,7 @@ def strictly_equal(x: Any, y: Any) -> bool:
613613
return x == y # type: ignore
614614

615615
# Fallback to identity check
616-
return x is y # pragma: no cover
616+
return x is y # nocov
617617

618618

619619
def run_effect_cleanup(cleanup_func: Ref[_EffectCleanFunc | None]) -> None:

‎src/reactpy/executors/asgi/middleware.py

Copy file name to clipboardExpand all lines: src/reactpy/executors/asgi/middleware.py
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ async def __call__(
166166
msg: dict[str, str] = orjson.loads(event["text"])
167167
if msg.get("type") == "layout-event":
168168
await ws.rendering_queue.put(msg)
169-
else: # pragma: no cover
169+
else: # nocov
170170
await asyncio.to_thread(
171171
_logger.warning, f"Unknown message type: {msg.get('type')}"
172172
)
@@ -205,7 +205,7 @@ async def run_dispatcher(self) -> None:
205205
# Determine component to serve by analyzing the URL and/or class parameters.
206206
if self.parent.multiple_root_components:
207207
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
209209
raise RuntimeError("Could not find component in URL path.")
210210
dotted_path = url_match["dotted_path"]
211211
if dotted_path not in self.parent.root_components:
@@ -215,7 +215,7 @@ async def run_dispatcher(self) -> None:
215215
component = self.parent.root_components[dotted_path]
216216
elif self.parent.root_component:
217217
component = self.parent.root_component
218-
else: # pragma: no cover
218+
else: # nocov
219219
raise RuntimeError("No root component provided.")
220220

221221
# Create a connection object by analyzing the websocket's query string.

‎src/reactpy/executors/asgi/pyscript.py

Copy file name to clipboardExpand all lines: src/reactpy/executors/asgi/pyscript.py
+1-3Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,7 @@ def __init__(
7979
self.html_head = html_head or html.head()
8080
self.html_lang = html_lang
8181

82-
def match_dispatch_path(
83-
self, scope: AsgiWebsocketScope
84-
) -> bool: # pragma: no cover
82+
def match_dispatch_path(self, scope: AsgiWebsocketScope) -> bool: # nocov
8583
"""We do not use a WebSocket dispatcher for Client-Side Rendering (CSR)."""
8684
return False
8785

‎src/reactpy/executors/asgi/standalone.py

Copy file name to clipboardExpand all lines: src/reactpy/executors/asgi/standalone.py
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
RootComponentConstructor,
3232
VdomDict,
3333
)
34-
from reactpy.utils import html_to_vdom, import_dotted_path
34+
from reactpy.utils import import_dotted_path, string_to_reactpy
3535

3636
_logger = getLogger(__name__)
3737

@@ -74,7 +74,7 @@ def __init__(
7474
extra_py = pyscript_options.get("extra_py", [])
7575
extra_js = pyscript_options.get("extra_js", {})
7676
config = pyscript_options.get("config", {})
77-
pyscript_head_vdom = html_to_vdom(
77+
pyscript_head_vdom = string_to_reactpy(
7878
pyscript_setup_html(extra_py, extra_js, config)
7979
)
8080
pyscript_head_vdom["tagName"] = ""
@@ -182,7 +182,7 @@ class ReactPyApp:
182182
async def __call__(
183183
self, scope: AsgiScope, receive: AsgiReceive, send: AsgiSend
184184
) -> None:
185-
if scope["type"] != "http": # pragma: no cover
185+
if scope["type"] != "http": # nocov
186186
if scope["type"] != "lifespan":
187187
msg = (
188188
"ReactPy app received unsupported request of type '%s' at path '%s'",

‎src/reactpy/executors/utils.py

Copy file name to clipboardExpand all lines: src/reactpy/executors/utils.py
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
REACTPY_RECONNECT_MAX_RETRIES,
1414
)
1515
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
1717

1818
logger = logging.getLogger(__name__)
1919

@@ -25,7 +25,7 @@ def import_components(dotted_paths: Iterable[str]) -> dict[str, Any]:
2525
}
2626

2727

28-
def check_path(url_path: str) -> str: # pragma: no cover
28+
def check_path(url_path: str) -> str: # nocov
2929
"""Check that a path is valid URL path."""
3030
if not url_path:
3131
return "URL path must not be empty."
@@ -41,7 +41,7 @@ def check_path(url_path: str) -> str: # pragma: no cover
4141

4242
def vdom_head_to_html(head: VdomDict) -> str:
4343
if isinstance(head, dict) and head.get("tagName") == "head":
44-
return vdom_to_html(head)
44+
return reactpy_to_string(head)
4545

4646
raise ValueError(
4747
"Invalid head element! Element must be either `html.head` or a string."

‎src/reactpy/pyscript/components.py

Copy file name to clipboardExpand all lines: src/reactpy/pyscript/components.py
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from reactpy import component, hooks
77
from reactpy.pyscript.utils import pyscript_component_html
88
from reactpy.types import ComponentType, Key
9-
from reactpy.utils import html_to_vdom
9+
from reactpy.utils import string_to_reactpy
1010

1111
if TYPE_CHECKING:
1212
from reactpy.types import VdomDict
@@ -22,15 +22,15 @@ def _pyscript_component(
2222
raise ValueError("At least one file path must be provided.")
2323

2424
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
2626

2727
if not rendered:
2828
# FIXME: This is needed to properly re-render PyScript during a WebSocket
2929
# disconnection / reconnection. There may be a better way to do this in the future.
3030
set_rendered(True)
3131
return None
3232

33-
component_vdom = html_to_vdom(
33+
component_vdom = string_to_reactpy(
3434
pyscript_component_html(tuple(str(fp) for fp in file_paths), initial, root)
3535
)
3636
component_vdom["tagName"] = ""

‎src/reactpy/pyscript/utils.py

Copy file name to clipboardExpand all lines: src/reactpy/pyscript/utils.py
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
import reactpy
1919
from reactpy.config import REACTPY_DEBUG, REACTPY_PATH_PREFIX, REACTPY_WEB_MODULES_DIR
2020
from reactpy.types import VdomDict
21-
from reactpy.utils import vdom_to_html
21+
from reactpy.utils import reactpy_to_string
2222

2323
if TYPE_CHECKING:
2424
from collections.abc import Sequence
@@ -77,7 +77,7 @@ def pyscript_component_html(
7777
file_paths: Sequence[str], initial: str | VdomDict, root: str
7878
) -> str:
7979
"""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)
8181
uuid = uuid4().hex
8282
executor_code = pyscript_executor_html(file_paths=file_paths, uuid=uuid, root=root)
8383

@@ -144,7 +144,7 @@ def extend_pyscript_config(
144144
return orjson.dumps(pyscript_config).decode("utf-8")
145145

146146

147-
def reactpy_version_string() -> str: # pragma: no cover
147+
def reactpy_version_string() -> str: # nocov
148148
from reactpy.testing.common import GITHUB_ACTIONS
149149

150150
local_version = reactpy.__version__

‎src/reactpy/templatetags/jinja.py

Copy file name to clipboardExpand all lines: src/reactpy/templatetags/jinja.py
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def render(self, *args: str, **kwargs: str) -> str:
2222
return pyscript_setup(*args, **kwargs)
2323

2424
# 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
2626

2727

2828
def component(dotted_path: str, **kwargs: str) -> str:

‎src/reactpy/testing/common.py

Copy file name to clipboardExpand all lines: src/reactpy/testing/common.py
+2-8Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from reactpy.config import REACTPY_TESTS_DEFAULT_TIMEOUT, REACTPY_WEB_MODULES_DIR
1717
from reactpy.core._life_cycle_hook import HOOK_STACK, LifeCycleHook
1818
from reactpy.core.events import EventHandler, to_event_handler_function
19+
from reactpy.utils import str_to_bool
1920

2021

2122
def clear_reactpy_web_modules_dir() -> None:
@@ -29,14 +30,7 @@ def clear_reactpy_web_modules_dir() -> None:
2930

3031

3132
_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", ""))
4034

4135

4236
class poll(Generic[_R]): # noqa: N801

‎src/reactpy/testing/display.py

Copy file name to clipboardExpand all lines: src/reactpy/testing/display.py
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ async def __aenter__(self) -> DisplayFixture:
5858

5959
self.page.set_default_timeout(REACTPY_TESTS_DEFAULT_TIMEOUT.current * 1000)
6060

61-
if not hasattr(self, "backend"): # pragma: no cover
61+
if not hasattr(self, "backend"): # nocov
6262
self.backend = BackendFixture()
6363
await es.enter_async_context(self.backend)
6464

‎src/reactpy/testing/utils.py

Copy file name to clipboardExpand all lines: src/reactpy/testing/utils.py
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
def find_available_port(
99
host: str, port_min: int = 8000, port_max: int = 9000
10-
) -> int: # pragma: no cover
10+
) -> int: # nocov
1111
"""Get a port that's available for the given host and port range"""
1212
for port in range(port_min, port_max):
1313
with closing(socket.socket()) as sock:

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.