Hello community,
here is the log from the commit of package python-humanfriendly for
openSUSE:Factory checked in at 2018-02-09 15:51:56
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-humanfriendly (Old)
and /work/SRC/openSUSE:Factory/.python-humanfriendly.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-humanfriendly"
Fri Feb 9 15:51:56 2018 rev:4 rq:574413 version:4.8
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-humanfriendly/python-humanfriendly.changes
2017-09-11 16:18:02.428731047 +0200
+++
/work/SRC/openSUSE:Factory/.python-humanfriendly.new/python-humanfriendly.changes
2018-02-09 15:52:02.221713860 +0100
@@ -1,0 +2,103 @@
+Thu Feb 8 21:59:46 UTC 2018 - [email protected]
+
+- Update to version 4.8
+ * Add coerce_pattern() function
+ * Improve code consistency
+- From version 4.7
+ * Support background colors and 256 color mode
+ * Tests for output(), message() and warning()
+- From version 4.6
+ * Support for bright terminal colors
+- From version 4.5
+ * Extend byte ranges, add RAM output to command line
+- From version 4.4.2
+ * Reduce clock source sensitivity* (MacOS on Travis CI)
+ * Fix `Double requirement given' error
+ * Try to fix Python 2.6 tests on Travis CI
+ * Change Sphinx documentation theme
+ * Bump copyright
+ * Try to enable MacOS builds on Travis CI
+ * Fix ImportError exception on Windows
+- From version 4.4.1
+ * Include docs to sdist
+ * PEP-8 fail
+ * Change cli test from 1.05 mm to 1.05 km
+ * Another correction to test_cli
+ * Corrected CLI format_length test
+ * Aim for more CLI coverage
+ * Add CLI tests for format-byte
+ * Added size test cases
+ * Size 1z is now valid
+- From version 4.4
+ * Add touch() to __all__
+ * Make touch() create intermediate directories
+ * Add humanfriendly.testing.touch() function
+- From version 4.3
+ * Don't log duplicate output in run_cli()
+- From version 4.2
+ * Reconfigure logging in run_cli()
+- From version 4.1
+ * Always log stderr as well in run_cli()
+- From version 4.0
+ * Improve humanfriendly.testing.run_cli() (backwards incompatible)
+- From version 3.8
+ * Make it easy to mock the $HOME directory
+- From version 3.7
+ * Customizable skipping of tests
+ * Added .pyc to .gitignore
+ * Added Exa, Zetta & Yotta prefixes and tests
+ * Added -S --format-bytes option to use powers of 1024 rather than 1000 from
command line.
+- From version 3.6.1
+ * Improve robustness of Patched{Attribute,Item}
+- From version 3.6
+ * Make retry limit configurable
+ * Refactor makefile and Travis CI configuration
+- From version 3.5
+ * Bug fix for TestCase.assertRaises()
+ * Enhance TestCase.assertRaises()
+- From version 3.4.1
+ * Bug fix for Python 3 syntax incompatibility
+- From version 3.4
+ * Promote command line test function to public API
+- From version 3.3
+ * Add humanfriendly.testing module (unittest helpers)
+ * Add humanfriendly.text.random_string() function
+ * Define humanfriendly.text.__all__
+ * Reorder functions in humanfriendly.text alphabetically
+- From version 3.2
+ * Auto-encode terminal output to avoid encoding errors
+ * Remove unused import
+- From version 3.1
+ * Improve usage message parsing and rendering
+- From version 3.0
+ * Add Python 3.6 to tested versions
+ * Silence flake8 complaints
+ * Add min, mins tests
+ * Support min, mins abbreviations for minutes
+- From version 2.4
+ * Restore Python 3 compatibility
+ * Make usage() and show_pager() more user friendly
+ * Remove redundant :py: prefixes in docstrings
+ * Improve docstrings in setup.py
+- From version 2.3.2
+ * Bug fix: Don't hard code conditional dependencies in wheels
+- From version 2.3.1
+ * Fix parse_usage() tripping up on commas in option labels
+ * Break test suite to reproduce parse_usage() bug
+- From version 2.3
+ * Switch to monotonic clock for timers
+ * Bump copyright
+ * Minor improvements to setup script
+ * Improve intersphinx references
+ * s/readthedocs.org/readthedocs.io/g
+- From version 2.2.1
+ * Fix timers being awkward as context managers
+ * Reproduce timers being awkward as context managers
+ * Minor improvements to reStructuredText in docstrings
+- From version 2.2
+ * Fix parse_date() choking on Unicode strings
+ * Reproduce parse_date() bug in test suite
+ * Fix flake8 warning
+ * Only use readline hints in prompts when stdin is tty
+
+-------------------------------------------------------------------
Old:
----
humanfriendly-4.4.1.tar.gz
New:
----
humanfriendly-4.8.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-humanfriendly.spec ++++++
--- /var/tmp/diff_new_pack.WvfYGp/_old 2018-02-09 15:52:03.501667888 +0100
+++ /var/tmp/diff_new_pack.WvfYGp/_new 2018-02-09 15:52:03.505667744 +0100
@@ -1,7 +1,7 @@
#
# spec file for package python-humanfriendly
#
-# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -20,18 +20,18 @@
# Dependency loop with colorlogs
%bcond_with test
Name: python-humanfriendly
-Version: 4.4.1
+Version: 4.8
Release: 0
Summary: Human friendly input/output for text interfaces using Python
License: MIT
Group: Development/Languages/Python
Url: https://github.com/xolox/humanfriendly
Source:
https://files.pythonhosted.org/packages/source/h/humanfriendly/humanfriendly-%{version}.tar.gz
-BuildRequires: fdupes
-BuildRequires: python-rpm-macros
BuildRequires: %{python_module devel}
BuildRequires: %{python_module setuptools}
+BuildRequires: fdupes
BuildRequires: python-monotonic
+BuildRequires: python-rpm-macros
%if %{with test}
BuildRequires: %{python_module coloredlogs >= 2}
%endif
++++++ humanfriendly-4.4.1.tar.gz -> humanfriendly-4.8.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/humanfriendly-4.4.1/LICENSE.txt
new/humanfriendly-4.8/LICENSE.txt
--- old/humanfriendly-4.4.1/LICENSE.txt 2017-03-01 02:42:51.000000000 +0100
+++ new/humanfriendly-4.8/LICENSE.txt 2018-01-04 13:06:45.000000000 +0100
@@ -1,4 +1,4 @@
-Copyright (c) 2017 Peter Odding
+Copyright (c) 2018 Peter Odding
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/humanfriendly-4.4.1/PKG-INFO
new/humanfriendly-4.8/PKG-INFO
--- old/humanfriendly-4.4.1/PKG-INFO 2017-08-07 22:59:43.000000000 +0200
+++ new/humanfriendly-4.8/PKG-INFO 2018-01-20 21:19:22.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: humanfriendly
-Version: 4.4.1
+Version: 4.8
Summary: Human friendly output for text interfaces using Python
Home-page: https://humanfriendly.readthedocs.io
Author: Peter Odding
@@ -32,7 +32,8 @@
size.
The `humanfriendly` package is currently tested on Python 2.6, 2.7,
3.4, 3.5,
- 3.6 and PyPy (2.7).
+ 3.6 and PyPy (2.7) on Linux and Mac OS X. While the intention is to
support
+ Windows as well, you may encounter some rough edges.
.. contents::
:local:
@@ -91,12 +92,16 @@
formatted number to standard output."
"``-s``, ``--format-size=BYTES``","Convert a byte count (given as
the integer ``BYTES``) into a human readable
string and print that string to standard output."
+ "``-b``, ``--binary``","Change the output of ``-s``,
``--format-size`` to use binary multiples of bytes
+ (base-2) instead of the default decimal multiples of bytes
(base-10)."
"``-t``, ``--format-timespan=SECONDS``","Convert a number of
seconds (given as the floating point number ``SECONDS``)
into a human readable timespan and print that string to standard
output."
- ``--parse-size=VALUE``,"Parse a human readable data size (given as
the string ``VALUE``) and print the
- number of bytes to standard output."
``--parse-length=VALUE``,"Parse a human readable length (given as
the string ``VALUE``) and print the
number of metres to standard output."
+ ``--parse-size=VALUE``,"Parse a human readable data size (given as
the string ``VALUE``) and print the
+ number of bytes to standard output."
+ ``--demo``,"Demonstrate changing the style and color of the
terminal font using ANSI
+ escape sequences."
"``-h``, ``--help``",Show this message and exit.
.. [[[end]]]
@@ -139,7 +144,7 @@
This software is licensed under the `MIT license`_.
- © 2017 Peter Odding.
+ © 2018 Peter Odding.
.. External references:
.. _#4: https://github.com/xolox/python-humanfriendly/issues/4
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/humanfriendly-4.4.1/README.rst
new/humanfriendly-4.8/README.rst
--- old/humanfriendly-4.4.1/README.rst 2017-06-29 00:08:23.000000000 +0200
+++ new/humanfriendly-4.8/README.rst 2018-01-04 21:39:27.000000000 +0100
@@ -24,7 +24,8 @@
size.
The `humanfriendly` package is currently tested on Python 2.6, 2.7, 3.4, 3.5,
-3.6 and PyPy (2.7).
+3.6 and PyPy (2.7) on Linux and Mac OS X. While the intention is to support
+Windows as well, you may encounter some rough edges.
.. contents::
:local:
@@ -83,12 +84,16 @@
formatted number to standard output."
"``-s``, ``--format-size=BYTES``","Convert a byte count (given as the
integer ``BYTES``) into a human readable
string and print that string to standard output."
+ "``-b``, ``--binary``","Change the output of ``-s``, ``--format-size`` to
use binary multiples of bytes
+ (base-2) instead of the default decimal multiples of bytes (base-10)."
"``-t``, ``--format-timespan=SECONDS``","Convert a number of seconds (given
as the floating point number ``SECONDS``)
into a human readable timespan and print that string to standard output."
- ``--parse-size=VALUE``,"Parse a human readable data size (given as the
string ``VALUE``) and print the
- number of bytes to standard output."
``--parse-length=VALUE``,"Parse a human readable length (given as the
string ``VALUE``) and print the
number of metres to standard output."
+ ``--parse-size=VALUE``,"Parse a human readable data size (given as the
string ``VALUE``) and print the
+ number of bytes to standard output."
+ ``--demo``,"Demonstrate changing the style and color of the terminal font
using ANSI
+ escape sequences."
"``-h``, ``--help``",Show this message and exit.
.. [[[end]]]
@@ -131,7 +136,7 @@
This software is licensed under the `MIT license`_.
-© 2017 Peter Odding.
+© 2018 Peter Odding.
.. External references:
.. _#4: https://github.com/xolox/python-humanfriendly/issues/4
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/humanfriendly-4.4.1/docs/conf.py
new/humanfriendly-4.8/docs/conf.py
--- old/humanfriendly-4.4.1/docs/conf.py 2017-06-29 00:08:23.000000000
+0200
+++ new/humanfriendly-4.8/docs/conf.py 2018-01-04 13:06:45.000000000 +0100
@@ -32,7 +32,7 @@
# General information about the project.
project = u'humanfriendly'
-copyright = u'2017, Peter Odding'
+copyright = u'2018, Peter Odding'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@@ -73,7 +73,4 @@
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
-html_theme = 'default'
-
-# Output file base name for HTML help builder.
-htmlhelp_basename = 'humanfriendlydoc'
+html_theme = 'nature'
Binary files old/humanfriendly-4.4.1/docs/images/ansi-demo.png and
new/humanfriendly-4.8/docs/images/ansi-demo.png differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/humanfriendly-4.4.1/humanfriendly/__init__.py
new/humanfriendly-4.8/humanfriendly/__init__.py
--- old/humanfriendly-4.4.1/humanfriendly/__init__.py 2017-08-07
22:59:18.000000000 +0200
+++ new/humanfriendly-4.8/humanfriendly/__init__.py 2018-01-20
21:19:02.000000000 +0100
@@ -1,7 +1,7 @@
# Human friendly input/output in Python.
#
# Author: Peter Odding <[email protected]>
-# Last Change: August 7, 2017
+# Last Change: January 20, 2018
# URL: https://humanfriendly.readthedocs.io
"""The main module of the `humanfriendly` package."""
@@ -39,7 +39,7 @@
from humanfriendly.compat import is_string, monotonic
# Semi-standard module versioning.
-__version__ = '4.4.1'
+__version__ = '4.8'
# Spinners are redrawn at most this many seconds.
minimum_spinner_interval = 0.2
@@ -52,6 +52,7 @@
hide_cursor_code = '\x1b[?25l'
show_cursor_code = '\x1b[?25h'
+# Named tuples to define units of size.
SizeUnit = collections.namedtuple('SizeUnit', 'divider, symbol, name')
CombinedUnit = collections.namedtuple('CombinedUnit', 'decimal, binary')
@@ -62,6 +63,9 @@
CombinedUnit(SizeUnit(1000**3, 'GB', 'gigabyte'), SizeUnit(1024**3, 'GiB',
'gibibyte')),
CombinedUnit(SizeUnit(1000**4, 'TB', 'terabyte'), SizeUnit(1024**4, 'TiB',
'tebibyte')),
CombinedUnit(SizeUnit(1000**5, 'PB', 'petabyte'), SizeUnit(1024**5, 'PiB',
'pebibyte')),
+ CombinedUnit(SizeUnit(1000**6, 'EB', 'exabyte'), SizeUnit(1024**6, 'EiB',
'exbibyte')),
+ CombinedUnit(SizeUnit(1000**7, 'ZB', 'zettabyte'), SizeUnit(1024**7,
'ZiB', 'zebibyte')),
+ CombinedUnit(SizeUnit(1000**8, 'YB', 'yottabyte'), SizeUnit(1024**8,
'YiB', 'yobibyte')),
)
# Common length size units, used for formatting and parsing.
@@ -104,11 +108,33 @@
return False
else:
msg = "Failed to coerce string to boolean! (%r)"
- raise ValueError(msg % value)
+ raise ValueError(format(msg, value))
else:
return bool(value)
+def coerce_pattern(value, flags=0):
+ """
+ Coerce strings to compiled regular expressions.
+
+ :param value: A string containing a regular expression pattern
+ or a compiled regular expression.
+ :param flags: The flags used to compile the pattern (an integer).
+ :returns: A compiled regular expression.
+ :raises: :exc:`~exceptions.ValueError` when `value` isn't a string
+ and also isn't a compiled regular expression.
+ """
+ if is_string(value):
+ value = re.compile(value, flags)
+ else:
+ empty_pattern = re.compile('')
+ pattern_type = type(empty_pattern)
+ if not isinstance(value, pattern_type):
+ msg = "Failed to coerce value to compiled regular expression! (%r)"
+ raise ValueError(format(msg, value))
+ return value
+
+
def format_size(num_bytes, keep_width=False, binary=False):
"""
Format a byte count as a human readable file size.
@@ -205,7 +231,7 @@
return int(tokens[0] * (unit.binary.divider if binary else
unit.decimal.divider))
# We failed to parse the size specification.
msg = "Failed to parse size! (input %r was tokenized as %r)"
- raise InvalidSize(msg % (size, tokens))
+ raise InvalidSize(format(msg, size, tokens))
def format_length(num_metres, keep_width=False):
@@ -274,7 +300,7 @@
return tokens[0] * unit['divider']
# We failed to parse the length specification.
msg = "Failed to parse length! (input %r was tokenized as %r)"
- raise InvalidLength(msg % (length, tokens))
+ raise InvalidLength(format(msg, length, tokens))
def format_number(number, num_decimals=2):
@@ -461,7 +487,7 @@
return float(tokens[0]) * unit['divider']
# We failed to parse the timespan specification.
msg = "Failed to parse timespan! (input %r was tokenized as %r)"
- raise InvalidTimespan(msg % (timespan, tokens))
+ raise InvalidTimespan(format(msg, timespan, tokens))
def parse_date(datestring):
@@ -532,7 +558,7 @@
return (year, month, day, 0, 0, 0)
except Exception:
msg = "Invalid date! (expected 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
but got: %r)"
- raise InvalidDate(msg % datestring)
+ raise InvalidDate(format(msg, datestring))
def format_path(pathname):
@@ -933,7 +959,7 @@
>>> parse_date('2013-06-XY')
Traceback (most recent call last):
File "humanfriendly.py", line 206, in parse_date
- raise InvalidDate, msg % datestring
+ raise InvalidDate(format(msg, datestring))
humanfriendly.InvalidDate: Invalid date! (expected 'YYYY-MM-DD' or
'YYYY-MM-DD HH:MM:SS' but got: '2013-06-XY')
"""
@@ -949,7 +975,7 @@
>>> parse_size('5 Z')
Traceback (most recent call last):
File "humanfriendly/__init__.py", line 267, in parse_size
- raise InvalidSize(msg % (size, tokens))
+ raise InvalidSize(format(msg, size, tokens))
humanfriendly.InvalidSize: Failed to parse size! (input '5 Z' was
tokenized as [5, 'Z'])
"""
@@ -965,7 +991,7 @@
>>> parse_length('5 Z')
Traceback (most recent call last):
File "humanfriendly/__init__.py", line 267, in parse_length
- raise InvalidLength(msg % (length, tokens))
+ raise InvalidLength(format(msg, length, tokens))
humanfriendly.InvalidLength: Failed to parse length! (input '5 Z' was
tokenized as [5, 'Z'])
"""
@@ -981,6 +1007,6 @@
>>> parse_timespan('1 age')
Traceback (most recent call last):
File "humanfriendly/__init__.py", line 419, in parse_timespan
- raise InvalidTimespan(msg % (timespan, tokens))
+ raise InvalidTimespan(format(msg, timespan, tokens))
humanfriendly.InvalidTimespan: Failed to parse timespan! (input '1 age'
was tokenized as [1, 'age'])
"""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/humanfriendly-4.4.1/humanfriendly/cli.py
new/humanfriendly-4.8/humanfriendly/cli.py
--- old/humanfriendly-4.4.1/humanfriendly/cli.py 2017-06-29
00:08:23.000000000 +0200
+++ new/humanfriendly-4.8/humanfriendly/cli.py 2018-01-14 03:27:50.000000000
+0100
@@ -1,7 +1,7 @@
# Human friendly input/output in Python.
#
# Author: Peter Odding <[email protected]>
-# Last Change: May 18, 2017
+# Last Change: January 14, 2018
# URL: https://humanfriendly.readthedocs.io
"""
@@ -46,20 +46,30 @@
Convert a byte count (given as the integer BYTES) into a human readable
string and print that string to standard output.
+ -b, --binary
+
+ Change the output of -s, --format-size to use binary multiples of bytes
+ (base-2) instead of the default decimal multiples of bytes (base-10).
+
-t, --format-timespan=SECONDS
Convert a number of seconds (given as the floating point number SECONDS)
into a human readable timespan and print that string to standard output.
+ --parse-length=VALUE
+
+ Parse a human readable length (given as the string VALUE) and print the
+ number of metres to standard output.
+
--parse-size=VALUE
Parse a human readable data size (given as the string VALUE) and print the
number of bytes to standard output.
- --parse-length=VALUE
+ --demo
- Parse a human readable length (given as the string VALUE) and print the
- number of metres to standard output.
+ Demonstrate changing the style and color of the terminal font using ANSI
+ escape sequences.
-h, --help
@@ -75,6 +85,8 @@
# Modules included in our package.
from humanfriendly import (
+ Spinner,
+ Timer,
format_length,
format_number,
format_size,
@@ -82,19 +94,28 @@
format_timespan,
parse_length,
parse_size,
- Spinner,
- Timer,
)
-from humanfriendly.terminal import output, usage, warning
+from humanfriendly.tables import format_smart_table
+from humanfriendly.terminal import (
+ ANSI_COLOR_CODES,
+ ANSI_TEXT_STYLES,
+ HIGHLIGHT_COLOR,
+ ansi_strip,
+ ansi_wrap,
+ find_terminal_size,
+ output,
+ usage,
+ warning,
+)
def main():
"""Command line interface for the ``humanfriendly`` program."""
try:
- options, arguments = getopt.getopt(sys.argv[1:], 'cd:hn:s:t:', [
- 'delimiter=', 'format-length=', 'format-number=', 'format-size=',
- 'format-table', 'format-timespan=', 'parse-length=',
- 'parse-size=', 'run-command', 'help',
+ options, arguments = getopt.getopt(sys.argv[1:], 'cd:l:n:s:bt:h', [
+ 'run-command', 'format-table', 'delimiter=', 'format-length=',
+ 'format-number=', 'format-size=', 'binary', 'format-timespan=',
+ 'parse-length=', 'parse-size=', 'demo', 'help',
])
except Exception as e:
warning("Error: %s", e)
@@ -102,6 +123,7 @@
actions = []
delimiter = None
should_format_table = False
+ binary = any(o in ('-b', '--binary') for o, v in options)
for option, value in options:
if option in ('-d', '--delimiter'):
delimiter = value
@@ -116,11 +138,13 @@
elif option in ('-n', '--format-number'):
actions.append(functools.partial(print_formatted_number, value))
elif option in ('-s', '--format-size'):
- actions.append(functools.partial(print_formatted_size, value))
+ actions.append(functools.partial(print_formatted_size, value,
binary))
elif option == '--format-table':
should_format_table = True
elif option in ('-t', '--format-timespan'):
actions.append(functools.partial(print_formatted_timespan, value))
+ elif option == '--demo':
+ actions.append(demonstrate_ansi_formatting)
elif option in ('-h', '--help'):
usage(__doc__)
return
@@ -160,9 +184,9 @@
output(format_number(float(value)))
-def print_formatted_size(value):
+def print_formatted_size(value, binary):
"""Print a human readable size."""
- output(format_size(int(value)))
+ output(format_size(int(value), binary=binary))
def print_formatted_table(delimiter):
@@ -187,3 +211,65 @@
def print_parsed_size(value):
"""Parse a human readable data size and print the number of bytes."""
output(parse_size(value))
+
+
+def demonstrate_ansi_formatting():
+ """Demonstrate the use of ANSI escape sequences."""
+ # First we demonstrate the supported text styles.
+ output('%s', ansi_wrap('Text styles:', bold=True))
+ styles = ['normal', 'bright']
+ styles.extend(ANSI_TEXT_STYLES.keys())
+ for style_name in sorted(styles):
+ options = dict(color=HIGHLIGHT_COLOR)
+ if style_name != 'normal':
+ options[style_name] = True
+ style_label = style_name.replace('_', ' ').capitalize()
+ output(' - %s', ansi_wrap(style_label, **options))
+ # Now we demonstrate named foreground and background colors.
+ for color_type, color_label in (('color', 'Foreground colors'),
+ ('background', 'Background colors')):
+ intensities = [
+ ('normal', dict()),
+ ('bright', dict(bright=True)),
+ ]
+ if color_type != 'background':
+ intensities.insert(0, ('faint', dict(faint=True)))
+ output('\n%s' % ansi_wrap('%s:' % color_label, bold=True))
+ output(format_smart_table([
+ [color_name] + [
+ ansi_wrap(
+ 'XXXXXX' if color_type != 'background' else (' ' * 6),
+ **dict(kw.items() + [(color_type, color_name)])
+ ) for label, kw in intensities
+ ] for color_name in sorted(ANSI_COLOR_CODES.keys())
+ ], column_names=['Color'] + [
+ label.capitalize() for label, kw in intensities
+ ]))
+ # Demonstrate support for 256 colors as well.
+ demonstrate_256_colors(0, 7, 'standard colors')
+ demonstrate_256_colors(8, 15, 'high-intensity colors')
+ demonstrate_256_colors(16, 231, '216 colors')
+ demonstrate_256_colors(232, 255, 'gray scale colors')
+
+
+def demonstrate_256_colors(i, j, group=None):
+ """Demonstrate 256 color mode support."""
+ # Generate the label.
+ label = '256 color mode'
+ if group:
+ label += ' (%s)' % group
+ output('\n' + ansi_wrap('%s:' % label, bold=True))
+ # Generate a simple rendering of the colors in the requested range and
+ # check if it will fit on a single line (given the terminal's width).
+ single_line = ''.join(' ' + ansi_wrap(str(n), color=n) for n in range(i, j
+ 1))
+ lines, columns = find_terminal_size()
+ if columns >= len(ansi_strip(single_line)):
+ output(single_line)
+ else:
+ # Generate a more complex rendering of the colors that will nicely wrap
+ # over multiple lines without using too many lines.
+ width = len(str(j)) + 1
+ colors_per_line = columns / width
+ colors = [ansi_wrap(str(n).rjust(width), color=n) for n in range(i, j
+ 1)]
+ blocks = [colors[n:n + colors_per_line] for n in range(0, len(colors),
colors_per_line)]
+ output('\n'.join(''.join(b) for b in blocks))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/humanfriendly-4.4.1/humanfriendly/terminal.py
new/humanfriendly-4.8/humanfriendly/terminal.py
--- old/humanfriendly-4.4.1/humanfriendly/terminal.py 2017-06-29
00:08:23.000000000 +0200
+++ new/humanfriendly-4.8/humanfriendly/terminal.py 2018-01-14
03:27:50.000000000 +0100
@@ -1,7 +1,7 @@
# Human friendly input/output in Python.
#
# Author: Peter Odding <[email protected]>
-# Last Change: May 18, 2017
+# Last Change: January 14, 2018
# URL: https://humanfriendly.readthedocs.io
"""
@@ -18,6 +18,7 @@
# Standard library modules.
import codecs
+import numbers
import os
import re
import subprocess
@@ -190,10 +191,18 @@
"""
Generate ANSI escape sequences for the given color and/or style(s).
- :param color: The name of a color (one of the strings 'black', 'red',
- 'green', 'yellow', 'blue', 'magenta', 'cyan' or 'white') or
- :data:`None` (the default) which means no escape sequence to
- switch color will be emitted.
+ :param color: The foreground color. Two types of values are supported:
+
+ - The name of a color (one of the strings 'black', 'red',
+ 'green', 'yellow', 'blue', 'magenta', 'cyan' or 'white').
+ - An integer that refers to the 256 color mode palette.
+
+ The value :data:`None` (the default) means no escape
+ sequence to switch color will be emitted.
+ :param background: The background color (see the description
+ of the `color` argument).
+ :param bright: Use high intensity colors instead of default colors
+ (a boolean, defaults to :data:`False`).
:param readline_hints: If :data:`True` then :func:`readline_wrap()` is
applied to the generated ANSI escape sequences (the
default is :data:`False`).
@@ -204,19 +213,51 @@
:returns: The ANSI escape sequences to enable the requested text styles or
an empty string if no styles were requested.
:raises: :exc:`~exceptions.ValueError` when an invalid color name is given.
+
+ Even though only eight named colors are supported, the use of `bright=True`
+ and `faint=True` increases the number of available colors to around 24 (it
+ may be slightly lower, for example because faint black is just black).
+
+ Starting in version 4.7 support for 256 color mode was added. While this
+ significantly increases the available colors it's not very human friendly
+ in usage because you need to look up color codes in the `256 color mode
+ palette <https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit>`_.
+
+ You can use the ``humanfriendly --demo`` command to get a demonstration of
+ the available colors, see also the screen shot below. Note that the small
+ font size in the screen shot was so that the demonstration of 256 color
+ mode support would fit into a single screen shot without scrolling :-)
+ (I wasn't feeling very creative).
+
+ .. image:: images/ansi-demo.png
"""
# Start with sequences that change text styles.
- sequences = [str(ANSI_TEXT_STYLES[k]) for k, v in kw.items() if k in
ANSI_TEXT_STYLES and v]
+ sequences = [ANSI_TEXT_STYLES[k] for k, v in kw.items() if k in
ANSI_TEXT_STYLES and v]
# Append the color code (if any).
- color_name = kw.get('color')
- if color_name:
- # Validate the color name.
- if color_name not in ANSI_COLOR_CODES:
- msg = "Invalid color name %r! (expected one of %s)"
- raise ValueError(msg % (color_name,
concatenate(sorted(ANSI_COLOR_CODES))))
- sequences.append('3%i' % ANSI_COLOR_CODES[color_name])
+ for color_type in 'color', 'background':
+ color_value = kw.get(color_type)
+ if isinstance(color_value, numbers.Number):
+ # Numeric values are assumed to be 256 color codes.
+ sequences.extend((
+ 39 if color_type == 'background' else 38,
+ 5, int(color_value)
+ ))
+ elif color_value:
+ # Other values are assumed to be strings containing one of the
known color names.
+ if color_value not in ANSI_COLOR_CODES:
+ msg = "Invalid color value %r! (expected an integer or one of
the strings %s)"
+ raise ValueError(msg % (color_value, concatenate(map(repr,
sorted(ANSI_COLOR_CODES)))))
+ # Pick the right offset for foreground versus background
+ # colors and regular intensity versus bright colors.
+ offset = (
+ (100 if kw.get('bright') else 40)
+ if color_type == 'background'
+ else (90 if kw.get('bright') else 30)
+ )
+ # Combine the offset and color code into a single integer.
+ sequences.append(offset + ANSI_COLOR_CODES[color_value])
if sequences:
- encoded = ANSI_CSI + ';'.join(sequences) + ANSI_SGR
+ encoded = ANSI_CSI + ';'.join(map(str, sequences)) + ANSI_SGR
return readline_wrap(encoded) if kw.get('readline_hints') else encoded
else:
return ''
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/humanfriendly-4.4.1/humanfriendly/tests.py
new/humanfriendly-4.8/humanfriendly/tests.py
--- old/humanfriendly-4.4.1/humanfriendly/tests.py 2017-08-07
22:55:14.000000000 +0200
+++ new/humanfriendly-4.8/humanfriendly/tests.py 2018-01-20
21:19:02.000000000 +0100
@@ -4,7 +4,7 @@
# Tests for the `humanfriendly' package.
#
# Author: Peter Odding <[email protected]>
-# Last Change: July 16, 2017
+# Last Change: January 20, 2018
# URL: https://humanfriendly.readthedocs.io
"""Test suite for the `humanfriendly` package."""
@@ -12,6 +12,7 @@
# Standard library modules.
import math
import os
+import re
import random
import subprocess
import sys
@@ -21,7 +22,7 @@
# Modules included in our package.
import humanfriendly
from humanfriendly import prompts
-from humanfriendly import compact, dedent, trim_empty_lines
+from humanfriendly import coerce_pattern, compact, dedent, trim_empty_lines
from humanfriendly.cli import main
from humanfriendly.compat import StringIO
from humanfriendly.prompts import (
@@ -52,8 +53,11 @@
connected_to_terminal,
find_terminal_size,
get_pager_command,
+ message,
+ output,
show_pager,
terminal_supports_colors,
+ warning,
)
from humanfriendly.testing import (
CallableTimedOut,
@@ -234,6 +238,19 @@
self.assertEqual(False, humanfriendly.coerce_boolean(value))
self.assertRaises(ValueError, humanfriendly.coerce_boolean, 'not a
boolean')
+ def test_pattern_coercion(self):
+ """Test :func:`humanfriendly.coerce_pattern()`."""
+ empty_pattern = re.compile('')
+ # Make sure strings are converted to compiled regular expressions.
+ assert isinstance(coerce_pattern('foobar'), type(empty_pattern))
+ # Make sure compiled regular expressions pass through untouched.
+ assert empty_pattern is coerce_pattern(empty_pattern)
+ # Make sure flags are respected.
+ pattern = coerce_pattern('foobar', re.IGNORECASE)
+ assert pattern.match('FOOBAR')
+ # Make sure invalid values raise the expected exception.
+ self.assertRaises(ValueError, coerce_pattern, [])
+
def test_format_timespan(self):
"""Test :func:`humanfriendly.format_timespan()`."""
minute = 60
@@ -313,11 +330,17 @@
self.assertEqual('1 GB', humanfriendly.format_size(1000 ** 3))
self.assertEqual('1 TB', humanfriendly.format_size(1000 ** 4))
self.assertEqual('1 PB', humanfriendly.format_size(1000 ** 5))
+ self.assertEqual('1 EB', humanfriendly.format_size(1000 ** 6))
+ self.assertEqual('1 ZB', humanfriendly.format_size(1000 ** 7))
+ self.assertEqual('1 YB', humanfriendly.format_size(1000 ** 8))
self.assertEqual('1 KiB', humanfriendly.format_size(1024 ** 1,
binary=True))
self.assertEqual('1 MiB', humanfriendly.format_size(1024 ** 2,
binary=True))
self.assertEqual('1 GiB', humanfriendly.format_size(1024 ** 3,
binary=True))
self.assertEqual('1 TiB', humanfriendly.format_size(1024 ** 4,
binary=True))
self.assertEqual('1 PiB', humanfriendly.format_size(1024 ** 5,
binary=True))
+ self.assertEqual('1 EiB', humanfriendly.format_size(1024 ** 6,
binary=True))
+ self.assertEqual('1 ZiB', humanfriendly.format_size(1024 ** 7,
binary=True))
+ self.assertEqual('1 YiB', humanfriendly.format_size(1024 ** 8,
binary=True))
self.assertEqual('45 KB', humanfriendly.format_size(1000 * 45))
self.assertEqual('2.9 TB', humanfriendly.format_size(1000 ** 4 * 2.9))
@@ -333,8 +356,14 @@
self.assertEqual(1024, humanfriendly.parse_size('1 kilobyte',
binary=True))
self.assertEqual(1000 ** 2 * 69, humanfriendly.parse_size('69 MB'))
self.assertEqual(1000 ** 3, humanfriendly.parse_size('1 GB'))
+ self.assertEqual(1000 ** 4, humanfriendly.parse_size('1 TB'))
+ self.assertEqual(1000 ** 5, humanfriendly.parse_size('1 PB'))
+ self.assertEqual(1000 ** 6, humanfriendly.parse_size('1 EB'))
+ self.assertEqual(1000 ** 7, humanfriendly.parse_size('1 ZB'))
+ self.assertEqual(1000 ** 8, humanfriendly.parse_size('1 YB'))
self.assertEqual(1000 ** 3 * 1.5, humanfriendly.parse_size('1.5 GB'))
- self.assertRaises(humanfriendly.InvalidSize, humanfriendly.parse_size,
'1z')
+ self.assertEqual(1024 ** 8 * 1.5, humanfriendly.parse_size('1.5 YiB'))
+ self.assertRaises(humanfriendly.InvalidSize, humanfriendly.parse_size,
'1q')
self.assertRaises(humanfriendly.InvalidSize, humanfriendly.parse_size,
'a')
def test_format_length(self):
@@ -527,19 +556,17 @@
# Test automatic timer.
automatic_timer = humanfriendly.Timer()
time.sleep(1)
- self.assertEqual(normalize_timestamp(humanfriendly.round_number(
- automatic_timer.elapsed_time,
- keep_width=True,
- )), '1.00')
+ # XXX The following normalize_timestamp(ndigits=0) calls are intended
+ # to compensate for unreliable clock sources in virtual machines
+ # like those encountered on Travis CI, see also:
+ # https://travis-ci.org/xolox/python-humanfriendly/jobs/323944263
+ self.assertEqual(normalize_timestamp(automatic_timer.elapsed_time, 0),
'1.00')
# Test resumable timer.
resumable_timer = humanfriendly.Timer(resumable=True)
for i in range(2):
with resumable_timer:
time.sleep(1)
- self.assertEqual(normalize_timestamp(humanfriendly.round_number(
- resumable_timer.elapsed_time,
- keep_width=True,
- )), '2.00')
+ self.assertEqual(normalize_timestamp(resumable_timer.elapsed_time, 0),
'2.00')
# Make sure Timer.__enter__() returns the timer object.
with humanfriendly.Timer(resumable=True) as timer:
assert timer is not None
@@ -672,6 +699,17 @@
random_byte_count = random.randint(1024, 1024 * 1024)
returncode, output = run_cli(main, '--format-size=%i' %
random_byte_count)
assert output.strip() == humanfriendly.format_size(random_byte_count)
+ # Test `humanfriendly --format-size --binary'.
+ random_byte_count = random.randint(1024, 1024 * 1024)
+ returncode, output = run_cli(main, '--format-size=%i' %
random_byte_count, '--binary')
+ assert output.strip() == humanfriendly.format_size(random_byte_count,
binary=True)
+ # Test `humanfriendly --format-length'.
+ random_len = random.randint(1024, 1024 * 1024)
+ returncode, output = run_cli(main, '--format-length=%i' % random_len)
+ assert output.strip() == humanfriendly.format_length(random_len)
+ random_len = float(random_len) / 12345.6
+ returncode, output = run_cli(main, '--format-length=%f' % random_len)
+ assert output.strip() == humanfriendly.format_length(random_len)
# Test `humanfriendly --format-table'.
returncode, output = run_cli(main, '--format-table', '--delimiter=\t',
input='1\t2\t3\n4\t5\t6\n7\t8\t9')
assert output.strip() == dedent('''
@@ -688,6 +726,14 @@
# Test `humanfriendly --parse-size'.
returncode, output = run_cli(main, '--parse-size=5 KB')
assert int(output) == humanfriendly.parse_size('5 KB')
+ # Test `humanfriendly --parse-size'.
+ returncode, output = run_cli(main, '--parse-size=5 YiB')
+ assert int(output) == humanfriendly.parse_size('5 YB', binary=True)
+ # Test `humanfriendly --parse-length'.
+ returncode, output = run_cli(main, '--parse-length=5 km')
+ assert int(output) == humanfriendly.parse_length('5 km')
+ returncode, output = run_cli(main, '--parse-length=1.05 km')
+ assert float(output) == humanfriendly.parse_length('1.05 km')
# Test `humanfriendly --run-command'.
returncode, output = run_cli(main, '--run-command', 'bash', '-c',
'sleep 2 && exit 42')
assert returncode == 42
@@ -700,6 +746,10 @@
assert ansi_style(inverse=True) == '%s7%s' % (ANSI_CSI, ANSI_SGR)
assert ansi_style(strike_through=True) == '%s9%s' % (ANSI_CSI,
ANSI_SGR)
assert ansi_style(color='blue') == '%s34%s' % (ANSI_CSI, ANSI_SGR)
+ assert ansi_style(background='blue') == '%s44%s' % (ANSI_CSI, ANSI_SGR)
+ assert ansi_style(color='blue', bright=True) == '%s94%s' % (ANSI_CSI,
ANSI_SGR)
+ assert ansi_style(color=214) == '%s38;5;214%s' % (ANSI_CSI, ANSI_SGR)
+ assert ansi_style(background=214) == '%s39;5;214%s' % (ANSI_CSI,
ANSI_SGR)
self.assertRaises(ValueError, ansi_style, color='unknown')
def test_ansi_width(self):
@@ -726,6 +776,41 @@
# Make sure ansi_wrap() ends the text by resetting the ANSI styles.
assert ansi_wrap(text, bold=True).endswith(ANSI_RESET)
+ def test_generate_output(self):
+ """Test the :func:`humanfriendly.terminal.output()` function."""
+ text = "Standard output generated by output()"
+ with CaptureOutput(merged=False) as capturer:
+ output(text)
+ self.assertEqual([text], capturer.stdout.get_lines())
+ self.assertEqual([], self.ignore_coverage_warning(capturer))
+
+ def test_generate_message(self):
+ """Test the :func:`humanfriendly.terminal.message()` function."""
+ text = "Standard error generated by message()"
+ with CaptureOutput(merged=False) as capturer:
+ message(text)
+ self.assertEqual([], capturer.stdout.get_lines())
+ self.assertIn(text, self.ignore_coverage_warning(capturer))
+
+ def test_generate_warning(self):
+ """Test the output(), message() and warning() functions."""
+ text = "Standard error generated by warning()"
+ with CaptureOutput(merged=False) as capturer:
+ warning(text)
+ self.assertEqual([], capturer.stdout.get_lines())
+ self.assertEqual([ansi_wrap(text, color='red')],
self.ignore_coverage_warning(capturer))
+
+ def ignore_coverage_warning(self, capturer):
+ """
+ Filter out coverage.py warning from standard error.
+
+ This is intended to remove the following line from the lines captured
+ on the standard error stream:
+
+ Coverage.py warning: No data was collected. (no-data-collected)
+ """
+ return [line for line in capturer.stderr.get_lines() if
'no-data-collected' not in line]
+
def test_clean_output(self):
"""Test :func:`humanfriendly.terminal.clean_terminal_output()`."""
# Simple output should pass through unharmed (single line).
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/humanfriendly-4.4.1/humanfriendly.egg-info/PKG-INFO
new/humanfriendly-4.8/humanfriendly.egg-info/PKG-INFO
--- old/humanfriendly-4.4.1/humanfriendly.egg-info/PKG-INFO 2017-08-07
22:59:43.000000000 +0200
+++ new/humanfriendly-4.8/humanfriendly.egg-info/PKG-INFO 2018-01-20
21:19:21.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: humanfriendly
-Version: 4.4.1
+Version: 4.8
Summary: Human friendly output for text interfaces using Python
Home-page: https://humanfriendly.readthedocs.io
Author: Peter Odding
@@ -32,7 +32,8 @@
size.
The `humanfriendly` package is currently tested on Python 2.6, 2.7,
3.4, 3.5,
- 3.6 and PyPy (2.7).
+ 3.6 and PyPy (2.7) on Linux and Mac OS X. While the intention is to
support
+ Windows as well, you may encounter some rough edges.
.. contents::
:local:
@@ -91,12 +92,16 @@
formatted number to standard output."
"``-s``, ``--format-size=BYTES``","Convert a byte count (given as
the integer ``BYTES``) into a human readable
string and print that string to standard output."
+ "``-b``, ``--binary``","Change the output of ``-s``,
``--format-size`` to use binary multiples of bytes
+ (base-2) instead of the default decimal multiples of bytes
(base-10)."
"``-t``, ``--format-timespan=SECONDS``","Convert a number of
seconds (given as the floating point number ``SECONDS``)
into a human readable timespan and print that string to standard
output."
- ``--parse-size=VALUE``,"Parse a human readable data size (given as
the string ``VALUE``) and print the
- number of bytes to standard output."
``--parse-length=VALUE``,"Parse a human readable length (given as
the string ``VALUE``) and print the
number of metres to standard output."
+ ``--parse-size=VALUE``,"Parse a human readable data size (given as
the string ``VALUE``) and print the
+ number of bytes to standard output."
+ ``--demo``,"Demonstrate changing the style and color of the
terminal font using ANSI
+ escape sequences."
"``-h``, ``--help``",Show this message and exit.
.. [[[end]]]
@@ -139,7 +144,7 @@
This software is licensed under the `MIT license`_.
- © 2017 Peter Odding.
+ © 2018 Peter Odding.
.. External references:
.. _#4: https://github.com/xolox/python-humanfriendly/issues/4
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/humanfriendly-4.4.1/humanfriendly.egg-info/SOURCES.txt
new/humanfriendly-4.8/humanfriendly.egg-info/SOURCES.txt
--- old/humanfriendly-4.4.1/humanfriendly.egg-info/SOURCES.txt 2017-08-07
22:59:43.000000000 +0200
+++ new/humanfriendly-4.8/humanfriendly.egg-info/SOURCES.txt 2018-01-20
21:19:21.000000000 +0100
@@ -7,6 +7,7 @@
setup.py
docs/conf.py
docs/index.rst
+docs/images/ansi-demo.png
docs/images/pretty-table.png
docs/images/spinner-basic.gif
docs/images/spinner-with-progress.gif
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/humanfriendly-4.4.1/humanfriendly.egg-info/requires.txt
new/humanfriendly-4.8/humanfriendly.egg-info/requires.txt
--- old/humanfriendly-4.4.1/humanfriendly.egg-info/requires.txt 2017-08-07
22:59:43.000000000 +0200
+++ new/humanfriendly-4.8/humanfriendly.egg-info/requires.txt 2018-01-20
21:19:21.000000000 +0100
@@ -5,3 +5,6 @@
[:python_version == "2.6" or python_version == "3.0"]
importlib
unittest2
+
+[:sys_platform == "win32"]
+pyreadline
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/humanfriendly-4.4.1/requirements-tests.txt
new/humanfriendly-4.8/requirements-tests.txt
--- old/humanfriendly-4.4.1/requirements-tests.txt 2017-06-29
00:08:23.000000000 +0200
+++ new/humanfriendly-4.8/requirements-tests.txt 2018-01-04
13:06:45.000000000 +0100
@@ -1,5 +1,9 @@
# Test suite requirements.
capturer >= 2.1
coloredlogs >= 2.0
-pytest >= 3.0.7
+pytest >= 3.0.7 ; python_version > '2.6'
pytest-cov >= 2.4.0
+
+# pytest release 3.3 drops Python 2.6 compatibility:
+# https://docs.pytest.org/en/latest/changelog.html#pytest-3-3-0-2017-11-23
+pytest < 3.3 ; python_version < '2.7'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/humanfriendly-4.4.1/setup.py
new/humanfriendly-4.8/setup.py
--- old/humanfriendly-4.4.1/setup.py 2017-07-01 22:45:42.000000000 +0200
+++ new/humanfriendly-4.8/setup.py 2018-01-04 13:06:45.000000000 +0100
@@ -3,7 +3,7 @@
# Setup script for the `humanfriendly' package.
#
# Author: Peter Odding <[email protected]>
-# Last Change: July 1, 2017
+# Last Change: January 1, 2018
# URL: https://humanfriendly.readthedocs.io
"""
@@ -50,6 +50,8 @@
install_requires.extend(('importlib', 'unittest2'))
if sys.version_info[:2] < (3, 3):
install_requires.append('monotonic')
+ if sys.platform == 'win32':
+ install_requires.append('pyreadline')
return sorted(install_requires)
@@ -57,7 +59,7 @@
"""Get the conditional dependencies for wheel distributions."""
extras_require = {}
if have_environment_marker_support():
- # Conditional `importlib' dependency.
+ # Conditional `importlib' and `unittest2' dependencies.
expression = ':%s' % ' or '.join([
'python_version == "2.6"',
'python_version == "3.0"',
@@ -72,6 +74,9 @@
'python_version == "3.2"',
])
extras_require[expression] = ['monotonic']
+ # Conditional `pyreadline' dependency.
+ expression = ':sys_platform == "win32"'
+ extras_require[expression] = 'pyreadline'
return extras_require