Skip to main content

Small like a pin, fast like a jet! A lightweight Dependency Injection library for python

Project description

Pinjet

Small like a pin, fast like a jet! A lightweight Dependency Injection library for Python


Installation

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

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

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:

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

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

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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pinjet-core-0.0.1b1.tar.gz (11.0 kB view details)

Uploaded Source

Built Distribution

pinjet_core-0.0.1b1-py3-none-any.whl (14.4 kB view details)

Uploaded Python 3

File details

Details for the file pinjet-core-0.0.1b1.tar.gz.

File metadata

  • Download URL: pinjet-core-0.0.1b1.tar.gz
  • Upload date:
  • Size: 11.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.7.2

File hashes

Hashes for pinjet-core-0.0.1b1.tar.gz
Algorithm Hash digest
SHA256 76bb96e9e2ec873966bad978cb792a8c881e8e846b2794974368d15d8681563a
MD5 849927e614c35a60f2f6bd4c7586624f
BLAKE2b-256 eee8baf81d478f2c515d31b3431952d6f8fffe29d876d06d1544046c8bcba024

See more details on using hashes here.

File details

Details for the file pinjet_core-0.0.1b1-py3-none-any.whl.

File metadata

File hashes

Hashes for pinjet_core-0.0.1b1-py3-none-any.whl
Algorithm Hash digest
SHA256 e823b8dabcb84ac36883dcd04e466ee479f1ce5f396b818f292da0adc53277ee
MD5 9ac13e323f0d9e9c250070720ba99185
BLAKE2b-256 d36f07c91dda9eba1b5813ae8371abdfcc6b8363419f5aa72081cc2b27cc6b25

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page