Python script & stdout - python

I have a simple python script (say, simple.py) , like
a = 5
b = a + a
print(b)
But I want the following: output the result as if these commands were executed in the interpreter, like
>>> a = 5
>>> b = a + a
>>> print(b)
10
And I want this in stdout :)
Is that possible in some simple way?
Also the same question for IPython.
This question can be helpful for writing a small "how-to" for working with Python/IPython interpreters

You can use the exec statement to execute a line of code (the following is Python 2 code, change the print statements to print() if you want it to be Python 3):
import sys
def run(filename):
with open(filename) as f:
for line in f:
print ">>> ", line
exec line
if __name__ == "__main__":
try:
run(sys.argv[1])
except KeyError:
print "You should pass one filename as parameter."
I should note that it does not appear to handle multi-line statements very well yet (as it's evaluating each line separately).

I modified from #Simeon 's code.
Save the code below as theprinter.py
import sys
if __name__ == '__main__':
c = {}
for line in sys.stdin:
sys.stdout.write('>>> %s' % line)
exec(line, c)
Usage: cat yourscript.py | python theprinter.py

Depending on which tools you are using to create your how-to, you may also want to use Sphinx, which is a tool to produce documentation in Python. It is able to output many formats, notably HTML and PDF. It has a very nice doctest module which allows to put Python instructions in your documentation. They will be run when you compile your documentation and the instructions as well as their results will be displayed.

This should be a good start for embedding and using a Python shell interactively (ActiveState code recipe).
For IPython, you could probably use IPShellEmbed.

Related

How to let user enter Python code as if they're running 'python' from terminal?

When you run "python" in your terminal, you get a running instance of python where you can run python code.
I want to do this, but after my script has run a few functions first.
But what I want is this type of interface at the end of my script.
I.e. I want to run my_script.py, in "main" call a few functions, but then after those functions, keep the "workspace" open with the >>> interface so that the user can run more python code.
if __name__ == "__main__":
excel_file = sys.argv[1]
my_list = load_file(excel_file)
#... do a bunch of other things
# open python interface now >>>
while True:
# accept user python commands like in the terminal example above
Is there any way to do this?
You could run your script in interactive mode so that when it would normally exit, you instead drop into an interactive Python interpreter:
python -i my_script.py
You could also enter interactive mode from the script file without the command line flag using the Python code module like in this example:
import code
if __name__ == "__main__":
# Do something.
code.interact(local = locals())
Alternatively, you could use something like the exec keyword in a loop to execute arbitrary commands and kind of "fake it" with something like this:
if __name__ == "__main__":
# Do something.
try:
while True:
exec(input(">>> "))
except KeyboardInterrupt:
sys.exit()
Though this approach is a lot less clean than using interactive mode, especially for executing multi-line code.
If I understand you correctly, you want to load files into variables and play around with those variables.
I think the better option, instead of your proposed makeshift workspace, is to use a Jupyter notebook.
This video gives a good introduction on how the workflow looks like to read an Excel file and play around with it.

In Python 3.x make print work like in Python 2 (as statement)

I wonder if the print function can be made work (without changing the syntax all over the place) like in Python 2 and earlier.
So I have the statement like:
print "Hello, World!"
And I like that syntax to work in Python 3. I've tried importing the library six, but that didn't do the trick (still a syntax error).
No, you cannot. The print statement is gone in Python 3; the compiler doesn't support it anymore.
You can make print() work like a function in Python 2; put this at the top of every module that uses print:
from __future__ import print_function
This will remove support for the print statement in Python 2 just like it is gone in Python 3, and you can use the print() function that ships with Python 2.
six can only help bridge code written with both Python 2 and 3 in mind; that includes replacing print statements with print() functions first.
You probably want to read the Porting Python 2 Code to Python 3 howto; it'll tell you about more such from __future__ imports as well, as well as introduce tools such as Modernize and Futurize that can help automate fixing Python 2 code to work on both Python 2 and 3.
You can use a regular expression to replace the print code of Python 2 with Python 3:
Find:
(print) (.*)(\n)
Replace with:
$1($2)$3
You can use the tool 2to3 is an Automated Python 2 to 3 code translation, as #Martijn Pieters♦ told :), you can get over a ride throw the old python and making the changes work into python 3, I make a simple example like this:
I created this file, python2.py:
#!python3
print 5
when I run it with python it obviously shows:
line 3
print 5 ^
SyntaxError: Missing parentheses in call to 'print'
so, you can transform it via terminal like this:
This is the important comand
$ 2to3 -w home/path_to_file/python2.py
-w parameter will write the file, if you want only see the future changes without apply them, just run it without -w.
after run it it will show something like
root: Generating grammar tables from /usr/lib/python2.7/lib2to3/PatternGrammar.txt
RefactoringTool: Refactored Desktop/stackoverflow/goto.py
--- Desktop/stackoverflow/goto.py (original)
+++ Desktop/stackoverflow/goto.py (refactored)
## -1,3 +1,3 ##
#!python3
-print 5
+print(5)
RefactoringTool: Files that were modified:
And the file will look like:
#!python3
print(5)
If you are okay with overriding builtins.__import__ and a simple regex to convert the print statements that don't have paren then you can do the following. Note that this doesn't actually change any file, just when you import them it'll read the code into a string, tweak that string, then send the fixed code to the compiler/importer
import re
import sys
if sys.version_info >= (3, 0):
import lib2to3
from lib2to3 import main, refactor
import os
import types
import builtins
import sys
import importlib
cache = {}
prevImport = builtins.__import__
def customImport(path, *args, **kwargs):
#print (path, args, kwargs)
try:
return fimport(path + ".py")
except:
return prevImport(path, *args, **kwargs)
def reload(filename):
fimport(filename.__file__, forceReload=True)
def fimport(filename, forceReload=False):
filename = os.path.abspath(filename)
modulePath = os.path.splitext(os.path.basename(filename))[0]
if filename in cache and not forceReload:
execval, modifyTime, module = cache[filename]
if modifyTime == os.path.getmtime(filename):
return module
f = open(filename)
text = f.read() + "\n"
p = re.compile("print")
res = []
curI = 0
for m in p.finditer(text):
i = m.start()
res.append(text[curI:i])
curI = i
pieceTmp = text[i:].split("\n")[0]
piece = text[i:].split("\n")[0].split("#")[0]
pieceAfter = piece[len('print'):].strip()
if pieceAfter[0] != '(':
resLine = "print" + "(" + pieceAfter + ")" + "\n"
res.append(resLine)
else:
res.append(pieceTmp)
curI += len(pieceTmp)+1
text = "".join(res)
f.close()
'''
# this code can run lib2to3 if you want but just for replacing prints that is not needed
#fixes = sorted(lib2to3.refactor.get_fixers_from_package('lib2to3.fixes'))
fixes = ['lib2to3.fixes.fix_print']
rt = lib2to3.main.StdoutRefactoringTool(fixes, {}, [], False, False)
res = str(rt.refactor_string(text, name=modulePath))
'''
res = text
res = compile(res, '<string>', 'exec')
module = types.ModuleType(modulePath)
module.__file__ = filename
cache[filename] = (res, os.path.getmtime(filename), module)
exec(res, module.__dict__)
return module
builtins.__import__ = customImport
importlib.reload = reload
If you save this code to, say, pastimport.py, then lets say I have some file named juniper.py:
def wow(a):
print a
Now if I want to call juniper.py from python3, I can just do
import pastimport
import juniper
juniper.wow("bean")
And it'll run :)
This could probably be faster and be more like the typical imports with caching and logging and stuff but I don't understand exactly how and when the pyc files are generated yet. There might also be edge cases with c plugins and such I'm not sure. So feel free to suggest improvements, but at least this is a proof of concept. I think you should be able to actually tweak the interpreter inputs and the values of the current file but I'm fiddling with that right now.
Also technically this lets you import any python2 file (2to3 fixes xrange, print, etc.) and if those files import other python2 files they'll be converted too since this overrides the import everyone uses. You can also implement arbitrary operator overloading, require static typing, actually require braces, and technically even import code from other languages or change python altogether with this single import. But I digress
The following snippet works for interactive use. When there is a print syntax error, the script executes the failed command again surrounded with "()" for python3 .
#!/usr/bin/python3.8 -i
import subprocess, shlex, sys, readline, traceback
def excepthook(exctype, value, tb):
if exctype is SyntaxError:
index = readline.get_current_history_length()
command = readline.get_history_item(index)
if command.find("print") != -1:
sp = command.split("print")[1]
new = "print" + "(" + sp + ")"
eval(new)
else:
traceback.print_exception(exctype, value, tb)
sys.excepthook = excepthook

Python Use of File = defined variable

I am attempting to output a file that was read and altered.
new_file = open("MC_QS_MODIFIED.inp","w")
...
...
print( final_data , file=new_file )
new_file.close()
and python is taking exception to the = in print(final_data, file=new_file )
It worked at home but now that I am attempting to run the script at work, Python 2.7.6 is giving me a syntax error. I am still pretty new to this, so I don't know if my code is 3.0+ and 2.7.6 doesn't like it or what.
In Python 2, print() is not a function but a statement, unless you tell Python you want to use the Python 3 syntax.
Put this at the top of your file:
from __future__ import print_function
to disable the print statement in the compiler so you can use the print() function instead.
You may run into other problems if you developed on Python 3 and try to run on Python 2, however.

Importing code from a file as a function in python

Essentially I have a script in one file which I would like to import and run as a function in another file. Here is the catch, the contents of the first file CANNOT be written as function definition it just needs to be a plain old script (I'm writing a simulator for my robotics kit so user experience is important). I have no idea how to go about this.
Adam
Anything can be written as a function.
If you additionally need the ability to call your script directly, you just use the __name__ == '__main__' trick:
def my_function():
... code goes here ...
if __name__ == '__main__':
my_function()
Now you can import my_function from the rest of your code, but still execute the file directly since the block at the end will call the function.
Assuming that the code in the file you need to import is a well bounded script - then you can read in as a text variable and use the "execfile" function to create a function from that script.
By well bounded I mean that you understand all the data it needs and you are able to provide all of it from your program.
An alternative would be to use the "system" call, or the subprocess module to call the script as if it was an external program (depending if you need the script output).
A final approach will be to use exec to create a function - see approach 3.
The approach you use determines what you need your other script to do ..
examples :
hello.py (your file you want to run, but can't change):
# Silly example to illustrate a script which does something.
fp = open("hello.txt", "a")
fp.write("Hello World !!!\n")
fp.close()
Three approaches to use hello.py without importing hello.py
import os
print "approach 1 - using system"
os.system("python hello.py")
print "approach 2 - using execfile"
execfile("hello.py", globals(), locals())
print "approach 3 - exec to create a function"
# read script into string and indent
with open("hello.py","r") as hfp:
hsrc = [" " + line for line in hfp]
# insert def line
hsrc.insert(0, "def func_hello():")
# execute our function definition
exec "\n".join( hsrc) in globals(), locals()
# you now have a function called func_hello, which you can call just like a normal function
func_hello()
func_hello()
print "My original script is still running"

Python doctest for shell scripts that test argument parsing without polluting docstring with os.popen()

Is there a way to write a python doctest string to test a script intended to be launched from the command line (terminal) that doesn't pollute the documentation examples with os.popen calls?
#!/usr/bin/env python
# filename: add
"""
Example:
>>> import os
>>> os.popen('add -n 1 2').read().strip()
'3'
"""
if __name__ == '__main__':
from argparse import ArgumentParser
p = ArgumentParser(description=__doc__.strip())
p.add_argument('-n',type = int, nargs = 2, default = 0,help = 'Numbers to add.')
p.add_argument('--test',action = 'store_true',help = 'Test script.')
a = p.parse_args()
if a.test:
import doctest
doctest.testmod()
if a.n and len(a.n)==2:
print a.n[0]+a.n[1]
Running doctest.testmod() without using popen just causes a test failure because the script is run within a python shell instead of a bash (or DOS) shell.
The advanced python course at LLNL suggests putting scripts in files that are separate from .py modules. But then the doctest strings only test the module, without the arg parsing. And my os.popen() approach pollutes the Examples documentation. Is there a better way?
Just found something looking like the answer you want:
shell-doctest.
doctest is meant to run python code, so you have to do a conversion somewhere. If you are determined to test the commandline interface directly via doctest, one possibility is to do a regexp substitution to __doc__ before you pass it to argparse, to take out the os.popen wrapper:
clean = re.sub(r"^>>> os\.popen\('(.*)'\).*", r"% \1", __doc__)
p = ArgumentParser(description=clean, ...)
(Of course there are all sorts of nicer ways to do that, depending on what you consider "nice").
That'll clean it up for the end user. If you also want it to look cleaner in the source, you can go the other way: Put commandline examples in the docstring and don't use doctest.testmodule(). Run your docstring through doctest.script_from_examples and post-process it to insert the os calls. (Then you'll have to embed it into something so you can test it with run_docstring_examples.) doctest doesn't care if the input is valid python, so you can do the following:
>>> print doctest.script_from_examples("""
Here is a commandline example I want converted:
>>> add -n 3 4
7
""")
# Here is a commandline example I want converted:
add -n 3 4
# Expected:
## 7
This will still expose the python prompt >>> in the help. If this bothers you, you may just have to process the string in both directions.
You can also load the docstring yourself and execute the command, like in this test.
import sys
module = sys.modules[__name__]
docstring = module.__doc__
# search in docstring for certain regex, and check that the following line(s) matches a pattern.

Categories