Skip to content

Commit 9fcecd5

Browse files
chore(internal): codegen related update
1 parent f59526f commit 9fcecd5

File tree

14 files changed

+304
-264
lines changed

14 files changed

+304
-264
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<!-- prettier-ignore -->
44
[![PyPI version](https://img.shields.io/pypi/v/cloudflare.svg?label=pypi%20(stable))](https://pypi.org/project/cloudflare/)
55

6-
The Cloudflare Python library provides convenient access to the Cloudflare REST API from any Python 3.8+
6+
The Cloudflare Python library provides convenient access to the Cloudflare REST API from any Python 3.9+
77
application. The library includes type definitions for all request params and response fields,
88
and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx).
99

@@ -474,7 +474,7 @@ print(cloudflare.__version__)
474474

475475
## Requirements
476476

477-
Python 3.8 or higher.
477+
Python 3.9 or higher.
478478

479479
## Contributing
480480

api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3026,7 +3026,7 @@ Methods:
30263026

30273027
- <code title="post /zones/{zone_id}/spectrum/apps">client.spectrum.apps.<a href="./src/cloudflare/resources/spectrum/apps.py">create</a>(\*, zone_id, \*\*<a href="src/cloudflare/types/spectrum/app_create_params.py">params</a>) -> <a href="./src/cloudflare/types/spectrum/app_create_response.py">Optional[AppCreateResponse]</a></code>
30283028
- <code title="put /zones/{zone_id}/spectrum/apps/{app_id}">client.spectrum.apps.<a href="./src/cloudflare/resources/spectrum/apps.py">update</a>(app_id, \*, zone_id, \*\*<a href="src/cloudflare/types/spectrum/app_update_params.py">params</a>) -> <a href="./src/cloudflare/types/spectrum/app_update_response.py">Optional[AppUpdateResponse]</a></code>
3029-
- <code title="get /zones/{zone_id}/spectrum/apps">client.spectrum.apps.<a href="./src/cloudflare/resources/spectrum/apps.py">list</a>(\*, zone_id, \*\*<a href="src/cloudflare/types/spectrum/app_list_params.py">params</a>) -> <a href="./src/cloudflare/types/spectrum/app_list_response.py">SyncV4PagePaginationArray[Optional[AppListResponse]]</a></code>
3029+
- <code title="get /zones/{zone_id}/spectrum/apps">client.spectrum.apps.<a href="./src/cloudflare/resources/spectrum/apps.py">list</a>(\*, zone_id, \*\*<a href="src/cloudflare/types/spectrum/app_list_params.py">params</a>) -> <a href="./src/cloudflare/types/spectrum/app_list_response.py">SyncV4PagePaginationArray[AppListResponse]</a></code>
30303030
- <code title="delete /zones/{zone_id}/spectrum/apps/{app_id}">client.spectrum.apps.<a href="./src/cloudflare/resources/spectrum/apps.py">delete</a>(app_id, \*, zone_id) -> <a href="./src/cloudflare/types/spectrum/app_delete_response.py">Optional[AppDeleteResponse]</a></code>
30313031
- <code title="get /zones/{zone_id}/spectrum/apps/{app_id}">client.spectrum.apps.<a href="./src/cloudflare/resources/spectrum/apps.py">get</a>(app_id, \*, zone_id) -> <a href="./src/cloudflare/types/spectrum/app_get_response.py">Optional[AppGetResponse]</a></code>
30323032

pyproject.toml

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,16 @@ dependencies = [
1515
"distro>=1.7.0, <2",
1616
"sniffio",
1717
]
18-
requires-python = ">= 3.8"
18+
requires-python = ">= 3.9"
1919
classifiers = [
2020
"Typing :: Typed",
2121
"Intended Audience :: Developers",
22-
"Programming Language :: Python :: 3.8",
2322
"Programming Language :: Python :: 3.9",
2423
"Programming Language :: Python :: 3.10",
2524
"Programming Language :: Python :: 3.11",
2625
"Programming Language :: Python :: 3.12",
2726
"Programming Language :: Python :: 3.13",
27+
"Programming Language :: Python :: 3.14",
2828
"Operating System :: OS Independent",
2929
"Operating System :: POSIX",
3030
"Operating System :: MacOS",
@@ -39,14 +39,14 @@ Homepage = "https://github.com/cloudflare/cloudflare-python"
3939
Repository = "https://github.com/cloudflare/cloudflare-python"
4040

4141
[project.optional-dependencies]
42-
aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.8"]
42+
aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.9"]
4343

4444
[tool.rye]
4545
managed = true
4646
# version pins are in requirements-dev.lock
4747
dev-dependencies = [
4848
"pyright==1.1.399",
49-
"mypy",
49+
"mypy==1.17",
5050
"respx",
5151
"pytest",
5252
"pytest-asyncio",
@@ -142,7 +142,7 @@ filterwarnings = [
142142
# there are a couple of flags that are still disabled by
143143
# default in strict mode as they are experimental and niche.
144144
typeCheckingMode = "strict"
145-
pythonVersion = "3.8"
145+
pythonVersion = "3.9"
146146

147147
exclude = [
148148
"_dev",
@@ -225,6 +225,8 @@ select = [
225225
"B",
226226
# remove unused imports
227227
"F401",
228+
# check for missing future annotations
229+
"FA102",
228230
# bare except statements
229231
"E722",
230232
# unused arguments
@@ -247,6 +249,8 @@ unfixable = [
247249
"T203",
248250
]
249251

252+
extend-safe-fixes = ["FA102"]
253+
250254
[tool.ruff.lint.flake8-tidy-imports.banned-api]
251255
"functools.lru_cache".msg = "This function does not retain type information for the wrapped function's arguments; The `lru_cache` function from `_utils` should be used instead"
252256

requirements-dev.lock

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ httpx==0.28.1
5959
# via cloudflare
6060
# via httpx-aiohttp
6161
# via respx
62-
httpx-aiohttp==0.1.8
62+
httpx-aiohttp==0.1.9
6363
# via cloudflare
6464
idna==3.4
6565
# via anyio
@@ -75,7 +75,7 @@ mdurl==0.1.2
7575
multidict==6.4.4
7676
# via aiohttp
7777
# via yarl
78-
mypy==1.14.1
78+
mypy==1.17.0
7979
mypy-extensions==1.0.0
8080
# via mypy
8181
nodeenv==1.8.0
@@ -84,6 +84,8 @@ nox==2023.4.22
8484
packaging==23.2
8585
# via nox
8686
# via pytest
87+
pathspec==0.12.1
88+
# via mypy
8789
platformdirs==3.11.0
8890
# via virtualenv
8991
pluggy==1.5.0

requirements.lock

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ httpcore==1.0.9
4343
httpx==0.28.1
4444
# via cloudflare
4545
# via httpx-aiohttp
46-
httpx-aiohttp==0.1.8
46+
httpx-aiohttp==0.1.9
4747
# via cloudflare
4848
idna==3.4
4949
# via anyio
@@ -55,21 +55,21 @@ multidict==6.4.4
5555
propcache==0.3.1
5656
# via aiohttp
5757
# via yarl
58-
pydantic==2.11.9
58+
pydantic==2.12.5
5959
# via cloudflare
60-
pydantic-core==2.33.2
60+
pydantic-core==2.41.5
6161
# via pydantic
6262
sniffio==1.3.0
6363
# via anyio
6464
# via cloudflare
65-
typing-extensions==4.12.2
65+
typing-extensions==4.15.0
6666
# via anyio
6767
# via cloudflare
6868
# via multidict
6969
# via pydantic
7070
# via pydantic-core
7171
# via typing-inspection
72-
typing-inspection==0.4.1
72+
typing-inspection==0.4.2
7373
# via pydantic
7474
yarl==1.20.0
7575
# via aiohttp

src/cloudflare/_models.py

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import os
44
import inspect
5+
import weakref
56
from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, Optional, cast
67
from datetime import date, datetime
78
from typing_extensions import (
@@ -257,32 +258,41 @@ def model_dump(
257258
mode: Literal["json", "python"] | str = "python",
258259
include: IncEx | None = None,
259260
exclude: IncEx | None = None,
261+
context: Any | None = None,
260262
by_alias: bool | None = None,
261263
exclude_unset: bool = False,
262264
exclude_defaults: bool = False,
263265
exclude_none: bool = False,
266+
exclude_computed_fields: bool = False,
264267
round_trip: bool = False,
265268
warnings: bool | Literal["none", "warn", "error"] = True,
266-
context: dict[str, Any] | None = None,
267-
serialize_as_any: bool = False,
268269
fallback: Callable[[Any], Any] | None = None,
270+
serialize_as_any: bool = False,
269271
) -> dict[str, Any]:
270272
"""Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump
271273
272274
Generate a dictionary representation of the model, optionally specifying which fields to include or exclude.
273275
274276
Args:
275277
mode: The mode in which `to_python` should run.
276-
If mode is 'json', the dictionary will only contain JSON serializable types.
277-
If mode is 'python', the dictionary may contain any Python objects.
278-
include: A list of fields to include in the output.
279-
exclude: A list of fields to exclude from the output.
278+
If mode is 'json', the output will only contain JSON serializable types.
279+
If mode is 'python', the output may contain non-JSON-serializable Python objects.
280+
include: A set of fields to include in the output.
281+
exclude: A set of fields to exclude from the output.
282+
context: Additional context to pass to the serializer.
280283
by_alias: Whether to use the field's alias in the dictionary key if defined.
281-
exclude_unset: Whether to exclude fields that are unset or None from the output.
282-
exclude_defaults: Whether to exclude fields that are set to their default value from the output.
283-
exclude_none: Whether to exclude fields that have a value of `None` from the output.
284-
round_trip: Whether to enable serialization and deserialization round-trip support.
285-
warnings: Whether to log warnings when invalid fields are encountered.
284+
exclude_unset: Whether to exclude fields that have not been explicitly set.
285+
exclude_defaults: Whether to exclude fields that are set to their default value.
286+
exclude_none: Whether to exclude fields that have a value of `None`.
287+
exclude_computed_fields: Whether to exclude computed fields.
288+
While this can be useful for round-tripping, it is usually recommended to use the dedicated
289+
`round_trip` parameter instead.
290+
round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T].
291+
warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors,
292+
"error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError].
293+
fallback: A function to call when an unknown value is encountered. If not provided,
294+
a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised.
295+
serialize_as_any: Whether to serialize fields with duck-typing serialization behavior.
286296
287297
Returns:
288298
A dictionary representation of the model.
@@ -299,6 +309,8 @@ def model_dump(
299309
raise ValueError("serialize_as_any is only supported in Pydantic v2")
300310
if fallback is not None:
301311
raise ValueError("fallback is only supported in Pydantic v2")
312+
if exclude_computed_fields != False:
313+
raise ValueError("exclude_computed_fields is only supported in Pydantic v2")
302314
dumped = super().dict( # pyright: ignore[reportDeprecated]
303315
include=include,
304316
exclude=exclude,
@@ -315,15 +327,17 @@ def model_dump_json(
315327
self,
316328
*,
317329
indent: int | None = None,
330+
ensure_ascii: bool = False,
318331
include: IncEx | None = None,
319332
exclude: IncEx | None = None,
333+
context: Any | None = None,
320334
by_alias: bool | None = None,
321335
exclude_unset: bool = False,
322336
exclude_defaults: bool = False,
323337
exclude_none: bool = False,
338+
exclude_computed_fields: bool = False,
324339
round_trip: bool = False,
325340
warnings: bool | Literal["none", "warn", "error"] = True,
326-
context: dict[str, Any] | None = None,
327341
fallback: Callable[[Any], Any] | None = None,
328342
serialize_as_any: bool = False,
329343
) -> str:
@@ -355,6 +369,10 @@ def model_dump_json(
355369
raise ValueError("serialize_as_any is only supported in Pydantic v2")
356370
if fallback is not None:
357371
raise ValueError("fallback is only supported in Pydantic v2")
372+
if ensure_ascii != False:
373+
raise ValueError("ensure_ascii is only supported in Pydantic v2")
374+
if exclude_computed_fields != False:
375+
raise ValueError("exclude_computed_fields is only supported in Pydantic v2")
358376
return super().json( # type: ignore[reportDeprecated]
359377
indent=indent,
360378
include=include,
@@ -574,6 +592,9 @@ class CachedDiscriminatorType(Protocol):
574592
__discriminator__: DiscriminatorDetails
575593

576594

595+
DISCRIMINATOR_CACHE: weakref.WeakKeyDictionary[type, DiscriminatorDetails] = weakref.WeakKeyDictionary()
596+
597+
577598
class DiscriminatorDetails:
578599
field_name: str
579600
"""The name of the discriminator field in the variant class, e.g.
@@ -616,8 +637,9 @@ def __init__(
616637

617638

618639
def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, ...]) -> DiscriminatorDetails | None:
619-
if isinstance(union, CachedDiscriminatorType):
620-
return union.__discriminator__
640+
cached = DISCRIMINATOR_CACHE.get(union)
641+
if cached is not None:
642+
return cached
621643

622644
discriminator_field_name: str | None = None
623645

@@ -670,7 +692,7 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any,
670692
discriminator_field=discriminator_field_name,
671693
discriminator_alias=discriminator_alias,
672694
)
673-
cast(CachedDiscriminatorType, union).__discriminator__ = details
695+
DISCRIMINATOR_CACHE.setdefault(union, details)
674696
return details
675697

676698

src/cloudflare/_streaming.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,12 @@ def __stream__(self) -> Iterator[_T]:
5454
process_data = self._client._process_response_data
5555
iterator = self._iter_events()
5656

57-
for sse in iterator:
58-
yield process_data(data=sse.json(), cast_to=cast_to, response=response)
59-
60-
# Ensure the entire stream is consumed
61-
for _sse in iterator:
62-
...
57+
try:
58+
for sse in iterator:
59+
yield process_data(data=sse.json(), cast_to=cast_to, response=response)
60+
finally:
61+
# Ensure the response is closed even if the consumer doesn't read all data
62+
response.close()
6363

6464
def __enter__(self) -> Self:
6565
return self
@@ -118,12 +118,12 @@ async def __stream__(self) -> AsyncIterator[_T]:
118118
process_data = self._client._process_response_data
119119
iterator = self._iter_events()
120120

121-
async for sse in iterator:
122-
yield process_data(data=sse.json(), cast_to=cast_to, response=response)
123-
124-
# Ensure the entire stream is consumed
125-
async for _sse in iterator:
126-
...
121+
try:
122+
async for sse in iterator:
123+
yield process_data(data=sse.json(), cast_to=cast_to, response=response)
124+
finally:
125+
# Ensure the response is closed even if the consumer doesn't read all data
126+
await response.aclose()
127127

128128
async def __aenter__(self) -> Self:
129129
return self

src/cloudflare/_utils/_sync.py

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
from __future__ import annotations
22

3-
import sys
43
import asyncio
54
import functools
6-
import contextvars
7-
from typing import Any, TypeVar, Callable, Awaitable
5+
from typing import TypeVar, Callable, Awaitable
86
from typing_extensions import ParamSpec
97

108
import anyio
@@ -15,34 +13,11 @@
1513
T_ParamSpec = ParamSpec("T_ParamSpec")
1614

1715

18-
if sys.version_info >= (3, 9):
19-
_asyncio_to_thread = asyncio.to_thread
20-
else:
21-
# backport of https://docs.python.org/3/library/asyncio-task.html#asyncio.to_thread
22-
# for Python 3.8 support
23-
async def _asyncio_to_thread(
24-
func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs
25-
) -> Any:
26-
"""Asynchronously run function *func* in a separate thread.
27-
28-
Any *args and **kwargs supplied for this function are directly passed
29-
to *func*. Also, the current :class:`contextvars.Context` is propagated,
30-
allowing context variables from the main thread to be accessed in the
31-
separate thread.
32-
33-
Returns a coroutine that can be awaited to get the eventual result of *func*.
34-
"""
35-
loop = asyncio.events.get_running_loop()
36-
ctx = contextvars.copy_context()
37-
func_call = functools.partial(ctx.run, func, *args, **kwargs)
38-
return await loop.run_in_executor(None, func_call)
39-
40-
4116
async def to_thread(
4217
func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs
4318
) -> T_Retval:
4419
if sniffio.current_async_library() == "asyncio":
45-
return await _asyncio_to_thread(func, *args, **kwargs)
20+
return await asyncio.to_thread(func, *args, **kwargs)
4621

4722
return await anyio.to_thread.run_sync(
4823
functools.partial(func, *args, **kwargs),
@@ -53,10 +28,7 @@ async def to_thread(
5328
def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]:
5429
"""
5530
Take a blocking function and create an async one that receives the same
56-
positional and keyword arguments. For python version 3.9 and above, it uses
57-
asyncio.to_thread to run the function in a separate thread. For python version
58-
3.8, it uses locally defined copy of the asyncio.to_thread function which was
59-
introduced in python 3.9.
31+
positional and keyword arguments.
6032
6133
Usage:
6234

src/cloudflare/_utils/_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ def is_given(obj: _T | NotGiven | Omit) -> TypeGuard[_T]:
133133
# Type safe methods for narrowing types with TypeVars.
134134
# The default narrowing for isinstance(obj, dict) is dict[unknown, unknown],
135135
# however this cause Pyright to rightfully report errors. As we know we don't
136-
# care about the contained types we can safely use `object` in it's place.
136+
# care about the contained types we can safely use `object` in its place.
137137
#
138138
# There are two separate functions defined, `is_*` and `is_*_t` for different use cases.
139139
# `is_*` is for when you're dealing with an unknown input

0 commit comments

Comments
 (0)