def test_basic(registry: ServiceRegistry, container: ServiceContainer): register_dataclass(registry, DummyGreeter) register_dataclass(registry, DummyCustomer) greeter: DummyGreeter = container.get(DummyGreeter) customer: DummyCustomer = container.get(DummyCustomer) assert 'dummy_greeter' == greeter.name assert 'dummy_customer' == customer.name
def make_context( container: ServiceContainer, component_name: str, **kwargs ) -> Dict[str, Any]: """ Make component fields, other info into dict for template context """ from wired_components.component import IWrapComponents, IComponent # Start with all the wrapped components context: Dict[str, Any] = container.get(IWrapComponents) # We get the component again in case there are multiple components # registered with the same name, but for more specific contexts. component_factory = container.get(IComponent, name=component_name) # TODO Try to replace this part with DI+props in wired.components # (see above in component_factory) component_instance = component_factory(**kwargs) # Copy all the fields into the context dict for field in dataclasses.fields(component_instance): context[field.name] = getattr(component_instance, field.name) return context
def wired_factory(container: ServiceContainer) -> Resource: """ Custom factory that gets a Datastore instance """ # Presumes that "url" is in the container ds: Datastore = container.get(Datastore) url: Url = container.get(Url) context: Resource = ds.customers.get(url.value) return context
def resource_factory(container: ServiceContainer) -> Resource: # Get dependencies root: Root = container.get(Root) page_context: PageContext = container.get(PageContext) # Extract what's needed and make a resource document_metadata: Dict[str, Any] = page_context.meta this_rtype = document_metadata.get('type', 'document') resource = root if this_rtype == 'homepage' else Document( name=page_context.pagename, parent=root, title=page_context.title) return resource
def resolved( self, container: ServiceContainer, obj: Any = None, namespace: Optional[Mapping[Text, Any]] = None, ) -> Any: params = {} if self.context: if self.context is FROM_SELF: assert obj params["context"] = obj elif isinstance(self.context, FromNamespace): assert namespace params["context"] = namespace[self.context.name] elif isinstance(self.context, FromProperty): assert obj params["context"] = getattr(obj, self.context.name) if self.name: if isinstance(self.name, FromNamespace): assert namespace params["name"] = namespace[self.name.name] elif isinstance(self.name, FromProperty): assert obj params["name"] = getattr(obj, self.name.name) else: params["name"] = self.name return container.get(self.iface_or_type, **params)
def test_view_decorator_function( registry: ServiceRegistry, view_container: ServiceContainer, simple_root, interface, ): # Test a view registered for all resources from wired_components.view import IView, View, register_view @dataclass class SomeView(View): flag: int = 99 if interface == 'ignore': # Simulate omitting the ``context=`` argument register_view(registry, SomeView) else: register_view(registry, SomeView, context=interface) # Get the view from the container view: SomeView = view_container.get(IView) # Assert some things assert isinstance(view, View) assert view.flag == 99
def injectable_factory(container: ServiceContainer): if use_props: # Just return the target, it will be # constructed when the props are available return target else: injector = container.get(Injector) instance = injector(target) return instance
def customer_interaction(container: ServiceContainer, customer: Customer) -> str: """ Customer comes in, handle the steps in greeting them """ # Get a Greeter using the customer as context. Use the Customer when # generating the greeting. greeter: Greeter = container.get(Greeter, context=customer) greeting = greeter(customer) return greeting
def __call__(self, previous: Paths, container: ServiceContainer) -> Paths: # Get the pathto service from the PageContext page_context = container.get(PageContext) pathto: Callable[[str, int], str] = getattr(page_context, 'pathto') # Handle a single item differently than a list if type(previous) in [list, tuple]: return tuple( [pathto(this_previous, 1) for this_previous in previous]) else: return pathto(previous, 1)
def injector_construction(container: ServiceContainer, target): """ Introspect dataclass and get arguments from container """ # Make the args dict that we will construct dataclass with args = {} # Iterate through the dataclass fields for field_name, field_type in get_type_hints(target).items(): if field_type != str: args[field_name] = container.get(field_type) # Now construct an instance of the target dataclass return target(**args)
def __call__(self, previous: Type, container: ServiceContainer): try: service = container.get(self.lookup_type) if isclass(service): # This "service" is actually injectable, instead of # a plain factory. At the moment, we just have a class. # Use this injector instance to turn it into an instance. from wired_injector import Injector injector = container.get(Injector) service = injector(service) if self.attr is None: return service else: return getattr(service, self.attr) except LookupError: # We don't want to just crash with a LookupError, as the # field might have a default. Thus, bail out of processing # the pipeline. from wired_injector.injector import SkipField raise SkipField()
def render_component(container: ServiceContainer, component_name: str, **kwargs) -> Markup: # Get the context for the template context = make_context(container, component_name, **kwargs) # Get the template template_name = f'{component_name.lower()}.jinja2' # Get the renderer renderer: JinjaRenderer = container.get(IJinjaRenderer) # Render and return m = renderer.render(context, template_name, container=container) return m
def render(self, context: Dict, template_name: str, container: ServiceContainer) -> Markup: """ Given a dataclass, flatten it and render with jinja2 template """ # Always put the wrapped components into the template context from ..component import IWrapComponents wrapped_components: Dict[str, Type] = container.get(IWrapComponents) context.update(wrapped_components) template: Template = self.environment.get_or_select_template( template_name) result = template.render(**context) m = Markup(result) return m
def wrap_components(container: ServiceContainer, ) -> Dict[str, Callable]: """ Wrap component rendering with a partial to gain access to container """ # Get all the components, from the container components: Dict[str, Any] = container.get(IAllComponents) # For each, wrap them in a partial that contains the container return { component_name: partial( render_component, component_name=component_name, container=container, ) for component_name, component in components.items() }
def copy_theme_resources(container: ServiceContainer, app: Sphinx): ctr: CopyThemeResources = container.get(CopyThemeResources) static_outdir = Path(app.outdir) / '_static' ctr(copy_asset, static_outdir)
def injector_construction(container: ServiceContainer, target): """ Introspect dataclass and get arguments from container """ from .models import Resource, Url, Settings if target in (Url, Settings): # Don't need to construct this one from a dataclass, # it's a singleton the container instance = container.get(target) return instance # Make the args dict that we will construct dataclass with args = {} # Get the context from the container context: Resource = container.get(Resource) # Iterate through the dataclass fields # Because fields() gives a string for the type, instead of the # actual type, let's get a mapping of field name -> field type fields_mapping = {f.name: f for f in fields(target)} # Now we can iterate over the fields using type hints for field_name, field_type in get_type_hints(target).items(): # Do some special cases first if field_type == ServiceContainer: # Doing this style of bailing out quickly for performance # reasons. Don't want to keep doing "if", though it # means some repetitions. args[field_name] = container continue # See if this field is using the injectable field, e.g. # url: str = injected(Url, attr='value') full_field: Field = fields_mapping[field_name] if full_field.metadata.get('injected', False): injected_info = full_field.metadata['injected'] injected_attr = injected_info['attr'] injected_type = injected_info['type_'] # Ask the registry for one of these injected_target = container.get(injected_type, context=context) # Get the specified attribute off that instance field_value = getattr(injected_target, injected_attr) args[field_name] = field_value continue # Now the general case, something like url: Url try: field_value = container.get(field_type, context=context) args[field_name] = field_value except TypeError: # Seems that wired, when looking up str, gives: # TypeError: can't set attributes of built-in/extension type 'str' # We will use that to our advantage to look for a dataclass # field default value. field_default = getattr(full_field, 'default', None) if field_default: args[field_name] = field_default continue else: raise LookupError() except LookupError: # Give up and work around ``wired`` unhelpful exception # by adding some context information. msg = f'Injector failed for {field_name} on {target.__name__}' raise LookupError(msg) # Now construct an instance of the target dataclass return target(**args)
def context_parents(container: ServiceContainer) -> Parents: context: Resource = container.get(Context) p = parents(context) return p
def factory(container: ServiceContainer) -> View: request: Request = container.get(Request) context: Resource = container.get(Resource) greeter: Greeter = container.get(Greeter, context=context) view = View(request=request, context=context, greeter=greeter) return view
def factory(container: ServiceContainer) -> Request: url: str = container.get(Url) request = Request(url=url, container=container) return request
def factory(container: ServiceContainer) -> Resource: # Presumes that "url" is in the container ds: Datastore = container.get(Datastore) url: str = container.get(Url) context: Resource = ds.customers.get(url) return context
def __init__(self, container: ServiceContainer): self.greeter = container.get(Greeter)
def __call__(self, previous: DC, container: ServiceContainer) -> Dict: # Either use the value to the left, or if provided an argument, # look it up if self.lookup_type is not None: previous = container.get(self.lookup_type) return asdict(previous)
def greeting_factory(container: ServiceContainer): greeter = container.get(Greeter) return Greeting(greeter)
def target(container: ServiceContainer): view = container.get(View) return view