From 199f34c4680bc5065dd974a8eecc77147fba7b39 Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Tue, 27 Aug 2024 10:57:53 +0200 Subject: [PATCH 01/30] add copy argument to __array__ methods --- src/silx/io/commonh5.py | 10 ++++++++-- src/silx/utils/array_like.py | 21 ++++++++++++++++----- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/silx/io/commonh5.py b/src/silx/io/commonh5.py index f04fe8d494..b0d44400a9 100644 --- a/src/silx/io/commonh5.py +++ b/src/silx/io/commonh5.py @@ -31,6 +31,8 @@ import numpy from . import utils +from .._utils import NP_OPTIONAL_COPY + __authors__ = ["V. Valls", "P. Knobel"] __license__ = "MIT" @@ -347,12 +349,16 @@ def external(self): :rtype: list or None""" return None - def __array__(self, dtype=None): + def __array__(self, dtype=None, copy=None): # Special case for (0,)*-shape datasets if numpy.prod(self.shape) == 0: return self[()] else: - return numpy.array(self[...], dtype=self.dtype if dtype is None else dtype) + return numpy.array( + self[...], + dtype=self.dtype if dtype is None else dtype, + copy=NP_OPTIONAL_COPY if copy is None else copy, + ) def __iter__(self): """Iterate over the first axis. TypeError if scalar.""" diff --git a/src/silx/utils/array_like.py b/src/silx/utils/array_like.py index b9b976bca8..cb303bae4e 100644 --- a/src/silx/utils/array_like.py +++ b/src/silx/utils/array_like.py @@ -1,6 +1,6 @@ # /*########################################################################## # -# Copyright (c) 2016-2021 European Synchrotron Radiation Facility +# Copyright (c) 2016-2024 European Synchrotron Radiation Facility # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -49,6 +49,9 @@ import numpy import numbers +from .._utils import NP_OPTIONAL_COPY + + __authors__ = ["P. Knobel"] __license__ = "MIT" __date__ = "26/04/2017" @@ -276,13 +279,17 @@ def __sort_indices(self, indices): ) return sorted_indices - def __array__(self, dtype=None): + def __array__(self, dtype=None, copy=None): """Cast the images into a numpy array, and return it. If a transposition has been done on this images, return a transposed view of a numpy array.""" return numpy.transpose( - numpy.array(self.images, dtype=dtype), self.transposition + numpy.array( + self.images, + dtype=dtype, + copy=NP_OPTIONAL_COPY if copy is None else copy), + self.transposition, ) def __len__(self): @@ -543,13 +550,17 @@ def __getitem__(self, item): return numpy.transpose(output_data_not_transposed, axes=output_dimensions) - def __array__(self, dtype=None): + def __array__(self, dtype=None, copy=None): """Cast the dataset into a numpy array, and return it. If a transposition has been done on this dataset, return a transposed view of a numpy array.""" return numpy.transpose( - numpy.array(self.dataset, dtype=dtype), self.transposition + numpy.array( + self.dataset, + dtype=dtype, + copy=NP_OPTIONAL_COPY if copy is None else copy), + self.transposition, ) def __len__(self): From e8bfa3f0d357e51e36aa95ee029bcce2635e2647 Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Fri, 14 Jun 2024 17:13:51 +0200 Subject: [PATCH 02/30] Try testing GUI on Windows --- .github/workflows/ci.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c119f8d5b7..afd993aacf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,13 +60,12 @@ jobs: QT_BINDING: PyQt6 RUN_TESTS_OPTIONS: --qt-binding=PyQt6 --no-opencl --low-mem - - name-suffix: "No GUI wheel" + - name-suffix: "PyQt5 wheel" os: windows-latest python-version: "3.9" BUILD_COMMAND: --wheel QT_BINDING: PyQt5 - RUN_TESTS_OPTIONS: --no-gui --low-mem - # No GUI tests on Windows + RUN_TESTS_OPTIONS: --qt-binding=PyQt5 --low-mem # Steps represent a sequence of tasks that will be executed as part of the job steps: @@ -91,6 +90,11 @@ jobs: python-version: ${{ matrix.python-version }} cache: "pip" + - name: Install mesa OpenGL + if: runner.os == 'Windows' + run: | + curl -fsS -o opengl32.dll http://www.silx.org/pub/silx/continuous_integration/opengl32_mingw-mesa-x86_64.dll + - name: Upgrade distribution modules run: | python -m pip install --upgrade pip From a970e37dde185e00d65e4b9f312e17681fbe46ee Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Mon, 17 Jun 2024 14:20:34 +0200 Subject: [PATCH 03/30] Compare realpath --- src/silx/gui/dialog/test/test_datafiledialog.py | 11 ++--------- src/silx/gui/dialog/test/test_imagefiledialog.py | 8 ++++---- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/silx/gui/dialog/test/test_datafiledialog.py b/src/silx/gui/dialog/test/test_datafiledialog.py index 1837f22609..d781e6ffac 100644 --- a/src/silx/gui/dialog/test/test_datafiledialog.py +++ b/src/silx/gui/dialog/test/test_datafiledialog.py @@ -123,19 +123,12 @@ def qWaitForPendingActions(self, dialog): raise RuntimeError("Still have pending actions") def assertSamePath(self, path1, path2): - path1_ = os.path.normcase(path1) - path2_ = os.path.normcase(path2) + path1_ = os.path.normcase(os.path.realpath(path1)) + path2_ = os.path.normcase(os.path.realpath(path2)) if path1_ != path2_: # Use the unittest API to log and display error self.assertEqual(path1, path2) - def assertNotSamePath(self, path1, path2): - path1_ = os.path.normcase(path1) - path2_ = os.path.normcase(path2) - if path1_ == path2_: - # Use the unittest API to log and display error - self.assertNotEqual(path1, path2) - class TestDataFileDialogInteraction(testutils.TestCaseQt, _UtilsMixin): def tearDown(self): diff --git a/src/silx/gui/dialog/test/test_imagefiledialog.py b/src/silx/gui/dialog/test/test_imagefiledialog.py index 9704b245a4..a70c1eb915 100644 --- a/src/silx/gui/dialog/test/test_imagefiledialog.py +++ b/src/silx/gui/dialog/test/test_imagefiledialog.py @@ -130,15 +130,15 @@ def qWaitForPendingActions(self, dialog): raise RuntimeError("Still have pending actions") def assertSamePath(self, path1, path2): - path1_ = os.path.normcase(path1) - path2_ = os.path.normcase(path2) + path1_ = os.path.normcase(os.path.realpath(path1)) + path2_ = os.path.normcase(os.path.realpath(path2)) if path1_ != path2_: # Use the unittest API to log and display error self.assertEqual(path1, path2) def assertNotSamePath(self, path1, path2): - path1_ = os.path.normcase(path1) - path2_ = os.path.normcase(path2) + path1_ = os.path.normcase(os.path.realpath(path1)) + path2_ = os.path.normcase(os.path.realpath(path2)) if path1_ == path2_: # Use the unittest API to log and display error self.assertNotEqual(path1, path2) From d0a0fad68368a25d49220112e90ab3a3979cbad3 Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Mon, 17 Jun 2024 15:26:59 +0200 Subject: [PATCH 04/30] Rename test files to use pytest default prefix: test_ and use lower case for all test file names --- .../test/{testBackgroundWidget.py => test_backgroundwidget.py} | 0 src/silx/gui/fit/test/{testFitConfig.py => test_fitconfig.py} | 0 src/silx/gui/fit/test/{testFitWidget.py => test_fitwidget.py} | 0 .../gui/plot/test/{testAlphaSlider.py => test_alphaslider.py} | 0 src/silx/gui/plot/test/{testAxis.py => test_axis.py} | 0 src/silx/gui/plot/test/{testColorBar.py => test_colorbar.py} | 0 .../gui/plot/test/{testCompareImages.py => test_compareimages.py} | 0 .../test/{testComplexImageView.py => test_compleximageview.py} | 0 .../plot/test/{testCurvesROIWidget.py => test_curvesroiwidget.py} | 0 src/silx/gui/plot/test/{testImageStack.py => test_imagestack.py} | 0 src/silx/gui/plot/test/{testImageView.py => test_imageview.py} | 0 .../gui/plot/test/{testInteraction.py => test_interaction.py} | 0 src/silx/gui/plot/test/{testItem.py => test_item.py} | 0 .../plot/test/{testLegendSelector.py => test_legendselector.py} | 0 .../test/{testLimitConstraints.py => test_limitconstraints.py} | 0 .../plot/test/{testMaskToolsWidget.py => test_masktoolswidget.py} | 0 ...lIntensityHistoAction.py => test_pixelintensityhistoaction.py} | 0 .../gui/plot/test/{testPlotActions.py => test_plotactions.py} | 0 .../plot/test/{testPlotInteraction.py => test_plotinteraction.py} | 0 src/silx/gui/plot/test/{testPlotWidget.py => test_plotwidget.py} | 0 .../{testPlotWidgetActiveItem.py => test_plotwidgetactiveitem.py} | 0 ...testPlotWidgetDataMargins.py => test_plotwidgetdatamargins.py} | 0 .../{testPlotWidgetNoBackend.py => test_plotwidgetnobackend.py} | 0 src/silx/gui/plot/test/{testPlotWindow.py => test_plotwindow.py} | 0 .../plot/test/{testRoiStatsWidget.py => test_roistatswidget.py} | 0 src/silx/gui/plot/test/{testSaveAction.py => test_saveaction.py} | 0 ...stScatterMaskToolsWidget.py => test_scattermasktoolswidget.py} | 0 .../gui/plot/test/{testScatterView.py => test_scatterview.py} | 0 src/silx/gui/plot/test/{testStackView.py => test_stackview.py} | 0 src/silx/gui/plot/test/{testStats.py => test_stats.py} | 0 src/silx/gui/plot/test/{testUtilsAxis.py => test_utilsaxis.py} | 0 .../{testCurveLegendsWidget.py => test_curvelegendswidget.py} | 0 src/silx/gui/plot/tools/test/{testProfile.py => test_profile.py} | 0 src/silx/gui/plot/tools/test/{testRoiCore.py => test_roicore.py} | 0 .../gui/plot/tools/test/{testRoiItems.py => test_roiitems.py} | 0 ...testScatterProfileToolBar.py => test_scatterprofiletoolbar.py} | 0 src/silx/gui/plot/tools/test/{testTools.py => test_tools.py} | 0 src/silx/gui/plot3d/test/{testGL.py => test_gl.py} | 0 .../test/{testScalarFieldView.py => test_scalarfieldview.py} | 0 .../gui/plot3d/test/{testSceneWidget.py => test_scenewidget.py} | 0 .../{testSceneWidgetPicking.py => test_scenewidgetpicking.py} | 0 .../gui/plot3d/test/{testSceneWindow.py => test_scenewindow.py} | 0 .../gui/plot3d/test/{testStatsWidget.py => test_statswidget.py} | 0 .../{testPositionInfoWidget.py => test_positioninfowidget.py} | 0 src/silx/gui/utils/test/{test.py => test_blocksignals.py} | 0 45 files changed, 0 insertions(+), 0 deletions(-) rename src/silx/gui/fit/test/{testBackgroundWidget.py => test_backgroundwidget.py} (100%) rename src/silx/gui/fit/test/{testFitConfig.py => test_fitconfig.py} (100%) rename src/silx/gui/fit/test/{testFitWidget.py => test_fitwidget.py} (100%) rename src/silx/gui/plot/test/{testAlphaSlider.py => test_alphaslider.py} (100%) rename src/silx/gui/plot/test/{testAxis.py => test_axis.py} (100%) rename src/silx/gui/plot/test/{testColorBar.py => test_colorbar.py} (100%) rename src/silx/gui/plot/test/{testCompareImages.py => test_compareimages.py} (100%) rename src/silx/gui/plot/test/{testComplexImageView.py => test_compleximageview.py} (100%) rename src/silx/gui/plot/test/{testCurvesROIWidget.py => test_curvesroiwidget.py} (100%) rename src/silx/gui/plot/test/{testImageStack.py => test_imagestack.py} (100%) rename src/silx/gui/plot/test/{testImageView.py => test_imageview.py} (100%) rename src/silx/gui/plot/test/{testInteraction.py => test_interaction.py} (100%) rename src/silx/gui/plot/test/{testItem.py => test_item.py} (100%) rename src/silx/gui/plot/test/{testLegendSelector.py => test_legendselector.py} (100%) rename src/silx/gui/plot/test/{testLimitConstraints.py => test_limitconstraints.py} (100%) rename src/silx/gui/plot/test/{testMaskToolsWidget.py => test_masktoolswidget.py} (100%) rename src/silx/gui/plot/test/{testPixelIntensityHistoAction.py => test_pixelintensityhistoaction.py} (100%) rename src/silx/gui/plot/test/{testPlotActions.py => test_plotactions.py} (100%) rename src/silx/gui/plot/test/{testPlotInteraction.py => test_plotinteraction.py} (100%) rename src/silx/gui/plot/test/{testPlotWidget.py => test_plotwidget.py} (100%) rename src/silx/gui/plot/test/{testPlotWidgetActiveItem.py => test_plotwidgetactiveitem.py} (100%) rename src/silx/gui/plot/test/{testPlotWidgetDataMargins.py => test_plotwidgetdatamargins.py} (100%) rename src/silx/gui/plot/test/{testPlotWidgetNoBackend.py => test_plotwidgetnobackend.py} (100%) rename src/silx/gui/plot/test/{testPlotWindow.py => test_plotwindow.py} (100%) rename src/silx/gui/plot/test/{testRoiStatsWidget.py => test_roistatswidget.py} (100%) rename src/silx/gui/plot/test/{testSaveAction.py => test_saveaction.py} (100%) rename src/silx/gui/plot/test/{testScatterMaskToolsWidget.py => test_scattermasktoolswidget.py} (100%) rename src/silx/gui/plot/test/{testScatterView.py => test_scatterview.py} (100%) rename src/silx/gui/plot/test/{testStackView.py => test_stackview.py} (100%) rename src/silx/gui/plot/test/{testStats.py => test_stats.py} (100%) rename src/silx/gui/plot/test/{testUtilsAxis.py => test_utilsaxis.py} (100%) rename src/silx/gui/plot/tools/test/{testCurveLegendsWidget.py => test_curvelegendswidget.py} (100%) rename src/silx/gui/plot/tools/test/{testProfile.py => test_profile.py} (100%) rename src/silx/gui/plot/tools/test/{testRoiCore.py => test_roicore.py} (100%) rename src/silx/gui/plot/tools/test/{testRoiItems.py => test_roiitems.py} (100%) rename src/silx/gui/plot/tools/test/{testScatterProfileToolBar.py => test_scatterprofiletoolbar.py} (100%) rename src/silx/gui/plot/tools/test/{testTools.py => test_tools.py} (100%) rename src/silx/gui/plot3d/test/{testGL.py => test_gl.py} (100%) rename src/silx/gui/plot3d/test/{testScalarFieldView.py => test_scalarfieldview.py} (100%) rename src/silx/gui/plot3d/test/{testSceneWidget.py => test_scenewidget.py} (100%) rename src/silx/gui/plot3d/test/{testSceneWidgetPicking.py => test_scenewidgetpicking.py} (100%) rename src/silx/gui/plot3d/test/{testSceneWindow.py => test_scenewindow.py} (100%) rename src/silx/gui/plot3d/test/{testStatsWidget.py => test_statswidget.py} (100%) rename src/silx/gui/plot3d/tools/test/{testPositionInfoWidget.py => test_positioninfowidget.py} (100%) rename src/silx/gui/utils/test/{test.py => test_blocksignals.py} (100%) diff --git a/src/silx/gui/fit/test/testBackgroundWidget.py b/src/silx/gui/fit/test/test_backgroundwidget.py similarity index 100% rename from src/silx/gui/fit/test/testBackgroundWidget.py rename to src/silx/gui/fit/test/test_backgroundwidget.py diff --git a/src/silx/gui/fit/test/testFitConfig.py b/src/silx/gui/fit/test/test_fitconfig.py similarity index 100% rename from src/silx/gui/fit/test/testFitConfig.py rename to src/silx/gui/fit/test/test_fitconfig.py diff --git a/src/silx/gui/fit/test/testFitWidget.py b/src/silx/gui/fit/test/test_fitwidget.py similarity index 100% rename from src/silx/gui/fit/test/testFitWidget.py rename to src/silx/gui/fit/test/test_fitwidget.py diff --git a/src/silx/gui/plot/test/testAlphaSlider.py b/src/silx/gui/plot/test/test_alphaslider.py similarity index 100% rename from src/silx/gui/plot/test/testAlphaSlider.py rename to src/silx/gui/plot/test/test_alphaslider.py diff --git a/src/silx/gui/plot/test/testAxis.py b/src/silx/gui/plot/test/test_axis.py similarity index 100% rename from src/silx/gui/plot/test/testAxis.py rename to src/silx/gui/plot/test/test_axis.py diff --git a/src/silx/gui/plot/test/testColorBar.py b/src/silx/gui/plot/test/test_colorbar.py similarity index 100% rename from src/silx/gui/plot/test/testColorBar.py rename to src/silx/gui/plot/test/test_colorbar.py diff --git a/src/silx/gui/plot/test/testCompareImages.py b/src/silx/gui/plot/test/test_compareimages.py similarity index 100% rename from src/silx/gui/plot/test/testCompareImages.py rename to src/silx/gui/plot/test/test_compareimages.py diff --git a/src/silx/gui/plot/test/testComplexImageView.py b/src/silx/gui/plot/test/test_compleximageview.py similarity index 100% rename from src/silx/gui/plot/test/testComplexImageView.py rename to src/silx/gui/plot/test/test_compleximageview.py diff --git a/src/silx/gui/plot/test/testCurvesROIWidget.py b/src/silx/gui/plot/test/test_curvesroiwidget.py similarity index 100% rename from src/silx/gui/plot/test/testCurvesROIWidget.py rename to src/silx/gui/plot/test/test_curvesroiwidget.py diff --git a/src/silx/gui/plot/test/testImageStack.py b/src/silx/gui/plot/test/test_imagestack.py similarity index 100% rename from src/silx/gui/plot/test/testImageStack.py rename to src/silx/gui/plot/test/test_imagestack.py diff --git a/src/silx/gui/plot/test/testImageView.py b/src/silx/gui/plot/test/test_imageview.py similarity index 100% rename from src/silx/gui/plot/test/testImageView.py rename to src/silx/gui/plot/test/test_imageview.py diff --git a/src/silx/gui/plot/test/testInteraction.py b/src/silx/gui/plot/test/test_interaction.py similarity index 100% rename from src/silx/gui/plot/test/testInteraction.py rename to src/silx/gui/plot/test/test_interaction.py diff --git a/src/silx/gui/plot/test/testItem.py b/src/silx/gui/plot/test/test_item.py similarity index 100% rename from src/silx/gui/plot/test/testItem.py rename to src/silx/gui/plot/test/test_item.py diff --git a/src/silx/gui/plot/test/testLegendSelector.py b/src/silx/gui/plot/test/test_legendselector.py similarity index 100% rename from src/silx/gui/plot/test/testLegendSelector.py rename to src/silx/gui/plot/test/test_legendselector.py diff --git a/src/silx/gui/plot/test/testLimitConstraints.py b/src/silx/gui/plot/test/test_limitconstraints.py similarity index 100% rename from src/silx/gui/plot/test/testLimitConstraints.py rename to src/silx/gui/plot/test/test_limitconstraints.py diff --git a/src/silx/gui/plot/test/testMaskToolsWidget.py b/src/silx/gui/plot/test/test_masktoolswidget.py similarity index 100% rename from src/silx/gui/plot/test/testMaskToolsWidget.py rename to src/silx/gui/plot/test/test_masktoolswidget.py diff --git a/src/silx/gui/plot/test/testPixelIntensityHistoAction.py b/src/silx/gui/plot/test/test_pixelintensityhistoaction.py similarity index 100% rename from src/silx/gui/plot/test/testPixelIntensityHistoAction.py rename to src/silx/gui/plot/test/test_pixelintensityhistoaction.py diff --git a/src/silx/gui/plot/test/testPlotActions.py b/src/silx/gui/plot/test/test_plotactions.py similarity index 100% rename from src/silx/gui/plot/test/testPlotActions.py rename to src/silx/gui/plot/test/test_plotactions.py diff --git a/src/silx/gui/plot/test/testPlotInteraction.py b/src/silx/gui/plot/test/test_plotinteraction.py similarity index 100% rename from src/silx/gui/plot/test/testPlotInteraction.py rename to src/silx/gui/plot/test/test_plotinteraction.py diff --git a/src/silx/gui/plot/test/testPlotWidget.py b/src/silx/gui/plot/test/test_plotwidget.py similarity index 100% rename from src/silx/gui/plot/test/testPlotWidget.py rename to src/silx/gui/plot/test/test_plotwidget.py diff --git a/src/silx/gui/plot/test/testPlotWidgetActiveItem.py b/src/silx/gui/plot/test/test_plotwidgetactiveitem.py similarity index 100% rename from src/silx/gui/plot/test/testPlotWidgetActiveItem.py rename to src/silx/gui/plot/test/test_plotwidgetactiveitem.py diff --git a/src/silx/gui/plot/test/testPlotWidgetDataMargins.py b/src/silx/gui/plot/test/test_plotwidgetdatamargins.py similarity index 100% rename from src/silx/gui/plot/test/testPlotWidgetDataMargins.py rename to src/silx/gui/plot/test/test_plotwidgetdatamargins.py diff --git a/src/silx/gui/plot/test/testPlotWidgetNoBackend.py b/src/silx/gui/plot/test/test_plotwidgetnobackend.py similarity index 100% rename from src/silx/gui/plot/test/testPlotWidgetNoBackend.py rename to src/silx/gui/plot/test/test_plotwidgetnobackend.py diff --git a/src/silx/gui/plot/test/testPlotWindow.py b/src/silx/gui/plot/test/test_plotwindow.py similarity index 100% rename from src/silx/gui/plot/test/testPlotWindow.py rename to src/silx/gui/plot/test/test_plotwindow.py diff --git a/src/silx/gui/plot/test/testRoiStatsWidget.py b/src/silx/gui/plot/test/test_roistatswidget.py similarity index 100% rename from src/silx/gui/plot/test/testRoiStatsWidget.py rename to src/silx/gui/plot/test/test_roistatswidget.py diff --git a/src/silx/gui/plot/test/testSaveAction.py b/src/silx/gui/plot/test/test_saveaction.py similarity index 100% rename from src/silx/gui/plot/test/testSaveAction.py rename to src/silx/gui/plot/test/test_saveaction.py diff --git a/src/silx/gui/plot/test/testScatterMaskToolsWidget.py b/src/silx/gui/plot/test/test_scattermasktoolswidget.py similarity index 100% rename from src/silx/gui/plot/test/testScatterMaskToolsWidget.py rename to src/silx/gui/plot/test/test_scattermasktoolswidget.py diff --git a/src/silx/gui/plot/test/testScatterView.py b/src/silx/gui/plot/test/test_scatterview.py similarity index 100% rename from src/silx/gui/plot/test/testScatterView.py rename to src/silx/gui/plot/test/test_scatterview.py diff --git a/src/silx/gui/plot/test/testStackView.py b/src/silx/gui/plot/test/test_stackview.py similarity index 100% rename from src/silx/gui/plot/test/testStackView.py rename to src/silx/gui/plot/test/test_stackview.py diff --git a/src/silx/gui/plot/test/testStats.py b/src/silx/gui/plot/test/test_stats.py similarity index 100% rename from src/silx/gui/plot/test/testStats.py rename to src/silx/gui/plot/test/test_stats.py diff --git a/src/silx/gui/plot/test/testUtilsAxis.py b/src/silx/gui/plot/test/test_utilsaxis.py similarity index 100% rename from src/silx/gui/plot/test/testUtilsAxis.py rename to src/silx/gui/plot/test/test_utilsaxis.py diff --git a/src/silx/gui/plot/tools/test/testCurveLegendsWidget.py b/src/silx/gui/plot/tools/test/test_curvelegendswidget.py similarity index 100% rename from src/silx/gui/plot/tools/test/testCurveLegendsWidget.py rename to src/silx/gui/plot/tools/test/test_curvelegendswidget.py diff --git a/src/silx/gui/plot/tools/test/testProfile.py b/src/silx/gui/plot/tools/test/test_profile.py similarity index 100% rename from src/silx/gui/plot/tools/test/testProfile.py rename to src/silx/gui/plot/tools/test/test_profile.py diff --git a/src/silx/gui/plot/tools/test/testRoiCore.py b/src/silx/gui/plot/tools/test/test_roicore.py similarity index 100% rename from src/silx/gui/plot/tools/test/testRoiCore.py rename to src/silx/gui/plot/tools/test/test_roicore.py diff --git a/src/silx/gui/plot/tools/test/testRoiItems.py b/src/silx/gui/plot/tools/test/test_roiitems.py similarity index 100% rename from src/silx/gui/plot/tools/test/testRoiItems.py rename to src/silx/gui/plot/tools/test/test_roiitems.py diff --git a/src/silx/gui/plot/tools/test/testScatterProfileToolBar.py b/src/silx/gui/plot/tools/test/test_scatterprofiletoolbar.py similarity index 100% rename from src/silx/gui/plot/tools/test/testScatterProfileToolBar.py rename to src/silx/gui/plot/tools/test/test_scatterprofiletoolbar.py diff --git a/src/silx/gui/plot/tools/test/testTools.py b/src/silx/gui/plot/tools/test/test_tools.py similarity index 100% rename from src/silx/gui/plot/tools/test/testTools.py rename to src/silx/gui/plot/tools/test/test_tools.py diff --git a/src/silx/gui/plot3d/test/testGL.py b/src/silx/gui/plot3d/test/test_gl.py similarity index 100% rename from src/silx/gui/plot3d/test/testGL.py rename to src/silx/gui/plot3d/test/test_gl.py diff --git a/src/silx/gui/plot3d/test/testScalarFieldView.py b/src/silx/gui/plot3d/test/test_scalarfieldview.py similarity index 100% rename from src/silx/gui/plot3d/test/testScalarFieldView.py rename to src/silx/gui/plot3d/test/test_scalarfieldview.py diff --git a/src/silx/gui/plot3d/test/testSceneWidget.py b/src/silx/gui/plot3d/test/test_scenewidget.py similarity index 100% rename from src/silx/gui/plot3d/test/testSceneWidget.py rename to src/silx/gui/plot3d/test/test_scenewidget.py diff --git a/src/silx/gui/plot3d/test/testSceneWidgetPicking.py b/src/silx/gui/plot3d/test/test_scenewidgetpicking.py similarity index 100% rename from src/silx/gui/plot3d/test/testSceneWidgetPicking.py rename to src/silx/gui/plot3d/test/test_scenewidgetpicking.py diff --git a/src/silx/gui/plot3d/test/testSceneWindow.py b/src/silx/gui/plot3d/test/test_scenewindow.py similarity index 100% rename from src/silx/gui/plot3d/test/testSceneWindow.py rename to src/silx/gui/plot3d/test/test_scenewindow.py diff --git a/src/silx/gui/plot3d/test/testStatsWidget.py b/src/silx/gui/plot3d/test/test_statswidget.py similarity index 100% rename from src/silx/gui/plot3d/test/testStatsWidget.py rename to src/silx/gui/plot3d/test/test_statswidget.py diff --git a/src/silx/gui/plot3d/tools/test/testPositionInfoWidget.py b/src/silx/gui/plot3d/tools/test/test_positioninfowidget.py similarity index 100% rename from src/silx/gui/plot3d/tools/test/testPositionInfoWidget.py rename to src/silx/gui/plot3d/tools/test/test_positioninfowidget.py diff --git a/src/silx/gui/utils/test/test.py b/src/silx/gui/utils/test/test_blocksignals.py similarity index 100% rename from src/silx/gui/utils/test/test.py rename to src/silx/gui/utils/test/test_blocksignals.py From d720d95879f4031d0d75763d353fbd4484fb8c10 Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Mon, 17 Jun 2024 15:28:40 +0200 Subject: [PATCH 05/30] Remove setting of test file,class,function: using pytest defaults --- pyproject.toml | 6 ------ src/silx/test/__init__.py | 3 --- 2 files changed, 9 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 4b95617e42..021b00bf42 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,12 +13,6 @@ safe = true [tool.pytest.ini_options] minversion = "6.0" -python_files = [ - "test/test*.py", - "test/Test*.py", -] -python_classes = "Test" -python_functions = "test" filterwarnings = [ "error", # note the use of single quote below to denote "raw" strings in TOML diff --git a/src/silx/test/__init__.py b/src/silx/test/__init__.py index 0f3d5ded04..bc5b9d8715 100644 --- a/src/silx/test/__init__.py +++ b/src/silx/test/__init__.py @@ -54,9 +54,6 @@ def run_tests(module: str = "silx", verbosity: int = 0, args=()): module, "--verbosity", str(verbosity), - '-o python_files=["test/test*.py","test/Test*.py"]', - '-o python_classes=["Test"]', - '-o python_functions=["test"]', ] + list(args), check=False, From ce9ab9b40b54d3fa6e3c1fa68c044c9dd218844f Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Mon, 17 Jun 2024 15:41:36 +0200 Subject: [PATCH 06/30] Move handling of warnings as errors to silx.test.run_tests --- pyproject.toml | 7 ------- src/silx/test/__init__.py | 4 ++++ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 021b00bf42..8094c56e8e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,10 +13,3 @@ safe = true [tool.pytest.ini_options] minversion = "6.0" -filterwarnings = [ - "error", - # note the use of single quote below to denote "raw" strings in TOML - 'ignore:tostring\(\) is deprecated. Use tobytes\(\) instead\.:DeprecationWarning:OpenGL', - 'ignore:Jupyter is migrating its paths to use standard platformdirs:DeprecationWarning', - 'ignore:Unable to import recommended hash:UserWarning:pytools', -] diff --git a/src/silx/test/__init__.py b/src/silx/test/__init__.py index bc5b9d8715..2a122b6658 100644 --- a/src/silx/test/__init__.py +++ b/src/silx/test/__init__.py @@ -54,6 +54,10 @@ def run_tests(module: str = "silx", verbosity: int = 0, args=()): module, "--verbosity", str(verbosity), + # Handle warning as errors unless explicitly skipped + "-Werror", + "-Wignore:tostring() is deprecated. Use tobytes() instead.:DeprecationWarning:OpenGL", + "-Wignore:Jupyter is migrating its paths to use standard platformdirs:DeprecationWarning", ] + list(args), check=False, From a5f05d77b900f0a4056f8cb78c6038f929f0ad72 Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Mon, 17 Jun 2024 15:46:30 +0200 Subject: [PATCH 07/30] use silx.test.run_tests instead of run_tests.py to run the tests --- .github/workflows/ci.yml | 10 +--------- ci/appveyor.yml | 8 ++------ 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index afd993aacf..db874fcfff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,46 +26,39 @@ jobs: python-version: "3.8" BUILD_OPTION: --sdist QT_BINDING: PyQt5 - RUN_TESTS_OPTIONS: --qt-binding=PyQt5 --no-opencl --low-mem - name-suffix: "PyQt5 wheel" os: macos-latest python-version: "3.10" BUILD_OPTION: --wheel QT_BINDING: PyQt5 - RUN_TESTS_OPTIONS: --qt-binding=PyQt5 --no-opencl --low-mem - name-suffix: "PySide6 sdist" os: ubuntu-latest python-version: "3.8" BUILD_OPTION: --sdist QT_BINDING: PySide6 - RUN_TESTS_OPTIONS: --qt-binding=PySide6 --no-opencl --low-mem - name-suffix: "PySide6 wheel" os: macos-latest python-version: "3.9" BUILD_OPTION: --wheel QT_BINDING: "PySide6<6.7" - RUN_TESTS_OPTIONS: --qt-binding=PySide6 --no-opencl --low-mem - name-suffix: "PyQt6 wheel" os: ubuntu-latest python-version: "3.11" BUILD_OPTION: --wheel QT_BINDING: PyQt6 - RUN_TESTS_OPTIONS: --qt-binding=PyQt6 --no-opengl --low-mem - name-suffix: "PyQt6 wheel" os: macos-latest python-version: "3.11" BUILD_OPTION: --wheel QT_BINDING: PyQt6 - RUN_TESTS_OPTIONS: --qt-binding=PyQt6 --no-opencl --low-mem - name-suffix: "PyQt5 wheel" os: windows-latest python-version: "3.9" BUILD_COMMAND: --wheel QT_BINDING: PyQt5 - RUN_TESTS_OPTIONS: --qt-binding=PyQt5 --low-mem # Steps represent a sequence of tasks that will be executed as part of the job steps: @@ -149,5 +142,4 @@ jobs: Xorg -noreset +extension GLX +extension RANDR +extension RENDER -logfile ./99.log -config ./ci/xorg.conf :99 & sleep 3 fi - echo "RUN_TESTS_OPTIONS="${{ matrix.RUN_TESTS_OPTIONS }} - python run_tests.py --installed -v ${{ matrix.RUN_TESTS_OPTIONS }} + python -c "import silx.test, sys; sys.exit(silx.test.run_tests(verbosity=1, args=('--low-mem', '--qt-binding=${{ matrix.QT_BINDING }}')));" diff --git a/ci/appveyor.yml b/ci/appveyor.yml index d4b0fa1790..65282e289f 100644 --- a/ci/appveyor.yml +++ b/ci/appveyor.yml @@ -27,19 +27,16 @@ environment: # Python 3.9 - PYTHON_DIR: "C:\\Python39-x64" QT_BINDING: "PyQt5" - WITH_GL_TEST: True PIP_OPTIONS: "-q --pre" # Python 3.12 - PYTHON_DIR: "C:\\Python312-x64" QT_BINDING: "PySide6<6.7" - WITH_GL_TEST: True PIP_OPTIONS: "-q --pre" # Python 3.11 - PYTHON_DIR: "C:\\Python311-x64" QT_BINDING: "PyQt6" - WITH_GL_TEST: True PIP_OPTIONS: "-q" @@ -56,7 +53,7 @@ install: - "python -m pip install %PIP_OPTIONS% --upgrade pip" # Download Mesa OpenGL in Python directory when testing OpenGL - - IF %WITH_GL_TEST%==True curl -fsS -o %PYTHON_DIR%\\opengl32.dll http://www.silx.org/pub/silx/continuous_integration/opengl32_mingw-mesa-x86_64.dll + - curl -fsS -o %PYTHON_DIR%\\opengl32.dll http://www.silx.org/pub/silx/continuous_integration/opengl32_mingw-mesa-x86_64.dll build_script: # Create build virtualenv @@ -118,8 +115,7 @@ before_test: test_script: # Run tests with selected Qt binding and without OpenCL - - echo "WITH_GL_TEST=%WITH_GL_TEST%" - - "python run_tests.py --installed -v --no-opencl --low-mem" + - python -c "import silx.test, sys; sys.exit(silx.test.run_tests(verbosity=1, args=('--no-opencl', '--low-mem', '--qt-binding=%QT_BINDING%')));" after_test: # Leave test virtualenv From 618ad66416527f8e8580f5cd1f204bc4c83faade Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Mon, 17 Jun 2024 17:13:54 +0200 Subject: [PATCH 08/30] Rework run_tests --- src/silx/test/__init__.py | 43 +++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/silx/test/__init__.py b/src/silx/test/__init__.py index 2a122b6658..f04c381a9e 100644 --- a/src/silx/test/__init__.py +++ b/src/silx/test/__init__.py @@ -24,6 +24,7 @@ """This package provides test of the root modules """ +import importlib import logging import subprocess import sys @@ -38,6 +39,9 @@ raise +import silx + + def run_tests(module: str = "silx", verbosity: int = 0, args=()): """Run tests in a subprocess @@ -45,20 +49,25 @@ def run_tests(module: str = "silx", verbosity: int = 0, args=()): :param verbosity: Requested level of verbosity :param args: List of extra arguments to pass to `pytest` """ - return subprocess.run( - [ - sys.executable, - "-m", - "pytest", - "--pyargs", - module, - "--verbosity", - str(verbosity), - # Handle warning as errors unless explicitly skipped - "-Werror", - "-Wignore:tostring() is deprecated. Use tobytes() instead.:DeprecationWarning:OpenGL", - "-Wignore:Jupyter is migrating its paths to use standard platformdirs:DeprecationWarning", - ] - + list(args), - check=False, - ).returncode + # Retrieve folder for packages and file for modules + imported_module = importlib.import_module(module) + if hasattr(imported_module, "__path__"): + tested_path = imported_module.__path__[0] + else: + tested_path = imported_module.__file__ + + cmd = [ + sys.executable, + "-m", + "pytest", + tested_path, + f"--rootdir={silx.__path__[0]}", + "--verbosity", + str(verbosity), + # Handle warning as errors unless explicitly skipped + "-Werror", + "-Wignore:tostring() is deprecated. Use tobytes() instead.:DeprecationWarning", + "-Wignore:Jupyter is migrating its paths to use standard platformdirs:DeprecationWarning", + ] + list(args) + + return subprocess.run(cmd, check=False).returncode From ab2927f4eda1ef02b732600e2d607f6969b836a8 Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Tue, 18 Jun 2024 09:37:18 +0200 Subject: [PATCH 09/30] remove PySide6 pinning --- .github/workflows/ci.yml | 2 +- ci/appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index db874fcfff..2d1305a719 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,7 +41,7 @@ jobs: os: macos-latest python-version: "3.9" BUILD_OPTION: --wheel - QT_BINDING: "PySide6<6.7" + QT_BINDING: PySide6 - name-suffix: "PyQt6 wheel" os: ubuntu-latest diff --git a/ci/appveyor.yml b/ci/appveyor.yml index 65282e289f..3e50a18c40 100644 --- a/ci/appveyor.yml +++ b/ci/appveyor.yml @@ -31,7 +31,7 @@ environment: # Python 3.12 - PYTHON_DIR: "C:\\Python312-x64" - QT_BINDING: "PySide6<6.7" + QT_BINDING: "PySide6" PIP_OPTIONS: "-q --pre" # Python 3.11 From 7b5a0b109ee813374dd087be29fb4469c425d82a Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Tue, 18 Jun 2024 09:55:31 +0200 Subject: [PATCH 10/30] pin pyside6<6.7 and do not uninstall already installed binding --- .github/workflows/ci.yml | 6 +----- ci/appveyor.yml | 1 - 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2d1305a719..16aa267196 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -109,12 +109,8 @@ jobs: - name: Pre-install dependencies run: | - if [ -s "ci/requirements-pinned.txt" ]; - then - pip install -r ci/requirements-pinned.txt; - fi + pip install -r ci/requirements-pinned.txt pip install --pre -r requirements.txt - pip uninstall -y PyQt5 PyQt6 PySide6 pip install --pre "${{ matrix.QT_BINDING }}" - name: Install pytest diff --git a/ci/appveyor.yml b/ci/appveyor.yml index 3e50a18c40..6ccf029335 100644 --- a/ci/appveyor.yml +++ b/ci/appveyor.yml @@ -91,7 +91,6 @@ before_test: - pip install %PIP_OPTIONS% -r requirements.txt # Install selected Qt binding - - "pip uninstall -y PyQt5 PySide6 PyQt6" - pip install %PIP_OPTIONS% "%QT_BINDING%" # Install pytest From 9029ffdd45ebcb44abb3048a82fe6815b8b29eb0 Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Tue, 18 Jun 2024 15:20:06 +0200 Subject: [PATCH 11/30] Avoid UserWarning when passing swmr=True and mode!='r' to h5py.File --- src/silx/io/test/test_h5py_utils.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/silx/io/test/test_h5py_utils.py b/src/silx/io/test/test_h5py_utils.py index 0d10a785a3..276814d6bb 100644 --- a/src/silx/io/test/test_h5py_utils.py +++ b/src/silx/io/test/test_h5py_utils.py @@ -1,5 +1,5 @@ # /*########################################################################## -# Copyright (C) 2016-2017 European Synchrotron Radiation Facility +# Copyright (C) 2016-2024 European Synchrotron Radiation Facility # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -76,10 +76,16 @@ def _subprocess_context(contextmgr, *args, **kw): def _open_context(filename, **kw): try: print(os.getpid(), "OPEN", filename, kw) + swmr_writer = kw.get("mode", "r") != "r" and kw.get("swmr", False) + if swmr_writer: + kw.pop("swmr") # Avoid a warning with h5py_utils.File(filename, **kw) as f: if kw.get("mode") == "w": f["check"] = True f.flush() + if swmr_writer: + f.swmr_mode = True # SWMR mode must be enabled afterwards + f.flush() yield f except Exception: print(" ", os.getpid(), "FAILED", filename, kw) From 1518c2a8e0416691932f684850ddadc6c83e4d62 Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Thu, 20 Jun 2024 17:26:55 +0200 Subject: [PATCH 12/30] Add filterwarnings for python3.8 CI --- src/silx/gui/plot/test/test_plotwidget.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/silx/gui/plot/test/test_plotwidget.py b/src/silx/gui/plot/test/test_plotwidget.py index 9a8b277991..f781a8d0ad 100755 --- a/src/silx/gui/plot/test/test_plotwidget.py +++ b/src/silx/gui/plot/test/test_plotwidget.py @@ -2057,6 +2057,7 @@ class TestSpecial_ExplicitMplBackend(TestSpecialBackend): @pytest.mark.filterwarnings("ignore:All-NaN slice encountered:RuntimeWarning") +@pytest.mark.filterwarnings("ignore:.* converting a masked element to nan.:UserWarning") @pytest.mark.parametrize("plotWidget", ("mpl", "gl"), indirect=True) @pytest.mark.parametrize( "xerror,yerror", From 69a07b509addf6ae2b6294eab3395dd308b02f7e Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Mon, 24 Jun 2024 16:53:35 +0200 Subject: [PATCH 13/30] Change asserts to print both provided and normalized paths --- .../gui/dialog/test/test_datafiledialog.py | 10 +++++----- .../gui/dialog/test/test_imagefiledialog.py | 20 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/silx/gui/dialog/test/test_datafiledialog.py b/src/silx/gui/dialog/test/test_datafiledialog.py index d781e6ffac..b690aab0b2 100644 --- a/src/silx/gui/dialog/test/test_datafiledialog.py +++ b/src/silx/gui/dialog/test/test_datafiledialog.py @@ -123,11 +123,11 @@ def qWaitForPendingActions(self, dialog): raise RuntimeError("Still have pending actions") def assertSamePath(self, path1, path2): - path1_ = os.path.normcase(os.path.realpath(path1)) - path2_ = os.path.normcase(os.path.realpath(path2)) - if path1_ != path2_: - # Use the unittest API to log and display error - self.assertEqual(path1, path2) + self.assertEqual( + os.path.normcase(os.path.realpath(path1)), + os.path.normcase(os.path.realpath(path2)), + msg=f"Paths differs: {path1} != {path2}", + ) class TestDataFileDialogInteraction(testutils.TestCaseQt, _UtilsMixin): diff --git a/src/silx/gui/dialog/test/test_imagefiledialog.py b/src/silx/gui/dialog/test/test_imagefiledialog.py index a70c1eb915..3324968f02 100644 --- a/src/silx/gui/dialog/test/test_imagefiledialog.py +++ b/src/silx/gui/dialog/test/test_imagefiledialog.py @@ -130,18 +130,18 @@ def qWaitForPendingActions(self, dialog): raise RuntimeError("Still have pending actions") def assertSamePath(self, path1, path2): - path1_ = os.path.normcase(os.path.realpath(path1)) - path2_ = os.path.normcase(os.path.realpath(path2)) - if path1_ != path2_: - # Use the unittest API to log and display error - self.assertEqual(path1, path2) + self.assertEqual( + os.path.normcase(os.path.realpath(path1)), + os.path.normcase(os.path.realpath(path2)), + msg=f"Paths differs: {path1} != {path2}", + ) def assertNotSamePath(self, path1, path2): - path1_ = os.path.normcase(os.path.realpath(path1)) - path2_ = os.path.normcase(os.path.realpath(path2)) - if path1_ == path2_: - # Use the unittest API to log and display error - self.assertNotEqual(path1, path2) + self.assertNotEqual( + os.path.normcase(os.path.realpath(path1)), + os.path.normcase(os.path.realpath(path2)), + msg=f"Paths are equals: {path1} == {path2}", + ) class TestImageFileDialogInteraction(testutils.TestCaseQt, _UtilsMixin): From f483058fc5a1deeaa844b72c37e7c8ea1d3e53be Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Mon, 24 Jun 2024 17:11:41 +0200 Subject: [PATCH 14/30] Ignore one more nan warning for test_plotwidget.py::testCurveErrors --- src/silx/gui/plot/test/test_plotwidget.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/silx/gui/plot/test/test_plotwidget.py b/src/silx/gui/plot/test/test_plotwidget.py index f781a8d0ad..0e273c11ef 100755 --- a/src/silx/gui/plot/test/test_plotwidget.py +++ b/src/silx/gui/plot/test/test_plotwidget.py @@ -1,6 +1,6 @@ # /*########################################################################## # -# Copyright (c) 2016-2023 European Synchrotron Radiation Facility +# Copyright (c) 2016-2024 European Synchrotron Radiation Facility # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -2058,6 +2058,7 @@ class TestSpecial_ExplicitMplBackend(TestSpecialBackend): @pytest.mark.filterwarnings("ignore:All-NaN slice encountered:RuntimeWarning") @pytest.mark.filterwarnings("ignore:.* converting a masked element to nan.:UserWarning") +@pytest.mark.filterwarnings("ignore:All-NaN axis encountered:RuntimeWarning") @pytest.mark.parametrize("plotWidget", ("mpl", "gl"), indirect=True) @pytest.mark.parametrize( "xerror,yerror", From e8494640caf7633fe4cae3a08f8af36a61d9b03b Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Tue, 25 Jun 2024 10:12:16 +0200 Subject: [PATCH 15/30] change way to deallocate widgets --- src/silx/gui/plot/tools/test/test_profile.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/silx/gui/plot/tools/test/test_profile.py b/src/silx/gui/plot/tools/test/test_profile.py index 61b95a6433..50e95f147d 100644 --- a/src/silx/gui/plot/tools/test/test_profile.py +++ b/src/silx/gui/plot/tools/test/test_profile.py @@ -63,9 +63,10 @@ def defaultPlot(self): self.qWaitForWindowExposed(widget) yield widget finally: + widget.setAttribute(qt.Qt.WA_DeleteOnClose) widget.close() - widget = None - self.qWait() + del widget + self.qapp.processEvents() @contextlib.contextmanager def imagePlot(self): @@ -77,9 +78,10 @@ def imagePlot(self): self.qWaitForWindowExposed(widget) yield widget finally: + widget.setAttribute(qt.Qt.WA_DeleteOnClose) widget.close() - widget = None - self.qWait() + del widget + self.qapp.processEvents() @contextlib.contextmanager def scatterPlot(self): @@ -101,9 +103,10 @@ def scatterPlot(self): self.qWaitForWindowExposed(widget) yield widget.getPlotWidget() finally: + widget.setAttribute(qt.Qt.WA_DeleteOnClose) widget.close() - widget = None - self.qWait() + del widget + self.qapp.processEvents() @contextlib.contextmanager def stackPlot(self): @@ -117,9 +120,10 @@ def stackPlot(self): self.qWaitForWindowExposed(widget) yield widget.getPlotWidget() finally: + widget.setAttribute(qt.Qt.WA_DeleteOnClose) widget.close() - widget = None - self.qWait() + del widget + self.qapp.processEvents() def waitPendingOperations(self, proflie): for _ in range(10): From f267bd064453b76248a9e129459b0da63cea1c4a Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Tue, 25 Jun 2024 14:02:37 +0200 Subject: [PATCH 16/30] avoid 8.3 filenames on Windows --- src/silx/gui/dialog/test/test_datafiledialog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/silx/gui/dialog/test/test_datafiledialog.py b/src/silx/gui/dialog/test/test_datafiledialog.py index b690aab0b2..7ef938d771 100644 --- a/src/silx/gui/dialog/test/test_datafiledialog.py +++ b/src/silx/gui/dialog/test/test_datafiledialog.py @@ -47,7 +47,7 @@ def setUpModule(): global _tmpDirectory - _tmpDirectory = tempfile.mkdtemp(prefix=__name__) + _tmpDirectory = os.path.realpath(tempfile.mkdtemp(prefix=__name__)) data = numpy.arange(100 * 100) data.shape = 100, 100 From 9008b3b8d0c641678b3b7218007618d16543b909 Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Tue, 25 Jun 2024 15:26:50 +0200 Subject: [PATCH 17/30] check urls as urls not as path --- .../gui/dialog/test/test_datafiledialog.py | 108 ++++++++++-------- .../gui/dialog/test/test_imagefiledialog.py | 75 ++++++------ 2 files changed, 101 insertions(+), 82 deletions(-) diff --git a/src/silx/gui/dialog/test/test_datafiledialog.py b/src/silx/gui/dialog/test/test_datafiledialog.py index 7ef938d771..7f6b17c09a 100644 --- a/src/silx/gui/dialog/test/test_datafiledialog.py +++ b/src/silx/gui/dialog/test/test_datafiledialog.py @@ -23,6 +23,8 @@ # ###########################################################################*/ """Test for silx.gui.hdf5 module""" +from __future__ import annotations + __authors__ = ["V. Valls"] __license__ = "MIT" __date__ = "08/03/2019" @@ -129,6 +131,22 @@ def assertSamePath(self, path1, path2): msg=f"Paths differs: {path1} != {path2}", ) + def assertSameUrls( + self, + url1: silx.io.url.DataUrl | str, + url2: silx.io.url.DataUrl | str, + ): + """Check that both DataUrls are equivalent""" + if isinstance(url1, str): + url1 = silx.io.url.DataUrl(url1) + if isinstance(url2, str): + url2 = silx.io.url.DataUrl(url2) + + self.assertEqual(url1.scheme(), url2.scheme()) + self.assertSamePath(url1.file_path(), url2.file_path()) + self.assertEqual(url1.data_path(), url2.data_path()) + self.assertEqual(url1.data_slice(), url2.data_slice()) + class TestDataFileDialogInteraction(testutils.TestCaseQt, _UtilsMixin): def tearDown(self): @@ -266,7 +284,7 @@ def testClickOnBackToParentTool(self): dialog.show() self.qWaitForWindowExposed(dialog) - url = testutils.findChildren(dialog, qt.QLineEdit, name="url")[0] + urlLineEdit = testutils.findChildren(dialog, qt.QLineEdit, name="url")[0] action = testutils.findChildren(dialog, qt.QAction, name="toParentAction")[0] toParentButton = testutils.getQToolButtonFromAction(action) filename = _tmpDirectory + "/data/data.h5" @@ -275,51 +293,47 @@ def testClickOnBackToParentTool(self): path = silx.io.url.DataUrl(file_path=filename, data_path="/group/image").path() dialog.selectUrl(path) self.qWaitForPendingActions(dialog) - path = silx.io.url.DataUrl( + url = silx.io.url.DataUrl( scheme="silx", file_path=filename, data_path="/group/image" - ).path() - self.assertSamePath(url.text(), path) + ) + self.assertSameUrls(urlLineEdit.text(), url) # test self.mouseClick(toParentButton, qt.Qt.LeftButton) self.qWaitForPendingActions(dialog) - path = silx.io.url.DataUrl( - scheme="silx", file_path=filename, data_path="/" - ).path() - self.assertSamePath(url.text(), path) + url = silx.io.url.DataUrl(scheme="silx", file_path=filename, data_path="/") + self.assertSameUrls(urlLineEdit.text(), url) self.mouseClick(toParentButton, qt.Qt.LeftButton) self.qWaitForPendingActions(dialog) - self.assertSamePath(url.text(), _tmpDirectory + "/data") + self.assertSamePath(urlLineEdit.text(), _tmpDirectory + "/data") self.mouseClick(toParentButton, qt.Qt.LeftButton) self.qWaitForPendingActions(dialog) - self.assertSamePath(url.text(), _tmpDirectory) + self.assertSamePath(urlLineEdit.text(), _tmpDirectory) def testClickOnBackToRootTool(self): dialog = self.createDialog() dialog.show() self.qWaitForWindowExposed(dialog) - url = testutils.findChildren(dialog, qt.QLineEdit, name="url")[0] + urlLineEdit = testutils.findChildren(dialog, qt.QLineEdit, name="url")[0] action = testutils.findChildren(dialog, qt.QAction, name="toRootFileAction")[0] button = testutils.getQToolButtonFromAction(action) filename = _tmpDirectory + "/data.h5" # init state - path = silx.io.url.DataUrl( + url = silx.io.url.DataUrl( scheme="silx", file_path=filename, data_path="/group/image" - ).path() - dialog.selectUrl(path) + ) + dialog.selectUrl(url.path()) self.qWaitForPendingActions(dialog) - self.assertSamePath(url.text(), path) + self.assertSameUrls(urlLineEdit.text(), url) self.assertTrue(button.isEnabled()) # test self.mouseClick(button, qt.Qt.LeftButton) self.qWaitForPendingActions(dialog) - path = silx.io.url.DataUrl( - scheme="silx", file_path=filename, data_path="/" - ).path() - self.assertSamePath(url.text(), path) + url = silx.io.url.DataUrl(scheme="silx", file_path=filename, data_path="/") + self.assertSameUrls(urlLineEdit.text(), url) # self.assertFalse(button.isEnabled()) def testClickOnBackToDirectoryTool(self): @@ -327,24 +341,24 @@ def testClickOnBackToDirectoryTool(self): dialog.show() self.qWaitForWindowExposed(dialog) - url = testutils.findChildren(dialog, qt.QLineEdit, name="url")[0] + urlLineEdit = testutils.findChildren(dialog, qt.QLineEdit, name="url")[0] action = testutils.findChildren(dialog, qt.QAction, name="toDirectoryAction")[0] button = testutils.getQToolButtonFromAction(action) filename = _tmpDirectory + "/data.h5" # init state - path = silx.io.url.DataUrl(file_path=filename, data_path="/group/image").path() - dialog.selectUrl(path) + url = silx.io.url.DataUrl(file_path=filename, data_path="/group/image") + dialog.selectUrl(url.path()) self.qWaitForPendingActions(dialog) - path = silx.io.url.DataUrl( + url = silx.io.url.DataUrl( scheme="silx", file_path=filename, data_path="/group/image" - ).path() - self.assertSamePath(url.text(), path) + ) + self.assertSameUrls(urlLineEdit.text(), url) self.assertTrue(button.isEnabled()) # test self.mouseClick(button, qt.Qt.LeftButton) self.qWaitForPendingActions(dialog) - self.assertSamePath(url.text(), _tmpDirectory) + self.assertSamePath(urlLineEdit.text(), _tmpDirectory) self.assertFalse(button.isEnabled()) # FIXME: There is an unreleased qt.QWidget without nameObject @@ -356,7 +370,7 @@ def testClickOnHistoryTools(self): dialog.show() self.qWaitForWindowExposed(dialog) - url = testutils.findChildren(dialog, qt.QLineEdit, name="url")[0] + urlLineEdit = testutils.findChildren(dialog, qt.QLineEdit, name="url")[0] forwardAction = testutils.findChildren( dialog, qt.QAction, name="forwardAction" )[0] @@ -371,15 +385,13 @@ def testClickOnHistoryTools(self): # Then we feed the history using selectPath dialog.selectUrl(filename) self.qWaitForPendingActions(dialog) - path2 = silx.io.url.DataUrl( - scheme="silx", file_path=filename, data_path="/" - ).path() - dialog.selectUrl(path2) + url = silx.io.url.DataUrl(scheme="silx", file_path=filename, data_path="/") + dialog.selectUrl(url.path()) self.qWaitForPendingActions(dialog) - path3 = silx.io.url.DataUrl( + url2 = silx.io.url.DataUrl( scheme="silx", file_path=filename, data_path="/group" - ).path() - dialog.selectUrl(path3) + ) + dialog.selectUrl(url2.path()) self.qWaitForPendingActions(dialog) self.assertFalse(forwardAction.isEnabled()) self.assertTrue(backwardAction.isEnabled()) @@ -389,14 +401,14 @@ def testClickOnHistoryTools(self): self.qWaitForPendingActions(dialog) self.assertTrue(forwardAction.isEnabled()) self.assertTrue(backwardAction.isEnabled()) - self.assertSamePath(url.text(), path2) + self.assertSameUrls(urlLineEdit.text(), url) button = testutils.getQToolButtonFromAction(forwardAction) self.mouseClick(button, qt.Qt.LeftButton) self.qWaitForPendingActions(dialog) self.assertFalse(forwardAction.isEnabled()) self.assertTrue(backwardAction.isEnabled()) - self.assertSamePath(url.text(), path3) + self.assertSameUrls(urlLineEdit.text(), url2) def testSelectImageFromEdf(self): dialog = self.createDialog() @@ -413,7 +425,7 @@ def testSelectImageFromEdf(self): dialog.selectUrl(url.path()) self.assertEqual(dialog._selectedData().shape, (100, 100)) self.assertSamePath(dialog.selectedFile(), filename) - self.assertSamePath(dialog.selectedUrl(), url.path()) + self.assertSameUrls(dialog.selectedUrl(), url) def testSelectImage(self): dialog = self.createDialog() @@ -422,14 +434,12 @@ def testSelectImage(self): # init state filename = _tmpDirectory + "/data.h5" - path = silx.io.url.DataUrl( - scheme="silx", file_path=filename, data_path="/image" - ).path() - dialog.selectUrl(path) + url = silx.io.url.DataUrl(scheme="silx", file_path=filename, data_path="/image") + dialog.selectUrl(url.path()) # test self.assertEqual(dialog._selectedData().shape, (100, 100)) self.assertSamePath(dialog.selectedFile(), filename) - self.assertSamePath(dialog.selectedUrl(), path) + self.assertSameUrls(dialog.selectedUrl(), url) def testSelectScalar(self): dialog = self.createDialog() @@ -438,14 +448,14 @@ def testSelectScalar(self): # init state filename = _tmpDirectory + "/data.h5" - path = silx.io.url.DataUrl( + url = silx.io.url.DataUrl( scheme="silx", file_path=filename, data_path="/scalar" - ).path() - dialog.selectUrl(path) + ) + dialog.selectUrl(url.path()) # test self.assertEqual(dialog._selectedData()[()], 10) self.assertSamePath(dialog.selectedFile(), filename) - self.assertSamePath(dialog.selectedUrl(), path) + self.assertSameUrls(dialog.selectedUrl(), url) def testSelectGroup(self): dialog = self.createDialog() @@ -489,9 +499,7 @@ def testSelectH5_Activate(self): self.qWaitForPendingActions(dialog) browser = testutils.findChildren(dialog, qt.QWidget, name="browser")[0] filename = _tmpDirectory + "/data.h5" - path = silx.io.url.DataUrl( - scheme="silx", file_path=filename, data_path="/" - ).path() + url = silx.io.url.DataUrl(scheme="silx", file_path=filename, data_path="/") index = browser.rootIndex().model().index(filename) # click browser.selectIndex(index) @@ -499,7 +507,7 @@ def testSelectH5_Activate(self): browser.activated.emit(index) self.qWaitForPendingActions(dialog) # test - self.assertSamePath(dialog.selectedUrl(), path) + self.assertSameUrls(dialog.selectedUrl(), url) def testSelectBadFileFormat_Activate(self): dialog = self.createDialog() diff --git a/src/silx/gui/dialog/test/test_imagefiledialog.py b/src/silx/gui/dialog/test/test_imagefiledialog.py index 3324968f02..44a3929b8e 100644 --- a/src/silx/gui/dialog/test/test_imagefiledialog.py +++ b/src/silx/gui/dialog/test/test_imagefiledialog.py @@ -23,6 +23,8 @@ # ###########################################################################*/ """Test for silx.gui.hdf5 module""" +from __future__ import annotations + __authors__ = ["V. Valls"] __license__ = "MIT" __date__ = "08/03/2019" @@ -143,6 +145,22 @@ def assertNotSamePath(self, path1, path2): msg=f"Paths are equals: {path1} == {path2}", ) + def assertSameUrls( + self, + url1: silx.io.url.DataUrl | str, + url2: silx.io.url.DataUrl | str, + ): + """Check that both DataUrls are equivalent""" + if isinstance(url1, str): + url1 = silx.io.url.DataUrl(url1) + if isinstance(url2, str): + url2 = silx.io.url.DataUrl(url2) + + self.assertEqual(url1.scheme(), url2.scheme()) + self.assertSamePath(url1.file_path(), url2.file_path()) + self.assertEqual(url1.data_path(), url2.data_path()) + self.assertEqual(url1.data_slice(), url2.data_slice()) + class TestImageFileDialogInteraction(testutils.TestCaseQt, _UtilsMixin): def tearDown(self): @@ -395,12 +413,11 @@ def testSelectImageFromEdf(self): # init state filename = _tmpDirectory + "/singleimage.edf" - path = filename - dialog.selectUrl(path) + dialog.selectUrl(filename) self.assertEqual(dialog.selectedImage().shape, (100, 100)) self.assertSamePath(dialog.selectedFile(), filename) - path = silx.io.url.DataUrl(scheme="fabio", file_path=filename).path() - self.assertSamePath(dialog.selectedUrl(), path) + url = silx.io.url.DataUrl(scheme="fabio", file_path=filename) + self.assertSameUrls(dialog.selectedUrl(), url) def testSelectImageFromEdf_Activate(self): dialog = self.createDialog() @@ -412,7 +429,7 @@ def testSelectImageFromEdf_Activate(self): self.qWaitForPendingActions(dialog) browser = testutils.findChildren(dialog, qt.QWidget, name="browser")[0] filename = _tmpDirectory + "/singleimage.edf" - path = silx.io.url.DataUrl(scheme="fabio", file_path=filename).path() + url = silx.io.url.DataUrl(scheme="fabio", file_path=filename).path() index = browser.rootIndex().model().index(filename) # click browser.selectIndex(index) @@ -422,7 +439,7 @@ def testSelectImageFromEdf_Activate(self): # test self.assertEqual(dialog.selectedImage().shape, (100, 100)) self.assertSamePath(dialog.selectedFile(), filename) - self.assertSamePath(dialog.selectedUrl(), path) + self.assertSameUrls(dialog.selectedUrl(), url) def testSelectFrameFromEdf(self): dialog = self.createDialog() @@ -431,16 +448,14 @@ def testSelectFrameFromEdf(self): # init state filename = _tmpDirectory + "/multiframe.edf" - path = silx.io.url.DataUrl( - scheme="fabio", file_path=filename, data_slice=(1,) - ).path() - dialog.selectUrl(path) + url = silx.io.url.DataUrl(scheme="fabio", file_path=filename, data_slice=(1,)) + dialog.selectUrl(url.path()) # test image = dialog.selectedImage() self.assertEqual(image.shape, (100, 100)) self.assertEqual(image[0, 0], 1) self.assertSamePath(dialog.selectedFile(), filename) - self.assertSamePath(dialog.selectedUrl(), path) + self.assertSameUrls(dialog.selectedUrl(), url) def testSelectImageFromMsk(self): dialog = self.createDialog() @@ -449,12 +464,12 @@ def testSelectImageFromMsk(self): # init state filename = _tmpDirectory + "/singleimage.msk" - path = silx.io.url.DataUrl(scheme="fabio", file_path=filename).path() - dialog.selectUrl(path) + url = silx.io.url.DataUrl(scheme="fabio", file_path=filename) + dialog.selectUrl(url.path()) # test self.assertEqual(dialog.selectedImage().shape, (100, 100)) self.assertSamePath(dialog.selectedFile(), filename) - self.assertSamePath(dialog.selectedUrl(), path) + self.assertSameUrls(dialog.selectedUrl(), url) def testSelectImageFromH5(self): dialog = self.createDialog() @@ -463,14 +478,12 @@ def testSelectImageFromH5(self): # init state filename = _tmpDirectory + "/data.h5" - path = silx.io.url.DataUrl( - scheme="silx", file_path=filename, data_path="/image" - ).path() - dialog.selectUrl(path) + url = silx.io.url.DataUrl(scheme="silx", file_path=filename, data_path="/image") + dialog.selectUrl(url.path()) # test self.assertEqual(dialog.selectedImage().shape, (100, 100)) self.assertSamePath(dialog.selectedFile(), filename) - self.assertSamePath(dialog.selectedUrl(), path) + self.assertSameUrls(dialog.selectedUrl(), url) def testSelectH5_Activate(self): dialog = self.createDialog() @@ -482,9 +495,7 @@ def testSelectH5_Activate(self): self.qWaitForPendingActions(dialog) browser = testutils.findChildren(dialog, qt.QWidget, name="browser")[0] filename = _tmpDirectory + "/data.h5" - path = silx.io.url.DataUrl( - scheme="silx", file_path=filename, data_path="/" - ).path() + url = silx.io.url.DataUrl(scheme="silx", file_path=filename, data_path="/") index = browser.rootIndex().model().index(filename) # click browser.selectIndex(index) @@ -492,7 +503,7 @@ def testSelectH5_Activate(self): browser.activated.emit(index) self.qWaitForPendingActions(dialog) # test - self.assertSamePath(dialog.selectedUrl(), path) + self.assertSameUrls(dialog.selectedUrl(), url) def testSelectFrameFromH5(self): dialog = self.createDialog() @@ -501,15 +512,15 @@ def testSelectFrameFromH5(self): # init state filename = _tmpDirectory + "/data.h5" - path = silx.io.url.DataUrl( + url = silx.io.url.DataUrl( scheme="silx", file_path=filename, data_path="/cube", data_slice=(1,) - ).path() - dialog.selectUrl(path) + ) + dialog.selectUrl(url.path()) # test self.assertEqual(dialog.selectedImage().shape, (100, 100)) self.assertEqual(dialog.selectedImage()[0, 0], 1) self.assertSamePath(dialog.selectedFile(), filename) - self.assertSamePath(dialog.selectedUrl(), path) + self.assertSameUrls(dialog.selectedUrl(), url) def testSelectSingleFrameFromH5(self): dialog = self.createDialog() @@ -518,18 +529,18 @@ def testSelectSingleFrameFromH5(self): # init state filename = _tmpDirectory + "/data.h5" - path = silx.io.url.DataUrl( + url = silx.io.url.DataUrl( scheme="silx", file_path=filename, data_path="/single_frame", data_slice=(0,), - ).path() - dialog.selectUrl(path) + ) + dialog.selectUrl(url.path()) # test self.assertEqual(dialog.selectedImage().shape, (100, 100)) self.assertEqual(dialog.selectedImage()[0, 0], 5) self.assertSamePath(dialog.selectedFile(), filename) - self.assertSamePath(dialog.selectedUrl(), path) + self.assertSameUrls(dialog.selectedUrl(), url) def testSelectBadFileFormat_Activate(self): dialog = self.createDialog() @@ -546,7 +557,7 @@ def testSelectBadFileFormat_Activate(self): browser.activated.emit(index) self.qWaitForPendingActions(dialog) # test - self.assertSamePath(dialog.selectedUrl(), filename) + self.assertSameUrls(dialog.selectedUrl(), filename) def _countSelectableItems(self, model, rootIndex): selectable = 0 From 4c6fbb8264b30106869fea5de424519bbc87899e Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Tue, 25 Jun 2024 16:24:05 +0200 Subject: [PATCH 18/30] use silx.test.run_tests from run_tests.py --- run_tests.py | 22 +++++----------------- src/silx/test/__init__.py | 26 +++++++++++++++++--------- 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/run_tests.py b/run_tests.py index 63f5612862..1dc6c99a46 100755 --- a/run_tests.py +++ b/run_tests.py @@ -170,22 +170,10 @@ def normalize_option(option): return os.path.join(PROJECT_PATH, *option_parts[2:]) return option - args = [normalize_option(p) for p in sys.argv[1:] if p != "--installed"] - - # Run test on PROJECT_PATH if nothing is specified - without_options = [a for a in args if not a.startswith("-")] - if len(without_options) == 0: - args += [PROJECT_PATH] - - argv = ["--rootdir", PROJECT_PATH] + args + test_module = importlib.import_module(f"{PROJECT_NAME}.test") sys.exit( - subprocess.run( - [ - sys.executable, - "-m", - "pytest", - ] - + argv, - check=False, - ).returncode + test_module.run_tests( + module=None, + args=[normalize_option(p) for p in sys.argv[1:] if p != "--installed"], + ) ) diff --git a/src/silx/test/__init__.py b/src/silx/test/__init__.py index f04c381a9e..3456b04d86 100644 --- a/src/silx/test/__init__.py +++ b/src/silx/test/__init__.py @@ -24,6 +24,9 @@ """This package provides test of the root modules """ +from __future__ import annotations + +from collections.abc import Sequence import importlib import logging import subprocess @@ -42,25 +45,21 @@ import silx -def run_tests(module: str = "silx", verbosity: int = 0, args=()): +def run_tests( + module: str | None = "silx", + verbosity: int = 0, + args: Sequence[str] = (), +): """Run tests in a subprocess :param module: Name of the silx module to test (default: 'silx') :param verbosity: Requested level of verbosity :param args: List of extra arguments to pass to `pytest` """ - # Retrieve folder for packages and file for modules - imported_module = importlib.import_module(module) - if hasattr(imported_module, "__path__"): - tested_path = imported_module.__path__[0] - else: - tested_path = imported_module.__file__ - cmd = [ sys.executable, "-m", "pytest", - tested_path, f"--rootdir={silx.__path__[0]}", "--verbosity", str(verbosity), @@ -70,4 +69,13 @@ def run_tests(module: str = "silx", verbosity: int = 0, args=()): "-Wignore:Jupyter is migrating its paths to use standard platformdirs:DeprecationWarning", ] + list(args) + if module is not None: + # Retrieve folder for packages and file for modules + imported_module = importlib.import_module(module) + cmd.append( + imported_module.__path__[0] + if hasattr(imported_module, "__path__") + else imported_module.__file__ + ) + return subprocess.run(cmd, check=False).returncode From 24228d8f9bf7405717bb8c980b78cc2d320e08eb Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Tue, 25 Jun 2024 16:33:15 +0200 Subject: [PATCH 19/30] update test matrix and use QT_API env var --- .github/workflows/ci.yml | 52 ++++++++++++++++++++++++---------------- ci/appveyor.yml | 10 ++++---- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16aa267196..a49b82a4ed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,40 +25,49 @@ jobs: os: ubuntu-20.04 python-version: "3.8" BUILD_OPTION: --sdist - QT_BINDING: PyQt5 - - name-suffix: "PyQt5 wheel" - os: macos-latest - python-version: "3.10" + QT_API: PyQt5 + - name-suffix: "PyQt6 wheel" + os: ubuntu-latest + python-version: "3.11" BUILD_OPTION: --wheel - QT_BINDING: PyQt5 - + QT_API: PyQt6 - name-suffix: "PySide6 sdist" os: ubuntu-latest - python-version: "3.8" + python-version: "3.12" BUILD_OPTION: --sdist - QT_BINDING: PySide6 - - name-suffix: "PySide6 wheel" + QT_API: PySide6 + + - name-suffix: "PyQt5 wheel" os: macos-latest - python-version: "3.9" + python-version: "3.10" BUILD_OPTION: --wheel - QT_BINDING: PySide6 - + QT_API: PyQt5 - name-suffix: "PyQt6 wheel" - os: ubuntu-latest - python-version: "3.11" + os: macos-latest + python-version: "3.12" BUILD_OPTION: --wheel - QT_BINDING: PyQt6 - - name-suffix: "PyQt6 wheel" + QT_API: PyQt6 + - name-suffix: "PySide6 wheel" os: macos-latest - python-version: "3.11" + python-version: "3.9" BUILD_OPTION: --wheel - QT_BINDING: PyQt6 + QT_API: PySide6 - name-suffix: "PyQt5 wheel" os: windows-latest python-version: "3.9" BUILD_COMMAND: --wheel - QT_BINDING: PyQt5 + QT_API: PyQt5 + - name-suffix: "PyQt6 wheel" + os: windows-latest + python-version: "3.12" + BUILD_COMMAND: --wheel + QT_API: PyQt6 + - name-suffix: "PySide6 wheel" + os: windows-latest + python-version: "3.10" + BUILD_COMMAND: --wheel + QT_API: PySide6 # Steps represent a sequence of tasks that will be executed as part of the job steps: @@ -111,7 +120,7 @@ jobs: run: | pip install -r ci/requirements-pinned.txt pip install --pre -r requirements.txt - pip install --pre "${{ matrix.QT_BINDING }}" + pip install --pre "${{ matrix.QT_API }}" - name: Install pytest run: | @@ -138,4 +147,5 @@ jobs: Xorg -noreset +extension GLX +extension RANDR +extension RENDER -logfile ./99.log -config ./ci/xorg.conf :99 & sleep 3 fi - python -c "import silx.test, sys; sys.exit(silx.test.run_tests(verbosity=1, args=('--low-mem', '--qt-binding=${{ matrix.QT_BINDING }}')));" + export QT_API=${{ matrix.QT_API }} + python -c "import silx.test, sys; sys.exit(silx.test.run_tests(verbosity=1, args=('--low-mem', '--qt-binding=${{ matrix.QT_API }}')));" diff --git a/ci/appveyor.yml b/ci/appveyor.yml index 6ccf029335..4558433236 100644 --- a/ci/appveyor.yml +++ b/ci/appveyor.yml @@ -26,17 +26,17 @@ environment: matrix: # Python 3.9 - PYTHON_DIR: "C:\\Python39-x64" - QT_BINDING: "PyQt5" + QT_API: "PyQt5" PIP_OPTIONS: "-q --pre" # Python 3.12 - PYTHON_DIR: "C:\\Python312-x64" - QT_BINDING: "PySide6" + QT_API: "PySide6" PIP_OPTIONS: "-q --pre" # Python 3.11 - PYTHON_DIR: "C:\\Python311-x64" - QT_BINDING: "PyQt6" + QT_API: "PyQt6" PIP_OPTIONS: "-q" @@ -91,7 +91,7 @@ before_test: - pip install %PIP_OPTIONS% -r requirements.txt # Install selected Qt binding - - pip install %PIP_OPTIONS% "%QT_BINDING%" + - pip install %PIP_OPTIONS% "%QT_API%" # Install pytest - "pip install %PIP_OPTIONS% pytest" @@ -114,7 +114,7 @@ before_test: test_script: # Run tests with selected Qt binding and without OpenCL - - python -c "import silx.test, sys; sys.exit(silx.test.run_tests(verbosity=1, args=('--no-opencl', '--low-mem', '--qt-binding=%QT_BINDING%')));" + - python -c "import silx.test, sys; sys.exit(silx.test.run_tests(verbosity=1, args=('--no-opencl', '--low-mem', '--qt-binding=%QT_API%')));" after_test: # Leave test virtualenv From baefd3c962fab008e04eef117948963101d2f1e6 Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Thu, 27 Jun 2024 11:32:04 +0200 Subject: [PATCH 20/30] Disable OpenGL tests on Windows --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a49b82a4ed..12c642b05f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,16 +58,22 @@ jobs: python-version: "3.9" BUILD_COMMAND: --wheel QT_API: PyQt5 + env: + WITH_GL_TEST: False - name-suffix: "PyQt6 wheel" os: windows-latest python-version: "3.12" BUILD_COMMAND: --wheel QT_API: PyQt6 + env: + WITH_GL_TEST: False - name-suffix: "PySide6 wheel" os: windows-latest python-version: "3.10" BUILD_COMMAND: --wheel QT_API: PySide6 + env: + WITH_GL_TEST: False # Steps represent a sequence of tasks that will be executed as part of the job steps: From 0ec10d954031cdb5b6baadb371c4f55c8ce6985e Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Thu, 11 Jul 2024 11:01:17 +0200 Subject: [PATCH 21/30] PySide6>=6.4 has a qWait function, let's use it --- src/silx/gui/utils/testutils.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/silx/gui/utils/testutils.py b/src/silx/gui/utils/testutils.py index 76d0b9b4ad..f97e0fab7b 100644 --- a/src/silx/gui/utils/testutils.py +++ b/src/silx/gui/utils/testutils.py @@ -323,17 +323,7 @@ def qWait(cls, ms=None): """ if ms is None: ms = cls.DEFAULT_TIMEOUT_WAIT - - if qt.BINDING == "PySide6": - # PySide has no qWait, provide a replacement - timeout = int(ms) - endTimeMS = int(time.time() * 1000) + timeout - qapp = qt.QApplication.instance() - while timeout > 0: - qapp.processEvents(qt.QEventLoop.AllEvents, timeout) - timeout = endTimeMS - int(time.time() * 1000) - else: - QTest.qWait(int(ms) + cls.TIMEOUT_WAIT) + QTest.qWait(int(ms) + cls.TIMEOUT_WAIT) def qWaitForWindowExposed(self, window, timeout=None): """Waits until the window is shown in the screen. From 870955b8a4c128f90538c6caae0fa8ede7e2658f Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Thu, 11 Jul 2024 15:14:35 +0200 Subject: [PATCH 22/30] run ci on macos x86_64 + disable fast fail --- .github/workflows/ci.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 12c642b05f..c8b591361e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,6 +19,7 @@ jobs: name: "Python ${{ matrix.python-version }} on ${{ matrix.os }} ${{ matrix.name-suffix }}" runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: include: - name-suffix: "PyQt5 sdist" @@ -38,17 +39,17 @@ jobs: QT_API: PySide6 - name-suffix: "PyQt5 wheel" - os: macos-latest + os: macos-13 python-version: "3.10" BUILD_OPTION: --wheel QT_API: PyQt5 - name-suffix: "PyQt6 wheel" - os: macos-latest + os: macos-13 python-version: "3.12" BUILD_OPTION: --wheel QT_API: PyQt6 - name-suffix: "PySide6 wheel" - os: macos-latest + os: macos-13 python-version: "3.9" BUILD_OPTION: --wheel QT_API: PySide6 From c00ba0e9e28472a8612544a7826c4bfc3e6e65d4 Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Thu, 11 Jul 2024 15:33:38 +0200 Subject: [PATCH 23/30] Fix test to delete the widget on close --- src/silx/gui/fit/test/test_backgroundwidget.py | 3 +++ src/silx/gui/plot/test/test_masktoolswidget.py | 2 ++ src/silx/gui/plot/test/test_scattermasktoolswidget.py | 2 ++ 3 files changed, 7 insertions(+) diff --git a/src/silx/gui/fit/test/test_backgroundwidget.py b/src/silx/gui/fit/test/test_backgroundwidget.py index 73e3fbab07..d851b9fd56 100644 --- a/src/silx/gui/fit/test/test_backgroundwidget.py +++ b/src/silx/gui/fit/test/test_backgroundwidget.py @@ -21,6 +21,7 @@ # THE SOFTWARE. # # ###########################################################################*/ +from silx.gui import qt from silx.gui.utils.testutils import TestCaseQt from .. import BackgroundWidget @@ -38,6 +39,8 @@ def setUp(self): self.qWaitForWindowExposed(self.bgdialog) def tearDown(self): + self.bgdialog.setAttribute(qt.Qt.WA_DeleteOnClose) + self.bgdialog.close() del self.bgdialog super(TestBackgroundWidget, self).tearDown() diff --git a/src/silx/gui/plot/test/test_masktoolswidget.py b/src/silx/gui/plot/test/test_masktoolswidget.py index 1428687311..7f97739d55 100644 --- a/src/silx/gui/plot/test/test_masktoolswidget.py +++ b/src/silx/gui/plot/test/test_masktoolswidget.py @@ -57,6 +57,8 @@ def setUp(self): self.maskWidget = self.widget.widget() def tearDown(self): + self.widget.setAttribute(qt.Qt.WA_DeleteOnClose) + self.widget.close() del self.maskWidget del self.widget super(TestMaskToolsWidget, self).tearDown() diff --git a/src/silx/gui/plot/test/test_scattermasktoolswidget.py b/src/silx/gui/plot/test/test_scattermasktoolswidget.py index 5dc14e1a7b..0ba3d5955b 100644 --- a/src/silx/gui/plot/test/test_scattermasktoolswidget.py +++ b/src/silx/gui/plot/test/test_scattermasktoolswidget.py @@ -60,6 +60,8 @@ def setUp(self): self.maskWidget = self.widget.widget() def tearDown(self): + self.widget.setAttribute(qt.Qt.WA_DeleteOnClose) + self.widget.close() del self.maskWidget del self.widget super(TestScatterMaskToolsWidget, self).tearDown() From f95fe90ffd5275b4603e18ad4854e3c9438a714d Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Thu, 11 Jul 2024 15:34:12 +0200 Subject: [PATCH 24/30] Accept SystemError during signal disconnect: occur in CI with PySide6.7 --- src/silx/gui/plot/MaskToolsWidget.py | 8 ++++---- src/silx/gui/plot/ScatterMaskToolsWidget.py | 4 ++-- src/silx/gui/plot/tools/profile/editors.py | 2 +- src/silx/gui/plot3d/SFViewParamTree.py | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/silx/gui/plot/MaskToolsWidget.py b/src/silx/gui/plot/MaskToolsWidget.py index 11bfc0629c..da8a4d17ee 100644 --- a/src/silx/gui/plot/MaskToolsWidget.py +++ b/src/silx/gui/plot/MaskToolsWidget.py @@ -396,7 +396,7 @@ def showEvent(self, event): self.plot.sigActiveImageChanged.disconnect( self._activeImageChangedAfterCare ) - except (RuntimeError, TypeError): + except (RuntimeError, TypeError, SystemError): pass # Sync with current active image @@ -406,14 +406,14 @@ def showEvent(self, event): def hideEvent(self, event): try: self.plot.sigActiveImageChanged.disconnect(self._activeImageChanged) - except (RuntimeError, TypeError): + except (RuntimeError, TypeError, SystemError): pass image = self.getMaskedItem() if image is not None: try: image.sigItemChanged.disconnect(self.__imageChanged) - except (RuntimeError, TypeError): + except (RuntimeError, TypeError, SystemError): pass # TODO should not happen if self.isMaskInteractionActivated(): @@ -503,7 +503,7 @@ def _setMaskedImage(self, image): # Disconnect from previous image try: previous.sigItemChanged.disconnect(self.__imageChanged) - except (RuntimeError, TypeError): + except (RuntimeError, TypeError, SystemError): pass # TODO fixme should not happen # Set the image diff --git a/src/silx/gui/plot/ScatterMaskToolsWidget.py b/src/silx/gui/plot/ScatterMaskToolsWidget.py index bf34220061..f036d2d808 100644 --- a/src/silx/gui/plot/ScatterMaskToolsWidget.py +++ b/src/silx/gui/plot/ScatterMaskToolsWidget.py @@ -284,7 +284,7 @@ def showEvent(self, event): self.plot.sigActiveScatterChanged.disconnect( self._activeScatterChangedAfterCare ) - except (RuntimeError, TypeError): + except (RuntimeError, TypeError, SystemError): pass self._activeScatterChanged(None, None) # Init mask + enable/disable widget self.plot.sigActiveScatterChanged.connect(self._activeScatterChanged) @@ -294,7 +294,7 @@ def hideEvent(self, event): # if the method is not connected this raises a TypeError and there is no way # to know the connected slots self.plot.sigActiveScatterChanged.disconnect(self._activeScatterChanged) - except (RuntimeError, TypeError): + except (RuntimeError, TypeError, SystemError): _logger.info(sys.exc_info()[1]) if self.isMaskInteractionActivated(): diff --git a/src/silx/gui/plot/tools/profile/editors.py b/src/silx/gui/plot/tools/profile/editors.py index d53f7755e4..29388a57d8 100644 --- a/src/silx/gui/plot/tools/profile/editors.py +++ b/src/silx/gui/plot/tools/profile/editors.py @@ -249,7 +249,7 @@ def __setEditor(self, widget, editor): if previousEditor is not None: try: previousEditor.sigDataCommited.disconnect(self._editorDataCommited) - except (RuntimeError, TypeError): + except (RuntimeError, TypeError, SystemError): pass layout.removeWidget(previousEditor) previousEditor.deleteLater() diff --git a/src/silx/gui/plot3d/SFViewParamTree.py b/src/silx/gui/plot3d/SFViewParamTree.py index 6eea5aeef1..f83fb3c89e 100644 --- a/src/silx/gui/plot3d/SFViewParamTree.py +++ b/src/silx/gui/plot3d/SFViewParamTree.py @@ -159,7 +159,7 @@ def _disconnectSignals(self): for signal, slot in self.__slots: try: signal.disconnect(slot) - except TypeError: + except (RuntimeError, TypeError, SystemError): pass def _enableRow(self, enable): @@ -1692,7 +1692,7 @@ def setModel(self, model): self.__openPersistentEditors(qt.QModelIndex(), False) try: prevModel.rowsRemoved.disconnect(self.rowsRemoved) - except TypeError: + except (RuntimeError, TypeError, SystemError): pass super(TreeView, self).setModel(model) From c83f2da91a13566d161170d4cda433bbb6334ab6 Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Thu, 11 Jul 2024 16:12:20 +0200 Subject: [PATCH 25/30] wait longer for pending operations --- src/silx/gui/hdf5/test/test_hdf5.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/silx/gui/hdf5/test/test_hdf5.py b/src/silx/gui/hdf5/test/test_hdf5.py index 2bb25304f6..1eca368f60 100755 --- a/src/silx/gui/hdf5/test/test_hdf5.py +++ b/src/silx/gui/hdf5/test/test_hdf5.py @@ -73,10 +73,10 @@ def setUp(self): super(TestHdf5TreeModel, self).setUp() def waitForPendingOperations(self, model): - for _ in range(10): + for _ in range(20): if not model.hasPendingOperations(): break - self.qWait(10) + self.qWait(200) else: raise RuntimeError("Still waiting for a pending operation") @@ -440,10 +440,10 @@ def tearDown(self): TestCaseQt.tearDown(self) def waitForPendingOperations(self, model): - for _ in range(10): + for _ in range(20): if not model.hasPendingOperations(): break - self.qWait(10) + self.qWait(200) else: raise RuntimeError("Still waiting for a pending operation") From 3a239307eb9bd63d16cb9eaa6f724a0969f361f3 Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Fri, 12 Jul 2024 11:30:13 +0200 Subject: [PATCH 26/30] fix test failures --- src/silx/gui/fit/test/test_backgroundwidget.py | 2 ++ src/silx/gui/hdf5/test/test_hdf5.py | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/silx/gui/fit/test/test_backgroundwidget.py b/src/silx/gui/fit/test/test_backgroundwidget.py index d851b9fd56..2001ad75b0 100644 --- a/src/silx/gui/fit/test/test_backgroundwidget.py +++ b/src/silx/gui/fit/test/test_backgroundwidget.py @@ -46,7 +46,9 @@ def tearDown(self): def testShow(self): self.bgdialog.show() + self.qWaitForWindowExposed(self.bgdialog) self.bgdialog.hide() + self.qapp.processEvents() def testAccept(self): self.bgdialog.accept() diff --git a/src/silx/gui/hdf5/test/test_hdf5.py b/src/silx/gui/hdf5/test/test_hdf5.py index 1eca368f60..a3b8f43a35 100755 --- a/src/silx/gui/hdf5/test/test_hdf5.py +++ b/src/silx/gui/hdf5/test/test_hdf5.py @@ -383,7 +383,9 @@ def testDropLastAsFirst(self): h5_1 = commonh5.File("/foo/bar/1.mock", "w") h5_2 = commonh5.File("/foo/bar/2.mock", "w") model.insertH5pyObject(h5_1) + self.qapp.processEvents() model.insertH5pyObject(h5_2) + self.qapp.processEvents() self.assertEqual(self.getItemName(model, 0), "1.mock") self.assertEqual(self.getItemName(model, 1), "2.mock") index = model.index(1, 0, qt.QModelIndex()) @@ -397,7 +399,9 @@ def testDropFirstAsLast(self): h5_1 = commonh5.File("/foo/bar/1.mock", "w") h5_2 = commonh5.File("/foo/bar/2.mock", "w") model.insertH5pyObject(h5_1) + self.qapp.processEvents() model.insertH5pyObject(h5_2) + self.qapp.processEvents() self.assertEqual(self.getItemName(model, 0), "1.mock") self.assertEqual(self.getItemName(model, 1), "2.mock") index = model.index(0, 0, qt.QModelIndex()) From 36787b6a954ac04d8681e60631d7036129d58391 Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Fri, 12 Jul 2024 15:54:35 +0200 Subject: [PATCH 27/30] avoid the venv to be a sub-folder of the project dir --- ci/appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/appveyor.yml b/ci/appveyor.yml index 4558433236..f5e9bac6a9 100644 --- a/ci/appveyor.yml +++ b/ci/appveyor.yml @@ -21,7 +21,7 @@ environment: global: WIN_SDK_ROOT: "C:\\Program Files\\Microsoft SDKs\\Windows" VENV_BUILD_DIR: "venv_build" - VENV_TEST_DIR: "venv_test" + VENV_TEST_DIR: "..\\venv_test" matrix: # Python 3.9 From 355f92d0bb19fe8b88e8ec5954255be5391ae6d5 Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Thu, 29 Aug 2024 11:34:43 +0200 Subject: [PATCH 28/30] Update ignored warnings --- src/silx/test/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/silx/test/__init__.py b/src/silx/test/__init__.py index 3456b04d86..379bd0d8b0 100644 --- a/src/silx/test/__init__.py +++ b/src/silx/test/__init__.py @@ -65,8 +65,10 @@ def run_tests( str(verbosity), # Handle warning as errors unless explicitly skipped "-Werror", - "-Wignore:tostring() is deprecated. Use tobytes() instead.:DeprecationWarning", + "-Wignore:tostring() is deprecated. Use tobytes() instead.:DeprecationWarning:OpenGL.GL.VERSION.GL_2_0", "-Wignore:Jupyter is migrating its paths to use standard platformdirs:DeprecationWarning", + "-Wignore:Unable to import recommended hash 'siphash24.siphash13', falling back to 'hashlib.sha256'. Run 'python3 -m pip install siphash24' to install the recommended hash.:UserWarning:pytools.persistent_dict", + ] + list(args) if module is not None: From 6e51b7b2a18f99e7cf181d3e01fe0b7a6e4ab125 Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Thu, 29 Aug 2024 16:57:56 +0200 Subject: [PATCH 29/30] ignore __array__ copy argument until h5py v3.12 is released --- src/silx/test/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/silx/test/__init__.py b/src/silx/test/__init__.py index 379bd0d8b0..98faae32b2 100644 --- a/src/silx/test/__init__.py +++ b/src/silx/test/__init__.py @@ -68,7 +68,8 @@ def run_tests( "-Wignore:tostring() is deprecated. Use tobytes() instead.:DeprecationWarning:OpenGL.GL.VERSION.GL_2_0", "-Wignore:Jupyter is migrating its paths to use standard platformdirs:DeprecationWarning", "-Wignore:Unable to import recommended hash 'siphash24.siphash13', falling back to 'hashlib.sha256'. Run 'python3 -m pip install siphash24' to install the recommended hash.:UserWarning:pytools.persistent_dict", - + # Remove __array__ ignore once h5py v3.12 is released + "-Wignore:__array__ implementation doesn't accept a copy keyword, so passing copy=False failed. __array__ must implement 'dtype' and 'copy' keyword arguments.:DeprecationWarning", ] + list(args) if module is not None: From bcaa18fc5bc0818601b19f1ed62db5a4ef7ed692 Mon Sep 17 00:00:00 2001 From: Thomas VINCENT Date: Thu, 11 Jul 2024 15:55:46 +0200 Subject: [PATCH 30/30] Rework CI config --- .github/workflows/ci.yml | 123 +++++++++++---------------------------- ci/appveyor.yml | 2 +- ci/xorg.conf | 21 ------- 3 files changed, 34 insertions(+), 112 deletions(-) delete mode 100644 ci/xorg.conf diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c8b591361e..11b0d93abf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,12 +1,9 @@ name: CI on: - # Triggers the workflow on push only for the master branch or pull request events push: - branches: [master] + branches: [main] pull_request: - - # Allows you to run this workflow manually from the Actions tab workflow_dispatch: defaults: @@ -16,143 +13,89 @@ defaults: jobs: # This workflow contains a single job called "build" build: - name: "Python ${{ matrix.python-version }} on ${{ matrix.os }} ${{ matrix.name-suffix }}" + name: "Python ${{ matrix.python-version }} on ${{ matrix.os }} ${{ matrix.QT_API }}" runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: - - name-suffix: "PyQt5 sdist" - os: ubuntu-20.04 + - os: ubuntu-20.04 python-version: "3.8" - BUILD_OPTION: --sdist QT_API: PyQt5 - - name-suffix: "PyQt6 wheel" - os: ubuntu-latest + - os: ubuntu-latest python-version: "3.11" - BUILD_OPTION: --wheel QT_API: PyQt6 - - name-suffix: "PySide6 sdist" - os: ubuntu-latest + - os: ubuntu-latest python-version: "3.12" - BUILD_OPTION: --sdist QT_API: PySide6 - - name-suffix: "PyQt5 wheel" - os: macos-13 + - os: macos-13 python-version: "3.10" - BUILD_OPTION: --wheel QT_API: PyQt5 - - name-suffix: "PyQt6 wheel" - os: macos-13 + - os: macos-13 python-version: "3.12" - BUILD_OPTION: --wheel QT_API: PyQt6 - - name-suffix: "PySide6 wheel" - os: macos-13 + - os: macos-13 python-version: "3.9" - BUILD_OPTION: --wheel QT_API: PySide6 - - name-suffix: "PyQt5 wheel" - os: windows-latest + - os: windows-latest python-version: "3.9" - BUILD_COMMAND: --wheel QT_API: PyQt5 - env: - WITH_GL_TEST: False - - name-suffix: "PyQt6 wheel" - os: windows-latest + - os: windows-latest python-version: "3.12" - BUILD_COMMAND: --wheel QT_API: PyQt6 - env: - WITH_GL_TEST: False - - name-suffix: "PySide6 wheel" - os: windows-latest + - os: windows-latest python-version: "3.10" - BUILD_COMMAND: --wheel QT_API: PySide6 - env: - WITH_GL_TEST: False - # Steps represent a sequence of tasks that will be executed as part of the job steps: - uses: actions/checkout@v4 - # Install X server packages + # Install packages: + # OpenCL lib and icd + # xvfb to run the GUI test headless # libegl1-mesa: Required by Qt xcb platform plugin - # ocl-icd-opencl-dev: OpenCL headers, lib and icd loader # libgl1-mesa-glx: For OpenGL # xserver-xorg-video-dummy: For OpenGL - # libxkbcommon-x11-0: needed for Qt plugins - - name: Install X server + # libxkbcommon-x11-0, ..: needed for Qt plugins + - name: Install system packages if: runner.os == 'Linux' run: | sudo apt-get update - sudo apt-get install libegl1-mesa ocl-icd-opencl-dev intel-opencl-icd libgl1-mesa-glx xserver-xorg-video-dummy libxkbcommon-x11-0 libxkbcommon0 libxkbcommon-dev libxcb-icccm4 libxcb-image0 libxcb-shm0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-render0 libxcb-shape0 libxcb-sync1 libxcb-xfixes0 libxcb-xinerama0 libxcb-xkb1 libxcb-cursor0 libxcb1 + sudo apt-get install ocl-icd-opencl-dev intel-opencl-icd xvfb libegl1-mesa libgl1-mesa-glx xserver-xorg-video-dummy libxkbcommon-x11-0 libxkbcommon0 libxkbcommon-dev libxcb-icccm4 libxcb-image0 libxcb-shm0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-render0 libxcb-shape0 libxcb-sync1 libxcb-xfixes0 libxcb-xinerama0 libxcb-xkb1 libxcb-cursor0 libxcb1 - # Runs a single command using the runners shell - - name: Set up Python - uses: actions/setup-python@v5 + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} cache: "pip" - - name: Install mesa OpenGL - if: runner.os == 'Windows' - run: | - curl -fsS -o opengl32.dll http://www.silx.org/pub/silx/continuous_integration/opengl32_mingw-mesa-x86_64.dll - - - name: Upgrade distribution modules + - name: Install build dependencies run: | - python -m pip install --upgrade pip - pip install --upgrade build setuptools wheel - pip install --upgrade --pre cython - - - name: Print python info used for build - run: | - python ./ci/info_platform.py + pip install --upgrade --pre build cython setuptools wheel pip list - - name: Generate source package or wheel + - name: Build + env: + MACOSX_DEPLOYMENT_TARGET: "10.9" run: | - if [ ${{ runner.os }} == 'macOS' ]; then - export MACOSX_DEPLOYMENT_TARGET=10.9; - fi - python -m build --no-isolation ${{ matrix.BUILD_OPTION }} + python -m build --no-isolation ls dist - - name: Pre-install dependencies + - name: Install run: | pip install -r ci/requirements-pinned.txt - pip install --pre -r requirements.txt pip install --pre "${{ matrix.QT_API }}" - - - name: Install pytest - run: | - pip install pytest - pip install pytest-xvfb - pip install pytest-mock - - - name: Install silx package - run: pip install --pre --find-links dist/ --no-cache-dir --no-index --no-build-isolation silx - - - name: Print python info used for tests - run: | + pip install --pre "$(ls dist/silx*.whl)[full,test]" python ./ci/info_platform.py pip list - # For Linux: Start X server with dummy video dirver - # Use this instead of Xvfb to have RANDR extension - # Otherwise there is a bug with Qt5.10.0 - - name: Run the tests + - name: Test + env: + QT_API: ${{ matrix.QT_API }} + SILX_TEST_LOW_MEM: "False" run: | - if [ ${{ runner.os }} == 'Linux' ]; then - export OCL_ICD_VENDORS=$(pwd)/intel_opencl_icd/vendors - export DISPLAY=:99.0 - Xorg -noreset +extension GLX +extension RANDR +extension RENDER -logfile ./99.log -config ./ci/xorg.conf :99 & - sleep 3 + if [ ${{ runner.os }} == 'Windows' ]; then + export WITH_GL_TEST=False fi - export QT_API=${{ matrix.QT_API }} - python -c "import silx.test, sys; sys.exit(silx.test.run_tests(verbosity=1, args=('--low-mem', '--qt-binding=${{ matrix.QT_API }}')));" + python -c "import silx.test, sys; sys.exit(silx.test.run_tests(verbosity=1, args=['--qt-binding=${{ matrix.QT_API }}']));" diff --git a/ci/appveyor.yml b/ci/appveyor.yml index f5e9bac6a9..4b5118dbd2 100644 --- a/ci/appveyor.yml +++ b/ci/appveyor.yml @@ -53,7 +53,7 @@ install: - "python -m pip install %PIP_OPTIONS% --upgrade pip" # Download Mesa OpenGL in Python directory when testing OpenGL - - curl -fsS -o %PYTHON_DIR%\\opengl32.dll http://www.silx.org/pub/silx/continuous_integration/opengl32_mingw-mesa-x86_64.dll + - curl -fsS -o %PYTHON_DIR%\\opengl32.dll https://www.silx.org/pub/silx/continuous_integration/opengl32_mingw-mesa-x86_64.dll build_script: # Create build virtualenv diff --git a/ci/xorg.conf b/ci/xorg.conf deleted file mode 100644 index 4038f31888..0000000000 --- a/ci/xorg.conf +++ /dev/null @@ -1,21 +0,0 @@ -Section "Device" - Identifier "Video Device" - Driver "dummy" -EndSection - -Section "Monitor" - Identifier "Monitor" - HorizSync 31.5-48.5 - VertRefresh 50-70 -EndSection - -Section "Screen" - Identifier "Default Screen" - Monitor "Monitor" - Device "Video Device" - DefaultDepth 24 - SubSection "Display" - Depth 24 - Modes "1024x768" - EndSubSection -EndSection