Skip to content

Build1 publisher3 min readPublished

PyCharm 2026.2 closes the SQLAlchemy false positives that got inspections switched off

JetBrains says 263 fixes shipped across the 2026.2 line. The five-ticket SQLAlchemy 2.0 cluster is the one with a scheduling argument attached for teams on modern declarative models.

The Engineer · Build desk

Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction

What happened

  • Across the PyCharm 2026.2 release line, JetBrains shipped 263 fixes and improvements, many improving Python code insight with more precise type inference, fewer false positives, smarter completion and imports, and more reliable refactoring.
  • SQLAlchemy has been a long-standing source of false positives in PyCharm, enough that several duplicate tickets accumulated over the years; the 2026.2 release resolves a batch of them for the 2.0 style.
  • String forward-references inside Mapped[...] now resolve correctly, e.g. posts: Mapped[list["Post"]] = relationship(back_populates="author") resolves "Post" to the model class.
  • PyCharm now correctly infers the mapped type returned by Session.get() instead of treating the result as the model class itself: reveal_type(session.get(Report, report_id)) was type[Report] | None and is now Report | None.
  • Modern hybrid_property setters written as @name.inplace.setter are recognized, so assigning to the property no longer produces a warning.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

JetBrains says the PyCharm 2026.2 release line shipped 263 fixes and improvements, with a stated focus on more precise type inference, fewer false positives, smarter completion and imports, and more reliable refactoring [1]. The item on that list with an actual upgrade argument attached is SQLAlchemy: the company describes the ORM as a long-standing source of false positives, enough that duplicate tickets accumulated over the years, and says this release resolves a batch of them for the 2.0 style [2].

Four specifics. String forward references inside `Mapped[...]` now resolve, so `Mapped[list["Post"]]` on a `relationship()` ties `"Post"` to the model class [3]. `Session.get()` yields the mapped type rather than the class object: `reveal_type` on `session.get(Report, report_id)` previously gave `type[Report] | None` and now gives `Report | None` [4]. Hybrid property setters written in the modern `@name.inplace.setter` form are recognized, so assigning to the property no longer produces a warning [5]. Model class attributes defined via mixins are picked up again, clearing the old unexpected-argument reports on model constructors [6]. Five tickets are cited against that group: PY-78816, PY-65142, PY-59732, PY-51906 and PY-28762 [7], more ticket IDs than any other group in the post [9].

That mixin item is the tell for how these bugs get consumed. A false unexpected-argument warning on every model constructor in a codebase with declarative mixins is not a minor irritation; it is the kind of noise that gets an inspection disabled team-wide, after which it stops catching real errors too. The `Session.get()` case is quieter and worse: an editor that believes it is holding `type[Report]` will complete the wrong names and stay silent about instance attribute access it cannot see. None of this alters runtime behavior. It alters whether the warnings your team has trained itself to scroll past are worth reading again.

The neighboring inference work lands on the same codebases. Strings used as metadata inside `Annotated[...]`, a Pydantic discriminator field name in JetBrains' example, are no longer parsed as forward references and flagged unresolved [8]. `isinstance` on an `int | float` union no longer narrows the `else` branch to `Never` and reports it unreachable, and narrowing survives a `while` loop, so re-narrowing an optional attribute inside the body stops producing a bogus missing-attribute error [10]. Augmented assignment was misanalyzed: `foo = 5` followed by `foo /= 2` inferred `int` and now infers `float | int` [11]. Enum member `.value` and `.name` return precise `Literal` types instead of a widened `str` or `int`, which JetBrains notes matches mypy's inference [12]. Starred expressions keep their element types, so returning `(1, *a())` where `a()` is `tuple[int, int]` no longer draws a spurious mismatch against `tuple[int, int, int]` [13]. Parameters of a decorated function are inferred from the decorator's `Callable` constraint rather than falling back to `Any` [14].

Worth keeping in proportion: this is JetBrains' own account of its own bug fixes, demonstrated on short snippets. The test is whether the same inference holds on a real declarative base with several layers of mixins, `TypeDecorator` columns and generated relationships, which is where the duplicate tickets came from in the first place. Teams that suppressed the ORM inspections years ago should re-enable them on a branch before assuming the noise is gone.

Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories