Skip to content

fix(autoreload): drop stale @overload registrations before reloading - #10551

Open
NoiceHax wants to merge 1 commit into
marimo-team:mainfrom
NoiceHax:fix/issue-5416
Open

fix(autoreload): drop stale @overload registrations before reloading#10551
NoiceHax wants to merge 1 commit into
marimo-team:mainfrom
NoiceHax:fix/issue-5416

Conversation

@NoiceHax

@NoiceHax NoiceHax commented Aug 14, 2026

Copy link
Copy Markdown

typing.overload keeps a private registry keyed by module, qualname, and the function's first line number, and get_overloads() reads it back. Autoreload re-executes the module but never clears that registry, so when a reload shifts line numbers the new entries sit next to the old ones and get_overloads() returns the overloads from every earlier version of the file. That breaks anything doing runtime dispatch on overloads, plum in particular.

superreload now drops the module's entry from the registry before reloading, and re-running the module fills it back in. There is no public API for clearing one module, clear_overloads() wipes the lot, so this reaches into typing._overload_registry and typing_extensions._overload_registry directly, wrapped in try/except with a debug log as asked for in the issue.

Tests are in tests/_runtime/reload/test_autoreload.py under TestOverloadRegistry. The regression test writes a module with two overloads, reloads it with an extra line at the top, and checks get_overloads() still returns 2.

Closes #5416

`typing.overload` (and `typing_extensions.overload` on Python 3.10)
records decorated functions in a private registry keyed by module,
qualname, and source line, which `get_overloads()` reads back. Because
the key includes the definition's first line number, reloading a module
whose line numbers shifted registered the new overloads alongside the
old ones instead of replacing them, so `get_overloads()` returned every
overload from every previous version of the module. That breaks runtime
introspection built on overloads, such as plum's multiple dispatch.
`superreload` now evicts the reloading module's entries from the
registry (defensively, since neither module exposes a public API for
it); re-executing the module repopulates them.

Closes marimo-team#5416
@vercel

vercel Bot commented Aug 14, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
marimo-docs Ready Ready Preview Aug 14, 2026 5:10am

Request Review

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@NoiceHax

Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

@mscolnick

Copy link
Copy Markdown
Contributor

@cubic-dev-ai

@cubic-dev-ai

cubic-dev-ai Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai

@mscolnick I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No issues found across 2 files

Architecture diagram
sequenceDiagram
    participant Client as User/Runtime
    participant MR as ModuleReloader
    participant SR as superreload()
    participant COM as clear_module_overloads()
    participant TY as typing/typing_extensions
    participant REG as _overload_registry
    participant MOD as Target Module

    Note over Client,SR: Autoreload Flow (on file change)
    
    Client->>MR: check(modules, reload=True)
    MR->>SR: superreload(module, old_objects)
    
    Note over SR,COM: Pre-reload cleanup phase
    
    SR->>SR: Capture module __dict__ snapshot
    SR->>COM: clear_module_overloads(module.__name__)
    
    COM->>TY: Access typing module(s)
    alt Both typing and typing_extensions present
        COM->>TY: Get _overload_registry attribute
        alt Registry is a dict
            COM->>REG: pop(modname, None)
            Note over REG: Removes only this module's<br/>stale overload entries
        else Registry missing/not a dict
            COM->>COM: Skip (no-op)
        end
    else Module not imported
        COM->>COM: Continue (no-op)
    end
    
    COM-->>SR: Return (clean registry)

    SR->>MOD: Re-execute module code
    MOD->>TY: Register @overload definitions
    TY->>REG: Set new entries (keyed by current line numbers)
    REG-->>TY: Confirm registration
    TY-->>MOD: Overloads registered
    
    Note over SR,REG: Post-reload state
    SR-->>MR: Return reloaded module
    MR-->>Client: Reload complete

    Note over Client,REG: Runtime queries (post-reload)
    Client->>MOD: get_overloads(func)
    MOD->>TY: Query overload registry
    TY->>REG: Lookup module + qualname
    REG-->>TY: Return only current line-number entries
    TY-->>MOD: Overload list (exactly 2)
    MOD-->>Client: Correct count (no duplicates)

    Note over COM,REG: Failure handling
    alt Exception during registry access
        COM->>COM: Log debug message
        COM->>COM: Continue reload (graceful degradation)
    end
Loading

Re-trigger cubic

shashvat-singham

This comment was marked as resolved.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Fixes an autoreload edge case where typing.get_overloads() / typing_extensions.get_overloads() can return duplicated overloads after a module reload shifts function line numbers, by clearing the per-module overload registry entries before re-execution.

Changes:

  • Add clear_module_overloads(modname) to evict a module’s stale entries from typing._overload_registry and typing_extensions._overload_registry prior to reload.
  • Invoke clear_module_overloads() in superreload() so stale overload registrations don’t accumulate across reloads.
  • Add regression tests ensuring overload introspection remains stable after reloads that shift source line numbers.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
marimo/_runtime/reload/autoreload.py Clears per-module @overload registry entries before superreload() to prevent duplicated overload introspection after line shifts.
tests/_runtime/reload/test_autoreload.py Adds targeted tests for scoped registry clearing and a regression test for duplicated overloads after reload.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

def clear_module_overloads(modname: str) -> None:
"""Drop `modname`'s `@overload` registrations ahead of a reload.

`typing.overload` (and `typing_extensions.overload` on Python 3.10)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Module autoreload can break typing.get_overloads inspection.

4 participants