b0o / blender.nvim

🔶 Develop Blender Add-ons with Neovim
Other
47 stars 4 forks source link

`nvim-dap` Breakpoint not Working #5

Open fangjunzhou opened 5 days ago

fangjunzhou commented 5 days ago

I've installed debugpy in my venv and nvim-dap for my neovim. But I cannot get the dap to stop at the breakpoint I set inside nvim.

Calling breakpoint() works just fine and I can debug or run repl when triggered

image

However, setting breakpoints by DapToggleBreakpoint inside nvim won't work.

image

Is there anything I need to setup for nvim-dap or is this feature not support by blender.nvim?

b0o commented 4 days ago

Hey there! DAP breakpoints are intended to work. I just tested and they work on my system. Do you see any relevant output in the logs? Can you also share your DAP config, Blender.nvim config, and the versions of the pynvim and debugpy in your virtualenv?

To see if this is an issue with your venv setup, you could try to clone my blender-addon-template repo. Follow the instructions in the README to setup the venv. Then try setting a breakpoint in my_addon/panel_mypanel.py:31, and inside of Blender toggle the "3D View > My Addon > My Panel" sidebar panel a few times, which should trigger that breakpoint.

fangjunzhou commented 3 days ago

Here's my dap.log

[ INFO ] 2024-10-12T20:29:28Z-0700 ] ...pack/myNeovimPackages/start/nvim-dap/lua/dap/session.lua:784 ]  "Telemetry" "ptvsd"
[ INFO ] 2024-10-12T20:29:28Z-0700 ] ...pack/myNeovimPackages/start/nvim-dap/lua/dap/session.lua:784 ]  "Telemetry" "debugpy"
[ WARN ] 2024-10-12T20:29:28Z-0700 ] ...pack/myNeovimPackages/start/nvim-dap/lua/dap/session.lua:1063 ] "No event handler for " {
  body = {
    sockets = { {
        host = "127.0.0.1",
        internal = false,
        port = 52367
      }, {
        host = "127.0.0.1",
        internal = true,
        port = 52368
      } }
  },
  event = "debugpySockets",
  seq = 3,
  type = "event"
}
[ WARN ] 2024-10-12T20:29:28Z-0700 ] ...pack/myNeovimPackages/start/nvim-dap/lua/dap/session.lua:1063 ] "No event handler for " {
  body = {
    host = "127.0.0.1",
    port = 52368
  },
  event = "debugpyWaitingForServer",
  seq = 5,
  type = "event"
}

Here's my global blender.nvim config:

require("blender").setup {
  profiles = { --                 Profile[]?       list of blender profiles
    --
    {
      name = "blender", --        string           profile name, must be unique
      cmd = "blender", --         string|string[]  command to run Blender
      use_launcher = false --   boolean?         whether to run the launcher.py script when starting Blender
      -- extra_args = {} --       string[]?        extra arguments to pass to Blender
      -- enable_dap = nil --      boolean?         whether to enable DAP for this profile (if nil, the global setting is used)
      -- watch = nil --           boolean?         whether to watch the add-on directory for changes (if nil, the global setting is used)
    },
  },
  dap = { --                      DapConfig?       DAP configuration
    enabled = true, --            boolean?         whether to enable DAP (can be overridden per profile)
  },
  notify = { --                   NotifyConfig?    notification configuration
    enabled = true, --            boolean?         whether to enable notifications
    verbosity = "INFO", --        "TRACE"|"DEBUG"|"INFO"|"WARN"|"ERROR"|"OFF"|vim.log.level?  log level for notifications
  },
  watch = { --                    WatchConfig?     file watcher configuration
    enabled = true, --            boolean?         whether to watch the add-on directory for changes (can be overridden per profile)
  },
}

I also have my local .nvim.lua setup:

local has_blender, blender = pcall(require, "blender")
if has_blender then
  blender.setup {
    profiles = {
      {
        name = "cs148-hw3",
        cmd = "blender",
        use_launcher = true,
        -- Open a specific file when launching Blender:
        extra_args = { vim.fn.getcwd() .. "/cs148-hw3.blend" },
      },
    },
  }
end

I'm using nixvim, so the configuration is in nix. But I believe there's no issue with my DAP configuration because I can debug normal python code. Here's my DAP config (equivalent to this DAP Python Configuration):

{ pkgs-lldb, helpers, ... }:

{
  plugins.dap = {
    enable = true;
    adapters = {
      executables = {
        lldb = {
          command = "${pkgs-lldb.llvmPackages.lldb}/bin/lldb-vscode";
          args = [
            "-i"
            "dap"
          ];
        };
      };
      servers = {
        python = ''
          function(cb, config)
            if config.request == "attach" then
              ---@diagnostic disable-next-line: undefined-field
              local port = (config.connect or config).port
              ---@diagnostic disable-next-line: undefined-field
              local host = (config.connect or config).host or "127.0.0.1"
              cb({
                type = "server",
                port = assert(port, "`connect.port` is required for a python `attach` configuration"),
                host = host,
                options = {
                  source_filetype = "python",
                },
              })
            else
              cb({
                type = "executable",
                command = os.getenv("VIRTUAL_ENV") .. "/bin/python",
                args = { "-m", "debugpy.adapter" },
                options = {
                  source_filetype = "python",
                },
              })
            end
          end
        '';
      };
    };
    configurations = {
      java = [
        {
          name = "Launch Java Main Class";
          type = "java";
          request = "launch";
        }
      ];
      cpp = [
        {
          name = "Launch C++ Executable";
          type = "lldb";
          request = "launch";
          program.__raw = ''
            function()
              return vim.fn.input("Path to executable: ", vim.fn.getcwd() .. "/", "file")
            end
          '';
          cwd = "\${workspaceFolder}";
          stopAtBeginningOfMainSubprogram = true;
          args = { };
        }
      ];
      python = [
        {
          name = "Launch Python File";
          type = "python";
          request = "launch";
          program = "\${file}";
          pythonPath.__raw = ''
            function()
              -- debugpy supports launching an application with a different interpreter then the one used to launch debugpy itself.
              -- The code below looks for a `venv` or `.venv` folder in the current directly and uses the python within.
              -- You could adapt this - to for example use the `VIRTUAL_ENV` environment variable.
              local cwd = vim.fn.getcwd()
              if vim.fn.executable(cwd .. '/venv/bin/python') == 1 then
                return cwd .. '/venv/bin/python'
              elseif vim.fn.executable(cwd .. '/.venv/bin/python') == 1 then
                return cwd .. '/.venv/bin/python'
              else
                return '/usr/bin/python'
              end
            end
          '';
        }
      ];
    };
    extensions.dap-ui.enable = true;
  };

  keymaps = [
    {
      mode = [ "n" ];
      key = "<f5>";
      action = helpers.mkRaw ''
        function()
            require("dap").continue()
        end
      '';
      options = {
        desc = "DAP Continue";
      };
    }
    {
      mode = [ "n" ];
      key = "<f17>";
      action = helpers.mkRaw ''
        function()
            require("dap").terminate()
        end
      '';
      options = {
        desc = "DAP Terminate";
      };
    }
    {
      mode = [ "n" ];
      key = "<f10>";
      action = helpers.mkRaw ''
        function()
            require("dap").step_over()
        end
      '';
      options = {
        desc = "DAP Step Over";
      };
    }
    {
      mode = [ "n" ];
      key = "<f11>";
      action = helpers.mkRaw ''
        function()
            require("dap").step_into()
        end
      '';
      options = {
        desc = "DAP Step Into";
      };
    }
    {
      mode = [ "n" ];
      key = "<f12>";
      action = helpers.mkRaw ''
        function()
            require("dap").step_out()
        end
      '';
      options = {
        desc = "DAP Step Out";
      };
    }
    {
      mode = [ "n" ];
      key = "<f9>";
      action = helpers.mkRaw ''
        function()
            require("dap").toggle_breakpoint()
        end
      '';
      options = {
        desc = "DAP Toggle Breakpoints";
      };
    }
    {
      mode = [ "n" ];
      key = "<f21>";
      action = helpers.mkRaw ''
        function()
            require("dap").clear_breakpoints()
        end
      '';
      options = {
        desc = "DAP Clear Breakpoints";
      };
    }
  ];

  extraConfigLua = ''
    local dap, dapui = require("dap"), require("dapui")
    dap.listeners.before.attach.dapui_config = function()
      dapui.open()
    end
    dap.listeners.before.launch.dapui_config = function()
      dapui.open()
    end
    dap.listeners.before.event_terminated.dapui_config = function()
      dapui.close()
    end
    dap.listeners.before.event_exited.dapui_config = function()
      dapui.close()
    end
  '';
}

I'll try to run the template later. But I think currently the most suspicious thing is using breakpoint() function in python can trigger the dap breakpoint while explicitly setting the breakpoint doesn't. This sounds like the python dap in blender can talk to nvim while nvim can't send command to blender to me.

fangjunzhou commented 3 days ago

Btw, here's the output from blender.nvim:

[Blender.nvim] INFO: Using virtual environment: /Users/fangjun/Documents/stanford/cs148/hw3/venv
[Blender.nvim] INFO: RPC socket: /var/folders/qh/c7l883gn5s548l5w18l25cwc0000gn/T/nix-shell.QMCLPh
/nvim.fangjun/wnA3FS/nvim.21610.1
[Blender.nvim] INFO: Addons to load: [{'load_dir': '/Users/fangjun/Documents/stanford/cs148/hw3/sr
c', 'module_name': 'src'}]
[Blender.nvim] INFO: Enable debugpy: yes
[Blender.nvim] INFO: Using virtual environment: /Users/fangjun/Documents/stanford/cs148/hw3/venv
[Blender.nvim] INFO: RPC socket: /var/folders/qh/c7l883gn5s548l5w18l25cwc0000gn/T/nix-shell.QMCLPh
RPC send: {'type': 'setup', 'python_exe': '/Applications/Blender.app/Contents/Resources/4.2/python
/bin/python3.11', 'blender_path': '/Applications/Blender.app/Contents/MacOS/Blender', 'blender_ver
sion': '4.2.1', 'scripts_folder': '/Applications/Blender.app/Contents/MacOS/4.2/scripts', 'path_ma
ppings': [{'src': '/Users/fangjun/Documents/stanford/cs148/hw3/src', 'load': '/Users/fangjun/Libra
ry/Application Support/Blender/4.2/scripts/addons/src'}], 'task_id': 2, 'channel_id': 30}
0.00s - Debugger warning: It seems that frozen modules are being used, which may
0.00s - make the debugger miss breakpoints. Please pass -Xfrozen_modules=off
0.00s - to python to disable frozen modules.
0.00s - Note: Debugging will proceed. Set PYDEVD_DISABLE_FILE_VALIDATION=1 to disable this validat
ion.
Debugpy listening on 127.0.0.1:52384
Waiting for debug client.
RPC send: {'type': 'setup_debugpy', 'host': '127.0.0.1', 'port': 52384}
Debug client attached.
Read blend: "/Users/fangjun/Documents/stanford/cs148/hw3/cs148-hw3.blend"
2024-10-12 20:52:12.216 Blender[23146:2338824] +[IMKClient subclass]: chose IMKClient_Legacy
fangjunzhou commented 3 days ago

I tried blender-addon-template and dap breakpoint works in that project. I'm still trying to figure out what's wrong with my project.

fangjunzhou commented 3 days ago

Latest finding: DAP breakpoints works with draw() functions in any bpy.types.Panel class in my project as well! But it doesn't work with the render() function in bpy.types.RenderEngine class.

At this point I'm pretty sure there's something wrong with the plugin.

I also noticed in output of the plugin it says

0.00s - Debugger warning: It seems that frozen modules are being used, which may
0.00s - make the debugger miss breakpoints. Please pass -Xfrozen_modules=off
0.00s - to python to disable frozen modules.
0.00s - Note: Debugging will proceed. Set PYDEVD_DISABLE_FILE_VALIDATION=1 to disable this validat
ion.

Not sure if this is related to the problem. For this project I'll just go back to use python breakpoint() to trigger the breakpoint.

This is still an amazing plugin btw! It makes my life infinitely easier to sync the code and boot up test blend file.

b0o commented 3 days ago

But it doesn't work with the render() function in bpy.types.RenderEngine class.

Oh that’s good to know, definitely helps narrow it down. Could you share a minimal example of this that I can test?

I also noticed in output of the plugin it says …

I’ll look into this, thanks.