Python equivalent of Cluster Yield CI's Scala plan-extractor
Project description
cy-py — Cluster Yield CI for Python
The Python equivalent of the Scala plan-extractor. Extracts Spark physical plans from PySpark pipelines so Cluster Yield CI can diff them on every PR — same JSON format, same deterministic Spark config, same GitHub Action.
30-Second Setup
1. Add the dependency
Poetry:
poetry add --group dev cy-python
pip:
# requirements.txt or pyproject.toml
cy-python
2. Extend SparkPipeline in your pipeline
from cy_py import SparkPipeline
from pyspark.sql import DataFrame, SparkSession
# Just add SparkPipeline as a base class — nothing else changes
class DailyOrderSummary(SparkPipeline):
def build(self, spark: SparkSession) -> DataFrame:
# ... your existing pipeline code ...
3. Add the GitHub Action
# .github/workflows/spark-plan-check.yml
name: Spark Plan Check
on:
pull_request:
branches: [main]
jobs:
plan-check:
runs-on: ubuntu-latest
permissions:
pull-requests: write
contents: read
steps:
- uses: actions/setup-python@v5
with: { python-version: '3.12' }
- uses: actions/setup-java@v4
with: { java-version: '11', distribution: 'temurin' }
- uses: clusteryieldanalytics/cluster-yield-ci@v1
with:
pipeline-class: my_package.pipelines:DailyOrderSummary
github-token: ${{ secrets.GITHUB_TOKEN }}
That's it. The action detects your Python build tool (Poetry or pip),
installs dependencies, calls your build() method, extracts the plan,
diffs it against the baseline from main, and posts findings on your PR.
How It Works
The pipeline-class input takes a Python import path. Under the hood:
- The action detects your build tool (
pyproject.tomlwith Poetry, or pip) - Installs your project (
poetry installorpip install -e .) - Runs
cy-py my_package.pipelines:DailyOrderSummaryon both branches cy-pyimports your class, callsbuild(spark), exports the physical plan as JSONcluster-yield-cidiffs the plans and posts findings
Scala ↔ Python Mapping
cy-py is a 1-1 port of the Scala library. Every component has a direct equivalent:
| Scala | Python | Purpose |
|---|---|---|
SparkPipeline trait |
SparkPipeline ABC |
Contract: build(spark) -> DataFrame |
PlanExtractor.exportPlan() |
export_plan() |
Export physical plan as JSON |
PlanExtractor.exportCatalog() |
export_catalog() |
Export table statistics |
PlanExtractor.createExtractionSession() |
create_extraction_session() |
Deterministic SparkSession for CI |
ExtractRunner (reflection) |
extract_runner (import + getattr) |
CLI that resolves and invokes pipelines |
com.example.MyPipeline |
my_package.module:MyPipeline |
Pipeline identification syntax |
MyPipeline#run |
my_package.module:MyPipeline#run |
Custom method override |
The output JSON is identical — plan.toJSON() from Spark's JVM, so the
same analyzer works for both Scala and Python pipelines.
API
SparkPipeline base class
The contract your pipeline implements. Just one method:
from cy_py import SparkPipeline
class MyPipeline(SparkPipeline):
def build(self, spark: SparkSession) -> DataFrame:
...
The base class is optional — cy-py also works with any class that has a
build(self, spark) method, or even a plain function. The base class
provides IDE autocompletion and type checking.
Custom method names
If your method isn't called build, append #methodName:
pipeline-class: my_package.pipelines:MyPipeline#run
Direct usage
For advanced workflows (multiple pipelines, custom Spark configs):
from cy_py import create_extraction_session, export_plan, export_catalog
spark = create_extraction_session()
df = MyPipeline().build(spark)
export_plan(df, "plans/current.json")
export_catalog(spark, ["db.orders", "db.customers"], "plans/catalog.json")
| Function | Description |
|---|---|
export_plan(df, path) |
Export physical plan as JSON (does not execute the query) |
export_catalog(spark, tables, path) |
Export catalog statistics for --catalog flag |
create_extraction_session() |
SparkSession with deterministic CI settings |
Deterministic Spark settings
create_extraction_session() configures Spark for reproducible plans:
spark.sql.adaptive.enabled = false— pre-AQE plan reflects code, not dataspark.sql.autoBroadcastJoinThreshold = -1— simulates production table sizeslocal[1]master, UI disabled, 200 shuffle partitions
These match the Scala extractor exactly, so plans are comparable across languages.
CLI
# Standard (method defaults to "build")
cy-py my_package.pipelines:DailyOrderSummary
# Custom output path
cy-py my_package.pipelines:DailyOrderSummary plans/current.json
# Custom method name
cy-py my_package.pipelines:DailyOrderSummary#run plans/current.json
Outputs plans/current.json (plan array) and plans/current.txt (human-readable tree).
Compatibility
| PySpark | Python | Java | Status |
|---|---|---|---|
| 3.3.x | 3.10+ | 8+ | Supported |
| 3.4.x | 3.10+ | 8+ | Supported |
| 3.5.x | 3.10+ | 11+ | Supported |
PySpark is pinned to >=3.3,<4. Uses only stable Spark APIs via the JVM bridge.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cy_python-0.1.0.tar.gz.
File metadata
- Download URL: cy_python-0.1.0.tar.gz
- Upload date:
- Size: 10.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
91a572f2009b68af30a7cf1c6dcb2c0140aaf4b3a9fa190d0ab6fb9d9d53427e
|
|
| MD5 |
c9da1630fe4d8f3cc788da8d8c466341
|
|
| BLAKE2b-256 |
87833ee98f2c8dcde4e15e2fd7c359d0a0e786f10fa202b6db7496d9121d32ee
|
Provenance
The following attestation bundles were made for cy_python-0.1.0.tar.gz:
Publisher:
publish.yml on clusteryieldanalytics/cy-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cy_python-0.1.0.tar.gz -
Subject digest:
91a572f2009b68af30a7cf1c6dcb2c0140aaf4b3a9fa190d0ab6fb9d9d53427e - Sigstore transparency entry: 946055727
- Sigstore integration time:
-
Permalink:
clusteryieldanalytics/cy-py@e753ba435d14d9fc814e3e0ecfb405b1dddc9dcd -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/clusteryieldanalytics
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e753ba435d14d9fc814e3e0ecfb405b1dddc9dcd -
Trigger Event:
release
-
Statement type:
File details
Details for the file cy_python-0.1.0-py3-none-any.whl.
File metadata
- Download URL: cy_python-0.1.0-py3-none-any.whl
- Upload date:
- Size: 13.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fc2613f7e97a7d0d4bd0186d767e57ca1af211b09fc98c873184befe960d27c0
|
|
| MD5 |
fc2bcf2a43a3fee863af14f2d80a34d5
|
|
| BLAKE2b-256 |
8dfe47d8fdc52328719134cd27986d14604f160e53d429310bc55f8a257694a6
|
Provenance
The following attestation bundles were made for cy_python-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on clusteryieldanalytics/cy-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cy_python-0.1.0-py3-none-any.whl -
Subject digest:
fc2613f7e97a7d0d4bd0186d767e57ca1af211b09fc98c873184befe960d27c0 - Sigstore transparency entry: 946055731
- Sigstore integration time:
-
Permalink:
clusteryieldanalytics/cy-py@e753ba435d14d9fc814e3e0ecfb405b1dddc9dcd -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/clusteryieldanalytics
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e753ba435d14d9fc814e3e0ecfb405b1dddc9dcd -
Trigger Event:
release
-
Statement type: