3

I have a function inside a module that is broken

broken.py

def brokenfunc():
    dependency()


def dependency():
    print "hello"

The routine depends on another function dependency, which is fine. I need to monkey patch broken, so in another module I did

patched.py

import broken

def brokenfunc():
    print "patched"

    dependency()

brokenfunc.__globals__["dependency"]=broken.brokenfunc.__globals__["dependency"]
broken.brokenfunc = brokenfunc

broken.brokenfunc()

Clearly, I have to override the globals because the dependency in the patched function is defined in the patched module and would look for dependency there.

This works, but I am unsatisfied with the hack. I tried to update the whole globals dictionary, but in that case I override too much and the broken function keeps runnning. Is this the correct way of doing it (considering also corner cases) or there's another, correct strategy?

Stefano Borini
  • 138,652
  • 96
  • 297
  • 431

1 Answers1

4

You can just reference the dependency in your new function:

import broken

def brokenfunc():
    print "patched"

    broken.dependency()

broken.brokenfunc = brokenfunc

or you can add dependency to your module globals by import:

import broken
from broken import dependency

def brokenfunc():
    print "patched"

    dependency()

broken.brokenfunc = brokenfunc

There is really no need to go to such lengths; brokenfunc.__globals__ is just your current module namespace.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Yep. Today I won the "How to make my life more complicated" prize – Stefano Borini Jul 08 '14 at 11:58
  • except my reason for overriding in `__globals__` is mocking. are there any corner-cases I should be aware of? (disclaimer: I've tried `mock` and `minimock`, but they didn't work out as I wished, so I do it manually instead). [Data model](https://docs.python.org/2/reference/datamodel.html) has it that *the attribute* (the reference itself) is read only. – n611x007 Aug 12 '15 at 10:58
  • Why the insistence on accessing `__globals__` though? There are more readable ways to accessing a module namespace, even dynamically. And try harder with `mock`, instead. – Martijn Pieters Aug 12 '15 at 11:00
  • I guess it may not be the module namespace, in case of nested def, as in terming it nonlocal. But you have a point, and I don't know the dynamic way. – n611x007 Aug 12 '15 at 11:01
  • 1
    @naxa: Nested functions are not so easily patched. See [Can you patch \*just\* a nested function with closure, or must the whole outer function be repeated?](http://stackoverflow.com/q/27550228) – Martijn Pieters Aug 12 '15 at 11:03
  • If I then do `from broken import brokenfunc`, will it be the patched one or original one? – Jackson Tale Oct 25 '19 at 09:26
  • @JacksonTale: that depends; you need to make sure that that import runs **after** the monkey patching has taken place. `brokenfunc` is a new global name referencing whatever `broken.brokenfunc` referenced at the time that `from broken import brokenfunc` ran. – Martijn Pieters Oct 25 '19 at 13:11