Skip to content

Commit 8907a7e

Browse files
committed
chore: sync shared codegen files from staging-next
1 parent 9707100 commit 8907a7e

4 files changed

Lines changed: 299 additions & 9 deletions

File tree

.stats.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
configured_endpoints: 2194
2-
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare%2Fcloudflare-0ce49e6bb0d3819f135b9a567b661205fdf5df21cff157eab2b7abd7b5b50347.yml
3-
openapi_spec_hash: 512a5bb3a32860590c8949765605d65a
4-
config_hash: 5367ae3e3a9a0d6578c2756965a99e3a
1+
configured_endpoints: 2184
2+
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare%2Fcloudflare-95d8075980561cae9cc6ce09b5f930b84eae6f0864cb155abe47db94df2ee294.yml
3+
openapi_spec_hash: 86610e8d69a7b3f8b83cb5f6a0a7c96e
4+
config_hash: 95239e8fc62638979bc21e538307bbe3

VALIDATION_REPORT.md

Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
# Python SDK Validation Report
2+
3+
**Branch:** `next` (at `9707100e1`)
4+
**Date:** 2026-04-24
5+
**Checkers:** pyright 1.1.399, mypy 1.17, ruff
6+
7+
---
8+
9+
## Summary
10+
11+
| Check | Result |
12+
|---------|--------|
13+
| ruff | PASS |
14+
| pyright | FAIL - 55 errors |
15+
| mypy | FAIL - 12 errors |
16+
17+
All failures are type-checking errors. There are **4 distinct issues** across 6 source files (+1 test file),
18+
producing the full error count due to cascading type-unknown propagation.
19+
20+
---
21+
22+
## Issue 1: Missing `Required` import in `asset_create_params.py`
23+
24+
**Files:** `src/cloudflare/types/ai/finetunes/asset_create_params.py`
25+
**Error count:** 3 (pyright) + 3 (mypy)
26+
**Error type:** `reportUndefinedVariable` / `name-defined`
27+
28+
### What's wrong
29+
30+
`Required` is used on lines 13, 15, and 18 but is not imported. The import line reads:
31+
32+
```python
33+
from typing_extensions import TypedDict
34+
```
35+
36+
It should be:
37+
38+
```python
39+
from typing_extensions import Required, TypedDict
40+
```
41+
42+
### Change history
43+
44+
| Date | Commit | What happened |
45+
|------------|-------------|---------------|
46+
| 2025-01-02 | `4493d6c28` | File created with `Required` import and `Required[str]` on `account_id` |
47+
| 2026-04-19 | `3b83baa3b` | Codegen removed `Required` import AND `Required` usage (made fields optional) |
48+
| 2026-04-23 | `988df8632` | Codegen re-added `Required` to 3 fields BUT did not restore the import |
49+
50+
### Root cause
51+
52+
The `988df8632` commit ("remove account_id and zone_id client options") re-promoted `account_id`, `file`, and
53+
`file_name` back to `Required` status but the codegen output omitted the corresponding
54+
`from typing_extensions import Required` update. This is a Stainless codegen bug -- it emitted `Required` usage
55+
without the import.
56+
57+
### Fix
58+
59+
Add `Required` to the import:
60+
61+
```python
62+
from typing_extensions import Required, TypedDict
63+
```
64+
65+
---
66+
67+
## Issue 2: `SchemaFieldStruct` and `SchemaFieldList` missing `TypedDict` base class
68+
69+
**Files:**
70+
- `src/cloudflare/types/pipelines/sink_create_params.py` (lines 293, 297)
71+
- `src/cloudflare/types/pipelines/stream_create_params.py` (lines 201, 205)
72+
73+
**Error count:** 4 (pyright) + 4 (mypy)
74+
**Error type:** `reportGeneralTypeIssues` + `reportCallIssue` / `call-arg`
75+
76+
### What's wrong
77+
78+
Both classes are declared as:
79+
80+
```python
81+
class SchemaFieldStruct(total=False):
82+
pass
83+
84+
class SchemaFieldList(total=False):
85+
pass
86+
```
87+
88+
They should inherit from `TypedDict`:
89+
90+
```python
91+
class SchemaFieldStruct(TypedDict, total=False):
92+
...
93+
94+
class SchemaFieldList(TypedDict, total=False):
95+
...
96+
```
97+
98+
Without `TypedDict`, these are plain class definitions. `total=False` is passed to
99+
`object.__init_subclass__()` which does not accept that keyword argument.
100+
101+
### Change history
102+
103+
| Date | Commit | What happened |
104+
|------------|-------------|---------------|
105+
| 2025-11-12 | `008556f6a` | Pipelines feature introduced these types (generated with stub bodies, had pyright ignores) |
106+
| 2026-02-11 | `1c415a2dd` | **Manual fix**: added `TypedDict` base class, proper `type`, `name`, `fields`/`element` fields |
107+
| 2026-04-19 | `3b83baa3b` | Codegen overwrote the manual fix, regenerating stubs without `TypedDict` base class |
108+
109+
### Root cause
110+
111+
Stainless codegen is emitting empty stub classes for the `struct` and `list` schema field variants. The OpenAPI
112+
spec likely defines these as recursive types (struct has `fields: [SchemaField]`, list has `element: SchemaField`)
113+
which the codegen can't fully resolve, so it emits empty stubs. A previous manual fix (`1c415a2dd`) was
114+
overwritten by subsequent codegen runs.
115+
116+
### Fix
117+
118+
Restore the `TypedDict` base class and proper fields as done in `1c415a2dd`. For both
119+
`sink_create_params.py` and `stream_create_params.py`:
120+
121+
```python
122+
class SchemaFieldStruct(TypedDict, total=False):
123+
type: Required[Literal["struct"]]
124+
metadata_key: Optional[str]
125+
name: str
126+
required: bool
127+
sql_name: str
128+
fields: Optional[List["SchemaField"]]
129+
130+
class SchemaFieldList(TypedDict, total=False):
131+
type: Required[Literal["list"]]
132+
metadata_key: Optional[str]
133+
name: str
134+
required: bool
135+
sql_name: str
136+
element: Optional["SchemaField"]
137+
```
138+
139+
This fix will be overwritten by the next codegen run unless the Stainless config or upstream OpenAPI spec is also
140+
fixed to properly model these recursive types.
141+
142+
---
143+
144+
## Issue 3: Missing `organization_profile_get_params` module
145+
146+
**Files:**
147+
- `src/cloudflare/resources/organizations/organization_profile.py` (line 22)
148+
- `tests/api_resources/organizations/test_organization_profile.py` (line 12)
149+
150+
**Error count:** 22 (pyright) + 1 (mypy) -- most pyright errors are cascading `Unknown` type propagation
151+
**Error type:** `reportMissingImports` / `import-not-found`
152+
153+
### What's wrong
154+
155+
Both the resource module and its test file import:
156+
157+
```python
158+
from ...types.organizations.organization_profile_get_params import Result
159+
```
160+
161+
But the file `organization_profile_get_params.py` does not exist. It was deleted by codegen.
162+
163+
### Change history
164+
165+
| Date | Commit | What happened |
166+
|------------|-------------|---------------|
167+
| 2026-02-11 | `1c415a2dd` | **Manual fix**: created `organization_profile_get_params.py` with `Result = OrganizationProfile` |
168+
| 2026-04-19 | `3b83baa3b` | Codegen created `organization_profile.py` resource file, importing `Result` from get_params |
169+
| 2026-04-22 | `0d6464258` | Codegen **deleted** `organization_profile_get_params.py` and removed it from `__init__.py` |
170+
| 2026-04-23 | `988df8632` | Resource file unchanged -- still imports from deleted module |
171+
172+
### Root cause
173+
174+
There is a sequencing inconsistency in the codegen output. The resource file (`organization_profile.py`) was
175+
generated expecting a `Result` type alias from a params file, but a later codegen run deleted that params file
176+
without updating the resource file's import. The `Result` type was just an alias for `OrganizationProfile`.
177+
178+
### Fix
179+
180+
Option A -- Recreate the deleted file:
181+
182+
Create `src/cloudflare/types/organizations/organization_profile_get_params.py`:
183+
```python
184+
from __future__ import annotations
185+
from typing_extensions import TypeAlias
186+
from .organization_profile import OrganizationProfile
187+
188+
__all__ = ["Result"]
189+
190+
Result: TypeAlias = OrganizationProfile
191+
```
192+
193+
And add to `src/cloudflare/types/organizations/__init__.py`:
194+
```python
195+
from .organization_profile import OrganizationProfile as OrganizationProfile
196+
```
197+
198+
Option B -- Inline the import (smaller diff):
199+
200+
In `organization_profile.py` and the test file, replace:
201+
```python
202+
from ...types.organizations.organization_profile_get_params import Result
203+
```
204+
with:
205+
```python
206+
from ...types.organizations.organization_profile import OrganizationProfile as Result
207+
```
208+
209+
Option A is preferred since it matches the pattern codegen expects and is more durable.
210+
211+
---
212+
213+
## Issue 4: `_get_api_list` called with unsupported `files` parameter
214+
215+
**Files:**
216+
- `src/cloudflare/resources/ai/to_markdown.py` (lines 118, 217)
217+
218+
**Error count:** 4 (pyright) + 2 (mypy) (2 calls x sync/async variants)
219+
**Error type:** `reportCallIssue` + `reportUnknownVariableType` / `call-arg`
220+
221+
### What's wrong
222+
223+
The `transform` method passes `files=files` to `self._get_api_list()`:
224+
225+
```python
226+
return self._get_api_list(
227+
...,
228+
body=maybe_transform(body, ...),
229+
files=files, # <-- not in get_api_list signature
230+
options=...,
231+
model=...,
232+
method="post",
233+
)
234+
```
235+
236+
The `get_api_list` method signature in `_base_client.py` is:
237+
238+
```python
239+
def get_api_list(self, path, *, model, page, body=None, options={}, method="get") -> SyncPageT
240+
```
241+
242+
There is no `files` parameter. The kwargs likely pass through to the underlying request options via `**options`,
243+
but the type signature rejects it.
244+
245+
### Change history
246+
247+
| Date | Commit | What happened |
248+
|------------|-------------|---------------|
249+
| 2026-02-11 | `f280942f4` | Added `# pyright: ignore` / `# type: ignore` to the **same pattern** in `radar/ai/to_markdown.py` |
250+
| 2026-04-19 | `3b83baa3b` | Codegen created the new `ai/to_markdown.py` with same `files=` pattern but **without** suppression comments |
251+
252+
### Root cause
253+
254+
This is a known Stainless codegen limitation: `_get_api_list` does not accept `files` in its typed signature, but
255+
multipart file upload endpoints that also return paginated lists need to pass files through. The older
256+
`radar/ai/to_markdown.py` has manual suppression comments that survive codegen. The newly generated
257+
`ai/to_markdown.py` was created fresh and lacks them.
258+
259+
### Fix
260+
261+
Add type-checker suppression comments matching the pattern in `radar/ai/to_markdown.py`:
262+
263+
```python
264+
files=files, # pyright: ignore[reportCallIssue] # type: ignore[call-arg]
265+
```
266+
267+
On both line 118 (sync) and line 217 (async) in `src/cloudflare/resources/ai/to_markdown.py`.
268+
269+
---
270+
271+
## Cross-cutting observations
272+
273+
1. **Manual fixes are fragile.** Issues 2, 3, and 4 all stem from manual fixes (commits `1c415a2dd` and
274+
`f280942f4`) being overwritten by subsequent Stainless codegen runs. Any fix applied here will face the same
275+
risk on the next codegen sync unless the upstream Stainless config or OpenAPI spec is also corrected.
276+
277+
2. **Upstream Stainless bugs to track:**
278+
- Missing `Required` import when re-promoting fields (Issue 1)
279+
- Empty stub classes for recursive TypedDict types in pipelines (Issue 2)
280+
- Inconsistent file deletion without updating dependents (Issue 3)
281+
- Missing `files` parameter in `_get_api_list` signature for multipart paginated endpoints (Issue 4)
282+
283+
3. **Recommended durable fixes:**
284+
- Issues 1, 3, 4: File Stainless bug reports so codegen output is correct
285+
- Issue 2: Fix the OpenAPI spec for pipelines to properly model recursive struct/list types, or add Stainless
286+
config overrides to emit correct TypedDict stubs

scripts/mock

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ set -e
44

55
cd "$(dirname "$0")/.."
66

7+
MOCK_PORT="${MOCK_PORT:-4012}"
8+
79
if [[ -n "$1" && "$1" != '--'* ]]; then
810
URL="$1"
911
shift
@@ -17,14 +19,14 @@ if [ -z "$URL" ]; then
1719
exit 1
1820
fi
1921

20-
echo "==> Starting mock server with URL ${URL}"
22+
echo "==> Starting mock server with URL ${URL} on port ${MOCK_PORT}"
2123

2224
# Run prism mock on the given spec
2325
if [ "$1" == "--daemon" ]; then
2426
# Pre-install the package so the download doesn't eat into the startup timeout
2527
npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism --version
2628

27-
npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" &> .prism.log &
29+
npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" -p "$MOCK_PORT" &> .prism.log &
2830

2931
# Wait for server to come online (max 30s)
3032
echo -n "Waiting for server"
@@ -48,5 +50,5 @@ if [ "$1" == "--daemon" ]; then
4850

4951
echo
5052
else
51-
npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL"
53+
npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" -p "$MOCK_PORT"
5254
fi

scripts/test

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ GREEN='\033[0;32m'
99
YELLOW='\033[0;33m'
1010
NC='\033[0m' # No Color
1111

12+
MOCK_PORT="${MOCK_PORT:-4012}"
13+
1214
function prism_is_running() {
13-
curl --silent "http://localhost:4010" >/dev/null 2>&1
15+
curl --silent "http://localhost:${MOCK_PORT}" >/dev/null 2>&1
1416
}
1517

1618
kill_server_on_port() {
@@ -27,7 +29,7 @@ function is_overriding_api_base_url() {
2729

2830
if ! is_overriding_api_base_url && ! prism_is_running ; then
2931
# When we exit this script, make sure to kill the background mock server process
30-
trap 'kill_server_on_port 4010' EXIT
32+
trap 'kill_server_on_port $MOCK_PORT' EXIT
3133

3234
# Start the dev server
3335
./scripts/mock --daemon

0 commit comments

Comments
 (0)