cd /news/developer-tools/whats-fixed-and-improved-in-pycharm-… · home topics developer-tools article
[ARTICLE · art-102988] src=blog.jetbrains.com ↗ pub= topic=developer-tools verified=true sentiment=↑ positive

What’s Fixed and Improved in PyCharm 2026.2

JetBrains released PyCharm 2026.2 with 263 fixes and improvements, focusing on Python code insight, type inference, and SQLAlchemy 2.0 support. The update resolves long-standing false positives in SQLAlchemy, improves control-flow narrowing, and fixes type inference for iterable unpacking and augmented assignments.

read7 min views3 publishedAug 19, 2026
What’s Fixed and Improved in PyCharm 2026.2
Image: Blog (auto-discovered)

Releases

Across the PyCharm 2026.2 release line, we shipped 263 fixes and improvements. Many improve Python code insight directly, with more precise type inference, fewer false positives, smarter completion and imports, and more reliable refactoring. Here are some of the smaller changes you’re likely to notice in everyday Python development.

SQLAlchemy 2.0 support #

SQLAlchemy has been a long-standing source of false positives – enough that several duplicate tickets have accumulated over the years. This release resolves a batch of them for the 2.0 style.

String forward-references inside Mapped[...]

resolve correctly:

posts: Mapped[list["Post"]] = relationship(back_populates="author")

PyCharm also correctly infers the mapped type returned by Session.get()

, instead of treating the result as the model class itself:

report = session.get(Report, report_id)

reveal_type(report)  # was: type[Report] | None   now: Report | None

Modern hybrid_property

setters written as @name.inplace.setter

are recognized, so assigning to the property no longer produces a warning. Model class attributes defined via mixins are picked up again, too, clearing the old unexpected argument reports on model constructors.

(PY-78816, PY-65142, PY-59732, PY-51906, PY-28762)

Code insight and type inference #

Control-flow narrowing and “unreachable code”

Several false This code is unreachable reports and instances of lost narrowing across loops have been fixed. The common issue: flow analysis either gave up or over-eagerly narrowed to Never

in branches it should have kept alive.

isinstance

on a numeric union no longer kills the else

branch:

def foo(y: int | float) -> None:

    if isinstance(y, float):

        pass

    else:

        print(y)  # was flagged unreachable, y inferred as Never

Narrowing also survives a while

loop, so re-narrowing an optional attribute inside the loop body no longer reports a bogus has no attribute error.

(PY-83206, PY-83354, PY-88265)

Strings inside type annotations

A string used as metadata inside Annotated[...]

– a Pydantic discriminator field name, for instance – is no longer parsed as a forward reference and flagged as unresolved.

Iterable unpacking and star expressions

PyCharm’s analysis of tuple and star unpacking could lose type information and fall back to Any

. Unpacking a starred value into a tuple lost its element types, *

-expansion collapsed to Any

, and several genuine errors went unreported. Starred expressions preserve their element types:

def a() -> tuple[int, int]:

    return 2, 3

def b() -> tuple[int, int, int]:

    return (1, *a())  # no more bogus "Expected tuple[int, int, int]"

(PY-12592, PY-27205, PY-43585, PY-90219)

Augmented assignment

A cluster of false positives came from augmented assignments being misanalyzed. A simple /=

on an int

produced the wrong type:

foo = 5

foo /= 2

reveal_type(foo)  # was: int   now: float | int

(PY-80622)

Self

and constructor return types

Self

binds correctly through classmethod

parameters typed as type[Self]

:

class A:

    @classmethod

    def bar(cls, y: type[Self]) -> Self: ...

x = A.bar(A)      # was a spurious "Expected type[A], got type[A]"

reveal_type(x)    # was: Any   now: A

Construction also respects __new__

, __init__

, and metaclass __call__

. When __new__

returns something other than an instance, that’s the constructed type – even when an __init__

is present. The same fix covers explicitly parameterized calls like MyClass[int]()

and __new__

assigned as a class attribute.

(PY-89296, PY-77611, PY-88644, PY-89571)

Enum members: Literal types for .value

and .name

Reading an enum member’s .value

or .name

yields a precise Literal

instead of a widened str

or int

, so assignments to Literal[...]

target type-check. This matches mypy

’s inference:

from enum import Enum

from typing import Literal

class E(Enum):

    a = "a"

b: Literal["a"] = E.a.value   # was: Expected 'Literal["a"]', got 'str'

n: Literal["a"] = E.a.name    # .name is a Literal too

Parameter types inferred from decorators

When a decorator constrains the callable it accepts, the decorated function’s parameters are inferred from that constraint instead of falling back to Any

:

from typing import Callable

def d(fn: Callable[[int], str]): ...

@d

def f(a):

    reveal_type(a)   # was: Any   now: int

(PY-79204)

Also fixed

  • Keyword arguments in a class header are validated against the base class’s __init_subclass__

signature, and offered in completion (PY-79173). - An ellipsis in a Callable

used as a PEP 695 type-parameter bound no longer reports a bogusInvalid type expression(PY-83570). - Type-checker findings are split into granular suppression codes rather than a single PyTypeChecker

id, and# noinspection

directives accept a simplified name form.PyTypeChecker

still works as a blanket ignore (PY-90265).

Completion and auto-import #

Smarter auto-import

Auto-import is now noticeably less noisy. Previously, if a module was already imported, PyCharm would offer to add a second, redundant import instead of qualifying through the one you already had. The quick-fix – and the completion popup – prefer to reuse the existing import.

Given pkg/src.py

containing MyClass

, and a file that already imports the module, Alt+Enter produces this:

from pkg import src  # no longer flagged as unused

src.MyClass

instead of adding from pkg.src import MyClass

. The same reuse logic applies to plain import pkg.src

, and to the auto-import completion on a second Ctrl+Space.

Nested classes can be auto-imported too, which is something PyCharm didn’t previously support:


class Outer:

    class Inner:

        pass


from mod import Outer

value = Outer.Inner()

(PY-87970, PY-87971, PY-87972, PY-88009, PY-88016)

Completion for unittest.mock.patch()

targets

Patching by string target previously offered no code assistance, so dotted paths had to be entered manually. The string argument to mock.patch(...)

gets code completion for modules, classes, and their attributes, and it no longer suggests the invalid as

keyword mid-path:

from unittest import mock


with mock.patch("sample.Foo.my_attr", 14):

    ...

(PY-89189, PY-89191, PY-89192)

Typed signatures when overriding built-in methods

Completing an override of a dunder or built-in method fills in the full annotated signature – and auto-imports the types it needs – instead of bare parameters:

from types import TracebackType

class A:

    def __exit__(self, exc_type: type[BaseException] | None,

                 exc_val: BaseException | None,

                 exc_tb: TracebackType | None): ...

(PY-79218)

Editor and inspections #

Type inlay hints

Inferred type arguments are shown inline at the call site, so you can see what a generic resolved to without hovering over it:

class A[T]:

    def __init__(self, t: T): ...

A[int](1)     # [int] shown as an inlay hint

Type names rendered inside inlay hints – return types and solved arguments alike – are also clickable, so you can jump straight to a type’s definition from the hint.

f-string

format-spec validation

PyCharm already validated the str.format()

mini-language. Those checks apply to f-strings

too, and PyCharm flags formatting a type that doesn’t implement __format__

:

data = 1

f"{data:.2f}"   # ok

f"{data:.2q}"   # now flagged: unsupported format spec

class A: ...

f"{A():d}"      # now flagged: A doesn't support the 'd' format

Refactoring #

The Rename refactoring also updates references to a module when the module itself is renamed. Previously, the renaming left importing sites pointing at the old name:


from ..provider import provider_module  # this reference is updated too

(PY-53274)

The Refactor | Field action is now Attribute, and the documentation says “instance attributes” to match Python terminology (PY-85828).

Conclusion #

Taken together, these changes make PyCharm’s understanding of Python more precise and predictable: fewer false positives, better type inference, smarter completion, and less time spent working around cases where the IDE gets valid code wrong.

Many of these improvements started with real-world examples reported by users. If PyCharm still misunderstands a typing pattern, framework API, or other valid Python code in your project, let us know in YouTrack – a small reproducer can help us turn that friction into the next fix.

Try PyCharm 2026.2 and let us know which improvements make the biggest difference for your workflow.

Thank you for using PyCharm!

Subscribe to PyCharm Blog updates

── more in #developer-tools 4 stories · sorted by recency
── more on @jetbrains 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/whats-fixed-and-impr…] indexed:0 read:7min 2026-08-19 ·