This course assumes that you selected the “Python” option when you ran aspect init
to create a new repository and also enabled Code Generation, Formatting and Linting, Version Stamping, and OCI Containers.
Requests example code
Begin with the “getting started” code for the Python “requests” module. This is an external package that gives an example of installing it from PyPI.
Create a folder named app
, then create a file __main__.py
(double underscores before and after main
). This is just the Python convention for an executable entry-point.
Copy the first codeblock from https://www.w3schools.com/python/module_requests.asp:
import requests
x = requests.get('https://w3schools.com/python/demopage.htm')
print(x.text)
Declare and pin requirements
Since the requests
package is a new dependency of the repository, you first need to declare that. This example uses a pyproject.toml
. Note that Bazel’s rules_python supports the simpler requirements.txt
format as well.
Add the “requests” string to the dependencies
section, now it should look like this:
dependencies = [
"requests",
]
Next you need to “compile” or “pin” the dependencies. Bazel can only provide reproducible builds if you give fully-locked description of the dependencies. There are a few options for pip compile
tools. This exercise uses uv pip compile
which comes from rules_uv, and it provides executable targets to update the pinned dependencies, which are used like bazel run //requirements:runtime.update
.
Take a look in the requirements
folder. There are files for different purposes; for example test.in
lists the pytest
dependency we’ll need for testing. Since there are multiple pinned dependencies, it would require multiple bazel run
commands to update everything. Our repository has a convenience script to make this easy, so you should only need to run:
% ./tools/repin
This results in updates to the requirements/all.in
file, which has the complete, constraint-solved set of dependencies for our repo. It includes hashes of each package for better supply-chain security.
This file is used by rules_python to install dependencies. Note in MODULE.bazel
that it’s referenced by a pip.parse
module extension:
pip.parse(
hub_name = "pip",
python_version = "3.12",
requirements_lock = "//requirements:all.txt",
)
Now we should be ready to use the dependency from Bazel-managed code.
Generate BUILD files
For Bazel to understand our Python code, it expects BUILD
files containing Python rules. Since we’ve written a program, we need to add a py_binary
rule to make it runnable.
As we learned in the Bazel 101 course, we only need to run aspect configure
.
We’ve compiled in the Gazelle extension from rules_python, which is much easier than building it yourself. If you don’t use the Aspect CLI, see the 203 course for a guide to installing Gazelle.
Now look in app/BUILD.bazel
to see the generated content.
At this point our py_binary
should be functional, so we can run it:
% bazel run app:app_bin