-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Add typing to component init #2276
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
Changes from all commits
5323742
8626b3d
c9bb168
91cc0d6
6bbafdc
c620f58
147798b
a5250e5
6d50f15
dee40cf
24a8e90
1f4aed9
88ca3f3
c18f0fd
544dcab
91a2a1d
ed360e1
b3061a4
a4ea22e
8029f15
b0ed806
ae345e0
d7136e3
a3dc517
a4f2c4a
12c5319
8e0dfea
453834b
13ff296
279950c
e6843fd
6dc0fc1
709d1e3
e6c2356
7bc56b9
9eee253
2adb37f
f228185
7b0da8a
fb8decb
197180c
8734e1b
d5b0ebe
4e583e3
150c2a5
96b931a
4045a61
46d05be
85c26b7
40bd21a
2de448e
648a1c6
8d185c8
ef6b054
6b30d08
dd5cb44
26a83e5
18fad2d
95d8b0d
00da064
d13b343
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,15 @@ | ||
import collections | ||
import hashlib | ||
from functools import wraps | ||
from typing import Callable, Optional, Any | ||
from typing import Callable, Optional, Any, List, Tuple | ||
|
||
import flask | ||
|
||
from .dependencies import ( | ||
handle_callback_args, | ||
handle_grouped_callback_args, | ||
Output, | ||
Input, | ||
) | ||
from .development.base_component import ComponentRegistry | ||
from .exceptions import ( | ||
|
@@ -62,14 +63,14 @@ def is_no_update(obj): | |
# pylint: disable=too-many-locals | ||
def callback( | ||
*_args, | ||
background=False, | ||
interval=1000, | ||
progress=None, | ||
progress_default=None, | ||
running=None, | ||
cancel=None, | ||
manager=None, | ||
cache_args_to_ignore=None, | ||
background: bool = False, | ||
interval: int = 1000, | ||
progress: Optional[Output] = None, | ||
progress_default: Any = None, | ||
running: Optional[List[Tuple[Output, Any, Any]]] = None, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. took me a moment to parse this :-) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's given as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you can just stop at a higher level |
||
cancel: Optional[List[Input]] = None, | ||
manager: Optional[BaseLongCallbackManager] = None, | ||
cache_args_to_ignore: Optional[list] = None, | ||
on_error: Optional[Callable[[Exception], Any]] = None, | ||
**_kwargs, | ||
): | ||
|
@@ -156,7 +157,7 @@ def callback( | |
callback_list = _kwargs.pop("callback_list", GLOBAL_CALLBACK_LIST) | ||
|
||
if background: | ||
long_spec = { | ||
long_spec: Any = { | ||
"interval": interval, | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,34 @@ | ||
from collections import OrderedDict | ||
import copy | ||
import numbers | ||
import os | ||
import typing | ||
from textwrap import fill, dedent | ||
|
||
from typing_extensions import TypedDict, NotRequired, Literal | ||
from dash.development.base_component import _explicitize_args | ||
from dash.exceptions import NonExistentEventException | ||
from ._all_keywords import python_keywords | ||
from ._collect_nodes import collect_nodes, filter_base_nodes | ||
from .base_component import Component | ||
from ._py_prop_typing import get_prop_typing, shapes, custom_imports | ||
from .base_component import Component, ComponentType | ||
|
||
import_string = """# AUTO GENERATED FILE - DO NOT EDIT | ||
|
||
# pylint: disable=unused-argument,too-many-locals | ||
import typing # noqa: F401 | ||
import numbers # noqa: F401 | ||
from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401 | ||
from dash.development.base_component import Component, _explicitize_args | ||
try: | ||
from dash.development.base_component import ComponentType # noqa: F401 | ||
except ImportError: | ||
ComponentType = typing.TypeVar("ComponentType", bound=Component) | ||
|
||
|
||
""" | ||
|
||
|
||
# pylint: disable=unused-argument,too-many-locals,too-many-branches | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. :-) |
||
def generate_class_string( | ||
T4rk1n marked this conversation as resolved.
Show resolved
Hide resolved
|
||
typename, | ||
props, | ||
|
@@ -54,8 +72,12 @@ def generate_class_string( | |
_base_nodes = {base_nodes} | ||
_namespace = '{namespace}' | ||
_type = '{typename}' | ||
{shapes} | ||
@_explicitize_args | ||
def __init__(self, {default_argtext}): | ||
def __init__( | ||
self, | ||
{default_argtext} | ||
): | ||
gvwilson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
self._prop_names = {list_of_valid_keys} | ||
self._valid_wildcard_attributes =\ | ||
{list_of_valid_wildcard_attr_prefixes} | ||
|
@@ -94,7 +116,9 @@ def __init__(self, {default_argtext}): | |
prop_keys = list(props.keys()) | ||
if "children" in props and "children" in list_of_valid_keys: | ||
prop_keys.remove("children") | ||
default_argtext = "children=None, " | ||
# TODO For dash 3.0, remove the Optional and = None for proper typing. | ||
# Also add the other required props after children. | ||
default_argtext = f"children: typing.Optional[{get_prop_typing('node', '', '', {})}] = None,\n " | ||
args = "{k: _locals[k] for k in _explicit_args if k != 'children'}" | ||
argtext = "children=children, **args" | ||
else: | ||
|
@@ -118,15 +142,31 @@ def __init__(self, {default_argtext}): | |
raise TypeError('Required argument children was not specified.') | ||
""" | ||
|
||
default_arglist = [ | ||
( | ||
f"{p:s}=Component.REQUIRED" | ||
if props[p]["required"] | ||
else f"{p:s}=Component.UNDEFINED" | ||
) | ||
for p in prop_keys | ||
if not p.endswith("-*") and p not in python_keywords and p != "setProps" | ||
] | ||
default_arglist = [] | ||
|
||
for prop_key in prop_keys: | ||
prop = props[prop_key] | ||
if ( | ||
prop_key.endswith("-*") | ||
or prop_key in python_keywords | ||
or prop_key == "setProps" | ||
): | ||
continue | ||
|
||
type_info = prop.get("type") | ||
|
||
if not type_info: | ||
print(f"Invalid prop type for typing: {prop_key}") | ||
default_arglist.append(f"{prop_key} = None") | ||
continue | ||
|
||
type_name = type_info.get("name") | ||
|
||
typed = get_prop_typing(type_name, typename, prop_key, type_info, namespace) | ||
|
||
arg_value = f"{prop_key}: typing.Optional[{typed}] = None" | ||
|
||
default_arglist.append(arg_value) | ||
|
||
if max_props: | ||
final_max_props = max_props - (1 if "children" in props else 0) | ||
|
@@ -139,7 +179,7 @@ def __init__(self, {default_argtext}): | |
"they may still be used as keyword arguments." | ||
) | ||
|
||
default_argtext += ", ".join(default_arglist + ["**kwargs"]) | ||
default_argtext += ",\n ".join(default_arglist + ["**kwargs"]) | ||
nodes = collect_nodes({k: v for k, v in props.items() if k != "children"}) | ||
|
||
return dedent( | ||
|
@@ -156,6 +196,7 @@ def __init__(self, {default_argtext}): | |
required_validation=required_validation, | ||
children_props=nodes, | ||
base_nodes=filter_base_nodes(nodes) + ["children"], | ||
shapes="\n".join(shapes.get(typename, {}).values()), | ||
) | ||
) | ||
|
||
|
@@ -179,20 +220,22 @@ def generate_class_file( | |
Returns | ||
------- | ||
""" | ||
import_string = ( | ||
"# AUTO GENERATED FILE - DO NOT EDIT\n\n" | ||
+ "from dash.development.base_component import " | ||
+ "Component, _explicitize_args\n\n\n" | ||
) | ||
imports = import_string | ||
|
||
class_string = generate_class_string( | ||
typename, props, description, namespace, prop_reorder_exceptions, max_props | ||
) | ||
|
||
custom_imp = custom_imports[namespace][typename] | ||
if custom_imp: | ||
imports += "\n".join(custom_imp) | ||
imports += "\n\n" | ||
|
||
file_name = f"{typename:s}.py" | ||
|
||
file_path = os.path.join(namespace, file_name) | ||
with open(file_path, "w", encoding="utf-8") as f: | ||
f.write(import_string) | ||
f.write(imports) | ||
f.write(class_string) | ||
|
||
print(f"Generated {file_name}") | ||
|
@@ -242,7 +285,16 @@ def generate_class( | |
string = generate_class_string( | ||
typename, props, description, namespace, prop_reorder_exceptions | ||
) | ||
scope = {"Component": Component, "_explicitize_args": _explicitize_args} | ||
scope = { | ||
"Component": Component, | ||
"ComponentType": ComponentType, | ||
"_explicitize_args": _explicitize_args, | ||
"typing": typing, | ||
"numbers": numbers, | ||
"TypedDict": TypedDict, | ||
"NotRequired": NotRequired, | ||
"Literal": Literal, | ||
} | ||
# pylint: disable=exec-used | ||
exec(string, scope) | ||
result = scope[typename] | ||
|
Uh oh!
There was an error while loading. Please reload this page.