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

Meta issue: better getattr support #11142

Open
2 tasks done
sobolevn opened this issue Sep 18, 2021 · 2 comments
Open
2 tasks done

Meta issue: better getattr support #11142

sobolevn opened this issue Sep 18, 2021 · 2 comments
Labels
feature meta Issues tracking a broad area of work

Comments

@sobolevn
Copy link
Member

sobolevn commented Sep 18, 2021

Feature

One thing that bothered me for a long time is getattr and its very broad return type even for simplest cases:

from typing import Literal

class Some:
    def __init__(self) -> None:
        self.a = 1

s = Some()

reveal_type(s.a)  # Revealed type is "builtins.int"
reveal_type(getattr(s, 'a'))  # Revealed type is "Any"
reveal_type(getattr(s, 'b'))  # Revealed type is "Any"
reveal_type(getattr(s, 'a', None))  # Revealed type is "Union[Any, None]"

a: Literal['a']
reveal_type(getattr(s, a))  # Revealed type is "Any"

ab: Literal['a', 'b']
reveal_type(getattr(s, ab))  # Revealed type is "Any"

So, I propose to add better support for it to mypy.

The same example, after the support is added:

from typing import Literal

class Some:
    def __init__(self) -> None:
        self.a = 1

s = Some()

reveal_type(s.a)  # Revealed type is "builtins.int"
reveal_type(getattr(s, 'a'))  # Revealed type is "builtins.int"
reveal_type(getattr(s, 'b'))  # error: "Some" has no attribute "b"
reveal_type(getattr(s, 'a', None))  # Revealed type is "builtins.int"

a: Literal['a']
reveal_type(getattr(s, a))  # Revealed type is "builtins.int"

ab: Literal['a', 'b']
reveal_type(getattr(s, ab))  # error: "Some" has no attribute "b"
reveal_type(getattr(s, ab, None))  # Revealed type is "builtins.int | None"

I have even written a custom plugin for it at some point.

Why?

This pattern is quite popular among Python developers. geattr(obj, 'string_key', None) is used for compatibility, optional dependencies, etc. That's something you can find in almost any project.

Technical details

I am going to utilize existing checkmember logic.
I even plan to support attribute plugins, where possible, because a.x and getattr(a, 'x') are semantically identical.
For now only str literals and Literal str types are going to be supported.

Related issues and problems

I am calling this issue "meta", because there are several very related problems I want to mention / solve here:

Next steps

I will start with a PR with a code prototype in a couple of days.

@bbatliner
Copy link

Also interested in this, for @property attributes: https://mypy-play.net/?mypy=0.910&python=3.10&gist=b6cb4542f9cd1888be942607a2c8ce1f

from typing import Literal

class Some:
    @property
    def foo(self) -> str: ...


s = Some()

foo_attr = s.foo
reveal_type(foo_attr) # builtins.str

foo_getattr = getattr(s, "foo")
reveal_type(foo_getattr) # Any

foo: Literal["foo"] = "foo"
foo_literal_getattr = getattr(s, foo)
reveal_type(foo_literal_getattr) # Any

@97littleleaf11 97littleleaf11 added the meta Issues tracking a broad area of work label Nov 12, 2021
@yarikoptic
Copy link
Contributor

I would appreciate anyone who would address this issue and thus avoid uglification of pretty Python codes needed to please mypy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature meta Issues tracking a broad area of work
Projects
None yet
Development

No branches or pull requests

4 participants