diff --git a/pyproject.toml b/pyproject.toml index f454cb21..22e7ff9a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -60,3 +60,19 @@ module = [ "testscenarios.*", ] ignore_missing_imports = true + +[tool.ruff.lint] +select = [ + "E", + "F", + "I", + "PIE", + "UP", + "RSE", + "RUF", +] +ignore = [ +] + +[tool.ruff.lint.pydocstyle] +convention = "google" diff --git a/scripts/_lp_release.py b/scripts/_lp_release.py index ddb24644..ef15baef 100644 --- a/scripts/_lp_release.py +++ b/scripts/_lp_release.py @@ -18,14 +18,13 @@ --sign'. """ -from datetime import datetime, timedelta, tzinfo import logging import os import sys +from datetime import datetime, timedelta, tzinfo -from launchpadlib.launchpad import Launchpad from launchpadlib import uris - +from launchpadlib.launchpad import Launchpad APP_NAME = "testtools-lp-release" CACHE_DIR = os.path.expanduser("~/.launchpadlib/cache") diff --git a/testtools/__init__.py b/testtools/__init__.py index 7421c8a6..80608726 100644 --- a/testtools/__init__.py +++ b/testtools/__init__.py @@ -3,32 +3,20 @@ """Extensions to the standard Python unittest library.""" __all__ = [ - "clone_test_with_new_id", - "CopyStreamResult", - "ConcurrentTestSuite", "ConcurrentStreamTestSuite", + "ConcurrentTestSuite", + "CopyStreamResult", "DecorateTestCaseResult", "ErrorHolder", "ExpectedException", "ExtendedToOriginalDecorator", "ExtendedToStreamDecorator", "FixtureSuite", - "iterate_tests", - "MultipleExceptions", "MultiTestResult", + "MultipleExceptions", "PlaceHolder", - "run_test_with", "ResourcedToStreamDecorator", - "Tagger", - "TestCase", - "TestByTestResult", - "TestResult", - "TestResultDecorator", - "TextTestResult", "RunTest", - "skip", - "skipIf", - "skipUnless", "StreamFailFast", "StreamResult", "StreamResultRouter", @@ -37,18 +25,29 @@ "StreamToDict", "StreamToExtendedDecorator", "StreamToQueue", + "Tagger", + "TestByTestResult", + "TestCase", "TestControl", + "TestResult", + "TestResultDecorator", + "TextTestResult", "ThreadsafeForwardingResult", "TimestampingStreamResult", + "__version__", + "clone_test_with_new_id", + "iterate_tests", + "run_test_with", + "skip", + "skipIf", + "skipUnless", "try_import", "unique_text_generator", "version", - "__version__", ] from testtools.helpers import try_import from testtools.matchers._impl import Matcher # noqa: F401 - from testtools.runtest import ( MultipleExceptions, RunTest, @@ -90,8 +89,8 @@ TimestampingStreamResult, ) from testtools.testsuite import ( - ConcurrentTestSuite, ConcurrentStreamTestSuite, + ConcurrentTestSuite, FixtureSuite, iterate_tests, ) diff --git a/testtools/compat.py b/testtools/compat.py index 437ba6af..9dd90b8c 100644 --- a/testtools/compat.py +++ b/testtools/compat.py @@ -3,12 +3,12 @@ """Compatibility support for python 2 and 3.""" __all__ = [ + "BytesIO", + "StringIO", "_b", "advance_iterator", "reraise", "unicode_output_stream", - "StringIO", - "BytesIO", ] import codecs @@ -19,7 +19,7 @@ import unicodedata # Ensure retro-compatibility with older testtools releases -from io import StringIO, BytesIO +from io import BytesIO, StringIO def reraise(exc_class, exc_obj, exc_tb, _marker=object()): @@ -71,7 +71,7 @@ def _slow_escape(text): def text_repr(text, multiline=None): """Rich repr for ``text`` returning unicode, triple quoted if ``multiline``.""" - nl = isinstance(text, bytes) and bytes((0xA,)) or "\n" + nl = (isinstance(text, bytes) and bytes((0xA,))) or "\n" if multiline is None: multiline = nl in text if not multiline: diff --git a/testtools/content.py b/testtools/content.py index 0dd6177d..deea8fc3 100644 --- a/testtools/content.py +++ b/testtools/content.py @@ -3,13 +3,13 @@ """Content - a MIME-like Content object.""" __all__ = [ - "attach_file", "Content", + "TracebackContent", + "attach_file", "content_from_file", "content_from_stream", "json_content", "text_content", - "TracebackContent", ] import codecs @@ -19,8 +19,7 @@ import traceback from testtools.compat import _b -from testtools.content_type import ContentType, JSON, UTF8_TEXT - +from testtools.content_type import JSON, UTF8_TEXT, ContentType _join_b = _b("").join @@ -62,9 +61,7 @@ class Content: def __init__(self, content_type, get_bytes): """Create a ContentType.""" if None in (content_type, get_bytes): - raise ValueError( - "None not permitted in {!r}, {!r}".format(content_type, get_bytes) - ) + raise ValueError(f"None not permitted in {content_type!r}, {get_bytes!r}") self.content_type = content_type self._get_bytes = get_bytes @@ -96,7 +93,7 @@ def iter_text(self): :raises ValueError: If the content type is not "text/*". """ if self.content_type.type != "text": - raise ValueError("Not a text type %r" % self.content_type) + raise ValueError(f"Not a text type {self.content_type!r}") return self._iter_text() def _iter_text(self): @@ -110,8 +107,9 @@ def _iter_text(self): yield final def __repr__(self): - return "".format( - self.content_type, _join_b(self.iter_bytes()) + return ( + f"" ) @@ -250,7 +248,7 @@ def text_content(text): """ if not isinstance(text, str): raise TypeError( - "text_content must be given text, not '%s'." % type(text).__name__ + f"text_content must be given text, not '{type(text).__name__}'." ) return Content(UTF8_TEXT, lambda: [text.encode("utf8")]) diff --git a/testtools/content_type.py b/testtools/content_type.py index 406dc90b..1736ceaf 100644 --- a/testtools/content_type.py +++ b/testtools/content_type.py @@ -15,9 +15,7 @@ class ContentType: def __init__(self, primary_type, sub_type, parameters=None): """Create a ContentType.""" if None in (primary_type, sub_type): - raise ValueError( - "None not permitted in {!r}, {!r}".format(primary_type, sub_type) - ) + raise ValueError(f"None not permitted in {primary_type!r}, {sub_type!r}") self.type = primary_type self.subtype = sub_type self.parameters = parameters or {} diff --git a/testtools/matchers/__init__.py b/testtools/matchers/__init__.py index ba63f41a..6f9a341d 100644 --- a/testtools/matchers/__init__.py +++ b/testtools/matchers/__init__.py @@ -18,9 +18,9 @@ "Always", "Annotate", "AnyMatch", + "ContainedByDict", "Contains", "ContainsAll", - "ContainedByDict", "ContainsDict", "DirContains", "DirExists", @@ -48,17 +48,17 @@ "MatchesSetwise", "MatchesStructure", "Never", - "NotEquals", "Not", + "NotEquals", "PathExists", "Raises", - "raises", "SameMembers", "SamePath", "StartsWith", "TarballContains", - "Warnings", "WarningMessage", + "Warnings", + "raises", ] from ._basic import ( @@ -120,11 +120,6 @@ MatchesPredicateWithParams, Not, ) -from ._warnings import ( - IsDeprecated, - WarningMessage, - Warnings, -) # XXX: These are not explicitly included in __all__. It's unclear how much of # the public interface they really are. @@ -133,3 +128,8 @@ Mismatch, MismatchError, ) +from ._warnings import ( + IsDeprecated, + WarningMessage, + Warnings, +) diff --git a/testtools/matchers/_basic.py b/testtools/matchers/_basic.py index 4e4b1453..c2ac5398 100644 --- a/testtools/matchers/_basic.py +++ b/testtools/matchers/_basic.py @@ -16,8 +16,8 @@ ] import operator -from pprint import pformat import re +from pprint import pformat from ..compat import ( text_repr, @@ -34,8 +34,7 @@ def _format(thing): - """ - Blocks of text with newlines are formatted as triple-quote + """Blocks of text with newlines are formatted as triple-quote strings. Everything else is pretty-printed. """ if isinstance(thing, (str, bytes)): @@ -74,8 +73,10 @@ def describe(self): actual = repr(self._actual) reference = repr(self._reference) if len(actual) + len(reference) > 70: - return "{}:\nreference = {}\nactual = {}\n".format( - self._mismatch_string, _format(self._reference), _format(self._actual) + return ( + f"{self._mismatch_string}:\n" + f"reference = {_format(self._reference)}\n" + f"actual = {_format(self._actual)}\n" ) else: if self._reference_on_right: @@ -164,8 +165,9 @@ def match(self, observed): if expected_only == observed_only == []: return return PostfixedMismatch( - "\nmissing: {}\nextra: {}".format( - _format(expected_only), _format(observed_only) + ( + f"\nmissing: {_format(expected_only)}\n" + f"extra: {_format(observed_only)}" ), _BinaryMismatch(observed, "elements differ", self.expected), ) @@ -182,8 +184,8 @@ def __init__(self, matchee, expected): self.expected = expected def describe(self): - return "{} does not start with {}.".format( - text_repr(self.matchee), text_repr(self.expected) + return ( + f"{text_repr(self.matchee)} does not start with {text_repr(self.expected)}." ) @@ -217,8 +219,8 @@ def __init__(self, matchee, expected): self.expected = expected def describe(self): - return "{} does not end with {}.".format( - text_repr(self.matchee), text_repr(self.expected) + return ( + f"{text_repr(self.matchee)} does not end with {text_repr(self.expected)}." ) @@ -272,7 +274,9 @@ def describe(self): if len(self.types) == 1: typestr = self.types[0].__name__ else: - typestr = "any of (%s)" % ", ".join(type.__name__ for type in self.types) + typestr = "any of ({})".format( + ", ".join(type.__name__ for type in self.types) + ) return f"'{self.matchee}' is not an instance of {typestr}" @@ -321,13 +325,13 @@ def __init__(self, pattern, flags=0): self.flags = flags def __str__(self): - args = ["%r" % self.pattern] + args = [f"{self.pattern!r}"] flag_arg = [] # dir() sorts the attributes for us, so we don't need to do it again. for flag in dir(re): if len(flag) == 1: if self.flags & getattr(re, flag): - flag_arg.append("re.%s" % flag) + flag_arg.append(f"re.{flag}") if flag_arg: args.append("|".join(flag_arg)) return "{}({})".format(self.__class__.__name__, ", ".join(args)) diff --git a/testtools/matchers/_datastructures.py b/testtools/matchers/_datastructures.py index 5d7edf2b..ab141968 100644 --- a/testtools/matchers/_datastructures.py +++ b/testtools/matchers/_datastructures.py @@ -193,9 +193,7 @@ def match(self, observed): if len(not_matched) == 0: if len(remaining_matchers) > 1: - msg = "There were {} matchers left over: ".format( - len(remaining_matchers) - ) + msg = f"There were {len(remaining_matchers)} matchers left over: " else: msg = "There was 1 matcher left over: " msg += ", ".join(map(str, remaining_matchers)) @@ -203,14 +201,10 @@ def match(self, observed): elif len(remaining_matchers) == 0: if len(not_matched) > 1: return Mismatch( - "There were {} values left over: {}".format( - len(not_matched), not_matched - ) + f"There were {len(not_matched)} values left over: {not_matched}" ) else: - return Mismatch( - "There was 1 value left over: {}".format(not_matched) - ) + return Mismatch(f"There was 1 value left over: {not_matched}") else: common_length = min(len(remaining_matchers), len(not_matched)) if common_length == 0: diff --git a/testtools/matchers/_dict.py b/testtools/matchers/_dict.py index 2c0eaecf..13d1575a 100644 --- a/testtools/matchers/_dict.py +++ b/testtools/matchers/_dict.py @@ -4,6 +4,8 @@ "KeysEqual", ] +from typing import ClassVar + from ..helpers import ( dict_subtract, filter_values, @@ -11,8 +13,8 @@ ) from ._higherorder import ( AnnotatedMismatch, - PrefixedMismatch, MismatchesAll, + PrefixedMismatch, ) from ._impl import Matcher, Mismatch @@ -131,7 +133,9 @@ def match(self, super_dict): def _format_matcher_dict(matchers): - return "{%s}" % (", ".join(sorted(f"{k!r}: {v}" for k, v in matchers.items()))) + return "{{{}}}".format( + ", ".join(sorted(f"{k!r}: {v}" for k, v in matchers.items())) + ) class _CombinedMatcher(Matcher): @@ -144,7 +148,7 @@ class _CombinedMatcher(Matcher): Not **entirely** dissimilar from ``MatchesAll``. """ - matcher_factories = {} + matcher_factories: ClassVar[dict] = {} def __init__(self, expected): super().__init__() @@ -154,9 +158,7 @@ def format_expected(self, expected): return repr(expected) def __str__(self): - return "{}({})".format( - self.__class__.__name__, self.format_expected(self._expected) - ) + return f"{self.__class__.__name__}({self.format_expected(self._expected)})" def match(self, observed): matchers = {k: v(self._expected) for k, v in self.matcher_factories.items()} @@ -172,7 +174,7 @@ class MatchesDict(_CombinedMatcher): expected dict. """ - matcher_factories = { + matcher_factories: ClassVar[dict] = { "Extra": _SubDictOf, "Missing": lambda m: _SuperDictOf(m, format_value=str), "Differences": _MatchCommonKeys, @@ -197,7 +199,7 @@ class ContainsDict(_CombinedMatcher): match. """ - matcher_factories = { + matcher_factories: ClassVar[dict] = { "Missing": lambda m: _SuperDictOf(m, format_value=str), "Differences": _MatchCommonKeys, } @@ -221,7 +223,7 @@ class ContainedByDict(_CombinedMatcher): match. """ - matcher_factories = { + matcher_factories: ClassVar[dict] = { "Extra": _SubDictOf, "Differences": _MatchCommonKeys, } @@ -249,10 +251,10 @@ def __init__(self, *expected): self.expected = list(expected) def __str__(self): - return "KeysEqual(%s)" % ", ".join(map(repr, self.expected)) + return "KeysEqual({})".format(", ".join(map(repr, self.expected))) def match(self, matchee): - from ._basic import _BinaryMismatch, Equals + from ._basic import Equals, _BinaryMismatch expected = sorted(self.expected) matched = Equals(expected).match(sorted(matchee.keys())) diff --git a/testtools/matchers/_doctest.py b/testtools/matchers/_doctest.py index 89cec67c..3bbcddf0 100644 --- a/testtools/matchers/_doctest.py +++ b/testtools/matchers/_doctest.py @@ -69,7 +69,7 @@ def __init__(self, example, flags=0): def __str__(self): if self.flags: - flagstr = ", flags=%d" % self.flags + flagstr = f", flags={self.flags}" else: flagstr = "" return f"DocTestMatches({self.want!r}{flagstr})" diff --git a/testtools/matchers/_exception.py b/testtools/matchers/_exception.py index 02e58c82..d583abff 100644 --- a/testtools/matchers/_exception.py +++ b/testtools/matchers/_exception.py @@ -15,7 +15,6 @@ Mismatch, ) - _error_repr = BaseException.__repr__ @@ -56,7 +55,7 @@ def __init__(self, exception, value_re=None): def match(self, other): if not isinstance(other, tuple): - return Mismatch("%r is not an exc_info tuple" % other) + return Mismatch(f"{other!r} is not an exc_info tuple") expected_class = self.expected if self._is_instance: expected_class = expected_class.__class__ @@ -65,17 +64,16 @@ def match(self, other): if self._is_instance: if other[1].args != self.expected.args: return Mismatch( - "{} has different arguments to {}.".format( - _error_repr(other[1]), _error_repr(self.expected) - ) + f"{_error_repr(other[1])} has different arguments to " + f"{_error_repr(self.expected)}." ) elif self.value_re is not None: return self.value_re.match(other[1]) def __str__(self): if self._is_instance: - return "MatchesException(%s)" % _error_repr(self.expected) - return "MatchesException(%s)" % repr(self.expected) + return f"MatchesException({_error_repr(self.expected)})" + return f"MatchesException({self.expected!r})" class Raises(Matcher): diff --git a/testtools/matchers/_filesystem.py b/testtools/matchers/_filesystem.py index d769106e..5452ef8c 100644 --- a/testtools/matchers/_filesystem.py +++ b/testtools/matchers/_filesystem.py @@ -3,8 +3,8 @@ """Matchers for things related to the filesystem.""" __all__ = [ - "FileContains", "DirExists", + "FileContains", "FileExists", "HasPermissions", "PathExists", @@ -130,7 +130,7 @@ def match(self, path): f.close() def __str__(self): - return "File at path exists and contains %s" % self.contents + return f"File at path exists and contains {self.contents}" class HasPermissions(Matcher): diff --git a/testtools/matchers/_higherorder.py b/testtools/matchers/_higherorder.py index a14df81b..7c67d0d2 100644 --- a/testtools/matchers/_higherorder.py +++ b/testtools/matchers/_higherorder.py @@ -5,8 +5,8 @@ "AllMatch", "Annotate", "AnyMatch", - "MatchesAny", "MatchesAll", + "MatchesAny", "Not", ] @@ -35,7 +35,9 @@ def match(self, matchee): return MismatchesAll(results) def __str__(self): - return "MatchesAny(%s)" % ", ".join([str(matcher) for matcher in self.matchers]) + return "MatchesAny({})".format( + ", ".join([str(matcher) for matcher in self.matchers]) + ) class MatchesAll: @@ -53,7 +55,7 @@ def __init__(self, *matchers, **options): self.first_only = options.get("first_only", False) def __str__(self): - return "MatchesAll(%s)" % ", ".join(map(str, self.matchers)) + return "MatchesAll({})".format(", ".join(map(str, self.matchers))) def match(self, matchee): results = [] @@ -193,13 +195,11 @@ def __init__(self, preprocessor, matcher, annotate=True): def _str_preprocessor(self): if isinstance(self.preprocessor, types.FunctionType): - return "" % self.preprocessor.__name__ + return f"" return str(self.preprocessor) def __str__(self): - return "AfterPreprocessing({}, {})".format( - self._str_preprocessor(), self.matcher - ) + return f"AfterPreprocessing({self._str_preprocessor()}, {self.matcher})" def match(self, value): after = self.preprocessor(value) @@ -276,9 +276,7 @@ def __init__(self, predicate, message): self.message = message def __str__(self): - return "{}({!r}, {!r})".format( - self.__class__.__name__, self.predicate, self.message - ) + return f"{self.__class__.__name__}({self.predicate!r}, {self.message!r})" def match(self, x): if not self.predicate(x): @@ -348,16 +346,14 @@ def __init__(self, predicate, message, name, *args, **kwargs): def __str__(self): args = [str(arg) for arg in self.args] - kwargs = ["%s=%s" % item for item in self.kwargs.items()] + kwargs = ["{}={}".format(*item) for item in self.kwargs.items()] args = ", ".join(args + kwargs) if self.name is None: - name = "MatchesPredicateWithParams({!r}, {!r})".format( - self.predicate, self.message - ) + name = f"MatchesPredicateWithParams({self.predicate!r}, {self.message!r})" else: name = self.name return f"{name}({args})" def match(self, x): if not self.predicate(x, *self.args, **self.kwargs): - return Mismatch(self.message.format(*((x,) + self.args), **self.kwargs)) + return Mismatch(self.message.format(*((x, *self.args)), **self.kwargs)) diff --git a/testtools/matchers/_impl.py b/testtools/matchers/_impl.py index f6a2174e..05aee234 100644 --- a/testtools/matchers/_impl.py +++ b/testtools/matchers/_impl.py @@ -95,8 +95,9 @@ def get_details(self): return getattr(self, "_details", {}) def __repr__(self): - return "".format( - id(self), self.__dict__ + return ( + f"" ) @@ -124,10 +125,9 @@ def __str__(self): matchee = text_repr(self.matchee, multiline=False) else: matchee = repr(self.matchee) - return "Match failed. Matchee: %s\nMatcher: %s\nDifference: %s\n" % ( - matchee, - self.matcher, - difference, + return ( + f"Match failed. Matchee: {matchee}\n" + f"Matcher: {self.matcher}\nDifference: {difference}\n" ) else: return difference diff --git a/testtools/matchers/_warnings.py b/testtools/matchers/_warnings.py index 15355d70..f243dc74 100644 --- a/testtools/matchers/_warnings.py +++ b/testtools/matchers/_warnings.py @@ -1,6 +1,6 @@ # Copyright (c) 2009-2016 testtools developers. See LICENSE for details. -__all__ = ["Warnings", "WarningMessage", "IsDeprecated"] +__all__ = ["IsDeprecated", "WarningMessage", "Warnings"] import warnings @@ -15,8 +15,7 @@ def WarningMessage(category_type, message=None, filename=None, lineno=None, line=None): - r""" - Create a matcher that will match `warnings.WarningMessage`\s. + r"""Create a matcher that will match `warnings.WarningMessage`\s. For example, to match captured `DeprecationWarning`s with a message about some ``foo`` being replaced with ``bar``: @@ -55,13 +54,10 @@ def WarningMessage(category_type, message=None, filename=None, lineno=None, line class Warnings: - """ - Match if the matchee produces warnings. - """ + """Match if the matchee produces warnings.""" def __init__(self, warnings_matcher=None): - """ - Create a Warnings matcher. + """Create a Warnings matcher. :param warnings_matcher: Optional validator for the warnings emitted by matchee. If no warnings_matcher is supplied then the simple fact that @@ -83,8 +79,7 @@ def __str__(self): def IsDeprecated(message): - """ - Make a matcher that checks that a callable produces exactly one + """Make a matcher that checks that a callable produces exactly one `DeprecationWarning`. :param message: Matcher for the warning message. diff --git a/testtools/run.py b/testtools/run.py index 5e49f1ed..419ff15f 100755 --- a/testtools/run.py +++ b/testtools/run.py @@ -8,16 +8,15 @@ $ python -m testtools.run testtools.tests.test_suite """ -from functools import partial import os.path import sys import unittest +from functools import partial from testtools import TextTestResult from testtools.compat import unicode_output_stream from testtools.testsuite import filter_by_ids, iterate_tests, sorted_tests - defaultTestLoader = unittest.defaultTestLoader defaultTestLoaderCls = unittest.TestLoader have_discover = True @@ -90,15 +89,15 @@ def list(self, test, loader): """List the tests that would be run if test() was run.""" test_ids, _ = list_test(test) for test_id in test_ids: - self.stdout.write("%s\n" % test_id) + self.stdout.write(f"{test_id}\n") errors = loader.errors if errors: for test_id in errors: - self.stdout.write("%s\n" % test_id) + self.stdout.write(f"{test_id}\n") sys.exit(2) def run(self, test): - "Run the given test case or test suite." + """Run the given test case or test suite.""" result = TextTestResult( unicode_output_stream(self.stdout), failfast=self.failfast, @@ -175,9 +174,9 @@ def __init__( self.testRunner = testRunner self.testLoader = testLoader progName = argv[0] - if progName.endswith("%srun.py" % os.path.sep): + if progName.endswith(f"{os.path.sep}run.py"): elements = progName.split(os.path.sep) - progName = "%s.run" % elements[-2] + progName = f"{elements[-2]}.run" else: progName = os.path.basename(argv[0]) self.progName = progName @@ -207,7 +206,7 @@ def __init__( runner.list(self.test) else: for test in iterate_tests(self.test): - self.stdout.write("%s\n" % test.id()) + self.stdout.write(f"{test.id()}\n") del self.testLoader.errors[:] def _getParentArgParser(self): diff --git a/testtools/testcase.py b/testtools/testcase.py index 84551861..c20dd5a3 100644 --- a/testtools/testcase.py +++ b/testtools/testcase.py @@ -3,15 +3,15 @@ """Test case related stuff.""" __all__ = [ + "ExpectedException", + "TestCase", "attr", "clone_test_with_new_id", - "ExpectedException", "gather_details", "run_test_with", "skip", "skipIf", "skipUnless", - "TestCase", "unique_text_generator", ] @@ -23,17 +23,17 @@ import unittest from unittest.case import SkipTest -from testtools.compat import reraise from testtools import content +from testtools.compat import reraise from testtools.helpers import try_import from testtools.matchers import ( Annotate, Contains, + Is, + IsInstance, MatchesAll, MatchesException, MismatchError, - Is, - IsInstance, Not, Raises, ) @@ -157,7 +157,7 @@ def gather_details(source_dict, target_dict): new_name = name disambiguator = itertools.count(1) while new_name in target_dict: - new_name = "%s-%d" % (name, next(disambiguator)) + new_name = f"{name}-{next(disambiguator)}" name = new_name target_dict[name] = _copy_content(content_object) @@ -508,7 +508,7 @@ def addDetailUniqueName(self, name, content_object): full_name = name suffix = 1 while full_name in existing_details: - full_name = "%s-%d" % (name, suffix) + full_name = f"{name}-{suffix}" suffix += 1 self.addDetail(full_name, content_object) @@ -602,7 +602,7 @@ def getUniqueString(self, prefix=None): """ if prefix is None: prefix = self.id() - return "%s-%d" % (prefix, self.getUniqueInteger()) + return f"{prefix}-{self.getUniqueInteger()}" def onException(self, exc_info, tb_label="traceback"): """Called when an exception propagates from test code. @@ -644,7 +644,7 @@ def _report_traceback(self, exc_info, tb_label="traceback"): while True: tb_id = next(id_gen) if tb_id: - tb_label = "%s-%d" % (tb_label, tb_id) + tb_label = f"{tb_label}-{tb_id}" if tb_label not in self.getDetails(): break self.addDetail( @@ -682,14 +682,10 @@ def _run_setup(self, result): ret = self.setUp() if not self.__setup_called: raise ValueError( - "In File: %s\n" + f"In File: {sys.modules[self.__class__.__module__].__file__}\n" "TestCase.setUp was not called. Have you upcalled all the " "way up the hierarchy from your setUp? e.g. Call " - "super(%s, self).setUp() from your setUp()." - % ( - sys.modules[self.__class__.__module__].__file__, - self.__class__.__name__, - ) + f"super({self.__class__.__name__}, self).setUp() from your setUp()." ) return ret @@ -703,14 +699,11 @@ def _run_teardown(self, result): ret = self.tearDown() if not self.__teardown_called: raise ValueError( - "In File: %s\n" + f"In File: {sys.modules[self.__class__.__module__].__file__}\n" "TestCase.tearDown was not called. Have you upcalled all the " "way up the hierarchy from your tearDown? e.g. Call " - "super(%s, self).tearDown() from your tearDown()." - % ( - sys.modules[self.__class__.__module__].__file__, - self.__class__.__name__, - ) + f"super({self.__class__.__name__}, self).tearDown() " + "from your tearDown()." ) return ret @@ -723,7 +716,7 @@ def _get_test_method(self): # We allow instantiation with no explicit method name # but not an *incorrect* or missing method name. raise ValueError( - "no such test method in %s: %s" % (self.__class__, method_name) + f"no such test method in {self.__class__}: {method_name}" ) else: return m @@ -779,10 +772,10 @@ def setUp(self): super().setUp() if self.__setup_called: raise ValueError( - "In File: %s\n" + f"In File: {sys.modules[self.__class__.__module__].__file__}\n" "TestCase.setUp was already called. Do not explicitly call " "setUp from your tests. In your own setUp, use super to call " - "the base setUp." % (sys.modules[self.__class__.__module__].__file__,) + "the base setUp." ) self.__setup_called = True @@ -790,11 +783,10 @@ def tearDown(self): super().tearDown() if self.__teardown_called: raise ValueError( - "In File: %s\n" + f"In File: {sys.modules[self.__class__.__module__].__file__}\n" "TestCase.tearDown was already called. Do not explicitly call " "tearDown from your tests. In your own tearDown, use super to " "call the base tearDown." - % (sys.modules[self.__class__.__module__].__file__,) ) self.__teardown_called = True @@ -1062,9 +1054,9 @@ def __exit__(self, exc_type, exc_value, traceback): except AttributeError: exc_name = str(self.expected) if self.msg: - error_msg = "{} not raised : {}".format(exc_name, self.msg) + error_msg = f"{exc_name} not raised : {self.msg}" else: - error_msg = "{} not raised".format(exc_name) + error_msg = f"{exc_name} not raised" raise self.test_case.failureException(error_msg) if not issubclass(exc_type, self.expected): # let unexpected exceptions pass through @@ -1104,7 +1096,7 @@ def __enter__(self): def __exit__(self, exc_type, exc_value, traceback): if exc_type is None: - error_msg = "%s not raised." % self.exc_type.__name__ + error_msg = f"{self.exc_type.__name__} not raised." if self.msg: error_msg = error_msg + " : " + self.msg raise AssertionError(error_msg) diff --git a/testtools/testresult/doubles.py b/testtools/testresult/doubles.py index 3dadaff3..5b326dbd 100644 --- a/testtools/testresult/doubles.py +++ b/testtools/testresult/doubles.py @@ -7,11 +7,11 @@ from testtools.tags import TagContext __all__ = [ + "ExtendedTestResult", "Python26TestResult", "Python27TestResult", - "ExtendedTestResult", - "TwistedTestResult", "StreamResult", + "TwistedTestResult", ] @@ -159,8 +159,7 @@ def wasSuccessful(self): class TwistedTestResult(LoggingBase): - """ - Emulate the relevant bits of :py:class:`twisted.trial.itrial.IReporter`. + """Emulate the relevant bits of :py:class:`twisted.trial.itrial.IReporter`. Used to ensure that we can use ``trial`` as a test runner. """ diff --git a/testtools/testresult/real.py b/testtools/testresult/real.py index 39d620b0..c6fdcbac 100644 --- a/testtools/testresult/real.py +++ b/testtools/testresult/real.py @@ -25,15 +25,16 @@ import datetime import email.message import math -from operator import methodcaller import sys import unittest +from operator import methodcaller +from typing import ClassVar from testtools.compat import _b from testtools.content import ( Content, - text_content, TracebackContent, + text_content, ) from testtools.content_type import ContentType from testtools.tags import TagContext @@ -524,7 +525,7 @@ class StreamResultRouter(StreamResult): the behaviour is undefined. Only a single route is chosen for any event. """ - _policies = {} + _policies: ClassVar[dict] = {} def __init__(self, fallback=None, do_start_stop_run=True): """Construct a StreamResultRouter with optional fallback. @@ -778,7 +779,7 @@ def _make_content_type(mime_type=None): type_parts = full_type.split("/") if "/" in full_type else None if not type_parts or len(type_parts) > 2: - raise Exception("Can't parse type '%s'" % full_type) + raise Exception(f"Can't parse type '{full_type}'") primary_type, sub_type = type_parts primary_type = primary_type.strip() @@ -1223,25 +1224,21 @@ def stopTestRun(self): self._show_list("FAIL", self.failures) for test in self.unexpectedSuccesses: self.stream.write( - "{}UNEXPECTED SUCCESS: {}\n{}".format(self.sep1, test.id(), self.sep2) + f"{self.sep1}UNEXPECTED SUCCESS: {test.id()}\n{self.sep2}" ) self.stream.write( - "\nRan %d test%s in %.3fs\n" - % (self.testsRun, plural, self._delta_to_float(stop - self.__start, 3)) + f"\nRan {self.testsRun} test{plural} in " + f"{self._delta_to_float(stop - self.__start, 3):.3f}s\n" ) if self.wasSuccessful(): self.stream.write("OK\n") else: self.stream.write("FAILED (") details = [] - details.append( - "failures=%d" - % ( - sum( - map(len, (self.failures, self.errors, self.unexpectedSuccesses)) - ) - ) + failure_count = sum( + map(len, (self.failures, self.errors, self.unexpectedSuccesses)) ) + details.append(f"failures={failure_count}") self.stream.write(", ".join(details)) self.stream.write(")\n") super().stopTestRun() @@ -1527,7 +1524,7 @@ def _check_args(self, err, details): param_count += 1 if param_count != 1: raise ValueError( - "Must pass only one of err '%s' and details '%s" % (err, details) + f"Must pass only one of err '{err}' and details '{details}" ) def _details_to_exc_info(self, details): @@ -1738,7 +1735,7 @@ def _check_args(self, err, details): param_count += 1 if param_count != 1: raise ValueError( - "Must pass only one of err '%s' and details '%s" % (err, details) + f"Must pass only one of err '{err}' and details '{details}" ) def startTestRun(self): @@ -1813,14 +1810,13 @@ def stopCleanResource(self, resource): def _convertResourceLifecycle(self, resource, method, phase): """Convert a resource lifecycle report to a stream event.""" - # If the resource implements the TestResourceManager.id() API, let's # use it, otherwise fallback to the class name. if hasattr(resource, "id"): resource_id = resource.id() else: - resource_id = "{}.{}".format( - resource.__class__.__module__, resource.__class__.__name__ + resource_id = ( + f"{resource.__class__.__module__}.{resource.__class__.__name__}" ) test_id = f"{resource_id}.{method}" diff --git a/testtools/tests/__init__.py b/testtools/tests/__init__.py index 2c24bb47..7859ef2e 100644 --- a/testtools/tests/__init__.py +++ b/testtools/tests/__init__.py @@ -10,7 +10,6 @@ def test_suite(): from testtools.tests import ( matchers, - twistedsupport, test_assert_that, test_compat, test_content, @@ -25,6 +24,7 @@ def test_suite(): test_testresult, test_testsuite, test_with_with, + twistedsupport, ) modules = [ diff --git a/testtools/tests/helpers.py b/testtools/tests/helpers.py index 4f65ccfe..9b9086ff 100644 --- a/testtools/tests/helpers.py +++ b/testtools/tests/helpers.py @@ -8,7 +8,7 @@ import sys -from testtools import TestResult +from testtools import TestResult, runtest from testtools.content import StackLinesContent from testtools.matchers import ( AfterPreprocessing, @@ -16,8 +16,6 @@ MatchesDict, MatchesListwise, ) -from testtools import runtest - # GZ 2010-08-12: Don't do this, pointlessly creates an exc_info cycle try: diff --git a/testtools/tests/matchers/test_basic.py b/testtools/tests/matchers/test_basic.py index b0ebf077..f621045d 100644 --- a/testtools/tests/matchers/test_basic.py +++ b/testtools/tests/matchers/test_basic.py @@ -1,28 +1,29 @@ # Copyright (c) 2008-2012 testtools developers. See LICENSE for details. import re +from typing import ClassVar from testtools import TestCase from testtools.compat import ( - text_repr, _b, + text_repr, ) from testtools.matchers._basic import ( - _BinaryMismatch, Contains, DoesNotEndWith, DoesNotStartWith, EndsWith, Equals, + GreaterThan, + HasLength, Is, IsInstance, LessThan, - GreaterThan, - HasLength, MatchesRegex, NotEquals, SameMembers, StartsWith, + _BinaryMismatch, ) from testtools.tests.helpers import FullStackRunTest from testtools.tests.matchers.helpers import TestMatchersInterface @@ -114,12 +115,15 @@ def test_long_unicode_and_object(self): class TestEqualsInterface(TestCase, TestMatchersInterface): matches_matcher = Equals(1) - matches_matches = [1] - matches_mismatches = [2] + matches_matches: ClassVar[list] = [1] + matches_mismatches: ClassVar[list] = [2] - str_examples = [("Equals(1)", Equals(1)), ("Equals('1')", Equals("1"))] + str_examples: ClassVar[list] = [ + ("Equals(1)", Equals(1)), + ("Equals('1')", Equals("1")), + ] - describe_examples = [ + describe_examples: ClassVar[list] = [ ("2 != 1", 2, Equals(1)), ( ( @@ -135,12 +139,15 @@ class TestEqualsInterface(TestCase, TestMatchersInterface): class TestNotEqualsInterface(TestCase, TestMatchersInterface): matches_matcher = NotEquals(1) - matches_matches = [2] - matches_mismatches = [1] + matches_matches: ClassVar[list] = [2] + matches_mismatches: ClassVar[list] = [1] - str_examples = [("NotEquals(1)", NotEquals(1)), ("NotEquals('1')", NotEquals("1"))] + str_examples: ClassVar[list] = [ + ("NotEquals(1)", NotEquals(1)), + ("NotEquals('1')", NotEquals("1")), + ] - describe_examples = [("1 == 1", 1, NotEquals(1))] + describe_examples: ClassVar[list] = [("1 == 1", 1, NotEquals(1))] class TestIsInterface(TestCase, TestMatchersInterface): @@ -148,12 +155,12 @@ class TestIsInterface(TestCase, TestMatchersInterface): bar = object() matches_matcher = Is(foo) - matches_matches = [foo] - matches_mismatches = [bar, 1] + matches_matches: ClassVar[list] = [foo] + matches_mismatches: ClassVar[list] = [bar, 1] - str_examples = [("Is(2)", Is(2))] + str_examples: ClassVar[list] = [("Is(2)", Is(2))] - describe_examples = [("2 is not 1", 2, Is(1))] + describe_examples: ClassVar[list] = [("2 is not 1", 2, Is(1))] class TestIsInstanceInterface(TestCase, TestMatchersInterface): @@ -161,15 +168,15 @@ class Foo: pass matches_matcher = IsInstance(Foo) - matches_matches = [Foo()] - matches_mismatches = [object(), 1, Foo] + matches_matches: ClassVar[list] = [Foo()] + matches_mismatches: ClassVar[list] = [object(), 1, Foo] - str_examples = [ + str_examples: ClassVar[list] = [ ("IsInstance(str)", IsInstance(str)), ("IsInstance(str, int)", IsInstance(str, int)), ] - describe_examples = [ + describe_examples: ClassVar[list] = [ ("'foo' is not an instance of int", "foo", IsInstance(int)), ( "'foo' is not an instance of any of (int, type)", @@ -181,14 +188,14 @@ class Foo: class TestLessThanInterface(TestCase, TestMatchersInterface): matches_matcher = LessThan(4) - matches_matches = [-5, 3] - matches_mismatches = [4, 5, 5000] + matches_matches: ClassVar[list] = [-5, 3] + matches_mismatches: ClassVar[list] = [4, 5, 5000] - str_examples = [ + str_examples: ClassVar[list] = [ ("LessThan(12)", LessThan(12)), ] - describe_examples = [ + describe_examples: ClassVar[list] = [ ("5 >= 4", 5, LessThan(4)), ("4 >= 4", 4, LessThan(4)), ] @@ -196,14 +203,14 @@ class TestLessThanInterface(TestCase, TestMatchersInterface): class TestGreaterThanInterface(TestCase, TestMatchersInterface): matches_matcher = GreaterThan(4) - matches_matches = [5, 8] - matches_mismatches = [-2, 0, 4] + matches_matches: ClassVar[list] = [5, 8] + matches_mismatches: ClassVar[list] = [-2, 0, 4] - str_examples = [ + str_examples: ClassVar[list] = [ ("GreaterThan(12)", GreaterThan(12)), ] - describe_examples = [ + describe_examples: ClassVar[list] = [ ("4 <= 5", 4, GreaterThan(5)), ("4 <= 4", 4, GreaterThan(4)), ] @@ -211,15 +218,15 @@ class TestGreaterThanInterface(TestCase, TestMatchersInterface): class TestContainsInterface(TestCase, TestMatchersInterface): matches_matcher = Contains("foo") - matches_matches = ["foo", "afoo", "fooa"] - matches_mismatches = ["f", "fo", "oo", "faoo", "foao"] + matches_matches: ClassVar[list] = ["foo", "afoo", "fooa"] + matches_mismatches: ClassVar[list] = ["f", "fo", "oo", "faoo", "foao"] - str_examples = [ + str_examples: ClassVar[list] = [ ("Contains(1)", Contains(1)), ("Contains('foo')", Contains("foo")), ] - describe_examples = [("1 not in 2", 2, Contains(1))] + describe_examples: ClassVar[list] = [("1 not in 2", 2, Contains(1))] class DoesNotStartWithTests(TestCase): @@ -234,7 +241,7 @@ def test_describe_non_ascii_unicode(self): suffix = "B\xa7" mismatch = DoesNotStartWith(string, suffix) self.assertEqual( - "{} does not start with {}.".format(text_repr(string), text_repr(suffix)), + f"{text_repr(string)} does not start with {text_repr(suffix)}.", mismatch.describe(), ) @@ -295,7 +302,7 @@ def test_describe_non_ascii_unicode(self): suffix = "B\xa7" mismatch = DoesNotEndWith(string, suffix) self.assertEqual( - "{} does not end with {}.".format(text_repr(string), text_repr(suffix)), + f"{text_repr(string)} does not end with {text_repr(suffix)}.", mismatch.describe(), ) @@ -346,25 +353,27 @@ def test_mismatch_sets_expected(self): class TestSameMembers(TestCase, TestMatchersInterface): matches_matcher = SameMembers([1, 1, 2, 3, {"foo": "bar"}]) - matches_matches = [ + matches_matches: ClassVar[list] = [ [1, 1, 2, 3, {"foo": "bar"}], [3, {"foo": "bar"}, 1, 2, 1], [3, 2, 1, {"foo": "bar"}, 1], (2, {"foo": "bar"}, 3, 1, 1), ] - matches_mismatches = [ + matches_mismatches: ClassVar[list] = [ {1, 2, 3}, [1, 1, 2, 3, 5], [1, 2, 3, {"foo": "bar"}], "foo", ] - describe_examples = [ + describe_examples: ClassVar[list] = [ ( ( "elements differ:\n" - "reference = ['apple', 'orange', 'canteloupe', 'watermelon', 'lemon', 'banana']\n" - "actual = ['orange', 'apple', 'banana', 'sparrow', 'lemon', 'canteloupe']\n" + "reference = ['apple', 'orange', 'canteloupe', 'watermelon', " + "'lemon', 'banana']\n" + "actual = ['orange', 'apple', 'banana', 'sparrow', " + "'lemon', 'canteloupe']\n" ": \n" "missing: ['watermelon']\n" "extra: ['sparrow']" @@ -390,17 +399,17 @@ class TestSameMembers(TestCase, TestMatchersInterface): ), ] - str_examples = [ + str_examples: ClassVar[list] = [ ("SameMembers([1, 2, 3])", SameMembers([1, 2, 3])), ] class TestMatchesRegex(TestCase, TestMatchersInterface): matches_matcher = MatchesRegex("a|b") - matches_matches = ["a", "b"] - matches_mismatches = ["c"] + matches_matches: ClassVar[list] = ["a", "b"] + matches_mismatches: ClassVar[list] = ["c"] - str_examples = [ + str_examples: ClassVar[list] = [ ("MatchesRegex('a|b')", MatchesRegex("a|b")), ("MatchesRegex('a|b', re.M)", MatchesRegex("a|b", re.M)), ("MatchesRegex('a|b', re.I|re.M)", MatchesRegex("a|b", re.I | re.M)), @@ -408,7 +417,7 @@ class TestMatchesRegex(TestCase, TestMatchersInterface): ("MatchesRegex({!r})".format("\xa7"), MatchesRegex("\xa7")), ] - describe_examples = [ + describe_examples: ClassVar[list] = [ ("'c' does not match /a|b/", "c", MatchesRegex("a|b")), ("'c' does not match /a\\d/", "c", MatchesRegex(r"a\d")), ( @@ -422,14 +431,14 @@ class TestMatchesRegex(TestCase, TestMatchersInterface): class TestHasLength(TestCase, TestMatchersInterface): matches_matcher = HasLength(2) - matches_matches = [[1, 2]] - matches_mismatches = [[], [1], [3, 2, 1]] + matches_matches: ClassVar[list] = [[1, 2]] + matches_mismatches: ClassVar[list] = [[], [1], [3, 2, 1]] - str_examples = [ + str_examples: ClassVar[list] = [ ("HasLength(2)", HasLength(2)), ] - describe_examples = [ + describe_examples: ClassVar[list] = [ ("len([]) != 1", [], HasLength(1)), ] diff --git a/testtools/tests/matchers/test_const.py b/testtools/tests/matchers/test_const.py index de18a29a..3b35fd10 100644 --- a/testtools/tests/matchers/test_const.py +++ b/testtools/tests/matchers/test_const.py @@ -1,5 +1,7 @@ # Copyright (c) 2016 testtools developers. See LICENSE for details. +from typing import ClassVar + from testtools import TestCase from testtools.matchers import Always, Never from testtools.tests.matchers.helpers import TestMatchersInterface @@ -9,22 +11,22 @@ class TestAlwaysInterface(TestMatchersInterface, TestCase): """:py:func:`~testtools.matchers.Always` always matches.""" matches_matcher = Always() - matches_matches = [42, object(), "hi mom"] - matches_mismatches = [] + matches_matches: ClassVar[list] = [42, object(), "hi mom"] + matches_mismatches: ClassVar[list] = [] - str_examples = [("Always()", Always())] - describe_examples = [] + str_examples: ClassVar[list] = [("Always()", Always())] + describe_examples: ClassVar[list] = [] class TestNeverInterface(TestMatchersInterface, TestCase): """:py:func:`~testtools.matchers.Never` never matches.""" matches_matcher = Never() - matches_matches = [] - matches_mismatches = [42, object(), "hi mom"] + matches_matches: ClassVar[list] = [] + matches_mismatches: ClassVar[list] = [42, object(), "hi mom"] - str_examples = [("Never()", Never())] - describe_examples = [("Inevitable mismatch on 42", 42, Never())] + str_examples: ClassVar[list] = [("Never()", Never())] + describe_examples: ClassVar[list] = [("Inevitable mismatch on 42", 42, Never())] def test_suite(): diff --git a/testtools/tests/matchers/test_datastructures.py b/testtools/tests/matchers/test_datastructures.py index fa85fadf..055df263 100644 --- a/testtools/tests/matchers/test_datastructures.py +++ b/testtools/tests/matchers/test_datastructures.py @@ -4,6 +4,7 @@ import io import re import sys +from typing import ClassVar from testtools import TestCase from testtools.matchers import ( @@ -16,8 +17,8 @@ from testtools.matchers._datastructures import ( ContainsAll, MatchesListwise, - MatchesStructure, MatchesSetwise, + MatchesStructure, ) from testtools.tests.helpers import FullStackRunTest from testtools.tests.matchers.helpers import TestMatchersInterface @@ -40,7 +41,7 @@ class TestMatchesListwise(TestCase): def test_docstring(self): failure_count, output = run_doctest(MatchesListwise, "MatchesListwise") if failure_count: - self.fail("Doctest failed with %s" % output) + self.fail(f"Doctest failed with {output}") class TestMatchesStructure(TestCase, TestMatchersInterface): @@ -50,14 +51,14 @@ def __init__(self, x, y): self.y = y matches_matcher = MatchesStructure(x=Equals(1), y=Equals(2)) - matches_matches = [SimpleClass(1, 2)] - matches_mismatches = [ + matches_matches: ClassVar[list] = [SimpleClass(1, 2)] + matches_mismatches: ClassVar[list] = [ SimpleClass(2, 2), SimpleClass(1, 1), SimpleClass(3, 3), ] - str_examples = [ + str_examples: ClassVar[list] = [ ("MatchesStructure(x=Equals(1))", MatchesStructure(x=Equals(1))), ("MatchesStructure(y=Equals(2))", MatchesStructure(y=Equals(2))), ( @@ -66,7 +67,7 @@ def __init__(self, x, y): ), ] - describe_examples = [ + describe_examples: ClassVar[list] = [ ( """\ Differences: [ @@ -214,14 +215,18 @@ def test_mismatch_and_two_too_many_values(self): class TestContainsAllInterface(TestCase, TestMatchersInterface): matches_matcher = ContainsAll(["foo", "bar"]) - matches_matches = [["foo", "bar"], ["foo", "z", "bar"], ["bar", "foo"]] - matches_mismatches = [["f", "g"], ["foo", "baz"], []] + matches_matches: ClassVar[list] = [ + ["foo", "bar"], + ["foo", "z", "bar"], + ["bar", "foo"], + ] + matches_mismatches: ClassVar[list] = [["f", "g"], ["foo", "baz"], []] - str_examples = [ + str_examples: ClassVar[list] = [ ("MatchesAll(Contains('foo'), Contains('bar'))", ContainsAll(["foo", "bar"])), ] - describe_examples = [ + describe_examples: ClassVar[list] = [ ( """Differences: [ 'baz' not in 'foo' diff --git a/testtools/tests/matchers/test_dict.py b/testtools/tests/matchers/test_dict.py index 1e5a7aef..c0900219 100644 --- a/testtools/tests/matchers/test_dict.py +++ b/testtools/tests/matchers/test_dict.py @@ -1,8 +1,10 @@ +from typing import ClassVar + from testtools import TestCase from testtools.matchers import ( Equals, - NotEquals, Not, + NotEquals, ) from testtools.matchers._dict import ( ContainedByDict, @@ -17,24 +19,24 @@ class TestMatchesAllDictInterface(TestCase, TestMatchersInterface): matches_matcher = MatchesAllDict({"a": NotEquals(1), "b": NotEquals(2)}) - matches_matches = [3, 4] - matches_mismatches = [1, 2] + matches_matches: ClassVar[list] = [3, 4] + matches_mismatches: ClassVar[list] = [1, 2] - str_examples = [ + str_examples: ClassVar[list] = [ ("MatchesAllDict({'a': NotEquals(1), 'b': NotEquals(2)})", matches_matcher) ] - describe_examples = [ + describe_examples: ClassVar[list] = [ ("""a: 1 == 1""", 1, matches_matcher), ] class TestKeysEqualEmpty(TestCase, TestMatchersInterface): matches_matcher = KeysEqual() - matches_matches = [ + matches_matches: ClassVar[list] = [ {}, ] - matches_mismatches = [ + matches_mismatches: ClassVar[list] = [ {"foo": 0, "bar": 1}, {"foo": 0}, {"bar": 1}, @@ -42,21 +44,21 @@ class TestKeysEqualEmpty(TestCase, TestMatchersInterface): {"a": None, "b": None, "c": None}, ] - str_examples = [ + str_examples: ClassVar[list] = [ ("KeysEqual()", KeysEqual()), ] - describe_examples = [ + describe_examples: ClassVar[list] = [ ("[] does not match {1: 2}: Keys not equal", {1: 2}, matches_matcher), ] class TestKeysEqualWithList(TestCase, TestMatchersInterface): matches_matcher = KeysEqual("foo", "bar") - matches_matches = [ + matches_matches: ClassVar[list] = [ {"foo": 0, "bar": 1}, ] - matches_mismatches = [ + matches_mismatches: ClassVar[list] = [ {}, {"foo": 0}, {"bar": 1}, @@ -64,11 +66,11 @@ class TestKeysEqualWithList(TestCase, TestMatchersInterface): {"a": None, "b": None, "c": None}, ] - str_examples = [ + str_examples: ClassVar[list] = [ ("KeysEqual('foo', 'bar')", KeysEqual("foo", "bar")), ] - describe_examples = [] + describe_examples: ClassVar[list] = [] def test_description(self): matchee = {"foo": 0, "bar": 1, "baz": 2} @@ -76,7 +78,7 @@ def test_description(self): description = mismatch.describe() self.assertThat( description, - Equals("['bar', 'foo'] does not match %r: Keys not equal" % (matchee,)), + Equals(f"['bar', 'foo'] does not match {matchee!r}: Keys not equal"), ) @@ -87,28 +89,28 @@ class TestKeysEqualWithDict(TestKeysEqualWithList): class TestSubDictOf(TestCase, TestMatchersInterface): matches_matcher = _SubDictOf({"foo": "bar", "baz": "qux"}) - matches_matches = [ + matches_matches: ClassVar[list] = [ {"foo": "bar", "baz": "qux"}, {"foo": "bar"}, ] - matches_mismatches = [ + matches_mismatches: ClassVar[list] = [ {"foo": "bar", "baz": "qux", "cat": "dog"}, {"foo": "bar", "cat": "dog"}, ] - str_examples = [] - describe_examples = [] + str_examples: ClassVar[list] = [] + describe_examples: ClassVar[list] = [] class TestMatchesDict(TestCase, TestMatchersInterface): matches_matcher = MatchesDict({"foo": Equals("bar"), "baz": Not(Equals("qux"))}) - matches_matches = [ + matches_matches: ClassVar[list] = [ {"foo": "bar", "baz": None}, {"foo": "bar", "baz": "quux"}, ] - matches_mismatches = [ + matches_mismatches: ClassVar[list] = [ {}, {"foo": "bar", "baz": "qux"}, {"foo": "bop", "baz": "qux"}, @@ -116,7 +118,7 @@ class TestMatchesDict(TestCase, TestMatchersInterface): {"foo": "bar", "cat": "dog"}, ] - str_examples = [ + str_examples: ClassVar[list] = [ ( "MatchesDict({{'baz': {}, 'foo': {}}})".format( Not(Equals("qux")), Equals("bar") @@ -125,7 +127,7 @@ class TestMatchesDict(TestCase, TestMatchersInterface): ), ] - describe_examples = [ + describe_examples: ClassVar[list] = [ ( "Missing: {\n 'baz': Not(Equals('qux')),\n 'foo': Equals('bar'),\n}", {}, @@ -160,12 +162,12 @@ class TestMatchesDict(TestCase, TestMatchersInterface): class TestContainsDict(TestCase, TestMatchersInterface): matches_matcher = ContainsDict({"foo": Equals("bar"), "baz": Not(Equals("qux"))}) - matches_matches = [ + matches_matches: ClassVar[list] = [ {"foo": "bar", "baz": None}, {"foo": "bar", "baz": "quux"}, {"foo": "bar", "baz": "quux", "cat": "dog"}, ] - matches_mismatches = [ + matches_mismatches: ClassVar[list] = [ {}, {"foo": "bar", "baz": "qux"}, {"foo": "bop", "baz": "qux"}, @@ -173,7 +175,7 @@ class TestContainsDict(TestCase, TestMatchersInterface): {"foo": "bar"}, ] - str_examples = [ + str_examples: ClassVar[list] = [ ( "ContainsDict({{'baz': {}, 'foo': {}}})".format( Not(Equals("qux")), Equals("bar") @@ -182,7 +184,7 @@ class TestContainsDict(TestCase, TestMatchersInterface): ), ] - describe_examples = [ + describe_examples: ClassVar[list] = [ ( "Missing: {\n 'baz': Not(Equals('qux')),\n 'foo': Equals('bar'),\n}", {}, @@ -212,20 +214,20 @@ class TestContainsDict(TestCase, TestMatchersInterface): class TestContainedByDict(TestCase, TestMatchersInterface): matches_matcher = ContainedByDict({"foo": Equals("bar"), "baz": Not(Equals("qux"))}) - matches_matches = [ + matches_matches: ClassVar[list] = [ {}, {"foo": "bar"}, {"foo": "bar", "baz": "quux"}, {"baz": "quux"}, ] - matches_mismatches = [ + matches_mismatches: ClassVar[list] = [ {"foo": "bar", "baz": "quux", "cat": "dog"}, {"foo": "bar", "baz": "qux"}, {"foo": "bop", "baz": "qux"}, {"foo": "bar", "cat": "dog"}, ] - str_examples = [ + str_examples: ClassVar[list] = [ ( "ContainedByDict({{'baz': {}, 'foo': {}}})".format( Not(Equals("qux")), Equals("bar") @@ -234,7 +236,7 @@ class TestContainedByDict(TestCase, TestMatchersInterface): ), ] - describe_examples = [ + describe_examples: ClassVar[list] = [ ( "Differences: {\n 'baz': 'qux' matches Equals('qux'),\n}", {"foo": "bar", "baz": "qux"}, diff --git a/testtools/tests/matchers/test_doctest.py b/testtools/tests/matchers/test_doctest.py index 3d443af4..a1ead2b7 100644 --- a/testtools/tests/matchers/test_doctest.py +++ b/testtools/tests/matchers/test_doctest.py @@ -1,6 +1,7 @@ # Copyright (c) 2008-2012 testtools developers. See LICENSE for details. import doctest +from typing import ClassVar from testtools import TestCase from testtools.compat import ( @@ -13,10 +14,13 @@ class TestDocTestMatchesInterface(TestCase, TestMatchersInterface): matches_matcher = DocTestMatches("Ran 1 test in ...s", doctest.ELLIPSIS) - matches_matches = ["Ran 1 test in 0.000s", "Ran 1 test in 1.234s"] - matches_mismatches = ["Ran 1 tests in 0.000s", "Ran 2 test in 0.000s"] + matches_matches: ClassVar[list] = ["Ran 1 test in 0.000s", "Ran 1 test in 1.234s"] + matches_mismatches: ClassVar[list] = [ + "Ran 1 tests in 0.000s", + "Ran 2 test in 0.000s", + ] - str_examples = [ + str_examples: ClassVar[list] = [ ( "DocTestMatches('Ran 1 test in ...s\\n')", DocTestMatches("Ran 1 test in ...s"), @@ -24,7 +28,7 @@ class TestDocTestMatchesInterface(TestCase, TestMatchersInterface): ("DocTestMatches('foo\\n', flags=8)", DocTestMatches("foo", flags=8)), ] - describe_examples = [ + describe_examples: ClassVar[list] = [ ( "Expected:\n Ran 1 tests in ...s\nGot:\n Ran 1 test in 0.123s\n", "Ran 1 test in 0.123s", @@ -35,14 +39,14 @@ class TestDocTestMatchesInterface(TestCase, TestMatchersInterface): class TestDocTestMatchesInterfaceUnicode(TestCase, TestMatchersInterface): matches_matcher = DocTestMatches("\xa7...", doctest.ELLIPSIS) - matches_matches = ["\xa7", "\xa7 more\n"] - matches_mismatches = ["\\xa7", "more \xa7", "\n\xa7"] + matches_matches: ClassVar[list] = ["\xa7", "\xa7 more\n"] + matches_mismatches: ClassVar[list] = ["\\xa7", "more \xa7", "\n\xa7"] - str_examples = [ + str_examples: ClassVar[list] = [ ("DocTestMatches({!r})".format("\xa7\n"), DocTestMatches("\xa7")), ] - describe_examples = [ + describe_examples: ClassVar[list] = [ ( "Expected:\n \xa7\nGot:\n a\n", "a", diff --git a/testtools/tests/matchers/test_exception.py b/testtools/tests/matchers/test_exception.py index 0c329cff..1d01266b 100644 --- a/testtools/tests/matchers/test_exception.py +++ b/testtools/tests/matchers/test_exception.py @@ -1,6 +1,7 @@ # Copyright (c) 2008-2012 testtools developers. See LICENSE for details. import sys +from typing import ClassVar from testtools import TestCase from testtools.matchers import ( @@ -28,30 +29,25 @@ class TestMatchesExceptionInstanceInterface(TestCase, TestMatchersInterface): error_foo = make_error(ValueError, "foo") error_bar = make_error(ValueError, "bar") error_base_foo = make_error(Exception, "foo") - matches_matches = [error_foo] - matches_mismatches = [error_bar, error_base_foo] + matches_matches: ClassVar[list] = [error_foo] + matches_mismatches: ClassVar[list] = [error_bar, error_base_foo] - if sys.version_info >= (3, 7): - # exception's repr has changed - _e = "" - else: - _e = "," + _e = "" - str_examples = [ + str_examples: ClassVar[list] = [ ( - "MatchesException(Exception('foo'%s))" % _e, + f"MatchesException(Exception('foo'{_e}))", MatchesException(Exception("foo")), ) ] - describe_examples = [ + describe_examples: ClassVar[list] = [ ( f"{Exception!r} is not a {ValueError!r}", error_base_foo, MatchesException(ValueError("foo")), ), ( - "ValueError('bar'%s) has different arguments to ValueError('foo'%s)." - % (_e, _e), + f"ValueError('bar'{_e}) has different arguments to ValueError('foo'{_e}).", error_bar, MatchesException(ValueError("foo")), ), @@ -63,11 +59,13 @@ class TestMatchesExceptionTypeInterface(TestCase, TestMatchersInterface): error_foo = make_error(ValueError, "foo") error_sub = make_error(UnicodeError, "bar") error_base_foo = make_error(Exception, "foo") - matches_matches = [error_foo, error_sub] - matches_mismatches = [error_base_foo] + matches_matches: ClassVar[list] = [error_foo, error_sub] + matches_mismatches: ClassVar[list] = [error_base_foo] - str_examples = [("MatchesException(%r)" % Exception, MatchesException(Exception))] - describe_examples = [ + str_examples: ClassVar[list] = [ + (f"MatchesException({Exception!r})", MatchesException(Exception)) + ] + describe_examples: ClassVar[list] = [ ( f"{Exception!r} is not a {ValueError!r}", error_base_foo, @@ -81,13 +79,13 @@ class TestMatchesExceptionTypeReInterface(TestCase, TestMatchersInterface): error_foo = make_error(ValueError, "foo") error_sub = make_error(UnicodeError, "foo") error_bar = make_error(ValueError, "bar") - matches_matches = [error_foo, error_sub] - matches_mismatches = [error_bar] + matches_matches: ClassVar[list] = [error_foo, error_sub] + matches_mismatches: ClassVar[list] = [error_bar] - str_examples = [ - ("MatchesException(%r)" % Exception, MatchesException(Exception, "fo.")) + str_examples: ClassVar[list] = [ + (f"MatchesException({Exception!r})", MatchesException(Exception, "fo.")) ] - describe_examples = [ + describe_examples: ClassVar[list] = [ ("'bar' does not match /fo./", error_bar, MatchesException(ValueError, "fo.")), ] @@ -99,13 +97,13 @@ class TestMatchesExceptionTypeMatcherInterface(TestCase, TestMatchersInterface): error_foo = make_error(ValueError, "foo") error_sub = make_error(UnicodeError, "foo") error_bar = make_error(ValueError, "bar") - matches_matches = [error_foo, error_sub] - matches_mismatches = [error_bar] + matches_matches: ClassVar[list] = [error_foo, error_sub] + matches_mismatches: ClassVar[list] = [error_bar] - str_examples = [ - ("MatchesException(%r)" % Exception, MatchesException(Exception, Equals("foo"))) + str_examples: ClassVar[list] = [ + (f"MatchesException({Exception!r})", MatchesException(Exception, Equals("foo"))) ] - describe_examples = [ + describe_examples: ClassVar[list] = [ (f"{error_bar[1]!r} != 5", error_bar, MatchesException(ValueError, Equals(5))), ] @@ -116,14 +114,14 @@ class TestRaisesInterface(TestCase, TestMatchersInterface): def boom(): raise Exception("foo") - matches_matches = [boom] - matches_mismatches = [lambda: None] + matches_matches: ClassVar[list] = [boom] + matches_mismatches: ClassVar[list] = [lambda: None] # Tricky to get function objects to render constantly, and the interfaces # helper uses assertEqual rather than (for instance) DocTestMatches. - str_examples = [] + str_examples: ClassVar[list] = [] - describe_examples = [] + describe_examples: ClassVar[list] = [] class TestRaisesExceptionMatcherInterface(TestCase, TestMatchersInterface): @@ -135,14 +133,14 @@ def boom_bar(): def boom_foo(): raise Exception("foo") - matches_matches = [boom_foo] - matches_mismatches = [lambda: None, boom_bar] + matches_matches: ClassVar[list] = [boom_foo] + matches_mismatches: ClassVar[list] = [lambda: None, boom_bar] # Tricky to get function objects to render constantly, and the interfaces # helper uses assertEqual rather than (for instance) DocTestMatches. - str_examples = [] + str_examples: ClassVar[list] = [] - describe_examples = [] + describe_examples: ClassVar[list] = [] class TestRaisesBaseTypes(TestCase): diff --git a/testtools/tests/matchers/test_filesystem.py b/testtools/tests/matchers/test_filesystem.py index 9f47f30d..9651a88f 100644 --- a/testtools/tests/matchers/test_filesystem.py +++ b/testtools/tests/matchers/test_filesystem.py @@ -48,7 +48,7 @@ def test_exists(self): def test_not_exists(self): doesntexist = os.path.join(self.mkdtemp(), "doesntexist") mismatch = PathExists().match(doesntexist) - self.assertThat("%s does not exist." % doesntexist, Equals(mismatch.describe())) + self.assertThat(f"{doesntexist} does not exist.", Equals(mismatch.describe())) class TestDirExists(TestCase, PathHelpers): @@ -67,9 +67,7 @@ def test_not_a_directory(self): filename = os.path.join(self.mkdtemp(), "foo") self.touch(filename) mismatch = DirExists().match(filename) - self.assertThat( - "%s is not a directory." % filename, Equals(mismatch.describe()) - ) + self.assertThat(f"{filename} is not a directory.", Equals(mismatch.describe())) class TestFileExists(TestCase, PathHelpers): @@ -89,7 +87,7 @@ def test_not_exists(self): def test_not_a_file(self): tempdir = self.mkdtemp() mismatch = FileExists().match(tempdir) - self.assertThat("%s is not a file." % tempdir, Equals(mismatch.describe())) + self.assertThat(f"{tempdir} is not a file.", Equals(mismatch.describe())) class TestDirContains(TestCase, PathHelpers): diff --git a/testtools/tests/matchers/test_higherorder.py b/testtools/tests/matchers/test_higherorder.py index 1bebe1a5..498a4a47 100644 --- a/testtools/tests/matchers/test_higherorder.py +++ b/testtools/tests/matchers/test_higherorder.py @@ -1,5 +1,7 @@ # Copyright (c) 2008-2011 testtools developers. See LICENSE for details. +from typing import ClassVar + from testtools import TestCase from testtools.matchers import ( DocTestMatches, @@ -15,8 +17,8 @@ Annotate, AnnotatedMismatch, AnyMatch, - MatchesAny, MatchesAll, + MatchesAny, MatchesPredicate, MatchesPredicateWithParams, Not, @@ -27,21 +29,21 @@ class TestAllMatch(TestCase, TestMatchersInterface): matches_matcher = AllMatch(LessThan(10)) - matches_matches = [ + matches_matches: ClassVar[list] = [ [9, 9, 9], (9, 9), iter([9, 9, 9, 9, 9]), ] - matches_mismatches = [ + matches_mismatches: ClassVar[list] = [ [11, 9, 9], iter([9, 12, 9, 11]), ] - str_examples = [ + str_examples: ClassVar[list] = [ ("AllMatch(LessThan(12))", AllMatch(LessThan(12))), ] - describe_examples = [ + describe_examples: ClassVar[list] = [ ( "Differences: [\n11 >= 10\n10 >= 10\n]", [11, 9, 10], @@ -52,13 +54,13 @@ class TestAllMatch(TestCase, TestMatchersInterface): class TestAnyMatch(TestCase, TestMatchersInterface): matches_matcher = AnyMatch(Equals("elephant")) - matches_matches = [ + matches_matches: ClassVar[list] = [ ["grass", "cow", "steak", "milk", "elephant"], (13, "elephant"), ["elephant", "elephant", "elephant"], {"hippo", "rhino", "elephant"}, ] - matches_mismatches = [ + matches_mismatches: ClassVar[list] = [ [], ["grass", "cow", "steak", "milk"], (13, 12, 10), @@ -66,11 +68,11 @@ class TestAnyMatch(TestCase, TestMatchersInterface): {"hippo", "rhino", "diplodocus"}, ] - str_examples = [ + str_examples: ClassVar[list] = [ ("AnyMatch(Equals('elephant'))", AnyMatch(Equals("elephant"))), ] - describe_examples = [ + describe_examples: ClassVar[list] = [ ( "Differences: [\n11 != 7\n9 != 7\n10 != 7\n]", [11, 9, 10], @@ -84,17 +86,17 @@ def parity(x): return x % 2 matches_matcher = AfterPreprocessing(parity, Equals(1)) - matches_matches = [3, 5] - matches_mismatches = [2] + matches_matches: ClassVar[list] = [3, 5] + matches_mismatches: ClassVar[list] = [2] - str_examples = [ + str_examples: ClassVar[list] = [ ( "AfterPreprocessing(, Equals(1))", AfterPreprocessing(parity, Equals(1)), ), ] - describe_examples = [ + describe_examples: ClassVar[list] = [ ( "0 != 1: after on 2", 2, @@ -106,17 +108,17 @@ def parity(x): class TestMatchersAnyInterface(TestCase, TestMatchersInterface): matches_matcher = MatchesAny(DocTestMatches("1"), DocTestMatches("2")) - matches_matches = ["1", "2"] - matches_mismatches = ["3"] + matches_matches: ClassVar[list] = ["1", "2"] + matches_mismatches: ClassVar[list] = ["3"] - str_examples = [ + str_examples: ClassVar[list] = [ ( "MatchesAny(DocTestMatches('1\\n'), DocTestMatches('2\\n'))", MatchesAny(DocTestMatches("1"), DocTestMatches("2")), ), ] - describe_examples = [ + describe_examples: ClassVar[list] = [ ( """Differences: [ Expected: @@ -138,17 +140,17 @@ class TestMatchersAnyInterface(TestCase, TestMatchersInterface): class TestMatchesAllInterface(TestCase, TestMatchersInterface): matches_matcher = MatchesAll(NotEquals(1), NotEquals(2)) - matches_matches = [3, 4] - matches_mismatches = [1, 2] + matches_matches: ClassVar[list] = [3, 4] + matches_mismatches: ClassVar[list] = [1, 2] - str_examples = [ + str_examples: ClassVar[list] = [ ( "MatchesAll(NotEquals(1), NotEquals(2))", MatchesAll(NotEquals(1), NotEquals(2)), ) ] - describe_examples = [ + describe_examples: ClassVar[list] = [ ( """Differences: [ 1 == 1 @@ -166,12 +168,14 @@ class TestMatchesAllInterface(TestCase, TestMatchersInterface): class TestAnnotate(TestCase, TestMatchersInterface): matches_matcher = Annotate("foo", Equals(1)) - matches_matches = [1] - matches_mismatches = [2] + matches_matches: ClassVar[list] = [1] + matches_mismatches: ClassVar[list] = [2] - str_examples = [("Annotate('foo', Equals(1))", Annotate("foo", Equals(1)))] + str_examples: ClassVar[list] = [ + ("Annotate('foo', Equals(1))", Annotate("foo", Equals(1))) + ] - describe_examples = [("2 != 1: foo", 2, Annotate("foo", Equals(1)))] + describe_examples: ClassVar[list] = [("2 != 1: foo", 2, Annotate("foo", Equals(1)))] def test_if_message_no_message(self): # Annotate.if_message returns the given matcher if there is no @@ -202,15 +206,15 @@ def test_forwards_details(self): class TestNotInterface(TestCase, TestMatchersInterface): matches_matcher = Not(Equals(1)) - matches_matches = [2] - matches_mismatches = [1] + matches_matches: ClassVar[list] = [2] + matches_mismatches: ClassVar[list] = [1] - str_examples = [ + str_examples: ClassVar[list] = [ ("Not(Equals(1))", Not(Equals(1))), ("Not(Equals('1'))", Not(Equals("1"))), ] - describe_examples = [("1 matches Equals(1)", 1, Not(Equals(1)))] + describe_examples: ClassVar[list] = [("1 matches Equals(1)", 1, Not(Equals(1)))] def is_even(x): @@ -219,17 +223,17 @@ def is_even(x): class TestMatchesPredicate(TestCase, TestMatchersInterface): matches_matcher = MatchesPredicate(is_even, "%s is not even") - matches_matches = [2, 4, 6, 8] - matches_mismatches = [3, 5, 7, 9] + matches_matches: ClassVar[list] = [2, 4, 6, 8] + matches_mismatches: ClassVar[list] = [3, 5, 7, 9] - str_examples = [ + str_examples: ClassVar[list] = [ ( "MatchesPredicate({!r}, {!r})".format(is_even, "%s is not even"), MatchesPredicate(is_even, "%s is not even"), ), ] - describe_examples = [ + describe_examples: ClassVar[list] = [ ("7 is not even", 7, MatchesPredicate(is_even, "%s is not even")), ] @@ -242,10 +246,10 @@ class TestMatchesPredicateWithParams(TestCase, TestMatchersInterface): matches_matcher = MatchesPredicateWithParams( between, "{0} is not between {1} and {2}" )(1, 9) - matches_matches = [2, 4, 6, 8] - matches_mismatches = [0, 1, 9, 10] + matches_matches: ClassVar[list] = [2, 4, 6, 8] + matches_mismatches: ClassVar[list] = [0, 1, 9, 10] - str_examples = [ + str_examples: ClassVar[list] = [ ( "MatchesPredicateWithParams({!r}, {!r})({})".format( between, "{0} is not between {1} and {2}", "1, 2" @@ -260,7 +264,7 @@ class TestMatchesPredicateWithParams(TestCase, TestMatchersInterface): ), ] - describe_examples = [ + describe_examples: ClassVar[list] = [ ( "1 is not between 2 and 3", 1, diff --git a/testtools/tests/matchers/test_impl.py b/testtools/tests/matchers/test_impl.py index eb534ee3..6d2ad7d5 100644 --- a/testtools/tests/matchers/test_impl.py +++ b/testtools/tests/matchers/test_impl.py @@ -67,10 +67,9 @@ def test_verbose_description(self): matcher = Equals(3) mismatch = matcher.match(2) e = MismatchError(matchee, matcher, mismatch, True) - expected = "Match failed. Matchee: %r\nMatcher: %s\nDifference: %s\n" % ( - matchee, - matcher, - matcher.match(matchee).describe(), + expected = ( + f"Match failed. Matchee: {matchee!r}\nMatcher: {matcher}\n" + f"Difference: {matcher.match(matchee).describe()}\n" ) self.assertEqual(expected, str(e)) @@ -80,10 +79,9 @@ def test_verbose_unicode(self): matchee = "\xa7" matcher = Equals("a") mismatch = matcher.match(matchee) - expected = "Match failed. Matchee: %s\nMatcher: %s\nDifference: %s\n" % ( - text_repr(matchee), - matcher, - mismatch.describe(), + expected = ( + f"Match failed. Matchee: {text_repr(matchee)}\nMatcher: {matcher}\n" + f"Difference: {mismatch.describe()}\n" ) e = MismatchError(matchee, matcher, mismatch, True) self.assertEqual(expected, str(e)) diff --git a/testtools/tests/matchers/test_warnings.py b/testtools/tests/matchers/test_warnings.py index 3c234532..2a640db2 100644 --- a/testtools/tests/matchers/test_warnings.py +++ b/testtools/tests/matchers/test_warnings.py @@ -1,17 +1,18 @@ # Copyright (c) 2008-2016 testtools developers. See LICENSE for details. import warnings +from typing import ClassVar from testtools import TestCase from testtools.matchers import ( AfterPreprocessing, - Equals, - MatchesStructure, - MatchesListwise, Contains, + Equals, HasLength, + MatchesListwise, + MatchesStructure, ) -from testtools.matchers._warnings import Warnings, IsDeprecated, WarningMessage +from testtools.matchers._warnings import IsDeprecated, WarningMessage, Warnings from testtools.tests.helpers import FullStackRunTest from testtools.tests.matchers.helpers import TestMatchersInterface @@ -27,8 +28,7 @@ def make_warning_message(message, category, filename=None, lineno=None, line=Non class TestWarningMessageCategoryTypeInterface(TestCase, TestMatchersInterface): - """ - Tests for `testtools.matchers._warnings.WarningMessage`. + """Tests for `testtools.matchers._warnings.WarningMessage`. In particular matching the ``category_type``. """ @@ -37,16 +37,15 @@ class TestWarningMessageCategoryTypeInterface(TestCase, TestMatchersInterface): warning_foo = make_warning_message("foo", DeprecationWarning) warning_bar = make_warning_message("bar", SyntaxWarning) warning_base = make_warning_message("base", Warning) - matches_matches = [warning_foo] - matches_mismatches = [warning_bar, warning_base] + matches_matches: ClassVar[list] = [warning_foo] + matches_mismatches: ClassVar[list] = [warning_bar, warning_base] - str_examples = [] - describe_examples = [] + str_examples: ClassVar[list] = [] + describe_examples: ClassVar[list] = [] class TestWarningMessageMessageInterface(TestCase, TestMatchersInterface): - """ - Tests for `testtools.matchers._warnings.WarningMessage`. + """Tests for `testtools.matchers._warnings.WarningMessage`. In particular matching the ``message``. """ @@ -56,16 +55,15 @@ class TestWarningMessageMessageInterface(TestCase, TestMatchersInterface): ) warning_foo = make_warning_message("foo", DeprecationWarning) warning_bar = make_warning_message("bar", DeprecationWarning) - matches_matches = [warning_foo] - matches_mismatches = [warning_bar] + matches_matches: ClassVar[list] = [warning_foo] + matches_mismatches: ClassVar[list] = [warning_bar] - str_examples = [] - describe_examples = [] + str_examples: ClassVar[list] = [] + describe_examples: ClassVar[list] = [] class TestWarningMessageFilenameInterface(TestCase, TestMatchersInterface): - """ - Tests for `testtools.matchers._warnings.WarningMessage`. + """Tests for `testtools.matchers._warnings.WarningMessage`. In particular matching the ``filename``. """ @@ -75,16 +73,15 @@ class TestWarningMessageFilenameInterface(TestCase, TestMatchersInterface): ) warning_foo = make_warning_message("foo", DeprecationWarning, filename="a") warning_bar = make_warning_message("bar", DeprecationWarning, filename="b") - matches_matches = [warning_foo] - matches_mismatches = [warning_bar] + matches_matches: ClassVar[list] = [warning_foo] + matches_mismatches: ClassVar[list] = [warning_bar] - str_examples = [] - describe_examples = [] + str_examples: ClassVar[list] = [] + describe_examples: ClassVar[list] = [] class TestWarningMessageLineNumberInterface(TestCase, TestMatchersInterface): - """ - Tests for `testtools.matchers._warnings.WarningMessage`. + """Tests for `testtools.matchers._warnings.WarningMessage`. In particular matching the ``lineno``. """ @@ -94,16 +91,15 @@ class TestWarningMessageLineNumberInterface(TestCase, TestMatchersInterface): ) warning_foo = make_warning_message("foo", DeprecationWarning, lineno=42) warning_bar = make_warning_message("bar", DeprecationWarning, lineno=21) - matches_matches = [warning_foo] - matches_mismatches = [warning_bar] + matches_matches: ClassVar[list] = [warning_foo] + matches_mismatches: ClassVar[list] = [warning_bar] - str_examples = [] - describe_examples = [] + str_examples: ClassVar[list] = [] + describe_examples: ClassVar[list] = [] class TestWarningMessageLineInterface(TestCase, TestMatchersInterface): - """ - Tests for `testtools.matchers._warnings.WarningMessage`. + """Tests for `testtools.matchers._warnings.WarningMessage`. In particular matching the ``line``. """ @@ -111,16 +107,15 @@ class TestWarningMessageLineInterface(TestCase, TestMatchersInterface): matches_matcher = WarningMessage(category_type=DeprecationWarning, line=Equals("x")) warning_foo = make_warning_message("foo", DeprecationWarning, line="x") warning_bar = make_warning_message("bar", DeprecationWarning, line="y") - matches_matches = [warning_foo] - matches_mismatches = [warning_bar] + matches_matches: ClassVar[list] = [warning_foo] + matches_mismatches: ClassVar[list] = [warning_bar] - str_examples = [] - describe_examples = [] + str_examples: ClassVar[list] = [] + describe_examples: ClassVar[list] = [] class TestWarningsInterface(TestCase, TestMatchersInterface): - """ - Tests for `testtools.matchers._warnings.Warnings`. + """Tests for `testtools.matchers._warnings.Warnings`. Specifically without the optional argument. """ @@ -130,19 +125,18 @@ class TestWarningsInterface(TestCase, TestMatchersInterface): def old_func(): warnings.warn("old_func is deprecated", DeprecationWarning, 2) - matches_matches = [old_func] - matches_mismatches = [lambda: None] + matches_matches: ClassVar[list] = [old_func] + matches_mismatches: ClassVar[list] = [lambda: None] # Tricky to get function objects to render constantly, and the interfaces # helper uses assertEqual rather than (for instance) DocTestMatches. - str_examples = [] + str_examples: ClassVar[list] = [] - describe_examples = [] + describe_examples: ClassVar[list] = [] class TestWarningsMatcherInterface(TestCase, TestMatchersInterface): - """ - Tests for `testtools.matchers._warnings.Warnings`. + """Tests for `testtools.matchers._warnings.Warnings`. Specifically with the optional matcher argument. """ @@ -159,16 +153,15 @@ def old_func(): def older_func(): warnings.warn("older_func is deprecated", DeprecationWarning, 2) - matches_matches = [old_func] - matches_mismatches = [lambda: None, older_func] + matches_matches: ClassVar[list] = [old_func] + matches_mismatches: ClassVar[list] = [lambda: None, older_func] - str_examples = [] - describe_examples = [] + str_examples: ClassVar[list] = [] + describe_examples: ClassVar[list] = [] class TestWarningsMatcherNoWarningsInterface(TestCase, TestMatchersInterface): - """ - Tests for `testtools.matchers._warnings.Warnings`. + """Tests for `testtools.matchers._warnings.Warnings`. Specifically with the optional matcher argument matching that there were no warnings. @@ -182,17 +175,15 @@ def nowarning_func(): def warning_func(): warnings.warn("warning_func is deprecated", DeprecationWarning, 2) - matches_matches = [nowarning_func] - matches_mismatches = [warning_func] + matches_matches: ClassVar[list] = [nowarning_func] + matches_mismatches: ClassVar[list] = [warning_func] - str_examples = [] - describe_examples = [] + str_examples: ClassVar[list] = [] + describe_examples: ClassVar[list] = [] class TestWarningMessage(TestCase): - """ - Tests for `testtools.matchers._warnings.WarningMessage`. - """ + """Tests for `testtools.matchers._warnings.WarningMessage`.""" run_tests_with = FullStackRunTest @@ -204,9 +195,7 @@ def old_func(): class TestIsDeprecated(TestCase): - """ - Tests for `testtools.matchers._warnings.IsDeprecated`. - """ + """Tests for `testtools.matchers._warnings.IsDeprecated`.""" run_tests_with = FullStackRunTest diff --git a/testtools/tests/samplecases.py b/testtools/tests/samplecases.py index 7a741e5e..67739760 100644 --- a/testtools/tests/samplecases.py +++ b/testtools/tests/samplecases.py @@ -240,18 +240,14 @@ def _test_error_traceback(case, traceback_matcher): A list that can be used with testscenarios to test every deterministic sample case that we have. """ -deterministic_sample_cases_scenarios = multiply_scenarios( - _make_behavior_scenarios("set_up"), - _make_behavior_scenarios("body"), - _make_behavior_scenarios("tear_down"), - _make_behavior_scenarios("cleanup"), -) + [ - ( - "tear_down_fails_after_upcall", - { - "post_tear_down_behavior": _error, - }, +deterministic_sample_cases_scenarios = [ + *multiply_scenarios( + _make_behavior_scenarios("set_up"), + _make_behavior_scenarios("body"), + _make_behavior_scenarios("tear_down"), + _make_behavior_scenarios("cleanup"), ), + ("tear_down_fails_after_upcall", {"post_tear_down_behavior": _error}), ] diff --git a/testtools/tests/test_assert_that.py b/testtools/tests/test_assert_that.py index 4dce0376..84fae077 100644 --- a/testtools/tests/test_assert_that.py +++ b/testtools/tests/test_assert_that.py @@ -86,10 +86,9 @@ def test_assertThat_message_is_annotated(self): def test_assertThat_verbose_output(self): matchee = "foo" matcher = Equals("bar") - expected = "Match failed. Matchee: %r\nMatcher: %s\nDifference: %s\n" % ( - matchee, - matcher, - matcher.match(matchee).describe(), + expected = ( + f"Match failed. Matchee: {matchee!r}\nMatcher: {matcher}\n" + f"Difference: {matcher.match(matchee).describe()}\n" ) self.assertFails( expected, self.assert_that_callable, matchee, matcher, verbose=True @@ -124,7 +123,7 @@ def test_assertThat_verbose_unicode(self): # unicode strings, we can still provide a meaningful error. matchee = "\xa7" matcher = Equals("a") - expected = "Match failed. Matchee: %s\nMatcher: %s\nDifference: %s\n\n" % ( + expected = "Match failed. Matchee: {}\nMatcher: {}\nDifference: {}\n\n".format( repr(matchee).replace("\\xa7", matchee), matcher, matcher.match(matchee).describe(), diff --git a/testtools/tests/test_compat.py b/testtools/tests/test_compat.py index 92a4d9a5..d6ac9812 100644 --- a/testtools/tests/test_compat.py +++ b/testtools/tests/test_compat.py @@ -8,7 +8,6 @@ import traceback import testtools - from testtools.compat import ( _b, reraise, @@ -186,12 +185,12 @@ def test_ascii_examples_multiline_unicode(self): def test_ascii_examples_defaultline_bytes(self): for s, one, multi in self.ascii_examples: - expected = "\n" in s and multi or one + expected = ("\n" in s and multi) or one self.assertEqual(text_repr(_b(s)), self.b_prefix + expected) def test_ascii_examples_defaultline_unicode(self): for s, one, multi in self.ascii_examples: - expected = "\n" in s and multi or one + expected = ("\n" in s and multi) or one self.assertEqual(text_repr(s), self.u_prefix + expected) def test_bytes_examples_oneline(self): diff --git a/testtools/tests/test_content.py b/testtools/tests/test_content.py index 2829c5fd..d30bf5eb 100644 --- a/testtools/tests/test_content.py +++ b/testtools/tests/test_content.py @@ -10,20 +10,20 @@ _b, ) from testtools.content import ( - attach_file, - Content, - content_from_file, - content_from_stream, JSON, - json_content, + Content, StackLinesContent, StacktraceContent, TracebackContent, + attach_file, + content_from_file, + content_from_stream, + json_content, text_content, ) from testtools.content_type import ( - ContentType, UTF8_TEXT, + ContentType, ) from testtools.matchers import ( Equals, @@ -33,7 +33,6 @@ ) from testtools.tests.helpers import an_exc_info - raises_value_error = Raises(MatchesException(ValueError)) @@ -199,8 +198,8 @@ def test_text_content_raises_TypeError_when_passed_non_text(self): lambda: text_content(value), raises( TypeError( - "text_content must be given text, not '%s'." - % type(value).__name__ + f"text_content must be given text, " + f"not '{type(value).__name__}'." ) ), ) diff --git a/testtools/tests/test_content_type.py b/testtools/tests/test_content_type.py index 63c124a0..c420ae7e 100644 --- a/testtools/tests/test_content_type.py +++ b/testtools/tests/test_content_type.py @@ -1,12 +1,12 @@ # Copyright (c) 2008, 2012 testtools developers. See LICENSE for details. from testtools import TestCase -from testtools.matchers import Equals, MatchesException, Raises from testtools.content_type import ( - ContentType, JSON, UTF8_TEXT, + ContentType, ) +from testtools.matchers import Equals, MatchesException, Raises class TestContentType(TestCase): diff --git a/testtools/tests/test_fixturesupport.py b/testtools/tests/test_fixturesupport.py index 31a1c5c9..a65ce70c 100644 --- a/testtools/tests/test_fixturesupport.py +++ b/testtools/tests/test_fixturesupport.py @@ -119,7 +119,7 @@ class BrokenFixture(fixtures.Fixture): def setUp(self): fixtures.Fixture.setUp(self) self.addDetail("content", content.text_content("foobar")) - raise Exception() + raise Exception fixture = BrokenFixture() diff --git a/testtools/tests/test_monkey.py b/testtools/tests/test_monkey.py index cf68abdd..cbf68d4b 100644 --- a/testtools/tests/test_monkey.py +++ b/testtools/tests/test_monkey.py @@ -16,9 +16,7 @@ def __init__(self): class MonkeyPatcherTest(TestCase): - """ - Tests for 'MonkeyPatcher' monkey-patching class. - """ + """Tests for 'MonkeyPatcher' monkey-patching class.""" def setUp(self): super().setUp() diff --git a/testtools/tests/test_run.py b/testtools/tests/test_run.py index 542ade7c..e3965644 100644 --- a/testtools/tests/test_run.py +++ b/testtools/tests/test_run.py @@ -5,8 +5,8 @@ import doctest import io import sys -from textwrap import dedent import unittest +from textwrap import dedent from unittest import TestSuite import testtools @@ -179,7 +179,7 @@ def list(self, test): ) except SystemExit: exc_info = sys.exc_info() - raise AssertionError("-l tried to exit. %r" % exc_info[1]) + raise AssertionError(f"-l tried to exit. {exc_info[1]!r}") self.assertEqual( [ { @@ -211,7 +211,7 @@ def list(self, test, loader=None): ) except SystemExit: exc_info = sys.exc_info() - raise AssertionError("-l tried to exit. %r" % exc_info[1]) + raise AssertionError(f"-l tried to exit. {exc_info[1]!r}") self.assertEqual( [ { @@ -230,7 +230,7 @@ def test_run_list(self): run.main(["prog", "-l", "testtools.runexample.test_suite"], out) except SystemExit: exc_info = sys.exc_info() - raise AssertionError("-l tried to exit. %r" % exc_info[1]) + raise AssertionError(f"-l tried to exit. {exc_info[1]!r}") self.assertEqual( """testtools.runexample.TestFoo.test_bar testtools.runexample.TestFoo.test_quux @@ -304,7 +304,7 @@ def test_run_orders_tests(self): ) except SystemExit: exc_info = sys.exc_info() - raise AssertionError("-l --load-list tried to exit. %r" % exc_info[1]) + raise AssertionError(f"-l --load-list tried to exit. {exc_info[1]!r}") self.assertEqual( """testtools.runexample.TestFoo.test_bar """, @@ -344,7 +344,7 @@ def test_run_load_list(self): ) except SystemExit: exc_info = sys.exc_info() - raise AssertionError("-l --load-list tried to exit. %r" % exc_info[1]) + raise AssertionError(f"-l --load-list tried to exit. {exc_info[1]!r}") self.assertEqual( """testtools.runexample.TestFoo.test_bar """, @@ -387,7 +387,7 @@ def test_load_list_preserves_custom_suites(self): # Evil resides in TestProgram. pass out = stdout.getDetails()["stdout"].as_text() - self.assertEqual(1, out.count("Setting up Printer"), "%r" % out) + self.assertEqual(1, out.count("Setting up Printer"), f"{out!r}") def test_run_failfast(self): stdout = self.useFixture(fixtures.StringStream("stdout")) diff --git a/testtools/tests/test_runtest.py b/testtools/tests/test_runtest.py index 37afecb5..a10b0bd8 100644 --- a/testtools/tests/test_runtest.py +++ b/testtools/tests/test_runtest.py @@ -4,12 +4,12 @@ from testtools import ( ExtendedToOriginalDecorator, - run_test_with, RunTest, TestCase, TestResult, + run_test_with, ) -from testtools.matchers import HasLength, MatchesException, Is, Raises +from testtools.matchers import HasLength, Is, MatchesException, Raises from testtools.testresult.doubles import ExtendedTestResult from testtools.tests.helpers import FullStackRunTest diff --git a/testtools/tests/test_testcase.py b/testtools/tests/test_testcase.py index eb6fc3ba..f07cc919 100644 --- a/testtools/tests/test_testcase.py +++ b/testtools/tests/test_testcase.py @@ -2,11 +2,11 @@ """Tests for extensions to the base test library.""" -from doctest import ELLIPSIS -from pprint import pformat -import sys import _thread +import sys import unittest +from doctest import ELLIPSIS +from pprint import pformat from testtools import ( DecorateTestCaseResult, @@ -25,8 +25,8 @@ _b, ) from testtools.content import ( - text_content, TracebackContent, + text_content, ) from testtools.matchers import ( Annotate, @@ -38,21 +38,21 @@ Raises, ) from testtools.testcase import ( - attr, Nullary, WithAttributes, + attr, ) from testtools.testresult.doubles import ( + ExtendedTestResult, Python26TestResult, Python27TestResult, - ExtendedTestResult, ) from testtools.tests.helpers import ( - an_exc_info, AsText, FullStackRunTest, LoggingResult, MatchesEvents, + an_exc_info, raise_, ) from testtools.tests.samplecases import ( @@ -97,7 +97,7 @@ def test_repr_just_id(self): # repr(placeholder) shows you how the object was constructed. test = PlaceHolder("test id") self.assertEqual( - "" % repr(test.id()), + f"", repr(test), ) @@ -105,16 +105,15 @@ def test_repr_with_description(self): # repr(placeholder) shows you how the object was constructed. test = PlaceHolder("test id", "description") self.assertEqual( - "".format( - test.id(), test.shortDescription() - ), + f"", repr(test), ) def test_repr_custom_outcome(self): test = PlaceHolder("test id", outcome="addSkip") self.assertEqual( - "" % (test.id()), + f"", repr(test), ) @@ -387,7 +386,7 @@ def raiseError(): self.assertIs( exception, 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): @@ -417,7 +416,6 @@ def test_assertRaises_function_repr_in_exception(self): # function in the error message, so it's easy to locate the problem. def foo(): """An arbitrary function.""" - pass self.assertThat( lambda: self.assertRaises(Exception, foo), @@ -600,7 +598,7 @@ class Foo: """Simple class for testing assertIsInstance.""" self.assertFails( - "'42' is not an instance of %s" % self._formatTypes(Foo), + f"'42' is not an instance of {self._formatTypes(Foo)}", self.assertIsInstance, 42, Foo, @@ -617,7 +615,7 @@ class Bar: """Another simple class for testing assertIsInstance.""" self.assertFails( - "'42' is not an instance of any of (%s)" % self._formatTypes([Foo, Bar]), + f"'42' is not an instance of any of ({self._formatTypes([Foo, Bar])})", self.assertIsInstance, 42, (Foo, Bar), @@ -730,10 +728,9 @@ def test_assertThat_message_is_annotated(self): def test_assertThat_verbose_output(self): matchee = "foo" matcher = Equals("bar") - expected = "Match failed. Matchee: %r\nMatcher: %s\nDifference: %s\n" % ( - matchee, - matcher, - matcher.match(matchee).describe(), + expected = ( + f"Match failed. Matchee: {matchee!r}\nMatcher: {matcher}\n" + f"Difference: {matcher.match(matchee).describe()}\n" ) self.assertFails(expected, self.assertThat, matchee, matcher, verbose=True) @@ -814,7 +811,7 @@ def test_assertThat_verbose_unicode(self): # unicode strings, we can still provide a meaningful error. matchee = "\xa7" matcher = Equals("a") - expected = "Match failed. Matchee: %s\nMatcher: %s\nDifference: %s\n\n" % ( + expected = "Match failed. Matchee: {}\nMatcher: {}\nDifference: {}\n\n".format( repr(matchee).replace("\\xa7", matchee), matcher, matcher.match(matchee).describe(), @@ -836,8 +833,8 @@ def test_assertEqual_nice_formatting(self): expected_error = "\n".join( [ "!=:", - "reference = %s" % pformat(a), - "actual = %s" % pformat(b), + f"reference = {pformat(a)}", + f"actual = {pformat(b)}", ": " + message, ] ) @@ -1318,9 +1315,9 @@ def test_getUniqueString(self): # getUniqueString returns the current test id followed by a unique # integer. name_one = self.getUniqueString() - self.assertEqual("%s-%d" % (self.id(), 1), name_one) + self.assertEqual(f"{self.id()}-1", name_one) name_two = self.getUniqueString() - self.assertEqual("%s-%d" % (self.id(), 2), name_two) + self.assertEqual(f"{self.id()}-2", name_two) def test_getUniqueString_prefix(self): # If getUniqueString is given an argument, it uses that argument as @@ -1464,7 +1461,7 @@ def test_addUnexpectedSuccess(self): class Case(TestCase): def test(this): this.addDetail("foo", self.get_content()) - raise testcase._UnexpectedSuccess() + raise testcase._UnexpectedSuccess self.assertDetailsProvided(Case("test"), "addUnexpectedSuccess", ["foo"]) diff --git a/testtools/tests/test_testresult.py b/testtools/tests/test_testresult.py index 429df27b..f77e6c78 100644 --- a/testtools/tests/test_testresult.py +++ b/testtools/tests/test_testresult.py @@ -6,16 +6,15 @@ import datetime import doctest import io -from itertools import chain -from itertools import combinations import os import platform -from queue import Queue import re import shutil import sys import tempfile import threading +from itertools import chain, combinations +from queue import Queue from unittest import TestSuite from testtools import ( @@ -34,11 +33,11 @@ StreamToExtendedDecorator, StreamToQueue, Tagger, + TestByTestResult, TestCase, TestControl, TestResult, TestResultDecorator, - TestByTestResult, TextTestResult, ThreadsafeForwardingResult, TimestampingStreamResult, @@ -50,11 +49,11 @@ ) from testtools.content import ( Content, + TracebackContent, content_from_stream, text_content, - TracebackContent, ) -from testtools.content_type import ContentType, UTF8_TEXT +from testtools.content_type import UTF8_TEXT, ContentType from testtools.helpers import try_import from testtools.matchers import ( AllMatch, @@ -67,24 +66,26 @@ MatchesRegex, Raises, ) -from testtools.tests.helpers import ( - an_exc_info, - FullStackRunTest, - LoggingResult, - run_with_stack_hidden, -) from testtools.testresult.doubles import ( + ExtendedTestResult, Python26TestResult, Python27TestResult, - ExtendedTestResult, - StreamResult as LoggingStreamResult, TwistedTestResult, ) +from testtools.testresult.doubles import ( + StreamResult as LoggingStreamResult, +) from testtools.testresult.real import ( _details_to_str, _merge_tags, utc, ) +from testtools.tests.helpers import ( + FullStackRunTest, + LoggingResult, + an_exc_info, + run_with_stack_hidden, +) testresources = try_import("testresources") @@ -554,7 +555,7 @@ def test_test_status(self): result.status(test_id=arg[0], test_status=arg[1], **kwargs) def _power_set(self, iterable): - "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)" + """powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)""" s = list(iterable) param_dicts = [] combos = (combinations(s, r) for r in range(len(s) + 1)) @@ -1447,7 +1448,8 @@ def test_traceback_formatting_without_stack_hidden(self): " return fn(*args, **kwargs)\n..." ' File "...testtools...testcase.py", line ..., in _run_test_method\n' " return self._get_test_method()()\n..." - ' File "...testtools...tests...test_testresult.py", line ..., in error\n' + ' File "...testtools...tests...test_testresult.py", ' + "line ..., in error\n" " 1 / 0\n..." "ZeroDivisionError: ...\n", doctest.ELLIPSIS | doctest.REPORT_UDIFF, @@ -1462,7 +1464,8 @@ def test_traceback_formatting_with_stack_hidden(self): result.errors[0][1], DocTestMatches( "Traceback (most recent call last):\n" - ' File "...testtools...tests...test_testresult.py", line ..., in error\n' + ' File "...testtools...tests...test_testresult.py", ' + "line ..., in error\n" " 1 / 0\n..." "ZeroDivisionError: ...\n", doctest.ELLIPSIS, @@ -1477,7 +1480,8 @@ def test_traceback_formatting_with_stack_hidden_mismatch(self): result.failures[0][1], DocTestMatches( "Traceback (most recent call last):\n" - ' File "...testtools...tests...test_testresult.py", line ..., in mismatch\n' + ' File "...testtools...tests...test_testresult.py", ' + "line ..., in mismatch\n" " self.assertEqual(1, 2)\n" "...MismatchError: 1 != 2\n", doctest.ELLIPSIS, @@ -1514,7 +1518,8 @@ def test_traceback_with_locals(self): " return self._get_test_method()()\n..." " result = ...\n" " self = ...\n" - ' File "...testtools...tests...test_testresult.py", line ..., in error\n' + ' File "...testtools...tests...test_testresult.py", ' + "line ..., in error\n" " 1 / 0\n..." " a = 1\n" " self = ...\n" @@ -1540,10 +1545,8 @@ def assertResultLogsEqual(self, expectedEvents): def test_repr(self): self.assertEqual( - "".format( - ExtendedToOriginalDecorator(self.result1), - ExtendedToOriginalDecorator(self.result2), - ), + f"", repr(self.multiResult), ) @@ -2848,7 +2851,7 @@ def _write_module(self, name, encoding, contents): # the file without closing it which breaks non-refcounted pythons codecs.lookup(encoding) except LookupError: - self.skipTest("Encoding unsupported by implementation: %r" % encoding) + self.skipTest(f"Encoding unsupported by implementation: {encoding!r}") f = codecs.open(os.path.join(self.dir, name + ".py"), "w", encoding) try: f.write(contents) @@ -2871,12 +2874,12 @@ def _setup_external_case(self, testline, coding="ascii", modulelevel="", suffix= # Older Python 2 versions don't see a coding declaration in a # docstring so it has to be in a comment, but then we can't # workaround bug: - "# coding: %s\n" + f"# coding: {coding}\n" "import testtools\n" - "%s\n" + f"{modulelevel}\n" "class Test(testtools.TestCase):\n" " def runTest(self):\n" - " %s\n" % (coding, modulelevel, testline), + f" {testline}\n", ) def _run_external_case(self): @@ -2899,7 +2902,7 @@ def _get_sample_text(self, encoding="unicode_internal"): return u, u except (LookupError, UnicodeError): pass - self.skipTest("Could not find a sample text for encoding: %r" % encoding) + self.skipTest(f"Could not find a sample text for encoding: {encoding!r}") def _as_output(self, text): return text @@ -2907,15 +2910,13 @@ def _as_output(self, text): def test_non_ascii_failure_string(self): """Assertion contents can be non-ascii and should get decoded""" text, raw = self._get_sample_text(_get_exception_encoding()) - textoutput = self._test_external_case("self.fail(%s)" % ascii(raw)) + textoutput = self._test_external_case(f"self.fail({raw!a})") self.assertIn(self._as_output(text), textoutput) def test_non_ascii_failure_string_via_exec(self): """Assertion via exec can be non-ascii and still gets decoded""" text, raw = self._get_sample_text(_get_exception_encoding()) - textoutput = self._test_external_case( - testline='exec ("self.fail(%s)")' % ascii(raw) - ) + textoutput = self._test_external_case(testline=f'exec ("self.fail({raw!a})")') self.assertIn(self._as_output(text), textoutput) def test_control_characters_in_failure_string(self): @@ -2943,16 +2944,16 @@ def test_assertion_text_shift_jis(self): """A terminal raw backslash in an encoded string is weird but fine""" example_text = "\u5341" textoutput = self._test_external_case( - coding="shift_jis", testline="self.fail('%s')" % example_text + coding="shift_jis", testline=f"self.fail('{example_text}')" ) output_text = example_text - self.assertIn(self._as_output("AssertionError: %s" % output_text), textoutput) + self.assertIn(self._as_output(f"AssertionError: {output_text}"), textoutput) def test_file_comment_iso2022_jp(self): """Control character escapes must be preserved if valid encoding""" example_text, _ = self._get_sample_text("iso2022_jp") textoutput = self._test_external_case( - coding="iso2022_jp", testline="self.fail('Simple') # %s" % example_text + coding="iso2022_jp", testline=f"self.fail('Simple') # {example_text}" ) self.assertIn(self._as_output(example_text), textoutput) @@ -2967,7 +2968,7 @@ def test_unicode_exception(self): ) textoutput = self._test_external_case( modulelevel=exception_class, - testline="raise FancyError(%s)" % ascii(example_text), + testline=f"raise FancyError({example_text!a})", ) self.assertIn(self._as_output(example_text), textoutput) @@ -3036,13 +3037,13 @@ def test_syntax_error_line_iso_8859_1(self): text, raw = self._get_sample_text("iso-8859-1") textoutput = self._setup_external_case("import bad") self._write_module( - "bad", "iso-8859-1", "# coding: iso-8859-1\n! = 0 # %s\n" % text + "bad", "iso-8859-1", f"# coding: iso-8859-1\n! = 0 # {text}\n" ) textoutput = self._run_external_case() self.assertIn( self._as_output( #'bad.py", line 2\n' - " ! = 0 # %s\n ^\nSyntaxError: " % (text,) + f" ! = 0 # {text}\n ^\nSyntaxError: " ), textoutput, ) @@ -3052,7 +3053,7 @@ def test_syntax_error_line_iso_8859_5(self): text, raw = self._get_sample_text("iso-8859-5") textoutput = self._setup_external_case("import bad") self._write_module( - "bad", "iso-8859-5", "# coding: iso-8859-5\n%% = 0 # %s\n" % text + "bad", "iso-8859-5", f"# coding: iso-8859-5\n% = 0 # {text}\n" ) textoutput = self._run_external_case() self.assertThat( @@ -3074,7 +3075,7 @@ def test_syntax_error_line_euc_jp(self): """Syntax error on a euc_jp line shows the line decoded""" text, raw = self._get_sample_text("euc_jp") textoutput = self._setup_external_case("import bad") - self._write_module("bad", "euc_jp", "# coding: euc_jp\n$ = 0 # %s\n" % text) + self._write_module("bad", "euc_jp", f"# coding: euc_jp\n$ = 0 # {text}\n") textoutput = self._run_external_case() # pypy uses cpython's multibyte codecs so has their behavior here if self._is_pypy: @@ -3095,11 +3096,10 @@ def test_syntax_error_line_utf_8(self): """Syntax error on a utf-8 line shows the line decoded""" text, raw = self._get_sample_text("utf-8") textoutput = self._setup_external_case("import bad") - self._write_module("bad", "utf-8", "\ufeff^ = 0 # %s\n" % text) + self._write_module("bad", "utf-8", f"\ufeff^ = 0 # {text}\n") textoutput = self._run_external_case() # Python 3.9 no longer prints the '\ufeff' - if sys.version_info >= (3, 9): - textoutput = textoutput.replace("\ufeff", "") + textoutput = textoutput.replace("\ufeff", "") self.assertThat( textoutput, MatchesRegex( diff --git a/testtools/tests/test_testsuite.py b/testtools/tests/test_testsuite.py index ae7f13fe..6de81193 100644 --- a/testtools/tests/test_testsuite.py +++ b/testtools/tests/test_testsuite.py @@ -3,22 +3,22 @@ """Test ConcurrentTestSuite and related things.""" import doctest -from pprint import pformat import unittest +from pprint import pformat from testtools import ( - ConcurrentTestSuite, ConcurrentStreamTestSuite, - iterate_tests, + ConcurrentTestSuite, PlaceHolder, TestByTestResult, TestCase, + iterate_tests, ) from testtools.helpers import try_import from testtools.matchers import DocTestMatches, Equals from testtools.testresult.doubles import StreamResult as LoggingStream -from testtools.testsuite import FixtureSuite, sorted_tests from testtools.tests.helpers import LoggingResult +from testtools.testsuite import FixtureSuite, sorted_tests FunctionFixture = try_import("fixtures.FunctionFixture") diff --git a/testtools/tests/test_with_with.py b/testtools/tests/test_with_with.py index 3a167096..0d22c057 100644 --- a/testtools/tests/test_with_with.py +++ b/testtools/tests/test_with_with.py @@ -9,8 +9,8 @@ ) from testtools.matchers import ( AfterPreprocessing, - Equals, EndsWith, + Equals, ) diff --git a/testtools/tests/twistedsupport/_helpers.py b/testtools/tests/twistedsupport/_helpers.py index 05c01077..4700e851 100644 --- a/testtools/tests/twistedsupport/_helpers.py +++ b/testtools/tests/twistedsupport/_helpers.py @@ -4,8 +4,8 @@ "NeedsTwistedTestCase", ] -from testtools.helpers import try_import from testtools import TestCase +from testtools.helpers import try_import defer = try_import("twisted.internet.defer") diff --git a/testtools/tests/twistedsupport/test_deferred.py b/testtools/tests/twistedsupport/test_deferred.py index 31692590..3d3ab39d 100644 --- a/testtools/tests/twistedsupport/test_deferred.py +++ b/testtools/tests/twistedsupport/test_deferred.py @@ -8,8 +8,8 @@ MatchesException, Raises, ) -from ._helpers import NeedsTwistedTestCase +from ._helpers import NeedsTwistedTestCase DeferredNotFired = try_import("testtools.twistedsupport._deferred.DeferredNotFired") extract_result = try_import("testtools.twistedsupport._deferred.extract_result") diff --git a/testtools/tests/twistedsupport/test_matchers.py b/testtools/tests/twistedsupport/test_matchers.py index 689cafd4..e292a481 100644 --- a/testtools/tests/twistedsupport/test_matchers.py +++ b/testtools/tests/twistedsupport/test_matchers.py @@ -2,6 +2,9 @@ """Tests for Deferred matchers.""" +from twisted.internet import defer +from twisted.python.failure import Failure + from testtools.content import TracebackContent from testtools.matchers import ( AfterPreprocessing, @@ -9,13 +12,9 @@ Is, MatchesDict, ) -from ._helpers import NeedsTwistedTestCase - - -from testtools.twistedsupport import has_no_result, failed, succeeded +from testtools.twistedsupport import failed, has_no_result, succeeded -from twisted.internet import defer -from twisted.python.failure import Failure +from ._helpers import NeedsTwistedTestCase def mismatches(description, details=None): @@ -65,9 +64,7 @@ def test_succeeded_does_no_match(self): self.assertThat( mismatch, mismatches( - Equals( - "No result expected on %r, found %r instead" % (deferred, result) - ) + Equals(f"No result expected on {deferred!r}, found {result!r} instead") ), ) @@ -81,7 +78,7 @@ def test_failed_does_not_match(self): self.assertThat( mismatch, mismatches( - Equals("No result expected on %r, found %r instead" % (deferred, fail)) + Equals(f"No result expected on {deferred!r}, found {fail!r} instead") ), ) @@ -139,8 +136,7 @@ def test_not_fired_fails(self): self.match(arbitrary_matcher, deferred), mismatches( Equals( - ("Success result expected on %r, found no result instead") - % (deferred,) + f"Success result expected on {deferred!r}, found no result instead" ) ), ) @@ -155,8 +151,8 @@ def test_failing_fails(self): self.match(arbitrary_matcher, deferred), mismatches( Equals( - "Success result expected on %r, found failure result " - "instead: %r" % (deferred, fail) + f"Success result expected on {deferred!r}, found failure result " + f"instead: {fail!r}" ), Equals( { @@ -202,8 +198,8 @@ def test_success_fails(self): self.match(matcher, deferred), mismatches( Equals( - "Failure result expected on %r, found success " - "result (%r) instead" % (deferred, result) + f"Failure result expected on {deferred!r}, found success " + f"result ({result!r}) instead" ) ), ) @@ -216,8 +212,7 @@ def test_no_result_fails(self): self.match(matcher, deferred), mismatches( Equals( - "Failure result expected on %r, found no result instead" - % (deferred,) + f"Failure result expected on {deferred!r}, found no result instead" ) ), ) diff --git a/testtools/tests/twistedsupport/test_runtest.py b/testtools/tests/twistedsupport/test_runtest.py index 8209b847..be5595aa 100644 --- a/testtools/tests/twistedsupport/test_runtest.py +++ b/testtools/tests/twistedsupport/test_runtest.py @@ -4,11 +4,12 @@ import os import signal +from typing import ClassVar from testtools import ( - skipIf, TestCase, TestResult, + skipIf, ) from testtools.helpers import try_import from testtools.matchers import ( @@ -29,6 +30,7 @@ AsText, MatchesEvents, ) + from ._helpers import NeedsTwistedTestCase DebugTwisted = try_import("testtools.twistedsupport._deferreddebug.DebugTwisted") @@ -71,44 +73,44 @@ def tearDown(self): super(X.Base, self).tearDown() class BaseExceptionRaised(Base): - expected_calls = ["setUp", "tearDown", "clean-up"] - expected_results = [("addError", SystemExit)] + expected_calls: ClassVar[list] = ["setUp", "tearDown", "clean-up"] + expected_results: ClassVar[list] = [("addError", SystemExit)] def test_something(self): raise SystemExit(0) class ErrorInSetup(Base): - expected_calls = ["setUp", "clean-up"] - expected_results = [("addError", RuntimeError)] + expected_calls: ClassVar[list] = ["setUp", "clean-up"] + expected_results: ClassVar[list] = [("addError", RuntimeError)] def setUp(self): super(X.ErrorInSetup, self).setUp() raise RuntimeError("Error in setUp") class ErrorInTest(Base): - expected_calls = ["setUp", "tearDown", "clean-up"] - expected_results = [("addError", RuntimeError)] + expected_calls: ClassVar[list] = ["setUp", "tearDown", "clean-up"] + expected_results: ClassVar[list] = [("addError", RuntimeError)] def test_something(self): raise RuntimeError("Error in test") class FailureInTest(Base): - expected_calls = ["setUp", "tearDown", "clean-up"] - expected_results = [("addFailure", AssertionError)] + expected_calls: ClassVar[list] = ["setUp", "tearDown", "clean-up"] + expected_results: ClassVar[list] = [("addFailure", AssertionError)] def test_something(self): self.fail("test failed") class ErrorInTearDown(Base): - expected_calls = ["setUp", "test", "clean-up"] - expected_results = [("addError", RuntimeError)] + expected_calls: ClassVar[list] = ["setUp", "test", "clean-up"] + expected_results: ClassVar[list] = [("addError", RuntimeError)] def tearDown(self): raise RuntimeError("Error in tearDown") class ErrorInCleanup(Base): - expected_calls = ["setUp", "test", "tearDown", "clean-up"] - expected_results = [("addError", ZeroDivisionError)] + expected_calls: ClassVar[list] = ["setUp", "test", "tearDown", "clean-up"] + expected_results: ClassVar[list] = [("addError", ZeroDivisionError)] def test_something(self): self.calls.append("test") @@ -117,8 +119,8 @@ def test_something(self): class ExpectThatFailure(Base): """Calling expectThat with a failing match fails the test.""" - expected_calls = ["setUp", "test", "tearDown", "clean-up"] - expected_results = [("addFailure", AssertionError)] + expected_calls: ClassVar[list] = ["setUp", "test", "tearDown", "clean-up"] + expected_results: ClassVar[list] = [("addFailure", AssertionError)] def test_something(self): self.calls.append("test") @@ -151,6 +153,7 @@ def test_runner(self): def make_integration_tests(): from unittest import TestSuite + from testtools import clone_test_with_new_id runners = [ @@ -174,7 +177,7 @@ def make_integration_tests(): for test in tests: new_test = clone_test_with_new_id( base_test, - "{}({}, {})".format(base_test.id(), runner_name, test.__name__), + f"{base_test.id()}({runner_name}, {test.__name__})", ) new_test.test_factory = test new_test.runner = runner @@ -894,8 +897,7 @@ def check_result(failure): self.assertThat( str(failure.value), Equals( - "RuntimeError, ZeroDivisionError not raised " - "(%r returned)" % (marker,) + f"RuntimeError, ZeroDivisionError not raised ({marker!r} returned)" ), ) diff --git a/testtools/tests/twistedsupport/test_spinner.py b/testtools/tests/twistedsupport/test_spinner.py index 772341b3..7601505a 100644 --- a/testtools/tests/twistedsupport/test_spinner.py +++ b/testtools/tests/twistedsupport/test_spinner.py @@ -5,16 +5,16 @@ import os import signal -from testtools.helpers import try_import from testtools import skipIf +from testtools.helpers import try_import from testtools.matchers import ( Equals, Is, MatchesException, Raises, ) -from ._helpers import NeedsTwistedTestCase +from ._helpers import NeedsTwistedTestCase _spinner = try_import("testtools.twistedsupport._spinner") diff --git a/testtools/testsuite.py b/testtools/testsuite.py index b129aa03..5e82bed9 100644 --- a/testtools/testsuite.py +++ b/testtools/testsuite.py @@ -3,19 +3,19 @@ """Test suites and related things.""" __all__ = [ - "ConcurrentTestSuite", "ConcurrentStreamTestSuite", + "ConcurrentTestSuite", "filter_by_ids", "iterate_tests", "sorted_tests", ] -from collections import Counter -from pprint import pformat -from queue import Queue import sys import threading import unittest +from collections import Counter +from pprint import pformat +from queue import Queue import testtools diff --git a/testtools/twistedsupport/__init__.py b/testtools/twistedsupport/__init__.py index 86516ae7..42cae985 100644 --- a/testtools/twistedsupport/__init__.py +++ b/testtools/twistedsupport/__init__.py @@ -3,30 +3,29 @@ """Support for testing code that uses Twisted.""" __all__ = [ - # Matchers - "succeeded", - "failed", - "has_no_result", # Running tests "AsynchronousDeferredRunTest", "AsynchronousDeferredRunTestForBrokenTwisted", - "SynchronousDeferredRunTest", "CaptureTwistedLogs", + "SynchronousDeferredRunTest", "assert_fails_with", + "failed", "flush_logged_errors", + "has_no_result", + # Matchers + "succeeded", ] from ._matchers import ( - succeeded, failed, has_no_result, + succeeded, ) - from ._runtest import ( AsynchronousDeferredRunTest, AsynchronousDeferredRunTestForBrokenTwisted, - SynchronousDeferredRunTest, CaptureTwistedLogs, + SynchronousDeferredRunTest, assert_fails_with, flush_logged_errors, ) diff --git a/testtools/twistedsupport/_deferred.py b/testtools/twistedsupport/_deferred.py index 50fdaa40..6f5c547f 100644 --- a/testtools/twistedsupport/_deferred.py +++ b/testtools/twistedsupport/_deferred.py @@ -2,13 +2,12 @@ """Utilities for Deferreds.""" -from typing import List, TYPE_CHECKING - from functools import partial +from typing import TYPE_CHECKING if TYPE_CHECKING: - from twisted.python.failure import Failure from twisted.internet.defer import Deferred + from twisted.python.failure import Failure from testtools.content import TracebackContent @@ -35,8 +34,8 @@ def extract_result(deferred): code). In those cases, it is better to add callbacks and errbacks as needed. """ - failures: List["Failure"] = [] - successes: List["Deferred"] = [] + failures: list[Failure] = [] + successes: list[Deferred] = [] deferred.addCallbacks(successes.append, failures.append) if len(failures) == 1: failures[0].raiseException() @@ -80,8 +79,8 @@ def on_deferred_result(deferred, on_success, on_failure, on_no_result): :return: Whatever is returned by the triggered callback. :rtype: ``T`` """ - successes: List["Deferred"] = [] - failures: List["Failure"] = [] + successes: list[Deferred] = [] + failures: list[Failure] = [] def capture(value, values): values.append(value) diff --git a/testtools/twistedsupport/_matchers.py b/testtools/twistedsupport/_matchers.py index 2b7cc9cf..0eebe0ee 100644 --- a/testtools/twistedsupport/_matchers.py +++ b/testtools/twistedsupport/_matchers.py @@ -24,9 +24,7 @@ class _NoResult: @staticmethod def _got_result(deferred, result): - return Mismatch( - "No result expected on %r, found %r instead" % (deferred, result) - ) + return Mismatch(f"No result expected on {deferred!r}, found {result!r} instead") def match(self, deferred): """Match ``deferred`` if it hasn't fired.""" @@ -55,7 +53,8 @@ def has_no_result(): ... File "testtools/assertions.py", line 22, in assert_that raise MismatchError(matchee, matcher, mismatch, verbose) - testtools.matchers._impl.MismatchError: No result expected on , found None instead + testtools.matchers._impl.MismatchError: No result expected on , found None instead As will this: @@ -64,7 +63,10 @@ def has_no_result(): ... File "testtools/assertions.py", line 22, in assert_that raise MismatchError(matchee, matcher, mismatch, verbose) - testtools.matchers._impl.MismatchError: No result expected on >>, found > instead + testtools.matchers._impl.MismatchError: No result expected on >>, found + > instead """ return _NO_RESULT @@ -80,15 +82,15 @@ def __init__(self, matcher): def _got_failure(deferred, failure): deferred.addErrback(lambda _: None) return Mismatch( - "Success result expected on %r, found failure result " - "instead: %r" % (deferred, failure), + f"Success result expected on {deferred!r}, found failure result " + f"instead: {failure!r}", {"traceback": failure_content(failure)}, ) @staticmethod def _got_no_result(deferred): return Mismatch( - "Success result expected on %r, found no result instead" % (deferred,) + f"Success result expected on {deferred!r}, found no result instead" ) def match(self, deferred): @@ -139,14 +141,14 @@ def _got_failure(self, deferred, failure): @staticmethod def _got_success(deferred, success): return Mismatch( - "Failure result expected on %r, found success " - "result (%r) instead" % (deferred, success) + f"Failure result expected on {deferred!r}, found success " + f"result ({success!r}) instead" ) @staticmethod def _got_no_result(deferred): return Mismatch( - "Failure result expected on %r, found no result instead" % (deferred,) + f"Failure result expected on {deferred!r}, found no result instead" ) def match(self, deferred): diff --git a/testtools/twistedsupport/_runtest.py b/testtools/twistedsupport/_runtest.py index a00a5eca..484814e4 100644 --- a/testtools/twistedsupport/_runtest.py +++ b/testtools/twistedsupport/_runtest.py @@ -30,10 +30,12 @@ def test_something(self): import sys from fixtures import CompoundFixture, Fixture +from twisted.internet import defer from testtools.content import Content, text_content from testtools.content_type import UTF8_TEXT from testtools.runtest import RunTest, _raise_force_fail_error + from ._deferred import extract_result from ._spinner import ( NoResultError, @@ -42,8 +44,6 @@ def test_something(self): trap_unhandled_errors, ) -from twisted.internet import defer - try: from twisted.logger import globalLogPublisher except ImportError: @@ -511,9 +511,8 @@ def got_failure(failure): if failure.check(*exc_types): return failure.value raise failureException( - "{} raised instead of {}:\n {}".format( - failure.type.__name__, expected_names, failure.getTraceback() - ) + f"{failure.type.__name__} raised instead of {expected_names}:\n" + f" {failure.getTraceback()}" ) return d.addCallbacks(got_success, got_failure) @@ -527,8 +526,9 @@ def __init__(self, junk): self, "The reactor still thinks it needs to do things. Close all " "connections, kill all processes and make sure all delayed " - "calls have either fired or been cancelled:\n%s" - % "".join(map(self._get_junk_info, junk)), + "calls have either fired or been cancelled:\n{}".format( + "".join(map(self._get_junk_info, junk)) + ), ) def _get_junk_info(self, junk): diff --git a/testtools/twistedsupport/_spinner.py b/testtools/twistedsupport/_spinner.py index 41520bbb..e0904204 100644 --- a/testtools/twistedsupport/_spinner.py +++ b/testtools/twistedsupport/_spinner.py @@ -8,25 +8,25 @@ __all__ = [ "NoResultError", - "not_reentrant", "ReentryError", "Spinner", "StaleJunkError", "TimeoutError", + "not_reentrant", "trap_unhandled_errors", ] -from fixtures import Fixture import signal -from typing import Union - -from ._deferreddebug import DebugTwisted +from typing import ClassVar, Union +from fixtures import Fixture from twisted.internet import defer from twisted.internet.interfaces import IReactorThreads from twisted.python.failure import Failure from twisted.python.util import mergeFunctionMetadata +from ._deferreddebug import DebugTwisted + class ReentryError(Exception): """Raised when we try to re-enter a function that forbids it.""" @@ -34,8 +34,7 @@ class ReentryError(Exception): def __init__(self, function): Exception.__init__( self, - "%r in not re-entrant but was called within a call to itself." - % (function,), + f"{function!r} in not re-entrant but was called within a call to itself.", ) @@ -130,7 +129,7 @@ def __init__(self, junk): Exception.__init__( self, "There was junk in the spinner from a previous run. " - "Use clear_junk() to clear it out: %r" % (junk,), + f"Use clear_junk() to clear it out: {junk!r}", ) @@ -146,7 +145,7 @@ class Spinner: _UNSET = object() # Signals that we save and restore for each spin. - _PRESERVED_SIGNALS = [ + _PRESERVED_SIGNALS: ClassVar[list] = [ "SIGINT", "SIGTERM", "SIGCHLD", @@ -187,7 +186,7 @@ def _get_result(self): self._failure.raiseException() # type: ignore if self._success is not self._UNSET: return self._success - raise NoResultError() + raise NoResultError def _got_failure(self, result): self._cancel_timeout()