Tell if Python is in interactive mode - python

In a Python script, is there any way to tell if the interpreter is in interactive mode? This would be useful so that, for instance, when you run an interactive Python session and import a module, slightly different code is executed (for example, logging is turned off).
I've looked at tell whether python is in -i mode and tried the code there, however, that function only returns true if Python has been invoked with the -i flag and not when the command used to invoke interactive mode is python with no arguments.
What I mean is something like this:
if __name__=="__main__":
#do stuff
elif __pythonIsInteractive__:
#do other stuff
else:
exit()

__main__.__file__ doesn't exist in the interactive interpreter:
import __main__ as main
print hasattr(main, '__file__')
This also goes for code run via python -c, but not python -m.

I compared all the methods I found and made a table of results. The best one seems to be this:
hasattr(sys, 'ps1')
If anyone has other scenarios that might differ, comment and I'll add it

sys.ps1 and sys.ps2 are only defined in interactive mode.

Use sys.flags:
if sys.flags.interactive:
#interactive
else:
#not interactive

From TFM: If no interface option is given, -i is implied, sys.argv[0] is an empty string ("") and the current directory will be added to the start of sys.path.
If the user invoked the interpreter with python and no arguments, as you mentioned, you could test this with if sys.argv[0] == ''. This also returns true if started with python -i, but according to the docs, they're functionally the same.

The following works both with and without the -i switch:
#!/usr/bin/python
import sys
# Set the interpreter bool
try:
if sys.ps1: interpreter = True
except AttributeError:
interpreter = False
if sys.flags.interactive: interpreter = True
# Use the interpreter bool
if interpreter: print 'We are in the Interpreter'
else: print 'We are running from the command line'

Here's something that would work. Put the following code snippet in a file, and assign the path to that file to the PYTHONSTARTUP environment variable.
__pythonIsInteractive__ = None
And then you can use
if __name__=="__main__":
#do stuff
elif '__pythonIsInteractive__' in globals():
#do other stuff
else:
exit()
http://docs.python.org/tutorial/interpreter.html#the-interactive-startup-file

Related

Running a Python script in optimized mode from another Python script

Is there a way to run a python script in optimized mode from another Python (Python 3) script?
If I have the following test.py script (which reads the built-in constant __debug__):
if __debug__:
print('Debug ON')
else:
print('Debug OFF')
then:
python text.py prints Debug ON
python -OO text.py prints Debug OFF
because of how the constant __debug__ works:
This constant is true if Python was not started with an -O option. See also the assert statement.
Also, the value of __debug__ cannot be changed at runtime: __debug__ is a constant, as noted in the documentation here and here. The value of __debug__ is determined when the Python interpreter starts.
The following correctly prints Debug OFF
import subprocess
subprocess.run(["python", "-OO", "test.py"])
But is there a more pythonic way?
The above doesn't seem very portable if the interpreter is not called python.
I've already searched here and the web without luck.
Using compile
I've come up with a solution using the built-in function compile, as follows.
Contents of the file main.py:
with open('test.py') as f:
source_code = f.read()
compiled = compile(
source_code,
filename='test.py', mode='exec', optimize=2)
exec(compiled)
Contents of the file test.py:
if __debug__:
print('Debug ON')
else:
print('Debug OFF')
The output from running python main.py is:
Debug OFF
Possible values for the parameter optimize:
-1: use same optimization level as the Python interpreter that is running the function compile
0: no optimization, and __debug__ == true
1: like -O, i.e., removes assert statements, and __debug__ == false
2: like -OO, i.e., removes also docstrings.
Don't know if it's the best option, just sharing if can be useful fo others.
Using subprocess.run
The subprocess-based approach is still more concise, and can be made portable by using sys.executable:
import subprocess
import sys
if not sys.executable:
raise RuntimeError(sys.executable)
proc = subprocess.run(
[sys.executable, '-OO', 'test.py'],
capture_output=True, text=True)
if proc.returncode != 0:
raise RuntimeError(proc.returncode)
The above code calls the function subprocess.run.
The check for the value of the variable sys.executable is motivated by the documentation of CPython:
If Python is unable to retrieve the real path to its executable, sys.executable will be an empty string or None.
The check is implemented with a raise statement, instead of an assert statement, in order to check also in cases that the above Python code is itself run with optimization requested from Python, e.g., by using python -O or python -OO or the environment variable PYTHONOPTIMIZE.
When optimization is requested, assert statements are removed.
Using raise statements also enables raising an exception other than AssertionError, in this case RuntimeError.
For running Python code that is within a function inside the same source file (i.e., inside main.py, not inside test.py), the function inspect.getsource can be used, together with the option -c of python.
By the way better answers are welcome!

How to determine if Python script was run via command line?

Background
I would like my Python script to pause before exiting using something similar to:
raw_input("Press enter to close.")
but only if it is NOT run via command line. Command line programs shouldn't behave this way.
Question
Is there a way to determine if my Python script was invoked from the command line:
$ python myscript.py
verses double-clicking myscript.py to open it with the default interpreter in the OS?
If you're running it without a terminal, as when you click on "Run" in Nautilus, you can just check if it's attached to a tty:
import sys
if sys.stdin and sys.stdin.isatty():
# running interactively
print("running interactively")
else:
with open('output','w') as f:
f.write("running in the background!\n")
But, as ThomasK points out, you seem to be referring to running it in a terminal that closes just after the program finishes. I think there's no way to do what you want without a workaround; the program is running in a regular shell and attached to a terminal. The decision of exiting immediately is done just after it finishes with information it doesn't have readily available (the parameters passed to the executing shell or terminal).
You could go about examining the parent process information and detecting differences between the two kinds of invocations, but it's probably not worth it in most cases. Have you considered adding a command line parameter to your script (think --interactive)?
What I wanted was answered here: Determine if the program is called from a script in Python
You can just determine between "python" and "bash". This was already answered I think, but you can keep it short as well.
#!/usr/bin/python
# -*- coding: utf-8 -*-
import psutil
import os
ppid = os.getppid() # Get parent process id
print(psutil.Process(ppid).name())
I don't think there's any reliable way to detect this (especially in a cross-platform manner). For example on OS X, when you double-click a .py file and it tuns with "Python Launcher", it runs in a terminal, identically to if you execute it manually.
Although it may have other issues, you could package the script up with something like py2exe or Platypus, then you can have the double-clickable icon run a specific bit of code to differentiate (import mycode; mycode.main(gui = True) for example)
If you run python IDLE then "pythonw.exe" is being used to run coding while when you run the command line "python.exe" is used to run coding. The python folder path can vary so you have to revert the path to the python folder. m = '\\' and m = m[0] is to get m to be '\' because of escaping.
import sys
a = sys.executable
m = '\\'
m = m[0]
while True:
b = len(a)
c = a[(b - 1)]
if c == m:
break
a = a[:(b - 1)]
if sys.executable == a + 'pythonw.exe':
print('Running in Python IDLE')
else:
print('Running in Command line')
Update for later versions (e.g. Python 3.6 on Ubuntu 16.04): The statement to get the name has changed to psutil.Process(os.getpid()).parent().name()
I believe this CAN be done. At least, here is how I got it working in Python 2.7 under Ubuntu 14.04:
#!/usr/bin/env python
import os, psutil
# do stuff here
if psutil.Process(os.getpid()).parent.name == 'gnome-terminal':
raw_input("Press enter to close...")
Note that -- in Ubuntu 14 with the Gnome desktop (aka Nautilus) -- you might need to do this:
from a Nautilus window (the file browser), select Edit(menu)->Preferences(item) then Behavior(tab)->Executable Text Files(section)->Ask Each Time(radio).
chmod your script to be executable, or -- from a Nautilus window (the file browser) -- right click on the file->Properties(item) then Permissions(tab)->Execute:Allow executing file as program(checkbox)
double-click your file. If you select "Run in Terminal", you should see the "Type enter to close..." prompt.
now try from a bash prompt; you should NOT see the prompt.
To see how this works, you can fiddle with this (based on the answer by from #EduardoIvanec):
#!/usr/bin/env python
import os
import sys
import psutil
def parent_list(proc=None, indent=0):
if not proc:
proc = psutil.Process(os.getpid())
pid = proc.pid
name = proc.name
pad = " " * indent
s = "{0}{1:5d} {2:s}".format(pad, pid, name)
parent = proc.parent
if parent:
s += "\n" + parent_list(parent, indent+1)
return s
def invoked_from_bash_cmdline():
return psutil.Process(os.getpid()).parent.name == "bash"
def invoked_as_run_in_terminal():
return psutil.Process(os.getpid()).parent.name == "gnome-terminal"
def invoked_as_run():
return psutil.Process(os.getpid()).parent.name == "init"
if sys.stdin.isatty():
print "running interactively"
print parent_list()
if invoked_as_run_in_terminal():
raw_input("Type enter to close...")
else:
with open('output','w') as f:
f.write("running in the background!\n")
f.write("parent list:\n")
f.write(parent_list())
From the idea behind this answer, adding for Win10 compatibility (Ripped from Python 2.7 script; modify as needed):
import os, psutil
status = 1
if __name__ =="__main__":
status = MainFunc(args)
args = sys.argv
running_windowed = False
running_from = psutil.Process(os.getpid()).parent().name()
if running_from == 'explorer.exe':
args.append([DEFAULT OR DOUBLE CLICK ARGS HERE])
running_windowed = True
if running_windowed:
print('Completed. Exit status of {}'.format(status))
ready = raw_input('Press Enter To Close')
sys.exit(status)
There is a number of switch like statements you could add to be more universal or handle different defaults.
This is typically done manually/, I don't think there is an automatic way to do it that works for every case.
You should add a --pause argument to your script that does the prompt for a key at the end.
When the script is invoked from a command line by hand, then the user can add --pause if desired, but by default there won't be any wait.
When the script is launched from an icon, the arguments in the icon should include the --pause, so that there is a wait. Unfortunately you will need to either document the use of this option so that the user knows that it needs to be added when creating an icon, or else, provide an icon creation function in your script that works for your target OS.
My solution was to create command line scripts using setuptools. Here are a the relevant parts of myScript.py:
def main(pause_on_error=False):
if run():
print("we're good!")
else:
print("an error occurred!")
if pause_on_error:
raw_input("\nPress Enter to close.")
sys.exit(1)
def run():
pass # run the program here
return False # or True if program runs successfully
if __name__ == '__main__':
main(pause_on_error=True)
And the relevant parts of setup.py:
setup(
entry_points={
'console_scripts': [
'myScript = main:main',
]
},
)
Now if I open myScript.py with the Python interpreter (on Windows), the console window waits for the user to press enter if an error occurs. On the command line, if I run 'myScript', the program will never wait for user input before closing.
Although this isn't a very good solution, it does work (in windows at least).
You could create a batch file with the following contents:
#echo off
for %%x in (%cmdcmdline%) do if /i "%%~x"=="/c" set DOUBLECLICKED=1
start <location of python script>
if defined DOUBLECLICKED pause
If you want to be able to do this with a single file, you could try the following:
#echo off
setlocal EnableDelayedExpansion
set LF=^
:: The 2 empty lines are necessary
for %%x in (%cmdcmdline%) do if /i "%%~x"=="/c" set DOUBLECLICKED=1
echo print("first line of python script") %LF% print("second and so on") > %temp%/pyscript.py
start /wait console_title pyscript.py
del %temp%/pyscript.py
if defined DOUBLECLICKED pause
Batch code from: Pausing a batch file when double-clicked but not when run from a console window?
Multi-line in batch from: DOS: Working with multi-line strings
Okay, the easiest way I found and made was to simply run the program in the command line, even if it was ran in the Python IDLE.
exist = lambda x: os.path.exists(x) ## Doesn't matter
if __name__ == '__main__':
fname = "SomeRandomFileName" ## Random default file name
if exist(fname)==False: ## exist() is a pre-defined lambda function
jot(fname) ## jot() is a function that creates a blank file
os.system('start YourProgram.py') ## << Insert your program name here
os.system('exit'); sys.exit() ## Exits current shell (Either IDLE or CMD)
os.system('color a') ## Makes it look cool! :p
main() ## Runs your code
os.system("del %s" % fname) ## Deletes file name for next time
Add this to the bottom of your script and once ran from either IDLE or Command Prompt, it will create a file, re-run the program in the CMD, and exits the first instance.
Hope that helps! :)
I also had that question and, for me, the best solution is to set an environment variable in my IDE (PyCharm) and check if that variable exists to know if the script is being executed either via the command line or via the IDE.
To set an environment variable in PyCharm check:
How to set environment variables in PyCharm?
Example code (environment variable: RUNNING_PYCHARM = True):
import os
# The script is being executed via the command line
if not("RUNNING_PYCHARM" in os.environ):
raw_input("Press enter to close.")
I hope it works for you.
Based on existing solutions and using sets:
import psutil
def running_interactively():
"""Return True if any of our parent processes is a known shell."""
shells = {"cmd.exe", "bash.exe", "powershell.exe", "WindowsTerminal.exe"}
parent_names = {parent.name() for parent in psutil.Process().parents()}
# print(parent_names)
# print(f"Shell in parents? {shells & parent_names}")
return bool(shells & parent_names)
if not running_interactively():
input("\nPress ENTER to continue.")
This answer is currently specific to Windows, but it can be reconfigured to work with other operating systems in theory. Rather than installing psutil module like most of these answers recommend, you can make use of the subprocess module and the Windows tasklist command to explicitly get the name of the parent process of your Python program.
import os
import subprocess
shells = {"bash.exe", "cmd.exe", "powershell.exe", "WindowsTerminal.exe"}
# These are standard examples, but it can also be used to detect:
# - Nested python.exe processes (IDLE, etc.)
# - IDEs used to develop your program (IPython, Eclipse, PyCharm, etc.)
# - Other operating system dependent shells
s = subprocess.check_output(["tasklist", "/v", "/fo", "csv", "/nh", "/fi", f"PID eq {os.getppid()}"])
# Execute tasklist command to get the verbose info without the header (/nh) of a single process in CSV format (/fo csv)
# Such that its PID is equal to os.getppid()
entry = s.decode("utf-8").strip().strip('"').split('","')
# Decode from bytes to str, remove end whitespace and quotations from CSV format
# And split along the quote delimited commas
# This process may differ and require adjustment when used for an OS other than Windows
condition = entry and entry[0] in shells
# Check first that entry is not an empty sequence, meaning the process has already ended
# If it still exists, check if the first element -- the executable -- exists as an element of the set of executables you're looking for
I hope this is helpful for anyone looking for an answer to this problem while minimizing the number of dependencies you'd need.
This was tested in Python 3.8 and uses an f-string in the subprocess.check_output line of the code, so please be sure to convert the f-string to a compatible syntax if you're working with a version of Python before f-strings were introduced.

Is it possible to know if you are in ipython or not?

i.e. can you do something like:
if we_are_in_ipython:
do_some_ipython_specific_stuff()
normal_python_stuff()
I guess what I'm trying to do is very loosely along the lines of #if DEBUG in C# (i.e. using ipython as a debugging tool and command line python to run the code without the debugging stuff in there).
Check for the __IPYTHON__ variable:
def is_ipython():
try:
__IPYTHON__
return True
except:
return False
As explained on the duplicate, you can use the try/except method, or
if '__IP' in globals():
# stuff for ipython
pass
Or check the __IPYTHON__ in builtin:
import __builtin__
if hasattr(__builtin__, '__IPYTHON__'):
# stuff for ipython
pass
Yes.
if 'get_ipython' in dir():
"""
when ipython is fired lot of variables like _oh, etc are used.
There are so many ways to find current python interpreter is ipython.
get_ipython is easiest is most appealing for readers to understand.
"""
do_some_thing_
else:
don_sonething_else

Check python interactive mode in sitecustomize

I have a MOTD-type message which prints on invocation of the interpreter. Currently this is printed up in sitecustomize. I'd like to suppress the message if the interpreter is not in interactive mode; unfortunately all of the checks in
Tell if Python is in interactive mode do not work in sitecustomize. (sys.argv, sys.ps1, __main__.__file__ are not populated.) Are there checks which work in sitecustomize?
JAB got me looking at the code and I ultimately came up with this:
import ctypes
import getopt
ctypes.pythonapi.Py_GetArgcArgv.restype = None
ctypes.pythonapi.Py_GetArgcArgv.argtypes = [
ctypes.POINTER(ctypes.c_int),
ctypes.POINTER(ctypes.POINTER(ctypes.c_char_p))]
count = ctypes.c_int()
args = ctypes.pointer(ctypes.c_char_p())
ctypes.pythonapi.Py_GetArgcArgv(ctypes.byref(count), ctypes.byref(args))
argc = count.value
argv = [args[i] for i in range(count.value)]
if argc > 1:
interactive = False
opts, args = getopt.getopt(argv[1:], 'i')
for o, a in opts:
if o == '-i':
interactive = True
else:
interactive = True
Kinda ugly (and for Py3k the c_char_p need to be c_wchar_p) but does the job.
Perhaps this idea for checking interpreter interactivity that utilizes the inspect module and checks stack frames might be of some use to you:
http://mail.python.org/pipermail/pythonmac-sig/2002-February/005054.html
You could also try looking directly at the source of pydoc.help(), which the above-linked code snippets were inspired by.
Just realized that you could simply utilize a file containing your interactive prompt with the PYTHONSTARTUP environment variable. The commands in the file pointed to by PYTHONSTARTUP will only be executed when the interpreter is run interactively.
http://docs.python.org/tutorial/interpreter.html#the-interactive-startup-file
If you don't want to set the environment variable outside of Python, you might be able to set the variable to the desired file in sitecustomize.py, but when I tried looking into it to find the loading order it took me right back to the link from the first part of my answer.
Checking the sys.flags is a cleaner way.
>>> import sys
>>> sys.flags.interactive
1
Note, the IDLE is also interactive in its nature, but the flag is not set. I would do below:
>>> if sys.flags.interactive or sys.modules.has_key('idlelib'):
>>> pass # do stuff specific to interactive.

When running a python script in IDLE, is there a way to pass in command line arguments (args)?

I'm testing some python code that parses command line input. Is there a way to pass this input in through IDLE? Currently I'm saving in the IDLE editor and running from a command prompt.
I'm running Windows.
It doesn't seem like IDLE provides a way to do this through the GUI, but you could do something like:
idle.py -r scriptname.py arg1 arg2 arg3
You can also set sys.argv manually, like:
try:
__file__
except:
sys.argv = [sys.argv[0], 'argument1', 'argument2', 'argument2']
(Credit http://wayneandlayne.com/2009/04/14/using-command-line-arguments-in-python-in-idle/)
In a pinch, Seth's #2 worked....
2) You can add a test line in front of your main function call which
supplies an array of arguments (or create a unit test which does the
same thing), or set sys.argv directly.
For example...
sys.argv = ["wordcount.py", "--count", "small.txt"]
Here are a couple of ways that I can think of:
1) You can call your "main" function directly on the IDLE console with arguments if you want.
2) You can add a test line in front of your main function call which supplies an array of arguments (or create a unit test which does the same thing), or set sys.argv directly.
3) You can run python in interactive mode on the console and pass in arguments:
C:\> python.exe -i some.py arg1 arg2
Command-line arguments have been added to IDLE in Python 3.7.4+. To auto-detect (any and older) versions of IDLE, and prompt for command-line argument values, you may paste (something like) this into the beginning of your code:
#! /usr/bin/env python3
import sys
def ok(x=None):
sys.argv.extend(e.get().split())
root.destroy()
if 'idlelib.rpc' in sys.modules:
import tkinter as tk
root = tk.Tk()
tk.Label(root, text="Command-line Arguments:").pack()
e = tk.Entry(root)
e.pack(padx=5)
tk.Button(root, text="OK", command=ok,
default=tk.ACTIVE).pack(pady=5)
root.bind("<Return>", ok)
root.bind("<Escape>", lambda x: root.destroy())
e.focus()
root.wait_window()
You would follow that with your regular code. ie. print(sys.argv)
Note that with IDLE in Python 3.7.4+, when using the Run... Customized command, it is NOT necessary to import sys to access argv.
If used in python 2.6/2.7 then be sure to capitalize: import Tkinter as tk
For this example I've tried to strike a happy balance between features & brevity. Feel free to add or take away features, as needed!
Based on the post by danben, here is my solution that works in IDLE:
try:
sys.argv = ['fibo3_5.py', '30']
fibonacci(int(sys.argv[1]))
except:
print(str('Then try some other way.'))
Auto-detect IDLE and Prompt for Command-line Arguments
#! /usr/bin/env python3
import sys
# Prompt user for (optional) command line arguments, when run from IDLE:
if 'idlelib' in sys.modules: sys.argv.extend(input("Args: ").split())
Change "input" to "raw_input" for Python2.
This code works great for me, I can use "F5" in IDLE and then call again from the interactive prompt:
def mainf(*m_args):
# Overrides argv when testing (interactive or below)
if m_args:
sys.argv = ["testing mainf"] + list(m_args)
...
if __name__ == "__main__":
if False: # not testing?
sys.exit(mainf())
else:
# Test/sample invocations (can test multiple in one run)
mainf("--foo=bar1", "--option2=val2")
mainf("--foo=bar2")
Visual Studio 2015 has an addon for Python. You can supply arguments with that. VS 2015 is now free.
import sys
sys.argv = [sys.argv[0], '-arg1', 'val1', '-arg2', 'val2']
//If you're passing command line for 'help' or 'verbose' you can say as:
sys.argv = [sys.argv[0], '-h']
IDLE now has a GUI way to add arguments to sys.argv! Under the 'Run' menu header select 'Run... Customized' or just Shift+F5...A dialog will appear and that's it!
Answer from veganaiZe produces a KeyError outside IDLE with python 3.6.3. This can be solved by replacing if sys.modules['idlelib']: by if 'idlelib' in sys.modules: as below.
import argparse
# Check if we are using IDLE
if 'idlelib' in sys.modules:
# IDLE is present ==> we are in test mode
print("""====== TEST MODE =======""")
args = parser.parse_args([list of args])
else:
# It's command line, this is production mode.
args = parser.parse_args()
There seems like as many ways to do this as users. Me being a noob, I just tested for arguments (how many). When the idle starts from windows explorer, it has just one argument (... that is len(sys.argv) returns 1) unless you started the IDLE with parameters. IDLE is just a bat file on Windows ... that points to idle.py; on linux, I don't use idle.
What I tend to do is on the startup ...
if len(sys.argv) == 1
sys.argv = [sys.argv[0], arg1, arg2, arg3....] <---- default arguments here
I realize that is using a sledge hammer but if you are just bringing up the IDLE by clicking it in the default install, it will work. Most of what I do is call the python from another language, so the only time it makes any difference is when I'm testing.
It is easy for a noob like me to understand.

Categories