-
-
Notifications
You must be signed in to change notification settings - Fork 649
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
improve ergonomics of checking for @union types with new @decorated_type_checkable decorator #8496
improve ergonomics of checking for @union types with new @decorated_type_checkable decorator #8496
Conversation
6786898
to
b6f239a
Compare
b6f239a
to
8942d22
Compare
### Problem `pants.util.meta.frozen_after_init` has a couple mypy type checking errors, specifically at: https://github.com/pantsbuild/pants/blob/00c4f8cc4a997f80a34f78b6d5fd6fca746befa7/src/python/pants/util/meta.py#L140-L144 This caused a failure on master, see https://travis-ci.org/pantsbuild/pants/jobs/600466692. ### Solution - Add `# type: ignore` to the offending lines until #8496 is merged. ### Result CI is unbroken!
eabed7c
to
dec7f0c
Compare
This is pretty meta-programming heavy. Is this actually worth it to avoid Any in this case? It's not a very widespread case, so it feels like maybe just removing the comment and letting it be would be preferable... |
Agreed with Stu. I appreciate how much thought you put into this PR - it’s cool to see how it works. However, I’d err on the side of keeping things as simple as possible and think |
I'd be fine reducing this to just the part that adds the |
dec7f0c
to
0138140
Compare
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.
Looks good :) Thanks!
src/python/pants/engine/objects.py
Outdated
@@ -167,3 +169,41 @@ def __iter__(self) -> Iterator[_C]: | |||
|
|||
def __bool__(self) -> bool: | |||
return bool(self.dependencies) | |||
|
|||
|
|||
@sentinel_attribute('_is_union') |
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.
Would we be able to drop the argument by referring instead to the symbol itself at a fixed field name for the check?
I'm imagining something like:
@sentinel_attribute
def union(cls):
...
def is_instance(self, obj: type) -> bool:
return obj._sentinel_attribute_type == self.__class__._sentinel_attribute_type
or something along those lines (depending on exactly what self
/cls
/metavariables exist in the context where is_instance
is actually defined). Where self.__class__._sentinel_attribute_type == union
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.
Thank you!!! Implemented!!!
src/python/pants/util/meta.py
Outdated
@@ -23,7 +24,7 @@ def __call__(cls, *args: Any, **kwargs: Any) -> Any: | |||
return cls.instance | |||
|
|||
|
|||
T = TypeVar("T") | |||
_T = TypeVar("_T") |
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.
Good change! The main reason I started making type vars private is to avoid autocomplete suggesting it in other files. Other files shouldn't really use this type var.
src/python/pants/util/meta.py
Outdated
""" | ||
|
||
@abstractmethod | ||
def __call__(self, cls: type) -> type: ... |
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.
Technically should be typing.Type
for the same reason it's typing.List
instead of 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.
What is that reason?
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.
To work with generics. You can't say list[int]
or type[Foo]
, but you can say List[int]
and Type[Foo]
.
However, PEP 585 is proposed to allow the former! https://www.python.org/dev/peps/pep-0585/ This is currently one of the most awkward parts of Python type hints.
src/python/pants/util/meta.py
Outdated
def define_instance_of(self, obj: type, **kwargs) -> type: | ||
return type(obj.__name__, (obj,), { | ||
'_sentinel_attribute_type': type(self), | ||
**kwargs | ||
}) |
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.
MyPy is cool with this?
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!! I thought it was just returning a type -- is there something suspicious about 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.
Looks great, thanks!
Now that it's not specifically relating to an attribute, I'm not 100% sure on the name... Something more declarative could be nice, but I don't have a good name... Something like @meta_type_checkable
or @can_check_is_subclass
or something... Except good?
f7847f1
to
6c0c8b3
Compare
6c0c8b3
to
304fa92
Compare
304fa92
to
0b207a4
Compare
0b207a4
to
431c42a
Compare
431c42a
to
abb2a59
Compare
[ci skip-rust-tests] [ci skip-jvm-tests] ### Problem `@decorated_type_checkable` was added in #8496, and it makes checking for certain decorator types a little cleaner. ### Solution - Use `@decorated_type_checkable` for `@side_effecting`. - Update the error message to avoid referring to console rules.
Problem
We manually check
getattr(some_type, '_is_union', False)
in multiple places in order to check ifsome_type
was declared with the@union
annotation. We would like to canonicalize this check and make it type-safe in some sense.Solution
@decorated_type_checkable
inpants.util.meta
, which contains two methods:define_instance_of(cls)
, which wraps a classcls
and sets the attribute_decorated_type_checkable_type
to the return value of@decorated_type_checkable
.is_instance(cls)
, which checks whether the sentinel attribute_decorated_type_checkable_type
matches the type of the return value of@decorated_type_checkable
.