On 02/11/2012 08:27 AM, Adam Spiers wrote:
> However, if I have (say) 70 repositories and only two of them
> have work in progress, `mr wip' outputs about 150 lines, of
> which only 10 lines are useful. [...] Is there any existing
> way I can make it only output the repositories for which the
> git_wip action generates some output?

I've had a similar problem with several of the ``mr`` operations
(``mr up``, ``mr st``, ...).  I've squelched uninteresting input
from long build output (for example) by using the attached
Python script.  I hunted around for something pre-built to do
this job, but I couldn't find anything.  I'd be interested in
pointers to similar tools, if anyone knows of any.

The basic idea is that output from "verbose" tools can be
categorized into interesting lines and mere boilerplate "status"
lines (which are useful only for keeping track of progress or
marking context when an interesting line comes around).

The ``pytee`` utility (attached) works like the standard Unix
``tee`` utility, with the additional ability to detect "status"
lines via user-supplied regular expressions.  For a series of
consecutive status lines that go to the console, each successive
line of status overwrites the previous one "in-place", giving
the impression of a static progress display.  But when a line of
interesting text comes by (that is, a line that doesn't match
the supplied regular expressions), the previous status line is
displayed along with the interesting line.

I alias ``mr`` to the following script, which I call
``mrwrap``::

    #!/bin/bash

    mr "$@" 2>&1 | pytee \
        --regex '^mr (status|update|push): /' \
        --regex '^(Everything|Already) up-to-date' \
        --regex '^\s*$'

That way, interesting output from invocations like ``mr st``
will not be carried away by boilerplate text.

I like that I can wrap existing programs without having to
modify them to make them suitably quiet.  I confess I'd like to
see ``mr`` have some kind of feature like this built-in, but I'm
satisfied enough with my work-around that I haven't mentioned it
until now.

Michael Henry

#!/usr/bin/env python

__VERSION__ = "0.5.3"

usage = """usage: %prog [options] [FILE]+"""

description = """\
Provides enhanced "tee" function.

- Input is always written unmodified to specified FILEs.
- Input (possibly modified) will be written to standard out.
- Input lines matching the supplied REGEX will be suppressed:
  - For N consecutive matching lines before a non-matching line, the first N-1
    lines will be suppressed, and the last will be kept for context.
  - Suppressed lines are overwritten in-place with subsequent lines (or, with
    --strip, they are suppressed entirely).
- Multiple --regex options will be joined with the logical "OR" operator, "|".
"""
import sys
import re
import os
import logging
from optparse import OptionParser, IndentedHelpFormatter

class Formatter(IndentedHelpFormatter):
    def format_description(self, description):
        if description:
            return description
        else:
            return ""

logger = logging.getLogger()
logger.addHandler(logging.StreamHandler(sys.stdout))

# Holds contents of most recent "status" line.
lastStatusLine = ""

def writeOut(s):
    sys.stdout.write(s)
    sys.stdout.flush()

def putLine(line, regex=""):
    global lastStatusLine
    if regex and re.search(regex, line):
        # This is just for status.
        line = line.rstrip()
        if not options.strip:
            width = max(len(line), len(lastStatusLine))
            writeOut(line.ljust(width) + "\r")
        lastStatusLine = line
    else:
        if lastStatusLine:
            if options.strip:
                writeOut(lastStatusLine)
            writeOut("\n")
        writeOut(line)
        lastStatusLine = ""

def tee(regex="", outNames=[], append=False):
    if append:
        mode = "a"
    else:
        mode = "w"
    outFiles = [open(f, mode) for f in outNames]
    while 1:
        line = sys.stdin.readline()
        if line:
            putLine(line, regex)
            for f in outFiles:
                f.write(line)
                f.flush()
        else:
            break
    if lastStatusLine and not options.strip:
        writeOut(" " * len(lastStatusLine) + "\r")
        #writeOut("\n")
    for f in outFiles:
        f.close()

def main():
    MAKE_REGEX = (
            r"^\s+\[.+\]|"
            r"^make\[\d+\]: (`|Entering |Leaving |Nothing )|"
            r"^make -r |"
            r"^\w+ finished$|"
            r"^(\S*/)?\bgcc\s"
            )

    parser = OptionParser(version="%prog" + __VERSION__,
            formatter=Formatter())
    parser.set_usage(usage)
    parser.set_description(description)
    parser.add_option("-a", "--append", action="store_true", dest="append",
            help="append to given files, do not overwrite")
    parser.add_option("-v", "--verbose", action="store_const", dest="verbose",
            const=logging.DEBUG,
            help="verbose output for debugging")
    parser.add_option("-q", "--quiet", action="store_const", dest="verbose",
            const=logging.WARNING,
            help="suppress informational output")
    parser.add_option("--regex", action="append", dest="regexList",
            default=[],
            metavar="REGEX",
            help="append a 'status' regular expression (e.g., " +
                "'^#.*' for comment lines)")
    parser.add_option("--make", action="append_const", dest="regexList",
            const=MAKE_REGEX,
            help="short for --regex '" + MAKE_REGEX +
                "'; useful for the output of a " +
                "`make` invocation")
    parser.add_option("--strip", action="store_true", dest="strip",
            default=False,
            help="strip out informational output that doesn't " +
                "immediately precede non-informational output, rather " +
                "than display and overwrite it in place")

    global options
    (options, args) = parser.parse_args()

    if options.verbose is None:
        logger.setLevel(logging.INFO)
    else:
        logger.setLevel(options.verbose)

    logger.debug('Finished option processing, options=%r' % options)

    logger.debug('arguments:')

    for arg in args:
        logger.debug('arg: %s' % arg)

    regex = r'|'.join(options.regexList)
    tee(regex, args, append=options.append)

    logger.debug('Finished.')

if __name__ == '__main__':
    main()

Enhanced 'tee' with in-place overwriting.
#!/usr/bin/env python

from setuptools import setup, find_packages

NAME="pytee"

for line in file(NAME + ".py"):
    if line.startswith("__VERSION__"):
        exec line in globals()
        break

setup(
        name=NAME,
        version=__VERSION__,
        packages=find_packages(),
        py_modules = [NAME],
        install_requires = [
            #"examplelib",
            ],
        entry_points={
            'console_scripts': [
                'pytee = pytee:main',
                ],
            },
        description="Enhanced 'tee' with in-place overwriting of 'status'",
        keywords="tee in-place overwriting",
        url="projects/pytee",
        author="Michael Henry",
        author_email="vcs-h...@drmikehenry.com",
        zip_safe=True,
        )
_______________________________________________
vcs-home mailing list
vcs-home@lists.madduck.net
http://lists.madduck.net/listinfo/vcs-home

Reply via email to