Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(internal): bump pydantic dependency #1413

Merged
merged 1 commit into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions requirements-dev.lock
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ attrs==23.1.0
azure-core==1.30.1
# via azure-identity
azure-identity==1.15.0
black==24.4.0
black==24.4.2
# via inline-snapshot
certifi==2023.7.22
# via httpcore
Expand All @@ -39,7 +39,7 @@ click==8.1.7
# via inline-snapshot
colorlog==6.7.0
# via nox
cryptography==42.0.5
cryptography==42.0.7
# via azure-identity
# via msal
# via pyjwt
Expand Down Expand Up @@ -111,9 +111,9 @@ py==1.11.0
# via pytest
pycparser==2.22
# via cffi
pydantic==2.4.2
pydantic==2.7.1
# via openai
pydantic-core==2.10.1
pydantic-core==2.18.2
# via pydantic
pyjwt==2.8.0
# via msal
Expand Down
8 changes: 4 additions & 4 deletions requirements.lock
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ numpy==1.26.4
# via openai
# via pandas
# via pandas-stubs
pandas==2.2.1
pandas==2.2.2
# via openai
pandas-stubs==2.2.1.240316
# via openai
pydantic==2.4.2
pydantic==2.7.1
# via openai
pydantic-core==2.10.1
pydantic-core==2.18.2
# via pydantic
python-dateutil==2.9.0.post0
# via pandas
Expand All @@ -53,7 +53,7 @@ sniffio==1.3.0
# via openai
tqdm==4.66.1
# via openai
types-pytz==2024.1.0.20240203
types-pytz==2024.1.0.20240417
# via pandas-stubs
typing-extensions==4.8.0
# via openai
Expand Down
20 changes: 16 additions & 4 deletions src/openai/_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
from ._constants import RAW_RESPONSE_HEADER

if TYPE_CHECKING:
from pydantic_core.core_schema import ModelField, ModelFieldsSchema
from pydantic_core.core_schema import ModelField, LiteralSchema, ModelFieldsSchema

__all__ = ["BaseModel", "GenericModel"]

Expand Down Expand Up @@ -251,7 +251,9 @@ def model_dump(
exclude_defaults: bool = False,
exclude_none: bool = False,
round_trip: bool = False,
warnings: bool = True,
warnings: bool | Literal["none", "warn", "error"] = True,
context: dict[str, Any] | None = None,
serialize_as_any: bool = False,
) -> dict[str, Any]:
"""Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump

Expand Down Expand Up @@ -279,6 +281,10 @@ def model_dump(
raise ValueError("round_trip is only supported in Pydantic v2")
if warnings != True:
raise ValueError("warnings is only supported in Pydantic v2")
if context is not None:
raise ValueError("context is only supported in Pydantic v2")
if serialize_as_any != False:
raise ValueError("serialize_as_any is only supported in Pydantic v2")
return super().dict( # pyright: ignore[reportDeprecated]
include=include,
exclude=exclude,
Expand All @@ -300,7 +306,9 @@ def model_dump_json(
exclude_defaults: bool = False,
exclude_none: bool = False,
round_trip: bool = False,
warnings: bool = True,
warnings: bool | Literal["none", "warn", "error"] = True,
context: dict[str, Any] | None = None,
serialize_as_any: bool = False,
) -> str:
"""Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump_json

Expand All @@ -324,6 +332,10 @@ def model_dump_json(
raise ValueError("round_trip is only supported in Pydantic v2")
if warnings != True:
raise ValueError("warnings is only supported in Pydantic v2")
if context is not None:
raise ValueError("context is only supported in Pydantic v2")
if serialize_as_any != False:
raise ValueError("serialize_as_any is only supported in Pydantic v2")
return super().json( # type: ignore[reportDeprecated]
indent=indent,
include=include,
Expand Down Expand Up @@ -550,7 +562,7 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any,
field_schema = field["schema"]

if field_schema["type"] == "literal":
for entry in field_schema["expected"]:
for entry in cast("LiteralSchema", field_schema)["expected"]:
if isinstance(entry, str):
mapping[entry] = variant
else:
Expand Down
8 changes: 4 additions & 4 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class NestedModel(BaseModel):

# mismatched types
m = NestedModel.construct(nested="hello!")
assert m.nested == "hello!"
assert cast(Any, m.nested) == "hello!"


def test_optional_nested_model() -> None:
Expand All @@ -48,7 +48,7 @@ class NestedModel(BaseModel):
# mismatched types
m3 = NestedModel.construct(nested={"foo"})
assert isinstance(cast(Any, m3.nested), set)
assert m3.nested == {"foo"}
assert cast(Any, m3.nested) == {"foo"}


def test_list_nested_model() -> None:
Expand Down Expand Up @@ -323,7 +323,7 @@ class Model(BaseModel):
assert len(m.items) == 2
assert isinstance(m.items[0], Submodel1)
assert m.items[0].level == -1
assert m.items[1] == 156
assert cast(Any, m.items[1]) == 156


def test_union_of_lists() -> None:
Expand Down Expand Up @@ -355,7 +355,7 @@ class Model(BaseModel):
assert len(m.items) == 2
assert isinstance(m.items[0], SubModel1)
assert m.items[0].level == -1
assert m.items[1] == 156
assert cast(Any, m.items[1]) == 156


def test_dict_of_union() -> None:
Expand Down
22 changes: 12 additions & 10 deletions tests/test_transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,20 +260,22 @@ class MyModel(BaseModel):
@parametrize
@pytest.mark.asyncio
async def test_pydantic_model_to_dictionary(use_async: bool) -> None:
assert await transform(MyModel(foo="hi!"), Any, use_async) == {"foo": "hi!"}
assert await transform(MyModel.construct(foo="hi!"), Any, use_async) == {"foo": "hi!"}
assert cast(Any, await transform(MyModel(foo="hi!"), Any, use_async)) == {"foo": "hi!"}
assert cast(Any, await transform(MyModel.construct(foo="hi!"), Any, use_async)) == {"foo": "hi!"}


@parametrize
@pytest.mark.asyncio
async def test_pydantic_empty_model(use_async: bool) -> None:
assert await transform(MyModel.construct(), Any, use_async) == {}
assert cast(Any, await transform(MyModel.construct(), Any, use_async)) == {}


@parametrize
@pytest.mark.asyncio
async def test_pydantic_unknown_field(use_async: bool) -> None:
assert await transform(MyModel.construct(my_untyped_field=True), Any, use_async) == {"my_untyped_field": True}
assert cast(Any, await transform(MyModel.construct(my_untyped_field=True), Any, use_async)) == {
"my_untyped_field": True
}


@parametrize
Expand All @@ -285,7 +287,7 @@ async def test_pydantic_mismatched_types(use_async: bool) -> None:
params = await transform(model, Any, use_async)
else:
params = await transform(model, Any, use_async)
assert params == {"foo": True}
assert cast(Any, params) == {"foo": True}


@parametrize
Expand All @@ -297,7 +299,7 @@ async def test_pydantic_mismatched_object_type(use_async: bool) -> None:
params = await transform(model, Any, use_async)
else:
params = await transform(model, Any, use_async)
assert params == {"foo": {"hello": "world"}}
assert cast(Any, params) == {"foo": {"hello": "world"}}


class ModelNestedObjects(BaseModel):
Expand All @@ -309,7 +311,7 @@ class ModelNestedObjects(BaseModel):
async def test_pydantic_nested_objects(use_async: bool) -> None:
model = ModelNestedObjects.construct(nested={"foo": "stainless"})
assert isinstance(model.nested, MyModel)
assert await transform(model, Any, use_async) == {"nested": {"foo": "stainless"}}
assert cast(Any, await transform(model, Any, use_async)) == {"nested": {"foo": "stainless"}}


class ModelWithDefaultField(BaseModel):
Expand All @@ -325,19 +327,19 @@ async def test_pydantic_default_field(use_async: bool) -> None:
model = ModelWithDefaultField.construct()
assert model.with_none_default is None
assert model.with_str_default == "foo"
assert await transform(model, Any, use_async) == {}
assert cast(Any, await transform(model, Any, use_async)) == {}

# should be included when the default value is explicitly given
model = ModelWithDefaultField.construct(with_none_default=None, with_str_default="foo")
assert model.with_none_default is None
assert model.with_str_default == "foo"
assert await transform(model, Any, use_async) == {"with_none_default": None, "with_str_default": "foo"}
assert cast(Any, await transform(model, Any, use_async)) == {"with_none_default": None, "with_str_default": "foo"}

# should be included when a non-default value is explicitly given
model = ModelWithDefaultField.construct(with_none_default="bar", with_str_default="baz")
assert model.with_none_default == "bar"
assert model.with_str_default == "baz"
assert await transform(model, Any, use_async) == {"with_none_default": "bar", "with_str_default": "baz"}
assert cast(Any, await transform(model, Any, use_async)) == {"with_none_default": "bar", "with_str_default": "baz"}


class TypedDictIterableUnion(TypedDict):
Expand Down