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

Bug or new behavior of generics parameterization? #98852

Closed
Eclips4 opened this issue Oct 29, 2022 · 11 comments
Closed

Bug or new behavior of generics parameterization? #98852

Eclips4 opened this issue Oct 29, 2022 · 11 comments
Assignees
Labels
3.10 only security fixes 3.11 only security fixes 3.12 bugs and security fixes stdlib Python modules in the Lib dir topic-typing type-bug An unexpected behavior, bug, or error type-crash A hard crash of the interpreter, possibly with a core dump

Comments

@Eclips4
Copy link
Member

Eclips4 commented Oct 29, 2022

from typing import TypeVar, Generic, Callable


T = TypeVar("T")
P = TypeVar("P")

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


S = Callable[[Test], P]

def test(arg: S[T]): ...

On python3.11 i can't run this code. Code falls on an attempt to parameterize S
Traceback:

Traceback (most recent call last):
  File "C:\Users\KIRILL-1\PycharmProjects\dataclass_factory\exp.py", line 12, in <module>                  
    def test(arg: S[T]): ...                                                                               
                  ~^^^                                                                                     
  File "C:\Users\KIRILL-1\AppData\Local\Programs\Python\Python311\Lib\typing.py", line 360, in inner       
    return cached(*args, **kwds)
           ^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\KIRILL-1\AppData\Local\Programs\Python\Python311\Lib\typing.py", line 1391, in __getitem__
    new_args = self._determine_new_args(args)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\KIRILL-1\AppData\Local\Programs\Python\Python311\Lib\typing.py", line 1440, in _determine_new_args
    subargs.append(new_arg_by_param[x])
                   ~~~~~~~~~~~~~~~~^^^
KeyError: ~T

How i understand, in 3.11, an attempt is being made to parameterize Test in S, but in 3.10 - not
Is it new behavior or a bug?

@Eclips4 Eclips4 added the type-bug An unexpected behavior, bug, or error label Oct 29, 2022
@AlexWaygood AlexWaygood added stdlib Python modules in the Lib dir 3.11 only security fixes topic-typing 3.12 bugs and security fixes labels Oct 29, 2022
@sobolevn
Copy link
Member

sobolevn commented Oct 29, 2022

This is how it used to work in 3.10: https://github.com/python/cpython/blame/85f88f63d96b07208c98a725391af7cb710fe06b/Lib/typing.py#L1060-L1081

>>> from typing import TypeVar, Generic, Callable
>>> 
>>> 
>>> T = TypeVar("T")
>>> P = TypeVar("P")
>>> 
>>> class Test(Generic[T]): ...
... 
>>> 
>>> S = Callable[[Test], P]
>>> 
>>> def test(arg: S[T]): ...
... 
>>> test.__annotations__
{'arg': typing.Callable[[__main__.Test], ~T]}

Clearly looks like a bug to me. I am investigating what is going on :)

@Eclips4
Copy link
Member Author

Eclips4 commented Oct 29, 2022

I know about how it works in 3.10 =)
My simple solution:

...
            if substfunc:
                new_arg = substfunc(new_arg_by_param[old_arg])
            elif not isinstance(old_arg, (_GenericAlias, GenericAlias, types.UnionType)):
                new_arg = old_arg
            else:
 ...

With that, code above works fine.

>>> test.__annotations__
{'arg': typing.Callable[[__main__.Test], ~T]}

What you think about this?

@sobolevn
Copy link
Member

Yes, looks like it might work. Previously the same logic was guarded by this check: https://github.com/python/cpython/blame/85f88f63d96b07208c98a725391af7cb710fe06b/Lib/typing.py#L1071

Would you like to send a PR with this proposal? :)

@gvanrossum
Copy link
Member

Could we first find which PR introduced the bug?

Also, @JelleZijlstra ^^

@Eclips4
Copy link
Member Author

Eclips4 commented Oct 30, 2022

It is bpo-44796 (#31143)

@serhiy-storchaka
Copy link
Member

Indeed, there is a bug in _collect_parameters which causes to skipping generic types.

Yet one interesting example:

>>> class Test(Generic[T]): ...
... 
>>> Test.__parameters__
(~T,)
>>> class Test2(Test): ...
... 
>>> Test2.__parameters__
()

But after fixing this code I got other test failure. And it seems that we have contradictory tests. Test in test_complex_subclasses expects that we can successfully create a class inheriting from generic base and Generic[].

# see gh-94607: this fails in that bug
class Sub(Base, Generic[T]):
...

But tests in test_generic_errors and test_generic_inheritance expect that Generic[] should include all typing variables, otherwise the class creation should fail.

with self.assertRaises(TypeError):
class MyGeneric(List[T], Generic[S]): ...

with self.assertRaises(TypeError):
class Point3D(Point2DGeneric[T], Generic[KT]):
c: KT

The only difference between these examples is that in the former case the generic base is a generic class, and in the latter cases it is a generic alias. I do not think that there should be difference between them in this context. We should either require Generic[] containing all typing variables or allow it only containing some of them.

See also #94607.

@JelleZijlstra
Copy link
Member

I don't see a contradiction. In class Sub(Base, Generic[T]):, Base should be equivalent to Base[Any], which is the general behavior for generics missing a subscription.

@serhiy-storchaka
Copy link
Member

It makes sense if you look at it this way. The problem is with distinguishing free and bound variables. Both are represented in __parameters__.

serhiy-storchaka added a commit to serhiy-storchaka/cpython that referenced this issue Oct 31, 2022
Fix subscription of type aliases containing bare generic types or types
like TypeVar: for example tuple[A, T][int] and tuple[TypeVar, T][int],
where A is a generic type, and T is a type variable.
@serhiy-storchaka serhiy-storchaka added the type-crash A hard crash of the interpreter, possibly with a core dump label Oct 31, 2022
@serhiy-storchaka
Copy link
Member

The issue is more serious and is not completely new.

In the following code:

from typing import *
T = TypeVar("T")
T2 = TypeVar("T2")
class A(Generic[T2]): ...

Tuple[A, T][int]
tuple[A, T][int]
Tuple[TypeVar, T][int]
tuple[TypeVar, T][int]

3.11 raises exception for the 1st and 3rd examples, produces incorrect result for the 2nd example, and crashes on the last example.

3.10 raises exception for the 2nd examples.

@serhiy-storchaka serhiy-storchaka added the 3.10 only security fixes label Nov 1, 2022
serhiy-storchaka added a commit that referenced this issue Nov 1, 2022
Fix subscription of type aliases containing bare generic types or types
like TypeVar: for example tuple[A, T][int] and tuple[TypeVar, T][int],
where A is a generic type, and T is a type variable.
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Nov 1, 2022
Fix subscription of type aliases containing bare generic types or types
like TypeVar: for example tuple[A, T][int] and tuple[TypeVar, T][int],
where A is a generic type, and T is a type variable.
(cherry picked from commit 0e15c31)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
miss-islington added a commit that referenced this issue Nov 1, 2022
Fix subscription of type aliases containing bare generic types or types
like TypeVar: for example tuple[A, T][int] and tuple[TypeVar, T][int],
where A is a generic type, and T is a type variable.
(cherry picked from commit 0e15c31)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
serhiy-storchaka added a commit to serhiy-storchaka/cpython that referenced this issue Nov 1, 2022
…es (pythonGH-98920)

Fix subscription of types.GenericAlias instances containing bare generic types:
for example tuple[A, T][int], where A is a generic type, and T is a type
variable.
serhiy-storchaka added a commit that referenced this issue Nov 1, 2022
…-98920) (GH-98969)

Fix subscription of types.GenericAlias instances containing bare generic types:
for example tuple[A, T][int], where A is a generic type, and T is a type
variable.
@hauntsaninja
Copy link
Contributor

Is there anything left to do here?

@serhiy-storchaka
Copy link
Member

It is completed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.10 only security fixes 3.11 only security fixes 3.12 bugs and security fixes stdlib Python modules in the Lib dir topic-typing type-bug An unexpected behavior, bug, or error type-crash A hard crash of the interpreter, possibly with a core dump
Projects
None yet
Development

No branches or pull requests

7 participants