There are a few techniques developer should learn to aid them in their work. In Chapter 1, Getting Started with Odoo Development, we already introduced the user interface Developer Mode. We also have available a server option providing some developer friendly features. We will be describing it in more detail next. After that we will discuss another relevant topic for developers: how to debug server side code.
The Odoo server provides the --dev
option to enable some developer features speeding up our development cycle, such as:
The --dev
option accepts a comma separated list of options, although the all option will be suitable most of the time. We can also specify the debugger we prefer to use. By default the Python debugger, pdb
, is used. Some people might prefer to install and use alternative debuggers. Here also supported are ipdb
and pudb
.
When working on Python code, the server needs to be restarted every time the code is changed, so that it is reloaded. The --dev
command line option makes that reloading: when the server detects that a Python file is changed, it automatically repeats the server loading sequence, making the code change immediately effective.
To use it just add the option --dev=all
to the server command:
$ ./odoo-bin -d todo --dev=all
For this to work the watchdog
Python package is required, and it should be installed as shown here:
$ pip install watchdog
We all know that a good part of a developer's work is to debug code. To do this we often make use of a code editor that can set breakpoints and run our program step by step.
If you're using Microsoft Windows as your development workstation, setting up an environment capable of running Odoo code from source is a nontrivial task. Also the fact that Odoo is a server that waits for client calls, and only then acts on them, makes it quite different to debug compared to client-side programs.
While it may look a little intimidating for newcomers, the most pragmatic approach to debug Odoo is to use the Python integrated debugger, pdb
. We will also introduce extensions to it that provide a richer user interface, similar to what sophisticated IDEs usually provide.
To use the debugger, the best approach is to insert a breakpoint on the code we want to inspect, typically a model method. This is done by inserting the following line in the desired place:
import pdb; pdb.set_trace()
Now restart the server so that the modified code is loaded. As soon as the program execution reaches that line, a (pdb)
Python prompt will be shown in the terminal window where the server is running, waiting for our input.
This prompt works as a Python shell, where you can run any expression or command in the current execution context. This means that the current variables can be inspected and even modified. These are the most important shortcut commands available:
h
(help) displays a summary of the pdb commands availablep
(print) evaluates and prints an expressionpp
(pretty print) is useful to print data structures such as dictionaries or listsl
(list) lists the code around the instruction to be executed nextn
(next) steps over to the next instructions
(step) steps into the current instructionc
(continue) continues execution normallyu
(up) move up the execution stackd
(down) move down in the execution stackThe Odoo server also supports the dev=all
option. If activated, when an exception is raised the server enters a post mortem mode at the corresponding line. This is a pdb
prompt, such as the one described earlier, allowing us to inspect the program state at the moment where the error was found.
While pdb
has the advantage of being available out-of-the-box, it can be quite terse, and a few more comfortable options exist.
Let's see how a simple debugging session looks like. We can start by adding debugger breakpoint in the first line of the do_populate_tasks
wizard method:
def do_populate_tasks(self): import pdb; pdb.set_trace() self.ensure_one() # ...
Now restart the server, open a To-do Tasks Wizard form, and click on the Get All button. This will trigger the do_populate_tasks
wizard method on the server, and the web client will stay in a Loading… state, waiting for the server response. Looking at the terminal window where the server is running, you will see something similar to this:
> /home/daniel/odoo-dev/custom-addons/todo_wizard/models/todo_wizard_model.py(54)do_populate_tasks() -> self.ensure_one() (Pdb)
This is the pdb
debugger prompt, and the two first lines give you information about where you are in the Python code execution. The first line informs the file, line number and function name you are in, and the second line is the next line of code to be run.
During a debug session, server log messages can creep in. These won't harm our debugging, but they can disturb us. We can avoid that by reducing the verbosity of the log messages. Most of the time these log messages will be from the werkzeug
module. We can silence them using the option --log-handler=werkzeug:CRITICAL
. If this is not enough, we can lower the general log level, using --log-level=warn
.
If we type h
now, we will see a quick reference of the commands available. Typing l
shows the current line of code and the surrounding lines of code.
Typing n
will run the current line of code and move to the next. If we just press Enter, the previous command will be repeated. So do that three times and we should be at the method's return statement.
We can inspect the content on any variable, such as the open_tasks
used in this method. and typing p open_tasks
or print open_tasks
will show the representation of that variable. Any Python expressions are allowed, even variable assignments. For example, to show a friendlier list with the Task names we could use:
(pdb) p open_tasks.mapped('name')
Running the return line, using n
once more, we will be shown the returning values of the function. Something like this:
--Return-- > /home/daniel/odoo-dev/custom-addons/todo_wizard/models/todo_wizard_model.py(59)do_populate_tasks()->{'res_id': 14, 'res_model': 'todo.wizard', 'target': 'new', 'type': 'ir.actions.act_window', ...} -> return self._reopen_form()
The debugging session will continue on the caller's lines of code, but we can finish it and continue normal execution typing c
.
While pdb
has the advantage of being available out of the box, it can be quite terse, and a few more comfortable options exist.
The Iron Python debugger, ipdb
, is popular choice that uses the same commands as pdb
, but adds improvements such as tab completion and syntax highlighting, for a more comfortable usage. It can be installed with:
$ sudo pip install ipdb
And a breakpoint is added with the line:
import ipdb; ipdb.set_trace()
Another alternative debugger is pudb
. It also supports the same commands as pdb
and works in text-only terminals, but uses a graphical display similar to what you can find in an IDE debugger. Useful information, such as the variables in the current context and their values, is readily available in the screen in their own windows:
It can be installed either through the system package manager or through pip
, as shown here:
$ sudo apt-get install python-pudb # using OS packages $ sudo pip install pudb # using pip, possibly in a virtualenv
Adding a pudb
breakpoint is done just the way you would expect:
import pudb; pudb.set_trace()
Sometimes we just need to inspect the values of some variables or check if some code blocks are being executed. A Python print
statement can do the job perfectly without stopping the execution flow. As we are running the server in a terminal window, the printed text will be shown in the standard output. But it won't be stored to the server log if it's being written to a file.
Another option to keep in mind is to set debug level log messages at sensitive points of our code if we feel that we might need them to investigate issues in a deployed instance. It would only be needed to elevate that server logging level to debug and then inspect the log files.
There are also a few tricks that allow us to inspect a running Odoo process.
For that we first need to find the corresponding process ID (PID). To find the PID run another terminal window and type:
$ ps ax | grep odoo-bin
The first column in the output is the PID for that process. Take a note on the PID for the process to inspect, since we will need it next.
Now we want to send a signal the process. The command used to do that is kill. By default it sends a signal to terminate a process, but it can also send other friendlier signals.
Knowing the PID for our running Odoo server process, we can print the traces of the code currently being executed using:
$ kill -3 <PID>
If we look at the terminal window or log file where the server output is being written, we will see the information on the several threads being run and detailed stack traces on what line of code they are running.
We can also see a dump of the cache/memory statistics using:
$ kill -USR1 <PID>