Question

tl;dr: Is it possible to inject a function with a global keyword to a module in a way that the injected global would close on that module? (how or why not?)


Long version with examples

It is suggested in Runtime injection of attributes into module namespace that to inject to a module's namespace one can use the setattr method:

import foo

## before
try:
    foo.bar()
except AttributeError:
    print 'ok'

# expected to print 'ok'

## magic
def bar():
    pass

setattr(foo,'bar',bar)

## after
foo.bar()
# expected to do nothing (instead of raising an error)

However this seems not to work as one may desire with the global keyword. For example:

## foo.py
a = 4
def bar():
    global a
    a = 5
def check(expectation):
    global a
    assert a == expectation, "%s == %s"%(a,expectation)
    a = 4

## rab.py
import foo
foo.bar()
# expected to return and it does
foo.check(5)
print 'bar: ok'
def rab():
    global a
    a = 6
setattr(foo,'rab',rab)
foo.rab()
# may be expected to return but it raises AssertionError:
foo.check(6)
print 'rab: ok' # never printed

Is it possible to inject rab in a way that this foo.a becomes 6 when foo.check runs, while keeping the original foo.check?

Is it possible to inject a function with a global keyword to a module in a way that the injected global would close on that module? (how or why not?)

Était-ce utile?

La solution

The global dictionary of rab is baked in. You can see it at rab.func_globals.

The attribute is readonly, so you can't replace it

You can make a new function in foo and push the code from rab into it though :)

type(rab)(rab.func_code, foo.__dict__)

eg.

## rab.py
import foo
foo.bar()
# expected to return and it does
foo.check(5)
print 'bar: ok'
def rab():
    global a
    a = 6
foo.rab = type(rab)(rab.func_code, foo.__dict__)
foo.rab()
# may be expected to return but it raises AssertionError:
foo.check(6)
print 'rab: ok'

But don't do it

Autres conseils

Not exactly in the way you're thinking about it, but you could do:

def rab():
    foo.a = 6

foo.rab = rab  # No need to `setattr` in this simple example
foo.rab()
foo.check(6)

globals will always refer to the global namespace in the module where the function is defined (there's no way to change this)1.

1This is really the only sensible way for it to behave -- after all, there's nothing preventing you from putting rab into multiple module namespaces.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top