diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 999cbd58..6b0fe76f 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "3.4.0-alpha" + ".": "3.5.0-alpha" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 46bb67dd..baaeb088 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 801 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/telnyx%2Ftelnyx-9b2e42d595d8704628cb792563adef970f306e60f81b682f41747f0e44321d2a.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/telnyx%2Ftelnyx-d5da60e6c0a34ecb5e91fc3e232836c75e32cdc1c1f348e6b12d88e8c1d77aea.yml openapi_spec_hash: c78b6c372d7b9a5d8735956b67e724ce -config_hash: 8edc14bfa964b5eeacf9529eec7991f1 +config_hash: 063a2550de7ae6c1b99a62bbd6a1a6ac diff --git a/CHANGELOG.md b/CHANGELOG.md index b162fb6f..1f256b1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 3.5.0-alpha (2025-09-17) + +Full Changelog: [v3.4.0-alpha...v3.5.0-alpha](https://github.com/team-telnyx/telnyx-python/compare/v3.4.0-alpha...v3.5.0-alpha) + +### Features + +* **api:** manual updates ([d4fd511](https://github.com/team-telnyx/telnyx-python/commit/d4fd5118081b91c12d3bf2a77c0ba1f12a7a04cb)) +* **api:** manual updates ([0eb047b](https://github.com/team-telnyx/telnyx-python/commit/0eb047bc54d338569aa15df91c1ebf7f741f3ec8)) +* **api:** rename enums with problematic characters ([1334c75](https://github.com/team-telnyx/telnyx-python/commit/1334c755da81fe115f35af23fceb1064fd3c2bc5)) + + +### Chores + +* **internal:** update pydantic dependency ([09a43dc](https://github.com/team-telnyx/telnyx-python/commit/09a43dc632831b1d7ae2f571a8d426cb57422526)) + ## 3.4.0-alpha (2025-09-09) Full Changelog: [v3.3.0-alpha...v3.4.0-alpha](https://github.com/team-telnyx/telnyx-python/compare/v3.3.0-alpha...v3.4.0-alpha) diff --git a/README.md b/README.md index 91f34f57..69be404e 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,13 @@ client = Telnyx( api_key=os.environ.get("TELNYX_API_KEY"), # This is the default and can be omitted ) -response = client.list_buckets() -print(response.buckets) +response = client.calls.dial( + connection_id="conn12345", + from_="+15557654321", + to="+15551234567", + webhook_url="https://your-webhook.url/events", +) +print(response.data) ``` While you can provide an `api_key` keyword argument, @@ -56,8 +61,13 @@ client = AsyncTelnyx( async def main() -> None: - response = await client.list_buckets() - print(response.buckets) + response = await client.calls.dial( + connection_id="conn12345", + from_="+15557654321", + to="+15551234567", + webhook_url="https://your-webhook.url/events", + ) + print(response.data) asyncio.run(main()) @@ -89,8 +99,13 @@ async def main() -> None: api_key="My API Key", http_client=DefaultAioHttpClient(), ) as client: - response = await client.list_buckets() - print(response.buckets) + response = await client.calls.dial( + connection_id="conn12345", + from_="+15557654321", + to="+15551234567", + webhook_url="https://your-webhook.url/events", + ) + print(response.data) asyncio.run(main()) @@ -114,10 +129,24 @@ from telnyx import Telnyx client = Telnyx() -access_ip_addresses = client.access_ip_address.list( - filter={}, +response = client.calls.dial( + connection_id="7267xxxxxxxxxxxxxx", + from_="+18005550101", + to="+18005550100 or sip:username@sip.telnyx.com", + answering_machine_detection_config={ + "after_greeting_silence_millis": 1000, + "between_words_silence_millis": 1000, + "greeting_duration_millis": 1000, + "greeting_silence_duration_millis": 2000, + "greeting_total_analysis_time_millis": 50000, + "initial_silence_millis": 1000, + "maximum_number_of_words": 1000, + "maximum_word_length_millis": 2000, + "silence_threshold": 512, + "total_analysis_time_millis": 5000, + }, ) -print(access_ip_addresses.filter) +print(response.answering_machine_detection_config) ``` ## File uploads @@ -154,7 +183,9 @@ from telnyx import Telnyx client = Telnyx() try: - client.list_buckets() + client.number_orders.create( + phone_numbers=[{"phone_number": "+15558675309"}], + ) except telnyx.APIConnectionError as e: print("The server could not be reached") print(e.__cause__) # an underlying Exception, likely raised within httpx. @@ -197,7 +228,9 @@ client = Telnyx( ) # Or, configure per-request: -client.with_options(max_retries=5).list_buckets() +client.with_options(max_retries=5).number_orders.create( + phone_numbers=[{"phone_number": "+15558675309"}], +) ``` ### Timeouts @@ -220,7 +253,9 @@ client = Telnyx( ) # Override per-request: -client.with_options(timeout=5.0).list_buckets() +client.with_options(timeout=5.0).number_orders.create( + phone_numbers=[{"phone_number": "+15558675309"}], +) ``` On timeout, an `APITimeoutError` is thrown. @@ -261,11 +296,15 @@ The "raw" Response object can be accessed by prefixing `.with_raw_response.` to from telnyx import Telnyx client = Telnyx() -response = client.with_raw_response.list_buckets() +response = client.number_orders.with_raw_response.create( + phone_numbers=[{ + "phone_number": "+15558675309" + }], +) print(response.headers.get('X-My-Header')) -client = response.parse() # get the object that `list_buckets()` would have returned -print(client.buckets) +number_order = response.parse() # get the object that `number_orders.create()` would have returned +print(number_order.data) ``` These methods return an [`APIResponse`](https://github.com/team-telnyx/telnyx-python/tree/master/src/telnyx/_response.py) object. @@ -279,7 +318,9 @@ The above interface eagerly reads the full response body when you make the reque To stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods. ```python -with client.with_streaming_response.list_buckets() as response: +with client.number_orders.with_streaming_response.create( + phone_numbers=[{"phone_number": "+15558675309"}], +) as response: print(response.headers.get("X-My-Header")) for line in response.iter_lines(): diff --git a/pyproject.toml b/pyproject.toml index 78062eb9..1c34ad53 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "telnyx" -version = "3.4.0-alpha" +version = "3.5.0-alpha" description = "The official Python library for the telnyx API" dynamic = ["readme"] license = "MIT" diff --git a/requirements-dev.lock b/requirements-dev.lock index 3e8ff321..b9b4f0d7 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -88,9 +88,9 @@ pluggy==1.5.0 propcache==0.3.1 # via aiohttp # via yarl -pydantic==2.10.3 +pydantic==2.11.9 # via telnyx -pydantic-core==2.27.1 +pydantic-core==2.33.2 # via pydantic pygments==2.18.0 # via rich @@ -126,6 +126,9 @@ typing-extensions==4.12.2 # via pydantic-core # via pyright # via telnyx + # via typing-inspection +typing-inspection==0.4.1 + # via pydantic virtualenv==20.24.5 # via nox yarl==1.20.0 diff --git a/requirements.lock b/requirements.lock index e5b822d9..93208037 100644 --- a/requirements.lock +++ b/requirements.lock @@ -55,9 +55,9 @@ multidict==6.4.4 propcache==0.3.1 # via aiohttp # via yarl -pydantic==2.10.3 +pydantic==2.11.9 # via telnyx -pydantic-core==2.27.1 +pydantic-core==2.33.2 # via pydantic sniffio==1.3.0 # via anyio @@ -68,5 +68,8 @@ typing-extensions==4.12.2 # via pydantic # via pydantic-core # via telnyx + # via typing-inspection +typing-inspection==0.4.1 + # via pydantic yarl==1.20.0 # via aiohttp diff --git a/src/telnyx/_models.py b/src/telnyx/_models.py index 3a6017ef..6a3cd1d2 100644 --- a/src/telnyx/_models.py +++ b/src/telnyx/_models.py @@ -256,7 +256,7 @@ def model_dump( mode: Literal["json", "python"] | str = "python", include: IncEx | None = None, exclude: IncEx | None = None, - by_alias: bool = False, + by_alias: bool | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, @@ -264,6 +264,7 @@ def model_dump( warnings: bool | Literal["none", "warn", "error"] = True, context: dict[str, Any] | None = None, serialize_as_any: bool = False, + fallback: Callable[[Any], Any] | None = None, ) -> dict[str, Any]: """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump @@ -295,10 +296,12 @@ def model_dump( raise ValueError("context is only supported in Pydantic v2") if serialize_as_any != False: 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") dumped = super().dict( # pyright: ignore[reportDeprecated] include=include, exclude=exclude, - by_alias=by_alias, + by_alias=by_alias if by_alias is not None else False, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, exclude_none=exclude_none, @@ -313,13 +316,14 @@ def model_dump_json( indent: int | None = None, include: IncEx | None = None, exclude: IncEx | None = None, - by_alias: bool = False, + by_alias: bool | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: 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: """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump_json @@ -348,11 +352,13 @@ def model_dump_json( raise ValueError("context is only supported in Pydantic v2") if serialize_as_any != False: 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") return super().json( # type: ignore[reportDeprecated] indent=indent, include=include, exclude=exclude, - by_alias=by_alias, + by_alias=by_alias if by_alias is not None else False, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, exclude_none=exclude_none, diff --git a/src/telnyx/_version.py b/src/telnyx/_version.py index 3c6577ed..2e2f1bb4 100644 --- a/src/telnyx/_version.py +++ b/src/telnyx/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "telnyx" -__version__ = "3.4.0-alpha" # x-release-please-version +__version__ = "3.5.0-alpha" # x-release-please-version diff --git a/src/telnyx/types/global_ip_latency_retrieve_response.py b/src/telnyx/types/global_ip_latency_retrieve_response.py index 0593af5c..d3fcdce7 100644 --- a/src/telnyx/types/global_ip_latency_retrieve_response.py +++ b/src/telnyx/types/global_ip_latency_retrieve_response.py @@ -97,19 +97,19 @@ class DataPercentileLatency_99(BaseModel): class DataPercentileLatency(BaseModel): - api_0: Optional[DataPercentileLatency_0] = FieldInfo(alias="0", default=None) + p0: Optional[DataPercentileLatency_0] = FieldInfo(alias="0", default=None) - api_100: Optional[DataPercentileLatency_100] = FieldInfo(alias="100", default=None) + p100: Optional[DataPercentileLatency_100] = FieldInfo(alias="100", default=None) - api_25: Optional[DataPercentileLatency_25] = FieldInfo(alias="25", default=None) + p25: Optional[DataPercentileLatency_25] = FieldInfo(alias="25", default=None) - api_50: Optional[DataPercentileLatency_50] = FieldInfo(alias="50", default=None) + p50: Optional[DataPercentileLatency_50] = FieldInfo(alias="50", default=None) - api_75: Optional[DataPercentileLatency_75] = FieldInfo(alias="75", default=None) + p75: Optional[DataPercentileLatency_75] = FieldInfo(alias="75", default=None) - api_90: Optional[DataPercentileLatency_90] = FieldInfo(alias="90", default=None) + p90: Optional[DataPercentileLatency_90] = FieldInfo(alias="90", default=None) - api_99: Optional[DataPercentileLatency_99] = FieldInfo(alias="99", default=None) + p99: Optional[DataPercentileLatency_99] = FieldInfo(alias="99", default=None) class DataProberLocation(BaseModel): diff --git a/tests/test_client.py b/tests/test_client.py index 14d7ce2f..3cef272e 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -712,20 +712,20 @@ def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str @mock.patch("telnyx._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, client: Telnyx) -> None: - respx_mock.get("/").mock(side_effect=httpx.TimeoutException("Test timeout error")) + respx_mock.post("/number_orders").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - client.with_streaming_response.list_buckets().__enter__() + client.number_orders.with_streaming_response.create().__enter__() assert _get_open_connections(self.client) == 0 @mock.patch("telnyx._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client: Telnyx) -> None: - respx_mock.get("/").mock(return_value=httpx.Response(500)) + respx_mock.post("/number_orders").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - client.with_streaming_response.list_buckets().__enter__() + client.number_orders.with_streaming_response.create().__enter__() assert _get_open_connections(self.client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @@ -752,9 +752,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.get("/").mock(side_effect=retry_handler) + respx_mock.post("/number_orders").mock(side_effect=retry_handler) - response = client.with_raw_response.list_buckets() + response = client.number_orders.with_raw_response.create() assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -776,9 +776,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.get("/").mock(side_effect=retry_handler) + respx_mock.post("/number_orders").mock(side_effect=retry_handler) - response = client.with_raw_response.list_buckets(extra_headers={"x-stainless-retry-count": Omit()}) + response = client.number_orders.with_raw_response.create(extra_headers={"x-stainless-retry-count": Omit()}) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @@ -799,9 +799,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.get("/").mock(side_effect=retry_handler) + respx_mock.post("/number_orders").mock(side_effect=retry_handler) - response = client.with_raw_response.list_buckets(extra_headers={"x-stainless-retry-count": "42"}) + response = client.number_orders.with_raw_response.create(extra_headers={"x-stainless-retry-count": "42"}) assert response.http_request.headers.get("x-stainless-retry-count") == "42" @@ -1527,20 +1527,20 @@ async def test_parse_retry_after_header(self, remaining_retries: int, retry_afte @mock.patch("telnyx._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, async_client: AsyncTelnyx) -> None: - respx_mock.get("/").mock(side_effect=httpx.TimeoutException("Test timeout error")) + respx_mock.post("/number_orders").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - await async_client.with_streaming_response.list_buckets().__aenter__() + await async_client.number_orders.with_streaming_response.create().__aenter__() assert _get_open_connections(self.client) == 0 @mock.patch("telnyx._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, async_client: AsyncTelnyx) -> None: - respx_mock.get("/").mock(return_value=httpx.Response(500)) + respx_mock.post("/number_orders").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - await async_client.with_streaming_response.list_buckets().__aenter__() + await async_client.number_orders.with_streaming_response.create().__aenter__() assert _get_open_connections(self.client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @@ -1568,9 +1568,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.get("/").mock(side_effect=retry_handler) + respx_mock.post("/number_orders").mock(side_effect=retry_handler) - response = await client.with_raw_response.list_buckets() + response = await client.number_orders.with_raw_response.create() assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -1593,9 +1593,11 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.get("/").mock(side_effect=retry_handler) + respx_mock.post("/number_orders").mock(side_effect=retry_handler) - response = await client.with_raw_response.list_buckets(extra_headers={"x-stainless-retry-count": Omit()}) + response = await client.number_orders.with_raw_response.create( + extra_headers={"x-stainless-retry-count": Omit()} + ) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @@ -1617,9 +1619,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.get("/").mock(side_effect=retry_handler) + respx_mock.post("/number_orders").mock(side_effect=retry_handler) - response = await client.with_raw_response.list_buckets(extra_headers={"x-stainless-retry-count": "42"}) + response = await client.number_orders.with_raw_response.create(extra_headers={"x-stainless-retry-count": "42"}) assert response.http_request.headers.get("x-stainless-retry-count") == "42"