-
-
Notifications
You must be signed in to change notification settings - Fork 74
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
type: Infer parameters types for __init__(default...)
, __get__
and __set__
#985
base: main
Are you sure you want to change the base?
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #985 +/- ##
=======================================
Coverage 87.25% 87.25%
=======================================
Files 9 9
Lines 4928 4936 +8
=======================================
+ Hits 4300 4307 +7
- Misses 628 629 +1 ☔ View full report in Codecov by Sentry. |
Not sure how type checkers like Mypy and Pyright like this, but the LSP seems to love it 🙂 |
Cool! |
__init__(default...)
, __get__
and __set__
This seems like a good start but I also worry that's it's actually dead end. The problem being that the typing is inherited entirely from the default but in many cases the default could be None or in fact only cover a subset of what is actually supported. |
The main point of this PR was to add type information to the parameter, not type validation. I'm unsure if we can add type validation with type checkers like MyPy, Pyright, or BasedPyright I have pushed some changes to the from __future__ import annotations
import param
from typing import reveal_type
class MyParams(param.Parameterized):
a = param.ClassSelector(default=1, class_=(list, int, str))
b = param.ClassSelector(default=1, class_=int)
params = MyParams()
reveal_type(params.a)
reveal_type(params.b)
params.a = [1]
params.a = "a"
params.b = "b" |
What is the difference between type information and type validation? Given how big of a topic typing Param is, writing down somewhere (e.g. issue, design doc) the direction we are taking would be worthwhile. I will struggle merging a PR without having the big picture in mind. |
Type information shows what the argument is, and type validation verifies that it matches what is expected. This PR currently adds information about the parameter. This is useful when running an LSP, as it currently always writes something like This means an LSP will understand that This does not do any validation. E.g.,
Understandable. The technology itself currently limits the big picture. I'm also afraid this will be postponed because we don't have a "perfect" solution. |
We likely could add some validation, this will, as far as I can see, come at the cost of type information (which could be ok). Below is an example of diff --git a/param/parameters.py b/param/parameters.py
index 508159a..b70745f 100644
--- a/param/parameters.py
+++ b/param/parameters.py
@@ -839,7 +839,7 @@ class Number(Dynamic[T]):
-class Integer(Number[T]):
+class Integer(Number[int]):
"""Numeric Parameter required to be an Integer"""
_slot_defaults = dict(Number._slot_defaults, default=0)
@@ -847,14 +847,14 @@ class Integer(Number[T]):
@typing.overload
def __init__(
self,
- default: T = 0, *, bounds=None, softbounds=None, inclusive_bounds=(True,True), step=None, set_hook=None,
+ default: int = 0, *, bounds=None, softbounds=None, inclusive_bounds=(True,True), step=None, set_hook=None,
allow_None=False, doc=None, label=None, precedence=None, instantiate=False,
constant=False, readonly=False, pickle_default_value=True, per_instance=True,
allow_refs=False, nested_refs=False
):
...
- def __init__(self, default: T = Undefined, *args, **kwargs):
+ def __init__(self, default: int = Undefined, *args, **kwargs):
super().__init__(default=default, *args, **kwargs)
def _validate_value(self, val, allow_None): |
Ok thanks I get what you mean with type information (inference) vs. validation (run-time checked).
Why?
I'm not sure I follow, what is not supported?
Which technology? How Param is implemented? What Python typing offers? Or both? Don't be afraid :) You've already mentioned a few interesting things in this PR (type information, LSP, type validation, dataclass_transform), it'd be valuable to collect them in a single document that can be augmented over time with our findings. Typing in Param is a big enough project that it requires this kind of approach imo. Perhaps this will even demonstrate that it is not possible to provide a robust typing experience with Param due its too-dynamic nature, making us think about some changes we could make to accommodate that (if typing is judged more important). At the very least, I'd love to read more explanation about the approach showcased in this PR and its tradeoffs. It's okay if it's not "perfect", but if it's wrong (e.g. how is |
We can get type validation. I'm using basedpyright as my LSP here. Setting the type-checking mode to "basic" (lowest type setting, see here) with the diff in #985 (comment), give the following result (note I have only activated this mode in this comment):
As you can see, every
See
Mostly, Python typing is pretty rough. Some changes could likely also be done in Param.
I haven't found a use for
It would be ideal, but currently, I'm just trying things out and seeing what sticks. I'm not even sure this works with other people's setups. |
Adding type hints to
__set__
,__get__
, and__init__
.Haven't tested on old versions, so for now this should be seen as a POC.
Code
With this PR:
Before: