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

Cannot determine type of attribute class in base class #4956

Closed
OJFord opened this issue Apr 23, 2018 · 9 comments
Closed

Cannot determine type of attribute class in base class #4956

OJFord opened this issue Apr 23, 2018 · 9 comments

Comments

@OJFord
Copy link

OJFord commented Apr 23, 2018

class Foo:
    class Thing(object): # type: ignore
        pass

class Thing:
    pass

class Bar:
    Thing = Thing

class FooBar(Foo, Bar):
    pass

scratch.py:11: error: Cannot determine type of 'Thing' in base class 'Foo'

Adding object doesn't help, nor #type: ignore as shown above.

However, Bar.Thing does type check; so there seems only to be a problem typing literal class-valued class attributes, when inherited. (Aside: many thanks if you can tell me the proper terminology for these Bar.Thing and Foo.Things - it's not easy to search for!)

Note Foo.Thing is not a problem until the inheriting class is introduced on L11.

@ilevkivskyi
Copy link
Member

This is not a bug but how mypy works. The error message can be made clearer however. The point is that inner classes are treated as types, not variables (i.e. as statically known), they cannot be re-assigned, but can be used in type annotations. On the contrary, an instance variable with type Type[Thing] (this is what is created by the assignment in your example) can be re-assigned, but can't be used in type annotations.

@OJFord
Copy link
Author

OJFord commented Apr 24, 2018

@ilevkivskyi Many thanks for your reply. I think it might help to explain why this is causing me a problem.

My Foo and Bar are really Django model 'mixins' (in quotes because they must inherit from Django's Model in order to provide extra ORM fields) and the inner class Thing is really Django's Meta, (not an actual metaclass) necessary to specify Meta.abstract = True in order not to create extra database tables for the 'mixin' itself.

The problem then is that, with Meta as inner class, mypy cannot determine its type; with Meta as instance variable, it conflicts with multiple 'mixins' (inheritance):

class IdMeta:
    abstract = True
    ordering = ('id,')

class Id(Model):
    Meta = IdMeta
    id = IntegerField(...)

class CreatedMeta:
    abstract = True

class Created(Model):
    Meta = CreatedMeta
    created = DateTimeField(...)

class RealModel(Model, Id, Created):
    ...

error: Definition of "Meta" in base class "Created" is incompatible with definition in base class "Id"

@ilevkivskyi
Copy link
Member

Support of Django is a separate project and will require writing a plugin, see #3539 (there are too much magic to typecheck normally).

Also it looks like we already have a very similar issue to yours #3855 with a similar motivation. So I think we can close this, feel free to re-open if you think this is a separate issue.

@OJFord
Copy link
Author

OJFord commented Apr 24, 2018

Thanks; apologies - it's certainly a duplicate.

Is there any way to workaround this though? At this point I would be quite happy with # type: ignore, or similar, but that doesn't work. Or is it simply not possible to use mypy in a Django project with this pattern?

@ilevkivskyi
Copy link
Member

I think # type: ignore at the point of use (i.e. at line 11 in your example) should work.

@OJFord
Copy link
Author

OJFord commented Apr 24, 2018

Nope, it complains before then, i.e. my RealModel is in a different file, but running mypy on just the file with the 'mixins' fails with:

error: Cannot determine type of 'Meta' in base class 'Foo'

# type: ignore on the Meta inner class has no impact; nor does putting it on the outer 'mixin' model Foo.

@ilevkivskyi
Copy link
Member

Can you put # type: ignore on the line where it is reported? (This is how ignores are supposed to work, they just silence errors.)

@OJFord
Copy link
Author

OJFord commented Apr 24, 2018

Oh no - sorry - I'm not sure how I screwed that up, that does work. Many thanks!

@sacha-senchuk
Copy link

FYI, similar issues #2871 #3855

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

No branches or pull requests

3 participants