so, first of all, I've been into trying to have aan as simple dependency container as possible dependency container in pythonPython and with your help managed to get conceive https://github.com/dareenzo/tinydicTinyDIC, many thanks.
Now, I use the Flask for my apps, and from by background with other languages I'm used to the idea of injecting dependencies into my controllers with the typical MVC frameworks. Flask advocates for using simple functions to handle the requests, rather than classes (although it supports class based views). Those functions are invoked by Flask dynamically upon receiving web requests and there's not much I could do with them in what regards to passing stuff for them. (Maybe I could use some globals like g
or current_app
inside the view to get the dependencies I need, but I don't like it ..., feels like using a Service Locator and taking instead of asking).
Luckily though 2 things occurred to me recently:
So, I came with this idea, that:
POC code is bellowbelow and I'd really appreciate your feedback.
initialInitial considerations on advantages:
- the required dependencies to handle the request are explicitly declared.
- we obey the principle of ask don't take (from
g
orcurrent_app
).
Thanks
so, first of all, I've been into trying to have a simple dependency container as possible in python and with your help managed to get conceive https://github.com/dareenzo/tinydic, many thanks.
Now, I use the Flask for my apps, and from by background with other languages I'm used to the idea of injecting dependencies into my controllers with the typical MVC frameworks. Flask advocates for using simple functions to handle the requests, rather than classes (although it supports class based views). Those functions are invoked by Flask dynamically upon receiving web requests and there's not much I could do with them in what regards to passing stuff for them. (Maybe I could use some globals like g
or current_app
inside the view to get the dependencies I need, but I don't like it ... feels like using a Service Locator and taking instead of asking).
Luckily though 2 things occurred to me recently
So, I came with this idea, that
POC code is bellow and I'd really appreciate your feedback
initial considerations on advantages:
- the required dependencies to handle the request are explicitly declared
- we obey the principle of ask don't take (from
g
orcurrent_app
)
Thanks
I've been into trying to have an as simple as possible dependency container in Python and with your help managed to conceive TinyDIC, many thanks.
Now, I use Flask for my apps, and from by background with other languages I'm used to the idea of injecting dependencies into my controllers with the typical MVC frameworks. Flask advocates for using simple functions to handle the requests, rather than classes (although it supports class based views). Those functions are invoked by Flask dynamically upon receiving web requests and there's not much I could do with them in what regards to passing stuff for them. (Maybe I could use some globals like g
or current_app
inside the view to get the dependencies I need, but I don't like it, feels like using a Service Locator and taking instead of asking).
Luckily 2 things occurred to me recently:
So, I came with this idea, that:
POC code is below and I'd really appreciate your feedback.
Initial considerations on advantages:
- the required dependencies to handle the request are explicitly declared.
- we obey the principle of ask don't take (from
g
orcurrent_app
).
from functools import partial
import inspect
from flask import Flask
class Container(object):
def __init__(self):
self._registry = {}
def register(self, key, value, constant=False):
if constant:
self._registry[key] = lambda container: value
else:
self._registry[key] = value
def inject(self, func):
func_name = func.__name__
type_hints = zip(func.__annotations__.keys(), func.__annotations__.values())
for parameter_name, parameter_type in type_hints:
if parameter_name == 'return': break
# NOTE: Assumes we're doing the right thing: depending on the interface and not the implementation
parameter_instance = self._registry.get(parameter_type.__qualname__)(self)
func = partial(func, parameter_instance)
func.__name__ = func_name # NOTE: required by Flask
return func
class UpperCaseFormatter:
def __init__(self):
pass
def format(self, string):
return string.upper()
# Composition root
# ---
container = Container()
container.register(UpperCaseFormatter.__qualname__, UpperCaseFormatter(), True)
# Web app
# ---
app = Flask(__name__)
@app.route('/<name>')
@container.inject # NOTE: must come first so final view version with dependencies already injected is pass to Flask's app.route
def index_action(formatter: UpperCaseFormatter, name):
return formatter.format(name)
app.run(debug=True)
from functools import partial
import inspect
from flask import Flask
class Container(object):
def __init__(self):
self._registry = {}
def register(self, key, value, constant=False):
if constant:
self._registry[key] = lambda container: value
else:
self._registry[key] = value
def inject(self, func):
func_name = func.__name__
type_hints = zip(func.__annotations__.keys(), func.__annotations__.values())
for parameter_name, parameter_type in type_hints:
if parameter_name == 'return': break
# NOTE: Assumes we're doing the right thing: depending on the interface and not the implementation
parameter_instance = self._registry.get(parameter_type.__qualname__)(self)
func = partial(func, parameter_instance)
func.__name__ = func_name # NOTE: required by Flask
return func
class UpperCaseFormatter:
def __init__(self):
pass
def format(self, string):
return string.upper()
# Composition root
# ---
container = Container()
container.register(UpperCaseFormatter.__qualname__, UpperCaseFormatter(), True)
# Web app
# ---
app = Flask(__name__)
@app.route('/<name>')
@container.inject # NOTE: must come first so final view version with dependencies already injected is pass to Flask's route
def index_action(formatter: UpperCaseFormatter, name):
return formatter.format(name)
app.run(debug=True)
from functools import partial
import inspect
from flask import Flask
class Container(object):
def __init__(self):
self._registry = {}
def register(self, key, value, constant=False):
if constant:
self._registry[key] = lambda container: value
else:
self._registry[key] = value
def inject(self, func):
func_name = func.__name__
type_hints = zip(func.__annotations__.keys(), func.__annotations__.values())
for parameter_name, parameter_type in type_hints:
if parameter_name == 'return': break
# NOTE: Assumes we're doing the right thing: depending on the interface and not the implementation
parameter_instance = self._registry.get(parameter_type.__qualname__)(self)
func = partial(func, parameter_instance)
func.__name__ = func_name # NOTE: required by Flask
return func
class UpperCaseFormatter:
def __init__(self):
pass
def format(self, string):
return string.upper()
# Composition root
# ---
container = Container()
container.register(UpperCaseFormatter.__qualname__, UpperCaseFormatter(), True)
# Web app
# ---
app = Flask(__name__)
@app.route('/<name>')
@container.inject # NOTE: must come first so final view version with dependencies already injected is pass to Flask's app.route
def index_action(formatter: UpperCaseFormatter, name):
return formatter.format(name)
app.run(debug=True)