-
Notifications
You must be signed in to change notification settings - Fork 243
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
Wrapper/Proxy Generic Type #802
Comments
Yeah, this looks like it would be a useful feature to have. But you should probably propose it on typing-sig, which it where most discussion about typing features happens nowadays. And I don't see how this could be implemented without somehow special-casing it in the type checkers (like Union), which means it requires serious use cases, serious discussion, serious commitment to implementation by multiple static type checker teams, and a PEP. (OTOH if it sails smoothly through typing-sig it might be added to the typing_extensions package so it wouldn't have to wait for Python 3.11 to be usable.) |
Hello @gvanrossum, What is the Has there been other discussions after this issue? Thanks a lot in advance. |
@bew right now it was decided to move from typing-sig (it is a mailing list: https://mail.python.org/mailman3/lists/typing-sig.python.org/) to here 🙂 |
This feature would be extremely based. JS has a proxy type that is fully supported in TS. class Target {
message1 = "hello"
message2 = "everyone"
};
class Handler<T> {
get = (target: T, prop: keyof T, receiver: any) => target[prop] + " world"
};
const proxy = new Proxy(new Target, new Handler);
console.log(proxy.message1) // "hello world" |
from typing import TypeVar, Generic
T = TypeVar("T")
class _Proxy(Generic[T]):
def __new__(cls, target: T) -> T:
return super().__new__(cls)
def __init__(self, target: T) -> None:
self.target: T = target
def __getattribute__(self, name: str) -> object:
print(name)
return getattr(object.__getattribute__(self, "target"), name)
def Proxy(target: T) -> T:
"""We use a factory function because you can't lie about the return type in `__new__`"""
return _Proxy(target) # type: ignore
class A:
i = 1
a = Proxy(A())
print(a.i) # 1
print(a) # <__main__._Proxy object at 0x00000209256BA260>
print(isinstance(a, A)) # true
print(isinstance(a, _Proxy)) # True
print(type(a)) # _Proxy You can get this functionality be hacking around making a factory function to produce the proxy and lie and ignore it's type. Would be a little nicer if we could lie about the return type in This is obviously not ideal as there are many complications around things like static methods and stuff. |
An even more based idea would be being able to have a generic base type, Pseudo code alert! class GenericSub(GenericBase[T]):
def foo() -> None: ....
class A:
def bar() -> None: ...
a = GenericSub[A]()
a.foo()
a.bar()
reveal_type(a) # GenericSub[A]
isinstance(a, A) # True
isinstance(a, GenericSub) # True |
I think the first thing we want is a way to extract a structural type from a nominal type: it would have all of the same attributes, but not be a subclass. Then we might want a way to transform types of things, but that gets complicated: https://www.typescriptlang.org/docs/handbook/2/mapped-types.html |
+1 for the feature. My use case is I'm writing my own mock class with the following API: patcher = Patcher(SomeClass)
patcher.some_value = 13 It would be great if mypy would check in this case that Other use-cases include the built-in |
I think the API for the proxy type could look similar to ParamSpec: A = AttrSpec('A')
class Proxy(Generic[A]):
def __setattr__(self, name: A.name, value: A.value) -> None:
...
def __delattr__(self, name: A.name) -> None:
...
def __getattr__(self, name: A.name) -> A.value:
... |
+1 for this feature. self.play(Square().animate.shift(UP)) In this case, we would want methods after # this should ideally just return _AnimationBuilder[Self] and autocomplete
# like a Square, instead of this
@property
def animate(self) -> _AnimationBuilder | Self: # Self for autocomplete
return _AnimationBuilder(self) Which then causes type errors because |
Another +1, the import threading
from typing import TypeVar, Generic, Proxy, Callable
T = TypeVar("T")
class ThreadLocal(Generic[T], Proxy[T]):
def __init__(self, builder: Callable[[], T]) -> None:
self._builder = builder
self._holder = threading.local()
def _get_inner(self) -> T:
try:
return self._holder.inner
except AttributeError:
self._holder.inner = self._builder()
return self._holder.inner
def __getattr__(self, name: str) -> Any:
return getattr(self._get_inner(), name) |
I've been putting some work into typeshed pull requests and I've noticed some places where I could really have used a Proxy Generic class - meaning I can inherit from Proxy[T] and that means that whatever attributes T has, Proxy[T] also has. We don't want to directly mark this as inheritance since then the distinction of a proxy is lost.
The examples I ran into are ProxyType and CallableProxyType in weakref or SynchronizedBase in multiprocessing,csharedtypes.
A big one would be unittest.Mock (See python/mypy#1188).
I'm sure other cases exist,
Advantages:
Disadvantages:
I'd really like to hear some opinions on the idea.
The text was updated successfully, but these errors were encountered: