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

Change in behavior regarding __getattr__ and @no_type_check #13319

Closed
PhilReinhold opened this issue Aug 3, 2022 · 3 comments
Closed

Change in behavior regarding __getattr__ and @no_type_check #13319

PhilReinhold opened this issue Aug 3, 2022 · 3 comments
Labels
bug mypy got something wrong

Comments

@PhilReinhold
Copy link

Bug Report

In #6251 it was recommended to use @no_type_check on __getattr__ if you wanted errors to still be raised for missing attributes. However this appears to no longer work since v0.930

To Reproduce

# test.py
from typing import no_type_check

# Using no_type_check decorator on __getattr__
class Base:
    @no_type_check
    def __getattr__(self, name):
        pass

class Model(Base):
    age: int


m = Model()
reveal_type(m.foo)

# Without no_type_check decorator on __getattr__
class Base2:
    def __getattr__(self, name):
        pass

class Model2(Base2):
    age: int


m2 = Model2()
reveal_type(m2.foo)

Expected Behavior

Following the discussion on #6251, I expected that adding a @no_type_check decorator would restore errors on missing attributes. I see this behavior on several versions <= 0.921

When running mypy test.py on version 0.921 I get the expected output

test.py:13: error: "Model" has no attribute "foo"
test.py:13: note: Revealed type is "Any"
test.py:24: note: Revealed type is "Any"

Actual Behavior

When running on versions 0.930-0.971 I do not see the (expected) first error indicating the missing attribute,

test.py:13: note: Revealed type is "Any"
test.py:24: note: Revealed type is "Any"

Your Environment

  • Mypy version used: Multiple, error appears on at least v0.930 v0.931 v0.941 v0.971 and not before
  • Mypy command-line flags: None
  • Mypy configuration options from mypy.ini (and other config files): None
  • Python version used: 3.10.4
  • Operating system and version: OSX 11.6.7
@PhilReinhold PhilReinhold added the bug mypy got something wrong label Aug 3, 2022
@hauntsaninja
Copy link
Collaborator

A workaround is to use:

if not TYPE_CHECKING:
    def __getattr__(self): ...

I'm not sure that no_type_check should work for this, let's see what others say. Would also be interesting to bisect and see what changed — will do this later with mypy_primer.

@JelleZijlstra
Copy link
Member

I don't think @no_type_check should work for this use case. PEP 484 says a function with that decorator should be treated as having no annotations, not that a type checker should ignore its existence.

@hauntsaninja
Copy link
Collaborator

Okay yeah, I'm going to close this as won't fix then.

For the record, mypy_primer -p ~/dev/mypy_primer/nogetattr.py --bisect --new v0.930 --old v0.920 --debug bisects it to:

4e34fecfe59d83aa3b9e04cbde1834aff7a47e5d is the first bad commit
commit 4e34fecfe59d83aa3b9e04cbde1834aff7a47e5d
Author: Nikita Sobolev <mail@sobolevn.me>
Date:   Fri Dec 3 16:20:30 2021 +0300

    Now `TypeInfo.get_method` also returns `Decorator` nodes (#11150)
    
    Support decorators properly in additional contexts.
    
    Closes #10409

This makes sense, previously mypy struggled to understand decorators on __getattr__

dae added a commit to ankitects/anki that referenced this issue Nov 4, 2022
thom311 added a commit to thom311/cluster-deployment-automation that referenced this issue Apr 19, 2024
ExtendedLogger mainly exists to make typing happy. It's a
subclass of logging.Logger (so typing understands it has all
the methods of that class). Additionally, it has a few extended
methods that we add.

This is hacked together by overwriting __getattribute__() and delegating
most calls to the wrapped logger.

This works, except that defining __getattribute__() in a class makes
mypy think that the object supports any kind of object. So
`logger.bogus()` is not flagged as an error and misses a linter error.

Work around that.

See-also: python/mypy#13319 (comment)
thom311 added a commit to thom311/cluster-deployment-automation that referenced this issue Apr 23, 2024
ExtendedLogger mainly exists to make typing happy. It's a
subclass of logging.Logger (so typing understands it has all
the methods of that class). Additionally, it has a few extended
methods that we add.

This is hacked together by overwriting __getattribute__() and delegating
most calls to the wrapped logger.

This works, except that defining __getattribute__() in a class makes
mypy think that the object supports any kind of object. So
`logger.bogus()` is not flagged as an error and misses a linter error.

Work around that.

See-also: python/mypy#13319 (comment)
thom311 added a commit to thom311/cluster-deployment-automation that referenced this issue Apr 24, 2024
ExtendedLogger mainly exists to make typing happy. It's a
subclass of logging.Logger (so typing understands it has all
the methods of that class). Additionally, it has a few extended
methods that we add.

This is hacked together by overwriting __getattribute__() and delegating
most calls to the wrapped logger.

This works, except that defining __getattribute__() in a class makes
mypy think that the object supports any kind of object. So
`logger.bogus()` is not flagged as an error and misses a linter error.

Work around that.

See-also: python/mypy#13319 (comment)
thom311 added a commit to thom311/cluster-deployment-automation that referenced this issue Apr 24, 2024
ExtendedLogger mainly exists to make typing happy. It's a
subclass of logging.Logger (so typing understands it has all
the methods of that class). Additionally, it has a few extended
methods that we add.

This is hacked together by overwriting __getattribute__() and delegating
most calls to the wrapped logger.

This works, except that defining __getattribute__() in a class makes
mypy think that the object supports any kind of object. So
`logger.bogus()` is not flagged as an error and misses a linter error.

Work around that.

See-also: python/mypy#13319 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong
Projects
None yet
Development

No branches or pull requests

3 participants