Skip to content

Breakpoints not honored after autoreloading #2001

Closed

Description

@memeplex

Before creating a new issue, please check the FAQ to see if your question is answered there.

Environment data

  • debugpy version: 1.8.20
  • OS and version: macOS Tahoe
  • Python version (& distribution if applicable, e.g. Anaconda): 3.13.5
  • Using VS Code or Visual Studio: VSCode

Actual behavior

In a Jupyter Notebook I have:

%load_ext autoreload
%autoreload 2

The notebook imports another module, let's say import util.

I set a breakpoint in util.py and launch the debugger from a cell calling some function util.fun. So far so good.

Now, after the debugging session ends, I do some changes in util.py, say rename util.fun to util.fun2. If I then launch the debugger from a cell calling util.fun2, debugpy doesn't stop at the breakpoint anymore.

In my example I was renaming a function, but this happens after other, more innocent looking, changes too.

Calling importlib.reload(util) fixes the issue. OTOH removing and readding the breakpoints has no effect at all.

Expected behavior

Breakpoints are honored after autoreloading.

Steps to reproduce:

The description provided a simple example.

Activity

  1. rchiodo commented on Mar 3, 2026

    @rchiodo
    Contributor

    I'm pretty sure this is because the kernel isn't reloading the lib. It's not the debugger, it's jupyter itself (which is why importlib.reload(util) makes it work).

    I don't think this is something that debugpy would fix. It's more like the jupyter extension in VS code.

  2. memeplex commented on Mar 3, 2026

    @memeplex
    Author

    But the behavior of autoreload is different to that of reload by design. Autoreload doesn't reload the module but just replaces the bytecode in changed objects, AFAIK. I fail to see how this has to do with the kernel. The behavior itself is correctly updated, the only thing failing is that breakpoints are missed.

  3. rchiodo commented on Mar 3, 2026

    @rchiodo
    Contributor

    I believe that would be incompatible with how the debugger finds the breakpoints then. The module wouldn't match.

  4. rchiodo commented on Mar 3, 2026

    @rchiodo
    Contributor

    Yeah if I restart the kernel it works. So auto reload is basically breaking the debugger's lookup of the module. I can verify by adding this code to the function.

    def fun4():
        print("Inside of fun4")
        import inspect
    
        frame = inspect.currentframe()
        print(frame.f_code.co_filename)

    With a restart, it prints this:

    Inside of fun4
    [c:\Users\rchiodo\source\repos\pylance-release\testing\single\src\util.py](file:///C:/Users/rchiodo/source/repos/pylance-release/testing/single/src/util.py)
    

    With autoreload it prints this:

    Inside of fun4
    <string>
    

    The debugger can't map to the breakpoint, so the breakpoint doesn't bind.

  5. memeplex commented on Mar 3, 2026

    @memeplex
    Author

    Is there any way to invalidate the debugger lookup table so that it behaves as if the file was just loaded?

  6. rchiodo commented on Mar 3, 2026

    @rchiodo
    Contributor

    Not change the co_filename on the generated code. It's how the debugger maps the breakpoint.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions