1
1
from collections import OrderedDict
2
2
import copy
3
+ import numbers
3
4
import os
5
+ import typing
4
6
from textwrap import fill , dedent
5
7
8
+ from typing_extensions import TypedDict , NotRequired , Literal
6
9
from dash .development .base_component import _explicitize_args
7
10
from dash .exceptions import NonExistentEventException
8
11
from ._all_keywords import python_keywords
9
12
from ._collect_nodes import collect_nodes , filter_base_nodes
10
- from .base_component import Component
13
+ from ._py_prop_typing import get_prop_typing , shapes , custom_imports
14
+ from .base_component import Component , ComponentType
11
15
16
+ import_string = """# AUTO GENERATED FILE - DO NOT EDIT
12
17
13
- # pylint: disable=unused-argument,too-many-locals
18
+ import typing # noqa: F401
19
+ import numbers # noqa: F401
20
+ from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
21
+ from dash.development.base_component import Component, _explicitize_args
22
+ try:
23
+ from dash.development.base_component import ComponentType # noqa: F401
24
+ except ImportError:
25
+ ComponentType = typing.TypeVar("ComponentType", bound=Component)
26
+
27
+
28
+ """
29
+
30
+
31
+ # pylint: disable=unused-argument,too-many-locals,too-many-branches
14
32
def generate_class_string (
15
33
typename ,
16
34
props ,
@@ -54,8 +72,12 @@ def generate_class_string(
54
72
_base_nodes = {base_nodes}
55
73
_namespace = '{namespace}'
56
74
_type = '{typename}'
75
+ {shapes}
57
76
@_explicitize_args
58
- def __init__(self, {default_argtext}):
77
+ def __init__(
78
+ self,
79
+ {default_argtext}
80
+ ):
59
81
self._prop_names = {list_of_valid_keys}
60
82
self._valid_wildcard_attributes =\
61
83
{list_of_valid_wildcard_attr_prefixes}
@@ -94,7 +116,9 @@ def __init__(self, {default_argtext}):
94
116
prop_keys = list (props .keys ())
95
117
if "children" in props and "children" in list_of_valid_keys :
96
118
prop_keys .remove ("children" )
97
- default_argtext = "children=None, "
119
+ # TODO For dash 3.0, remove the Optional and = None for proper typing.
120
+ # Also add the other required props after children.
121
+ default_argtext = f"children: typing.Optional[{ get_prop_typing ('node' , '' , '' , {})} ] = None,\n "
98
122
args = "{k: _locals[k] for k in _explicit_args if k != 'children'}"
99
123
argtext = "children=children, **args"
100
124
else :
@@ -118,15 +142,31 @@ def __init__(self, {default_argtext}):
118
142
raise TypeError('Required argument children was not specified.')
119
143
"""
120
144
121
- default_arglist = [
122
- (
123
- f"{ p :s} =Component.REQUIRED"
124
- if props [p ]["required" ]
125
- else f"{ p :s} =Component.UNDEFINED"
126
- )
127
- for p in prop_keys
128
- if not p .endswith ("-*" ) and p not in python_keywords and p != "setProps"
129
- ]
145
+ default_arglist = []
146
+
147
+ for prop_key in prop_keys :
148
+ prop = props [prop_key ]
149
+ if (
150
+ prop_key .endswith ("-*" )
151
+ or prop_key in python_keywords
152
+ or prop_key == "setProps"
153
+ ):
154
+ continue
155
+
156
+ type_info = prop .get ("type" )
157
+
158
+ if not type_info :
159
+ print (f"Invalid prop type for typing: { prop_key } " )
160
+ default_arglist .append (f"{ prop_key } = None" )
161
+ continue
162
+
163
+ type_name = type_info .get ("name" )
164
+
165
+ typed = get_prop_typing (type_name , typename , prop_key , type_info , namespace )
166
+
167
+ arg_value = f"{ prop_key } : typing.Optional[{ typed } ] = None"
168
+
169
+ default_arglist .append (arg_value )
130
170
131
171
if max_props :
132
172
final_max_props = max_props - (1 if "children" in props else 0 )
@@ -139,7 +179,7 @@ def __init__(self, {default_argtext}):
139
179
"they may still be used as keyword arguments."
140
180
)
141
181
142
- default_argtext += ", " .join (default_arglist + ["**kwargs" ])
182
+ default_argtext += ",\n " .join (default_arglist + ["**kwargs" ])
143
183
nodes = collect_nodes ({k : v for k , v in props .items () if k != "children" })
144
184
145
185
return dedent (
@@ -156,6 +196,7 @@ def __init__(self, {default_argtext}):
156
196
required_validation = required_validation ,
157
197
children_props = nodes ,
158
198
base_nodes = filter_base_nodes (nodes ) + ["children" ],
199
+ shapes = "\n " .join (shapes .get (typename , {}).values ()),
159
200
)
160
201
)
161
202
@@ -179,20 +220,22 @@ def generate_class_file(
179
220
Returns
180
221
-------
181
222
"""
182
- import_string = (
183
- "# AUTO GENERATED FILE - DO NOT EDIT\n \n "
184
- + "from dash.development.base_component import "
185
- + "Component, _explicitize_args\n \n \n "
186
- )
223
+ imports = import_string
187
224
188
225
class_string = generate_class_string (
189
226
typename , props , description , namespace , prop_reorder_exceptions , max_props
190
227
)
228
+
229
+ custom_imp = custom_imports [namespace ][typename ]
230
+ if custom_imp :
231
+ imports += "\n " .join (custom_imp )
232
+ imports += "\n \n "
233
+
191
234
file_name = f"{ typename :s} .py"
192
235
193
236
file_path = os .path .join (namespace , file_name )
194
237
with open (file_path , "w" , encoding = "utf-8" ) as f :
195
- f .write (import_string )
238
+ f .write (imports )
196
239
f .write (class_string )
197
240
198
241
print (f"Generated { file_name } " )
@@ -242,7 +285,16 @@ def generate_class(
242
285
string = generate_class_string (
243
286
typename , props , description , namespace , prop_reorder_exceptions
244
287
)
245
- scope = {"Component" : Component , "_explicitize_args" : _explicitize_args }
288
+ scope = {
289
+ "Component" : Component ,
290
+ "ComponentType" : ComponentType ,
291
+ "_explicitize_args" : _explicitize_args ,
292
+ "typing" : typing ,
293
+ "numbers" : numbers ,
294
+ "TypedDict" : TypedDict ,
295
+ "NotRequired" : NotRequired ,
296
+ "Literal" : Literal ,
297
+ }
246
298
# pylint: disable=exec-used
247
299
exec (string , scope )
248
300
result = scope [typename ]
0 commit comments