From c7770c135deaa52da078794c67d5e3f5dbe3455d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 9 Jun 2024 16:19:47 -0500 Subject: [PATCH] fix: revert pydantic changes for HA compat (#7) --- .coveragerc | 25 +++++++++++++++++++++++++ src/uiprotect/cli/base.py | 5 +++++ src/uiprotect/data/base.py | 23 +++++++++++++++++++---- src/uiprotect/data/bootstrap.py | 6 +++++- src/uiprotect/data/devices.py | 5 ++++- src/uiprotect/data/nvr.py | 11 +++++++++-- src/uiprotect/data/types.py | 15 ++++++++++++--- src/uiprotect/data/user.py | 5 ++++- src/uiprotect/utils.py | 14 ++++++++++++-- tests/data/test_camera.py | 5 +++++ tests/data/test_chime.py | 5 +++++ tests/data/test_light.py | 5 +++++ tests/data/test_nvr.py | 5 +++++ tests/data/test_sensor.py | 5 +++++ tests/test_utils.py | 9 +++++++-- 15 files changed, 127 insertions(+), 16 deletions(-) create mode 100644 .coveragerc diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 00000000..4d657328 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,25 @@ +[run] +source = src/uiprotect + +omit = + site + +[report] +# Regexes for lines to exclude from consideration +exclude_lines = + # Have to re-enable the standard pragma + pragma: no cover + + # Don't complain about missing debug-only code: + def __repr__ + + # Don't complain if tests don't hit defensive assertion code: + raise AssertionError + raise NotImplementedError + raise exceptions.NotSupportedError + + # TYPE_CHECKING and @overload blocks are never executed during pytest run + # except ImportError: are never executed as well + if TYPE_CHECKING: + @overload + except ImportError: diff --git a/src/uiprotect/cli/base.py b/src/uiprotect/cli/base.py index 34420644..26216dec 100644 --- a/src/uiprotect/cli/base.py +++ b/src/uiprotect/cli/base.py @@ -14,6 +14,11 @@ from uiprotect.exceptions import BadRequest, NvrError, StreamError from uiprotect.utils import run_async +try: + from pydantic.v1 import ValidationError +except ImportError: + from pydantic import ValidationError # type: ignore[assignment] + T = TypeVar("T") OPTION_FORCE = typer.Option(False, "-f", "--force", help="Skip confirmation prompt") diff --git a/src/uiprotect/data/base.py b/src/uiprotect/data/base.py index 2de8cd95..607479ce 100644 --- a/src/uiprotect/data/base.py +++ b/src/uiprotect/data/base.py @@ -11,9 +11,6 @@ from typing import TYPE_CHECKING, Any, ClassVar, TypeVar from uuid import UUID -from pydantic.v1 import BaseModel -from pydantic.v1.fields import SHAPE_DICT, SHAPE_LIST, PrivateAttr - from uiprotect.data.types import ( ModelType, PercentFloat, @@ -37,10 +34,20 @@ to_snake_case, ) +try: + from pydantic.v1 import BaseModel + from pydantic.v1.fields import SHAPE_DICT, SHAPE_LIST, PrivateAttr +except ImportError: + from pydantic import BaseModel # type: ignore[assignment, no-redef] + from pydantic.fields import ( # type: ignore[attr-defined, assignment, no-redef] + SHAPE_DICT, + SHAPE_LIST, + PrivateAttr, + ) + if TYPE_CHECKING: from asyncio.events import TimerHandle - from pydantic.v1.typing import DictStrAny, SetStr from typing_extensions import Self # requires Python 3.11+ from uiprotect.api import ProtectApiClient @@ -48,6 +55,14 @@ from uiprotect.data.nvr import Event from uiprotect.data.user import User + try: + from pydantic.v1.typing import DictStrAny, SetStr + except ImportError: + from pydantic.typing import ( # type: ignore[assignment, no-redef] + DictStrAny, + SetStr, + ) + ProtectObject = TypeVar("ProtectObject", bound="ProtectBaseObject") RECENT_EVENT_MAX = timedelta(seconds=30) diff --git a/src/uiprotect/data/bootstrap.py b/src/uiprotect/data/bootstrap.py index 4db31c43..478c97ed 100644 --- a/src/uiprotect/data/bootstrap.py +++ b/src/uiprotect/data/bootstrap.py @@ -11,7 +11,11 @@ from uuid import UUID from aiohttp.client_exceptions import ServerDisconnectedError -from pydantic.v1 import PrivateAttr, ValidationError + +try: + from pydantic.v1 import PrivateAttr, ValidationError +except ImportError: + from pydantic import PrivateAttr, ValidationError # type: ignore[assignment] from uiprotect.data.base import ( RECENT_EVENT_MAX, diff --git a/src/uiprotect/data/devices.py b/src/uiprotect/data/devices.py index 5635dc56..4c677f96 100644 --- a/src/uiprotect/data/devices.py +++ b/src/uiprotect/data/devices.py @@ -12,7 +12,10 @@ from pathlib import Path from typing import TYPE_CHECKING, Any, Literal, cast -from pydantic.v1.fields import PrivateAttr +try: + from pydantic.v1.fields import PrivateAttr +except ImportError: + from pydantic.fields import PrivateAttr from uiprotect.data.base import ( EVENT_PING_INTERVAL, diff --git a/src/uiprotect/data/nvr.py b/src/uiprotect/data/nvr.py index 5dd7bdc5..8b536703 100644 --- a/src/uiprotect/data/nvr.py +++ b/src/uiprotect/data/nvr.py @@ -15,7 +15,6 @@ import aiofiles import orjson from aiofiles import os as aos -from pydantic.v1.fields import PrivateAttr from uiprotect.data.base import ( ProtectBaseObject, @@ -58,8 +57,16 @@ from uiprotect.exceptions import BadRequest, NotAuthorized from uiprotect.utils import RELEASE_CACHE, process_datetime +try: + from pydantic.v1.fields import PrivateAttr +except ImportError: + from pydantic.fields import PrivateAttr + if TYPE_CHECKING: - from pydantic.v1.typing import SetStr + try: + from pydantic.v1.typing import SetStr + except ImportError: + from pydantic.typing import SetStr # type: ignore[assignment, no-redef] _LOGGER = logging.getLogger(__name__) diff --git a/src/uiprotect/data/types.py b/src/uiprotect/data/types.py index 11b46668..c512adc3 100644 --- a/src/uiprotect/data/types.py +++ b/src/uiprotect/data/types.py @@ -5,9 +5,18 @@ from typing import Any, Literal, Optional, TypeVar, Union from packaging.version import Version as BaseVersion -from pydantic.v1 import BaseModel, ConstrainedInt -from pydantic.v1.color import Color as BaseColor -from pydantic.v1.types import ConstrainedFloat, ConstrainedStr + +try: + from pydantic.v1 import BaseModel, ConstrainedInt + from pydantic.v1.color import Color as BaseColor + from pydantic.v1.types import ConstrainedFloat, ConstrainedStr +except ImportError: + from pydantic import BaseModel, ConstrainedInt # type: ignore[assignment, no-redef] + from pydantic.color import Color as BaseColor # type: ignore[assignment, no-redef] + from pydantic.types import ( # type: ignore[assignment, no-redef] + ConstrainedFloat, + ConstrainedStr, + ) KT = TypeVar("KT") VT = TypeVar("VT") diff --git a/src/uiprotect/data/user.py b/src/uiprotect/data/user.py index b01937f8..e36721ff 100644 --- a/src/uiprotect/data/user.py +++ b/src/uiprotect/data/user.py @@ -6,7 +6,10 @@ from functools import cache from typing import Any -from pydantic.v1.fields import PrivateAttr +try: + from pydantic.v1.fields import PrivateAttr +except ImportError: + from pydantic.fields import PrivateAttr from uiprotect.data.base import ProtectBaseObject, ProtectModel, ProtectModelWithId from uiprotect.data.types import ModelType, PermissionNode diff --git a/src/uiprotect/utils.py b/src/uiprotect/utils.py index 165f26fe..53d8d0b1 100644 --- a/src/uiprotect/utils.py +++ b/src/uiprotect/utils.py @@ -28,8 +28,6 @@ import jwt from aiohttp import ClientResponse -from pydantic.v1.fields import SHAPE_DICT, SHAPE_LIST, SHAPE_SET, ModelField -from pydantic.v1.utils import to_camel from uiprotect.data.types import ( Color, @@ -40,6 +38,18 @@ ) from uiprotect.exceptions import NvrError +try: + from pydantic.v1.fields import SHAPE_DICT, SHAPE_LIST, SHAPE_SET, ModelField + from pydantic.v1.utils import to_camel +except ImportError: + from pydantic.fields import ( # type: ignore[assignment, no-redef, attr-defined] + SHAPE_DICT, + SHAPE_LIST, + SHAPE_SET, + ModelField, + ) + from pydantic.utils import to_camel # type: ignore[assignment, no-redef] + if TYPE_CHECKING: from uiprotect.api import ProtectApiClient from uiprotect.data import CoordType, Event diff --git a/tests/data/test_camera.py b/tests/data/test_camera.py index a262ab73..03614f91 100644 --- a/tests/data/test_camera.py +++ b/tests/data/test_camera.py @@ -25,6 +25,11 @@ from uiprotect.exceptions import BadRequest from uiprotect.utils import to_js_time +try: + from pydantic.v1 import ValidationError +except ImportError: + from pydantic import ValidationError + @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("status", [True, False]) diff --git a/tests/data/test_chime.py b/tests/data/test_chime.py index 1403a272..4996d9d8 100644 --- a/tests/data/test_chime.py +++ b/tests/data/test_chime.py @@ -11,6 +11,11 @@ from uiprotect.data import RingSetting from uiprotect.exceptions import BadRequest +try: + from pydantic.v1 import ValidationError +except ImportError: + from pydantic import ValidationError + if TYPE_CHECKING: from uiprotect.data import Camera, Chime diff --git a/tests/data/test_light.py b/tests/data/test_light.py index fd4ea059..e1016c90 100644 --- a/tests/data/test_light.py +++ b/tests/data/test_light.py @@ -13,6 +13,11 @@ from uiprotect.exceptions import BadRequest from uiprotect.utils import to_ms +try: + from pydantic.v1 import ValidationError +except ImportError: + from pydantic import ValidationError + if TYPE_CHECKING: from uiprotect.data import Camera, Light diff --git a/tests/data/test_nvr.py b/tests/data/test_nvr.py index 505f6b9c..fc68c587 100644 --- a/tests/data/test_nvr.py +++ b/tests/data/test_nvr.py @@ -18,6 +18,11 @@ from uiprotect.exceptions import BadRequest from uiprotect.utils import to_ms +try: + from pydantic.v1 import ValidationError +except ImportError: + from pydantic import ValidationError + @pytest.mark.parametrize("status", [True, False]) @pytest.mark.asyncio() diff --git a/tests/data/test_sensor.py b/tests/data/test_sensor.py index a848cd67..e708ad0b 100644 --- a/tests/data/test_sensor.py +++ b/tests/data/test_sensor.py @@ -11,6 +11,11 @@ from uiprotect.data.types import MountType from uiprotect.exceptions import BadRequest +try: + from pydantic.v1 import ValidationError +except ImportError: + from pydantic import ValidationError + if TYPE_CHECKING: from uiprotect.data import Camera, Light, Sensor diff --git a/tests/test_utils.py b/tests/test_utils.py index 18aefeda..de0ba347 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -4,11 +4,16 @@ from uuid import UUID import pytest -from pydantic.v1.config import BaseConfig -from pydantic.v1.fields import ModelField from uiprotect.utils import convert_unifi_data, dict_diff, to_snake_case +try: + from pydantic.v1.config import BaseConfig + from pydantic.v1.fields import ModelField +except ImportError: + from pydantic.config import BaseConfig + from pydantic.fields import ModelField # type: ignore[attr-defined] + def test_dict_diff_equal(): assert dict_diff({}, {}) == {}