From 8144165fdf83f933be1587edade9836e767bea51 Mon Sep 17 00:00:00 2001 From: aaronayres35 <36972686+aaronayres35@users.noreply.github.com> Date: Mon, 23 Jan 2023 08:25:33 -0600 Subject: [PATCH 1/5] Fix CI on latest ubuntu (#849) * grab correct package set for using qt on ubuntu latest * same changes to other action --- .github/workflows/ets-from-source.yml | 7 ++++++- .github/workflows/test-with-edm.yml | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ets-from-source.yml b/.github/workflows/ets-from-source.yml index af80d7484..5cc91fe09 100644 --- a/.github/workflows/ets-from-source.yml +++ b/.github/workflows/ets-from-source.yml @@ -24,7 +24,11 @@ jobs: - name: Install Qt dependencies for Linux run: | sudo apt-get update - sudo apt-get install qt5-default + sudo apt-get install qtbase5-dev + sudo apt-get install qtchooser + sudo apt-get install qt5-qmake + sudo apt-get install qtbase5-dev-tools + sudo apt-get install libegl1 sudo apt-get install libxkbcommon-x11-0 sudo apt-get install libglu1-mesa-dev sudo apt-get install libxcb-icccm4 @@ -33,6 +37,7 @@ jobs: sudo apt-get install libxcb-randr0 sudo apt-get install libxcb-render-util0 sudo apt-get install libxcb-xinerama0 + sudo apt-get install libxcb-shape0 shell: bash if: runner.os == 'Linux' - name: Cache EDM packages diff --git a/.github/workflows/test-with-edm.yml b/.github/workflows/test-with-edm.yml index f3c7106ea..6ff7e9d98 100644 --- a/.github/workflows/test-with-edm.yml +++ b/.github/workflows/test-with-edm.yml @@ -22,7 +22,11 @@ jobs: - name: Install Qt dependencies for Linux run: | sudo apt-get update - sudo apt-get install qt5-default + sudo apt-get install qtbase5-dev + sudo apt-get install qtchooser + sudo apt-get install qt5-qmake + sudo apt-get install qtbase5-dev-tools + sudo apt-get install libegl1 sudo apt-get install libxkbcommon-x11-0 sudo apt-get install libglu1-mesa-dev sudo apt-get install libxcb-icccm4 @@ -31,6 +35,7 @@ jobs: sudo apt-get install libxcb-randr0 sudo apt-get install libxcb-render-util0 sudo apt-get install libxcb-xinerama0 + sudo apt-get install libxcb-shape0 if: matrix.toolkit != 'null' - name: Cache EDM packages uses: actions/cache@v2 From e56d56d3552dbb671adfc7d77488b26d2436dd5b Mon Sep 17 00:00:00 2001 From: aaronayres35 <36972686+aaronayres35@users.noreply.github.com> Date: Mon, 23 Jan 2023 09:18:58 -0600 Subject: [PATCH 2/5] Prevent axis related plot crash when plot is resized too small (#848) * dont raise if datalow > datahigh * additional NaN checking in auto_ticks * add some logging about the bad range coming from the mappper * better warning message --- chaco/axis.py | 17 +++++++++++------ chaco/ticks.py | 2 ++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/chaco/axis.py b/chaco/axis.py index d2818e168..9635b8fe5 100644 --- a/chaco/axis.py +++ b/chaco/axis.py @@ -10,6 +10,8 @@ """ Defines the PlotAxis class, and associated validator and UI. """ +import logging + # Major library import from numpy import ( array, @@ -58,6 +60,8 @@ from .label import Label from .log_mapper import LogMapper +logger = logging.getLogger(__name__) + def DEFAULT_TICK_FORMATTER(val): return ("%f" % val).rstrip("0").rstrip(".") @@ -500,20 +504,21 @@ def _compute_tick_positions(self, gc, overlay_component=None): screenlow, screenhigh = screenhigh, screenlow if ( - (datalow == datahigh) + (datalow >= datahigh) or (screenlow == screenhigh) or (datalow in [inf, -inf]) or (datahigh in [inf, -inf]) ): + if datalow > datahigh: + logger.warning( + "{self.mapper} has an invalid data range with " + "low={datalow} > high={datahigh}; unable to compute axis " + "ticks." + ) self._reset_cache() self._cache_valid = True return - if datalow > datahigh: - raise RuntimeError( - "DataRange low is greater than high; unable to compute axis ticks." - ) - if not self.tick_generator: return diff --git a/chaco/ticks.py b/chaco/ticks.py index 9eb160f81..284cb742b 100644 --- a/chaco/ticks.py +++ b/chaco/ticks.py @@ -305,6 +305,8 @@ def auto_ticks( if upper > end: end += tick_interval + if isnan([start, end]).any(): + return [] ticks = arange(start, end + (tick_interval / 2.0), tick_interval) if len(ticks) < 2: From 7276fd9fa4efa514d5226dfc0ef43b0b54a07b86 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Tue, 24 Jan 2023 07:55:06 +0000 Subject: [PATCH 3/5] Add Python 3.8 tests (#851) This adds a parallel set of tests for Python 3.8 rather than trying to adapt the current set of tests. Also updates edmtool to allow Python 3.8 and PySide6, and pyproject.toml to allow for known issue with setuptools. Needed to make one change to code due to newer version of NumPy being picked up. No requirement that they pass for PRs to be mergeable yet, but will likely add that once this PR is approved. --- .github/workflows/test-with-edm-3.8.yml | 80 +++++++++++++++++++++++++ chaco/plot_containers.py | 5 +- ci/edmtool.py | 12 ++-- pyproject.toml | 2 +- 4 files changed, 92 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/test-with-edm-3.8.yml diff --git a/.github/workflows/test-with-edm-3.8.yml b/.github/workflows/test-with-edm-3.8.yml new file mode 100644 index 000000000..19c5cfd61 --- /dev/null +++ b/.github/workflows/test-with-edm-3.8.yml @@ -0,0 +1,80 @@ +# This workflow targets stable released dependencies from EDM. +# Note that some packages may not actually be installed from EDM but from +# PyPI, see ci/edmtool.py implementations. + +name: Test with EDM on Python 3.8 + +on: pull_request + +env: + INSTALL_EDM_VERSION: 3.5.0 + +jobs: + + # Test against EDM packages on Linux + test-edm-linux-38: + strategy: + matrix: + toolkit: ['null', 'pyside6'] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Qt dependencies for Linux + run: | + sudo apt-get update + sudo apt-get install libegl1 + sudo apt-get install libxkbcommon-x11-0 + sudo apt-get install libglu1-mesa-dev + sudo apt-get install libxcb-icccm4 + sudo apt-get install libxcb-image0 + sudo apt-get install libxcb-keysyms1 + sudo apt-get install libxcb-randr0 + sudo apt-get install libxcb-render-util0 + sudo apt-get install libxcb-xinerama0 + sudo apt-get install libxcb-shape0 + # Needed to work around https://bugreports.qt.io/browse/PYSIDE-1547 + sudo apt-get install libopengl0 + if: matrix.toolkit != 'null' + - name: Cache EDM packages + uses: actions/cache@v2 + with: + path: ~/.cache + key: ${{ runner.os }}-3.8-${{ matrix.toolkit }}-${{ hashFiles('ci/edmtool.py') }} + - name: Setup EDM + uses: enthought/setup-edm-action@v1 + with: + edm-version: ${{ env.INSTALL_EDM_VERSION }} + - name: Install click to the default EDM environment + run: edm install -y wheel click coverage + - name: Install test environment + run: edm run -- python ci/edmtool.py install --runtime=3.8 --toolkit=${{ matrix.toolkit }} + - name: Flake8 + run: edm run -- python ci/edmtool.py flake8 --runtime=3.8 --toolkit=${{ matrix.toolkit }} + if: matrix.toolkit == 'null' + - name: Run tests + run: xvfb-run --server-args="-screen 0 1024x768x24" edm run -- python ci/edmtool.py test --runtime=3.8 --toolkit=${{ matrix.toolkit }} + + # Test against EDM packages on Windows and OSX + test-with-edm-38: + strategy: + matrix: + os: [macos-latest, windows-latest] + toolkit: ['null', 'pyside6'] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v2 + - name: Cache EDM packages + uses: actions/cache@v2 + with: + path: ~/.cache + key: ${{ runner.os }}-3.8-${{ matrix.toolkit }}-${{ hashFiles('ci/edmtool.py') }} + - name: Setup EDM + uses: enthought/setup-edm-action@v1 + with: + edm-version: ${{ env.INSTALL_EDM_VERSION }} + - name: Install click to the default EDM environment + run: edm install -y wheel click coverage + - name: Install test environment + run: edm run -- python ci/edmtool.py install --runtime=3.8 --toolkit=${{ matrix.toolkit }} + - name: Run tests + run: edm run -- python ci/edmtool.py test --runtime=3.8 --toolkit=${{ matrix.toolkit }} diff --git a/chaco/plot_containers.py b/chaco/plot_containers.py index 7ee0046e6..f5eaa4df4 100644 --- a/chaco/plot_containers.py +++ b/chaco/plot_containers.py @@ -18,6 +18,7 @@ array, cumsum, hstack, + resize, sum, zeros, zeros_like, @@ -182,7 +183,7 @@ class GridPlotContainer(BasePlotContainer): # The internal component grid, in row-major order. This gets updated # when any of the following traits change: shape, components, grid_components - _grid = Array + _grid = Array() _cached_total_size = Any _h_size_prefs = Any @@ -510,7 +511,7 @@ def _reflow_layout(self): numrows, numcols = divmod(len(self.components), self.shape[0]) self.shape = (numrows, numcols) grid = array(self.components, dtype=object) - grid.resize(self.shape) + grid = resize(grid, self.shape) grid[grid == 0] = None self._grid = grid self._layout_needed = True diff --git a/ci/edmtool.py b/ci/edmtool.py index 118ec605a..dbd8ddb1f 100644 --- a/ci/edmtool.py +++ b/ci/edmtool.py @@ -77,7 +77,8 @@ import click supported_combinations = { - '3.6': {'pyside2', 'pyqt', 'pyqt5', 'null'}, + '3.6': {'pyside2', 'pyqt5', 'null'}, + '3.8': {'pyside6', 'null'}, } dependencies = { @@ -95,7 +96,7 @@ "swig", } -pypi_dependencies = {"sphinx-copybutton"} +pypi_dependencies = {} # Dependencies we install from source for cron tests # Order from packages with the most dependencies to one with the least @@ -111,6 +112,7 @@ extra_dependencies = { 'pyside2': {'pyside2'}, + 'pyside6': {'pyside6'}, 'pyqt': {'pyqt'}, 'pyqt5': {'pyqt5'}, 'null': set() @@ -118,7 +120,8 @@ doc_dependencies = { "sphinx", - "enthought_sphinx_theme" + "enthought_sphinx_theme", + "sphinx-copybutton", } doc_ignore = { @@ -161,6 +164,7 @@ environment_vars = { 'pyside2': {'ETS_TOOLKIT': 'qt4', 'QT_API': 'pyside2'}, + 'pyside6': {'ETS_TOOLKIT': 'qt4', 'QT_API': 'pyside6'}, 'pyqt': {'ETS_TOOLKIT': 'qt4', 'QT_API': 'pyqt'}, 'pyqt5': {'ETS_TOOLKIT': 'qt4', 'QT_API': 'pyqt5'}, 'null': {'ETS_TOOLKIT': 'null.image'}, @@ -204,7 +208,7 @@ def install(runtime, toolkit, environment, editable, source): | ci_dependencies ) - if toolkit == "pyside2": + if toolkit.startswith("pyside"): addn_repositories = "--add-repository enthought/lgpl" else: addn_repositories = "" diff --git a/pyproject.toml b/pyproject.toml index b1256a319..26b1ea674 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["cython", "oldest-supported-numpy", "setuptools", "wheel"] +requires = ["cython", "oldest-supported-numpy", "setuptools<65.2", "wheel"] build-backend = "setuptools.build_meta" [tool.cibuildwheel] From a58ec5e950d19b47e21dbffec707b58d6b5db026 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Tue, 24 Jan 2023 07:55:25 +0000 Subject: [PATCH 4/5] MAINT: Use bool instead of deprecated np.bool (#852) Co-authored-by: Poruri Sai Rahul --- chaco/multi_array_data_source.py | 2 +- chaco/tools/lasso_selection.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/chaco/multi_array_data_source.py b/chaco/multi_array_data_source.py index 1c2aa8347..9663bb90e 100644 --- a/chaco/multi_array_data_source.py +++ b/chaco/multi_array_data_source.py @@ -13,7 +13,7 @@ import warnings # Major package imports -from numpy import nanmax, nanmin, array, shape, ones, bool, newaxis, nan_to_num +from numpy import nanmax, nanmin, array, shape, ones, newaxis, nan_to_num # Enthought library imports from traits.api import Any, Int, Tuple diff --git a/chaco/tools/lasso_selection.py b/chaco/tools/lasso_selection.py index cc17851f8..fa446189d 100644 --- a/chaco/tools/lasso_selection.py +++ b/chaco/tools/lasso_selection.py @@ -158,11 +158,11 @@ def normal_mouse_down(self, event): """ # We may want to generalize this for the n-dimensional case... - self._active_selection = empty((0, 2), dtype=numpy.bool) + self._active_selection = empty((0, 2), dtype=bool) if self.selection_datasource is not None: self.selection_datasource.metadata[self.metadata_name] = zeros( - len(self.selection_datasource.get_data()), dtype=numpy.bool + len(self.selection_datasource.get_data()), dtype=bool ) self.selection_mode = "include" self.event_state = "selecting" @@ -197,7 +197,7 @@ def selecting_mouse_up(self, event): self._update_selection() self._previous_selections.append(self._active_selection) - self._active_selection = empty((0, 2), dtype=numpy.bool) + self._active_selection = empty((0, 2), dtype=bool) def selecting_mouse_move(self, event): """Handles the mouse moving when the tool is in the 'selecting' state. @@ -246,12 +246,12 @@ def normal_key_pressed(self, event): # ---------------------------------------------------------------------- def _dataspace_points_default(self): - return empty((0, 2), dtype=numpy.bool) + return empty((0, 2), dtype=bool) def _reset(self): """Resets the selection""" self.event_state = "normal" - self._active_selection = empty((0, 2), dtype=numpy.bool) + self._active_selection = empty((0, 2), dtype=bool) self._previous_selections = [] self._update_selection() @@ -279,7 +279,7 @@ def _update_selection(self): return selected_mask = zeros( - self.selection_datasource._data.shape, dtype=numpy.bool + self.selection_datasource._data.shape, dtype=bool ) data = self._get_data() From be389d238dec30c638094d2071aa497b114e45c2 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Tue, 24 Jan 2023 13:07:14 +0000 Subject: [PATCH 5/5] Fix Test Warning on Python 3.8 (#856) This fixes #854 and fixes #855 --- chaco/plots/image_plot.py | 7 +++---- chaco/plots/tests/test_image_plot.py | 2 +- chaco/tools/pan_tool.py | 6 ++++-- chaco/tools/tests/test_data_label_tool.py | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/chaco/plots/image_plot.py b/chaco/plots/image_plot.py index 30284542a..a76b8f44d 100644 --- a/chaco/plots/image_plot.py +++ b/chaco/plots/image_plot.py @@ -22,13 +22,12 @@ from traits.api import ( Bool, Enum, + Float, Instance, - List, Range, Tuple, Property, cached_property, - Union, ) from kiva.agg import GraphicsContextArray @@ -84,7 +83,7 @@ class ImagePlot(Base2DPlot): # Tuple-defined rectangle (x, y, dx, dy) in screen space in which the # **_cached_image** is to be drawn. - _cached_dest_rect = Union(Tuple, List, transient=True) + _cached_dest_rect = Tuple(Float, Float, Float, Float, transient=True) # Bool indicating whether the origin is top-left or bottom-right. # The name "principal diagonal" is borrowed from linear algebra. @@ -285,7 +284,7 @@ def _compute_cached_image(self, data=None, mapper=None): # Update cached image and rectangle. self._cached_image = self._kiva_array_from_numpy_array(data) - self._cached_dest_rect = screen_rect + self._cached_dest_rect = tuple(screen_rect) self._image_cache_valid = True def _kiva_array_from_numpy_array(self, data): diff --git a/chaco/plots/tests/test_image_plot.py b/chaco/plots/tests/test_image_plot.py index 448af7637..afed30367 100644 --- a/chaco/plots/tests/test_image_plot.py +++ b/chaco/plots/tests/test_image_plot.py @@ -37,7 +37,7 @@ # The Quartz backend rescales pixel values, so use a higher threshold. MAX_RMS_ERROR = 16 if ETSConfig.kiva_backend == "quartz" else 1 -IMAGE = np.random.random_integers(0, 255, size=(100, 200)).astype(np.uint8) +IMAGE = np.random.randint(0, 256, size=(100, 200)).astype(np.uint8) RGB = np.dstack([IMAGE] * 3) # Rendering adds rows and columns for some reason. TRIM_RENDERED = (slice(1, -1), slice(1, -1), 0) diff --git a/chaco/tools/pan_tool.py b/chaco/tools/pan_tool.py index 61f2e28bd..66a4e9ae4 100644 --- a/chaco/tools/pan_tool.py +++ b/chaco/tools/pan_tool.py @@ -231,8 +231,10 @@ def panning_mouse_move(self, event): newlow = mapper.map_data(screenlow + screen_delta) # Use .set_bounds() so that we don't generate two range_changed - # events on the DataRange - mapper.range.set_bounds(newlow, newhigh) + # events on the DataRange. + # Do an explicit conversion to float because raw values may not + # be floating-point types, which makes NumPy unhappy (#854). + mapper.range.set_bounds(float(newlow), float(newhigh)) event.handled = True diff --git a/chaco/tools/tests/test_data_label_tool.py b/chaco/tools/tests/test_data_label_tool.py index c4deabb85..1f40c55af 100644 --- a/chaco/tools/tests/test_data_label_tool.py +++ b/chaco/tools/tests/test_data_label_tool.py @@ -10,7 +10,7 @@ from chaco.api import ArrayPlotData, DataLabel, Plot from chaco.tools.api import DataLabelTool -IMAGE = np.random.random_integers(0, 255, size=(100, 200)).astype(np.uint8) +IMAGE = np.random.randint(0, 256, size=(100, 200)).astype(np.uint8) RGB = np.dstack([IMAGE] * 3)