-
-
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
Dynamic base classes are not handled #2477
Comments
Simpler repro (without the typeshed/sqlalchemy stuff): def func(): pass
Base = func()
class C(Base): pass # errors on this line Errors (
(Update: used |
The reason this doesn't work is that the type of
|
Should we close this then or update the docs somewhere? Maybe the error message could also be better. |
I think that we should at least see if we can make the error message better. |
improved error message would be helpful. I did discover Is there a github issue I should be tracking for the semantic analysis improvement, or just watch the release notes? |
Let's use this issue to track both the error message and the underlying semantic analysis issue for now. |
I seem to have hit a very similar bug that's a bit more convoluted (involving file1.py: from typing import Any
Base: Any file2.py: from typing import Optional
from .file1 import Base
class SubClass(Base):
pass
def optional_return_func() -> Optional[SubClass]:
return SubClass()
def should_fail() -> int:
test_var = optional_return_func()
reveal_type(test_var)
if not test_var:
return 1
return 'not an int' When I run mypy on this, the result is The expected result should be Hope that makes sense, let me know if any more information would be helpful. |
@JukkaL sorry for the ping, but I just wanted to check and see if you think the issue I described above is similar enough that leaving it as part of this one is good, or if I should open a new, separate issue for it. |
@Deimos The |
Oh, yes, it does seem to be fixed using current github master. That's great, thanks (and sorry for bothering you about something that was already fixed). |
But if I add a import typing as t
base: t.Any = make_base()
class A(base): pass
def make_list() -> t.Sequence[A]:
return A() does not yield an error which makes anything using this class ( |
But what would you expect? Except for a few trivial edge cases, when the base class is computed dynamically, mypy is unable to understand the structure of your class. That's why it gives an error when you use a dynamic base class -- if you shut it up using [I changed the issue topic because it has nothing to do with sqlalchemy.] |
I understand that mypy can't determine the class, however it might be nice to give mypy a typehint of some sort. Capturing all the possibilities of the base are probably still not possible, however it would be possible to check somethings. I suspected that doing class A: pass
Base = A
Base2: Type[A] = A
class B(Base): pass
class C(A): pass
class D(Base2): pass only gives an error for class |
If you have a base class that you think would make for a suitable static substitute for the dynamic base, you could try this (I am using base = object
def make_base(): pass
base = make_base() # type: ignore
class C(base):
def foo(self): pass
c = C()
c.foo()
c.bar() # Error, proving static type checking honors the `base = object`. You might also do something with from typing import TYPE_CHECKING
def make_base(): pass
if TYPE_CHECKING:
base = object
else:
base = make_base()
class C(base):
def foo(self): pass
c = C()
c.foo()
c.bar() # Error again |
It is important to make a distinction between type aliases and variables with type B = A # makes a type alias
C: Type[A] # creates a variable The point is that for aliases we know the value statically since type alias cannot be re-bound, so that it can be used in the type context (like an annotation or a base class). In contrast we don't know the value of a variable statically. Consider this example: class C1: ...
class C2: ...
A: Type[Union[C1, C2]]
class B(A): ... it is not clear what would this mean, since one cannot subclass a union. I like the idea of using |
I was looking for an existing GitHub issue for a problem I was having, and I think this might be the one. I've been trying to define types for Django where the stub files use generic types for from typing import Any, Generic, TypeVar
ModelType = TypeVar('ModelType')
class Foo:
pass
class QuerySet(Generic[ModelType]):
pass
class Manager(Generic[ModelType]):
@classmethod
def from_queryset(cls, queryset_class: QuerySet[Any]) -> 'Manager[ModelType]':
...
class Bar(Manager.from_queryset(QuerySet)):
pass That's enough code to reproduce the problem. So far, my workaround is to define |
@w0rp: *anything* with an expression that's not a simple class reference
will cause that error. Do you have a specific question?
|
No sorry, I just thought I'd mention my use case here in case any other Django users notice the same issue I did. |
Any updates on this issue? |
This also does not work: def model(base: Any) -> Callable[...,T]:
class ModelProxy(base): # fails
# .... While this does: def model(base: Any) -> Callable[...,T]:
wut: Any = base
class ModelProxy(wut): # works
# .... |
@germaniumhq Created #5865 to track that particular issue. |
Currently mypy supports writing plugins, including using I don't think we need to keep this open. One should either use plugins for supported frameworks, or the |
* ignore missing imports https://mypy.readthedocs.io/en/latest/running_mypy.html#ignore-missing-imports * mypy doesn't handle dynamic base classes see workaround in python/mypy#2477 * rework language_code handling to resolve TypeError mypy was flagging this logic due to the fact that current_locale is Optional[str] * TimedRotatingFileHandler delay arg should be bool
* ignore missing imports https://mypy.readthedocs.io/en/latest/running_mypy.html#ignore-missing-imports * mypy doesn't handle dynamic base classes see workaround in python/mypy#2477 * rework language_code handling to resolve TypeError mypy was flagging this logic due to the fact that current_locale is Optional[str] * TimedRotatingFileHandler delay arg should be bool
* ignore missing imports https://mypy.readthedocs.io/en/latest/running_mypy.html#ignore-missing-imports * mypy doesn't handle dynamic base classes see workaround in python/mypy#2477 * rework language_code handling to resolve TypeError mypy was flagging this logic due to the fact that current_locale is Optional[str] * TimedRotatingFileHandler delay arg should be bool
* ignore missing imports https://mypy.readthedocs.io/en/latest/running_mypy.html#ignore-missing-imports * mypy doesn't handle dynamic base classes see workaround in python/mypy#2477 * rework language_code handling to resolve TypeError mypy was flagging this logic due to the fact that current_locale is Optional[str] * TimedRotatingFileHandler delay arg should be bool
If mypy can't parse it, it is unhelpful to say it's a type error |
In case others are interested in the Django type checking issues I mentioned here and they find this thread, I'd like to point to this plugin for mypy: https://github.com/mkurnikov/django-stubs I haven't started using it yet, but it should solve a lot of Django type checking issues. |
Edit: Please disregard, I was able to use I can't get this to work: if TYPE_CHECKING:
test_mixin_class = TestCase
else:
test_mixin_class = object
class CanCreateTestMixin(test_mixin_class): I get
on the
|
@dms-cat Note the workaround from Guido above does not work when wrapped in a function in general: def foo():
from typing import TYPE_CHECKING
def make_base(): pass
if TYPE_CHECKING:
base = object
else:
base = make_base()
class C(base): # !! Variable "base" is not valid as a type
def foo(self): pass
c = C()
c.foo()
c.bar() # Error again Simpler: if True: # works
base = object
class C(base):
pass
def f():
base = object
class C(base): # Variable "base" is not valid as a type / Invalid base class "base"
pass (0.750+dev.a94e649de794ecd169a5bcc42274e5205ffbb1fb) |
This helps accomodate the growing Bot class, since it's easily divided into several logical components anyway. Each component is an ABC (Abstract Base Class) that functions as a mixin and thus can access all the other attributes of the Bot class. Bot subclasses all the mixins to unify them. Telethon uses the same approach to divide TelegramClient into many mixins that each contain different categories of API methods. An adaptive MixinBase reference that is defined as abc.ABC during runtime and Bot during type checking is used as the subclass for each mixin to work around python/mypy#5837. Furthermore, the MixinBase reference itself has its type specified as Any to work around python/mypy#2477. The only change for outsiders should be the Bot class being moved to the `core` module. Signed-off-by: Danny Lin <[email protected]>
For anyone who comes across this via the original report, a simple way to address sqlalchemy/mypy issues is to also import DeclarativeMeta.
|
Thanks @micahjsmith, just ran into this for the first time (on a 2-yo project, no clue why it was never reporting before). |
Mypy struggles with dynamic type creation. See: python/mypy#2477
Mypy struggles with dynamic type creation. See: python/mypy#2477
Mypy struggles with dynamic type creation. See: python/mypy#2477
Mypy struggles with dynamic type creation. See: python/mypy#2477
Mypy struggles with dynamic type creation. See: python/mypy#2477
Mypy struggles with dynamic type creation. See: python/mypy#2477
repro:
Output is something like
when it should be clean
The text was updated successfully, but these errors were encountered: