Welcome to the next pikoTutorial!
Print exceptions traceback after catching them
Consider the following example code that raises an exception:
def send_request():
raise RuntimeError('Send request timeout!')
def do_something():
send_request()
def do_something_else():
send_request()
do_something()
do_something_else()When we run it, we see the following output:
Traceback (most recent call last):
File "<main.py>", line 11, in <module>
File "<main.py>", line 5, in do_something
File "<main.py>", line 2, in send_request
RuntimeError: Send request timeout!The traceback provides detailed information, including where the error occurred, the exception type, and the error message. However, the downside is that the program crashes without attempting to handle the error. This often leads developers to focus on implementing error handling without considering the need for debugging when errors persist. Let’s add error handling to catch the exception:
try:
do_something()
do_something_else()
except RuntimeError as e:
print(f'Operation failed: {e}')This code handles the exception gracefully, but the output is now:
Operation failed: Send request timeout!While this output is clean, it lacks the valuable traceback details we had before, making it harder to identify which function caused the error. It may be fine in some cases, but here it’s especially problematic because send_request() function is used in more than one place in the code, so the exception could come either from do_something() or do_something_else() function. This information may be priceless during debugging and fortunately there’s a way to bring it back without sacraficing program’s robustness. Python provides a traceback module for that:
import traceback
try:
do_something()
do_something_else()
except RuntimeError as e:
print(f'Operation failed: {e}\n' \
f'{traceback.format_exc()}')Now, the output includes the full traceback, helping us pinpoint where the exception occurred:
Operation failed: Send request timeout!
Traceback (most recent call last):
File "<main.py>", line 13, in <module>
File "<main.py>", line 7, in do_something
File "<main.py>", line 4, in send_request
RuntimeError: Send request timeout!This approach allows for effective error handling while maintaining critical debugging information.
Note for beginners: Some of you may now start asking: why bother with
tracebackmodule to obtain debugging information if just callingraisedirectly after printing error inexceptblock will display them too? The thing is that in this example we don’t want to exit the application after the exception has been thrown. Re-raising the exception will indeed preserve all the display information, but it will also terminate the program.
Avoid mutable default arguments
Using mutable default arguments (like lists or dictionaries) in function definitions can lead to unexpected behavior because they maintain their state across multiple function calls. Consider the following example:
def add_item(item, items=[]):
items.append(item)
return items
print(add_item(2))
print(add_item(3))You might expect each call to produce a single-element list, [2] and [3]. However, the actual output is:
[2]
[2, 3]This is what I meant by saying about retaining state across multiple function calls. The default value of items has changed from an empty list to a list with 2 inside added there during the first function call.

AI is powerful. Snippets are instant.
Stop prompting for the same patterns repeatedly. Get almost 100 free VS Code snippets for C++, Python, CMake and Bazel from piko::snippets GitHub repository.
Use virtual environments
Python’s virtualenv or venv modules allow you to create isolated environments for your projects. This practice keeps dependencies required by different projects separate and prevents version conflicts. To create a virtual environment:
python -m venv myenvIn order to use the environment, you need to first activate it in the currently used terminal. To do that on Windows, call:
myenv\Scripts\activateOn Unix or MacOS:
source myenv/bin/activateFrom now on, you can use pip install as usual and whenever you want to save your project’s dependencies, store the in a requirements.txt file suing command:
pip freeze > requirements.txtCommit this file to your project’s repository because it will allow everyone else to install all the necessary dependencies in their local virtual environments by calling:
pip install -r requirements.txtUse enumerate for indexing
When iterating over a list and you need both the index and the value, use enumerate() instead of manually managing the index variable. This approach is more Pythonic and eliminates errors associated with manual index handling.
Instead of:
index = 0
for value in my_list:
print(index, value)
index += 1Use:
for index, value in enumerate(my_list):
print(index, value)enumerate() is not only cleaner but also more readable, making your code less prone to bugs.
Use context managers for resource anagement
If you’re familiar with RAII concept (Resource Acquisition Is Initialization), context managers may be treated as RAII implementation for Python. They ensure that resources like files, sockets or database connections are properly managed and automatically cleaned up after use. The most common example can be opening a file:
with open('file.txt', 'r') as f:
content = f.read()After that, f is automatically closed at the end of with block.
Read also:
- GTest and short-circuit evaluation in C++
- AI is powerful. Snippets are instant.
- From AUTOSAR to S-Core: the first C++ pub/sub implementation
- How to write Arduino Uno code with Python?
- Combining Bazel with Docker
- Running commands with timeout on Linux
- Running Python unit tests with CMake
- Thirdparty dependencies with FetchContent
- Bug of the week #11
- Combining CMake with Docker
- How to search the internet from Linux terminal?
- Folding expressions in C++
- How to derive from an enum in Python?
- Bug of the week #10
- Trying ROS2: client/server within a single container
- Make C++ a better place #4: Go as an alternative
- How to convert hex to dec in Linux terminal?
- Setting up a Python project with CMake
- Separating builds for different configs with Bazel
- Trying ROS2: pub/sub within a single container
- Bug of the week #9
- UDP multicasting with Python
- Destruction order vs thread safety in C++
- Let’s review some code: C++ #2
- Make C++ a better place #3: D as an alternative
- Registering callback using std::function in C++
- Bug of the week #8
- TCP client/server with Python
- Simple menus in Bash scripts with select
- Calling member function on a nullptr in C++
- Bug of the week #7
- Python lru_cache explained
- How to dockerize a Python application?
- Make C++ a better place #2: CppFront as an alternative
- Parameters combinations in GoogleTest
- Data transfer with curl
- Python reduce explained
- Bug of the week #6
- Custom literals in C++
- Linux and hash command
- 5 Python good practices which make life easier
- Let’s review some code: Python #1
- Make C++ a better place #1: What does better mean
- Enums vs enum class in C++
- Bug of the week #5
- UDP client/server with Python
- Hard links in Linux
- Functions calling order in unit tests in C++
- Bug of the week #4
- Yield in Python – state machines, coroutines and more
- Copy files from another branch with Git
- Make C++ a better place #0: Introduction
- 5 misconceptions about std::move in C++
- How to use xargs on Linux?
- How to test method call order with unittest in Python?
- Bug of the week #3
- Build & run C++ unit tests with CMake
- Arrange text with sort on Linux
- Key derivation function with Python
- Let’s review some code #1: C++









