Package: components
ComponentsPython library to facilitate modular components that can be combined through dependency injection.Getting StartedDefine your components by subclassing fromComponent. Then you can use them in other components through dependency injection as follows:fromcomponentsimportComponentclassLogWriter(Component):def__init__(self,path:str="logs/logfile.txt"):self.path=pathclassApplication(Component):def__init__(self,logger:LogWriter,parameter1:int=42):self.parameter1=parameter1self.logger=loggerdefrun(self):print("paramter1:",self.parameter1)print("logger:",type(self.logger))print("log path:",self.logger.path)if__name__=="__main__":app=Application.resolve()app.run()Components and parameter can all be supplied to theresolvefunction, including parameters of subcomponents. In this example you can also instantiateappas follows:app = Application.resolve(parameter1=9)app = Application.resolve(path="output/logs/stdout.log")app = Application.resolve(logger_path="output/logs/stdout.log")Note that parameters of subcomponents can be addressed by their own name (when no conflicts are present) or by their more defined name which includes the subcomponent's name(s) separated with underscores. In some cases, when conflicting paramter names occur, the more defined name is be required.Additionally, paramters can be supplied through class attributes. Consider the following example:classRotationalLogWriter(LogWriter):def__init__(self,path:str="logs/logfile.txt",rotations:int=5):super().__init__(path)self.rotations=rotationsclassCustomApplication(Application):logger:RotationalLogWriterparameter1=8defrun(self):super().run()print("log rotations:",self.logger.rotations)if__name__=="__main__":app=Application.resolve()print("app.run")app.run()custom_app=CustomApplication.resolve()print("\ncustom_app.run")custom_app.run()Which gives the output:app.run
paramter1: 42
logger: <class '__main__.LogWriter'>
log path: logs/logfile.txt

custom_app.run
paramter1: 8
logger: <class '__main__.RotationalLogWriter'>
log path: logs/logfile.txt
log rotations: 5Finally, it is also possible to turn Components into commands for a command line interface (CLI). Simply create acli = components.cli.CLI()object and have yourComponentextend fromcli.Command. Then the command will be registered and itsrunfunction will be called when the command is used from the command line.fromcomponents.cliimportCLIcli=CLI()classApplicationAsCommand(Application,cli.Command):logger:RotationalLogWriterdefrun(self):super().run()print("log rotations:",self.logger.rotations)if__name__=="__main__":print("cli.run")cli.run()In the command line, this gives:> python3 example/example_app.py ApplicationAsCommand --parameter1 80cli.runparamter1: 80logger: <class '__main__.RotationalLogWriter'>log path: logs/logfile.txtlog rotations: 5> python3 example/example_app.py ApplicationAsCommand -husage: example_app.py ApplicationAsCommand [-h] [--path str] [--rotations int] [--parameter1 int]optional arguments:-h, --help            show this help message and exit--path str, --logger-path str(default: logs/logfile.txt)--rotations int, --logger-rotations int(default: 5)--parameter1 int      (default: 42)Advanced UsageLists of ComponentsIn addition to providing singleComponents, the resolver will also instantiate lists ofComponentswhen requested with theTupletype hint. This can be useful for supplying a variable amount ofComponentsfor example, for theListenerpattern.This example illustrates the usage ofTuplewithComponents.fromtypingimportTupleclassSubComp1(Component):def__init__(self,par=42,par1:int=3):self.par=parself.par1=par1classSubComp2(Component):def__init__(self,par=9,par2:str="Test"):self.par=parself.par2=par2classComp(Component):def__init__(self,components:Tuple[Component,...]):self.components=componentsclassParentComp(Comp):components:Tuple[SubComp1,SubComp2]Comp.resolve()will result in an empty list for thecomponentsvariable, whereas callingParentComp.resolve()will provide a list with two components of the following types:[SubComp1, SubComp2]to be filled into thecomponentsparameter.Non-identifying parametersThe default__repr__ofComponentscalls the functionidentifierwhich shows the component name with its parameters between round braces. Additionally there isnameandfull_identifierto respectively only return the name or to recursively include subcomponent identifiers.However, some parameters should not be listed in the__repr__of an object. These can be indicated by prefixing them with an underscore (_) as if they were private/protected members. The parameter can then be provided using the name without underscore or with underscore.Technical DetailsWIPExplain semantics of conflicting param namesExplain that creating an object withoutresolvedoes not take attributes into account...Future WorkAdd@argumentannotation to indicate class attributes that are arguments for parameters (allows to detect mistyped names for example).Add support for lists in command line.Suggestions? Contactme!