From d9f490f5bf74066ca0da76aaa45188882843ba37 Mon Sep 17 00:00:00 2001 From: Google Team Member Date: Tue, 22 Sep 2026 03:01:37 -0700 Subject: [PATCH] chore: regenerate FUTURE_COPYBARA_INTEGRATE_REVIEW=https://github.com/googleapis/python-genai/pull/2977 from googleapis:release-please--branches--main 73b713c31bd3c704f6e68bf9fc144abd6f2c9983 PiperOrigin-RevId: 985866850 --- google/genai/_gaos/utils/__init__.py | 3 -- google/genai/_gaos/utils/response_helpers.py | 5 +- google/genai/_gaos/utils/retries.py | 45 ++++++++--------- google/genai/_gaos/utils/serializers.py | 30 ++---------- google/genai/_gaos/utils/unions.py | 51 ++++++-------------- 5 files changed, 42 insertions(+), 92 deletions(-) diff --git a/google/genai/_gaos/utils/__init__.py b/google/genai/_gaos/utils/__init__.py index 89349a94d..588cd98f5 100644 --- a/google/genai/_gaos/utils/__init__.py +++ b/google/genai/_gaos/utils/__init__.py @@ -53,7 +53,6 @@ async def run_sync_in_thread(func: Callable[..., _T], *args) -> _T: from .security import get_security, get_security_from_env from .serializers import ( - ALLOW_UNKNOWN_UNION_VARIANTS, get_pydantic_model, marshal_json, unmarshal, @@ -126,7 +125,6 @@ async def run_sync_in_thread(func: Callable[..., _T], *args) -> _T: "stream_to_bytes", "stream_to_bytes_async", "template_url", - "ALLOW_UNKNOWN_UNION_VARIANTS", "unmarshal", "unmarshal_json", "validate_decimal", @@ -149,7 +147,6 @@ async def run_sync_in_thread(func: Callable[..., _T], *args) -> _T: "parse_duration": ".datetimes", "get_global_from_env": ".values", "get_headers": ".headers", - "ALLOW_UNKNOWN_UNION_VARIANTS": ".serializers", "get_pydantic_model": ".serializers", "get_query_params": ".queryparams", "get_response_headers": ".headers", diff --git a/google/genai/_gaos/utils/response_helpers.py b/google/genai/_gaos/utils/response_helpers.py index bbb844d39..97f690922 100644 --- a/google/genai/_gaos/utils/response_helpers.py +++ b/google/genai/_gaos/utils/response_helpers.py @@ -53,7 +53,6 @@ from .._version import __response_mode_header__ from .._hooks.types import AfterParseErrorContext from .eventstreaming import Stream, AsyncStream -from .serializers import ALLOW_UNKNOWN_UNION_VARIANTS from .unmarshal_json_response import unmarshal_json_response P = ParamSpec("P") @@ -208,9 +207,7 @@ def _synthesized_decoder(raw: str, _t: Any = chunk_t) -> Any: raise ValueError( f"Synthesized SSE decoder expected an envelope of shape {{'data': ...}}, got {envelope!r}. Pass decoder= to parse(...) to handle non-standard envelopes." ) - return _t.model_validate( - envelope["data"], context={ALLOW_UNKNOWN_UNION_VARIANTS: True} - ) + return _t.model_validate(envelope["data"]) resolved_decoder = _synthesized_decoder diff --git a/google/genai/_gaos/utils/retries.py b/google/genai/_gaos/utils/retries.py index 1dc009996..0b47124aa 100644 --- a/google/genai/_gaos/utils/retries.py +++ b/google/genai/_gaos/utils/retries.py @@ -26,6 +26,17 @@ import httpx +try: + import httpx2 +except ImportError: + httpx2 = None + +_RETRY_EXCEPTIONS = ( + (httpx.NetworkError, httpx.TimeoutException) + if httpx2 is None + else (httpx.NetworkError, httpx.TimeoutException, httpx2.NetworkError, httpx2.TimeoutException) +) + class BackoffStrategy: """Exponential backoff strategy configuration.""" @@ -131,18 +142,6 @@ def __init__(self, inner: Exception): self.inner = inner -_TRANSPORT_ERROR_NAMES = frozenset({"NetworkError", "TimeoutException"}) -_TRANSPORT_ERROR_BASES = frozenset({"TransportError", "RequestError", "HTTPError"}) - - -def _is_transport_error(exception: BaseException) -> bool: - """Report whether an exception is a connection or timeout failure.""" - if isinstance(exception, (httpx.NetworkError, httpx.TimeoutException)): - return True - names = {base.__name__ for base in type(exception).__mro__} - return bool(names & _TRANSPORT_ERROR_NAMES) and _TRANSPORT_ERROR_BASES <= names - - def _parse_retry_after_header(response: httpx.Response) -> Optional[int]: """Parse Retry-After header from response. @@ -249,15 +248,14 @@ def do_request(attempt: int) -> httpx.Response: if should_retry: raise TemporaryError(res) + except _RETRY_EXCEPTIONS as exception: + if retries.config.retry_connection_errors: + raise + + raise PermanentError(exception) from exception except TemporaryError: raise except Exception as exception: - if ( - _is_transport_error(exception) - and retries.config.retry_connection_errors - ): - raise - raise PermanentError(exception) from exception return res @@ -310,15 +308,14 @@ async def do_request(attempt: int) -> httpx.Response: if should_retry: raise TemporaryError(res) + except _RETRY_EXCEPTIONS as exception: + if retries.config.retry_connection_errors: + raise + + raise PermanentError(exception) from exception except TemporaryError: raise except Exception as exception: - if ( - _is_transport_error(exception) - and retries.config.retry_connection_errors - ): - raise - raise PermanentError(exception) from exception return res diff --git a/google/genai/_gaos/utils/serializers.py b/google/genai/_gaos/utils/serializers.py index 99747b4f6..cbfec2493 100644 --- a/google/genai/_gaos/utils/serializers.py +++ b/google/genai/_gaos/utils/serializers.py @@ -129,26 +129,11 @@ def validate(c): return validate -ALLOW_UNKNOWN_UNION_VARIANTS = "speakeasy_allow_unknown_union_variants" -"""Validation-context key enabling the Unknown fallback on open discriminated -unions. The SDK sets it when deserializing server responses; validation -without it (e.g. of user-constructed request payloads) stays strict. Pass -``context={ALLOW_UNKNOWN_UNION_VARIANTS: True}`` to ``model_validate`` to -opt in when parsing response payloads manually.""" - - def unmarshal_json(raw, typ: Any) -> Any: - return unmarshal( - from_json(raw), typ, coerce_iterables=False, allow_unknown_union_variants=True - ) + return unmarshal(from_json(raw), typ, coerce_iterables=False) -def unmarshal( - val, - typ: Any, - coerce_iterables: bool = True, - allow_unknown_union_variants: bool = False, -) -> Any: +def unmarshal(val, typ: Any, coerce_iterables: bool = True) -> Any: if coerce_iterables: val = _coerce_iterables_for_type(val, typ) unmarshaller = create_model( @@ -157,12 +142,7 @@ def unmarshal( __config__=ConfigDict(populate_by_name=True, arbitrary_types_allowed=True), ) - if allow_unknown_union_variants: - m = unmarshaller.model_validate( - {"body": val}, context={ALLOW_UNKNOWN_UNION_VARIANTS: True} - ) - else: - m = unmarshaller(body=val) + m = unmarshaller(body=val) # pyright: ignore[reportAttributeAccessIssue] return m.body # type: ignore @@ -173,9 +153,7 @@ def unmarshal( def construct_unvalidated(value: Any, typ: Any, _depth: int = 0) -> Any: try: - return unmarshal( - value, typ, coerce_iterables=True, allow_unknown_union_variants=True - ) + return unmarshal(value, typ, coerce_iterables=True) except Exception: try: return _construct_lenient(value, typ, _depth) diff --git a/google/genai/_gaos/utils/unions.py b/google/genai/_gaos/utils/unions.py index a205b82d5..ca2fc46de 100644 --- a/google/genai/_gaos/utils/unions.py +++ b/google/genai/_gaos/utils/unions.py @@ -17,14 +17,14 @@ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" -from typing import Any, Mapping +from typing import Any -from pydantic import BaseModel, TypeAdapter, ValidationError, ValidationInfo +from pydantic import BaseModel, TypeAdapter, ValidationError +from .serializers import construct_unvalidated def parse_open_union( v: Any, - info: ValidationInfo, *, disc_key: str, variants: dict[str, Any], @@ -35,47 +35,28 @@ def parse_open_union( """Parse an open discriminated union value with forward-compatibility. Known discriminator values are dispatched to their variant types. - - The Unknown fallback only applies when the validation context carries - ALLOW_UNKNOWN_UNION_VARIANTS, which the SDK sets when deserializing - server responses. There, unknown discriminator values — or known - discriminator values whose payload fails variant validation (e.g. a - partial variant emitted by a newer server) — produce an instance of the - fallback class, preserving the raw payload for inspection. Without the - flag (e.g. user-constructed request payloads), invalid values raise so - mistakes surface locally instead of being sent to the server. + Unknown discriminator values — or known discriminator values whose + payload fails variant validation (e.g. a partial variant emitted by a + newer server) — produce an instance of the fallback class, preserving + the raw payload for inspection. Non-dict values and dicts missing the discriminator deliberately raise instead of falling back, so pydantic can try sibling branches of an enclosing union (e.g. None in Optional[...]). """ - # pylint: disable=import-outside-toplevel - from .serializers import ALLOW_UNKNOWN_UNION_VARIANTS - if isinstance(v, BaseModel): return v if not isinstance(v, dict) or disc_key not in v: raise ValueError(f"{union_name}: expected object with '{disc_key}' field") - context = info.context - fallback_allowed = isinstance(context, Mapping) and bool( - context.get(ALLOW_UNKNOWN_UNION_VARIANTS) - ) disc = v[disc_key] variant_cls = variants.get(disc) - if variant_cls is None: - if fallback_allowed: + if variant_cls is not None: + try: + if isinstance(variant_cls, type) and issubclass(variant_cls, BaseModel): + return variant_cls.model_validate(v) + return TypeAdapter(variant_cls).validate_python(v) + except ValidationError: + if lenient: + return construct_unvalidated(v, variant_cls) return unknown_cls(raw=v) - raise ValueError(f"{union_name}: unrecognized {disc_key} value {disc!r}") - try: - if isinstance(variant_cls, type) and issubclass(variant_cls, BaseModel): - return variant_cls.model_validate(v, context=info.context) - return TypeAdapter(variant_cls).validate_python(v, context=info.context) - except ValidationError: - if not fallback_allowed: - raise - if lenient: - # pylint: disable=import-outside-toplevel - from .serializers import construct_unvalidated - - return construct_unvalidated(v, variant_cls) - return unknown_cls(raw=v) + return unknown_cls(raw=v)