airbytehq / airbytehq/airbyte-api-python-sdk

Speakeasy generator bug: missing model_rebuild() for models with circular TYPE_CHECKING imports

Offen
#186 0 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen
Vorherrschende Sprache
Python
Sterne
61
Forks
27
Ø Merge
4 Min.
Gemergte PRs (30 T.)
2

Beschreibung

## Problem

Speakeasy's Python generator uses `TYPE_CHECKING` imports to break circular schema references, but only emits `model_rebuild()` for the immediate forward-referenced model — not for models that transitively depend on it. This causes `PydanticUserError: not fully defined` at instantiation time for any model in the dependency chain.

## Affected Schema

`RowFilteringOperationNot` has a recursive `conditions` field:

```yaml
RowFilteringOperationNot:
properties:
conditions:
type: array
items:
$ref: "#/components/schemas/RowFilteringOperation" # circular
RowFilteringOperation:
oneOf:
- $ref: "#/components/schemas/RowFilteringOperationEqual"
- $ref: "#/components/schemas/RowFilteringOperationNot" # back-reference
```

The upstream spec marks this: `x-airbyte-circular-ref: true`.

## Symptoms

Speakeasy generates `RowFilteringOperationNot1` (with `1` suffix from name collision) and imports `RowFilteringOperation` under `TYPE_CHECKING`. It emits `RowFilteringOperationNot1.model_rebuild()` in `__init__.py` but NOT for dependent models. Result:

```
PydanticUserError: \`ConnectionResponse\` is not fully defined;
you should define \`RowFilteringOperationNot1\`, then call \`ConnectionResponse.model_rebuild()\`.
```

3 models affected: `ConnectionsResponse`, `RowFilteringMapperConfiguration`, `StreamConfigurations`.

## Workaround

We use an OpenAPI overlay to break the circular `$ref` by pointing `RowFilteringOperationNot.conditions.items` directly at `RowFilteringOperationEqual` instead of `RowFilteringOperation`:

```yaml
# overlays/python_speakeasy.yaml
actions:
- target: "$.components.schemas.RowFilteringOperationNot.properties.conditions.items"
update:
$ref: "#/components/schemas/RowFilteringOperationEqual"
```

This removes the recursion (`NOT(NOT(x)) = x`, so nested NOT is redundant) and allows Speakeasy to generate clean direct imports without `TYPE_CHECKING`.

## Investigation

- All Speakeasy fix flags were tested individually and in combination (PRs #180-#183) — none fixed this
- Overlay renames were tested (PR #184) — the `1` suffix and `TYPE_CHECKING` pattern are caused by the circular dependency, not the name collision
- The overlay flattening approach (PR #184 final iteration) works: all 738 model imports pass, all 4 previously broken models pass `model_json_schema()`

## Upstream Fix Needed

Speakeasy should emit `model_rebuild()` for ALL models that transitively depend on `TYPE_CHECKING` forward refs, not just the immediate one. This would fix the issue without needing the overlay workaround.

## Related

- #178 — original bug report for forward-reference errors in 1.0.0
- PR #179 — fixed NameError variant (enabled `nameResolutionFeb2025` + `conflictResistantModelImportsFeb2026`)
- PR #184 — overlay fix for the model_rebuild variant

---
[Devin session](https://app.devin.ai/sessions/854c664803f3400387fdaa02e123b888)

_Reported by @aaronsteers, investigated and documented in [Devin session](https://app.devin.ai/sessions/854c664803f3400387fdaa02e123b888)._

Beitragsleitfaden

Beitragsleitfaden öffnen

Rechercherichtung

Inspect the generated __init__.py entries and circular TYPE_CHECKING imports for RowFilteringOperationNot1, ConnectionsResponse, RowFilteringMapperConfiguration, and StreamConfigurations. Reproduce the failure by instantiating the affected models, then run model_json_schema() as reported. Done means transitively dependent models handle the forward references without relying on overlays/python_speakeasy.yaml.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
openapi, python
Bereich
api
Issue-Typ
Bug
Schwierigkeit
4/5
Geschätzter Aufwand
3-5 Tage
Aktivitätsstatus
Ruhig
Klarheit
Größtenteils klar
Anfängerfreundlichkeit
38/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.