annotated-types / annotated-types

Reusable constraint types to use with typing.Annotated
MIT License
455 stars 17 forks source link

Test regression on PyPy3.10: `FAILED tests/test_main.py::test_predicate_repr[pred2-Predicate(math.isfinite)] - AssertionError: assert 'Predicate(isfinite)' == 'Predicate(math.isfinite)'` #71

Open mgorny opened 1 month ago

mgorny commented 1 month ago

The 0.7.0 release introduces a test regression on PyPy3.10:

$ pypy3.10 -m pytest
========================================================= test session starts =========================================================
platform linux -- Python 3.10.14[pypy-7.3.16-final], pytest-8.2.0, pluggy-1.5.0
rootdir: /tmp/annotated-types
configfile: pyproject.toml
testpaths: tests
plugins: anyio-4.3.0, subtests-0.12.1, asyncio-0.23.6, xprocess-1.0.1, xdist-3.6.1
asyncio: mode=strict
collected 256 items                                                                                                                   

tests/test_grouped_metadata.py ..                                                                                               [  0%]
tests/test_main.py ............................................................................................................ [ 42%]
............................................................................................................................... [ 92%]
................F..                                                                                                             [100%]

============================================================== FAILURES ===============================================================
_________________________________________ test_predicate_repr[pred2-Predicate(math.isfinite)] _________________________________________

pred = Predicate(isfinite), repr_ = 'Predicate(math.isfinite)'

    @pytest.mark.parametrize(
        "pred, repr_",
        [
            (annotated_types.Predicate(func=a_predicate_fn), "Predicate(a_predicate_fn)"),
            (annotated_types.Predicate(func=str.isascii), "Predicate(str.isascii)"),
            (annotated_types.Predicate(func=math.isfinite), "Predicate(math.isfinite)"),
            (annotated_types.Predicate(func=bool), "Predicate(bool)"),
            (annotated_types.Predicate(func := lambda _: True), f"Predicate({func!r})"),
        ],
    )
    def test_predicate_repr(pred: annotated_types.Predicate, repr_: str) -> None:
>       assert repr(pred) == repr_
E       AssertionError: assert 'Predicate(isfinite)' == 'Predicate(math.isfinite)'
E         
E         - Predicate(math.isfinite)
E         ?           -----
E         + Predicate(isfinite)

tests/test_main.py:162: AssertionError
======================================================= short test summary info =======================================================
FAILED tests/test_main.py::test_predicate_repr[pred2-Predicate(math.isfinite)] - AssertionError: assert 'Predicate(isfinite)' == 'Predicate(math.isfinite)'
==================================================== 1 failed, 255 passed in 1.74s ====================================================

I guess PyPy implements it as a builtin without a loadable module.

$ python3.10 -c 'import math; print((math.isfinite, math.isfinite.__self__))'
(<built-in function isfinite>, <module 'math' from '/usr/lib/python3.10/lib-dynload/math.cpython-310-x86_64-linux-gnu.so'>)
$ pypy3.10 -c 'import math; print((math.isfinite, math.isfinite.__self__))'
(<built-in function isfinite>, None)

CC @mattip, @cfbolz

Zac-HD commented 1 month ago

That's not a regression, it's just a newly added test which doesn't pass on PyPy. See https://github.com/annotated-types/annotated-types/compare/v0.6.0...v0.7.0

IMO the specific repr here isn't actually that important, and we can fix the test by picking some other predicate function from the standard library which does have a module attribute under PyPy.

mattip commented 1 month ago

I think this is an issue with the built-in function repr on PyPy. math.isfinite does have math as its __module__, but that does not show up in the repr.

mgorny commented 1 month ago

I think this is an issue with the built-in function repr on PyPy. math.isfinite does have math as its __module__, but that does not show up in the repr.

It doesn't really show up in repr() in CPython either:

>>> repr(math.isfinite)
'<built-in function isfinite>'
mattip commented 1 month ago

Sorry, I guess the problem is with built-in functions __self__ in PyPy.

cfbolz commented 1 month ago

yeah, builtin-functions have a __self__ that is always None in PyPy. maybe we should use the module there, where we know it?

cfbolz commented 1 month ago

here's that the docs say about builtin functions:

  • __self__ is set to None (but see the next item).
  • __module__ is the name of the module the function was defined in or None if unavailable. See function.module.

I don't really know how to interpret this, tbh. It sounds like __self__ being None is fine, but what does "see the next item" mean?

JelleZijlstra commented 1 month ago

I think "the next item" refers to the next section on builtin methods, where __self__ is different.

cfbolz commented 1 month ago

@JelleZijlstra ok, I suppose that makes sense. but then the description is in direct contradiction to the actual behaviour, where math.isinf.__self__ is math is True.