Services¶
A service in Wireup is any class or function decorated with @service
.
Services can live anywhere but must be registered with the container.
When creating a container, you can use the service_module
parameter to pass a list of modules which Wireup
will recursively scan for services, or pass them individually to the services
parameter.
Class Services¶
The simplest way to define a service is with a class:
from wireup import service
@service
class VehicleRepository: ...
@service
class RentalService:
# VehicleRepository is automatically injected
def __init__(self, repository: VehicleRepository) -> None: ...
Factory Services¶
For complex initialization, use factories. These are regular functions
decorated with @service
that construct and return service instances. The function must have a return
type. Wireup uses this to figure out what kind of service the factory creates.
@service
def create_payment_processor(
api_key: Annotated[str, Inject(param="STRIPE_API_KEY")]
) -> PaymentProcessor:
processor = PaymentProcessor()
processor.configure(api_key)
return processor
Dependency Resolution¶
Wireup uses type annotations to resolve dependencies. Parameter names are for readability only:
# These are equivalent:
@service
def create_rental_service(repo: VehicleRepository) -> RentalService:
return RentalService(repo)
@service
def create_rental_service(vehicle_store: VehicleRepository) -> RentalService:
return RentalService(vehicle_store)
Tip
You might have noticed the use of Injected[T]
in the documentation.
In Wireup's own services, this is not necessary because Wireup assumes ownership of all dependencies for its services.
However, this may not be the case when injecting into functions, as some arguments might be provided by other decorators or callers.
When injecting into a function, Wireup requires the Injected[T]
syntax to make it explicit that it should
inject that parameter. This ensures that if the requested dependency is not known,
an error is raised instead of silently skipping the parameter.