Skip to content

Commit

Permalink
Change ansi2html' to coloredlogs --convert' (fixes #8)
Browse files Browse the repository at this point in the history
See issue 8 on GitHub (request to change the name):
  #8
  • Loading branch information
xolox committed Oct 14, 2015
1 parent 949af3e commit eaef420
Show file tree
Hide file tree
Showing 9 changed files with 180 additions and 48 deletions.
5 changes: 4 additions & 1 deletion .coveragerc
@@ -1,3 +1,6 @@
[run]
source = coloredlogs
omit = coloredlogs/demo.py, coloredlogs/tests.py
omit = coloredlogs/tests.py

[report]
exclude_lines = assert False
22 changes: 12 additions & 10 deletions README.rst
Expand Up @@ -92,19 +92,20 @@ e-mailed to you by cron there's a simple way to set it up::

MAILTO="your-email-address@here"
CONTENT_TYPE="text/html"
* * * * * root ansi2html your-command
* * * * * root coloredlogs --to-html your-command

The ``ansi2html`` program is installed when you install `coloredlogs`. It runs
The ``coloredlogs`` program is installed when you install the `coloredlogs`
package. When you execute ``coloredlogs --to-html your-command`` it runs
``your-command`` under the external program ``script`` (you need to have this
installed to get ``ansi2html`` working). This makes ``your-command`` think that
it's attached to an interactive terminal which means it will output ANSI escape
sequences and ``ansi2html`` converts these to HTML. Yes, this is a bit
convoluted, but it works great :-)
installed). This makes ``your-command`` think that it's attached to an
interactive terminal which means it will output ANSI escape sequences which
will then be converted to HTML by the ``coloredlogs`` program. Yes, this is a
bit convoluted, but it works great :-)

You can use ``ansi2html`` without `coloredlogs`, but please note that it only
supports normal text, bold text and text with one of the foreground colors
black, red, green, yellow, blue, magenta, cyan and white (these are the
portable ANSI color codes).
You can use this feature without using `coloredlogs` in your Python modules,
but please note that only normal text, bold text and text with one of the
foreground colors black, red, green, yellow, blue, magenta, cyan and white
(these are the portable ANSI color codes) are supported.

Contact
-------
Expand All @@ -121,6 +122,7 @@ This software is licensed under the `MIT license`_.

© 2015 Peter Odding.


.. External references:
.. _ANSI escape sequences: http://en.wikipedia.org/wiki/ANSI_escape_code#Colors
.. _cron: https://en.wikipedia.org/wiki/Cron
Expand Down
2 changes: 1 addition & 1 deletion coloredlogs/__init__.py
Expand Up @@ -7,7 +7,7 @@
"""Colored terminal output for Python's :mod:`logging` module."""

# Semi-standard module versioning.
__version__ = '1.0.1'
__version__ = '2.0'

# Standard library modules.
import copy
Expand Down
102 changes: 102 additions & 0 deletions coloredlogs/cli.py
@@ -0,0 +1,102 @@
# Command line interface for the coloredlogs package.
#
# Author: Peter Odding <peter@peterodding.com>
# Last Change: October 14, 2015
# URL: https://coloredlogs.readthedocs.org

"""
Usage: coloredlogs [OPTIONS] [ARGS]
The coloredlogs program provides a simple command line interface for the Python
package by the same name.
Supported options:
-c, --convert, --to-html
Capture the output of an external command (given by the positional
arguments) and convert ANSI escape sequences in the output to HTML.
If the `coloredlogs' program is attached to an interactive terminal it will
write the generated HTML to a temporary file and open that file in a web
browser, otherwise the generated HTML will be written to standard output.
This requires the `script' program to fake the external command into
thinking that it's attached to an interactive terminal (in order to enable
output of ANSI escape sequences).
-d, --demo
Perform a simple demonstration of the coloredlogs package to show the
colored logging on an interactive terminal.
-h, --help
Show this message and exit.
"""

# Standard library modules.
import functools
import getopt
import logging
import sys
import tempfile
import webbrowser

# External dependencies.
from humanfriendly.terminal import connected_to_terminal, usage

# Modules included in our package.
from coloredlogs.converter import capture, convert
from coloredlogs.demo import demonstrate_colored_logging

# Initialize a logger for this module.
logger = logging.getLogger(__name__)


def main():
"""Command line interface for the `coloredlogs` program."""
actions = []
try:
# Parse the command line arguments.
options, arguments = getopt.getopt(sys.argv[1:], 'cdh', [
'convert', 'to-html', 'demo', 'help',
])
# Map command line options to actions.
for option, value in options:
if option in ('-c', '--convert', '--to-html'):
actions.append(functools.partial(convert_command_output, *arguments))
arguments = []
elif option in ('-d', '--demo'):
actions.append(demonstrate_colored_logging)
elif option in ('-h', '--help'):
usage(__doc__)
return
else:
assert False, "Programming error: Unhandled option!"
if not actions:
usage(__doc__)
return
except Exception as e:
sys.stderr.write("Error: %s\n" % e)
sys.exit(1)
for function in actions:
function()


def convert_command_output(*command):
"""
Command line interface for ``coloredlogs --to-html``.
Takes a command (and its arguments) and runs the program under ``script``
(emulating an interactive terminal), intercepts the output of the command
and converts ANSI escape sequences in the output to HTML.
"""
html_output = convert(capture(command))
if connected_to_terminal():
fd, temporary_file = tempfile.mkstemp(suffix='.html')
with open(temporary_file, 'w') as handle:
handle.write(html_output)
webbrowser.open(temporary_file)
else:
print(html_output)
21 changes: 0 additions & 21 deletions coloredlogs/converter.py
Expand Up @@ -10,9 +10,6 @@
import pipes
import re
import subprocess
import sys
import tempfile
import webbrowser

# Portable color codes from http://en.wikipedia.org/wiki/ANSI_escape_code#Colors.
EIGHT_COLOR_PALETTE = (
Expand All @@ -32,24 +29,6 @@
token_pattern = re.compile('(https?://\\S+|www\\.\\S+|\x1b\\[.*?m)', re.UNICODE)


def main():
"""
Command line interface for the ``ansi2html`` program.
Takes a command (and its arguments) and runs the program under ``script``
(emulating an interactive terminal), intercepts the output of the command
and converts ANSI escape sequences in the output to HTML.
"""
html_output = convert(capture(sys.argv[1:]))
if sys.stdout.isatty():
fd, filename = tempfile.mkstemp(suffix='.html')
with open(filename, 'w') as handle:
handle.write(html_output)
webbrowser.open(filename)
else:
print(html_output)


def capture(command, encoding='UTF-8'):
"""
Capture the output of an external program as if it runs in an interactive terminal.
Expand Down
18 changes: 8 additions & 10 deletions coloredlogs/demo.py
Expand Up @@ -15,18 +15,19 @@

# If my verbose logger is installed, we'll use that for the demo.
try:
from verboselogs import VerboseLogger as DemoLogger
from verboselogs import VerboseLogger as getLogger
except ImportError:
from logging import getLogger as DemoLogger
from logging import getLogger

# Initialize the logger and handler.
logger = DemoLogger('coloredlogs')
# Initialize a logger for this module.
logger = getLogger(__name__)


def main():
"""Command line interface for the `coloredlogs` demonstration."""
def demonstrate_colored_logging():
"""A simple demonstration of the `coloredlogs` package."""
# Initialize colored output to the terminal.
coloredlogs.install(level=logging.DEBUG)
coloredlogs.install()
coloredlogs.set_level(logging.DEBUG)
# Print some examples with different timestamps.
for level in ['debug', 'verbose', 'info', 'warn', 'error', 'critical']:
if hasattr(logger, level):
Expand All @@ -40,6 +41,3 @@ class RandomException(Exception):
except Exception as e:
logger.exception(e)
logger.info("Done, exiting ..")

if __name__ == '__main__':
main()
49 changes: 46 additions & 3 deletions coloredlogs/tests.py
Expand Up @@ -11,17 +11,20 @@
import random
import re
import string
import sys
import unittest

# External dependencies.
from humanfriendly.terminal import ansi_wrap

# The module we're testing.
import coloredlogs
import coloredlogs.cli
import coloredlogs.converter

# External test dependency required to test support for custom log levels.
import verboselogs
# External test dependencies.
from capturer import CaptureOutput
from verboselogs import VerboseLogger

# Compatibility with Python 2 and 3.
try:
Expand Down Expand Up @@ -57,7 +60,7 @@ def setUp(self):
self.stream = StringIO()
self.handler = coloredlogs.ColoredStreamHandler(stream=self.stream, isatty=False)
self.logger_name = ''.join(random.choice(string.ascii_letters) for i in range(25))
self.logger = verboselogs.VerboseLogger(self.logger_name)
self.logger = VerboseLogger(self.logger_name)
self.logger.addHandler(self.handler)

def test_is_verbose(self):
Expand Down Expand Up @@ -147,3 +150,43 @@ def test_output_interception(self):
"""Test capturing of output from external commands."""
expected_output = 'testing, 1, 2, 3 ..'
assert coloredlogs.converter.capture(['sh', '-c', 'echo -n %s' % expected_output]) == expected_output

def test_cli_demo(self):
"""Test the command line colored logging demonstration."""
with CaptureOutput() as capturer:
main('coloredlogs', '--demo')
output = capturer.get_text()
# Make sure the output contains all of the expected logging level names.
for name in 'debug', 'info', 'warning', 'error', 'critical':
assert name.upper() in output

def test_cli_conversion(self):
"""Test the command line HTML conversion."""
output = main('coloredlogs', '--convert', 'coloredlogs', '--demo', capture=True)
# Make sure the output is encoded as HTML.
assert '<span' in output

def test_implicit_usage_message(self):
"""Test that the usage message is shown when no actions are given."""
assert 'Usage:' in main('coloredlogs', capture=True)

def test_explicit_usage_message(self):
"""Test that the usage message is shown when ``--help`` is given."""
assert 'Usage:' in main('coloredlogs', '--help', capture=True)


def main(*arguments, **options):
"""Simple wrapper to run the command line interface."""
capture = options.get('capture', False)
saved_argv = sys.argv
saved_stdout = sys.stdout
try:
sys.argv = arguments
if capture:
sys.stdout = StringIO()
coloredlogs.cli.main()
if capture:
return sys.stdout.getvalue()
finally:
sys.argv = saved_argv
sys.stdout = saved_stdout
5 changes: 3 additions & 2 deletions setup.py
Expand Up @@ -35,20 +35,21 @@

setup(name='coloredlogs',
version=version_string,
description='Colored stream handler for the logging module',
description="Colored terminal output for Python's logging module",
long_description=readme_text,
url='https://coloredlogs.readthedocs.org',
author='Peter Odding',
author_email='peter@peterodding.com',
packages=find_packages(),
entry_points=dict(console_scripts=[
'ansi2html = coloredlogs.converter:main',
'coloredlogs = coloredlogs.cli:main',
]),
install_requires=[
'humanfriendly >= 1.25.1',
],
test_suite='coloredlogs.tests',
tests_require=[
'capturer',
'verboselogs',
],
classifiers=[
Expand Down
4 changes: 4 additions & 0 deletions tox.ini
Expand Up @@ -8,10 +8,14 @@ envlist = py26, py27, py34, pypy

[testenv]
deps =
capturer
pytest
verboselogs
commands = py.test

[flake8]
exclude = .tox
max-line-length = 120

[pep257]
ignore = D400

0 comments on commit eaef420

Please sign in to comment.