Hello community,

here is the log from the commit of package python-jaraco.text for 
openSUSE:Factory checked in at 2020-01-16 18:17:03
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-jaraco.text (Old)
 and      /work/SRC/openSUSE:Factory/.python-jaraco.text.new.26092 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-jaraco.text"

Thu Jan 16 18:17:03 2020 rev:4 rq:764934 version:3.2.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-jaraco.text/python-jaraco.text.changes    
2019-03-26 22:34:40.997673047 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-jaraco.text.new.26092/python-jaraco.text.changes
 2020-01-16 18:17:06.904834786 +0100
@@ -1,0 +2,7 @@
+Thu Jan 16 09:44:33 UTC 2020 - Tomáš Chvátal <tchva...@suse.com>
+
+- Update to 3.2.0:
+  * Added normalize_newlines function.
+  * Added wrap and unwrap functions and lorem_ipsum attribute containing the 
Lorem Ipsum sample text.
+
+-------------------------------------------------------------------

Old:
----
  jaraco.text-3.0.tar.gz

New:
----
  jaraco.text-3.2.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-jaraco.text.spec ++++++
--- /var/tmp/diff_new_pack.mGmOGz/_old  2020-01-16 18:17:08.104835465 +0100
+++ /var/tmp/diff_new_pack.mGmOGz/_new  2020-01-16 18:17:08.104835465 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-jaraco.text
 #
-# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2020 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -18,7 +18,7 @@
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-jaraco.text
-Version:        3.0
+Version:        3.2.0
 Release:        0
 Summary:        Tools to work with text
 License:        MIT
@@ -31,12 +31,15 @@
 BuildRequires:  %{python_module setuptools_scm}
 BuildRequires:  %{python_module setuptools}
 BuildRequires:  fdupes
+BuildRequires:  python-importlib_resources
 BuildRequires:  python-rpm-macros
 Requires:       python-jaraco.base >= 6.1
-Requires:       python-jaraco.collections
 Requires:       python-jaraco.functools
 Requires:       python-six
 BuildArch:      noarch
+%ifpython2
+Requires:       python-importlib_resources
+%endif
 %python_subpackages
 
 %description
@@ -44,7 +47,7 @@
 
 %prep
 %setup -q -n jaraco.text-%{version}
-sed -i 's/--flake8//' pytest.ini
+sed -i 's/--flake8 --black --cov//' pytest.ini
 rm -rf jaraco.text.egg-info
 
 %build
@@ -52,6 +55,7 @@
 
 %install
 %python_install
+%python_expand install -Dm 0644 jaraco/text/*.txt 
%{buildroot}%{$python_sitelib}/jaraco/text/
 
 %{python_expand rm -f %{buildroot}%{$python_sitelib}/jaraco/__init__.py* \
   %{buildroot}%{$python_sitelib}/jaraco/__pycache__/__init__.*
@@ -67,7 +71,6 @@
 %license LICENSE
 %doc docs/*.rst README.rst CHANGES.rst
 %{python_sitelib}/jaraco.text-%{version}-py*.egg-info
-%{python_sitelib}/jaraco/text.py*
-%pycache_only %{python_sitelib}/jaraco/__pycache__/text*.py*
+%{python_sitelib}/jaraco*
 
 %changelog

++++++ jaraco.text-3.0.tar.gz -> jaraco.text-3.2.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jaraco.text-3.0/.coveragerc 
new/jaraco.text-3.2.0/.coveragerc
--- old/jaraco.text-3.0/.coveragerc     1970-01-01 01:00:00.000000000 +0100
+++ new/jaraco.text-3.2.0/.coveragerc   2019-12-01 18:38:50.000000000 +0100
@@ -0,0 +1,5 @@
+[run]
+omit = .tox/*
+
+[report]
+show_missing = True
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jaraco.text-3.0/.flake8 new/jaraco.text-3.2.0/.flake8
--- old/jaraco.text-3.0/.flake8 2019-03-09 21:18:25.000000000 +0100
+++ new/jaraco.text-3.2.0/.flake8       2019-12-01 18:38:50.000000000 +0100
@@ -1,10 +1,9 @@
 [flake8]
+max-line-length = 88
 ignore =
-       # Allow tabs for indentation
-       W191
-       # Workaround for https://github.com/PyCQA/pycodestyle/issues/836
-       E117
        # W503 violates spec https://github.com/PyCQA/pycodestyle/issues/513
        W503
        # W504 has issues 
https://github.com/OCA/maintainer-quality-tools/issues/545
        W504
+       # Black creates whitespace before colon
+       E203
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jaraco.text-3.0/.pre-commit-config.yaml 
new/jaraco.text-3.2.0/.pre-commit-config.yaml
--- old/jaraco.text-3.0/.pre-commit-config.yaml 1970-01-01 01:00:00.000000000 
+0100
+++ new/jaraco.text-3.2.0/.pre-commit-config.yaml       2019-12-01 
18:38:50.000000000 +0100
@@ -0,0 +1,5 @@
+repos:
+- repo: https://github.com/psf/black
+  rev: 19.3b0
+  hooks:
+  - id: black
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jaraco.text-3.0/.travis.yml 
new/jaraco.text-3.2.0/.travis.yml
--- old/jaraco.text-3.0/.travis.yml     2019-03-09 21:18:25.000000000 +0100
+++ new/jaraco.text-3.2.0/.travis.yml   2019-12-01 18:38:50.000000000 +0100
@@ -1,11 +1,10 @@
 dist: xenial
-sudo: false
 language: python
 
 python:
 - 2.7
 - 3.6
-- &latest_py3 3.7
+- &latest_py3 3.8
 
 jobs:
   fast_finish: true
@@ -14,11 +13,6 @@
     if: tag IS present
     python: *latest_py3
     before_script: skip
-    env:
-    - TWINE_USERNAME=jaraco
-    # TWINE_PASSWORD
-    - secure: 
hRuKEsJzVjxdDsFGq+QNZX8BqCJuKqxO7WnZxu+a96lMqS3ER2Swi7pVUzdj+e2hzNPyetBTyhU8gI9A+FwBfOUHjiEZjjCm89/PKW3H5/QCS6u8298a9yTYQapraR6tmSxBq20t44DA5cb0mfqqynNJ90OSbh3drCOBw/8x4IZhjZA9ma3Y7H7jKVVBvHpGw8XFDy0tZGXkp3tJ3IrW6JNWj8/BdNfi0zX/z7e6sx9iWXMU9bUj1+hAW3uKhFWGBMO2i0r72xsl7JXWx2FSrLAOFDxh+kuBEjJnWO44rI+IYzKGWnmFNiuvrw9Ds5QQoCkgJLmn0i25qj+hhUXJHzUgNnG6xfmCk7EH6DWmpbd1QrPOxSsgJm6Z1EKETZ2jBosnRHi+Ica1QfphcQMoBQG8ZsHS1GumnIWuP2VhxgMpEur9Qf8w0T7GEpyRkgduq7MVumQNYdcU6lW3EQ6P99KQiIaAgGLM6ISMDszp7pC1tpc3F7lWiAU5IQih4vNNF1CVKtGFemPX+reikR5Mx4ft7ByfNLNPFOMF7K5Vc0beUydkuiiQToGChU5Aa7L2MfUuO/1KXQ6++J0mjTWcMSYk5DEUcupLqNS+9FkTGPDp91jpOmixJbBfY2LhGMAwTIi4+enmM3KoHUEwtuGQwJzY1/dw8ts+2R7jX9O+dBQ=
-    - TOX_TESTENV_PASSENV="TWINE_USERNAME TWINE_PASSWORD"
     script: tox -e release
 
 cache: pip
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jaraco.text-3.0/CHANGES.rst 
new/jaraco.text-3.2.0/CHANGES.rst
--- old/jaraco.text-3.0/CHANGES.rst     2019-03-09 21:18:25.000000000 +0100
+++ new/jaraco.text-3.2.0/CHANGES.rst   2019-12-01 18:38:50.000000000 +0100
@@ -1,3 +1,19 @@
+v3.2.0
+======
+
+Added normalize_newlines function.
+
+3.1
+===
+
+Added ``wrap`` and ``unwrap`` functions and ``lorem_ipsum``
+attribute containing the Lorem Ipsum sample text.
+
+3.0.1
+=====
+
+Declare missing dependency on six.
+
 3.0
 ===
 
@@ -9,7 +25,7 @@
 <https://pypi.org/project/future-fstrings>`_ for compatibilty
 with older Pythons. This change eliminates the dependency on
 jaraco.collections and thus for now removes the circular dependency
-as reported in #4.
+as reported in #3.
 
 2.0
 ===
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jaraco.text-3.0/PKG-INFO 
new/jaraco.text-3.2.0/PKG-INFO
--- old/jaraco.text-3.0/PKG-INFO        2019-03-09 21:18:44.000000000 +0100
+++ new/jaraco.text-3.2.0/PKG-INFO      2019-12-01 18:39:17.240367000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: jaraco.text
-Version: 3.0
+Version: 3.2.0
 Summary: Module for text manipulation
 Home-page: https://github.com/jaraco/jaraco.text
 Author: Jason R. Coombs
@@ -14,6 +14,10 @@
         .. image:: https://img.shields.io/travis/jaraco/jaraco.text/master.svg
            :target: https://travis-ci.org/jaraco/jaraco.text
         
+        .. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+           :target: https://github.com/psf/black
+           :alt: Code style: Black
+        
         .. .. image:: 
https://img.shields.io/appveyor/ci/jaraco/jaraco-text/master.svg
         ..    :target: 
https://ci.appveyor.com/project/jaraco/jaraco-text/branch/master
         
@@ -27,5 +31,5 @@
 Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
 Requires-Python: >=2.7
-Provides-Extra: docs
 Provides-Extra: testing
+Provides-Extra: docs
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jaraco.text-3.0/README.rst 
new/jaraco.text-3.2.0/README.rst
--- old/jaraco.text-3.0/README.rst      2019-03-09 21:18:25.000000000 +0100
+++ new/jaraco.text-3.2.0/README.rst    2019-12-01 18:38:50.000000000 +0100
@@ -6,6 +6,10 @@
 .. image:: https://img.shields.io/travis/jaraco/jaraco.text/master.svg
    :target: https://travis-ci.org/jaraco/jaraco.text
 
+.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+   :target: https://github.com/psf/black
+   :alt: Code style: Black
+
 .. .. image:: https://img.shields.io/appveyor/ci/jaraco/jaraco-text/master.svg
 ..    :target: https://ci.appveyor.com/project/jaraco/jaraco-text/branch/master
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jaraco.text-3.0/appveyor.yml 
new/jaraco.text-3.2.0/appveyor.yml
--- old/jaraco.text-3.0/appveyor.yml    2019-03-09 21:18:25.000000000 +0100
+++ new/jaraco.text-3.2.0/appveyor.yml  2019-12-01 18:38:50.000000000 +0100
@@ -18,7 +18,7 @@
   - '%LOCALAPPDATA%\pip\Cache'
 
 test_script:
-  - "python -m pip install tox tox-venv"
+  - "python -m pip install -U tox tox-venv virtualenv"
   - "tox"
 
 version: '{build}'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jaraco.text-3.0/docs/conf.py 
new/jaraco.text-3.2.0/docs/conf.py
--- old/jaraco.text-3.0/docs/conf.py    2019-03-09 21:18:25.000000000 +0100
+++ new/jaraco.text-3.2.0/docs/conf.py  2019-12-01 18:38:50.000000000 +0100
@@ -1,25 +1,25 @@
 #!/usr/bin/env python3
 # -*- coding: utf-8 -*-
 
-extensions = ["sphinx.ext.autodoc", "jaraco.packaging.sphinx", "rst.linker"]
+extensions = ['sphinx.ext.autodoc', 'jaraco.packaging.sphinx', 'rst.linker']
 
 master_doc = "index"
 
 link_files = {
-    "../CHANGES.rst": dict(
-        using=dict(GH="https://github.com";),
+    '../CHANGES.rst': dict(
+        using=dict(GH='https://github.com'),
         replace=[
             dict(
-                pattern=r"(Issue #|\B#)(?P<issue>\d+)",
-                url="{package_url}/issues/{issue}",
+                pattern=r'(Issue #|\B#)(?P<issue>\d+)',
+                url='{package_url}/issues/{issue}',
             ),
             dict(
-                pattern=r"^(?m)((?P<scm_version>v?\d+(\.\d+){1,2}))\n[-=]+\n",
-                with_scm="{text}\n{rev[timestamp]:%d %b %Y}\n",
+                pattern=r'^(?m)((?P<scm_version>v?\d+(\.\d+){1,2}))\n[-=]+\n',
+                with_scm='{text}\n{rev[timestamp]:%d %b %Y}\n',
             ),
             dict(
-                pattern=r"PEP[- ](?P<pep_number>\d+)",
-                url="https://www.python.org/dev/peps/pep-{pep_number:0>4}/",
+                pattern=r'PEP[- ](?P<pep_number>\d+)',
+                url='https://www.python.org/dev/peps/pep-{pep_number:0>4}/',
             ),
         ],
     )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' "old/jaraco.text-3.0/jaraco/text/Lorem ipsum.txt" 
"new/jaraco.text-3.2.0/jaraco/text/Lorem ipsum.txt"
--- "old/jaraco.text-3.0/jaraco/text/Lorem ipsum.txt"   1970-01-01 
01:00:00.000000000 +0100
+++ "new/jaraco.text-3.2.0/jaraco/text/Lorem ipsum.txt" 2019-12-01 
18:38:50.000000000 +0100
@@ -0,0 +1,2 @@
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor 
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis 
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu 
fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in 
culpa qui officia deserunt mollit anim id est laborum.
+Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam varius, 
turpis et commodo pharetra, est eros bibendum elit, nec luctus magna felis 
sollicitudin mauris. Integer in mauris eu nibh euismod gravida. Duis ac tellus 
et risus vulputate vehicula. Donec lobortis risus a elit. Etiam tempor. Ut 
ullamcorper, ligula eu tempor congue, eros est euismod turpis, id tincidunt 
sapien risus a quam. Maecenas fermentum consequat mi. Donec fermentum. 
Pellentesque malesuada nulla a mi. Duis sapien sem, aliquet nec, commodo eget, 
consequat quis, neque. Aliquam faucibus, elit ut dictum aliquet, felis nisl 
adipiscing sapien, sed malesuada diam lacus eget erat. Cras mollis scelerisque 
nunc. Nullam arcu. Aliquam consequat. Curabitur augue lorem, dapibus quis, 
laoreet et, pretium ac, nisi. Aenean magna nisl, mollis quis, molestie eu, 
feugiat in, orci. In hac habitasse platea dictumst.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jaraco.text-3.0/jaraco/text/__init__.py 
new/jaraco.text-3.2.0/jaraco/text/__init__.py
--- old/jaraco.text-3.0/jaraco/text/__init__.py 1970-01-01 01:00:00.000000000 
+0100
+++ new/jaraco.text-3.2.0/jaraco/text/__init__.py       2019-12-01 
18:38:50.000000000 +0100
@@ -0,0 +1,500 @@
+from __future__ import absolute_import, unicode_literals, print_function
+
+import re
+import itertools
+import textwrap
+import functools
+
+import six
+
+try:
+    from importlib import resources
+except ImportError:  # pragma: nocover
+    import importlib_resources as resources
+
+from jaraco.functools import compose, method_cache
+
+
+def substitution(old, new):
+    """
+    Return a function that will perform a substitution on a string
+    """
+    return lambda s: s.replace(old, new)
+
+
+def multi_substitution(*substitutions):
+    """
+    Take a sequence of pairs specifying substitutions, and create
+    a function that performs those substitutions.
+
+    >>> multi_substitution(('foo', 'bar'), ('bar', 'baz'))('foo')
+    'baz'
+    """
+    substitutions = itertools.starmap(substitution, substitutions)
+    # compose function applies last function first, so reverse the
+    #  substitutions to get the expected order.
+    substitutions = reversed(tuple(substitutions))
+    return compose(*substitutions)
+
+
+class FoldedCase(six.text_type):
+    """
+    A case insensitive string class; behaves just like str
+    except compares equal when the only variation is case.
+
+    >>> s = FoldedCase('hello world')
+
+    >>> s == 'Hello World'
+    True
+
+    >>> 'Hello World' == s
+    True
+
+    >>> s != 'Hello World'
+    False
+
+    >>> s.index('O')
+    4
+
+    >>> s.split('O')
+    ['hell', ' w', 'rld']
+
+    >>> sorted(map(FoldedCase, ['GAMMA', 'alpha', 'Beta']))
+    ['alpha', 'Beta', 'GAMMA']
+
+    Sequence membership is straightforward.
+
+    >>> "Hello World" in [s]
+    True
+    >>> s in ["Hello World"]
+    True
+
+    You may test for set inclusion, but candidate and elements
+    must both be folded.
+
+    >>> FoldedCase("Hello World") in {s}
+    True
+    >>> s in {FoldedCase("Hello World")}
+    True
+
+    String inclusion works as long as the FoldedCase object
+    is on the right.
+
+    >>> "hello" in FoldedCase("Hello World")
+    True
+
+    But not if the FoldedCase object is on the left:
+
+    >>> FoldedCase('hello') in 'Hello World'
+    False
+
+    In that case, use in_:
+
+    >>> FoldedCase('hello').in_('Hello World')
+    True
+
+    >>> FoldedCase('hello') > FoldedCase('Hello')
+    False
+    """
+
+    def __lt__(self, other):
+        return self.lower() < other.lower()
+
+    def __gt__(self, other):
+        return self.lower() > other.lower()
+
+    def __eq__(self, other):
+        return self.lower() == other.lower()
+
+    def __ne__(self, other):
+        return self.lower() != other.lower()
+
+    def __hash__(self):
+        return hash(self.lower())
+
+    def __contains__(self, other):
+        return super(FoldedCase, self).lower().__contains__(other.lower())
+
+    def in_(self, other):
+        "Does self appear in other?"
+        return self in FoldedCase(other)
+
+    # cache lower since it's likely to be called frequently.
+    @method_cache
+    def lower(self):
+        return super(FoldedCase, self).lower()
+
+    def index(self, sub):
+        return self.lower().index(sub.lower())
+
+    def split(self, splitter=' ', maxsplit=0):
+        pattern = re.compile(re.escape(splitter), re.I)
+        return pattern.split(self, maxsplit)
+
+
+def is_decodable(value):
+    r"""
+    Return True if the supplied value is decodable (using the default
+    encoding).
+
+    >>> is_decodable(b'\xff')
+    False
+    >>> is_decodable(b'\x32')
+    True
+    """
+    # TODO: This code could be expressed more consisely and directly
+    # with a jaraco.context.ExceptionTrap, but that adds an unfortunate
+    # long dependency tree, so for now, use boolean literals.
+    try:
+        value.decode()
+    except UnicodeDecodeError:
+        return False
+    return True
+
+
+def is_binary(value):
+    r"""
+    Return True if the value appears to be binary (that is, it's a byte
+    string and isn't decodable).
+
+    >>> is_binary(b'\xff')
+    True
+    >>> is_binary('\xff')
+    False
+    """
+    return isinstance(value, bytes) and not is_decodable(value)
+
+
+def trim(s):
+    r"""
+    Trim something like a docstring to remove the whitespace that
+    is common due to indentation and formatting.
+
+    >>> trim("\n\tfoo = bar\n\t\tbar = baz\n")
+    'foo = bar\n\tbar = baz'
+    """
+    return textwrap.dedent(s).strip()
+
+
+def wrap(s):
+    """
+    Wrap lines of text, retaining existing newlines as
+    paragraph markers.
+
+    >>> print(wrap(lorem_ipsum))
+    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
+    eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
+    minim veniam, quis nostrud exercitation ullamco laboris nisi ut
+    aliquip ex ea commodo consequat. Duis aute irure dolor in
+    reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
+    pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
+    culpa qui officia deserunt mollit anim id est laborum.
+    <BLANKLINE>
+    Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam
+    varius, turpis et commodo pharetra, est eros bibendum elit, nec luctus
+    magna felis sollicitudin mauris. Integer in mauris eu nibh euismod
+    gravida. Duis ac tellus et risus vulputate vehicula. Donec lobortis
+    risus a elit. Etiam tempor. Ut ullamcorper, ligula eu tempor congue,
+    eros est euismod turpis, id tincidunt sapien risus a quam. Maecenas
+    fermentum consequat mi. Donec fermentum. Pellentesque malesuada nulla
+    a mi. Duis sapien sem, aliquet nec, commodo eget, consequat quis,
+    neque. Aliquam faucibus, elit ut dictum aliquet, felis nisl adipiscing
+    sapien, sed malesuada diam lacus eget erat. Cras mollis scelerisque
+    nunc. Nullam arcu. Aliquam consequat. Curabitur augue lorem, dapibus
+    quis, laoreet et, pretium ac, nisi. Aenean magna nisl, mollis quis,
+    molestie eu, feugiat in, orci. In hac habitasse platea dictumst.
+    """
+    paragraphs = s.splitlines()
+    wrapped = ('\n'.join(textwrap.wrap(para)) for para in paragraphs)
+    return '\n\n'.join(wrapped)
+
+
+def unwrap(s):
+    r"""
+    Given a multi-line string, return an unwrapped version.
+
+    >>> wrapped = wrap(lorem_ipsum)
+    >>> wrapped.count('\n')
+    20
+    >>> unwrapped = unwrap(wrapped)
+    >>> unwrapped.count('\n')
+    1
+    >>> print(unwrapped)
+    Lorem ipsum dolor sit amet, consectetur adipiscing ...
+    Curabitur pretium tincidunt lacus. Nulla gravida orci ...
+
+    """
+    paragraphs = re.split(r'\n\n+', s)
+    cleaned = (para.replace('\n', ' ') for para in paragraphs)
+    return '\n'.join(cleaned)
+
+
+lorem_ipsum = resources.read_text(__name__, 'Lorem ipsum.txt')
+
+
+class Splitter(object):
+    """object that will split a string with the given arguments for each call
+
+    >>> s = Splitter(',')
+    >>> s('hello, world, this is your, master calling')
+    ['hello', ' world', ' this is your', ' master calling']
+    """
+
+    def __init__(self, *args):
+        self.args = args
+
+    def __call__(self, s):
+        return s.split(*self.args)
+
+
+def indent(string, prefix=' ' * 4):
+    """
+    >>> indent('foo')
+    '    foo'
+    """
+    return prefix + string
+
+
+class WordSet(tuple):
+    """
+    Given a Python identifier, return the words that identifier represents,
+    whether in camel case, underscore-separated, etc.
+
+    >>> WordSet.parse("camelCase")
+    ('camel', 'Case')
+
+    >>> WordSet.parse("under_sep")
+    ('under', 'sep')
+
+    Acronyms should be retained
+
+    >>> WordSet.parse("firstSNL")
+    ('first', 'SNL')
+
+    >>> WordSet.parse("you_and_I")
+    ('you', 'and', 'I')
+
+    >>> WordSet.parse("A simple test")
+    ('A', 'simple', 'test')
+
+    Multiple caps should not interfere with the first cap of another word.
+
+    >>> WordSet.parse("myABCClass")
+    ('my', 'ABC', 'Class')
+
+    The result is a WordSet, so you can get the form you need.
+
+    >>> WordSet.parse("myABCClass").underscore_separated()
+    'my_ABC_Class'
+
+    >>> WordSet.parse('a-command').camel_case()
+    'ACommand'
+
+    >>> WordSet.parse('someIdentifier').lowered().space_separated()
+    'some identifier'
+
+    Slices of the result should return another WordSet.
+
+    >>> WordSet.parse('taken-out-of-context')[1:].underscore_separated()
+    'out_of_context'
+
+    >>> WordSet.from_class_name(WordSet()).lowered().space_separated()
+    'word set'
+
+    >>> example = WordSet.parse('figured it out')
+    >>> example.headless_camel_case()
+    'figuredItOut'
+    >>> example.dash_separated()
+    'figured-it-out'
+
+    """
+
+    _pattern = re.compile('([A-Z]?[a-z]+)|([A-Z]+(?![a-z]))')
+
+    def capitalized(self):
+        return WordSet(word.capitalize() for word in self)
+
+    def lowered(self):
+        return WordSet(word.lower() for word in self)
+
+    def camel_case(self):
+        return ''.join(self.capitalized())
+
+    def headless_camel_case(self):
+        words = iter(self)
+        first = next(words).lower()
+        new_words = itertools.chain((first,), WordSet(words).camel_case())
+        return ''.join(new_words)
+
+    def underscore_separated(self):
+        return '_'.join(self)
+
+    def dash_separated(self):
+        return '-'.join(self)
+
+    def space_separated(self):
+        return ' '.join(self)
+
+    def __getitem__(self, item):
+        result = super(WordSet, self).__getitem__(item)
+        if isinstance(item, slice):
+            result = WordSet(result)
+        return result
+
+    # for compatibility with Python 2
+    def __getslice__(self, i, j):  # pragma: nocover
+        return self.__getitem__(slice(i, j))
+
+    @classmethod
+    def parse(cls, identifier):
+        matches = cls._pattern.finditer(identifier)
+        return WordSet(match.group(0) for match in matches)
+
+    @classmethod
+    def from_class_name(cls, subject):
+        return cls.parse(subject.__class__.__name__)
+
+
+# for backward compatibility
+words = WordSet.parse
+
+
+def simple_html_strip(s):
+    r"""
+    Remove HTML from the string `s`.
+
+    >>> str(simple_html_strip(''))
+    ''
+
+    >>> print(simple_html_strip('A <bold>stormy</bold> day in paradise'))
+    A stormy day in paradise
+
+    >>> print(simple_html_strip('Somebody <!-- do not --> tell the truth.'))
+    Somebody  tell the truth.
+
+    >>> print(simple_html_strip('What about<br/>\nmultiple lines?'))
+    What about
+    multiple lines?
+    """
+    html_stripper = re.compile('(<!--.*?-->)|(<[^>]*>)|([^<]+)', re.DOTALL)
+    texts = (match.group(3) or '' for match in html_stripper.finditer(s))
+    return ''.join(texts)
+
+
+class SeparatedValues(six.text_type):
+    """
+    A string separated by a separator. Overrides __iter__ for getting
+    the values.
+
+    >>> list(SeparatedValues('a,b,c'))
+    ['a', 'b', 'c']
+
+    Whitespace is stripped and empty values are discarded.
+
+    >>> list(SeparatedValues(' a,   b   , c,  '))
+    ['a', 'b', 'c']
+    """
+
+    separator = ','
+
+    def __iter__(self):
+        parts = self.split(self.separator)
+        return six.moves.filter(None, (part.strip() for part in parts))
+
+
+class Stripper:
+    r"""
+    Given a series of lines, find the common prefix and strip it from them.
+
+    >>> lines = [
+    ...     'abcdefg\n',
+    ...     'abc\n',
+    ...     'abcde\n',
+    ... ]
+    >>> res = Stripper.strip_prefix(lines)
+    >>> res.prefix
+    'abc'
+    >>> list(res.lines)
+    ['defg\n', '\n', 'de\n']
+
+    If no prefix is common, nothing should be stripped.
+
+    >>> lines = [
+    ...     'abcd\n',
+    ...     '1234\n',
+    ... ]
+    >>> res = Stripper.strip_prefix(lines)
+    >>> res.prefix = ''
+    >>> list(res.lines)
+    ['abcd\n', '1234\n']
+    """
+
+    def __init__(self, prefix, lines):
+        self.prefix = prefix
+        self.lines = map(self, lines)
+
+    @classmethod
+    def strip_prefix(cls, lines):
+        prefix_lines, lines = itertools.tee(lines)
+        prefix = functools.reduce(cls.common_prefix, prefix_lines)
+        return cls(prefix, lines)
+
+    def __call__(self, line):
+        if not self.prefix:
+            return line
+        null, prefix, rest = line.partition(self.prefix)
+        return rest
+
+    @staticmethod
+    def common_prefix(s1, s2):
+        """
+        Return the common prefix of two lines.
+        """
+        index = min(len(s1), len(s2))
+        while s1[:index] != s2[:index]:
+            index -= 1
+        return s1[:index]
+
+
+def remove_prefix(text, prefix):
+    """
+    Remove the prefix from the text if it exists.
+
+    >>> remove_prefix('underwhelming performance', 'underwhelming ')
+    'performance'
+
+    >>> remove_prefix('something special', 'sample')
+    'something special'
+    """
+    null, prefix, rest = text.rpartition(prefix)
+    return rest
+
+
+def remove_suffix(text, suffix):
+    """
+    Remove the suffix from the text if it exists.
+
+    >>> remove_suffix('name.git', '.git')
+    'name'
+
+    >>> remove_suffix('something special', 'sample')
+    'something special'
+    """
+    rest, suffix, null = text.partition(suffix)
+    return rest
+
+
+def normalize_newlines(text):
+    r"""
+    Replace alternate newlines with the canonical newline.
+
+    >>> normalize_newlines('Lorem Ipsum\u2029')
+    'Lorem Ipsum\n'
+    >>> normalize_newlines('Lorem Ipsum\r\n')
+    'Lorem Ipsum\n'
+    >>> normalize_newlines('Lorem Ipsum\x85')
+    'Lorem Ipsum\n'
+    """
+    newlines = ['\r\n', '\r', '\n', '\u0085', '\u2028', '\u2029']
+    pattern = '|'.join(newlines)
+    return re.sub(pattern, '\n', text)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jaraco.text-3.0/jaraco/text.py 
new/jaraco.text-3.2.0/jaraco/text.py
--- old/jaraco.text-3.0/jaraco/text.py  2019-03-09 21:18:25.000000000 +0100
+++ new/jaraco.text-3.2.0/jaraco/text.py        1970-01-01 01:00:00.000000000 
+0100
@@ -1,403 +0,0 @@
-from __future__ import absolute_import, unicode_literals, print_function
-
-import re
-import itertools
-import textwrap
-import functools
-
-import six
-
-from jaraco.functools import compose, method_cache
-
-
-def substitution(old, new):
-       """
-       Return a function that will perform a substitution on a string
-       """
-       return lambda s: s.replace(old, new)
-
-
-def multi_substitution(*substitutions):
-       """
-       Take a sequence of pairs specifying substitutions, and create
-       a function that performs those substitutions.
-
-       >>> multi_substitution(('foo', 'bar'), ('bar', 'baz'))('foo')
-       'baz'
-       """
-       substitutions = itertools.starmap(substitution, substitutions)
-       # compose function applies last function first, so reverse the
-       #  substitutions to get the expected order.
-       substitutions = reversed(tuple(substitutions))
-       return compose(*substitutions)
-
-
-class FoldedCase(six.text_type):
-       """
-       A case insensitive string class; behaves just like str
-       except compares equal when the only variation is case.
-
-       >>> s = FoldedCase('hello world')
-
-       >>> s == 'Hello World'
-       True
-
-       >>> 'Hello World' == s
-       True
-
-       >>> s != 'Hello World'
-       False
-
-       >>> s.index('O')
-       4
-
-       >>> s.split('O')
-       ['hell', ' w', 'rld']
-
-       >>> sorted(map(FoldedCase, ['GAMMA', 'alpha', 'Beta']))
-       ['alpha', 'Beta', 'GAMMA']
-
-       Sequence membership is straightforward.
-
-       >>> "Hello World" in [s]
-       True
-       >>> s in ["Hello World"]
-       True
-
-       You may test for set inclusion, but candidate and elements
-       must both be folded.
-
-       >>> FoldedCase("Hello World") in {s}
-       True
-       >>> s in {FoldedCase("Hello World")}
-       True
-
-       String inclusion works as long as the FoldedCase object
-       is on the right.
-
-       >>> "hello" in FoldedCase("Hello World")
-       True
-
-       But not if the FoldedCase object is on the left:
-
-       >>> FoldedCase('hello') in 'Hello World'
-       False
-
-       In that case, use in_:
-
-       >>> FoldedCase('hello').in_('Hello World')
-       True
-
-       """
-       def __lt__(self, other):
-               return self.lower() < other.lower()
-
-       def __gt__(self, other):
-               return self.lower() > other.lower()
-
-       def __eq__(self, other):
-               return self.lower() == other.lower()
-
-       def __ne__(self, other):
-               return self.lower() != other.lower()
-
-       def __hash__(self):
-               return hash(self.lower())
-
-       def __contains__(self, other):
-               return super(FoldedCase, 
self).lower().__contains__(other.lower())
-
-       def in_(self, other):
-               "Does self appear in other?"
-               return self in FoldedCase(other)
-
-       # cache lower since it's likely to be called frequently.
-       @method_cache
-       def lower(self):
-               return super(FoldedCase, self).lower()
-
-       def index(self, sub):
-               return self.lower().index(sub.lower())
-
-       def split(self, splitter=' ', maxsplit=0):
-               pattern = re.compile(re.escape(splitter), re.I)
-               return pattern.split(self, maxsplit)
-
-
-def is_decodable(value):
-       r"""
-       Return True if the supplied value is decodable (using the default
-       encoding).
-
-       >>> is_decodable(b'\xff')
-       False
-       >>> is_decodable(b'\x32')
-       True
-       """
-       # TODO: This code could be expressed more consisely and directly
-       # with a jaraco.context.ExceptionTrap, but that adds an unfortunate
-       # long dependency tree, so for now, use boolean literals.
-       try:
-               value.decode()
-       except UnicodeDecodeError:
-               return False
-       return True
-
-
-def is_binary(value):
-       """
-       Return True if the value appears to be binary (that is, it's a byte
-       string and isn't decodable).
-       """
-       return isinstance(value, bytes) and not is_decodable(value)
-
-
-def trim(s):
-       r"""
-       Trim something like a docstring to remove the whitespace that
-       is common due to indentation and formatting.
-
-       >>> trim("\n\tfoo = bar\n\t\tbar = baz\n")
-       'foo = bar\n\tbar = baz'
-       """
-       return textwrap.dedent(s).strip()
-
-
-class Splitter(object):
-       """object that will split a string with the given arguments for each 
call
-
-       >>> s = Splitter(',')
-       >>> s('hello, world, this is your, master calling')
-       ['hello', ' world', ' this is your', ' master calling']
-       """
-       def __init__(self, *args):
-               self.args = args
-
-       def __call__(self, s):
-               return s.split(*self.args)
-
-
-def indent(string, prefix=' ' * 4):
-       return prefix + string
-
-
-class WordSet(tuple):
-       """
-       Given a Python identifier, return the words that identifier represents,
-       whether in camel case, underscore-separated, etc.
-
-       >>> WordSet.parse("camelCase")
-       ('camel', 'Case')
-
-       >>> WordSet.parse("under_sep")
-       ('under', 'sep')
-
-       Acronyms should be retained
-
-       >>> WordSet.parse("firstSNL")
-       ('first', 'SNL')
-
-       >>> WordSet.parse("you_and_I")
-       ('you', 'and', 'I')
-
-       >>> WordSet.parse("A simple test")
-       ('A', 'simple', 'test')
-
-       Multiple caps should not interfere with the first cap of another word.
-
-       >>> WordSet.parse("myABCClass")
-       ('my', 'ABC', 'Class')
-
-       The result is a WordSet, so you can get the form you need.
-
-       >>> WordSet.parse("myABCClass").underscore_separated()
-       'my_ABC_Class'
-
-       >>> WordSet.parse('a-command').camel_case()
-       'ACommand'
-
-       >>> WordSet.parse('someIdentifier').lowered().space_separated()
-       'some identifier'
-
-       Slices of the result should return another WordSet.
-
-       >>> WordSet.parse('taken-out-of-context')[1:].underscore_separated()
-       'out_of_context'
-
-       >>> WordSet.from_class_name(WordSet()).lowered().space_separated()
-       'word set'
-       """
-       _pattern = re.compile('([A-Z]?[a-z]+)|([A-Z]+(?![a-z]))')
-
-       def capitalized(self):
-               return WordSet(word.capitalize() for word in self)
-
-       def lowered(self):
-               return WordSet(word.lower() for word in self)
-
-       def camel_case(self):
-               return ''.join(self.capitalized())
-
-       def headless_camel_case(self):
-               words = iter(self)
-               first = next(words).lower()
-               return itertools.chain((first,), WordSet(words).camel_case())
-
-       def underscore_separated(self):
-               return '_'.join(self)
-
-       def dash_separated(self):
-               return '-'.join(self)
-
-       def space_separated(self):
-               return ' '.join(self)
-
-       def __getitem__(self, item):
-               result = super(WordSet, self).__getitem__(item)
-               if isinstance(item, slice):
-                       result = WordSet(result)
-               return result
-
-       # for compatibility with Python 2
-       def __getslice__(self, i, j):
-               return self.__getitem__(slice(i, j))
-
-       @classmethod
-       def parse(cls, identifier):
-               matches = cls._pattern.finditer(identifier)
-               return WordSet(match.group(0) for match in matches)
-
-       @classmethod
-       def from_class_name(cls, subject):
-               return cls.parse(subject.__class__.__name__)
-
-
-# for backward compatibility
-words = WordSet.parse
-
-
-def simple_html_strip(s):
-       r"""
-       Remove HTML from the string `s`.
-
-       >>> str(simple_html_strip(''))
-       ''
-
-       >>> print(simple_html_strip('A <bold>stormy</bold> day in paradise'))
-       A stormy day in paradise
-
-       >>> print(simple_html_strip('Somebody <!-- do not --> tell the truth.'))
-       Somebody  tell the truth.
-
-       >>> print(simple_html_strip('What about<br/>\nmultiple lines?'))
-       What about
-       multiple lines?
-       """
-       html_stripper = re.compile('(<!--.*?-->)|(<[^>]*>)|([^<]+)', re.DOTALL)
-       texts = (
-               match.group(3) or ''
-               for match
-               in html_stripper.finditer(s)
-       )
-       return ''.join(texts)
-
-
-class SeparatedValues(six.text_type):
-       """
-       A string separated by a separator. Overrides __iter__ for getting
-       the values.
-
-       >>> list(SeparatedValues('a,b,c'))
-       ['a', 'b', 'c']
-
-       Whitespace is stripped and empty values are discarded.
-
-       >>> list(SeparatedValues(' a,   b   , c,  '))
-       ['a', 'b', 'c']
-       """
-       separator = ','
-
-       def __iter__(self):
-               parts = self.split(self.separator)
-               return six.moves.filter(None, (part.strip() for part in parts))
-
-
-class Stripper:
-       r"""
-       Given a series of lines, find the common prefix and strip it from them.
-
-       >>> lines = [
-       ...     'abcdefg\n',
-       ...     'abc\n',
-       ...     'abcde\n',
-       ... ]
-       >>> res = Stripper.strip_prefix(lines)
-       >>> res.prefix
-       'abc'
-       >>> list(res.lines)
-       ['defg\n', '\n', 'de\n']
-
-       If no prefix is common, nothing should be stripped.
-
-       >>> lines = [
-       ...     'abcd\n',
-       ...     '1234\n',
-       ... ]
-       >>> res = Stripper.strip_prefix(lines)
-       >>> res.prefix = ''
-       >>> list(res.lines)
-       ['abcd\n', '1234\n']
-       """
-       def __init__(self, prefix, lines):
-               self.prefix = prefix
-               self.lines = map(self, lines)
-
-       @classmethod
-       def strip_prefix(cls, lines):
-               prefix_lines, lines = itertools.tee(lines)
-               prefix = functools.reduce(cls.common_prefix, prefix_lines)
-               return cls(prefix, lines)
-
-       def __call__(self, line):
-               if not self.prefix:
-                       return line
-               null, prefix, rest = line.partition(self.prefix)
-               return rest
-
-       @staticmethod
-       def common_prefix(s1, s2):
-               """
-               Return the common prefix of two lines.
-               """
-               index = min(len(s1), len(s2))
-               while s1[:index] != s2[:index]:
-                       index -= 1
-               return s1[:index]
-
-
-def remove_prefix(text, prefix):
-       """
-       Remove the prefix from the text if it exists.
-
-       >>> remove_prefix('underwhelming performance', 'underwhelming ')
-       'performance'
-
-       >>> remove_prefix('something special', 'sample')
-       'something special'
-       """
-       null, prefix, rest = text.rpartition(prefix)
-       return rest
-
-
-def remove_suffix(text, suffix):
-       """
-       Remove the suffix from the text if it exists.
-
-       >>> remove_suffix('name.git', '.git')
-       'name'
-
-       >>> remove_suffix('something special', 'sample')
-       'something special'
-       """
-       rest, suffix, null = text.partition(suffix)
-       return rest
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jaraco.text-3.0/jaraco.text.egg-info/PKG-INFO 
new/jaraco.text-3.2.0/jaraco.text.egg-info/PKG-INFO
--- old/jaraco.text-3.0/jaraco.text.egg-info/PKG-INFO   2019-03-09 
21:18:44.000000000 +0100
+++ new/jaraco.text-3.2.0/jaraco.text.egg-info/PKG-INFO 2019-12-01 
18:39:16.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: jaraco.text
-Version: 3.0
+Version: 3.2.0
 Summary: Module for text manipulation
 Home-page: https://github.com/jaraco/jaraco.text
 Author: Jason R. Coombs
@@ -14,6 +14,10 @@
         .. image:: https://img.shields.io/travis/jaraco/jaraco.text/master.svg
            :target: https://travis-ci.org/jaraco/jaraco.text
         
+        .. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+           :target: https://github.com/psf/black
+           :alt: Code style: Black
+        
         .. .. image:: 
https://img.shields.io/appveyor/ci/jaraco/jaraco-text/master.svg
         ..    :target: 
https://ci.appveyor.com/project/jaraco/jaraco-text/branch/master
         
@@ -27,5 +31,5 @@
 Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
 Requires-Python: >=2.7
-Provides-Extra: docs
 Provides-Extra: testing
+Provides-Extra: docs
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jaraco.text-3.0/jaraco.text.egg-info/SOURCES.txt 
new/jaraco.text-3.2.0/jaraco.text.egg-info/SOURCES.txt
--- old/jaraco.text-3.0/jaraco.text.egg-info/SOURCES.txt        2019-03-09 
21:18:44.000000000 +0100
+++ new/jaraco.text-3.2.0/jaraco.text.egg-info/SOURCES.txt      2019-12-01 
18:39:17.000000000 +0100
@@ -1,4 +1,6 @@
+.coveragerc
 .flake8
+.pre-commit-config.yaml
 .readthedocs.yml
 .travis.yml
 CHANGES.rst
@@ -15,9 +17,10 @@
 docs/history.rst
 docs/index.rst
 jaraco/__init__.py
-jaraco/text.py
 jaraco.text.egg-info/PKG-INFO
 jaraco.text.egg-info/SOURCES.txt
 jaraco.text.egg-info/dependency_links.txt
 jaraco.text.egg-info/requires.txt
-jaraco.text.egg-info/top_level.txt
\ No newline at end of file
+jaraco.text.egg-info/top_level.txt
+jaraco/text/Lorem ipsum.txt
+jaraco/text/__init__.py
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jaraco.text-3.0/jaraco.text.egg-info/requires.txt 
new/jaraco.text-3.2.0/jaraco.text.egg-info/requires.txt
--- old/jaraco.text-3.0/jaraco.text.egg-info/requires.txt       2019-03-09 
21:18:44.000000000 +0100
+++ new/jaraco.text-3.2.0/jaraco.text.egg-info/requires.txt     2019-12-01 
18:39:16.000000000 +0100
@@ -1,4 +1,8 @@
 jaraco.functools
+six
+
+[:python_version < "3.7"]
+importlib_resources
 
 [docs]
 sphinx
@@ -7,5 +11,7 @@
 
 [testing]
 pytest!=3.7.3,>=3.5
-pytest-checkdocs
+pytest-checkdocs>=1.2.3
 pytest-flake8
+pytest-black-multipy
+pytest-cov
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jaraco.text-3.0/pyproject.toml 
new/jaraco.text-3.2.0/pyproject.toml
--- old/jaraco.text-3.0/pyproject.toml  2019-03-09 21:18:25.000000000 +0100
+++ new/jaraco.text-3.2.0/pyproject.toml        2019-12-01 18:38:50.000000000 
+0100
@@ -1,3 +1,6 @@
 [build-system]
 requires = ["setuptools>=34.4", "wheel", "setuptools_scm>=1.15"]
 build-backend = "setuptools.build_meta"
+
+[tool.black]
+skip-string-normalization = true
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jaraco.text-3.0/pytest.ini 
new/jaraco.text-3.2.0/pytest.ini
--- old/jaraco.text-3.0/pytest.ini      2019-03-09 21:18:25.000000000 +0100
+++ new/jaraco.text-3.2.0/pytest.ini    2019-12-01 18:38:50.000000000 +0100
@@ -1,11 +1,5 @@
 [pytest]
 norecursedirs=dist build .tox .eggs
-addopts=--doctest-modules --flake8
+addopts=--doctest-modules --flake8 --black --cov
 doctest_optionflags=ALLOW_UNICODE ELLIPSIS
 filterwarnings=
-       ignore:Possible nested set::pycodestyle:113
-       ignore:Using or importing the ABCs::flake8:410
-       # workaround for https://sourceforge.net/p/docutils/bugs/348/
-       ignore:'U' mode is deprecated::docutils.io
-       # workaround for https://gitlab.com/pycqa/flake8/issues/275
-       ignore:You passed a bytestring as `filenames`.::flake8
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jaraco.text-3.0/setup.cfg 
new/jaraco.text-3.2.0/setup.cfg
--- old/jaraco.text-3.0/setup.cfg       2019-03-09 21:18:44.000000000 +0100
+++ new/jaraco.text-3.2.0/setup.cfg     2019-12-01 18:39:17.240367000 +0100
@@ -22,13 +22,17 @@
 python_requires = >=2.7
 install_requires = 
        jaraco.functools
+       six
+       importlib_resources; python_version < "3.7"
 setup_requires = setuptools_scm >= 1.15.0
 
 [options.extras_require]
 testing = 
        pytest >= 3.5, !=3.7.3
-       pytest-checkdocs
+       pytest-checkdocs >= 1.2.3
        pytest-flake8
+       pytest-black-multipy
+       pytest-cov
 docs = 
        sphinx
        jaraco.packaging >= 3.2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jaraco.text-3.0/skeleton.md 
new/jaraco.text-3.2.0/skeleton.md
--- old/jaraco.text-3.0/skeleton.md     2019-03-09 21:18:25.000000000 +0100
+++ new/jaraco.text-3.2.0/skeleton.md   2019-12-01 18:38:50.000000000 +0100
@@ -50,7 +50,8 @@
 - setuptools declarative configuration using setup.cfg
 - tox for running tests
 - A README.rst as reStructuredText with some popular badges, but with 
readthedocs and appveyor badges commented out
-- A CHANGES.rst file intended for publishing release notes about the project.
+- A CHANGES.rst file intended for publishing release notes about the project
+- Use of [black](https://black.readthedocs.io/en/stable/) for code formatting 
(disabled on unsupported Python 3.5 and earlier)
 
 ## Packaging Conventions
 
@@ -84,7 +85,7 @@
 
 Other environments (invoked with `tox -e {name}`) supplied include:
 
-  - a `build-docs` environment to build the documentation
+  - a `docs` environment to build the documentation
   - a `release` environment to publish the package to PyPI
 
 A pytest.ini is included to define common options around running tests. In 
particular:
@@ -97,18 +98,12 @@
 
 Relies a .flake8 file to correct some default behaviors:
 
-- allow tabs for indentation (legacy for jaraco projects)
-- disable mutually incompatible rules W503 and W504.
+- disable mutually incompatible rules W503 and W504
+- support for black format
 
 ## Continuous Integration
 
-The project is pre-configured to run tests in 
[Travis-CI](https://travis-ci.org) (.travis.yml). Any new project must be 
enabled either through their web site or with the `travis enable` command. In 
addition to running tests, an additional deploy stage is configured to 
automatically release tagged commits. The username and password for PyPI must 
be configured for each project using the `travis` command and only after the 
travis project is created. As releases are cut with 
[twine](https://pypi.org/project/twine), the two values are supplied through 
the `TWINE_USERNAME` and `TWINE_PASSWORD`. To configure the latter as a secret, 
run the following command:
-
-```
-echo "TWINE_PASSWORD={password}" | travis encrypt
-```
-
-Or disable it in the CI definition and configure it through the web UI.
+The project is pre-configured to run tests in 
[Travis-CI](https://travis-ci.org) (.travis.yml). Any new project must be 
enabled either through their web site or with the `travis enable` command.
 
 Features include:
 - test against Python 2 and 3
@@ -117,12 +112,26 @@
 
 Also provided is a minimal template for running under Appveyor (Windows).
 
+### Continuous Deployments
+
+In addition to running tests, an additional deploy stage is configured to 
automatically release tagged commits to PyPI using [API 
tokens](https://pypi.org/help/#apitoken). The release process expects an 
authorized token to be configured with Travis as the TWINE_PASSWORD environment 
variable. After the Travis project is created, configure the token through the 
web UI or with a command like the following (bash syntax):
+
+```
+TWINE_PASSWORD={token} travis env copy TWINE_PASSWORD
+```
+
 ## Building Documentation
 
-Documentation is automatically built by [Read the 
Docs](https://readthedocs.org) when the project is registered with it, by way 
of the .readthedocs.yml file. To test the docs build manually, a tox env may be 
invoked as `tox -e build-docs`. Both techniques rely on the dependencies 
declared in `setup.cfg/options.extras_require.docs`.
+Documentation is automatically built by [Read the 
Docs](https://readthedocs.org) when the project is registered with it, by way 
of the .readthedocs.yml file. To test the docs build manually, a tox env may be 
invoked as `tox -e docs`. Both techniques rely on the dependencies declared in 
`setup.cfg/options.extras_require.docs`.
 
 In addition to building the sphinx docs scaffolded in `docs/`, the docs build 
a `history.html` file that first injects release dates and hyperlinks into the 
CHANGES.rst before incorporating it as history in the docs.
 
 ## Cutting releases
 
 By default, tagged commits are released through the continuous integration 
deploy stage.
+
+Releases may also be cut manually by invoking the tox environment `release` 
with the PyPI token set as the TWINE_PASSWORD:
+
+```
+TWINE_PASSWORD={token} tox -e release
+```
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jaraco.text-3.0/tox.ini new/jaraco.text-3.2.0/tox.ini
--- old/jaraco.text-3.0/tox.ini 2019-03-09 21:18:25.000000000 +0100
+++ new/jaraco.text-3.2.0/tox.ini       2019-12-01 18:38:50.000000000 +0100
@@ -1,30 +1,41 @@
 [tox]
 envlist = python
-minversion = 2.4
+minversion = 3.2
+# https://github.com/jaraco/skeleton/issues/6
+tox_pip_extensions_ext_venv_update = true
+# Ensure that a late version of pip is used even on tox-venv.
+requires =
+       tox-pip-version>=0.0.6
+       tox-venv
+
 
 [testenv]
 deps =
        setuptools>=31.0.1
+pip_version = pip
 commands =
        pytest {posargs}
 usedevelop = True
 extras = testing
 
-[testenv:build-docs]
+[testenv:docs]
 extras =
-    docs
-    testing
+       docs
+       testing
 changedir = docs
 commands =
-    python -m sphinx . {toxinidir}/build/html
+       python -m sphinx . {toxinidir}/build/html
 
 [testenv:release]
 skip_install = True
 deps =
        pep517>=0.5
-       # workaround for https://github.com/pypa/twine/issues/423
-       git+https://github.com/pypa/twine
-       path.py
+       twine[keyring]>=1.13
+       path
+passenv =
+       TWINE_PASSWORD
+setenv =
+       TWINE_USERNAME = {env:TWINE_USERNAME:__token__}
 commands =
        python -c "import path; path.Path('dist').rmtree_p()"
        python -m pep517.build .


Reply via email to