From fefb5c10c10054f28fcccf0d9f44204de93e9fe3 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Sun, 4 Feb 2024 18:57:35 +0000 Subject: [PATCH 1/5] fix: prevent crash when platform.architecture() is not allowed (#334) --- src/anthropic/_base_client.py | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/anthropic/_base_client.py b/src/anthropic/_base_client.py index d7e5127d..73bd2411 100644 --- a/src/anthropic/_base_client.py +++ b/src/anthropic/_base_client.py @@ -1836,8 +1836,12 @@ def __str__(self) -> str: def get_platform() -> Platform: - system = platform.system().lower() - platform_name = platform.platform().lower() + try: + system = platform.system().lower() + platform_name = platform.platform().lower() + except Exception: + return "Unknown" + if "iphone" in platform_name or "ipad" in platform_name: # Tested using Python3IDE on an iPhone 11 and Pythonista on an iPad 7 # system is Darwin and platform_name is a string like: @@ -1880,8 +1884,8 @@ def platform_headers(version: str) -> Dict[str, str]: "X-Stainless-Package-Version": version, "X-Stainless-OS": str(get_platform()), "X-Stainless-Arch": str(get_architecture()), - "X-Stainless-Runtime": platform.python_implementation(), - "X-Stainless-Runtime-Version": platform.python_version(), + "X-Stainless-Runtime": get_python_runtime(), + "X-Stainless-Runtime-Version": get_python_version(), } @@ -1897,9 +1901,27 @@ def __str__(self) -> str: Arch = Union[OtherArch, Literal["x32", "x64", "arm", "arm64", "unknown"]] +def get_python_runtime() -> str: + try: + return platform.python_implementation() + except Exception: + return "unknown" + + +def get_python_version() -> str: + try: + return platform.python_version() + except Exception: + return "unknown" + + def get_architecture() -> Arch: - python_bitness, _ = platform.architecture() - machine = platform.machine().lower() + try: + python_bitness, _ = platform.architecture() + machine = platform.machine().lower() + except Exception: + return "unknown" + if machine in ("arm64", "aarch64"): return "arm64" From ea3ed7b8b91314721129480d164d7bf3bafec26c Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 6 Feb 2024 09:48:49 +0000 Subject: [PATCH 2/5] chore(internal): support serialising iterable types (#336) --- src/anthropic/_utils/__init__.py | 2 ++ src/anthropic/_utils/_transform.py | 9 +++++++- src/anthropic/_utils/_typing.py | 9 +++++++- src/anthropic/_utils/_utils.py | 4 ++++ tests/test_transform.py | 34 +++++++++++++++++++++++++++++- 5 files changed, 55 insertions(+), 3 deletions(-) diff --git a/src/anthropic/_utils/__init__.py b/src/anthropic/_utils/__init__.py index 0fb811a9..b5790a87 100644 --- a/src/anthropic/_utils/__init__.py +++ b/src/anthropic/_utils/__init__.py @@ -9,6 +9,7 @@ is_mapping as is_mapping, is_tuple_t as is_tuple_t, parse_date as parse_date, + is_iterable as is_iterable, is_sequence as is_sequence, coerce_float as coerce_float, is_mapping_t as is_mapping_t, @@ -33,6 +34,7 @@ is_list_type as is_list_type, is_union_type as is_union_type, extract_type_arg as extract_type_arg, + is_iterable_type as is_iterable_type, is_required_type as is_required_type, is_annotated_type as is_annotated_type, strip_annotated_type as strip_annotated_type, diff --git a/src/anthropic/_utils/_transform.py b/src/anthropic/_utils/_transform.py index 3a1c1496..2cb7726c 100644 --- a/src/anthropic/_utils/_transform.py +++ b/src/anthropic/_utils/_transform.py @@ -9,11 +9,13 @@ from ._utils import ( is_list, is_mapping, + is_iterable, ) from ._typing import ( is_list_type, is_union_type, extract_type_arg, + is_iterable_type, is_required_type, is_annotated_type, strip_annotated_type, @@ -157,7 +159,12 @@ def _transform_recursive( if is_typeddict(stripped_type) and is_mapping(data): return _transform_typeddict(data, stripped_type) - if is_list_type(stripped_type) and is_list(data): + if ( + # List[T] + (is_list_type(stripped_type) and is_list(data)) + # Iterable[T] + or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) + ): inner_type = extract_type_arg(stripped_type, 0) return [_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] diff --git a/src/anthropic/_utils/_typing.py b/src/anthropic/_utils/_typing.py index c1d1ebb9..c036991f 100644 --- a/src/anthropic/_utils/_typing.py +++ b/src/anthropic/_utils/_typing.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import Any, TypeVar, cast +from typing import Any, TypeVar, Iterable, cast +from collections import abc as _c_abc from typing_extensions import Required, Annotated, get_args, get_origin from .._types import InheritsGeneric @@ -15,6 +16,12 @@ def is_list_type(typ: type) -> bool: return (get_origin(typ) or typ) == list +def is_iterable_type(typ: type) -> bool: + """If the given type is `typing.Iterable[T]`""" + origin = get_origin(typ) or typ + return origin == Iterable or origin == _c_abc.Iterable + + def is_union_type(typ: type) -> bool: return _is_union(get_origin(typ)) diff --git a/src/anthropic/_utils/_utils.py b/src/anthropic/_utils/_utils.py index 1c5c21a8..93c95517 100644 --- a/src/anthropic/_utils/_utils.py +++ b/src/anthropic/_utils/_utils.py @@ -164,6 +164,10 @@ def is_list(obj: object) -> TypeGuard[list[object]]: return isinstance(obj, list) +def is_iterable(obj: object) -> TypeGuard[Iterable[object]]: + return isinstance(obj, Iterable) + + def deepcopy_minimal(item: _T) -> _T: """Minimal reimplementation of copy.deepcopy() that will only copy certain object types: diff --git a/tests/test_transform.py b/tests/test_transform.py index 1d1fb9bc..2e1d9605 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, List, Union, Optional +from typing import Any, List, Union, Iterable, Optional, cast from datetime import date, datetime from typing_extensions import Required, Annotated, TypedDict @@ -265,3 +265,35 @@ def test_pydantic_default_field() -> None: assert model.with_none_default == "bar" assert model.with_str_default == "baz" assert transform(model, Any) == {"with_none_default": "bar", "with_str_default": "baz"} + + +class TypedDictIterableUnion(TypedDict): + foo: Annotated[Union[Bar8, Iterable[Baz8]], PropertyInfo(alias="FOO")] + + +class Bar8(TypedDict): + foo_bar: Annotated[str, PropertyInfo(alias="fooBar")] + + +class Baz8(TypedDict): + foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] + + +def test_iterable_of_dictionaries() -> None: + assert transform({"foo": [{"foo_baz": "bar"}]}, TypedDictIterableUnion) == {"FOO": [{"fooBaz": "bar"}]} + assert cast(Any, transform({"foo": ({"foo_baz": "bar"},)}, TypedDictIterableUnion)) == {"FOO": [{"fooBaz": "bar"}]} + + def my_iter() -> Iterable[Baz8]: + yield {"foo_baz": "hello"} + yield {"foo_baz": "world"} + + assert transform({"foo": my_iter()}, TypedDictIterableUnion) == {"FOO": [{"fooBaz": "hello"}, {"fooBaz": "world"}]} + + +class TypedDictIterableUnionStr(TypedDict): + foo: Annotated[Union[str, Iterable[Baz8]], PropertyInfo(alias="FOO")] + + +def test_iterable_union_str() -> None: + assert transform({"foo": "bar"}, TypedDictIterableUnionStr) == {"FOO": "bar"} + assert cast(Any, transform(iter([{"foo_baz": "bar"}]), Union[str, Iterable[Baz8]])) == [{"fooBaz": "bar"}] From 2ebaf1d6a85b638b502661735e3ffc5b58d5c241 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 6 Feb 2024 10:30:14 +0000 Subject: [PATCH 3/5] chore(internal): add lint command (#337) --- pyproject.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 9ed5c6f8..0ae5939d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,6 +74,10 @@ format = { chain = [ "format:ruff" = "ruff format" "format:isort" = "isort ." +"lint" = { chain = [ + "check:ruff", + "typecheck", +]} "check:ruff" = "ruff ." "fix:ruff" = "ruff --fix ." From 6e7761b89c9ef226bd8f7df465445526c08fdb2f Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Tue, 6 Feb 2024 15:20:23 +0000 Subject: [PATCH 4/5] fix(types): loosen most List params types to Iterable (#338) --- src/anthropic/resources/beta/messages.py | 30 +++++++++---------- .../types/beta/message_create_params.py | 4 +-- src/anthropic/types/beta/message_param.py | 4 +-- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/anthropic/resources/beta/messages.py b/src/anthropic/resources/beta/messages.py index d549bf22..2e04d085 100644 --- a/src/anthropic/resources/beta/messages.py +++ b/src/anthropic/resources/beta/messages.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, overload +from typing import List, Iterable, overload from functools import partial from typing_extensions import Literal @@ -45,7 +45,7 @@ def create( self, *, max_tokens: int, - messages: List[MessageParam], + messages: Iterable[MessageParam], model: str, metadata: message_create_params.Metadata | NotGiven = NOT_GIVEN, stop_sequences: List[str] | NotGiven = NOT_GIVEN, @@ -215,7 +215,7 @@ def create( self, *, max_tokens: int, - messages: List[MessageParam], + messages: Iterable[MessageParam], model: str, stream: Literal[True], metadata: message_create_params.Metadata | NotGiven = NOT_GIVEN, @@ -385,7 +385,7 @@ def create( self, *, max_tokens: int, - messages: List[MessageParam], + messages: Iterable[MessageParam], model: str, stream: bool, metadata: message_create_params.Metadata | NotGiven = NOT_GIVEN, @@ -555,7 +555,7 @@ def create( self, *, max_tokens: int, - messages: List[MessageParam], + messages: Iterable[MessageParam], model: str, metadata: message_create_params.Metadata | NotGiven = NOT_GIVEN, stop_sequences: List[str] | NotGiven = NOT_GIVEN, @@ -602,7 +602,7 @@ def stream( self, *, max_tokens: int, - messages: List[MessageParam], + messages: Iterable[MessageParam], model: str, metadata: message_create_params.Metadata | NotGiven = NOT_GIVEN, stop_sequences: List[str] | NotGiven = NOT_GIVEN, @@ -625,7 +625,7 @@ def stream( self, *, max_tokens: int, - messages: List[MessageParam], + messages: Iterable[MessageParam], model: str, metadata: message_create_params.Metadata | NotGiven = NOT_GIVEN, stop_sequences: List[str] | NotGiven = NOT_GIVEN, @@ -648,7 +648,7 @@ def stream( self, *, max_tokens: int, - messages: List[MessageParam], + messages: Iterable[MessageParam], model: str, metadata: message_create_params.Metadata | NotGiven = NOT_GIVEN, stop_sequences: List[str] | NotGiven = NOT_GIVEN, @@ -713,7 +713,7 @@ async def create( self, *, max_tokens: int, - messages: List[MessageParam], + messages: Iterable[MessageParam], model: str, metadata: message_create_params.Metadata | NotGiven = NOT_GIVEN, stop_sequences: List[str] | NotGiven = NOT_GIVEN, @@ -883,7 +883,7 @@ async def create( self, *, max_tokens: int, - messages: List[MessageParam], + messages: Iterable[MessageParam], model: str, stream: Literal[True], metadata: message_create_params.Metadata | NotGiven = NOT_GIVEN, @@ -1053,7 +1053,7 @@ async def create( self, *, max_tokens: int, - messages: List[MessageParam], + messages: Iterable[MessageParam], model: str, stream: bool, metadata: message_create_params.Metadata | NotGiven = NOT_GIVEN, @@ -1223,7 +1223,7 @@ async def create( self, *, max_tokens: int, - messages: List[MessageParam], + messages: Iterable[MessageParam], model: str, metadata: message_create_params.Metadata | NotGiven = NOT_GIVEN, stop_sequences: List[str] | NotGiven = NOT_GIVEN, @@ -1270,7 +1270,7 @@ def stream( self, *, max_tokens: int, - messages: List[MessageParam], + messages: Iterable[MessageParam], model: str, metadata: message_create_params.Metadata | NotGiven = NOT_GIVEN, stop_sequences: List[str] | NotGiven = NOT_GIVEN, @@ -1293,7 +1293,7 @@ def stream( self, *, max_tokens: int, - messages: List[MessageParam], + messages: Iterable[MessageParam], model: str, metadata: message_create_params.Metadata | NotGiven = NOT_GIVEN, stop_sequences: List[str] | NotGiven = NOT_GIVEN, @@ -1316,7 +1316,7 @@ def stream( self, *, max_tokens: int, - messages: List[MessageParam], + messages: Iterable[MessageParam], model: str, metadata: message_create_params.Metadata | NotGiven = NOT_GIVEN, stop_sequences: List[str] | NotGiven = NOT_GIVEN, diff --git a/src/anthropic/types/beta/message_create_params.py b/src/anthropic/types/beta/message_create_params.py index ec7667c5..99199e57 100644 --- a/src/anthropic/types/beta/message_create_params.py +++ b/src/anthropic/types/beta/message_create_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, Union, Optional +from typing import List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict from .message_param import MessageParam @@ -22,7 +22,7 @@ class MessageCreateParamsBase(TypedDict, total=False): for details. """ - messages: Required[List[MessageParam]] + messages: Required[Iterable[MessageParam]] """Input messages. Our models are trained to operate on alternating `user` and `assistant` diff --git a/src/anthropic/types/beta/message_param.py b/src/anthropic/types/beta/message_param.py index ffc2ace8..3a4c2c98 100644 --- a/src/anthropic/types/beta/message_param.py +++ b/src/anthropic/types/beta/message_param.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, Union +from typing import Union, Iterable from typing_extensions import Literal, Required, TypedDict from .text_block_param import TextBlockParam @@ -11,6 +11,6 @@ class MessageParam(TypedDict, total=False): - content: Required[Union[str, List[TextBlockParam]]] + content: Required[Union[str, Iterable[TextBlockParam]]] role: Required[Literal["user", "assistant"]] From 18bf7e00adabdff0227d3022a6ac654cc2c4c2b0 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 7 Feb 2024 05:01:20 +0000 Subject: [PATCH 5/5] release: 0.15.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 15 +++++++++++++++ pyproject.toml | 2 +- src/anthropic/_version.py | 2 +- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 8f3e0a49..56b9e502 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.15.0" + ".": "0.15.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index e91001e5..c2412d0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 0.15.1 (2024-02-07) + +Full Changelog: [v0.15.0...v0.15.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.15.0...v0.15.1) + +### Bug Fixes + +* prevent crash when platform.architecture() is not allowed ([#334](https://github.com/anthropics/anthropic-sdk-python/issues/334)) ([fefb5c1](https://github.com/anthropics/anthropic-sdk-python/commit/fefb5c10c10054f28fcccf0d9f44204de93e9fe3)) +* **types:** loosen most List params types to Iterable ([#338](https://github.com/anthropics/anthropic-sdk-python/issues/338)) ([6e7761b](https://github.com/anthropics/anthropic-sdk-python/commit/6e7761b89c9ef226bd8f7df465445526c08fdb2f)) + + +### Chores + +* **internal:** add lint command ([#337](https://github.com/anthropics/anthropic-sdk-python/issues/337)) ([2ebaf1d](https://github.com/anthropics/anthropic-sdk-python/commit/2ebaf1d6a85b638b502661735e3ffc5b58d5c241)) +* **internal:** support serialising iterable types ([#336](https://github.com/anthropics/anthropic-sdk-python/issues/336)) ([ea3ed7b](https://github.com/anthropics/anthropic-sdk-python/commit/ea3ed7b8b91314721129480d164d7bf3bafec26c)) + ## 0.15.0 (2024-02-02) Full Changelog: [v0.14.1...v0.15.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.14.1...v0.15.0) diff --git a/pyproject.toml b/pyproject.toml index 0ae5939d..ef3e8fc4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "anthropic" -version = "0.15.0" +version = "0.15.1" description = "The official Python library for the anthropic API" readme = "README.md" license = "MIT" diff --git a/src/anthropic/_version.py b/src/anthropic/_version.py index 02d59119..50a90d2c 100644 --- a/src/anthropic/_version.py +++ b/src/anthropic/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "anthropic" -__version__ = "0.15.0" # x-release-please-version +__version__ = "0.15.1" # x-release-please-version