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