python-dependency-injector
python-dependency-injector copied to clipboard
Building a container with a list of derived classes
Thank you for this clean DI framework!
I have a CLI that I am attempting to refactor, where in it I have a concept of Services. This is a list of configurable objects, but all should be derived from a common base object.
The documentation points to providers.List, which seemed promising except it requires arguments to be coded.
Here is a picture of our configuration:
self.file = {
"Cli": {
"Version": "latest",
"Resources": {
"Qa": {
"Failover": {
"Activities": [
"qa-activity-one",
"qa-activity-two",
],
"Services": [
{
"CloudWatchService": {
"Name": "qa-cloudwatch-service-1"
}
},
{"S3Service": {"Name": "qa-s3-service-1"}},
],
"Configuration": {},
}
},
"Prod": {
"Failover": {
"Activities": [
"prod-activity-one",
"prod-activity-two",
],
"Services": [
{
"CloudWatchService": {
"Name": "prod-cloudwatch-service-1"
}
},
{"S3Service": {"Name": "prod-s3-service-1"}},
],
"Configuration": {},
}
},
},
}
}
From it, Services is an array of different service types.
class BaseService:
def __init__(self, Name):
self.Name = Name
def perform(self):
pass
class CloudWatchService(BaseService):
def __init__(self, Name):
self.Name = Name
def perform(self):
logger.debug("CloudWatchService")
class S3Service(BaseService):
def __init__(self, Name):
self.Name = Name
def perform(self):
logger.debug("S3Service")
class Failover:
"""
Contains Activities, Services, and Configuration, while also responsible for providing EffectiveActivities.
"""
def __init__(self, Activities, Services, Configuration):
self.Activities = Activities
self.Services = Services
self.Configuration = Configuration
class Environment:
"""
Provides for a selectable environment, such as Dev, Qa, and Prod containing the full graph of Failover configuration.
"""
def __init__(self, Failover):
self.Failover = Failover
class Resources:
def __init__(self, Environment):
self.Environment = Environment
class Cli:
def __init__(self, Version, Resources):
self.Version = Version
self.Resources = Resources
When creating a container, this works, except the Services are still a dictionary and not converted to classes:
class Container(containers.DynamicContainer):
def getFailoverProvider(environment, resources):
activities = resources[environment].Failover.Activities
configuration = resources[environment].Failover.Configuration
services = providers.Factory(list, resources[environment].Failover.Services)
failover = providers.Singleton(
Failover,
Activities=activities,
Configuration=configuration,
Services=services
)
return failover
config = providers.Configuration()
environment = providers.Selector(
config.environment,
Qa=providers.Singleton(
Environment, Failover=getFailoverProvider("Qa", config.OneCli.Resources)
),
Prod=providers.Singleton(
Environment, Failover=getFailoverProvider("Prod", config.OneCli.Resources)
),
)
resources = providers.Singleton(Resources, Environment=environment)
Cli = providers.Singleton(OneCli, config.OneCli.Version, resources)
Here is my test:
class FailoverTest(unittest.TestCase):
def setUp(self):
logger = logging.getLogger()
logger.level = logging.INFO
stream_handler = logging.StreamHandler(sys.stdout)
logger.addHandler(stream_handler)
self.file = {
"Cli": {
"Version": "latest",
"Resources": {
"Qa": {
"Failover": {
"Activities": [
"qa-activity-one",
"qa-activity-two",
],
"Services": [
{
"CloudWatchService": {
"Name": "qa-cloudwatch-service-1"
}
},
{"S3Service": {"Name": "qa-s3-service-1"}},
],
"Configuration": {},
}
},
"Prod": {
"Failover": {
"Activities": [
"prod-activity-one",
"prod-activity-two",
],
"Services": [
{
"CloudWatchService": {
"Name": "prod-cloudwatch-service-1"
}
},
{"S3Service": {"Name": "prod-s3-service-1"}},
],
"Configuration": {},
}
},
},
}
}
def test_qa_failover_init(self):
# Arrange
container = Container()
container.config.from_dict(self.file)
container.config.environment.from_value("Qa")
# Act
logger.debug(container.config)
cli = container.Cli()
# Assert
self.assertIsInstance(cli, Cli)
self.assertTrue(oneCli.Version == "latest")
self.assertIsInstance(cli.Resources, Resources)
self.assertIsNotNone(cli.Resources)
self.assertIsInstance(cli.Resources.Environment, Environment)
self.assertIsNotNone(cli.Resources.Environment)
self.assertIn(
"qa-activity-one", cli.Resources.Environment.Failover.Activities
)
# I don't want this: name = cli.Resources.Environment.Failover.Services[0]['CloudWatchService']['Name']
# I want this: name = cli.Resources.Environment.Failover.Services[0].CloudWatchService.Name
How can create list of derived objects based from the configuration loaded at runtime?