Repository: fractal-analytics-platform/fractal-tasks-core (language: python)

Issue:
Relaxing pydantic requirements
Hi @tcompa

I have seen that switching to `pydantic v2` #592 still has some issues. But I have encountered a couple of critical installation issues with packages depending on `pydantic>2` while trying to create new tasks (for example, https://github.com/bioimage-io/spec-bioimage-io that requires `pydantic>=2.6.3, 3`).  

Would it be an option to use the legacy `pydantic.v1` like in https://github.com/napari/napari/pull/6358 until there is a good solution for V2 and, in the meantime, update the requirements for `fractal-tasks-core`?

Cellpose normalization
## Checklist before merging
- [ x] I added an appropriate entry to `CHANGELOG.md`

The cellpose_segmentation task currently accepts a custom normalization option. The problem is, that if two channels are used for the cellpose segmentation, both channels are normalized with the same `CellposeCustomNormalizer`, but one might want to normalize them with different parameters.
I added an optional `normalize2` argument to `cellpose_segmentation`, and updated `segement_ROI` accordingly.
No tests are implemented yet.


Discussion / hints:
For context, we use Pydantic for three parts of fractal-tasks-core

1. We define models for objects to be used within the core library and/or tasks (including for task arguments - see point 3).
2. We validate arguments of the task functions, with the `validate_arguments` decorator
3. We generate JSON Schemas for the task arguments, with the core functionality being the one in this block https://github.com/fractal-analytics-platform/fractal-tasks-core/blob/243b3f43fde234be77d3469866938d9ae803ef58/fractal_tasks_core/dev/lib_args_schemas.py#L230-L235

---

> Would it be an option to use the legacy pydantic.v1 like in https://github.com/napari/napari/pull/6358 until there is a good solution for V2 and, in the meantime, update the requirements for fractal-tasks-core?

Yes, I think this should work. We would bump the required version e.g. to `>=2.0.0`, and replace all current imports with `pydantic.v1`. Note that the three items above would still all work in Pydantic V1. This means that if spec-bioimage-io defines a Pydantic V2 model that you may want to use for a task argument, it's not guaranteed that it will work. If V2 models are only used internally, within spec-bioimage-io, then I don't think there will be issues.

Whether this intermediate step makes sense mostly depends on when we plan to tackle #592. For the record, preparing #737 was not a crazy amount of work (there are a bunch of validators that need to be ported to the new style, and https://github.com/pydantic/bump-pydantic also helped). There is a single critical point which concerns point 3 above, but the rest is mostly repetitive work..
Thanks for the clarification. In both problematic dependencies I have, pydantic is only used internally. So it won't be exposed to `fractal-tasks-core` validation/schema generation. 
The only issue I can foresee is that if not properly documented, this can be confusing for task creators. They might be tempted to use V2 models in their tasks. 

@jluethi, what's your opinion on this issue? 

If we decide to take the intermediate step, I can prepare a PR. It should be fairly straightforward, and I will see more of the fractal code base. 

@lorenzocerrone Is this urgent or can we review on Wednesday on how best to proceed?

My takeaway would be: I'm generally in favor of moving us to actually use Pydantic V2 in tasks core. 

We will need to ensure that the **manifest building works for Pydantic V1 & v2** at least for a certain transition phase though, as the current **task packages by other people may also use Pydantic V1**.
If we can have a good way of switching fractal tasks core to Pydantic V2 while maintaining this backwards compatibility, I'd be in favor of this being focused on instead of the `pydantic.v1` imports. Maybe also a good way to have deeper fractal task core discussion between @tcompa & @lorenzocerrone :)

If we see this transition to be tricky, then I'm fine with first building `pydantic.v1` version of fractal-tasks-core.
@jluethi, No, it's not urgent; we can discuss it on Wednesday. @tcompa, we can also find a time slot on Wednesday to discuss the fractal core in detail. 

The most recent versions of Pydantic V1 now also offer the possibility of doing `from pydantic.v1 import ...`, to simplify the V2 transition (see https://docs.pydantic.dev/1.10/changelog).

I think this is helpful for us, and we could proceed as follows:
1. Bump the Pydantic requirement to `==1.10.16` (which is likely [the last V1 version](https://github.com/pydantic/pydantic/pull/9162#issuecomment-2145199409)).
2. Replace all imports with `pydantic.v1`.
3. Check that all tests pass.
4. Change the Pydantic requirement to accept both `==1.10.16` and `>=2.7.0` (or any other recent 2.X version). 
5. Update Pydantic to V2 in poetry.lock, and check that all tests pass.

This allows us to unlock the dependency constraint, while keeping fractal-tasks-core fully in Pydantic V1 and with not version-specific code. The next step is then #592, which means that:
* The core-library and tasks will fully move to Pydantic V2
* Manifest building will work both for generating `pydantic_v1` schemas (by using `pydantic.v1` imports and the current logic) and `pydantic_v2` schemas (by using a new logic similar to the one I tested in #737).
* Manifest building for fractal-tasks-core itself will use the `pydantic_v2` option. Other packages can specify this by setting `args_schema_version`, or we can infer it at runtime by looking at the installed Pydantic version.

What would **not** be supported is the following scenario
* I have a task package with Pydantic V1 models, also used as argument
* I want to build the manifest with Pydantic V2

Another non-supported use case (possibly more critical) would be
* I re-use some of the fractal-tasks-core input models, which will be (after #592) written in Pydantic V2, and I need these models when building my manifest.
* I want to stick to Pydantic V1 for my package dependency and internal models.
Thanks a lot for the PR @fstur ! Very useful functionality to have indeed.

As discussed today, @lorenzocerrone and I refactored the Cellpose input models to handle the normalization aspects better: Normalization can now be specified when selecting the channel. This needed a bit of reworking to make it work with the Pydantic models afterwards.
I also used this opportunity to move a bit of code out of the task logic into the new `CellposeChannel1InputModel`.

Things that remain:

- [x] Add specific tests for the normalization to ensure it works as expected => check coverage & add new unit tests for normalization functions (goal is not to test cellpose behavior itself)
- [x] Refactor other cellpose inputs into an `Advanced Cellpose Parameters` model to keep the task reasonably compact
- [x] Update Changelog with API changes to Cellpose task
- [x] Test locally on some real data with different normalization options

----

Unrelated (cc @tcompa ):
I'm suggesting we disabled flake8 E203 checks, because this block has a flake8 vs. black conflict about whether there should be spaces around the colon:
```
x[channels[0] - 1 : channels[0]] = normalized_img(
                x[channels[0] - 1 : channels[0]],
                lower_p=normalize.lower_percentile,
                upper_p=normalize.upper_percentile,
                lower_bound=normalize.lower_bound,
                upper_bound=normalize.upper_bound,
            )
```
It's also what made the checks above fail before.

I also added an exception for flake8 W503, which conflicts with black in how to format the cellpose tests.
(unrelated: do we want to consider moving to ruff linting?)
> Unrelated (cc @tcompa ): I'm suggesting we disabled flake8 E203 [...]

Agreed. This handling of `:` also annoys me, on top of often requiring `# noqa` comments.

> (unrelated: do we want to consider moving to ruff linting?)

Sure, nothing against it. It's not like pre-commit takes very long (`time poetry run pre-commit run --all-files` takes ~1.7 seconds on my machine, which includes reordering imports, black, flake8 and bandit), but of course a speedup wouldn't hurt.
Once we have it in this repo, let's also make sure we adopt it in fractal-server and fractal-tasks-core - to keep them consistent.
Hey @fstur
I finished my work on this PR. It now has the channel & normalization input combined, plus the whole normalization logic moved to its own, tests helper function (see `_normalize_cellpose_channels`).
I also modified the advanced model inputs into a `CellposeModelParams` object that can be passed to the task, so the logic of the task input changes a bit both on the Python side as well as the Fractal web side.

Can you check whether this version now works for you? I'd be happy with merging this into fractal tasks core & then providing the normalization approach for both channels.

-----

@tcompa Can you review this PR?

One of the slightly weird things I'm doing is making `channel2` optional in the task signature to avoid it being expanded in Fractal web. Because the whole block is already quite big.
Fractal web always initializes an object for it though, because the Normalizer has default values => I'll open a separate issue to discuss this further.

-----

@lorenzocerrone If you also want to have a look, open for further feedback :) But not a requirement that you review the PR.

> One of the slightly weird things I'm doing is making channel2 optional in the task signature to avoid it being expanded in Fractal web

This comment is not relevant any more as of https://github.com/fractal-analytics-platform/fractal-tasks-core/pull/738/commits/b63547f5c0295d812c10d5359053d561da6020c5, right?
> This comment is not relevant any more as of https://github.com/fractal-analytics-platform/fractal-tasks-core/commit/b63547f5c0295d812c10d5359053d561da6020c5, right?

Right! Now I made the normalize part of channel have a default. Would be great if you could review if the use of Field with default_factory mode is a good approach for this
Re: `channel2` type hint and JSON Schema

I could not find a better way for making `channel2` fully optional (as in "it is set to `null` in `fractal-web` UI unless at lease one of its attribute was set explicitly by the user"). Thus I'd proceed as in the PR: the `is_set()` method is quite clear in its meaning, and it doesn't introduce a lot of complexity.

Higher-level: I agree about

> Fractal web always initializes an object for it though, because the Normalizer has default values

and this is what blocks us towards a clean version where `channel2` is fully optional. Note that this issue is at least partly a consequence of `fractal-web` logic: when working with a CLI client we wouldn't have issues in setting a `None` default for an `Optional` function argument.

---

> I'll open a separate issue to discuss this further.

I think it's worth continuing this discussion (in a different issue, indeed), because it sounds like a possibly frequent pattern. Note that the outcome in principle could also look like "refactor the `CellposeCustomNormalizer` model so that it has no default value", since this would likely solve the whole issue.

[continues below, concerning `default_factory`]
Re: `default_factory`

Using `arg: Field(default_factory=Model)` rather than `arg: Model = Model()` has a difference, but I think it is not relevant for this case. Note that the latter is the one we are currently using in `main` for
https://github.com/fractal-analytics-platform/fractal-tasks-core/blob/00cbfdfc062d6c615cef073b29d4628224903fa0/fractal_tasks_core/tasks/cellpose_segmentation.py#L214


The main difference is that the produced JSON Schemas are different. In the example below, I reproduce a minimal example with two function arguments (one always using `default_factory`, and the other using the same `Model()` option):

Python
```python
from typing import Optional, Literal
from pydantic import BaseModel, Field


class Normalizer(BaseModel):
    """
    Description of Normalizer

    Attributes:
        type: Description of type
        time: Description of time
    """
    type: Literal["option_a", "option_b"] = "option_a"

class MyModelDefaultFactory(BaseModel):
    """
    Description of ChannelModel

    Attributes:
        normalizer: Description of normalizer.
        time: Description of time.
        attr: Description of attr1.
    """
    normalizer: Normalizer = Field(default_factory=Normalizer)
    time: float = Field(default_factory=time.perf_counter)
    attr: Optional[str] = None

class MyModelObjectInit(BaseModel):
    """
    Description of ChannelModel

    Attributes:
        normalizer: Description of normalizer.
        time: Description of time.
        attr: Description of attr1.
    """
    normalizer: Normalizer = Normalizer()
    time: float = time.perf_counter()
    attr: Optional[str] = None


@validate_arguments
def function(
        arg_default_factory: MyModelDefaultFactory = Field(default_factory=MyModelDefaultFactory),
        arg2: MyModelObjectInit = MyModelObjectInit(),
        ):
    """
    Description of function

    Args:
        arg_default_factory: Description of arg_default_factory
        arg2: Description of arg2
    """
    pass
```
which leads to
```json
{
  "title": "Function",
  "type": "object",
  "properties": {
    "arg_default_factory": {
      "$ref": "#/definitions/MyModelDefaultFactory",
      "title": "Arg_Default_Factory",
      "description": "Description of arg_default_factory"
    },
    "arg2": {
      "title": "Arg2",
      "default": {
        "normalizer": {
          "type": "option_a"
        },
        "time": 24457.131491715,
        "attr": null
      },
      "allOf": [
        {
          "$ref": "#/definitions/MyModelObjectInit"
        }
      ],
      "description": "Description of arg2"
    }
  },
  "additionalProperties": false,
  "definitions": {
    "Normalizer": {
      "title": "Normalizer",
      "description": "Description of Normalizer",
      "type": "object",
      "properties": {
        "type": {
          "title": "Type",
          "default": "option_a",
          "enum": [
            "option_a",
            "option_b"
          ],
          "type": "string"
        }
      }
    },
    "MyModelDefaultFactory": {
      "title": "MyModelDefaultFactory",
      "description": "Description of ChannelModel",
      "type": "object",
      "properties": {
        "normalizer": {
          "$ref": "#/definitions/Normalizer",
          "title": "Normalizer"
        },
        "time": {
          "title": "Time",
          "type": "number"
        },
        "attr": {
          "title": "Attr",
          "type": "string"
        }
      }
    },
    "MyModelObjectInit": {
      "title": "MyModelObjectInit",
      "description": "Description of ChannelModel",
      "type": "object",
      "properties": {
        "normalizer": {
          "title": "Normalizer",
          "default": {
            "type": "option_a"
          },
          "allOf": [
            {
              "$ref": "#/definitions/Normalizer"
            }
          ]
        },
        "time": {
          "title": "Time",
          "default": 24457.131491715,
          "type": "number"
        },
        "attr": {
          "title": "Attr",
          "type": "string"
        }
      }
    }
  }
}
```

The main difference here is the presence of
```json
 "default": {
        "normalizer": {
          "type": "option_a"
        },
        "time": 24457.131491715,
        "attr": null
      },
```
for `arg2`, while it is missing when we use `default_factory`.
Part of the explanation is that `default_factory` should not be filled upon schema generation (see e.g. https://github.com/pydantic/pydantic/issues/1520), and the `time` attribute that I include above helps to show this behavior (it is present when it's called explicitly, but not when using a `default_factory=time.perf_counter`).

# Does this matter for `fractal-web`?

In the fractal-web UI, this difference appears as irrelevant. If I don't modify any form field, current data are identical for the two cases (apart from the `time` attribute, but that is expected):
![image](https://github.com/fractal-analytics-platform/fractal-tasks-core/assets/3862206/8131cb77-dada-4c2c-97c5-1fb48f074455)


# Does this matter for `fractal-server`?

For top-level arguments (that is, this does not concern nested attribute of complex arguments), the presence/absence of a `default` in the JSON Schema does matter in fractal-server. If a `default` value is present, then it is used as a basis during any `insert-worfklowtask` or `patch-workflowtask` operation.
Considering the example above:
1. If we always go through `fractal-web`, there will always be a client-provided value (and therefore the default is not relevant).
2. If we use a different client, then the usage based on `default_factory` will have no default in the arguments JSON file, and the default will be generated at runtime via the `default_factory`.

I cannot find any issue with either option, right now, but I wouldn't be surprised if we eventually identify some edge case of one or the other.

[continues with a TL;DR]
TL;DR re: `default_factory`

1. The advantage of using `default_factory` is that we are not using a mutable function argument (which is a bad practice, but irrelevant for the standard command-line usage of fractal tasks)
2. The advantage of using `arg: MyModel = MyModel()` is that the `default` field in the JSON Schema is set, making it more transparent.
3. The two cases appear in the same in `fractal-web`, for my dummy example and also for the current PR.
4. I cannot think of an actual problematic edge case related to using one or the other.

---

If I were to choose, I would replace
```python
channel2: CellposeChannel2InputModel = Field(default_factory=CellposeChannel2InputModel)
```
with
```python
channel2: CellposeChannel2InputModel = CellposeChannel2InputModel()
```
which creates a more transparent JSON Schema that includes
```diff           "channel2": {
-            "$ref": "#/definitions/CellposeChannel2InputModel",
             "title": "Channel2",
+            "default": {
+              "wavelength_id": null,
+              "label": null,
+              "normalize": {
+                "type": "default",
+                "lower_percentile": null,
+                "upper_percentile": null,
+                "lower_bound": null,
+                "upper_bound": null
+              }
+            },
+            "allOf": [
+              {
+                "$ref": "#/definitions/CellposeChannel2InputModel"
+              }
+            ],
```
but this is just personal preference and I have no specific reasons for it.
This closes my feedback on the arguments/schema-related part of the PR.

I can still review the rest of the PR, if it's useful, but that will be a bit later.

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 68f9be57f..6f822cbdc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@
     * Refactor Cellpose Task inputs: Support independent normalization of 2 input channels in the Cellpose task (\#738).
     * Rename `task.cellpose_transforms` into `tasks.cellpose_utils` (\#738).
 * Development:
+    * Switch to transitional pydantic.v1 imports, changes pydantic requirement to `==1.10.16` or `>=2.6.3` (\#760).
     * Support JSON-Schema generation for `Enum` task arguments (\#749).
     * Make JSON-Schema generation tools more flexible, to simplify testing (\#749).
 * Update documentation (\#751).
diff --git a/fractal_tasks_core/__init__.py b/fractal_tasks_core/__init__.py
index 4b38628ea..b45e18c80 100644
--- a/fractal_tasks_core/__init__.py
+++ b/fractal_tasks_core/__init__.py
@@ -1,6 +1,26 @@
 import logging
 
 
+def _check_pydantic_version():
+    """
+    Temporary check for pydantic version.
+    To be removed after moving to pydantic v2 is complete.
+    """
+    import importlib.metadata
+    from packaging import version
+
+    pydantic_version = version.parse(importlib.metadata.version("pydantic"))
+    pydantic_v1 = version.parse("1.10.16")
+    pydantic_v2 = version.parse("2.6.3")
+    if pydantic_version != pydantic_v1 and pydantic_version < pydantic_v2:
+        raise ImportError(
+            f"Pydantic version {pydantic_version} is not supported. "
+            f"Please use version =={pydantic_v1} or  >={pydantic_v2}."
+        )
+
+
+_check_pydantic_version()
+
 logging.basicConfig(
     level=logging.INFO, format="%(asctime)s; %(levelname)s; %(message)s"
 )
diff --git a/fractal_tasks_core/channels.py b/fractal_tasks_core/channels.py
index d3cbfa0f5..226323e94 100644
--- a/fractal_tasks_core/channels.py
+++ b/fractal_tasks_core/channels.py
@@ -18,8 +18,8 @@
 from typing import Union
 
 import zarr
-from pydantic import BaseModel
-from pydantic import validator
+from pydantic.v1 import BaseModel
+from pydantic.v1 import validator
 
 from fractal_tasks_core import __OME_NGFF_VERSION__
 
diff --git a/fractal_tasks_core/dev/lib_args_schemas.py b/fractal_tasks_core/dev/lib_args_schemas.py
index 60b84bd65..a28a5429e 100644
--- a/fractal_tasks_core/dev/lib_args_schemas.py
+++ b/fractal_tasks_core/dev/lib_args_schemas.py
@@ -21,11 +21,11 @@
 from typing import Optional
 
 from docstring_parser import parse as docparse
-from pydantic.decorator import ALT_V_ARGS
-from pydantic.decorator import ALT_V_KWARGS
-from pydantic.decorator import V_DUPLICATE_KWARGS
-from pydantic.decorator import V_POSITIONAL_ONLY_NAME
-from pydantic.decorator import ValidatedFunction
+from pydantic.v1.decorator import ALT_V_ARGS
+from pydantic.v1.decorator import ALT_V_KWARGS
+from pydantic.v1.decorator import V_DUPLICATE_KWARGS
+from pydantic.v1.decorator import V_POSITIONAL_ONLY_NAME
+from pydantic.v1.decorator import ValidatedFunction
 
 from fractal_tasks_core.dev.lib_descriptions import (
     _get_class_attrs_descriptions,
diff --git a/fractal_tasks_core/dev/lib_signature_constraints.py b/fractal_tasks_core/dev/lib_signature_constraints.py
index 9a4d7abf6..a16efae47 100644
--- a/fractal_tasks_core/dev/lib_signature_constraints.py
+++ b/fractal_tasks_core/dev/lib_signature_constraints.py
@@ -15,10 +15,10 @@
 from pathlib import Path
 from typing import Callable
 
-from pydantic.decorator import ALT_V_ARGS
-from pydantic.decorator import ALT_V_KWARGS
-from pydantic.decorator import V_DUPLICATE_KWARGS
-from pydantic.decorator import V_POSITIONAL_ONLY_NAME
+from pydantic.v1.decorator import ALT_V_ARGS
+from pydantic.v1.decorator import ALT_V_KWARGS
+from pydantic.v1.decorator import V_DUPLICATE_KWARGS
+from pydantic.v1.decorator import V_POSITIONAL_ONLY_NAME
 
 FORBIDDEN_PARAM_NAMES = (
     "args",
diff --git a/fractal_tasks_core/dev/task_models.py b/fractal_tasks_core/dev/task_models.py
index 89a650866..2d2ff9835 100644
--- a/fractal_tasks_core/dev/task_models.py
+++ b/fractal_tasks_core/dev/task_models.py
@@ -18,7 +18,7 @@
 from typing import Any
 from typing import Optional
 
-from pydantic import BaseModel
+from pydantic.v1 import BaseModel
 
 
 class _BaseTask(BaseModel):
diff --git a/fractal_tasks_core/labels.py b/fractal_tasks_core/labels.py
index 2dff5ac88..f15f82f91 100644
--- a/fractal_tasks_core/labels.py
+++ b/fractal_tasks_core/labels.py
@@ -16,7 +16,7 @@
 from typing import Optional
 
 import zarr.hierarchy
-from pydantic.error_wrappers import ValidationError
+from pydantic.v1.error_wrappers import ValidationError
 
 from fractal_tasks_core.ngff import NgffImageMeta
 from fractal_tasks_core.zarr_utils import OverwriteNotAllowedError
diff --git a/fractal_tasks_core/ngff/specs.py b/fractal_tasks_core/ngff/specs.py
index 3c5700c34..9d65cc62e 100644
--- a/fractal_tasks_core/ngff/specs.py
+++ b/fractal_tasks_core/ngff/specs.py
@@ -7,9 +7,9 @@
 from typing import Optional
 from typing import Union
 
-from pydantic import BaseModel
-from pydantic import Field
-from pydantic import validator
+from pydantic.v1 import BaseModel
+from pydantic.v1 import Field
+from pydantic.v1 import validator
 
 
 logger = logging.getLogger(__name__)
diff --git a/fractal_tasks_core/roi/v1_checks.py b/fractal_tasks_core/roi/v1_checks.py
index aa46366b7..fc3aa4672 100644
--- a/fractal_tasks_core/roi/v1_checks.py
+++ b/fractal_tasks_core/roi/v1_checks.py
@@ -17,7 +17,7 @@
 
 import anndata as ad
 import zarr
-from pydantic.error_wrappers import ValidationError
+from pydantic.v1.error_wrappers import ValidationError
 
 from fractal_tasks_core.tables.v1 import MaskingROITableAttrs
 
diff --git a/fractal_tasks_core/tables/v1.py b/fractal_tasks_core/tables/v1.py
index 67d26f73e..12aa78132 100644
--- a/fractal_tasks_core/tables/v1.py
+++ b/fractal_tasks_core/tables/v1.py
@@ -11,9 +11,9 @@
 import anndata as ad
 import zarr.hierarchy
 from anndata.experimental import write_elem
-from pydantic import BaseModel
-from pydantic import validator
-from pydantic.error_wrappers import ValidationError
+from pydantic.v1 import BaseModel
+from pydantic.v1 import validator
+from pydantic.v1.error_wrappers import ValidationError
 
 from fractal_tasks_core.zarr_utils import OverwriteNotAllowedError
 
diff --git a/fractal_tasks_core/tasks/apply_registration_to_image.py b/fractal_tasks_core/tasks/apply_registration_to_image.py
index a8e3a718b..d7b435ecc 100644
--- a/fractal_tasks_core/tasks/apply_registration_to_image.py
+++ b/fractal_tasks_core/tasks/apply_registration_to_image.py
@@ -21,7 +21,7 @@
 import dask.array as da
 import numpy as np
 import zarr
-from pydantic.decorator import validate_arguments
+from pydantic.v1.decorator import validate_arguments
 
 from fractal_tasks_core.ngff import load_NgffImageMeta
 from fractal_tasks_core.ngff.zarr_utils import load_NgffWellMeta
diff --git a/fractal_tasks_core/tasks/calculate_registration_image_based.py b/fractal_tasks_core/tasks/calculate_registration_image_based.py
index 5829eefd1..50560c120 100644
--- a/fractal_tasks_core/tasks/calculate_registration_image_based.py
+++ b/fractal_tasks_core/tasks/calculate_registration_image_based.py
@@ -18,7 +18,7 @@
 import dask.array as da
 import numpy as np
 import zarr
-from pydantic.decorator import validate_arguments
+from pydantic.v1.decorator import validate_arguments
 from skimage.registration import phase_cross_correlation
 
 from fractal_tasks_core.channels import get_channel_from_image_zarr
diff --git a/fractal_tasks_core/tasks/cellpose_segmentation.py b/fractal_tasks_core/tasks/cellpose_segmentation.py
index f9ae41a5d..637ee32ed 100644
--- a/fractal_tasks_core/tasks/cellpose_segmentation.py
+++ b/fractal_tasks_core/tasks/cellpose_segmentation.py
@@ -26,8 +26,8 @@
 import pandas as pd
 import zarr
 from cellpose import models
-from pydantic import Field
-from pydantic.decorator import validate_arguments
+from pydantic.v1 import Field
+from pydantic.v1.decorator import validate_arguments
 
 import fractal_tasks_core
 from fractal_tasks_core.labels import prepare_label_group
diff --git a/fractal_tasks_core/tasks/cellpose_utils.py b/fractal_tasks_core/tasks/cellpose_utils.py
index b764b0882..42726496a 100644
--- a/fractal_tasks_core/tasks/cellpose_utils.py
+++ b/fractal_tasks_core/tasks/cellpose_utils.py
@@ -16,10 +16,10 @@
 from typing import Optional
 
 import numpy as np
-from pydantic import BaseModel
-from pydantic import Field
-from pydantic import root_validator
-from pydantic import validator
+from pydantic.v1 import BaseModel
+from pydantic.v1 import Field
+from pydantic.v1 import root_validator
+from pydantic.v1 import validator
 
 from fractal_tasks_core.channels import ChannelInputModel
 from fractal_tasks_core.channels import ChannelNotFoundError
diff --git a/fractal_tasks_core/tasks/cellvoyager_to_ome_zarr_compute.py b/fractal_tasks_core/tasks/cellvoyager_to_ome_zarr_compute.py
index 11a8aba90..fc1ae6380 100644
--- a/fractal_tasks_core/tasks/cellvoyager_to_ome_zarr_compute.py
+++ b/fractal_tasks_core/tasks/cellvoyager_to_ome_zarr_compute.py
@@ -18,7 +18,7 @@
 import zarr
 from anndata import read_zarr
 from dask.array.image import imread
-from pydantic.decorator import validate_arguments
+from pydantic.v1.decorator import validate_arguments
 
 from fractal_tasks_core.cellvoyager.filenames import (
     glob_with_multiple_patterns,
diff --git a/fractal_tasks_core/tasks/cellvoyager_to_ome_zarr_init.py b/fractal_tasks_core/tasks/cellvoyager_to_ome_zarr_init.py
index ec1e8a098..831dde3c2 100644
--- a/fractal_tasks_core/tasks/cellvoyager_to_ome_zarr_init.py
+++ b/fractal_tasks_core/tasks/cellvoyager_to_ome_zarr_init.py
@@ -17,7 +17,7 @@
 from typing import Optional
 
 import pandas as pd
-from pydantic.decorator import validate_arguments
+from pydantic.v1.decorator import validate_arguments
 
 import fractal_tasks_core
 from fractal_tasks_core.cellvoyager.filenames import (
diff --git a/fractal_tasks_core/tasks/cellvoyager_to_ome_zarr_init_multiplex.py b/fractal_tasks_core/tasks/cellvoyager_to_ome_zarr_init_multiplex.py
index 8e7f9b890..723e4f9c9 100644
--- a/fractal_tasks_core/tasks/cellvoyager_to_ome_zarr_init_multiplex.py
+++ b/fractal_tasks_core/tasks/cellvoyager_to_ome_zarr_init_multiplex.py
@@ -18,7 +18,7 @@
 
 import pandas as pd
 import zarr
-from pydantic.decorator import validate_arguments
+from pydantic.v1.decorator import validate_arguments
 from zarr.errors import ContainsGroupError
 
 import fractal_tasks_core
diff --git a/fractal_tasks_core/tasks/copy_ome_zarr_hcs_plate.py b/fractal_tasks_core/tasks/copy_ome_zarr_hcs_plate.py
index 6d6be9cc4..9f35db5f2 100644
--- a/fractal_tasks_core/tasks/copy_ome_zarr_hcs_plate.py
+++ b/fractal_tasks_core/tasks/copy_ome_zarr_hcs_plate.py
@@ -16,7 +16,7 @@
 from typing import Any
 
 import zarr
-from pydantic.decorator import validate_arguments
+from pydantic.v1.decorator import validate_arguments
 
 import fractal_tasks_core
 from fractal_tasks_core.ngff.specs import NgffPlateMeta
diff --git a/fractal_tasks_core/tasks/find_registration_consensus.py b/fractal_tasks_core/tasks/find_registration_consensus.py
index d93b0d6b9..b2825e7be 100644
--- a/fractal_tasks_core/tasks/find_registration_consensus.py
+++ b/fractal_tasks_core/tasks/find_registration_consensus.py
@@ -17,7 +17,7 @@
 
 import anndata as ad
 import zarr
-from pydantic.decorator import validate_arguments
+from pydantic.v1.decorator import validate_arguments
 
 from fractal_tasks_core.roi import (
     are_ROI_table_columns_valid,
diff --git a/fractal_tasks_core/tasks/illumination_correction.py b/fractal_tasks_core/tasks/illumination_correction.py
index 06e056181..646b47570 100644
--- a/fractal_tasks_core/tasks/illumination_correction.py
+++ b/fractal_tasks_core/tasks/illumination_correction.py
@@ -22,7 +22,7 @@
 import dask.array as da
 import numpy as np
 import zarr
-from pydantic.decorator import validate_arguments
+from pydantic.v1.decorator import validate_arguments
 from skimage.io import imread
 
 from fractal_tasks_core.channels import get_omero_channel_list
@@ -106,7 +106,6 @@ def illumination_correction(
     # Advanced parameters
     suffix: str = "_illum_corr",
 ) -> dict[str, Any]:
-
     """
     Applies illumination correction to the images in the OME-Zarr.
 
diff --git a/fractal_tasks_core/tasks/image_based_registration_hcs_init.py b/fractal_tasks_core/tasks/image_based_registration_hcs_init.py
index f1496e0e1..47989280d 100644
--- a/fractal_tasks_core/tasks/image_based_registration_hcs_init.py
+++ b/fractal_tasks_core/tasks/image_based_registration_hcs_init.py
@@ -15,7 +15,7 @@
 import logging
 from typing import Any
 
-from pydantic.decorator import validate_arguments
+from pydantic.v1.decorator import validate_arguments
 
 from fractal_tasks_core.tasks._registration_utils import (
     create_well_acquisition_dict,
diff --git a/fractal_tasks_core/tasks/import_ome_zarr.py b/fractal_tasks_core/tasks/import_ome_zarr.py
index 1213147e7..c720654e3 100644
--- a/fractal_tasks_core/tasks/import_ome_zarr.py
+++ b/fractal_tasks_core/tasks/import_ome_zarr.py
@@ -17,7 +17,7 @@
 
 import dask.array as da
 import zarr
-from pydantic.decorator import validate_arguments
+from pydantic.v1.decorator import validate_arguments
 
 from fractal_tasks_core.channels import update_omero_channels
 from fractal_tasks_core.ngff import detect_ome_ngff_type
diff --git a/fractal_tasks_core/tasks/init_group_by_well_for_multiplexing.py b/fractal_tasks_core/tasks/init_group_by_well_for_multiplexing.py
index 75ba7c96b..6f6bff63a 100644
--- a/fractal_tasks_core/tasks/init_group_by_well_for_multiplexing.py
+++ b/fractal_tasks_core/tasks/init_group_by_well_for_multiplexing.py
@@ -14,7 +14,7 @@
 """
 import logging
 
-from pydantic.decorator import validate_arguments
+from pydantic.v1.decorator import validate_arguments
 
 from fractal_tasks_core.tasks._registration_utils import (
     create_well_acquisition_dict,
diff --git a/fractal_tasks_core/tasks/io_models.py b/fractal_tasks_core/tasks/io_models.py
index 75c7c9cfe..93e4425c4 100644
--- a/fractal_tasks_core/tasks/io_models.py
+++ b/fractal_tasks_core/tasks/io_models.py
@@ -1,9 +1,9 @@
 from typing import Literal
 from typing import Optional
 
-from pydantic import BaseModel
-from pydantic import Field
-from pydantic import validator
+from pydantic.v1 import BaseModel
+from pydantic.v1 import Field
+from pydantic.v1 import validator
 
 from fractal_tasks_core.channels import ChannelInputModel
 from fractal_tasks_core.channels import OmeroChannel
diff --git a/fractal_tasks_core/tasks/maximum_intensity_projection.py b/fractal_tasks_core/tasks/maximum_intensity_projection.py
index 6c3adb924..bec0f72cf 100644
--- a/fractal_tasks_core/tasks/maximum_intensity_projection.py
+++ b/fractal_tasks_core/tasks/maximum_intensity_projection.py
@@ -18,7 +18,7 @@
 import anndata as ad
 import dask.array as da
 import zarr
-from pydantic.decorator import validate_arguments
+from pydantic.v1.decorator import validate_arguments
 from zarr.errors import ContainsArrayError
 
 from fractal_tasks_core.ngff import load_NgffImageMeta
diff --git a/fractal_tasks_core/tasks/napari_workflows_wrapper.py b/fractal_tasks_core/tasks/napari_workflows_wrapper.py
index d0060bcaa..c37b1a7a4 100644
--- a/fractal_tasks_core/tasks/napari_workflows_wrapper.py
+++ b/fractal_tasks_core/tasks/napari_workflows_wrapper.py
@@ -22,7 +22,7 @@
 import pandas as pd
 import zarr
 from napari_workflows._io_yaml_v1 import load_workflow
-from pydantic.decorator import validate_arguments
+from pydantic.v1.decorator import validate_arguments
 
 import fractal_tasks_core
 from fractal_tasks_core.channels import get_channel_from_image_zarr
@@ -208,7 +208,7 @@ def napari_workflows_wrapper(
     if image_inputs:
         img_array = da.from_zarr(f"{zarr_url}/{level}")
         # Loop over image inputs and assign corresponding channel of the image
-        for (name, params) in image_inputs:
+        for name, params in image_inputs:
             channel = get_channel_from_image_zarr(
                 image_zarr_path=zarr_url,
                 wavelength_id=params.channel.wavelength_id,
@@ -261,7 +261,7 @@ def napari_workflows_wrapper(
             upscale_labels = True
         # Loop over label inputs and load corresponding (upscaled) image
         input_label_arrays = {}
-        for (name, params) in label_inputs:
+        for name, params in label_inputs:
             label_name = params.label_name
             label_array_raw = da.from_zarr(
                 f"{zarr_url}/labels/{label_name}/{level}"
@@ -393,7 +393,7 @@ def napari_workflows_wrapper(
 
         # Loop over label outputs and (1) set zattrs, (2) create zarr group
         output_label_zarr_groups: dict[str, Any] = {}
-        for (name, out_params) in label_outputs:
+        for name, out_params in label_outputs:
 
             # (1a) Rescale OME-NGFF datasets (relevant for level>0)
             if not ngff_image_meta.multiscale.axes[0].name == "c":
@@ -467,7 +467,7 @@ def napari_workflows_wrapper(
         if out_params.type == "dataframe"
     ]
     output_dataframe_lists: dict[str, list] = {}
-    for (name, out_params) in dataframe_outputs:
+    for name, out_params in dataframe_outputs:
         output_dataframe_lists[name] = []
         logger.info(f"Prepared output with {name=} and {out_params=}")
         logger.info(f"{output_dataframe_lists=}")
@@ -581,7 +581,7 @@ def napari_workflows_wrapper(
 
     # Output handling: "dataframe" type (for each output, concatenate ROI
     # dataframes, clean up, and store in a AnnData table on-disk)
-    for (name, out_params) in dataframe_outputs:
+    for name, out_params in dataframe_outputs:
         table_name = out_params.table_name
         # Concatenate all FOV dataframes
         list_dfs = output_dataframe_lists[name]
@@ -617,7 +617,7 @@ def napari_workflows_wrapper(
 
     # Output handling: "label" type (for each output, build and write to disk
     # pyramid of coarser levels)
-    for (name, out_params) in label_outputs:
+    for name, out_params in label_outputs:
         label_name = out_params.label_name
         build_pyramid(
             zarrurl=f"{zarr_url}/labels/{label_name}",
diff --git a/poetry.lock b/poetry.lock
index 7ac71ec1e..4f82d58d2 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,4 +1,4 @@
-# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
+# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
 
 [[package]]
 name = "alabaster"
@@ -38,6 +38,17 @@ doc = ["awkward (>=2.0.7)", "ipython", "myst-parser", "nbsphinx", "readthedocs-s
 gpu = ["cupy"]
 test = ["awkward (>=2.3)", "boltons", "dask[array,distributed] (>=2022.09.2)", "httpx", "joblib", "loompy (>=3.0.5)", "matplotlib", "openpyxl", "pyarrow", "pytest (>=7.3)", "pytest-cov (>=2.10)", "pytest-memray", "pytest-mock", "scanpy", "scikit-learn", "zarr"]
 
+[[package]]
+name = "annotated-types"
+version = "0.7.0"
+description = "Reusable constraint types to use with typing.Annotated"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"},
+    {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
+]
+
 [[package]]
 name = "anyio"
 version = "4.4.0"
@@ -265,17 +276,17 @@ tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "p
 
 [[package]]
 name = "autopep8"
-version = "2.2.0"
+version = "2.3.0"
 description = "A tool that automatically formats Python code to conform to the PEP 8 style guide"
 optional = true
 python-versions = ">=3.8"
 files = [
-    {file = "autopep8-2.2.0-py2.py3-none-any.whl", hash = "sha256:05418a981f038969d8bdcd5636bf15948db7555ae944b9f79b5a34b35f1370d4"},
-    {file = "autopep8-2.2.0.tar.gz", hash = "sha256:d306a0581163ac29908280ad557773a95a9bede072c0fafed6f141f5311f43c1"},
+    {file = "autopep8-2.3.0-py2.py3-none-any.whl", hash = "sha256:b716efa70cbafbf4a2c9c5ec1cabfa037a68f9e30b04c74ffa5864dd49b8f7d2"},
+    {file = "autopep8-2.3.0.tar.gz", hash = "sha256:5cfe45eb3bef8662f6a3c7e28b7c0310c7310d340074b7f0f28f9810b44b7ef4"},
 ]
 
 [package.dependencies]
-pycodestyle = ">=2.11.0"
+pycodestyle = ">=2.12.0"
 tomli = {version = "*", markers = "python_version < \"3.11\""}
 
 [[package]]
@@ -632,28 +643,28 @@ files = [
 
 [[package]]
 name = "cmake"
-version = "3.29.3"
+version = "3.29.5.1"
 description = "CMake is an open-source, cross-platform family of tools designed to build, test and package software"
 optional = true
 python-versions = ">=3.7"
 files = [
-    {file = "cmake-3.29.3-py3-none-macosx_10_10_universal2.macosx_10_10_x86_64.macosx_11_0_arm64.macosx_11_0_universal2.whl", hash = "sha256:355f515826023338094514a2181724e297ed2145bc0792dacaa9ed3772b98733"},
-    {file = "cmake-3.29.3-py3-none-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ab5eb91e7f5bbfc2f0e23c964c3a3e74c6e6a26e9b59b57b87192d249b1b7162"},
-    {file = "cmake-3.29.3-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ae9e5dcd77822f89e042ad820ef25a52327bb0d15fd7a492ad4886edb31fae52"},
-    {file = "cmake-3.29.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b09d1f0f46a880fdfc50374917fd4c850d9428b244535343bb5411658a36e202"},
-    {file = "cmake-3.29.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d05cf16a6fb370cc344b3552ab321524cba1f067da240876c09cab571bf6ec0"},
-    {file = "cmake-3.29.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c0a23fbb3daeecdc42d233c1a2df233714c2db59e75ab154e2af469c1c308a5"},
-    {file = "cmake-3.29.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1037218e135302f396eca444e24ca892d8a440589f1a859313e06484f10c350f"},
-    {file = "cmake-3.29.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c84eead2ea6f596fe5ac58beedbfc9bc1f460c410c481348b3783b4794f4b1a2"},
-    {file = "cmake-3.29.3-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:e1fd53ca2f24dc0aad54934c2472cb83e273b94b4bad23fcdbd438515881f5a7"},
-    {file = "cmake-3.29.3-py3-none-musllinux_1_1_i686.whl", hash = "sha256:00225a2be8422d4b6f2ad2da10d7dfe2ad844748bd1defa94f236bfabb0d2d44"},
-    {file = "cmake-3.29.3-py3-none-musllinux_1_1_ppc64le.whl", hash = "sha256:28fe371f1865943118a0f669af87344c799751f85a5be084197c006ee6329d89"},
-    {file = "cmake-3.29.3-py3-none-musllinux_1_1_s390x.whl", hash = "sha256:ad184528fa9560bf4167279e8e4e7168a5fa1cc87a9f0b4b99ffbc79588b0cf9"},
-    {file = "cmake-3.29.3-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:40cd0ec1310e52fa29b4e2b07829d56ae95f01ea0b2479ece359259849269f86"},
-    {file = "cmake-3.29.3-py3-none-win32.whl", hash = "sha256:a2c15ab9e4922d71d98a6495a5fd661dd00b3d4ada79a3d183f996fff45db011"},
-    {file = "cmake-3.29.3-py3-none-win_amd64.whl", hash = "sha256:dd8aaffe5d8dc2dd41421dc63c39b64df30a7109392e276e2b6d021805b770e9"},
-    {file = "cmake-3.29.3-py3-none-win_arm64.whl", hash = "sha256:6672a873855e9a8f954390d0352c1d09b034a36b5f4cc5da012ae292f28623f7"},
-    {file = "cmake-3.29.3.tar.gz", hash = "sha256:d04adb1a8b878e92a734742cb0db9c59e3828abcf8ec9c930eb8a01faa00c9df"},
+    {file = "cmake-3.29.5.1-py3-none-macosx_11_0_universal2.macosx_10_10_x86_64.macosx_11_0_arm64.whl", hash = "sha256:fae935a9500e82897df18c47c9cf18c9d7dda39f85da23ab17f5e9a4224d0380"},
+    {file = "cmake-3.29.5.1-py3-none-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:aae85f3ef965baac4fee26d4264e31184e02f53cbfa13a62479efdace5553acd"},
+    {file = "cmake-3.29.5.1-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:58a4b69d2677cd2694d00ab1c24b348dd11946deb3102be102bad2b9b4fd1b86"},
+    {file = "cmake-3.29.5.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bdf8f0b629d1adb63d273c79a6b9a775a0f1e31cd2c030f8ae777958c5b70ae"},
+    {file = "cmake-3.29.5.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33ce2cd40f77b59a61ffbc53d26474a5f9e1d51dbd4246792795cbd4dad596a4"},
+    {file = "cmake-3.29.5.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ec908f111f3833891e8c6763a6532cf5d29b293c71a144ffb4bf8ca5c6fb469"},
+    {file = "cmake-3.29.5.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:11a8ebc34367a0070fc1bcf5e68cb84fbe9ac4b2a16c3991e08aaea7c502f97f"},
+    {file = "cmake-3.29.5.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:290d100c768e2e637c8ba6054693fa18d4fd087f782f14fff0bb0c4262569a8f"},
+    {file = "cmake-3.29.5.1-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:13d39eb27db7af21dfe35565f327975002e59dabcd742bc0115eff3d1d7c2364"},
+    {file = "cmake-3.29.5.1-py3-none-musllinux_1_1_i686.whl", hash = "sha256:db20b4824ddc54698d778f8e1138316b6849068f5e5e116c101ded19aeb07a1d"},
+    {file = "cmake-3.29.5.1-py3-none-musllinux_1_1_ppc64le.whl", hash = "sha256:247629be51051d2fc8ae07e00c86e7acbfb41336a2e43b1da86ed8014ec617ac"},
+    {file = "cmake-3.29.5.1-py3-none-musllinux_1_1_s390x.whl", hash = "sha256:350f2e8ae25d7cdc8ccc6d8a943c0b8df23083a32edec5b6a7d2035cfe2eb1b5"},
+    {file = "cmake-3.29.5.1-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:ad86a514b116426796e6ae48cc561a5a154787acc49894d8e5ef85e7b289191e"},
+    {file = "cmake-3.29.5.1-py3-none-win32.whl", hash = "sha256:137d7d5ed6539b1cb28bf67b39ee4b36738c8c4a5789fe1804a16e43993ab8de"},
+    {file = "cmake-3.29.5.1-py3-none-win_amd64.whl", hash = "sha256:b4154ed968a54a9f5b2e00212297f7b6b6351b23e54c1b674ae55233e8a6f0ab"},
+    {file = "cmake-3.29.5.1-py3-none-win_arm64.whl", hash = "sha256:e6236d54b9575104887a62117a32ebe08ad9ba364df4afe7c21794ecc36a1e8b"},
+    {file = "cmake-3.29.5.1.tar.gz", hash = "sha256:dce57b4a4439b3955036ef0b6050b7f796480e5d3965b4160b6eee2ac9500891"},
 ]
 
 [package.extras]
@@ -832,13 +843,13 @@ tests = ["pytest", "pytest-cov", "pytest-xdist"]
 
 [[package]]
 name = "dask"
-version = "2024.5.2"
+version = "2024.6.0"
 description = "Parallel PyData with Task Scheduling"
 optional = false
 python-versions = ">=3.9"
 files = [
-    {file = "dask-2024.5.2-py3-none-any.whl", hash = "sha256:acc2cfe41d9e0151c216ac40396dbe34df13bc3d8c51dfece190349e4f2243af"},
-    {file = "dask-2024.5.2.tar.gz", hash = "sha256:5c9722c44d0195e78b6e54197aa3302e6fcaaac2310fd3014560bcb86253dcb3"},
+    {file = "dask-2024.6.0-py3-none-any.whl", hash = "sha256:de0ced6cd46dbc6c01120c8870457af46d667940805a4be063a74dd467466804"},
+    {file = "dask-2024.6.0.tar.gz", hash = "sha256:6882ce7e485336d707e540080ed48e01f9c09485d52a2928ea05f9a9e44bb433"},
 ]
 
 [package.dependencies]
@@ -857,7 +868,7 @@ array = ["numpy (>=1.21)"]
 complete = ["dask[array,dataframe,diagnostics,distributed]", "lz4 (>=4.3.2)", "pyarrow (>=7.0)", "pyarrow-hotfix"]
 dataframe = ["dask-expr (>=1.1,<1.2)", "dask[array]", "pandas (>=1.3)"]
 diagnostics = ["bokeh (>=2.4.2)", "jinja2 (>=2.10.3)"]
-distributed = ["distributed (==2024.5.2)"]
+distributed = ["distributed (==2024.6.0)"]
 test = ["pandas[test]", "pre-commit", "pytest", "pytest-cov", "pytest-rerunfailures", "pytest-timeout", "pytest-xdist"]
 
 [[package]]
@@ -1002,13 +1013,13 @@ files = [
 
 [[package]]
 name = "fastjsonschema"
-version = "2.19.1"
+version = "2.20.0"
 description = "Fastest Python implementation of JSON schema"
 optional = true
 python-versions = "*"
 files = [
-    {file = "fastjsonschema-2.19.1-py3-none-any.whl", hash = "sha256:3672b47bc94178c9f23dbb654bf47440155d4db9df5f7bc47643315f9c405cd0"},
-    {file = "fastjsonschema-2.19.1.tar.gz", hash = "sha256:e3126a94bdc4623d3de4485f8d468a12f02a67921315ddc87836d6e456dc789d"},
+    {file = "fastjsonschema-2.20.0-py3-none-any.whl", hash = "sha256:5875f0b0fa7a0043a91e93a9b8f793bcbbba9691e7fd83dca95c28ba26d21f0a"},
+    {file = "fastjsonschema-2.20.0.tar.gz", hash = "sha256:3d48fc5300ee96f5d116f10fe6f28d938e6008f59a6a025c2649475b87f76a23"},
 ]
 
 [package.extras]
@@ -1016,46 +1027,41 @@ devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benc
 
 [[package]]
 name = "fastremap"
-version = "1.14.1"
+version = "1.14.2"
 description = "Remap, mask, renumber, unique, and in-place transposition of 3D labeled images. Point cloud too."
 optional = true
-python-versions = ">=3.7,<4.0"
-files = [
-    {file = "fastremap-1.14.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:36cf8e90dd6000c4cdefba353659f0a5bf0de23bda918edded4dca58e627994a"},
-    {file = "fastremap-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0b2723cee9ffd034479cab10662245faf256396a30a129f8a24bb7468fcdeba"},
-    {file = "fastremap-1.14.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccd70c1e2ec83ebd9ae8681f9610f44f8766238e3550563318e09e20b5420aec"},
-    {file = "fastremap-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7562dc6ad1757615fb976a45b4dae8dd60d2058d33863e4b5779c1e9afa0896b"},
-    {file = "fastremap-1.14.1-cp310-cp310-win32.whl", hash = "sha256:6f15e8387cf0278be892300b9654a4ccae88e2d086712af1c889cd689129f778"},
-    {file = "fastremap-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:4333c0ddd210fada5e5544e047d544fb53c48e8e7cc6ea7d8bea633409323d1a"},
-    {file = "fastremap-1.14.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:90525811a7f142a75e94c356dba7759e10b49fa37deece8abac49e7fae6f53d0"},
-    {file = "fastremap-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dcb8df5b4f7d5c37e436bdb55493558477a77c07d2dba46c87eeaba9667a31a"},
-    {file = "fastremap-1.14.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d83c02626b9cc5da3b34abf879dc07a33c02c4ae8234e1085ed0f2114034bda"},
-    {file = "fastremap-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ed8b0252419404a8fc08d5f4e1d6e6a9d4109fbb6cd0975cbe50abd4f6274d5"},
-    {file = "fastremap-1.14.1-cp311-cp311-win32.whl", hash = "sha256:fd1ded92afc008166483b77881bcc537ef8fe9b373c705b2d89556a4d2ced0b0"},
-    {file = "fastremap-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:e856bfe126aa0447cd22142b2f8ead38500cf43b6ffe56e4e5337c236ec73003"},
-    {file = "fastremap-1.14.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e8d4ddcf17d688df975e3076d108d02ddc9ecf17c7139b3efa55ee496a8e257"},
-    {file = "fastremap-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:907026ab7cc365fb6d947a2f4e32e64bd086e48186ed708d85d84cd64e3b2aa5"},
-    {file = "fastremap-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbbced671b0d4024810c9cef04f99601a69fd4cda757dc4eae7ed1e16227035f"},
-    {file = "fastremap-1.14.1-cp312-cp312-win32.whl", hash = "sha256:3318537edcee3b1b781fadd5106591e16b0b8e405e638216cfe29506fa2bc4b7"},
-    {file = "fastremap-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:c047f8270fa1ed9db385e88cb1576a940ecc63e44ccbc70ec1b902dde395326f"},
-    {file = "fastremap-1.14.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bedd51db0d9d93897e9e7e1deabfe9370169b5cd69e60fc99deae9e1390494d9"},
-    {file = "fastremap-1.14.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ae47cbc4f7783a505e60b4c515d9985ae2b79b88a18ee6e138fe27e9bbdd2e3"},
-    {file = "fastremap-1.14.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df4ae6e92dd67fbc77721e1db9631a4acfd27ea120cbf1ed6251bf91c4088ac1"},
-    {file = "fastremap-1.14.1-cp37-cp37m-win32.whl", hash = "sha256:c722c4c61054bae95e22cf82f85472025585ba8ace1386bdf905529e87a88351"},
-    {file = "fastremap-1.14.1-cp37-cp37m-win_amd64.whl", hash = "sha256:bfd4ed7f0c436348ef815e9aecedf314c145f55328b32feea65b60b3ca43cf46"},
-    {file = "fastremap-1.14.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:fd0605b4da0fecc5a09da9714d074948b5f729bbcfb2144b50908f0369b42fbf"},
-    {file = "fastremap-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17fcf3e4abd8577df5619726f8c8bce7e407f1e435e25acd4a4442dd6a62c553"},
-    {file = "fastremap-1.14.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:102849233601e0dcb92952f91ea6e54d90107c6e62642ccc6d563bb727fe70af"},
-    {file = "fastremap-1.14.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c87ec9442ec47ee29d4d1ba6bdc1258d9563567ec851590508133849a38be97d"},
-    {file = "fastremap-1.14.1-cp38-cp38-win32.whl", hash = "sha256:20aea5c0baac25b6532514627bdec12651062a01d903652fb6787a8551ee1f02"},
-    {file = "fastremap-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:b83202dcf6b61d42b90dfcf39253d979e6200c96cd86a2672b2add7e532b2924"},
-    {file = "fastremap-1.14.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f2cb17ebf607d0473d487422e9d8f856de94736fb77b6a4a44adfc39e8793049"},
-    {file = "fastremap-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20b53fca2dcbef0956ac9362ff1b487717532f5ad97a439876f5e2b3a1f4768b"},
-    {file = "fastremap-1.14.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fdc45f2609304ceba67ff088e925570cbcd84b54b0049813badc1fb6cfc0422"},
-    {file = "fastremap-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44ee473b948e58df8dfe15107d99d6dcfa54c3ce0eb9ce0b0cfe98446ef0b6de"},
-    {file = "fastremap-1.14.1-cp39-cp39-win32.whl", hash = "sha256:9430813cf0af429054923eedef608653a34bcdac7066568895b946eae7b69bbd"},
-    {file = "fastremap-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:964aad28d612614b84403d6da1406c2c9bb84dea043aba2ec152421a88429259"},
-    {file = "fastremap-1.14.1.tar.gz", hash = "sha256:067d42d6cb3b1b0789889efd1d7fae58006c82ada4a8446d40e9e838b358ee7c"},
+python-versions = "<4.0,>=3.8"
+files = [
+    {file = "fastremap-1.14.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fb671467d52ca054661c9c60a064626e55bbfc6e39af79ee2935ad15df5fdf1b"},
+    {file = "fastremap-1.14.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3b9df2ad1ace2de68c53a43e2912823fd5e27c581e4dd25aafb3087d5b26ecc"},
+    {file = "fastremap-1.14.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62a953e099aa68b08b58e04f02ceccca75d6fddeab09c913c6dbfe894182bfb6"},
+    {file = "fastremap-1.14.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68d406a0cbd343fccaa6bfa88b2e2fe3888b060d320df4a320a90e11475af820"},
+    {file = "fastremap-1.14.2-cp310-cp310-win32.whl", hash = "sha256:8000a91ca1159feacf99f872e2e59d993667e684463964d8572b2feb307c19dc"},
+    {file = "fastremap-1.14.2-cp310-cp310-win_amd64.whl", hash = "sha256:6df19994e6f771d25c1b74ad2ef376781d613582d75c97d07ae4b58ad2396c06"},
+    {file = "fastremap-1.14.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d24030e1a7a204df98211234588ab324cce212097df5d2a2f3ddb3e02aeb755c"},
+    {file = "fastremap-1.14.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a16074bbdeaf5c66c62d80c4a464640bd8209ee7247aa014811b5772b56a7785"},
+    {file = "fastremap-1.14.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc351a7e7d479f5e24791b78c45242615cf7bce7f0adbce54395311c746a014"},
+    {file = "fastremap-1.14.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de8f122071ac506b9ca22e1ecd0bb157e8ed4c479b90ad567056f2a2a4bc96b6"},
+    {file = "fastremap-1.14.2-cp311-cp311-win32.whl", hash = "sha256:aa76b0edfed8ed7c457bf497f133b48181d535c63ca81be4fd2e3b0cfa359b5a"},
+    {file = "fastremap-1.14.2-cp311-cp311-win_amd64.whl", hash = "sha256:a5bab4b7bb3f983fc2b01c6ff10cc56207340f4ebb495d6c556707310250ac79"},
+    {file = "fastremap-1.14.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ba4e5e6dc12aec00aeacccfc6f4f974bd2db65a6ae97eceaf8b52948b8fbbe54"},
+    {file = "fastremap-1.14.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d087bb86d48925fcd27d941702646aaec318be671f9ae7e61d85e09c154e18ec"},
+    {file = "fastremap-1.14.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35e420ea8ba4af45abbc8df228ce67e6ce662b2d1ccfc074504eff410e994f96"},
+    {file = "fastremap-1.14.2-cp312-cp312-win32.whl", hash = "sha256:a9bce3685867b809a8b6f386681b97ccf82c2ab8d5804aceee026c827e4baf37"},
+    {file = "fastremap-1.14.2-cp312-cp312-win_amd64.whl", hash = "sha256:418bcccd97393f13992f516a8a72eedeb8684e1e496d0cec21ae6ee0eabb7bbe"},
+    {file = "fastremap-1.14.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:87f764ed78b8709fe8c7271fc1a1fd012baa8eaf5a46ffae39b7ac02e8666b16"},
+    {file = "fastremap-1.14.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1984b81dc2c67c18094b9fd2e8608b3f6c6b54f5da05dfeeedd53d7c2a992dd4"},
+    {file = "fastremap-1.14.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:781c1fe055b30166f1c9da3187446b1477ab9cc59cc978dca5293d952d5e0c9a"},
+    {file = "fastremap-1.14.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8d0c31bfffef1883030bf9d59be0bceee78affd2de4b9a9447efb6a951592f0"},
+    {file = "fastremap-1.14.2-cp38-cp38-win32.whl", hash = "sha256:0978571738deaec51ecb26fea98af7d9b3e1258f91d457c5b1f59ac106007926"},
+    {file = "fastremap-1.14.2-cp38-cp38-win_amd64.whl", hash = "sha256:39840888baaaa67e2ab5b85bfddb7e5cc3c85427b21cc7d4d3923ab497653f5a"},
+    {file = "fastremap-1.14.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b959f5d61a84bfd00dad2e2d0b4d1a0985639fd66cb900a99654d05911605c01"},
+    {file = "fastremap-1.14.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44c89c6e0c610c024a956cb4dc389ad6f36596d089319f8d7786f69b581fa57d"},
+    {file = "fastremap-1.14.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:613d31c103f21169d829615cfaf99b019f2d5420988d699b00ff370244e28ac6"},
+    {file = "fastremap-1.14.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a23634d5cd3183946b126dbd556a94570d4c92f0f3518ed592aa39fbfbb7544"},
+    {file = "fastremap-1.14.2-cp39-cp39-win32.whl", hash = "sha256:4fa52fb69dbe27edf3dcd672e35d5fb0ca72f2fa7dd9db04bc410dadb41002ad"},
+    {file = "fastremap-1.14.2-cp39-cp39-win_amd64.whl", hash = "sha256:bedacfab5abf891280c4212177b4e01dcbaba40b3eb41473210adff8a983a20a"},
+    {file = "fastremap-1.14.2.tar.gz", hash = "sha256:6f6ed313a44c8214fa97576338fb4af691eb9e3f3153b121f5f345abece7b8b2"},
 ]
 
 [package.dependencies]
@@ -1227,13 +1233,13 @@ dev = ["flake8", "markdown", "twine", "wheel"]
 
 [[package]]
 name = "griffe"
-version = "0.45.2"
+version = "0.47.0"
 description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API."
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "griffe-0.45.2-py3-none-any.whl", hash = "sha256:297ec8530d0c68e5b98ff86fb588ebc3aa3559bb5dc21f3caea8d9542a350133"},
-    {file = "griffe-0.45.2.tar.gz", hash = "sha256:83ce7dcaafd8cb7f43cbf1a455155015a1eb624b1ffd93249e5e1c4a22b2fdb2"},
+    {file = "griffe-0.47.0-py3-none-any.whl", hash = "sha256:07a2fd6a8c3d21d0bbb0decf701d62042ccc8a576645c7f8799fe1f10de2b2de"},
+    {file = "griffe-0.47.0.tar.gz", hash = "sha256:95119a440a3c932b13293538bdbc405bee4c36428547553dc6b327e7e7d35e5a"},
 ]
 
 [package.dependencies]
@@ -1731,13 +1737,13 @@ files = [
 
 [[package]]
 name = "jsonpointer"
-version = "2.4"
+version = "3.0.0"
 description = "Identify specific nodes in a JSON document (RFC 6901)"
 optional = true
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*"
+python-versions = ">=3.7"
 files = [
-    {file = "jsonpointer-2.4-py2.py3-none-any.whl", hash = "sha256:15d51bba20eea3165644553647711d150376234112651b4f1811022aecad7d7a"},
-    {file = "jsonpointer-2.4.tar.gz", hash = "sha256:585cee82b70211fa9e6043b7bb89db6e1aa49524340dde8ad6b63206ea689d88"},
+    {file = "jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942"},
+    {file = "jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef"},
 ]
 
 [[package]]
@@ -1967,13 +1973,13 @@ test = ["jupyter-server (>=2.0.0)", "pytest (>=7.0)", "pytest-jupyter[server] (>
 
 [[package]]
 name = "jupyterlab"
-version = "4.2.1"
+version = "4.2.2"
 description = "JupyterLab computational environment"
 optional = true
 python-versions = ">=3.8"
 files = [
-    {file = "jupyterlab-4.2.1-py3-none-any.whl", hash = "sha256:6ac6e3827b3c890e6e549800e8a4f4aaea6a69321e2240007902aa7a0c56a8e4"},
-    {file = "jupyterlab-4.2.1.tar.gz", hash = "sha256:a10fb71085a6900820c62d43324005046402ffc8f0fde696103e37238a839507"},
+    {file = "jupyterlab-4.2.2-py3-none-any.whl", hash = "sha256:59ee9b839f43308c3dfd55d72d1f1a299ed42a7f91f2d1afe9c12a783f9e525f"},
+    {file = "jupyterlab-4.2.2.tar.gz", hash = "sha256:a534b6a25719a92a40d514fb133a9fe8f0d9981b0bbce5d8a5fcaa33344a3038"},
 ]
 
 [package.dependencies]
@@ -1988,6 +1994,7 @@ jupyter-server = ">=2.4.0,<3"
 jupyterlab-server = ">=2.27.1,<3"
 notebook-shim = ">=0.2"
 packaging = "*"
+setuptools = ">=40.1.0"
 tomli = {version = ">=1.2.2", markers = "python_version < \"3.11\""}
 tornado = ">=6.2.0"
 traitlets = "*"
@@ -2221,43 +2228,43 @@ files = [
 
 [[package]]
 name = "lit"
-version = "18.1.6"
+version = "18.1.7"
 description = "A Software Testing Tool"
 optional = true
 python-versions = "*"
 files = [
-    {file = "lit-18.1.6-py3-none-any.whl", hash = "sha256:58fc0bb1912f7e2a692d598e34c3b1675826e25bdc8078098025fb0fa28784a9"},
-    {file = "lit-18.1.6.tar.gz", hash = "sha256:70878fb0a2eee81c95898ed59605b0ee5e41565f8fd382322bca769a2bc3d4e5"},
+    {file = "lit-18.1.7-py3-none-any.whl", hash = "sha256:684629e3af788bd0a61ca253a2dbb46bda8fd40c9022e3925d8ff067b67549f7"},
+    {file = "lit-18.1.7.tar.gz", hash = "sha256:2ddd9be26bdcc6da03aea3ec456c6945eb5a09dbde548d3500bff9b8ed4763bb"},
 ]
 
 [[package]]
 name = "llvmlite"
-version = "0.42.0"
+version = "0.43.0"
 description = "lightweight wrapper around basic LLVM functionality"
 optional = true
 python-versions = ">=3.9"
 files = [
-    {file = "llvmlite-0.42.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3366938e1bf63d26c34fbfb4c8e8d2ded57d11e0567d5bb243d89aab1eb56098"},
-    {file = "llvmlite-0.42.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c35da49666a21185d21b551fc3caf46a935d54d66969d32d72af109b5e7d2b6f"},
-    {file = "llvmlite-0.42.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70f44ccc3c6220bd23e0ba698a63ec2a7d3205da0d848804807f37fc243e3f77"},
-    {file = "llvmlite-0.42.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:763f8d8717a9073b9e0246998de89929071d15b47f254c10eef2310b9aac033d"},
-    {file = "llvmlite-0.42.0-cp310-cp310-win_amd64.whl", hash = "sha256:8d90edf400b4ceb3a0e776b6c6e4656d05c7187c439587e06f86afceb66d2be5"},
-    {file = "llvmlite-0.42.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ae511caed28beaf1252dbaf5f40e663f533b79ceb408c874c01754cafabb9cbf"},
-    {file = "llvmlite-0.42.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81e674c2fe85576e6c4474e8c7e7aba7901ac0196e864fe7985492b737dbab65"},
-    {file = "llvmlite-0.42.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb3975787f13eb97629052edb5017f6c170eebc1c14a0433e8089e5db43bcce6"},
-    {file = "llvmlite-0.42.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5bece0cdf77f22379f19b1959ccd7aee518afa4afbd3656c6365865f84903f9"},
-    {file = "llvmlite-0.42.0-cp311-cp311-win_amd64.whl", hash = "sha256:7e0c4c11c8c2aa9b0701f91b799cb9134a6a6de51444eff5a9087fc7c1384275"},
-    {file = "llvmlite-0.42.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:08fa9ab02b0d0179c688a4216b8939138266519aaa0aa94f1195a8542faedb56"},
-    {file = "llvmlite-0.42.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b2fce7d355068494d1e42202c7aff25d50c462584233013eb4470c33b995e3ee"},
-    {file = "llvmlite-0.42.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebe66a86dc44634b59a3bc860c7b20d26d9aaffcd30364ebe8ba79161a9121f4"},
-    {file = "llvmlite-0.42.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d47494552559e00d81bfb836cf1c4d5a5062e54102cc5767d5aa1e77ccd2505c"},
-    {file = "llvmlite-0.42.0-cp312-cp312-win_amd64.whl", hash = "sha256:05cb7e9b6ce69165ce4d1b994fbdedca0c62492e537b0cc86141b6e2c78d5888"},
-    {file = "llvmlite-0.42.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bdd3888544538a94d7ec99e7c62a0cdd8833609c85f0c23fcb6c5c591aec60ad"},
-    {file = "llvmlite-0.42.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d0936c2067a67fb8816c908d5457d63eba3e2b17e515c5fe00e5ee2bace06040"},
-    {file = "llvmlite-0.42.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a78ab89f1924fc11482209f6799a7a3fc74ddc80425a7a3e0e8174af0e9e2301"},
-    {file = "llvmlite-0.42.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7599b65c7af7abbc978dbf345712c60fd596aa5670496561cc10e8a71cebfb2"},
-    {file = "llvmlite-0.42.0-cp39-cp39-win_amd64.whl", hash = "sha256:43d65cc4e206c2e902c1004dd5418417c4efa6c1d04df05c6c5675a27e8ca90e"},
-    {file = "llvmlite-0.42.0.tar.gz", hash = "sha256:f92b09243c0cc3f457da8b983f67bd8e1295d0f5b3746c7a1861d7a99403854a"},
+    {file = "llvmlite-0.43.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a289af9a1687c6cf463478f0fa8e8aa3b6fb813317b0d70bf1ed0759eab6f761"},
+    {file = "llvmlite-0.43.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d4fd101f571a31acb1559ae1af30f30b1dc4b3186669f92ad780e17c81e91bc"},
+    {file = "llvmlite-0.43.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d434ec7e2ce3cc8f452d1cd9a28591745de022f931d67be688a737320dfcead"},
+    {file = "llvmlite-0.43.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6912a87782acdff6eb8bf01675ed01d60ca1f2551f8176a300a886f09e836a6a"},
+    {file = "llvmlite-0.43.0-cp310-cp310-win_amd64.whl", hash = "sha256:14f0e4bf2fd2d9a75a3534111e8ebeb08eda2f33e9bdd6dfa13282afacdde0ed"},
+    {file = "llvmlite-0.43.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3e8d0618cb9bfe40ac38a9633f2493d4d4e9fcc2f438d39a4e854f39cc0f5f98"},
+    {file = "llvmlite-0.43.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0a9a1a39d4bf3517f2af9d23d479b4175ead205c592ceeb8b89af48a327ea57"},
+    {file = "llvmlite-0.43.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1da416ab53e4f7f3bc8d4eeba36d801cc1894b9fbfbf2022b29b6bad34a7df2"},
+    {file = "llvmlite-0.43.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:977525a1e5f4059316b183fb4fd34fa858c9eade31f165427a3977c95e3ee749"},
+    {file = "llvmlite-0.43.0-cp311-cp311-win_amd64.whl", hash = "sha256:d5bd550001d26450bd90777736c69d68c487d17bf371438f975229b2b8241a91"},
+    {file = "llvmlite-0.43.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f99b600aa7f65235a5a05d0b9a9f31150c390f31261f2a0ba678e26823ec38f7"},
+    {file = "llvmlite-0.43.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:35d80d61d0cda2d767f72de99450766250560399edc309da16937b93d3b676e7"},
+    {file = "llvmlite-0.43.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eccce86bba940bae0d8d48ed925f21dbb813519169246e2ab292b5092aba121f"},
+    {file = "llvmlite-0.43.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df6509e1507ca0760787a199d19439cc887bfd82226f5af746d6977bd9f66844"},
+    {file = "llvmlite-0.43.0-cp312-cp312-win_amd64.whl", hash = "sha256:7a2872ee80dcf6b5dbdc838763d26554c2a18aa833d31a2635bff16aafefb9c9"},
+    {file = "llvmlite-0.43.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9cd2a7376f7b3367019b664c21f0c61766219faa3b03731113ead75107f3b66c"},
+    {file = "llvmlite-0.43.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:18e9953c748b105668487b7c81a3e97b046d8abf95c4ddc0cd3c94f4e4651ae8"},
+    {file = "llvmlite-0.43.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74937acd22dc11b33946b67dca7680e6d103d6e90eeaaaf932603bec6fe7b03a"},
+    {file = "llvmlite-0.43.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc9efc739cc6ed760f795806f67889923f7274276f0eb45092a1473e40d9b867"},
+    {file = "llvmlite-0.43.0-cp39-cp39-win_amd64.whl", hash = "sha256:47e147cdda9037f94b399bf03bfd8a6b6b1f2f90be94a454e3386f006455a9b4"},
+    {file = "llvmlite-0.43.0.tar.gz", hash = "sha256:ae2b5b5c3ef67354824fb75517c8db5fbe93bc02cd9671f3c62271626bc041d5"},
 ]
 
 [[package]]
@@ -2399,13 +2406,13 @@ source = ["Cython (==0.29.37)"]
 
 [[package]]
 name = "magicgui"
-version = "0.8.2"
+version = "0.8.3"
 description = "build GUIs from python types"
 optional = true
 python-versions = ">=3.8"
 files = [
-    {file = "magicgui-0.8.2-py3-none-any.whl", hash = "sha256:23ad789a058e6f5b4c9f9c7b804cbb73633c2d44c5141a10b71fabcd4847ae2e"},
-    {file = "magicgui-0.8.2.tar.gz", hash = "sha256:470176d4864007f42af2f1a64a1224a665b8afec14e5c8efa1e6c644b4100096"},
+    {file = "magicgui-0.8.3-py3-none-any.whl", hash = "sha256:ee763f3908a344cc73e1ed0981885114937ce3077e60e4feb8148e64f0e762a3"},
+    {file = "magicgui-0.8.3.tar.gz", hash = "sha256:862b02e472f4cc2081ccfb9e8e1d91ab30ce91b88f9d9ff8a44e0d27f317f4ab"},
 ]
 
 [package.dependencies]
@@ -2413,7 +2420,7 @@ docstring-parser = ">=0.7"
 psygnal = ">=0.6.1"
 qtpy = ">=1.7.0"
 superqt = {version = ">=0.6.1", extras = ["iconify"]}
-typing-extensions = ">=4.1.0"
+typing-extensions = ">=4.6"
 
 [package.extras]
 dev = ["ipython", "mypy", "pdbpp", "pre-commit", "pyqt6", "rich", "ruff"]
@@ -3286,24 +3293,24 @@ test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"]
 
 [[package]]
 name = "nodeenv"
-version = "1.9.0"
+version = "1.9.1"
 description = "Node.js virtual environment builder"
 optional = false
 python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
 files = [
-    {file = "nodeenv-1.9.0-py2.py3-none-any.whl", hash = "sha256:508ecec98f9f3330b636d4448c0f1a56fc68017c68f1e7857ebc52acf0eb879a"},
-    {file = "nodeenv-1.9.0.tar.gz", hash = "sha256:07f144e90dae547bf0d4ee8da0ee42664a42a04e02ed68e06324348dafe4bdb1"},
+    {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"},
+    {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"},
 ]
 
 [[package]]
 name = "notebook"
-version = "7.2.0"
+version = "7.2.1"
 description = "Jupyter Notebook - A web-based notebook environment for interactive computing"
 optional = true
 python-versions = ">=3.8"
 files = [
-    {file = "notebook-7.2.0-py3-none-any.whl", hash = "sha256:b4752d7407d6c8872fc505df0f00d3cae46e8efb033b822adacbaa3f1f3ce8f5"},
-    {file = "notebook-7.2.0.tar.gz", hash = "sha256:34a2ba4b08ad5d19ec930db7484fb79746a1784be9e1a5f8218f9af8656a141f"},
+    {file = "notebook-7.2.1-py3-none-any.whl", hash = "sha256:f45489a3995746f2195a137e0773e2130960b51c9ac3ce257dbc2705aab3a6ca"},
+    {file = "notebook-7.2.1.tar.gz", hash = "sha256:4287b6da59740b32173d01d641f763d292f49c30e7a51b89c46ba8473126341e"},
 ]
 
 [package.dependencies]
@@ -3365,37 +3372,37 @@ testing = ["jsonschema", "magicgui", "napari-plugin-engine", "napari-svg (==0.1.
 
 [[package]]
 name = "numba"
-version = "0.59.1"
+version = "0.60.0"
 description = "compiling Python code using LLVM"
 optional = true
 python-versions = ">=3.9"
 files = [
-    {file = "numba-0.59.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:97385a7f12212c4f4bc28f648720a92514bee79d7063e40ef66c2d30600fd18e"},
-    {file = "numba-0.59.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0b77aecf52040de2a1eb1d7e314497b9e56fba17466c80b457b971a25bb1576d"},
-    {file = "numba-0.59.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3476a4f641bfd58f35ead42f4dcaf5f132569c4647c6f1360ccf18ee4cda3990"},
-    {file = "numba-0.59.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:525ef3f820931bdae95ee5379c670d5c97289c6520726bc6937a4a7d4230ba24"},
-    {file = "numba-0.59.1-cp310-cp310-win_amd64.whl", hash = "sha256:990e395e44d192a12105eca3083b61307db7da10e093972ca285c85bef0963d6"},
-    {file = "numba-0.59.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:43727e7ad20b3ec23ee4fc642f5b61845c71f75dd2825b3c234390c6d8d64051"},
-    {file = "numba-0.59.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:411df625372c77959570050e861981e9d196cc1da9aa62c3d6a836b5cc338966"},
-    {file = "numba-0.59.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2801003caa263d1e8497fb84829a7ecfb61738a95f62bc05693fcf1733e978e4"},
-    {file = "numba-0.59.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dd2842fac03be4e5324ebbbd4d2d0c8c0fc6e0df75c09477dd45b288a0777389"},
-    {file = "numba-0.59.1-cp311-cp311-win_amd64.whl", hash = "sha256:0594b3dfb369fada1f8bb2e3045cd6c61a564c62e50cf1f86b4666bc721b3450"},
-    {file = "numba-0.59.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1cce206a3b92836cdf26ef39d3a3242fec25e07f020cc4feec4c4a865e340569"},
-    {file = "numba-0.59.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8c8b4477763cb1fbd86a3be7050500229417bf60867c93e131fd2626edb02238"},
-    {file = "numba-0.59.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d80bce4ef7e65bf895c29e3889ca75a29ee01da80266a01d34815918e365835"},
-    {file = "numba-0.59.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f7ad1d217773e89a9845886401eaaab0a156a90aa2f179fdc125261fd1105096"},
-    {file = "numba-0.59.1-cp312-cp312-win_amd64.whl", hash = "sha256:5bf68f4d69dd3a9f26a9b23548fa23e3bcb9042e2935257b471d2a8d3c424b7f"},
-    {file = "numba-0.59.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4e0318ae729de6e5dbe64c75ead1a95eb01fabfe0e2ebed81ebf0344d32db0ae"},
-    {file = "numba-0.59.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0f68589740a8c38bb7dc1b938b55d1145244c8353078eea23895d4f82c8b9ec1"},
-    {file = "numba-0.59.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:649913a3758891c77c32e2d2a3bcbedf4a69f5fea276d11f9119677c45a422e8"},
-    {file = "numba-0.59.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9712808e4545270291d76b9a264839ac878c5eb7d8b6e02c970dc0ac29bc8187"},
-    {file = "numba-0.59.1-cp39-cp39-win_amd64.whl", hash = "sha256:8d51ccd7008a83105ad6a0082b6a2b70f1142dc7cfd76deb8c5a862367eb8c86"},
-    {file = "numba-0.59.1.tar.gz", hash = "sha256:76f69132b96028d2774ed20415e8c528a34e3299a40581bae178f0994a2f370b"},
-]
-
-[package.dependencies]
-llvmlite = "==0.42.*"
-numpy = ">=1.22,<1.27"
+    {file = "numba-0.60.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d761de835cd38fb400d2c26bb103a2726f548dc30368853121d66201672e651"},
+    {file = "numba-0.60.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:159e618ef213fba758837f9837fb402bbe65326e60ba0633dbe6c7f274d42c1b"},
+    {file = "numba-0.60.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1527dc578b95c7c4ff248792ec33d097ba6bef9eda466c948b68dfc995c25781"},
+    {file = "numba-0.60.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe0b28abb8d70f8160798f4de9d486143200f34458d34c4a214114e445d7124e"},
+    {file = "numba-0.60.0-cp310-cp310-win_amd64.whl", hash = "sha256:19407ced081d7e2e4b8d8c36aa57b7452e0283871c296e12d798852bc7d7f198"},
+    {file = "numba-0.60.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a17b70fc9e380ee29c42717e8cc0bfaa5556c416d94f9aa96ba13acb41bdece8"},
+    {file = "numba-0.60.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3fb02b344a2a80efa6f677aa5c40cd5dd452e1b35f8d1c2af0dfd9ada9978e4b"},
+    {file = "numba-0.60.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5f4fde652ea604ea3c86508a3fb31556a6157b2c76c8b51b1d45eb40c8598703"},
+    {file = "numba-0.60.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4142d7ac0210cc86432b818338a2bc368dc773a2f5cf1e32ff7c5b378bd63ee8"},
+    {file = "numba-0.60.0-cp311-cp311-win_amd64.whl", hash = "sha256:cac02c041e9b5bc8cf8f2034ff6f0dbafccd1ae9590dc146b3a02a45e53af4e2"},
+    {file = "numba-0.60.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d7da4098db31182fc5ffe4bc42c6f24cd7d1cb8a14b59fd755bfee32e34b8404"},
+    {file = "numba-0.60.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:38d6ea4c1f56417076ecf8fc327c831ae793282e0ff51080c5094cb726507b1c"},
+    {file = "numba-0.60.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:62908d29fb6a3229c242e981ca27e32a6e606cc253fc9e8faeb0e48760de241e"},
+    {file = "numba-0.60.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0ebaa91538e996f708f1ab30ef4d3ddc344b64b5227b67a57aa74f401bb68b9d"},
+    {file = "numba-0.60.0-cp312-cp312-win_amd64.whl", hash = "sha256:f75262e8fe7fa96db1dca93d53a194a38c46da28b112b8a4aca168f0df860347"},
+    {file = "numba-0.60.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:01ef4cd7d83abe087d644eaa3d95831b777aa21d441a23703d649e06b8e06b74"},
+    {file = "numba-0.60.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:819a3dfd4630d95fd574036f99e47212a1af41cbcb019bf8afac63ff56834449"},
+    {file = "numba-0.60.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0b983bd6ad82fe868493012487f34eae8bf7dd94654951404114f23c3466d34b"},
+    {file = "numba-0.60.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c151748cd269ddeab66334bd754817ffc0cabd9433acb0f551697e5151917d25"},
+    {file = "numba-0.60.0-cp39-cp39-win_amd64.whl", hash = "sha256:3031547a015710140e8c87226b4cfe927cac199835e5bf7d4fe5cb64e814e3ab"},
+    {file = "numba-0.60.0.tar.gz", hash = "sha256:5df6158e5584eece5fc83294b949fd30b9f1125df7708862205217e068aabf16"},
+]
+
+[package.dependencies]
+llvmlite = "==0.43.*"
+numpy = ">=1.22,<2.1"
 
 [[package]]
 name = "numcodecs"
@@ -3663,18 +3670,18 @@ wheel = "*"
 
 [[package]]
 name = "opencv-python-headless"
-version = "4.10.0.82"
+version = "4.10.0.84"
 description = "Wrapper package for OpenCV python bindings."
 optional = true
 python-versions = ">=3.6"
 files = [
-    {file = "opencv-python-headless-4.10.0.82.tar.gz", hash = "sha256:de9e742c1b9540816fbd115b0b03841d41ed0c65566b0d7a5371f98b131b7e6d"},
-    {file = "opencv_python_headless-4.10.0.82-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:a09ed50ba21cc5bf5d436cb0e784ad09c692d6b1d1454252772f6c8f2c7b4088"},
-    {file = "opencv_python_headless-4.10.0.82-cp37-abi3-macosx_12_0_x86_64.whl", hash = "sha256:977a5fd21e1fe0d3d2134887db4441f8725abeae95150126302f31fcd9f548fa"},
-    {file = "opencv_python_headless-4.10.0.82-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db4ec6755838b0be12510bfc9ffb014779c612418f11f4f7e6f505c36124a3aa"},
-    {file = "opencv_python_headless-4.10.0.82-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10a37fa5276967ecf6eb297295b16b28b7a2eb3b568ca0ee469fb1a5954de298"},
-    {file = "opencv_python_headless-4.10.0.82-cp37-abi3-win32.whl", hash = "sha256:94736e9b322d13db4768fd35588ad5e8995e78e207263076bfbee18aac835ad5"},
-    {file = "opencv_python_headless-4.10.0.82-cp37-abi3-win_amd64.whl", hash = "sha256:c1822fa23d1641c0249ed5eb906f4c385f7959ff1bd601a776d56b0c18914af4"},
+    {file = "opencv-python-headless-4.10.0.84.tar.gz", hash = "sha256:f2017c6101d7c2ef8d7bc3b414c37ff7f54d64413a1847d89970b6b7069b4e1a"},
+    {file = "opencv_python_headless-4.10.0.84-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:a4f4bcb07d8f8a7704d9c8564c224c8b064c63f430e95b61ac0bffaa374d330e"},
+    {file = "opencv_python_headless-4.10.0.84-cp37-abi3-macosx_12_0_x86_64.whl", hash = "sha256:5ae454ebac0eb0a0b932e3406370aaf4212e6a3fdb5038cc86c7aea15a6851da"},
+    {file = "opencv_python_headless-4.10.0.84-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46071015ff9ab40fccd8a163da0ee14ce9846349f06c6c8c0f2870856ffa45db"},
+    {file = "opencv_python_headless-4.10.0.84-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:377d08a7e48a1405b5e84afcbe4798464ce7ee17081c1c23619c8b398ff18295"},
+    {file = "opencv_python_headless-4.10.0.84-cp37-abi3-win32.whl", hash = "sha256:9092404b65458ed87ce932f613ffbb1106ed2c843577501e5768912360fc50ec"},
+    {file = "opencv_python_headless-4.10.0.84-cp37-abi3-win_amd64.whl", hash = "sha256:afcf28bd1209dd58810d33defb622b325d3cbe49dcd7a43a902982c33e5fad05"},
 ]
 
 [package.dependencies]
@@ -3700,13 +3707,13 @@ files = [
 
 [[package]]
 name = "packaging"
-version = "24.0"
+version = "24.1"
 description = "Core utilities for Python packages"
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
 files = [
-    {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"},
-    {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
+    {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
+    {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
 ]
 
 [[package]]
@@ -4038,13 +4045,13 @@ twisted = ["twisted"]
 
 [[package]]
 name = "prompt-toolkit"
-version = "3.0.45"
+version = "3.0.47"
 description = "Library for building powerful interactive command lines in Python"
 optional = true
 python-versions = ">=3.7.0"
 files = [
-    {file = "prompt_toolkit-3.0.45-py3-none-any.whl", hash = "sha256:a29b89160e494e3ea8622b09fa5897610b437884dcdcd054fdc1308883326c2a"},
-    {file = "prompt_toolkit-3.0.45.tar.gz", hash = "sha256:07c60ee4ab7b7e90824b61afa840c8f5aad2d46b3e2e10acc33d8ecc94a49089"},
+    {file = "prompt_toolkit-3.0.47-py3-none-any.whl", hash = "sha256:0d7bfa67001d5e39d02c224b663abc33687405033a8c422d0d675a5a13361d10"},
+    {file = "prompt_toolkit-3.0.47.tar.gz", hash = "sha256:1e1b29cb58080b1e69f207c893a1a7bf16d127a5c30c9d17a25a5d77792e5360"},
 ]
 
 [package.dependencies]
@@ -4144,13 +4151,13 @@ tests = ["pytest"]
 
 [[package]]
 name = "pycodestyle"
-version = "2.11.1"
+version = "2.12.0"
 description = "Python style guide checker"
 optional = true
 python-versions = ">=3.8"
 files = [
-    {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"},
-    {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"},
+    {file = "pycodestyle-2.12.0-py2.py3-none-any.whl", hash = "sha256:949a39f6b86c3e1515ba1787c2022131d165a8ad271b11370a8819aa070269e4"},
+    {file = "pycodestyle-2.12.0.tar.gz", hash = "sha256:442f950141b4f43df752dd303511ffded3a04c2b6fb7f65980574f0c31e6e79c"},
 ]
 
 [[package]]
@@ -4184,55 +4191,22 @@ files = [
 
 [[package]]
 name = "pydantic"
-version = "1.10.15"
-description = "Data validation and settings management using python type hints"
+version = "2.7.4"
+description = "Data validation using Python type hints"
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
 files = [
-    {file = "pydantic-1.10.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:22ed12ee588b1df028a2aa5d66f07bf8f8b4c8579c2e96d5a9c1f96b77f3bb55"},
-    {file = "pydantic-1.10.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:75279d3cac98186b6ebc2597b06bcbc7244744f6b0b44a23e4ef01e5683cc0d2"},
-    {file = "pydantic-1.10.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50f1666a9940d3d68683c9d96e39640f709d7a72ff8702987dab1761036206bb"},
-    {file = "pydantic-1.10.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82790d4753ee5d00739d6cb5cf56bceb186d9d6ce134aca3ba7befb1eedbc2c8"},
-    {file = "pydantic-1.10.15-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d207d5b87f6cbefbdb1198154292faee8017d7495a54ae58db06762004500d00"},
-    {file = "pydantic-1.10.15-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e49db944fad339b2ccb80128ffd3f8af076f9f287197a480bf1e4ca053a866f0"},
-    {file = "pydantic-1.10.15-cp310-cp310-win_amd64.whl", hash = "sha256:d3b5c4cbd0c9cb61bbbb19ce335e1f8ab87a811f6d589ed52b0254cf585d709c"},
-    {file = "pydantic-1.10.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c3d5731a120752248844676bf92f25a12f6e45425e63ce22e0849297a093b5b0"},
-    {file = "pydantic-1.10.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c365ad9c394f9eeffcb30a82f4246c0006417f03a7c0f8315d6211f25f7cb654"},
-    {file = "pydantic-1.10.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3287e1614393119c67bd4404f46e33ae3be3ed4cd10360b48d0a4459f420c6a3"},
-    {file = "pydantic-1.10.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be51dd2c8596b25fe43c0a4a59c2bee4f18d88efb8031188f9e7ddc6b469cf44"},
-    {file = "pydantic-1.10.15-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6a51a1dd4aa7b3f1317f65493a182d3cff708385327c1c82c81e4a9d6d65b2e4"},
-    {file = "pydantic-1.10.15-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4e316e54b5775d1eb59187f9290aeb38acf620e10f7fd2f776d97bb788199e53"},
-    {file = "pydantic-1.10.15-cp311-cp311-win_amd64.whl", hash = "sha256:0d142fa1b8f2f0ae11ddd5e3e317dcac060b951d605fda26ca9b234b92214986"},
-    {file = "pydantic-1.10.15-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7ea210336b891f5ea334f8fc9f8f862b87acd5d4a0cbc9e3e208e7aa1775dabf"},
-    {file = "pydantic-1.10.15-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3453685ccd7140715e05f2193d64030101eaad26076fad4e246c1cc97e1bb30d"},
-    {file = "pydantic-1.10.15-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bea1f03b8d4e8e86702c918ccfd5d947ac268f0f0cc6ed71782e4b09353b26f"},
-    {file = "pydantic-1.10.15-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:005655cabc29081de8243126e036f2065bd7ea5b9dff95fde6d2c642d39755de"},
-    {file = "pydantic-1.10.15-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:af9850d98fc21e5bc24ea9e35dd80a29faf6462c608728a110c0a30b595e58b7"},
-    {file = "pydantic-1.10.15-cp37-cp37m-win_amd64.whl", hash = "sha256:d31ee5b14a82c9afe2bd26aaa405293d4237d0591527d9129ce36e58f19f95c1"},
-    {file = "pydantic-1.10.15-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5e09c19df304b8123938dc3c53d3d3be6ec74b9d7d0d80f4f4b5432ae16c2022"},
-    {file = "pydantic-1.10.15-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7ac9237cd62947db00a0d16acf2f3e00d1ae9d3bd602b9c415f93e7a9fc10528"},
-    {file = "pydantic-1.10.15-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:584f2d4c98ffec420e02305cf675857bae03c9d617fcfdc34946b1160213a948"},
-    {file = "pydantic-1.10.15-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbc6989fad0c030bd70a0b6f626f98a862224bc2b1e36bfc531ea2facc0a340c"},
-    {file = "pydantic-1.10.15-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d573082c6ef99336f2cb5b667b781d2f776d4af311574fb53d908517ba523c22"},
-    {file = "pydantic-1.10.15-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6bd7030c9abc80134087d8b6e7aa957e43d35714daa116aced57269a445b8f7b"},
-    {file = "pydantic-1.10.15-cp38-cp38-win_amd64.whl", hash = "sha256:3350f527bb04138f8aff932dc828f154847fbdc7a1a44c240fbfff1b57f49a12"},
-    {file = "pydantic-1.10.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:51d405b42f1b86703555797270e4970a9f9bd7953f3990142e69d1037f9d9e51"},
-    {file = "pydantic-1.10.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a980a77c52723b0dc56640ced396b73a024d4b74f02bcb2d21dbbac1debbe9d0"},
-    {file = "pydantic-1.10.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67f1a1fb467d3f49e1708a3f632b11c69fccb4e748a325d5a491ddc7b5d22383"},
-    {file = "pydantic-1.10.15-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:676ed48f2c5bbad835f1a8ed8a6d44c1cd5a21121116d2ac40bd1cd3619746ed"},
-    {file = "pydantic-1.10.15-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:92229f73400b80c13afcd050687f4d7e88de9234d74b27e6728aa689abcf58cc"},
-    {file = "pydantic-1.10.15-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2746189100c646682eff0bce95efa7d2e203420d8e1c613dc0c6b4c1d9c1fde4"},
-    {file = "pydantic-1.10.15-cp39-cp39-win_amd64.whl", hash = "sha256:394f08750bd8eaad714718812e7fab615f873b3cdd0b9d84e76e51ef3b50b6b7"},
-    {file = "pydantic-1.10.15-py3-none-any.whl", hash = "sha256:28e552a060ba2740d0d2aabe35162652c1459a0b9069fe0db7f4ee0e18e74d58"},
-    {file = "pydantic-1.10.15.tar.gz", hash = "sha256:ca832e124eda231a60a041da4f013e3ff24949d94a01154b137fc2f2a43c3ffb"},
+    {file = "pydantic-2.7.4-py3-none-any.whl", hash = "sha256:ee8538d41ccb9c0a9ad3e0e5f07bf15ed8015b481ced539a1759d8cc89ae90d0"},
+    {file = "pydantic-2.7.4.tar.gz", hash = "sha256:0c84efd9548d545f63ac0060c1e4d39bb9b14db8b3c0652338aecc07b5adec52"},
 ]
 
 [package.dependencies]
-typing-extensions = ">=4.2.0"
+annotated-types = ">=0.4.0"
+pydantic-core = "2.18.4"
+typing-extensions = ">=4.6.1"
 
 [package.extras]
-dotenv = ["python-dotenv (>=0.10.4)"]
-email = ["email-validator (>=1.0.3)"]
+email = ["email-validator (>=2.0.0)"]
 
 [[package]]
 name = "pydantic-compat"
@@ -4252,6 +4226,97 @@ pydantic = "*"
 dev = ["black", "ipython", "mypy", "pdbpp", "pre-commit", "pytest", "pytest-cov", "rich", "ruff"]
 test = ["pytest (>=6.0)", "pytest-cov"]
 
+[[package]]
+name = "pydantic-core"
+version = "2.18.4"
+description = "Core functionality for Pydantic validation and serialization"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "pydantic_core-2.18.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f76d0ad001edd426b92233d45c746fd08f467d56100fd8f30e9ace4b005266e4"},
+    {file = "pydantic_core-2.18.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:59ff3e89f4eaf14050c8022011862df275b552caef8082e37b542b066ce1ff26"},
+    {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a55b5b16c839df1070bc113c1f7f94a0af4433fcfa1b41799ce7606e5c79ce0a"},
+    {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4d0dcc59664fcb8974b356fe0a18a672d6d7cf9f54746c05f43275fc48636851"},
+    {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8951eee36c57cd128f779e641e21eb40bc5073eb28b2d23f33eb0ef14ffb3f5d"},
+    {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4701b19f7e3a06ea655513f7938de6f108123bf7c86bbebb1196eb9bd35cf724"},
+    {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e00a3f196329e08e43d99b79b286d60ce46bed10f2280d25a1718399457e06be"},
+    {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:97736815b9cc893b2b7f663628e63f436018b75f44854c8027040e05230eeddb"},
+    {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6891a2ae0e8692679c07728819b6e2b822fb30ca7445f67bbf6509b25a96332c"},
+    {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bc4ff9805858bd54d1a20efff925ccd89c9d2e7cf4986144b30802bf78091c3e"},
+    {file = "pydantic_core-2.18.4-cp310-none-win32.whl", hash = "sha256:1b4de2e51bbcb61fdebd0ab86ef28062704f62c82bbf4addc4e37fa4b00b7cbc"},
+    {file = "pydantic_core-2.18.4-cp310-none-win_amd64.whl", hash = "sha256:6a750aec7bf431517a9fd78cb93c97b9b0c496090fee84a47a0d23668976b4b0"},
+    {file = "pydantic_core-2.18.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:942ba11e7dfb66dc70f9ae66b33452f51ac7bb90676da39a7345e99ffb55402d"},
+    {file = "pydantic_core-2.18.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b2ebef0e0b4454320274f5e83a41844c63438fdc874ea40a8b5b4ecb7693f1c4"},
+    {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a642295cd0c8df1b86fc3dced1d067874c353a188dc8e0f744626d49e9aa51c4"},
+    {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f09baa656c904807e832cf9cce799c6460c450c4ad80803517032da0cd062e2"},
+    {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98906207f29bc2c459ff64fa007afd10a8c8ac080f7e4d5beff4c97086a3dabd"},
+    {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19894b95aacfa98e7cb093cd7881a0c76f55731efad31073db4521e2b6ff5b7d"},
+    {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fbbdc827fe5e42e4d196c746b890b3d72876bdbf160b0eafe9f0334525119c8"},
+    {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f85d05aa0918283cf29a30b547b4df2fbb56b45b135f9e35b6807cb28bc47951"},
+    {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e85637bc8fe81ddb73fda9e56bab24560bdddfa98aa64f87aaa4e4b6730c23d2"},
+    {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2f5966897e5461f818e136b8451d0551a2e77259eb0f73a837027b47dc95dab9"},
+    {file = "pydantic_core-2.18.4-cp311-none-win32.whl", hash = "sha256:44c7486a4228413c317952e9d89598bcdfb06399735e49e0f8df643e1ccd0558"},
+    {file = "pydantic_core-2.18.4-cp311-none-win_amd64.whl", hash = "sha256:8a7164fe2005d03c64fd3b85649891cd4953a8de53107940bf272500ba8a788b"},
+    {file = "pydantic_core-2.18.4-cp311-none-win_arm64.whl", hash = "sha256:4e99bc050fe65c450344421017f98298a97cefc18c53bb2f7b3531eb39bc7805"},
+    {file = "pydantic_core-2.18.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6f5c4d41b2771c730ea1c34e458e781b18cc668d194958e0112455fff4e402b2"},
+    {file = "pydantic_core-2.18.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2fdf2156aa3d017fddf8aea5adfba9f777db1d6022d392b682d2a8329e087cef"},
+    {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4748321b5078216070b151d5271ef3e7cc905ab170bbfd27d5c83ee3ec436695"},
+    {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847a35c4d58721c5dc3dba599878ebbdfd96784f3fb8bb2c356e123bdcd73f34"},
+    {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c40d4eaad41f78e3bbda31b89edc46a3f3dc6e171bf0ecf097ff7a0ffff7cb1"},
+    {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:21a5e440dbe315ab9825fcd459b8814bb92b27c974cbc23c3e8baa2b76890077"},
+    {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01dd777215e2aa86dfd664daed5957704b769e726626393438f9c87690ce78c3"},
+    {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4b06beb3b3f1479d32befd1f3079cc47b34fa2da62457cdf6c963393340b56e9"},
+    {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:564d7922e4b13a16b98772441879fcdcbe82ff50daa622d681dd682175ea918c"},
+    {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0eb2a4f660fcd8e2b1c90ad566db2b98d7f3f4717c64fe0a83e0adb39766d5b8"},
+    {file = "pydantic_core-2.18.4-cp312-none-win32.whl", hash = "sha256:8b8bab4c97248095ae0c4455b5a1cd1cdd96e4e4769306ab19dda135ea4cdb07"},
+    {file = "pydantic_core-2.18.4-cp312-none-win_amd64.whl", hash = "sha256:14601cdb733d741b8958224030e2bfe21a4a881fb3dd6fbb21f071cabd48fa0a"},
+    {file = "pydantic_core-2.18.4-cp312-none-win_arm64.whl", hash = "sha256:c1322d7dd74713dcc157a2b7898a564ab091ca6c58302d5c7b4c07296e3fd00f"},
+    {file = "pydantic_core-2.18.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:823be1deb01793da05ecb0484d6c9e20baebb39bd42b5d72636ae9cf8350dbd2"},
+    {file = "pydantic_core-2.18.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ebef0dd9bf9b812bf75bda96743f2a6c5734a02092ae7f721c048d156d5fabae"},
+    {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae1d6df168efb88d7d522664693607b80b4080be6750c913eefb77e34c12c71a"},
+    {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f9899c94762343f2cc2fc64c13e7cae4c3cc65cdfc87dd810a31654c9b7358cc"},
+    {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99457f184ad90235cfe8461c4d70ab7dd2680e28821c29eca00252ba90308c78"},
+    {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18f469a3d2a2fdafe99296a87e8a4c37748b5080a26b806a707f25a902c040a8"},
+    {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7cdf28938ac6b8b49ae5e92f2735056a7ba99c9b110a474473fd71185c1af5d"},
+    {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:938cb21650855054dc54dfd9120a851c974f95450f00683399006aa6e8abb057"},
+    {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:44cd83ab6a51da80fb5adbd9560e26018e2ac7826f9626bc06ca3dc074cd198b"},
+    {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:972658f4a72d02b8abfa2581d92d59f59897d2e9f7e708fdabe922f9087773af"},
+    {file = "pydantic_core-2.18.4-cp38-none-win32.whl", hash = "sha256:1d886dc848e60cb7666f771e406acae54ab279b9f1e4143babc9c2258213daa2"},
+    {file = "pydantic_core-2.18.4-cp38-none-win_amd64.whl", hash = "sha256:bb4462bd43c2460774914b8525f79b00f8f407c945d50881568f294c1d9b4443"},
+    {file = "pydantic_core-2.18.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:44a688331d4a4e2129140a8118479443bd6f1905231138971372fcde37e43528"},
+    {file = "pydantic_core-2.18.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a2fdd81edd64342c85ac7cf2753ccae0b79bf2dfa063785503cb85a7d3593223"},
+    {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86110d7e1907ab36691f80b33eb2da87d780f4739ae773e5fc83fb272f88825f"},
+    {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46387e38bd641b3ee5ce247563b60c5ca098da9c56c75c157a05eaa0933ed154"},
+    {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:123c3cec203e3f5ac7b000bd82235f1a3eced8665b63d18be751f115588fea30"},
+    {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc1803ac5c32ec324c5261c7209e8f8ce88e83254c4e1aebdc8b0a39f9ddb443"},
+    {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53db086f9f6ab2b4061958d9c276d1dbe3690e8dd727d6abf2321d6cce37fa94"},
+    {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:abc267fa9837245cc28ea6929f19fa335f3dc330a35d2e45509b6566dc18be23"},
+    {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a0d829524aaefdebccb869eed855e2d04c21d2d7479b6cada7ace5448416597b"},
+    {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:509daade3b8649f80d4e5ff21aa5673e4ebe58590b25fe42fac5f0f52c6f034a"},
+    {file = "pydantic_core-2.18.4-cp39-none-win32.whl", hash = "sha256:ca26a1e73c48cfc54c4a76ff78df3727b9d9f4ccc8dbee4ae3f73306a591676d"},
+    {file = "pydantic_core-2.18.4-cp39-none-win_amd64.whl", hash = "sha256:c67598100338d5d985db1b3d21f3619ef392e185e71b8d52bceacc4a7771ea7e"},
+    {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:574d92eac874f7f4db0ca653514d823a0d22e2354359d0759e3f6a406db5d55d"},
+    {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1f4d26ceb5eb9eed4af91bebeae4b06c3fb28966ca3a8fb765208cf6b51102ab"},
+    {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77450e6d20016ec41f43ca4a6c63e9fdde03f0ae3fe90e7c27bdbeaece8b1ed4"},
+    {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d323a01da91851a4f17bf592faf46149c9169d68430b3146dcba2bb5e5719abc"},
+    {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43d447dd2ae072a0065389092a231283f62d960030ecd27565672bd40746c507"},
+    {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:578e24f761f3b425834f297b9935e1ce2e30f51400964ce4801002435a1b41ef"},
+    {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:81b5efb2f126454586d0f40c4d834010979cb80785173d1586df845a632e4e6d"},
+    {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ab86ce7c8f9bea87b9d12c7f0af71102acbf5ecbc66c17796cff45dae54ef9a5"},
+    {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:90afc12421df2b1b4dcc975f814e21bc1754640d502a2fbcc6d41e77af5ec312"},
+    {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:51991a89639a912c17bef4b45c87bd83593aee0437d8102556af4885811d59f5"},
+    {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:293afe532740370aba8c060882f7d26cfd00c94cae32fd2e212a3a6e3b7bc15e"},
+    {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b48ece5bde2e768197a2d0f6e925f9d7e3e826f0ad2271120f8144a9db18d5c8"},
+    {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eae237477a873ab46e8dd748e515c72c0c804fb380fbe6c85533c7de51f23a8f"},
+    {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:834b5230b5dfc0c1ec37b2fda433b271cbbc0e507560b5d1588e2cc1148cf1ce"},
+    {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e858ac0a25074ba4bce653f9b5d0a85b7456eaddadc0ce82d3878c22489fa4ee"},
+    {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2fd41f6eff4c20778d717af1cc50eca52f5afe7805ee530a4fbd0bae284f16e9"},
+    {file = "pydantic_core-2.18.4.tar.gz", hash = "sha256:ec3beeada09ff865c344ff3bc2f427f5e6c26401cc6113d77e372c3fdac73864"},
+]
+
+[package.dependencies]
+typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
+
 [[package]]
 name = "pygments"
 version = "2.18.0"
@@ -5299,13 +5364,13 @@ toolz = "*"
 
 [[package]]
 name = "superqt"
-version = "0.6.6"
+version = "0.6.7"
 description = "Missing widgets and components for PyQt/PySide"
 optional = true
 python-versions = ">=3.8"
 files = [
-    {file = "superqt-0.6.6-py3-none-any.whl", hash = "sha256:636feb8ef587fe82c87bb979dc896855c9ad6fbded9fcb0b94c896d163851bb8"},
-    {file = "superqt-0.6.6.tar.gz", hash = "sha256:792e09165c8a788ee245bdb784e018f9077fb309253354d86793cdf1d092f99f"},
+    {file = "superqt-0.6.7-py3-none-any.whl", hash = "sha256:4300a0e38c4dd36ae06d1e33dc75e33d238252251c43311cc45da52888476223"},
+    {file = "superqt-0.6.7.tar.gz", hash = "sha256:af80d5687ec75df6c3e54119ee895e42f79742ed326684c79425414b5c20f1e3"},
 ]
 
 [package.dependencies]
@@ -5517,7 +5582,7 @@ opt-einsum = ["opt-einsum (>=3.3)"]
 name = "tornado"
 version = "6.4.1"
 description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed."
-optional = false
+optional = true
 python-versions = ">=3.8"
 files = [
     {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:163b0aafc8e23d8cdc3c9dfb24c5368af84a81e3364745ccb4427669bf84aec8"},
@@ -5635,13 +5700,13 @@ files = [
 
 [[package]]
 name = "typing-extensions"
-version = "4.12.1"
+version = "4.12.2"
 description = "Backported and Experimental Type Hints for Python 3.8+"
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "typing_extensions-4.12.1-py3-none-any.whl", hash = "sha256:6024b58b69089e5a89c347397254e35f1bf02a907728ec7fee9bf0fe837d203a"},
-    {file = "typing_extensions-4.12.1.tar.gz", hash = "sha256:915f5e35ff76f56588223f15fdd5938f9a1cf9195c0de25130c627e4d597f6d1"},
+    {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
+    {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
 ]
 
 [[package]]
@@ -5660,13 +5725,13 @@ dev = ["flake8", "flake8-annotations", "flake8-bandit", "flake8-bugbear", "flake
 
 [[package]]
 name = "urllib3"
-version = "2.2.1"
+version = "2.2.2"
 description = "HTTP library with thread-safe connection pooling, file post, and more."
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"},
-    {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"},
+    {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"},
+    {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"},
 ]
 
 [package.extras]
@@ -5697,37 +5762,32 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess
 
 [[package]]
 name = "vispy"
-version = "0.14.2"
+version = "0.14.3"
 description = "Interactive visualization in Python"
 optional = true
-python-versions = ">=3.8"
+python-versions = ">=3.9"
 files = [
-    {file = "vispy-0.14.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:be9cafc28da544692e4377168448b0901c06eef62057deeab861e7c38fe5f042"},
-    {file = "vispy-0.14.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bed0fefc54c0b63cbc326b4fca043cbb490a329a14edb2e24f5d714afc56d8e2"},
-    {file = "vispy-0.14.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b178cd8921c02de0405a5c4af7a4d48185849810bccc30d404ac4bac0f36846"},
-    {file = "vispy-0.14.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2db2a4487ede8356eea4c29a189b345ca7f904bb507d0193356e8c3924a85ac8"},
-    {file = "vispy-0.14.2-cp310-cp310-win_amd64.whl", hash = "sha256:0738c0b606939978309280c5528dc08db368d6b3a88e8eee532e5df9050d98cb"},
-    {file = "vispy-0.14.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:15bb4e80e533651b9f9132839a62cabf053988f2a87f066d492bdba6a807a3f0"},
-    {file = "vispy-0.14.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f3230929b11e2515f59396f8972dd0864e6d4133297a29b30581173a59b8fc30"},
-    {file = "vispy-0.14.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:492eba503d1831fd55f16c8d17a543a3f811c2acee22fb212b9402622685a165"},
-    {file = "vispy-0.14.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2a0b26219e6b307b039a925da00def4926b9255adf88fd24beeb3301e120e6"},
-    {file = "vispy-0.14.2-cp311-cp311-win_amd64.whl", hash = "sha256:8bf14394d0cd612751e8df29ac530538185ae963b0969a19fc648da04413c71a"},
-    {file = "vispy-0.14.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3b93616278cbcfb5a529c06d278085cf57d93ce57dc063078998564fbcca3a57"},
-    {file = "vispy-0.14.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f6af62ff8f5c3723bf15fddd8deb0057eb750e665af5698920c46e2351997df8"},
-    {file = "vispy-0.14.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d50eeca053fdcd7f2f1f93204f327116018269453ef99a092f79308eab76ddd3"},
-    {file = "vispy-0.14.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b151f038b09829eddeffd1138e10a5cf98cdd3ef5f76427abd04718c33e0492"},
-    {file = "vispy-0.14.2-cp312-cp312-win_amd64.whl", hash = "sha256:6f493f37db6130ae2a60fb97ad714429dd4b4fa8551883a3a6aa374efab7e04f"},
-    {file = "vispy-0.14.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e389673720aaff3ef647c9bbf15ebb0d50cfb7d959b59a321056087eec8ab7de"},
-    {file = "vispy-0.14.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:721e076169075af80ae000b691a7d8b568a297deb9c3b781f6840b8e60c9514e"},
-    {file = "vispy-0.14.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95f1e6463ffc8aca6fdb4101cb65196b2725ca9f677a267acf2c675c660d12dd"},
-    {file = "vispy-0.14.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9951832b2bc1f964d9fc916c207f7771357ca34747863cfbd4a7a34cbed76550"},
-    {file = "vispy-0.14.2-cp38-cp38-win_amd64.whl", hash = "sha256:6d944ccd0d7fb1b8fa694781cb036cb1011853e6d3e1038f5b4da4d0094ed9a1"},
-    {file = "vispy-0.14.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:deb724e8af31d3d6bd135b88cf7a17fc457af02a27796fcade9a14b9747c36c0"},
-    {file = "vispy-0.14.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:98405adc58b9fb119dceb7c6606b05304cf1e21826f7877e19c43c750b03386b"},
-    {file = "vispy-0.14.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ab02e56e655a0e53c60f2b3b4fbc87361fbd6126d28fc9ad11e32313eab9a3"},
-    {file = "vispy-0.14.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:679d151bd767f9b04d5d8cb73caf46f5ffbd73437ac707e1ed703172e7496fcd"},
-    {file = "vispy-0.14.2-cp39-cp39-win_amd64.whl", hash = "sha256:cf5882d996e31c94d67a678ffa41575c14c23cba856baf2f048a4bf5c2bbaa37"},
-    {file = "vispy-0.14.2.tar.gz", hash = "sha256:eed8b44d6f5c87bd295b24aa9a2e0e4e6dc6a905ccee01b2d41c8fbf0a767b3d"},
+    {file = "vispy-0.14.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df35d09a5638392875e605008e3efaebc91238d169bda1fadd74851eb0762cbc"},
+    {file = "vispy-0.14.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:69f32f914bbb42c029e9eead272418a3a29c3d52d413a479c8ba32eab34ccab8"},
+    {file = "vispy-0.14.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b244bdfb70aebf1d8d926cd16408fd32bb204a5e1aa55813368f75f90c09389"},
+    {file = "vispy-0.14.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0bdc2bdf1b7aef27ca1a744ea7de7333e81e5ec4dc6bb532977fef8fed703cf8"},
+    {file = "vispy-0.14.3-cp310-cp310-win_amd64.whl", hash = "sha256:f211440444edea428c9d0ffb70447e945015071efb3332408c6335b07c47574e"},
+    {file = "vispy-0.14.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fe3ae49fbc6fd7f53fa34a5bbe693eb7fb6b69316fb7fe60c5e2d352afafe278"},
+    {file = "vispy-0.14.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f474f415d280e5ed71f5a513c4d42d59049710b11f144fa85c312fd639c08a9b"},
+    {file = "vispy-0.14.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af4863e7ba8ec4985ab8772d86b11dc71b3ab20f29c7e044fb35a1a009da5f98"},
+    {file = "vispy-0.14.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66bcb62a004bb97544fd14b9035c8194d8074a8dbc3eea6a6f9a3a9f5fc1ff08"},
+    {file = "vispy-0.14.3-cp311-cp311-win_amd64.whl", hash = "sha256:12d8e23ffb865e6d491d71cbf0dc54f53ca41b9167f5de99cdb08921a111f585"},
+    {file = "vispy-0.14.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f624b58c9a62e68aeb279678f9ae042cf875c24f650b042e2a7005fde9f2f3e2"},
+    {file = "vispy-0.14.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:31b1fdd1e1924ca04fce250fb958412fbcefe4f1e4e6fffa12eb4040c00b0963"},
+    {file = "vispy-0.14.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca7aebb4280e3754ae60c673dafb2f5acc26d6182761215281b07e696962e013"},
+    {file = "vispy-0.14.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a90896898b10b31760634a955031dc048fda41fd6e21ee4ff3e12ebf16970b09"},
+    {file = "vispy-0.14.3-cp312-cp312-win_amd64.whl", hash = "sha256:2b39304dae410fde21723cdcf50cae71ba611479f01cb8e30116493ce318fcab"},
+    {file = "vispy-0.14.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3a7e467e07b5e1be38233c70dab81de43bc3a9221a8fc309dec1b084d5676abf"},
+    {file = "vispy-0.14.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6fef00a20b1e040b70d869f0e4aea7e4e301a82a97bd2a5253730ef1b9664d21"},
+    {file = "vispy-0.14.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59d95977c3f6b7a42761e6e5349a27846f00e17d83891c3d8a19da12115f0e2a"},
+    {file = "vispy-0.14.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da3a3b05a72916e9cb39b013e67d7608db32e47943ede7adb85b7d5e085ee015"},
+    {file = "vispy-0.14.3-cp39-cp39-win_amd64.whl", hash = "sha256:9778390a5df31bf19e1cec15ba477f24720708d3d6febe678e4e7ca82e031f84"},
+    {file = "vispy-0.14.3.tar.gz", hash = "sha256:efbbb847a908baf7e7169ab9bf296138a39364f367e6cb0a8ec03ad71699d31d"},
 ]
 
 [package.dependencies]
@@ -5739,6 +5799,7 @@ packaging = "*"
 
 [package.extras]
 doc = ["myst-parser", "numpydoc", "pillow", "pydata-sphinx-theme", "pyopengl", "pytest", "sphinx-gallery", "sphinxcontrib-apidoc"]
+glfw = ["glfw"]
 io = ["Pillow", "meshio"]
 ipython-static = ["ipython"]
 pyglet = ["pyglet (>=1.2)"]
@@ -5808,18 +5869,18 @@ files = [
 
 [[package]]
 name = "webcolors"
-version = "1.13"
+version = "24.6.0"
 description = "A library for working with the color formats defined by HTML and CSS."
 optional = true
-python-versions = ">=3.7"
+python-versions = ">=3.8"
 files = [
-    {file = "webcolors-1.13-py3-none-any.whl", hash = "sha256:29bc7e8752c0a1bd4a1f03c14d6e6a72e93d82193738fa860cbff59d0fcc11bf"},
-    {file = "webcolors-1.13.tar.gz", hash = "sha256:c225b674c83fa923be93d235330ce0300373d02885cef23238813b0d5668304a"},
+    {file = "webcolors-24.6.0-py3-none-any.whl", hash = "sha256:8cf5bc7e28defd1d48b9e83d5fc30741328305a8195c29a8e668fa45586568a1"},
+    {file = "webcolors-24.6.0.tar.gz", hash = "sha256:1d160d1de46b3e81e58d0a280d0c78b467dc80f47294b91b1ad8029d2cedb55b"},
 ]
 
 [package.extras]
 docs = ["furo", "sphinx", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-notfound-page", "sphinxext-opengraph"]
-tests = ["pytest", "pytest-cov"]
+tests = ["coverage[toml]"]
 
 [[package]]
 name = "webencodings"
@@ -5989,18 +6050,18 @@ jupyter = ["ipytree (>=0.2.2)", "ipywidgets (>=8.0.0)", "notebook"]
 
 [[package]]
 name = "zipp"
-version = "3.19.1"
+version = "3.19.2"
 description = "Backport of pathlib-compatible object wrapper for zip files"
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "zipp-3.19.1-py3-none-any.whl", hash = "sha256:2828e64edb5386ea6a52e7ba7cdb17bb30a73a858f5eb6eb93d8d36f5ea26091"},
-    {file = "zipp-3.19.1.tar.gz", hash = "sha256:35427f6d5594f4acf82d25541438348c26736fa9b3afa2754bcd63cdb99d8e8f"},
+    {file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"},
+    {file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"},
 ]
 
 [package.extras]
 doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
-test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"]
+test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"]
 
 [extras]
 fractal-tasks = ["Pillow", "cellpose", "imageio-ffmpeg", "napari-segment-blobs-and-things-with-membranes", "napari-skimage-regionprops", "napari-tools-menu", "napari-workflows", "scikit-image", "torch"]
@@ -6008,4 +6069,4 @@ fractal-tasks = ["Pillow", "cellpose", "imageio-ffmpeg", "napari-segment-blobs-a
 [metadata]
 lock-version = "2.0"
 python-versions = "^3.9"
-content-hash = "87d9d741e2ebb500d19a7188d654d9551739fec26c2446aa2bc7b060b60e1765"
+content-hash = "223fdf04096424ba29e3f8f91564aacdbc7009fbbaf24365351532dda71f6c0d"
diff --git a/pyproject.toml b/pyproject.toml
index 42ef0e143..20b33ce42 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -31,7 +31,7 @@ numpy = "<2"
 pandas = ">=1.2.0,<2"
 defusedxml = "^0.7.1"
 lxml = "^4.9.1"
-pydantic = "<2"
+pydantic = ">=1.10.16"
 docstring-parser = "^0.15"
 anndata = ">=0.8.0,<0.11.0"
 filelock = "3.13.*"


Test patch:
diff --git a/tests/data/fake_package_for_schema_experiments/my_package/lib_custom_models.py b/tests/data/fake_package_for_schema_experiments/my_package/lib_custom_models.py
index a242589bc..0fba2d6df 100644
--- a/tests/data/fake_package_for_schema_experiments/my_package/lib_custom_models.py
+++ b/tests/data/fake_package_for_schema_experiments/my_package/lib_custom_models.py
@@ -1,4 +1,4 @@
-from pydantic import BaseModel
+from pydantic.v1 import BaseModel
 
 
 class CustomModel(BaseModel):
diff --git a/tests/dev/test_create_schema_for_single_task.py b/tests/dev/test_create_schema_for_single_task.py
index 1a7b17057..afc4c307a 100644
--- a/tests/dev/test_create_schema_for_single_task.py
+++ b/tests/dev/test_create_schema_for_single_task.py
@@ -2,7 +2,7 @@
 
 import pytest
 from devtools import debug
-from pydantic.decorator import validate_arguments
+from pydantic.v1.decorator import validate_arguments
 
 from fractal_tasks_core.dev.lib_args_schemas import (
     create_schema_for_single_task,
diff --git a/tests/dev/test_enum_arguments.py b/tests/dev/test_enum_arguments.py
index 5b1262e97..a003217b1 100644
--- a/tests/dev/test_enum_arguments.py
+++ b/tests/dev/test_enum_arguments.py
@@ -2,7 +2,7 @@
 from enum import Enum
 
 from devtools import debug
-from pydantic.decorator import validate_arguments
+from pydantic.v1.decorator import validate_arguments
 
 from fractal_tasks_core.dev.lib_args_schemas import (
     create_schema_for_single_task,
diff --git a/tests/tasks/test_unit_napari_workflows_wrapper.py b/tests/tasks/test_unit_napari_workflows_wrapper.py
index 93e2e8b46..616ba9a1d 100644
--- a/tests/tasks/test_unit_napari_workflows_wrapper.py
+++ b/tests/tasks/test_unit_napari_workflows_wrapper.py
@@ -2,7 +2,7 @@
 
 import pytest
 from devtools import debug
-from pydantic.error_wrappers import ValidationError
+from pydantic.v1.error_wrappers import ValidationError
 
 from fractal_tasks_core.tasks.napari_workflows_wrapper import (
     napari_workflows_wrapper,
diff --git a/tests/tasks/test_valid_task_interface.py b/tests/tasks/test_valid_task_interface.py
index 260b6b726..3a75f2e23 100644
--- a/tests/tasks/test_valid_task_interface.py
+++ b/tests/tasks/test_valid_task_interface.py
@@ -24,9 +24,9 @@ def validate_command(cmd: str):
     assert result.returncode == 1
     stderr = result.stderr.decode()
     debug(stderr)
-    # Valid stderr includes pydantic.error_wrappers.ValidationError (type
+    # Valid stderr includes pydantic.v1.error_wrappers.ValidationError (type
     # match between model and function, but tmp_file_args has wrong arguments)
-    assert "pydantic.error_wrappers.ValidationError" in stderr
+    assert "pydantic.v1.error_wrappers.ValidationError" in stderr
     # Valid stderr must include a mention of "unexpected keyword arguments",
     # because we are including some invalid arguments
     assert "unexpected keyword arguments" in stderr