
On Fri, Jun 17, 2011 at 9:22 AM, Alex Light <scialexlight@gmail.com> wrote:
Doing things this way however removes one of the chief benefits of the inject statement, the ability to create multiple versions of the same function with different sets of shared data.
The only way I could see runtime injection work is if you limited the injection to names already tied to the locals, in the same way parameters are. This would require one of the 3 solutions that Nick outlined. Let's assume the injection values were stored on the function object, like closures and defaults are, perhaps in an attribute named __atdef__. Then runtime injection could be used to replace __atdef__, like you can with the defaults, if it were not read-only. However, If __atdef__ were read-only, like __closure__, the runtime injection would have to do some trickery, like you have to do if you are going to mess with the closures [1]. This is a hack, in my mind, since being read-only indicates to me that the expectation is you shouldn't touch it! With that said, in that case a runtime injection function would have to generate a new function. The new function would have to have the desired __atdef__, and match any other read-only attribute. Then the injection function would copy into the new function object all the remaining attributes of the old function, including the code object. Here's an example of what I mean: def inject(f, *args): if len(args) != len(f.__atdef__): raise TypeError("__atdef__ mismatch") func = FunctionType(f.__code__, f.__globals__, f.__name__, f.__defaults__, f.__closure__, tuple(args)) # copy in the remaining attributes, like __doc__ return func You can already do this with closures and defaults. If the elements of __atdef__ are cells, like with __closure__ then you would have to throw in a little more logic. If you wanted to do kwargs, you would have to introspect the names corresponding to __atdef__. I don't know if this would have a performance impact on the new function, in case __defaults__ or __closure__ are more than just attributes on the function object. Like I said, this is a hack around the read-only attribute, but it shows that with the solutions Nick outlined, you can still have an injection function (could be used as a decorator, I suppose) . The only catch is that the names for __atdef__ would be tied into the function body at definition time, which I think is a good thing. Finally, regardless of if __atdef__ were read-only or not, I think a runtime injection function should return a new function and leave the old one untouched. That seems to meet the use-case that you presented. -eric [1] Here's an example: from types import FunctionType INJECTEDKEY = "injected_{}" OUTERLINE = " outer_{0} = injected_{0}" INNERLINE = " inner_{0} = outer_{0}" SOURCE= ("def not_important():", " def also_not_important():", " return also_not_important") def inject_closure(f, *args): injected = {} source = list(SOURCE) for i in range(len(args)): source.insert(1, OUTERLINE.format(i)) source.insert(-1, INNERLINE.format(i)) injected[INJECTEDKEY.format(i)] = args[i] exec("\n".join(source), injected, injected) closure = injected["not_important"]().__closure__ func = FunctionType(f.__code__, f.__globals__, f.__name__, f.__defaults__, closure) func.__annotations__ = f.__annotations__ func.__doc__ = f.__doc__ func.__kwdefaults__ = f.__kwdefaults__ func.__module__ = f.__module__ return func