-
Notifications
You must be signed in to change notification settings - Fork 6
Description
Summary
Enable applications to override services defined by their dependencies. This unblocks the http_cache → http_client architecture (#784), where http_cache needs to override http_client's transport service to inject caching behavior.
Context
Current Problem:
Applications cannot override services from their dependencies. When app A depends on app B and tries to override B's services, validation fails with:
ValueError: Service with name <service> is not defined, but override flag is set.
Root Cause:
Validation happens too early during ServiceDefinitionCollection.__iter__ (harp/services/models.py:176-177), only checking services within a single YAML file rather than the full container context.
Current Workaround:
http_cache manually modifies http_client's service definition after loading (see harp_apps/http_cache/app.py:22-38).
Input
Service Definition YAML:
# http_cache/services.yml overriding http_client service
services:
- name: "http_client"
override: true # or override: "merge"
type: "httpx.AsyncClient"
defaults:
transport: !ref "http_cache.transport"Output and Testing Scenarios
Expected Behavior:
- Happy Path: http_cache overrides http_client service → cache transport is injected successfully
- Chained Override: A → B → C where both A and B override same service → A's override takes precedence
- Sibling Conflict: A → C ← B where both try to override same service → clear error with dependency context
- Invalid Override: App tries to override non-existent service → helpful error message
Possible Implementation
Chosen Approach: Container-aware validation
Modify ServiceDefinitionCollection.__iter__ to accept an optional container parameter for validation context:
def __iter__(self, container=None):
# When validating override flag:
if service.override:
# Check both collection AND container
if service.name not in _map and (not container or service.name not in container):
raise ValueError(f"Service {service.name} not defined...")Benefits:
- Minimal code change
- Backward compatible (container parameter is optional)
- Preserves existing validation flow
Override Semantics
Two modes will be supported:
-
override: true(replacement mode)- Behavior to be determined after documenting current implementation
- Either complete replacement or type & args only
-
override: "merge"(merge mode)- Merges arguments and defaults with base service
- Override values win in conflicts
- Can change type if explicitly specified
- Deep merge for nested dictionaries
Validation Rules
Override Direction: Unidirectional (dependent → dependency only)
- ✅ http_cache can override http_client services
- ❌ http_client cannot override http_cache services
Conflict Resolution:
- Chained overrides allowed (A → B → C)
- Sibling conflicts raise error with dependency graph context
Current Challenges
- Need to document existing override behavior before finalizing
override: truesemantics - Container reference must be passed through the loading chain
- Error messages need to include dependency graph context for clarity
Acceptance Criteria
- Document current override semantics in codebase
- Implement container-aware validation in
ServiceDefinitionCollection - http_cache can override http_client service without workaround
- Chained overrides work correctly
- Sibling conflicts raise clear errors with context
- Both
override: trueandoverride: "merge"modes work as specified - Backward compatible with existing single-app service definitions
- Enable skipped test in
harp_apps/http_cache/tests/test_settings.py - Add comprehensive tests for cross-app overrides
Related
- feat(http-client): migrate to hishel 1.x #784 (hishel 1.0 migration - blocked by this)
- feat(http-client)\!: complete hishel 1.x migration with breaking config changes #792 (PR blocked by this issue)
- feat: add application dependency resolution with topological sorting #623 (application dependency resolution - provides ordering foundation)