Pre-processing Imports in Python

Inspired by David Beazley’s talk on various hacks that can be made to Python’s import system, I whipped up the following script for running a preprocessor over a file at import-time. In most cases, I would say this is far from a good idea, but it might be useful for creating small domain-specific languages, or defining new literals, or something like that.

import sys
import re

class ImportPreprocessor:
    def __init__(self, loader, preprocess):
        self._loader = loader
        self._preprocess = preprocess

    def find_spec(self, name, path, target=None):
        spec = self._loader.find_spec(name, path, target)
        spec.loader = ImportPreprocessor(spec.loader, self._preprocess)
        return spec

    def exec_module(self, mod):
        source = self.loader.get_source(mod.__name__)
        source = self._preprocess(source)
        code = compile(source, mod.__file__, 'exec')
        exec(code, mod.__dict__)
        return mod

def percent_to_float(source):
    pattern = r'(\d+)%'
    source = re.sub(pattern, r'(\1/100)', source)
    return source

sys.meta_path[3] = ImportPreprocessor(sys.meta_path[3], percent_to_float)

import test

"""
# contents of test.py
x = 40 * 25%
"""

print(test.x) # 10.0