From 99041691c6c4e16d77cea36222105b7c811306e3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 12:04:27 +0000 Subject: [PATCH 1/3] fix(parsing): parse extra field types --- src/increase/_models.py | 25 +++++++++++++++++++++++-- tests/test_models.py | 29 ++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/increase/_models.py b/src/increase/_models.py index ffcbf67bc..b8387ce98 100644 --- a/src/increase/_models.py +++ b/src/increase/_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 43aa0a417..e97a30009 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 887e44875b4811e50c3095eb11aa49b25707e6c3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 17:33:41 +0000 Subject: [PATCH 2/3] feat(api): api update --- .stats.yml | 4 ++-- src/increase/types/physical_card.py | 10 +++++++--- src/increase/types/physical_card_create_params.py | 15 ++++++++++++--- tests/api_resources/test_physical_cards.py | 2 ++ 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/.stats.yml b/.stats.yml index 02225f19a..4f46bd0e2 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 202 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/increase%2Fincrease-43d34b4805f305e7e2c71644ade73722d053018acd6009352360e8a87cbe2250.yml -openapi_spec_hash: cfff23de89960d895052350a33499cf1 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/increase%2Fincrease-f755cd0f7b44abb0c5ec9857c8ac47d778b3d753335d9a5cc3b73e4fdf9f2f96.yml +openapi_spec_hash: 16295ba5ba27744cd4f1464d640dadcf config_hash: a185e9a72778cc4658ea73fb3a7f1354 diff --git a/src/increase/types/physical_card.py b/src/increase/types/physical_card.py index 8cf4a3b6e..0f00f8688 100644 --- a/src/increase/types/physical_card.py +++ b/src/increase/types/physical_card.py @@ -21,6 +21,9 @@ class ShipmentAddress(BaseModel): city: str """The city of the shipping address.""" + country: str + """The country of the shipping address.""" + line1: str """The first line of the shipping address.""" @@ -37,7 +40,7 @@ class ShipmentAddress(BaseModel): """The postal code of the shipping address.""" state: str - """The US state of the shipping address.""" + """The state of the shipping address.""" class ShipmentTrackingUpdate(BaseModel): @@ -98,12 +101,13 @@ class Shipment(BaseModel): address: ShipmentAddress """The location to where the card's packing label is addressed.""" - method: Literal["usps", "fedex_priority_overnight", "fedex_2_day"] + method: Literal["usps", "fedex_priority_overnight", "fedex_2_day", "dhl_worldwide_express"] """The shipping method. - - `usps` - USPS Post with tracking. + - `usps` - USPS Post. - `fedex_priority_overnight` - FedEx Priority Overnight, no signature. - `fedex_2_day` - FedEx 2-day. + - `dhl_worldwide_express` - DHL Worldwide Express, international shipping only. """ schedule: Literal["next_day", "same_day"] diff --git a/src/increase/types/physical_card_create_params.py b/src/increase/types/physical_card_create_params.py index 935d1cd31..3de994bc1 100644 --- a/src/increase/types/physical_card_create_params.py +++ b/src/increase/types/physical_card_create_params.py @@ -46,7 +46,15 @@ class ShipmentAddress(TypedDict, total=False): """The postal code of the shipping address.""" state: Required[str] - """The US state of the shipping address.""" + """The state of the shipping address.""" + + country: str + """ + The two-character ISO 3166-1 code of the country where the card should be + shipped (e.g., `US`). Please reach out to + [support@increase.com](mailto:support@increase.com) to ship cards + internationally. + """ line2: str """The second line of the shipping address.""" @@ -62,12 +70,13 @@ class Shipment(TypedDict, total=False): address: Required[ShipmentAddress] """The address to where the card should be shipped.""" - method: Required[Literal["usps", "fedex_priority_overnight", "fedex_2_day"]] + method: Required[Literal["usps", "fedex_priority_overnight", "fedex_2_day", "dhl_worldwide_express"]] """The shipping method to use. - - `usps` - USPS Post with tracking. + - `usps` - USPS Post. - `fedex_priority_overnight` - FedEx Priority Overnight, no signature. - `fedex_2_day` - FedEx 2-day. + - `dhl_worldwide_express` - DHL Worldwide Express, international shipping only. """ schedule: Literal["next_day", "same_day"] diff --git a/tests/api_resources/test_physical_cards.py b/tests/api_resources/test_physical_cards.py index e324b9b62..ca526387f 100644 --- a/tests/api_resources/test_physical_cards.py +++ b/tests/api_resources/test_physical_cards.py @@ -57,6 +57,7 @@ def test_method_create_with_all_params(self, client: Increase) -> None: "name": "Ian Crease", "postal_code": "10045", "state": "NY", + "country": "x", "line2": "Unit 2", "line3": "x", "phone_number": "x", @@ -283,6 +284,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncIncrease) "name": "Ian Crease", "postal_code": "10045", "state": "NY", + "country": "x", "line2": "Unit 2", "line3": "x", "phone_number": "x", From 4157f91b74901bc52b29a99489813ecb992a483e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 17:34:07 +0000 Subject: [PATCH 3/3] release: 0.269.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/increase/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 37556d6b0..fa440d4de 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.268.0" + ".": "0.269.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e07a236b..6d5c4c3a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 0.269.0 (2025-07-22) + +Full Changelog: [v0.268.0...v0.269.0](https://github.com/Increase/increase-python/compare/v0.268.0...v0.269.0) + +### Features + +* **api:** api update ([887e448](https://github.com/Increase/increase-python/commit/887e44875b4811e50c3095eb11aa49b25707e6c3)) + + +### Bug Fixes + +* **parsing:** parse extra field types ([9904169](https://github.com/Increase/increase-python/commit/99041691c6c4e16d77cea36222105b7c811306e3)) + ## 0.268.0 (2025-07-21) Full Changelog: [v0.267.0...v0.268.0](https://github.com/Increase/increase-python/compare/v0.267.0...v0.268.0) diff --git a/pyproject.toml b/pyproject.toml index e00559bbf..370d145d8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "increase" -version = "0.268.0" +version = "0.269.0" description = "The official Python library for the increase API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/increase/_version.py b/src/increase/_version.py index 308918e30..9a198e68f 100644 --- a/src/increase/_version.py +++ b/src/increase/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "increase" -__version__ = "0.268.0" # x-release-please-version +__version__ = "0.269.0" # x-release-please-version