From 6fc0eb4ed77a3b4627cca7031a544ce71c1f7434 Mon Sep 17 00:00:00 2001 From: Varun Chawla Date: Sat, 7 Feb 2026 21:51:25 -0800 Subject: [PATCH 1/2] fix: Python 3.14 compatibility for datetime parsing Fixes #706 - Replace deprecated pydantic.v1.datetime_parse imports with TypeAdapter - Use pydantic.TypeAdapter for datetime/date parsing in Pydantic v2 - Suppress Pydantic v1 compatibility warnings on Python 3.14+ - Add Python 3.13 and 3.14 to supported versions in pyproject.toml This resolves the UserWarning about Pydantic V1 incompatibility with Python 3.14 while maintaining backward compatibility with older Python versions. Signed-off-by: Varun Chawla --- pyproject.toml | 2 ++ src/cohere/core/pydantic_utilities.py | 44 ++++++++++++++++++++++----- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 04a187b5c..b0df181f3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,8 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Operating System :: OS Independent", "Operating System :: POSIX", "Operating System :: MacOS", diff --git a/src/cohere/core/pydantic_utilities.py b/src/cohere/core/pydantic_utilities.py index 185e5c4f6..ea6a41af7 100644 --- a/src/cohere/core/pydantic_utilities.py +++ b/src/cohere/core/pydantic_utilities.py @@ -2,6 +2,8 @@ # nopycln: file import datetime as dt +import sys +import warnings from collections import defaultdict from typing import Any, Callable, ClassVar, Dict, List, Mapping, Optional, Set, Tuple, Type, TypeVar, Union, cast @@ -10,14 +12,40 @@ IS_PYDANTIC_V2 = pydantic.VERSION.startswith("2.") if IS_PYDANTIC_V2: - from pydantic.v1.datetime_parse import parse_date as parse_date - from pydantic.v1.datetime_parse import parse_datetime as parse_datetime - from pydantic.v1.fields import ModelField as ModelField - from pydantic.v1.json import ENCODERS_BY_TYPE as encoders_by_type # type: ignore[attr-defined] - from pydantic.v1.typing import get_args as get_args - from pydantic.v1.typing import get_origin as get_origin - from pydantic.v1.typing import is_literal_type as is_literal_type - from pydantic.v1.typing import is_union as is_union + # Suppress pydantic.v1 compatibility warnings on Python 3.14+ + if sys.version_info >= (3, 14): + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", message="Core Pydantic V1 functionality") + from pydantic.v1.fields import ModelField as ModelField + from pydantic.v1.json import ENCODERS_BY_TYPE as encoders_by_type # type: ignore[attr-defined] + from pydantic.v1.typing import get_args as get_args + from pydantic.v1.typing import get_origin as get_origin + from pydantic.v1.typing import is_literal_type as is_literal_type + from pydantic.v1.typing import is_union as is_union + else: + from pydantic.v1.fields import ModelField as ModelField + from pydantic.v1.json import ENCODERS_BY_TYPE as encoders_by_type # type: ignore[attr-defined] + from pydantic.v1.typing import get_args as get_args + from pydantic.v1.typing import get_origin as get_origin + from pydantic.v1.typing import is_literal_type as is_literal_type + from pydantic.v1.typing import is_union as is_union + + # Use TypeAdapter for datetime parsing (Python 3.14 compatible) + _datetime_adapter = pydantic.TypeAdapter(dt.datetime) # type: ignore[attr-defined] + _date_adapter = pydantic.TypeAdapter(dt.date) # type: ignore[attr-defined] + + def parse_datetime(v: Any) -> dt.datetime: + """Parse datetime using Pydantic v2 TypeAdapter (Python 3.14 compatible).""" + if isinstance(v, dt.datetime): + return v + return _datetime_adapter.validate_python(v) + + def parse_date(v: Any) -> dt.date: + """Parse date using Pydantic v2 TypeAdapter (Python 3.14 compatible).""" + if isinstance(v, dt.date): + return v + return _date_adapter.validate_python(v) + else: from pydantic.datetime_parse import parse_date as parse_date # type: ignore[no-redef] from pydantic.datetime_parse import parse_datetime as parse_datetime # type: ignore[no-redef] From 5557402a1dab7f878ba5ceace2574d2b2d69822d Mon Sep 17 00:00:00 2001 From: Varun Chawla Date: Sun, 8 Feb 2026 10:09:27 -0800 Subject: [PATCH 2/2] fix: prevent parse_date from returning datetime objects datetime is a subclass of date, so isinstance(v, dt.date) matches datetime objects too. Add explicit check to exclude datetime instances from the short-circuit path, ensuring they get properly converted to date by the TypeAdapter. --- src/cohere/core/pydantic_utilities.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cohere/core/pydantic_utilities.py b/src/cohere/core/pydantic_utilities.py index ea6a41af7..06af8c8ff 100644 --- a/src/cohere/core/pydantic_utilities.py +++ b/src/cohere/core/pydantic_utilities.py @@ -42,7 +42,7 @@ def parse_datetime(v: Any) -> dt.datetime: def parse_date(v: Any) -> dt.date: """Parse date using Pydantic v2 TypeAdapter (Python 3.14 compatible).""" - if isinstance(v, dt.date): + if isinstance(v, dt.date) and not isinstance(v, dt.datetime): return v return _date_adapter.validate_python(v)