diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 00000000..a46193bb --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,55 @@ +name: analyze pyqtgraph + +on: pull_request + +jobs: + analyze: + name: analyze + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. + fetch-depth: 2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: '3.9' + - name: Install Dependencies + run: | + python -m pip install --upgrade pip + pip install PyQt5 numpy scipy six + echo "CODEQL_PYTHON=$(which python)" >> $GITHUB_ENV + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: 'python' + # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + queries: +security-and-quality + setup-python-dependencies: false + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000..e4c7f10c --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,166 @@ +name: test pyqtgraph + +on: [push, pull_request] + +jobs: + test: + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + qt-lib: [pyqt5, pyside2] + python-version: [3.7, 3.8, 3.9] + include: + - python-version: "3.7" + qt-version: "~=5.12.0" + numpy-version: "~=1.17.0" + - python-version: "3.8" + qt-version: "~=5.15.0" + numpy-version: "~=1.19.0" + - python-version: "3.9" + qt-version: "" + numpy-version: "" + steps: + - uses: actions/checkout@v2 + - name: Setup Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + # Semantic version range syntax or exact version of a Python version + python-version: ${{ matrix.python-version }} + - name: "Install Windows-Mesa OpenGL DLL" + if: runner.os == 'Windows' + run: | + curl -LJO https://github.com/pal1000/mesa-dist-win/releases/download/19.2.7/mesa3d-19.2.7-release-msvc.7z + 7z x mesa3d-19.2.7-release-msvc.7z + cd x64 + xcopy opengl32.dll C:\windows\system32\mesadrv.dll* + xcopy opengl32.dll C:\windows\syswow64\mesadrv.dll* + REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v DLL /t REG_SZ /d "mesadrv.dll" /f + REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v DriverVersion /t REG_DWORD /d 1 /f + REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v Flags /t REG_DWORD /d 1 /f + REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v Version /t REG_DWORD /d 2 /f + REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v DLL /t REG_SZ /d "mesadrv.dll" /f + REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v DriverVersion /t REG_DWORD /d 1 /f + REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v Flags /t REG_DWORD /d 1 /f + REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v Version /t REG_DWORD /d 2 /f + shell: cmd + - name: Install Dependencies + run: | + pip install --upgrade pip + pip install ${{ matrix.qt-lib }}${{ matrix.qt-version }} numpy${{ matrix.numpy-version }} scipy pyopengl h5py six matplotlib + pip install . + pip install pytest pytest-cov pytest-xdist coverage + - name: "Install Linux VirtualDisplay" + if: runner.os == 'Linux' + run: | + sudo apt-get install -y libxkbcommon-x11-0 x11-utils + sudo apt-get install --no-install-recommends -y libyaml-dev libegl1-mesa libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 + pip install pytest-xvfb + - name: 'Debug Info' + run: | + echo python location: `which python` + echo python version: `python --version` + echo pytest location: `which pytest` + echo installed packages + pip list + echo pyqtgraph system info + python -c "import pyqtgraph as pg; pg.systemInfo()" + shell: bash + env: + QT_DEBUG_PLUGINS: 1 + - name: 'XVFB Display Info' + run: | + xvfb-run --server-args="-screen 0, 1920x1200x24 -ac +extension GLX +render -noreset" python -c "from pyqtgraph.opengl.glInfo import GLTest" + xvfb-run --server-args="-screen 0, 1920x1200x24 -ac +extension GLX +render -noreset" python -m pyqtgraph.util.get_resolution + if: runner.os == 'Linux' + - name: 'Display Info' + run: | + python -c "from pyqtgraph.opengl.glInfo import GLTest" + python -m pyqtgraph.util.get_resolution + if: runner.os != 'Linux' + - name: Run Tests + run: | + mkdir $SCREENSHOT_DIR + pytest . -v \ + -n auto \ + --junitxml=junit/test-results.xml \ + --cov pyqtgraph --cov-report=xml --cov-report=html + shell: bash + - name: Upload Screenshots + uses: actions/upload-artifact@v2 + with: + name: Screenshots (Python ${{ matrix.python-version }} - Qt-Bindings ${{ matrix.qt-lib }} - OS ${{ matrix.os }}) + path: $SCREENSHOT_DIR + if-no-files-found: ignore + - name: Upload Unit Test Results + if: always() + uses: actions/upload-artifact@v2 + with: + name: Unit Test Results (Python ${{ matrix.python-version }} - Qt-Bindings ${{ matrix.qt-lib }} - OS ${{ matrix.os }}) + path: junit/test-results.xml + env: + SCREENSHOT_DIR: ./screenshots + + build-docs: + name: build docs + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Setup Python 3.9 + uses: actions/setup-python@v2 + with: + python-version: '3.9' + - name: Install Dependencies + run: | + cd doc + python -m pip install -r requirements.txt + - name: Build Documentation + run: | + cd doc + make html SPHINXOPTS='-W -v' + - name: Upload Build Docs + uses: actions/upload-artifact@v2 + with: + name: html-docs + path: | + doc/build/html + + linting: + name: check linting + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup Python 3.9 + uses: actions/setup-python@v2 + with: + python-version: '3.9' + - name: "Install Dependencies" + run: | + pip install flake8 + - name: "Check Linting" + run: | + python -m flake8 . + + build-wheel: + name: build wheel + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Setup Python 3.9 + uses: actions/setup-python@v2 + with: + python-version: '3.9' + - name: Build Wheel + run: | + python -m pip install setuptools wheel + python setup.py bdist_wheel + - name: Archive pyqtgraph wheel + uses: actions/upload-artifact@v2 + with: + name: wheel + path: | + dist diff --git a/README.md b/README.md index c86f5445..88f2a78c 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,14 @@ - -[![Build Status](https://pyqtgraph.visualstudio.com/pyqtgraph/_apis/build/status/pyqtgraph.pyqtgraph?branchName=master)](https://pyqtgraph.visualstudio.com/pyqtgraph/_build/latest?definitionId=17&branchName=master) -[![Documentation Status](https://readthedocs.org/projects/pyqtgraph/badge/?version=latest)](https://pyqtgraph.readthedocs.io/en/latest/?badge=latest) - PyQtGraph ========= +[![PyPi](https://img.shields.io/pypi/v/pyqtgraph.svg)](https://pypi.org/project/pyqtgraph/) +[![conda-forge](https://img.shields.io/conda/vn/conda-forge/pyqtgraph.svg)](https://anaconda.org/conda-forge/pyqtgraph) +[![Build Status](https://github.com/pyqtgraph/pyqtgraph/workflows/main/badge.svg)](https://github.com/pyqtgraph/pyqtgraph/workflows/main/badge.svg) +[![CodeQL Status](https://github.com/pyqtgraph/pyqtgraph/workflows/codeql-analysis/badge.svg)](https://github.com/pyqtgraph/pyqtgraph/workflows/codeql-analysis/badge.svg) +[![Documentation Status](https://readthedocs.org/projects/pyqtgraph/badge/?version=latest)](https://pyqtgraph.readthedocs.io/en/latest/?badge=latest) +[![Total alerts](https://img.shields.io/lgtm/alerts/g/pyqtgraph/pyqtgraph.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/pyqtgraph/pyqtgraph/alerts/) +[![Language grade: Python](https://img.shields.io/lgtm/grade/python/g/pyqtgraph/pyqtgraph.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/pyqtgraph/pyqtgraph/context:python) + A pure-Python graphics library for PyQt5/PySide2 Copyright 2020 Luke Campagnola, University of North Carolina at Chapel Hill @@ -24,21 +28,20 @@ pyqtgraph has adopted [NEP 29](https://numpy.org/neps/nep-0029-deprecation_polic This project supports: * All minor versions of Python released 42 months prior to the project, and at minimum the two latest minor versions. - * All minor versions of numpy released in the 24 months prior to the project, and at minimum the last three minor versions. * All minor versions of Qt 5 currently supported by upstream Qt (Note, Qt 6 support is not yet implemented) - Currently this means: * Python 3.7+ -* Qt 5.12, 5.15 +* Qt 5.12-5.15 * Required - * PyQt5, or PySide2 + * PyQt5 or PySide2 * `numpy` 1.17+ * Optional * `scipy` for image processing * `pyopengl` for 3D graphics + * `pyopengl` on macOS Big Sur only works with python 3.9.1+ * `hdf5` for large hdf5 binary format support Qt Bindings Test Matrix @@ -46,12 +49,12 @@ Qt Bindings Test Matrix The following table represents the python environments we test in our CI system. Our CI system uses Ubuntu 18.04, Windows Server 2019, and macOS 10.15 base images. -| Qt-Bindings | Python 3.6 | Python 3.7 | Python 3.8 | +| Qt-Bindings | Python 3.7 | Python 3.8 | Python 3.9 | | :------------- | :----------------: | :----------------: | :----------------: | -| PyQt5-5.9 | :white_check_mark: | :x: | :x: | -| PySide2-5.13 | :x: | :white_check_mark: | :x: | -| PyQt5-Latest | :x: | :x: | :white_check_mark: | -| PySide2-Latest | :x: | :x: | :white_check_mark: | +| PySide2-5.12 | :white_check_mark: | :x: | :x: | +| PyQt5-5.12 | :white_check_mark: | :x: | :x: | +| PySide2-5.15 | :x: | :white_check_mark: | :white_check_mark: | +| PyQt5-5.15 | :x: | :white_check_mark: | :white_check_mark: | Support ------- diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index 5e87923e..00000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,113 +0,0 @@ -trigger: - branches: - include: - - '*' # Build for all branches if they have a azure-pipelines.yml file. - tags: - include: - - 'v*' # Ensure that we are building for tags starting with 'v' (Official Versions) - -# Build only for PRs for master branch -pr: - autoCancel: true - branches: - include: - - master - - develop - -variables: - OFFICIAL_REPO: 'pyqtgraph/pyqtgraph' - DEFAULT_MERGE_BRANCH: 'master' - disable.coverage.autogenerate: 'true' - -stages: -- stage: pre_build - jobs: - - job: check_diff_size - pool: - vmImage: 'Ubuntu 18.04' - steps: - - bash: | - git config --global advice.detachedHead false - mkdir ~/repo-clone && cd ~/repo-clone - git init - - git remote add -t $(Build.SourceBranchName) origin $(Build.Repository.Uri) - git remote add -t ${DEFAULT_MERGE_BRANCH} upstream https://github.com/${OFFICIAL_REPO}.git - - git fetch origin $(Build.SourceBranchName) - git fetch upstream ${DEFAULT_MERGE_BRANCH} - - git checkout $(Build.SourceBranchName) - MERGE_SIZE=`du -s . | sed -e "s/\t.*//"` - echo -e "Merge Size ${MERGE_SIZE}" - - git checkout ${DEFAULT_MERGE_BRANCH} - TARGET_SIZE=`du -s . | sed -e "s/\t.*//"` - echo -e "Target Size ${TARGET_SIZE}" - - if [ "${MERGE_SIZE}" != "${TARGET_SIZE}" ]; then - SIZE_DIFF=`expr \( ${MERGE_SIZE} - ${TARGET_SIZE} \)`; - else - SIZE_DIFF=0; - fi; - echo -e "Estimated content size difference = ${SIZE_DIFF} kB" && - test ${SIZE_DIFF} -lt 100; - displayName: 'Diff Size Check' - - job: "style_check" - pool: - vmImage: "Ubuntu 18.04" - steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: 3.7 - - bash: | - pip install flake8 - python setup.py style - displayName: 'flake8 check' - - job: "build_docs" - pool: - vmImage: 'Ubuntu 18.04' - steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: 3.8 - - script: | - cd doc - python -m pip install -r requirements.txt - make html SPHINXOPTS='-W -v' - displayName: "Build docs" - -- stage: build - dependsOn: [] - jobs: - - job: "build_wheel" - pool: - vmImage: 'Ubuntu 18.04' - steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: 3.8 - - script: | - python -m pip install setuptools wheel - python setup.py bdist_wheel --universal - displayName: "Build Python Wheel" - continueOnError: false - - publish: dist - artifact: wheel - -- stage: test - displayName: "Test Suite" - dependsOn: build - jobs: - - template: azure-test-template.yml - parameters: - name: linux - vmImage: 'Ubuntu 18.04' - - template: azure-test-template.yml - parameters: - name: windows - vmImage: 'windows-2019' - - template: azure-test-template.yml - parameters: - name: macOS - vmImage: 'macOS-10.15' \ No newline at end of file diff --git a/azure-test-template.yml b/azure-test-template.yml deleted file mode 100644 index 7ab0dce2..00000000 --- a/azure-test-template.yml +++ /dev/null @@ -1,208 +0,0 @@ -# Azure Pipelines CI job template for PyDM Tests -# https://docs.microsoft.com/en-us/azure/devops/pipelines/languages/anaconda?view=azure-devops -parameters: - name: '' - vmImage: '' - -jobs: -- job: ${{ parameters.name }} - pool: - vmImage: ${{ parameters.vmImage }} - strategy: - matrix: - Python36-PyQt5-5.9: - python.version: "3.6" - qt.bindings: "pyqt" - install.method: "conda" - Python37-PySide2-5.13: - python.version: "3.7" - qt.bindings: "pyside2" - install.method: "conda" - Python38-PyQt5-Latest: - python.version: '3.8' - qt.bindings: "PyQt5" - install.method: "pip" - Python38-PySide2-Latest: - python.version: '3.8' - qt.bindings: "PySide2" - install.method: "pip" - - steps: - - task: DownloadPipelineArtifact@2 - inputs: - source: 'current' - artifact: wheel - path: 'dist' - - - task: ScreenResolutionUtility@1 - inputs: - displaySettings: 'specific' - width: '1920' - height: '1080' - condition: eq(variables['agent.os'], 'Windows_NT' ) - - - task: UsePythonVersion@0 - inputs: - versionSpec: $(python.version) - condition: eq(variables['install.method'], 'pip') - - - script: | - curl -LJO https://github.com/pal1000/mesa-dist-win/releases/download/19.1.0/mesa3d-19.1.0-release-msvc.exe - 7z x mesa3d-19.1.0-release-msvc.exe - cd x64 - xcopy opengl32.dll C:\windows\system32\mesadrv.dll* - xcopy opengl32.dll C:\windows\syswow64\mesadrv.dll* - REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v DLL /t REG_SZ /d "mesadrv.dll" /f - REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v DriverVersion /t REG_DWORD /d 1 /f - REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v Flags /t REG_DWORD /d 1 /f - REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v Version /t REG_DWORD /d 2 /f - REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v DLL /t REG_SZ /d "mesadrv.dll" /f - REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v DriverVersion /t REG_DWORD /d 1 /f - REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v Flags /t REG_DWORD /d 1 /f - REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v Version /t REG_DWORD /d 2 /f - displayName: "Install Windows-Mesa OpenGL DLL" - condition: eq(variables['agent.os'], 'Windows_NT') - - - bash: | - if [ $(agent.os) == 'Linux' ] - then - echo "##vso[task.prependpath]$CONDA/bin" - elif [ $(agent.os) == 'Darwin' ] - then - sudo chown -R $USER $CONDA - echo "##vso[task.prependpath]$CONDA/bin" - elif [ $(agent.os) == 'Windows_NT' ] - then - echo "##vso[task.prependpath]$CONDA/Scripts" - else - echo 'Just what OS are you using?' - fi - displayName: 'Add Conda To $PATH' - condition: eq(variables['install.method'], 'conda' ) - continueOnError: false - - - bash: | - if [ $(install.method) == "conda" ] - then - conda update --all --yes --quiet - conda config --env --set always_yes true - if [ $(python.version) == '2.7' ] - then - conda config --set restore_free_channel true - fi - if [ $(qt.bindings) == "pyside2" ] || ([ $(qt.bindings) == 'pyside' ] && [ $(agent.os) == 'Darwin' ]) - then - conda config --prepend channels conda-forge - fi - conda create --name test-environment-$(python.version) python=$(python.version) --yes --quiet - source activate test-environment-$(python.version) - conda info - - if [ $(qt.bindings) == "pyside2" ] - then - conda install $(qt.bindings) --yes --quiet --strict-channel-priority - else - conda install $(qt.bindings) --yes --quiet - fi - conda install numpy scipy pyopengl h5py six --yes --quiet - pip install matplotlib - else - pip install $(qt.bindings) numpy scipy pyopengl h5py six matplotlib - fi - pip install pytest pytest-cov coverage pytest-xdist - if [ $(python.version) == "2.7" ] - then - pip install pytest-faulthandler==1.6.0 - export PYTEST_ADDOPTS="--faulthandler-timeout=15" - else - pip install pytest pytest-cov coverage - fi - displayName: "Install Dependencies" - - - bash: | - if [ $(install.method) == "conda" ] - then - source activate test-environment-$(python.version) - fi - python -m pip install --no-index --find-links=dist pyqtgraph - displayName: 'Install Wheel' - - - bash: | - sudo apt-get install -y libxkbcommon-x11-dev - # workaround for QTBUG-84489 - sudo apt-get install -y libxcb-xfixes0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 - if [ $(install.method) == "conda" ] - then - source activate test-environment-$(python.version) - fi - if [ $(python.version) == "2.7" ] - then - pip install PyVirtualDisplay==0.2.5 pytest-xvfb==1.2.0 - else - pip install pytest-xvfb - fi - displayName: "Virtual Display Setup" - condition: eq(variables['agent.os'], 'Linux' ) - - - bash: | - export QT_DEBUG_PLUGINS=1 - if [ $(install.method) == "conda" ] - then - source activate test-environment-$(python.version) - fi - echo python location: `which python` - echo python version: `python --version` - echo pytest location: `which pytest` - echo installed packages - pip list - echo pyqtgraph system info - python -c "import pyqtgraph as pg; pg.systemInfo()" - echo display information - if [ $(agent.os) == 'Linux' ] - then - export DISPLAY=:99.0 - Xvfb :99 -screen 0 1920x1200x24 -ac +extension GLX +render -noreset & - sleep 3 - fi - python -m pyqtgraph.util.get_resolution - echo openGL information - python -c "from pyqtgraph.opengl.glInfo import GLTest" - displayName: 'Debug Info' - continueOnError: false - - - bash: | - if [ $(install.method) == "conda" ] - then - source activate test-environment-$(python.version) - fi - mkdir -p "$SCREENSHOT_DIR" - # echo "If Screenshots are generated, they may be downloaded from:" - # echo "https://dev.azure.com/pyqtgraph/pyqtgraph/_apis/build/builds/$(Build.BuildId)/artifacts?artifactName=Screenshots&api-version=5.0" - pytest . -v \ - -n 1 \ - --junitxml=junit/test-results.xml \ - --cov pyqtgraph --cov-report=xml --cov-report=html - displayName: 'Unit tests' - env: - AZURE: 1 - SCREENSHOT_DIR: $(Build.ArtifactStagingDirectory)/screenshots - - - task: PublishBuildArtifacts@1 - displayName: 'Publish Screenshots' - condition: failed() - inputs: - pathtoPublish: $(Build.ArtifactStagingDirectory)/screenshots - artifactName: Screenshots - - - task: PublishTestResults@2 - condition: succeededOrFailed() - inputs: - testResultsFiles: '**/test-*.xml' - testRunTitle: 'Test Results for $(agent.os) - $(python.version) - $(qt.bindings) - $(install.method)' - publishRunAttachments: true - - - task: PublishCodeCoverageResults@1 - inputs: - codeCoverageTool: Cobertura - summaryFileLocation: '$(System.DefaultWorkingDirectory)/**/coverage.xml' - reportDirectory: '$(System.DefaultWorkingDirectory)/**/htmlcov' diff --git a/examples/ExampleApp.py b/examples/ExampleApp.py index ea98373f..ef280979 100644 --- a/examples/ExampleApp.py +++ b/examples/ExampleApp.py @@ -1,4 +1,6 @@ +import keyword import os +import re import sys import subprocess import pyqtgraph as pg @@ -104,7 +106,7 @@ examples = OrderedDict([ # based on https://github.com/art1415926535/PyQt5-syntax-highlighting -QRegExp = QtCore.QRegExp +QRegularExpression = QtCore.QRegularExpression QFont = QtGui.QFont QColor = QtGui.QColor @@ -208,55 +210,45 @@ class PythonHighlighter(QSyntaxHighlighter): """Syntax highlighter for the Python language. """ # Python keywords - keywords = [ - 'and', 'assert', 'break', 'class', 'continue', 'def', - 'del', 'elif', 'else', 'except', 'exec', 'finally', - 'for', 'from', 'global', 'if', 'import', 'in', - 'is', 'lambda', 'not', 'or', 'pass', 'print', - 'raise', 'return', 'try', 'while', 'yield', - 'None', 'True', 'False', 'async', 'await', - ] + keywords = keyword.kwlist # Python operators operators = [ - '=', + r'=', # Comparison - '==', '!=', '<', '<=', '>', '>=', + r'==', r'!=', r'<', r'<=', r'>', r'>=', # Arithmetic - '\+', '-', '\*', '/', '//', '\%', '\*\*', + r'\+', r"-", r'\*', r'/', r'//', r'%', r'\*\*', # In-place - '\+=', '-=', '\*=', '/=', '\%=', + r'\+=', r'-=', r'\*=', r'/=', r'\%=', # Bitwise - '\^', '\|', '\&', '\~', '>>', '<<', + r'\^', r'\|', r'&', r'~', r'>>', r'<<', ] # Python braces braces = [ - '\{', '\}', '\(', '\)', '\[', '\]', + r'\{', r'\}', r'\(', r'\)', r'\[', r'\]', ] def __init__(self, document): QSyntaxHighlighter.__init__(self, document) # Multi-line strings (expression, flag, style) - # FIXME: The triple-quotes in these two lines will mess up the - # syntax highlighting from this point onward - self.tri_single = (QRegExp("'''"), 1, 'string2') - self.tri_double = (QRegExp('"""'), 2, 'string2') + self.tri_single = (QRegularExpression("'''"), 1, 'string2') + self.tri_double = (QRegularExpression('"""'), 2, 'string2') rules = [] # Keyword, operator, and brace rules rules += [(r'\b%s\b' % w, 0, 'keyword') for w in PythonHighlighter.keywords] - rules += [(r'%s' % o, 0, 'operator') + rules += [(o, 0, 'operator') for o in PythonHighlighter.operators] - rules += [(r'%s' % b, 0, 'brace') + rules += [(b, 0, 'brace') for b in PythonHighlighter.braces] # All other rules rules += [ - # 'self' (r'\bself\b', 0, 'self'), @@ -277,12 +269,8 @@ class PythonHighlighter(QSyntaxHighlighter): # From '#' until a newline (r'#[^\n]*', 0, 'comment'), - ] - - # Build a QRegExp for each pattern - self.rules = [(QRegExp(pat), index, fmt) - for (pat, index, fmt) in rules] + self.rules = rules @property def styles(self): @@ -294,16 +282,14 @@ class PythonHighlighter(QSyntaxHighlighter): """ # Do other syntax formatting for expression, nth, format in self.rules: - index = expression.indexIn(text, 0) format = self.styles[format] - while index >= 0: - # We actually want the index of the nth match - index = expression.pos(nth) - length = len(expression.cap(nth)) - self.setFormat(index, length, format) - index = expression.indexIn(text, index + length) - + for n, match in enumerate(re.finditer(expression, text)): + if n < nth: + continue + start = match.start() + length = match.end() - start + self.setFormat(start, length, format) self.setCurrentBlockState(0) # Do multi-line strings @@ -312,11 +298,16 @@ class PythonHighlighter(QSyntaxHighlighter): in_multiline = self.match_multiline(text, *self.tri_double) def match_multiline(self, text, delimiter, in_state, style): - """Do highlighting of multi-line strings. ``delimiter`` should be a - ``QRegExp`` for triple-single-quotes or triple-double-quotes, and - ``in_state`` should be a unique integer to represent the corresponding - state changes when inside those strings. Returns True if we're still - inside a multi-line string when this function is finished. + """Do highlighting of multi-line strings. + + =========== ========================================================== + delimiter (QRegularExpression) for triple-single-quotes or + triple-double-quotes + in_state (int) to represent the corresponding state changes when + inside those strings. Returns True if we're still inside a + multi-line string when this function is finished. + style (str) representation of the kind of style to use + =========== ========================================================== """ # If inside triple-single quotes, start at 0 if self.previousBlockState() == in_state: @@ -324,17 +315,19 @@ class PythonHighlighter(QSyntaxHighlighter): add = 0 # Otherwise, look for the delimiter on this line else: - start = delimiter.indexIn(text) + match = delimiter.match(text) + start = match.capturedStart() # Move past this match - add = delimiter.matchedLength() + add = match.capturedLength() # As long as there's a delimiter match on this line... while start >= 0: # Look for the ending delimiter - end = delimiter.indexIn(text, start + add) + match = delimiter.match(text, start + add) + end = match.capturedEnd() # Ending delimiter on this line? if end >= add: - length = end - start + add + delimiter.matchedLength() + length = end - start + add + match.capturedLength() self.setCurrentBlockState(0) # No; multi-line string else: @@ -343,7 +336,8 @@ class PythonHighlighter(QSyntaxHighlighter): # Apply formatting self.setFormat(start, length, self.styles[style]) # Look for the next match - start = delimiter.indexIn(text, start + length) + match = delimiter.match(text, start + length) + start = match.capturedStart() # Return True if still inside a multi-line string, False otherwise if self.currentBlockState() == in_state: diff --git a/examples/test_examples.py b/examples/test_examples.py index 685b71d7..a0be0750 100644 --- a/examples/test_examples.py +++ b/examples/test_examples.py @@ -10,6 +10,7 @@ import importlib import itertools import pytest import os, sys +import platform import subprocess import time if __name__ == "__main__" and (__package__ is None or __package__==''): @@ -69,75 +70,83 @@ conditionalExamples = { ) ), 'GLVolumeItem.py': exceptionCondition( - not(sys.platform == "darwin" and - sys.version_info[0] == 2 and - (frontends[Qt.PYQT4] or frontends[Qt.PYSIDE])), + not(platform.system() == "Darwin" and + tuple(map(int, platform.mac_ver()[0].split("."))) >= (10, 16) and + (sys.version_info <= (3, 8, 7) or + (sys.version_info >= (3, 9) and sys.version_info < (3, 9, 1)))), reason=( - "glClear does not work on macOS + Python2.7 + Qt4: ", - "https://github.com/pyqtgraph/pyqtgraph/issues/939" + "pyopenGL cannot find openGL libray on big sur: " + "https://github.com/python/cpython/pull/21241" ) ), 'GLIsosurface.py': exceptionCondition( - not(sys.platform == "darwin" and - sys.version_info[0] == 2 and - (frontends[Qt.PYQT4] or frontends[Qt.PYSIDE])), + not(platform.system() == "Darwin" and + tuple(map(int, platform.mac_ver()[0].split("."))) >= (10, 16) and + (sys.version_info <= (3, 8, 7) or + (sys.version_info >= (3, 9) and sys.version_info < (3, 9, 1)))), reason=( - "glClear does not work on macOS + Python2.7 + Qt4: ", - "https://github.com/pyqtgraph/pyqtgraph/issues/939" + "pyopenGL cannot find openGL libray on big sur: " + "https://github.com/python/cpython/pull/21241" ) ), 'GLSurfacePlot.py': exceptionCondition( - not(sys.platform == "darwin" and - sys.version_info[0] == 2 and - (frontends[Qt.PYQT4] or frontends[Qt.PYSIDE])), + not(platform.system() == "Darwin" and + tuple(map(int, platform.mac_ver()[0].split("."))) >= (10, 16) and + (sys.version_info <= (3, 8, 7) or + (sys.version_info >= (3, 9) and sys.version_info < (3, 9, 1)))), reason=( - "glClear does not work on macOS + Python2.7 + Qt4: ", - "https://github.com/pyqtgraph/pyqtgraph/issues/939" + "pyopenGL cannot find openGL libray on big sur: " + "https://github.com/python/cpython/pull/21241" ) ), 'GLScatterPlotItem.py': exceptionCondition( - not(sys.platform == "darwin" and - sys.version_info[0] == 2 and - (frontends[Qt.PYQT4] or frontends[Qt.PYSIDE])), + not(platform.system() == "Darwin" and + tuple(map(int, platform.mac_ver()[0].split("."))) >= (10, 16) and + (sys.version_info <= (3, 8, 7) or + (sys.version_info >= (3, 9) and sys.version_info < (3, 9, 1)))), reason=( - "glClear does not work on macOS + Python2.7 + Qt4: ", - "https://github.com/pyqtgraph/pyqtgraph/issues/939" + "pyopenGL cannot find openGL libray on big sur: " + "https://github.com/python/cpython/pull/21241" ) ), 'GLshaders.py': exceptionCondition( - not(sys.platform == "darwin" and - sys.version_info[0] == 2 and - (frontends[Qt.PYQT4] or frontends[Qt.PYSIDE])), + not(platform.system() == "Darwin" and + tuple(map(int, platform.mac_ver()[0].split("."))) >= (10, 16) and + (sys.version_info <= (3, 8, 7) or + (sys.version_info >= (3, 9) and sys.version_info < (3, 9, 1)))), reason=( - "glClear does not work on macOS + Python2.7 + Qt4: ", - "https://github.com/pyqtgraph/pyqtgraph/issues/939" + "pyopenGL cannot find openGL libray on big sur: " + "https://github.com/python/cpython/pull/21241" ) ), 'GLLinePlotItem.py': exceptionCondition( - not(sys.platform == "darwin" and - sys.version_info[0] == 2 and - (frontends[Qt.PYQT4] or frontends[Qt.PYSIDE])), + not(platform.system() == "Darwin" and + tuple(map(int, platform.mac_ver()[0].split("."))) >= (10, 16) and + (sys.version_info <= (3, 8, 7) or + (sys.version_info >= (3, 9) and sys.version_info < (3, 9, 1)))), reason=( - "glClear does not work on macOS + Python2.7 + Qt4: ", - "https://github.com/pyqtgraph/pyqtgraph/issues/939" + "pyopenGL cannot find openGL libray on big sur: " + "https://github.com/python/cpython/pull/21241" ) ), 'GLMeshItem.py': exceptionCondition( - not(sys.platform == "darwin" and - sys.version_info[0] == 2 and - (frontends[Qt.PYQT4] or frontends[Qt.PYSIDE])), + not(platform.system() == "Darwin" and + tuple(map(int, platform.mac_ver()[0].split("."))) >= (10, 16) and + (sys.version_info <= (3, 8, 7) or + (sys.version_info >= (3, 9) and sys.version_info < (3, 9, 1)))), reason=( - "glClear does not work on macOS + Python2.7 + Qt4: ", - "https://github.com/pyqtgraph/pyqtgraph/issues/939" + "pyopenGL cannot find openGL libray on big sur: " + "https://github.com/python/cpython/pull/21241" ) ), 'GLImageItem.py': exceptionCondition( - not(sys.platform == "darwin" and - sys.version_info[0] == 2 and - (frontends[Qt.PYQT4] or frontends[Qt.PYSIDE])), + not(platform.system() == "Darwin" and + tuple(map(int, platform.mac_ver()[0].split("."))) >= (10, 16) and + (sys.version_info <= (3, 8, 7) or + (sys.version_info >= (3, 9) and sys.version_info < (3, 9, 1)))), reason=( - "glClear does not work on macOS + Python2.7 + Qt4: ", - "https://github.com/pyqtgraph/pyqtgraph/issues/939" + "pyopenGL cannot find openGL libray on big sur: " + "https://github.com/python/cpython/pull/21241" ) ) } diff --git a/pyqtgraph/functions.py b/pyqtgraph/functions.py index a2949ef7..fb716904 100644 --- a/pyqtgraph/functions.py +++ b/pyqtgraph/functions.py @@ -38,7 +38,7 @@ SI_PREFIXES_ASCII = 'yzafpnum kMGTPEZY' SI_PREFIX_EXPONENTS = dict([(SI_PREFIXES[i], (i-8)*3) for i in range(len(SI_PREFIXES))]) SI_PREFIX_EXPONENTS['u'] = -6 -FLOAT_REGEX = re.compile(r'(?P[+-]?((((\d+(\.\d*)?)|(\d*\.\d+))([eE][+-]?\d+)?)|((?i)(nan)|(inf))))\s*((?P[u' + SI_PREFIXES + r']?)(?P\w.*))?$') +FLOAT_REGEX = re.compile(r'(?P[+-]?((((\d+(\.\d*)?)|(\d*\.\d+))([eE][+-]?\d+)?)|((?i:nan)|(inf))))\s*((?P[u' + SI_PREFIXES + r']?)(?P\w.*))?$') INT_REGEX = re.compile(r'(?P[+-]?\d+)\s*(?P[u' + SI_PREFIXES + r']?)(?P.*)$') diff --git a/pyqtgraph/tests/image_testing.py b/pyqtgraph/tests/image_testing.py index cfb62bb9..12101b2c 100644 --- a/pyqtgraph/tests/image_testing.py +++ b/pyqtgraph/tests/image_testing.py @@ -212,7 +212,7 @@ def assertImageApproved(image, standardFile, message=None, **kwargs): else: if os.getenv('TRAVIS') is not None: saveFailedTest(image, stdImage, standardFile, upload=True) - elif os.getenv('AZURE') is not None: + elif os.getenv('CI') is not None: standardFile = os.path.join(os.getenv("SCREENSHOT_DIR", "screenshots"), standardFile) saveFailedTest(image, stdImage, standardFile) print(graphstate) @@ -509,7 +509,7 @@ def getTestDataRepo(): if not os.path.isdir(parentPath): os.makedirs(parentPath) - if os.getenv('TRAVIS') is not None or os.getenv('AZURE') is not None: + if os.getenv('TRAVIS') is not None or os.getenv('CI') is not None: # Create a shallow clone of the test-data repository (to avoid # downloading more data than is necessary) os.makedirs(dataPath) diff --git a/pyqtgraph/tests/test_reload.py b/pyqtgraph/tests/test_reload.py index 934d2ed2..11b12cf2 100644 --- a/pyqtgraph/tests/test_reload.py +++ b/pyqtgraph/tests/test_reload.py @@ -1,6 +1,7 @@ import tempfile, os, sys, shutil, time import pyqtgraph as pg import pyqtgraph.reload +import pytest pgpath = os.path.join(os.path.dirname(pg.__file__), '..') @@ -41,7 +42,11 @@ def remove_cache(mod): if os.path.isdir(cachedir): shutil.rmtree(cachedir) - +@pytest.mark.skipif( + pg.Qt.QT_LIB == "PySide2" + and pg.Qt.PySide2.__version__.startswith("5.15") + and sys.version_info > (3, 9), + reason="Unknown Issue") def test_reload(): py3 = sys.version_info >= (3,) diff --git a/pyqtgraph/widgets/MatplotlibWidget.py b/pyqtgraph/widgets/MatplotlibWidget.py index c5b6c980..232918ee 100644 --- a/pyqtgraph/widgets/MatplotlibWidget.py +++ b/pyqtgraph/widgets/MatplotlibWidget.py @@ -1,7 +1,7 @@ from ..Qt import QtGui, QtCore, QT_LIB import matplotlib -if QT_LIB != 'PyQt5': +if QT_LIB not in ['PyQt5', "PySide2"]: if QT_LIB == 'PySide': matplotlib.rcParams['backend.qt4']='PySide' diff --git a/pytest.ini b/pytest.ini index 355e9dfd..a16534bd 100644 --- a/pytest.ini +++ b/pytest.ini @@ -16,4 +16,6 @@ filterwarnings = # py36/pyside2_512 specific issue ignore:split\(\) requires a non-empty pattern match\.:FutureWarning # pyqtgraph specific warning we want to ignore during testing - ignore:Visible window deleted. To prevent this, store a reference to the window object. \ No newline at end of file + ignore:Visible window deleted. To prevent this, store a reference to the window object. + # xvfb warnings + ignore:Unknown config option:pytest.PytestConfigWarning diff --git a/tools/setupHelpers.py b/tools/setupHelpers.py index 642c5d77..63b255d3 100644 --- a/tools/setupHelpers.py +++ b/tools/setupHelpers.py @@ -379,7 +379,7 @@ def getGitVersion(tagPrefix): 'describe', '--tags', '--dirty', - '--match=%s*'%tagPrefix]).strip().decode('utf-8') + '--match="%s*"'%tagPrefix]).strip().decode('utf-8') # chop off prefix assert v.startswith(tagPrefix)