polymo 1.0.1__tar.gz → 1.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. {polymo-1.0.1 → polymo-1.2.0}/PKG-INFO +27 -25
  2. {polymo-1.0.1 → polymo-1.2.0}/README.md +21 -17
  3. {polymo-1.0.1 → polymo-1.2.0}/pyproject.toml +1 -6
  4. polymo-1.2.0/src/polymo/cli.py +70 -0
  5. {polymo-1.0.1 → polymo-1.2.0}/src/polymo/codegen/templates/core.py.jinja +48 -0
  6. polymo-1.2.0/src/polymo/codegen/templates/dp.py.jinja +131 -0
  7. polymo-1.0.1/src/polymo/cli.py +0 -70
  8. polymo-1.0.1/src/polymo/codegen/templates/dp.py.jinja +0 -97
  9. {polymo-1.0.1 → polymo-1.2.0}/src/polymo/__init__.py +0 -0
  10. {polymo-1.0.1 → polymo-1.2.0}/src/polymo/builder/__init__.py +0 -0
  11. {polymo-1.0.1 → polymo-1.2.0}/src/polymo/builder/app.py +0 -0
  12. {polymo-1.0.1 → polymo-1.2.0}/src/polymo/builder/preview.py +0 -0
  13. {polymo-1.0.1 → polymo-1.2.0}/src/polymo/builder/static/examples/countries.polymo.json +0 -0
  14. {polymo-1.0.1 → polymo-1.2.0}/src/polymo/builder/static/examples/githubrepos.polymo.json +0 -0
  15. {polymo-1.0.1 → polymo-1.2.0}/src/polymo/builder/static/examples/jsonplaceholder.polymo.json +0 -0
  16. {polymo-1.0.1 → polymo-1.2.0}/src/polymo/builder/static/examples/jsonplaceholder_endpoints.polymo.json +0 -0
  17. {polymo-1.0.1 → polymo-1.2.0}/src/polymo/builder/static/examples/weather.polymo.json +0 -0
  18. {polymo-1.0.1 → polymo-1.2.0}/src/polymo/builder/static/favicon.ico +0 -0
  19. {polymo-1.0.1 → polymo-1.2.0}/src/polymo/builder/static/favicon.png +0 -0
  20. {polymo-1.0.1 → polymo-1.2.0}/src/polymo/builder/static/index.html +0 -0
  21. {polymo-1.0.1 → polymo-1.2.0}/src/polymo/builder/static/logo.png +0 -0
  22. {polymo-1.0.1 → polymo-1.2.0}/src/polymo/builder/static/logo192.png +0 -0
  23. {polymo-1.0.1 → polymo-1.2.0}/src/polymo/builder/static/main.css +0 -0
  24. {polymo-1.0.1 → polymo-1.2.0}/src/polymo/builder/static/main.js +0 -0
  25. {polymo-1.0.1 → polymo-1.2.0}/src/polymo/builder/static/mockServiceWorker.js +0 -0
  26. {polymo-1.0.1 → polymo-1.2.0}/src/polymo/builder/templates/index.html +0 -0
  27. {polymo-1.0.1 → polymo-1.2.0}/src/polymo/codegen/__init__.py +0 -0
  28. {polymo-1.0.1 → polymo-1.2.0}/src/polymo/codegen/generator.py +0 -0
  29. {polymo-1.0.1 → polymo-1.2.0}/src/polymo/codegen/templating.py +0 -0
  30. {polymo-1.0.1 → polymo-1.2.0}/src/polymo/config.py +0 -0
  31. {polymo-1.0.1 → polymo-1.2.0}/src/polymo/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: polymo
3
- Version: 1.0.1
3
+ Version: 1.2.0
4
4
  Summary: Generate standalone Lakeflow Declarative Pipelines scripts from a point-and-click REST connector builder
5
5
  Keywords: spark,pyspark,rest,api,ingestion,data-engineering,etl,http
6
6
  Author: Daniel Tom
@@ -21,18 +21,16 @@ Classifier: Programming Language :: Python :: 3.13
21
21
  Classifier: Operating System :: OS Independent
22
22
  Classifier: Typing :: Typed
23
23
  Requires-Dist: jinja2>=3.1.6
24
- Requires-Dist: fastapi>=0.110 ; extra == 'builder'
25
- Requires-Dist: uvicorn>=0.24 ; extra == 'builder'
26
- Requires-Dist: jinja2>=3.1.6 ; extra == 'builder'
27
- Requires-Dist: pyspark>=4 ; extra == 'builder'
28
- Requires-Dist: pyarrow>=13 ; extra == 'builder'
29
- Requires-Dist: requests>=2.31 ; extra == 'builder'
24
+ Requires-Dist: fastapi>=0.110
25
+ Requires-Dist: uvicorn>=0.24
26
+ Requires-Dist: pyspark>=4
27
+ Requires-Dist: pyarrow>=13
28
+ Requires-Dist: requests>=2.31
30
29
  Requires-Python: >=3.10
31
30
  Project-URL: Documentation, https://dan1elt0m.github.io/polymo/
32
31
  Project-URL: Homepage, https://github.com/dan1elt0m/polymo
33
32
  Project-URL: Issues, https://github.com/dan1elt0m/polymo/issues
34
33
  Project-URL: Repository, https://github.com/dan1elt0m/polymo.git
35
- Provides-Extra: builder
36
34
  Description-Content-Type: text/markdown
37
35
 
38
36
  <p align="center">
@@ -71,10 +69,16 @@ PARAMS: dict = {"_limit": 20}
71
69
 
72
70
  # … fetch/retry helpers elided …
73
71
 
72
+ # … inline Data Source (RestSource/_Reader) elided; the batch @dp.table
73
+ # below ingests through it, same as Lakeflow requires for the streaming
74
+ # variant …
75
+
76
+ spark.dataSource.register(RestSource)
77
+
78
+
74
79
  @dp.table(name="posts")
75
80
  def posts():
76
- rows = list(fetch_records())
77
- return spark.createDataFrame(rows)
81
+ return spark.read.format("posts_source").load()
78
82
  ```
79
83
 
80
84
  See [docs/superpowers/specs/2026-08-28-codegen-pivot-design.md](docs/superpowers/specs/2026-08-28-codegen-pivot-design.md) for the full design behind this pivot.
@@ -108,30 +112,28 @@ The YAML runtime (`spark.read.format("polymo")`, `PolymoConfig`,
108
112
  ready to move yet.
109
113
 
110
114
  ## How to start?
111
- Locally you probably want to install polymo along with the Builder UI:
112
115
 
113
116
  ```bash
114
- pip install "polymo[builder]"
117
+ pip install polymo
115
118
  ```
116
119
 
117
- This comes with all UI deps such as pyspark.
120
+ This installs everything you need, Builder UI included — FastAPI, Uvicorn,
121
+ PySpark, PyArrow, and requests all come along, so there's nothing extra to
122
+ opt into.
123
+
124
+ Note that `polymo` is never a dependency of the scripts it generates —
125
+ nothing you export needs polymo installed to run.
126
+
127
+ ## Launch the builder UI
118
128
 
119
- Scripting connector generation directly in Python (calling `generate()` /
120
- `parse_config()` without the UI — see the [API reference](docs/api.md))
121
- doesn't need pyspark at all. In that case, just install the bare minimum
122
- deps with:
123
129
  ```bash
124
- pip install polymo
130
+ polymo
125
131
  ```
126
132
 
127
- Note that `pip install polymo` alone does not install the Builder UI, and
128
- `polymo` is never a dependency of the scripts it generates — nothing you
129
- export needs polymo installed to run.
133
+ Or without installing it first:
130
134
 
131
- ## Launch the builder UI
132
-
133
- ```bash
134
- polymo builder
135
+ ```bash
136
+ uvx polymo
135
137
  ```
136
138
 
137
139
  #### (Optional) Run the Builder in Docker
@@ -34,10 +34,16 @@ PARAMS: dict = {"_limit": 20}
34
34
 
35
35
  # … fetch/retry helpers elided …
36
36
 
37
+ # … inline Data Source (RestSource/_Reader) elided; the batch @dp.table
38
+ # below ingests through it, same as Lakeflow requires for the streaming
39
+ # variant …
40
+
41
+ spark.dataSource.register(RestSource)
42
+
43
+
37
44
  @dp.table(name="posts")
38
45
  def posts():
39
- rows = list(fetch_records())
40
- return spark.createDataFrame(rows)
46
+ return spark.read.format("posts_source").load()
41
47
  ```
42
48
 
43
49
  See [docs/superpowers/specs/2026-08-28-codegen-pivot-design.md](docs/superpowers/specs/2026-08-28-codegen-pivot-design.md) for the full design behind this pivot.
@@ -71,30 +77,28 @@ The YAML runtime (`spark.read.format("polymo")`, `PolymoConfig`,
71
77
  ready to move yet.
72
78
 
73
79
  ## How to start?
74
- Locally you probably want to install polymo along with the Builder UI:
75
80
 
76
81
  ```bash
77
- pip install "polymo[builder]"
82
+ pip install polymo
78
83
  ```
79
84
 
80
- This comes with all UI deps such as pyspark.
85
+ This installs everything you need, Builder UI included — FastAPI, Uvicorn,
86
+ PySpark, PyArrow, and requests all come along, so there's nothing extra to
87
+ opt into.
88
+
89
+ Note that `polymo` is never a dependency of the scripts it generates —
90
+ nothing you export needs polymo installed to run.
91
+
92
+ ## Launch the builder UI
81
93
 
82
- Scripting connector generation directly in Python (calling `generate()` /
83
- `parse_config()` without the UI — see the [API reference](docs/api.md))
84
- doesn't need pyspark at all. In that case, just install the bare minimum
85
- deps with:
86
94
  ```bash
87
- pip install polymo
95
+ polymo
88
96
  ```
89
97
 
90
- Note that `pip install polymo` alone does not install the Builder UI, and
91
- `polymo` is never a dependency of the scripts it generates — nothing you
92
- export needs polymo installed to run.
98
+ Or without installing it first:
93
99
 
94
- ## Launch the builder UI
95
-
96
- ```bash
97
- polymo builder
100
+ ```bash
101
+ uvx polymo
98
102
  ```
99
103
 
100
104
  #### (Optional) Run the Builder in Docker
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "polymo"
3
- version = "1.0.1"
3
+ version = "1.2.0"
4
4
  description = "Generate standalone Lakeflow Declarative Pipelines scripts from a point-and-click REST connector builder"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -27,13 +27,8 @@ classifiers = [
27
27
  ]
28
28
  dependencies = [
29
29
  "jinja2>=3.1.6",
30
- ]
31
-
32
- [project.optional-dependencies]
33
- builder = [
34
30
  "fastapi>=0.110",
35
31
  "uvicorn>=0.24",
36
- "jinja2>=3.1.6",
37
32
  "pyspark>=4",
38
33
  "pyarrow>=13",
39
34
  "requests>=2.31",
@@ -0,0 +1,70 @@
1
+ """Command line entry points for polymo."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ from typing import Sequence
7
+
8
+
9
+ def main(argv: Sequence[str] | None = None) -> int:
10
+ parser = argparse.ArgumentParser(
11
+ prog="polymo", description="Launch the local polymo Builder UI"
12
+ )
13
+ parser.add_argument(
14
+ "--host", default="127.0.0.1", help="Host to bind (default: %(default)s)"
15
+ )
16
+ parser.add_argument(
17
+ "--port", type=int, default=8000, help="Port to bind (default: %(default)s)"
18
+ )
19
+ parser.add_argument(
20
+ "--reload",
21
+ action="store_true",
22
+ help="Enable auto-reload (useful during development)",
23
+ )
24
+
25
+ args = parser.parse_args(argv)
26
+
27
+ if not _require_builder_deps():
28
+ return 1
29
+
30
+ import uvicorn
31
+
32
+ uvicorn.run(
33
+ "polymo.builder.app:create_app",
34
+ host=args.host,
35
+ port=args.port,
36
+ reload=args.reload,
37
+ factory=True,
38
+ )
39
+ return 0
40
+
41
+
42
+ def _require_builder_deps() -> bool:
43
+ """Verify the install is intact (pyspark present and compatible).
44
+
45
+ polymo now depends on pyspark unconditionally, so a missing or
46
+ incompatible pyspark means the install itself is broken. Returns True
47
+ when the environment is ready to launch the builder. On a missing
48
+ pyspark install, prints a friendly hint and returns False so the caller
49
+ can exit cleanly instead of tracebacking.
50
+ """
51
+ try:
52
+ import pyspark
53
+ except ModuleNotFoundError:
54
+ print(
55
+ "polymo's dependencies are incomplete — reinstall with: "
56
+ "pip install --force-reinstall polymo"
57
+ )
58
+ return False
59
+
60
+ if not pyspark.__version__.startswith("4."):
61
+ raise ImportError(
62
+ "pyspark>=4.0.0 is required: reinstall with "
63
+ "pip install --force-reinstall polymo"
64
+ )
65
+
66
+ return True
67
+
68
+
69
+ if __name__ == "__main__": # pragma: no cover
70
+ raise SystemExit(main())
@@ -45,6 +45,7 @@ WINDOWS = {{ windows_repr }}
45
45
  # durable path (e.g. a Databricks Volume: /Volumes/cat/schema/vol/{{ stream_name_comment }}.json).
46
46
  STATE_PATH = {{ state_path_repr }}
47
47
  {% if not has_windows %}
48
+ # used by the builder preview; the dp table below tracks its own cursor
48
49
  LAST_CURSOR = {"value": None}
49
50
  {% endif %}
50
51
 
@@ -448,6 +449,53 @@ def fetch_records(extra_params=None, path=None):
448
449
  LAST_CURSOR["value"] = value
449
450
  {% endif %}
450
451
  {% endif %}
452
+ {% if not schema_ddl %}
453
+
454
+
455
+ def _infer_schema():
456
+ """Sample records to derive a DDL schema (explicit SCHEMA recommended)."""
457
+ from itertools import islice
458
+
459
+ {% if has_windows %}
460
+ sample = []
461
+ for window in WINDOWS:
462
+ sample.extend(islice(fetch_records(**window), max(1, 50 // len(WINDOWS))))
463
+ {% else %}
464
+ sample = list(islice(fetch_records(), 50))
465
+ {% endif %}
466
+ if not sample:
467
+ raise RuntimeError(
468
+ "cannot infer a schema from an empty response; set an explicit schema"
469
+ )
470
+ fields = {}
471
+ for record in sample:
472
+ for key, value in record.items():
473
+ if value is None:
474
+ continue
475
+ if isinstance(value, bool):
476
+ candidate = "BOOLEAN"
477
+ elif isinstance(value, int):
478
+ candidate = "BIGINT"
479
+ elif isinstance(value, float):
480
+ candidate = "DOUBLE"
481
+ else:
482
+ candidate = "STRING"
483
+ current = fields.get(key)
484
+ if current is None:
485
+ fields[key] = candidate
486
+ elif current == candidate:
487
+ pass
488
+ elif {current, candidate} == {"BIGINT", "DOUBLE"}:
489
+ fields[key] = "DOUBLE"
490
+ else:
491
+ fields[key] = "STRING"
492
+ if not fields:
493
+ raise RuntimeError(
494
+ "cannot infer a schema: sampled records have no fields; set an explicit"
495
+ " schema"
496
+ )
497
+ return ", ".join(f"`{name}` {type_}" for name, type_ in fields.items())
498
+ {% endif %}
451
499
  {% if streaming %}
452
500
 
453
501
 
@@ -0,0 +1,131 @@
1
+ from pyspark import pipelines as dp # noqa: E402
2
+ from pyspark.sql import SparkSession # noqa: E402
3
+
4
+ spark = SparkSession.getActiveSession()
5
+
6
+ {% if schema_ddl %}
7
+ SCHEMA = {{ schema_ddl | tojson }}
8
+
9
+ {% endif %}
10
+
11
+ {% if streaming %}
12
+ from pyspark.sql.datasource import DataSource, SimpleDataSourceStreamReader # noqa: E402
13
+
14
+ # Assumes SCHEMA is a comma-separated "name TYPE" DDL string (as emitted
15
+ # above); column names are taken as the first whitespace-separated token.
16
+ COLUMNS = [f.split()[0] for f in SCHEMA.split(",")]
17
+
18
+
19
+ class _Reader(SimpleDataSourceStreamReader):
20
+ def initialOffset(self):
21
+ return {"page": 0}
22
+
23
+ def read(self, start):
24
+ page = start["page"]
25
+ records = fetch_page(page)
26
+ rows = [tuple(r.get(c) for c in COLUMNS) for r in records]
27
+ next_page = page + 1 if records else page
28
+ return iter(rows), {"page": next_page}
29
+
30
+ def readBetweenOffsets(self, start, end):
31
+ rows = []
32
+ for page in range(start["page"], end["page"]):
33
+ for r in fetch_page(page):
34
+ rows.append(tuple(r.get(c) for c in COLUMNS))
35
+ return iter(rows)
36
+
37
+
38
+ class RestStreamSource(DataSource):
39
+ @classmethod
40
+ def name(cls):
41
+ return "{{ func_name }}_stream"
42
+
43
+ def schema(self):
44
+ return SCHEMA
45
+
46
+ def simpleStreamReader(self, schema):
47
+ return _Reader()
48
+
49
+
50
+ spark.dataSource.register(RestStreamSource)
51
+
52
+
53
+ @dp.table(name={{ table_name_repr }})
54
+ def {{ func_name }}():
55
+ return spark.readStream.format("{{ func_name }}_stream").load()
56
+ {% else %}
57
+ {% if has_windows %}
58
+ from pyspark.sql.datasource import DataSource, DataSourceReader, InputPartition # noqa: E402
59
+ {% else %}
60
+ from pyspark.sql.datasource import DataSource, DataSourceReader # noqa: E402
61
+ {% endif %}
62
+
63
+
64
+ class RestSource(DataSource):
65
+ @classmethod
66
+ def name(cls):
67
+ return "{{ func_name }}_source"
68
+
69
+ def schema(self):
70
+ return {% if schema_ddl %}SCHEMA{% else %}_infer_schema(){% endif %}
71
+
72
+
73
+ def reader(self, schema):
74
+ return _Reader(schema)
75
+
76
+
77
+ class _Reader(DataSourceReader):
78
+ def __init__(self, schema):
79
+ self._columns = [field.name for field in schema.fields]
80
+
81
+ {% if has_windows %}
82
+ def partitions(self):
83
+ return [InputPartition(index) for index in range(len(WINDOWS))]
84
+
85
+ def read(self, partition):
86
+ records = fetch_records(**WINDOWS[partition.value])
87
+ {% else %}
88
+ def read(self, partition):
89
+ records = fetch_records()
90
+ {% endif %}
91
+ {% if incremental_mode %}
92
+ cursor = None
93
+ {% endif %}
94
+ for record in records:
95
+ {% if incremental_mode %}
96
+ value = record.get({{ cursor_field_repr }})
97
+ if value is not None and (cursor is None or value > cursor):
98
+ cursor = value
99
+ {% endif %}
100
+ yield tuple(_cell(record.get(column)) for column in self._columns)
101
+ {% if incremental_mode %}
102
+ # read() runs per partition on the executor that owns it, so each
103
+ # partition writes its own max cursor independently; whichever
104
+ # partition finishes last wins. Batch tables re-fetch fully each
105
+ # run, so a stale cursor only costs redundant fetching, not missed
106
+ # data. STATE_PATH must point somewhere every executor can reach
107
+ # (e.g. a Databricks Volume, per the comment on it above) for this
108
+ # to work at all.
109
+ _write_state(cursor)
110
+ {% endif %}
111
+
112
+
113
+ def _cell(value):
114
+ """Nested structures become JSON strings; scalars pass through."""
115
+ if isinstance(value, (dict, list)):
116
+ import json
117
+
118
+ return json.dumps(value)
119
+ return value
120
+
121
+
122
+ spark.dataSource.register(RestSource)
123
+
124
+
125
+ {% if partition_strategy == "pagination" %}
126
+ # partition strategy "pagination" runs sequentially in generated scripts
127
+ {% endif %}
128
+ @dp.table(name={{ table_name_repr }})
129
+ def {{ func_name }}():
130
+ return spark.read.format("{{ func_name }}_source").load()
131
+ {% endif %}
@@ -1,70 +0,0 @@
1
- """Command line entry points for polymo."""
2
-
3
- from __future__ import annotations
4
-
5
- import argparse
6
- from typing import Sequence
7
-
8
-
9
- def main(argv: Sequence[str] | None = None) -> int:
10
- parser = argparse.ArgumentParser(
11
- prog="polymo", description="Utilities for the polymo toolkit"
12
- )
13
- subparsers = parser.add_subparsers(dest="command")
14
- bld_parser = subparsers.add_parser("builder", help="Launch the local builder UI")
15
- bld_parser.add_argument(
16
- "--host", default="127.0.0.1", help="Host to bind (default: %(default)s)"
17
- )
18
- bld_parser.add_argument(
19
- "--port", type=int, default=8000, help="Port to bind (default: %(default)s)"
20
- )
21
- bld_parser.add_argument(
22
- "--reload",
23
- action="store_true",
24
- help="Enable auto-reload (useful during development)",
25
- )
26
-
27
- args = parser.parse_args(argv)
28
-
29
- if args.command == "builder":
30
- if not _require_builder_deps():
31
- return 1
32
-
33
- import uvicorn
34
-
35
- uvicorn.run(
36
- "polymo.builder.app:create_app",
37
- host=args.host,
38
- port=args.port,
39
- reload=args.reload,
40
- factory=True,
41
- )
42
- return 0
43
-
44
- parser.print_help()
45
- return 1
46
-
47
-
48
- def _require_builder_deps() -> bool:
49
- """Verify the ``builder`` extra (pyspark) is installed and compatible.
50
-
51
- Returns True when the environment is ready to launch the builder.
52
- On a missing pyspark install, prints a friendly hint and returns False
53
- so the caller can exit cleanly instead of tracebacking.
54
- """
55
- try:
56
- import pyspark
57
- except ModuleNotFoundError:
58
- print("The builder needs extras: pip install 'polymo[builder]'")
59
- return False
60
-
61
- if not pyspark.__version__.startswith("4."):
62
- raise ImportError(
63
- "pyspark>=4.0.0 is required: run pip install 'polymo[builder]'"
64
- )
65
-
66
- return True
67
-
68
-
69
- if __name__ == "__main__": # pragma: no cover
70
- raise SystemExit(main())
@@ -1,97 +0,0 @@
1
- from pyspark import pipelines as dp # noqa: E402
2
- from pyspark.sql import SparkSession # noqa: E402
3
-
4
- spark = SparkSession.getActiveSession()
5
-
6
- {% if schema_ddl %}
7
- SCHEMA = {{ schema_ddl | tojson }}
8
-
9
- {% endif %}
10
-
11
- {% if streaming %}
12
- from pyspark.sql.datasource import DataSource, SimpleDataSourceStreamReader # noqa: E402
13
-
14
- # Assumes SCHEMA is a comma-separated "name TYPE" DDL string (as emitted
15
- # above); column names are taken as the first whitespace-separated token.
16
- COLUMNS = [f.split()[0] for f in SCHEMA.split(",")]
17
-
18
-
19
- class _Reader(SimpleDataSourceStreamReader):
20
- def initialOffset(self):
21
- return {"page": 0}
22
-
23
- def read(self, start):
24
- page = start["page"]
25
- records = fetch_page(page)
26
- rows = [tuple(r.get(c) for c in COLUMNS) for r in records]
27
- next_page = page + 1 if records else page
28
- return iter(rows), {"page": next_page}
29
-
30
- def readBetweenOffsets(self, start, end):
31
- rows = []
32
- for page in range(start["page"], end["page"]):
33
- for r in fetch_page(page):
34
- rows.append(tuple(r.get(c) for c in COLUMNS))
35
- return iter(rows)
36
-
37
-
38
- class RestStreamSource(DataSource):
39
- @classmethod
40
- def name(cls):
41
- return "{{ func_name }}_stream"
42
-
43
- def schema(self):
44
- return SCHEMA
45
-
46
- def simpleStreamReader(self, schema):
47
- return _Reader()
48
-
49
-
50
- spark.dataSource.register(RestStreamSource)
51
-
52
-
53
- @dp.table(name={{ table_name_repr }})
54
- def {{ func_name }}():
55
- return spark.readStream.format("{{ func_name }}_stream").load()
56
- {% else %}
57
- {% if windows_repr %}
58
- @dp.table(name={{ table_name_repr }})
59
- def {{ func_name }}():
60
- sc = spark.sparkContext
61
- rows = sc.parallelize(WINDOWS, len(WINDOWS)).flatMap(
62
- lambda window: list(fetch_records(**window))
63
- ).collect()
64
- {% if incremental_mode %}
65
- # fetch_records runs on executors under parallelize/flatMap, which don't
66
- # share memory with the driver, so the cursor can't be tracked as
67
- # in-process state; compute it here instead, from the rows collected
68
- # back on the driver.
69
- cursor_values = [
70
- r.get({{ cursor_field_repr }})
71
- for r in rows
72
- if isinstance(r, dict) and r.get({{ cursor_field_repr }}) is not None
73
- ]
74
- _write_state(max(cursor_values) if cursor_values else None)
75
- {% endif %}
76
- {% if schema_ddl %}
77
- return spark.createDataFrame(rows, schema=SCHEMA)
78
- {% else %}
79
- return spark.createDataFrame(rows)
80
- {% endif %}
81
- {% else %}
82
- {% if partition_strategy == "pagination" %}
83
- # partition strategy "pagination" runs sequentially in generated scripts
84
- {% endif %}
85
- @dp.table(name={{ table_name_repr }})
86
- def {{ func_name }}():
87
- rows = list(fetch_records())
88
- {% if incremental_mode %}
89
- _write_state(LAST_CURSOR["value"])
90
- {% endif %}
91
- {% if schema_ddl %}
92
- return spark.createDataFrame(rows, schema=SCHEMA)
93
- {% else %}
94
- return spark.createDataFrame(rows)
95
- {% endif %}
96
- {% endif %}
97
- {% endif %}
File without changes
File without changes
File without changes
File without changes