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

subscriptable typing #172

Closed
asmodehn opened this issue Dec 2, 2020 · 2 comments · Fixed by #173
Closed

subscriptable typing #172

asmodehn opened this issue Dec 2, 2020 · 2 comments · Fixed by #173

Comments

@asmodehn
Copy link

asmodehn commented Dec 2, 2020

Long story short

Subscripting the FrozenList type currently breaks.
I'm guessing a user would expect the FrozenList works the same as standard List as far as typing is concerned...

Example script:

from dataclasses import dataclass
from frozenlist import FrozenList

@dataclass
class MyClass:

    lst: FrozenList[int]


dc = MyClass(lst=FrozenList([1,2,3]))
print(dc)

Expected behaviour

I would expect this to work as with List:

from dataclasses import dataclass
from typing import List

@dataclass
class MyClass:

    lst: List[int]


dc = MyClass(lst=list([1,2,3]))
print(dc)
MyClass(lst=[1, 2, 3])

Actual behaviour

Traceback (most recent call last):
  File "fltest.py", line 5, in <module>
    class MyClass:
  File "fltest.py", line 7, in MyClass
    lst: FrozenList[int]
TypeError: 'type' object is not subscriptable

Your environment

$ python --version
Python 3.8.5
$ pip list | grep frozenlist
frozenlist                    1.1.1     
@asmodehn
Copy link
Author

asmodehn commented Dec 2, 2020

While I understand the details and reason of this behaviour, it caught me off-guard, so I thought I mention it.

Maybe the constructor and the type could be different (like list() / List) ?
Maybe also there could be a close match between List/FrozenList and the Set/FrozenSet with set()/frozenset() usage...

@mjpieters
Copy link
Collaborator

mjpieters commented Dec 2, 2020

FrozenList works exactly like list here. You are confusing list, the concrete type, with typing.List, the type annotation generic.

The generic types from the typing module are not the same thing as the list or set concrete types, at least not unless you use Python 3.9, and use from __future__ import annotations.

You can see the difference in your own example:

class MyClass:

    lst: List[int]
    #    ^^^^ Capital L

dc = MyClass(lst=list([1,2,3]))
#                ^^^^ lowercase l

FrozenSet is not a generic type, it is a concrete class. If you tried to do the same with list (lst: list[int]) you'd get the same exception!

And while I understand that the naming convention for the generic typing versions is a source of confusion (using CamelCasing names where the built-in types use lowercase), FrozenSet should not be using lowercase naming just to avoid confusion here. With PEP 585 the core language is moving towards the concrete types supporting being used as generic type hints, and the confusion is only temporary.

The correct method is to either use from __future__ import annotations, or put the type in quotes as a forward reference. Both work.

With from __future__ import annotations:

from __future__ import annotations
from dataclasses import dataclass
from frozenlist import FrozenList

class MyClass:

    lst: FrozenList[int]

 
dc = MyClass(lst=FrozenList([1,2,3]))
print(dc)

or with a forward reference:

from __future__ import annotations
from dataclasses import dataclass
from frozenlist import FrozenList

class MyClass:

    lst: "FrozenList[int]"

 
dc = MyClass(lst=FrozenList([1,2,3]))
print(dc)

Both forms are recognized and supported by mypy as well.

That said, with Python 3.9 now implementing PEP 585, we could look into adding __class_getitem__ = classmethod(types.GenericAlias) to the class.

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

Successfully merging a pull request may close this issue.

2 participants