|  | # Copyright 2015 The ChromiumOS Authors | 
|  | # Use of this source code is governed by a BSD-style license that can be | 
|  | # found in the LICENSE file. | 
|  |  | 
|  | """Abstract ObjectFactory class used for injection of external dependencies.""" | 
|  |  | 
|  |  | 
|  | class ObjectFactoryIllegalOperation(Exception): | 
|  | """Raised when attempting an illegal ObjectFactory operation.""" | 
|  |  | 
|  |  | 
|  | _NO_SINGLETON_INSTANCE = object() | 
|  |  | 
|  |  | 
|  | class ObjectFactory: | 
|  | """Abstract object factory, used for injection of external dependencies. | 
|  |  | 
|  | A call to Setup(...) is necessary before a call to GetInstance(). | 
|  | """ | 
|  |  | 
|  | _object_name = "" | 
|  | _is_setup = False | 
|  | _setup_type = None | 
|  | _setup_instance = None | 
|  | _types = {} | 
|  |  | 
|  | def __init__( | 
|  | self, object_name, setup_types, allowed_transitions=None | 
|  | ) -> None: | 
|  | """ObjectFactory constructor. | 
|  |  | 
|  | Args: | 
|  | object_name: Human readable name for the type of object that this | 
|  | factory generates. | 
|  | setup_types: A (set up type name -> generator function) dictionary, | 
|  | which teaches ObjectFactory how to construct instances after | 
|  | setup has been called. For set up types where a singleton | 
|  | instance is specified at setup(...) time, generator function | 
|  | should be None. | 
|  | allowed_transitions: Optional function, where | 
|  | allowed_transitions(from_type, to_type) specifies whether | 
|  | transition from |from_type| to |to_type| is allowed. If | 
|  | unspecified, no transitions are allowed. | 
|  | """ | 
|  |  | 
|  | self._object_name = object_name | 
|  | self._types = setup_types | 
|  | self._allowed_transitions = allowed_transitions | 
|  |  | 
|  | def Setup( | 
|  | self, setup_type, singleton_instance=_NO_SINGLETON_INSTANCE | 
|  | ) -> None: | 
|  | # Prevent set up to unknown types. | 
|  | if setup_type not in self._types: | 
|  | raise ObjectFactoryIllegalOperation( | 
|  | "Unknown %s setup_type %s" % (self._object_name, setup_type) | 
|  | ) | 
|  |  | 
|  | # Prevent illegal setup transitions. | 
|  | if self._is_setup: | 
|  | if self._allowed_transitions: | 
|  | if not self._allowed_transitions(self._setup_type, setup_type): | 
|  | raise ObjectFactoryIllegalOperation( | 
|  | "Illegal set up transition from %s to %s." | 
|  | % (self._setup_type, setup_type) | 
|  | ) | 
|  | else: | 
|  | raise ObjectFactoryIllegalOperation( | 
|  | "%s already set up." % self._object_name | 
|  | ) | 
|  |  | 
|  | # Allow singleton_instance if and only if factory method for this type | 
|  | # is None. | 
|  | instance_supplied = singleton_instance != _NO_SINGLETON_INSTANCE | 
|  | factory_is_none = self._types[setup_type] is None | 
|  | if instance_supplied != factory_is_none: | 
|  | raise ObjectFactoryIllegalOperation( | 
|  | "singleton_instance should be supplied if and only if " | 
|  | "setup_type has a factory that is None." | 
|  | ) | 
|  |  | 
|  | self._setup_type = setup_type | 
|  | self._setup_instance = singleton_instance | 
|  | self._is_setup = True | 
|  |  | 
|  | @property | 
|  | def is_setup(self): | 
|  | """Returns True iff a call to get_instance is expected to succeed.""" | 
|  | return self._is_setup | 
|  |  | 
|  | @property | 
|  | def setup_type(self): | 
|  | """Returns the setup_type.""" | 
|  | return self._setup_type | 
|  |  | 
|  | def GetInstance(self): | 
|  | """Returns an object instance iff setup has been called. | 
|  |  | 
|  | Raises: | 
|  | ObjectFactoryIllegalOperation: if setup has not yet been called. | 
|  | """ | 
|  | if not self.is_setup: | 
|  | raise ObjectFactoryIllegalOperation( | 
|  | "%s is not set up." % self._object_name | 
|  | ) | 
|  | if self._setup_instance != _NO_SINGLETON_INSTANCE: | 
|  | return self._setup_instance | 
|  | return self._types[self.setup_type]() | 
|  |  | 
|  | def _clear_setup(self) -> None: | 
|  | """Clear setup, for testing purposes only.""" | 
|  | self._setup_type = None | 
|  | self._is_setup = False | 
|  | self._setup_instance = _NO_SINGLETON_INSTANCE |