Programmatically replacing an API for another module

Tom Plunket tomas at fancy.org
Mon Nov 20 19:52:51 EST 2006


Larry Bates wrote:

> Whatever points to something in Python is what is called.

Right.

> f=abc
> print f(1,2)
> f=efg
> print f(1,2)

Sure.  I realized after posting that what I really wanted to ask was
how to replace an imported module.  Steve's earlier response is akin
to what I'm looking for, although with far too much work.  ;)

> As long as you implement the full API, you can replace functions
> with others quite easily.  Note: this also works with class methods.

Right, I can replace a function at a time:

import os

def my_join(*args):
   return '/'.join(*args)

os.path.join = my_join

...but I'm wondering if there's a straight-forward way to inject the
local list of functions into another module.

What I ran into after posting is that I don't know how to get the
__dict__ for the module's scope; is it available?  I.e. I can do
'print os.path.__dict__', but 'print __dict__' at module scope results
in a NameError since __dict__ is undefined at that scope.  However, if
I could do 'os.path = local_module' somehow, I'd be getting basically
what I want.  Maybe I could just have a my_os_path.py, and in my test
code import it as os.path, and then the other code would see that
os.path is already loaded and not bother doing so again?  Haven't
tried it, am interested in the answer, but I do have enough to work
with for now...  Replacing a module in this way also ensures that I'm
only calling the parts of the API that I intend to call, which is a
handy side-effect.

> Observation: IMHO it is going to be really hard to keep your functional
> shims up to date as you change your REAL functions/methods.  You also
> are testing different code than you are executing which doesn't make
> the unit tests nearly as valuable.

Depends on what all I'm mocking.  If I'm mocking an expensive service
across the internet that is time consuming to connect to, or even
mocking a well-defined system API call that is similarly time
consuming to call, then I can stub something in that doesn't do
anything but validate the arguments.  Additionally, I can "document"
the caveats and possibly-errant behavior with my mock, and
subsequently get automatic testing that I'm dealing with API bugs
properly.

These aren't functional shims, which would imply that they sit in
between two other bits of interesting code under my control.  They're
functional leaves, which are the end point of the operational chain.
E.g. I queue up all of the commands that I'm sending to the database
and verify that they're what I intended to send, and I accept that the
database will interpret those commands as it should.

Thanks for the feedback, regardless,
-tom!



More information about the Python-list mailing list