Debugging Python code in the terminal (with examples)
Tags: python, tutorial, beginners, productivity
Published: 2019-12-29 17:46:11

*For one of my projects, I wrote a new endpoint in a Flask application. My project had many pieces that built on each other, so I wanted to test in various ways and expand my backend debugging tool belt. Here are 3 tools I learned to use and now regularly use for debugging Python code.* 🐍

# 1. curl
[curl](https://github.com/curl/curl) is a command line tool for making HTTP requests. I like it because it’s a quick way to test endpoints are doing what I expect, but I found writing a curl confusing at first.

### Breaking down the anatomy of a curl
![curl get request](https://thepracticaldev.s3.amazonaws.com/i/vwzrs1z248vevzhxa8yo.png)
*Example GET request to retrieve a specific student’s info*

`-v`
is optional and stands for *verbose*. With it, you’ll see all the details of the request and response that are normally hidden, which can be useful for debugging.

`-H`
stands for *header*, and it’s how to specify extra headers to include in the request, such as an authentication token. In my example, the endpoint can only be accessed by school employees, so I’ve added a header to indicate that I am making an internal origin request. Otherwise, the request response would say I’m unauthorized. (It's not necessary to know what Envoy is for the example, but here’s more info if you’re curious: [1](https://www.envoyproxy.io/) & [2](https://www.envoyproxy.io/docs/envoy/v1.9.0/configuration/http_conn_man/headers#x-envoy-internal))

You don’t always have to specify extra headers (depends on the API or type of request), and you can include many headers in your request.

![curl post request](https://thepracticaldev.s3.amazonaws.com/i/9j7pohvit895bydaqtwb.png)
*Example POST request to make a new student*

`-X POST`
will perform a POST request. `-X` allows you to change the HTTP method used because the default is GET.

`-d`
stands for *data*. With this option, you can specify what data you’re sending to the HTTP server, in the same way that a browser does when you fill out a form and click the submit button. There are various ways to pass data in a curl POST request; in my example, I’m sending JSON.

`Content-Type`
Since I’m sending data, I need to let the server know the [format of the data](https://www.talend.com/blog/2015/12/10/understanding-http-content-negotiation/).

# 2. Python Interpreter
The [Python interpreter](https://docs.python.org/3/tutorial/interpreter.html) is a tool that comes with your Python installation. It’s an interactive [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop) in which you can type and execute Python code. I use it often to import and interact with the files I’ve written.

### Getting Acquainted

![test db model schema in python interpreter](https://thepracticaldev.s3.amazonaws.com/i/hjowzhyglasvj9h43wlu.png)
`service_venv ipython`
is how I launch the interpreter. But it’s more commonly invoked by typing `python` or `python3`  (depending on your installation). Note: [IPython](https://ipython.readthedocs.io/en/stable/interactive/tutorial.html) provides extra features than the standard Python interpreter, but the examples I’m showing can be performed in the standard interpreter as well.

`from app.models.student import *`
imports the specific file to use in the interpreter. I am specifying the directory path of the *student.py* file: there’s a folder *app* at the top level directory which contains the folder *models* which contains the file *student.py*. `import *` states that I want everything (`*`) in that file. Note: when importing a file, do not add the file ending.

The import statement is necessary; without it, in my example, `StudentModel` and `.save()` would be undefined and the Python interpreter would throw an error message.

Everything’s setup, and now I can test if my code is working as expected!

![python method](https://thepracticaldev.s3.amazonaws.com/i/sbstmt64we5zxw7ap78p.png)
![testing python method in python interpreter](https://thepracticaldev.s3.amazonaws.com/i/2sa0sha75ghusbt5f6za.png)

In this example, I'm testing a specific method `get_students` in the Python interpreter.

What are some investigations I could do? Perhaps I can test what happens when a user passes in a string value. Or I can see what happens if a user passes in a valid integer value but a student with that id doesn't exist. Am I returning a helpful error message? In the method `get_students`, I haven't done error handling...yet! In my experience, testing in the Python interpreter has been great for "ooooh yeah need to add that" realizations.  

## 3. Pytest
[Pytest](https://docs.pytest.org/en/latest/getting-started.html) is a Python testing framework that can be setup quickly to run tests and report the results. It’s also another command line tool!

![testing a method with pytest](https://thepracticaldev.s3.amazonaws.com/i/y3h11b0lph26bk3sozxv.png)

Here’s an example of an integration test that tests that a GET call returns the expected data.

I learned how to write tests by studying examples, and I was confused by what values were in a test, like *json_body*. So, I'd take tests a part and add print statements, like in the example below. But when running the test, I wouldn't see the output of the print statements. 🧐

![testing a method with pytest](https://thepracticaldev.s3.amazonaws.com/i/i1ttn5grckzlft855ul3.png)
 
## `-s`
![seeing the console output of pytest results](https://thepracticaldev.s3.amazonaws.com/i/lzs8eg1p362tjbqbjezo.png)

`python -m pytest`
is how to [invoke testing](http://doc.pytest.org/en/latest/usage.html) through the Python interpreter. `service_venv` is specific to my development environment.

`tests/integration/test_student_resource.py`
is the directory path of the file I want to test.

`-s`
is the star of this example. Adding this [option](https://stackoverflow.com/questions/14405063/how-can-i-see-normal-print-output-created-during-pytest-run) allows all print statements in tests to get printed in the terminal when the tests is run. Now I'd be able to see the value of *print(json_body)*.

Pytest is a powerful tool with powerful features, but this small `-s` flag has helped me to debug and learn how to write tests.

And that's it! I'm excited to see what other debugging tools I pick up as I continue to work on backend projects.

![that's all folks!](https://media.giphy.com/media/upg0i1m4DLe5q/giphy.gif)