diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4a97af8..9a0201f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,12 +11,12 @@ on: jobs: test: - name: test py${{ matrix.python-version }} on ${{ matrix.os }} + name: test py-${{ matrix.python-version }} on ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu, macos] - python-version: ['3.7', '3.8', '3.9', '3.10'] + python-version: ['3.7', '3.8', '3.9', '3.10', 'pypy-3.7', 'pypy-3.8', 'pypy-3.9'] runs-on: ${{ matrix.os }}-latest @@ -39,10 +39,10 @@ jobs: key: ${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-${{ hashFiles('tests/requirements.txt') }} - run: pip install -r tests/requirements.txt - if: steps.cache.outputs.cache-hit != 'true' +# if: steps.cache.outputs.cache-hit != 'true' # breaks pypy tests - run: poetry install - if: steps.cache.outputs.cache-hit != 'true' +# if: steps.cache.outputs.cache-hit != 'true' # breaks pypy tests - run: make test diff --git a/dirty_equals/_dict.py b/dirty_equals/_dict.py index 0731279..965d7eb 100644 --- a/dirty_equals/_dict.py +++ b/dirty_equals/_dict.py @@ -121,7 +121,8 @@ def _filter_dict(self, d: Dict[Any, Any]) -> Dict[Any, Any]: return {k: v for k, v in d.items() if not self._ignore_value(v)} def _ignore_value(self, v: Any) -> bool: - if isinstance(v, (DirtyEquals, DirtyEqualsMeta)): + # `isinstance(v, (DirtyEquals, DirtyEqualsMeta))` seems to always return `True` on pypy, no idea why + if type(v) in (DirtyEquals, DirtyEqualsMeta): return False elif callable(self.ignore): return self.ignore(v) diff --git a/docs/usage.md b/docs/usage.md index 4506d63..260ab49 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -23,6 +23,10 @@ assert ['a', 'b', 'c'] == ~Contains('z') ## Initialised vs. Class comparison +!!! warning + + This does not work with PyPy. + *dirty-equals* allows comparison with types regardless of whether they've been initialised. This saves users adding `()` in lots of places. diff --git a/tests/test_base.py b/tests/test_base.py index c0163b6..f411bed 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -1,3 +1,5 @@ +import platform + import pytest from dirty_equals import Contains, IsApprox, IsInt, IsNegative, IsOneOf, IsPositive, IsStr @@ -58,6 +60,7 @@ def test_dict_compare(): assert v == {'foo': IsInt() & IsApprox(1), 'bar': IsPositive() | IsNegative(), 'spam': ~IsStr()} +@pytest.mark.skipif(platform.python_implementation() == 'PyPy', reason='PyPy does not metaclass dunder methods') def test_not_repr(): v = ~IsInt assert str(v) == '~IsInt' @@ -68,6 +71,16 @@ def test_not_repr(): assert str(v) == '~IsInt' +def test_not_repr_instance(): + v = ~IsInt() + assert str(v) == '~IsInt()' + + with pytest.raises(AssertionError): + assert 1 == v + + assert str(v) == '~IsInt()' + + def test_repr(): v = ~IsInt assert str(v) == '~IsInt' diff --git a/tests/test_boolean.py b/tests/test_boolean.py index a8946b5..d89b601 100644 --- a/tests/test_boolean.py +++ b/tests/test_boolean.py @@ -1,3 +1,5 @@ +import platform + import pytest from dirty_equals import IsFalseLike, IsTrueLike @@ -43,11 +45,17 @@ def test_is_false_like_repr(): assert repr(IsFalseLike(allow_strings=True)) == 'IsFalseLike(allow_strings=True)' +@pytest.mark.skipif(platform.python_implementation() == 'PyPy', reason='PyPy does not metaclass dunder methods') def test_dirty_not_equals(): with pytest.raises(AssertionError): assert 0 != IsFalseLike +def test_dirty_not_equals_instance(): + with pytest.raises(AssertionError): + assert 0 != IsFalseLike() + + def test_invalid_initialization(): with pytest.raises(TypeError, match='takes 1 positional argument but 2 were given'): IsFalseLike(True) diff --git a/tests/test_docs.py b/tests/test_docs.py index cb325b4..3114b40 100644 --- a/tests/test_docs.py +++ b/tests/test_docs.py @@ -1,4 +1,5 @@ import importlib.util +import platform import re from pathlib import Path from textwrap import dedent @@ -65,5 +66,6 @@ def pytest_generate_tests(metafunc): metafunc.parametrize('module_name,source_code', generate_code_chunks('dirty_equals', 'docs')) +@pytest.mark.skipif(platform.python_implementation() == 'PyPy', reason='PyPy does not metaclass dunder methods') def test_docs_examples(module_name, source_code, import_execute): import_execute(module_name, source_code, True) diff --git a/tests/test_inspection.py b/tests/test_inspection.py index eb791f4..2698cb0 100644 --- a/tests/test_inspection.py +++ b/tests/test_inspection.py @@ -74,8 +74,8 @@ def test_has_name(value, dirty): 'value,dirty', [ (Foo(1, 2), HasAttributes(a=1, b=2)), - (Foo(1, 's'), HasAttributes(a=IsInt, b=IsStr)), - (Foo(1, 2), ~HasAttributes(a=IsInt, b=IsStr)), + (Foo(1, 's'), HasAttributes(a=IsInt(), b=IsStr())), + (Foo(1, 2), ~HasAttributes(a=IsInt(), b=IsStr())), (Foo(1, 2), ~HasAttributes(a=1, b=2, c=3)), (Foo(1, 2), HasAttributes(a=1, b=2, spam=AnyThing)), (Foo(1, 2), ~HasAttributes(a=1, b=2, missing=AnyThing)),