diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 8255acf..69eb19a 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.19.1" + ".": "1.20.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 26179c7..561a7a7 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 11 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-6620330945de41f1c453692af40842f08fe1fd281ff6ba4e79d447c941ebd783.yml -openapi_spec_hash: 861a43669d27d942d4bd3e36a398e95b -config_hash: 083e432ea397a9018371145493400188 +configured_endpoints: 12 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/brand-dev%2Fbrand.dev-070cac50467cd07e8601e948fc97e3c82ea0630d6a0b0f24164335e396893e6a.yml +openapi_spec_hash: b887bcfe688f72b3a34ee24246d12955 +config_hash: 86160e220c81f47769a71c9343e486d8 diff --git a/CHANGELOG.md b/CHANGELOG.md index 66d9387..1ef695c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## 1.20.0 (2025-11-19) + +Full Changelog: [v1.19.1...v1.20.0](https://github.com/brand-dot-dev/python-sdk/compare/v1.19.1...v1.20.0) + +### Features + +* **api:** manual updates ([152e848](https://github.com/brand-dot-dev/python-sdk/commit/152e8489b6ecbbe717d48ac0c437e88c64e923a5)) + + +### Bug Fixes + +* compat with Python 3.14 ([8602245](https://github.com/brand-dot-dev/python-sdk/commit/860224506b4fe79750cff976b1bed305f4f826da)) +* **compat:** update signatures of `model_dump` and `model_dump_json` for Pydantic v1 ([768e2d1](https://github.com/brand-dot-dev/python-sdk/commit/768e2d13ddf92adde07ad4715419caa4b3257338)) + + +### Chores + +* **package:** drop Python 3.8 support ([ecc46d0](https://github.com/brand-dot-dev/python-sdk/commit/ecc46d081a6d8ecbbdb633e444218f0d12487f2b)) + ## 1.19.1 (2025-11-04) Full Changelog: [v1.19.0...v1.19.1](https://github.com/brand-dot-dev/python-sdk/compare/v1.19.0...v1.19.1) diff --git a/README.md b/README.md index 41e137d..5ee938a 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![PyPI version](https://img.shields.io/pypi/v/brand.dev.svg?label=pypi%20(stable))](https://pypi.org/project/brand.dev/) -The Brand Dev Python library provides convenient access to the Brand Dev REST API from any Python 3.8+ +The Brand Dev Python library provides convenient access to the Brand Dev REST API from any Python 3.9+ application. The library includes type definitions for all request params and response fields, and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx). @@ -396,7 +396,7 @@ print(brand.dev.__version__) ## Requirements -Python 3.8 or higher. +Python 3.9 or higher. ## Contributing diff --git a/api.md b/api.md index cec128a..cd8c7b2 100644 --- a/api.md +++ b/api.md @@ -9,6 +9,7 @@ from brand.dev.types import ( BrandIdentifyFromTransactionResponse, BrandPrefetchResponse, BrandRetrieveByEmailResponse, + BrandRetrieveByIsinResponse, BrandRetrieveByNameResponse, BrandRetrieveByTickerResponse, BrandRetrieveNaicsResponse, @@ -25,6 +26,7 @@ Methods: - client.brand.identify_from_transaction(\*\*params) -> BrandIdentifyFromTransactionResponse - client.brand.prefetch(\*\*params) -> BrandPrefetchResponse - client.brand.retrieve_by_email(\*\*params) -> BrandRetrieveByEmailResponse +- client.brand.retrieve_by_isin(\*\*params) -> BrandRetrieveByIsinResponse - client.brand.retrieve_by_name(\*\*params) -> BrandRetrieveByNameResponse - client.brand.retrieve_by_ticker(\*\*params) -> BrandRetrieveByTickerResponse - client.brand.retrieve_naics(\*\*params) -> BrandRetrieveNaicsResponse diff --git a/pyproject.toml b/pyproject.toml index 8541931..5667b40 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "brand.dev" -version = "1.19.1" +version = "1.20.0" description = "The official Python library for the brand.dev API" dynamic = ["readme"] license = "Apache-2.0" @@ -15,11 +15,10 @@ dependencies = [ "distro>=1.7.0, <2", "sniffio", ] -requires-python = ">= 3.8" +requires-python = ">= 3.9" classifiers = [ "Typing :: Typed", "Intended Audience :: Developers", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", @@ -141,7 +140,7 @@ filterwarnings = [ # there are a couple of flags that are still disabled by # default in strict mode as they are experimental and niche. typeCheckingMode = "strict" -pythonVersion = "3.8" +pythonVersion = "3.9" exclude = [ "_dev", diff --git a/src/brand/dev/_models.py b/src/brand/dev/_models.py index 6a3cd1d..ca9500b 100644 --- a/src/brand/dev/_models.py +++ b/src/brand/dev/_models.py @@ -2,6 +2,7 @@ import os import inspect +import weakref from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, Optional, cast from datetime import date, datetime from typing_extensions import ( @@ -256,15 +257,16 @@ def model_dump( mode: Literal["json", "python"] | str = "python", include: IncEx | None = None, exclude: IncEx | None = None, + context: Any | None = None, by_alias: bool | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, + exclude_computed_fields: bool = False, round_trip: bool = False, warnings: bool | Literal["none", "warn", "error"] = True, - context: dict[str, Any] | None = None, - serialize_as_any: bool = False, fallback: Callable[[Any], Any] | None = None, + serialize_as_any: bool = False, ) -> dict[str, Any]: """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump @@ -272,16 +274,24 @@ def model_dump( Args: mode: The mode in which `to_python` should run. - If mode is 'json', the dictionary will only contain JSON serializable types. - If mode is 'python', the dictionary may contain any Python objects. - include: A list of fields to include in the output. - exclude: A list of fields to exclude from the output. + If mode is 'json', the output will only contain JSON serializable types. + If mode is 'python', the output may contain non-JSON-serializable Python objects. + include: A set of fields to include in the output. + exclude: A set of fields to exclude from the output. + context: Additional context to pass to the serializer. by_alias: Whether to use the field's alias in the dictionary key if defined. - exclude_unset: Whether to exclude fields that are unset or None from the output. - exclude_defaults: Whether to exclude fields that are set to their default value from the output. - exclude_none: Whether to exclude fields that have a value of `None` from the output. - round_trip: Whether to enable serialization and deserialization round-trip support. - warnings: Whether to log warnings when invalid fields are encountered. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that are set to their default value. + exclude_none: Whether to exclude fields that have a value of `None`. + exclude_computed_fields: Whether to exclude computed fields. + While this can be useful for round-tripping, it is usually recommended to use the dedicated + `round_trip` parameter instead. + round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T]. + warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors, + "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError]. + fallback: A function to call when an unknown value is encountered. If not provided, + a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised. + serialize_as_any: Whether to serialize fields with duck-typing serialization behavior. Returns: A dictionary representation of the model. @@ -298,6 +308,8 @@ def model_dump( raise ValueError("serialize_as_any is only supported in Pydantic v2") if fallback is not None: raise ValueError("fallback is only supported in Pydantic v2") + if exclude_computed_fields != False: + raise ValueError("exclude_computed_fields is only supported in Pydantic v2") dumped = super().dict( # pyright: ignore[reportDeprecated] include=include, exclude=exclude, @@ -314,15 +326,17 @@ def model_dump_json( self, *, indent: int | None = None, + ensure_ascii: bool = False, include: IncEx | None = None, exclude: IncEx | None = None, + context: Any | None = None, by_alias: bool | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, + exclude_computed_fields: bool = False, round_trip: bool = False, warnings: bool | Literal["none", "warn", "error"] = True, - context: dict[str, Any] | None = None, fallback: Callable[[Any], Any] | None = None, serialize_as_any: bool = False, ) -> str: @@ -354,6 +368,10 @@ def model_dump_json( raise ValueError("serialize_as_any is only supported in Pydantic v2") if fallback is not None: raise ValueError("fallback is only supported in Pydantic v2") + if ensure_ascii != False: + raise ValueError("ensure_ascii is only supported in Pydantic v2") + if exclude_computed_fields != False: + raise ValueError("exclude_computed_fields is only supported in Pydantic v2") return super().json( # type: ignore[reportDeprecated] indent=indent, include=include, @@ -573,6 +591,9 @@ class CachedDiscriminatorType(Protocol): __discriminator__: DiscriminatorDetails +DISCRIMINATOR_CACHE: weakref.WeakKeyDictionary[type, DiscriminatorDetails] = weakref.WeakKeyDictionary() + + class DiscriminatorDetails: field_name: str """The name of the discriminator field in the variant class, e.g. @@ -615,8 +636,9 @@ def __init__( def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, ...]) -> DiscriminatorDetails | None: - if isinstance(union, CachedDiscriminatorType): - return union.__discriminator__ + cached = DISCRIMINATOR_CACHE.get(union) + if cached is not None: + return cached discriminator_field_name: str | None = None @@ -669,7 +691,7 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, discriminator_field=discriminator_field_name, discriminator_alias=discriminator_alias, ) - cast(CachedDiscriminatorType, union).__discriminator__ = details + DISCRIMINATOR_CACHE.setdefault(union, details) return details diff --git a/src/brand/dev/_utils/_sync.py b/src/brand/dev/_utils/_sync.py index ad7ec71..f6027c1 100644 --- a/src/brand/dev/_utils/_sync.py +++ b/src/brand/dev/_utils/_sync.py @@ -1,10 +1,8 @@ from __future__ import annotations -import sys import asyncio import functools -import contextvars -from typing import Any, TypeVar, Callable, Awaitable +from typing import TypeVar, Callable, Awaitable from typing_extensions import ParamSpec import anyio @@ -15,34 +13,11 @@ T_ParamSpec = ParamSpec("T_ParamSpec") -if sys.version_info >= (3, 9): - _asyncio_to_thread = asyncio.to_thread -else: - # backport of https://docs.python.org/3/library/asyncio-task.html#asyncio.to_thread - # for Python 3.8 support - async def _asyncio_to_thread( - func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs - ) -> Any: - """Asynchronously run function *func* in a separate thread. - - Any *args and **kwargs supplied for this function are directly passed - to *func*. Also, the current :class:`contextvars.Context` is propagated, - allowing context variables from the main thread to be accessed in the - separate thread. - - Returns a coroutine that can be awaited to get the eventual result of *func*. - """ - loop = asyncio.events.get_running_loop() - ctx = contextvars.copy_context() - func_call = functools.partial(ctx.run, func, *args, **kwargs) - return await loop.run_in_executor(None, func_call) - - async def to_thread( func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs ) -> T_Retval: if sniffio.current_async_library() == "asyncio": - return await _asyncio_to_thread(func, *args, **kwargs) + return await asyncio.to_thread(func, *args, **kwargs) return await anyio.to_thread.run_sync( functools.partial(func, *args, **kwargs), @@ -53,10 +28,7 @@ async def to_thread( def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]: """ Take a blocking function and create an async one that receives the same - positional and keyword arguments. For python version 3.9 and above, it uses - asyncio.to_thread to run the function in a separate thread. For python version - 3.8, it uses locally defined copy of the asyncio.to_thread function which was - introduced in python 3.9. + positional and keyword arguments. Usage: diff --git a/src/brand/dev/_version.py b/src/brand/dev/_version.py index 1730d0d..ab7552d 100644 --- a/src/brand/dev/_version.py +++ b/src/brand/dev/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "brand.dev" -__version__ = "1.19.1" # x-release-please-version +__version__ = "1.20.0" # x-release-please-version diff --git a/src/brand/dev/resources/brand.py b/src/brand/dev/resources/brand.py index 8749438..5574c02 100644 --- a/src/brand/dev/resources/brand.py +++ b/src/brand/dev/resources/brand.py @@ -14,6 +14,7 @@ brand_screenshot_params, brand_styleguide_params, brand_retrieve_naics_params, + brand_retrieve_by_isin_params, brand_retrieve_by_name_params, brand_retrieve_by_email_params, brand_retrieve_by_ticker_params, @@ -37,6 +38,7 @@ from ..types.brand_screenshot_response import BrandScreenshotResponse from ..types.brand_styleguide_response import BrandStyleguideResponse from ..types.brand_retrieve_naics_response import BrandRetrieveNaicsResponse +from ..types.brand_retrieve_by_isin_response import BrandRetrieveByIsinResponse from ..types.brand_retrieve_by_name_response import BrandRetrieveByNameResponse from ..types.brand_retrieve_by_email_response import BrandRetrieveByEmailResponse from ..types.brand_retrieve_by_ticker_response import BrandRetrieveByTickerResponse @@ -515,6 +517,122 @@ def retrieve_by_email( cast_to=BrandRetrieveByEmailResponse, ) + def retrieve_by_isin( + self, + *, + isin: str, + force_language: Literal[ + "albanian", + "arabic", + "azeri", + "bengali", + "bulgarian", + "cebuano", + "croatian", + "czech", + "danish", + "dutch", + "english", + "estonian", + "farsi", + "finnish", + "french", + "german", + "hausa", + "hawaiian", + "hindi", + "hungarian", + "icelandic", + "indonesian", + "italian", + "kazakh", + "kyrgyz", + "latin", + "latvian", + "lithuanian", + "macedonian", + "mongolian", + "nepali", + "norwegian", + "pashto", + "pidgin", + "polish", + "portuguese", + "romanian", + "russian", + "serbian", + "slovak", + "slovene", + "somali", + "spanish", + "swahili", + "swedish", + "tagalog", + "turkish", + "ukrainian", + "urdu", + "uzbek", + "vietnamese", + "welsh", + ] + | Omit = omit, + max_speed: bool | Omit = omit, + timeout_ms: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BrandRetrieveByIsinResponse: + """ + Retrieve brand information using an ISIN (International Securities + Identification Number). This endpoint looks up the company associated with the + ISIN and returns its brand data. + + Args: + isin: ISIN (International Securities Identification Number) to retrieve brand data for + (e.g., 'AU000000IMD5', 'US0378331005'). Must be exactly 12 characters: 2 letters + followed by 9 alphanumeric characters and ending with a digit. + + force_language: Optional parameter to force the language of the retrieved brand data. + + max_speed: Optional parameter to optimize the API call for maximum speed. When set to true, + the API will skip time-consuming operations for faster response at the cost of + less comprehensive data. + + timeout_ms: Optional timeout in milliseconds for the request. If the request takes longer + than this value, it will be aborted with a 408 status code. Maximum allowed + value is 300000ms (5 minutes). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/brand/retrieve-by-isin", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "isin": isin, + "force_language": force_language, + "max_speed": max_speed, + "timeout_ms": timeout_ms, + }, + brand_retrieve_by_isin_params.BrandRetrieveByIsinParams, + ), + ), + cast_to=BrandRetrieveByIsinResponse, + ) + def retrieve_by_name( self, *, @@ -1517,6 +1635,122 @@ async def retrieve_by_email( cast_to=BrandRetrieveByEmailResponse, ) + async def retrieve_by_isin( + self, + *, + isin: str, + force_language: Literal[ + "albanian", + "arabic", + "azeri", + "bengali", + "bulgarian", + "cebuano", + "croatian", + "czech", + "danish", + "dutch", + "english", + "estonian", + "farsi", + "finnish", + "french", + "german", + "hausa", + "hawaiian", + "hindi", + "hungarian", + "icelandic", + "indonesian", + "italian", + "kazakh", + "kyrgyz", + "latin", + "latvian", + "lithuanian", + "macedonian", + "mongolian", + "nepali", + "norwegian", + "pashto", + "pidgin", + "polish", + "portuguese", + "romanian", + "russian", + "serbian", + "slovak", + "slovene", + "somali", + "spanish", + "swahili", + "swedish", + "tagalog", + "turkish", + "ukrainian", + "urdu", + "uzbek", + "vietnamese", + "welsh", + ] + | Omit = omit, + max_speed: bool | Omit = omit, + timeout_ms: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BrandRetrieveByIsinResponse: + """ + Retrieve brand information using an ISIN (International Securities + Identification Number). This endpoint looks up the company associated with the + ISIN and returns its brand data. + + Args: + isin: ISIN (International Securities Identification Number) to retrieve brand data for + (e.g., 'AU000000IMD5', 'US0378331005'). Must be exactly 12 characters: 2 letters + followed by 9 alphanumeric characters and ending with a digit. + + force_language: Optional parameter to force the language of the retrieved brand data. + + max_speed: Optional parameter to optimize the API call for maximum speed. When set to true, + the API will skip time-consuming operations for faster response at the cost of + less comprehensive data. + + timeout_ms: Optional timeout in milliseconds for the request. If the request takes longer + than this value, it will be aborted with a 408 status code. Maximum allowed + value is 300000ms (5 minutes). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/brand/retrieve-by-isin", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "isin": isin, + "force_language": force_language, + "max_speed": max_speed, + "timeout_ms": timeout_ms, + }, + brand_retrieve_by_isin_params.BrandRetrieveByIsinParams, + ), + ), + cast_to=BrandRetrieveByIsinResponse, + ) + async def retrieve_by_name( self, *, @@ -2069,6 +2303,9 @@ def __init__(self, brand: BrandResource) -> None: self.retrieve_by_email = to_raw_response_wrapper( brand.retrieve_by_email, ) + self.retrieve_by_isin = to_raw_response_wrapper( + brand.retrieve_by_isin, + ) self.retrieve_by_name = to_raw_response_wrapper( brand.retrieve_by_name, ) @@ -2108,6 +2345,9 @@ def __init__(self, brand: AsyncBrandResource) -> None: self.retrieve_by_email = async_to_raw_response_wrapper( brand.retrieve_by_email, ) + self.retrieve_by_isin = async_to_raw_response_wrapper( + brand.retrieve_by_isin, + ) self.retrieve_by_name = async_to_raw_response_wrapper( brand.retrieve_by_name, ) @@ -2147,6 +2387,9 @@ def __init__(self, brand: BrandResource) -> None: self.retrieve_by_email = to_streamed_response_wrapper( brand.retrieve_by_email, ) + self.retrieve_by_isin = to_streamed_response_wrapper( + brand.retrieve_by_isin, + ) self.retrieve_by_name = to_streamed_response_wrapper( brand.retrieve_by_name, ) @@ -2186,6 +2429,9 @@ def __init__(self, brand: AsyncBrandResource) -> None: self.retrieve_by_email = async_to_streamed_response_wrapper( brand.retrieve_by_email, ) + self.retrieve_by_isin = async_to_streamed_response_wrapper( + brand.retrieve_by_isin, + ) self.retrieve_by_name = async_to_streamed_response_wrapper( brand.retrieve_by_name, ) diff --git a/src/brand/dev/types/__init__.py b/src/brand/dev/types/__init__.py index bd36eb4..eae55c9 100644 --- a/src/brand/dev/types/__init__.py +++ b/src/brand/dev/types/__init__.py @@ -13,9 +13,11 @@ from .brand_screenshot_response import BrandScreenshotResponse as BrandScreenshotResponse from .brand_styleguide_response import BrandStyleguideResponse as BrandStyleguideResponse from .brand_retrieve_naics_params import BrandRetrieveNaicsParams as BrandRetrieveNaicsParams +from .brand_retrieve_by_isin_params import BrandRetrieveByIsinParams as BrandRetrieveByIsinParams from .brand_retrieve_by_name_params import BrandRetrieveByNameParams as BrandRetrieveByNameParams from .brand_retrieve_naics_response import BrandRetrieveNaicsResponse as BrandRetrieveNaicsResponse from .brand_retrieve_by_email_params import BrandRetrieveByEmailParams as BrandRetrieveByEmailParams +from .brand_retrieve_by_isin_response import BrandRetrieveByIsinResponse as BrandRetrieveByIsinResponse from .brand_retrieve_by_name_response import BrandRetrieveByNameResponse as BrandRetrieveByNameResponse from .brand_retrieve_by_ticker_params import BrandRetrieveByTickerParams as BrandRetrieveByTickerParams from .brand_retrieve_by_email_response import BrandRetrieveByEmailResponse as BrandRetrieveByEmailResponse diff --git a/src/brand/dev/types/brand_retrieve_by_isin_params.py b/src/brand/dev/types/brand_retrieve_by_isin_params.py new file mode 100644 index 0000000..db2731b --- /dev/null +++ b/src/brand/dev/types/brand_retrieve_by_isin_params.py @@ -0,0 +1,88 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["BrandRetrieveByIsinParams"] + + +class BrandRetrieveByIsinParams(TypedDict, total=False): + isin: Required[str] + """ + ISIN (International Securities Identification Number) to retrieve brand data for + (e.g., 'AU000000IMD5', 'US0378331005'). Must be exactly 12 characters: 2 letters + followed by 9 alphanumeric characters and ending with a digit. + """ + + force_language: Literal[ + "albanian", + "arabic", + "azeri", + "bengali", + "bulgarian", + "cebuano", + "croatian", + "czech", + "danish", + "dutch", + "english", + "estonian", + "farsi", + "finnish", + "french", + "german", + "hausa", + "hawaiian", + "hindi", + "hungarian", + "icelandic", + "indonesian", + "italian", + "kazakh", + "kyrgyz", + "latin", + "latvian", + "lithuanian", + "macedonian", + "mongolian", + "nepali", + "norwegian", + "pashto", + "pidgin", + "polish", + "portuguese", + "romanian", + "russian", + "serbian", + "slovak", + "slovene", + "somali", + "spanish", + "swahili", + "swedish", + "tagalog", + "turkish", + "ukrainian", + "urdu", + "uzbek", + "vietnamese", + "welsh", + ] + """Optional parameter to force the language of the retrieved brand data.""" + + max_speed: Annotated[bool, PropertyInfo(alias="maxSpeed")] + """Optional parameter to optimize the API call for maximum speed. + + When set to true, the API will skip time-consuming operations for faster + response at the cost of less comprehensive data. + """ + + timeout_ms: Annotated[int, PropertyInfo(alias="timeoutMS")] + """Optional timeout in milliseconds for the request. + + If the request takes longer than this value, it will be aborted with a 408 + status code. Maximum allowed value is 300000ms (5 minutes). + """ diff --git a/src/brand/dev/types/brand_retrieve_by_isin_response.py b/src/brand/dev/types/brand_retrieve_by_isin_response.py new file mode 100644 index 0000000..a9bbe4d --- /dev/null +++ b/src/brand/dev/types/brand_retrieve_by_isin_response.py @@ -0,0 +1,481 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = [ + "BrandRetrieveByIsinResponse", + "Brand", + "BrandAddress", + "BrandBackdrop", + "BrandBackdropColor", + "BrandBackdropResolution", + "BrandColor", + "BrandIndustries", + "BrandIndustriesEic", + "BrandLinks", + "BrandLogo", + "BrandLogoColor", + "BrandLogoResolution", + "BrandSocial", + "BrandStock", +] + + +class BrandAddress(BaseModel): + city: Optional[str] = None + """City name""" + + country: Optional[str] = None + """Country name""" + + country_code: Optional[str] = None + """Country code""" + + postal_code: Optional[str] = None + """Postal or ZIP code""" + + state_code: Optional[str] = None + """State or province code""" + + state_province: Optional[str] = None + """State or province name""" + + street: Optional[str] = None + """Street address""" + + +class BrandBackdropColor(BaseModel): + hex: Optional[str] = None + """Color in hexadecimal format""" + + name: Optional[str] = None + """Name of the color""" + + +class BrandBackdropResolution(BaseModel): + aspect_ratio: Optional[float] = None + """Aspect ratio of the image (width/height)""" + + height: Optional[int] = None + """Height of the image in pixels""" + + width: Optional[int] = None + """Width of the image in pixels""" + + +class BrandBackdrop(BaseModel): + colors: Optional[List[BrandBackdropColor]] = None + """Array of colors in the backdrop image""" + + resolution: Optional[BrandBackdropResolution] = None + """Resolution of the backdrop image""" + + url: Optional[str] = None + """URL of the backdrop image""" + + +class BrandColor(BaseModel): + hex: Optional[str] = None + """Color in hexadecimal format""" + + name: Optional[str] = None + """Name of the color""" + + +class BrandIndustriesEic(BaseModel): + industry: Literal[ + "Aerospace & Defense", + "Technology", + "Finance", + "Healthcare", + "Retail & E-commerce", + "Entertainment", + "Education", + "Government & Nonprofit", + "Industrial & Energy", + "Automotive & Transportation", + "Lifestyle & Leisure", + "Luxury & Fashion", + "News & Media", + "Sports", + "Real Estate & PropTech", + "Legal & Compliance", + "Telecommunications", + "Agriculture & Food", + "Professional Services & Agencies", + "Chemicals & Materials", + "Logistics & Supply Chain", + "Hospitality & Tourism", + "Construction & Built Environment", + "Consumer Packaged Goods (CPG)", + ] + """Industry classification enum""" + + subindustry: Literal[ + "Defense Systems & Military Hardware", + "Aerospace Manufacturing", + "Avionics & Navigation Technology", + "Subsea & Naval Defense Systems", + "Space & Satellite Technology", + "Defense IT & Systems Integration", + "Software (B2B)", + "Software (B2C)", + "Cloud Infrastructure & DevOps", + "Cybersecurity", + "Artificial Intelligence & Machine Learning", + "Data Infrastructure & Analytics", + "Hardware & Semiconductors", + "Fintech Infrastructure", + "eCommerce & Marketplace Platforms", + "Developer Tools & APIs", + "Web3 & Blockchain", + "XR & Spatial Computing", + "Banking & Lending", + "Investment Management & WealthTech", + "Insurance & InsurTech", + "Payments & Money Movement", + "Accounting, Tax & Financial Planning Tools", + "Capital Markets & Trading Platforms", + "Financial Infrastructure & APIs", + "Credit Scoring & Risk Management", + "Cryptocurrency & Digital Assets", + "BNPL & Alternative Financing", + "Healthcare Providers & Services", + "Pharmaceuticals & Drug Development", + "Medical Devices & Diagnostics", + "Biotechnology & Genomics", + "Digital Health & Telemedicine", + "Health Insurance & Benefits Tech", + "Clinical Trials & Research Platforms", + "Mental Health & Wellness", + "Healthcare IT & EHR Systems", + "Consumer Health & Wellness Products", + "Online Marketplaces", + "Direct-to-Consumer (DTC) Brands", + "Retail Tech & Point-of-Sale Systems", + "Omnichannel & In-Store Retail", + "E-commerce Enablement & Infrastructure", + "Subscription & Membership Commerce", + "Social Commerce & Influencer Platforms", + "Fashion & Apparel Retail", + "Food, Beverage & Grocery E-commerce", + "Streaming Platforms (Video, Music, Audio)", + "Gaming & Interactive Entertainment", + "Creator Economy & Influencer Platforms", + "Advertising, Adtech & Media Buying", + "Film, TV & Production Studios", + "Events, Venues & Live Entertainment", + "Virtual Worlds & Metaverse Experiences", + "K-12 Education Platforms & Tools", + "Higher Education & University Tech", + "Online Learning & MOOCs", + "Test Prep & Certification", + "Corporate Training & Upskilling", + "Tutoring & Supplemental Learning", + "Education Management Systems (LMS/SIS)", + "Language Learning", + "Creator-Led & Cohort-Based Courses", + "Special Education & Accessibility Tools", + "Government Technology & Digital Services", + "Civic Engagement & Policy Platforms", + "International Development & Humanitarian Aid", + "Philanthropy & Grantmaking", + "Nonprofit Operations & Fundraising Tools", + "Public Health & Social Services", + "Education & Youth Development Programs", + "Environmental & Climate Action Organizations", + "Legal Aid & Social Justice Advocacy", + "Municipal & Infrastructure Services", + "Manufacturing & Industrial Automation", + "Energy Production (Oil, Gas, Nuclear)", + "Renewable Energy & Cleantech", + "Utilities & Grid Infrastructure", + "Industrial IoT & Monitoring Systems", + "Construction & Heavy Equipment", + "Mining & Natural Resources", + "Environmental Engineering & Sustainability", + "Energy Storage & Battery Technology", + "Automotive OEMs & Vehicle Manufacturing", + "Electric Vehicles (EVs) & Charging Infrastructure", + "Mobility-as-a-Service (MaaS)", + "Fleet Management", + "Public Transit & Urban Mobility", + "Autonomous Vehicles & ADAS", + "Aftermarket Parts & Services", + "Telematics & Vehicle Connectivity", + "Aviation & Aerospace Transport", + "Maritime Shipping", + "Fitness & Wellness", + "Beauty & Personal Care", + "Home & Living", + "Dating & Relationships", + "Hobbies, Crafts & DIY", + "Outdoor & Recreational Gear", + "Events, Experiences & Ticketing Platforms", + "Designer & Luxury Apparel", + "Accessories, Jewelry & Watches", + "Footwear & Leather Goods", + "Beauty, Fragrance & Skincare", + "Fashion Marketplaces & Retail Platforms", + "Sustainable & Ethical Fashion", + "Resale, Vintage & Circular Fashion", + "Fashion Tech & Virtual Try-Ons", + "Streetwear & Emerging Luxury", + "Couture & Made-to-Measure", + "News Publishing & Journalism", + "Digital Media & Content Platforms", + "Broadcasting (TV & Radio)", + "Podcasting & Audio Media", + "News Aggregators & Curation Tools", + "Independent & Creator-Led Media", + "Newsletters & Substack-Style Platforms", + "Political & Investigative Media", + "Trade & Niche Publications", + "Media Monitoring & Analytics", + "Professional Teams & Leagues", + "Sports Media & Broadcasting", + "Sports Betting & Fantasy Sports", + "Fitness & Athletic Training Platforms", + "Sportswear & Equipment", + "Esports & Competitive Gaming", + "Sports Venues & Event Management", + "Athlete Management & Talent Agencies", + "Sports Tech & Performance Analytics", + "Youth, Amateur & Collegiate Sports", + "Real Estate Marketplaces", + "Property Management Software", + "Rental Platforms", + "Mortgage & Lending Tech", + "Real Estate Investment Platforms", + "Law Firms & Legal Services", + "Legal Tech & Automation", + "Regulatory Compliance", + "E-Discovery & Litigation Tools", + "Contract Management", + "Governance, Risk & Compliance (GRC)", + "IP & Trademark Management", + "Legal Research & Intelligence", + "Compliance Training & Certification", + "Whistleblower & Ethics Reporting", + "Mobile & Wireless Networks (3G/4G/5G)", + "Broadband & Fiber Internet", + "Satellite & Space-Based Communications", + "Network Equipment & Infrastructure", + "Telecom Billing & OSS/BSS Systems", + "VoIP & Unified Communications", + "Internet Service Providers (ISPs)", + "Edge Computing & Network Virtualization", + "IoT Connectivity Platforms", + "Precision Agriculture & AgTech", + "Crop & Livestock Production", + "Food & Beverage Manufacturing & Processing", + "Food Distribution", + "Restaurants & Food Service", + "Agricultural Inputs & Equipment", + "Sustainable & Regenerative Agriculture", + "Seafood & Aquaculture", + "Management Consulting", + "Marketing & Advertising Agencies", + "Design, Branding & Creative Studios", + "IT Services & Managed Services", + "Staffing, Recruiting & Talent", + "Accounting & Tax Firms", + "Public Relations & Communications", + "Business Process Outsourcing (BPO)", + "Professional Training & Coaching", + "Specialty Chemicals", + "Commodity & Petrochemicals", + "Polymers, Plastics & Rubber", + "Coatings, Adhesives & Sealants", + "Industrial Gases", + "Advanced Materials & Composites", + "Battery Materials & Energy Storage", + "Electronic Materials & Semiconductor Chemicals", + "Agrochemicals & Fertilizers", + "Freight & Transportation Tech", + "Last-Mile Delivery", + "Warehouse Automation", + "Supply Chain Visibility Platforms", + "Logistics Marketplaces", + "Shipping & Freight Forwarding", + "Cold Chain Logistics", + "Reverse Logistics & Returns", + "Cross-Border Trade Tech", + "Transportation Management Systems (TMS)", + "Hotels & Accommodation", + "Vacation Rentals & Short-Term Stays", + "Restaurant Tech & Management", + "Travel Booking Platforms", + "Tourism Experiences & Activities", + "Cruise Lines & Marine Tourism", + "Hospitality Management Systems", + "Event & Venue Management", + "Corporate Travel Management", + "Travel Insurance & Protection", + "Construction Management Software", + "BIM/CAD & Design Tools", + "Construction Marketplaces", + "Equipment Rental & Management", + "Building Materials & Procurement", + "Construction Workforce Management", + "Project Estimation & Bidding", + "Modular & Prefab Construction", + "Construction Safety & Compliance", + "Smart Building Technology", + "Food & Beverage CPG", + "Home & Personal Care CPG", + "CPG Analytics & Insights", + "Direct-to-Consumer CPG Brands", + "CPG Supply Chain & Distribution", + "Private Label Manufacturing", + "CPG Retail Intelligence", + "Sustainable CPG & Packaging", + "Beauty & Cosmetics CPG", + "Health & Wellness CPG", + ] + """Subindustry classification enum""" + + +class BrandIndustries(BaseModel): + eic: Optional[List[BrandIndustriesEic]] = None + """Easy Industry Classification - array of industry and subindustry pairs""" + + +class BrandLinks(BaseModel): + blog: Optional[str] = None + """URL to the brand's blog or news page""" + + careers: Optional[str] = None + """URL to the brand's careers or job opportunities page""" + + contact: Optional[str] = None + """URL to the brand's contact or contact us page""" + + pricing: Optional[str] = None + """URL to the brand's pricing or plans page""" + + privacy: Optional[str] = None + """URL to the brand's privacy policy page""" + + terms: Optional[str] = None + """URL to the brand's terms of service or terms and conditions page""" + + +class BrandLogoColor(BaseModel): + hex: Optional[str] = None + """Color in hexadecimal format""" + + name: Optional[str] = None + """Name of the color""" + + +class BrandLogoResolution(BaseModel): + aspect_ratio: Optional[float] = None + """Aspect ratio of the image (width/height)""" + + height: Optional[int] = None + """Height of the image in pixels""" + + width: Optional[int] = None + """Width of the image in pixels""" + + +class BrandLogo(BaseModel): + colors: Optional[List[BrandLogoColor]] = None + """Array of colors in the logo""" + + mode: Optional[Literal["light", "dark", "has_opaque_background"]] = None + """ + Indicates when this logo is best used: 'light' = best for light mode, 'dark' = + best for dark mode, 'has_opaque_background' = can be used for either as image + has its own background + """ + + resolution: Optional[BrandLogoResolution] = None + """Resolution of the logo image""" + + type: Optional[Literal["icon", "logo"]] = None + """Type of the logo based on resolution (e.g., 'icon', 'logo')""" + + url: Optional[str] = None + """CDN hosted url of the logo (ready for display)""" + + +class BrandSocial(BaseModel): + type: Optional[str] = None + """Type of social media, e.g., 'facebook', 'twitter'""" + + url: Optional[str] = None + """URL of the social media page""" + + +class BrandStock(BaseModel): + exchange: Optional[str] = None + """Stock exchange name""" + + ticker: Optional[str] = None + """Stock ticker symbol""" + + +class Brand(BaseModel): + address: Optional[BrandAddress] = None + """Physical address of the brand""" + + backdrops: Optional[List[BrandBackdrop]] = None + """An array of backdrop images for the brand""" + + colors: Optional[List[BrandColor]] = None + """An array of brand colors""" + + description: Optional[str] = None + """A brief description of the brand""" + + domain: Optional[str] = None + """The domain name of the brand""" + + email: Optional[str] = None + """Company email address""" + + industries: Optional[BrandIndustries] = None + """Industry classification information for the brand""" + + is_nsfw: Optional[bool] = None + """Indicates whether the brand content is not safe for work (NSFW)""" + + links: Optional[BrandLinks] = None + """Important website links for the brand""" + + logos: Optional[List[BrandLogo]] = None + """An array of logos associated with the brand""" + + phone: Optional[str] = None + """Company phone number""" + + slogan: Optional[str] = None + """The brand's slogan""" + + socials: Optional[List[BrandSocial]] = None + """An array of social media links for the brand""" + + stock: Optional[BrandStock] = None + """ + Stock market information for this brand (will be null if not a publicly traded + company) + """ + + title: Optional[str] = None + """The title or name of the brand""" + + +class BrandRetrieveByIsinResponse(BaseModel): + brand: Optional[Brand] = None + """Detailed brand information""" + + code: Optional[int] = None + """HTTP status code""" + + status: Optional[str] = None + """Status of the response, e.g., 'ok'""" diff --git a/tests/api_resources/test_brand.py b/tests/api_resources/test_brand.py index 7dfc647..2963c49 100644 --- a/tests/api_resources/test_brand.py +++ b/tests/api_resources/test_brand.py @@ -16,6 +16,7 @@ BrandScreenshotResponse, BrandStyleguideResponse, BrandRetrieveNaicsResponse, + BrandRetrieveByIsinResponse, BrandRetrieveByNameResponse, BrandRetrieveByEmailResponse, BrandRetrieveByTickerResponse, @@ -286,6 +287,51 @@ def test_streaming_response_retrieve_by_email(self, client: BrandDev) -> None: assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve_by_isin(self, client: BrandDev) -> None: + brand = client.brand.retrieve_by_isin( + isin="SE60513A9993", + ) + assert_matches_type(BrandRetrieveByIsinResponse, brand, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve_by_isin_with_all_params(self, client: BrandDev) -> None: + brand = client.brand.retrieve_by_isin( + isin="SE60513A9993", + force_language="albanian", + max_speed=True, + timeout_ms=1, + ) + assert_matches_type(BrandRetrieveByIsinResponse, brand, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve_by_isin(self, client: BrandDev) -> None: + response = client.brand.with_raw_response.retrieve_by_isin( + isin="SE60513A9993", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + brand = response.parse() + assert_matches_type(BrandRetrieveByIsinResponse, brand, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve_by_isin(self, client: BrandDev) -> None: + with client.brand.with_streaming_response.retrieve_by_isin( + isin="SE60513A9993", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + brand = response.parse() + assert_matches_type(BrandRetrieveByIsinResponse, brand, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve_by_name(self, client: BrandDev) -> None: @@ -815,6 +861,51 @@ async def test_streaming_response_retrieve_by_email(self, async_client: AsyncBra assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve_by_isin(self, async_client: AsyncBrandDev) -> None: + brand = await async_client.brand.retrieve_by_isin( + isin="SE60513A9993", + ) + assert_matches_type(BrandRetrieveByIsinResponse, brand, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve_by_isin_with_all_params(self, async_client: AsyncBrandDev) -> None: + brand = await async_client.brand.retrieve_by_isin( + isin="SE60513A9993", + force_language="albanian", + max_speed=True, + timeout_ms=1, + ) + assert_matches_type(BrandRetrieveByIsinResponse, brand, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve_by_isin(self, async_client: AsyncBrandDev) -> None: + response = await async_client.brand.with_raw_response.retrieve_by_isin( + isin="SE60513A9993", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + brand = await response.parse() + assert_matches_type(BrandRetrieveByIsinResponse, brand, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve_by_isin(self, async_client: AsyncBrandDev) -> None: + async with async_client.brand.with_streaming_response.retrieve_by_isin( + isin="SE60513A9993", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + brand = await response.parse() + assert_matches_type(BrandRetrieveByIsinResponse, brand, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve_by_name(self, async_client: AsyncBrandDev) -> None: diff --git a/tests/test_models.py b/tests/test_models.py index 44e5d4c..66e3565 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -9,7 +9,7 @@ from brand.dev._utils import PropertyInfo from brand.dev._compat import PYDANTIC_V1, parse_obj, model_dump, model_json -from brand.dev._models import BaseModel, construct_type +from brand.dev._models import DISCRIMINATOR_CACHE, BaseModel, construct_type class BasicModel(BaseModel): @@ -809,7 +809,7 @@ class B(BaseModel): UnionType = cast(Any, Union[A, B]) - assert not hasattr(UnionType, "__discriminator__") + assert not DISCRIMINATOR_CACHE.get(UnionType) m = construct_type( value={"type": "b", "data": "foo"}, type_=cast(Any, Annotated[UnionType, PropertyInfo(discriminator="type")]) @@ -818,7 +818,7 @@ class B(BaseModel): assert m.type == "b" assert m.data == "foo" # type: ignore[comparison-overlap] - discriminator = UnionType.__discriminator__ + discriminator = DISCRIMINATOR_CACHE.get(UnionType) assert discriminator is not None m = construct_type( @@ -830,7 +830,7 @@ class B(BaseModel): # if the discriminator details object stays the same between invocations then # we hit the cache - assert UnionType.__discriminator__ is discriminator + assert DISCRIMINATOR_CACHE.get(UnionType) is discriminator @pytest.mark.skipif(PYDANTIC_V1, reason="TypeAliasType is not supported in Pydantic v1")