-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
Use TypeIs in is_dataclass #11929
Use TypeIs in is_dataclass #11929
Conversation
7721a02
to
3f47361
Compare
This comment has been minimized.
This comment has been minimized.
@erictraut Is this a problem with Pyright? |
@NeilGirdhar, can you be more specific? I'm not sure what you're asking. |
I think it's about this CI failure:
I haven't looked at this code in detail to figure out whether pyright is correct here. |
So, I removed one of the overloads, but from my testing it didn't seem necessary anymore. I'll quickly try replacing it to see if it makes a difference. (Edit: still broken. Removing it since it doesn't belong anymore IMO.) The failure in PyRIght seems to be because Pyright isn't evaluating the overloads for |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@NeilGirdhar, PEP 742 allows some variation between type checkers. What you're seeing here is an example of this. Pyright's evaluation is arguably more accurate (narrower) and more consistent than mypy's, but both results are defensible. I think you'll need to change your test case to accommodate this variation. PEP 742 says:
If you try this self-contained code sample in both pyright and mypy, you'll see that mypy is inconsistent but pyright is consistent. from dataclasses import Field
from typing import Any, ClassVar, Protocol, runtime_checkable
from typing_extensions import TypeIs
@runtime_checkable
class DataclassInstance(Protocol):
__dataclass_fields__: ClassVar[dict[str, Field[Any]]]
def is_dataclass(obj: type) -> TypeIs[type[DataclassInstance]]: ...
def test1(x: type) -> None:
if isinstance(x, DataclassInstance):
reveal_type(x) # Both mypy and pyright reveal <subclass of "type" and "DataclassInstance">
def test2(x: type) -> None:
if is_dataclass(x):
reveal_type(x) # Mypy reveals "DataclassInstance" but pyright gives results consistent with "isinstance" |
@erictraut Sorry, but I don't understand how these two functions are supposed to be the same. In the first function, you are checking against If you change Similarly, if you change the isinstance line to Therefore, I believe this is a bug in Pyright even if as you say PEP 742 doesn't specify this case. |
Ah yes, you're correct. This is a bug in pyright. |
@NeilGirdhar, this will be fixed in the next release of pyright. I typically publish a new version every Tuesday evening PST. You could either wait until then to land this PR or you could temporarily comment out this test case. |
27bb3ff
to
e48608a
Compare
This comment has been minimized.
This comment has been minimized.
@JelleZijlstra There appear to be some errors with updating PyRight. Would you mind updating PyRight when you have a chance? |
Dependencies are updated automatically by renovate each UTC night. |
Diff from mypy_primer, showing the effect of this PR on open source code: pydantic (https://github.com/samuelcolvin/pydantic)
+ pydantic/v1/json.py:80: error: No overload variant of "asdict" matches argument type "type[DataclassInstance]" [call-overload]
+ pydantic/v1/json.py:80: note: Possible overload variants:
+ pydantic/v1/json.py:80: note: def asdict(obj: DataclassInstance) -> dict[str, Any]
+ pydantic/v1/json.py:80: note: def [_T] asdict(obj: DataclassInstance, *, dict_factory: Callable[[list[tuple[str, Any]]], _T]) -> _T
+ pydantic/deprecated/json.py:98: error: No overload variant of "asdict" matches argument type "type[DataclassInstance]" [call-overload]
+ pydantic/deprecated/json.py:98: note: Possible overload variants:
+ pydantic/deprecated/json.py:98: note: def asdict(obj: DataclassInstance) -> dict[str, Any]
+ pydantic/deprecated/json.py:98: note: def [_T] asdict(obj: DataclassInstance, *, dict_factory: Callable[[list[tuple[str, Any]]], _T]) -> _T
hydra-zen (https://github.com/mit-ll-responsible-ai/hydra-zen)
- src/hydra_zen/structured_configs/_implementations.py:1129: error: Argument 2 to "builds" of "BuildsFn" has incompatible type "**dict[str, int | float | Path | DataClass_ | type[DataClass_] | Enum | Any | Sequence[HydraSupportedType] | Mapping[Any, HydraSupportedType] | None]"; expected "Literal[False] | None" [arg-type]
- src/hydra_zen/structured_configs/_implementations.py:1129: error: Argument 2 to "builds" of "BuildsFn" has incompatible type "**dict[str, int | float | Path | DataClass_ | type[DataClass_] | Enum | Any | Sequence[HydraSupportedType] | Mapping[Any, HydraSupportedType] | None]"; expected "bool" [arg-type]
- src/hydra_zen/structured_configs/_implementations.py:1129: error: Argument 2 to "builds" of "BuildsFn" has incompatible type "**dict[str, int | float | Path | DataClass_ | type[DataClass_] | Enum | Any | Sequence[HydraSupportedType] | Mapping[Any, HydraSupportedType] | None]"; expected "Builds[Callable[[Callable[..., Any]], Callable[..., Any]]] | ZenPartialBuilds[Callable[[Callable[..., Any]], Callable[..., Any]]] | HydraPartialBuilds[Callable[[Callable[..., Any]], Callable[..., Any]]] | Just[Callable[[Callable[..., Any]], Callable[..., Any]]] | type[Builds[Callable[[Callable[..., Any]], Callable[..., Any]]]] | type[ZenPartialBuilds[Callable[[Callable[..., Any]], Callable[..., Any]]]] | type[HydraPartialBuilds[Callable[[Callable[..., Any]], Callable[..., Any]]]] | type[Just[Callable[[Callable[..., Any]], Callable[..., Any]]]] | Callable[[Callable[..., Any]], Callable[..., Any]] | str | None | Sequence[Builds[Callable[[Callable[..., Any]], Callable[..., Any]]] | ZenPartialBuilds[Callable[[Callable[..., Any]], Callable[..., Any]]] | HydraPartialBuilds[Callable[[Callable[..., Any]], Callable[..., Any]]] | Just[Callable[[Callable[..., Any]], Callable[..., Any]]] | type[Builds[Callable[[Callable[..., Any]], Callable[..., Any]]]] | type[ZenPartialBuilds[Callable[[Callable[..., Any]], Callable[..., Any]]]] | type[HydraPartialBuilds[Callable[[Callable[..., Any]], Callable[..., Any]]]] | type[Just[Callable[[Callable[..., Any]], Callable[..., Any]]]] | Callable[[Callable[..., Any]], Callable[..., Any]] | str | None]" [arg-type]
- src/hydra_zen/structured_configs/_implementations.py:1129: error: Argument 2 to "builds" of "BuildsFn" has incompatible type "**dict[str, int | float | Path | DataClass_ | type[DataClass_] | Enum | Any | Sequence[HydraSupportedType] | Mapping[Any, HydraSupportedType] | None]"; expected "Mapping[str, SupportedPrimitive] | None" [arg-type]
- src/hydra_zen/structured_configs/_implementations.py:1129: error: Argument 2 to "builds" of "BuildsFn" has incompatible type "**dict[str, int | float | Path | DataClass_ | type[DataClass_] | Enum | Any | Sequence[HydraSupportedType] | Mapping[Any, HydraSupportedType] | None]"; expected "list[str | DataClass_ | type[DataClass_] | Mapping[str, str | Sequence[str] | None]] | None" [arg-type]
- src/hydra_zen/structured_configs/_implementations.py:1129: error: Argument 2 to "builds" of "BuildsFn" has incompatible type "**dict[str, int | float | Path | DataClass_ | type[DataClass_] | Enum | Any | Sequence[HydraSupportedType] | Mapping[Any, HydraSupportedType] | None]"; expected "str | None" [arg-type]
- src/hydra_zen/structured_configs/_implementations.py:1129: error: Argument 2 to "builds" of "BuildsFn" has incompatible type "**dict[str, int | float | Path | DataClass_ | type[DataClass_] | Enum | Any | Sequence[HydraSupportedType] | Mapping[Any, HydraSupportedType] | None]"; expected "tuple[type[DataClass_], ...]" [arg-type]
- src/hydra_zen/structured_configs/_implementations.py:1129: error: Argument 2 to "builds" of "BuildsFn" has incompatible type "**dict[str, int | float | Path | DataClass_ | type[DataClass_] | Enum | Any | Sequence[HydraSupportedType] | Mapping[Any, HydraSupportedType] | None]"; expected "ZenConvert | None" [arg-type]
- src/hydra_zen/structured_configs/_implementations.py:1129: error: Argument 2 to "builds" of "BuildsFn" has incompatible type "**dict[str, int | float | Path | DataClass_ | type[DataClass_] | Enum | Any | Sequence[HydraSupportedType] | Mapping[Any, HydraSupportedType] | None]"; expected "T" [arg-type]
- src/hydra_zen/structured_configs/_implementations.py:1161: error: Incompatible types in assignment (expression has type "Just", variable has type "type[Builds[Any]]") [assignment]
pytest (https://github.com/pytest-dev/pytest)
+ src/_pytest/_io/pprint.py:117: error: Right operand of "and" is never evaluated [unreachable]
+ src/_pytest/_io/pprint.py:125: error: Statement is unreachable [unreachable]
streamlit (https://github.com/streamlit/streamlit)
+ lib/streamlit/runtime/caching/hashing.py: note: In member "_to_bytes" of class "_CacheFuncHasher":
+ lib/streamlit/runtime/caching/hashing.py:409:34: error: No overload variant of "asdict" matches argument type "Type[DataclassInstance]" [call-overload]
+ lib/streamlit/runtime/caching/hashing.py:409:34: note: Possible overload variants:
+ lib/streamlit/runtime/caching/hashing.py:409:34: note: def asdict(obj: DataclassInstance) -> Dict[str, Any]
+ lib/streamlit/runtime/caching/hashing.py:409:34: note: def [_T] asdict(obj: DataclassInstance, *, dict_factory: Callable[[List[Tuple[str, Any]]], _T]) -> _T
|
Updated. Ready for pull. |
Looking at the primer output: pydantic and streamlit are true positives. They use pytest is a false positive: It's basically complaining about this code (where if is_dataclass(object) and not isinstance(object, type): ... I'm unsure why that is. Maybe it's assuming that |
Definitely a weird choice. So, can the PR be pulled, or should we wait for the dependent projects to fix bugs? |
I'm fine with merging this, despite the false positive, but I'll leave it open for a day or two if someone has an idea how to fix/work around it. |
Sounds good 😄 ! |
Thanks for the quick merge. If anyone has time, it may be worth mentioning that most of the other uses of |
Fixes #9723