bzd-python
How to run, test, build, type-check, format, and manage Python dependencies in the bzd monorepo
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
How to run, test, build, type-check, format, and manage Python dependencies in the bzd monorepo
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
How to write, build, test, and contribute Node.js/JavaScript/Vue code in the bzd monorepo
Core software design principles — ALWAYS load this before planning and before writing any implementation code
How to build, test, run, and maintain Bazel targets in the bzd monorepo — always load this before invoking any ./tools/bazel command
How to read, write, and wire BDL (Bzd Description Language) files and their Bazel rules in the bzd monorepo — load this before touching any .bdl file or bdl_* Bazel rule
How to write, build, test, and contribute C++ code in the bzd monorepo
How to build/test esp32/esp32s3/etc targets in the bzd monorepo.
| name | bzd-python |
| description | How to run, test, build, type-check, format, and manage Python dependencies in the bzd monorepo |
| compatibility | opencode |
rules_python (hermetic interpreter — never invoke system python3 or pip directly)--config=mypy)unittest (no pytest)camelCase for functions/variables (repo-wide convention, not standard Python snake_case)| Path | Purpose |
|---|---|
python/bzd/ | Core bzd_python library module (parsers, utils, HTTP, logging, validation, etc.) |
python/MODULE.bazel | Declares the bzd_python Bazel module |
python/defs.bzl | Public API re-exports for custom Python Bazel rules |
python/requirements.in | Direct pip deps for bzd_python library — edit this to add deps |
python/requirements.txt | Locked pip deps for bzd_python (auto-generated, do NOT edit manually) |
tools/python/requirements.in | Direct pip deps for root workspace apps/tools — edit this to add deps |
tools/python/requirements.txt | Locked pip deps for root workspace (auto-generated, do NOT edit manually) |
tools/python/pyproject.toml | Config for mypy, yapf, ruff (tabs, 120-col limit) |
tools/python/BUILD.bazel | Bazel targets for mypy, ruff, yapf, codespell binaries |
| Hub name | requirements.in | Used in BUILD files via |
|---|---|---|
bzd_python_pip | python/requirements.in | load("@bzd_python_pip//:requirements.bzl", "requirement") |
pip | tools/python/requirements.in | load("@pip//:requirements.bzl", "requirement") |
# Run all python tests in the bzd_python library module
./tools/bazel test @bzd_python//...
# Run a specific test target
./tools/bazel test @bzd_python//bzd/utils/tests:dict
./tools/bazel test @bzd_python//bzd/parser/tests:grammar
# Build a py_binary
./tools/bazel build @bzd_python//bzd/apps/map_analyzer:map_analyzer
# Run a py_binary
./tools/bazel run @bzd_python//bzd/apps/map_analyzer:map_analyzer
# Run all Python tests in the root workspace
./tools/bazel test //apps/...
mypy runs as a Bazel build aspect — not a standalone command.
# Run mypy on the entire bzd_python library module
./tools/bazel build --config=mypy @bzd_python//...
# Run mypy on all root workspace targets
./tools/bazel build --config=mypy //...
# Run mypy on a single target
./tools/bazel build --config=mypy @bzd_python//bzd/utils:run
mypy settings (from tools/python/pyproject.toml):
"mypy-ignore" in its BUILD.bazel entryType hints are mandatory on all function signatures.
# Run sanitizer on changed files (formats + lints, includes ruff for Python)
./tools/bazel run //:sanitizer
# Run on ALL files in the repo
./tools/bazel run //:sanitizer -- --all
# Check-only mode (reports issues, no writes)
./tools/bazel run //:sanitizer -- --check --all
Style rules (from tools/python/pyproject.toml):
To exclude a file or directory from sanitizer checks, place a .sanitizerignore file in that directory.
Create the .py source file(s):
python/bzd/<module>/my_lib.pyapps/<app_name>/my_module.pyCreate or update the BUILD.bazel:
load("@rules_python//python:defs.bzl", "py_library")
py_library(
name = "my_lib",
srcs = ["my_lib.py"],
visibility = ["//visibility:public"],
deps = [
"@bzd_python//bzd/utils:run", # example internal dep
],
)
Import paths follow the directory structure from the workspace root:
python/bzd/utils/my_lib.py → from bzd.utils.my_lib import MyClassapps/my_app/my_module.py → from apps.my_app.my_module import MyClassReference in other BUILD files:
python/ (same module): "//bzd/utils:my_lib"bzd_python: "@bzd_python//bzd/utils:my_lib"apps/ targeting another app: "//apps/my_app:my_module"python/bzd/ library code (hub: bzd_python_pip)Add the package name to python/requirements.in
Regenerate the lockfile:
./tools/bazel run @bzd_python//:requirements.update
Use in BUILD.bazel:
load("@bzd_python_pip//:requirements.bzl", "requirement")
py_library(
name = "my_lib",
srcs = ["my_lib.py"],
deps = [requirement("paramiko")],
)
apps/ or tools/ code (hub: pip)Add the package name to tools/python/requirements.in
Regenerate the lockfile:
./tools/bazel run //tools/python:requirements.update
Use in BUILD.bazel:
load("@pip//:requirements.bzl", "requirement")
py_binary(
name = "my_app",
srcs = ["my_app.py"],
deps = [requirement("pillow")],
)
All tests use Python's built-in unittest. Test file naming convention: <module>_test.py.
# my_feature_test.py
import unittest
class TestMyFeature(unittest.TestCase):
def testSomething(self) -> None:
self.assertEqual(1 + 1, 2)
if __name__ == "__main__":
unittest.main()
# BUILD.bazel
load("@rules_python//python:defs.bzl", "py_test")
py_test(
name = "my_feature",
srcs = ["my_feature_test.py"],
main = "my_feature_test.py",
visibility = ["//visibility:public"],
deps = ["//my_lib:my_feature"],
)
Use args with $(location ...) and data to pass file paths to the test:
py_test(
name = "parser",
srcs = ["parser_test.py"],
args = [
"$(location testdata.json)",
"$(location testdata2.json)",
],
data = [
"testdata.json",
"testdata2.json",
],
deps = [":parser"],
)
In the test file, read paths from sys.argv:
import sys, pathlib, unittest
class TestParser(unittest.TestCase):
dataFile: pathlib.Path
if __name__ == "__main__":
TestParser.dataFile = pathlib.Path(sys.argv.pop(1))
unittest.main()
[py_test(
name = path.replace(".py", ""),
srcs = [path],
deps = [":my_lib"],
) for path in glob(["*_test.py"])]
All custom rules are in python/private/ and exported via python/defs.bzl.
bzd_python_binary_libraryExposes Bazel-built executables as importable Python path constants:
load("@bzd_python//:defs.bzl", "bzd_python_binary_library")
bzd_python_binary_library(
name = "my_binaries",
executables = {"//tools/my_tool": "my_tool"},
)
bzd_python_hermetic_launcherWraps a py_binary to use the hermetic Bazel Python interpreter instead of the system one:
load("@bzd_python//:defs.bzl", "bzd_python_hermetic_launcher")
bzd_python_hermetic_launcher(
name = "my_app.hermetic",
binary = ":my_app",
)
bzd_python_ociBuilds a Docker OCI image from a py_binary (automatically applies the hermetic launcher):
load("@bzd_python//:defs.bzl", "bzd_python_oci")
bzd_python_oci(
name = "image",
binary = ":my_app",
base = "@oci_base_image",
)
# Produces: :image (OCI artifact), :image.load (for `docker load`)
from typing import ...)Args:, Returns:)camelCase for functions and variables (repo-wide convention)PascalCasemyHelper_(), value_)print(): use bzd.logging (from bzd.logging import Logger)importlib: declare all deps in BUILD.bazel#bzd/ import prefix: that pattern applies only to Node.js files# Full quality gate (runs everything: tests, mypy, sanitizer)
./quality_gate.sh
# Static analysis only (mypy aspect on all targets)
./tools/bazel build --config=mypy //...
# Sanitizer check only (ruff + codespell)
./tools/bazel run //:sanitizer -- --check --all
The CI pipeline runs @bzd_python//... in the test stage and --config=mypy in the static analysis stage. Always run the sanitizer before committing.