diff --git a/.gitignore b/.gitignore index 96e42d86..0dcb47e1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ .prism.log -.vscode _dev __pycache__ diff --git a/.release-please-manifest.json b/.release-please-manifest.json index d0068599..4bb14de0 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.2.0-alpha.74" + ".": "0.2.0-alpha.75" } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..5b010307 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.analysis.importFormat": "relative", +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 866c2ef4..2066d7f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## 0.2.0-alpha.75 (2025-07-31) + +Full Changelog: [v0.2.0-alpha.74...v0.2.0-alpha.75](https://github.com/openlayer-ai/openlayer-python/compare/v0.2.0-alpha.74...v0.2.0-alpha.75) + +### Features + +* Add Programmatic Configuration Support for Tracing Decorators ([#495](https://github.com/openlayer-ai/openlayer-python/issues/495)) ([12b0f28](https://github.com/openlayer-ai/openlayer-python/commit/12b0f28ce2c361bd766b3be44f0c835d71a77bde)) + + +### Bug Fixes + +* **parsing:** parse extra field types ([674a00b](https://github.com/openlayer-ai/openlayer-python/commit/674a00b600ebfda1929863b7af38d26bb73a25a8)) + + +### Chores + +* **project:** add settings file for vscode ([499890c](https://github.com/openlayer-ai/openlayer-python/commit/499890c3272a663a1768ef664563a366fed0cf40)) + ## 0.2.0-alpha.74 (2025-07-22) Full Changelog: [v0.2.0-alpha.73...v0.2.0-alpha.74](https://github.com/openlayer-ai/openlayer-python/compare/v0.2.0-alpha.73...v0.2.0-alpha.74) diff --git a/pyproject.toml b/pyproject.toml index 388aabf7..e26690c9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openlayer" -version = "0.2.0-alpha.74" +version = "0.2.0-alpha.75" description = "The official Python library for the openlayer API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openlayer/_models.py b/src/openlayer/_models.py index ffcbf67b..b8387ce9 100644 --- a/src/openlayer/_models.py +++ b/src/openlayer/_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/src/openlayer/_version.py b/src/openlayer/_version.py index ad26fa33..25930fa2 100644 --- a/src/openlayer/_version.py +++ b/src/openlayer/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openlayer" -__version__ = "0.2.0-alpha.74" # x-release-please-version +__version__ = "0.2.0-alpha.75" # x-release-please-version diff --git a/tests/test_models.py b/tests/test_models.py index 59ce692a..02d71189 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"
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: