[Python-ideas] 'Injecting' objects as function-local constants

Steven D'Aprano steve at pearwood.info
Fri Jun 17 07:37:46 CEST 2011


Nick Coghlan wrote:
> On Fri, Jun 17, 2011 at 3:15 AM, Jan Kaliszewski <zuo at chopin.edu.pl> wrote:
>> or even (to stress that it is a language syntax construct:
>>
>>    @inject mem=collections.Counter(), MAX_MEM=1000
>>    def do_and_remember(val, verbose=False):
> 
> While that would require the from__future__ dance to make "inject" a
> new keyword, I like it much better than the
> looks-like-a-decorator-but-isn't syntax.

What benefit is there in making inject a keyword? Even super isn't a 
keyword.

As far as I'm concerned, inject need only be a function in the functools 
module, not even a built-in, let alone a keyword.

Here's a quick and dirty version that comes close to the spirit of 
inject, as I see it. Thanks to Alex Light's earlier version.


# Credit to Alex Light.
from contextlib import contextmanager
from functools import wraps

def inject(**localArgs):
     def decorator(func):
         glbs = func.__globals__
         @wraps(func)
         def inner(*args, **kwargs):
             with _modifyGlobals(glbs, localArgs):
                 ret = func(*args, **kwargs)
             return ret
         return inner
     return decorator

@contextmanager
def _modifyGlobals(glbls, additions):
     frmglbls = glbls.copy()
     try:
         glbls.update(additions)
         yield
     finally:
         glbls.clear()
         glbls.update(frmglbls)



And demonstrating it in use:


 >>> def func(obj):
...     print(len)
...     return len(obj)+1
...
 >>> import builtins
 >>> newfunc = inject(len=lambda o: print(o) or builtins.len(o))(func)
 >>>
 >>> func([])  # Original function unchanged, still uses uninjected len.
<built-in function len>
1
 >>> newfunc([])  # New function uses injected len.
<function <lambda> at 0xb7c2f86c>
[]
1


And as a decorator:

 >>> @inject(a=1)
... def spam():
...     print(a)
...
 >>> a = 42
 >>> spam()
1


Unfortunately, this proof-of-concept inject function doesn't actually 
inject into locals, hence the "import builtins" work-around. But it 
demonstrates the intent, and the API.



-- 
Steven



More information about the Python-ideas mailing list