This is how I debug QGIS using VSCode on OSX. Previously I used this approach. But since then ptvsd
was deprecated and I also needed to install python modules backed by native C code. This is much easier to do in a conda based environment (actually, I didn't find a way to make it work with the official QGIS installer :-)).
Follow this description to get QGIS running in conda.
ptvsd is superseded by debugpy.
Activate your conda environment and instal debugpy:
> conda activate conda_qgis
> conda install debugpy
-
Open the plugin code directory. This could for instance be
/Users/asger/Library/Application Support/QGIS/QGIS3/profiles/default/python/plugins/QlrBrowser
. -
Select the Python interpreter from your conda environment which has QGIS installed.
-
In
Debug panel
clickAdd Configuration...
in the configuration drop down. -
When VSCode Asks for an Environment select
Python
. -
Then VSCode shows a
Select a debug configuration
dropdown where you selectRemote Attach
. -
VSCode asks for a host name. Just use
localhost
-
Lastly you are asked for a port number. Use
5678
. Now VSCode should have aPython: Remote Attach
option in the debug configurations drop down . -
You need to change the
pathMappings
in the configuration manually. Click the cogwheel next to the configurations drop down and edit thepathMappings
like to look like this
"pathMappings": [
{
"localRoot": "${workspaceFolder}",
"remoteRoot": "${workspaceFolder}"
}
]
Your launch.json
may now look like this
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python: Current File",
"type": "python",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal"
},
{
"name": "Python: Remote Attach",
"type": "python",
"request": "attach",
"port": 5678,
"host": "localhost",
"pathMappings": [
{
"localRoot": "${workspaceFolder}",
"remoteRoot": "${workspaceFolder}"
}
]
}
]
}
So far there does not seem to be a QGIS plugin which starts a debugpy remote debugger. There is a plugin for the ptvsd
debugger but that is now deprecated in favour of debugpy
. (Note to self: Update that plugin to use debugpy
.)
We have to invoke the debug adapter from our code. So put code like this somewhere it will be hit
import debugpy
import shutil
debugpy.configure(python=shutil.which("python"))
debugpy.listen(('localhost', 5678))
debugpy.wait_for_client() # blocks execution until client is attached
If you are writing a plugin a good place to put the code could be at the top of the plugin´s __init__.py
file.
Now, when the above code is hit QGIS will wait patiently for you to attach to the debugger. In VS Code open the Debug panel and start debugging using the Python: Remote Attach configuration
defined above.
Processing by default executes the processAlgorithm(...)
method on a seperate thread. So your breakpoints in the method will not be hit if your debug adapter runs in the main thread which will be the case if you have done as described above.
There are two possible solutions to this problem.
Add this method to your QgsProcessingAlgorithm
class:
def flags(self):
return QgsProcessingAlgorithm.FlagNoThreading
See the documentation about flags()
.
An other option is invoking the debug adapter in the thread which executes the processAlgorithm(...)
method. Put these lines at the top of the processAlgorithm(...)
method like this:
def processAlgorithm(self, parameters, context, feedback):
import debugpy
import shutil
debugpy.configure(python=shutil.which("python"))
debugpy.listen(('localhost', 5678))
debugpy.wait_for_client() # blocks execution until client is attached
# Actual code will be below this line
Make sure these lines are not hit when putting the code into production!
Note that you cannot run two adapters on the same port. Either invoke only one adapter or make your adapters use different ports. When attaching to the debugger you naturally need to specify the right port (see configuring launch.json
above).