-
-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Add typing to component init. #2276
base: dev
Are you sure you want to change the base?
Conversation
def __init__( | ||
self, | ||
{default_argtext} | ||
): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder, now that we're Py3-only, maybe we should call black
on the generated files as part of the generation process? We already do that in the core component packages as a separate step.
Hi - this is really, really cool. I would love to see this landed into Dash, and if I can help with reviewing this PR, debugging any tests, etc., please let me know. I was working on a separate effort to generate stub (.pyi) files, so I have tinkered around with this part of the codebase a little bit. I'm excited to see where this goes! |
optionalArray: typing.List = Component.UNDEFINED, | ||
optionalBool: bool = Component.UNDEFINED, | ||
optionalFunc: typing.Any = Component.UNDEFINED, | ||
optionalNumber: typing.Union[float, int] = Component.UNDEFINED, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alexcjohnson @T4rk1n just wondering if people can pass in Decimals in here as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If to_json_plotly
can serialize it then yes. It's pretty flexible, so probably Decimal
is fine, but I haven't tested it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Decimal
can serialize, I think we can put numbers.Number
to get all the number types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
numbers.Number
is too much, it includes complex
- how about numbers.Real
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://docs.python.org/3/library/numbers.html#the-numeric-tower
Number is the base class, then they extend the last one, to get the Integral
you need all the other 3 implemented. Tested with numpy and Number is what get the most support and our to_json
supports them, I dont think custom implementation of those works.
def __init__( | ||
self, | ||
children: typing.Union[str, int, float, Component, typing.List[typing.Union[str, int, float, Component]]] = None, | ||
optionalArray: typing.List = Component.UNDEFINED, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should be a List[Any]
. mypy will throw an error on this.
also, wondering if we should use the parent type Iterable
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typing.List
is same as typing.List[typing.Any]
, iterable might not json compatible, I think the List is the most safe for this typing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are certainly a number of other iterables people frequently use though - tuple
, range
, dict_keys
, dict_values
, etc etc... I wouldn't want to push people into casting everything to a list, that would be unnecessary overhead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
range, dict_keys, dict_values,
Those already needs to be cast to list for json.
I guess we could do a union of Tuple and List.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still open 😎
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can add Tuple as union for untyped arrays, but arrayOf
is really a different typing on both side.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for updating array
. Not sure what you mean by "arrayOf
is really a different typing on both side", but I think it's fairly important to accept tuples in arrayOf
. I recall for example various database drivers returning query results as either nested tuples or tuples of dicts.
I wonder if it would be worth defining up front a couple of extra types we can reuse later, like:
JsonNumber = typing.Union[int, float, numbers.Real] # or whatever we end up with for numbers
JsonArray = typing.Union[typing.List, typing.Tuple]
ReactNode = typing.Union[
typing.Union[str, JsonNumber, Component], # note more numbers here!
JsonArray[typing.Union[str, JsonNumber, Component]]
]
If we did something like that, would we be able to change:
optionalArrayOf: typing.List[typing.Union[int, float, numbers.Number]] = Component.UNDEFINED
into this?
optionalArrayOf: JsonArray[JsonNumber] = Component.UNDEFINED
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's better to go without defining type aliases that would require to be imported and would break backward compatibility for component libraries that compiled with the new typing but the user has dash locked to a previous version.
I went with The checker and my IDE don't recognize the type of |
optionalString: str = Component.UNDEFINED, | ||
optionalSymbol: typing.Any = Component.UNDEFINED, | ||
optionalNode: typing.Union[str, int, float, Component, typing.List[typing.Union[str, int, float, Component]]] = Component.UNDEFINED, | ||
optionalElement: Component = Component.UNDEFINED, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh, never noticed this before... do we actually support Element
as distinct from Node
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it ends up rendering the same as node but for typing it allows only a single component to be put and not a list of string/number/components.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are there any examples of this in existing components? Does it work, either as children
or with components-as-props?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before component as props, it was kind of invalid prop as only the children would be hydrated and children would be treated as a node, component as props also doesn't differ those Element from node.
The collect_nodes make both node and element into component:
dash/dash/development/_collect_nodes.py
Lines 1 to 2 in 453834b
def is_node(value): | |
return value in ("node", "element") |
I think the only other place we have Element defined is in the typescript props:
element?: JSX.Element; |
But the prop itself was not tested as it was before component as props was supported.
requires-install.txt
Outdated
@@ -5,3 +5,5 @@ dash_core_components==2.0.0 | |||
dash_table==5.0.0 | |||
importlib-metadata==4.8.3;python_version<"3.7" | |||
contextvars==2.4;python_version<"3.7" | |||
typing_extensions==4.1.1;python_version<"3.7" | |||
typing_extensions>=4.4.0;python_version>"3.6" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typing_extensions>=4.4.0;python_version>"3.6" | |
typing_extensions>=4.4.0;python_version>="3.7" |
Just to be more obviously symmetric with the <"3.7"
I wonder though, if 4.1.1 is sufficient would we be better off just collapsing these into one line? If someone on 3.7+ has a version >=4.1.1,<4.4.0 pinned for some reason is that a problem?
typing_extensions>=4.1.1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
4.1.1 has fix for imports on 3.7, it's the last version that works with 3.6. I don't see anything we actually uses in the later version so we can try a single version >=4.1.1
"import typing # noqa: F401\n" | ||
"import numbers # noqa: F401\n" | ||
"import enum # noqa: F401\n" | ||
"from typing_extensions import TypedDict, NotRequired # noqa: F401\n" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a big deal, but in principle we can avoid noqa: F401
if we look at class_string
and drop any imports we don't need.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
The docstring on the components are not in the right position for IDE support, the docstring for the props should be under the |
There is a typing error with plotly Figure objects on |
@@ -5,3 +5,5 @@ dash_core_components==2.0.0 | |||
dash_table==5.0.0 | |||
importlib-metadata==4.8.3;python_version<"3.7" | |||
contextvars==2.4;python_version<"3.7" | |||
typing_extensions>=4.1.1 | |||
stringcase>=1.2.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need stringcase
in requires-install
? Couldn't it go in requires-dev
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's needed for the old runtime component loader 😞
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
haha ok - we should put a deprecation warning on runtime component generation, and remove it whenever we decide to do a Dash 3.0
Add python
typing
to generated components__init__
.Contributor Checklist
CHANGELOG.md