Search code examples
pythonpython-3.xsubprocesscompiler-optimizationassert

Running a Python script in optimized mode from another Python script


Is there a way to run a python script in optimized mode from another Python (Python 3) script?

If I have the following test.py script (which reads the built-in constant __debug__):

if __debug__:
    print('Debug ON')
else:
    print('Debug OFF')

then:

  • python text.py prints Debug ON
  • python -OO text.py prints Debug OFF

because of how the constant __debug__ works:

This constant is true if Python was not started with an -O option. See also the assert statement.

Also, the value of __debug__ cannot be changed at runtime: __debug__ is a constant, as noted in the documentation here and here. The value of __debug__ is determined when the Python interpreter starts.

The following correctly prints Debug OFF

import subprocess
subprocess.run(["python", "-OO", "test.py"])

But is there a more pythonic way? The above doesn't seem very portable if the interpreter is not called python.

I've already searched here and the web without luck.


Solution

  • Using compile

    I've come up with a solution using the built-in function compile, as follows.

    Contents of the file main.py:

    with open('test.py') as f:
        source_code = f.read()
    compiled = compile(
        source_code,
        filename='test.py', mode='exec', optimize=2)
    exec(compiled)
    

    Contents of the file test.py:

    if __debug__:
        print('Debug ON')
    else:
        print('Debug OFF')
    

    The output from running python main.py is:

    Debug OFF
    

    Possible values for the parameter optimize:

    • -1: use same optimization level as the Python interpreter that is running the function compile
    • 0: no optimization, and __debug__ == true
    • 1: like -O, i.e., removes assert statements, and __debug__ == false
    • 2: like -OO, i.e., removes also docstrings.

    Don't know if it's the best option, just sharing if can be useful fo others.

    Using subprocess.run

    The subprocess-based approach is still more concise, and can be made portable by using sys.executable:

    import subprocess
    import sys
    
    if not sys.executable:
        raise RuntimeError(sys.executable)
    proc = subprocess.run(
        [sys.executable, '-OO', 'test.py'],
        capture_output=True, text=True)
    if proc.returncode != 0:
        raise RuntimeError(proc.returncode)
    

    The above code calls the function subprocess.run.

    The check for the value of the variable sys.executable is motivated by the documentation of CPython:

    If Python is unable to retrieve the real path to its executable, sys.executable will be an empty string or None.

    The check is implemented with a raise statement, instead of an assert statement, in order to check also in cases that the above Python code is itself run with optimization requested from Python, e.g., by using python -O or python -OO or the environment variable PYTHONOPTIMIZE.

    When optimization is requested, assert statements are removed.

    Using raise statements also enables raising an exception other than AssertionError, in this case RuntimeError.

    For running Python code that is within a function inside the same source file (i.e., inside main.py, not inside test.py), the function inspect.getsource can be used, together with the option -c of python.

    By the way better answers are welcome!