Setting Up MonoRepo Using Bazel (Part-2 Python)

Akhilesh Mahajan
4 min readMar 16, 2023

--

In Part-1, I have explained how we can set up Monorepo using Bazel in Go-lang. Now, In this part, I will explain the same in Python.

Step 1: Install Bazel

Before we can begin setting up our monorepo, we need to make sure that Bazel is installed on our system. Bazel can be installed on Linux, macOS, and Windows. You can find the installation instructions for your platform on the official Bazel website.

Bazel

Step 2: Create a Workspace

In Bazel, a workspace is a directory that contains all of the source code and build files for your projects. To create a new workspace, create a new directory and add a WORKSPACE file to it. The WORKSPACE file tells Bazel that this directory is a workspace and specifies any external dependencies that your projects may have.

Step 3: Define the Project Directory

Each project in your monorepo should be defined as a Golang package. You can create a directory for each package and include a “BUILD” file in each directory to define the build rules for the package.

  1. Create your projects folder as in the below image.
Creating python project in the projects directory

2. In the python folder, create a directory sample_library and python_app.

3. In both the directory, create BUILD files and respective calculator.py and app.py files.

Step 4: Add Dependencies

  1. Define the external dependencies in the WORKSPACE file. (To load any external dependencies)
###############################################################################################
################################ PYTHON RULES LOADING #########################################
###############################################################################################
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
rules_python_version = "740825b7f74930c62f44af95c9a4c1bd428d2c53" # Latest @ 2021-06-23
http_archive(
name = "rules_python",
sha256 = "09a3c4791c61b62c2cbc5b2cbea4ccc32487b38c7a2cc8f87a794d7a659cc742", #"3474c5815da4cb003ff22811a36a11894927eda1c2e64bf2dac63e914bfdf30f",
strip_prefix = "rules_python-{}".format(rules_python_version),
url = "https://github.com/bazelbuild/rules_python/archive/{}.zip".format(rules_python_version),
)
# TO LOAD ANY EXTERNAL PYTHON LIBRARY, LOAD pip_parse rule
# DEFINE A REQUIREMENTS_LOCK.TXT FILE AND INSTALL THE DEPENCIES PRESENT IN IThon
load("@rules_python//python:pip.bzl", "pip_parse")
# Create a central repo that knows about the dependencies needed from
# requirements_lock.txt.
pip_parse(
name = "python_deps",
requirements_lock = "//:requirements_lock.txt",
)
# Load the starlark macro which will define your dependencies.
load("@python_deps//:requirements.bzl", "install_deps")
# Call it to define repos for your requirements.
install_deps()

This will load all the rules defined for python in bazel.

NOTE: Important points to know:

  • pip_parse is the function in the python rules that will install dependencies from requirements_lock.txt file.
  • create a central external repo using the pip_parse function.
  • this will create requirements.bzl file in the central external repo within which we have install_deps a function.
  • this function call will install dependencies in the requirements_lock.txt file.

2. To use the rules, write the following code in the BUILD file of the sample_library directory.

py_library(
name = "calculator",
srcs = ["calculator.py"],
visibility = ["//visibility:public"],
)
py_test(
name = "calculator_test",
srcs = ["calculator_test.py"],
deps = [
"//projects/python_folder/sample_library:calculator",
],
)

The py_library rule set the functions in the calcuator.py as library functions. If you want to use any function from this package, import the library using the path of the file.

Example: from projects.python_folder.sample_library.calculator import Calculator

3. Similarly, define the rules in the BUILD file of the python_app directory.

load("@python_deps//:requirements.bzl", "requirement")
py_binary(
name = "app",
srcs = ["app.py"],
deps = [
"//projects/python_folder/sample_library:calculator",
requirement("Flask"), #or '@python_deps_pypi__flask//:pkg'
],
main = "app.py"
)
py_image(
name = "samplepython_image",
srcs = ["app.py"],
main = "app.py",
# platform = "local",
deps = [
"//projects/python_folder/sample_library:calculator",
requirement("Flask"),
],
)
  • py_binary function will set the corresponding file into the binary executable file.
  • To use the external downloaded dependencies in code, load the central external repo (requirements) we created in the previous step in BUILD file of your project. And then add the respective dependency in deps section

Note:
A) There are 2 ways to specify the dependency.

1. Using requirement function
requirements("Flask")
2. Directly mentioning the package
@python_deps_pypi__flask//:pkg
General: @{central_repo_name}_{package_ name}//:pkg

B) This dependency on the libaray means, when you run app.py file in app folder, it will first build the files that are defined in the deps ( dependencies ), then after this build app.py file.

Step 5: Build and Run files

  • To BUILD the Build. bazel files

Run bazel build //Projects/python_folder/... or bazel build //...

  • To RUN all the test files in the python project

Run bazel test //Projects/python_folder/calculator/...

  • To RUN the Flask application

Run bazel run //Projects/python_folder/python_app:app

The Localhost server is up and ready to listen to the GET request.

Github Repo Link: https://github.com/Akhilesh53/Monorepo

References: https://github.com/bazelbuild/rules_go

Follow on LinkedIn for more…

--

--