52

Is there a framework equivalent to Guice (http://code.google.com/p/google-guice) for Python?

Alexander Oh
  • 24,223
  • 14
  • 73
  • 76
Mark Roddy
  • 27,122
  • 19
  • 67
  • 71

18 Answers18

25

Spring Python is an offshoot of the Java-based Spring Framework and Spring Security, targeted for Python. This project currently contains the following features:

  • Inversion Of Control (dependency injection) - use either classic XML, or the python @Object decorator (similar to the Spring JavaConfig subproject) to wire things together. While the @Object format isn't identical to the Guice style (centralized wiring vs. wiring information in each class), it is a valuable way to wire your python app.
  • Aspect-oriented Programming - apply interceptors in a horizontal programming paradigm (instead of vertical OOP inheritance) for things like transactions, security, and caching.
  • DatabaseTemplate - Reading from the database requires a monotonous cycle of opening cursors, reading rows, and closing cursors, along with exception handlers. With this template class, all you need is the SQL query and row-handling function. Spring Python does the rest.
  • Database Transactions - Wrapping multiple database calls with transactions can make your code hard to read. This module provides multiple ways to define transactions without making things complicated.
  • Security - Plugin security interceptors to lock down access to your methods, utilizing both authentication and domain authorization.
  • Remoting - It is easy to convert your local application into a distributed one. If you have already built your client and server pieces using the IoC container, then going from local to distributed is just a configuration change.
  • Samples - to help demonstrate various features of Spring Python, some sample applications have been created:
    • PetClinic - Spring Framework's sample web app has been rebuilt from the ground up using python web containers including: CherryPy. Go check it out for an example of how to use this framework. (NOTE: Other python web frameworks will be added to this list in the future).
    • Spring Wiki - Wikis are powerful ways to store and manage content, so we created a simple one as a demo!
    • Spring Bot - Use Spring Python to build a tiny bot to manage the IRC channel of your open source project.
gregturn
  • 2,625
  • 3
  • 25
  • 40
  • 16
    It's worth mentioning that SpringPython does **not** support Python 3, and it haven't seen any activity since 2014... – canni Jul 14 '16 at 14:43
20

I like this simple and neat framework.

http://pypi.python.org/pypi/injector/

Dependency injection as a formal pattern is less useful in Python than in other languages, primarily due to its support for keyword arguments, the ease with which objects can be mocked, and its dynamic nature.

That said, a framework for assisting in this process can remove a lot of boiler-plate from larger applications. That's where Injector can help. It automatically and transitively provides keyword arguments with their values. As an added benefit, Injector encourages nicely compartmentalized code through the use of Module s.

While being inspired by Guice, it does not slavishly replicate its API. Providing a Pythonic API trumps faithfulness.

Andro Selva
  • 53,910
  • 52
  • 193
  • 240
11

As an alternative to monkeypatching, I like DI. A nascent project such as http://code.google.com/p/snake-guice/ may fit the bill.

Or see the blog post Dependency Injection in Python by Dennis Kempin (Aug '08).

Kirk Woll
  • 76,112
  • 22
  • 180
  • 195
user35818
  • 111
  • 2
9

pinject (https://github.com/google/pinject) is a newer alternative. It seems to be maintained by Google and follows a similar pattern to Guice (https://code.google.com/p/google-guice/), it's Java counterpart.

phss
  • 1,012
  • 10
  • 22
  • 4
    While pinject seems great, it has not been maintained for years, with pull requests adding support for Python 2.6 and 3.x left open for about as long. – David Pärsson Sep 07 '16 at 07:01
  • Just FYI that pinject seems active again. – outofthecave Jun 29 '20 at 20:40
  • Now it seems unmaintained again. Last commit was nearly 2 years ago and the github repository shows a warning *"This repository has been archived by the owner on Jan 10, 2023. It is now read-only."*. – Socowi Jan 12 '23 at 08:53
5

Besides that:

  1. Zope component architekture
  2. pyContainer
Bartosz Radaczyński
  • 18,396
  • 14
  • 54
  • 61
3

If you just want to do dependency injection in Python, you don't need a framework. Have a look at Dependency Injection the Python Way. It's really quick and easy, and only c. 50 lines of code.

Skilldrick
  • 69,215
  • 34
  • 177
  • 229
  • 4
    I did not like this article because the dependencies are not really injected and the classes will depend on the feature locator. – chiborg May 03 '11 at 09:42
3

There is a somewhat Guicey python-inject project. It's quite active, and a LOT less code then Spring-python, but then again, I haven't found a reason to use it yet.

Michael_Scharf
  • 33,154
  • 22
  • 74
  • 95
Y.H Wong
  • 7,151
  • 3
  • 33
  • 35
3

Will leave my 5 cents here :)

https://pypi.python.org/pypi/dependency_injector

"""Pythonic way for Dependency Injection."""

from dependency_injector import providers
from dependency_injector import injections


@providers.DelegatedCallable
def get_user_info(user_id):
    """Return user info."""
    raise NotImplementedError()


@providers.Factory
@injections.inject(get_user_info=get_user_info)
class AuthComponent(object):
    """Some authentication component."""

    def __init__(self, get_user_info):
        """Initializer."""
        self.get_user_info = get_user_info

    def authenticate_user(self, token):
        """Authenticate user by token."""
        user_info = self.get_user_info(user_id=token + '1')
        return user_info


print AuthComponent
print get_user_info


@providers.override(get_user_info)
@providers.DelegatedCallable
def get_user_info(user_id):
    """Return user info."""
    return {'user_id': user_id}


print AuthComponent().authenticate_user(token='abc')
# {'user_id': 'abc1'}

UPDATED

Some time passed and Dependency Injector is a bit different now. It's better to start from Dependency Injector GitHub page for getting actual examples - https://github.com/ets-labs/python-dependency-injector

Roman Mogylatov
  • 516
  • 4
  • 10
  • 1
    I like this answer because you gave code. If I get a chance to try it, I'll give my vote. – Mike D Jun 15 '17 at 23:13
  • Hi @MikeD, thanks, nice to hear. Code here is a bit outdated, please, go to https://github.com/ets-labs/python-dependency-injector so you could find actual one. – Roman Mogylatov Jun 17 '17 at 10:44
3

After years using Python without any DI autowiring framework and Java with Spring I've come to realize that plain simple Python code often doesn't need a framework for dependency injection autowiring (autowiring is what Guice and Spring both do in Java), i.e., just doing something like this is enough:

def foo(dep = None):  # great for unit testing!
    ...

This is pure dependency injection (quite simple) but without magical frameworks for automatically injecting them for you.

Though as I dealt with bigger applications this approach wasn't cutting it anymore. So I've come up with injectable a micro-framework that wouldn't feel non-pythonic and yet would provide first class dependency injection autowiring.

Under the motto Dependency Injection for Humans™ this is what it looks like:

# some_service.py
class SomeService:
    @autowired
    def __init__(
        self,
        database: Autowired(Database),
        message_brokers: Autowired(List[Broker]),
    ):
        pending = database.retrieve_pending_messages()
        for broker in message_brokers:
            broker.send_pending(pending)
# database.py
@injectable
class Database:
    ...
# message_broker.py
class MessageBroker(ABC):
    def send_pending(messages):
        ...
# kafka_producer.py
@injectable
class KafkaProducer(MessageBroker):
    ...
# sqs_producer.py
@injectable
class SQSProducer(MessageBroker):
    ...
Rodrigo Oliveira
  • 1,452
  • 4
  • 19
  • 36
1

I made a lib to do this https://github.com/ettoreleandrotognoli/python-cdi I hope that helps

It's available on pypi: https://pypi.python.org/pypi/pycdi

With it you can make injections with python2

import logging
from logging import Logger

from pycdi import Inject, Singleton, Producer
from pycdi.shortcuts import call


@Producer(str, _context='app_name')
def get_app_name():
    return 'PyCDI'


@Singleton(produce_type=Logger)
@Inject(app_name=str, _context='app_name')
def get_logger(app_name):
    return logging.getLogger(app_name)


@Inject(name=(str, 'app_name'), logger=Logger)
def main(name, logger):
    logger.info('I\'m starting...')
    print('Hello World!!!\nI\'m a example of %s' % name)
    logger.debug('I\'m finishing...')


call(main)

And using type hints from python3

import logging
from logging import Logger

from pycdi import Inject, Singleton, Producer
from pycdi.shortcuts import call


@Producer(_context='app_name')
def get_app_name() -> str:
    return 'PyCDI'


@Singleton()
@Inject(logger_name='app_name')
def get_logger(logger_name: str) -> Logger:
    return logging.getLogger(logger_name)


@Inject(name='app_name')
def main(name: str, logger: Logger):
    logger.info('I\'m starting...')
    print('Hello World!!!\nI\'m a example of %s' % name)
    logger.debug('I\'m finishing...')


call(main)
  • 1
    While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/low-quality-posts/17853169) – curveball Nov 06 '17 at 12:00
  • I have put some examples , is better now? – Éttore Leandro Tognoli Nov 06 '17 at 13:01
  • the answer in its form is definitely better now. As for the content - I cannot say :) – curveball Nov 06 '17 at 13:15
  • Ok, thanks! So you only check if the answer follow a pattern not the content? – Éttore Leandro Tognoli Nov 06 '17 at 13:18
  • I saw your answer through special moreration section on this site. There are guidlines as to how to write a good answer regardless of the content - you can find them here on SO. When you post just a link - it is not considered to be a good answer. You may post links but they should be accompanied by your own thoughts, comments, code etc. – curveball Nov 06 '17 at 13:43
  • I'm only trying to understand the system, the question was very specific "Is there a framework equivalent to Guice (http://code.google.com/p/google-guice) for Python?" a simple yes could answer that. But I answer "I made somethint to do that <>" and it is not good enought. Whereas a answer like this " I like this simple and neat framework. <>" receives 13 votes. But nevermind, I wont lose more of my time with this. – Éttore Leandro Tognoli Nov 06 '17 at 14:26
  • Aside from bad answers bad questions exist as well. Also you shouldn't take this word - "bad" - literally and personally. "Bad" answer/question in this context mostly refers to formatting, grammar and logical structure, not the content itself. The question you cited is a bad one. – curveball Nov 06 '17 at 14:48
  • Just read the faqs to get a feeling for how a good answer/question should look. There are tons of bad answers/questions on here. Some of them are pretty old and they are treated as legacy. They are closed, but not deleted because they generate some traffic (sometimes significant traffic) for the site. Others are still displayed because the algorithm has't discovered them yet. The SO managers as of now don't want to turn SO into a link farm. You have all the tools to give meaninful answers here. Besides, the links are not prohibited as such - just give a little more than a link. – curveball Nov 06 '17 at 14:48
1

Enterprython is a small framework providing dependency-injection, building the object graph automatically based on type hints.

Tobias Hermann
  • 9,936
  • 6
  • 61
  • 134
1

Here is a small example for a dependency injection container that does constructor injection based on the constructor argument names:

http://code.activestate.com/recipes/576609-non-invasive-dependency-injection/

chiborg
  • 26,978
  • 14
  • 97
  • 115
1

Here's a good comparison (19 September 2020): Comparison of Dependency Injection Libraries in Python, and my favorite one

His winners are:

  1. proofit404/dependencies (Injector) "simple, but provided all the necessary features. If you need something that’s not provided then just think about the design in your application, cause the flow might be somewhere there. Beautiful configuration. Perfect match for agile projects"

  2. ets-labs/python-dependency-injector "very expanded library, with constant support, the problem is it’s boilerplate, if that does not bother you, then it’s a great choice"

Community
  • 1
  • 1
0

There's dyject (http://dyject.com), a lightweight framework for both Python 2 and Python 3 that uses the built-in ConfigParser

djsumdog
  • 2,560
  • 1
  • 29
  • 55
0

If you want a guice like (the new new like they say), I recently made something close in Python 3 that best suited my simple needs for a side project.

All you need is an @inject on a method (__init__ included of course). The rest is done through annotations.

from py3njection import inject
from some_package import ClassToInject

class Demo:
    @inject
    def __init__(self, object_to_use: ClassToInject):
        self.dependency = object_to_use

demo = Demo()

https://pypi.python.org/pypi/py3njection

Aigrefin
  • 125
  • 8
0

I recently released a neat (IMHO) micro library for DI in python:

https://github.com/suned/serum

0

If you prefer a really tiny solution there's a little function, it is just a dependency setter.

https://github.com/liuggio/Ultra-Lightweight-Dependency-Injector-Python

  • 1
    This is not more convenient than instantiating the objects directly. Dependency injection does not require a framework. – deamon Apr 29 '12 at 19:44
0

I'm actively developing pinject for Python >= 3.6. It's quite easy to use:

class MyObject:
    my_service: MyService = INJECTED
    my_config: str = INJECTED
David
  • 2,942
  • 33
  • 16