Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
5 changes: 2 additions & 3 deletions scripts/_lp_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
35 changes: 17 additions & 18 deletions testtools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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,
Expand Down Expand Up @@ -90,8 +89,8 @@
TimestampingStreamResult,
)
from testtools.testsuite import (
ConcurrentTestSuite,
ConcurrentStreamTestSuite,
ConcurrentTestSuite,
FixtureSuite,
iterate_tests,
)
Expand Down
8 changes: 4 additions & 4 deletions testtools/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()):
Expand Down Expand Up @@ -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:
Expand Down
20 changes: 9 additions & 11 deletions testtools/content.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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):
Expand All @@ -110,8 +107,9 @@ def _iter_text(self):
yield final

def __repr__(self):
return "<Content type={!r}, value={!r}>".format(
self.content_type, _join_b(self.iter_bytes())
return (
f"<Content type={self.content_type!r}, "
f"value={_join_b(self.iter_bytes())!r}>"
)


Expand Down Expand Up @@ -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")])

Expand Down
4 changes: 1 addition & 3 deletions testtools/content_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
Expand Down
18 changes: 9 additions & 9 deletions testtools/matchers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
"Always",
"Annotate",
"AnyMatch",
"ContainedByDict",
"Contains",
"ContainsAll",
"ContainedByDict",
"ContainsDict",
"DirContains",
"DirExists",
Expand Down Expand Up @@ -48,17 +48,17 @@
"MatchesSetwise",
"MatchesStructure",
"Never",
"NotEquals",
"Not",
"NotEquals",
"PathExists",
"Raises",
"raises",
"SameMembers",
"SamePath",
"StartsWith",
"TarballContains",
"Warnings",
"WarningMessage",
"Warnings",
"raises",
]

from ._basic import (
Expand Down Expand Up @@ -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.
Expand All @@ -133,3 +128,8 @@
Mismatch,
MismatchError,
)
from ._warnings import (
IsDeprecated,
WarningMessage,
Warnings,
)
32 changes: 18 additions & 14 deletions testtools/matchers/_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
]

import operator
from pprint import pformat
import re
from pprint import pformat

from ..compat import (
text_repr,
Expand All @@ -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)):
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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),
)
Expand All @@ -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)}."
)


Expand Down Expand Up @@ -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)}."
)


Expand Down Expand Up @@ -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}"


Expand Down Expand Up @@ -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))
Expand Down
Loading