diff options
Diffstat (limited to 'elogv')
-rwxr-xr-x | elogv | 291 |
1 files changed, 168 insertions, 123 deletions
@@ -20,48 +20,57 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -import os -import sys -import re -from datetime import datetime +import bz2 import curses import curses.ascii -import textwrap -from portage import settings as port_settings -from glob import glob import gettext -import locale import gzip -import bz2 +import locale import lzma +import os +import re import signal +import sys +import textwrap +from datetime import datetime +from glob import glob + +from portage import settings as port_settings _LOCALE_CATEGORY_PAIRS = ( - (locale.LC_COLLATE, 'LC_COLLATE'), - (locale.LC_CTYPE, 'LC_CTYPE'), - (locale.LC_MESSAGES, 'LC_MESSAGES'), - (locale.LC_MONETARY, 'LC_MONETARY'), - (locale.LC_NUMERIC, 'LC_NUMERIC'), - (locale.LC_TIME, 'LC_TIME'), - - (locale.LC_ALL, 'LC_ALL'), + (locale.LC_COLLATE, "LC_COLLATE"), + (locale.LC_CTYPE, "LC_CTYPE"), + (locale.LC_MESSAGES, "LC_MESSAGES"), + (locale.LC_MONETARY, "LC_MONETARY"), + (locale.LC_NUMERIC, "LC_NUMERIC"), + (locale.LC_TIME, "LC_TIME"), + (locale.LC_ALL, "LC_ALL"), ) def report_bad_locale(variable, value): - py_version = '%s.%s.%s' % sys.version_info[:3] + py_version = "%s.%s.%s" % sys.version_info[:3] print('ERROR: Locale "%s" does not seem to be supported.' % value, file=sys.stderr) if value not in locale.locale_alias: - print(' Note: Locale "%s" is not a known alias to Python %s (check locale.locale_alias).' % (value, py_version), file=sys.stderr) - if not ('.' in value or '@' in value): - print(' Hint: Try specifying the encoding (e.g. %s=%s.UTF-8).' % (variable, value), file=sys.stderr) + print( + ' Note: Locale "%s" is not a known alias to Python %s (check locale.locale_alias).' + % (value, py_version), + file=sys.stderr, + ) + if not ("." in value or "@" in value): + print( + " Hint: Try specifying the encoding (e.g. %s=%s.UTF-8)." + % (variable, value), + file=sys.stderr, + ) + reported_bad_locales = set() # Enable support for user locale try: - locale.setlocale(locale.LC_ALL, '') + locale.setlocale(locale.LC_ALL, "") except locale.Error: # Find guilty value and variable for category, variable in _LOCALE_CATEGORY_PAIRS: @@ -83,7 +92,7 @@ for category, variable in _LOCALE_CATEGORY_PAIRS: try: locale.getlocale(category) - except ValueError as e: + except ValueError: value = os.environ[variable] if value in reported_bad_locales: continue @@ -95,11 +104,12 @@ if reported_bad_locales: # Setup gettext. -gettext.textdomain('elogv') +gettext.textdomain("elogv") _ = gettext.gettext # This text is used on the in-line help -helptext = _(""" +helptext = _( + """ Elogv is a portage elog viewer based on curses and python, you can use these keys to control the behavior of the program: @@ -127,7 +137,8 @@ you can use these keys to control the behavior of the program: - / -> starts a search prompt, write a string and will be showed the next package that contains your string, use ESC to exit - q -> quit -""") +""" +) (normal, selected, einfo, elog, ewarn, eerror) = range(6) (PATH, CAT, PN, DATE, CLASS) = range(5) @@ -137,6 +148,7 @@ class_char = "*" list_format = "%s/%s - %s" date_format = "%x" + # Exceptions classes class TermTooSmall(Exception): """Terminal too small.""" @@ -165,11 +177,11 @@ def date2str(d): b = d.strftime(date_format) for encoding in ( - locale.getlocale(locale.LC_TIME)[1], - locale.getlocale()[1], - sys.getdefaultencoding(), - 'utf-8', - ): + locale.getlocale(locale.LC_TIME)[1], + locale.getlocale()[1], + sys.getdefaultencoding(), + "utf-8", + ): if encoding is None: continue @@ -182,24 +194,25 @@ def date2str(d): except UnicodeDecodeError: pass else: - raise ValueError('Cannot decode byte stream') + raise ValueError("Cannot decode byte stream") try: if not isinstance(b, str): - b = u.encode('ascii') + b = u.encode("ascii") else: b = u except UnicodeEncodeError: # Prevent crash locales like ja_JP.UTF-8, e.g. "2014年10月24日" # https://bugs.gentoo.org/show_bug.cgi?id=464962 - b = u.encode('ascii', errors='replace') + b = u.encode("ascii", errors="replace") return b + # Main class (called with curses.wrapper later) class ElogViewer: def __init__(self, screen): - #curses.curs_set(0) + # curses.curs_set(0) self.screen = screen # Our color pairs @@ -214,20 +227,20 @@ class ElogViewer: pass # This attributes are used to manage the scrolling of the list - # of files + # of files self.pposy = 0 self.usel = 0 # Method used to order the list: use DATE for order by date, CAT to # order by category name, PN to order by package name, CLASS to order # by class second value set if the list should be reversed (True o False) - self.sort_method = [ DATE, False] + self.sort_method = [DATE, False] # Initialize screen self.init_screen() c = self.screen.getch() - while c not in ( ord("q"), curses.ascii.ESC): + while c not in (ord("q"), curses.ascii.ESC): ## Scrolling keys ## if c in (curses.KEY_DOWN, ord("j")): self.change_usel(1) @@ -237,12 +250,12 @@ class ElogViewer: elif c in (curses.KEY_NPAGE, ord("f")): self.change_usel(10) - + elif c in (curses.KEY_PPAGE, ord("b")): self.change_usel(-10) elif c in (curses.KEY_END, ord("G")): - self.change_usel(len(self.packages)-1, False) + self.change_usel(len(self.packages) - 1, False) elif c in (curses.KEY_HOME, ord("g")): self.change_usel(0, False) @@ -317,12 +330,12 @@ class ElogViewer: elif c == ord("/"): word = "" - self.screen.move(self.height-1,2) + self.screen.move(self.height - 1, 2) self.screen.addstr("/") subc = self.screen.getch() while subc != curses.ascii.ESC: if subc == ord("\n"): - self.search(word,1) + self.search(word, 1) elif subc == curses.ascii.BS: word = word[:-1] self.screen.delch() @@ -330,10 +343,10 @@ class ElogViewer: elif curses.ascii.isalpha(subc): word += chr(subc) self.screen.addstr(chr(subc)) - self.search(word) + self.search(word) subc = self.screen.getch() - self.screen.hline(self.height-1,2,curses.ACS_HLINE,(len(word)+1)) - self.screen.addstr(self.height-2,2," "*20) + self.screen.hline(self.height - 1, 2, curses.ACS_HLINE, (len(word) + 1)) + self.screen.addstr(self.height - 2, 2, " " * 20) # Get another key from the user c = self.screen.getch() @@ -351,14 +364,15 @@ class ElogViewer: # Screen Look&Feel self.screen.border() - self.screen.hline(self.height//2,1, "_", self.width-2) + self.screen.hline(self.height // 2, 1, "_", self.width - 2) m = _(" Press F1 or h to show the help screen ") - self.screen.addstr(self.height-1, self.width-len(m)-1, m) + self.screen.addstr(self.height - 1, self.width - len(m) - 1, m) self.screen.refresh() # Initialize log file window - self.log_win = curses.newwin(self.height//2-2, self.width-2, - self.height//2+1, 1) + self.log_win = curses.newwin( + self.height // 2 - 2, self.width - 2, self.height // 2 + 1, 1 + ) # Draw other window of the screen self.fill_file_pad() @@ -367,31 +381,37 @@ class ElogViewer: self.logf_wrap = self.wrap_logf_lines() self.show_log() - def change_usel(self,n,relative=True): + def change_usel(self, n, relative=True): prev_usel = self.usel if not relative: self.usel = n - elif n < 0 and self.usel+n < 0: + elif n < 0 and self.usel + n < 0: self.usel = 0 - elif n > 0 and self.usel+n > len(self.packages)-1: - self.usel = len(self.packages)-1 + elif n > 0 and self.usel + n > len(self.packages) - 1: + self.usel = len(self.packages) - 1 else: self.usel += n prev_pkg = self.packages[prev_usel] pkg = self.packages[self.usel] - self.file_pad.addstr(prev_usel,1, - class_char, - curses.A_BOLD + curses.color_pair(prev_pkg[CLASS])) - self.file_pad.addstr(prev_usel,3, - list_format % (prev_pkg[CAT], prev_pkg[PN], date2str(prev_pkg[DATE]) ), - curses.color_pair(normal)) - self.file_pad.addstr(self.usel,1, - class_char, - curses.A_BOLD + curses.color_pair(pkg[CLASS])) - self.file_pad.addstr(self.usel,3, - list_format % (pkg[CAT], pkg[PN], date2str(pkg[DATE]) ), - curses.color_pair(selected)) + self.file_pad.addstr( + prev_usel, 1, class_char, curses.A_BOLD + curses.color_pair(prev_pkg[CLASS]) + ) + self.file_pad.addstr( + prev_usel, + 3, + list_format % (prev_pkg[CAT], prev_pkg[PN], date2str(prev_pkg[DATE])), + curses.color_pair(normal), + ) + self.file_pad.addstr( + self.usel, 1, class_char, curses.A_BOLD + curses.color_pair(pkg[CLASS]) + ) + self.file_pad.addstr( + self.usel, + 3, + list_format % (pkg[CAT], pkg[PN], date2str(pkg[DATE])), + curses.color_pair(selected), + ) first = self.pposy last = (self.height // 2 - 2) + first @@ -414,12 +434,12 @@ class ElogViewer: self.show_log() @staticmethod - def open(file, mode='rt'): - if file.endswith('.xz'): + def open(file, mode="rt"): + if file.endswith(".xz"): return lzma.open(file, mode=mode) - elif file.endswith('.gz'): + elif file.endswith(".gz"): return gzip.open(file, mode=mode) - elif file.endswith('.bz2'): + elif file.endswith(".bz2"): return bz2.open(file, mode=mode) else: return open(file, mode=mode) @@ -432,7 +452,7 @@ class ElogViewer: Redraws file pad, first half of the screen. Can be used to scroll or simply update """ - self.file_pad.refresh(self.pposy,0,1,1,self.height//2-1,self.width-2) + self.file_pad.refresh(self.pposy, 0, 1, 1, self.height // 2 - 1, self.width - 2) def get_packages_key(self, k): return k[self.sort_method[0]] @@ -444,7 +464,9 @@ class ElogViewer: """ # Get the list of files try: - file_list = glob(os.path.join(elogdir,"*:*:*.log*")) + glob(os.path.join(elogdir,"*","*:*.log*")) + file_list = glob(os.path.join(elogdir, "*:*:*.log*")) + glob( + os.path.join(elogdir, "*", "*:*.log*") + ) except OSError: raise CannotOpenElogdir() @@ -465,13 +487,15 @@ class ElogViewer: # then we split the string using as pattern / or : to obtain in any # case # ( "x11-themes", "haematite-xcursors", "1.0:20091018-195827.log") - tmpfilepath = re.sub('\\.log[^/]*$', '.log', filepath) - split_up = re.split(":|" + os.path.sep, tmpfilepath.replace(elogdir + os.path.sep, "")) + tmpfilepath = re.sub("\\.log[^/]*$", ".log", filepath) + split_up = re.split( + ":|" + os.path.sep, tmpfilepath.replace(elogdir + os.path.sep, "") + ) if len(split_up) < 3: continue (cat, pn, other) = split_up[-3:] date = datetime.strptime(other, "%Y%m%d-%H%M%S.log") - self.packages.append( (filepath, cat, pn, date, self.get_class(filepath)) ) + self.packages.append((filepath, cat, pn, date, self.get_class(filepath))) if not self.packages: raise NoLogFiles() @@ -479,24 +503,26 @@ class ElogViewer: # Maybe that after removing files self.usel points to a wrong index, # so this will prevent a crash if self.usel >= len(self.packages): - self.usel = len(self.packages)-1 + self.usel = len(self.packages) - 1 # We also have to update self.pposy if self.pposy > self.usel: - self.pposy = max(0, self.usel-10) + self.pposy = max(0, self.usel - 10) # Sort the list if self.sort_method[0] in (DATE, CLASS): - self.packages.sort(key=self.get_packages_key,reverse=not self.sort_method[1]) + self.packages.sort( + key=self.get_packages_key, reverse=not self.sort_method[1] + ) else: self.packages.sort(key=self.get_packages_key, reverse=self.sort_method[1]) # Curses' newpad fails for nlines >32767 so we truncate the list # of log files to not exceed that limit (issue #10) - MAX_ENTRIES = 2 ** 15 - 1 + MAX_ENTRIES = 2**15 - 1 if len(self.packages) > MAX_ENTRIES: self.packages = self.packages[:MAX_ENTRIES] - self.file_pad = curses.newpad(len(self.packages),self.width) + self.file_pad = curses.newpad(len(self.packages), self.width) self.file_pad.erase() for i, pkg in enumerate(self.packages): @@ -513,14 +539,17 @@ class ElogViewer: else: cp = normal - self.file_pad.addstr(i,1, - class_char, - curses.A_BOLD + curses.color_pair(pkg[CLASS])) - self.file_pad.addstr(i,3, - list_format % (pkg[CAT], pkg[PN], date2str(pkg[DATE]) ), - curses.color_pair(cp)) - - def get_class(self,filepath): + self.file_pad.addstr( + i, 1, class_char, curses.A_BOLD + curses.color_pair(pkg[CLASS]) + ) + self.file_pad.addstr( + i, + 3, + list_format % (pkg[CAT], pkg[PN], date2str(pkg[DATE])), + curses.color_pair(cp), + ) + + def get_class(self, filepath): """ Get the highest elog class in a file """ @@ -540,7 +569,6 @@ class ElogViewer: """ Takes a file-like object and wraps long lines. Returns a list iterator. """ - result = [] self.logf.seek(0) for line in self.logf.readlines(): if not line.strip(): @@ -548,9 +576,9 @@ class ElogViewer: yield "\n" else: # Returns a list of new lines minus the line ending \n - wrapped_line = textwrap.wrap(line, width=self.width-2) - for l in wrapped_line: - yield l + "\n" + wrapped_lines = textwrap.wrap(line, width=self.width - 2) + for output_line in wrapped_lines: + yield output_line + "\n" def show_log(self): """ @@ -561,7 +589,7 @@ class ElogViewer: self.log_win.erase() shown_all = False - for i in range(0,self.height//2-4): + for i in range(0, self.height // 2 - 4): try: x = next(self.logf_wrap) except StopIteration: @@ -571,27 +599,31 @@ class ElogViewer: break try: - if x.startswith('INFO:'): - self.log_win.addstr(x[:self.width-2],curses.color_pair(einfo)) - elif x.startswith('WARN:'): - self.log_win.addstr(x[:self.width-2],curses.color_pair(ewarn)) - elif x.startswith('ERROR:'): - self.log_win.addstr(x[:self.width-2],curses.color_pair(eerror)) - elif x.startswith('LOG:'): - self.log_win.addstr(x[:self.width-2],curses.color_pair(elog)) + if x.startswith("INFO:"): + self.log_win.addstr(x[: self.width - 2], curses.color_pair(einfo)) + elif x.startswith("WARN:"): + self.log_win.addstr(x[: self.width - 2], curses.color_pair(ewarn)) + elif x.startswith("ERROR:"): + self.log_win.addstr(x[: self.width - 2], curses.color_pair(eerror)) + elif x.startswith("LOG:"): + self.log_win.addstr(x[: self.width - 2], curses.color_pair(elog)) else: - self.log_win.addstr(x[:self.width-2],curses.color_pair(normal)) + self.log_win.addstr(x[: self.width - 2], curses.color_pair(normal)) except curses.error: pass - if not shown_all: + if not shown_all: s = _("Continue...") - self.log_win.addstr(self.height//2-3, self.width-len(s)-4, s, - curses.color_pair(normal)) + self.log_win.addstr( + self.height // 2 - 3, + self.width - len(s) - 4, + s, + curses.color_pair(normal), + ) self.log_win.refresh() - def remove_file(self,n): + def remove_file(self, n): """ Delete from the filesystem a slice of elog files n can be: @@ -614,24 +646,25 @@ class ElogViewer: end = self.usel + int(n) for item in self.packages[start:end]: - os.remove(os.path.join(elogdir,item[0])) + os.remove(os.path.join(elogdir, item[0])) def show_help(self): # Setup help window helpwin_height = self.height // 3 * 2 helpwin_corner = (self.height // 6, self.width // 2 - 40) - helpwin = curses.newwin(helpwin_height, 80, - helpwin_corner[0], helpwin_corner[1]) + helpwin = curses.newwin( + helpwin_height, 80, helpwin_corner[0], helpwin_corner[1] + ) helplines = helptext.splitlines() # Setup help pad row = 0 maxrow = len(helplines) - helppad = curses.newpad(maxrow,80) + helppad = curses.newpad(maxrow, 80) # Insert helptext on the pad for i in range(maxrow): - helppad.addstr(i,0,helplines[i]) + helppad.addstr(i, 0, helplines[i]) # Loop to manage user actions c = None @@ -640,31 +673,39 @@ class ElogViewer: helpwin.border() helpwin.refresh() - helppad.refresh(row,0,helpwin_corner[0]+1,helpwin_corner[1]+1, - helpwin_height+helpwin_corner[0]-2,80+helpwin_corner[1]-2) + helppad.refresh( + row, + 0, + helpwin_corner[0] + 1, + helpwin_corner[1] + 1, + helpwin_height + helpwin_corner[0] - 2, + 80 + helpwin_corner[1] - 2, + ) c = self.screen.getch() if c == curses.KEY_NPAGE: - if row+10 <= maxrow: + if row + 10 <= maxrow: row += 10 elif c == curses.KEY_PPAGE: - if row-10 >= 0: + if row - 10 >= 0: row -= 10 elif c in (curses.KEY_DOWN, ord("j")): - if row+1 < maxrow: + if row + 1 < maxrow: row += 1 elif c in (curses.KEY_UP, ord("k")): if row > 0: row -= 1 def search(self, word, div=0): - for x in self.packages[self.usel+div:]: + for x in self.packages[self.usel + div :]: if re.search(word, "%s/%s" % x[1:3]): - self.change_usel(self.packages.index(x),False) + self.change_usel(self.packages.index(x), False) break else: - self.screen.addstr(self.height-2,2,_("Not Found!"), - curses.color_pair(eerror)) + self.screen.addstr( + self.height - 2, 2, _("Not Found!"), curses.color_pair(eerror) + ) + if __name__ == "__main__": if "--help" in sys.argv: @@ -672,10 +713,12 @@ if __name__ == "__main__": sys.exit() # Get the path of the elogdir - if port_settings['PORT_LOGDIR']: - elogdir = os.path.join(port_settings['PORT_LOGDIR'],"elog") + if port_settings["PORT_LOGDIR"]: + elogdir = os.path.join(port_settings["PORT_LOGDIR"], "elog") else: - elogdir = os.path.join(os.sep,port_settings['EPREFIX'],"var","log","portage","elog") + elogdir = os.path.join( + os.sep, port_settings["EPREFIX"], "var", "log", "portage", "elog" + ) signal.signal(signal.SIGTSTP, handle_sig_tstp) signal.signal(signal.SIGCONT, handle_sig_cont) @@ -691,7 +734,9 @@ if __name__ == "__main__": sys.exit(1) except CannotOpenElogdir: print(_("Cannot open"), elogdir) - print(_("Please check if the directory exists and if it's readable by your user.")) + print( + _("Please check if the directory exists and if it's readable by your user.") + ) sys.exit(1) except KeyboardInterrupt: pass |