Collections for the Injector Package
Project description
Injector Collections
This package adds collections to the
Injector python package. A
collection is an injectable class containing several other classes, which were
tagged using the @CollectionItem
keyword and a designated Collection
-class.
Installation
pip install injector_collections
Setup
To be able to use this package, You must create a new module-directory inside
your python project. The name of this module-directory does not matter, let's
name it my_collections
here. Inside this module-directory You must create a
file (module) named stubs.py
(and of course an __init__.py
).
Provided your root-directory is named src
, the following file-tree will be the
result:
src
└── my_collections
├── __init__.py
└── stubs.py
All files can be initially empty.
Usage / Example
Be sure to have done everything described in Setup.
Let's say You have an application and want to add several plugins which are all used in the app afterwards, but of course this list of plugins must be easily extensible. You already use injector to instantiate your App:
# app.py
from injector import inject
class App:
@inject
def __init__(self, '''plugins shall be injected here''', '''some other injected classes'''):
# here comes some code
def run(self):
# plugins should be executed here
# runs the app
from injector import Injector
injector = Injector()
outer = injector.get(App)
Now the first step is to create a stub collection for your plugins:
# my_collections/stub.py
from injector_collections import Collection
class PluginCollection(Collection):
pass
Note: The collection class (here PluginCollection
) should not have any
implementation. Currently any implementation will just be ignored and cannot be
used after the actual class was generated from the stub.
Next add some Plugins as an example. And Tag them with @PluginCollection
and
your previously defined PluginCollection
as argument:
# plugins.py
from injector_collections import CollectionItem
# The @CollectionItem decorator needs those stubs as argument
from my_collections.stubs import PluginCollection
@CollectionItem(PluginCollection)
class HelloPlugin:
def run(self):
print("Hello Friends!")
@CollectionItem(PluginCollection)
class GoodbyPlugin:
def run(self):
print("Goodby Friends!")
Important: Currently you need to import CollectionItem
literally, as the
code will be scanned for files containing the @CollectionItem
string, which
will then be imported to generate the collections!
Now we're almost done. The last thing to do, before we can use our actual collections is to generate them, of course! Create a small script under your projects root-directory:
# generate.py
from injector import inject
from injector_collections import generateCollections
# First argument of generateCollections is your my_collections-module name, the
# second a list of modules containing any collection items (in this case your
# plugins-module).
generateCollections(inject, "my_collections", ['plugins'])
And execute it:
python ./generate.py
Now you just need to import the PluginCollection
to your App
and use it:
# app.py
# mark that this time, we import from the generated collections, not the stubs!
from my_collections.generated import PluginCollection
from plugins import HelloPlugin
from injector import inject
class App:
@inject
def __init__(self, plugins: PluginCollection, '''some other injected classes'''):
# here comes some code
self.plugins = plugins
def run(self):
# plugins.items contains a dict containing the plugins:
for plugin in self.plugins.items.values():
plugin.run() # prints "Hello Friends!" and "Goodby Friends!"
# Or just call a single plugin from the collection:
self.plugins[HelloPlugin].run()
# also getting plugins simply by class name (if unambigous in this
# collection) is possible
self.plugins.byClassname['HelloPlugin'].run()
...
Type Hinting for Items in a Collection
If all items in a collection e.g. implement a common Interface, the generated
Collections may make use of type-Hinting. Simply implement a
getItemType
-Method in your stubs like that:
# my_collections/stub.py
from plugins import PluginInterface
from injector_collections import Collection
class PluginCollection(Collection):
@classmethod
def getItemType(cls):
return PluginInterface
After that the PluginCollection.items
, PluginCollection.__get__
,
PluginCollection.__set__
und PluginCollection.byClassname
attributes/methods
have proper type hints on PluginItemInterface
.
Items with Assisted Injection
If some of the items in the created collection have non-injectable parameters,
one can use the assisted
-parameter for the @CollectionItem
-decorator, e.g.:
from injector_collections import CollectionItem
from my_collections.stubs import PluginCollection
@CollectionItem(PluginCollection, assisted=True)
class HelloPlugin:
def __init__(someNotInjectableParameter: SomeClass):
self.param = someNotInjectableParameter
def run(self):
print("Hello Friends!")
Now the item will not be directly injected into the Collection, but instead a
ClassAssitedBuilder
instance for that item. Don't forget to adjust the type
hinting.
FAQ or Why does it not work?
The process of creating the collections is quite clumsy and assumes that the developer sets quite a lot of things right. If it does not work, it is quite likely, that You just forgot one of these awful details:
- Is there a stub for the collection?
- Are all items annotated with @CollectionItem?
- Is the Type-Argument of @CollectionItem the correct collection (stub)?
- Are the items all inside a proper module hierarchy (the directory and all
parent directories of the items should contain an
__init__.py
). - Have You generated the collections using
generateCollections
? - Did You add the module or a parent module of the items to the module list of
generateCollections
?
When the complexity of the project increases, it is quite likely, that You will encounter recursive import problems. Some things You can do:
- Import only stubs, wherever possible.
- If You just need typehinting for a class, which may cause import recursion.
You can import it only for type-checking like this:
from abc import abstractmethod from typing import TYPE_CHECKING # avoid circular imports, we only need PipelineContext for the LSP (type check) if TYPE_CHECKING: from xyz import Someclass class SomeInterface: @abstractmethod def hello() -> 'SomeClass': pass
- There are some really strange cases, where a certain collection name causes an
error with
injector
. I can't say, if it's a bug ininjector
orinjector_collections
. Just try another collection name. - If the project grows, there may be circular dependencies, because all stubs
are in a single file. That can be circumvented by changing the file structure:
src └── my_collections ├── __init__.py └── stubs ├── __init__.py ├── PluginCollection.py └── SomeOtherCollection.py
__init__.py
must have the following contents:from my_collections.stubs.PluginCollection import * from my_collections.stubs.SomeOtherCollection import * ... # other collection definitions not included in a seperate file
Now import frommy_collections.stubs.PluginCollection
andmy_collections.stubs.SomeOtherCollection
seperately in your code when using@CollectionItem(PluginCollection)
or@CollectionItem(SomeOtherCollection)
. - There may be another bug, especially related to assisted injection. This may
be circumvented with using different imports on collection generation and
afterwards:
if GENERATING_INJECTOR_COLLECTIONS: from my_collections.generated import PluginCollection else: from injection.collections.generated.PluginCollection import PluginCollection
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file injector_collections-0.0.23.tar.gz
.
File metadata
- Download URL: injector_collections-0.0.23.tar.gz
- Upload date:
- Size: 6.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.0.1.dev0+g94f810c.d20240510 CPython/3.12.4
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | c4675267a0d29788b73b247f607b04bd8144df3be9246b6eb0d5dc88c62b12d4 |
|
MD5 | 8b6c469080bace4a26a42dd9b7925a3d |
|
BLAKE2b-256 | 3528094a4f8b82204c2369b38096c215aa8d95144eb5d9f02e479e097fbfa73d |
File details
Details for the file injector_collections-0.0.23-py3-none-any.whl
.
File metadata
- Download URL: injector_collections-0.0.23-py3-none-any.whl
- Upload date:
- Size: 8.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.0.1.dev0+g94f810c.d20240510 CPython/3.12.4
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 21f12045d3b7574a1c671caf97d9f4ae9f8788f483338baec4b4ea12e3204125 |
|
MD5 | bf5df6eb8a49e8b232af79a79dcc9916 |
|
BLAKE2b-256 | faac48ab37f41fa0981ff6b40dd41980a494cdc7fa45459dd971750863422e30 |