hypothesis icon indicating copy to clipboard operation
hypothesis copied to clipboard

`ghostwriter.magic(numpy.matmul)` fails with NumPy 1.26.4 and Python 3.12

Open roehling opened this issue 1 year ago • 3 comments

Traceback from pytest invocation below. From what I can tell, inspect.signature raises ValueError: callable <ufunc 'matmul'> is not supported by signature in Python 3.11, but returns <Signature (*args, **kwargs)> in Python 3.12. This makes ghostwriter._get_params return an empty OrderedDict, which is subsequently padded with None by zip_longest.

============================= test session starts ==============================
platform linux -- Python 3.12.2, pytest-8.1.1, pluggy-1.4.0
rootdir: /<<PKGBUILDDIR>>
configfile: pytest.ini
plugins: hypothesis-6.100.0, flaky-3.8.1
collected 6152 items / 1 error / 4037 deselected / 1 skipped / 2115 selected

==================================== ERRORS ====================================
_ ERROR collecting .pybuild/cpython3_3.12_hypothesis/build/tests/ghostwriter/test_expected_output.py _
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/_pytest/runner.py", line 340, in from_call
    result: Optional[TResult] = func()
                                ^^^^^^
  File "/usr/lib/python3/dist-packages/_pytest/runner.py", line 388, in collect
    return list(collector.collect())
                ^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/_pytest/python.py", line 576, in collect
    self._register_setup_module_fixture()
  File "/usr/lib/python3/dist-packages/_pytest/python.py", line 589, in _register_setup_module_fixture
    self.obj, ("setUpModule", "setup_module")
    ^^^^^^^^
  File "/usr/lib/python3/dist-packages/_pytest/python.py", line 315, in obj
    self._obj = obj = self._getobj()
                      ^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/_pytest/python.py", line 573, in _getobj
    return importtestmodule(self.path, self.config)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/_pytest/python.py", line 520, in importtestmodule
    mod = import_path(
          ^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/_pytest/pathlib.py", line 584, in import_path
    importlib.import_module(module_name)
  File "/usr/lib/python3.12/importlib/__init__.py", line 90, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
  File "/usr/lib/python3/dist-packages/_pytest/assertion/rewrite.py", line 178, in exec_module
    exec(co, module.__dict__)
  File "/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12_hypothesis/build/tests/ghostwriter/test_expected_output.py", line 132, in <module>
    ("magic_gufunc", ghostwriter.magic(numpy.matmul)),
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12_hypothesis/build/hypothesis/extra/ghostwriter.py", line 1301, in magic
    make_(_make_ufunc_body, func, annotate=annotate)
  File "/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12_hypothesis/build/hypothesis/extra/ghostwriter.py", line 1202, in make_
    imp, body = how(*args, **kwargs, except_=except_, style=style)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12_hypothesis/build/hypothesis/extra/ghostwriter.py", line 1884, in _make_ufunc_body
    call=_write_call(func, *ascii_lowercase[: func.nin], except_=except_),
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12_hypothesis/build/hypothesis/extra/ghostwriter.py", line 781, in _write_call
    args = ", ".join(
           ^^^^^^^^^^
  File "/<<PKGBUILDDIR>>/.pybuild/cpython3_3.12_hypothesis/build/hypothesis/extra/ghostwriter.py", line 784, in <genexpr>
    if p.kind is inspect.Parameter.POSITIONAL_ONLY
       ^^^^^^
AttributeError: 'NoneType' object has no attribute 'kind'
=========================== short test summary info ============================
ERROR tests/ghostwriter/test_expected_output.py - AttributeError: 'NoneType' ...
!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!

roehling avatar Apr 03 '24 15:04 roehling

I actually can't reproduce with python 3.12.2 + numpy 1.26.4 on macos:

>>> assert numpy.__version__ == "1.26.4"
>>> assert sys.version_info[:3] == (3, 12, 2)
>>> ghostwriter.magic(numpy.matmul)
...

but #3955 ran into the same error, so I'd expect this to also be fixed once that is merged.

Liam-DeVoe avatar Apr 26 '24 20:04 Liam-DeVoe

Ran into the same error, yes, but since it wasn't failing in CI we didn't make any changes. I think the fix will be to use if p is None or p.kind... as the condition here.

Zac-HD avatar Apr 26 '24 21:04 Zac-HD

another option would be to manually raise here if the signature detected by inspect.signature is *args, **kwargs and the tricks below are likely to work.

keewis avatar Apr 28 '24 12:04 keewis

So far as I can tell, this is working now:

Python 3.12.4 (main, Jul 12 2024, 16:02:27) [GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from hypothesis.extra.ghostwriter import magic
>>> import numpy as np
>>> magic(np.matmul)
'# This test code was written by the `hypothesis.extra.ghostwriter` module\n# and is provided under the Creative Commons Zero public domain dedication.\n\nimport numpy\nfrom hypothesis import given, strategies as st\nfrom hypothesis.extra.numpy import arrays, mutually_broadcastable_shapes\n\n\n@given(\n    data=st.data(),\n    shapes=mutually_broadcastable_shapes(signature="(n?,k),(k,m?)->(n?,m?)"),\n    types=st.sampled_from([sig for sig in numpy.matmul.types if "O" not in sig]),\n)\ndef test_gufunc_matmul(data, shapes, types):\n    input_shapes, expected_shape = shapes\n    input_dtypes, expected_dtype = types.split("->")\n    array_strats = [\n        arrays(dtype=dtp, shape=shp, elements={"allow_nan": True})\n        for dtp, shp in zip(input_dtypes, input_shapes)\n    ]\n\n    a, b = data.draw(st.tuples(*array_strats))\n    result = numpy.matmul(a, b)\n\n    assert result.shape == expected_shape\n    assert result.dtype.char == expected_dtype\n'
>>> np.__version__
'1.26.4'

Zac-HD avatar Nov 10 '24 02:11 Zac-HD