-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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
Function argument rejected as a base class #5865
Comments
I am having this issue as well. I am not sure why an object of type In my example, I use from typing import Type, TypeVar
T = TypeVar("T")
def h(tp: Type[T]) -> Type[T]:
class Foo(tp):
pass
return Foo I would expect |
@Victor-Savu Your example also seems like a bug to me, but it's not quite the same as this one. Feel free to report a new issue. |
Hi @JelleZijlstra! Thank you for the explanation! I went back to the documentation and I am convinced you and @JukkaL are right that the code in the first comment should produce not errors in mypy. I found this stack overflow response very helpful as well. I would still like to make the point that not any import pytest
def subobject(an_object: object) -> type:
class A(an_object): pass # error: Invalid type "an_object", Invalid base class
return A
def test_subobject() -> None:
assert isinstance(Exception, type)
assert isinstance(subobject(Exception), type)
assert issubclass(subobject(Exception), Exception)
assert isinstance(1, object)
with pytest.raises(TypeError):
subobject(1) The On the other hand, if we fix the problem, the runtime error will be detected ahead of time by mypy as well. def subclass(a_type: type) -> type:
class A(a_type): pass # error: Invalid type "a_type", Invalid base class
return A
def test_subclass() -> None:
assert isinstance(Exception, type)
assert isinstance(subclass(Exception), type)
assert issubclass(subclass(Exception), Exception)
assert isinstance(1, object)
with pytest.raises(TypeError):
subclass(1) # error: Argument 1 to "subclass" has incompatible type "int"; expected "type" The code above should only have one type error (the last line) as the error on the second line is caused by the current issue. |
I'll add here another case, which I believe is related, even though I'm not sure to understand all the whys and wherefores. from typing import Type
class A: pass
a = A
a2: Type[A] = A # Ok
class B(a): pass # Ok
class B2(a2): pass # Errors: not valid as a type; Invalid base class
def f(cv: Type[A]):
class B3(cv): pass # Errors: not valid as a type; Invalid base class
return B3
f(a) # Ok
f(a2) # Ok I would expect that if I promise the type checker that my variable is of the class type, it would accept it as base class. Is there some subtleties I don't get? |
@frankie567 That's why mypy complains about using |
@JukkaL Thank you for this explanation, I understand now 🙂Sorry for the noise on the ticket! |
This does not only affect bases from args, but also the local scope / function body. I've looked a bit into why the following is not working, and it appears to be due to the fact that Lines 1472 to 1477 in a94e649
There is some code in Lines 897 to 917 in a94e649
import typing
# works
base = object
class C(base):
pass
def f1() -> None:
base = object
class C(base): # Variable "base" is not valid as a type / Invalid base class "base"
pass
def f2(base: typing.Any) -> None:
class C(base): # Variable "base" is not valid as a type / Invalid base class "base"
pass
# works
def f3(base: typing.Any) -> None:
base2: typing.Any = base
class C(base2):
pass mypy 0.750+dev.a94e649de794ecd169a5bcc42274e5205ffbb1fb |
Any update on this issue? It's quite a big one IMO. |
One more case, very similar to @blueyed's, but in the context of a classmethod -- and I also found a third incorrect error message (name-defined) that I don't think was mentioned yet in this thread: even though from typing import TypeVar
C = TypeVar("C", bound="MyClass")
class MyClass:
class InnerClass:
pass
@classmethod
def dynamic_subclass(cls: type[C]) -> type[C]:
# Mypy emits two errors on the following line:
# error: Variable "cls" is not valid as a type [valid-type]
# error: Invalid base class "cls" [misc]
class MySubClass(cls):
# Mypy emits one error on the following line:
# Name "cls.InnerClass" is not defined [name-defined]
class MySubInnerClass(cls.InnerClass):
pass
pass
return MySubClass
# to show that everything works as expected at runtime:
SubClass = MyClass.dynamic_subclass()
my_subclass_instance = SubClass() |
Fixes #5865 Looks quite easy and safe, unless I am missing something. Most changes in the diff are just moving stuff around. Previously we only applied argument types before type checking, but it looks like we can totally do this in semantic analyzer. I also enable variable annotated as `type` (or equivalently `Type[Any]`), this use case was mentioned in the comments. This PR also accidentally fixes two additional bugs, one related to type variables with values vs walrus operator, another one for type variables with values vs self types. I include test cases for those as well.
Class
D
in the example below is rejected, even though classE
, which looks very similar, is accepted:The above example shouldn't generate errors.
(Originally reported by @germaniumhq in #2477 (comment).)
The text was updated successfully, but these errors were encountered: