This document summarizes the implementation of the deps_to_remove feature for py_library targets in the Gazelle Python extension. The feature allows automatic generation of a deps_to_remove attribute that contains dependencies violating ordering constraints defined in a deps-order.txt file.
- All dependencies must be included in the
depsattribute (normal behavior) - Violating dependencies must also be included in the
deps_to_removeattribute - Dependency ordering is defined by a
deps-order.txtfile at the repository root - Files listed earlier in
deps-order.txtcan be depended upon by files listed later, but not vice versa
File: /workspaces/rules_python/gazelle/python/kinds.go
Added deps_to_remove attribute support to pyLibraryKind:
MergeableAttrs: map[string]bool{
"srcs": true,
"deps_to_remove": true,
},
ResolveAttrs: map[string]bool{
"deps": true,
"pyi_deps": true,
"deps_to_remove": true,
},File: /workspaces/rules_python/gazelle/python/target.go
- Added
depsToRemovefield totargetBuilderstruct - Added helper methods:
addDepToRemove(),addDepsToRemove() - Updated
build()method to store source files for ordering constraints
File: /workspaces/rules_python/gazelle/python/resolve.go
DepsOrderResolver Structure:
type DepsOrderResolver struct {
fileToIndex map[string]int // File to ordering index mapping
loaded bool // Loading state
importToSrcs map[string][]string // Import to source files mapping
}Key Methods:
LoadDepsOrder()- Parsesdeps-order.txtfileGetMedianIndex()- Calculates median ordering index for source filesShouldAddToDepsToRemove()- Determines if dependency violates orderingRegisterImportSources()- Maps import specs to source files
- During Import Registration: Map import specs to their source files
- During Dependency Resolution: Register dependency labels to source files
- During Rule Finalization: Apply ordering constraints to create
deps_to_remove
File: /workspaces/rules_python/gazelle/python/language.go
Updated NewLanguage() to initialize the resolver with DepsOrderResolver:
return &Python{
Resolver: Resolver{
depsOrderResolver: NewDepsOrderResolver(),
},
}- File Indexing: Each file in
deps-order.txtgets an index (0, 1, 2, ...) - Median Calculation: For targets with multiple sources, calculate median index
- Violation Detection: If
currentTargetIndex < dependencyTargetIndex, it's a violation - Attribute Population: Violating dependencies are added to both
depsanddeps_to_remove
The implementation handles path matching flexibly:
- Tries exact path matches first (
pkg/file.py) - Falls back to filename matches (
file.py) - Supports both repo-relative and package-relative paths
Files: core.py → utils.py → high_level.py
Scenario: All dependencies follow correct ordering
utils.pydepends oncore.py✅ (valid: index 1 → index 0)high_level.pydepends on both ✅ (valid: index 2 → index 0,1)
Expected Result: No deps_to_remove attributes (all dependencies are valid)
Files: foundation.py → middleware.py → application.py
Scenario: Contains dependency ordering violations
foundation.pydepends onmiddleware.py❌ (violation: index 0 → index 1)middleware.pydepends onapplication.py❌ (violation: index 1 → index 2)
Expected Result:
foundationtarget:deps_to_remove = [":middleware"]middlewaretarget:deps_to_remove = [":application"]
gazelle/python/
├── kinds.go # Attribute definitions
├── target.go # Target building logic
├── resolve.go # Dependency resolution & ordering
├── language.go # Language initialization
└── testdata/
├── deps_to_remove_with_order/ # Valid dependencies test
│ ├── deps-order.txt
│ ├── core.py, utils.py, high_level.py
│ ├── BUILD.in, BUILD.out
│ └── test.yaml
└── deps_to_remove_ordering_violation/ # Violation test
├── deps-order.txt
├── foundation.py, middleware.py, application.py
├── BUILD.in, BUILD.out
└── test.yaml
# Comments are supported
core/base.py
utils/helpers.py
features/advanced.py
bazel run //:gazellepy_library(
name = "advanced",
srcs = ["advanced.py"],
deps = [
"//core:base", # Valid dependency
"//utils:helpers", # Valid dependency
],
# deps_to_remove is empty - no violations
)
py_library(
name = "helpers",
srcs = ["helpers.py"],
deps = ["//features:advanced"], # Invalid dependency
deps_to_remove = ["//features:advanced"], # Marked for removal
)- Automated Detection: Automatically identifies dependency ordering violations
- Build Compatibility: All dependencies remain in
depsfor build correctness - Tooling Integration:
deps_to_removecan be used by linters, analyzers, etc. - Flexible Configuration: Simple text file configuration
- Backward Compatible: No
deps-order.txtmeans no constraints applied
This implementation provides a robust foundation for dependency ordering enforcement in Python projects using Bazel and Gazelle.