Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

unittest.case: Further stub improvements #8077

Merged
merged 10 commits into from
Jun 20, 2022
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
22 changes: 12 additions & 10 deletions stdlib/_typeshed/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -50,21 +50,23 @@ class SupportsAnext(Protocol[_T_co]):

# Comparison protocols

class SupportsDunderLT(Protocol):
def __lt__(self, __other: Any) -> bool: ...
class SupportsDunderLT(Protocol[_T_contra]):
def __lt__(self, __other: _T_contra) -> bool: ...

class SupportsDunderGT(Protocol):
def __gt__(self, __other: Any) -> bool: ...
class SupportsDunderGT(Protocol[_T_contra]):
def __gt__(self, __other: _T_contra) -> bool: ...

class SupportsDunderLE(Protocol):
def __le__(self, __other: Any) -> bool: ...
class SupportsDunderLE(Protocol[_T_contra]):
def __le__(self, __other: _T_contra) -> bool: ...

class SupportsDunderGE(Protocol):
def __ge__(self, __other: Any) -> bool: ...
class SupportsDunderGE(Protocol[_T_contra]):
def __ge__(self, __other: _T_contra) -> bool: ...

class SupportsAllComparisons(SupportsDunderLT, SupportsDunderGT, SupportsDunderLE, SupportsDunderGE, Protocol): ...
class SupportsAllComparisons(
SupportsDunderLT[Any], SupportsDunderGT[Any], SupportsDunderLE[Any], SupportsDunderGE[Any], Protocol
): ...

SupportsRichComparison: TypeAlias = SupportsDunderLT | SupportsDunderGT
SupportsRichComparison: TypeAlias = SupportsDunderLT[Any] | SupportsDunderGT[Any]
SupportsRichComparisonT = TypeVar("SupportsRichComparisonT", bound=SupportsRichComparison) # noqa: Y001

# Dunder protocols
Expand Down
93 changes: 45 additions & 48 deletions stdlib/unittest/case.pyi
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging
import sys
import unittest.result
from _typeshed import Self, SupportsDunderGE, SupportsSub
from _typeshed import Self, SupportsDunderGE, SupportsDunderGT, SupportsDunderLE, SupportsDunderLT, SupportsSub
from collections.abc import Callable, Container, Iterable, Mapping, Sequence, Set as AbstractSet
from contextlib import AbstractContextManager
from types import TracebackType
Expand All @@ -17,14 +17,18 @@ from typing import (
SupportsAbs,
SupportsRound,
TypeVar,
Union,
overload,
)
from typing_extensions import ParamSpec
from typing_extensions import ParamSpec, TypeAlias
from warnings import WarningMessage

if sys.version_info >= (3, 9):
from types import GenericAlias

if sys.version_info >= (3, 10):
from types import UnionType

_T = TypeVar("_T")
_S = TypeVar("_S", bound=SupportsSub[Any, Any])
_E = TypeVar("_E", bound=BaseException)
Expand Down Expand Up @@ -75,7 +79,12 @@ def skipUnless(condition: object, reason: str) -> Callable[[_FT], _FT]: ...
class SkipTest(Exception):
def __init__(self, reason: str) -> None: ...

class _SupportsAbsAndDunderGE(SupportsDunderGE, SupportsAbs[Any], Protocol): ...
class _SupportsAbsAndDunderGE(SupportsDunderGE[Any], SupportsAbs[Any], Protocol): ...

if sys.version_info >= (3, 10):
_IsInstanceClassInfo: TypeAlias = Union[type, UnionType, tuple[type | UnionType | tuple[Any, ...], ...]]
else:
_IsInstanceClassInfo: TypeAlias = Union[type, tuple[type | tuple[Any, ...], ...]]

class TestCase:
failureException: type[BaseException]
Expand Down Expand Up @@ -105,18 +114,30 @@ class TestCase:
def assertNotEqual(self, first: Any, second: Any, msg: Any = ...) -> None: ...
def assertTrue(self, expr: Any, msg: Any = ...) -> None: ...
def assertFalse(self, expr: Any, msg: Any = ...) -> None: ...
def assertIs(self, expr1: Any, expr2: Any, msg: Any = ...) -> None: ...
def assertIsNot(self, expr1: Any, expr2: Any, msg: Any = ...) -> None: ...
def assertIsNone(self, obj: Any, msg: Any = ...) -> None: ...
def assertIsNotNone(self, obj: Any, msg: Any = ...) -> None: ...
def assertIs(self, expr1: object, expr2: object, msg: Any = ...) -> None: ...
def assertIsNot(self, expr1: object, expr2: object, msg: Any = ...) -> None: ...
def assertIsNone(self, obj: object, msg: Any = ...) -> None: ...
def assertIsNotNone(self, obj: object, msg: Any = ...) -> None: ...
def assertIn(self, member: Any, container: Iterable[Any] | Container[Any], msg: Any = ...) -> None: ...
def assertNotIn(self, member: Any, container: Iterable[Any] | Container[Any], msg: Any = ...) -> None: ...
def assertIsInstance(self, obj: Any, cls: type | tuple[type, ...], msg: Any = ...) -> None: ...
def assertNotIsInstance(self, obj: Any, cls: type | tuple[type, ...], msg: Any = ...) -> None: ...
def assertGreater(self, a: Any, b: Any, msg: Any = ...) -> None: ...
def assertGreaterEqual(self, a: Any, b: Any, msg: Any = ...) -> None: ...
def assertLess(self, a: Any, b: Any, msg: Any = ...) -> None: ...
def assertLessEqual(self, a: Any, b: Any, msg: Any = ...) -> None: ...
def assertIsInstance(self, obj: object, cls: _IsInstanceClassInfo, msg: Any = ...) -> None: ...
def assertNotIsInstance(self, obj: object, cls: _IsInstanceClassInfo, msg: Any = ...) -> None: ...
@overload
def assertGreater(self, a: SupportsDunderGT[_T], b: _T, msg: Any = ...) -> None: ...
@overload
def assertGreater(self, a: _T, b: SupportsDunderLT[_T], msg: Any = ...) -> None: ...
@overload
def assertGreaterEqual(self, a: SupportsDunderGE[_T], b: _T, msg: Any = ...) -> None: ...
@overload
def assertGreaterEqual(self, a: _T, b: SupportsDunderLE[_T], msg: Any = ...) -> None: ...
@overload
def assertLess(self, a: SupportsDunderLT[_T], b: _T, msg: Any = ...) -> None: ...
@overload
def assertLess(self, a: _T, b: SupportsDunderGT[_T], msg: Any = ...) -> None: ...
@overload
def assertLessEqual(self, a: SupportsDunderLT[_T], b: _T, msg: Any = ...) -> None: ...
@overload
def assertLessEqual(self, a: _T, b: SupportsDunderGT[_T], msg: Any = ...) -> None: ...
# `assertRaises`, `assertRaisesRegex`, and `assertRaisesRegexp`
# are not using `ParamSpec` intentionally,
# because they might be used with explicitly wrong arg types to raise some error in tests.
Expand Down Expand Up @@ -249,45 +270,21 @@ class TestCase:
def _formatMessage(self, msg: str | None, standardMsg: str) -> str: ... # undocumented
def _getAssertEqualityFunc(self, first: Any, second: Any) -> Callable[..., None]: ... # undocumented
if sys.version_info < (3, 12):
def failUnlessEqual(self, first: Any, second: Any, msg: Any = ...) -> None: ...
def assertEquals(self, first: Any, second: Any, msg: Any = ...) -> None: ...
def failIfEqual(self, first: Any, second: Any, msg: Any = ...) -> None: ...
def assertNotEquals(self, first: Any, second: Any, msg: Any = ...) -> None: ...
def failUnless(self, expr: bool, msg: Any = ...) -> None: ...
def assert_(self, expr: bool, msg: Any = ...) -> None: ...
def failIf(self, expr: bool, msg: Any = ...) -> None: ...
@overload
def failUnlessRaises( # type: ignore[misc]
self,
exception: type[BaseException] | tuple[type[BaseException], ...],
callable: Callable[_P, object] = ...,
*args: _P.args,
**kwargs: _P.kwargs,
) -> None: ...
@overload
def failUnlessRaises(self, exception: type[_E] | tuple[type[_E], ...], msg: Any = ...) -> _AssertRaisesContext[_E]: ...
failUnlessEqual = assertEqual
assertEquals = assertEqual
failIfEqual = assertNotEqual
assertNotEquals = assertNotEqual
failUnless = assertTrue
assert_ = assertTrue
failIf = assertFalse
failUnlessRaises = assertRaises
failUnlessAlmostEqual = assertAlmostEqual
assertAlmostEquals = assertAlmostEqual
failIfAlmostEqual = assertNotAlmostEqual
assertNotAlmostEquals = assertNotAlmostEqual
def assertRegexpMatches(self, text: AnyStr, regex: AnyStr | Pattern[AnyStr], msg: Any = ...) -> None: ...
def assertNotRegexpMatches(self, text: AnyStr, regex: AnyStr | Pattern[AnyStr], msg: Any = ...) -> None: ...
@overload
def assertRaisesRegexp( # type: ignore[misc]
self,
exception: type[BaseException] | tuple[type[BaseException], ...],
expected_regex: str | bytes | Pattern[str] | Pattern[bytes],
callable: Callable[..., object],
*args: Any,
**kwargs: Any,
) -> None: ...
@overload
def assertRaisesRegexp(
self,
exception: type[_E] | tuple[type[_E], ...],
expected_regex: str | bytes | Pattern[str] | Pattern[bytes],
msg: Any = ...,
) -> _AssertRaisesContext[_E]: ...
assertRegexpMatches = assertRegex
assertNotRegexpMatches = assertNotRegex
assertRaisesRegexp = assertRaisesRegex
def assertDictContainsSubset(
self, subset: Mapping[Any, Any], dictionary: Mapping[Any, Any], msg: object = ...
) -> None: ...
Expand Down
51 changes: 51 additions & 0 deletions test_cases/stdlib/test_unittest.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@

case = unittest.TestCase()

###
# Tests for assertAlmostEqual
###

case.assertAlmostEqual(2.4, 2.41)
case.assertAlmostEqual(Fraction(49, 50), Fraction(48, 50))
case.assertAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1), delta=timedelta(hours=1))
Expand All @@ -20,10 +24,57 @@
case.assertAlmostEqual("foo", "bar") # type: ignore
case.assertAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) # type: ignore

###
# Tests for assertNotAlmostEqual
###

case.assertNotAlmostEqual(Fraction(49, 50), Fraction(48, 50))
case.assertNotAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1), delta=timedelta(hours=1))
case.assertNotAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1), None, "foo", timedelta(hours=1))

case.assertNotAlmostEqual(2.4, 2.41, places=9, delta=0.02) # type: ignore
case.assertNotAlmostEqual("foo", "bar") # type: ignore
case.assertNotAlmostEqual(datetime(1999, 1, 2), datetime(1999, 1, 2, microsecond=1)) # type: ignore

###
# Tests for assertGreater
###


class Spam:
def __lt__(self, other: object) -> bool:
return True


class Eggs:
def __gt__(self, other: object) -> bool:
return True


class Ham:
def __lt__(self, other: "Ham") -> bool:
if not isinstance(other, Ham):
return NotImplemented
return True


class Bacon:
def __gt__(self, other: "Bacon") -> bool:
if not isinstance(other, Bacon):
return NotImplemented
return True


case.assertGreater(5.8, 3)
case.assertGreater(Decimal("4.5"), Fraction(3, 2))
case.assertGreater(Fraction(3, 2), 0.9)
case.assertGreater(Eggs(), object())
case.assertGreater(object(), Spam())
case.assertGreater(Ham(), Ham())
case.assertGreater(Bacon(), Bacon())

case.assertGreater(object(), object()) # type: ignore
case.assertGreater(datetime(1999, 1, 2), 1) # type: ignore
case.assertGreater(Spam(), Eggs()) # type: ignore
case.assertGreater(Ham(), Bacon()) # type: ignore
case.assertGreater(Bacon(), Ham()) # type: ignore