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
5 changes: 2 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: [3.8, 3.9, "3.10", 3.11, 3.12, pypy3.9, pypy3.10]
python-version: ["3.9", "3.10", "3.11", "3.12", "pypy3.9", "pypy3.10"]

steps:
- uses: actions/checkout@v4
Expand All @@ -32,8 +32,7 @@ jobs:

- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install --upgrade setuptools wheel setuptools_scm
python -m pip install --upgrade pip wheel
python -m pip install sphinx
python -m pip install ".[test,twisted,dev]"

Expand Down
31 changes: 31 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,37 @@ testtools NEWS

Changes and improvements to testtools_, grouped by release.

2.8.0
~~~~~

Changes
-------

* Drop support for Python 3.8. (Stephen Finucane)

* Remove a number of deprecated classes and methods. (Stephen Finucane)

* ``testtools.matchers``

* ``AfterPreproccessing`` (use ``AfterPreprocessing``)

* ``testtools.testcase``

* ``TestSkipped`` (use ``unittest.SkipTest``)
* ``TestCase.skip`` (use ``TestCase.skipTest``)
* ``TestCase.failUnlessEqual`` (use ``TestCase.assertEqual``)
* ``TestCase.assertEquals`` (use ``TestCase.assertEqual``)
* ``TestCase.failUnlessRaises`` (use ``TestCase.assertRaises``)
* ``TestCase.assertItemsEqual`` (use ``TestCase.assertCountEqual``)

* ``testtools.testresult.real``

* ``domap`` (no replacement)

* ``testtools.twistedsupport._runtest``

* ``run_with_log_observers`` (no replacement)

2.7.2
~~~~~

Expand Down
48 changes: 18 additions & 30 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,18 +1,5 @@
[tool.mypy]
warn_redundant_casts = true
warn_unused_configs = true
check_untyped_defs = true

[[tool.mypy.overrides]]
module = [
"fixtures.*",
"testresources.*",
"testscenarios.*",
]
ignore_missing_imports = true

[build-system]
requires = ["setuptools>=61", "hatchling", "hatch_vcs"]
requires = ["hatchling", "hatch_vcs"]
build-backend = "hatchling.build"

[project]
Expand All @@ -27,7 +14,6 @@ classifiers = [
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
Expand All @@ -39,22 +25,16 @@ classifiers = [
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Software Development :: Testing",
]
dependencies = ["setuptools; python_version>='3.12'"]
dynamic = ["version"]
requires-python = ">=3.8"
requires-python = ">=3.9"

[project.urls]
Homepage = "https://github.com/testing-cabal/testtools"

[tool.setuptools]
include-package-data = false

[tool.setuptools.packages.find]
include = ["testtools*"]
exclude = ["man*"]

[tool.files]
packages = "testtools"
[project.optional-dependencies]
test = ["testscenarios", "testresources"]
twisted = ["Twisted", "fixtures"]
dev = ["ruff==0.11.2"]

[tool.hatch.version]
source = "vcs"
Expand All @@ -68,7 +48,15 @@ version = {version!r}
__version__ = {version_tuple!r}
"""

[project.optional-dependencies]
test = ["testscenarios", "testresources"]
twisted = ["Twisted", "fixtures"]
dev = ["ruff==0.11.2"]
[tool.mypy]
warn_redundant_casts = true
warn_unused_configs = true
check_untyped_defs = true

[[tool.mypy.overrides]]
module = [
"fixtures.*",
"testresources.*",
"testscenarios.*",
]
ignore_missing_imports = true
4 changes: 0 additions & 4 deletions setup.py

This file was deleted.

54 changes: 36 additions & 18 deletions testtools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,32 @@
iterate_tests,
)


def __get_git_version():
import os
import subprocess

cwd = os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir)

try:
out = subprocess.check_output(
["git", "describe"], stderr=subprocess.STDOUT, cwd=cwd
)
except (OSError, subprocess.CalledProcessError):
return None

try:
version = out.strip().decode("utf-8")
except UnicodeDecodeError:
return None

if "-" in version: # after tag
# convert version-N-githash to version.postN+githash
return version.replace("-", ".post", 1).replace("-g", "+git", 1)
else:
return version


# same format as sys.version_info: "A tuple containing the five components of
# the version number: major, minor, micro, releaselevel, and serial. All
# values except releaselevel are integers; the release level is 'alpha',
Expand All @@ -109,21 +135,13 @@
# Otherwise it is major.minor.micro~$(revno).

try:
# If setuptools_scm is installed (e.g. in a development environment with
# an editable install), then use it to determine the version dynamically.
from setuptools_scm import get_version

# This will fail with LookupError if the package is not installed in
# editable mode or if Git is not installed.
version = get_version(root="..", relative_to=__file__)
__version__ = tuple([int(v) if v.isdigit() else v for v in version.split(".")])
except (ImportError, LookupError):
# As a fallback, use the version that is hard-coded in the file.
try:
from ._version import __version__, version
except ModuleNotFoundError:
# The user is probably trying to run this without having installed
# the package, so complain.
raise RuntimeError(
"Testtools is not correctly installed. Please install it with pip."
)
from ._version import __version__, version
except ModuleNotFoundError:
# package is not installed
if version := __get_git_version():
# we're in a git repo
__version__ = tuple([int(v) if v.isdigit() else v for v in version.split(".")])
else:
# we're working with a tarball or similar
version = "0.0.0"
__version__ = (0, 0, 0)
19 changes: 0 additions & 19 deletions testtools/matchers/_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import operator
from pprint import pformat
import re
import warnings

from ..compat import (
text_repr,
Expand Down Expand Up @@ -71,24 +70,6 @@ def __init__(self, actual, mismatch_string, reference, reference_on_right=True):
self._reference = reference
self._reference_on_right = reference_on_right

@property
def expected(self):
warnings.warn(
f"{self.__class__.__name__}.expected deprecated after 1.8.1",
DeprecationWarning,
stacklevel=2,
)
return self._reference

@property
def other(self):
warnings.warn(
f"{self.__class__.__name__}.other deprecated after 1.8.1",
DeprecationWarning,
stacklevel=2,
)
return self._actual

def describe(self):
actual = repr(self._actual)
reference = repr(self._reference)
Expand Down
4 changes: 2 additions & 2 deletions testtools/matchers/_exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import sys

from ._basic import MatchesRegex
from ._higherorder import AfterPreproccessing
from ._higherorder import AfterPreprocessing
from ._impl import (
Matcher,
Mismatch,
Expand Down Expand Up @@ -47,7 +47,7 @@ def __init__(self, exception, value_re=None):
Matcher.__init__(self)
self.expected = exception
if isinstance(value_re, str):
value_re = AfterPreproccessing(str, MatchesRegex(value_re), False)
value_re = AfterPreprocessing(str, MatchesRegex(value_re), False)
self.value_re = value_re
expected_type = type(self.expected)
self._is_instance = not any(
Expand Down
5 changes: 0 additions & 5 deletions testtools/matchers/_higherorder.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,11 +212,6 @@ def match(self, value):
return matcher.match(after)


# This is the old, deprecated. spelling of the name, kept for backwards
# compatibility.
AfterPreproccessing = AfterPreprocessing


class AllMatch:
"""Matches if all provided values match the given matcher."""

Expand Down
45 changes: 4 additions & 41 deletions testtools/testcase.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import types
import unittest
from unittest.case import SkipTest
import warnings

from testtools.compat import reraise
from testtools import content
Expand Down Expand Up @@ -50,18 +49,6 @@
)


class TestSkipped(SkipTest):
"""Raised within TestCase.run() when a test is skipped."""

def __init__(self, *args, **kwargs):
warnings.warn(
"Use SkipTest from unittest instead.",
DeprecationWarning,
stacklevel=2,
)
super().__init__(*args, **kwargs)


class _UnexpectedSuccess(Exception):
"""An unexpected success was raised.

Expand Down Expand Up @@ -281,8 +268,10 @@ def _reset(self):

def __eq__(self, other):
eq = getattr(unittest.TestCase, "__eq__", None)
if eq is not None and not unittest.TestCase.__eq__(self, other):
return False
if eq is not None:
eq_ = unittest.TestCase.__eq__(self, other)
if eq_ is NotImplemented or not eq_:
return False
return self.__dict__ == getattr(other, "__dict__", None)

# We need to explicitly set this since we're overriding __eq__
Expand All @@ -293,17 +282,6 @@ def __repr__(self):
# We add id to the repr because it makes testing testtools easier.
return f"<{self.id()} id=0x{id(self):0x}>"

def _deprecate(original_func):
def deprecated_func(*args, **kwargs):
warnings.warn(
"Please use {0} instead.".format(original_func.__name__),
DeprecationWarning,
stacklevel=2,
)
return original_func(*args, **kwargs)

return deprecated_func

def addDetail(self, name, content_object):
"""Add a detail to be reported with this test's outcome.

Expand Down Expand Up @@ -355,15 +333,6 @@ def skipTest(self, reason):
"""
raise self.skipException(reason)

def skip(self, reason):
"""DEPRECATED: Use skipTest instead."""
warnings.warn(
"Only valid in 1.8.1 and earlier. Use skipTest instead.",
DeprecationWarning,
stacklevel=2,
)
self.skipTest(reason)

def _formatTypes(self, classOrIterable):
"""Format a class or a bunch of classes for display in an error."""
className = getattr(classOrIterable, "__name__", None)
Expand Down Expand Up @@ -418,8 +387,6 @@ def assertEqual(self, expected, observed, message=""):
matcher = _FlippedEquals(expected)
self.assertThat(observed, matcher, message)

failUnlessEqual = assertEquals = _deprecate(assertEqual)

def assertIn(self, needle, haystack, message=""):
"""Assert that needle is in haystack."""
self.assertThat(haystack, Contains(needle), message)
Expand Down Expand Up @@ -495,8 +462,6 @@ def match(self, matchee):
self.assertThat(our_callable, matcher)
return capture.matchee

failUnlessRaises = _deprecate(assertRaises)

def assertThat(self, matchee, matcher, message="", verbose=False):
"""Assert that matchee is matched by matcher.

Expand All @@ -508,8 +473,6 @@ def assertThat(self, matchee, matcher, message="", verbose=False):
if mismatch_error is not None:
raise mismatch_error

assertItemsEqual = _deprecate(unittest.TestCase.assertCountEqual)

def addDetailUniqueName(self, name, content_object):
"""Add a detail to the test, but ensure it's name is unique.

Expand Down
16 changes: 0 additions & 16 deletions testtools/testresult/real.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
from operator import methodcaller
import sys
import unittest
import warnings

from testtools.compat import _b
from testtools.content import (
Expand Down Expand Up @@ -436,21 +435,6 @@ def status(
"""


def domap(function, *sequences):
"""A strict version of 'map' that's guaranteed to run on all inputs.
DEPRECATED since testtools 1.8.1: Internal code should use _strict_map.
External code should look for other solutions for their strict mapping
needs.
"""
warnings.warn(
"domap deprecated since 1.8.1. Please implement your own strict map.",
DeprecationWarning,
stacklevel=2,
)
return _strict_map(function, *sequences)


def _strict_map(function, *sequences):
return list(map(function, *sequences))

Expand Down
Loading