-
Notifications
You must be signed in to change notification settings - Fork 64
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 AnyPath
instead of to_anypath
as constructor by fixing type issues
#146
Comments
I tried a new idea for The idea I had was: Python 3.8 introduced the So what I tried was getting the intersection of the However, I still ran into some limitations that I think means this doesn't sufficiently solve our problems:
We could maybe work around the Pydantic thing by monkeypatching Pydantic's internals to include our custom validator without adding a method to our Code from my experimentation on this branch. Here are also some minimal examples that demonstrate the basic principles: Protocol with constructor dispatchfrom typing import Protocol, runtime_checkable
@runtime_checkable
class Base(Protocol):
def __new__(cls, val: int):
if val >= 0:
return ChildA(val)
else:
return ChildB(val)
def method(self):
pass
class ChildA:
def __init__(self, val: int):
self.val = val
def method(self):
pass
class ChildB:
def __init__(self, val: int):
self.val = val
def method(self):
pass
## Construction dispatch works
Base(4)
#> <ChildA object at 0x7fc36281ce50>
Base(-9)
#> <ChildB object at 0x7fc36281c7c0>
## Subtype-checking works for isinstance
isinstance(ChildA(10), Base)
#> True
isinstance(ChildB(-5), Base)
#> True Protocol with dispatch and Pydanticfrom typing import Protocol, runtime_checkable
from pydantic import BaseModel
def base_validator(val):
return Base(val)
@runtime_checkable
class Base(Protocol):
def __new__(cls, val: int):
if val >= 0:
return ChildA(val)
else:
return ChildB(val)
@classmethod
def __get_validators__(cls):
yield base_validator
def method(self):
pass
class ChildA:
def __init__(self, val: int):
self.val = val
def method(self):
pass
class ChildB:
def __init__(self, val: int):
self.val = val
def method(self):
pass
## Construction dispatch works
Base(4)
#> <ChildA object at 0x7fc36286a1f0>
Base(-9)
#> <ChildB object at 0x7fc36286a970>
## Pydantic dispatch works
class MyModel(BaseModel):
base: Base
MyModel(base=9)
#> MyModel(base=<ChildA object at 0x7fc36281c700>)
MyModel(base=-4)
#> MyModel(base=<ChildB object at 0x7fc36281c310>)
## Type-checking fails
isinstance(ChildA(10), Base)
#> False
isinstance(ChildB(-5), Base)
#> False Created at 2022-02-08 23:09:15 EST by reprexlite v0.4.2 |
Maybe something like It would possibly break |
There's also some discussion about |
It seems this arcane spell fixes most of the typing issues for us: SfAnyPath: TypeAlias = cast( # type: ignore
Annotated[
Callable[[str], CloudPath | Path], BeforeValidator(AnyPath.validate), PlainSerializer(str)
],
AnyPath,
) |
When implementing #142 I encountered a tricky type annotation scenario and used a workaround.
The
AnyPath
constructor instead ofto_anypath
in the code referenced below runs fine, butmypy
complains. We should determine the right type annotations to useAnyPath(p)
instead ofto_anypath
.I couldn't use the
AnyPath
constructorFor some reason, this was the only way I could make
mypy
happy after the call here. Maybe you've got a nice solution:cloudpathlib/cloudpathlib/cloudpath.py
Lines 695 to 697 in 8dca2ed
If I change that line to
anypath.AnyPath(destination)
, I see these errors even if I# type: ignore
that line.Originally posted by @pjbull in #142 (comment)
The text was updated successfully, but these errors were encountered: