Commit Graph
1 Commits
Author SHA1 Message Date
Josh66andClaude Opus 5.5 bea5e205f4 fix(python): a class with PEP 695 type parameters reads its base list
pythonBaseList, the resolver's reader of a Python base list behind module-global,
self. and class-name dispatch, expected the ( of the list right after the class
name. In class Repo[T](Base[T]): it found the [ of the type parameters, so the
class seemed to have no bases there, though extraction gave it its extends edge:
current = Repo() then current.run(), and Repo.run(x), reached nothing.

The reader now skips a type parameter list between the name and the bases, with
brackets balanced, so bounds like T: dict[str, int], *Ts and **P go whole, and a
constraint tuple like [T: (int, str)] isn't taken for the base list. Either list
can wrap over several lines. Anything else after the name and its parameters,
such as the : of class NoBases[T]:, still means no base list.

Validation:
- The test file: 4 of 7 fail on main (the controls pass), 7 pass here, and the
  multiline, generic-base and type-argument suites still pass.
- A fixture of ours on the built CLI, kernel on: Repo.run(None), a wrapped
  list, a constraint tuple and a module global reach no method on main and
  Base::run here. class NoBases[T] reaches neither run, and
  Typed[T](Base[User]) does not reach User.save.
- A/B against main on 11 Python repos, 486,377 edges (requests, flask,
  litestar, mypy, sqlalchemy, polar's and django's packages, and four small
  apps that use PEP 695 bases): nothing changes. Neither do express and gin,
  whose full dumps are byte-identical. polar has 39 such classes, and none is
  reached by a call this reader decides.
- Index cost on sqlalchemy, median of 3 interleaved: 23.4 s on main and here.

Original PR #3105 by @JosefAschauer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 17:48:17 -05:00