I have many ideas in the course of any given day. They are dredged up from the depths of my subconscious and placed on my mental in-tray, to be sorted in to mental heaps. Most are placed on to the “that’ll never work” heap, others are thrown on the “better not, that’s probably illegal” heap or the “plutonium isn’t available at every corner drug store you know” heap. Occasionally though, some make it to my mental out-tray.
One recent idea was about recording demos in a game written with PyGame, which is something that is not that hard to do, but requires a bit of forethought to reliably record and play back sequences of events. It would be nice if there were some way of transparently recording the on-screen action and playing it back like a movie. Recording a video file on the fly is possible, but it would probably be too processor intensive. What would be better, is the ability to store and time-stamp the function calls used to create the display/audio so that they could later be streamed back in from a file. There are actualy some commercial tools that do this with OpenGL, by providing a proxy DLL that hooks in to the GL function calls - but I figured it could be done in straight Python, given its highly dynamic nature.
A little coding convinced me that this is the case. I wrote a script that can be used to replace every callable in a module or class with a proxy callable that can record the name of the function that has been called and the parameters that it was called with. I tried it with one of my OpenGL demonstrations and it worked perfectly, the demo ran as normal, but it streamed details about every GL function call. All it took in the OpenGL code was replacing two imports with a proxy_import function I had written (see below).
#from OpenGL.GL import *
#from OpenGL.GLU import *
import pystream
pystream.proxy_import('OpenGL.GL', '*')
pystream.proxy_import('OpenGL.GLU', '*')
I’m reasonably satisfied that recording the OpenGL output of a game is practical, but I’m in no hurry to implement it in full since I have plenty of work to do on other projects. The code I wrote to hook in to a module strikes me as something that could have a number of potential uses - beyond OpenGL and games. For instance, it could be used to turn a class in to a remotely callable object. The code is below.
class FunctionProxy(object):
def __init__(self, name, function):
self.__name = name
self.__function = function
def __call__(self, *args, **kwargs):
print self.__name
return self.__function(*args, **kwargs)
class ObjectProxy(object):
def __init__(self, my_object, name=None, scope=None):
self.__object = my_object
self.__dir = self.__object.__dict__.keys()
self.__name = name
for symbol in self.__dir:
attr = getattr(self.__object, symbol)
func_name = symbol
if self.__name is not None:
func_name = self.__name + '.' + func_name
if hasattr(attr, '__call__'):
function_proxy = FunctionProxy(func_name, attr)
setattr(self, symbol, function_proxy)
if scope is not None:
scope[func_name] = function_proxy
else:
setattr(self, symbol, attr)
if scope is not None and not func_name.startswith('__'):
scope[func_name] = attr
def proxy_import(module_name, from_list=None, scope=None):
if from_list is not None:
from_list = [f.strip() for f in from_list.split(',')]
if scope is None:
import sys
scope = sys._getframe().f_back.f_globals
module = __import__(module_name, scope, None, from_list)
if from_list is None:
proxy = ObjectProxy(module, name=module_name)
scope[module_name] = proxy
elif from_list and from_list[0]=='*':
proxy = ObjectProxy(module, scope=scope)
else:
proxy = ObjectProxy(module)
for symbol in from_list:
scope[symbol] = getattr(proxy, symbol, scope=scope)
return proxy
if __name__ == "__main__":
#from math import *
proxy_import('math', '*')
print abs(sin(radians(60)))
Download this code
Update: Fixed some nonsense code in the FunctionProxy class (never code after midnight).