From b1cfa7cba91cb51de5e6aa0f227a30664871c4e5 Mon Sep 17 00:00:00 2001 From: Hugo Date: Sat, 24 Nov 2018 12:36:43 +0200 Subject: [PATCH 1/5] Test on Travis CI --- .travis.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..06e840d --- /dev/null +++ b/.travis.yml @@ -0,0 +1,21 @@ +language: python +cache: pip + +# Supported CPython versions: +# https://en.wikipedia.org/wiki/CPython#Version_history +matrix: + fast_finish: true + include: + - python: 3.7 + dist: xenial + - python: 3.6 + - python: 3.5 + - python: 3.4 + - python: 2.7 + +install: + - pip install -U pip + - pip install -U tox-travis + +script: + - tox From 3e9ad47e0945abfe93944a354a767b180110cdee Mon Sep 17 00:00:00 2001 From: Hugo Date: Sat, 24 Nov 2018 12:45:10 +0200 Subject: [PATCH 2/5] Drop support for EOL Python 2.6 --- create_py26_env.sh | 48 ----------------------------- pathspec/compat.py | 2 +- pathspec/tests/test_gitwildmatch.py | 7 +---- pathspec/tests/test_pathspec.py | 5 +-- pathspec/tests/test_util.py | 7 +---- setup.py | 1 - test_py26.sh | 19 ------------ 7 files changed, 4 insertions(+), 85 deletions(-) delete mode 100755 create_py26_env.sh delete mode 100755 test_py26.sh diff --git a/create_py26_env.sh b/create_py26_env.sh deleted file mode 100755 index 3968119..0000000 --- a/create_py26_env.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/bash -# -# This script creates the Python 2.6 virtual environment for testing at -# ".py26-env". This is required because Tox has removed support for Python -# 2.6. -# -# NOTE: This requires 'python2.6' to be installed and 'virtualenv' for it. -# -set -e - -if ! which python2.6 > /dev/null; then - echo "Command 'python2.6' not found. Please install Python 2.6." - echo - echo "On Ubuntu, you can download and install Python 2.6 from the deadsnakes PPA by" - echo "executing the following:" - echo - echo "sudo add-apt-repository ppa:deadsnakes/ppa" - echo "sudo apt-get update" - echo "sudo apt-get install python2.6" - echo - echo "Once Python 2.6 is installed, please run this script again." - exit 1 -fi - -if python2.6 -c 'import virtualenv' 2>&1 | grep -q 'No module named virtualenv'; then - echo "Module 'virtualenv' not found. Please install virtualenv 15.2.0 for Python 2.6." - echo - echo "You can download and install virtualenv from PyPI by executing the following:" - echo - echo "wget https://files.pythonhosted.org/packages/b1/72/2d70c5a1de409ceb3a27ff2ec007ecdd5cc52239e7c74990e32af57affe9/virtualenv-15.2.0.tar.gz" - echo "tar -xzf virtualenv-15.2.0.tar.gz" - echo "cd virtualenv-15.2.0" - echo "python2.6 setup.py build" - echo "sudo python2.6 setup.py install" - echo - echo "Once virtualenv is installed, please run this script again." - exit 1 -fi - -# Create environment. -python2.6 -m virtualenv .py26-env - -# Activate environment. -cd .py26-env -source bin/activate - -# Install test dependencies. -pip install unittest2 diff --git a/pathspec/compat.py b/pathspec/compat.py index 7ac875a..2077930 100644 --- a/pathspec/compat.py +++ b/pathspec/compat.py @@ -32,5 +32,5 @@ def iterkeys(mapping): # Python 3.6+. from collections.abc import Collection as collection_type except ImportError: - # Python 2.6 - 3.5. + # Python 2.7 - 3.5. from collections import Container as collection_type diff --git a/pathspec/tests/test_gitwildmatch.py b/pathspec/tests/test_gitwildmatch.py index 65c672c..c5a6aa2 100644 --- a/pathspec/tests/test_gitwildmatch.py +++ b/pathspec/tests/test_gitwildmatch.py @@ -6,12 +6,7 @@ import re import sys - -try: - # Python 2.6. - import unittest2 as unittest -except ImportError: - import unittest +import unittest import pathspec.patterns.gitwildmatch import pathspec.util diff --git a/pathspec/tests/test_pathspec.py b/pathspec/tests/test_pathspec.py index d880242..35ffe67 100644 --- a/pathspec/tests/test_pathspec.py +++ b/pathspec/tests/test_pathspec.py @@ -3,10 +3,7 @@ This script tests ``PathSpec``. """ -try: - import unittest2 as unittest -except ImportError: - import unittest +import unittest import pathspec diff --git a/pathspec/tests/test_util.py b/pathspec/tests/test_util.py index 7c13b50..0c4e800 100644 --- a/pathspec/tests/test_util.py +++ b/pathspec/tests/test_util.py @@ -7,13 +7,8 @@ import os import os.path import shutil -import sys import tempfile - -try: - import unittest2 as unittest -except ImportError: - import unittest +import unittest from pathspec.util import iter_tree, RecursionError diff --git a/setup.py b/setup.py index fe2d02f..31fd8ba 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,6 @@ "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.2", diff --git a/test_py26.sh b/test_py26.sh deleted file mode 100755 index 8a1552e..0000000 --- a/test_py26.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash -# -# This script tests Python 2.6 using the virtual environment at ".py26-env". -# This is required because Tox has removed support for Python 2.6. -# -set -e - -# Activate environment. -cd .py26-env -source bin/activate - -# Install pathspec. -cd .. -python setup.py clean --all -python setup.py build -python setup.py install - -# Test pathspec. -python setup.py test From b347cef9e66ac704c6e5588b2310b4f0bf90ac36 Mon Sep 17 00:00:00 2001 From: Hugo Date: Sat, 24 Nov 2018 12:46:25 +0200 Subject: [PATCH 3/5] Drop support for EOL Python 3.2 and 3.3 --- setup.py | 2 -- tox.ini | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 31fd8ba..e800511 100644 --- a/setup.py +++ b/setup.py @@ -28,8 +28,6 @@ "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.2", - "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", diff --git a/tox.ini b/tox.ini index 2737360..c45a7d5 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27, py33, py34, py35, py36, py37, pypy, pypy3 +envlist = py27, py34, py35, py36, py37, pypy, pypy3 [testenv] commands = python setup.py test From f4c584545e34bec0bea0f0d5bbf5c93f31ea7afa Mon Sep 17 00:00:00 2001 From: Hugo Date: Sat, 24 Nov 2018 12:47:04 +0200 Subject: [PATCH 4/5] Upgrade Python syntax with pyupgrade https://github.com/asottile/pyupgrade --- pathspec/pathspec.py | 6 ++-- pathspec/pattern.py | 8 ++--- pathspec/patterns/gitwildmatch.py | 2 +- pathspec/tests/test_gitwildmatch.py | 48 ++++++++++++++--------------- pathspec/tests/test_pathspec.py | 12 ++++---- pathspec/util.py | 6 ++-- 6 files changed, 41 insertions(+), 41 deletions(-) diff --git a/pathspec/pathspec.py b/pathspec/pathspec.py index 7143284..c420cfe 100644 --- a/pathspec/pathspec.py +++ b/pathspec/pathspec.py @@ -66,10 +66,10 @@ def from_lines(cls, pattern_factory, lines): if isinstance(pattern_factory, string_types): pattern_factory = util.lookup_pattern(pattern_factory) if not callable(pattern_factory): - raise TypeError("pattern_factory:{0!r} is not callable.".format(pattern_factory)) + raise TypeError("pattern_factory:{!r} is not callable.".format(pattern_factory)) if isinstance(lines, (bytes, unicode)): - raise TypeError("lines:{0!r} is not an iterable.".format(lines)) + raise TypeError("lines:{!r} is not an iterable.".format(lines)) lines = [pattern_factory(line) for line in lines if line] return cls(lines) @@ -107,7 +107,7 @@ def match_files(self, files, separators=None): :class:`str`). """ if isinstance(files, (bytes, unicode)): - raise TypeError("files:{0!r} is not an iterable.".format(files)) + raise TypeError("files:{!r} is not an iterable.".format(files)) file_map = util.normalize_files(files, separators=separators) matched_files = util.match_files(self.patterns, iterkeys(file_map)) diff --git a/pathspec/pattern.py b/pathspec/pattern.py index 8c6daeb..4ba4edf 100644 --- a/pathspec/pattern.py +++ b/pathspec/pattern.py @@ -42,7 +42,7 @@ def match(self, files): Returns an :class:`~collections.abc.Iterable` yielding each matched file path (:class:`str`). """ - raise NotImplementedError("{0}.{1} must override match().".format(self.__class__.__module__, self.__class__.__name__)) + raise NotImplementedError("{}.{} must override match().".format(self.__class__.__module__, self.__class__.__name__)) class RegexPattern(Pattern): @@ -79,7 +79,7 @@ def __init__(self, pattern, include=None): """ if isinstance(pattern, (unicode, bytes)): - assert include is None, "include:{0!r} must be null when pattern:{1!r} is a string.".format(include, pattern) + assert include is None, "include:{!r} must be null when pattern:{!r} is a string.".format(include, pattern) regex, include = self.pattern_to_regex(pattern) # NOTE: Make sure to allow a null regular expression to be # returned for a null-operation. @@ -94,10 +94,10 @@ def __init__(self, pattern, include=None): elif pattern is None: # NOTE: Make sure to allow a null pattern to be passed for a # null-operation. - assert include is None, "include:{0!r} must be null when pattern:{1!r} is null.".format(include, pattern) + assert include is None, "include:{!r} must be null when pattern:{!r} is null.".format(include, pattern) else: - raise TypeError("pattern:{0!r} is not a string, RegexObject, or None.".format(pattern)) + raise TypeError("pattern:{!r} is not a string, RegexObject, or None.".format(pattern)) super(RegexPattern, self).__init__(include) self.regex = regex diff --git a/pathspec/patterns/gitwildmatch.py b/pathspec/patterns/gitwildmatch.py index dfe81a9..d930032 100644 --- a/pathspec/patterns/gitwildmatch.py +++ b/pathspec/patterns/gitwildmatch.py @@ -45,7 +45,7 @@ def pattern_to_regex(cls, pattern): return_type = bytes pattern = pattern.decode(_BYTES_ENCODING) else: - raise TypeError("pattern:{0!r} is not a unicode or byte string.".format(pattern)) + raise TypeError("pattern:{!r} is not a unicode or byte string.".format(pattern)) pattern = pattern.strip() diff --git a/pathspec/tests/test_gitwildmatch.py b/pathspec/tests/test_gitwildmatch.py index c5a6aa2..e7ae959 100644 --- a/pathspec/tests/test_gitwildmatch.py +++ b/pathspec/tests/test_gitwildmatch.py @@ -53,10 +53,10 @@ def test_01_absolute(self): 'an/absolute/file/path/foo', 'foo/an/absolute/file/path', ])) - self.assertEqual(results, set([ + self.assertEqual(results, { 'an/absolute/file/path', 'an/absolute/file/path/foo', - ])) + }) def test_01_absolute_root(self): """ @@ -93,13 +93,13 @@ def test_01_relative(self): 'spam/foo', 'foo/spam/bar', ])) - self.assertEqual(results, set([ + self.assertEqual(results, { 'spam', 'spam/', 'foo/spam', 'spam/foo', 'foo/spam/bar', - ])) + }) def test_01_relative_nested(self): """ @@ -124,10 +124,10 @@ def test_01_relative_nested(self): 'foo/spam/bar', 'bar/foo/spam', ])) - self.assertEqual(results, set([ + self.assertEqual(results, { 'foo/spam', 'foo/spam/bar', - ])) + }) def test_02_comment(self): """ @@ -176,7 +176,7 @@ def test_03_child_double_asterisk(self): 'spam/bar', 'foo/spam/bar', ])) - self.assertEqual(results, set(['spam/bar'])) + self.assertEqual(results, {'spam/bar'}) def test_03_inner_double_asterisk(self): """ @@ -203,11 +203,11 @@ def test_03_inner_double_asterisk(self): 'left/bar/right/foo', 'foo/left/bar/right', ])) - self.assertEqual(results, set([ + self.assertEqual(results, { 'left/bar/right', 'left/foo/bar/right', 'left/bar/right/foo', - ])) + }) def test_03_only_double_asterisk(self): """ @@ -235,10 +235,10 @@ def test_03_parent_double_asterisk(self): 'foo/spam', 'foo/spam/bar', ])) - self.assertEqual(results, set([ + self.assertEqual(results, { 'foo/spam', 'foo/spam/bar', - ])) + }) def test_04_infix_wildcard(self): """ @@ -264,13 +264,13 @@ def test_04_infix_wildcard(self): 'foo-hello-bar/b', 'a/foo-hello-bar/b', ])) - self.assertEqual(results, set([ + self.assertEqual(results, { 'foo--bar', 'foo-hello-bar', 'a/foo-hello-bar', 'foo-hello-bar/b', 'a/foo-hello-bar/b', - ])) + }) def test_04_postfix_wildcard(self): """ @@ -296,13 +296,13 @@ def test_04_postfix_wildcard(self): 'foo/~temp-bar', 'foo/~temp-bar/baz', ])) - self.assertEqual(results, set([ + self.assertEqual(results, { '~temp-', '~temp-foo', '~temp-foo/bar', 'foo/~temp-bar', 'foo/~temp-bar/baz', - ])) + }) def test_04_prefix_wildcard(self): """ @@ -326,12 +326,12 @@ def test_04_prefix_wildcard(self): 'foo/bar.py', 'foo/bar.py/baz', ])) - self.assertEqual(results, set([ + self.assertEqual(results, { 'bar.py', 'bar.py/', 'foo/bar.py', 'foo/bar.py/baz', - ])) + }) def test_05_directory(self): """ @@ -358,11 +358,11 @@ def test_05_directory(self): 'foo/dir/bar', 'dir', ])) - self.assertEqual(results, set([ + self.assertEqual(results, { 'dir/', 'foo/dir/', 'foo/dir/bar', - ])) + }) def test_06_registered(self): """ @@ -406,7 +406,7 @@ def test_07_match_bytes_and_bytes(self): """ pattern = GitWildMatchPattern(b'*.py') results = set(pattern.match([b'a.py'])) - self.assertEqual(results, set([b'a.py'])) + self.assertEqual(results, {b'a.py'}) def test_07_match_bytes_and_bytes_complete(self): """ @@ -416,7 +416,7 @@ def test_07_match_bytes_and_bytes_complete(self): escaped = b"".join(b"\\" + encoded[i:i+1] for i in range(len(encoded))) pattern = GitWildMatchPattern(escaped) results = set(pattern.match([encoded])) - self.assertEqual(results, set([encoded])) + self.assertEqual(results, {encoded}) @unittest.skipIf(sys.version_info[0] >= 3, "Python 3 is strict") def test_07_match_bytes_and_unicode(self): @@ -425,7 +425,7 @@ def test_07_match_bytes_and_unicode(self): """ pattern = GitWildMatchPattern(b'*.py') results = set(pattern.match(['a.py'])) - self.assertEqual(results, set(['a.py'])) + self.assertEqual(results, {'a.py'}) @unittest.skipIf(sys.version_info[0] == 2, "Python 2 is lenient") def test_07_match_bytes_and_unicode_fail(self): @@ -444,7 +444,7 @@ def test_07_match_unicode_and_bytes(self): """ pattern = GitWildMatchPattern('*.py') results = set(pattern.match([b'a.py'])) - self.assertEqual(results, set([b'a.py'])) + self.assertEqual(results, {b'a.py'}) @unittest.skipIf(sys.version_info[0] == 2, "Python 2 is lenient") def test_07_match_unicode_and_bytes_fail(self): @@ -462,4 +462,4 @@ def test_07_match_unicode_and_unicode(self): """ pattern = GitWildMatchPattern('*.py') results = set(pattern.match(['a.py'])) - self.assertEqual(results, set(['a.py'])) + self.assertEqual(results, {'a.py'}) diff --git a/pathspec/tests/test_pathspec.py b/pathspec/tests/test_pathspec.py index 35ffe67..1f5bb8b 100644 --- a/pathspec/tests/test_pathspec.py +++ b/pathspec/tests/test_pathspec.py @@ -30,11 +30,11 @@ def test_01_current_dir_paths(self): './src/test2/b.txt', './src/test2/c/c.txt', ])) - self.assertEqual(results, set([ + self.assertEqual(results, { './src/test2/a.txt', './src/test2/b.txt', './src/test2/c/c.txt', - ])) + }) def test_01_match_files(self): """ @@ -74,11 +74,11 @@ def test_01_windows_current_dir_paths(self): '.\\src\\test2\\b.txt', '.\\src\\test2\\c\\c.txt', ], separators=('\\',))) - self.assertEqual(results, set([ + self.assertEqual(results, { '.\\src\\test2\\a.txt', '.\\src\\test2\\b.txt', '.\\src\\test2\\c\\c.txt', - ])) + }) def test_01_windows_paths(self): """ @@ -96,11 +96,11 @@ def test_01_windows_paths(self): 'src\\test2\\b.txt', 'src\\test2\\c\\c.txt', ], separators=('\\',))) - self.assertEqual(results, set([ + self.assertEqual(results, { 'src\\test2\\a.txt', 'src\\test2\\b.txt', 'src\\test2\\c\\c.txt', - ])) + }) def test_02_eq(self): """ diff --git a/pathspec/util.py b/pathspec/util.py index 0b29ea7..daaf68b 100644 --- a/pathspec/util.py +++ b/pathspec/util.py @@ -46,7 +46,7 @@ def iter_tree(root, on_error=None, follow_links=None): each file (:class:`str`) relative to *root*. """ if on_error is not None and not callable(on_error): - raise TypeError("on_error:{0!r} is not callable.".format(on_error)) + raise TypeError("on_error:{!r} is not callable.".format(on_error)) if follow_links is None: follow_links = True @@ -242,9 +242,9 @@ def register_pattern(name, pattern_factory, override=None): (:data:`False`). Default is :data:`None` for :data:`False`. """ if not isinstance(name, string_types): - raise TypeError("name:{0!r} is not a string.".format(name)) + raise TypeError("name:{!r} is not a string.".format(name)) if not callable(pattern_factory): - raise TypeError("pattern_factory:{0!r} is not callable.".format(pattern_factory)) + raise TypeError("pattern_factory:{!r} is not callable.".format(pattern_factory)) if name in _registered_patterns and not override: raise AlreadyRegisteredError(name, _registered_patterns[name]) _registered_patterns[name] = pattern_factory From 86f30293c4ff75f88a8164d0c625f1b3c5a9085e Mon Sep 17 00:00:00 2001 From: Hugo Date: Sat, 24 Nov 2018 12:50:24 +0200 Subject: [PATCH 5/5] Add python_requires to help pip --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index e800511..5c648da 100644 --- a/setup.py +++ b/setup.py @@ -19,6 +19,7 @@ url="https://github.com/cpburnz/python-path-specification", description="Utility library for gitignore style pattern matching of file paths.", long_description=readme + "\n\n" + changes, + python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', classifiers=[ "Development Status :: 4 - Beta", "Intended Audience :: Developers",