Skip to content
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

Attempt to create a subclass of a generic class results in TypeError: '_AnyCall' object isn't subscriptable #755

Open
ANogin opened this issue May 18, 2024 · 9 comments

Comments

@ANogin
Copy link

ANogin commented May 18, 2024

I have

T = TypeVar('T')

class Foo(Generic[T]):
   ...

class Bar(Foo[Something]):
   ...

And micropython complains:

TypeError: '_AnyCall' object isn't subscriptable

for the Foo[Something]...

@Josverl
Copy link
Owner

Josverl commented May 18, 2024

Can you add some more detail on the type checker you are using, the python version on your host and the stubs package and version you are using?

@Josverl
Copy link
Owner

Josverl commented May 20, 2024

If I test your sample in Pyright's playground it also shows an error ( but a different one )

Code sample in pyright playground

from typing import TypeVar, Generic, List, Tuple

T = TypeVar('T')

class Foo(Generic[T]):
   ...

# Something does not exist

class Baz(Foo[Something]):
   ...

# Something needs to exist 
Something = List[Tuple]

class Bar(Foo[Something]):
   ...

@Josverl
Copy link
Owner

Josverl commented May 20, 2024

And a quick test does seem not show an obvious error.

  • python 3.11, pylance, micropython-samd-stubs
from typing import Dict, Generic, TypeVar
from typing_extensions import reveal_type

T = TypeVar("T")
from machine import Pin, Signal


class Registry(Generic[T]):
    def __init__(self) -> None:
        self._store: Dict[str, T] = {}

    def set_item(self, k: str, v: T) -> None:
        self._store[k] = v

    def get_item(self, k: str) -> T:
        return self._store[k]


inputs = Registry[Pin]()
inputs.set_item("button", Pin(4, Pin.IN))
inputs.set_item("X", Pin(18, Pin.IN))
inputs.set_item("Y", Pin(19, Pin.IN))

outputs = Registry[Signal]()
outputs.set_item("Player-A", Signal(Pin(5, Pin.OUT)))
outputs.set_item("Player-B", Signal(Pin(6, Pin.OUT)))
outputs.set_item("Game over", Signal(Pin(8, Pin.OUT), invert=True))

b = inputs.get_item("button")
reveal_type(b)  # Expected `Pin`, got `Pin`

x = inputs.get_item("X")
reveal_type(x)  # Expected `Pin`, got `Pin`

game_over = outputs.get_item("Game over")
reveal_type(game_over)  # Expected `Signal`, got `Signal`

@ANogin
Copy link
Author

ANogin commented May 20, 2024

Couple of things, sorry for not being sufficiently clear:

  • I tried to outline a hist of what my actual messy code is doing, and used Something as a placeholder for something complicated I have defined in my actual case.
  • It type checks in mypy (as it should), but fails when actually trying to run in micropython.

@Josverl
Copy link
Owner

Josverl commented May 25, 2024

Then it may be that you need to add the round brackets , that I tend to forget as well
So rather than inputs = Registry[Pin] use inputs = Registry[Pin]()

Or in your sample foo = Foo[Something]()

@ANogin
Copy link
Author

ANogin commented May 29, 2024

@js

Then it may be that you need to add the round brackets , that I tend to forget as well So rather than inputs = Registry[Pin] use inputs = Registry[Pin]()

Or in your sample foo = Foo[Something]()

Note that I do not have any foo - I have a subclass definition. Again, note that this works correctly with mypy and the syntax is correct. The issue is that the hack you use to define Generic causes subclasses to fail in micropython.

@ANogin
Copy link
Author

ANogin commented May 29, 2024

E.g.

% cat test.py

from typing import TypeVar, Generic, Any

T = TypeVar('T')

class Foo(Generic[T]):
   pass

class Bar(Foo[Any]):
   pass

% mypy test.py
Success: no issues found in 1 source file
% python3 test.py
% wget https://github.com/Josverl/micropython-stubs/raw/main/mip/typing.mpy
--2024-05-28 21:12:05-- https://github.com/Josverl/micropython-stubs/raw/main/mip/typing.mpy
Resolving github.com (github.com)... 140.82.116.3
Connecting to github.com (github.com)|140.82.116.3|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://raw.githubusercontent.com/Josverl/micropython-stubs/main/mip/typing.mpy [following]
--2024-05-28 21:12:06-- https://raw.githubusercontent.com/Josverl/micropython-stubs/main/mip/typing.mpy
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.110.133, 185.199.109.133, 185.199.111.133, ...
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.110.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1669 (1.6K) [application/octet-stream]
Saving to: ‘typing.mpy’

typing.mpy 100%[=================================================>] 1.63K --.-KB/s in 0s

2024-05-28 21:12:06 (15.9 MB/s) - ‘typing.mpy’ saved [1669/1669]

% micropython
MicroPython v1.22.2 on 2024-02-20; darwin [GCC 4.2.1] version
Use Ctrl-D to exit, Ctrl-E for paste mode
>>> import test
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "test.py", line 8, in <module>
TypeError: '_AnyCall' object isn't subscriptable

@Josverl
Copy link
Owner

Josverl commented May 30, 2024

Thanks , now I know what to look for .
Likely _AnyCall needs a __getitem__ method

@andrewleech
Copy link

Ah, Generic is subscriptable, but doing so returns AnyCall which isn't. Maybe subscriptable should inherit from AnyCall, but return a new subscriptable so it can be used recursively like this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants