thinc icon indicating copy to clipboard operation
thinc copied to clipboard

Support Cython 3 (and also Python 3.13).

Open J08nY opened this issue 11 months ago • 14 comments

Description

Fixes the build on Python 3.13 (with Cython 3). Fixes #926 and fixes #947.

Types of change

Removal of Cython version restriction and a small fix.

Checklist

  • [x] I confirm that I have the right to submit this contribution under the project's MIT license.
  • [ ] I ran the tests, and all new and existing tests passed.
  • [x] My changes don't require a change to the documentation, or if they do, I've added all required information.

J08nY avatar Jan 16 '25 11:01 J08nY

It would also be neat to check the Cython 3.1 alpha release or Cython's master branch against free-threaded 3.13.

See https://py-free-threading.github.io for more info about supporting free-threaded Python in native code.

ngoldbaum avatar Jan 22 '25 18:01 ngoldbaum

Any progress on getting this in and cutting a new release? Python 3.13 is now 4 months old.

J08nY avatar Feb 02 '25 11:02 J08nY

I would be willing to test this in conda-forge (where we run the test suite anyway), but thinc depends on srsly, confection & wasabi, so those need to be solved first.

image

Xref https://github.com/conda-forge/srsly-feedstock/pull/53

h-vetinari avatar Feb 06 '25 05:02 h-vetinari

I would be willing to test this in conda-forge (where we run the test suite anyway), but thinc depends on srsly, confection & wasabi, so those need to be solved first.

It seems that all of srsly, confection & wasabi now install under Python 3.13. Atleast in my environment.

J08nY avatar Feb 07 '25 15:02 J08nY

I am getting lots of warnings while running tests under 3.13:

thinc/tests/layers/test_layers_api.py: 1594 warnings
thinc/tests/layers/test_lstm.py: 238 warnings
thinc/tests/layers/test_transforms.py: 296 warnings
thinc/tests/layers/test_with_transforms.py: 2508 warnings
thinc/tests/model/test_validation.py: 145 warnings
thinc/tests/test_config.py: 66 warnings
  /tmp/virt/lib/python3.13/site-packages/pydantic/v1/typing.py:68: DeprecationWarning: Failing to pass a value to the 'type_params' parameter of 'typing.ForwardRef._evaluate' is deprecated, as it leads to incorrect behaviour when calling typing.ForwardRef._evaluate on a stringified annotation that references a PEP 695 type parameter. It will be disallowed in Python 3.15.
    return cast(Any, type_)._evaluate(globalns, localns, recursive_guard=set())

Also some errors:

============================================================================================= short test summary info =============================================================================================
FAILED thinc/tests/backends/test_ops.py::test_ops_consistency[NumpyOps] - AssertionError: alloc
FAILED thinc/tests/layers/test_layers_api.py::test_layers_from_config[SparseLinear.v1-kwargs55-in_data55-out_data55] - NameError: name 'InT' is not defined
FAILED thinc/tests/layers/test_layers_api.py::test_layers_from_config[SparseLinear.v2-kwargs56-in_data56-out_data56] - NameError: name 'InT' is not defined
FAILED thinc/tests/layers/test_layers_api.py::test_layers_from_config[premap_ids.v1-kwargs59-in_data59-out_data59] - NameError: name 'InT' is not defined
FAILED thinc/tests/layers/test_layers_api.py::test_dropout[data2] - thinc.util.DataValidationError: 
FAILED thinc/tests/layers/test_layers_api.py::test_layers_batching_all[premap_ids.v1-kwargs59-in_data59-out_data59] - NameError: name 'InT' is not defined

which expand out to this, and are clearly connected to the warning:

thinc/tests/layers/test_layers_api.py:211: in util_batch_unbatch_array
    model.initialize(in_data, out_data)
thinc/model.py:316: in initialize
    validate_fwd_input_output(self.name, self._func, X, Y)
thinc/util.py:588: in validate_fwd_input_output
    ArgModel.update_forward_refs(**types.__dict__)
../virt/lib/python3.13/site-packages/pydantic/v1/main.py:814: in update_forward_refs
    update_model_forward_refs(cls, cls.__fields__.values(), cls.__config__.json_encoders, localns)
../virt/lib/python3.13/site-packages/pydantic/v1/typing.py:559: in update_model_forward_refs
    update_field_forward_refs(f, globalns=globalns, localns=localns)
../virt/lib/python3.13/site-packages/pydantic/v1/typing.py:525: in update_field_forward_refs
    field.type_ = evaluate_forwardref(field.type_, globalns, localns or None)
../virt/lib/python3.13/site-packages/pydantic/v1/typing.py:68: in evaluate_forwardref
    return cast(Any, type_)._evaluate(globalns, localns, recursive_guard=set())
/usr/lib/python3.13/typing.py:1081: in _evaluate
    eval(self.__forward_code__, globalns, localns),

Seems to be an upstream pydantic issue: https://github.com/pydantic/pydantic/issues/9613

J08nY avatar Feb 07 '25 15:02 J08nY

Pinging this. Could anyone have a look, fix the failing test and merge? It is blocking a lot of people depending on spacy.

J08nY avatar Feb 10 '25 19:02 J08nY

requirements.txt should also be updated, since it's referenced in the README.

If no-one responds in a few more weeks, maybe email Explosion.

salty-horse avatar Feb 12 '25 08:02 salty-horse

I tried this in https://github.com/conda-forge/thinc-feedstock/pull/126. On python <3.13, the test suite fails with:

=============================== warnings summary ===============================
$PREFIX/lib/python3.12/site-packages/thinc/tests/layers/test_mnist.py:81
  $PREFIX/lib/python3.12/site-packages/thinc/tests/layers/test_mnist.py:81: PytestUnknownMarkWarning: Unknown pytest.mark.slow - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.slow

$PREFIX/lib/python3.12/site-packages/thinc/tests/regression/issue519/test_issue519.py:13
  $PREFIX/lib/python3.12/site-packages/thinc/tests/regression/issue519/test_issue519.py:13: PytestUnknownMarkWarning: Unknown pytest.mark.slow - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.slow

tests/layers/test_shim.py::test_shim_can_roundtrip_with_path_subclass
tests/model/test_model.py::test_model_can_roundtrip_with_path_subclass
  $PREFIX/lib/python3.12/site-packages/thinc/tests/conftest.py:54: PytestDeprecationWarning: 
  Module 'pathy' was found, but when imported by pytest it raised:
      ImportError("cannot import name '_PosixFlavour' from 'pathlib' ($PREFIX/lib/python3.12/pathlib.py)")
  In pytest 9.1 this warning will become an error by default.
  You can fix the underlying problem, or alternatively overwrite this behavior and silence this warning by passing exc_type=ImportError explicitly.
  See https://docs.pytest.org/en/stable/deprecations.html#pytest-importorskip-default-behavior-regarding-importerror
    pytest.importorskip("pathy")

Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== short test summary info ============================
FAILED tests/backends/test_ops.py::test_ops_consistency[NumpyOps] - AssertionError: alloc
assert 'Shape' == 'typing.Tuple[int, ...]'
  
  - typing.Tuple[int, ...]
  + Shape
FAILED tests/layers/test_layers_api.py::test_layers_from_config[SparseLinear.v1-kwargs55-in_data55-out_data55] - NameError: name 'InT' is not defined
FAILED tests/layers/test_layers_api.py::test_layers_from_config[SparseLinear.v2-kwargs56-in_data56-out_data56] - NameError: name 'InT' is not defined
FAILED tests/layers/test_layers_api.py::test_layers_from_config[premap_ids.v1-kwargs59-in_data59-out_data59] - NameError: name 'InT' is not defined
FAILED tests/layers/test_layers_api.py::test_dropout[data2] - thinc.util.DataValidationError: 

Data validation error in 'dropout'
X: <class 'thinc.types.Padded'> Y: <class 'thinc.types.Padded'>

X                     not a valid numpy or cupy array
X                     not a valid numpy or cupy array
X                     not a valid numpy or cupy array
X                     not a valid numpy or cupy array
X                     not a valid numpy or cupy array
X                     not a valid numpy or cupy array
X                     not a valid numpy or cupy array
X                     not a valid numpy or cupy array
X                     value is not a valid sequence 
X                     instance of Ragged, tuple or dict expected
X -> size_at_t        wrong array data type (expected int32/int64/uint32/uint64, got float32)
Y -> 0                not a valid numpy or cupy array
Y -> 0                not a valid numpy or cupy array
Y -> 0                not a valid numpy or cupy array
Y -> 0                not a valid numpy or cupy array
Y -> 0                not a valid numpy or cupy array
Y -> 0                not a valid numpy or cupy array
Y -> 0                not a valid numpy or cupy array
Y -> 0                not a valid numpy or cupy array
Y -> 0                value is not a valid sequence 
Y -> 0                instance of Ragged, tuple or dict expected
Y -> 0 -> size_at_t   wrong array data type (expected int32/int64/uint32/uint64, got float32)
FAILED tests/layers/test_layers_api.py::test_layers_batching_all[premap_ids.v1-kwargs59-in_data59-out_data59] - NameError: name 'InT' is not defined
=========== 6 failed, 1219 passed, 119 skipped, 4 warnings in 11.81s ===========

For 3.13, we'd also need to lift the cap here https://github.com/explosion/thinc/blob/256e403d027c63422742f01bc01fcf9ced7acc4f/setup.cfg#L37 because we only have 3.13 builds for blis>=1.1 (though it's technically possible to backport this to 1.0, that makes no sense IMO in terms of policy).

h-vetinari avatar Feb 27 '25 21:02 h-vetinari

I can confirm that this PR allows to build thinc on Arch Linux with Python 3.13

But for tests also need this change

diff --git i/thinc/tests/layers/test_linear.py w/thinc/tests/layers/test_linear.py
index 345669d8..d91061a4 100644
--- i/thinc/tests/layers/test_linear.py
+++ w/thinc/tests/layers/test_linear.py
@@ -1,7 +1,7 @@
 import numpy
 import pytest
 from hypothesis import given, settings
-from mock import MagicMock
+from unittest.mock import MagicMock
 from numpy.testing import assert_allclose

 from thinc.api import SGD, Dropout, Linear, chain
diff --git i/thinc/tests/layers/test_with_debug.py w/thinc/tests/layers/test_with_debug.py
index 3f65a3ac..3e31c71f 100644
--- i/thinc/tests/layers/test_with_debug.py
+++ w/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

davispuh avatar Mar 06 '25 18:03 davispuh

I can confirm that this PR allows to build thinc on Arch Linux with Python 3.13

But for tests also need this change

diff --git i/thinc/tests/layers/test_linear.py w/thinc/tests/layers/test_linear.py
index 345669d8..d91061a4 100644
--- i/thinc/tests/layers/test_linear.py
+++ w/thinc/tests/layers/test_linear.py
@@ -1,7 +1,7 @@
 import numpy
 import pytest
 from hypothesis import given, settings
-from mock import MagicMock
+from unittest.mock import MagicMock
 from numpy.testing import assert_allclose

 from thinc.api import SGD, Dropout, Linear, chain
diff --git i/thinc/tests/layers/test_with_debug.py w/thinc/tests/layers/test_with_debug.py
index 3f65a3ac..3e31c71f 100644
--- i/thinc/tests/layers/test_with_debug.py
+++ w/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

Does this fix all of the test failures? I haven't had a chance to try.

J08nY avatar Mar 07 '25 10:03 J08nY

It only fixes so that tests itself run.

There are some test failures but I'm not sure if they're due Python 3.13 as they might be because of some other reason.

==================================================================== FAILURES =====================================================================
_________________________________________________________ test_ops_consistency[NumpyOps] __________________________________________________________

op = <class 'thinc.backends.numpy_ops.NumpyOps'>

    @pytest.mark.parametrize("op", [NumpyOps, CupyOps])
    def test_ops_consistency(op):
        """Test that specific ops don't define any methods that are not on the
        Ops base class and that all ops methods define the exact same arguments."""
        attrs = [m for m in dir(op) if not m.startswith("_")]
        for attr in attrs:
            assert hasattr(Ops, attr)
            method = getattr(op, attr)
            if hasattr(method, "__call__"):
                sig = inspect.signature(method)
                params = [p for p in sig.parameters][1:]
                base_sig = inspect.signature(getattr(Ops, attr))
                base_params = [p for p in base_sig.parameters][1:]
                assert params == base_params, attr
                defaults = [p.default for p in sig.parameters.values()][1:]
                base_defaults = [p.default for p in base_sig.parameters.values()][1:]
                assert defaults == base_defaults, attr
                # If args are type annotated, their types should be the same
                annots = [p.annotation for p in sig.parameters.values()][1:]
                base_annots = [p.annotation for p in base_sig.parameters.values()][1:]
                for i, (p1, p2) in enumerate(zip(annots, base_annots)):
                    if p1 != inspect.Parameter.empty and p2 != inspect.Parameter.empty:
                        # Need to check string value to handle TypeVars etc.
>                       assert str(p1) == str(p2), attr
E                       AssertionError: alloc
E                       assert 'Shape' == 'typing.Tuple[int, ...]'
E
E                         - typing.Tuple[int, ...]
E                         + Shape

thinc/tests/backends/test_ops.py:155: AssertionError
_____________________________________ test_layers_from_config[SparseLinear.v1-kwargs55-in_data55-out_data55] ______________________________________

name = 'SparseLinear.v1', kwargs = {}, in_data = (array([1, 2, 3], dtype=uint64), array([1., 2., 3.], dtype=float32), array([1, 1], dtype=int32))
out_data = array([[4., 2., 3., 4.],
       [1., 5., 3., 1.],
       [9., 8., 5., 7.]], dtype=float32)

    @pytest.mark.parametrize("name,kwargs,in_data,out_data", TEST_CASES)
    def test_layers_from_config(name, kwargs, in_data, out_data):
        cfg = {"@layers": name, **kwargs}
        filled_cfg = registry.fill({"config": cfg})
        assert srsly.is_json_serializable(filled_cfg)
        model = registry.resolve({"config": cfg})["config"]
        if "LSTM" in name:
            model = with_padded(model)
        valid = True
        with data_validation(valid):
>           model.initialize(in_data, out_data)

thinc/tests/layers/test_layers_api.py:153:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
thinc/model.py:316: in initialize
    validate_fwd_input_output(self.name, self._func, X, Y)
thinc/util.py:588: in validate_fwd_input_output
    ArgModel.update_forward_refs(**types.__dict__)
pydantic/v1/main.py:814: in update_forward_refs
    update_model_forward_refs(cls, cls.__fields__.values(), cls.__config__.json_encoders, localns)
pydantic/v1/typing.py:559: in update_model_forward_refs
    update_field_forward_refs(f, globalns=globalns, localns=localns)
pydantic/v1/typing.py:525: in update_field_forward_refs
    field.type_ = evaluate_forwardref(field.type_, globalns, localns or None)
pydantic/v1/typing.py:68: in evaluate_forwardref
    return cast(Any, type_)._evaluate(globalns, localns, recursive_guard=set())
typing.py:1081: in _evaluate
    eval(self.__forward_code__, globalns, localns),
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

>   ???
E   NameError: name 'InT' is not defined

<string>:1: NameError
_____________________________________ test_layers_from_config[SparseLinear.v2-kwargs56-in_data56-out_data56] ______________________________________

name = 'SparseLinear.v2', kwargs = {}, in_data = (array([1, 2, 3], dtype=uint64), array([1., 2., 3.], dtype=float32), array([1, 1], dtype=int32))
out_data = array([[4., 2., 3., 4.],
       [1., 5., 3., 1.],
       [9., 8., 5., 7.]], dtype=float32)

    @pytest.mark.parametrize("name,kwargs,in_data,out_data", TEST_CASES)
    def test_layers_from_config(name, kwargs, in_data, out_data):
        cfg = {"@layers": name, **kwargs}
        filled_cfg = registry.fill({"config": cfg})
        assert srsly.is_json_serializable(filled_cfg)
        model = registry.resolve({"config": cfg})["config"]
        if "LSTM" in name:
            model = with_padded(model)
        valid = True
        with data_validation(valid):
>           model.initialize(in_data, out_data)

thinc/tests/layers/test_layers_api.py:153:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
thinc/model.py:316: in initialize
    validate_fwd_input_output(self.name, self._func, X, Y)
thinc/util.py:588: in validate_fwd_input_output
    ArgModel.update_forward_refs(**types.__dict__)
pydantic/v1/main.py:814: in update_forward_refs
    update_model_forward_refs(cls, cls.__fields__.values(), cls.__config__.json_encoders, localns)
pydantic/v1/typing.py:559: in update_model_forward_refs
    update_field_forward_refs(f, globalns=globalns, localns=localns)
pydantic/v1/typing.py:525: in update_field_forward_refs
    field.type_ = evaluate_forwardref(field.type_, globalns, localns or None)
pydantic/v1/typing.py:68: in evaluate_forwardref
    return cast(Any, type_)._evaluate(globalns, localns, recursive_guard=set())
typing.py:1081: in _evaluate
    eval(self.__forward_code__, globalns, localns),
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

>   ???
E   NameError: name 'InT' is not defined

<string>:1: NameError
______________________________________ test_layers_from_config[premap_ids.v1-kwargs59-in_data59-out_data59] _______________________________________

name = 'premap_ids.v1', kwargs = {'column': 1, 'mapping_table': {}}, in_data = array([[1, 4],
       [2, 5],
       [3, 6]])
out_data = array([[1, 2, 3],
       [4, 5, 6]], dtype=int32)

    @pytest.mark.parametrize("name,kwargs,in_data,out_data", TEST_CASES)
    def test_layers_from_config(name, kwargs, in_data, out_data):
        cfg = {"@layers": name, **kwargs}
        filled_cfg = registry.fill({"config": cfg})
        assert srsly.is_json_serializable(filled_cfg)
        model = registry.resolve({"config": cfg})["config"]
        if "LSTM" in name:
            model = with_padded(model)
        valid = True
        with data_validation(valid):
>           model.initialize(in_data, out_data)

thinc/tests/layers/test_layers_api.py:153:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
thinc/model.py:316: in initialize
    validate_fwd_input_output(self.name, self._func, X, Y)
thinc/util.py:588: in validate_fwd_input_output
    ArgModel.update_forward_refs(**types.__dict__)
pydantic/v1/main.py:814: in update_forward_refs
    update_model_forward_refs(cls, cls.__fields__.values(), cls.__config__.json_encoders, localns)
pydantic/v1/typing.py:559: in update_model_forward_refs
    update_field_forward_refs(f, globalns=globalns, localns=localns)
pydantic/v1/typing.py:525: in update_field_forward_refs
    field.type_ = evaluate_forwardref(field.type_, globalns, localns or None)
pydantic/v1/typing.py:68: in evaluate_forwardref
    return cast(Any, type_)._evaluate(globalns, localns, recursive_guard=set())
typing.py:1081: in _evaluate
    eval(self.__forward_code__, globalns, localns),
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

>   ???
E   NameError: name 'InT' is not defined

<string>:1: NameError
_______________________________________________________________ test_dropout[data2] _______________________________________________________________

data = Padded(data=array([[[0., 0., 0.],
        [0., 0., 0.],
        [0., 0., 0.]],

       [[0., 0., 0.],
        [0., 0.,...array([1., 2., 3.], dtype=float32), lengths=array([1, 2, 3, 4], dtype=int32), indices=array([1, 2, 3, 4], dtype=int32))

    @pytest.mark.parametrize("data", [array2d, ragged, padded, [array2d, array2d]])
    def test_dropout(data):
        model = Dropout(0.2)
>       model.initialize(data, data)

thinc/tests/layers/test_layers_api.py:181:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
thinc/model.py:316: in initialize
    validate_fwd_input_output(self.name, self._func, X, Y)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

name = 'dropout', func = <function forward at 0x7bd4ac549d00>
X = Padded(data=array([[[0., 0., 0.],
        [0., 0., 0.],
        [0., 0., 0.]],

       [[0., 0., 0.],
        [0., 0.,...array([1., 2., 3.], dtype=float32), lengths=array([1, 2, 3, 4], dtype=int32), indices=array([1, 2, 3, 4], dtype=int32))
Y = Padded(data=array([[[0., 0., 0.],
        [0., 0., 0.],
        [0., 0., 0.]],

       [[0., 0., 0.],
        [0., 0.,...array([1., 2., 3.], dtype=float32), lengths=array([1, 2, 3, 4], dtype=int32), indices=array([1, 2, 3, 4], dtype=int32))

    def validate_fwd_input_output(
        name: str, func: Callable[[Any, Any, bool], Any], X: Any, Y: Any
    ) -> None:
        """Validate the input and output of a forward function against the type
        annotations, if available. Used in Model.initialize with the input and
        output samples as they pass through the network.
        """
        sig = inspect.signature(func)
        empty = inspect.Signature.empty
        params = list(sig.parameters.values())
        if len(params) != 3:
            bad_params = f"{len(params)} ({', '.join([p.name for p in params])})"
            err = f"Invalid forward function. Expected 3 arguments (model, X , is_train), got {bad_params}"
            raise DataValidationError(name, X, Y, [{"msg": err}])
        annot_x = params[1].annotation
        annot_y = sig.return_annotation
        sig_args: Dict[str, Any] = {"__config__": _ArgModelConfig}
        args = {}
        if X is not None and annot_x != empty:
            if isinstance(X, list) and len(X) > 5:
                X = X[:5]
            sig_args["X"] = (annot_x, ...)
            args["X"] = X
        if Y is not None and annot_y != empty:
            if isinstance(Y, list) and len(Y) > 5:
                Y = Y[:5]
            sig_args["Y"] = (annot_y, ...)
            args["Y"] = (Y, lambda x: x)
        ArgModel = create_model("ArgModel", **sig_args)
        # Make sure the forward refs are resolved and the types used by them are
        # available in the correct scope. See #494 for details.
        ArgModel.update_forward_refs(**types.__dict__)
        try:
            ArgModel.parse_obj(args)
        except ValidationError as e:
>           raise DataValidationError(name, X, Y, e.errors()) from None
E           thinc.util.DataValidationError:
E
E           Data validation error in 'dropout'
E           X: <class 'thinc.types.Padded'> Y: <class 'thinc.types.Padded'>
E
E           X                     not a valid numpy or cupy array
E           X                     not a valid numpy or cupy array
E           X                     not a valid numpy or cupy array
E           X                     not a valid numpy or cupy array
E           X                     not a valid numpy or cupy array
E           X                     not a valid numpy or cupy array
E           X                     not a valid numpy or cupy array
E           X                     not a valid numpy or cupy array
E           X                     value is not a valid sequence
E           X                     instance of Ragged, tuple or dict expected
E           X -> size_at_t        wrong array data type (expected int32/int64/uint32/uint64, got float32)
E           Y -> 0                not a valid numpy or cupy array
E           Y -> 0                not a valid numpy or cupy array
E           Y -> 0                not a valid numpy or cupy array
E           Y -> 0                not a valid numpy or cupy array
E           Y -> 0                not a valid numpy or cupy array
E           Y -> 0                not a valid numpy or cupy array
E           Y -> 0                not a valid numpy or cupy array
E           Y -> 0                not a valid numpy or cupy array
E           Y -> 0                value is not a valid sequence
E           Y -> 0                instance of Ragged, tuple or dict expected
E           Y -> 0 -> size_at_t   wrong array data type (expected int32/int64/uint32/uint64, got float32)

thinc/util.py:592: DataValidationError
______________________________________ test_layers_batching_all[premap_ids.v1-kwargs59-in_data59-out_data59] ______________________________________

name = 'premap_ids.v1', kwargs = {'column': 1, 'mapping_table': {}}, in_data = array([[1, 4],
       [2, 5],
       [3, 6]])
out_data = array([[1, 2, 3],
       [4, 5, 6]], dtype=int32)

    @pytest.mark.parametrize("name,kwargs,in_data,out_data", TEST_CASES)
    def test_layers_batching_all(name, kwargs, in_data, out_data):
        cfg = {"@layers": name, **kwargs}
        model = registry.resolve({"config": cfg})["config"]
        if "expand_window" in name:
            return
        if "LSTM" in name:
            model = with_padded(model)
            util_batch_unbatch_list(model, in_data, out_data)
        else:
            if isinstance(in_data, OPS.xp.ndarray) and in_data.ndim == 2:
                if isinstance(out_data, OPS.xp.ndarray) and out_data.ndim == 2:
>                   util_batch_unbatch_array(model, in_data, out_data)

thinc/tests/layers/test_layers_api.py:200:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
thinc/tests/layers/test_layers_api.py:211: in util_batch_unbatch_array
    model.initialize(in_data, out_data)
thinc/model.py:316: in initialize
    validate_fwd_input_output(self.name, self._func, X, Y)
thinc/util.py:588: in validate_fwd_input_output
    ArgModel.update_forward_refs(**types.__dict__)
pydantic/v1/main.py:814: in update_forward_refs
    update_model_forward_refs(cls, cls.__fields__.values(), cls.__config__.json_encoders, localns)
pydantic/v1/typing.py:559: in update_model_forward_refs
    update_field_forward_refs(f, globalns=globalns, localns=localns)
pydantic/v1/typing.py:525: in update_field_forward_refs
    field.type_ = evaluate_forwardref(field.type_, globalns, localns or None)
pydantic/v1/typing.py:68: in evaluate_forwardref
    return cast(Any, type_)._evaluate(globalns, localns, recursive_guard=set())
typing.py:1081: in _evaluate
    eval(self.__forward_code__, globalns, localns),
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

>   ???
E   NameError: name 'InT' is not defined

<string>:1: NameError
================================================================ warnings summary =================================================================
../..thinc/tests/layers/test_mnist.py:81
  thinc/tests/layers/test_mnist.py:81: PytestUnknownMarkWarning: Unknown pytest.mark.slow - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.slow

../..thinc/tests/regression/issue519/test_issue519.py:13
  thinc/tests/regression/issue519/test_issue519.py:13: PytestUnknownMarkWarning: Unknown pytest.mark.slow - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.slow

tests/backends/test_ops.py: 80 warnings
  thinc/tests/backends/test_ops.py:1541: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)
    assert ops.xp.isclose(x_torch.grad.item() * dY, float(dx_thinc), atol=1e-06)

tests/backends/test_ops.py: 360 warnings
  thinc/tests/backends/test_ops.py:1549: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)
    x_torch.grad.item() * dY, float(backward(dY_thinc, X=x_thinc)), atol=1e-06

tests/backends/test_ops.py: 40 warnings
  thinc/tests/backends/test_ops.py:1533: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)
    assert ops.xp.isclose(x_torch.grad.item() * dY, float(dx_thinc), atol=1e-06)

tests/layers/test_layers_api.py: 1746 warnings
tests/layers/test_lstm.py: 238 warnings
tests/layers/test_transforms.py: 296 warnings
tests/layers/test_with_transforms.py: 2508 warnings
tests/model/test_validation.py: 145 warnings
tests/test_config.py: 66 warnings
  pydantic/v1/typing.py:68: DeprecationWarning: Failing to pass a value to the 'type_params' parameter of 'typing.ForwardRef._evaluate' is deprecated, as it leads to incorrect behaviour when calling typing.ForwardRef._evaluate on a stringified annotation that references a PEP 695 type parameter. It will be disallowed in Python 3.15.
    return cast(Any, type_)._evaluate(globalns, localns, recursive_guard=set())

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
============================================================= short test summary info =============================================================
FAILED tests/backends/test_ops.py::test_ops_consistency[NumpyOps] - AssertionError: alloc
FAILED tests/layers/test_layers_api.py::test_layers_from_config[SparseLinear.v1-kwargs55-in_data55-out_data55] - NameError: name 'InT' is not defined
FAILED tests/layers/test_layers_api.py::test_layers_from_config[SparseLinear.v2-kwargs56-in_data56-out_data56] - NameError: name 'InT' is not defined
FAILED tests/layers/test_layers_api.py::test_layers_from_config[premap_ids.v1-kwargs59-in_data59-out_data59] - NameError: name 'InT' is not defined
FAILED tests/layers/test_layers_api.py::test_dropout[data2] - thinc.util.DataValidationError:
FAILED tests/layers/test_layers_api.py::test_layers_batching_all[premap_ids.v1-kwargs59-in_data59-out_data59] - NameError: name 'InT' is not defined
=========================================== 6 failed, 1298 passed, 88 skipped, 5481 warnings in 22.47s ============================================

davispuh avatar Mar 07 '25 17:03 davispuh

Thanks for attention on this. We've had a size restriction on PyPi that prevented releases on spaCy, and there's issues associated with Python 3.13 to do with the Pydantic usage for validation. I'm working on the update now.

We need to target the 8.3.x branch. That's the one spaCy uses.

I've been streaming some spaCy and ecosystem development to increase transparency, so you can see some of the issues around Pydantic in this stream: https://www.youtube.com/watch?v=TTEWWJ2PmZQ&list=PLBmcuObd5An5_iAxNYLJa_xWmNzsYce8c&index=1&t=2882s . I've had to work on a consulting project the last couple of weeks so I had to pause work on this, but I'm back on it now.

honnibal avatar Apr 03 '25 08:04 honnibal

Guys any news on this? Lots of packages are depending on spaCy and it can't update to support for Py 3.13.

Unknownuserfrommars avatar Apr 04 '25 08:04 Unknownuserfrommars

Not sure how the just-released spacy 3.8.7 works with python 3.13, given that it has a thinc>=8.3.4,<8.4.0 build requirement, and there's no 3.13 support for thinc yet... 🤔

h-vetinari avatar May 23 '25 12:05 h-vetinari

PLEASE merge this PR so downstream dependencies like SpaCy (and my own projects) can finally use Python 3.13.

shredEngineer avatar Jul 06 '25 08:07 shredEngineer

This PR is not really ready though. I verified that the test failures still happen and I do not have time to fix investigate them.

J08nY avatar Jul 06 '25 10:07 J08nY

thinc v8.3.6 has been out for ~3 month with cython 3 & py313 support; the requirements file (on that branch) is not up to date though: https://github.com/explosion/thinc/blob/037c264eb7fc607387025f5dc6f68d738c972405/requirements.txt#L20

h-vetinari avatar Jul 06 '25 10:07 h-vetinari

Interesting. Anyone knows, why this support is not in the master branch or the v9.x versions?

J08nY avatar Jul 06 '25 12:07 J08nY

@J08nY The v9 branch is ahead of what spaCy is supporting, and the plan is actually to phase out Thinc in spaCy, so I haven't been keeping it updated. Basically the v8.3.x branch is the one that matters.

honnibal avatar Jul 11 '25 11:07 honnibal

I see. So v8.3.x supports Python 3.13 and v9.x does not. And spaCy targets v8.3.x. So it may make sense to pick out the Python 3.13 support from the v8.3.x branch to main as well right? If that is possible. I will close this PR the, as it really does not do much and people should just use v8.3.x then.

J08nY avatar Jul 14 '25 10:07 J08nY