-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: allow callbacks as strings (#18)
* feat: allow callbacks as strings * test: more tests * test: coverage
- Loading branch information
1 parent
2924c02
commit 51c2f90
Showing
7 changed files
with
219 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import re | ||
from importlib import import_module | ||
from typing import Any | ||
|
||
_identifier_plus_dash = "(?:[a-zA-Z_][a-zA-Z_0-9-]+)" | ||
_dotted_name = f"(?:(?:{_identifier_plus_dash}\\.)*{_identifier_plus_dash})" | ||
PYTHON_NAME_PATTERN = re.compile(f"^({_dotted_name}):({_dotted_name})$") | ||
|
||
|
||
def _validate_python_name(name: str) -> str: | ||
"""Assert that `name` is a valid python name: e.g. `module.submodule:funcname`.""" | ||
if name and not PYTHON_NAME_PATTERN.match(name): | ||
msg = ( | ||
f"{name!r} is not a valid python_name. A python_name must " | ||
"be of the form '{obj.__module__}:{obj.__qualname__}' (e.g. " | ||
"'my_package.a_module:some_function')." | ||
) | ||
if ".<locals>." in name: # pragma: no cover | ||
*_, a, b = name.split(".<locals>.") | ||
a = a.split(":")[-1] | ||
msg += ( | ||
" Note: functions defined in local scopes are not yet supported. " | ||
f"Please move function {b!r} to the global scope of module {a!r}" | ||
) | ||
raise ValueError(msg) | ||
return name | ||
|
||
|
||
def import_python_name(python_name: str) -> Any: | ||
"""Import object from a fully qualified python name. | ||
Examples | ||
-------- | ||
>>> import_python_name("my_package.a_module:some_function") | ||
<function some_function at 0x...> | ||
>>> import_python_name('pydantic:BaseModel') | ||
<class 'pydantic.main.BaseModel'> | ||
""" | ||
_validate_python_name(python_name) # shows the best error message | ||
if match := PYTHON_NAME_PATTERN.match(python_name): | ||
module_name, funcname = match.groups() | ||
mod = import_module(module_name) | ||
return getattr(mod, funcname) | ||
raise ValueError( # pragma: no cover | ||
f"Could not parse python_name: {python_name!r}" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
from unittest.mock import Mock | ||
|
||
GLOBAL_MOCK = Mock(name="GLOBAL") | ||
|
||
|
||
def run_me() -> bool: | ||
GLOBAL_MOCK() | ||
return True | ||
|
||
|
||
attr = "not a callble" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,21 @@ | ||
from app_model.types import Icon | ||
import pytest | ||
from pydantic import ValidationError | ||
|
||
from app_model.types import Action, Icon | ||
|
||
|
||
def test_icon_validate(): | ||
assert Icon.validate('"fa5s.arrow_down"') == Icon( | ||
dark='"fa5s.arrow_down"', light='"fa5s.arrow_down"' | ||
) | ||
|
||
|
||
def test_action_validation(): | ||
with pytest.raises(ValidationError, match="'s!adf' is not a valid python_name"): | ||
Action(id="test", title="test", callback="s!adf") | ||
|
||
with pytest.raises(ValidationError): | ||
Action(id="test", title="test", callback=list()) | ||
|
||
with pytest.raises(ValidationError, match="'x.<locals>:asdf' is not a valid"): | ||
Action(id="test", title="test", callback="x.<locals>:asdf") |