Skip to content

hasattr(v, "__iter__") does not narrow int | Iterable[int] #5582

Open

Description

@wRAR

Describe the Bug

Looks related to #684, I haven't checked what was done there but "In the negative case we do nothing, because the attribute could be present but set to None/empty string, etc." may be related, as with hasattr() we actually know that the attribute is not present.

from collections.abc import Iterable


def f(v: int | Iterable[int]) -> int:
    if hasattr(v, "__iter__"):
        return 0
    return v  # bad-return: Iterable[int] | int


def g(v: int | Iterable[int]) -> Iterable[int]:
    if hasattr(v, "__iter__"):
        return v  # bad-return: Iterable[int] | int
    return [v]  # bad-return: list[Iterable[int] | int]

Sandbox Link

https://pyrefly.org/sandbox/?project=v2.pZVNDoMgFISvQuICF02fa0_Rve2CtAa7kUYo5-88hFjSSozu-J_AfG8oRuZKiqTsYC8isrVv4QZDmiY7dG_BfzQWRgdllXNT7U_BYw5S9vgPd018_0jhIqZLYtnIHlkIffU6H4Ij3hJv7DffFC7ulm1-4arEZR4Q0Se4Ayr1G-ENg87XsUJu92Jw7mVborTdTJr6kR7mbinbQYLrSHn1DF9CfpwwoRgsjj1CtZyhliWmJSONFUVMPw

(Only applicable for extension issues) IDE Information

No response

Activity

  1. added
    needs-discussionAn issue where it's not clear whether there is a bug or we are behaving as expected.
    narrowingIssues with narrowing - root cause is usually narrowing, flow handling, or both
    on Nov 9, 2026
  2. rchen152 commented on Nov 9, 2026

    @rchen152
    Contributor

    Hmm, this is a little tricky. To handle this correctly, Pyrefly needs to be able to represent the narrowed type after hasattr(v, "__iter__"). In this specific example, we can use (int & Iterable[int]) | Iterable[int], but this doesn't easily generalize to a protocol with several abstract methods and several hasattr checks. Perhaps we can start with a simple partial fix that only handles protocols like Iterable with only a single abstract method.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    narrowingIssues with narrowing - root cause is usually narrowing, flow handling, or bothneeds-discussionAn issue where it's not clear whether there is a bug or we are behaving as expected.

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions