I'm trying to 'intercept' all calls to a specific module, and reroute them to another object. I'd like to do this so that I can have a fairly simple plugin architecture.
For example, in main.py
import renderer
renderer.draw('circle')
In renderer.py
specificRenderer = OpenGLRenderer()
#Then, i'd like to route all calls from main.py so that
#specificRenderer.methodName(methodArgs) is called
# i.e. the above example would call specificRenderer.draw('circle')
This means that any function can just import renderer and use it, without worrying about the details. It also means that I can completely change the renderer just by creating another object and assigning it to the 'specificRenderer' value in renderer.py
Any ideas?
My answer is very similar to @kindall's although I got the idea elsewhere. It goes a step further in the sense that it replaces the module object that's usually put in the
sys.modules
list with an instance of a class of your own design. At a minimum such a class would need to look something like this:File
renderer.py
:The
__getattr__()
method simply forwards most attribute accesses on to the real renderer object. The advantage to this level of indirection is that with it you can add your own attributes to the private_renderer
class and access them through therenderer
object imported just as though they were part of anOpenGLRenderer
object. If you give them the same name as something already in anOpenGLRenderer
object, they will be called instead, are free to forward, log, ignore, and/or modify the call before passing it along -- which can sometimes be very handy.Class instances placed in
sys.modules
are effectively singletons, so if the module is imported in other scripts in the application, they will all share the single instance created by the first one.If you don't mind that
import renderer
results an object rather than a module, then see kindall's brilliant solution.If you want to make
@property
work (i.e. each time you fetchrenderer.mytime
, you want the function corresponding toOpenGLRenderer.mytime
get called) and you want to keeprenderer
as a module, then it's impossible. Example:If you don't care about properties, i.e. it's OK for you that
mytime
gets called only once (at module load time), and it will keep returning the same timestamp, then it's possible to do it by copying all symbols from the object to the module:However, this is a one-time copy. If you add methods or other attributes to
specificRenderer
later dynamically, or change some attributes later, then they won't be automatically copied to therenderer
module. This can be fixed, however, by some ugly__setattr__
hacking.The simplest way to do that is to have main.py do
instead, then just name
specificRenderer
renderer
.Edit: This answer does not do what the OP wants; it doesn't instantiate an object and then let calls to a module be redirected to that same object. This answer is about changing which rendering module is being used.
Easiest might be to import the OpenGLRenderer in the main.py program like this:
That's code in just one place, and in the rest of your module OpenGLRenderer can be referred to as
renderer
.If you have several modules like main.py, you could have your renderer.py file be just the same line:
and then other modules can use
If OpenGLRenderer doesn't quite quack right yet, you can monkeypatch it to work as you need in the renderer.py module.
In
renderer.py
:The module name is now mapped to the
OpenGLRenderer
instance, andimport renderer
in other modules will get the same instance.Actually, you don't even need the separate module. You can just do:
... first thing in your main module. Imports of
renderer
in other modules, once again, will refer to the same instance.Are you sure you really want to do this in the first place? It isn't really how people expect modules to behave.