Welcome, guest | Sign In | My Account | Store | Cart

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.

Python, 46 lines
 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.