I spent the weekend thinking about this, and overall I think I like this idea! I just want to clarify a little bit about how AttrSpec would/should work:
class MyProxy(Generic[A]):
# is this right?
def __getitem__(self, xyz: A.name) -> A.value: ...
# or this
def __getitem__(self, xyz: A.xyz) -> A.value: ...
# what happens if you use A.name/A.value in non-dunder methods?
# type error? Or do these just have to match the signature
# of the method on A?
def dtype(self) -> A.value: ...
def compute_something(self, other: A.name) -> A.value: ...
I would also like to suggest maybe the ability to put a bound on the type of items that can be proxied - that would allow for type errors if you did something like:
class Foo: ...
A = AttrSpec("A", bound=Foo)
class FooProxy(Generic[A]):
def __init__(self, proxy: A) -> None:
self.proxied = proxy
def __getattr__(self, name: A.name) -> A.value:
# do some stuff assuming A is a subtype of Foo
...
FooProxy(3) # type error
What do you think?