Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit a4e81e7

Browse filesBrowse files
authored
Merge pull request #20848 from QuLogic/pypy
FIX: PyPy wheels and tests
2 parents af1c95f + e9fbe9f commit a4e81e7
Copy full SHA for a4e81e7

11 files changed

+66
-14
lines changed

‎.github/workflows/cibuildwheel.yml

Copy file name to clipboardExpand all lines: .github/workflows/cibuildwheel.yml
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ jobs:
4747

4848
- name: Install cibuildwheel
4949
run: |
50-
python -m pip install cibuildwheel==1.9.0
50+
python -m pip install cibuildwheel==2.1.1
5151
5252
- name: Build minimum NumPy for aarch64
5353
if: matrix.cibw_archs == 'aarch64' && steps.numpy-cache.outputs.cache-hit != 'true'
@@ -93,7 +93,7 @@ jobs:
9393
CIBW_BUILD: "pp37-*"
9494
CIBW_BEFORE_BUILD: pip install certifi numpy==${{ env.min-numpy-version }}
9595
CIBW_ARCHS: ${{ matrix.cibw_archs }}
96-
if: runner.os != 'Windows' && matrix.cibw_archs != 'aarch64'
96+
if: matrix.cibw_archs != 'aarch64'
9797

9898
- name: Validate that LICENSE files are included in wheels
9999
run: |

‎lib/matplotlib/backend_bases.py

Copy file name to clipboardExpand all lines: lib/matplotlib/backend_bases.py
+10-4Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1618,14 +1618,20 @@ def wrapper(*args, **kwargs):
16181618
if frame is None:
16191619
# when called in embedded context may hit frame is None.
16201620
break
1621+
# Work around sphinx-gallery not setting __name__.
1622+
frame_name = frame.f_globals.get('__name__', '')
16211623
if re.match(r'\A(matplotlib|mpl_toolkits)(\Z|\.(?!tests\.))',
1622-
# Work around sphinx-gallery not setting __name__.
1623-
frame.f_globals.get('__name__', '')):
1624-
if public_api.match(frame.f_code.co_name):
1625-
name = frame.f_code.co_name
1624+
frame_name):
1625+
name = frame.f_code.co_name
1626+
if public_api.match(name):
16261627
if name in ('print_figure', '_no_output_draw'):
16271628
seen_print_figure = True
16281629

1630+
elif frame_name == '_functools':
1631+
# PyPy adds an extra frame without module prefix for this
1632+
# functools wrapper, which we ignore to assume we're still in
1633+
# Matplotlib code.
1634+
continue
16291635
else:
16301636
break
16311637

‎lib/matplotlib/patches.py

Copy file name to clipboardExpand all lines: lib/matplotlib/patches.py
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1451,7 +1451,8 @@ def _make_verts(self):
14511451

14521452

14531453
docstring.interpd.update(
1454-
FancyArrow="\n".join(inspect.getdoc(FancyArrow.__init__).splitlines()[2:]))
1454+
FancyArrow="\n".join(
1455+
(inspect.getdoc(FancyArrow.__init__) or "").splitlines()[2:]))
14551456

14561457

14571458
class CirclePolygon(RegularPolygon):

‎lib/matplotlib/scale.py

Copy file name to clipboardExpand all lines: lib/matplotlib/scale.py
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -620,10 +620,11 @@ def _get_scale_docs():
620620
"""
621621
docs = []
622622
for name, scale_class in _scale_mapping.items():
623+
docstring = inspect.getdoc(scale_class.__init__) or ""
623624
docs.extend([
624625
f" {name!r}",
625626
"",
626-
textwrap.indent(inspect.getdoc(scale_class.__init__), " " * 8),
627+
textwrap.indent(docstring, " " * 8),
627628
""
628629
])
629630
return "\n".join(docs)

‎lib/matplotlib/tests/test_animation.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_animation.py
+15-3Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import gc
21
import os
32
from pathlib import Path
3+
import platform
44
import subprocess
55
import sys
66
import weakref
@@ -87,10 +87,15 @@ def test_null_movie_writer(anim):
8787

8888
@pytest.mark.parametrize('anim', [dict(klass=dict)], indirect=['anim'])
8989
def test_animation_delete(anim):
90+
if platform.python_implementation() == 'PyPy':
91+
# Something in the test setup fixture lingers around into the test and
92+
# breaks pytest.warns on PyPy. This garbage collection fixes it.
93+
# https://foss.heptapod.net/pypy/pypy/-/issues/3536
94+
np.testing.break_cycles()
9095
anim = animation.FuncAnimation(**anim)
9196
with pytest.warns(Warning, match='Animation was deleted'):
9297
del anim
93-
gc.collect()
98+
np.testing.break_cycles()
9499

95100

96101
def test_movie_writer_dpi_default():
@@ -201,6 +206,11 @@ def test_save_animation_smoketest(tmpdir, writer, frame_format, output, anim):
201206
])
202207
@pytest.mark.parametrize('anim', [dict(klass=dict)], indirect=['anim'])
203208
def test_animation_repr_html(writer, html, want, anim):
209+
if platform.python_implementation() == 'PyPy':
210+
# Something in the test setup fixture lingers around into the test and
211+
# breaks pytest.warns on PyPy. This garbage collection fixes it.
212+
# https://foss.heptapod.net/pypy/pypy/-/issues/3536
213+
np.testing.break_cycles()
204214
if (writer == 'imagemagick' and html == 'html5'
205215
# ImageMagick delegates to ffmpeg for this format.
206216
and not animation.FFMpegWriter.isAvailable()):
@@ -214,7 +224,8 @@ def test_animation_repr_html(writer, html, want, anim):
214224
if want is None:
215225
assert html is None
216226
with pytest.warns(UserWarning):
217-
del anim # Animtion was never run, so will warn on cleanup.
227+
del anim # Animation was never run, so will warn on cleanup.
228+
np.testing.break_cycles()
218229
else:
219230
assert want in html
220231

@@ -324,6 +335,7 @@ def frames_generator():
324335
writer = NullMovieWriter()
325336
anim.save('unused.null', writer=writer)
326337
assert len(frames_generated) == 5
338+
np.testing.break_cycles()
327339
for f in frames_generated:
328340
# If cache_frame_data is True, then the weakref should be alive;
329341
# if cache_frame_data is False, then the weakref should be dead (None).

‎lib/matplotlib/tests/test_backend_tk.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_backend_tk.py
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import functools
22
import inspect
33
import os
4+
import platform
45
import re
56
import subprocess
67
import sys
@@ -111,6 +112,9 @@ def legitimate_quit():
111112
print("success")
112113

113114

115+
@pytest.mark.skipif(platform.python_implementation() != 'CPython',
116+
reason='PyPy does not support Tkinter threading: '
117+
'https://foss.heptapod.net/pypy/pypy/-/issues/1929')
114118
@pytest.mark.backend('TkAgg', skip_on_importerror=True)
115119
@pytest.mark.flaky(reruns=3)
116120
@_isolated_tk_test(success_count=1)

‎lib/matplotlib/tests/test_backends_interactive.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_backends_interactive.py
+7Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import inspect
44
import json
55
import os
6+
import platform
67
import signal
78
import subprocess
89
import sys
@@ -220,6 +221,12 @@ def _test_thread_impl():
220221
elif param.values[0].get("QT_API") == "PySide2":
221222
param.marks.append(
222223
pytest.mark.xfail(raises=subprocess.CalledProcessError))
224+
elif backend == "tkagg" and platform.python_implementation() != 'CPython':
225+
param.marks.append(
226+
pytest.mark.xfail(
227+
reason='PyPy does not support Tkinter threading: '
228+
'https://foss.heptapod.net/pypy/pypy/-/issues/1929',
229+
strict=True))
223230

224231

225232
@pytest.mark.parametrize("env", _thread_safe_backends)

‎lib/matplotlib/tests/test_cbook.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_cbook.py
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,11 +198,13 @@ def count(self):
198198
return count1
199199

200200
def is_empty(self):
201+
np.testing.break_cycles()
201202
assert self.callbacks._func_cid_map == {}
202203
assert self.callbacks.callbacks == {}
203204
assert self.callbacks._pickled_cids == set()
204205

205206
def is_not_empty(self):
207+
np.testing.break_cycles()
206208
assert self.callbacks._func_cid_map != {}
207209
assert self.callbacks.callbacks != {}
208210

‎lib/matplotlib/tests/test_quiver.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_quiver.py
+8-1Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
import platform
2+
import sys
3+
14
import numpy as np
25
import pytest
3-
import sys
6+
47
from matplotlib import pyplot as plt
58
from matplotlib.testing.decorators import image_comparison
69

@@ -15,6 +18,8 @@ def draw_quiver(ax, **kw):
1518
return Q
1619

1720

21+
@pytest.mark.skipif(platform.python_implementation() != 'CPython',
22+
reason='Requires CPython')
1823
def test_quiver_memory_leak():
1924
fig, ax = plt.subplots()
2025

@@ -27,6 +32,8 @@ def test_quiver_memory_leak():
2732
assert sys.getrefcount(ttX) == 2
2833

2934

35+
@pytest.mark.skipif(platform.python_implementation() != 'CPython',
36+
reason='Requires CPython')
3037
def test_quiver_key_memory_leak():
3138
fig, ax = plt.subplots()
3239

‎lib/matplotlib/tests/test_style.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_style.py
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
from contextlib import contextmanager
2-
import gc
32
from pathlib import Path
43
from tempfile import TemporaryDirectory
54
import sys
65

6+
import numpy as np
77
import pytest
88

99
import matplotlib as mpl
@@ -165,7 +165,7 @@ def test_xkcd_no_cm():
165165
assert mpl.rcParams["path.sketch"] is None
166166
plt.xkcd()
167167
assert mpl.rcParams["path.sketch"] == (1, 100, 2)
168-
gc.collect()
168+
np.testing.break_cycles()
169169
assert mpl.rcParams["path.sketch"] == (1, 100, 2)
170170

171171

‎src/_c_internal_utils.c

Copy file name to clipboardExpand all lines: src/_c_internal_utils.c
+12Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,13 @@ mpl_GetCurrentProcessExplicitAppUserModelID(PyObject* module)
6868
wchar_t* appid = NULL;
6969
HRESULT hr = GetCurrentProcessExplicitAppUserModelID(&appid);
7070
if (FAILED(hr)) {
71+
#if defined(PYPY_VERSION_NUM) && PYPY_VERSION_NUM < 0x07030600
72+
/* Remove when we require PyPy 7.3.6 */
73+
PyErr_SetFromWindowsErr(hr);
74+
return NULL;
75+
#else
7176
return PyErr_SetFromWindowsErr(hr);
77+
#endif
7278
}
7379
PyObject* py_appid = PyUnicode_FromWideChar(appid, -1);
7480
CoTaskMemFree(appid);
@@ -89,7 +95,13 @@ mpl_SetCurrentProcessExplicitAppUserModelID(PyObject* module, PyObject* arg)
8995
HRESULT hr = SetCurrentProcessExplicitAppUserModelID(appid);
9096
PyMem_Free(appid);
9197
if (FAILED(hr)) {
98+
#if defined(PYPY_VERSION_NUM) && PYPY_VERSION_NUM < 0x07030600
99+
/* Remove when we require PyPy 7.3.6 */
100+
PyErr_SetFromWindowsErr(hr);
101+
return NULL;
102+
#else
92103
return PyErr_SetFromWindowsErr(hr);
104+
#endif
93105
}
94106
Py_RETURN_NONE;
95107
#else

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.