20

Inspired by another question here, I would like to retrieve the Python interpreter's full command line in a portable way. That is, I want to get the original argv of the interpreter, not the sys.argv which excludes options to the interpreter itself (like -m, -O, etc.).

sys.flags tells us which boolean options were set, but it doesn't tell us about -m arguments, and the set of flags is bound to change over time, creating a maintenance burden.

On Linux you can use procfs to retrieve the original command line, but this is not portable (and it's sort of gross):

open('/proc/{}/cmdline'.format(os.getpid())).read().split('\0')
2
  • 6
    this is a great question ... As far as I can tell, this isn't possible (in CPython). It looks to me like Py_Main does some parsing to get the commandline arguments, then calls PySys_SetArgv with the remaining arguments and does nothing else with *argc and **argv. There is Py_GetArgcArgv, that you could probably hook into -- But I don't see it anywhere in the documented C-API... Commented Feb 5, 2015 at 6:10
  • 1
    .split('\0') would be more correct than .replace('\0', ' ') -- otherwise you cannot distinguish between arguments containing a space and separate arguments. Commented Feb 5, 2015 at 6:15

2 Answers 2

13

You can use ctypes

~$ python2 -B -R -u
Python 2.7.9 (default, Dec 11 2014, 04:42:00) 
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
Persistent session history and tab completion are enabled.
>>> import ctypes
>>> argv = ctypes.POINTER(ctypes.c_char_p)()
>>> argc = ctypes.c_int()
>>> ctypes.pythonapi.Py_GetArgcArgv(ctypes.byref(argc), ctypes.byref(argv))
1227013240
>>> argc.value
4
>>> argv[0]
'python2'
>>> argv[1]
'-B'
>>> argv[2]
'-R'
>>> argv[3]
'-u'
Sign up to request clarification or add additional context in comments.

1 Comment

This no longer works in Python 3.7 >>> argv[0] is now b'p'
9

I'm going to add another answer to this. @bav had the right answer for Python 2.7, but it breaks in Python 3 as @szmoore points out (not just 3.7). The code below, however, will work in both Python 2 and Python 3 (the key to that is c_wchar_p in Python 3 instead of c_char_p in Python 2) and will properly convert the argv into a Python list so that it's safe to use in other Python code without segfaulting:

def get_python_interpreter_arguments():
    argc = ctypes.c_int()
    argv = ctypes.POINTER(ctypes.c_wchar_p if sys.version_info >= (3, ) else ctypes.c_char_p)()
    ctypes.pythonapi.Py_GetArgcArgv(ctypes.byref(argc), ctypes.byref(argv))

    # Ctypes are weird. They can't be used in list comprehensions, you can't use `in` with them, and you can't
    # use a for-each loop on them. We have to do an old-school for-i loop.
    arguments = list()
    for i in range(argc.value - len(sys.argv) + 1):
        arguments.append(argv[i])

    return arguments

You'll notice that it also returns only the interpreter arguments and excludes the augments found in sys.argv. You can eliminate this behavior by removing - len(sys.argv) + 1.

3 Comments

What OS platform and interpreter version did you use? In both tests I tried it crashed. I tested on Windows 10 using Python 3.7, and Debian 9 using Python 3.5.
The above runs on Python 2.7, 3.5, 3.6, and 3.7 on Ubuntu 16.04 and on macOS 10.13. I don't use Windows, so I've no way of testing on that. I basically assume all things C-related in Python work completely differently (or just don't work) in Windows. But that definitely works across what appears to be a broad range of Unix.
@JamesThomasMoon works for me, on Ubuntu 20.04 with Python 3.8.10 and 2.7.18.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.