diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d3c1ee78..b2c112ff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,13 +23,14 @@ jobs: run: pipx run check-manifest test: - name: ${{ matrix.platform }} (${{ matrix.python-version }}) + name: ${{ matrix.platform }} (${{ matrix.python-version }}) ${{ matrix.pydantic }} runs-on: ${{ matrix.platform }} strategy: fail-fast: false matrix: python-version: [3.8, 3.9, "3.10", "3.11"] platform: [ubuntu-latest, macos-latest, windows-latest] + pydantic: ["pydantic<2", "pydantic>2"] steps: - uses: actions/checkout@v4 @@ -42,6 +43,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip + pip install "${{ matrix.pydantic }}" pip install -e .[json,docs,testing] - name: Test Main docs build diff --git a/pyproject.toml b/pyproject.toml index faf99a7e..7b7f65e4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,7 +35,7 @@ dependencies = [ "appdirs", "build>=1", "psygnal>=0.3.0", - "pydantic<2", + "pydantic", "tomli-w", "tomli; python_version < '3.11'", "rich", diff --git a/src/npe2/_dynamic_plugin.py b/src/npe2/_dynamic_plugin.py index 6ff8910d..7b98f4fc 100644 --- a/src/npe2/_dynamic_plugin.py +++ b/src/npe2/_dynamic_plugin.py @@ -14,7 +14,7 @@ overload, ) -from pydantic import BaseModel, ValidationError +from npe2._pydantic_compat import BaseModel, ValidationError from ._plugin_manager import PluginManager from .manifest.contributions import ( diff --git a/src/npe2/_pydantic_compat.py b/src/npe2/_pydantic_compat.py new file mode 100644 index 00000000..1e093cf1 --- /dev/null +++ b/src/npe2/_pydantic_compat.py @@ -0,0 +1,54 @@ +try: + # pydantic v2 + from pydantic.v1 import ( + BaseModel, + Extra, + Field, + PrivateAttr, + ValidationError, + color, + conlist, + constr, + root_validator, + validator, + ) + from pydantic.v1.error_wrappers import ErrorWrapper + from pydantic.v1.fields import SHAPE_LIST + from pydantic.v1.generics import GenericModel + from pydantic.v1.main import ModelMetaclass +except ImportError: + # pydantic v2 + from pydantic import ( + BaseModel, + Extra, + Field, + PrivateAttr, + ValidationError, + color, + conlist, + constr, + root_validator, + validator, + ) + from pydantic.error_wrappers import ErrorWrapper + from pydantic.fields import SHAPE_LIST + from pydantic.generics import GenericModel + from pydantic.main import ModelMetaclass + + +__all__ = ( + "BaseModel", + "Extra", + "Field", + "ValidationError", + "root_validator", + "validator", + "PrivateAttr", + "color", + "conlist", + "constr", + "ModelMetaclass", + "ErrorWrapper", + "GenericModel", + "SHAPE_LIST", +) diff --git a/src/npe2/implements.py b/src/npe2/implements.py index 6759e80c..c5260dac 100644 --- a/src/npe2/implements.py +++ b/src/npe2/implements.py @@ -2,7 +2,7 @@ from inspect import Parameter, Signature from typing import Any, Callable, List, Sequence, Type, TypeVar -from pydantic import BaseModel +from npe2._pydantic_compat import BaseModel from .manifest import contributions diff --git a/src/npe2/implements.pyi b/src/npe2/implements.pyi index 61f83fde..54d04a3f 100644 --- a/src/npe2/implements.pyi +++ b/src/npe2/implements.pyi @@ -1,6 +1,6 @@ from typing import Any, Callable, TypeVar -from pydantic import BaseModel as BaseModel +from npe2._pydantic_compat import BaseModel as BaseModel from .manifest import PluginManifest as PluginManifest from .manifest import contributions as contributions diff --git a/src/npe2/manifest/_bases.py b/src/npe2/manifest/_bases.py index 630b75bf..ec4d4368 100644 --- a/src/npe2/manifest/_bases.py +++ b/src/npe2/manifest/_bases.py @@ -4,7 +4,8 @@ from typing import Callable, Dict, Optional, Union import yaml -from pydantic import BaseModel, PrivateAttr + +from npe2._pydantic_compat import BaseModel, PrivateAttr class ImportExportModel(BaseModel): diff --git a/src/npe2/manifest/_package_metadata.py b/src/npe2/manifest/_package_metadata.py index d2af3c13..ffe24f8b 100644 --- a/src/npe2/manifest/_package_metadata.py +++ b/src/npe2/manifest/_package_metadata.py @@ -1,8 +1,14 @@ from importlib import metadata from typing import Dict, List, Literal, Optional, Union -from pydantic import BaseModel, Extra, Field, constr, root_validator -from pydantic.fields import SHAPE_LIST +from npe2._pydantic_compat import ( + SHAPE_LIST, + BaseModel, + Extra, + Field, + constr, + root_validator, +) # https://packaging.python.org/specifications/core-metadata/ diff --git a/src/npe2/manifest/contributions/_commands.py b/src/npe2/manifest/contributions/_commands.py index 07a34fce..5bcc1d6c 100644 --- a/src/npe2/manifest/contributions/_commands.py +++ b/src/npe2/manifest/contributions/_commands.py @@ -1,7 +1,6 @@ from typing import TYPE_CHECKING, Any, Optional, Union -from pydantic import BaseModel, Extra, Field, validator - +from npe2._pydantic_compat import BaseModel, Extra, Field, validator from npe2.manifest import _validators from npe2.types import PythonName diff --git a/src/npe2/manifest/contributions/_configuration.py b/src/npe2/manifest/contributions/_configuration.py index d5ec93ea..0a9f03c3 100644 --- a/src/npe2/manifest/contributions/_configuration.py +++ b/src/npe2/manifest/contributions/_configuration.py @@ -1,6 +1,6 @@ from typing import Any, Dict, List, Literal, Optional, Union -from pydantic import BaseModel, Field, conlist, root_validator, validator +from npe2._pydantic_compat import BaseModel, Field, conlist, root_validator, validator from ._json_schema import ( Draft07JsonSchema, diff --git a/src/npe2/manifest/contributions/_contributions.py b/src/npe2/manifest/contributions/_contributions.py index 05f5a3c8..fc956925 100644 --- a/src/npe2/manifest/contributions/_contributions.py +++ b/src/npe2/manifest/contributions/_contributions.py @@ -1,6 +1,6 @@ from typing import Dict, List, Optional -from pydantic import BaseModel, Field, validator +from npe2._pydantic_compat import BaseModel, Field, validator from ._commands import CommandContribution from ._configuration import ConfigurationContribution diff --git a/src/npe2/manifest/contributions/_icon.py b/src/npe2/manifest/contributions/_icon.py index 8043695c..ff13a32a 100644 --- a/src/npe2/manifest/contributions/_icon.py +++ b/src/npe2/manifest/contributions/_icon.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from npe2._pydantic_compat import BaseModel class Icon(BaseModel): diff --git a/src/npe2/manifest/contributions/_json_schema.py b/src/npe2/manifest/contributions/_json_schema.py index 47a9f0fd..0bb44831 100644 --- a/src/npe2/manifest/contributions/_json_schema.py +++ b/src/npe2/manifest/contributions/_json_schema.py @@ -2,7 +2,14 @@ from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Type, Union -from pydantic import BaseModel, Field, PrivateAttr, conlist, root_validator, validator +from npe2._pydantic_compat import ( + BaseModel, + Field, + PrivateAttr, + conlist, + root_validator, + validator, +) if TYPE_CHECKING: from jsonschema.exceptions import ValidationError diff --git a/src/npe2/manifest/contributions/_keybindings.py b/src/npe2/manifest/contributions/_keybindings.py index b8d1986e..9d696c89 100644 --- a/src/npe2/manifest/contributions/_keybindings.py +++ b/src/npe2/manifest/contributions/_keybindings.py @@ -1,7 +1,6 @@ from typing import Optional -from pydantic.fields import Field - +from npe2._pydantic_compat import Field from npe2.manifest.utils import Executable diff --git a/src/npe2/manifest/contributions/_menus.py b/src/npe2/manifest/contributions/_menus.py index 59d19a66..99f592b8 100644 --- a/src/npe2/manifest/contributions/_menus.py +++ b/src/npe2/manifest/contributions/_menus.py @@ -1,7 +1,6 @@ from typing import Optional, Union -from pydantic import BaseModel, Field - +from npe2._pydantic_compat import BaseModel, Field from npe2.manifest.utils import Executable diff --git a/src/npe2/manifest/contributions/_readers.py b/src/npe2/manifest/contributions/_readers.py index 93c7bb2d..0135edd1 100644 --- a/src/npe2/manifest/contributions/_readers.py +++ b/src/npe2/manifest/contributions/_readers.py @@ -1,8 +1,7 @@ from functools import wraps from typing import List, Optional -from pydantic import Extra, Field - +from npe2._pydantic_compat import Extra, Field from npe2.manifest.utils import Executable, v2_to_v1 from npe2.types import ReaderFunction diff --git a/src/npe2/manifest/contributions/_sample_data.py b/src/npe2/manifest/contributions/_sample_data.py index af162a11..05917a24 100644 --- a/src/npe2/manifest/contributions/_sample_data.py +++ b/src/npe2/manifest/contributions/_sample_data.py @@ -1,9 +1,7 @@ from abc import ABC, abstractmethod from typing import TYPE_CHECKING, List, Optional, Union -from pydantic.fields import Field -from pydantic.generics import GenericModel - +from npe2._pydantic_compat import Field, GenericModel from npe2.manifest.utils import Executable from npe2.types import LayerData diff --git a/src/npe2/manifest/contributions/_submenu.py b/src/npe2/manifest/contributions/_submenu.py index 2ba486f3..0eea3180 100644 --- a/src/npe2/manifest/contributions/_submenu.py +++ b/src/npe2/manifest/contributions/_submenu.py @@ -1,7 +1,6 @@ from typing import Optional, Union -from pydantic import BaseModel -from pydantic.fields import Field +from npe2._pydantic_compat import BaseModel, Field from ._icon import Icon diff --git a/src/npe2/manifest/contributions/_themes.py b/src/npe2/manifest/contributions/_themes.py index 8085c2b3..2bf34241 100644 --- a/src/npe2/manifest/contributions/_themes.py +++ b/src/npe2/manifest/contributions/_themes.py @@ -1,8 +1,7 @@ import sys from typing import Literal, Optional, Union -from pydantic import BaseModel, color -from pydantic.fields import Field +from npe2._pydantic_compat import BaseModel, Field, color # pydantic doesn't implement color equality? diff --git a/src/npe2/manifest/contributions/_widgets.py b/src/npe2/manifest/contributions/_widgets.py index 4929f6b4..cdd7edcf 100644 --- a/src/npe2/manifest/contributions/_widgets.py +++ b/src/npe2/manifest/contributions/_widgets.py @@ -2,8 +2,7 @@ from typing import TYPE_CHECKING, Callable, Optional -from pydantic import Extra, Field - +from npe2._pydantic_compat import Extra, Field from npe2.manifest.utils import Executable from npe2.types import Widget diff --git a/src/npe2/manifest/contributions/_writers.py b/src/npe2/manifest/contributions/_writers.py index 395e6fc7..00aea179 100644 --- a/src/npe2/manifest/contributions/_writers.py +++ b/src/npe2/manifest/contributions/_writers.py @@ -1,8 +1,7 @@ from enum import Enum from typing import List, Tuple -from pydantic import BaseModel, Extra, Field, validator - +from npe2._pydantic_compat import BaseModel, Extra, Field, validator from npe2.manifest.utils import Executable diff --git a/src/npe2/manifest/schema.py b/src/npe2/manifest/schema.py index b8f6115b..a4f9aede 100644 --- a/src/npe2/manifest/schema.py +++ b/src/npe2/manifest/schema.py @@ -8,10 +8,16 @@ from pathlib import Path from typing import Iterator, List, Literal, NamedTuple, Optional, Sequence, Union -from pydantic import Extra, Field, ValidationError, root_validator, validator -from pydantic.error_wrappers import ErrorWrapper -from pydantic.main import BaseModel, ModelMetaclass - +from npe2._pydantic_compat import ( + BaseModel, + ErrorWrapper, + Extra, + Field, + ModelMetaclass, + ValidationError, + root_validator, + validator, +) from npe2.types import PythonName from . import _validators @@ -438,9 +444,8 @@ def _from_package_or_name( If the name does not resolve to either a distribution name or a filename. """ - from pydantic import ValidationError - from npe2 import PluginManifest + from npe2._pydantic_compat import ValidationError try: return PluginManifest.from_file(package_or_filename) diff --git a/src/npe2/manifest/utils.py b/src/npe2/manifest/utils.py index bdbabf7b..b2c9830a 100644 --- a/src/npe2/manifest/utils.py +++ b/src/npe2/manifest/utils.py @@ -18,9 +18,7 @@ Union, ) -from pydantic import PrivateAttr -from pydantic.generics import GenericModel - +from npe2._pydantic_compat import GenericModel, PrivateAttr from npe2.types import PythonName if TYPE_CHECKING: diff --git a/tests/test_manifest.py b/tests/test_manifest.py index 70addc04..eb6576c6 100644 --- a/tests/test_manifest.py +++ b/tests/test_manifest.py @@ -3,9 +3,9 @@ from unittest.mock import patch import pytest -from pydantic import ValidationError from npe2 import PluginManifest +from npe2._pydantic_compat import ValidationError from npe2.manifest import PackageMetadata from npe2.manifest.schema import ENTRY_POINT diff --git a/tests/test_validations.py b/tests/test_validations.py index d5a8e0a3..0fa2a23c 100644 --- a/tests/test_validations.py +++ b/tests/test_validations.py @@ -1,9 +1,9 @@ import json import pytest -from pydantic import ValidationError from npe2 import PluginManifest +from npe2._pydantic_compat import ValidationError from npe2.manifest import _validators # the docstrings here are used to assert the validation error that is printed.