Wrapper/Proxy Generic Type
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 https://github.com/python/mypy/issues/1188). I'm sure other cases exist,
Advantages:
- Representing a proxy type easily
- Allowing type checkers to enforce typing
Disadvantages:
- Functions that get T won't accept Proxy[T] as an argument. Although the current state is probably something similar.
I'd really like to hear some opinions on the idea.
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 typing-sig thing you're talking about? (a repo? can't find it on GH)
I'm coming from python/mypy#1188 and had the similar idea of a Proxy generic type (or intersection?) to solve the problem and have Mock[T] be typed like a Mock + Proxy[T] or similar.
Has there been other discussions after this issue? Could you link to them from here?
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 __new__.
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 SomeClass class has some_value attribute of the type int.
Other use-cases include the built-in setattr and delattr that could check if the passed second argument, if static, is present in instance attributes.
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:
...