6

Let's say you have a string containing a path to a Python script and you want to load and execute that script in transparent way (so that there are not fundamental differences for inner script compared to being run directly via “python path”). And then get the resulting global dict. I thought that runpy.run_path() does this but there are two issues. If the path contains some unicode character then it doesn't work (see http://bugs.python.org/issue17588). And most importantly, given global dict is just a copy of original one, since this original one is purged when temporary module object is garbage collected. So a function object have corrupted __globals__ dict (see http://bugs.python.org/issue18331).

Have you any ideas how to run the inner script?

Update: See my current approach – http://bpaste.net/show/RzAbQxLNXiJsXYm2fplz/ . Any suggestions? Improvements? For example about the details of what can be different from point of view of a script being run. I know about issue with realoading __main__.

user87690
  • 687
  • 3
  • 25
  • 3
    rewrite the "inner" script to be usable as a module... – l4mpi Jul 28 '13 at 12:09
  • [execfile()](http://docs.python.org/2/library/functions.html?highlight=execfile#execfile). – martineau Jul 28 '13 at 12:14
  • @l4mpi: The path and inner script is not fixed. – user87690 Jul 28 '13 at 12:35
  • `random = __import__("random")` to import the `random` module using a string. – TankorSmash Jul 28 '13 at 13:37
  • @TankorSmash: I don't want to import it by name, but run it from path. Also the path can contain unicode characters, so I cannot even use compile() function. – user87690 Jul 28 '13 at 14:23
  • You could add the module's path to the `sys.path` list – TankorSmash Jul 28 '13 at 14:41
  • @user87690 why would the path containing unicode prevent you from using `compile`? Compile takes a string (or AST object) as input, so you can just open and read the file and pass the contents to compile. – l4mpi Jul 28 '13 at 15:00
  • @martineau: execfile() does not exist in Python 3. I could rewrite it using exec() but for nice traceback, I need to get code object first using compile which doesn't work with Unicode filename. – user87690 Jul 28 '13 at 15:32
  • @l4mpi: Just try it, it doesn't work (on Windows). Some inner C code tries to encode the filename (even if there's no reason to do so, it's a string). See http://bugs.python.org/issue17588, http://bugs.python.org/issue11619. – user87690 Jul 28 '13 at 15:36
  • 1
    Surely you can temporarily work around the unicode filename bug in `Complile()`... – martineau Jul 28 '13 at 16:21
  • 1
    @user87690 these issues just mean you can't pass unicode paths as the second argument to `compile`; just try passing something else. From the [docs](http://docs.python.org/3.3/library/functions.html#compile): `The filename argument should give the file from which the code was read; pass some recognizable value if it wasn’t read from a file ('' is commonly used).` If this doesn't work for you, consider copying the required files to a temporary folder and executing them from there. – l4mpi Jul 28 '13 at 16:24
  • @martineau: I'm trying to. What's funny is that whole this problem is a workaround to another bug which I encountered when trying to workaround another bug. :-) – user87690 Jul 28 '13 at 16:42
  • @l4mpi: I cannot copy the files – they souldn't notice that they're being run from another script and not directly. When you put '' to compile, then the tracebacks of inner script doesn't show code lines. My current approach is to compile with ' – user87690 Jul 28 '13 at 16:46

1 Answers1

1

Importing a module executes the code at the top level, and that module's "global" namespace gets imported as the name of the module

james@bodacious:~$cat test.py
def func():
  pass

myname = "michael caine"

print "hello, %s" % myname
james@bodacious:~$python
Python 2.7.5 (default, Jul 12 2013, 18:42:21)
[GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.0.68)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import test
hello, michael caine
>>> dir(test)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'func', 'myname']
>>>

if the code you want to be run is at the top level of the file, just importing the module will execute the code and give you access to its "global" namespace all in one convenient package. If the code you want to run isn't at the top level (eg, if it's in a main() function that only gets triggered with the common if __name__=="__main__" trick), you can call that function yourself:

james@bodacious:~$cat test.py

def main():
    print "hello there!"

if __name__=="__main__":
    main()
james@bodacious:~$python
Python 2.7.5 (default, Jul 12 2013, 18:42:21)
[GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.0.68)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import test
>>> test.main()
hello there!
>>>

Of course, it's possible that you file you want to import is not in sys.path, so can't be simply loaded with import. A simplistic solution might be to manipulate sys.path, but How to import a module given the full path? describes better solutions using imp.load_source()

Community
  • 1
  • 1
James Polley
  • 7,977
  • 2
  • 29
  • 33
  • This will only work if the script's top level logic is in a function that gets called from the `if __name__ == "__main__"` boilerplate. While that's a good design, you may not be able to rely on it being used by other code. – Blckknght Aug 01 '13 at 00:19
  • That's correct, calling `main()` will only be useful if the code you want to run is inside a function called `main()`. If it's inside a different function, you'll have to call that function instead. If the function takes arguments, you'll have to provide those as well. – James Polley Aug 01 '13 at 12:23
  • I have the path of the script, it may not be in sys.path to be loaded. Also this doesn't work for unicode filename, since there's a bug so you cannot “import α”. – user87690 Aug 01 '13 at 19:18