Skip to content

Commit cca58a5

Browse files
committed
2 parents f90f0b9 + 0aed308 commit cca58a5

File tree

1,331 files changed

+30977
-51424
lines changed

Some content is hidden

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

1,331 files changed

+30977
-51424
lines changed

.devcontainer/Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT}
33

44
USER vscode
55

6-
RUN curl -sSf https://rye.astral.sh/get | RYE_VERSION="0.24.0" RYE_INSTALL_OPTION="--yes" bash
6+
RUN curl -sSf https://rye.astral.sh/get | RYE_VERSION="0.35.0" RYE_INSTALL_OPTION="--yes" bash
77
ENV PATH=/home/vscode/.rye/shims:$PATH
88

99
RUN echo "[[ -d .venv ]] && source .venv/bin/activate" >> /home/vscode/.bashrc

.github/workflows/ci.yml

+5-4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ on:
66
pull_request:
77
branches:
88
- main
9+
- next
910

1011
jobs:
1112
lint:
@@ -21,7 +22,7 @@ jobs:
2122
curl -sSf https://rye.astral.sh/get | bash
2223
echo "$HOME/.rye/shims" >> $GITHUB_PATH
2324
env:
24-
RYE_VERSION: 0.24.0
25+
RYE_VERSION: '0.35.0'
2526
RYE_INSTALL_OPTION: '--yes'
2627

2728
- name: Install dependencies
@@ -41,7 +42,7 @@ jobs:
4142
curl -sSf https://rye.astral.sh/get | bash
4243
echo "$HOME/.rye/shims" >> $GITHUB_PATH
4344
env:
44-
RYE_VERSION: 0.24.0
45+
RYE_VERSION: '0.35.0'
4546
RYE_INSTALL_OPTION: '--yes'
4647

4748
- name: Bootstrap
@@ -61,8 +62,8 @@ jobs:
6162
curl -sSf https://rye.astral.sh/get | bash
6263
echo "$HOME/.rye/shims" >> $GITHUB_PATH
6364
env:
64-
RYE_VERSION: 0.24.0
65-
RYE_INSTALL_OPTION: "--yes"
65+
RYE_VERSION: '0.35.0'
66+
RYE_INSTALL_OPTION: '--yes'
6667
- name: Install dependencies
6768
run: |
6869
rye sync --all-features

.github/workflows/publish-pypi.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ jobs:
2121
curl -sSf https://rye.astral.sh/get | bash
2222
echo "$HOME/.rye/shims" >> $GITHUB_PATH
2323
env:
24-
RYE_VERSION: 0.24.0
25-
RYE_INSTALL_OPTION: "--yes"
24+
RYE_VERSION: '0.35.0'
25+
RYE_INSTALL_OPTION: '--yes'
2626

2727
- name: Publish to PyPI
2828
run: |

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
.prism.log
12
.vscode
23
_dev
34

.release-please-manifest.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "3.0.1"
2+
".": "3.1.0"
33
}

.stats.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
configured_endpoints: 1353
2-
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare%2Fcloudflare-1274668bf5bb40cc6a93aa05b9b1c96050656b905a292bccdb53941f50eaf81e.yml
1+
configured_endpoints: 1256
2+
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare%2Fcloudflare-923d8c7667b68c786e6c026c4f4851798943c7d68ea055c0043d9253413c5847.yml

CHANGELOG.md

+307
Large diffs are not rendered by default.

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ and offers both synchronous and asynchronous clients powered by [httpx](https://
88

99
## Documentation
1010

11-
The REST API documentation can be found [on developers.cloudflare.com](https://developers.cloudflare.com/api). The full API of this library can be found in [api.md](api.md).
11+
The REST API documentation can be found on [developers.cloudflare.com](https://developers.cloudflare.com/api). The full API of this library can be found in [api.md](api.md).
1212

1313
## Installation
1414

@@ -351,7 +351,7 @@ You can directly override the [httpx client](https://www.python-httpx.org/api/#c
351351

352352
- Support for proxies
353353
- Custom transports
354-
- Additional [advanced](https://www.python-httpx.org/advanced/#client-instances) functionality
354+
- Additional [advanced](https://www.python-httpx.org/advanced/clients/) functionality
355355

356356
```python
357357
from cloudflare import Cloudflare, DefaultHttpxClient

api.md

+110-690
Large diffs are not rendered by default.

bin/publish-pypi

+3
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,7 @@
33
set -eux
44
mkdir -p dist
55
rye build --clean
6+
# Patching importlib-metadata version until upstream library version is updated
7+
# https://github.com/pypa/twine/issues/977#issuecomment-2189800841
8+
"$HOME/.rye/self/bin/python3" -m pip install 'importlib-metadata==7.2.1'
69
rye publish --yes --token=$PYPI_TOKEN

pyproject.toml

+17-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "cloudflare"
3-
version = "3.0.1"
3+
version = "3.1.0"
44
description = "The official Python library for the cloudflare API"
55
dynamic = ["readme"]
66
license = "Apache-2.0"
@@ -58,6 +58,7 @@ dev-dependencies = [
5858
"nox",
5959
"dirty-equals>=0.6.0",
6060
"importlib-metadata>=6.7.0",
61+
"rich>=13.7.1",
6162

6263
]
6364

@@ -99,6 +100,21 @@ include = [
99100
[tool.hatch.build.targets.wheel]
100101
packages = ["src/cloudflare"]
101102

103+
[tool.hatch.build.targets.sdist]
104+
# Basically everything except hidden files/directories (such as .github, .devcontainers, .python-version, etc)
105+
include = [
106+
"/*.toml",
107+
"/*.json",
108+
"/*.lock",
109+
"/*.md",
110+
"/mypy.ini",
111+
"/noxfile.py",
112+
"bin/*",
113+
"examples/*",
114+
"src/*",
115+
"tests/*",
116+
]
117+
102118
[tool.hatch.metadata.hooks.fancy-pypi-readme]
103119
content-type = "text/markdown"
104120

requirements-dev.lock

+11-2
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66
# features: []
77
# all-features: true
88
# with-sources: false
9+
# generate-hashes: false
910

1011
-e file:.
1112
annotated-types==0.6.0
1213
# via pydantic
13-
anyio==4.1.0
14+
anyio==4.4.0
1415
# via cloudflare
1516
# via httpx
1617
argcomplete==3.1.2
@@ -44,7 +45,11 @@ idna==3.4
4445
importlib-metadata==7.0.0
4546
iniconfig==2.0.0
4647
# via pytest
47-
mypy==1.7.1
48+
markdown-it-py==3.0.0
49+
# via rich
50+
mdurl==0.1.2
51+
# via markdown-it-py
52+
mypy==1.10.1
4853
mypy-extensions==1.0.0
4954
# via mypy
5055
nodeenv==1.8.0
@@ -63,6 +68,8 @@ pydantic==2.7.1
6368
# via cloudflare
6469
pydantic-core==2.18.2
6570
# via pydantic
71+
pygments==2.18.0
72+
# via rich
6673
pyright==1.1.364
6774
pytest==7.1.1
6875
# via pytest-asyncio
@@ -72,6 +79,7 @@ python-dateutil==2.8.2
7279
pytz==2023.3.post1
7380
# via dirty-equals
7481
respx==0.20.2
82+
rich==13.7.1
7583
ruff==0.1.9
7684
setuptools==68.2.2
7785
# via nodeenv
@@ -86,6 +94,7 @@ tomli==2.0.1
8694
# via mypy
8795
# via pytest
8896
typing-extensions==4.8.0
97+
# via anyio
8998
# via cloudflare
9099
# via mypy
91100
# via pydantic

requirements.lock

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66
# features: []
77
# all-features: true
88
# with-sources: false
9+
# generate-hashes: false
910

1011
-e file:.
1112
annotated-types==0.6.0
1213
# via pydantic
13-
anyio==4.1.0
14+
anyio==4.4.0
1415
# via cloudflare
1516
# via httpx
1617
certifi==2023.7.22
@@ -38,6 +39,7 @@ sniffio==1.3.0
3839
# via cloudflare
3940
# via httpx
4041
typing-extensions==4.8.0
42+
# via anyio
4143
# via cloudflare
4244
# via pydantic
4345
# via pydantic-core

src/cloudflare/_base_client.py

+40-14
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
HttpxSendArgs,
5959
AsyncTransport,
6060
RequestOptions,
61+
HttpxRequestFiles,
6162
ModelBuilderProtocol,
6263
)
6364
from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping
@@ -459,6 +460,7 @@ def _build_request(
459460
headers = self._build_headers(options)
460461
params = _merge_mappings(self.default_query, options.params)
461462
content_type = headers.get("Content-Type")
463+
files = options.files
462464

463465
# If the given Content-Type header is multipart/form-data then it
464466
# has to be removed so that httpx can generate the header with
@@ -472,14 +474,23 @@ def _build_request(
472474
headers.pop("Content-Type")
473475

474476
# As we are now sending multipart/form-data instead of application/json
475-
# we need to tell httpx to use it, https://www.python-httpx.org/advanced/#multipart-file-encoding
477+
# we need to tell httpx to use it, https://www.python-httpx.org/advanced/clients/#multipart-file-encoding
476478
if json_data:
477479
if not is_dict(json_data):
478480
raise TypeError(
479481
f"Expected query input to be a dictionary for multipart requests but got {type(json_data)} instead."
480482
)
481483
kwargs["data"] = self._serialize_multipartform(json_data)
482484

485+
# httpx determines whether or not to send a "multipart/form-data"
486+
# request based on the truthiness of the "files" argument.
487+
# This gets around that issue by generating a dict value that
488+
# evaluates to true.
489+
#
490+
# https://github.com/encode/httpx/discussions/2399#discussioncomment-3814186
491+
if not files:
492+
files = cast(HttpxRequestFiles, ForceMultipartDict())
493+
483494
# TODO: report this error to httpx
484495
return self._client.build_request( # pyright: ignore[reportUnknownMemberType]
485496
headers=headers,
@@ -492,7 +503,7 @@ def _build_request(
492503
# https://github.com/microsoft/pyright/issues/3526#event-6715453066
493504
params=self.qs.stringify(cast(Mapping[str, Any], params)) if params else None,
494505
json=json_data,
495-
files=options.files,
506+
files=files,
496507
**kwargs,
497508
)
498509

@@ -868,9 +879,9 @@ def __exit__(
868879
def _prepare_options(
869880
self,
870881
options: FinalRequestOptions, # noqa: ARG002
871-
) -> None:
882+
) -> FinalRequestOptions:
872883
"""Hook for mutating the given options"""
873-
return None
884+
return options
874885

875886
def _prepare_request(
876887
self,
@@ -944,8 +955,13 @@ def _request(
944955
stream: bool,
945956
stream_cls: type[_StreamT] | None,
946957
) -> ResponseT | _StreamT:
958+
# create a copy of the options we were given so that if the
959+
# options are mutated later & we then retry, the retries are
960+
# given the original options
961+
input_options = model_copy(options)
962+
947963
cast_to = self._maybe_override_cast_to(cast_to, options)
948-
self._prepare_options(options)
964+
options = self._prepare_options(options)
949965

950966
retries = self._remaining_retries(remaining_retries, options)
951967
request = self._build_request(options)
@@ -968,7 +984,7 @@ def _request(
968984

969985
if retries > 0:
970986
return self._retry_request(
971-
options,
987+
input_options,
972988
cast_to,
973989
retries,
974990
stream=stream,
@@ -983,7 +999,7 @@ def _request(
983999

9841000
if retries > 0:
9851001
return self._retry_request(
986-
options,
1002+
input_options,
9871003
cast_to,
9881004
retries,
9891005
stream=stream,
@@ -1011,7 +1027,7 @@ def _request(
10111027
if retries > 0 and self._should_retry(err.response):
10121028
err.response.close()
10131029
return self._retry_request(
1014-
options,
1030+
input_options,
10151031
cast_to,
10161032
retries,
10171033
err.response.headers,
@@ -1426,9 +1442,9 @@ async def __aexit__(
14261442
async def _prepare_options(
14271443
self,
14281444
options: FinalRequestOptions, # noqa: ARG002
1429-
) -> None:
1445+
) -> FinalRequestOptions:
14301446
"""Hook for mutating the given options"""
1431-
return None
1447+
return options
14321448

14331449
async def _prepare_request(
14341450
self,
@@ -1507,8 +1523,13 @@ async def _request(
15071523
# execute it earlier while we are in an async context
15081524
self._platform = await asyncify(get_platform)()
15091525

1526+
# create a copy of the options we were given so that if the
1527+
# options are mutated later & we then retry, the retries are
1528+
# given the original options
1529+
input_options = model_copy(options)
1530+
15101531
cast_to = self._maybe_override_cast_to(cast_to, options)
1511-
await self._prepare_options(options)
1532+
options = await self._prepare_options(options)
15121533

15131534
retries = self._remaining_retries(remaining_retries, options)
15141535
request = self._build_request(options)
@@ -1529,7 +1550,7 @@ async def _request(
15291550

15301551
if retries > 0:
15311552
return await self._retry_request(
1532-
options,
1553+
input_options,
15331554
cast_to,
15341555
retries,
15351556
stream=stream,
@@ -1544,7 +1565,7 @@ async def _request(
15441565

15451566
if retries > 0:
15461567
return await self._retry_request(
1547-
options,
1568+
input_options,
15481569
cast_to,
15491570
retries,
15501571
stream=stream,
@@ -1567,7 +1588,7 @@ async def _request(
15671588
if retries > 0 and self._should_retry(err.response):
15681589
await err.response.aclose()
15691590
return await self._retry_request(
1570-
options,
1591+
input_options,
15711592
cast_to,
15721593
retries,
15731594
err.response.headers,
@@ -1863,6 +1884,11 @@ def make_request_options(
18631884
return options
18641885

18651886

1887+
class ForceMultipartDict(Dict[str, None]):
1888+
def __bool__(self) -> bool:
1889+
return True
1890+
1891+
18661892
class OtherPlatform:
18671893
def __init__(self, name: str) -> None:
18681894
self.name = name

0 commit comments

Comments
 (0)