Security News
tea.xyz Spam Plagues npm and RubyGems Package Registries
Tea.xyz, a crypto project aimed at rewarding open source contributions, is once again facing backlash due to an influx of spam packages flooding public package registries.
Python library to facilitate modular components that can be combined through dependency injection.
Readme
Python library to facilitate modular components that can be combined through dependency injection.
Define your components by subclassing from Component
. Then you can use them in other components through dependency injection as follows:
from components import Component
class LogWriter(Component):
def __init__(self, path: str = "logs/logfile.txt"):
self.path = path
class Application(Component):
def __init__(self, logger: LogWriter, parameter1: int = 42):
self.parameter1 = parameter1
self.logger = logger
def run(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 the resolve
function, including parameters of subcomponents. In this example you can also instantiate app
as 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:
class RotationalLogWriter(LogWriter):
def __init__(self, path: str = "logs/logfile.txt", rotations: int = 5):
super().__init__(path)
self.rotations = rotations
class CustomApplication(Application):
logger: RotationalLogWriter
parameter1 = 8
def run(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: 5
Finally, it is also possible to turn Components into commands for a command line interface (CLI). Simply create a cli = components.cli.CLI()
object and have your Component
extend from cli.Command
. Then the command will be registered and its run
function will be called when the command is used from the command line.
from components.cli import CLI
cli = CLI()
class ApplicationAsCommand(Application, cli.Command):
logger: RotationalLogWriter
def run(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 80
cli.run
paramter1: 80
logger: <class '__main__.RotationalLogWriter'>
log path: logs/logfile.txt
log rotations: 5
> python3 example/example_app.py ApplicationAsCommand -h
usage: 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)
In addition to providing single Components
, the resolver will also instantiate lists of Components
when requested with the Tuple
type hint. This can be useful for supplying a variable amount of Components
for example, for the Listener
pattern.
This example illustrates the usage of Tuple
with Components
.
from typing import Tuple
class SubComp1(Component):
def __init__(self, par=42, par1: int=3):
self.par = par
self.par1 = par1
class SubComp2(Component):
def __init__(self, par=9, par2: str="Test"):
self.par = par
self.par2 = par2
class Comp(Component):
def __init__(self, components: Tuple[Component, ...]):
self.components = components
class ParentComp(Comp):
components: Tuple[SubComp1, SubComp2]
Comp.resolve()
will result in an empty list for the components
variable, whereas calling ParentComp.resolve()
will provide a list with two components of the following types: [SubComp1, SubComp2]
to be filled into the components
parameter.
The default __repr__
of Components
calls the function identifier
which shows the component name with its parameters between round braces. Additionally there is name
and full_identifier
to 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.
WIP
resolve
does not take attributes into account@argument
annotation to indicate class attributes that are arguments for parameters (allows to detect mistyped names for example).FAQs
Python library to facilitate modular components that can be combined through dependency injection.
We found that components demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Tea.xyz, a crypto project aimed at rewarding open source contributions, is once again facing backlash due to an influx of spam packages flooding public package registries.
Security News
As cyber threats become more autonomous, AI-powered defenses are crucial for businesses to stay ahead of attackers who can exploit software vulnerabilities at scale.
Security News
UnitedHealth Group disclosed that the ransomware attack on Change Healthcare compromised protected health information for millions in the U.S., with estimated costs to the company expected to reach $1 billion.