Re-indent libprs500.cli to 4 spaces as per PEP8

This commit is contained in:
Kovid Goyal 2006-12-22 19:57:01 +00:00
parent 567017f238
commit 4b2afac719
2 changed files with 287 additions and 287 deletions

View File

@ -30,275 +30,275 @@ from libprs500.errors import ArgumentError, DeviceError
MINIMUM_COL_WIDTH = 12 #: Minimum width of columns in ls output MINIMUM_COL_WIDTH = 12 #: Minimum width of columns in ls output
def human_readable(size): def human_readable(size):
""" Convert a size in bytes into a human readle form """ """ Convert a size in bytes into a human readle form """
if size < 1024: divisor, suffix = 1, "" if size < 1024: divisor, suffix = 1, ""
elif size < 1024*1024: divisor, suffix = 1024., "K" elif size < 1024*1024: divisor, suffix = 1024., "K"
elif size < 1024*1024*1024: divisor, suffix = 1024*1024, "M" elif size < 1024*1024*1024: divisor, suffix = 1024*1024, "M"
elif size < 1024*1024*1024*1024: divisor, suffix = 1024*1024, "G" elif size < 1024*1024*1024*1024: divisor, suffix = 1024*1024, "G"
size = str(size/divisor) size = str(size/divisor)
if size.find(".") > -1: size = size[:size.find(".")+2] if size.find(".") > -1: size = size[:size.find(".")+2]
return size + suffix return size + suffix
class FileFormatter(object): class FileFormatter(object):
def __init__(self, file, term): def __init__(self, file, term):
self.term = term self.term = term
self.is_dir = file.is_dir self.is_dir = file.is_dir
self.is_readonly = file.is_readonly self.is_readonly = file.is_readonly
self.size = file.size self.size = file.size
self.ctime = file.ctime self.ctime = file.ctime
self.wtime = file.wtime self.wtime = file.wtime
self.name = file.name self.name = file.name
self.path = file.path self.path = file.path
@apply @apply
def mode_string(): def mode_string():
doc=""" The mode string for this file. There are only two modes read-only and read-write """ doc=""" The mode string for this file. There are only two modes read-only and read-write """
def fget(self): def fget(self):
mode, x = "-", "-" mode, x = "-", "-"
if self.is_dir: mode, x = "d", "x" if self.is_dir: mode, x = "d", "x"
if self.is_readonly: mode += "r-"+x+"r-"+x+"r-"+x if self.is_readonly: mode += "r-"+x+"r-"+x+"r-"+x
else: mode += "rw"+x+"rw"+x+"rw"+x else: mode += "rw"+x+"rw"+x+"rw"+x
return mode return mode
return property(**locals()) return property(**locals())
@apply @apply
def name_in_color(): def name_in_color():
doc=""" The name in ANSI text. Directories are blue, ebooks are green """ doc=""" The name in ANSI text. Directories are blue, ebooks are green """
def fget(self): def fget(self):
cname = self.name cname = self.name
blue, green, normal = "", "", "" blue, green, normal = "", "", ""
if self.term: blue, green, normal = self.term.BLUE, self.term.GREEN, self.term.NORMAL if self.term: blue, green, normal = self.term.BLUE, self.term.GREEN, self.term.NORMAL
if self.is_dir: cname = blue + self.name + normal if self.is_dir: cname = blue + self.name + normal
else: else:
ext = self.name[self.name.rfind("."):] ext = self.name[self.name.rfind("."):]
if ext in (".pdf", ".rtf", ".lrf", ".lrx", ".txt"): cname = green + self.name + normal if ext in (".pdf", ".rtf", ".lrf", ".lrx", ".txt"): cname = green + self.name + normal
return cname return cname
return property(**locals()) return property(**locals())
@apply @apply
def human_readable_size(): def human_readable_size():
doc=""" File size in human readable form """ doc=""" File size in human readable form """
def fget(self): def fget(self):
human_readable(self.size) human_readable(self.size)
return property(**locals()) return property(**locals())
@apply @apply
def modification_time(): def modification_time():
doc=""" Last modified time in the Linux ls -l format """ doc=""" Last modified time in the Linux ls -l format """
def fget(self): def fget(self):
return time.strftime("%Y-%m-%d %H:%M", time.localtime(self.wtime)) return time.strftime("%Y-%m-%d %H:%M", time.localtime(self.wtime))
return property(**locals()) return property(**locals())
@apply @apply
def creation_time(): def creation_time():
doc=""" Last modified time in the Linux ls -l format """ doc=""" Last modified time in the Linux ls -l format """
def fget(self): def fget(self):
return time.strftime("%Y-%m-%d %H:%M", time.localtime(self.ctime)) return time.strftime("%Y-%m-%d %H:%M", time.localtime(self.ctime))
return property(**locals()) return property(**locals())
def info(dev): def info(dev):
info = dev.get_device_information() info = dev.get_device_information()
print "Device name: ", info[0] print "Device name: ", info[0]
print "Device version: ", info[1] print "Device version: ", info[1]
print "Software version:", info[2] print "Software version:", info[2]
print "Mime type: ", info[3] print "Mime type: ", info[3]
def ls(dev, path, term, recurse=False, color=False, human_readable_size=False, ll=False, cols=0): def ls(dev, path, term, recurse=False, color=False, human_readable_size=False, ll=False, cols=0):
def col_split(l, cols): # split list l into columns def col_split(l, cols): # split list l into columns
rows = len(l) / cols rows = len(l) / cols
if len(l) % cols: if len(l) % cols:
rows += 1 rows += 1
m = [] m = []
for i in range(rows): for i in range(rows):
m.append(l[i::rows]) m.append(l[i::rows])
return m return m
def row_widths(table): # Calculate widths for each column in the row-wise table def row_widths(table): # Calculate widths for each column in the row-wise table
tcols = len(table[0]) tcols = len(table[0])
rowwidths = [ 0 for i in range(tcols) ] rowwidths = [ 0 for i in range(tcols) ]
for row in table: for row in table:
c = 0 c = 0
for item in row: for item in row:
rowwidths[c] = len(item) if len(item) > rowwidths[c] else rowwidths[c] rowwidths[c] = len(item) if len(item) > rowwidths[c] else rowwidths[c]
c += 1 c += 1
return rowwidths return rowwidths
output = StringIO.StringIO() output = StringIO.StringIO()
if path.endswith("/"): path = path[:-1] if path.endswith("/"): path = path[:-1]
dirs = dev.list(path, recurse) dirs = dev.list(path, recurse)
for dir in dirs: for dir in dirs:
if recurse: print >>output, dir[0] + ":" if recurse: print >>output, dir[0] + ":"
lsoutput, lscoloutput = [], [] lsoutput, lscoloutput = [], []
files = dir[1] files = dir[1]
maxlen = 0 maxlen = 0
if ll: # Calculate column width for size column if ll: # Calculate column width for size column
for file in files: for file in files:
size = len(str(file.size)) size = len(str(file.size))
if human_readable_size: if human_readable_size:
file = FileFormatter(file, term) file = FileFormatter(file, term)
size = len(file.human_readable_size) size = len(file.human_readable_size)
if size > maxlen: maxlen = size if size > maxlen: maxlen = size
for file in files: for file in files:
file = FileFormatter(file, term) file = FileFormatter(file, term)
name = file.name name = file.name
lsoutput.append(name) lsoutput.append(name)
if color: name = file.name_in_color if color: name = file.name_in_color
lscoloutput.append(name) lscoloutput.append(name)
if ll: if ll:
size = str(file.size) size = str(file.size)
if human_readable_size: size = file.human_readable_size if human_readable_size: size = file.human_readable_size
print >>output, file.mode_string, ("%"+str(maxlen)+"s")%size, file.modification_time, name print >>output, file.mode_string, ("%"+str(maxlen)+"s")%size, file.modification_time, name
if not ll and len(lsoutput) > 0: if not ll and len(lsoutput) > 0:
trytable = [] trytable = []
for colwidth in range(MINIMUM_COL_WIDTH, cols): for colwidth in range(MINIMUM_COL_WIDTH, cols):
trycols = int(cols/colwidth) trycols = int(cols/colwidth)
trytable = col_split(lsoutput, trycols) trytable = col_split(lsoutput, trycols)
works = True works = True
for row in trytable: for row in trytable:
row_break = False row_break = False
for item in row: for item in row:
if len(item) > colwidth - 1: if len(item) > colwidth - 1:
works, row_break = False, True works, row_break = False, True
break break
if row_break: break if row_break: break
if works: break if works: break
rowwidths = row_widths(trytable) rowwidths = row_widths(trytable)
trytablecol = col_split(lscoloutput, len(trytable[0])) trytablecol = col_split(lscoloutput, len(trytable[0]))
for r in range(len(trytable)): for r in range(len(trytable)):
for c in range(len(trytable[r])): for c in range(len(trytable[r])):
padding = rowwidths[c] - len(trytable[r][c]) padding = rowwidths[c] - len(trytable[r][c])
print >>output, trytablecol[r][c], "".ljust(padding), print >>output, trytablecol[r][c], "".ljust(padding),
print >>output print >>output
print >>output print >>output
listing = output.getvalue().rstrip()+ "\n" listing = output.getvalue().rstrip()+ "\n"
output.close() output.close()
return listing return listing
def main(): def main():
term = TerminalController() term = TerminalController()
cols = term.COLS cols = term.COLS
parser = OptionParser(usage="usage: %prog [options] command args\n\ncommand is one of: info, books, df, ls, cp, mkdir, touch, cat, rm\n\n"+ parser = OptionParser(usage="usage: %prog [options] command args\n\ncommand is one of: info, books, df, ls, cp, mkdir, touch, cat, rm\n\n"+
"For help on a particular command: %prog command", version="libprs500 version: " + VERSION) "For help on a particular command: %prog command", version="libprs500 version: " + VERSION)
parser.add_option("--log-packets", help="print out packet stream to stdout. "+\ parser.add_option("--log-packets", help="print out packet stream to stdout. "+\
"The numbers in the left column are byte offsets that allow the packet size to be read off easily.", "The numbers in the left column are byte offsets that allow the packet size to be read off easily.",
dest="log_packets", action="store_true", default=False) dest="log_packets", action="store_true", default=False)
parser.remove_option("-h") parser.remove_option("-h")
parser.disable_interspersed_args() # Allow unrecognized options parser.disable_interspersed_args() # Allow unrecognized options
options, args = parser.parse_args() options, args = parser.parse_args()
if len(args) < 1: if len(args) < 1:
parser.print_help()
return 1
command = args[0]
args = args[1:]
dev = PRS500Device(log_packets=options.log_packets)
try:
if command == "df":
total = dev.total_space(end_session=False)
free = dev.free_space()
where = ("Memory", "Stick", "Card")
print "Filesystem\tSize \tUsed \tAvail \tUse%"
for i in range(3):
print "%-10s\t%s\t%s\t%s\t%s"%(where[i], human_readable(total[i]), human_readable(total[i]-free[i]), human_readable(free[i]),\
str(0 if total[i]==0 else int(100*(total[i]-free[i])/(total[i]*1.)))+"%")
elif command == "books":
print "Books in main memory:"
for book in dev.books(): print book
print "\nBooks on storage card:"
for book in dev.books(oncard=True): print book
elif command == "mkdir":
parser = OptionParser(usage="usage: %prog mkdir [options] path\nCreate a directory on the device\n\npath must begin with /,a:/ or b:/")
if len(args) != 1:
parser.print_help()
sys.exit(1)
dev.mkdir(args[0])
elif command == "ls":
parser = OptionParser(usage="usage: %prog ls [options] path\nList files on the device\n\npath must begin with /,a:/ or b:/")
parser.add_option("--color", help="show ls output in color", dest="color", action="store_true", default=False)
parser.add_option("-l", help="In addition to the name of each file, print the file type, permissions, and timestamp (the modification time, in the local timezone). Times are local.", dest="ll", action="store_true", default=False)
parser.add_option("-R", help="Recursively list subdirectories encountered. /dev and /proc are omitted", dest="recurse", action="store_true", default=False)
parser.remove_option("-h")
parser.add_option("-h", "--human-readable", help="show sizes in human readable format", dest="hrs", action="store_true", default=False)
options, args = parser.parse_args(args)
if len(args) != 1:
parser.print_help() parser.print_help()
return 1 return 1
print ls(dev, args[0], term, color=options.color, recurse=options.recurse, ll=options.ll, human_readable_size=options.hrs, cols=cols),
elif command == "info": command = args[0]
info(dev) args = args[1:]
elif command == "cp": dev = PRS500Device(log_packets=options.log_packets)
usage="usage: %prog cp [options] source destination\nCopy files to/from the device\n\n"+\ try:
if command == "df":
total = dev.total_space(end_session=False)
free = dev.free_space()
where = ("Memory", "Stick", "Card")
print "Filesystem\tSize \tUsed \tAvail \tUse%"
for i in range(3):
print "%-10s\t%s\t%s\t%s\t%s"%(where[i], human_readable(total[i]), human_readable(total[i]-free[i]), human_readable(free[i]),\
str(0 if total[i]==0 else int(100*(total[i]-free[i])/(total[i]*1.)))+"%")
elif command == "books":
print "Books in main memory:"
for book in dev.books(): print book
print "\nBooks on storage card:"
for book in dev.books(oncard=True): print book
elif command == "mkdir":
parser = OptionParser(usage="usage: %prog mkdir [options] path\nCreate a directory on the device\n\npath must begin with /,a:/ or b:/")
if len(args) != 1:
parser.print_help()
sys.exit(1)
dev.mkdir(args[0])
elif command == "ls":
parser = OptionParser(usage="usage: %prog ls [options] path\nList files on the device\n\npath must begin with /,a:/ or b:/")
parser.add_option("--color", help="show ls output in color", dest="color", action="store_true", default=False)
parser.add_option("-l", help="In addition to the name of each file, print the file type, permissions, and timestamp (the modification time, in the local timezone). Times are local.", dest="ll", action="store_true", default=False)
parser.add_option("-R", help="Recursively list subdirectories encountered. /dev and /proc are omitted", dest="recurse", action="store_true", default=False)
parser.remove_option("-h")
parser.add_option("-h", "--human-readable", help="show sizes in human readable format", dest="hrs", action="store_true", default=False)
options, args = parser.parse_args(args)
if len(args) != 1:
parser.print_help()
return 1
print ls(dev, args[0], term, color=options.color, recurse=options.recurse, ll=options.ll, human_readable_size=options.hrs, cols=cols),
elif command == "info":
info(dev)
elif command == "cp":
usage="usage: %prog cp [options] source destination\nCopy files to/from the device\n\n"+\
"One of source or destination must be a path on the device. \n\nDevice paths have the form\n"+\ "One of source or destination must be a path on the device. \n\nDevice paths have the form\n"+\
"prs500:mountpoint/my/path\n"+\ "prs500:mountpoint/my/path\n"+\
"where mountpoint is one of /, a: or b:\n\n"+\ "where mountpoint is one of /, a: or b:\n\n"+\
"source must point to a file for which you have read permissions\n"+\ "source must point to a file for which you have read permissions\n"+\
"destination must point to a file or directory for which you have write permissions" "destination must point to a file or directory for which you have write permissions"
parser = OptionParser(usage=usage) parser = OptionParser(usage=usage)
options, args = parser.parse_args(args) options, args = parser.parse_args(args)
if len(args) != 2: if len(args) != 2:
parser.print_help() parser.print_help()
return 1 return 1
if args[0].startswith("prs500:"): if args[0].startswith("prs500:"):
outfile = args[1] outfile = args[1]
path = args[0][7:] path = args[0][7:]
if path.endswith("/"): path = path[:-1] if path.endswith("/"): path = path[:-1]
if os.path.isdir(outfile): if os.path.isdir(outfile):
outfile = os.path.join(outfile, path[path.rfind("/")+1:]) outfile = os.path.join(outfile, path[path.rfind("/")+1:])
try: try:
outfile = open(outfile, "w") outfile = open(outfile, "w")
except IOError, e: except IOError, e:
print >> sys.stderr, e print >> sys.stderr, e
parser.print_help() parser.print_help()
return 1 return 1
dev.get_file(path, outfile) dev.get_file(path, outfile)
outfile.close() outfile.close()
elif args[1].startswith("prs500:"): elif args[1].startswith("prs500:"):
try: try:
infile = open(args[0], "r") infile = open(args[0], "r")
except IOError, e: except IOError, e:
print >> sys.stderr, e print >> sys.stderr, e
parser.print_help() parser.print_help()
return 1 return 1
dev.put_file(infile, args[1][7:]) dev.put_file(infile, args[1][7:])
infile.close() infile.close()
else: else:
parser.print_help() parser.print_help()
return 1 return 1
elif command == "cat": elif command == "cat":
outfile = sys.stdout outfile = sys.stdout
parser = OptionParser(usage="usage: %prog cat path\nShow file on the device\n\npath should point to a file on the device and must begin with /,a:/ or b:/") parser = OptionParser(usage="usage: %prog cat path\nShow file on the device\n\npath should point to a file on the device and must begin with /,a:/ or b:/")
options, args = parser.parse_args(args) options, args = parser.parse_args(args)
if len(args) != 1: if len(args) != 1:
parser.print_help() parser.print_help()
return 1 return 1
if args[0].endswith("/"): path = args[0][:-1] if args[0].endswith("/"): path = args[0][:-1]
else: path = args[0] else: path = args[0]
outfile = sys.stdout outfile = sys.stdout
dev.get_file(path, outfile) dev.get_file(path, outfile)
elif command == "rm": elif command == "rm":
parser = OptionParser(usage="usage: %prog rm path\nDelete files from the device\n\npath should point to a file or empty directory on the device "+\ parser = OptionParser(usage="usage: %prog rm path\nDelete files from the device\n\npath should point to a file or empty directory on the device "+\
"and must begin with /,a:/ or b:/\n\n"+\ "and must begin with /,a:/ or b:/\n\n"+\
"rm will DELETE the file. Be very CAREFUL") "rm will DELETE the file. Be very CAREFUL")
options, args = parser.parse_args(args) options, args = parser.parse_args(args)
if len(args) != 1: if len(args) != 1:
parser.print_help() parser.print_help()
return 1
dev.rm(args[0])
elif command == "touch":
parser = OptionParser(usage="usage: %prog touch path\nCreate an empty file on the device\n\npath should point to a file on the device and must begin with /,a:/ or b:/\n\n"+
"Unfortunately, I cant figure out how to update file times on the device, so if path already exists, touch does nothing" )
options, args = parser.parse_args(args)
if len(args) != 1:
parser.print_help()
return 1
dev.touch(args[0])
else:
parser.print_help()
if dev.handle: dev.close()
return 1
except (ArgumentError, DeviceError), e:
print >>sys.stderr, e
return 1 return 1
dev.rm(args[0]) return 0
elif command == "touch":
parser = OptionParser(usage="usage: %prog touch path\nCreate an empty file on the device\n\npath should point to a file on the device and must begin with /,a:/ or b:/\n\n"+
"Unfortunately, I cant figure out how to update file times on the device, so if path already exists, touch does nothing" )
options, args = parser.parse_args(args)
if len(args) != 1:
parser.print_help()
return 1
dev.touch(args[0])
else:
parser.print_help()
if dev.handle: dev.close()
return 1
except (ArgumentError, DeviceError), e:
print >>sys.stderr, e
return 1
return 0

View File

@ -25,16 +25,16 @@ class TerminalController:
values are initialized to the control sequence necessary to values are initialized to the control sequence necessary to
perform a given action. These can be simply included in normal perform a given action. These can be simply included in normal
output to the terminal: output to the terminal:
>>> term = TerminalController() >>> term = TerminalController()
>>> print 'This is '+term.GREEN+'green'+term.NORMAL >>> print 'This is '+term.GREEN+'green'+term.NORMAL
Alternatively, the `render()` method can used, which replaces Alternatively, the `render()` method can used, which replaces
'${action}' with the string required to perform 'action': '${action}' with the string required to perform 'action':
>>> term = TerminalController() >>> term = TerminalController()
>>> print term.render('This is ${GREEN}green${NORMAL}') >>> print term.render('This is ${GREEN}green${NORMAL}')
If the terminal doesn't support a given action, then the value of If the terminal doesn't support a given action, then the value of
the corresponding instance variable will be set to ''. As a the corresponding instance variable will be set to ''. As a
result, the above code will still work on terminals that do not result, the above code will still work on terminals that do not
@ -42,11 +42,11 @@ class TerminalController:
Also, this means that you can test whether the terminal supports a Also, this means that you can test whether the terminal supports a
given action by simply testing the truth value of the given action by simply testing the truth value of the
corresponding instance variable: corresponding instance variable:
>>> term = TerminalController() >>> term = TerminalController()
>>> if term.CLEAR_SCREEN: >>> if term.CLEAR_SCREEN:
... print 'This terminal supports clearning the screen.' ... print 'This terminal supports clearning the screen.'
Finally, if the width and height of the terminal are known, then Finally, if the width and height of the terminal are known, then
they will be stored in the `COLS` and `LINES` attributes. they will be stored in the `COLS` and `LINES` attributes.
""" """
@ -56,28 +56,28 @@ class TerminalController:
DOWN = '' #: Move the cursor down one line DOWN = '' #: Move the cursor down one line
LEFT = '' #: Move the cursor left one char LEFT = '' #: Move the cursor left one char
RIGHT = '' #: Move the cursor right one char RIGHT = '' #: Move the cursor right one char
# Deletion: # Deletion:
CLEAR_SCREEN = '' #: Clear the screen and move to home position CLEAR_SCREEN = '' #: Clear the screen and move to home position
CLEAR_EOL = '' #: Clear to the end of the line. CLEAR_EOL = '' #: Clear to the end of the line.
CLEAR_BOL = '' #: Clear to the beginning of the line. CLEAR_BOL = '' #: Clear to the beginning of the line.
CLEAR_EOS = '' #: Clear to the end of the screen CLEAR_EOS = '' #: Clear to the end of the screen
# Output modes: # Output modes:
BOLD = '' #: Turn on bold mode BOLD = '' #: Turn on bold mode
BLINK = '' #: Turn on blink mode BLINK = '' #: Turn on blink mode
DIM = '' #: Turn on half-bright mode DIM = '' #: Turn on half-bright mode
REVERSE = '' #: Turn on reverse-video mode REVERSE = '' #: Turn on reverse-video mode
NORMAL = '' #: Turn off all modes NORMAL = '' #: Turn off all modes
# Cursor display: # Cursor display:
HIDE_CURSOR = '' #: Make the cursor invisible HIDE_CURSOR = '' #: Make the cursor invisible
SHOW_CURSOR = '' #: Make the cursor visible SHOW_CURSOR = '' #: Make the cursor visible
# Terminal size: # Terminal size:
COLS = None #: Width of the terminal (None for unknown) COLS = None #: Width of the terminal (None for unknown)
LINES = None #: Height of the terminal (None for unknown) LINES = None #: Height of the terminal (None for unknown)
# Foreground colors: # Foreground colors:
BLACK = BLUE = GREEN = CYAN = RED = MAGENTA = YELLOW = WHITE = '' BLACK = BLUE = GREEN = CYAN = RED = MAGENTA = YELLOW = WHITE = ''
@ -92,7 +92,7 @@ class TerminalController:
HIDE_CURSOR=cinvis SHOW_CURSOR=cnorm""".split() HIDE_CURSOR=cinvis SHOW_CURSOR=cnorm""".split()
_COLORS = """BLACK BLUE GREEN CYAN RED MAGENTA YELLOW WHITE""".split() _COLORS = """BLACK BLUE GREEN CYAN RED MAGENTA YELLOW WHITE""".split()
_ANSICOLORS = "BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE".split() _ANSICOLORS = "BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE".split()
def __init__(self, term_stream=sys.stdout): def __init__(self, term_stream=sys.stdout):
""" """
Create a `TerminalController` and initialize its attributes Create a `TerminalController` and initialize its attributes
@ -104,15 +104,15 @@ class TerminalController:
# Curses isn't available on all platforms # Curses isn't available on all platforms
try: import curses try: import curses
except: return except: return
# If the stream isn't a tty, then assume it has no capabilities. # If the stream isn't a tty, then assume it has no capabilities.
if not term_stream.isatty(): return if not term_stream.isatty(): return
# Check the terminal type. If we fail, then assume that the # Check the terminal type. If we fail, then assume that the
# terminal has no capabilities. # terminal has no capabilities.
try: curses.setupterm() try: curses.setupterm()
except: return except: return
# Look up numeric capabilities. # Look up numeric capabilities.
self.COLS = curses.tigetnum('cols') self.COLS = curses.tigetnum('cols')
self.LINES = curses.tigetnum('lines') self.LINES = curses.tigetnum('lines')
@ -121,7 +121,7 @@ class TerminalController:
for capability in self._STRING_CAPABILITIES: for capability in self._STRING_CAPABILITIES:
(attrib, cap_name) = capability.split('=') (attrib, cap_name) = capability.split('=')
setattr(self, attrib, self._tigetstr(cap_name) or '') setattr(self, attrib, self._tigetstr(cap_name) or '')
# Colors # Colors
set_fg = self._tigetstr('setf') set_fg = self._tigetstr('setf')
if set_fg: if set_fg:
@ -139,7 +139,7 @@ class TerminalController:
if set_bg_ansi: if set_bg_ansi:
for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS): for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS):
setattr(self, 'BG_'+color, curses.tparm(set_bg_ansi, i) or '') setattr(self, 'BG_'+color, curses.tparm(set_bg_ansi, i) or '')
def _tigetstr(self, cap_name): def _tigetstr(self, cap_name):
# String capabilities can include "delays" of the form "$<2>". # String capabilities can include "delays" of the form "$<2>".
# For any modern terminal, we should be able to just ignore # For any modern terminal, we should be able to just ignore
@ -147,7 +147,7 @@ class TerminalController:
import curses import curses
cap = curses.tigetstr(cap_name) or '' cap = curses.tigetstr(cap_name) or ''
return re.sub(r'\$<\d+>[/*]?', '', cap) return re.sub(r'\$<\d+>[/*]?', '', cap)
def render(self, template): def render(self, template):
""" """
Replace each $-substitutions in the given template string with Replace each $-substitutions in the given template string with
@ -155,7 +155,7 @@ class TerminalController:
'' (if it's not). '' (if it's not).
""" """
return re.sub(r'\$\$|\${\w+}', self._render_sub, template) return re.sub(r'\$\$|\${\w+}', self._render_sub, template)
def _render_sub(self, match): def _render_sub(self, match):
s = match.group() s = match.group()
if s == '$$': return s if s == '$$': return s
@ -169,40 +169,40 @@ class ProgressBar:
""" """
A 3-line progress bar, which looks like:: A 3-line progress bar, which looks like::
Header Header
20% [===========----------------------------------] 20% [===========----------------------------------]
progress message progress message
The progress bar is colored, if the terminal supports color The progress bar is colored, if the terminal supports color
output; and adjusts to the width of the terminal. output; and adjusts to the width of the terminal.
""" """
BAR = '%3d%% ${GREEN}[${BOLD}%s%s${NORMAL}${GREEN}]${NORMAL}\n' BAR = '%3d%% ${GREEN}[${BOLD}%s%s${NORMAL}${GREEN}]${NORMAL}\n'
HEADER = '${BOLD}${CYAN}%s${NORMAL}\n\n' HEADER = '${BOLD}${CYAN}%s${NORMAL}\n\n'
def __init__(self, term, header): def __init__(self, term, header):
self.term = term self.term = term
if not (self.term.CLEAR_EOL and self.term.UP and self.term.BOL): if not (self.term.CLEAR_EOL and self.term.UP and self.term.BOL):
raise ValueError("Terminal isn't capable enough -- you " raise ValueError("Terminal isn't capable enough -- you "
"should use a simpler progress dispaly.") "should use a simpler progress dispaly.")
self.width = self.term.COLS or 75 self.width = self.term.COLS or 75
self.bar = term.render(self.BAR) self.bar = term.render(self.BAR)
self.header = self.term.render(self.HEADER % header.center(self.width)) self.header = self.term.render(self.HEADER % header.center(self.width))
self.cleared = 1 #: true if we haven't drawn the bar yet. self.cleared = 1 #: true if we haven't drawn the bar yet.
self.update(0, '') self.update(0, '')
def update(self, percent, message): def update(self, percent, message):
if self.cleared: if self.cleared:
sys.stdout.write(self.header) sys.stdout.write(self.header)
self.cleared = 0 self.cleared = 0
n = int((self.width-10)*percent) n = int((self.width-10)*percent)
sys.stdout.write( sys.stdout.write(
self.term.BOL + self.term.UP + self.term.CLEAR_EOL + self.term.BOL + self.term.UP + self.term.CLEAR_EOL +
(self.bar % (100*percent, '='*n, '-'*(self.width-10-n))) + (self.bar % (100*percent, '='*n, '-'*(self.width-10-n))) +
self.term.CLEAR_EOL + message.center(self.width)) self.term.CLEAR_EOL + message.center(self.width))
def clear(self): def clear(self):
if not self.cleared: if not self.cleared:
sys.stdout.write(self.term.BOL + self.term.CLEAR_EOL + sys.stdout.write(self.term.BOL + self.term.CLEAR_EOL +
self.term.UP + self.term.CLEAR_EOL + self.term.UP + self.term.CLEAR_EOL +
self.term.UP + self.term.CLEAR_EOL) self.term.UP + self.term.CLEAR_EOL)
self.cleared = 1 self.cleared = 1