Skip to content
Open
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
13 changes: 0 additions & 13 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,23 +75,10 @@ module = [
]
ignore_missing_imports = true

[[tool.mypy.overrides]]
module = "fixtures.*"
ignore_missing_imports = true
follow_imports = "skip"

[[tool.mypy.overrides]]
module = [
# FIXME(stephenfin): We would like to remove all modules from this list
# except tests (we're not sadists)
"testtools.assertions",
"testtools.compat",
"testtools.matchers.*",
"testtools.monkey",
"testtools.run",
"testtools.runtest",
"testtools.testcase",
"testtools.testresult.*",
"testtools.twistedsupport.*",
"tests.*",
]
Expand Down
2 changes: 2 additions & 0 deletions tests/matchers/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class MatcherTestProtocol(Protocol):
class TestMatchersInterface:
"""Mixin class that provides test methods for matcher interfaces."""

__test__ = False # Tell pytest not to collect this as a test class

def test_matches_match(self: MatcherTestProtocol) -> None:
matcher = self.matches_matcher
matches = self.matches_matches
Expand Down
4 changes: 4 additions & 0 deletions tests/test_testresult.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ def make_exception_info(exceptionFactory, *args, **kwargs):
class TestControlContract:
"""Stopping test runs."""

__test__ = False # Tell pytest not to collect this as a test class

# These are provided by the class that uses this mixin
makeResult: Any
assertFalse: Any
Expand Down Expand Up @@ -573,6 +575,8 @@ def makeResult(self):


class TestStreamResultContract:
__test__ = False # Tell pytest not to collect this as a test class

# These are provided by the class that uses this mixin
addCleanup: Any

Expand Down
9 changes: 8 additions & 1 deletion testtools/assertions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@

"""Assertion helpers."""

from typing import TypeVar

from testtools.matchers import (
Annotate,
Matcher,
MismatchError,
)

T = TypeVar("T")


def assert_that(matchee, matcher, message="", verbose=False):
def assert_that(
matchee: T, matcher: Matcher[T], message: str = "", verbose: bool = False
) -> None:
"""Assert that matchee is matched by matcher.

This should only be used when you need to use a function based
Expand Down
31 changes: 16 additions & 15 deletions testtools/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
import sys
import unicodedata
from io import BytesIO, StringIO # for backwards-compat
from typing import IO


def _slow_escape(text):
def _slow_escape(text: str) -> str:
"""Escape unicode ``text`` leaving printable characters unmodified

The behaviour emulates the Python 3 implementation of repr, see
Expand All @@ -26,7 +27,7 @@ def _slow_escape(text):
does not handle astral characters correctly on Python builds with 16 bit
rather than 32 bit unicode type.
"""
output = []
output: list[str | bytes] = []
for c in text:
o = ord(c)
if o < 256:
Expand All @@ -43,14 +44,14 @@ def _slow_escape(text):
output.append(c.encode("unicode-escape"))
else:
output.append(c)
return "".join(output)
return "".join(output) # type: ignore[arg-type]


def text_repr(text, multiline=None):
def text_repr(text: str | bytes, multiline: bool | None = None) -> str:
"""Rich repr for ``text`` returning unicode, triple quoted if ``multiline``."""
nl = (isinstance(text, bytes) and bytes((0xA,))) or "\n"
if multiline is None:
multiline = nl in text
multiline = nl in text # type: ignore[operator]
if not multiline:
# Use normal repr for single line of unicode
return repr(text)
Expand All @@ -60,7 +61,7 @@ def text_repr(text, multiline=None):
# making sure that quotes are not escaped.
offset = len(prefix) + 1
lines = []
for line in text.split(nl):
for line in text.split(nl): # type: ignore[arg-type]
r = repr(line)
q = r[-1]
lines.append(r[offset:-1].replace("\\" + q, q))
Expand All @@ -87,7 +88,7 @@ def text_repr(text, multiline=None):
return "".join([prefix, quote, escaped_text, quote])


def unicode_output_stream(stream):
def unicode_output_stream(stream: IO[str]) -> IO[str]:
"""Get wrapper for given stream that writes any unicode without exception

Characters that can't be coerced to the encoding of the stream, or 'ascii'
Expand All @@ -103,21 +104,21 @@ def unicode_output_stream(stream):
# attribute).
return stream
try:
writer = codecs.getwriter(stream.encoding or "")
writer = codecs.getwriter(stream.encoding or "") # type: ignore[attr-defined]
except (AttributeError, LookupError):
return codecs.getwriter("ascii")(stream, "replace")
return codecs.getwriter("ascii")(stream, "replace") # type: ignore[arg-type, return-value]
if writer.__module__.rsplit(".", 1)[1].startswith("utf"):
# The current stream has a unicode encoding so no error handler is needed
return stream
# Python 3 doesn't seem to make this easy, handle a common case
try:
return stream.__class__(
stream.buffer,
stream.encoding,
return stream.__class__( # type: ignore[call-arg, return-value]
stream.buffer, # type: ignore[attr-defined]
stream.encoding, # type: ignore[attr-defined]
"replace",
stream.newlines,
stream.line_buffering,
stream.newlines, # type: ignore[attr-defined]
stream.line_buffering, # type: ignore[attr-defined]
)
except AttributeError:
pass
return writer(stream, "replace")
return writer(stream, "replace") # type: ignore[arg-type, return-value]
6 changes: 5 additions & 1 deletion testtools/content.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,8 @@ class TracebackContent(Content):

def __init__(
self,
err: tuple[type[BaseException], BaseException, types.TracebackType | None],
err: tuple[type[BaseException], BaseException, types.TracebackType | None]
| tuple[None, None, None],
test: _TestCase | None,
capture_locals: bool = False,
) -> None:
Expand All @@ -211,6 +212,9 @@ def __init__(
raise ValueError("err may not be None")

exctype, value, tb = err
# Ensure we have a real exception, not the (None, None, None) variant
assert exctype is not None, "exctype must not be None"
assert value is not None, "value must not be None"
# Skip test runner traceback levels
if StackLinesContent.HIDE_INTERNAL_STACK:
while tb and "__unittest" in tb.tb_frame.f_globals:
Expand Down
Loading
Loading