From 439890c9e56a90e396564cf2e6cd578b9230e2bf Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 11 Jun 2021 11:09:27 +0300 Subject: Drop support for EOL Python 3.5 --- .github/workflows/test.yml | 2 +- NEWS | 3 +++ README.rst | 2 +- doc/hacking.rst | 2 +- doc/overview.rst | 4 ++-- scripts/all-pythons | 2 +- setup.cfg | 1 - setup.py | 2 +- tox.ini | 2 +- 9 files changed, 11 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 44c27b7..a52e0ad 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: [3.5, 3.6, 3.7, 3.8, 3.9, "3.10", pypy3] + python-version: [3.6, 3.7, 3.8, 3.9, "3.10", pypy3] steps: - uses: actions/checkout@v2 diff --git a/NEWS b/NEWS index b9c7b1b..91068c2 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,9 @@ Improvements * Add support for Python 3.10. (Jürgen Gmach) +* Drop support for Python 3.5 (EOL). + (Hugo van Kemenade) + * Distutils integration is deprecated and will be removed in the next major version. diff --git a/README.rst b/README.rst index df8302d..3a377fe 100644 --- a/README.rst +++ b/README.rst @@ -34,7 +34,7 @@ under the same license as Python, see LICENSE for details. Supported platforms ------------------- - * Python 3.5+ or PyPy3 + * Python 3.6+ or PyPy3 If you would like to use testtools for earlier Pythons, consult the compatibility docs: diff --git a/doc/hacking.rst b/doc/hacking.rst index d0ded96..6cb6423 100644 --- a/doc/hacking.rst +++ b/doc/hacking.rst @@ -15,7 +15,7 @@ Coding style In general, follow `PEP 8`_ except where consistency with the standard library's unittest_ module would suggest otherwise. -testtools supports Python 3.5 and later. +testtools supports Python 3.6 and later. Copyright assignment -------------------- diff --git a/doc/overview.rst b/doc/overview.rst index 4681933..e978420 100644 --- a/doc/overview.rst +++ b/doc/overview.rst @@ -84,7 +84,7 @@ Cross-Python compatibility -------------------------- testtools gives you the very latest in unit testing technology in a way that -will work with Python 3.5+ and PyPy3. +will work with Python 3.6+ and PyPy3. If you wish to use testtools with Python 2.4 or 2.5, then please use testtools 0.9.15. @@ -94,4 +94,4 @@ If you wish to use testtools with Python 2.6 or 3.2, then please use testtools If you wish to use testtools with Python 3.3 or 3.4, then please use testtools 2.3.0. -If you wish to use testtools with Python 2.7, then please use testtools 2.4.0. +If you wish to use testtools with Python 2.7 or 3.5, then please use testtools 2.4.0. diff --git a/scripts/all-pythons b/scripts/all-pythons index 0fbe010..28793ec 100755 --- a/scripts/all-pythons +++ b/scripts/all-pythons @@ -89,5 +89,5 @@ def now(): if __name__ == '__main__': sys.path.append(ROOT) result = TestProtocolClient(sys.stdout) - for version in '3.5 3.6 3.7 3.8 3.9 3.10'.split(): + for version in '3.6 3.7 3.8 3.9 3.10'.split(): run_for_python(version, result, sys.argv[1:]) diff --git a/setup.cfg b/setup.cfg index c31f8f3..e9cbecd 100644 --- a/setup.cfg +++ b/setup.cfg @@ -12,7 +12,6 @@ classifier = Operating System :: OS Independent Programming Language :: Python Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 diff --git a/setup.py b/setup.py index 3aa9218..e76930c 100755 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ except: setuptools.setup( - python_requires='>=3.5', + python_requires='>=3.6', cmdclass=cmd_class, setup_requires=['pbr'], pbr=True) diff --git a/tox.ini b/tox.ini index 0416e06..82ce299 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py35,py36,py37,py38,py39,py310,pypy3 +envlist = py36,py37,py38,py39,py310,pypy3 minversion = 1.6 [testenv] -- cgit v1.2.1 From 182bb4b405f29f33a9086ef35c0fa814ac299447 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 7 Feb 2022 14:16:32 +0200 Subject: Upgrade Python syntax with pyupgrade --py36-plus --- scripts/_lp_release.py | 30 ++++++++--------- testtools/compat.py | 1 - testtools/content_type.py | 4 +-- testtools/matchers/_basic.py | 20 +++++------ testtools/matchers/_const.py | 2 +- testtools/matchers/_datastructures.py | 8 ++--- testtools/matchers/_dict.py | 6 ++-- testtools/matchers/_doctest.py | 2 +- testtools/matchers/_exception.py | 4 +-- testtools/matchers/_higherorder.py | 18 +++++----- testtools/matchers/_impl.py | 2 +- testtools/matchers/_warnings.py | 2 +- testtools/testcase.py | 5 ++- testtools/testresult/real.py | 22 ++++++------- testtools/tests/matchers/test_basic.py | 16 ++++----- testtools/tests/matchers/test_datastructures.py | 4 +-- testtools/tests/matchers/test_exception.py | 6 ++-- testtools/tests/matchers/test_impl.py | 2 +- testtools/tests/samplecases.py | 4 +-- testtools/tests/test_monkey.py | 44 ++++++++++++------------- testtools/tests/test_testcase.py | 14 ++++---- testtools/tests/test_testresult.py | 1 - testtools/tests/twistedsupport/test_runtest.py | 4 +-- testtools/testsuite.py | 6 ++-- testtools/twistedsupport/_deferred.py | 2 +- testtools/twistedsupport/_runtest.py | 4 +-- testtools/twistedsupport/_spinner.py | 2 +- 27 files changed, 116 insertions(+), 119 deletions(-) diff --git a/scripts/_lp_release.py b/scripts/_lp_release.py index e19a139..80ddc92 100644 --- a/scripts/_lp_release.py +++ b/scripts/_lp_release.py @@ -80,16 +80,16 @@ def assign_fix_committed_to_next(testtools, next_milestone): """Find all 'Fix Committed' and make sure they are in 'next'.""" fixed_bugs = list(testtools.searchTasks(status=FIX_COMMITTED)) for task in fixed_bugs: - LOG.debug("{}".format(task.title)) + LOG.debug(f"{task.title}") if task.milestone != next_milestone: task.milestone = next_milestone - LOG.info("Re-assigning {}".format(task.title)) + LOG.info(f"Re-assigning {task.title}") task.lp_save() def rename_milestone(next_milestone, new_name): """Rename 'next_milestone' to 'new_name'.""" - LOG.info("Renaming {} to {}".format(next_milestone.name, new_name)) + LOG.info(f"Renaming {next_milestone.name} to {new_name}") next_milestone.name = new_name next_milestone.lp_save() @@ -103,8 +103,8 @@ def get_release_notes_and_changelog(news_path): def is_heading_marker(line, marker_char): return line and line == marker_char * len(line) - LOG.debug("Loading NEWS from {}".format(news_path)) - with open(news_path, 'r') as news: + LOG.debug(f"Loading NEWS from {news_path}") + with open(news_path) as news: for line in news: line = line.strip() if state is None: @@ -146,7 +146,7 @@ def get_release_notes_and_changelog(news_path): def release_milestone(milestone, release_notes, changelog): date_released = datetime.now(tz=UTC) LOG.info( - "Releasing milestone: {}, date {}".format(milestone.name, date_released)) + f"Releasing milestone: {milestone.name}, date {date_released}") release = milestone.createProductRelease( date_released=date_released, changelog=changelog, @@ -159,20 +159,20 @@ def release_milestone(milestone, release_notes, changelog): def create_milestone(series, name): """Create a new milestone in the same series as 'release_milestone'.""" - LOG.info("Creating milestone {} in series {}".format(name, series.name)) + LOG.info(f"Creating milestone {name} in series {series.name}") return series.newMilestone(name=name) def close_fixed_bugs(milestone): tasks = list(milestone.searchTasks()) for task in tasks: - LOG.debug("Found {}".format(task.title)) + LOG.debug(f"Found {task.title}") if task.status == FIX_COMMITTED: - LOG.info("Closing {}".format(task.title)) + LOG.info(f"Closing {task.title}") task.status = FIX_RELEASED else: LOG.warning( - "Bug not fixed, removing from milestone: {}".format(task.title)) + f"Bug not fixed, removing from milestone: {task.title}") task.milestone = None task.lp_save() @@ -184,7 +184,7 @@ def upload_tarball(release, tarball_path): with open(sig_path) as sig: sig_content = sig.read() tarball_name = os.path.basename(tarball_path) - LOG.info("Uploading tarball: {}".format(tarball_path)) + LOG.info(f"Uploading tarball: {tarball_path}") release.add_file( file_type=CODE_RELEASE_TARBALL, file_content=tarball_content, filename=tarball_name, @@ -198,17 +198,17 @@ def release_project(launchpad, project_name, next_milestone_name): next_milestone = testtools.getMilestone(name=next_milestone_name) release_name, release_notes, changelog = get_release_notes_and_changelog( get_path('NEWS')) - LOG.info("Releasing {} {}".format(project_name, release_name)) + LOG.info(f"Releasing {project_name} {release_name}") # Since reversing these operations is hard, and inspecting errors from # Launchpad is also difficult, do some looking before leaping. errors = [] - tarball_path = get_path('dist/{}-{}.tar.gz'.format(project_name, release_name)) + tarball_path = get_path(f'dist/{project_name}-{release_name}.tar.gz') if not os.path.isfile(tarball_path): - errors.append("{} does not exist".format(tarball_path)) + errors.append(f"{tarball_path} does not exist") if not os.path.isfile(tarball_path + '.asc'): errors.append("{} does not exist".format(tarball_path + '.asc')) if testtools.getMilestone(name=release_name): - errors.append("Milestone {} exists on {}".format(release_name, project_name)) + errors.append(f"Milestone {release_name} exists on {project_name}") if errors: for error in errors: LOG.error(error) diff --git a/testtools/compat.py b/testtools/compat.py index 95d7f44..a9556e6 100644 --- a/testtools/compat.py +++ b/testtools/compat.py @@ -2,7 +2,6 @@ """Compatibility support for python 2 and 3.""" -__metaclass__ = type __all__ = [ '_b', 'advance_iterator', diff --git a/testtools/content_type.py b/testtools/content_type.py index 7a171a4..18db09f 100644 --- a/testtools/content_type.py +++ b/testtools/content_type.py @@ -30,10 +30,10 @@ class ContentType: if self.parameters: params = '; ' params += '; '.join( - sorted('{}="{}"'.format(k, v) for k, v in self.parameters.items())) + sorted(f'{k}="{v}"' for k, v in self.parameters.items())) else: params = '' - return "{}/{}{}".format(self.type, self.subtype, params) + return f"{self.type}/{self.subtype}{params}" JSON = ContentType('application', 'json') diff --git a/testtools/matchers/_basic.py b/testtools/matchers/_basic.py index 99d8c1b..7c5d88f 100644 --- a/testtools/matchers/_basic.py +++ b/testtools/matchers/_basic.py @@ -51,7 +51,7 @@ class _BinaryComparison: self.expected = expected def __str__(self): - return "{}({!r})".format(self.__class__.__name__, self.expected) + return f"{self.__class__.__name__}({self.expected!r})" def match(self, other): if self.comparator(other, self.expected): @@ -75,7 +75,7 @@ class _BinaryMismatch(Mismatch): @property def expected(self): warnings.warn( - '{}.expected deprecated after 1.8.1'.format(self.__class__.__name__), + f'{self.__class__.__name__}.expected deprecated after 1.8.1', DeprecationWarning, stacklevel=2, ) @@ -84,7 +84,7 @@ class _BinaryMismatch(Mismatch): @property def other(self): warnings.warn( - '{}.other deprecated after 1.8.1'.format(self.__class__.__name__), + f'{self.__class__.__name__}.other deprecated after 1.8.1', DeprecationWarning, stacklevel=2, ) @@ -102,7 +102,7 @@ class _BinaryMismatch(Mismatch): left, right = actual, reference else: left, right = reference, actual - return "{} {} {}".format(left, self._mismatch_string, right) + return f"{left} {self._mismatch_string} {right}" class Equals(_BinaryComparison): @@ -176,7 +176,7 @@ class SameMembers(Matcher): self.expected = expected def __str__(self): - return '{}({!r})'.format(self.__class__.__name__, self.expected) + return f'{self.__class__.__name__}({self.expected!r})' def match(self, observed): expected_only = list_subtract(self.expected, observed) @@ -216,7 +216,7 @@ class StartsWith(Matcher): self.expected = expected def __str__(self): - return "StartsWith({!r})".format(self.expected) + return f"StartsWith({self.expected!r})" def match(self, matchee): if not matchee.startswith(self.expected): @@ -251,7 +251,7 @@ class EndsWith(Matcher): self.expected = expected def __str__(self): - return "EndsWith({!r})".format(self.expected) + return f"EndsWith({self.expected!r})" def match(self, matchee): if not matchee.endswith(self.expected): @@ -292,7 +292,7 @@ class NotAnInstance(Mismatch): else: typestr = 'any of (%s)' % ', '.join(type.__name__ for type in self.types) - return "'{}' is not an instance of {}".format(self.matchee, typestr) + return f"'{self.matchee}' is not an instance of {typestr}" class DoesNotContain(Mismatch): @@ -307,7 +307,7 @@ class DoesNotContain(Mismatch): self.needle = needle def describe(self): - return "{!r} not in {!r}".format(self.needle, self.matchee) + return f"{self.needle!r} not in {self.matchee!r}" class Contains(Matcher): @@ -321,7 +321,7 @@ class Contains(Matcher): self.needle = needle def __str__(self): - return "Contains({!r})".format(self.needle) + return f"Contains({self.needle!r})" def match(self, matchee): try: diff --git a/testtools/matchers/_const.py b/testtools/matchers/_const.py index 827ce83..3103a2c 100644 --- a/testtools/matchers/_const.py +++ b/testtools/matchers/_const.py @@ -40,7 +40,7 @@ class _Never: def match(self, value): return Mismatch( - 'Inevitable mismatch on {!r}'.format(value)) + f'Inevitable mismatch on {value!r}') def Never(): diff --git a/testtools/matchers/_datastructures.py b/testtools/matchers/_datastructures.py index 7c0f9bd..9a7f84f 100644 --- a/testtools/matchers/_datastructures.py +++ b/testtools/matchers/_datastructures.py @@ -137,7 +137,7 @@ class MatchesStructure: def __str__(self): kws = [] for attr, matcher in sorted(self.kws.items()): - kws.append("{}={}".format(attr, matcher)) + kws.append(f"{attr}={matcher}") return "{}({})".format(self.__class__.__name__, ', '.join(kws)) def match(self, value): @@ -208,18 +208,18 @@ class MatchesSetwise: if common_length == 0: raise AssertionError("common_length can't be 0 here") if common_length > 1: - msg = "There were {} mismatches".format(common_length) + msg = f"There were {common_length} mismatches" else: msg = "There was 1 mismatch" if len(remaining_matchers) > len(not_matched): extra_matchers = remaining_matchers[common_length:] - msg += " and {} extra matcher".format(len(extra_matchers)) + msg += f" and {len(extra_matchers)} extra matcher" if len(extra_matchers) > 1: msg += "s" msg += ': ' + ', '.join(map(str, extra_matchers)) elif len(not_matched) > len(remaining_matchers): extra_values = not_matched[common_length:] - msg += " and {} extra value".format(len(extra_values)) + msg += f" and {len(extra_values)} extra value" if len(extra_values) > 1: msg += "s" msg += ': ' + str(extra_values) diff --git a/testtools/matchers/_dict.py b/testtools/matchers/_dict.py index d67f0e7..29ec104 100644 --- a/testtools/matchers/_dict.py +++ b/testtools/matchers/_dict.py @@ -36,7 +36,7 @@ class MatchesAllDict(Matcher): self.matchers = matchers def __str__(self): - return 'MatchesAllDict({})'.format(_format_matcher_dict(self.matchers)) + return f'MatchesAllDict({_format_matcher_dict(self.matchers)})' def match(self, observed): mismatches = {} @@ -56,7 +56,7 @@ class DictMismatches(Mismatch): def describe(self): lines = ['{'] lines.extend( - [' {!r}: {},'.format(key, mismatch.describe()) + [f' {key!r}: {mismatch.describe()},' for (key, mismatch) in sorted(self.mismatches.items())]) lines.append('}') return '\n'.join(lines) @@ -133,7 +133,7 @@ class _SuperDictOf(Matcher): def _format_matcher_dict(matchers): return '{%s}' % ( - ', '.join(sorted('{!r}: {}'.format(k, v) for k, v in matchers.items()))) + ', '.join(sorted(f'{k!r}: {v}' for k, v in matchers.items()))) class _CombinedMatcher(Matcher): diff --git a/testtools/matchers/_doctest.py b/testtools/matchers/_doctest.py index a9882b2..8ae31ff 100644 --- a/testtools/matchers/_doctest.py +++ b/testtools/matchers/_doctest.py @@ -69,7 +69,7 @@ class DocTestMatches: flagstr = ", flags=%d" % self.flags else: flagstr = "" - return 'DocTestMatches({!r}{})'.format(self.want, flagstr) + return f'DocTestMatches({self.want!r}{flagstr})' def _with_nl(self, actual): result = self.want.__class__(actual) diff --git a/testtools/matchers/_exception.py b/testtools/matchers/_exception.py index f272907..66aaa90 100644 --- a/testtools/matchers/_exception.py +++ b/testtools/matchers/_exception.py @@ -60,7 +60,7 @@ class MatchesException(Matcher): if self._is_instance: expected_class = expected_class.__class__ if not issubclass(other[0], expected_class): - return Mismatch('{!r} is not a {!r}'.format(other[0], expected_class)) + return Mismatch(f'{other[0]!r} is not a {expected_class!r}') if self._is_instance: if other[1].args != self.expected.args: return Mismatch('{} has different arguments to {}.'.format( @@ -95,7 +95,7 @@ class Raises(Matcher): def match(self, matchee): try: result = matchee() - return Mismatch('{!r} returned {!r}'.format(matchee, result)) + return Mismatch(f'{matchee!r} returned {result!r}') # Catch all exceptions: Raises() should be able to match a # KeyboardInterrupt or SystemExit. except: diff --git a/testtools/matchers/_higherorder.py b/testtools/matchers/_higherorder.py index 88335d4..4871ae6 100644 --- a/testtools/matchers/_higherorder.py +++ b/testtools/matchers/_higherorder.py @@ -95,7 +95,7 @@ class Not: self.matcher = matcher def __str__(self): - return 'Not({})'.format(self.matcher) + return f'Not({self.matcher})' def match(self, other): mismatch = self.matcher.match(other) @@ -113,7 +113,7 @@ class MatchedUnexpectedly(Mismatch): self.other = other def describe(self): - return "{!r} matches {}".format(self.other, self.matcher) + return f"{self.other!r} matches {self.matcher}" class Annotate: @@ -134,7 +134,7 @@ class Annotate: return cls(annotation, matcher) def __str__(self): - return 'Annotate({!r}, {})'.format(self.annotation, self.matcher) + return f'Annotate({self.annotation!r}, {self.matcher})' def match(self, other): mismatch = self.matcher.match(other) @@ -151,7 +151,7 @@ class PostfixedMismatch(MismatchDecorator): self.mismatch = mismatch def describe(self): - return '{}: {}'.format(self.original.describe(), self.annotation) + return f'{self.original.describe()}: {self.annotation}' AnnotatedMismatch = PostfixedMismatch @@ -164,7 +164,7 @@ class PrefixedMismatch(MismatchDecorator): self.prefix = prefix def describe(self): - return '{}: {}'.format(self.prefix, self.original.describe()) + return f'{self.prefix}: {self.original.describe()}' class AfterPreprocessing: @@ -206,7 +206,7 @@ class AfterPreprocessing: after = self.preprocessor(value) if self.annotate: matcher = Annotate( - "after {} on {!r}".format(self._str_preprocessor(), value), + f"after {self._str_preprocessor()} on {value!r}", self.matcher) else: matcher = self.matcher @@ -225,7 +225,7 @@ class AllMatch: self.matcher = matcher def __str__(self): - return 'AllMatch({})'.format(self.matcher) + return f'AllMatch({self.matcher})' def match(self, values): mismatches = [] @@ -244,7 +244,7 @@ class AnyMatch: self.matcher = matcher def __str__(self): - return 'AnyMatch({})'.format(self.matcher) + return f'AnyMatch({self.matcher})' def match(self, values): mismatches = [] @@ -360,7 +360,7 @@ class _MatchesPredicateWithParams(Matcher): self.predicate, self.message) else: name = self.name - return '{}({})'.format(name, args) + return f'{name}({args})' def match(self, x): if not self.predicate(x, *self.args, **self.kwargs): diff --git a/testtools/matchers/_impl.py b/testtools/matchers/_impl.py index 55c7c52..b7dc4fa 100644 --- a/testtools/matchers/_impl.py +++ b/testtools/matchers/_impl.py @@ -149,7 +149,7 @@ class MismatchDecorator: self.original = original def __repr__(self): - return ''.format(self.original) + return f'' def describe(self): return self.original.describe() diff --git a/testtools/matchers/_warnings.py b/testtools/matchers/_warnings.py index 0b808d4..f6a6ece 100644 --- a/testtools/matchers/_warnings.py +++ b/testtools/matchers/_warnings.py @@ -91,7 +91,7 @@ class Warnings: return Mismatch('Expected at least one warning, got none') def __str__(self): - return 'Warnings({!s})'.format(self.warnings_matcher) + return f'Warnings({self.warnings_matcher!s})' def IsDeprecated(message): diff --git a/testtools/testcase.py b/testtools/testcase.py index 522c95d..012def3 100644 --- a/testtools/testcase.py +++ b/testtools/testcase.py @@ -2,7 +2,6 @@ """Test case related stuff.""" -__metaclass__ = type __all__ = [ 'attr', 'clone_test_with_new_id', @@ -205,7 +204,7 @@ def unique_text_generator(prefix): index = 0 while True: unique_text = _unique_text(BASE_CP, CP_RANGE, index) - yield '{}-{}'.format(prefix, unique_text) + yield f'{prefix}-{unique_text}' index = index + 1 @@ -278,7 +277,7 @@ class TestCase(unittest.TestCase): def __repr__(self): # We add id to the repr because it makes testing testtools easier. - return "<{} id=0x{:0x}>".format(self.id(), id(self)) + return f"<{self.id()} id=0x{id(self):0x}>" def addDetail(self, name, content_object): """Add a detail to be reported with this test's outcome. diff --git a/testtools/testresult/real.py b/testtools/testresult/real.py index 8313656..8c7f372 100644 --- a/testtools/testresult/real.py +++ b/testtools/testresult/real.py @@ -589,7 +589,7 @@ class StreamResultRouter(StreamResult): """ policy_method = StreamResultRouter._policies.get(policy, None) if not policy_method: - raise ValueError("bad policy {!r}".format(policy)) + raise ValueError(f"bad policy {policy!r}") policy_method(self, sink, **policy_args) if do_start_stop_run: self._sinks.append(sink) @@ -599,7 +599,7 @@ class StreamResultRouter(StreamResult): def _map_route_code_prefix(self, sink, route_prefix, consume_route=False): if '/' in route_prefix: raise TypeError( - "{!r} is more than one route step long".format(route_prefix)) + f"{route_prefix!r} is more than one route step long") self._route_code_prefixes[route_prefix] = (sink, consume_route) _policies['route_code_prefix'] = _map_route_code_prefix @@ -1152,7 +1152,7 @@ class TextTestResult(TestResult): def _show_list(self, label, error_list): for test, output in error_list: self.stream.write(self.sep1) - self.stream.write("{}: {}\n".format(label, test.id())) + self.stream.write(f"{label}: {test.id()}\n") self.stream.write(self.sep2) self.stream.write(output) @@ -1230,7 +1230,7 @@ class ThreadsafeForwardingResult(TestResult): self._test_tags = set(), set() def __repr__(self): - return '<{} {!r}>'.format(self.__class__.__name__, self.result) + return f'<{self.__class__.__name__} {self.result!r}>' def _any_tags(self, tags): return bool(tags[0] or tags[1]) @@ -1371,7 +1371,7 @@ class ExtendedToOriginalDecorator: self._shouldStop = False def __repr__(self): - return '<{} {!r}>'.format(self.__class__.__name__, self.decorated) + return f'<{self.__class__.__name__} {self.decorated!r}>' def __getattr__(self, name): return getattr(self.decorated, name) @@ -1741,7 +1741,7 @@ class ResourcedToStreamDecorator(ExtendedToStreamDecorator): resource_id = "{}.{}".format( resource.__class__.__module__, resource.__class__.__name__) - test_id = '{}.{}'.format(resource_id, method) + test_id = f'{resource_id}.{method}' if phase == 'start': test_status = 'inprogress' @@ -2051,8 +2051,8 @@ class _StringException(Exception): def _format_text_attachment(name, text): if '\n' in text: - return "{}: {{{{{{\n{}\n}}}}}}\n".format(name, text) - return "{}: {{{{{{{}}}}}}}".format(name, text) + return f"{name}: {{{{{{\n{text}\n}}}}}}\n" + return f"{name}: {{{{{{{text}}}}}}}" def _details_to_str(details, special=None): @@ -2080,7 +2080,7 @@ def _details_to_str(details, special=None): continue # We want the 'special' attachment to be at the bottom. if key == special: - special_content = '{}\n'.format(text) + special_content = f'{text}\n' continue text_attachments.append(_format_text_attachment(key, text)) if text_attachments and not text_attachments[-1].endswith('\n'): @@ -2091,11 +2091,11 @@ def _details_to_str(details, special=None): if binary_attachments: lines.append('Binary content:\n') for name, content_type in binary_attachments: - lines.append(' {} ({})\n'.format(name, content_type)) + lines.append(f' {name} ({content_type})\n') if empty_attachments: lines.append('Empty attachments:\n') for name in empty_attachments: - lines.append(' {}\n'.format(name)) + lines.append(f' {name}\n') if (binary_attachments or empty_attachments) and text_attachments: lines.append('\n') lines.append('\n'.join(text_attachments)) diff --git a/testtools/tests/matchers/test_basic.py b/testtools/tests/matchers/test_basic.py index 1d107ee..bfda8fa 100644 --- a/testtools/tests/matchers/test_basic.py +++ b/testtools/tests/matchers/test_basic.py @@ -44,12 +44,12 @@ class Test_BinaryMismatch(TestCase): def test_short_objects(self): o1, o2 = self.CustomRepr('a'), self.CustomRepr('b') mismatch = _BinaryMismatch(o1, "!~", o2) - self.assertEqual(mismatch.describe(), "{!r} !~ {!r}".format(o1, o2)) + self.assertEqual(mismatch.describe(), f"{o1!r} !~ {o2!r}") def test_short_mixed_strings(self): b, u = _b("\xa7"), "\xa7" mismatch = _BinaryMismatch(b, "!~", u) - self.assertEqual(mismatch.describe(), "{!r} !~ {!r}".format(b, u)) + self.assertEqual(mismatch.describe(), f"{b!r} !~ {u!r}") def test_long_bytes(self): one_line_b = self._long_b.replace(_b("\n"), _b(" ")) @@ -241,7 +241,7 @@ class DoesNotStartWithTests(TestCase): string = _b("A\xA7") suffix = _b("B\xA7") mismatch = DoesNotStartWith(string, suffix) - self.assertEqual("{!r} does not start with {!r}.".format(string, suffix), + self.assertEqual(f"{string!r} does not start with {suffix!r}.", mismatch.describe()) @@ -256,12 +256,12 @@ class StartsWithTests(TestCase): def test_str_with_bytes(self): b = _b("\xA7") matcher = StartsWith(b) - self.assertEqual("StartsWith({!r})".format(b), str(matcher)) + self.assertEqual(f"StartsWith({b!r})", str(matcher)) def test_str_with_unicode(self): u = "\xA7" matcher = StartsWith(u) - self.assertEqual("StartsWith({!r})".format(u), str(matcher)) + self.assertEqual(f"StartsWith({u!r})", str(matcher)) def test_match(self): matcher = StartsWith("bar") @@ -302,7 +302,7 @@ class DoesNotEndWithTests(TestCase): string = _b("A\xA7") suffix = _b("B\xA7") mismatch = DoesNotEndWith(string, suffix) - self.assertEqual("{!r} does not end with {!r}.".format(string, suffix), + self.assertEqual(f"{string!r} does not end with {suffix!r}.", mismatch.describe()) @@ -317,12 +317,12 @@ class EndsWithTests(TestCase): def test_str_with_bytes(self): b = _b("\xA7") matcher = EndsWith(b) - self.assertEqual("EndsWith({!r})".format(b), str(matcher)) + self.assertEqual(f"EndsWith({b!r})", str(matcher)) def test_str_with_unicode(self): u = "\xA7" matcher = EndsWith(u) - self.assertEqual("EndsWith({!r})".format(u), str(matcher)) + self.assertEqual(f"EndsWith({u!r})", str(matcher)) def test_match(self): matcher = EndsWith("arf") diff --git a/testtools/tests/matchers/test_datastructures.py b/testtools/tests/matchers/test_datastructures.py index 86ba943..bb85c77 100644 --- a/testtools/tests/matchers/test_datastructures.py +++ b/testtools/tests/matchers/test_datastructures.py @@ -119,12 +119,12 @@ class TestMatchesSetwise(TestCase): description_matcher): mismatch = matcher.match(value) if mismatch is None: - self.fail("{} matched {}".format(matcher, value)) + self.fail(f"{matcher} matched {value}") actual_description = mismatch.describe() self.assertThat( actual_description, Annotate( - "{} matching {}".format(matcher, value), + f"{matcher} matching {value}", description_matcher)) def test_matches(self): diff --git a/testtools/tests/matchers/test_exception.py b/testtools/tests/matchers/test_exception.py index 2a959f0..8431985 100644 --- a/testtools/tests/matchers/test_exception.py +++ b/testtools/tests/matchers/test_exception.py @@ -43,7 +43,7 @@ class TestMatchesExceptionInstanceInterface(TestCase, TestMatchersInterface): MatchesException(Exception('foo'))) ] describe_examples = [ - ("{!r} is not a {!r}".format(Exception, ValueError), + (f"{Exception!r} is not a {ValueError!r}", error_base_foo, MatchesException(ValueError("foo"))), ("ValueError('bar'%s) has different arguments to ValueError('foo'%s)." @@ -67,7 +67,7 @@ class TestMatchesExceptionTypeInterface(TestCase, TestMatchersInterface): MatchesException(Exception)) ] describe_examples = [ - ("{!r} is not a {!r}".format(Exception, ValueError), + (f"{Exception!r} is not a {ValueError!r}", error_base_foo, MatchesException(ValueError)), ] @@ -107,7 +107,7 @@ class TestMatchesExceptionTypeMatcherInterface(TestCase, TestMatchersInterface): MatchesException(Exception, Equals('foo'))) ] describe_examples = [ - ("{!r} != 5".format(error_bar[1]), + (f"{error_bar[1]!r} != 5", error_bar, MatchesException(ValueError, Equals(5))), ] diff --git a/testtools/tests/matchers/test_impl.py b/testtools/tests/matchers/test_impl.py index 8d6309c..c2558bf 100644 --- a/testtools/tests/matchers/test_impl.py +++ b/testtools/tests/matchers/test_impl.py @@ -113,7 +113,7 @@ class TestMismatchDecorator(TestCase): x = Mismatch("description", {'foo': 'bar'}) decorated = MismatchDecorator(x) self.assertEqual( - ''.format(x), + f'', repr(decorated)) diff --git a/testtools/tests/samplecases.py b/testtools/tests/samplecases.py index bd793d7..3575f57 100644 --- a/testtools/tests/samplecases.py +++ b/testtools/tests/samplecases.py @@ -133,8 +133,8 @@ def _make_behavior_scenarios(stage): Ordering is not consistent. """ return ( - ('{}={}'.format(stage, behavior), - {'{}_behavior'.format(stage): function}) + (f'{stage}={behavior}', + {f'{stage}_behavior': function}) for (behavior, function) in behaviors ) diff --git a/testtools/tests/test_monkey.py b/testtools/tests/test_monkey.py index ceae685..7e8d755 100644 --- a/testtools/tests/test_monkey.py +++ b/testtools/tests/test_monkey.py @@ -33,9 +33,9 @@ class MonkeyPatcherTest(TestCase): # We can't assert that all state is unchanged, but at least we can # check our test object. - self.assertEquals(self.original_object.foo, self.test_object.foo) - self.assertEquals(self.original_object.bar, self.test_object.bar) - self.assertEquals(self.original_object.baz, self.test_object.baz) + self.assertEqual(self.original_object.foo, self.test_object.foo) + self.assertEqual(self.original_object.bar, self.test_object.bar) + self.assertEqual(self.original_object.baz, self.test_object.baz) def test_construct_with_patches(self): # Constructing a 'MonkeyPatcher' with patches adds all of the given @@ -43,23 +43,23 @@ class MonkeyPatcherTest(TestCase): patcher = MonkeyPatcher((self.test_object, 'foo', 'haha'), (self.test_object, 'bar', 'hehe')) patcher.patch() - self.assertEquals('haha', self.test_object.foo) - self.assertEquals('hehe', self.test_object.bar) - self.assertEquals(self.original_object.baz, self.test_object.baz) + self.assertEqual('haha', self.test_object.foo) + self.assertEqual('hehe', self.test_object.bar) + self.assertEqual(self.original_object.baz, self.test_object.baz) def test_patch_existing(self): # Patching an attribute that exists sets it to the value defined in the # patch. self.monkey_patcher.add_patch(self.test_object, 'foo', 'haha') self.monkey_patcher.patch() - self.assertEquals(self.test_object.foo, 'haha') + self.assertEqual(self.test_object.foo, 'haha') def test_patch_non_existing(self): # Patching a non-existing attribute sets it to the value defined in # the patch. self.monkey_patcher.add_patch(self.test_object, 'doesntexist', 'value') self.monkey_patcher.patch() - self.assertEquals(self.test_object.doesntexist, 'value') + self.assertEqual(self.test_object.doesntexist, 'value') def test_restore_non_existing(self): # Restoring a value that didn't exist before the patch deletes the @@ -76,18 +76,18 @@ class MonkeyPatcherTest(TestCase): self.monkey_patcher.add_patch(self.test_object, 'foo', 'blah') self.monkey_patcher.add_patch(self.test_object, 'foo', 'BLAH') self.monkey_patcher.patch() - self.assertEquals(self.test_object.foo, 'BLAH') + self.assertEqual(self.test_object.foo, 'BLAH') self.monkey_patcher.restore() - self.assertEquals(self.test_object.foo, self.original_object.foo) + self.assertEqual(self.test_object.foo, self.original_object.foo) def test_restore_twice_is_a_no_op(self): # Restoring an already-restored monkey patch is a no-op. self.monkey_patcher.add_patch(self.test_object, 'foo', 'blah') self.monkey_patcher.patch() self.monkey_patcher.restore() - self.assertEquals(self.test_object.foo, self.original_object.foo) + self.assertEqual(self.test_object.foo, self.original_object.foo) self.monkey_patcher.restore() - self.assertEquals(self.test_object.foo, self.original_object.foo) + self.assertEqual(self.test_object.foo, self.original_object.foo) def test_run_with_patches_decoration(self): # run_with_patches runs the given callable, passing in all arguments @@ -99,8 +99,8 @@ class MonkeyPatcherTest(TestCase): return 'foo' result = self.monkey_patcher.run_with_patches(f, 1, 2, c=10) - self.assertEquals('foo', result) - self.assertEquals([(1, 2, 10)], log) + self.assertEqual('foo', result) + self.assertEqual([(1, 2, 10)], log) def test_repeated_run_with_patches(self): # We can call the same function with run_with_patches more than @@ -111,11 +111,11 @@ class MonkeyPatcherTest(TestCase): self.monkey_patcher.add_patch(self.test_object, 'foo', 'haha') result = self.monkey_patcher.run_with_patches(f) - self.assertEquals( + self.assertEqual( ('haha', self.original_object.bar, self.original_object.baz), result) result = self.monkey_patcher.run_with_patches(f) - self.assertEquals( + self.assertEqual( ('haha', self.original_object.bar, self.original_object.baz), result) @@ -123,16 +123,16 @@ class MonkeyPatcherTest(TestCase): # run_with_patches restores the original values after the function has # executed. self.monkey_patcher.add_patch(self.test_object, 'foo', 'haha') - self.assertEquals(self.original_object.foo, self.test_object.foo) + self.assertEqual(self.original_object.foo, self.test_object.foo) self.monkey_patcher.run_with_patches(lambda: None) - self.assertEquals(self.original_object.foo, self.test_object.foo) + self.assertEqual(self.original_object.foo, self.test_object.foo) def test_run_with_patches_restores_on_exception(self): # run_with_patches restores the original values even when the function # raises an exception. def _(): - self.assertEquals(self.test_object.foo, 'haha') - self.assertEquals(self.test_object.bar, 'blahblah') + self.assertEqual(self.test_object.foo, 'haha') + self.assertEqual(self.test_object.bar, 'blahblah') raise RuntimeError("Something went wrong!") self.monkey_patcher.add_patch(self.test_object, 'foo', 'haha') @@ -140,8 +140,8 @@ class MonkeyPatcherTest(TestCase): self.assertThat(lambda:self.monkey_patcher.run_with_patches(_), Raises(MatchesException(RuntimeError("Something went wrong!")))) - self.assertEquals(self.test_object.foo, self.original_object.foo) - self.assertEquals(self.test_object.bar, self.original_object.bar) + self.assertEqual(self.test_object.foo, self.original_object.foo) + self.assertEqual(self.test_object.bar, self.original_object.bar) class TestPatchHelper(TestCase): diff --git a/testtools/tests/test_testcase.py b/testtools/tests/test_testcase.py index 014f680..1b2ee73 100644 --- a/testtools/tests/test_testcase.py +++ b/testtools/tests/test_testcase.py @@ -351,7 +351,7 @@ class TestAssertions(TestCase): self.assertEqual(1, len(raisedExceptions)) self.assertTrue( exception is raisedExceptions[0], - "{!r} is not {!r}".format(exception, raisedExceptions[0])) + f"{exception!r} is not {raisedExceptions[0]!r}") def test_assertRaises_with_multiple_exceptions(self): # assertRaises((ExceptionOne, ExceptionTwo), function) asserts that @@ -382,12 +382,12 @@ class TestAssertions(TestCase): self.assertThat( lambda: self.assertRaises(Exception, foo), Raises( - MatchesException(self.failureException, '.*{!r}.*'.format(foo)))) + MatchesException(self.failureException, f'.*{foo!r}.*'))) def test_assertRaisesRegexp(self): # assertRaisesRegexp asserts that function raises particular exception # with particular message. - self.assertRaisesRegexp(RuntimeError, r"M\w*e", self.raiseError, + self.assertRaisesRegex(RuntimeError, r"M\w*e", self.raiseError, RuntimeError, "Message") def test_assertRaisesRegexp_wrong_error_type(self): @@ -734,7 +734,7 @@ class TestAssertions(TestCase): 'a', repr('\xa7')[1:-1], "'''", - 'actual = {!r}'.format(b), + f'actual = {b!r}', ': ' + message, ]) self.assertFails(expected_error, self.assertEqual, a, b, message) @@ -1097,7 +1097,7 @@ class TestExpectedFailure(TestWithDetails): class ReferenceTest(TestCase): @unittest.expectedFailure def test_fails_expectedly(self): - self.assertEquals(1, 0) + self.assertEqual(1, 0) test = ReferenceTest('test_fails_expectedly') result = test.run() @@ -1107,7 +1107,7 @@ class TestExpectedFailure(TestWithDetails): class ReferenceTest(TestCase): @unittest.expectedFailure def test_passes_unexpectedly(self): - self.assertEquals(1, 1) + self.assertEqual(1, 1) test = ReferenceTest('test_passes_unexpectedly') result = test.run() @@ -1672,7 +1672,7 @@ class TestSkipping(TestCase): setup_ran = False def setUp(self): - super(SkippingTestCase, self).setUp() + super().setUp() self.setup_ran = True def test_skipped(self): diff --git a/testtools/tests/test_testresult.py b/testtools/tests/test_testresult.py index 4fbf15d..0c07914 100644 --- a/testtools/tests/test_testresult.py +++ b/testtools/tests/test_testresult.py @@ -2,7 +2,6 @@ """Test TestResults and related things.""" -__metaclass__ = type import codecs import datetime diff --git a/testtools/tests/twistedsupport/test_runtest.py b/testtools/tests/twistedsupport/test_runtest.py index 2a9ddf3..dd363a5 100644 --- a/testtools/tests/twistedsupport/test_runtest.py +++ b/testtools/tests/twistedsupport/test_runtest.py @@ -804,7 +804,7 @@ class TestAssertFailsWith(NeedsTwistedTestCase): failure.trap(self.failureException) self.assertThat( str(failure.value), - Equals("RuntimeError not raised ({!r} returned)".format(marker))) + Equals(f"RuntimeError not raised ({marker!r} returned)")) d.addCallbacks( lambda x: self.fail("Should not have succeeded"), check_result) return d @@ -867,7 +867,7 @@ class TestAssertFailsWith(NeedsTwistedTestCase): failure.trap(CustomException) self.assertThat( str(failure.value), - Equals("RuntimeError not raised ({!r} returned)".format(marker))) + Equals(f"RuntimeError not raised ({marker!r} returned)")) return d.addCallbacks( lambda x: self.fail("Should not have succeeded"), check_result) diff --git a/testtools/testsuite.py b/testtools/testsuite.py index b6284fb..79fc485 100644 --- a/testtools/testsuite.py +++ b/testtools/testsuite.py @@ -171,7 +171,7 @@ class ConcurrentStreamTestSuite: elif event == 'startTestRun': pass else: - raise ValueError('unknown event type {!r}'.format(event)) + raise ValueError(f'unknown event type {event!r}') except: for thread, process_result in threads.values(): # Signal to each TestControl in the ExtendedToStreamDecorator @@ -187,7 +187,7 @@ class ConcurrentStreamTestSuite: except Exception: # The run logic itself failed. case = testtools.ErrorHolder( - "broken-runner-'{}'".format(route_code), + f"broken-runner-'{route_code}'", error=sys.exc_info()) case.run(process_result) finally: @@ -306,7 +306,7 @@ def sorted_tests(suite_or_case, unpack_outer=False): test_id: count for test_id, count in seen.items() if count > 1} if duplicates: raise ValueError( - 'Duplicate test ids detected: {}'.format(pformat(duplicates))) + f'Duplicate test ids detected: {pformat(duplicates)}') tests = _flatten_tests(suite_or_case, unpack_outer=unpack_outer) tests.sort() diff --git a/testtools/twistedsupport/_deferred.py b/testtools/twistedsupport/_deferred.py index e14d903..71cea9d 100644 --- a/testtools/twistedsupport/_deferred.py +++ b/testtools/twistedsupport/_deferred.py @@ -11,7 +11,7 @@ class DeferredNotFired(Exception): """Raised when we extract a result from a Deferred that's not fired yet.""" def __init__(self, deferred): - msg = "{!r} has not fired yet.".format(deferred) + msg = f"{deferred!r} has not fired yet." super().__init__(msg) diff --git a/testtools/twistedsupport/_runtest.py b/testtools/twistedsupport/_runtest.py index 85ab051..c8860e9 100644 --- a/testtools/twistedsupport/_runtest.py +++ b/testtools/twistedsupport/_runtest.py @@ -506,7 +506,7 @@ def assert_fails_with(d, *exc_types, **kwargs): def got_success(result): raise failureException( - "{} not raised ({!r} returned)".format(expected_names, result)) + f"{expected_names} not raised ({result!r} returned)") def got_failure(failure): if failure.check(*exc_types): @@ -533,4 +533,4 @@ class UncleanReactorError(Exception): ret = str(junk) else: ret = repr(junk) - return ' {}\n'.format(ret) + return f' {ret}\n' diff --git a/testtools/twistedsupport/_spinner.py b/testtools/twistedsupport/_spinner.py index 3a92c8f..e01c48c 100644 --- a/testtools/twistedsupport/_spinner.py +++ b/testtools/twistedsupport/_spinner.py @@ -106,7 +106,7 @@ class TimeoutError(Exception): def __init__(self, function, timeout): Exception.__init__(self, - "{!r} took longer than {} seconds".format(function, timeout)) + f"{function!r} took longer than {timeout} seconds") class NoResultError(Exception): -- cgit v1.2.1 From 91c84ca33677be1ee43fb93eae8e29c6ba6dcaf2 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 7 Jun 2022 16:38:32 +0300 Subject: Add final noop job to indicate success and set as required --- .github/workflows/test.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6d96083..67bcba9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -45,3 +45,10 @@ jobs: run: | make clean-sphinx docs + success: + needs: build + runs-on: ubuntu-latest + name: test successful + steps: + - name: Success + run: echo Test successful -- cgit v1.2.1