質問

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?)

役に立ちましたか?

解決

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

他のヒント

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.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top