How to get last python output? - python

I´m trying to get last python output and I dont know how to do it.
Basically what Iím trying to do is detect the last output so, for example:
print("Hello World")
last_output = get_last_output()
print() # For avoiding confutions
print(last_output)
# Would print "\n"
print("Hello World", end="")
last_output = get_last_output()
print() # For avoiding confutions
print(last_output)
# Would print "Hello World"
I would also love this awnser would work independently of the console

Assuming "the last output" is the last not empty string written to sys.stdout, one option is to assign an object with the write(data) and flush() methods to sys.stdout, so you can save what should be the output:
import sys
class StdoutHandler:
def __init__(self):
self.last_output = ""
def start(self):
self._handled_stdout = sys.stdout
sys.stdout = self
def write(self, data: str):
# write(data="") is called for the end kwarg in print(..., end="")
if data:
self.last_output = data
self._handled_stdout.write(data)
def end(self):
sys.stdout = self._handled_stdout
def flush(self):
self._handled_stdout.flush()
stdout_handler = StdoutHandler()
stdout_handler.start()
print("Hello World")
last_output = stdout_handler.last_output
print(repr(last_output))
# Prints '\n'
print("Hello World", end="")
last_output = stdout_handler.last_output
print()
print(repr(last_output))
# Prints 'Hello World'
print("Hello", "World", end="")
last_output = stdout_handler.last_output
print()
print(repr(last_output))
# Prints 'World'
I got the idea from How to duplicate sys.stdout to a log file?
Disclaimer: #Baelfire18 asked me to help him answer this question

Related

How to set a prefix for all print() output in python?

I am printing to a console in python. I am looking for a one off piece of code so that all print statments after a line of code have 4 spaces at the start. Eg.
print('Computer: Hello world')
print.setStart(' ')
print('receiving...')
print('received!')
print.setStart('')
print('World: Hi!')
Output:
Computer: Hello world
receiving...
received!
World: Hi!
This would be helpful for tabbing all of the output that is contained in a function, and setting when functions output are tabbed. Is this possible?
You can define a print function which first prints your prefix, and then internally calls the built-in print function. You can even make your custom print() function to look at the call-stack and accordingly determine how many spaces to use as a prefix:
import builtins
import traceback
def print(*objs, **kwargs):
my_prefix = len(traceback.format_stack())*" "
builtins.print(my_prefix, *objs, **kwargs)
Test it out:
def func_f():
print("Printing from func_f")
func_g()
def func_g():
print ("Printing from func_g")
func_f()
Output:
Printing from func_f
Printing from func_g
Reverting back to the built-in print() function:
When you are done with your custom printing, and want to start using the built-in print() function, just use del to "delete" your own definition of print:
del print
Why not define your own custom function and use that when needed:
def tprint(*args):
print(' ', *args)
It would be used like so:
print('Computer: Hello world')
tprint('receiving...')
tprint('received!')
print('World: Hi!')
Output:
Computer: Hello world
receiving...
received!
World: Hi!
You might want to use specific prefixes only at specific places
import sys
from contextlib import contextmanager
#contextmanager
def add_prefix(prefix):
global is_new_line
orig_write = sys.stdout.write
is_new_line = True
def new_write(*args, **kwargs):
global is_new_line
if args[0] == "\n":
is_new_line = True
elif is_new_line:
orig_write("[" + str(prefix) + "]: ")
is_new_line = False
orig_write(*args, **kwargs)
sys.stdout.write = new_write
yield
sys.stdout.write = orig_write
with add_prefix("Computer 1"):
print("Do something", "cool")
print("Do more stuffs")
with add_prefix("Computer 2"):
print("Do further stuffs")
print("Done")
#[Computer 1]: Do something cool
#[Computer 1]: Do more stuffs
#[Computer 2]: Do further stuffs
#Done
The advantage is that it's a utility function, i.e. you just have to import to use it, without having to redefine every time you write a new script.

Fully mimic stdout in tkinter text widget to support the end parameter from Python's print function

How do I fully mimic sys.stdout in a tkinter text widget so that I can use the print() function (mainly the end parameter) to its full capacity? So that I can use print("Hello World", end='\r') to print to the text widget where if I print something else it will overwrite the previously printed text.
class Main:
def __init__(self):
self.master = tk.Tk()
self.output = tk.Text(self.master)
self.output.pack()
sys.stdout = self
self.master.mainloop()
def write(self, txt):
self.output.insert(tk.INSERT, str(txt))
self.output.see('end')
def flush(self):
# What do I do here?
Running
print("Hello World", end='\r')
print("Goodbye World", end='\r')
Gives the following in the text widget
Hello World
Goodbye World
But I expect it to show the following instead
Goodbye World
As a bonus question, how would I (re)define the flush functionality of stdout to work with a tkinter text widget?
I fixed the problem with the return carriage by updating the write function with the following:
def write(self, txt):
if '\r' in txt:
self.return_carriage = True
return
if self.return_carriage:
self.output.delete(tk.INSERT + " linestart", tk.INSERT + " lineend")
self.return_carriage = False
self.output.insert(tk.INSERT, str(txt))
self.output.see('end')
This only solves the problem when using end='\r' in the print() function though. This still does not mimic the full sys.stdout.write functionality.

How to put all print result in a function into a variable?

I have a function in Python:
def f():
...
a lot of code
...
print "hello"
...
a lot of code
...
I want to call this function, however, the print result will be put into a variable instead print on the screen directly. How can I do this with Python?
ps:
please don't just return, sometimes I don't know where the print statement is.
Assuming that print is writing to sys.stdout, you can temporarily replace that with something like a StringIO object.
stdout = sys.stdout
sys.stdout = StringIO()
f()
x = sys.stdout.getvalue()
sys.stdout = stdout
Or, if you have a reference to the file handle that print is using, you can use that instead of sys.stdout.
If there are multiple uses of print from inside f, and you only want to capture some of them (say, only from a function g called from inside f), I'm afraid you are out of luck. The amount of introspection you would need to do would make it possible would allow you to simply re-implement the function to accumulate the desired output in a variable instead of using print.
Use a decorator like below
import sys
from StringIO import StringIO
s = StringIO()
def catch_stdout(user_method):
sys.stdout = s
def decorated(*args, **kwargs):
user_method(*args, **kwargs)
sys.stdout = sys.__stdout__
print 'printing result of all prints in one go'
s.seek(0, 0)
print s.read()
return decorated
#catch_stdout
def test():
print 'hello '
print 'world '
test()
You could also define your own context manager if you find you need to do this a lot so you can capture the output for a block of statements, eg:
import contextlib
from StringIO import StringIO
import sys
#contextlib.contextmanager
def capture_stdout():
old_stdout = sys.stdout
sys.stdout = StringIO()
yield sys.stdout, old_stdout
sys.stdout = old_stdout
Then use as follows:
def something():
print 'this is something'
# All prints that go to stdout inside this block either called
# directly or indirectly will be put into a StringIO object instead
# unless the original stdout is used directly...
with capture_print() as (res, stdout):
print 'hello',
print >> stdout, "I'm the original stdout!"
something()
print res.getvalue() + 'blah' # normal print to stdout outside with block
Gives you:
I'm the original stdout
hello this is something
blah
def f():
#code
variable = 'hello\n'
#code
variable += 'hello2\n'
#code
...
print(variable)
or
def f():
#code
variable = 'hello\n'
#code
variable += 'hello2\n'
#code
...
return(variable)
and then
print(f())

Redirect print to string list?

I know how to redirect print to a file.
import sys
orig_stdout = sys.stdout
f = file('out.txt', 'w')
sys.stdout = f
for i in range(2):
print ('i = ', i)
sys.stdout = orig_stdout
f.close()
I need to do the same but w/out a file: keep print output in a string list. How to do it in Py3k?
Edit: I can have 3rd party prints in a middle part, not my own prints, so code must be universal for usual "print()".
import sys
class ListStream:
def __init__(self):
self.data = []
def write(self, s):
self.data.append(s)
sys.stdout = x = ListStream()
for i in range(2):
print ('i = ', i)
sys.stdout = sys.__stdout__
print(x.data)
yields
['i = ', ' ', '0', '\n', 'i = ', ' ', '1', '\n']
Tip: You don't need to save the original sys.stdout
orig_stdout = sys.stdout
since sys.stdout can be reset with
sys.stdout = sys.__stdout__
You could also add some syntactic sugar by making ListStream a contextmanager:
import sys
class ListStream:
def __init__(self):
self.data = []
def write(self, s):
self.data.append(s)
def __enter__(self):
sys.stdout = self
return self
def __exit__(self, ext_type, exc_value, traceback):
sys.stdout = sys.__stdout__
By adding the __enter__ and __exit__ methods, you can now use ListStream in a with-statement which will automatically reset sys.stdout for you when Python exits the with-suite:
with ListStream() as x:
for i in range(2):
print ('i = ', i)
print(x.data)
Instead of rolling your own class, I think it's easiest to replace sys.stdout (which is simply a TextIOWrapper) with a StringIO instance you keep a reference to:
import sys
from io import StringIO
s = StringIO()
sys.stdout = s
print('yo')
print('this is stuff')
print('hi')
s.getvalue()
Out[38]: 'yo\nthis is stuff\nhi\n'
s.getvalue().splitlines()
Out[39]: ['yo', 'this is stuff', 'hi']
As #unutbu says, you can restore the original stdout with sys.stdout = sys.__stdout__; I particlarly like the idea of using a context manager to temporarily redirect stdout to where you want it to go.
That's something I often do when I need to build a ncurses application:
import sys
# in this wrapper class you can use a string list instead of a full string like I'm doing
class StdOutWrapper:
lines = []
def write(self,txt):
self.lines.append(txt)
# here is a method so you can get stuff out of your wrapper class
# I am rebuilding the text, but you can do whatever you want!
def get_text(self,beg,end):
return '\n'.join(self.lines)
mystdout = StdOutWrapper()
sys.stdout = mystdout
sys.stderr = mystdout
# do your stuff here that needs to be printed out in a string list
for i in range(2):
print ('i = ', i)
# you don't need to make your variable to cache the `stdout`/`stderr` as they still exist
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
it is working fine with python 3 and python 2.
I would write a function to do it for you, rather than trying to redirect stdout to a list (which I don't think could possibly work anyway, but don't quote me on that).
def lprint(text):
global string_list
try: string_list.append(text)
except NameError as e:
string_list = [text]
for i in range(2):
lprint ("i = {}".format(i))
print(string_list)
[OUT]: ["i = 0","i = 1"]

python: change sys.stdout print to custom print function

Im trying to understand how to create a custom print function.
(using python 2.7)
import sys
class CustomPrint():
def __init__(self):
self.old_stdout=sys.stdout #save stdout
def write(self, text):
sys.stdout = self.old_stdout #restore normal stdout and print
print 'custom Print--->' + text
sys.stdout= self # make stdout use CustomPrint on next 'print'
# this is the line that trigers the problem
# how to avoid this??
myPrint = CustomPrint()
sys.stdout = myPrint
print 'why you make 2 lines??...'
The code above prints this to console:
>>>
custom Print--->why you make 2 lines??...
custom Print--->
>>>
and i want to print only one line:
>>>
1custom Print--->why you make 2 lines??...
>>>
But cant figure out how to make this custom print work , i understand that there's some kind of recursion that triggers the second output to the console (i use self.write , to assign stdout to self.write himself !)
how can i make this work ? or is my approach just completely wrong...
It's not recursion. What happens is your write function is called twice, once with the text you expect, second time with just '\n'. Try this:
import sys
class CustomPrint():
def __init__(self):
self.old_stdout=sys.stdout
def write(self, text):
text = text.rstrip()
if len(text) == 0: return
self.old_stdout.write('custom Print--->' + text + '\n')
def flush(self):
self.old_stdout.flush()
What I do in the above code is I add the new line character to the text passed in the first call, and make sure the second call made by the print statement, the one meant to print new line, doesn't print anything.
Now try to comment out the first two lines and see what happens:
def write(self, text):
#text = text.rstrip()
#if len(text) == 0: return
self.old_stdout.write('custom Print--->' + text + '\n')
One solution may be to use a context manager if it's localised.
#!/usr/bin/env python
from __future__ import print_function
from contextlib import contextmanager
#############################
#contextmanager
def no_stdout():
import sys
old_stdout = sys.stdout
class CustomPrint():
def __init__(self, stdout):
self.old_stdout = stdout
def write(self, text):
if len(text.rstrip()):
self.old_stdout.write('custom Print--->' + text)
sys.stdout = CustomPrint(old_stdout)
try:
yield
finally:
sys.stdout = old_stdout
#############################
print("BEFORE")
with no_stdout():
print("WHY HELLO!\n")
print("DING DONG!\n")
print("AFTER")
The above produces:
BEFORE
custom Print--->WHY HELLO!
custom Print--->DING DONG!
AFTER
The code would need tidying up esp. around what the class should do WRT setting stdout back to what it was.
How about doing from __future__ import print_function. This way you will use Python3 print function instead of print statement from Python2. Then you can redefine the print function:
def print(*args, **kwargs):
__builtins__.print("Custom--->", *args, **kwargs)
There is a catch however, you will have to start using print function.

Categories