Skip to content
This repository was archived by the owner on Jan 15, 2025. It is now read-only.
26 changes: 21 additions & 5 deletions flake8_idom_hooks/utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import ast
import re
from contextlib import contextmanager
from typing import Any, Iterator, List, Tuple

_COMPONENT_DECORATOR_NAME_PATTERN = re.compile(r"^(idom.(\w+\.)*)?component$")


@contextmanager
def set_current(obj: Any, **attrs: Any) -> Iterator[None]:
Expand All @@ -28,12 +31,25 @@ def is_hook_def(node: ast.FunctionDef) -> bool:


def is_component_def(node: ast.FunctionDef) -> bool:
return is_component_function_name(node.name)


def is_component_function_name(name: str) -> bool:
return name[0].upper() == name[0] and "_" not in name
return any(map(_is_component_decorator, node.decorator_list))


def is_hook_function_name(name: str) -> bool:
return name.lstrip("_").startswith("use_")


def _is_component_decorator(node: ast.AST) -> bool:
return (
_COMPONENT_DECORATOR_NAME_PATTERN.match(
".".join(reversed(list(_get_dotted_name(node))))
)
is not None
)


def _get_dotted_name(node: ast.AST) -> Iterator[str]:
while isinstance(node, ast.Attribute):
yield node.attr
node = node.value
if isinstance(node, ast.Name):
yield node.id
8 changes: 8 additions & 0 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
REQUIREMENTS_DIR = ROOT / "requirements"


@session
def format(session: Session) -> None:
install_requirements(session, "style")
session.run("black", ".")
session.run("isort", ".")


@session
def test(session: Session) -> None:
session.notify("test_style")
Expand All @@ -17,6 +24,7 @@ def test(session: Session) -> None:
@session
def test_style(session: Session) -> None:
install_requirements(session, "style")
session.run("black", "--check", ".")
session.run("isort", "--check", ".")
session.run("flake8", ".")

Expand Down
29 changes: 29 additions & 0 deletions tests/hook_usage_test_cases.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import idom
from idom import component


@component
def HookInIf():
if True:
# error: ROH102 hook 'use_state' used inside if statement
use_state


@component
def HookInElif():
if False:
pass
Expand All @@ -12,6 +18,7 @@ def HookInElif():
use_state


@component
def HookInElse():
if False:
pass
Expand All @@ -20,6 +27,7 @@ def HookInElse():
use_state


@component
def HookInIfExp():
(
# error: ROH102 hook 'use_state' used inside inline if expression
Expand All @@ -29,6 +37,7 @@ def HookInIfExp():
)


@component
def HookInElseOfIfExp():
(
None
Expand All @@ -39,6 +48,7 @@ def HookInElseOfIfExp():
)


@component
def HookInTry():
try:
# error: ROH102 hook 'use_state' used inside try statement
Expand All @@ -47,6 +57,7 @@ def HookInTry():
pass


@component
def HookInExcept():
try:
raise ValueError()
Expand All @@ -55,6 +66,7 @@ def HookInExcept():
use_state


@component
def HookInFinally():
try:
pass
Expand All @@ -63,12 +75,14 @@ def HookInFinally():
use_state


@component
def HookInForLoop():
for i in range(3):
# error: ROH102 hook 'use_state' used inside for loop
use_state


@component
def HookInWhileLoop():
while True:
# error: ROH102 hook 'use_state' used inside while loop
Expand All @@ -82,18 +96,27 @@ def use_state():


def generic_function():

# error: ROH101 hook 'use_state' used outside component or hook definition
use_state


@component
def use_state():
use_other


@component
def Component():
use_state


@idom.component
def IdomLongImportComponent():
use_state


@component
def use_custom_hook():
use_state

Expand All @@ -110,6 +133,7 @@ def not_hook_or_component():
use_state


@component
def CheckEffects():
x = 1
y = 2
Expand Down Expand Up @@ -210,25 +234,30 @@ def impropper_usage_of_effect_as_decorator():
)


@component
def make_component():
# nested component definitions are ok.
@component
def NestedComponent():
use_state


some_global_variable


@component
def Component():
# referencing a global variable is OK
use_effect(lambda: some_global_variable, [])


if True:

@component
def Component():
# this is ok since the conditional is outside the component
use_state

@component
def use_other():
use_state
32 changes: 0 additions & 32 deletions tox.ini

This file was deleted.