From 1565976475592d88432d30362ca94dbd3ed25ec7 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Tue, 18 Apr 2023 16:14:16 +0100 Subject: [PATCH] Fix CI (#1036) This gets a reasonable baseline CI working for Kiva and Enable. It covers Python 3.8 and 3.10 with a reasonable set of Kiva backends and ETS toolkits. It splits out the Linux Qt dependency installation into a separate action as is done in other ETS projects so it can be re-used across workflows. There are a number of test cases skipped: - WxPython on MacOS and Linux due to lack of appropriate wheels - PyQt5 on Python 3.10 due to #1037 - PyQt5 on Windows due to #1038 This also adds a skip to a Cairo backend test case due to #1035 and a couple of drive-by fixes to the Cairo Kiva backend (Cairo was previously not being tested). --- .github/actions/install-qt-support/README.md | 27 ++++++ .github/actions/install-qt-support/action.yml | 30 ++++++ .github/workflows/bleeding-edge.yml | 90 ++++++++++-------- .github/workflows/test-with-edm.yml | 47 ++-------- .github/workflows/test-with-pip.yml | 93 +++++++++++++++++++ enable/__init__.py | 16 ++++ kiva/cairo.py | 25 ++++- kiva/tests/test_cairo_drawing.py | 6 ++ pyproject.toml | 2 +- 9 files changed, 257 insertions(+), 79 deletions(-) create mode 100644 .github/actions/install-qt-support/README.md create mode 100644 .github/actions/install-qt-support/action.yml create mode 100644 .github/workflows/test-with-pip.yml diff --git a/.github/actions/install-qt-support/README.md b/.github/actions/install-qt-support/README.md new file mode 100644 index 000000000..47c86e009 --- /dev/null +++ b/.github/actions/install-qt-support/README.md @@ -0,0 +1,27 @@ +# Install Qt dependencies + +This action calls `apt-get` to install packages required for running Qt on Ubuntu. + +## Inputs + +There are no inputs. + +## Outputs + +There are no outputs. + +## Example usage + +```yml + +jobs: + test: + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v3 + - name: Install Qt dependencies + uses: ./.github/actions/install-qt-support +``` diff --git a/.github/actions/install-qt-support/action.yml b/.github/actions/install-qt-support/action.yml new file mode 100644 index 000000000..66b98ab7e --- /dev/null +++ b/.github/actions/install-qt-support/action.yml @@ -0,0 +1,30 @@ +name: install-qt-support +description: 'Install supporting OS packages for Qt-using code' +runs: + using: composite + steps: + - name: Install Linux packages for Qt + if: runner.os == 'Linux' + run: | + sudo apt-get update + 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 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 + sudo apt-get install libxcb-cursor0 + sudo apt-get install pulseaudio + sudo apt-get install libpulse-mainloop-glib0 + # Needed to work around https://bugreports.qt.io/browse/PYSIDE-1547 + sudo apt-get install libopengl0 + # Needed for Qt6 video playback + sudo apt-get install libgstreamer-gl1.0-0 + shell: bash diff --git a/.github/workflows/bleeding-edge.yml b/.github/workflows/bleeding-edge.yml index 294c99e23..def91f6b7 100644 --- a/.github/workflows/bleeding-edge.yml +++ b/.github/workflows/bleeding-edge.yml @@ -12,9 +12,15 @@ jobs: test-ets: strategy: matrix: - os: [ubuntu-latest] - toolkit: ['pyside6', 'pyside2'] - python-version: [3.8] + os: ['ubuntu-latest', 'macos-latest', 'windows-latest'] + toolkit: ['null', 'pyside6', 'wx'] + python-version: ['3.10'] + exclude: + # No Wx wheels available for ubuntu or macos + - os: 'ubuntu-latest' + toolkit: 'wx' + - os: 'macos-latest' + toolkit: 'wx' runs-on: ${{ matrix.os }} steps: - name: Check out @@ -23,57 +29,63 @@ jobs: uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - - name: Install PySide2 (from PyPI) dependencies for Linux + - name: Install Qt dependencies + uses: ./.github/actions/install-qt-support + if: matrix.toolkit != 'wx' && matrix.toolkit != 'null' + - name: Install dependencies for Linux run: | - sudo apt-get update - 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 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 - if: matrix.toolkit != 'wx' - - name: Install Wx dependencies for Linux + # needed for GL + sudo apt-get install libglu1-mesa-dev + # needed for Celiagg + sudo apt-get install libfreetype-dev libharfbuzz-dev + # needed for Cairo + sudo apt-get install libcairo2-dev + if: matrix.os == 'ubuntu-latest' + - name: Install dependencies for Mac run: | - sudo apt-get update - sudo apt-get install libsdl2-2.0-0 - if: matrix.toolkit == 'wx' - - name: Install GL dependencies for Linux - run: sudo apt-get install libglu1-mesa-dev + brew install cairo + if: matrix.os == 'macos-latest' - name: Install build dependencies run: | - python -m pip install -U pip wheel - - name: Install prebuilt wxPython - run: python -m pip install -f https://extras.wxpython.org/wxPython4/extras/linux/gtk3/ubuntu-20.04 wxPython - if: matrix.toolkit == 'wx' - - name: Install prebuilt celiagg - run: python -m pip install celiagg + python -m pip install --upgrade pip wheel + - name: Install local packages + run: pip install ".[cairo,gl,layout,pdf,svg,test,${{ matrix.toolkit }}]" - name: Install source dependencies run: | - python -m pip install git+http://github.com/enthought/traits.git#egg=traits - python -m pip install git+http://github.com/enthought/pyface.git#egg=pyface[${{ matrix.toolkit }}] - python -m pip install git+http://github.com/enthought/traitsui.git#egg=traitsui[${{ matrix.toolkit }}] - - name: Install local packages - run: python -m pip install .[gl,layout,pdf,svg,test] + python -m pip install git+https://github.com/enthought/traits.git + python -m pip install git+https://github.com/enthought/pyface.git + python -m pip install git+https://github.com/enthought/traitsui.git + python -m pip install git+https://github.com/celiagg/celiagg.git - name: Sanity check package version run: python -m pip list - - name: Run enable test suite + - name: Run kiva test suite (Linux) + env: + PYTHONFAULTHANDLER: 1 + ETS_QT4_IMPORTS: 1 + run: xvfb-run python -m unittest discover -v kiva + if: matrix.os == 'ubuntu-latest' + working-directory: ${{ runner.temp }} + - name: Run kiva test suite (not Linux) env: PYTHONFAULTHANDLER: 1 + ETS_QT4_IMPORTS: 1 + run: python -m unittest discover -v kiva + if: matrix.os != 'ubuntu-latest' + working-directory: ${{ runner.temp }} + - name: Run enable test suite (Linux) + env: + PYTHONFAULTHANDLER: 1 + ETS_QT4_IMPORTS: 1 # kiva agg requires at least 15-bit color depth. run: xvfb-run --server-args="-screen 0 1024x768x24" python -m unittest discover -v enable + if: matrix.os == 'ubuntu-latest' working-directory: ${{ runner.temp }} - - name: Run kiva test suite + - name: Run enable test suite (not Linux) env: PYTHONFAULTHANDLER: 1 - run: xvfb-run python -m unittest discover -v kiva + ETS_QT4_IMPORTS: 1 + run: python -m unittest discover -v enable + if: matrix.os != 'ubuntu-latest' working-directory: ${{ runner.temp }} notify-on-failure: diff --git a/.github/workflows/test-with-edm.yml b/.github/workflows/test-with-edm.yml index bfde540e4..d9bc87da3 100644 --- a/.github/workflows/test-with-edm.yml +++ b/.github/workflows/test-with-edm.yml @@ -15,36 +15,14 @@ jobs: test-edm-linux: strategy: matrix: - toolkit: ['null', 'pyqt5', 'pyside2'] - runtime: ['3.6'] - include: - - toolkit: 'pyside6' - runtime: '3.8' - runs-on: ubuntu-latest + toolkit: ['null', 'pyside6'] + runtime: ['3.8'] + runs-on: "ubuntu-latest" steps: - uses: actions/checkout@v3 - - name: Install Qt dependencies for Linux - run: | - sudo apt-get update - 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 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 + - name: Install Qt dependencies + uses: ./.github/actions/install-qt-support if: matrix.toolkit != 'wx' - - name: Install Wx dependencies for Linux - run: | - sudo apt-get update - sudo apt-get install libsdl1.2-dev - if: matrix.toolkit == 'wx' - name: Install GL dependencies for Linux run: sudo apt-get install libglu1-mesa-dev - name: Cache EDM packages @@ -64,20 +42,13 @@ jobs: # kiva agg requires at least 15-bit color depth. run: xvfb-run --server-args="-screen 0 1024x768x24" edm run -- python ci/edmtool.py test --toolkit=${{ matrix.toolkit }} --runtime=${{ matrix.runtime }} - # Test against EDM packages on Windows and OSX + # Test against EDM packages on Windows test-with-edm: strategy: matrix: - os: [macos-latest, windows-latest] - toolkit: ['pyqt5', 'pyside2', 'wx'] - runtime: ['3.6'] - include: - - os: macos-latest - toolkit: 'pyside6' - runtime: '3.8' - - os: windows-latest - toolkit: 'pyside6' - runtime: '3.8' + os: ["windows-latest"] + toolkit: ['null', 'pyside6', 'wx'] + runtime: ['3.8'] runs-on: ${{ matrix.os }} env: # Set root directory, mainly for Windows, so that the EDM Python diff --git a/.github/workflows/test-with-pip.yml b/.github/workflows/test-with-pip.yml new file mode 100644 index 000000000..a343b8cb7 --- /dev/null +++ b/.github/workflows/test-with-pip.yml @@ -0,0 +1,93 @@ + +# This workflow installs dependencies from main branch + +name: Test with pip + +on: + pull_request: + # Make it possible to manually trigger the workflow + workflow_dispatch: + +jobs: + test-ets: + strategy: + matrix: + os: ['ubuntu-latest', 'macos-latest', 'windows-latest'] + toolkit: ['null', 'pyside2', 'pyside6', 'pyqt5', 'wx'] + python-version: ['3.8', '3.10'] + exclude: + # No Wx wheels available for ubuntu or macos + - os: 'ubuntu-latest' + toolkit: 'wx' + - os: 'macos-latest' + toolkit: 'wx' + # PyQt5 API doesn't automatically cast float -> int, see #1037 + - toolkit: 'pyqt5' + python-version: '3.10' + # Kiva tests hanging on windows, see #1038 + - os: 'windows-latest' + toolkit: 'pyqt5' + runs-on: ${{ matrix.os }} + steps: + - name: Check out + uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install Qt dependencies + uses: ./.github/actions/install-qt-support + if: matrix.toolkit != 'wx' && matrix.toolkit != 'null' + - name: Install dependencies for Linux + run: | + # needed for GL + sudo apt-get install libglu1-mesa-dev + # needed for Celiagg + sudo apt-get install libfreetype-dev libharfbuzz-dev + # needed for Cairo + sudo apt-get install libcairo2-dev + if: matrix.os == 'ubuntu-latest' + - name: Install dependencies for Mac + run: | + brew install cairo + if: matrix.os == 'macos-latest' + - name: Install build dependencies + run: | + python -m pip install --upgrade pip wheel + - name: Install local packages + run: pip install ".[cairo,gl,layout,pdf,svg,test,${{ matrix.toolkit }}]" + - name: Install celiagg manually + # This is needed until new release of celiagg + # - numpy is needed for install in current released version + run: pip install celiagg + - name: Sanity check package version + run: pip list + - name: Run kiva test suite (Linux) + env: + PYTHONFAULTHANDLER: 1 + ETS_QT4_IMPORTS: 1 + run: xvfb-run python -m unittest discover -v kiva + if: matrix.os == 'ubuntu-latest' + working-directory: ${{ runner.temp }} + - name: Run kiva test suite (not Linux) + env: + PYTHONFAULTHANDLER: 1 + ETS_QT4_IMPORTS: 1 + run: python -m unittest discover -v kiva + if: matrix.os != 'ubuntu-latest' + working-directory: ${{ runner.temp }} + - name: Run enable test suite (Linux) + env: + PYTHONFAULTHANDLER: 1 + ETS_QT4_IMPORTS: 1 + # kiva agg requires at least 15-bit color depth. + run: xvfb-run --server-args="-screen 0 1024x768x24" python -m unittest discover -v enable + if: matrix.os == 'ubuntu-latest' + working-directory: ${{ runner.temp }} + - name: Run enable test suite (not Linux) + env: + PYTHONFAULTHANDLER: 1 + ETS_QT4_IMPORTS: 1 + run: python -m unittest discover -v enable + if: matrix.os != 'ubuntu-latest' + working-directory: ${{ runner.temp }} diff --git a/enable/__init__.py b/enable/__init__.py index 8ec8cd4d4..0722a979b 100644 --- a/enable/__init__.py +++ b/enable/__init__.py @@ -32,9 +32,25 @@ "pdf": ["reportlab"], # Dependencies for SVG backend "svg": ["pyparsing"], + # Dependencies for Celiagg backend + "celiagg": ["celiagg"], + # Dependencies for Cairo backend + "cairo": ["pycairo"], # Dependencies purely for running tests. "test": [ "PyPDF2<3.0", # for pdf drawing tests in kiva. "setuptools", ], + # Dependencies for PySide6 + "pyside6": ["pyface[pyside6]", "traitsui[pyside6]"], + # Dependencies for PySide2 + "pyside2": ["pyface[pyside2]", "traitsui[pyside2]"], + # Dependencies for PyQt6 + "pyqt6": ["pyface[pyqt6]", "traitsui[pyqt6]"], + # Dependencies for PyQt5 + "pyqt5": ["pyface[pyqt5]", "traitsui[pyqt5]"], + # Dependencies for WxPython + "wx": ["pyface[wx]", "traitsui[wx]"], + # Dependencies for null backend (nothing right now) + "null": [], } diff --git a/kiva/cairo.py b/kiva/cairo.py index cdae9c61f..cfe0faaea 100644 --- a/kiva/cairo.py +++ b/kiva/cairo.py @@ -662,6 +662,29 @@ def curve_to(self, x_ctrl1, y_ctrl1, x_ctrl2, y_ctrl2, x_to, y_to): """ self._ctx.curve_to(x_ctrl1, y_ctrl1, x_ctrl2, y_ctrl2, x_to, y_to) + def quad_curve_to(self, x_ctrl, y_ctrl, x_to, y_to): + """ Draw a quadratic bezier curve from the current point. + + Parameters + ---------- + x_ctrl : float + X-value of the control point + y_ctrl : float + Y-value of the control point. + x_to : float + X-value of the ending point of the curve + y_to : float + Y-value of the ending point of the curve. + """ + # A quadratic Bezier curve is just a special case of the cubic. + # Can't reuse definition from superclass because we don't track current point + x0, y0 = self._ctx.get_current_point() + xc1 = (x0 + x_ctrl + x_ctrl) / 3.0 + yc1 = (y0 + y_ctrl + y_ctrl) / 3.0 + xc2 = (x_to + x_ctrl + x_ctrl) / 3.0 + yc2 = (y_to + y_ctrl + y_ctrl) / 3.0 + self.curve_to(xc1, yc1, xc2, yc2, x_to, y_to) + def arc(self, x, y, radius, start_angle, end_angle, cw=False): """ Draw a circular arc. @@ -972,7 +995,7 @@ def set_font(self, font): else: weight = cairo.FONT_WEIGHT_NORMAL - if font.style in contants.italic_styles: + if font.style in constants.italic_styles: style = cairo.FONT_SLANT_ITALIC else: style = cairo.FONT_SLANT_NORMAL diff --git a/kiva/tests/test_cairo_drawing.py b/kiva/tests/test_cairo_drawing.py index 413963057..221cd27ca 100644 --- a/kiva/tests/test_cairo_drawing.py +++ b/kiva/tests/test_cairo_drawing.py @@ -21,7 +21,13 @@ @unittest.skipIf(CAIRO_NOT_AVAILABLE, "Cannot import cairo") class TestCairoDrawing(DrawingImageTester, unittest.TestCase): + def create_graphics_context(self, width=600, height=600, pixel_scale=2.0): from kiva.cairo import GraphicsContext return GraphicsContext((width, height), base_pixel_scale=pixel_scale) + + # currently broken when scaling the image; see 1035 + @unittest.expectedFailure + def test_image(self): + super().test_image() diff --git a/pyproject.toml b/pyproject.toml index 7e84b62f6..54d49e451 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,3 @@ [build-system] -requires = ["cython", "oldest-supported-numpy", "setuptools<65.2", "swig", "wheel"] +requires = ["cython", "oldest-supported-numpy", "setuptools", "swig", "wheel"] build-backend = "setuptools.build_meta"