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

Metaclass confusion with properties #7945

Open
ronaldoussoren opened this issue Nov 13, 2019 · 2 comments
Open

Metaclass confusion with properties #7945

ronaldoussoren opened this issue Nov 13, 2019 · 2 comments
Labels
bug mypy got something wrong priority-2-low topic-descriptors Properties, class vs. instance attributes topic-metaclasses

Comments

@ronaldoussoren
Copy link

I'm using python 3.8 and mypy 0.740. The script below prints "21" and "42" when run, but fails to type check with mypy:

class meta (type):
    pass

class value (metaclass=meta):
    pass


class submeta (meta):
    @property
    def attr(cls) -> int:
        return 42

class subvalue (metaclass=submeta):
    @property
    def attr(self) -> int:
        return 21

o = subvalue()
a = o.attr
print(a)
a = subvalue.attr
print(a)

The mypy diagnostics say:

meta-test.py:24: error: Incompatible types in assignment (expression has type "Callable[[subvalue], int]", variable has type "int")
Found 1 error in 1 file (checked 1 source file)

The error is incorrect, "subvalue.attr" has type "int", as it resolves to the property defined on the metaclass, not to the property defined on the class.

This is fairly esoteric as written, but is the structure used by PyObjC to expose Objective-C classes. PyObjC uses metaclasses to expose class methods because ObjC classes can have instance- and class-methods with the same name.

@msullivan msullivan added bug mypy got something wrong priority-2-low labels Nov 13, 2019
@msullivan
Copy link
Collaborator

My original inclination was to despair about this issue, since a lot of stuff about class level variables are a total mess. (We don't have a clear delineation between instance and class variables in general, I think.)

On thinking about it some more, it might not be too bad to fix with some adjustments to analyze_instance_member_access.

This is all pretty subtle, though. I had to think for a while about why this works (because properties are data descriptors, even when they aren't actually writable) even though it does not work for classes.

(I'm guessing PyObjC has custom classmethod decorators that are data descriptors or some such?)

@ronaldoussoren
Copy link
Author

(I'm guessing PyObjC has custom classmethod decorators that are data descriptors or some such?)

That's more or less correct. PyObjC has custom method objects that are data descriptors, not just for class methods.

PyObjC uses a similar class structure as the ObjC runtime, meaning there are two parallel class hierarchies: Every Objective-C class has its own metaclass, and that metaclass subclasses the metaclass of the superclass.

That is:

  object                         -metaclass-> type
      objc.objc_object    -metaclass->     objc.objc_class
         NSObject            -metaclass->            meta NSObject
             NSArray          -metaclass->               meta NSArray

NSArray subclasses NSObject, and NSArray has a metaclass "meta NSArray" that subclasses "meta NSObject".

To make life interesting this structure is generated in C code, and the metaclass is not directly referenced from a Python namespace (the only reference is the class attribute of the regular class), but that's not really relevant for this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong priority-2-low topic-descriptors Properties, class vs. instance attributes topic-metaclasses
Projects
None yet
Development

No branches or pull requests

3 participants