From 8d330ecf44cc82a24dd6bd5313ff0941f95b7f43 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Sat, 6 Jan 2024 13:50:50 -0500 Subject: [PATCH 1/5] MNT: Compat with pytest 8.1 --- pytest_mpl/plugin.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/pytest_mpl/plugin.py b/pytest_mpl/plugin.py index 625bf99..f3822ec 100644 --- a/pytest_mpl/plugin.py +++ b/pytest_mpl/plugin.py @@ -58,6 +58,9 @@ {actual_path}""" PYTEST_LT_7 = Version(pytest.__version__) < Version("7.0.0") +PYTEST_GE_8_0 = any([_pytest_version.is_devrelease, + _pytest_version.is_prerelease, + _pytest_version >= Version('8.0')]) # The following are the subsets of formats supported by the Matplotlib image # comparison machinery @@ -128,11 +131,19 @@ def wrapper(*args, **kwargs): item.obj = figure_interceptor(plugin, item.obj) -def pytest_report_header(config, startdir): - import matplotlib - import matplotlib.ft2font - return ["Matplotlib: {0}".format(matplotlib.__version__), - "Freetype: {0}".format(matplotlib.ft2font.__freetype_version__)] +if PYTEST_GE_8_0: + def pytest_report_header(config, start_path): + import matplotlib + import matplotlib.ft2font + return ["Matplotlib: {0}".format(matplotlib.__version__), + "Freetype: {0}".format(matplotlib.ft2font.__freetype_version__)] + +else: + def pytest_report_header(config, startdir): + import matplotlib + import matplotlib.ft2font + return ["Matplotlib: {0}".format(matplotlib.__version__), + "Freetype: {0}".format(matplotlib.ft2font.__freetype_version__)] def pytest_addoption(parser): From 267114a95a003282d2ddafc4b745054ed1533d04 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Sat, 6 Jan 2024 13:54:43 -0500 Subject: [PATCH 2/5] Fix version comparison --- pytest_mpl/plugin.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pytest_mpl/plugin.py b/pytest_mpl/plugin.py index f3822ec..6648167 100644 --- a/pytest_mpl/plugin.py +++ b/pytest_mpl/plugin.py @@ -57,7 +57,8 @@ Actual shape: {actual_shape} {actual_path}""" -PYTEST_LT_7 = Version(pytest.__version__) < Version("7.0.0") +_pytest_version = Version(pytest.__version__) +PYTEST_LT_7 = _pytest_version < Version("7.0.0") PYTEST_GE_8_0 = any([_pytest_version.is_devrelease, _pytest_version.is_prerelease, _pytest_version >= Version('8.0')]) From 2cdaba0c567be4880e19ada7d734b134c659e79e Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Mon, 8 Jan 2024 22:23:54 -0500 Subject: [PATCH 3/5] Reverts bd7b5c61863170c809c182c9a3bcd81697b52f1a --- .github/workflows/test_and_publish.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/test_and_publish.yml b/.github/workflows/test_and_publish.yml index 4271318..9a3c898 100644 --- a/.github/workflows/test_and_publish.yml +++ b/.github/workflows/test_and_publish.yml @@ -42,8 +42,7 @@ jobs: - linux: py311-test-mpl36 - linux: py311-test-mpl37 # Test different versions of pytest - # Skip pytestdev until hook wrapper issue is fixed - # - linux: py312-test-mpldev-pytestdev + - linux: py312-test-mpldev-pytestdev - linux: py39-test-mpl33-pytest62 - linux: py38-test-mpl31-pytest54 coverage: 'codecov' From e57d11efeaf67d932f48e3ba4c5453c713a259a1 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Tue, 9 Jan 2024 11:48:23 -0500 Subject: [PATCH 4/5] Remove args from pytest_report_header and fix up the other pytest 8 compat problem with old-style hook wrapper. Update flake8 setting to reflect what is actually in the code. --- pytest_mpl/plugin.py | 139 ++++++++++++++++++++++--------------------- setup.cfg | 6 +- 2 files changed, 73 insertions(+), 72 deletions(-) diff --git a/pytest_mpl/plugin.py b/pytest_mpl/plugin.py index 6648167..7b59184 100644 --- a/pytest_mpl/plugin.py +++ b/pytest_mpl/plugin.py @@ -132,19 +132,11 @@ def wrapper(*args, **kwargs): item.obj = figure_interceptor(plugin, item.obj) -if PYTEST_GE_8_0: - def pytest_report_header(config, start_path): - import matplotlib - import matplotlib.ft2font - return ["Matplotlib: {0}".format(matplotlib.__version__), - "Freetype: {0}".format(matplotlib.ft2font.__freetype_version__)] - -else: - def pytest_report_header(config, startdir): - import matplotlib - import matplotlib.ft2font - return ["Matplotlib: {0}".format(matplotlib.__version__), - "Freetype: {0}".format(matplotlib.ft2font.__freetype_version__)] +def pytest_report_header(): + import matplotlib + import matplotlib.ft2font + return ["Matplotlib: {0}".format(matplotlib.__version__), + "Freetype: {0}".format(matplotlib.ft2font.__freetype_version__)] def pytest_addoption(parser): @@ -815,67 +807,76 @@ def pytest_runtest_call(self, item): # noqa # Run test and get figure object wrap_figure_interceptor(self, item) - yield - if test_name not in self.return_value: - # Test function did not complete successfully - summary['status'] = 'failed' - summary['status_msg'] = ('Test function raised an exception ' - 'before returning a figure.') - self._test_results[test_name] = summary - return - fig = self.return_value[test_name] - - if remove_text: - remove_ticks_and_titles(fig) - - result_dir = self.make_test_results_dir(item) - - # What we do now depends on whether we are generating the - # reference images or simply running the test. - if self.generate_dir is not None: - summary['status'] = 'skipped' - summary['image_status'] = 'generated' - summary['status_msg'] = 'Skipped test, since generating image.' - generate_image = self.generate_baseline_image(item, fig) - if self.results_always: # Make baseline image available in HTML - result_image = (result_dir / f"baseline.{ext}").absolute() - shutil.copy(generate_image, result_image) - summary['baseline_image'] = \ - result_image.relative_to(self.results_dir).as_posix() - - if self.generate_hash_library is not None: - summary['hash_status'] = 'generated' - image_hash = self.generate_image_hash(item, fig) - self._generated_hash_library[test_name] = image_hash - summary['baseline_hash'] = image_hash - - # Only test figures if not generating images - if self.generate_dir is None: - # Compare to hash library - if self.hash_library or compare.kwargs.get('hash_library', None): - msg = self.compare_image_to_hash_library(item, fig, result_dir, summary=summary) - - # Compare against a baseline if specified - else: - msg = self.compare_image_to_baseline(item, fig, result_dir, summary=summary) - - close_mpl_figure(fig) - if msg is None: - if not self.results_always: - shutil.rmtree(result_dir) - for image_type in ['baseline_image', 'diff_image', 'result_image']: - summary[image_type] = None # image no longer exists - else: + # See https://github.com/pytest-dev/pytest/issues/11714 + result = yield + try: + if test_name not in self.return_value: + # Test function did not complete successfully + summary['status'] = 'failed' + summary['status_msg'] = ('Test function raised an exception ' + 'before returning a figure.') self._test_results[test_name] = summary - pytest.fail(msg, pytrace=False) + return + fig = self.return_value[test_name] + + if remove_text: + remove_ticks_and_titles(fig) + + result_dir = self.make_test_results_dir(item) + + # What we do now depends on whether we are generating the + # reference images or simply running the test. + if self.generate_dir is not None: + summary['status'] = 'skipped' + summary['image_status'] = 'generated' + summary['status_msg'] = 'Skipped test, since generating image.' + generate_image = self.generate_baseline_image(item, fig) + if self.results_always: # Make baseline image available in HTML + result_image = (result_dir / f"baseline.{ext}").absolute() + shutil.copy(generate_image, result_image) + summary['baseline_image'] = \ + result_image.relative_to(self.results_dir).as_posix() + + if self.generate_hash_library is not None: + summary['hash_status'] = 'generated' + image_hash = self.generate_image_hash(item, fig) + self._generated_hash_library[test_name] = image_hash + summary['baseline_hash'] = image_hash + + # Only test figures if not generating images + if self.generate_dir is None: + # Compare to hash library + if self.hash_library or compare.kwargs.get('hash_library', None): + msg = self.compare_image_to_hash_library(item, fig, result_dir, summary=summary) + + # Compare against a baseline if specified + else: + msg = self.compare_image_to_baseline(item, fig, result_dir, summary=summary) + + close_mpl_figure(fig) + + if msg is None: + if not self.results_always: + shutil.rmtree(result_dir) + for image_type in ['baseline_image', 'diff_image', 'result_image']: + summary[image_type] = None # image no longer exists + else: + self._test_results[test_name] = summary + pytest.fail(msg, pytrace=False) - close_mpl_figure(fig) + close_mpl_figure(fig) - self._test_results[test_name] = summary + self._test_results[test_name] = summary - if summary['status'] == 'skipped': - pytest.skip(summary['status_msg']) + if summary['status'] == 'skipped': + pytest.skip(summary['status_msg']) + except BaseException as e: + if hasattr(result, "force_exception"): # pluggy>=1.2.0 + result.force_exception(e) + else: + result._result = None + result._excinfo = (type(e), e, e.__traceback__) def generate_summary_json(self): json_file = self.results_dir / 'results.json' diff --git a/setup.cfg b/setup.cfg index f8026f9..a874af3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -55,11 +55,11 @@ filterwarnings = ignore:The NumPy module was reloaded [flake8] -max-line-length = 100 -ignore = W504 +max-line-length = 120 +ignore = W503,W504 [pycodestyle] -max_line_length = 100 +max_line_length = 120 [isort] balanced_wrapping = True From 32da10ac62e4d2bddf6877c1acdf9768f23e34b7 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Tue, 9 Jan 2024 11:52:07 -0500 Subject: [PATCH 5/5] Auto-cancel duplicate CI and remove unnecessary pytest version check I added earlier. --- .github/workflows/test_and_publish.yml | 4 ++++ pytest_mpl/plugin.py | 6 +----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test_and_publish.yml b/.github/workflows/test_and_publish.yml index 9a3c898..e2519a7 100644 --- a/.github/workflows/test_and_publish.yml +++ b/.github/workflows/test_and_publish.yml @@ -13,6 +13,10 @@ on: # Allow manual runs through the web UI workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: test: uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v1 diff --git a/pytest_mpl/plugin.py b/pytest_mpl/plugin.py index 7b59184..c5b9574 100644 --- a/pytest_mpl/plugin.py +++ b/pytest_mpl/plugin.py @@ -57,11 +57,7 @@ Actual shape: {actual_shape} {actual_path}""" -_pytest_version = Version(pytest.__version__) -PYTEST_LT_7 = _pytest_version < Version("7.0.0") -PYTEST_GE_8_0 = any([_pytest_version.is_devrelease, - _pytest_version.is_prerelease, - _pytest_version >= Version('8.0')]) +PYTEST_LT_7 = Version(pytest.__version__) < Version("7.0.0") # The following are the subsets of formats supported by the Matplotlib image # comparison machinery