Skip to content

Commit 80d9fef

Browse files
feat(client): add support for aiohttp
1 parent ed26a10 commit 80d9fef

File tree

629 files changed

+2020
-628
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

629 files changed

+2020
-628
lines changed

README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,42 @@ asyncio.run(main())
7070

7171
Functionality between the synchronous and asynchronous clients is otherwise identical.
7272

73+
### With aiohttp
74+
75+
By default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend.
76+
77+
You can enable this by installing `aiohttp`:
78+
79+
```sh
80+
# install from PyPI
81+
pip install cloudflare[aiohttp]
82+
```
83+
84+
Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`:
85+
86+
```python
87+
import os
88+
import asyncio
89+
from cloudflare import DefaultAioHttpClient
90+
from cloudflare import AsyncCloudflare
91+
92+
93+
async def main() -> None:
94+
async with AsyncCloudflare(
95+
api_token=os.environ.get("CLOUDFLARE_API_TOKEN"), # This is the default and can be omitted
96+
http_client=DefaultAioHttpClient(),
97+
) as client:
98+
zone = await client.zones.create(
99+
account={"id": "023e105f4ecef8ad9ca31a8372d0c353"},
100+
name="example.com",
101+
type="full",
102+
)
103+
print(zone.id)
104+
105+
106+
asyncio.run(main())
107+
```
108+
73109
## Using types
74110

75111
Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like:

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ classifiers = [
3737
Homepage = "https://github.com/cloudflare/cloudflare-python"
3838
Repository = "https://github.com/cloudflare/cloudflare-python"
3939

40+
[project.optional-dependencies]
41+
aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.6"]
4042

4143
[tool.rye]
4244
managed = true

requirements-dev.lock

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,24 @@
1010
# universal: false
1111

1212
-e file:.
13+
aiohappyeyeballs==2.6.1
14+
# via aiohttp
15+
aiohttp==3.12.8
16+
# via cloudflare
17+
# via httpx-aiohttp
18+
aiosignal==1.3.2
19+
# via aiohttp
1320
annotated-types==0.6.0
1421
# via pydantic
1522
anyio==4.4.0
1623
# via cloudflare
1724
# via httpx
1825
argcomplete==3.1.2
1926
# via nox
27+
async-timeout==5.0.1
28+
# via aiohttp
29+
attrs==25.3.0
30+
# via aiohttp
2031
certifi==2023.7.22
2132
# via httpcore
2233
# via httpx
@@ -34,23 +45,33 @@ execnet==2.1.1
3445
# via pytest-xdist
3546
filelock==3.12.4
3647
# via virtualenv
48+
frozenlist==1.6.2
49+
# via aiohttp
50+
# via aiosignal
3751
h11==0.14.0
3852
# via httpcore
3953
httpcore==1.0.2
4054
# via httpx
4155
httpx==0.28.1
4256
# via cloudflare
57+
# via httpx-aiohttp
4358
# via respx
59+
httpx-aiohttp==0.1.6
60+
# via cloudflare
4461
idna==3.4
4562
# via anyio
4663
# via httpx
64+
# via yarl
4765
importlib-metadata==7.0.0
4866
iniconfig==2.0.0
4967
# via pytest
5068
markdown-it-py==3.0.0
5169
# via rich
5270
mdurl==0.1.2
5371
# via markdown-it-py
72+
multidict==6.4.4
73+
# via aiohttp
74+
# via yarl
5475
mypy==1.14.1
5576
mypy-extensions==1.0.0
5677
# via mypy
@@ -65,6 +86,9 @@ platformdirs==3.11.0
6586
# via virtualenv
6687
pluggy==1.5.0
6788
# via pytest
89+
propcache==0.3.1
90+
# via aiohttp
91+
# via yarl
6892
pydantic==2.10.3
6993
# via cloudflare
7094
pydantic-core==2.27.1
@@ -98,11 +122,14 @@ tomli==2.0.2
98122
typing-extensions==4.12.2
99123
# via anyio
100124
# via cloudflare
125+
# via multidict
101126
# via mypy
102127
# via pydantic
103128
# via pydantic-core
104129
# via pyright
105130
virtualenv==20.24.5
106131
# via nox
132+
yarl==1.20.0
133+
# via aiohttp
107134
zipp==3.17.0
108135
# via importlib-metadata

requirements.lock

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,51 @@
1010
# universal: false
1111

1212
-e file:.
13+
aiohappyeyeballs==2.6.1
14+
# via aiohttp
15+
aiohttp==3.12.8
16+
# via cloudflare
17+
# via httpx-aiohttp
18+
aiosignal==1.3.2
19+
# via aiohttp
1320
annotated-types==0.6.0
1421
# via pydantic
1522
anyio==4.4.0
1623
# via cloudflare
1724
# via httpx
25+
async-timeout==5.0.1
26+
# via aiohttp
27+
attrs==25.3.0
28+
# via aiohttp
1829
certifi==2023.7.22
1930
# via httpcore
2031
# via httpx
2132
distro==1.8.0
2233
# via cloudflare
2334
exceptiongroup==1.2.2
2435
# via anyio
36+
frozenlist==1.6.2
37+
# via aiohttp
38+
# via aiosignal
2539
h11==0.14.0
2640
# via httpcore
2741
httpcore==1.0.2
2842
# via httpx
2943
httpx==0.28.1
3044
# via cloudflare
45+
# via httpx-aiohttp
46+
httpx-aiohttp==0.1.6
47+
# via cloudflare
3148
idna==3.4
3249
# via anyio
3350
# via httpx
51+
# via yarl
52+
multidict==6.4.4
53+
# via aiohttp
54+
# via yarl
55+
propcache==0.3.1
56+
# via aiohttp
57+
# via yarl
3458
pydantic==2.10.3
3559
# via cloudflare
3660
pydantic-core==2.27.1
@@ -41,5 +65,8 @@ sniffio==1.3.0
4165
typing-extensions==4.12.2
4266
# via anyio
4367
# via cloudflare
68+
# via multidict
4469
# via pydantic
4570
# via pydantic-core
71+
yarl==1.20.0
72+
# via aiohttp

src/cloudflare/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
UnprocessableEntityError,
3737
APIResponseValidationError,
3838
)
39-
from ._base_client import DefaultHttpxClient, DefaultAsyncHttpxClient
39+
from ._base_client import DefaultHttpxClient, DefaultAioHttpClient, DefaultAsyncHttpxClient
4040
from ._utils._logs import setup_logging as _setup_logging
4141

4242
__all__ = [
@@ -78,6 +78,7 @@
7878
"DEFAULT_CONNECTION_LIMITS",
7979
"DefaultHttpxClient",
8080
"DefaultAsyncHttpxClient",
81+
"DefaultAioHttpClient",
8182
]
8283

8384
if not _t.TYPE_CHECKING:

src/cloudflare/_base_client.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1331,6 +1331,24 @@ def __init__(self, **kwargs: Any) -> None:
13311331
super().__init__(**kwargs)
13321332

13331333

1334+
try:
1335+
import httpx_aiohttp
1336+
except ImportError:
1337+
1338+
class _DefaultAioHttpClient(httpx.AsyncClient):
1339+
def __init__(self, **_kwargs: Any) -> None:
1340+
raise RuntimeError("To use the aiohttp client you must have installed the package with the `aiohttp` extra")
1341+
else:
1342+
1343+
class _DefaultAioHttpClient(httpx_aiohttp.HttpxAiohttpClient): # type: ignore
1344+
def __init__(self, **kwargs: Any) -> None:
1345+
kwargs.setdefault("timeout", DEFAULT_TIMEOUT)
1346+
kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS)
1347+
kwargs.setdefault("follow_redirects", True)
1348+
1349+
super().__init__(**kwargs)
1350+
1351+
13341352
if TYPE_CHECKING:
13351353
DefaultAsyncHttpxClient = httpx.AsyncClient
13361354
"""An alias to `httpx.AsyncClient` that provides the same defaults that this SDK
@@ -1339,8 +1357,12 @@ def __init__(self, **kwargs: Any) -> None:
13391357
This is useful because overriding the `http_client` with your own instance of
13401358
`httpx.AsyncClient` will result in httpx's defaults being used, not ours.
13411359
"""
1360+
1361+
DefaultAioHttpClient = httpx.AsyncClient
1362+
"""An alias to `httpx.AsyncClient` that changes the default HTTP transport to `aiohttp`."""
13421363
else:
13431364
DefaultAsyncHttpxClient = _DefaultAsyncHttpxClient
1365+
DefaultAioHttpClient = _DefaultAioHttpClient
13441366

13451367

13461368
class AsyncHttpxClientWrapper(DefaultAsyncHttpxClient):

tests/api_resources/accounts/logs/test_audit.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,9 @@ def test_path_params_list(self, client: Cloudflare) -> None:
105105

106106

107107
class TestAsyncAudit:
108-
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
108+
parametrize = pytest.mark.parametrize(
109+
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
110+
)
109111

110112
@pytest.mark.skip(reason="TODO:investigate broken test")
111113
@parametrize

tests/api_resources/accounts/test_members.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,9 @@ def test_path_params_get(self, client: Cloudflare) -> None:
508508

509509

510510
class TestAsyncMembers:
511-
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
511+
parametrize = pytest.mark.parametrize(
512+
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
513+
)
512514

513515
@pytest.mark.skip(reason="HTTP 422 error from prism")
514516
@parametrize

tests/api_resources/accounts/test_roles.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,9 @@ def test_path_params_get(self, client: Cloudflare) -> None:
115115

116116

117117
class TestAsyncRoles:
118-
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
118+
parametrize = pytest.mark.parametrize(
119+
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
120+
)
119121

120122
@parametrize
121123
async def test_method_list(self, async_client: AsyncCloudflare) -> None:

tests/api_resources/accounts/test_subscriptions.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,9 @@ def test_path_params_get(self, client: Cloudflare) -> None:
232232

233233

234234
class TestAsyncSubscriptions:
235-
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
235+
parametrize = pytest.mark.parametrize(
236+
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
237+
)
236238

237239
@parametrize
238240
async def test_method_create(self, async_client: AsyncCloudflare) -> None:

tests/api_resources/accounts/test_tokens.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,9 @@ def test_path_params_verify(self, client: Cloudflare) -> None:
536536

537537

538538
class TestAsyncTokens:
539-
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
539+
parametrize = pytest.mark.parametrize(
540+
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
541+
)
540542

541543
@pytest.mark.skip(reason="TODO: investigate broken test")
542544
@parametrize

tests/api_resources/accounts/tokens/test_permission_groups.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,9 @@ def test_path_params_get(self, client: Cloudflare) -> None:
104104

105105

106106
class TestAsyncPermissionGroups:
107-
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
107+
parametrize = pytest.mark.parametrize(
108+
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
109+
)
108110

109111
@pytest.mark.skip(reason="TODO: investigate broken test")
110112
@parametrize

tests/api_resources/accounts/tokens/test_value.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@ def test_path_params_update(self, client: Cloudflare) -> None:
7575

7676

7777
class TestAsyncValue:
78-
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
78+
parametrize = pytest.mark.parametrize(
79+
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
80+
)
7981

8082
@pytest.mark.skip(reason="TODO: investigate broken test")
8183
@parametrize

tests/api_resources/acm/test_total_tls.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,9 @@ def test_path_params_get(self, client: Cloudflare) -> None:
111111

112112

113113
class TestAsyncTotalTLS:
114-
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
114+
parametrize = pytest.mark.parametrize(
115+
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
116+
)
115117

116118
@parametrize
117119
async def test_method_create(self, async_client: AsyncCloudflare) -> None:

tests/api_resources/addressing/address_maps/test_accounts.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,9 @@ def test_path_params_delete(self, client: Cloudflare) -> None:
120120

121121

122122
class TestAsyncAccounts:
123-
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
123+
parametrize = pytest.mark.parametrize(
124+
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
125+
)
124126

125127
@parametrize
126128
async def test_method_update(self, async_client: AsyncCloudflare) -> None:

tests/api_resources/addressing/address_maps/test_ips.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,9 @@ def test_path_params_delete(self, client: Cloudflare) -> None:
145145

146146

147147
class TestAsyncIPs:
148-
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
148+
parametrize = pytest.mark.parametrize(
149+
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
150+
)
149151

150152
@parametrize
151153
async def test_method_update(self, async_client: AsyncCloudflare) -> None:

tests/api_resources/addressing/address_maps/test_zones.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,9 @@ def test_path_params_delete(self, client: Cloudflare) -> None:
145145

146146

147147
class TestAsyncZones:
148-
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
148+
parametrize = pytest.mark.parametrize(
149+
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
150+
)
149151

150152
@parametrize
151153
async def test_method_update(self, async_client: AsyncCloudflare) -> None:

tests/api_resources/addressing/prefixes/test_advertisement_status.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,9 @@ def test_path_params_get(self, client: Cloudflare) -> None:
135135

136136

137137
class TestAsyncAdvertisementStatus:
138-
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
138+
parametrize = pytest.mark.parametrize(
139+
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
140+
)
139141

140142
@parametrize
141143
async def test_method_edit(self, async_client: AsyncCloudflare) -> None:

tests/api_resources/addressing/prefixes/test_bgp_prefixes.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,9 @@ def test_path_params_get(self, client: Cloudflare) -> None:
257257

258258

259259
class TestAsyncBGPPrefixes:
260-
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
260+
parametrize = pytest.mark.parametrize(
261+
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
262+
)
261263

262264
@parametrize
263265
async def test_method_create(self, async_client: AsyncCloudflare) -> None:

0 commit comments

Comments
 (0)