Dependencias
Reflect Dependencies
Sección titulada «Reflect Dependencies»La clase ReflectDependencies es el motor de análisis de dependencias del sistema de reflexión de Orionis. Su propósito es inspeccionar las firmas de constructores, métodos y funciones callable para categorizar automáticamente sus parámetros como resueltos o no resueltos, proporcionando la información necesaria para que el contenedor de inyección de dependencias (IoC) pueda resolver e inyectar servicios de forma automática.
Este componente es fundamental para el funcionamiento interno del framework, ya que permite al contenedor de servicios determinar qué dependencias puede resolver automáticamente y cuáles requieren configuración explícita por parte del desarrollador.
Importación
Sección titulada «Importación»from orionis.services.introspection.dependencies.reflection import ReflectDependenciesInicialización
Sección titulada «Inicialización»La clase ReflectDependencies recibe un objeto objetivo (target) que puede ser una clase, una función o None. El análisis de dependencias se realiza bajo demanda al invocar los métodos de inspección.
from orionis.services.introspection.dependencies.reflection import ReflectDependencies
class UserService: def __init__(self, repo: UserRepository, name: str, retries: int = 3) -> None: self.repo = repo self.name = name self.retries = retries
reflection = ReflectDependencies(UserService)También acepta funciones y callables:
def process_data(data: list, mode: str = "fast") -> str: return f"{len(data)}-{mode}"
reflection = ReflectDependencies(process_data)Contrato
Sección titulada «Contrato»La clase ReflectDependencies implementa el contrato IReflectDependencies, que define tres métodos abstractos de inspección:
from orionis.services.introspection.dependencies.contracts.reflection import IReflectDependenciesEntidades
Sección titulada «Entidades»El sistema utiliza dos entidades inmutables (dataclasses congeladas) para representar los resultados del análisis.
Argument
Sección titulada «Argument»Representa un parámetro individual con toda su metadata de tipo y estado de resolución.
from orionis.services.introspection.dependencies.entities.argument import Argument| Campo | Tipo | Descripción |
|---|---|---|
name | str | Nombre del parámetro |
resolved | bool | Si la dependencia fue resuelta |
module_name | str | Módulo donde está definido el tipo |
class_name | str | Nombre del tipo/clase del parámetro |
type | type | Objeto tipo Python del parámetro |
full_class_path | str | Ruta completa al tipo (module.Class) |
is_keyword_only | bool | Si es un parámetro keyword-only |
default | Any | None | Valor por defecto, si lo tiene |
Argument es un dataclass congelado (frozen=True), lo que lo hace inmutable y hashable.
Signature
Sección titulada «Signature»Agrupa los resultados del análisis en tres diccionarios categorizados.
from orionis.services.introspection.dependencies.entities.signature import Signature| Campo | Tipo | Descripción |
|---|---|---|
resolved | dict[str, Argument] | Parámetros resueltos automáticamente |
unresolved | dict[str, Argument] | Parámetros que no pudieron resolverse |
ordered | dict[str, Argument] | Todos los parámetros en orden de declaración |
Lógica de Resolución
Sección titulada «Lógica de Resolución»El sistema clasifica cada parámetro según las siguientes reglas, evaluadas en orden de prioridad:
- Parámetros omitidos:
self,cls,*argsy**kwargsse excluyen del análisis. - Sin anotación ni valor por defecto: se clasifican como no resueltos — el contenedor no tiene información suficiente para inyectarlos.
- Con valor por defecto: se clasifican como resueltos — el contenedor puede utilizar el valor proporcionado.
- Anotados con tipo builtin (
str,int,float, etc.) sin valor por defecto: se clasifican como no resueltos — los tipos primitivos no pueden resolverse automáticamente. - Anotados con tipo no-builtin: se clasifican como resueltos — el contenedor puede resolver la dependencia por tipo a través del IoC.
class PaymentService: def __init__( self, gateway: PaymentGateway, # Resuelto (tipo no-builtin) currency: str, # No resuelto (builtin sin default) retries: int = 3, # Resuelto (tiene valor por defecto) ) -> None: ...
rd = ReflectDependencies(PaymentService)sig = rd.constructorSignature()
sig.resolved # {"gateway": Argument(...), "retries": Argument(...)}sig.unresolved # {"currency": Argument(...)}sig.ordered # {"gateway": ..., "currency": ..., "retries": ...}Inspección de Constructores
Sección titulada «Inspección de Constructores»constructorSignature
Sección titulada «constructorSignature»Analiza el método __init__ de una clase y categoriza sus parámetros.
class EmailService: def __init__(self, mailer: Mailer, subject: str, max_retries: int = 5) -> None: ...
rd = ReflectDependencies(EmailService)sig = rd.constructorSignature()
# Dependencia resuelta por tiposig.resolved["mailer"].class_name # "Mailer"sig.resolved["mailer"].resolved # True
# Dependencia resuelta por valor por defectosig.resolved["max_retries"].default # 5sig.resolved["max_retries"].resolved # True
# Dependencia no resueltasig.unresolved["subject"].class_name # "str"sig.unresolved["subject"].resolved # FalseInspección de Métodos
Sección titulada «Inspección de Métodos»methodSignature
Sección titulada «methodSignature»Analiza la firma de un método específico de la clase objetivo.
class DataProcessor: def process(self, value: int, mode: str = "fast") -> str: return f"{value}-{mode}"
rd = ReflectDependencies(DataProcessor)sig = rd.methodSignature("process")
sig.unresolved["value"].class_name # "int"sig.resolved["mode"].default # "fast"Si el método no existe, se lanza un AttributeError:
rd.methodSignature("nonexistent")# AttributeErrorInspección de Callables
Sección titulada «Inspección de Callables»callableSignature
Sección titulada «callableSignature»Analiza la firma de una función o callable pasado como objetivo.
def calculate(a: int, b: str = "hello") -> str: return f"{a}-{b}"
rd = ReflectDependencies(calculate)sig = rd.callableSignature()
sig.unresolved["a"].class_name # "int"sig.resolved["b"].default # "hello"Si el objetivo no es callable, se lanza un TypeError:
rd = ReflectDependencies("not callable")rd.callableSignature()# TypeErrorMétodos de Signature
Sección titulada «Métodos de Signature»La entidad Signature ofrece métodos para consultar y filtrar los resultados del análisis.
| Método | Retorno | Descripción |
|---|---|---|
noArgumentsRequired() | bool | True si no hay dependencias |
hasUnresolvedArguments() | bool | True si existen dependencias no resueltas |
arguments() | dict_items | Pares (nombre, Argument) en orden |
items() | dict_items | Alias de arguments() |
getAllOrdered() | dict[str, Argument] | Todas las dependencias en orden |
getResolved() | dict[str, Argument] | Solo dependencias resueltas |
getUnresolved() | dict[str, Argument] | Solo dependencias no resueltas |
getPositionalOnly() | dict[str, Argument] | Dependencias posicionales |
getKeywordOnly() | dict[str, Argument] | Dependencias keyword-only |
toDict() | dict[str, dict] | Todas las dependencias como diccionarios |
resolvedToDict() | dict[str, dict] | Resueltas como diccionarios |
unresolvedToDict() | dict[str, dict] | No resueltas como diccionarios |
positionalOnlyToDict() | dict[str, dict] | Posicionales como diccionarios |
keywordOnlyToDict() | dict[str, dict] | Keyword-only como diccionarios |
Ejemplo de Consulta
Sección titulada «Ejemplo de Consulta»sig = rd.constructorSignature()
# Verificar si requiere argumentosif sig.noArgumentsRequired(): print("No requiere dependencias")
# Verificar si hay dependencias sin resolverif sig.hasUnresolvedArguments(): for name, arg in sig.getUnresolved().items(): print(f" {name}: {arg.class_name} (no resuelto)")
# Iterar sobre todas las dependencias en ordenfor name, arg in sig.items(): status = "resuelto" if arg.resolved else "no resuelto" print(f"{name}: {arg.class_name} [{status}]")Parámetros Keyword-Only
Sección titulada «Parámetros Keyword-Only»El sistema detecta correctamente parámetros keyword-only (definidos después de * en la firma) y los marca con is_keyword_only=True en el Argument.
class Config: def __init__(self, *, label: str, count: int = 0) -> None: self.label = label self.count = count
rd = ReflectDependencies(Config)sig = rd.constructorSignature()
# Filtrar solo keyword-onlykeyword_args = sig.getKeywordOnly()# {"label": Argument(..., is_keyword_only=True), "count": Argument(..., is_keyword_only=True)}
# Filtrar solo posicionalespositional_args = sig.getPositionalOnly()# {} (vacío, ya que todos son keyword-only)