Skip to content

Comments

[ty] Delay computation of 'unbound' visibility for implicit instance attributes#18669

Merged
carljm merged 1 commit intomainfrom
david/fix-627
Jun 13, 2025
Merged

[ty] Delay computation of 'unbound' visibility for implicit instance attributes#18669
carljm merged 1 commit intomainfrom
david/fix-627

Conversation

@sharkdp
Copy link
Contributor

@sharkdp sharkdp commented Jun 13, 2025

Summary

Consider the following example, which leads to a excessively large runtime on main. The reason for this is the following. When inferring types for self.a, we look up the a attribute on C. While looking for implicit instance attributes, we go through every method and check for self.a = … assignments. There are no such assignments here, but we always have an implicit self.a = <unbound> binding at the beginning over every method. This binding accumulates a complex visibility constraint in C.f, due to the isinstance checks. While evaluating that constraint, we need to infer the type of self.b. There's no binding for self.b either, but there's also an implicit self.b = <unbound> binding with the same complex visibility constraint (involving self.b recursively). This leads to a combinatorial explosion:

class C:
    def f(self: "C"):
        if isinstance(self.a, str):
            return

        if isinstance(self.b, str):
            return
        if isinstance(self.b, str):
            return
        if isinstance(self.b, str):
            return
        # repeat 20 times

(note that the self parameter here is annotated explicitly because we currently still infer Unknown for self otherwise)

The fix proposed here is rather simple: when there are no self.name = … attribute assignments in a given method, we skip evaluating the visibility constraint of the implicit self.name = <unbound> binding. This should also generally help with performance, because that's a very common case.

This is not a fix for cases where there are actual bindings in the method. When we add self.a = 1; self.b = 1 to that example above, we still see that combinatorial explosion of runtime. I still think it's worth to make this optimization, as it fixes the problems with pandas and sqlalchemy reported by users. I will open a ticket to track that separately.

closes astral-sh/ty#627
closes astral-sh/ty#641

Test Plan

  • Made sure that ty finishes quickly on the MREs in Freeze when checking project ty#627
  • Made sure that ty finishes quickly on pandas
  • Made sure that ty finishes quickly on sqlalchemy

@sharkdp sharkdp requested a review from carljm as a code owner June 13, 2025 19:32
@sharkdp sharkdp added the ty Multi-file analysis & type inference label Jun 13, 2025
@sharkdp
Copy link
Contributor Author

sharkdp commented Jun 13, 2025

We should probably also add a regression test/benchmark. I can look into that next week, in a follow-up, in case we want to merge this sooner.

@github-actions
Copy link
Contributor

mypy_primer results

No ecosystem changes detected ✅

Copy link
Contributor

@carljm carljm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thank you!

@carljm carljm merged commit 89d915a into main Jun 13, 2025
35 checks passed
@carljm carljm deleted the david/fix-627 branch June 13, 2025 19:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TY hanging when checking files Freeze when checking project

2 participants