# Pinjet
#### Small like a pin, fast like a jet! A lightweight Dependency Injection library for Python
---
## Installation
```shell
pip install pinjet-core
```
---
## Introdcution
Numerous Dependency Injection (DI) frameworks exist for Python, yet none are flawless or intuitive, whether in terms of syntax or programmatically configuring. This led to the creation of Pinjet, a simple and intuitive DI library, that requires minimal or no configuration code at all. Simply annotate your classes and methods with **Pinjet decorators**, and it take cares of the rest. Pinjet follows **annotation-first** approach, drawing inspiration from Java **Spring** and **Spring Boot**, making project initialization as effortless as possible. This approach is particularly beneficial for developers experienced in Java and Spring Boot, easing their transition to **Pinjet** projects.
*In Pinjet, the terms "annotation" and "decorator" are frequently interchanged and carry the same meaning.*
#### Quick Exmaple
```python
from pinjet.core.resolver.dependency_resolver import DependencyResolver
from pinjet.core.annotations.injectable import injectable
@injectable
class InjectableClassToBeResolved:
pass
result = DependencyResolver.resolve(InjectableClassToBeResolved)
```
Pinjet simplifies the usage by employing `@injectable` to mark any classes requiring injection. Unlike Spring Boot, where figuring out when to use `@Component` or `@Service` can be daunting, Pinjet offers a more straightforward approach.
---
## Customizing Dependency Scope
By default, any class marked with `@injectable` is considered a singleton, ensuring the same instance is returned every time `resolve()` is called. However, if you prefer a new instance on each call to `resolve()`, simply specify the dependency scope as **PROTOTYPE** in `@injectable`.
#### Example
```python
from pinjet.core.annotations.injectable import injectable
from pinjet.core.constants.dependency_scope import DependencyScope
from pinjet.core.resolver.dependency_resolver import DependencyResolver
@injectable(scope=DependencyScope.PROTOTYPE)
class PrototypeClassToBeResolved:
pass
resolved_instance_1 = DependencyResolver.resolve(PrototypeClassToBeResolved)
resolved_instance_2 = DependencyResolver.resolve(PrototypeClassToBeResolved)
is_different_instance: bool = resolved_instance_1 is not resolved_instance_2 # True
```
---
## Customization with Providers
Pinjet allows customization of class instantiation and resolution via `@provider` and `@provides`. For instance:
```python
from argparse import ArgumentParser
from pinjet.core.annotations.provider import provider, provides
from pinjet.core.resolver.dependency_resolver import DependencyResolver
@provider
class ArgumentParserProvider:
@provides
def get_argument_parser(self) -> ArgumentParser:
argument_parser = ArgumentParser()
argument_parser.add_argument('--environment', type=str, required=False)
argument_parser.set_defaults(configuration='development')
return argument_parser
argument_parser = DependencyResolver.resolve(ArgumentParser)
```
This approach is also applicable when injecting classes from third-party libraries that cannot be modified to include `@injectable`.
---
## Scanning Ahead of Time
For any Python projects where codes are distributed across modules, Pinjet recommends wrapping the program's starting point, which is often, `__main__()` with `@pinjet_application`. This allows Pinjet to:
- scan the repository to identify all dependencies with the help of `@pinjet_application`
- ensuring `DependencyResolver` is primed to serve the application.
#### Example
```python
from argparse import ArgumentParser
from pinjet.core.annotations.bootstrap import pinjet_application
from pinjet.core.resolver.dependency_resolver import DependencyResolver
@pinjet_application
def __main__():
argument_parser: ArgumentParser = DependencyResolver.resolve(ArgumentParser)
if __name__ == '__main__':
__main__()
```
Here ArgumentParser is configured to instantiate using `@provider` and `@provides` on a separate module which Pinjet is not aware of, if the program starting point is not marked with `@pinjet_application`
---
## Decorator Type Safety
To enhance developer experience, Pinjet introduces decorator type safety. For example, `@injectable` is exclusively meant for **classes**, while `@pinjet_application` is meant for **functions**. Introducing `@target`, a special decorator, which ensures decorators are applied to their intended elements.
#### Quick Demonstration
```python
from functools import wraps
from pinjet.core.annotations.target import target
from pinjet.core.constants.element_type import ElementType
@target(ElementType.FUNCTION)
def simple_function_decorator(function):
@wraps(function)
def decorator(*args, **kwargs):
result = function(*args, **kwargs)
return result
return decorator
@simple_function_decorator
def sample_function_to_be_decorated():
print('Inside sample_method_to_be_decorated')
sample_function_to_be_decorated() # prints: 'Inside sample_method_to_be_decorated'
```
With `@target` we are now enforcing that the decorator `simple_function_decorator` can only be used on functions. Attempting to use a decorator on an incorrect element type will raise a `TargetElementTypeMisMatchException` with a comprehensive error message.
You can write your own custom decorator and enforce its usage with `@target` as well!
---
## Development Status
Pinjet is in its early stages and under active development. Numerous features are planned for future releases. Suggestions, improvements, and open-source contributions are most welcome. While the project may initially seem small like a pin, it is poised to grow as fast as a jet!
---
# Thank you
Raw data
{
"_id": null,
"home_page": null,
"name": "pinjet-core",
"maintainer": null,
"docs_url": null,
"requires_python": null,
"maintainer_email": null,
"keywords": "python, dependency injection, di framework, ioc, inversion of control",
"author": "abetrack3 (Abrar Shahriar Abeed)",
"author_email": "<abrarshahriar2361@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/ee/e8/baf81d478f2c515d31b3431952d6f8fffe29d876d06d1544046c8bcba024/pinjet-core-0.0.1b1.tar.gz",
"platform": null,
"description": "\n# Pinjet\n#### Small like a pin, fast like a jet! A lightweight Dependency Injection library for Python\n\n---\n\n## Installation\n\n```shell\npip install pinjet-core\n```\n---\n\n## Introdcution\n\nNumerous Dependency Injection (DI) frameworks exist for Python, yet none are flawless or intuitive, whether in terms of syntax or programmatically configuring. This led to the creation of Pinjet, a simple and intuitive DI library, that requires minimal or no configuration code at all. Simply annotate your classes and methods with **Pinjet decorators**, and it take cares of the rest. Pinjet follows **annotation-first** approach, drawing inspiration from Java **Spring** and **Spring Boot**, making project initialization as effortless as possible. This approach is particularly beneficial for developers experienced in Java and Spring Boot, easing their transition to **Pinjet** projects.\n\n*In Pinjet, the terms \"annotation\" and \"decorator\" are frequently interchanged and carry the same meaning.*\n\n#### Quick Exmaple\n```python\nfrom pinjet.core.resolver.dependency_resolver import DependencyResolver\nfrom pinjet.core.annotations.injectable import injectable\n\n@injectable\nclass InjectableClassToBeResolved:\n pass\n\nresult = DependencyResolver.resolve(InjectableClassToBeResolved)\n\n```\n\nPinjet simplifies the usage by employing `@injectable` to mark any classes requiring injection. Unlike Spring Boot, where figuring out when to use `@Component` or `@Service` can be daunting, Pinjet offers a more straightforward approach.\n\n---\n\n## Customizing Dependency Scope\n\nBy default, any class marked with `@injectable` is considered a singleton, ensuring the same instance is returned every time `resolve()` is called. However, if you prefer a new instance on each call to `resolve()`, simply specify the dependency scope as **PROTOTYPE** in `@injectable`.\n\n#### Example\n```python\n\nfrom pinjet.core.annotations.injectable import injectable\nfrom pinjet.core.constants.dependency_scope import DependencyScope\nfrom pinjet.core.resolver.dependency_resolver import DependencyResolver\n\n\n@injectable(scope=DependencyScope.PROTOTYPE)\nclass PrototypeClassToBeResolved:\n pass\n\nresolved_instance_1 = DependencyResolver.resolve(PrototypeClassToBeResolved)\nresolved_instance_2 = DependencyResolver.resolve(PrototypeClassToBeResolved)\n\nis_different_instance: bool = resolved_instance_1 is not resolved_instance_2 # True\n```\n\n---\n\n## Customization with Providers\n\nPinjet allows customization of class instantiation and resolution via `@provider` and `@provides`. For instance:\n\n```python\nfrom argparse import ArgumentParser\nfrom pinjet.core.annotations.provider import provider, provides\nfrom pinjet.core.resolver.dependency_resolver import DependencyResolver\n\n@provider\nclass ArgumentParserProvider:\n\n @provides\n def get_argument_parser(self) -> ArgumentParser:\n argument_parser = ArgumentParser()\n argument_parser.add_argument('--environment', type=str, required=False)\n argument_parser.set_defaults(configuration='development')\n return argument_parser\n\nargument_parser = DependencyResolver.resolve(ArgumentParser)\n```\n\nThis approach is also applicable when injecting classes from third-party libraries that cannot be modified to include `@injectable`.\n\n---\n\n## Scanning Ahead of Time\n\nFor any Python projects where codes are distributed across modules, Pinjet recommends wrapping the program's starting point, which is often, `__main__()` with `@pinjet_application`. This allows Pinjet to:\n\n- scan the repository to identify all dependencies with the help of `@pinjet_application`\n- ensuring `DependencyResolver` is primed to serve the application.\n\n#### Example\n\n```python\nfrom argparse import ArgumentParser\n\nfrom pinjet.core.annotations.bootstrap import pinjet_application\nfrom pinjet.core.resolver.dependency_resolver import DependencyResolver\n\n\n@pinjet_application\ndef __main__():\n\n argument_parser: ArgumentParser = DependencyResolver.resolve(ArgumentParser)\n\n\nif __name__ == '__main__':\n __main__()\n```\nHere ArgumentParser is configured to instantiate using `@provider` and `@provides` on a separate module which Pinjet is not aware of, if the program starting point is not marked with `@pinjet_application`\n\n---\n\n## Decorator Type Safety\n\nTo enhance developer experience, Pinjet introduces decorator type safety. For example, `@injectable` is exclusively meant for **classes**, while `@pinjet_application` is meant for **functions**. Introducing `@target`, a special decorator, which ensures decorators are applied to their intended elements.\n\n#### Quick Demonstration\n\n```python\nfrom functools import wraps\nfrom pinjet.core.annotations.target import target\nfrom pinjet.core.constants.element_type import ElementType\n\n@target(ElementType.FUNCTION)\ndef simple_function_decorator(function):\n\n @wraps(function)\n def decorator(*args, **kwargs):\n\n result = function(*args, **kwargs)\n\n return result\n\n return decorator\n\n@simple_function_decorator\ndef sample_function_to_be_decorated():\n print('Inside sample_method_to_be_decorated')\n\nsample_function_to_be_decorated() # prints: 'Inside sample_method_to_be_decorated'\n\n```\n\nWith `@target` we are now enforcing that the decorator `simple_function_decorator` can only be used on functions. Attempting to use a decorator on an incorrect element type will raise a `TargetElementTypeMisMatchException` with a comprehensive error message.\n\nYou can write your own custom decorator and enforce its usage with `@target` as well!\n\n---\n\n## Development Status\n\nPinjet is in its early stages and under active development. Numerous features are planned for future releases. Suggestions, improvements, and open-source contributions are most welcome. While the project may initially seem small like a pin, it is poised to grow as fast as a jet!\n\n---\n\n# Thank you\n",
"bugtrack_url": null,
"license": null,
"summary": "Small like a pin, fast like a jet! A lightweight Dependency Injection library for python",
"version": "0.0.1b1",
"project_urls": null,
"split_keywords": [
"python",
" dependency injection",
" di framework",
" ioc",
" inversion of control"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "d36f07c91dda9eba1b5813ae8371abdfcc6b8363419f5aa72081cc2b27cc6b25",
"md5": "9ac13e323f0d9e9c250070720ba99185",
"sha256": "e823b8dabcb84ac36883dcd04e466ee479f1ce5f396b818f292da0adc53277ee"
},
"downloads": -1,
"filename": "pinjet_core-0.0.1b1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "9ac13e323f0d9e9c250070720ba99185",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 14423,
"upload_time": "2024-04-13T13:09:04",
"upload_time_iso_8601": "2024-04-13T13:09:04.084207Z",
"url": "https://files.pythonhosted.org/packages/d3/6f/07c91dda9eba1b5813ae8371abdfcc6b8363419f5aa72081cc2b27cc6b25/pinjet_core-0.0.1b1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "eee8baf81d478f2c515d31b3431952d6f8fffe29d876d06d1544046c8bcba024",
"md5": "849927e614c35a60f2f6bd4c7586624f",
"sha256": "76bb96e9e2ec873966bad978cb792a8c881e8e846b2794974368d15d8681563a"
},
"downloads": -1,
"filename": "pinjet-core-0.0.1b1.tar.gz",
"has_sig": false,
"md5_digest": "849927e614c35a60f2f6bd4c7586624f",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 10965,
"upload_time": "2024-04-13T13:09:06",
"upload_time_iso_8601": "2024-04-13T13:09:06.517586Z",
"url": "https://files.pythonhosted.org/packages/ee/e8/baf81d478f2c515d31b3431952d6f8fffe29d876d06d1544046c8bcba024/pinjet-core-0.0.1b1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-04-13 13:09:06",
"github": false,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"lcname": "pinjet-core"
}