Search code examples
pythonflagsrunpy

Is there a way to pass flags to runpy?


I am looking for a way to pass miscellaneous options using runpy or other tools.

In particular, I would like to get the output of an optimized python script in another non-optimized python script.

python -O tobeoptimized.py

I have tried using subprocess but I can't extract the object that I need as I do in runpy.

from subprocess import PIPE, run
command = ['python','-O','tobeoptimized.py']
result = run(command, stdout=PIPE, stderr=PIPE, universal_newlines=True)

Solution

  • It's doable.
    The compile built-in function takes optimize parameter. Its values are 0, 1 and 2. If it's 0 which is like without -O, 1 is -O and 2 is -OO on the command line.
    To make runpy run module / path optimized it must be patched. Before doing that I'll define two functions for illustration.

    This is a test module. If it's run without optimization it won't print.
    app.py

    assert 0, "assert zero"
    print("Optimized")
    

    These two functions don't do all the details as runpy does. So won't function in full.

    import importlib
    import runpy
    
    optimize = 1
    
    
    def run_module(mod_name):
        spec = importlib.util.find_spec(mod_name)
        source = spec.loader.get_source(spec.name)
        code = compile(source, spec.name + ".py", "exec", optimize=optimize)
        d = {}
        exec(code, d)
        return d
    
    
    def run_path(path):
        with open(path) as f:
            source = f.read()
        code = compile(source, path, "exec", optimize=optimize)
        d = {}
        exec(code, d)
        return d
    

    This one is to patch a function in runpy which runpy.run_module uses to get code object of the module to run. The patch provides optimized code object.

    import runpy
    
    optimize = 1
    
    def _get_module_details_wrapper(func):
        def tmp(*args, **kwargs):
            mod_name, spec, _ = func(*args, **kwargs)
            source = spec.loader.get_source(spec.name)
            code = compile(source, spec.name + ".py", "exec", optimize=optimize)
            return mod_name, spec, code
    
        return tmp
    
    runpy._get_module_details = _get_module_details_wrapper(runpy._get_module_details)
    runpy.run_module('app')
    

    UPDATE
    runpy.run_path can be run with optimization turned on.

    def optimize_compile(o):
        from functools import partial
        import builtins
    
        builtins.compile = partial(compile, optimize=o)
    
    
    optimize_compile(1)
    runpy.run_path("app.py")
    
    
    optimize_compile(0)
    try:
        runpy.run_path("app.py")
    except AssertionError:
        print("assertion")
    
    optimize_compile(1)
    runpy.run_path("app.py")