Alternative to execfile in Python 3? [duplicate]
Asked Answered
G

4

69

Python 2 had the builtin function execfile, which was removed in Python 3.0. This question discusses alternatives for Python 3.0, but some considerable changes have been made since Python 3.0.

What is the best alternative to execfile for Python 3.2, and future Python 3.x versions?

Georg answered 15/6, 2011 at 12:2 Comment(0)
T
79

The 2to3 script replaces

execfile(filename, globals, locals)

by

exec(compile(open(filename, "rb").read(), filename, 'exec'), globals, locals)

This seems to be the official recommendation. You may want to use a with block to ensure that the file is promptly closed again:

with open(filename, "rb") as source_file:
    code = compile(source_file.read(), filename, "exec")
exec(code, globals, locals)

You can omit the globals and locals arguments to execute the file in the current scope, or use exec(code, {}) to use a new temporary dictionary as both the globals and locals dictionary, effectively executing the file in a new temporary scope.

Terrarium answered 15/6, 2011 at 12:7 Comment(20)
Why is this better than Lennart's version?Georg
@Matt: The advantages are (a) error message will include the correct filename and (b) it seems to be the official recommendation, so maybe there are advantages we aren't aware of. If you omit the globals and locals parameters, it will also work in all versions of Python.Terrarium
I know this is the official recommendation, but with this I get these: "ResourceWarning: unclosed file <_io.TextIOWrapper name='..." errors. It's just out test runner so it doesn't matter much, but still..Brucie
@VPeric: On a release build of Python, this warning should be silenced by default. Quite valid point, though.Terrarium
@VPeric: I've created an issue in Python's bug tracker for this.Terrarium
@Sven Thanks! I've added myself to the nosy there, too. Still, if I tried to use the solution you provided there for my code, I get the following error: "SyntaxError: unqualified exec is not allowed in function 'test_file' it contains a nested function with free variables" So yeah, again, this isn't so important but I'd like to fix the leaks. (and it could help us spot other warnings)Brucie
@VPeric: Your problem is completely unrelated to both this SO question and the Python bug tracker issue, which both deal with the question of how to rewrite execfile() in Python 3.x. You are using Python 2.7, so all this isn't relevant for you. I explained in this comment what your actual problem is.Terrarium
This has a problem with default system encoding (e.g. on Windows).Upbow
@techtonik: Thanks. I guess opening the file in binary mode and having compile() figure out the right encoding should fix this -- I updated the answer accordingly.Terrarium
I thought that relying on file's destructor to close the file is considered a bad practice.Avery
@SvenMarnach it may be Python docs issue, but binary bytes are not listed among accepted inputs for compile().Upbow
@techtonik: I know it's not documented, but compile() does actually accept bytes, and it respects any encoding specification in the source code to decode it to a string, so I think it's the most robust way to do this.Terrarium
@SvenMarnach, if you can post a link to official recommendation or source code (so that people can be sure it won't be "fixed" in future), I'd give it a +1. =)Upbow
@techtonik: It's the only reasonable behaviour, so it won't change. Here's a link to the current source code of importlib, which uses compile() on a bytes object: hg.python.org/cpython/file/9bce03920afe/Lib/importlib/…Terrarium
Doesn't seems to work on 3.4.3: TypeError: exec() arg 2 must be a dict, not builtin_function_or_methodPullen
@kenorb: It does work in Python 3.4. You apparently tried to use the last line without replacing locals and globals with whatever you want to have there instead. They are just placeholders, I can't know what you want to pass in, or whether you want to omit them altogether. I consider this the "official" recommendation because this is how the "official" 2to3 tool migrates execfile() calls.Terrarium
thanks. Can anyone please explain the significance of local and global arguments here?Decussate
@vinaygarg These are dictionaries with the global and lcoal variables the file is supposed to be executed within. You can omit them to execute the file in the current scope. A common alternative is to use exec(..., {}) to execute the file in a new temporary scope.Terrarium
Shouldn't globals() and locals() be executed functions?Husch
@AidasBendoraitis I used globals and locals as variable names here; I could just as well have used x and y or whatever. You usually wouldn't do exec(code, globals(), locals()), since this would be the same as just exec(code).Terrarium
I
69
execfile(filename)

can be replaced with

exec(open(filename).read())

which works in all versions of Python

Newer versions of Python will warn you that you didn't close that file, so then you can do this is you want to get rid of that warning:

with open(filename) as infile:
    exec(infile.read())

But really, if you care about closing files, you should care enough to not use exec in the first place.

Insufficient answered 15/6, 2011 at 12:15 Comment(14)
Why is this better than Sven's version?Georg
@Matt: It's simpler?Insufficient
Must be something due to my code, but when I use this instead of execfile, I get: "SyntaxError: unqualified exec is not allowed in function 'test_file' it contains a nested function with free variables" (in Python 2.7)Brucie
@VPeric: In Python 2.x, you need to use the exec ... in ... form of exec in such a situation. For example exec code in globals() will execute the code in the module's global namespace. Note that the exec'ed code can't change local variables in a way that is reliably visible by the nested function.Terrarium
@Sven I'm actually trying to write code that will work in both version (in py3k after 2to3, of course) and preferably close files it opens (unlike what 2to3 currently does automatically). None of the stuff on this page works. We are probably just relying on some obscure detail, but I can't figure it out. The code is here (search for execfile). If you wouldn't mind taking a look, I'd be much obliged! :)Brucie
@VPeric: All of the stuff here works, if you have a specific problem, make it a question.Insufficient
File doesnt have to be closed?Carmel
No, it will be closed when garbage collected, so it doesn't have to (although it's a good idea to close it).Insufficient
This answer does not fit my needs, as the executed file cannot be inspected. For example, it cannot know its own directory.Keeter
Can I use this for executing environment module initialization file?Cresida
Possibly, but you probably shouldn't.Insufficient
This is the only answer that actually works.Benefaction
The answer works but I get this warning: ResourceWarning: unclosed fileTletski
Yes. As mentioned elsewhere, you really shouldn't do this, first of all, so why you care about a warning is beyond me. :-) But I'll update my answer.Insufficient
P
9

In Python3.x this is the closest thing I could come up with to executing a file directly, that matches running python /path/to/somefile.py.

Notes:

  • Uses binary reading to avoid encoding issues
  • Garenteed to close the file (Python3.x warns about this)
  • defines __main__, some scripts depend on this to check if they are loading as a module or not for eg. if __name__ == "__main__"
  • setting __file__ is nicer for exception messages and some scripts use __file__ to get the paths of other files relative to them.
def exec_full(filepath):
    global_namespace = {
        "__file__": filepath,
        "__name__": "__main__",
    }
    with open(filepath, 'rb') as file:
        exec(compile(file.read(), filepath, 'exec'), global_namespace)

# Execute the file.
exec_full("/path/to/somefile.py")
Peabody answered 1/12, 2012 at 6:41 Comment(2)
i have this as the old version: execfile(join(dirname(file), 'ExcelLibrary', 'version.py')) so with the new.... exec(open(filename).read()) how does that become?Intermixture
@Intermixture it can't be done (easily) in a single line, that's why I've provided a function - exec_fullPeabody
T
4

Standard runpy.run_path is an alternative.

Tom answered 18/10, 2015 at 20:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.