So what's all the excitement about? MetaPython introduces some hooks to allow you to modify module code just before it is seen by the Python interpreter (at what I'm calling "import time"). The import-time syntax is pretty simple, and is (almost) all denoted by a question mark
?
prefix (question marks are currently syntax errors in regular Python). Here is a trivial example that defines an import-time function (which as we will see can be used as a macro) that will conditionally remove a function call (and the evaluation of its associated arguments). Suppose the following text is saved in a file "cremove.mpy":
def cremove(debug, expr):
debug = eval(str(debug))
defcode empty_result:
pass
if debug:
result = expr
else:
result = empty_result
return result
The idea here is that
cremove
will be called with two metapython.Code
values, debug
and expr
. cremove
will convert debug to its Python code representation by calling str()
and then evaluate the result. If debug
is true, then expr
will be returned. Otherwise a pass
statement will be returned (defined using the MetaPython import-time construct defcode
which defines a code template). To actually call cremove as a macro, we will need to define another MetaPython module, say "test_cremove.mpy":
import logging
logging.basicConfig(level=logging.DEBUG)
log = logging.getLogger(__name__)
?from cremove import cremove
def do_test():
?cremove(True, log.debug('This statement will be logged'))
?cremove(False, log.debug('This statement will not be logged'))
Here, we do an import-time import (the
?from... import
business). This makes the module we're importing available at import-time (a regular import would be seen just as another line of Python code at import-time). To actually call cremove as a macro, we just need to prefix it with the "?" as shown.Now, to actually test this, we'll need to install MetaPython and fire up an interpreter. MetaPython is available from the CheeseShop, so to get it just run
easy_install MetaPython
. Once it's installed, we can test our MetaPython code as follows:
>>> import metapython; metapython.install_import_hook()
>>> import test_cremove
>>> test_cremove.do_test()
DEBUG:test_cremove:This statement will be logged
Since macro expansion can get pretty complex and it's always tricky debugging code you've never seen, the fully-expanded module is available as the
__expanded__
attribute of the MetaPython module:
>>> print test_cremove.__expanded__
import logging
logging .basicConfig (level =logging .DEBUG )
log =logging .getLogger (__name__ )
def do_test ():
log .debug ('This statement will be logged')
pass
There's a lot more to MetaPython, but hopefully this has whetted your appetite. There's a short tutorial available that shows how you can implement the
collections.namedtuple
class factory using a macro. It also shows how you can use Jinja2 syntax along with the defcode construct to dynamically produce Python code. Have fun, and let me know what you think!