Yet another way to provide this information. I wanted to get the right answers in ANY situation: Python 2 or 3, compiled or interpreted (interactive or batch). The following code has been tested under Windows, Python 2.7, Python 3.4, IDLE, IPython (sh and Qt), PyInstaller, cx_Freeze, py2exe.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | def scriptinfo():
'''
Returns a dictionary with information about the running top level Python
script:
---------------------------------------------------------------------------
dir: directory containing script or compiled executable
name: name of script or executable
source: name of source code file
---------------------------------------------------------------------------
"name" and "source" are identical if and only if running interpreted code.
When running code compiled by py2exe or cx_freeze, "source" contains
the name of the originating Python script.
If compiled by PyInstaller, "source" contains no meaningful information.
'''
import os, sys, inspect
#---------------------------------------------------------------------------
# scan through call stack for caller information
#---------------------------------------------------------------------------
for teil in inspect.stack():
# skip system calls
if teil[1].startswith("<"):
continue
if teil[1].upper().startswith(sys.exec_prefix.upper()):
continue
trc = teil[1]
# trc contains highest level calling script name
# check if we have been compiled
if getattr(sys, 'frozen', False):
scriptdir, scriptname = os.path.split(sys.executable)
return {"dir": scriptdir,
"name": scriptname,
"source": trc}
# from here on, we are in the interpreted case
scriptdir, trc = os.path.split(trc)
# if trc did not contain directory information,
# the current working directory is what we need
if not scriptdir:
scriptdir = os.getcwd()
scr_dict ={"name": trc,
"source": trc,
"dir": scriptdir}
return scr_dict
|
Motivation:
Every now and then the script directory contains data for the running application: application data, configuration information, multi-language data, etc.
Why this approach:
__file__ is not always available (e.g. when compiled, and also under some IDEs).
Likewise sys.argv[0]: If your script is invoked in an IDE or in batch like "python -c execfile('script.py')", you will not find its name in sys.argv.
Things are easy in the compiled case: sys.executable is where to look (provided you know how to find out if you are indeed compiled).
But your code gets overloaded with adjusting to those differences - for something that looks like an easy question.
How it works: The function scans through the inspect.stack() and skips any system calls. The last valid entry is the name of the top level script source. Then we check whether we are compiled. That is about it.
Example Usage:
-----------------------------
import ScriptInfo as si
whoami = si.scriptinfo()
''' whoami['dir'] is the path of the script source, or, when compiled, the path of the binary
whoami['name'] is the name of the script source or the binary ("script.exe" in Windows)
whoami['source'] is the name of the script source. Different from 'name' if and only if we have been compiled (can be used to test this fact). '''
-----------------------------
When does it work: So far tested under Windows (7, 32bit) only. In any combination of Python 2.7.9 and 3.4.1, IDLE, IPython (sh and Qt), batch interpreter, PyInstaller, py2exe, cx-Freeze.
Also works when invoked like this:
python -c execfile('script.py') (Python 2 only)
- or -
python -c exec(compile(open('script.py').read(), 'script.py', 'exec')) (Python 2 and 3)
When does it not (quite) work:
(1) python -c exec(open('script.py').read()) -- will not work, however run fine as a Python program
(2) PyInstaller is not available under Python 3 yet.
(3) If compiled with PyInstaller, the 'source' parameter will not contain meaningful information. The other two info ('name' and 'dir') are correct. Testing whether being compiled works.
(4) If you have not installed Python (eg. portable version), the interpreted case will probably not work correctly, because intermediate system calls in inspect.stack() will not correctly be recognized.