diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9b943f25c..c5430a54c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,19 +2,26 @@ name: tests on: push: + branches: [main, v8.3.x] paths-ignore: - "website/**" - "*.md" pull_request: - types: [opened, synchronize, reopened, edited] + branches: ["*"] paths-ignore: - "website/**" - "*.md" + workflow_dispatch: # allows you to trigger manually + +# When this workflow is queued, automatically cancel any previous running +# or pending jobs from the same branch +concurrency: + group: tests-${{ github.ref }} + cancel-in-progress: true jobs: validate: name: Validate - if: github.repository_owner == 'explosion' runs-on: ubuntu-latest steps: - name: Check out repo @@ -35,26 +42,31 @@ jobs: python -m isort thinc --check - name: flake8 run: | - python -m pip install flake8==5.0.4 + python -m pip install flake8 -c requirements.txt python -m flake8 thinc --count --select=E901,E999,F821,F822,F823,W605 --show-source --statistics + - name: mypy + run: | + python -m pip install mypy -c requirements.txt + python -m pip install catalogue confection numpy packaging pydantic + python -m mypy thinc --no-implicit-reexport + tests: - name: Test - needs: Validate - if: github.repository_owner == 'explosion' + name: ${{ matrix.os }} - Python ${{ matrix.python_version }} strategy: fail-fast: false matrix: - os: [ubuntu-latest, windows-latest, macos-15-intel] - python_version: ["3.12"] - include: - - os: windows-latest - python_version: "3.10" - - os: macos-15-intel + os: + - ubuntu-latest + - ubuntu-24.04-arm + - macos-15-intel + - macos-latest + - windows-latest + - windows-11-arm + python_version: ["3.10", "3.11", "3.12", "3.13"] + exclude: + - os: windows-11-arm python_version: "3.10" - - os: ubuntu-latest - python_version: "3.11" - - os: windows-latest - python_version: "3.11" + runs-on: ${{ matrix.os }} env: NOTEBOOK_KERNEL: "thinc-notebook-tests" @@ -68,26 +80,11 @@ jobs: with: python-version: ${{ matrix.python_version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip setuptools wheel - pip install -r requirements.txt - - - name: Build sdist - run: | - # Remove the '.eggs' directory in case it's not empty - # due to setuptools quirks - rm -rf .eggs - python setup.py build_ext --inplace - rm -rf .eggs - python setup.py sdist --formats=gztar - shell: bash + - name: Install build dependencies + run: python -m pip install --upgrade build pip wheel - - name: Run mypy - run: python -m mypy thinc --no-implicit-reexport - if: | - matrix.python_version != '3.6' && - matrix.python_version != '3.7' + - name: Build sdist and wheel + run: python -m build - name: Delete source directory run: rm -rf thinc @@ -99,28 +96,24 @@ jobs: pip freeze --exclude pywin32 > installed.txt pip uninstall -y -r installed.txt - - name: Install from sdist - run: | - SDIST=$(python -c "import os;print(os.listdir('./dist')[-1])" 2>&1) - PIP_CONSTRAINT="build-constraints.txt" pip install dist/$SDIST + - name: Install from wheel + run: pip install dist/*.whl shell: bash - name: Test import run: python -c "import thinc" - name: Install test requirements - run: | - pip install -r requirements.txt + run: pip install -r requirements.txt - name: Install notebook test requirements - run: | - pip install ipykernel pydot graphviz - python -m ipykernel install --name thinc-notebook-tests --user - if: matrix.python_version != '3.12' + run: python -m ipykernel install --name thinc-notebook-tests --user + + - name: List installed packages + run: python -m pip list - name: Run tests without extras - run: | - python -m pytest --pyargs thinc -Werror --cov=thinc --cov-report=term + run: python -m pytest --pyargs thinc --cov=thinc --cov-report=term # Notes on numpy requirements hacks: # 1. torch does not have a direct numpy requirement but is compiled @@ -156,8 +149,8 @@ jobs: pip uninstall -y tensorflow pip install "thinc-apple-ops>=1.0.0,<2.0.0" python -m pytest --pyargs thinc_apple_ops - if: matrix.os == 'macos-15-intel' && matrix.python_version == '3.10' + if: runner.os == 'macOS' && matrix.python_version == '3.10' - name: Run tests with thinc-apple-ops run: python -m pytest --pyargs thinc - if: matrix.os == 'macos-15-intel' && matrix.python_version == '3.10' + if: runner.os == 'macOS' && matrix.python_version == '3.10' diff --git a/README.md b/README.md index 47dc81701..c53ce346b 100644 --- a/README.md +++ b/README.md @@ -36,8 +36,8 @@ their favorite framework. ## 🚀 Quickstart -Thinc is compatible with **Python 3.6+** and runs on **Linux**, **macOS** and -**Windows**. The latest releases with binary wheels are available from +Thinc runs on **Linux**, **macOS** and **Windows**. +The latest releases with binary wheels are available from [pip](https://pypi.python.org/pypi/thinc). Before you install Thinc and its dependencies, make sure that your `pip`, `setuptools` and `wheel` are up to date. For the most recent releases, pip 19.3 or newer is recommended. @@ -121,8 +121,8 @@ the button next to the notebook name. Thinc uses [`black`](https://github.com/psf/black) for auto-formatting, [`flake8`](http://flake8.pycqa.org/en/latest/) for linting and -[`mypy`](https://mypy.readthedocs.io/en/latest/) for type checking. All code is -written compatible with **Python 3.6+**, with type hints wherever possible. See +[`mypy`](https://mypy.readthedocs.io/en/latest/) for type checking. All code +includes type hints wherever possible. See the [type reference](https://thinc.ai/docs/api-types) for more details on Thinc's custom types. diff --git a/build-constraints.txt b/build-constraints.txt deleted file mode 100644 index ccad7ef35..000000000 --- a/build-constraints.txt +++ /dev/null @@ -1,2 +0,0 @@ -# build version constraints for use with wheelwright + multibuild -numpy>=2.0.0,<3.0.0 diff --git a/pyproject.toml b/pyproject.toml index bcb8a68d7..243a60209 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,3 +63,19 @@ repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest [tool.isort] profile = "black" + +[tool.pytest.ini_options] +addopts = "--strict-markers --strict-config -v -r sxfE --color=yes --durations=10" +xfail_strict = true +markers = [ + "slow: test with long runtime", +] +filterwarnings = [ + "error", + # This is triggered by pytest -p thinc.tests.enable_tensorflow + # Not an issue as the coverage upon import is recorded during the + # pytest run "without extras". + "ignore:Module thinc was previously imported, but not measured:coverage.exceptions.CoverageWarning", + # https://github.com/coveragepy/coveragepy/issues/1790 + "ignore:Plugin file tracers:coverage.exceptions.CoverageWarning", +] diff --git a/requirements.txt b/requirements.txt index 6e93ca62c..9fc46fdc4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,36 +3,29 @@ murmurhash>=1.0.2,<1.1.0 cymem>=2.0.2,<2.1.0 preshed>=3.0.2,<3.1.0 blis>=1.3.0,<1.4.0 -srsly>=2.4.0,<3.0.0 +srsly>=2.4.0,<3.1.0 wasabi>=0.8.1,<1.2.0 catalogue>=2.0.4,<2.1.0 -confection>=0.0.1,<1.0.0 -ml_datasets>=0.2.0,<0.3.0; python_version < "3.11" +confection>=0.0.1,<1.1.0 +ml_datasets>=0.2.0,<0.3.0 # Third-party dependencies pydantic>=2.0.0,<3.0.0 numpy>=2.0.0,<3.0.0 packaging>=20.0 -# Backports of modern Python features -dataclasses>=0.6,<1.0; python_version < "3.7" -typing_extensions>=3.7.4.1,<4.5.0; python_version < "3.8" -contextvars>=2.4,<3; python_version < "3.7" # Development dependencies -cython>=0.29.0 -hypothesis>=3.27.0,<6.72.2 +cython>=3.0,<4.0 +hypothesis>=3.27.0,<6.149 pytest>=5.2.0,!=7.1.0 -pytest-cov>=2.7.0,<5.0.0 +pytest-cov>=2.7.0,<8.0.0 coverage>=5.0.0,<8.0.0 -mock>=2.0.0,<3.0.0 -flake8>=3.5.0,<3.6.0 -mypy>=1.5.0,<1.6.0; platform_machine != "aarch64" and python_version >= "3.8" -types-mock>=0.1.1 -types-contextvars>=0.1.2; python_version < "3.7" -types-dataclasses>=0.1.3; python_version < "3.7" -importlib_resources; python_version < "3.7" +flake8==5.0.4 +mypy>=1.5.0,<1.6.0; platform_machine != "aarch64" # Executing notebook tests -ipykernel>=5.1.4,<5.2.0 -nbconvert>=5.6.1,<6.5.0 -nbformat>=5.0.4,<5.2.0 +ipykernel>=5.1.4,<7.2 +pydot +graphviz +nbconvert>=5.6.1,<7.17 +nbformat>=5.0.4,<5.11 # Test to_disk/from_disk against pathlib.Path subclasses pathy>=0.3.5 black>=22.0,<23.0 diff --git a/setup.cfg b/setup.cfg index 173efcf65..85057c38e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -17,10 +17,6 @@ classifiers = Operating System :: Microsoft :: Windows Programming Language :: Cython Programming Language :: Python :: 3 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 Programming Language :: Python :: 3.12 @@ -46,19 +42,15 @@ install_requires = cymem>=2.0.2,<2.1.0 preshed>=3.0.2,<3.1.0 wasabi>=0.8.1,<1.2.0 - srsly>=2.4.0,<3.0.0 + srsly>=2.4.0,<3.1.0 catalogue>=2.0.4,<2.1.0 - confection>=0.0.1,<1.0.0 + confection>=0.0.1,<1.1.0 # Third-party dependencies setuptools - numpy>=1.19.0,<3.0.0 + numpy>=1.21.0,<3.0.0 pydantic>=2.0.0,<3.0.0 packaging>=20.0 - # Backports of modern Python features - dataclasses>=0.6,<1.0; python_version < "3.7" - typing_extensions>=3.7.4.1,<5.0.0; python_version < "3.8" - contextvars>=2.4,<3; python_version < "3.7" - + [options.entry_points] pytest_randomly.random_seeder = thinc = thinc.api:fix_random_seed diff --git a/thinc/tests/layers/test_basic_tagger.py b/thinc/tests/layers/test_basic_tagger.py index 3bc772940..3131ebb91 100644 --- a/thinc/tests/layers/test_basic_tagger.py +++ b/thinc/tests/layers/test_basic_tagger.py @@ -63,6 +63,7 @@ def get_shuffled_batches(Xs, Ys, batch_size): @pytest.mark.parametrize( ("depth", "width", "vector_width", "nb_epoch"), [(2, 32, 16, 5)] ) +@pytest.mark.xfail(reason="Flaky live download from the internet", strict=False) def test_small_end_to_end(depth, width, vector_width, nb_epoch, create_model, ancora): (train_X, train_Y), (dev_X, dev_Y) = ancora batch_size = 8 diff --git a/thinc/tests/layers/test_linear.py b/thinc/tests/layers/test_linear.py index b1752fba2..e51e1dda1 100644 --- a/thinc/tests/layers/test_linear.py +++ b/thinc/tests/layers/test_linear.py @@ -1,7 +1,8 @@ +from unittest.mock import MagicMock + import numpy import pytest from hypothesis import given, settings -from mock import MagicMock from numpy.testing import assert_allclose from thinc.api import SGD, Dropout, Linear, chain diff --git a/thinc/tests/layers/test_mnist.py b/thinc/tests/layers/test_mnist.py index 060007cfd..8fdb74fa3 100644 --- a/thinc/tests/layers/test_mnist.py +++ b/thinc/tests/layers/test_mnist.py @@ -1,3 +1,5 @@ +import platform + import pytest from thinc.api import ( @@ -79,6 +81,11 @@ def create_model(request): @pytest.mark.slow +@pytest.mark.xfail( + platform.system() == "Darwin", + reason="SSL: CERTIFICATE_VERIFY_FAILED", + strict=False, # Works on macos-15-intel Python 3.10, for some reason +) @pytest.mark.parametrize(("width", "nb_epoch", "min_score"), [(32, 20, 0.8)]) def test_small_end_to_end(width, nb_epoch, min_score, create_model, mnist): batch_size = 128 diff --git a/thinc/tests/layers/test_with_debug.py b/thinc/tests/layers/test_with_debug.py index 3f65a3ac3..3e31c71f0 100644 --- a/thinc/tests/layers/test_with_debug.py +++ b/thinc/tests/layers/test_with_debug.py @@ -1,4 +1,4 @@ -from mock import MagicMock +from unittest.mock import MagicMock from thinc.api import Linear, with_debug diff --git a/thinc/tests/model/test_model.py b/thinc/tests/model/test_model.py index f93b46c8c..2284e93b2 100644 --- a/thinc/tests/model/test_model.py +++ b/thinc/tests/model/test_model.py @@ -1,3 +1,4 @@ +import platform import threading import time from collections import Counter @@ -513,6 +514,11 @@ def dummy_model(name, layers): assert a.layers[1].get_ref("y") == y_debug +@pytest.mark.xfail( + platform.system() == "Darwin", + reason="SSL: CERTIFICATE_VERIFY_FAILED", + strict=False, # Works on macos-15-intel Python 3.10, for some reason +) def test_with_debug(): pytest.importorskip("ml_datasets") import ml_datasets diff --git a/thinc/tests/test_types.py b/thinc/tests/test_types.py index d92446980..1a3310d30 100644 --- a/thinc/tests/test_types.py +++ b/thinc/tests/test_types.py @@ -19,7 +19,10 @@ ) -@pytest.mark.xfail(reason="Validation currently disabled") +@pytest.mark.xfail( + reason="Validation currently disabled", + strict=False, # Some parameters pass, other fail +) @pytest.mark.parametrize( "arr,arr_type", [