3

Is there a way in Python to detect, within a process, where that process is being executed? I have some code that includes the getpass.getpass() function, which is broken in Spyder, and it's annoying to go back and forth between the command line and the IDE all the time. It would be useful if I could add code like:

if not being run from Spyder:
    use getpass
else:
    use alternative
Air
  • 8,274
  • 2
  • 53
  • 88
  • 1
    Does `sys.executable` help you? http://docs.python.org/2/library/sys.html#sys.executable – Dan Lecocq Jul 18 '13 at 15:58
  • @DanLecocq That is helpful. I'll work with it a bit and see what I can come up with. – Air Jul 18 '13 at 16:07
  • I tried that, but the problem is that Spyder just calls the normal python executable. One thing I noted is that it overrides the sys.exit() call so that it does not kill your interpreter. Anybody got an idea how to exploit that? – Markus Jul 18 '13 at 16:40

4 Answers4

9

Here is the solution I ended up using. After reading Markus's answer, I noticed that Spyder adds half a dozen or so environment variables to os.environ with names like SPYDER_ENCODING, SPYDER_SHELL_ID, etc. Detecting the presence of any of these seems relatively unambiguous, compared to detecting the absence of a variable with as generic a name as 'PYTHONSTARTUP'. The code is simple, and works independently of Spyder's startup script (as far as I can tell):

if any('SPYDER' in name for name in os.environ)
    # use alternative
else:        
    # use getpass

Since the string is at the beginning of each environment variable name, you could also use str.startswith, but it's less flexible, and a little bit slower (I was curious):

>>> import timeit
>>> s = timeit.Timer("[name.startswith('SPYDER') for name in os.environ]", "import os")
>>> i = timeit.Timer("['SPYDER' in name for name in os.environ]", "import os")
>>> s.timeit()
16.18333065883474
>>> i.timeit()
6.156869294143846

The sys.executable method may or may not be useful depending on your installation. I have a couple WinPython installations and a separate Python 2.7 installation, so I was able to check the condition sys.executable.find('WinPy') == -1 to detect a folder name in the path of the executable Spyder uses. Since the warning that shows in IDLE when you try to use getpass is less "loud" than it could be, in my opinion, I ended up also checking the condition sys.executable.find('pythonw.exe') == -1 to make it slightly louder. Using sys.executable only, that method looks like:

if sys.executable.find('pythonw.exe') == sys.executable.find('WinPy') == -1:
    # use getpass
else:        
    # use alternative

But since I want this to work on other machines, and it's much more likely that another user would modify their WinPython installation folder name than that they would rename their IDLE executable, my final code uses sys.executable to detect IDLE and os.environ to detect Spyder, providing a "louder" warning in either case and keeping the code from breaking in the latter.

if any('SPYDER' in name for name in os.environ) \
   or 'pythonw.exe' in sys.executable:
    password = raw_input('WARNING: PASSWORD WILL BE SHOWN ON SCREEN\n\n' * 3
                         + 'Please enter your password: ')
else:        
    password = getpass.getpass("Please enter your password: ")
Community
  • 1
  • 1
Air
  • 8,274
  • 2
  • 53
  • 88
  • 1
    I can add that this approach works also with the PyCharme IDE, i.e. simply replace "SPYDER" with "PYCHARM". – emher Mar 27 '17 at 20:34
  • This will give a false positive if the user has pre-defined SPYDER_something in their environment. – plasmo Aug 11 '17 at 08:40
  • 1
    if you want a shorter phrase (and a tiny speed improvement) fully specify a single env var name, e.g. `if 'SPYDER_ENCODING' in os.environ.keys():` (or in my case `'PYZO_PREFIX'`) – matt wilkie Aug 25 '21 at 16:45
  • @mattwilkie While that approach is valid, here I was mainly worried about the third party project changing the specific environment variables it was setting in some future version, introducing a bug in my code. Where credentials are concerned, it pays to be defensive. I see no reason to expect that even in the worst case scenario python would take so long to search through `os.environ` and `sys.executable` as to affect the user's experience. but if you have reason to expect otherwise, I can see optimizing more pro-actively. – Air Aug 28 '21 at 20:48
  • @Air for me the main reason I like `if X in Y` is the shorter phrase. I don't need to pause and think through what the aditional `Y for Y in Z` does. Execution speed improvement is just a happy byproduct. However I am only interested in that 1 specific var which is different from your scenario. – matt wilkie Mar 05 '22 at 21:30
2

By default, Spyder uses a startup scrip, see Preferences -> Console -> Adanced setting. This option is usually set to the scientific_startup.py file that loads pylab et al.

The easiest solution is to just add a global variable to the file and then use that in your if statement, e.g. add this line at the end of scientific_startup.py:

SPYDER_IDE_ACTIVE = True

In your script:

if not 'SPYDER_IDE_ACTIVE' in globals():
    use getpass
else:
    use alternative

This will work without throwing an error. You can also use exceptions if you like that more.

A second solution would be (if you cannot modify that file for some reason) to just check if the environment variable PYTHONSTARTUP is set. On my machine (using the Anaconda Python stack), it is not set for a regular Python shell. You could do

import os
if not 'PYTHONSTARTUP' in os.environ:
    use getpass
else:
    use alternative
Markus
  • 494
  • 3
  • 12
  • 1
    Thanks, this was helpful. Suggest editing to reflect that os.environ is not callable though. – Air Jul 19 '13 at 15:59
  • Also, `globals()` only gives the module namespace, so your first suggestion won't work when importing the module that calls `getpass`. – Air Jul 19 '13 at 21:29
  • Ah, thank. Removed the braces. Have to think about the issue with importing, though. – Markus Jul 21 '13 at 21:02
1

Spyder provides the option of executing the current editor script in a native system terminal. This would produce identical behavior as if you were running from the command line. To set this up, open the Run Settings dialog by hitting F6. Then select the radio button "Execute in an external System terminal". Now run the script as usual by hitting F5. You should be able to use getpass in the normal fashion with this approach.

Jed
  • 1,011
  • 1
  • 9
  • 15
  • 1
    You can also enable "Interact with the Python interpreter after execution" from the same Run Settings dialog, and that will keep the interpreter open after the code runs or after it throws an error. – Jed Aug 13 '13 at 20:29
0

You could add env variable when running in Spyder and check it in code.

twil
  • 6,032
  • 1
  • 30
  • 28