Skip to content

allow string return type #817

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ on:
- cron: "0 0 * * *"

jobs:
python-coverage:
python-exhaustive:
uses: ./.github/workflows/.nox-session.yml
with:
job-name: "python-{0}"
session-name: test_python_suite
session-name: test_python
session-arguments: --maxfail=3
python-environments:
uses: ./.github/workflows/.nox-session.yml
Expand Down
2 changes: 1 addition & 1 deletion noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ def test_python_types(session: Session) -> None:
install_requirements_file(session, "check-types")
install_requirements_file(session, "pkg-deps")
install_requirements_file(session, "pkg-extras")
session.run("mypy", "--strict", "src/idom")
session.run("mypy", "--show-error-codes", "--strict", "src/idom")


@nox.session
Expand Down
2 changes: 1 addition & 1 deletion src/idom/_option.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def __init__(self, new_opt: Option[_O], name: str) -> None:
self._new_opt = new_opt

@property # type: ignore
def _current(self) -> _O: # type: ignore
def _current(self) -> _O:
warnings.warn(
f"{self.name!r} has been renamed to {self._new_opt.name!r}",
DeprecationWarning,
Expand Down
8 changes: 4 additions & 4 deletions src/idom/core/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

import inspect
from functools import wraps
from typing import Any, Callable, Dict, Optional, Tuple, Union
from typing import Any, Callable, Dict, Optional, Tuple

from .types import ComponentType, VdomDict


def component(
function: Callable[..., Union[ComponentType, VdomDict | None]]
function: Callable[..., ComponentType | VdomDict | str | None]
) -> Callable[..., Component]:
"""A decorator for defining a new component.

Expand Down Expand Up @@ -39,7 +39,7 @@ class Component:

def __init__(
self,
function: Callable[..., ComponentType | VdomDict | None],
function: Callable[..., ComponentType | VdomDict | str | None],
key: Optional[Any],
args: Tuple[Any, ...],
kwargs: Dict[str, Any],
Expand All @@ -51,7 +51,7 @@ def __init__(
self._kwargs = kwargs
self._sig = sig

def render(self) -> VdomDict | ComponentType | None:
def render(self) -> ComponentType | VdomDict | str | None:
return self.type(*self._args, **self._kwargs)

def should_render(self, new: Component) -> bool:
Expand Down
6 changes: 1 addition & 5 deletions src/idom/core/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@

from typing_extensions import Protocol

from idom.config import IDOM_DEBUG_MODE
from idom.utils import Ref

from ._thread_local import ThreadLocal
Expand Down Expand Up @@ -226,9 +225,6 @@ def use_debug_value(
:func:`id` is different). By default these are inferred based on local
variables that are referenced by the given function.
"""
if not IDOM_DEBUG_MODE.current:
return # pragma: no cover

old: Ref[Any] = _use_const(lambda: Ref(object()))
memo_func = message if callable(message) else lambda: message
new = use_memo(memo_func, dependencies)
Expand Down Expand Up @@ -541,7 +537,7 @@ def _try_to_infer_closure_values(
else:
return None
else:
return cast("Sequence[Any] | None", values)
return values


def current_hook() -> LifeCycleHook:
Expand Down
2 changes: 1 addition & 1 deletion src/idom/core/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ def _render_component(
old_parent_model = parent.model.current
old_parent_children = old_parent_model["children"]
parent.model.current = {
**old_parent_model,
**old_parent_model, # type: ignore[misc]
"children": [
*old_parent_children[:index],
new_state.model.current,
Expand Down
2 changes: 1 addition & 1 deletion src/idom/core/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class ComponentType(Protocol):
This is used to see if two component instances share the same definition.
"""

def render(self) -> VdomDict | ComponentType | None:
def render(self) -> VdomDict | ComponentType | str | None:
"""Render the component's view model."""

def should_render(self: _OwnType, new: _OwnType) -> bool:
Expand Down
3 changes: 1 addition & 2 deletions src/idom/testing/display.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
async_playwright,
)

from idom import html
from idom.config import IDOM_TESTING_DEFAULT_TIMEOUT
from idom.types import RootComponentConstructor

Expand Down Expand Up @@ -49,7 +48,7 @@ async def goto(self, path: str, query: Any | None = None) -> None:
await self.page.goto(self.backend.url(path, query))

async def root_element(self) -> ElementHandle:
element = await self.page.wait_for_selector(f"#app", state="attached")
element = await self.page.wait_for_selector("#app", state="attached")
if element is None:
raise RuntimeError("Root element not attached") # pragma: no cover
return element
Expand Down
15 changes: 10 additions & 5 deletions src/idom/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ def __repr__(self) -> str:
return f"{type(self).__name__}({current})"


def html_to_vdom(html: str, *transforms: _ModelTransform, strict: bool = True) -> VdomDict:
def html_to_vdom(
html: str, *transforms: _ModelTransform, strict: bool = True
) -> VdomDict:
"""Transform HTML into a DOM model. Unique keys can be provided to HTML elements
using a ``key=...`` attribute within your HTML tag.

Expand All @@ -82,7 +84,9 @@ def html_to_vdom(html: str, *transforms: _ModelTransform, strict: bool = True) -
recover=not strict,
)
try:
nodes: List = fragments_fromstring(html, no_leading_text=True, parser=parser)
nodes: list[etree._Element] = fragments_fromstring(
html, no_leading_text=True, parser=parser
)
except etree.XMLSyntaxError as e:
if not strict:
raise e # pragma: no cover
Expand Down Expand Up @@ -139,10 +143,11 @@ def _etree_to_vdom(
attributes = dict(node.items())
key = attributes.pop("key", None)

vdom: VdomDict
if hasattr(idom.html, node.tag):
vdom = getattr(idom.html, node.tag)(attributes, *children, key=key)
else:
vdom: VdomDict = {"tagName": node.tag}
vdom = {"tagName": node.tag}
if children:
vdom["children"] = children
if attributes:
Expand All @@ -160,7 +165,7 @@ def _etree_to_vdom(
return vdom


def _mutate_vdom(vdom: VdomDict):
def _mutate_vdom(vdom: VdomDict) -> None:
"""Performs any necessary mutations on the VDOM attributes to meet VDOM spec.

Currently, this function only transforms the ``style`` attribute into a dictionary whose keys are
Expand Down Expand Up @@ -216,5 +221,5 @@ def _hypen_to_camel_case(string: str) -> str:
return first.lower() + remainder.title().replace("-", "")


class HTMLParseError(etree.LxmlSyntaxError):
class HTMLParseError(etree.LxmlSyntaxError): # type: ignore[misc]
"""Raised when an HTML document cannot be parsed using strict parsing."""
13 changes: 2 additions & 11 deletions tests/test_core/test_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
capture_idom_logs,
)
from idom.utils import Ref
from tests.tooling.asserts import assert_same_items
from tests.tooling.hooks import use_toggle


@pytest.fixture(autouse=True)
Expand Down Expand Up @@ -495,11 +495,6 @@ def SomeComponent():
)


def use_toggle(init=False):
state, set_state = idom.hooks.use_state(init)
return state, lambda: set_state(lambda old: not old)


async def test_model_key_preserves_callback_identity_for_common_elements(caplog):
called_good_trigger = idom.Ref(False)
good_handler = StaticEventHandler()
Expand Down Expand Up @@ -818,13 +813,9 @@ async def test_elements_and_components_with_the_same_key_can_be_interchanged():
set_toggle = idom.Ref()
effects = []

def use_toggle():
state, set_state = idom.hooks.use_state(True)
return state, lambda: set_state(not state)

@idom.component
def Root():
toggle, set_toggle.current = use_toggle()
toggle, set_toggle.current = use_toggle(True)
if toggle:
return SomeComponent("x")
else:
Expand Down
15 changes: 3 additions & 12 deletions tests/test_html.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,17 @@
import pytest

from idom import component, config, html, use_state
from idom import component, config, html
from idom.testing import DisplayFixture, poll
from idom.utils import Ref


def use_toggle(initial=True):
state, set_state = use_state(initial)
return state, lambda: set_state(not state)


def use_counter(initial_value):
state, set_state = use_state(initial_value)
return state, lambda: set_state(state + 1)
from tests.tooling.hooks import use_counter, use_toggle


async def test_script_mount_unmount(display: DisplayFixture):
toggle_is_mounted = Ref()

@component
def Root():
is_mounted, toggle_is_mounted.current = use_toggle()
is_mounted, toggle_is_mounted.current = use_toggle(True)
return html.div(
html.div({"id": "mount-state", "data-value": False}),
HasScript() if is_mounted else html.div(),
Expand Down
11 changes: 11 additions & 0 deletions tests/tooling/hooks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from idom import use_state


def use_toggle(init=False):
state, set_state = use_state(init)
return state, lambda: set_state(lambda old: not old)


def use_counter(initial_value):
state, set_state = use_state(initial_value)
return state, lambda: set_state(state + 1)