mirror of
				https://github.com/ets-labs/python-dependency-injector.git
				synced 2025-10-30 23:47:40 +03:00 
			
		
		
		
	Add single container prototype
This commit is contained in:
		
							parent
							
								
									8cad8c6b65
								
							
						
					
					
						commit
						436bb1a5a2
					
				
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -1,3 +1,4 @@ | ||||||
|  | from pathlib import Path | ||||||
| from typing import ( | from typing import ( | ||||||
|     Generic, |     Generic, | ||||||
|     Type, |     Type, | ||||||
|  | @ -34,6 +35,7 @@ class Container: | ||||||
|     def __init__(self) -> None: ... |     def __init__(self) -> None: ... | ||||||
|     def __deepcopy__(self, memo: Optional[Dict[str, Any]]) -> Provider: ... |     def __deepcopy__(self, memo: Optional[Dict[str, Any]]) -> Provider: ... | ||||||
|     def __setattr__(self, name: str, value: Union[Provider, Any]) -> None: ... |     def __setattr__(self, name: str, value: Union[Provider, Any]) -> None: ... | ||||||
|  |     def __getattr__(self, name: str) -> Provider: ... | ||||||
|     def __delattr__(self, name: str) -> None: ... |     def __delattr__(self, name: str) -> None: ... | ||||||
|     def set_providers(self, **providers: Provider): ... |     def set_providers(self, **providers: Provider): ... | ||||||
|     def set_provider(self, name: str, provider: Provider) -> None: ... |     def set_provider(self, name: str, provider: Provider) -> None: ... | ||||||
|  | @ -48,6 +50,9 @@ class Container: | ||||||
|     def apply_container_providers_overridings(self) -> None: ... |     def apply_container_providers_overridings(self) -> None: ... | ||||||
|     def reset_singletons(self) -> SingletonResetContext[C_Base]: ... |     def reset_singletons(self) -> SingletonResetContext[C_Base]: ... | ||||||
|     def check_dependencies(self) -> None: ... |     def check_dependencies(self) -> None: ... | ||||||
|  |     def from_schema(self, schema: Dict[Any, Any]) -> None: ... | ||||||
|  |     def from_yaml_schema(self, filepath: Union[Path, str]) -> None: | ||||||
|  |     def from_json_schema(self, filepath: Union[Path, str]) -> None: | ||||||
|     @overload |     @overload | ||||||
|     def resolve_provider_name(self, provider: Provider) -> str: ... |     def resolve_provider_name(self, provider: Provider) -> str: ... | ||||||
|     @classmethod |     @classmethod | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ import six | ||||||
| 
 | 
 | ||||||
| from . import providers, errors | from . import providers, errors | ||||||
| from .providers cimport __is_future_or_coroutine | from .providers cimport __is_future_or_coroutine | ||||||
|  | from .schema import build_schema | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| if sys.version_info[:2] >= (3, 6): | if sys.version_info[:2] >= (3, 6): | ||||||
|  | @ -330,6 +331,21 @@ class DynamicContainer(Container): | ||||||
|             f'{", ".join(undefined_names)}', |             f'{", ".join(undefined_names)}', | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|  |     def from_schema(self, schema): | ||||||
|  |         """Build container providers from schema.""" | ||||||
|  |         for name, provider in build_schema(schema).items(): | ||||||
|  |             self.set_provider(name, provider) | ||||||
|  | 
 | ||||||
|  |     def from_yaml_schema(self, filepath): | ||||||
|  |         """Build container providers from YAML file schema.""" | ||||||
|  |         # TODO | ||||||
|  |         ... | ||||||
|  | 
 | ||||||
|  |     def from_json_schema(self, filepath): | ||||||
|  |         """Build container providers from JSON file schema.""" | ||||||
|  |         # TODO | ||||||
|  |         ... | ||||||
|  | 
 | ||||||
|     def resolve_provider_name(self, provider): |     def resolve_provider_name(self, provider): | ||||||
|         """Try to resolve provider name.""" |         """Try to resolve provider name.""" | ||||||
|         for provider_name, container_provider in self.providers.items(): |         for provider_name, container_provider in self.providers.items(): | ||||||
|  |  | ||||||
							
								
								
									
										120
									
								
								src/dependency_injector/schema.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								src/dependency_injector/schema.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,120 @@ | ||||||
|  | """Schema module.""" | ||||||
|  | 
 | ||||||
|  | import importlib | ||||||
|  | from typing import Dict, Any, Type, Optional | ||||||
|  | 
 | ||||||
|  | from . import providers | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | Schema = Dict[Any, Any] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def build_schema(schema: Schema) -> Dict[str, providers.Provider]: | ||||||
|  |     """Build provider schema.""" | ||||||
|  |     built = {} | ||||||
|  | 
 | ||||||
|  |     for provider_name, data in schema['providers'].items(): | ||||||
|  |         provider_type = _get_provider_cls(data['provider']) | ||||||
|  |         args = [] | ||||||
|  | 
 | ||||||
|  |         provides = data.get('provides') | ||||||
|  |         if provides: | ||||||
|  |             provides = _import_string(provides) | ||||||
|  |             if provides: | ||||||
|  |                 args.append(provides) | ||||||
|  | 
 | ||||||
|  |         provider = provider_type(*args) | ||||||
|  |         built[provider_name] = provider | ||||||
|  | 
 | ||||||
|  |     for provider_name, data in schema['providers'].items(): | ||||||
|  |         provider = built[provider_name] | ||||||
|  |         args = [] | ||||||
|  |         kwargs = {} | ||||||
|  | 
 | ||||||
|  |         arg_injections = data.get('args') | ||||||
|  |         if arg_injections: | ||||||
|  |             for arg in arg_injections: | ||||||
|  |                 injection = _resolve_provider(arg, built) | ||||||
|  |                 if not injection: | ||||||
|  |                     injection = arg | ||||||
|  |                 args.append(injection) | ||||||
|  |         if args: | ||||||
|  |             provider.add_args(*args) | ||||||
|  | 
 | ||||||
|  |         kwarg_injections = data.get('kwargs') | ||||||
|  |         if kwarg_injections: | ||||||
|  |             for name, arg in kwarg_injections.items(): | ||||||
|  |                 injection = _resolve_provider(arg, built) | ||||||
|  |                 if not injection: | ||||||
|  |                     injection = arg | ||||||
|  |                 kwargs[name] = injection | ||||||
|  |         if kwargs: | ||||||
|  |             provider.add_kwargs(**kwargs) | ||||||
|  | 
 | ||||||
|  |         # TODO: add attributes | ||||||
|  | 
 | ||||||
|  |     return built | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def _resolve_provider(name: str, built: Dict[Any, providers.Provider]) -> Optional[providers.Provider]: | ||||||
|  |     segments = name.split('.') | ||||||
|  |     try: | ||||||
|  |         provider = built[segments[0]] | ||||||
|  |     except KeyError: | ||||||
|  |         return None | ||||||
|  | 
 | ||||||
|  |     for segment in segments[1:]: | ||||||
|  |         if segment == 'as_int()': | ||||||
|  |             provider = provider.as_int() | ||||||
|  |         elif segment == 'as_float()': | ||||||
|  |             provider = provider.as_float() | ||||||
|  |         elif segment.startswith('is_'):  # TODO | ||||||
|  |             provider = provider.as_(str) | ||||||
|  |             ... | ||||||
|  |         else: | ||||||
|  |             try: | ||||||
|  |                 provider = getattr(provider, segment) | ||||||
|  |             except AttributeError: | ||||||
|  |                 return None | ||||||
|  |     return provider | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def _get_provider_cls(provider_cls_name: str) -> Type[providers.Provider]: | ||||||
|  |     std_provider_type = _fetch_provider_cls_from_std(provider_cls_name) | ||||||
|  |     if std_provider_type: | ||||||
|  |         return std_provider_type | ||||||
|  | 
 | ||||||
|  |     custom_provider_type = _import_provider_cls(provider_cls_name) | ||||||
|  |     if custom_provider_type: | ||||||
|  |         return custom_provider_type | ||||||
|  | 
 | ||||||
|  |     raise SchemaError(f'Undefined provider class: "{provider_cls_name}"') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def _fetch_provider_cls_from_std(provider_cls_name: str) -> Optional[Type[providers.Provider]]: | ||||||
|  |     return getattr(providers, provider_cls_name, None) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def _import_provider_cls(provider_cls_name: str) -> Optional[Type[providers.Provider]]: | ||||||
|  |     try: | ||||||
|  |         cls = _import_string(provider_cls_name) | ||||||
|  |     except (ImportError, ValueError) as exception: | ||||||
|  |         raise SchemaError(f'Can not import provider "{provider_cls_name}"') from exception | ||||||
|  |     except AttributeError: | ||||||
|  |         return None | ||||||
|  |     else: | ||||||
|  |         if isinstance(cls, type) and not issubclass(cls, providers.Provider): | ||||||
|  |             raise SchemaError(f'Provider class "{cls}" is not a subclass of providers base class') | ||||||
|  |         return cls | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def _import_string(string_name: str) -> Optional[object]: | ||||||
|  |     segments = string_name.split('.') | ||||||
|  |     module_name = '.'.join(segments[:-1]) | ||||||
|  |     member = segments[-1] | ||||||
|  |     module = importlib.import_module(module_name) | ||||||
|  |     return getattr(module, member, None) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class SchemaError(Exception): | ||||||
|  |     """Schema-related error.""" | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user