Skip to content

[Python] Generalize recursive imports handling beyond jobs - #7167

Open
Sankalp-Mittal wants to merge 1 commit into
mainfrom
sankalp-mittal/recursive-imports
Open

Sankalp-Mittal wants to merge 1 commit into
mainfrom
sankalp-mittal/recursive-imports

Conversation

@Sankalp-Mittal

@Sankalp-Mittal Sankalp-Mittal commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Summary

This generalizes the hack we used for generating the code for Jobs, to any resource that has circular imports, so we can remove the hardcoded references to Jobs.

Changes

  • quote_recursive_references now finds cycles between dataclasses with a DFS from the resource root, and quotes each reference that points back to a dataclass still being visited. Previously jobs.ForEachTask was hardcoded.
  • _write_exports now emits _resolve_recursive_imports() for every dataclass with quoted references, in any namespace. This replaces the if namespace == "jobs" FIXME branch. The resolver builds globalns from the classes that __init__.py already imports.

Why

Resolves DECO-28419. Until now only jobs (Task -> ForEachTask -> Task) could have recursive types. A new resource with a cycle would hit a circular import at load time, or fail to resolve forward references in _transform.

Starting the DFS from the root breaks the edge that points back towards the root, so jobs keeps the same quoting as before (ForEachTask.task). Generated _models are unchanged. The only generated diff is the jobs resolver: it no longer re-imports Task, and its globalns drops VariableOr (which isn't a string annotation, so it was never needed) and adds TaskParam.

Tests

  • Regenerated all namespaces: jobs is still the only one with a cycle, and every _models file is byte-identical.
  • New codegen_tests/test_generated_dataclass_patch.py covers a cycle outside jobs that goes through a list (Tree -> Node -> Branch -> list[Node]).
  • Manually checked that Job.from_dict with a nested for_each_task builds a real Task on Python 3.10–3.13.
  • I couldn't run ./task pydabs-codegen end to end locally because of PyPI network failures. I ran the generator and ruff by hand instead, and I'm relying on CI for the codegen pytest suite.

This pull request and its description were written by Isaac.

Detect dataclass cycles with a DFS from the resource root and quote the
back-references, instead of hardcoding jobs.ForEachTask. Emit the
_resolve_recursive_imports patch in any namespace that has quoted references,
instead of only for jobs.

Co-authored-by: Isaac <no-reply@databricks.com>
@eng-dev-ecosystem-bot

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: ad29193

Run: 39944158011

Env ✅​pass 🙈​skip Time
✅​ aws linux-2core-8gb 281 15 3:57
✅​ aws-windows-latest-4core-16gb 283 13 6:03
✅​ azure linux-2core-8gb 280 15 3:56
✅​ azure-windows-latest-4core-16gb 282 13 4:10
✅​ gcp linux-2core-8gb 281 15 3:58
✅​ gcp-windows-latest-4core-16gb 283 13 3:13
Top 3 slowest tests (at least 2 minutes):
duration env testname
5:54 aws-windows-latest-4core-16gb TestAccept
4:06 azure-windows-latest-4core-16gb TestAccept
3:07 gcp-windows-latest-4core-16gb TestAccept

@Sankalp-Mittal
Sankalp-Mittal marked this pull request as ready for review October 23, 2026 13:06
@Sankalp-Mittal
Sankalp-Mittal requested review from a team as code owners October 23, 2026 13:06

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants