Created
December 20, 2018 22:54
-
-
Save josiah14/c7ad1887de5d3f93908952774ff9770f to your computer and use it in GitHub Desktop.
Functional Dependency Injection In Python (Inspired by the Reader Monad)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Reader(object): | |
pass | |
def to_camel_case(snake_str): | |
parts = snake_str.split('_') | |
return parts[0] + ''.join([s.capitalize() for s in parts[1:]]) | |
def strip_leading_underscores(string): | |
if string.startswith('_'): | |
string = string[1:] | |
string = string[1:] if string.startswith('_') else string | |
return string | |
def inject_dependency_kwarg(dependency_name, dependency_value, *args): | |
new_reader_name = \ | |
Reader.__name__ + to_camel_case(dependency_name).capitalize() | |
new_reader = type(new_reader_name, (Reader,), {})() | |
new_reader.__setattr__('__name__', new_reader_name) | |
for func in args: | |
func_name = strip_leading_underscores(func.__name__) | |
partial_func = partial(func, **{dependency_name: dependency_value}) | |
partial_func.__setattr__('__name__', func_name) | |
new_reader.__setattr__(func_name, partial_func) | |
return new_reader | |
def inject_dependency_first_arg(dependency_name, dependency_value, *args): | |
new_reader_name = \ | |
Reader.__name__ + to_camel_case(dependency_name).capitalize() | |
new_reader = type(new_reader_name, (Reader,), {})() | |
new_reader.__setattr__('__name__', new_reader_name) | |
for func in args: | |
func_name = strip_leading_underscores(func.__name__) | |
partial_func = partial(func, dependency_value) | |
partial_func.__setattr__('__name__', func_name) | |
new_reader.__setattr__(func_name, partial_func) | |
return new_reader | |
def import_public_attributes(obj, parent_frame_locals): | |
""" | |
Imports the public attributes of the provided object so that they | |
can be used without referencing the object from the parent frame (the caller). | |
:param obj: The object whose public attributes to import | |
:param parant_frame_locals: The dictionary returned from locals() in the | |
function call. | |
Example: | |
In [118]: def add(*args, logger=None): | |
...: logger.info('adding %s' % str(args)) | |
...: return reduce(lambda acc, x: acc + x, args) | |
...: | |
In [119]: logger_reader = inject_dependency_kwarg('logger', logger, add) | |
In [120]: logger_reader.add(1,2,3) | |
2018/12/18 14:25:42 adding (1, 2, 3) | |
Out[120]: 6 | |
In [121]: logger_reader.add.__name__ | |
Out[121]: 'add' | |
In [122]: import_public_attributes(logger_reader, locals()) | |
In [123]: add | |
Out[123]: functools.partial(<function add at 0x127a47840>, logger=<logging.Logger object at 0x1279fc160>) | |
In [125]: add(1,2,3) | |
2018/12/18 16:58:39 adding (1, 2, 3) | |
Out[125]: 6 | |
In [126]: one_reader = inject_dependency_first_arg('one', 1, logger_reader.add) | |
In [128]: one_reader.add(2,3) | |
2018/12/18 17:00:38 adding (1, 2, 3) | |
Out[128]: 6 | |
In [129]: import_public_attributes(one_reader, locals()) | |
In [130]: add(2,3) | |
2018/12/18 17:00:59 adding (1, 2, 3) | |
Out[130]: 6 | |
""" | |
public_attrs = [attr for attr in dir(obj) if not attr.startswith('_')] | |
for attr in public_attrs: | |
parent_frame_locals[attr] = obj.__getattribute__(attr) |
Author
josiah14
commented
Dec 21, 2018
TODO: make it so that you don't have to pass in locals()
, but rather, the function extracts the locals()
out of the caller-frame itself. May or may not be possible...
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment