Skip to content

Commit 6d7254f

Browse files
committed
fix tests and upgrade to compat latest idom ver
1 parent 15e7804 commit 6d7254f

File tree

2 files changed

+57
-53
lines changed

2 files changed

+57
-53
lines changed

idom_router/router.py

+39-38
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,24 @@
55
from typing import Any, Callable, Iterator, Sequence
66
from urllib.parse import parse_qs
77

8-
from idom import component, create_context, use_context, use_memo, use_state
8+
from idom import (
9+
component,
10+
create_context,
11+
use_memo,
12+
use_state,
13+
use_context,
14+
use_location,
15+
)
916
from idom.core.types import VdomAttributesAndChildren, VdomDict
1017
from idom.core.vdom import coalesce_attributes_and_children
11-
from idom.types import ComponentType, Context, Location
18+
from idom.types import ComponentType, Location, Context
1219
from idom.web.module import export, module_from_file
20+
from idom.backend.hooks import ConnectionContext, use_connection
21+
from idom.backend.types import Connection, Location
1322
from starlette.routing import compile_path as _compile_starlette_path
1423

1524
from idom_router.types import RoutePattern, RouteCompiler, Route
1625

17-
try:
18-
from typing import Protocol
19-
except ImportError: # pragma: no cover
20-
from typing_extensions import Protocol # type: ignore
21-
2226

2327
def compile_starlette_route(route: str) -> RoutePattern:
2428
pattern, _, converters = _compile_starlette_path(route)
@@ -30,25 +34,29 @@ def router(
3034
*routes: Route,
3135
compiler: RouteCompiler = compile_starlette_route,
3236
) -> ComponentType | None:
33-
initial_location = use_location()
34-
location, set_location = use_state(initial_location)
37+
old_conn = use_connection()
38+
location, set_location = use_state(old_conn.location)
39+
3540
compiled_routes = use_memo(
3641
lambda: [(compiler(r), e) for r, e in _iter_routes(routes)],
3742
dependencies=routes,
3843
)
3944
for compiled_route, element in compiled_routes:
4045
match = compiled_route.pattern.match(location.pathname)
4146
if match:
42-
return _LocationStateContext(
43-
element,
44-
value=_LocationState(
45-
location,
46-
set_location,
47-
{
48-
k: compiled_route.converters[k](v)
49-
for k, v in match.groupdict().items()
50-
},
47+
convs = compiled_route.converters
48+
return ConnectionContext(
49+
_route_state_context(
50+
element,
51+
value=_RouteState(
52+
set_location,
53+
{
54+
k: convs[k](v) if k in convs else v
55+
for k, v in match.groupdict().items()
56+
},
57+
),
5158
),
59+
value=Connection(old_conn.scope, location, old_conn.carrier),
5260
key=compiled_route.pattern.pattern,
5361
)
5462
return None
@@ -57,23 +65,18 @@ def router(
5765
@component
5866
def link(*attributes_or_children: VdomAttributesAndChildren, to: str) -> VdomDict:
5967
attributes, children = coalesce_attributes_and_children(attributes_or_children)
60-
set_location = _use_location_state().set_location
68+
set_location = _use_route_state().set_location
6169
attrs = {
6270
**attributes,
6371
"to": to,
6472
"onClick": lambda event: set_location(Location(**event)),
6573
}
66-
return _Link(attrs, *children)
67-
68-
69-
def use_location() -> Location:
70-
"""Get the current route location"""
71-
return _use_location_state().location
74+
return _link(attrs, *children)
7275

7376

7477
def use_params() -> dict[str, Any]:
7578
"""Get parameters from the currently matching route pattern"""
76-
return _use_location_state().params
79+
return use_context(_route_state_context).params
7780

7881

7982
def use_query(
@@ -94,29 +97,27 @@ def use_query(
9497
)
9598

9699

100+
def _use_route_state() -> _RouteState:
101+
return use_context(_route_state_context)
102+
103+
97104
def _iter_routes(routes: Sequence[Route]) -> Iterator[tuple[str, Any]]:
98105
for r in routes:
99106
for path, element in _iter_routes(r.routes):
100107
yield r.path + path, element
101108
yield r.path, r.element
102109

103110

104-
def _use_location_state() -> _LocationState:
105-
location_state = use_context(_LocationStateContext)
106-
assert location_state is not None, "No location state. Did you use a Router?"
107-
return location_state
111+
_link = export(
112+
module_from_file("idom-router", file=Path(__file__).parent / "bundle.js"),
113+
"Link",
114+
)
108115

109116

110117
@dataclass
111-
class _LocationState:
112-
location: Location
118+
class _RouteState:
113119
set_location: Callable[[Location], None]
114120
params: dict[str, Any]
115121

116122

117-
_LocationStateContext: Context[_LocationState | None] = create_context(None)
118-
119-
_Link = export(
120-
module_from_file("idom-router", file=Path(__file__).parent / "bundle.js"),
121-
"Link",
122-
)
123+
_route_state_context: Context[_RouteState | None] = create_context(None)

tests/test_router.py

+18-15
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import pytest
2-
from idom import Ref, component, html
1+
import re
2+
3+
from idom import Ref, component, html, use_location
34
from idom.testing import DisplayFixture
45

56
from idom_router import (
67
Route,
78
router,
89
link,
9-
use_location,
1010
use_params,
1111
use_query,
1212
)
@@ -45,7 +45,7 @@ def sample():
4545
await display.goto("/missing")
4646

4747
try:
48-
root_element = display.root_element()
48+
root_element = await display.root_element()
4949
except AttributeError:
5050
root_element = await display.page.wait_for_selector(
5151
f"#display-{display._next_view_id}", state="attached"
@@ -162,10 +162,6 @@ def sample():
162162
await display.page.wait_for_selector("#success")
163163

164164

165-
def custom_path_compiler(path):
166-
pattern = re.compile(path)
167-
168-
169165
async def test_custom_path_compiler(display: DisplayFixture):
170166
expected_params = {}
171167

@@ -178,26 +174,33 @@ def check_params():
178174
def sample():
179175
return router(
180176
Route(
181-
"/first/{first:str}",
177+
r"/first/(?P<first>\d+)",
182178
check_params(),
183179
Route(
184-
"/second/{second:str}",
180+
r"/second/(?P<second>[\d\.]+)",
185181
check_params(),
186182
Route(
187-
"/third/{third:str}",
183+
r"/third/(?P<third>[\d,]+)",
188184
check_params(),
189185
),
190186
),
191187
),
192-
compiler=lambda path: RoutePattern(re.compile()),
188+
compiler=lambda path: RoutePattern(
189+
re.compile(rf"^{path}$"),
190+
{
191+
"first": int,
192+
"second": float,
193+
"third": lambda s: list(map(int, s.split(","))),
194+
},
195+
),
193196
)
194197

195198
await display.show(sample)
196199

197200
for path, expected_params in [
198-
("/first/1", {"first": "1"}),
199-
("/first/1/second/2", {"first": "1", "second": "2"}),
200-
("/first/1/second/2/third/3", {"first": "1", "second": "2", "third": "3"}),
201+
("/first/1", {"first": 1}),
202+
("/first/1/second/2.1", {"first": 1, "second": 2.1}),
203+
("/first/1/second/2.1/third/3,3", {"first": 1, "second": 2.1, "third": [3, 3]}),
201204
]:
202205
await display.goto(path)
203206
await display.page.wait_for_selector("#success")

0 commit comments

Comments
 (0)