From 3f64d12c61eb36be75b9b5044f0cc2e3b6d52658 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 15 Jul 2025 02:05:08 +0000 Subject: [PATCH 1/4] feat: clean up environment call outs --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index 99756fc..dcaf86c 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,6 @@ pip install --pre replicate[aiohttp] Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`: ```python -import os import asyncio from replicate import DefaultAioHttpClient from replicate import AsyncReplicate @@ -91,9 +90,7 @@ from replicate import AsyncReplicate async def main() -> None: async with AsyncReplicate( - bearer_token=os.environ.get( - "REPLICATE_API_TOKEN" - ), # This is the default and can be omitted + bearer_token="My Bearer Token", http_client=DefaultAioHttpClient(), ) as replicate: prediction = await replicate.predictions.get( From d54a68644ded46361180488d36ede3d2c1ee4723 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 02:04:26 +0000 Subject: [PATCH 2/4] fix(parsing): ignore empty metadata --- src/replicate/_models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/replicate/_models.py b/src/replicate/_models.py index 528d568..ffcbf67 100644 --- a/src/replicate/_models.py +++ b/src/replicate/_models.py @@ -439,7 +439,7 @@ def construct_type(*, value: object, type_: object, metadata: Optional[List[Any] type_ = type_.__value__ # type: ignore[unreachable] # unwrap `Annotated[T, ...]` -> `T` - if metadata is not None: + if metadata is not None and len(metadata) > 0: meta: tuple[Any, ...] = tuple(metadata) elif is_annotated_type(type_): meta = get_args(type_)[1:] From 4fed49d8cc6e6a68c59505141574470fb33221f1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 23 Jul 2025 02:05:51 +0000 Subject: [PATCH 3/4] fix(parsing): parse extra field types --- src/replicate/_models.py | 25 +++++++++++++++++++++++-- tests/test_models.py | 29 ++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/replicate/_models.py b/src/replicate/_models.py index ffcbf67..b8387ce 100644 --- a/src/replicate/_models.py +++ b/src/replicate/_models.py @@ -208,14 +208,18 @@ def construct( # pyright: ignore[reportIncompatibleMethodOverride] else: fields_values[name] = field_get_default(field) + extra_field_type = _get_extra_fields_type(__cls) + _extra = {} for key, value in values.items(): if key not in model_fields: + parsed = construct_type(value=value, type_=extra_field_type) if extra_field_type is not None else value + if PYDANTIC_V2: - _extra[key] = value + _extra[key] = parsed else: _fields_set.add(key) - fields_values[key] = value + fields_values[key] = parsed object.__setattr__(m, "__dict__", fields_values) @@ -370,6 +374,23 @@ def _construct_field(value: object, field: FieldInfo, key: str) -> object: return construct_type(value=value, type_=type_, metadata=getattr(field, "metadata", None)) +def _get_extra_fields_type(cls: type[pydantic.BaseModel]) -> type | None: + if not PYDANTIC_V2: + # TODO + return None + + schema = cls.__pydantic_core_schema__ + if schema["type"] == "model": + fields = schema["schema"] + if fields["type"] == "model-fields": + extras = fields.get("extras_schema") + if extras and "cls" in extras: + # mypy can't narrow the type + return extras["cls"] # type: ignore[no-any-return] + + return None + + def is_basemodel(type_: type) -> bool: """Returns whether or not the given type is either a `BaseModel` or a union of `BaseModel`""" if is_union(type_): diff --git a/tests/test_models.py b/tests/test_models.py index e39dc96..3e53c16 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,5 +1,5 @@ import json -from typing import Any, Dict, List, Union, Optional, cast +from typing import TYPE_CHECKING, Any, Dict, List, Union, Optional, cast from datetime import datetime, timezone from typing_extensions import Literal, Annotated, TypeAliasType @@ -934,3 +934,30 @@ class Type2(BaseModel): ) assert isinstance(model, Type1) assert isinstance(model.value, InnerType2) + + +@pytest.mark.skipif(not PYDANTIC_V2, reason="this is only supported in pydantic v2 for now") +def test_extra_properties() -> None: + class Item(BaseModel): + prop: int + + class Model(BaseModel): + __pydantic_extra__: Dict[str, Item] = Field(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + + other: str + + if TYPE_CHECKING: + + def __getattr__(self, attr: str) -> Item: ... + + model = construct_type( + type_=Model, + value={ + "a": {"prop": 1}, + "other": "foo", + }, + ) + assert isinstance(model, Model) + assert model.a.prop == 1 + assert isinstance(model.a, Item) + assert model.other == "foo" From a6dec5f59dcf5c53ce3d9b779380d3342758d28f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 23 Jul 2025 02:06:12 +0000 Subject: [PATCH 4/4] release: 2.0.0-alpha.12 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 14 ++++++++++++++ pyproject.toml | 2 +- src/replicate/_version.py | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 19cfee5..98f85dd 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.0.0-alpha.11" + ".": "2.0.0-alpha.12" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 6095bb2..308668a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 2.0.0-alpha.12 (2025-07-23) + +Full Changelog: [v2.0.0-alpha.11...v2.0.0-alpha.12](https://github.com/replicate/replicate-python-stainless/compare/v2.0.0-alpha.11...v2.0.0-alpha.12) + +### Features + +* clean up environment call outs ([3f64d12](https://github.com/replicate/replicate-python-stainless/commit/3f64d12c61eb36be75b9b5044f0cc2e3b6d52658)) + + +### Bug Fixes + +* **parsing:** ignore empty metadata ([d54a686](https://github.com/replicate/replicate-python-stainless/commit/d54a68644ded46361180488d36ede3d2c1ee4723)) +* **parsing:** parse extra field types ([4fed49d](https://github.com/replicate/replicate-python-stainless/commit/4fed49d8cc6e6a68c59505141574470fb33221f1)) + ## 2.0.0-alpha.11 (2025-07-12) Full Changelog: [v2.0.0-alpha.10...v2.0.0-alpha.11](https://github.com/replicate/replicate-python-stainless/compare/v2.0.0-alpha.10...v2.0.0-alpha.11) diff --git a/pyproject.toml b/pyproject.toml index 8ba0b24..a9b4da9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "replicate" -version = "2.0.0-alpha.11" +version = "2.0.0-alpha.12" description = "The official Python library for the replicate API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/replicate/_version.py b/src/replicate/_version.py index 203297c..6edac9f 100644 --- a/src/replicate/_version.py +++ b/src/replicate/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "replicate" -__version__ = "2.0.0-alpha.11" # x-release-please-version +__version__ = "2.0.0-alpha.12" # x-release-please-version