Skip to content

Instantly share code, notes, and snippets.

@zipcode
Created July 8, 2014 06:10
Show Gist options
  • Save zipcode/ca6f5286658d3c81756c to your computer and use it in GitHub Desktop.
Save zipcode/ca6f5286658d3c81756c to your computer and use it in GitHub Desktop.
from functools import wraps
class instancemethod(object):
def __init__(self, f):
self.f = f
def __get__(self, obj, cls=None):
if obj is None:
obj = cls.instance()
@wraps(self.f)
def newfunc(*args, **kwargs):
return self.f(obj, *args, **kwargs)
return newfunc
class InjectorException(Exception):
pass
class Injector(object):
__instance = None
def __init__(self):
self.__bindings = {}
self.bindvalue("Injector", self)
@classmethod
def instance(cls):
if cls.__instance is None:
cls.__instance = cls()
return cls.__instance
def setinstance(cls):
cls.__class__.__instance = cls
@instancemethod
def bind(self, name, obj, force=False):
if not force and self.__bindings.get(name) is not None:
raise InjectorException("Duplicate binding of %s" % name)
self.__bindings[name] = obj
return self
@instancemethod
def bindvalue(self, name, value, **kwargs):
self.bind(name, lambda: value, **kwargs)
@instancemethod
def inject(self, name):
obj = self.__bindings[name]
if obj is None:
raise InjectorException("No binding for %s" % name)
return obj()
def inject(*bindings, **kwbindings):
def decorate(cls):
@wraps(cls)
def newcls(*args, **kwargs):
args = list(args)
for position in xrange(len(args), len(bindings)):
if bindings[position] is None:
raise InjectorException("Attempted to bind in a position marked \"None\". Did you get the argument list right?")
injected = Injector.inject(bindings[position])
args.append(injected)
for key in kwbindings.keys():
if kwargs.get(key) is None:
kwargs[key] = Injector.inject(kwbindings[key])
return cls(*args, **kwargs)
return newcls
return decorate
def bind(name=None):
if not (isinstance(name, str) or name is None):
raise AssertionError("Cannot bind object as a name. Did you write @bind instead of @bind()?")
def decorate(cls):
Injector.bind(name or cls.__name__, cls)
return cls
return decorate
def bindinstance(name=None):
if not (isinstance(name, str) or name is None):
raise AssertionError("Cannot bindinstance object. Did you write @bindinstance instead of @bindinstance()?")
def decorate(cls):
Injector.bindvalue(name or cls.__name__, cls())
return cls
return decorate
def example():
# Save the old injector
i = Injector.instance()
Injector().setinstance()
Injector.bindvalue("message", "Hello World")
@bindinstance()
class Printer(object):
def __init__(self):
self.count = 0
def say(self, message):
print "%03d : %s" % (self.count, message)
self.count += 1
@inject("message", printer="Printer")
def do(message, printer=None):
printer.say(message)
printer.say(message + "!!")
do()
do("yep!")
print "--- manually setting printer"
do(printer = Printer())
# Return to old injector
i.setinstance()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment