summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Matusiak <numerodix@gmail.com>2021-05-05 14:53:05 +1000
committerMartin Matusiak <numerodix@gmail.com>2021-05-05 14:53:05 +1000
commit90a8a7ea912833f23ac900d3224a545a677b083c (patch)
tree26c0cf191bd7243ce09e4505abc6a6668571d595
parentc703fabb5d222dbfd2b845b8f3695f19774200e9 (diff)
parent41066f3dab3040e7c57f0f40764c13800707c513 (diff)
downloadansicolor-90a8a7ea912833f23ac900d3224a545a677b083c.tar.gz
Merge branch 'develop'
-rw-r--r--.github/workflows/github-actions.yml19
-rw-r--r--.gitignore2
-rw-r--r--.travis.yml15
-rw-r--r--CONTRIBUTORS7
-rw-r--r--LICENSE2
-rw-r--r--README.rst111
-rw-r--r--ansicolor/__init__.py79
-rw-r--r--ansicolor/ansicolor.py206
-rw-r--r--ansicolor/demos.py114
-rw-r--r--dev-requirements.txt4
-rw-r--r--setup.cfg3
-rw-r--r--setup.py29
-rw-r--r--tests/test_colors.py340
-rw-r--r--tests/test_imports.py1
-rw-r--r--tox.ini4
15 files changed, 657 insertions, 279 deletions
diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml
new file mode 100644
index 0000000..57c3b47
--- /dev/null
+++ b/.github/workflows/github-actions.yml
@@ -0,0 +1,19 @@
+name: ansicolor github actions CI
+on: [push]
+jobs:
+ run-unit-tests:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event."
+ - run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub!"
+ - run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}."
+ - name: Check out repository code
+ uses: actions/checkout@v2
+ - run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner."
+ - run: echo "🖥️ The workflow is now ready to test your code on the runner."
+ - name: List files in the repository
+ run: |
+ ls ${{ github.workspace }}
+ - run: pip3 install -r dev-requirements.txt
+ - run: py.test -v
+ - run: echo "🍏 This job's status is ${{ job.status }}." \ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 4be52a0..22ea07a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,11 @@
*.egg-info/
*.pyc
.cache/
+.coverage
.tox/
build/
dist/
docs/_build/
+htmlcov/
.vscode/
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 5dd83ab..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-language: python
-
-python: 2.7
-
-env:
- - TOX_ENV=py27
- - TOX_ENV=py34
-
-install:
- - pip install tox
-
-script: tox -e $TOX_ENV
-
-notifications:
- email: true
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
new file mode 100644
index 0000000..398ea13
--- /dev/null
+++ b/CONTRIBUTORS
@@ -0,0 +1,7 @@
+Original author:
+ Martin Matusiak <numerodix@gmail.com>
+
+Contributors:
+ Daniel Axtens <daniel@axtens.net>
+ Hypnoes <hypnoes@qq.com>
+ Michael Overmeyer <m.overmeyer@yahoo.ca>
diff --git a/LICENSE b/LICENSE
index 05d5c63..a9eb1c3 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright 2008-2014 Martin Matusiak
+Copyright 2008-2021 Martin Matusiak
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/README.rst b/README.rst
index 2fcd237..6fdf480 100644
--- a/README.rst
+++ b/README.rst
@@ -4,16 +4,13 @@ ansicolor
.. image:: https://badge.fury.io/py/ansicolor.png
:target: https://badge.fury.io/py/ansicolor
-.. image:: https://travis-ci.org/numerodix/ansicolor.png?branch=master
- :target: https://travis-ci.org/numerodix/ansicolor
-
.. image:: https://img.shields.io/pypi/wheel/ansicolor.svg
:target: https://pypi.python.org/pypi/ansicolor/
.. image:: https://img.shields.io/pypi/l/ansicolor.svg
:target: https://pypi.python.org/pypi/ansicolor/
-Python version support: CPython 2.6, 2.7, 3.2, 3.3, 3.4 and PyPy.
+Python version support: CPython 2.7, 3.2+.
Introduction
@@ -30,6 +27,8 @@ Installation
$ pip install ansicolor
+You can also download `tarballs from Github`_.
+
Documentation
-------------
@@ -49,4 +48,108 @@ Take a look at the ``demos`` to see what's possible.
$ python -m ansicolor.demos --diff
+
+Maintenance tasks
+-----------------
+
+
+Setting up a development environment (Ubuntu)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code:: bash
+
+ # if you don't have `mkvirtualenv` & `workon` functions in your shell
+ $ sudo apt install virtualenvwrapper
+
+ # creating the virtual env the first time
+ $ mkvirtualenv ansicolor
+ (ansicolor) $ pip install -r dev-requirements.txt
+
+ # re-activating the virtual env next time
+ $ workon ansicolor
+
+All the steps below assume you have an activated virtual env (even though the
+``(ansicolor)`` prompt is not shown).
+
+
+Running unit tests
+^^^^^^^^^^^^^^^^^^
+
+.. code:: bash
+
+ $ py.test
+
+
+Measuring code coverage
+^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code:: bash
+
+ $ py.test --cov=ansicolor.ansicolor
+ $ coverage html
+ # open htmlcov/index.html in the browser
+
+
+Running all possible tests under tox
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+We use ``tox`` to run both the unit tests and the demos under several different
+Python interpreter versions. Depending on which interpreters you have installed
+(this is managed system-wide and not covered in this README) ``tox`` will most
+likely give you a partial success.
+
+.. code:: bash
+
+ # to run against all interpreters
+ $ tox
+
+ # to run only against selected interpreters
+ $ tox -e py27,py38
+
+
+Checking code style
+^^^^^^^^^^^^^^^^^^^
+
+.. code:: bash
+
+ $ flake8 ansicolor
+
+
+Re-formatting code
+^^^^^^^^^^^^^^^^^^
+
+.. code:: bash
+
+ $ black ansicolor tests
+
+
+Release a new version
+^^^^^^^^^^^^^^^^^^^^^
+
+1. Make sure ``develop`` branch is in sync with ``master``.
+2. Make all the changes on ``develop``.
+
+Quality assurance (see steps above for how to):
+
+1. Make sure all tests are passing.
+2. Make sure test coverage has not declined.
+3. Make sure ``tox`` run succeeds on all (installed) interpreters.
+4. Make sure ``flake8`` checker passes.
+5. Make sure ``black`` formatter has no changes to make.
+
+Doing a release:
+
+1. Bump version in ``ansicolor/__init__.py``.
+2. Git tag the new version and push the tag. This allows users/packagers to
+ download an auto-generated zip/tarball of the tagged release from Github.
+3. ``python setup.py sdist``
+4. ``python setup.py bdist_wheel``
+5. ``twine upload dist/*``
+
+Finally:
+
+1. Merge ``develop`` into ``master``.
+
+
.. _`documentation`: https://ansicolor.readthedocs.org/
+.. _`tarballs from Github`: https://github.com/numerodix/ansicolor/tags
diff --git a/ansicolor/__init__.py b/ansicolor/__init__.py
index b423896..c56a499 100644
--- a/ansicolor/__init__.py
+++ b/ansicolor/__init__.py
@@ -1,36 +1,59 @@
from __future__ import absolute_import
-from ansicolor.ansicolor import * # noqa
+from ansicolor.ansicolor import black
+from ansicolor.ansicolor import blue
+from ansicolor.ansicolor import cyan
+from ansicolor.ansicolor import green
+from ansicolor.ansicolor import magenta
+from ansicolor.ansicolor import red
+from ansicolor.ansicolor import white
+from ansicolor.ansicolor import yellow
+
+from ansicolor.ansicolor import colorize
+from ansicolor.ansicolor import colorize_v2
+from ansicolor.ansicolor import get_code
+from ansicolor.ansicolor import get_code_v2
+from ansicolor.ansicolor import wrap_string
+
+from ansicolor.ansicolor import highlight_string
+from ansicolor.ansicolor import get_highlighter
+
+from ansicolor.ansicolor import strip_escapes
+from ansicolor.ansicolor import justify_formatted
+
+from ansicolor.ansicolor import colordiff
+from ansicolor.ansicolor import set_term_title
+from ansicolor.ansicolor import write_out
+from ansicolor.ansicolor import write_err
+
+from ansicolor.ansicolor import Colors
__all__ = [
- 'black',
- 'blue',
- 'cyan',
- 'green',
- 'magenta',
- 'red',
- 'white',
- 'yellow',
-
- 'colorize',
- 'wrap_string',
- 'get_code',
-
- 'highlight_string',
- 'get_highlighter',
-
- 'strip_escapes',
- 'justify_formatted',
-
- 'colordiff',
- 'set_term_title',
- 'write_out',
- 'write_err',
-
- 'Colors',
+ "black",
+ "blue",
+ "cyan",
+ "green",
+ "magenta",
+ "red",
+ "white",
+ "yellow",
+ "colorize",
+ "colorize_v2",
+ "get_code",
+ "get_code_v2",
+ "wrap_string",
+ "highlight_string",
+ "get_highlighter",
+ "strip_escapes",
+ "justify_formatted",
+ "colordiff",
+ "set_term_title",
+ "write_out",
+ "write_err",
+ "Colors",
]
-__major_version__ = "0.2"
-__release__ = "6"
+__major_version__ = "0.3"
+__release__ = "0"
__version__ = "%s.%s" % (__major_version__, __release__)
diff --git a/ansicolor/ansicolor.py b/ansicolor/ansicolor.py
index f84a938..75f34e6 100644
--- a/ansicolor/ansicolor.py
+++ b/ansicolor/ansicolor.py
@@ -8,33 +8,28 @@ import warnings
__all__ = [ # noqa
- 'black',
- 'blue',
- 'cyan',
- 'green',
- 'magenta',
- 'red',
- 'white',
- 'yellow',
-
- 'colorize',
- 'colorize_v2',
- 'wrap_string',
- 'get_code',
- 'get_code_v2',
-
- 'highlight_string',
- 'get_highlighter',
-
- 'strip_escapes',
- 'justify_formatted',
-
- 'colordiff',
- 'set_term_title',
- 'write_out',
- 'write_err',
-
- 'Colors',
+ "black",
+ "blue",
+ "cyan",
+ "green",
+ "magenta",
+ "red",
+ "white",
+ "yellow",
+ "colorize",
+ "colorize_v2",
+ "wrap_string",
+ "get_code",
+ "get_code_v2",
+ "highlight_string",
+ "get_highlighter",
+ "strip_escapes",
+ "justify_formatted",
+ "colordiff",
+ "set_term_title",
+ "write_out",
+ "write_err",
+ "Colors",
]
@@ -43,7 +38,8 @@ _disabled = (not os.environ.get("TERM")) or (os.environ.get("TERM") == "dumb")
class Colors(object):
- '''Container class for colors'''
+ """Container class for colors"""
+
@classmethod
def new(cls, colorname):
try:
@@ -62,7 +58,8 @@ class Colors(object):
for color in cls._colorlist:
yield color
-## Define Colors members
+
+# Define Colors members
Colors.new("Black")
Colors.new("Red")
Colors.new("Green")
@@ -73,32 +70,37 @@ Colors.new("Cyan")
Colors.new("White")
-## Define coloring shorthands
+# Define coloring shorthands
def make_func(color):
def f(s, bold=False, reverse=False):
return colorize(s, color, bold=bold, reverse=reverse)
- f.__doc__ = """
+
+ f.__doc__ = (
+ """
Colorize string in %s
:param string s: The string to colorize.
:param bool bold: Whether to mark up in bold.
:param bool reverse: Whether to mark up in reverse video.
:rtype: string
- """ % color.__name__.lower()
+ """
+ % color.__name__.lower()
+ )
return f
+
for color in Colors.iter():
globals()[color.__name__.lower()] = make_func(color)
-## Define highlighting colors
+# Define highlighting colors
highlights = [
Colors.Green,
Colors.Yellow,
Colors.Cyan,
Colors.Blue,
Colors.Magenta,
- Colors.Red
+ Colors.Red,
]
highlight_map = {}
@@ -106,7 +108,7 @@ for (n, h) in enumerate(highlights):
highlight_map[n] = [color for color in Colors.iter() if h == color].pop()
-## Coloring functions
+# Coloring functions
def get_highlighter(colorid):
"""
Map a color index to a highlighting color.
@@ -117,6 +119,7 @@ def get_highlighter(colorid):
return highlight_map[colorid % len(highlights)]
+
def get_code(color, bold=False, reverse=False):
"""
Returns the escape code for styling with the given color,
@@ -132,16 +135,17 @@ def get_code(color, bold=False, reverse=False):
if _disabled:
return ""
- fmt = '0;0'
+ fmt = "0;0"
if bold and reverse:
- fmt = '1;7'
+ fmt = "1;7"
elif reverse:
- fmt = '0;7'
+ fmt = "0;7"
elif bold:
- fmt = '0;1'
- color = (color is not None) and ';3%s' % color.id or ''
+ fmt = "0;1"
+ color = (color is not None) and ";3%s" % color.id or ""
+
+ return "\033[" + fmt + color + "m"
- return '\033[' + fmt + color + 'm'
def get_code_v2(color, bold=False, reverse=False, underline=False, blink=False):
"""
@@ -159,18 +163,23 @@ def get_code_v2(color, bold=False, reverse=False, underline=False, blink=False):
if _disabled:
return ""
- fmt = '0'
- l = []
- if bold: l.append('1')
- if underline: l.append('4')
- if blink: l.append('5')
- if reverse: l.append('7')
- if len(l) != 0:
- fmt = ';'.join(l)
+ fmt = "0"
+ items = []
+ if bold:
+ items.append("1")
+ if underline:
+ items.append("4")
+ if blink:
+ items.append("5")
+ if reverse:
+ items.append("7")
+ if len(items) != 0:
+ fmt = ";".join(items)
- color = (color is not None) and ';3%s' % color.id or ''
+ color = (color is not None) and ";3%s" % color.id or ""
+
+ return "\033[" + fmt + color + "m"
- return '\033[' + fmt + color + 'm'
def colorize(s, color, bold=False, reverse=False, start=None, end=None):
"""
@@ -193,14 +202,25 @@ def colorize(s, color, bold=False, reverse=False, start=None, end=None):
between = s[start:end]
after = s[end:]
- return ("%s%s%s%s%s" % (before,
- get_code(color, bold=bold, reverse=reverse),
- between,
- get_code(None),
- after))
-
-def colorize_v2(s, color, bold=False, reverse=False, underline=False, blink=False,
- start=None, end=None):
+ return "%s%s%s%s%s" % (
+ before,
+ get_code(color, bold=bold, reverse=reverse),
+ between,
+ get_code(None),
+ after,
+ )
+
+
+def colorize_v2(
+ s,
+ color,
+ bold=False,
+ reverse=False,
+ underline=False,
+ blink=False,
+ start=None,
+ end=None,
+):
"""
Colorize a string with the color given.
:param string s: The string to colorize.
@@ -222,14 +242,15 @@ def colorize_v2(s, color, bold=False, reverse=False, underline=False, blink=Fals
between = s[start:end]
after = s[end:]
- return ("%s%s%s%s%s" % (before,
- get_code_v2(color, bold=bold,
- underline=underline,
- blink=blink,
- reverse=reverse),
- between,
- get_code_v2(None),
- after))
+ return "%s%s%s%s%s" % (
+ before,
+ get_code_v2(
+ color, bold=bold, underline=underline, blink=blink, reverse=reverse
+ ),
+ between,
+ get_code_v2(None),
+ after,
+ )
def wrap_string(s, pos, color, bold=False, reverse=False):
@@ -253,12 +274,14 @@ def wrap_string(s, pos, color, bold=False, reverse=False):
if _disabled:
if pos == 0:
pos = 1
- return s[:pos - 1] + "|" + s[pos:]
+ return s[: pos - 1] + "|" + s[pos:]
- return "%s%s%s%s" % (get_code(color, bold=bold, reverse=reverse),
- s[:pos],
- get_code(None),
- s[pos:])
+ return "%s%s%s%s" % (
+ get_code(color, bold=bold, reverse=reverse),
+ s[:pos],
+ get_code(None),
+ s[pos:],
+ )
def highlight_string(s, *spanlists, **kw):
@@ -276,7 +299,7 @@ def highlight_string(s, *spanlists, **kw):
The `color` parameter has been deprecated in favor of `colors`.
"""
- colors = kw.get('colors', [])
+ colors = kw.get("colors", [])
# pair span with color and id of the list -> (span, color, list_id)
tuples = []
@@ -335,13 +358,13 @@ def highlight_string(s, *spanlists, **kw):
# allow bold/reverse/nocolor styling as parameters
if color:
- if kw.get('color'):
- color = kw.get('color')
+ if kw.get("color"):
+ color = kw.get("color")
warnings.warn("color is deprecated", DeprecationWarning, 2)
- elif kw.get('nocolor'):
+ elif kw.get("nocolor"):
color = None
- bold = kw.get('bold') or bold
- reverse = kw.get('reverse') or reverse
+ bold = kw.get("bold") or bold
+ reverse = kw.get("reverse") or reverse
if layer == 2:
bold = True
@@ -357,7 +380,7 @@ def highlight_string(s, *spanlists, **kw):
cursor = pos
segments.append(s[cursor:])
- return ''.join(segments)
+ return "".join(segments)
def colordiff(x, y, color_x=Colors.Cyan, color_y=Colors.Green, debug=False):
@@ -382,23 +405,25 @@ def colordiff(x, y, color_x=Colors.Cyan, color_y=Colors.Green, debug=False):
rather than the longest common subsequence, but this just causes the
diff to show more changed characters, the result is still correct"""
sm = difflib.SequenceMatcher(None, x, y)
- seq = ''
+ seq = ""
for match in sm.get_matching_blocks():
- seq += x[match.a:match.a + match.size]
+ seq += x[match.a : match.a + match.size]
return seq
def make_generator(it):
g = ((i, e) for (i, e) in enumerate(it))
+
def f():
try:
return next(g)
except StopIteration:
return (-1, None)
+
return f
def log(s):
if debug:
- print(s)
+ print(s) # pragma: no cover
seq = compute_seq(x, y)
log(">>> %s , %s -> %s" % (x, y, seq))
@@ -421,31 +446,31 @@ def colordiff(x, y, color_x=Colors.Cyan, color_y=Colors.Green, debug=False):
# character the same in all sets
# -> unchanged
if s == a == b:
- log(' %s' % s)
+ log(" %s" % s)
(sid, s) = it_seq()
(aid, a) = it_x()
(bid, b) = it_y()
# character the same in orig and common
# -> added in new
elif s == a:
- log('+%s' % b)
+ log("+%s" % b)
y_spans.append((bid, bid + 1))
(bid, b) = it_y()
# character the same in new and common
# -> removed in orig
elif s == b:
- log('-%s' % a)
+ log("-%s" % a)
x_spans.append((aid, aid + 1))
(aid, a) = it_x()
# character not the same (eg. case change)
# -> removed in orig and added in new
elif a != b:
if a:
- log('-%s' % a)
+ log("-%s" % a)
x_spans.append((aid, aid + 1))
(aid, a) = it_x()
if b:
- log('+%s' % b)
+ log("+%s" % b)
y_spans.append((bid, bid + 1))
(bid, b) = it_y()
@@ -477,10 +502,10 @@ def strip_escapes(s):
:rtype: string
"""
- return re.sub('\033[[](?:(?:[0-9]*;)*)(?:[0-9]*m)', '', s)
+ return re.sub("\033\[(?:(?:[0-9]*;)*)(?:[0-9]*m)", "", s)
-## Output functions
+# Output functions
def set_term_title(s):
"""
Set the title of a terminal window.
@@ -491,6 +516,7 @@ def set_term_title(s):
if not _disabled:
sys.stdout.write("\033]2;%s\007" % s)
+
def write_to(target, s):
# assuming we have escapes in the string
if not _disabled:
@@ -499,6 +525,7 @@ def write_to(target, s):
target.write(s)
target.flush()
+
def write_out(s):
"""
Write a string to ``sys.stdout``, strip escapes if output is a pipe.
@@ -508,6 +535,7 @@ def write_out(s):
write_to(sys.stdout, s)
+
def write_err(s):
"""
Write a string to ``sys.stderr``, strip escapes if output is a pipe.
diff --git a/ansicolor/demos.py b/ansicolor/demos.py
index d724250..69bba6b 100644
--- a/ansicolor/demos.py
+++ b/ansicolor/demos.py
@@ -17,92 +17,129 @@ def demo_color():
lst = []
- lst.extend([[], ['>>> Without colors'], []])
+ lst.extend([[], [">>> Without colors"], []])
line = []
line.append(colorize("Standard".ljust(width), None))
line.append(colorize("Bold".ljust(width), None, bold=True))
line.append(colorize("Reverse".ljust(width), None, reverse=True))
- line.append(colorize("Bold & Rev".ljust(width), None, bold=True, reverse=True)) # noqa
+ line.append(
+ colorize("Bold & Rev".ljust(width), None, bold=True, reverse=True)
+ ) # noqa
lst.append(line)
- lst.extend([[], ['>>> Using colors'], []])
+ lst.extend([[], [">>> Using colors"], []])
for color in Colors.iter():
line = []
line.append(colorize(color.__name__.ljust(width), color))
line.append(colorize(color.__name__.ljust(width), color, bold=True)) # noqa
line.append(colorize(color.__name__.ljust(width), color, reverse=True)) # noqa
- line.append(colorize(color.__name__.ljust(width), color, bold=True, reverse=True)) # noqa
+ line.append(
+ colorize(color.__name__.ljust(width), color, bold=True, reverse=True)
+ ) # noqa
lst.append(line)
- lst.extend([[], ['>>> Using highlighting colors'], []])
+ lst.extend([[], [">>> Using highlighting colors"], []])
for color in Colors.iter():
color = get_highlighter(color.id)
line = []
line.append(colorize(color.__name__.ljust(width), color))
line.append(colorize(color.__name__.ljust(width), color, bold=True)) # noqa
line.append(colorize(color.__name__.ljust(width), color, reverse=True)) # noqa
- line.append(colorize(color.__name__.ljust(width), color, bold=True, reverse=True)) # noqa
+ line.append(
+ colorize(color.__name__.ljust(width), color, bold=True, reverse=True)
+ ) # noqa
lst.append(line)
for line in lst:
for item in line:
- write_out('%s ' % item)
+ write_out("%s " % item)
write_out("\n")
+
def demo_color_v2():
width = 10
lst = []
- lst.extend([[], ['>>> Without colors'], []])
+ lst.extend([[], [">>> Without colors"], []])
line = []
line.append(colorize_v2("Standard".ljust(width), None))
line.append(colorize_v2("Bold".ljust(width), None, bold=True))
line.append(colorize_v2("Underline".ljust(width), None, underline=True))
line.append(colorize_v2("Blink".ljust(width), None, blink=True))
line.append(colorize_v2("Reverse".ljust(width), None, reverse=True))
- line.append(colorize_v2("Bold & Rev".ljust(width), None, bold=True, underline=True, reverse=True)) # noqa
+ line.append(
+ colorize_v2(
+ "Bold & Rev".ljust(width), None, bold=True, underline=True, reverse=True
+ )
+ ) # noqa
lst.append(line)
- lst.extend([[], ['>>> Using colors_v2'], []])
+ lst.extend([[], [">>> Using colors_v2"], []])
for color in Colors.iter():
line = []
line.append(colorize_v2(color.__name__.ljust(width), color))
line.append(colorize_v2(color.__name__.ljust(width), color, bold=True)) # noqa
- line.append(colorize_v2(color.__name__.ljust(width), color, underline=True)) # noqa
+ line.append(
+ colorize_v2(color.__name__.ljust(width), color, underline=True)
+ ) # noqa
line.append(colorize_v2(color.__name__.ljust(width), color, blink=True)) # noqa
- line.append(colorize_v2(color.__name__.ljust(width), color, reverse=True)) # noqa
- line.append(colorize_v2(color.__name__.ljust(width), color, bold=True, underline=True, reverse=True)) # noqa
+ line.append(
+ colorize_v2(color.__name__.ljust(width), color, reverse=True)
+ ) # noqa
+ line.append(
+ colorize_v2(
+ color.__name__.ljust(width),
+ color,
+ bold=True,
+ underline=True,
+ reverse=True,
+ )
+ ) # noqa
lst.append(line)
- lst.extend([[], ['>>> Using highlighting colors_v2'], []])
+ lst.extend([[], [">>> Using highlighting colors_v2"], []])
for color in Colors.iter():
color = get_highlighter(color.id)
line = []
line.append(colorize_v2(color.__name__.ljust(width), color))
line.append(colorize_v2(color.__name__.ljust(width), color, bold=True)) # noqa
- line.append(colorize_v2(color.__name__.ljust(width), color, underline=True)) # noqa
+ line.append(
+ colorize_v2(color.__name__.ljust(width), color, underline=True)
+ ) # noqa
line.append(colorize_v2(color.__name__.ljust(width), color, blink=True)) # noqa
- line.append(colorize_v2(color.__name__.ljust(width), color, reverse=True)) # noqa
- line.append(colorize_v2(color.__name__.ljust(width), color, bold=True, underline=True, reverse=True)) # noqa
+ line.append(
+ colorize_v2(color.__name__.ljust(width), color, reverse=True)
+ ) # noqa
+ line.append(
+ colorize_v2(
+ color.__name__.ljust(width),
+ color,
+ bold=True,
+ underline=True,
+ reverse=True,
+ )
+ ) # noqa
lst.append(line)
for line in lst:
for item in line:
- write_out('%s ' % item)
+ write_out("%s " % item)
write_out("\n")
+
def _demo_highlight(reverse=False):
rxs = [
- '(b+).*\\1',
- '(c+).*\\1',
- '(d+).*\\1',
- '(e+).*\\1',
+ "(b+).*\\1",
+ "(c+).*\\1",
+ "(d+).*\\1",
+ "(e+).*\\1",
]
s = """\
aaabbbcccdddeeefffeeedddcccbbbaaa
fffeeedddcccbbbaaabbbcccdddeeefff
"""
+
def display(rxs, s):
spanlists = []
colors = []
@@ -122,17 +159,19 @@ fffeeedddcccbbbaaabbbcccdddeeefff
for (i, rx) in enumerate(rxs):
color = get_highlighter(i)
color = colorize(color.__name__.ljust(10), color)
- write_out('Regex %s: %s %s\n' % (i, color, rx))
+ write_out("Regex %s: %s %s\n" % (i, color, rx))
write_out(s)
for i in range(0, len(rxs) + 1):
- write_out('\n')
+ write_out("\n")
display(rxs[:i], s)
+
def demo_highlight():
_demo_highlight()
+
def demo_highlight_reverse():
_demo_highlight(reverse=True)
@@ -140,31 +179,30 @@ def demo_highlight_reverse():
def demo_diff():
def display_diff(s, t):
(s_fmt, t_fmt) = colordiff(s, t)
- write_out('>>> %s\n' % s_fmt)
- write_out(' %s\n\n' % t_fmt)
-
- display_diff('first last', 'First Last')
- display_diff('the the boss', 'the boss')
- display_diff('the coder', 'the first coder')
- display_diff('agcat', 'gac')
- display_diff('XMJYAUZ', 'MZJAWXU')
- display_diff('abcdfghjqz', 'abcdefgijkrxyz')
+ write_out(">>> %s\n" % s_fmt)
+ write_out(" %s\n\n" % t_fmt)
+ display_diff("first last", "First Last")
+ display_diff("the the boss", "the boss")
+ display_diff("the coder", "the first coder")
+ display_diff("agcat", "gac")
+ display_diff("XMJYAUZ", "MZJAWXU")
+ display_diff("abcdfghjqz", "abcdefgijkrxyz")
-if __name__ == '__main__':
+if __name__ == "__main__":
try:
action = sys.argv[1]
except IndexError:
print("Usage: %s [ --color | --highlight | --diff ]" % sys.argv[0])
sys.exit(1)
- if action == '--color':
+ if action == "--color":
demo_color()
demo_color_v2()
- elif action == '--highlight':
+ elif action == "--highlight":
demo_highlight()
- elif action == '--highlight-reverse':
+ elif action == "--highlight-reverse":
demo_highlight_reverse()
- elif action == '--diff':
+ elif action == "--diff":
demo_diff()
diff --git a/dev-requirements.txt b/dev-requirements.txt
index 7735157..1d9fe17 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -1,6 +1,10 @@
+black
cheesecake
flake8
+ipdb
pytest
+pytest-cov
+pytest-randomly
sphinx
tox
twine
diff --git a/setup.cfg b/setup.cfg
index 4c21470..92e2d45 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,7 @@
[flake8]
exclude = .tox/,build/,docs/
-ignore = E301,E302,E303
+ignore = E301,E302,E303,W503,W605
+max_line_length = 88
[wheel]
universal = 1
diff --git a/setup.py b/setup.py
index 049fbd3..5f3955d 100644
--- a/setup.py
+++ b/setup.py
@@ -4,29 +4,24 @@ import ansicolor
setup(
- name='ansicolor',
+ name="ansicolor",
version=ansicolor.__version__,
description=(
- 'A library to produce ansi color output '
- 'and colored highlighting and diffing'
+ "A library to produce ansi color output and colored highlighting and diffing"
),
- author='Martin Matusiak',
- author_email='numerodix@gmail.com',
- url='https://github.com/numerodix/ansicolor',
-
+ author="Martin Matusiak",
+ author_email="numerodix@gmail.com",
+ url="https://github.com/numerodix/ansicolor",
packages=[
- 'ansicolor',
+ "ansicolor",
],
classifiers=[
- 'License :: OSI Approved :: Apache Software License',
- 'Programming Language :: Python :: 2.6',
- 'Programming Language :: Python :: 2.7',
- 'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.2',
- 'Programming Language :: Python :: 3.3',
- 'Programming Language :: Python :: 3.4',
- 'Programming Language :: Python :: Implementation :: CPython',
- 'Programming Language :: Python :: Implementation :: PyPy',
+ "License :: OSI Approved :: Apache Software License",
+ "Programming Language :: Python :: 2.7",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.8",
+ "Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: Implementation :: CPython",
],
# don't install as zipped egg
zip_safe=False,
diff --git a/tests/test_colors.py b/tests/test_colors.py
index ea1fb38..37c53e3 100644
--- a/tests/test_colors.py
+++ b/tests/test_colors.py
@@ -1,153 +1,325 @@
+import contextlib
+
from ansicolor import Colors
from ansicolor import blue
from ansicolor import colordiff
from ansicolor import colorize
+from ansicolor import colorize_v2
from ansicolor import get_code
+from ansicolor import get_code_v2
from ansicolor import get_highlighter
+from ansicolor import highlight_string
from ansicolor import justify_formatted
from ansicolor import red
+from ansicolor import set_term_title
from ansicolor import strip_escapes
from ansicolor import wrap_string
-from ansicolor import get_code_v2
-from ansicolor import colorize_v2
+from ansicolor import write_err
+from ansicolor import write_out
+import ansicolor
+
+
+@contextlib.contextmanager
+def disabled_state():
+ ansicolor.ansicolor._disabled = True
+
+ try:
+ # run the test with colors disabled
+ yield
+
+ finally:
+ ansicolor.ansicolor._disabled = False
def test_codes():
# reset code
- assert '\033[0;0m' == get_code(None)
+ assert "\033[0;0m" == get_code(None)
# plain color codes
- assert '\033[0;0;30m' == get_code(Colors.Black)
- assert '\033[0;0;31m' == get_code(Colors.Red)
- assert '\033[0;0;32m' == get_code(Colors.Green)
- assert '\033[0;0;33m' == get_code(Colors.Yellow)
- assert '\033[0;0;34m' == get_code(Colors.Blue)
- assert '\033[0;0;35m' == get_code(Colors.Magenta)
- assert '\033[0;0;36m' == get_code(Colors.Cyan)
- assert '\033[0;0;37m' == get_code(Colors.White)
+ assert "\033[0;0;30m" == get_code(Colors.Black)
+ assert "\033[0;0;31m" == get_code(Colors.Red)
+ assert "\033[0;0;32m" == get_code(Colors.Green)
+ assert "\033[0;0;33m" == get_code(Colors.Yellow)
+ assert "\033[0;0;34m" == get_code(Colors.Blue)
+ assert "\033[0;0;35m" == get_code(Colors.Magenta)
+ assert "\033[0;0;36m" == get_code(Colors.Cyan)
+ assert "\033[0;0;37m" == get_code(Colors.White)
# bold color
- assert '\033[0;1;31m' == get_code(Colors.Red, bold=True)
+ assert "\033[0;1;31m" == get_code(Colors.Red, bold=True)
# reverse color
- assert '\033[0;7;31m' == get_code(Colors.Red, reverse=True)
+ assert "\033[0;7;31m" == get_code(Colors.Red, reverse=True)
# bold + reverse color
- assert '\033[1;7;31m' == get_code(Colors.Red, bold=True, reverse=True)
+ assert "\033[1;7;31m" == get_code(Colors.Red, bold=True, reverse=True)
+
+
+def test_codes_disabled():
+ with disabled_state():
+ assert "" == get_code(Colors.Black)
+
def test_codes_v2():
# reset code
- assert '\033[0m' == get_code_v2(None)
+ assert "\033[0m" == get_code_v2(None)
# plain color codes
- assert '\033[0;30m' == get_code_v2(Colors.Black)
- assert '\033[0;31m' == get_code_v2(Colors.Red)
- assert '\033[0;32m' == get_code_v2(Colors.Green)
- assert '\033[0;33m' == get_code_v2(Colors.Yellow)
- assert '\033[0;34m' == get_code_v2(Colors.Blue)
- assert '\033[0;35m' == get_code_v2(Colors.Magenta)
- assert '\033[0;36m' == get_code_v2(Colors.Cyan)
- assert '\033[0;37m' == get_code_v2(Colors.White)
+ assert "\033[0;30m" == get_code_v2(Colors.Black)
+ assert "\033[0;31m" == get_code_v2(Colors.Red)
+ assert "\033[0;32m" == get_code_v2(Colors.Green)
+ assert "\033[0;33m" == get_code_v2(Colors.Yellow)
+ assert "\033[0;34m" == get_code_v2(Colors.Blue)
+ assert "\033[0;35m" == get_code_v2(Colors.Magenta)
+ assert "\033[0;36m" == get_code_v2(Colors.Cyan)
+ assert "\033[0;37m" == get_code_v2(Colors.White)
# bold, underline, blink, reverse color
- assert '\033[1;31m' == get_code_v2(Colors.Red, bold=True)
- assert '\033[4;31m' == get_code_v2(Colors.Red, underline=True)
- assert '\033[5;31m' == get_code_v2(Colors.Red, blink=True)
- assert '\033[7;31m' == get_code_v2(Colors.Red, reverse=True)
+ assert "\033[1;31m" == get_code_v2(Colors.Red, bold=True)
+ assert "\033[4;31m" == get_code_v2(Colors.Red, underline=True)
+ assert "\033[5;31m" == get_code_v2(Colors.Red, blink=True)
+ assert "\033[7;31m" == get_code_v2(Colors.Red, reverse=True)
# mixed color
- assert '\033[1;4;31m' == get_code_v2(Colors.Red, bold=True, underline=True)
- assert '\033[1;5;31m' == get_code_v2(Colors.Red, bold=True, blink=True)
- assert '\033[1;7;31m' == get_code_v2(Colors.Red, bold=True, reverse=True)
-
- assert '\033[4;5;31m' == get_code_v2(Colors.Red, underline=True, blink=True)
- assert '\033[4;7;31m' == get_code_v2(Colors.Red, underline=True, reverse=True)
-
- assert '\033[5;7;31m' == get_code_v2(Colors.Red, blink=True, reverse=True)
-
- assert '\033[1;4;5;31m' == get_code_v2(Colors.Red, bold=True, underline=True, blink=True)
- assert '\033[1;4;7;31m' == get_code_v2(Colors.Red, bold=True, underline=True, reverse=True)
- assert '\033[1;5;7;31m' == get_code_v2(Colors.Red, bold=True, blink=True, reverse=True)
- assert '\033[1;4;5;7;31m' == get_code_v2(Colors.Red, bold=True, underline=True, blink=True, reverse=True)
+ assert "\033[1;4;31m" == get_code_v2(Colors.Red, bold=True, underline=True)
+ assert "\033[1;5;31m" == get_code_v2(Colors.Red, bold=True, blink=True)
+ assert "\033[1;7;31m" == get_code_v2(Colors.Red, bold=True, reverse=True)
+
+ assert "\033[4;5;31m" == get_code_v2(Colors.Red, underline=True, blink=True)
+ assert "\033[4;7;31m" == get_code_v2(Colors.Red, underline=True, reverse=True)
+
+ assert "\033[5;7;31m" == get_code_v2(Colors.Red, blink=True, reverse=True)
+
+ assert "\033[1;4;5;31m" == get_code_v2(
+ Colors.Red, bold=True, underline=True, blink=True
+ )
+ assert "\033[1;4;7;31m" == get_code_v2(
+ Colors.Red, bold=True, underline=True, reverse=True
+ )
+ assert "\033[1;5;7;31m" == get_code_v2(
+ Colors.Red, bold=True, blink=True, reverse=True
+ )
+ assert "\033[1;4;5;7;31m" == get_code_v2(
+ Colors.Red, bold=True, underline=True, blink=True, reverse=True
+ )
+
+
+def test_codes_v2_disabled():
+ with disabled_state():
+ assert "" == get_code_v2(Colors.Black)
+
def test_coloring():
- assert '\033[0;0;31m' + 'hi' + '\033[0;0m' == red('hi')
+ assert "\033[0;0;31m" + "hi" + "\033[0;0m" == red("hi")
-def test_highlights():
+def test_get_hightlighter():
# can I get a highlighter?
assert Colors.Green == get_highlighter(0)
assert Colors.Yellow == get_highlighter(1)
-def test_colorize1():
+def test_highlight_string_one_layer():
+ text = "aaabbbaaa"
+ spanlists = [
+ [(3, 6)],
+ ]
+
assert (
- get_code(Colors.Red)
- + "Hi there"
- + get_code(None)
- ) == colorize("Hi there", Colors.Red)
+ "aaa" + get_code(Colors.Green) + "bbb" + get_code(None) + "aaa"
+ ) == highlight_string(text, *spanlists)
+
+
+def test_highlight_string_one_color_chosen():
+ text = "aaabbbaaa"
+ spanlists = [
+ [(3, 6)],
+ ]
-def test_colorize2():
assert (
- "H"
- + get_code(Colors.Red)
- + "i ther"
- + get_code(None)
- + "e"
- ) == colorize("Hi there", Colors.Red, start=1, end=7)
+ "aaa" + get_code(Colors.Cyan) + "bbb" + get_code(None) + "aaa"
+ ) == highlight_string(text, *spanlists, color=Colors.Cyan)
+
+
+def test_highlight_string_nocolor():
+ text = "aaabbbaaa"
+ spanlists = [
+ [(3, 6)],
+ ]
-def test_colorize_v2():
assert (
- get_code(Colors.Red)
- + "Hi there"
- + get_code(None)
- ) == colorize("Hi there", Colors.Red)
+ "aaa" + get_code(None) + "bbb" + get_code(None) + "aaa"
+ ) == highlight_string(text, *spanlists, nocolor=True)
+
+
+def test_highlight_string_four_layers_inside_out():
+ text = "aaabbbcccdddeeefffeeedddcccbbbaaa"
+ spanlists = [
+ [(3, 30)],
+ [(6, 27)],
+ [(9, 24)],
+ [(12, 21)],
+ ]
+ colors = [
+ Colors.Green,
+ Colors.Yellow,
+ Colors.Cyan,
+ Colors.Blue,
+ ]
assert (
- "H"
- + get_code(Colors.Red)
- + "i ther"
+ "aaa"
+ + get_code(Colors.Green)
+ + "bbb"
+ + get_code(Colors.Yellow, bold=True)
+ + "ccc"
+ + get_code(Colors.Cyan, reverse=True)
+ + "ddd"
+ + get_code(Colors.Blue, bold=True, reverse=True)
+ + "eeefffeee"
+ + get_code(Colors.Cyan, reverse=True)
+ + "ddd"
+ + get_code(Colors.Yellow, bold=True)
+ + "ccc"
+ + get_code(Colors.Green)
+ + "bbb"
+ get_code(None)
- + "e"
- ) == colorize("Hi there", Colors.Red, start=1, end=7)
+ + "aaa"
+ ) == highlight_string(text, *spanlists, colors=colors)
-def test_wrap_string():
+def test_highlight_string_four_layers_outside_in():
+ text = "fffeeedddcccbbbaaabbbcccdddeeefff"
+ spanlists = [[(12, 21)], [(9, 24)], [(6, 27)], [(3, 30)]]
+ colors = [
+ Colors.Green,
+ Colors.Yellow,
+ Colors.Cyan,
+ Colors.Blue,
+ ]
+
assert (
- get_code(Colors.Red)
- + "Hi "
+ "fff"
+ + get_code(Colors.Blue)
+ + "eee"
+ + get_code(Colors.Blue, bold=True)
+ + "ddd"
+ + get_code(Colors.Blue, reverse=True)
+ + "ccc"
+ + get_code(Colors.Blue, bold=True, reverse=True)
+ + "bbbaaabbb"
+ + get_code(Colors.Blue, reverse=True)
+ + "ccc"
+ + get_code(Colors.Blue, bold=True)
+ + "ddd"
+ + get_code(Colors.Blue)
+ + "eee"
+ get_code(None)
- + "there"
- ) == wrap_string("Hi there", 3, Colors.Red)
+ + "fff"
+ ) == highlight_string(text, *spanlists, colors=colors)
+
+
+def test_colorize():
+ assert (get_code(Colors.Red) + "Hi there" + get_code(None)) == colorize(
+ "Hi there", Colors.Red
+ )
+
+
+def test_colorize_with_start_end():
+ assert ("H" + get_code(Colors.Red) + "i ther" + get_code(None) + "e") == colorize(
+ "Hi there", Colors.Red, start=1, end=7
+ )
+
+
+def test_colorize_v2():
+ assert (get_code_v2(Colors.Red) + "Hi there" + get_code_v2(None)) == colorize_v2(
+ "Hi there", Colors.Red
+ )
+
+ assert (
+ "H" + get_code_v2(Colors.Red) + "i ther" + get_code_v2(None) + "e"
+ ) == colorize_v2("Hi there", Colors.Red, start=1, end=7)
+
+
+def test_wrap_string():
+ assert (get_code(Colors.Red) + "Hi " + get_code(None) + "there") == wrap_string(
+ "Hi there", 3, Colors.Red
+ )
+
+
+def test_wrap_string_disabled():
+ with disabled_state():
+ assert "Hi|there" == wrap_string("Hi there", 3, Colors.Red)
+ assert "|i there" == wrap_string("Hi there", 0, Colors.Red)
def test_strip_escapes():
- assert "Hi there" == strip_escapes(
- colorize("Hi there", Colors.Red, start=3)
+ assert "Hi there" == strip_escapes(colorize("Hi there", Colors.Red, start=3))
+
+ assert (
+ strip_escapes(
+ colorize("Hi", None, bold=True)
+ + " there, "
+ + colorize("stranger", Colors.Green, bold=True)
+ )
+ == "Hi there, stranger"
)
- assert strip_escapes(
- colorize("Hi", None, bold=True) +
- " there, " +
- colorize("stranger", Colors.Green, bold=True)
- ) == "Hi there, stranger"
+def test_colordiff_different():
+ x, y = colordiff("hi bob", "hi there", color_x=Colors.Red, color_y=Colors.Blue)
-def test_colordiff():
- x, y = colordiff("hi bob", "hi there",
- color_x=Colors.Red, color_y=Colors.Blue)
+ def fx(s):
+ return red(s, reverse=True)
- fx = lambda s: red(s, reverse=True)
- fy = lambda s: blue(s, reverse=True)
+ def fy(s):
+ return blue(s, reverse=True)
assert x == "hi " + fx("b") + fx("o") + fx("b")
assert y == "hi " + fy("t") + fy("h") + fy("e") + fy("r") + fy("e")
+def test_colordiff_edited():
+ x, y = colordiff("hi bobby", "hi bob", color_x=Colors.Red, color_y=Colors.Blue)
+
+ def fx(s):
+ return red(s, reverse=True)
+
+ assert x == "hi bob" + fx("b") + fx("y")
+ assert y == "hi bob"
+
+
def test_justify_formatted():
def rjust(s, width):
return s.rjust(width)
- assert justify_formatted(
- red("hi"), rjust, 10
- ) == " " + red("hi")
+ assert justify_formatted(red("hi"), rjust, 10) == " " + red("hi")
+
+
+def test_set_term_title(capsys):
+ set_term_title("ansicolor demo")
+
+ captured = capsys.readouterr()
+ assert "\033]2;ansicolor demo\007" == captured.out
+
+
+def test_write_out(capfd):
+ text = colorize("Hi there", Colors.Red, start=3, end=6)
+
+ # escapes will be stripped since we rely on capfd and os.isatty detects a
+ # tty device on the other end
+ write_out(text)
+
+ captured = capfd.readouterr()
+ assert "Hi there" == captured.out
+
+
+def test_write_err(capfd):
+ text = colorize("Hi there", Colors.Red, start=3, end=6)
+
+ # escapes will be stripped since we rely on capfd and os.isatty detects a
+ # tty device on the other end
+ write_err(text)
+
+ captured = capfd.readouterr()
+ assert "Hi there" == captured.err
diff --git a/tests/test_imports.py b/tests/test_imports.py
index ed5f654..4b07ce6 100644
--- a/tests/test_imports.py
+++ b/tests/test_imports.py
@@ -9,6 +9,7 @@ def test_importability():
from ansicolor import yellow # noqa
from ansicolor import Colors # noqa
+
Colors.Black
Colors.Blue
Colors.Cyan
diff --git a/tox.ini b/tox.ini
index e14dc94..7e6ee32 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist=py26, py27, py32, py33, py34, pypy
+envlist=py27, py38, py39
develop=true
[testenv]
@@ -7,7 +7,7 @@ setenv=
TERM=xterm
commands=
- pip install pytest flake8
+ pip install pytest
python -m ansicolor.demos --color
python -m ansicolor.demos --diff
python -m ansicolor.demos --highlight