summaryrefslogtreecommitdiff
path: root/python-components.spec
diff options
context:
space:
mode:
Diffstat (limited to 'python-components.spec')
-rw-r--r--python-components.spec585
1 files changed, 585 insertions, 0 deletions
diff --git a/python-components.spec b/python-components.spec
new file mode 100644
index 0000000..0c0d244
--- /dev/null
+++ b/python-components.spec
@@ -0,0 +1,585 @@
+%global _empty_manifest_terminate_build 0
+Name: python-components
+Version: 1.2.8
+Release: 1
+Summary: Python library to facilitate modular components that can be combined through dependency injection.
+License: MIT License
+URL: https://github.com/JoeyDP/Components
+Source0: https://mirrors.nju.edu.cn/pypi/web/packages/a0/98/0dccf937253cdbdc3185ddbcbe93813845722424dfcde21bdfc0b78756ff/components-1.2.8.tar.gz
+BuildArch: noarch
+
+
+%description
+# Components
+Python library to facilitate modular components that can be combined through dependency injection.
+
+## Getting Started
+Define your components by subclassing from `Component`. Then you can use them in other components through dependency injection as follows:
+```python
+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:
+```python
+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.
+```python
+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:
+```console
+> 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)
+
+```
+
+
+## Advanced Usage
+
+### Lists of Components
+
+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`.
+
+```python
+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.
+
+### Non-identifying parameters
+
+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.
+
+
+## Technical Details
+WIP
+ - Explain semantics of conflicting param names
+ - Explain that creating an object without `resolve` does not take attributes into account
+ - ...
+
+## Future Work
+ - Add `@argument` annotation to indicate class attributes that are arguments for parameters (allows to detect mistyped names for example).
+ - Add support for lists in command line.
+ - Suggestions? Contact [me](mailto:joeydepauw@gmail.com)!
+
+
+
+
+%package -n python3-components
+Summary: Python library to facilitate modular components that can be combined through dependency injection.
+Provides: python-components
+BuildRequires: python3-devel
+BuildRequires: python3-setuptools
+BuildRequires: python3-pip
+%description -n python3-components
+# Components
+Python library to facilitate modular components that can be combined through dependency injection.
+
+## Getting Started
+Define your components by subclassing from `Component`. Then you can use them in other components through dependency injection as follows:
+```python
+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:
+```python
+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.
+```python
+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:
+```console
+> 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)
+
+```
+
+
+## Advanced Usage
+
+### Lists of Components
+
+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`.
+
+```python
+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.
+
+### Non-identifying parameters
+
+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.
+
+
+## Technical Details
+WIP
+ - Explain semantics of conflicting param names
+ - Explain that creating an object without `resolve` does not take attributes into account
+ - ...
+
+## Future Work
+ - Add `@argument` annotation to indicate class attributes that are arguments for parameters (allows to detect mistyped names for example).
+ - Add support for lists in command line.
+ - Suggestions? Contact [me](mailto:joeydepauw@gmail.com)!
+
+
+
+
+%package help
+Summary: Development documents and examples for components
+Provides: python3-components-doc
+%description help
+# Components
+Python library to facilitate modular components that can be combined through dependency injection.
+
+## Getting Started
+Define your components by subclassing from `Component`. Then you can use them in other components through dependency injection as follows:
+```python
+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:
+```python
+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.
+```python
+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:
+```console
+> 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)
+
+```
+
+
+## Advanced Usage
+
+### Lists of Components
+
+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`.
+
+```python
+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.
+
+### Non-identifying parameters
+
+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.
+
+
+## Technical Details
+WIP
+ - Explain semantics of conflicting param names
+ - Explain that creating an object without `resolve` does not take attributes into account
+ - ...
+
+## Future Work
+ - Add `@argument` annotation to indicate class attributes that are arguments for parameters (allows to detect mistyped names for example).
+ - Add support for lists in command line.
+ - Suggestions? Contact [me](mailto:joeydepauw@gmail.com)!
+
+
+
+
+%prep
+%autosetup -n components-1.2.8
+
+%build
+%py3_build
+
+%install
+%py3_install
+install -d -m755 %{buildroot}/%{_pkgdocdir}
+if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi
+if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi
+if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi
+if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi
+pushd %{buildroot}
+if [ -d usr/lib ]; then
+ find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/lib64 ]; then
+ find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/bin ]; then
+ find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/sbin ]; then
+ find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+touch doclist.lst
+if [ -d usr/share/man ]; then
+ find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst
+fi
+popd
+mv %{buildroot}/filelist.lst .
+mv %{buildroot}/doclist.lst .
+
+%files -n python3-components -f filelist.lst
+%dir %{python3_sitelib}/*
+
+%files help -f doclist.lst
+%{_docdir}/*
+
+%changelog
+* Wed May 10 2023 Python_Bot <Python_Bot@openeuler.org> - 1.2.8-1
+- Package Spec generated