DEV Community

Cover image for Modern Bazel with Python- Module 1 - Basics
Sushil Baligar
Sushil Baligar

Posted on • Edited on

Modern Bazel with Python- Module 1 - Basics

Why Bazel is Revolutionary for Python Development

Before diving into code, let's understand why Google open-sourced their internal build system and why it's becoming essential for serious Python development.

The Problem with Traditional Python Build Systems

Most Python developers are familiar with this workflow:

pip install -r requirements.txt
python setup.py build
python -m pytest
Enter fullscreen mode Exit fullscreen mode

This approach breaks down at scale:

  1. Inconsistent builds: "Works on my machine" syndrome
  2. Slow rebuilds: Everything rebuilds even when only one file changes
  3. Dependency hell: Version conflicts across projects
  4. No parallelization: Tests and builds run sequentially
  5. Language barriers: Hard to integrate C++, Java, or other languages

How Bazel Solves These Problems

Bazel introduces several revolutionary concepts:

Hermetic Builds: Same inputs always produce identical outputs, regardless of the machine or environment.

Incremental Builds: Only rebuilds what actually changed, using cryptographic hashing to detect changes.

Massive Parallelization: Builds independent targets simultaneously across multiple cores.

Remote Caching: Share build artifacts across your entire team or CI/CD pipeline.

Multi-language Support: Python, Java, C++, Go, and more in a single build system.

Why MODULE.bazel Over WORKSPACE

Bazel is transitioning from WORKSPACE files to MODULE.bazel (called "Bzlmod") for several reasons:

  1. Better dependency resolution: Handles version conflicts automatically
  2. Simplified syntax: Less boilerplate, more intuitive
  3. Improved performance: Faster loading and resolution
  4. Future-proof: This is where Bazel is heading

Now let's build our first modern Bazel Python project!


Setting Up Your First Modern Bazel Python Project

Project Structure Overview

We'll create a minimal but complete Bazel project:

bazel-python-tutorial/
├── MODULE.bazel          # Modern dependency management (replaces WORKSPACE)
├── .bazelrc             # Build configuration
├── .bazelversion        # Lock Bazel version for team consistency
├── BUILD.bazel          # Build instructions for this directory
└── hello.py             # Our Python source code
Enter fullscreen mode Exit fullscreen mode

Step 1: Initialize Your Project

Create your project directory:

mkdir bazel-python-tutorial
cd bazel-python-tutorial
Enter fullscreen mode Exit fullscreen mode

Step 2: Create MODULE.bazel - The Modern Way

The MODULE.bazel file is your project's dependency manifest. It's cleaner and more powerful than the old WORKSPACE approach.

# MODULE.bazel
module(
    name = "bazel_python_tutorial",
    version = "1.0.0",
)

bazel_dep(name = "rules_python", version = "0.29.0")

# Simplified Python setup - let Bazel find system Python
python = use_extension("@rules_python//python/extensions:python.bzl", "python")
python.toolchain(
    python_version = "3.11",
)
Enter fullscreen mode Exit fullscreen mode

Key Differences from WORKSPACE:

  • No need for http_archive or SHA hashes
  • Automatic version resolution
  • Cleaner, more declarative syntax
  • Built-in dependency management

Step 3: Create .bazelrc - Build Configuration

# .bazelrc
# Configuration file that sets default behavior for Bazel commands
# Think of this as your "build preferences"

# === Build Configuration ===
build --verbose_failures
build --show_progress_rate_limit=5

test --test_output=errors
test --test_summary=detailed

common --enable_bzlmod
Enter fullscreen mode Exit fullscreen mode

Step 4: Lock Bazel Version (.bazelversion)

7.1.1
Enter fullscreen mode Exit fullscreen mode

Step 5: Create BUILD.bazel - Build Instructions

# BUILD.bazel
"""
Build instructions for the root directory.
Every directory containing source code needs a BUILD.bazel file.
"""

# Import the py_binary rule from Python rules
load("@rules_python//python:defs.bzl", "py_binary")

# Define a Python executable target
py_binary(
    name = "hello",
    srcs = ["hello.py"],           # Source files to include
    main = "hello.py",             # Entry point file
)
Enter fullscreen mode Exit fullscreen mode

Understanding py_binary:

  • name: What you'll type in bazel run //:{name}
  • srcs: List of Python files this target includes
  • main: Which file contains if __name__ == "__main__":
  • python_version: Ensures Python 3 compatibility

Step 6: Write Your Python Code

#!/usr/bin/env python3
# hello.py
"""
Your first Bazel Python program!
This demonstrates modern Bazel with Python.
"""

import sys
from typing import List

def create_greeting(name: str, enthusiasm_level: int = 1) -> str:
    """
    Create a personalized greeting with variable enthusiasm.

    Args:
        name: The name to greet
        enthusiasm_level: Number of exclamation marks (1-3)

    Returns:
        A formatted greeting string
    """
    exclamation = "!" * min(max(enthusiasm_level, 1), 3)
    return f"Hello, {name}{exclamation}"

def display_bazel_info() -> None:
    """Display information about this Bazel build."""
    print("🚀 Modern Bazel + Python Demo")
    print(f"Python version: {sys.version}")
    print(f"Running from: {__file__}")
    print("Built with: Bazel + MODULE.bazel (Bzlmod)")
    print("-" * 50)

def main(args: List[str] = None) -> None:
    """
    Main entry point of our application.

    Args:
        args: Command line arguments (optional)
    """
    display_bazel_info()

    # Basic greeting
    print(create_greeting("Bazel World"))

    # Enthusiastic greeting
    print(create_greeting("Modern Python Developer", 3))

    # Success message
    print("\n✅ Congratulations! You've successfully:")
    print("   • Set up modern Bazel with MODULE.bazel")
    print("   • Built your first py_binary target")
    print("   • Used Bzlmod for dependency management")
    print("   • Created a reproducible, scalable build")

if __name__ == "__main__":
    main(sys.argv[1:])
Enter fullscreen mode Exit fullscreen mode

Building and Running Your First Modern Bazel Python Program

Build the Project

# Build your hello target
bazel build //:hello
Enter fullscreen mode Exit fullscreen mode

What happens behind the scenes:

  1. Bazel reads MODULE.bazel and downloads Python rules
  2. Sets up Python 3.11 toolchain
  3. Analyzes BUILD.bazel to understand dependencies
  4. Compiles and packages your Python code
  5. Creates executable in bazel-bin/

Run the Program

# Run your hello target
bazel run //:hello
Enter fullscreen mode Exit fullscreen mode

Expected Output:

🚀 Modern Bazel + Python Demo
Python version: 3.11.x (main, ...)
Running from: /path/to/your/project/hello.py
Built with: Bazel + MODULE.bazel (Bzlmod)
--------------------------------------------------
Hello, Bazel World!
Hello, Modern Python Developer!!!

✅ Congratulations! You've successfully:
   • Set up modern Bazel with MODULE.bazel
   • Built your first py_binary target
   • Used Bzlmod for dependency management
   • Created a reproducible, scalable build
Enter fullscreen mode Exit fullscreen mode

Understanding Bazel Labels

The //:hello syntax is called a "Bazel label":

  • // = Root of the workspace (where MODULE.bazel lives)
  • : = Separator between package and target
  • hello = Target name (from name = "hello" in BUILD.bazel)

Useful Commands to Try

# List all available targets in your project
bazel query //...

# Get detailed information about the hello target
bazel query //:hello --output=build

# See what files Bazel generated
ls -la bazel-bin/

# Clean all build outputs
bazel clean

# Build with verbose output (great for learning)
bazel build //:hello --verbose_failures --announce_rc
Enter fullscreen mode Exit fullscreen mode

What Makes This Better Than Traditional Python?

Reproducible Builds

Every developer on your team will get identical builds because:

  • Python version is locked via MODULE.bazel
  • Bazel version is locked via .bazelversion
  • All dependencies are precisely specified

Incremental Builds

Change one line in hello.py and rebuild:

# Edit hello.py, then rebuild
bazel build //:hello
Enter fullscreen mode Exit fullscreen mode

Bazel only rebuilds what changed - incredibly fast!

Scalability Foundation

This simple setup scales to:

  • Hundreds of Python modules
  • Mixed-language projects (Python + C++ + Java)
  • Microservices architectures
  • Thousands of developers

Hope this helps! Follow me for more such updates!
https://www.linkedin.com/in/sushilbaligar/
https://github.com/sushilbaligar
https://dev.to/sushilbaligar
https://medium.com/@sushilbaligar

Top comments (0)