Skip to content

Commit 48ddf38

Browse files
Merge branch 'aspect-build:main' into main
2 parents bf10020 + d639754 commit 48ddf38

114 files changed

Lines changed: 4716 additions & 674 deletions

File tree

Some content is hidden

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

.github/workflows/ci-workflows.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,21 @@ jobs:
5959
- name: Test
6060
working-directory: ${{ matrix.workspace.path }}
6161
run: aspect test --task-key=test-${{ matrix.workspace.slug }}-${{ matrix.bazel.id }} ${{ matrix.bazel.flags }} -- //...
62+
# Standalone scripts that drive bazel directly (e.g. asserting a
63+
# build *fails*), so they can't be an `sh_test` under `//...` above.
64+
- name: e2e standalone test.sh scripts
65+
if: matrix.workspace.slug == 'e2e'
66+
run: |
67+
set -uo pipefail
68+
status=0
69+
for script in e2e/cases/*/test.sh; do
70+
echo "::group::${script}"
71+
if bash "${script}"; then
72+
echo "PASS: ${script}"
73+
else
74+
echo "FAIL: ${script}"
75+
status=1
76+
fi
77+
echo "::endgroup::"
78+
done
79+
exit "${status}"

MODULE.bazel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ bazel_dep(name = "rules_python", version = "1.0.0")
2020
bazel_dep(name = "rules_shell", version = "0.6.1")
2121
bazel_dep(name = "tar.bzl", version = "0.10.1")
2222
bazel_dep(name = "with_cfg.bzl", version = "0.14.6")
23-
bazel_dep(name = "hermetic_launcher", version = "0.0.9")
23+
bazel_dep(name = "hermetic_launcher", version = "0.0.11")
2424

2525
bazel_lib_toolchains = use_extension("@tar.bzl//tar:extensions.bzl", "toolchains")
2626
use_repo(bazel_lib_toolchains, "bsd_tar_toolchains")

README.md

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -368,16 +368,25 @@ the link target on a subset of binaries.
368368
369369
### Debugger Support (VSCode/PyCharm)
370370

371-
Attach debuggers using `debugpy`:
371+
Attach DAP-compatible debuggers (VSCode, PyCharm, Neovim, etc.) using
372+
[debugpy](https://github.com/microsoft/debugpy). This requires a wrapper
373+
entrypoint that starts a debugpy listener before running your application —
374+
simply adding `debugpy` to `deps` is not enough.
372375

373-
```starlark
374-
# In debug mode, wraps the binary with debugpy listener
375-
py_binary(
376-
name = "app_debug",
377-
srcs = ["main.py"],
378-
deps = ["//:lib", "@pypi//debugpy"],
379-
env = {"DEBUGPY_WAIT": "1"}, # Wait for IDE attachment
380-
)
376+
See the [complete debugger example](examples/debugger/) for a working
377+
setup, including a `py_debuggable_binary` macro that handles the wrapper
378+
generation automatically.
379+
380+
Quick overview:
381+
382+
```sh
383+
cd examples/debugger
384+
385+
# Start with debugpy listener, wait for IDE to attach:
386+
DEBUGPY_WAIT=1 bazel run //:app
387+
388+
# Release mode — no debugpy, runs directly:
389+
bazel run //:app --config=release
381390
```
382391

383392
VSCode `launch.json`:

docs/uv.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,19 @@ over the dependency group targets in which that requirement is defined.
286286
Hub requirement targets are _incompatible_ with dependency group configurations in which the
287287
requirement in question is not defined.
288288

289+
### Conditional dependencies and the empty target
290+
291+
When a package is gated entirely behind an environment marker (e.g.
292+
`iniconfig; sys_platform == 'win32'`), the hub generates a `select()` alias
293+
with one arm per marker expression plus a `//conditions:default` arm that
294+
resolves to an internal empty `py_library`. This makes the `select()` total on
295+
all platforms: on non-Windows hosts the alias selects the empty library, which
296+
contributes no sources and no transitive deps, rather than failing analysis.
297+
298+
If you inspect the generated BUILD files under the hub repository you will see
299+
targets named `empty` and `empty_whl` — these are intentional stubs and not a
300+
sign of a missing dependency.
301+
289302
Each dependency group requirement is backed by a `whl_install` rule which chooses among
290303
prebuilt wheels listed in the lockfile to produce the equivalent of a
291304
`py_library`.

e2e/BUILD.bazel

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,34 @@ write_source_files(
8282
# one constraint string.
8383
"snapshots/whl_install.bare_linux_wheels.grpcio.BUILD.bazel": "@whl_install__uv_bare_linux_wheels__grpcio__1_80_0//:BUILD.bazel",
8484

85+
# whl_install for the azure-core / azure-core-tracing-opentelemetry
86+
# pair (cases/azure-core-tracing-overlap). Pins the namespace_dirs /
87+
# regular_roots metadata emission for both sides of a regular
88+
# package spanning wheels: azure-core's `regular_roots =
89+
# ["azure/core"]` vs the tracing wheel's 4-dir namespace skeleton
90+
# reaching down to `azure/core/tracing/ext`. A regression that
91+
# stops emitting either attr silently degrades the venv's
92+
# PySiteMerge trigger back to the broken `.pth`-only behaviour —
93+
# the runtime test would catch that too, but the snapshot shows
94+
# WHICH side of the metadata went missing.
95+
"snapshots/whl_install.azure_overlap.azure_core.BUILD.bazel": "@whl_install__azure_core_tracing_import__azure_core__1_38_0//:BUILD.bazel",
96+
"snapshots/whl_install.azure_overlap.azure_core_tracing_opentelemetry.BUILD.bazel": "@whl_install__azure_core_tracing_import__azure_core_tracing_opentelemetry__1_0_0b11//:BUILD.bazel",
97+
8598
# whl_install for cryptography 46.0.5, which ships BOTH cp311-abi3
8699
# AND cp38-abi3 wheels for the same platforms. Pins the select_chain
87100
# arm mapping so a regression that drops source-specificity
88101
# disambiguation flips ~150 arms to the cp38 wheel in the diff.
89102
"snapshots/abi3_compat.whl_install.cryptography.BUILD.bazel": "@whl_install__abi3_compat__cryptography__46_0_5//:BUILD.bazel",
90103

104+
# whl_install for cffi 2.0.0, whose platform wheels each install a
105+
# DIFFERENT C-extension top-level (`_cffi_backend.cpython-312-
106+
# darwin.so` vs `...-x86_64-linux-gnu.so` vs win `.pyd`). Pins that
107+
# top-level / console-script metadata is keyed per wheel file, so a
108+
# regression back to unioning across platform wheels (which leaks an
109+
# inactive wheel's package surface into the active configuration)
110+
# shows up as the per-wheel dicts collapsing into one merged list.
111+
"snapshots/abi3_compat.whl_install.cffi.BUILD.bazel": "@whl_install__abi3_compat__cffi__2_0_0//:BUILD.bazel",
112+
91113
# @sdist_build for jpype1 with uv.override_package toolchains/env
92114
# ----------------------------------------------------------------
93115
# Covers the override_package -> repo-rule -> BUILD-template
@@ -106,16 +128,25 @@ write_source_files(
106128
# install_tree.short_path — stable across unrelated lockfile
107129
# churn; only added/removed/version-bumped wheels rotate them.
108130
#
109-
# * pth_namespace_547 — keyed branch, minimal 2-contributor
110-
# namespace case (jaraco-*).
111-
# * firebase_admin — keyed branch at scale (~8 google-*
112-
# namespace contributors); catches ordering / scale-only
113-
# regressions that the 2-line minimum can't.
131+
# * pth_namespace_547 — minimal 2-contributor namespace case
132+
# (jaraco-*). The concrete per-entry namespace merge fully
133+
# covers both wheels, so the snapshot pins that neither
134+
# appears in the .pth fallback.
135+
# * firebase_admin — namespace merge at scale (~8 google-*
136+
# contributors, nested google/cloud namespace); catches
137+
# ordering / scale-only regressions that the 2-wheel
138+
# minimum can't.
114139
# * wheel_root_pth — non-keyed branch via `py_unpacked_wheel`
115140
# without `top_levels`; pins the `site.addsitedir(...)`
116141
# shape so wheel-root `.pth` shims keep running.
142+
# * pth_install_tree_fallback — two install_tree wheels colliding
143+
# on a top-level; the collision loser reaches `_format_imp`'s
144+
# site-packages branch and must emit a PLAIN escape path (not
145+
# `site.addsitedir`, which would re-run its already-projected
146+
# root `.pth`). Reverting that branch changes this snapshot.
117147
"snapshots/pth_namespace_547.test.venv.pth": "//cases/pth-namespace-547:test.venv.pth",
118148
"snapshots/firebase_admin.test_tool.venv.pth": "//cases/firebase-admin-import:test_tool.venv.pth",
119149
"snapshots/wheel_root_pth.venv.pth": "//cases/uv-include-group:wheel_root_pth_test.venv.pth",
150+
"snapshots/pth_install_tree_fallback.venv.pth": "//cases/pth-install-tree-fallback:test.venv.pth",
120151
},
121152
)

e2e/MODULE.bazel

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,13 @@ uv = use_extension("@aspect_rules_py//uv:extensions.bzl", "uv")
9191

9292
# Per-case MODULE fragments. Each redeclares its own use_extension(...) proxies.
9393
include("//cases/arch-alias-marker:setup.MODULE.bazel")
94+
include("//cases/azure-core-tracing-overlap:setup.MODULE.bazel")
95+
include("//cases/azure-namespace-sibling:setup.MODULE.bazel")
9496
include("//cases/cross-repo-610:setup.MODULE.bazel")
9597
include("//cases/current-py-toolchain-bazel-env-896:setup.MODULE.bazel")
9698
include("//cases/firebase-admin-import:setup.MODULE.bazel")
9799
include("//cases/freethreaded-805:setup.MODULE.bazel")
100+
include("//cases/inactive-marker:setup.MODULE.bazel")
98101
include("//cases/interpreter-local-pyvenv-home:setup.MODULE.bazel")
99102
include("//cases/multi-project-hub:setup.MODULE.bazel")
100103
include("//cases/oci:setup.MODULE.bazel")
@@ -127,6 +130,7 @@ include("//cases/uv-pyproject-cases:setup.MODULE.bazel")
127130
include("//cases/uv-requirements-bzl:setup.MODULE.bazel")
128131
include("//cases/uv-sdist-fallback:setup.MODULE.bazel")
129132
include("//cases/uv-sdist-jdk-build:setup.MODULE.bazel")
133+
include("//cases/uv-sdist-mpicc:setup.MODULE.bazel")
130134
include("//cases/uv-sdist-native-build:setup.MODULE.bazel")
131135
include("//cases/uv-toolchain:setup.MODULE.bazel")
132136
include("//cases/uv-whl-install-output-group:setup.MODULE.bazel")
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
load("@aspect_rules_py//py:defs.bzl", "py_test")
2+
3+
# Regression test: overlapping regular-package trees across wheels.
4+
#
5+
# azure-core owns `azure/core/` and `azure/core/tracing/` as regular
6+
# packages (both ship an `__init__.py`); azure-core-tracing-opentelemetry
7+
# installs `azure/core/tracing/ext/` into that same tree from a separate
8+
# wheel. Unlike the PEP 420 namespace cases (pth-namespace-547,
9+
# firebase-admin-import), regular packages don't merge `__path__` across
10+
# sys.path entries — venv assembly has to physically merge the
11+
# overlapping directories or `azure.core.tracing.ext` is unreachable.
12+
#
13+
# Reported by OpenAI: their `oai_otel_init` package hit
14+
# ModuleNotFoundError: No module named 'azure.core.tracing.ext.opentelemetry_span'
15+
# under rules_py while the same pair works in any flat site-packages
16+
# install.
17+
#
18+
# Variants:
19+
# :test — py_test default (.pth strategy)
20+
# :test_tool — py_venv_test variant (symlink-forest venv)
21+
22+
py_test(
23+
name = "test",
24+
srcs = ["test.py"],
25+
dep_group = "azure-core-tracing-import",
26+
main = "test.py",
27+
deps = [
28+
"@pypi_azure_core_tracing//azure_core",
29+
"@pypi_azure_core_tracing//azure_core_tracing_opentelemetry",
30+
],
31+
)
32+
33+
# Naming: `test_tool` (not `test_venv`) to avoid the tree-artifact /
34+
# internal-venv-dir prefix collision described in
35+
# cases/firebase-admin-import/BUILD.bazel.
36+
py_test(
37+
name = "test_tool",
38+
srcs = ["test.py"],
39+
dep_group = "azure-core-tracing-import",
40+
expose_venv = True,
41+
isolated = False,
42+
main = "test.py",
43+
python_version = "3.11",
44+
deps = [
45+
"@pypi_azure_core_tracing//azure_core",
46+
"@pypi_azure_core_tracing//azure_core_tracing_opentelemetry",
47+
],
48+
)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[project]
2+
name = "azure-core-tracing-import"
3+
version = "0.0.0"
4+
# azure-core ships `azure/core/` as a REGULAR package (it has an
5+
# `__init__.py`, only the `azure/` top level is a PEP 420 namespace).
6+
# azure-core-tracing-opentelemetry contributes `azure/core/tracing/ext/`
7+
# INSIDE that regular package from a separate wheel. Regular packages
8+
# don't merge `__path__` across sys.path entries, so this pair only
9+
# works if venv assembly physically merges the overlapping directory
10+
# trees — the namespace `.pth` machinery cannot help here.
11+
requires-python = ">=3.11"
12+
dependencies = [
13+
# `build` + `setuptools` match the hub's default_build_dependencies
14+
# declared in //MODULE.bazel. The uv extension expects every project
15+
# lockfile to be able to resolve them.
16+
"build",
17+
"setuptools",
18+
"azure-core==1.38.0",
19+
"azure-core-tracing-opentelemetry==1.0.0b11",
20+
]
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"""Regression: overlapping regular-package trees across wheels.
2+
3+
azure-core owns the regular package `azure/core/` (and
4+
`azure/core/tracing/`); azure-core-tracing-opentelemetry installs
5+
`azure/core/tracing/ext/` into that same tree from a separate wheel.
6+
"""
7+
8+
uv = use_extension("@aspect_rules_py//uv:extensions.bzl", "uv")
9+
uv.declare_hub(hub_name = "pypi_azure_core_tracing")
10+
uv.project(
11+
hub_name = "pypi_azure_core_tracing",
12+
lock = "//cases/azure-core-tracing-overlap:uv.lock",
13+
pyproject = "//cases/azure-core-tracing-overlap:pyproject.toml",
14+
)
15+
use_repo(uv, "pypi_azure_core_tracing")
16+
17+
# Exposed so e2e/BUILD.bazel can snapshot the namespace_dirs / regular_roots
18+
# metadata the whl_install repo rule emits for this overlapping pair.
19+
use_repo(uv, "whl_install__azure_core_tracing_import__azure_core__1_38_0")
20+
use_repo(uv, "whl_install__azure_core_tracing_import__azure_core_tracing_opentelemetry__1_0_0b11")
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
"""Regression: overlapping regular-package trees across wheels.
2+
3+
azure-core 1.38.0 ships `azure/core/` and `azure/core/tracing/` as
4+
REGULAR packages (each has an `__init__.py`); only the `azure/` top
5+
level is a PEP 420 namespace. azure-core-tracing-opentelemetry
6+
1.0.0b11 ships `azure/core/tracing/ext/opentelemetry_span.py` — a
7+
subpackage nested inside a regular package owned by a *different*
8+
wheel.
9+
10+
Regular packages do not merge `__path__` across sys.path entries, so
11+
the namespace `.pth` + addsitedir machinery (see
12+
cases/firebase-admin-import, cases/pth-namespace-547) cannot make
13+
`azure.core.tracing.ext` reachable: once Python resolves
14+
`azure.core.tracing` to azure-core's directory, the `ext/` directory
15+
contributed by the other wheel is invisible unless venv assembly
16+
physically merges the two trees the way a flat `pip install` into one
17+
site-packages would.
18+
19+
Reported by OpenAI from their `oai_otel_init` package, which uses this
20+
exact Azure package pair.
21+
"""
22+
23+
import sys
24+
25+
26+
def test_azure_core_tracing_ext_import():
27+
# The failing import from the report: requires `ext/` (from
28+
# azure-core-tracing-opentelemetry) to be visible inside the
29+
# `azure.core.tracing` regular package (from azure-core).
30+
from azure.core.tracing.ext.opentelemetry_span import OpenTelemetrySpan
31+
32+
assert OpenTelemetrySpan is not None
33+
34+
35+
def test_azure_core_still_intact():
36+
# The merge must not break azure-core's own modules next to the
37+
# grafted `ext/` directory.
38+
from azure.core.settings import settings
39+
from azure.core.tracing import SpanKind
40+
41+
assert settings is not None
42+
assert SpanKind is not None
43+
44+
45+
def test_azure_core_tracing_is_regular_package():
46+
"""Guard the premise of this test case.
47+
48+
If a future azure-core converts `azure.core.tracing` into a
49+
namespace package, this case would silently degrade into a
50+
duplicate of the pth-namespace-547 coverage. Fail loudly so the
51+
fixture gets re-pointed at another overlapping pair.
52+
"""
53+
import azure.core.tracing
54+
55+
assert azure.core.tracing.__file__ is not None, (
56+
"azure.core.tracing should be a regular package with __init__.py"
57+
)
58+
59+
60+
if __name__ == "__main__":
61+
test_azure_core_tracing_ext_import()
62+
test_azure_core_still_intact()
63+
test_azure_core_tracing_is_regular_package()
64+
print("PASS: azure.core.tracing.ext imports correctly")
65+
sys.exit(0)

0 commit comments

Comments
 (0)