Skip to content

Commit 4e8a58e

Browse files
authored
Better 111 support (#230)
1 parent b6c5ed5 commit 4e8a58e

File tree

77 files changed

+1656
-323
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+1656
-323
lines changed

CLAUDE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ For verification of all code changes made, always run:
1313
The command above should complete without errors.
1414
Note that it isn't necessary to run bazel test on individual targets targets since the codebase is small - for simplicity just always use `bazel test //...`.
1515

16-
`bazel run //:gen -- --package examples --destdir tests/examples_goldfiles`
16+
`rm -rf tests/examples_goldfiles/examples && bazel run //:gen -- --package examples --destdir tests/examples_goldfiles`
1717
The command above should complete without errors
18-
There should be no diffs un the tests/examples_goldfiles directory
18+
There should be no diffs under the tests/examples_goldfiles/examples directory
1919

2020
`ruff check src tests misc`
2121
The command above should complete without errors

MODULE.bazel.lock

Lines changed: 919 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/python/111/BUILD

Lines changed: 0 additions & 19 deletions
This file was deleted.

examples/python/111/README.md

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,48 @@
1-
# 111
1+
# 1:1:1
22

3-
This example shows that a Python project may be organized using Bazel's [1:1:1](https://bazel.build/basics/dependencies#using_fine-grained_modules_and_the_111_rule) rule.
3+
This example shows a Python project using Bazel's [1:1:1](https://bazel.build/basics/dependencies#using_fine-grained_modules_and_the_111_rule) structure of "one BUILD file per Bazel package". Why is this interesting? Because the `1:1:1` structure is a common layout when using Bazel, especially when using [Gazelle](https://github.com/bazel-contrib/bazel-gazelle) to generate BUILD files. However, when generating manifests and artifacts for a package manager, we typically do not want to generate at the granularity of the Bazel package level but instead at the "project level" - Poppy handles this translation.
44

5-
Note that this works well for Python projects because the wheel file construction is external to Bazel. On the other hand, this doesn't work well for Java based projects because the jar file building is done by Bazel (and so with 1:1:1 we'd end up with too many jar files - they would have to be merged into a single jar before being uploaded to the package manager).
65

6+
## Looking around
77

8-
## The pyproject.toml file
8+
This example consists of 2 libraries, [communicator](communicator) and [computer](computer).
9+
- `communicator` - has modules [phone](communicator/phone) and [caller](communicator/caller). The phone module has [111 mode enabled](communicator/phone/md/pyproject.in): `generation_mode` is set to `dynamic_111`. The `caller` module depends on the `phone` module.
10+
- `computer` - has modules [amiga](computer/amiga) and [user](computer/user). Both modules have 111 mode enabled. The `user` module depends on the `amiga` module.
11+
- Library dependencies: `communicator` depends on `computer`; this link exists bcause `phone` depends on `amiga` [here](communicator/phone/src/phone/emulator/BUILD).
912

10-
Run this command:
13+
The following `poppy` command shows how both libraries are related:
1114

1215
```
13-
bazel run @poppy//package/py -- -l examples/python/111 -a gen
16+
bazel run //:query -- --package examples/python/111/communicator --library_release_plan_tree
17+
18+
examples/python/111/communicator ++ 2.0.1
19+
examples/python/111/computer ++ 1.0.1
20+
21+
++ artifact has never been released
22+
```
23+
24+
25+
## About 1:1:1 mode
26+
27+
Support for `1:1:1` package structure must be set explicitly in the manifest file: `generation_mode = "dynamic_111"` [example](communicator/phone/md/pyproject.in).
28+
Additionally, the manifest file must specify an "aggregation" target that has as dependencies all child `1:1:1` targets, in that example linked below that is configured by `target_name = "venv"`. For Python, this works well with a `venv` target. Note that if the `venv` target includes tests, those should be excluded explicitly (in the linked example, that is done using `excluded_dependency_paths = ["tests"]`.
29+
Finally, the module structure must follow the modern Python project structure with top level `src` and `tests` directories.
30+
31+
32+
## The 1:1:1 manifest
33+
34+
The generated manifest (pyproject.toml) aggregates all dependencies from child Bazel packages if they are:
35+
- External (pip) dependencies
36+
- Source dependencies that point outside of the 1:1:1 module
37+
38+
As an example, generate the `communicator` library manifest files:
39+
1440
```
41+
bazel run //:gen -- --package examples/python/111/communicator --destdir /tmp/py
42+
```
43+
44+
Since `communicator` depends on `computer`, and each library has 2 modules, 4 manifests are generated.
45+
46+
The `communicator/phone` manifest at `/tmp/py/examples/python/111/communicator/phone/pyproject.toml` lists these dependencies:
1547

16-
Note [111_mode is enabled](md/pyproject.in) and that the child Bazel packages do not define any Poppy related metadata - dependencies declared in child Bazel packages' BUILD.bazel files are "pushed up" to, and included in, the `pyproject.toml` file generated for the `111` project.
48+
- `uvicorn[standard]>=0.34.0` brought in through [communicator/phone/src/phone/ringtone/BUILD](communicator/phone/src/phone/ringtone/BUILD)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
load("@aspect_rules_py//py:defs.bzl", "py_binary")
2+
3+
4+
py_binary(
5+
name = "caller",
6+
srcs = ["main.py",],
7+
deps = ["//examples/python/111/communicator/phone/src/phone"],
8+
)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import mobile
2+
3+
4+
def run():
5+
# let's make a call
6+
for event in mobile.make_outbound_call():
7+
print(event)
8+
9+
# start the amiga emulator to play lemmings
10+
mobile.run_amiga_emulator()
11+
12+
13+
if __name__ == "__main__":
14+
run()
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
artifact(
2+
artifact_id = "caller",
3+
version = "2.0.1",
4+
generation_mode = "dynamic",
5+
excluded_dependency_paths = ["tests"],
6+
)
7+
8+
artifact_update(
9+
version_increment_strategy = "minor",
10+
)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
load("@aspect_rules_py//py:defs.bzl", "py_venv")
2+
3+
4+
py_venv(
5+
name = "venv",
6+
testonly = True,
7+
deps = [
8+
"//examples/python/111/communicator/phone/src/phone",
9+
"//examples/python/111/communicator/phone/src/phone/emulator",
10+
"//examples/python/111/communicator/phone/src/phone/message",
11+
"//examples/python/111/communicator/phone/src/phone/ringtone",
12+
"//examples/python/111/communicator/phone/tests",
13+
],
14+
)
15+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
artifact(
2+
artifact_id = "phone",
3+
version = "2.0.1",
4+
generation_mode = "dynamic_111",
5+
target_name = "venv",
6+
excluded_dependency_paths = ["tests"],
7+
)
8+
9+
artifact_update(
10+
version_increment_strategy = "minor",
11+
)

0 commit comments

Comments
 (0)