Let's say I have a file script.py
located at path = "foo/bar/script.py"
. I'm looking for a way in Python to programmatically execute script.py
from within my main Python program through a function execute_script()
. However, I've got a few requirements that seem to prevent me from employing a naive approach involving importlib
or exec()
:
script.py
should get executed in a "fresh-looking" Python environment as if it were run through$ python script.py
. That is, all relevant globals like__name__
,__file__
,sys.modules
,sys.path
and the working directory should be set accordingly and as little information as possible should leak from my main program into the file's execution. (It is okay, though, ifscript.py
could find out through theinspect
module that it wasn't executed through$ python script.py
directly.)I need access to the result of the execution, i.e.
execute_script()
should return the module given byscript.py
with all its variables, functions and classes. (This prevents starting a new Python interpreter in a subprocess.)execute_script()
must internally useopen()
to read inscript.py
. This is so that I can use thepyfakefs
package to mock out the file system during unit tests. (This prevents a simple solution involving importlib.)execute_script()
must not (permanently) modify any global state in my main program likesys.path
orsys.modules
.If possible,
script.py
should not be able to affect my main program's global state. (At the very least it should not be able to affectsys.path
andsys.modules
in my main program.)I need to be able to modify the
sys.path
thatscript.py
sees.execute_function()
should therefore accept an optional list of system paths as argument.Stack traces and handling of errors occurring during the execution of
script.py
should work as usual. (This makes a solution involvingexec()
difficult.)The solution should be as future-proof as possible and not depend on implementation details of the Python interpreter.
I'd be very grateful for any ideas!
I just came across the fact that
exec()
also accepts code objects (that can be obtained e.g. fromcompile()
) and have come up with an approach that seems to fulfill nearly all requirements. "nearly" because with the exception ofsys.path
andsys.modules
the script can still affect the global state of the main program. Moreover, it also gets to see all modules that are imported beforeexecute_script()
is called. For the time being I'm happy with this, though.Here is the full code including tests: