python cmd line application with blessings and cmd - python

I would like to write cmd-line application with Python, which in the upper part of the terminal shows the status of something, I'd like to monitor, while in the lower part I (and my colleages) have the normal ipython interpreter, which allows us to manipulate the behaviour of the application.
In order to show the status display, I was thinking of using blessings. At first I thought about using cmd or cmd2 in order to allow run-time manipulation of the status display, but then I though, why should one implement a lot of do_something methods, when one can have the same functionality (including tab-completion and online help) for free, using the ipython interpreter.
Here is my first approach
## -- first_try.py
import time
import random
from threading import Thread
from blessings import Terminal
term = Terminal()
def display_func1():
print time.asctime()
print "Some int: ", random.randint(0, 10)
print "Some float: ", random.random()*10
def display_func2():
print time.asctime()
print "Some int: ", random.randint(0, 10)
print " details:", [random.randint(0, 10) for i in range(7)]
print "Some float: ", random.random()*10
print " details:", [random.random()*10 for i in range(5)]
class StatusDisplay(Thread):
def __init__(self, display_func):
self._text = ""
self._stop = False
self.display = display_func
Thread.__init__(self)
def run(self):
while not self._stop:
with term.location():
self.clear_top_lines()
print term.move(0, 0)
print 70*'-'
self.display()
print 70*'-'
self.print_help()
time.sleep(0.1)
def stop(self):
self._stop = True
def print_help(self):
print
print "In order to manipulate the status display"
print "type: "
print " sd.display = display_func2 and if you want to go back."
print " sd.display = display_func1"
print " Or implement your own display function and hook it into the display"
def clear_top_lines(self, n=10):
with term.location():
for i in range(n):
print term.move(0, i) + term.clear_eol()
time.sleep(2)
sd = StatusDisplay(display_func1)
sd.daemon = True
sd.start()
When I invoke this script, e.g. like this:
ipython -i --no-banner first_try.py
And hit a couple of times enter, then I have in the top of the page, something that looks like the status display, I'd like to have, and in the lower part, I can still work with the ipython interpreter, due to the -i parameter.
I think I am working here in the wrong direction, but I really would like to have this kind of feature in my application. Can somebody give me a push into the right direction?

Related

Return object to end parallel while True loop

I am writing a little API to make my python command line output look just a little nicer. But there is still a big problem, because I would like to be able to call a function like display_loading_until_event(text) which should:
Immediately return a way to "finish" the loading animation
Update the loading animation every 0.25 seconds.
I also tried using Process from the multiprocessing library but it doesn't seem to work:
from time import sleep
def display_loading_until_event(message):
from multiprocessing import Process
class Stop:
def __init__(self):
self.run = True
class LoadingAnimationProcess(Process):
def __init__(self, stopper, *args, **kwargs):
Process.__init__(self, *args, **kwargs)
self.stop = stopper
def run(self):
def get_loading(position):
return positions[position] # Returns the way the loading animation looks like (position: ["\", "|", "-"])
print(get_loading(0), end=message) # Simplified version
pos = 0
while self.stop.run:
sleep(0.25)
pos = pos + 1
print("\r" + get_loading(pos) + message, end="") # Again, simplified
if pos == len(positions) - 1:
pos = -1
sleep(0.25)
print("\r" + "✔" + message) # Prints ✔ to signal that the loading is done
stopper = Stop()
loading = LoadingAnimationProcess(stopper)
loading.start() # Starts the process
def stop():
stopper.run = False
sleep(1)
# loading.join() ???
return stop # This therefore returns a function to stop the loading, right?
# So this should in theory work:
positions = ["\\", "|", "-"]
endfnc = display_loading_until_event("Hello loading")
sleep(4)
endfnc()
# And now the loading SHOULD STOP
This code is just an example, the real code is a bit more complex but this should do.
The loading animation doesn't work on powershell or so (\r is the thing that breaks it.)
The current code works and all things after the display_loading_until_event call get executed but the loading doesn't stop.
And also I don't think that this is the right way to do this...
This kind of does the job:
How to end a while loop in another Thread?
I just return the "condition.set" method and it works!

How do you store the click command line arguments/options for reference?

New to click here so I'm still learning. How do I store the click arguments/options in an object for later reference within my application? I thought of just creating an object and returning it from the setup function, but it doesn't seem to work. Here is what I did:
import click
import sys
class Cfg(object):
component = ""
command = ""
obj = ""
my_cfg = Cfg()
#click.command()
#click.argument('component')
#click.argument("command")
#click.argument("obj")
def set_args(component, command, obj):
cfg = Cfg()
if component != "optdata":
sys.stderr.write("Invalid option")
sys.exit(1)
else:
cfg.component = component
cfg.command = command
cfg.obj = obj
return cfg
if __name__ == "__main__":
app_cfg = Cfg()
app_cfg = set_args() # Never actually completes here.
print("Component = ", app_cfg.component, "Command = ", app_cfg.command, "Obj = ", app_cfg.obj)
There is some sort of exception raised in core.py which just does a sys.exit and doesn't raise any sort of actual error.
Your design goes against the idea of Click: You're not supposed to treat "parsing the options" and "doing the work" as two separate steps:
import click
#click.command()
#click.argument("component", type=click.Choice(["optdata"]))
#click.argument("command")
#click.argument("obj")
def cli(component, command, obj):
print("Component = ", component, "Command = ", command, "Obj = ", obj)
# put your business logic here
if __name__ == "__main__":
cli()
The pattern is to call the function that processes the command line options and then have that function call any other functionality.

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.

Progress Bar not updating in Python container on Kubernetes

I am trying to get a simple progress bar working in my Python container on Kubernetes. However, it doesn't output anything until the job is done and the progress bar reaches 100%. The same exact code works perfectly in a local Docker container. So what is it that Kubernetes has in place that prevents me from seeing the progress bar updating in real time in its logs?
Progress bar code:
import sys
import time
class Color:
BOLD = '\033[1m'
ENDC = '\033[0m'
ERASE_LINE = '\x1b[2K\r'
def percentage(current, total):
percent = 100 * float(current) / float(total)
return percent
class ProgressBar:
def __init__(self, total):
self._total = total
self._current = 0
self.print()
def update(self):
self._current += 1
self.print()
def print(self):
percent = percentage(self._current, self._total)
sys.stdout.write("\r")
sys.stdout.write(Color.BOLD + "[%-50s] %d%%" % ('=' * int(percent / 2), percent) + Color.ENDC)
sys.stdout.flush()
if __name__=="__main__":
print("Ready to run soon...")
time.sleep(10)
print("Running!")
pbar = ProgressBar(255)
for i in range(255):
time.sleep(0.03)
pbar.update()
When logging, rather than displaying things in a TTY to a human, you generally need to log in complete lines ending in \n. Rather than a progress bar, I would usually recommend something like printing out 10%...\n20%...\n etc. Up to you how often you print the current state.
Update:
You can update your script to detect if the terminal is TTY, and change the behaviour accordingly
Use this:
import os, sys
if os.isatty(sys.stdout.fileno()):

Python wait x secs for a key and continue execution if not pressed

I'm a n00b to python, and I'm looking a code snippet/sample which performs the following:
Display a message like "Press any key to configure or wait X seconds to continue"
Wait, for example, 5 seconds and continue execution, or enter a configure() subroutine if a key is pressed.
Thank you for your help!
Yvan Janssens
If you're on Unix/Linux then the select module will help you.
import sys
from select import select
print "Press any key to configure or wait 5 seconds..."
timeout = 5
rlist, wlist, xlist = select([sys.stdin], [], [], timeout)
if rlist:
print "Config selected..."
else:
print "Timed out..."
If you're on Windows, then look into the msvcrt module. (Note this doesn't work in IDLE, but will in cmd prompt)
import sys, time, msvcrt
timeout = 5
startTime = time.time()
inp = None
print "Press any key to configure or wait 5 seconds... "
while True:
if msvcrt.kbhit():
inp = msvcrt.getch()
break
elif time.time() - startTime > timeout:
break
if inp:
print "Config selected..."
else:
print "Timed out..."
Edit Changed the code samples so you could tell whether there was a timeout or a keypress...
Python doesn't have any standard way to catch this, it gets keyboard input only through input() and raw_input().
If you really want this you could use Tkinter or pygame to catch the keystrokes as "events". There are also some platform-specific solutions like pyHook. But if it's not absolutely vital to your program, I suggest you make it work another way.
If you combine time.sleep, threading.Thread, and sys.stdin.read you can easily wait for a specified amount of time for input and then continue.
t = threading.Thread(target=sys.stdin.read(1) args=(1,))
t.start()
time.sleep(5)
t.join()
Here's how I did it:
import threading
import time
import sys
class MyThread(threading.Thread):
def __init__(self, threadID, name, counter, f):
super().__init__()
self.threadID = threadID
self.name = name
self.counter = counter
self.func = f
def run(self):
self.func()
class KeyboardMonitor:
def __init__(self):
# Setting a boolean flag is atomic in Python.
# It's hard to imagine a boolean being
# anything else, with or without the GIL.
# If inter-thread communication is anything more complicated than
# a couple of flags, you should replace low level variables with
# a thread safe buffer.
self.keepGoing = True
def wait4KeyEntry(self):
while self.keepGoing:
s = input("Type q to quit: ")
if s == "q":
self.keepGoing = False
def mainThread(self, f, *args, **kwargs):
"""Pass in some main function you want to run, and this will run it
until keepGoing = False. The first argument of function f must be
this class, so that that function can check the keepGoing flag and
quit when keepGoing is false."""
keyboardThread = MyThread(1, "keyboard_thread", 0, self.wait4KeyEntry)
keyboardThread.start()
while self.keepGoing:
f(self, *args, **kwargs)
def main(keyMonitorInst, *args, **kwargs):
while keyMonitorInst.keepGoing:
print("Running again...")
time.sleep(1)
if __name__ == "__main__":
uut = KeyboardMonitor()
uut.mainThread(main)
Rather than make a blocking call time out, my approach is to start a thread that waits for the user to enter input, while another thread does something else. The two processes communicate through a small number of atomic operations: in this case, setting a boolean flag. For anything more complicated than atomic operations, obviously you should replace the atomic variable with a threadsafe buffer of some kind.

Categories