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 .