streamwright 0.0.1__tar.gz → 0.1.2__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 (61) hide show
  1. streamwright-0.1.2/PKG-INFO +77 -0
  2. streamwright-0.1.2/README.md +49 -0
  3. streamwright-0.1.2/pyproject.toml +62 -0
  4. streamwright-0.1.2/setup.py +21 -0
  5. streamwright-0.1.2/src/streamwright/core/__init__.py +18 -0
  6. streamwright-0.1.2/src/streamwright/core/catalog.py +150 -0
  7. streamwright-0.1.2/src/streamwright/core/cli.py +590 -0
  8. streamwright-0.1.2/src/streamwright/core/config/__init__.py +0 -0
  9. streamwright-0.1.2/src/streamwright/core/config/inputs.py +228 -0
  10. streamwright-0.1.2/src/streamwright/core/config/loader.py +220 -0
  11. streamwright-0.1.2/src/streamwright/core/config/reader.py +34 -0
  12. streamwright-0.1.2/src/streamwright/core/connectors.json +91 -0
  13. streamwright-0.1.2/src/streamwright/core/engine/__init__.py +0 -0
  14. streamwright-0.1.2/src/streamwright/core/engine/queries.py +130 -0
  15. streamwright-0.1.2/src/streamwright/core/engine/runner.py +983 -0
  16. streamwright-0.1.2/src/streamwright/core/engine/sql.py +1294 -0
  17. streamwright-0.1.2/src/streamwright/core/net/__init__.py +0 -0
  18. streamwright-0.1.2/src/streamwright/core/net/downloads.py +93 -0
  19. streamwright-0.1.2/src/streamwright/core/net/http.py +453 -0
  20. streamwright-0.1.2/src/streamwright/core/outputs/__init__.py +0 -0
  21. streamwright-0.1.2/src/streamwright/core/outputs/dlt_output.py +252 -0
  22. streamwright-0.1.2/src/streamwright/core/outputs/exporter.py +289 -0
  23. streamwright-0.1.2/src/streamwright/core/outputs/output.py +509 -0
  24. streamwright-0.1.2/src/streamwright/core/outputs/staging.py +206 -0
  25. streamwright-0.1.2/src/streamwright/core/outputs/warehouse.py +546 -0
  26. streamwright-0.1.2/src/streamwright/core/runtime/__init__.py +0 -0
  27. streamwright-0.1.2/src/streamwright/core/runtime/components.py +392 -0
  28. streamwright-0.1.2/src/streamwright/core/runtime/logs.py +791 -0
  29. streamwright-0.1.2/src/streamwright/core/runtime/templates.py +199 -0
  30. streamwright-0.1.2/src/streamwright/core/runtime/testing.py +179 -0
  31. streamwright-0.1.2/src/streamwright/core/validation/__init__.py +0 -0
  32. streamwright-0.1.2/src/streamwright/core/validation/engine.py +595 -0
  33. streamwright-0.1.2/src/streamwright/core/validation/schema.py +384 -0
  34. streamwright-0.1.2/src/streamwright/core/validation/source.py +1187 -0
  35. streamwright-0.1.2/src/streamwright.egg-info/PKG-INFO +77 -0
  36. streamwright-0.1.2/src/streamwright.egg-info/SOURCES.txt +51 -0
  37. streamwright-0.1.2/src/streamwright.egg-info/entry_points.txt +2 -0
  38. streamwright-0.1.2/src/streamwright.egg-info/requires.txt +7 -0
  39. streamwright-0.1.2/src/streamwright.egg-info/top_level.txt +1 -0
  40. streamwright-0.1.2/tests/test_connectors.py +534 -0
  41. streamwright-0.1.2/tests/test_dlt_output.py +304 -0
  42. streamwright-0.1.2/tests/test_exact_decimals.py +442 -0
  43. streamwright-0.1.2/tests/test_exporter.py +69 -0
  44. streamwright-0.1.2/tests/test_logging.py +968 -0
  45. streamwright-0.1.2/tests/test_output_formats.py +456 -0
  46. streamwright-0.1.2/tests/test_runner.py +866 -0
  47. streamwright-0.1.2/tests/test_sql.py +380 -0
  48. streamwright-0.1.2/tests/test_transform.py +1297 -0
  49. streamwright-0.1.2/tests/test_validation.py +1735 -0
  50. streamwright-0.1.2/tests/test_warehouse_output.py +591 -0
  51. streamwright-0.0.1/PKG-INFO +0 -40
  52. streamwright-0.0.1/README.md +0 -17
  53. streamwright-0.0.1/pyproject.toml +0 -34
  54. streamwright-0.0.1/streamwright.egg-info/PKG-INFO +0 -40
  55. streamwright-0.0.1/streamwright.egg-info/SOURCES.txt +0 -9
  56. streamwright-0.0.1/streamwright.egg-info/entry_points.txt +0 -2
  57. streamwright-0.0.1/streamwright.egg-info/requires.txt +0 -1
  58. streamwright-0.0.1/streamwright.egg-info/top_level.txt +0 -1
  59. {streamwright-0.0.1 → streamwright-0.1.2}/LICENSE +0 -0
  60. {streamwright-0.0.1 → streamwright-0.1.2}/setup.cfg +0 -0
  61. {streamwright-0.0.1 → streamwright-0.1.2/src}/streamwright.egg-info/dependency_links.txt +0 -0
@@ -0,0 +1,77 @@
1
+ Metadata-Version: 2.4
2
+ Name: streamwright
3
+ Version: 0.1.2
4
+ Summary: Declarative data connectors — pull from APIs, files and databases with YAML, shape with SQL, write anywhere.
5
+ Author: Karthick Jaganathan
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/karthick-jaganathan/streamwright
8
+ Project-URL: Repository, https://github.com/karthick-jaganathan/streamwright
9
+ Project-URL: Documentation, https://streamwright.web.app/docs/
10
+ Keywords: data,pipeline,etl,connector,singer,yaml
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: pyyaml==6.0.2
22
+ Requires-Dist: requests<3,>=2.25
23
+ Requires-Dist: duckdb>=1.5.2
24
+ Requires-Dist: pytz
25
+ Provides-Extra: dlt
26
+ Requires-Dist: dlt>=1.0; extra == "dlt"
27
+ Dynamic: license-file
28
+
29
+ # StreamWright — the `streamwright` CLI and runtime
30
+
31
+ **StreamWright turns a few YAML files into a working data pipeline.** You describe *what*
32
+ to pull — from an API, a file store or a database — and StreamWright handles the *how*:
33
+ signing in, paging, retries, shaping the data with DuckDB SQL, and writing it out.
34
+ No per-connector code, and one command runs any source.
35
+
36
+ The `streamwright` package is the heart of it: the `streamwright` command plus the runtime for
37
+ `kind: source` sources. *(Formerly published as `adapt-core`.)*
38
+
39
+ - **`streamwright run`** executes a source · **`streamwright validate`** checks one · **`streamwright connectors`** browses and installs connectors.
40
+ - A source is a folder — `source.yaml` (`spec`, `auth`, `http`) and one file per stream in `streams/` — or a single YAML file.
41
+ - Each stream **fetches** (HTTP requests / SDK connectors), **shapes** (sandboxed DuckDB SQL), and **writes** (files, DuckDB/DuckLake or dlt).
42
+
43
+ ## Install
44
+
45
+ ```bash
46
+ pip install streamwright
47
+ streamwright --help
48
+ ```
49
+
50
+ This installs the `streamwright` CLI, the runtime and the connector hub — but no
51
+ connectors yet. Add the ones you need on demand:
52
+
53
+ ```bash
54
+ streamwright connectors list # browse the hub
55
+ streamwright connectors install files # a reader (files, s3, gcs, postgres)
56
+ streamwright connectors install google_ads # an API connector (pulls in its SDK)
57
+ ```
58
+
59
+ ## Quickstart
60
+
61
+ ```bash
62
+ pip install streamwright
63
+ streamwright connectors install files
64
+
65
+ # the runnable examples live in the repo — clone it to try one end-to-end
66
+ git clone https://github.com/karthick-jaganathan/streamwright.git
67
+ streamwright validate streamwright/examples
68
+ streamwright run streamwright/examples/sources/readers/files_demo \
69
+ --set data_root=streamwright/examples/sources/readers/files_demo/data \
70
+ --allow-connector files --output jsonl:out
71
+ ```
72
+
73
+ 📖 **Full documentation:** https://streamwright.web.app/docs/ — command line,
74
+ streams, outputs, logging, connectors & readers and the Python API.
75
+
76
+ - Architecture guide: https://streamwright.web.app/docs/architecture/
77
+ - Connectors & readers: https://streamwright.web.app/docs/connectors/
@@ -0,0 +1,49 @@
1
+ # StreamWright — the `streamwright` CLI and runtime
2
+
3
+ **StreamWright turns a few YAML files into a working data pipeline.** You describe *what*
4
+ to pull — from an API, a file store or a database — and StreamWright handles the *how*:
5
+ signing in, paging, retries, shaping the data with DuckDB SQL, and writing it out.
6
+ No per-connector code, and one command runs any source.
7
+
8
+ The `streamwright` package is the heart of it: the `streamwright` command plus the runtime for
9
+ `kind: source` sources. *(Formerly published as `adapt-core`.)*
10
+
11
+ - **`streamwright run`** executes a source · **`streamwright validate`** checks one · **`streamwright connectors`** browses and installs connectors.
12
+ - A source is a folder — `source.yaml` (`spec`, `auth`, `http`) and one file per stream in `streams/` — or a single YAML file.
13
+ - Each stream **fetches** (HTTP requests / SDK connectors), **shapes** (sandboxed DuckDB SQL), and **writes** (files, DuckDB/DuckLake or dlt).
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ pip install streamwright
19
+ streamwright --help
20
+ ```
21
+
22
+ This installs the `streamwright` CLI, the runtime and the connector hub — but no
23
+ connectors yet. Add the ones you need on demand:
24
+
25
+ ```bash
26
+ streamwright connectors list # browse the hub
27
+ streamwright connectors install files # a reader (files, s3, gcs, postgres)
28
+ streamwright connectors install google_ads # an API connector (pulls in its SDK)
29
+ ```
30
+
31
+ ## Quickstart
32
+
33
+ ```bash
34
+ pip install streamwright
35
+ streamwright connectors install files
36
+
37
+ # the runnable examples live in the repo — clone it to try one end-to-end
38
+ git clone https://github.com/karthick-jaganathan/streamwright.git
39
+ streamwright validate streamwright/examples
40
+ streamwright run streamwright/examples/sources/readers/files_demo \
41
+ --set data_root=streamwright/examples/sources/readers/files_demo/data \
42
+ --allow-connector files --output jsonl:out
43
+ ```
44
+
45
+ 📖 **Full documentation:** https://streamwright.web.app/docs/ — command line,
46
+ streams, outputs, logging, connectors & readers and the Python API.
47
+
48
+ - Architecture guide: https://streamwright.web.app/docs/architecture/
49
+ - Connectors & readers: https://streamwright.web.app/docs/connectors/
@@ -0,0 +1,62 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "streamwright"
7
+ version = "0.1.2"
8
+ description = "Declarative data connectors — pull from APIs, files and databases with YAML, shape with SQL, write anywhere."
9
+ readme = "README.md"
10
+ license = "Apache-2.0"
11
+ authors = [
12
+ {name = "Karthick Jaganathan"},
13
+ ]
14
+ keywords = ["data", "pipeline", "etl", "connector", "singer", "yaml"]
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Intended Audience :: Developers",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ ]
24
+ requires-python = ">=3.10"
25
+ dependencies = [
26
+ "pyyaml==6.0.2",
27
+ "requests>=2.25,<3",
28
+ # `select`: the SQL that shapes each stream's records runs in an embedded DuckDB (which needs pytz for
29
+ # timestamps with time zones)
30
+ "duckdb>=1.5.2",
31
+ "pytz",
32
+ ]
33
+
34
+ [project.optional-dependencies]
35
+ # --output dlt:DESTINATION; also install the destination's extra, e.g. "dlt[duckdb]" or "dlt[bigquery]"
36
+ dlt = ["dlt>=1.0"]
37
+
38
+ [project.urls]
39
+ Homepage = "https://github.com/karthick-jaganathan/streamwright"
40
+ Repository = "https://github.com/karthick-jaganathan/streamwright"
41
+ Documentation = "https://streamwright.web.app/docs/"
42
+
43
+ [project.scripts]
44
+ # `streamwright run`, `streamwright validate`, `streamwright connectors` - one CLI (see streamwright.core.cli)
45
+ streamwright = "streamwright.core.cli:main"
46
+
47
+ [tool.setuptools]
48
+ # src layout: `streamwright` is a namespace package shared with the connector distributions (streamwright.connectors.*)
49
+ package-dir = {"" = "src"}
50
+ packages = [
51
+ "streamwright.core",
52
+ "streamwright.core.engine",
53
+ "streamwright.core.outputs",
54
+ "streamwright.core.net",
55
+ "streamwright.core.config",
56
+ "streamwright.core.validation",
57
+ "streamwright.core.runtime",
58
+ ]
59
+
60
+ [tool.setuptools.package-data]
61
+ # the bundled connector catalog (default for `streamwright connectors`, refreshed from $STREAMWRIGHT_HUB_URL)
62
+ "streamwright.core" = ["connectors.json"]
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env python
2
+ # /*************************************************************************
3
+ # * Copyright 2025 Karthick Jaganathan
4
+ # *
5
+ # * Licensed under the Apache License, Version 2.0 (the "License");
6
+ # * you may not use this file except in compliance with the License.
7
+ # * You may obtain a copy of the License at
8
+ # *
9
+ # * https://www.apache.org/licenses/LICENSE-2.0
10
+ # *
11
+ # * Unless required by applicable law or agreed to in writing, software
12
+ # * distributed under the License is distributed on an "AS IS" BASIS,
13
+ # * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # * See the License for the specific language governing permissions and
15
+ # * limitations under the License.
16
+ # **************************************************************************/
17
+
18
+ from setuptools import setup
19
+
20
+ # Configuration is now in pyproject.toml
21
+ setup()
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env python
2
+ # /*************************************************************************
3
+ # * Copyright 2025 Karthick Jaganathan
4
+ # *
5
+ # * Licensed under the Apache License, Version 2.0 (the "License");
6
+ # * you may not use this file except in compliance with the License.
7
+ # * You may obtain a copy of the License at
8
+ # *
9
+ # * https://www.apache.org/licenses/LICENSE-2.0
10
+ # *
11
+ # * Unless required by applicable law or agreed to in writing, software
12
+ # * distributed under the License is distributed on an "AS IS" BASIS,
13
+ # * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # * See the License for the specific language governing permissions and
15
+ # * limitations under the License.
16
+ # **************************************************************************/
17
+
18
+ """Run-time StreamWright sources (`kind: source`). Entry point: the `streamwright` command (streamwright.core.cli)."""
@@ -0,0 +1,150 @@
1
+ #!/usr/bin/env python
2
+ """The connector catalog: discover and install connectors from the StreamWright hub.
3
+
4
+ A catalog is a static index (bundled with streamwright, optionally refreshed from a
5
+ remote hub URL) that maps a connector key to how it's installed - a PyPI
6
+ requirement, a pinned git URL, or an image. The key is the same name used in
7
+ source YAML (`provider`/`sdk`); the install source is just a delivery detail, so
8
+ a package can move from git to PyPI without changing what users type.
9
+
10
+ Security: non-"official" connectors run third-party code, so `install` asks for
11
+ confirmation (or `--yes`) and refuses non-interactive installs without it.
12
+ """
13
+ import json
14
+ import os
15
+ import subprocess
16
+ import sys
17
+ from pathlib import Path
18
+
19
+ try:
20
+ from importlib import resources
21
+ except ImportError: # pragma: no cover
22
+ import importlib_resources as resources
23
+
24
+ from streamwright.core.runtime import components
25
+
26
+ HUB_ENV = "STREAMWRIGHT_HUB_URL"
27
+ CACHE = Path(os.path.expanduser("~/.streamwright/connectors.json"))
28
+
29
+
30
+ def _bundled():
31
+ text = resources.files("streamwright.core").joinpath("connectors.json").read_text()
32
+ return json.loads(text)
33
+
34
+
35
+ def _remote(url):
36
+ import requests
37
+
38
+ response = requests.get(url, timeout=10)
39
+ response.raise_for_status()
40
+ data = response.json()
41
+ try:
42
+ CACHE.parent.mkdir(parents=True, exist_ok=True)
43
+ CACHE.write_text(json.dumps(data))
44
+ except OSError:
45
+ pass
46
+ return data
47
+
48
+
49
+ def load(hub_url=None):
50
+ """The catalog: the remote hub if configured (cached), else the bundled default."""
51
+ url = hub_url or os.environ.get(HUB_ENV)
52
+ if url:
53
+ try:
54
+ return _remote(url)
55
+ except Exception: # offline or hub down: fall back
56
+ pass
57
+ if CACHE.exists():
58
+ try:
59
+ return json.loads(CACHE.read_text())
60
+ except Exception:
61
+ pass
62
+ return _bundled()
63
+
64
+
65
+ def connectors(hub_url=None):
66
+ return load(hub_url).get("connectors", {})
67
+
68
+
69
+ def installed():
70
+ try:
71
+ return set(components.available())
72
+ except Exception:
73
+ return set()
74
+
75
+
76
+ def search(text, hub_url=None):
77
+ text = (text or "").lower()
78
+ hits = {}
79
+ for key, entry in connectors(hub_url).items():
80
+ haystack = " ".join([key, entry.get("title", ""), entry.get("summary", ""),
81
+ entry.get("family", "")]).lower()
82
+ if text in haystack:
83
+ hits[key] = entry
84
+ return hits
85
+
86
+
87
+ def _source_of(install):
88
+ if "pip" in install:
89
+ return "PyPI: " + install["pip"]
90
+ if "git" in install:
91
+ return "git: " + install["git"]
92
+ if "image" in install:
93
+ return "image: " + install["image"]
94
+ return "unknown"
95
+
96
+
97
+ def listing(hub_url=None):
98
+ """Lines for `streamwright connectors list`: every catalog connector with install state + trust."""
99
+ have = installed()
100
+ lines = []
101
+ by_family = {}
102
+ for key, entry in connectors(hub_url).items():
103
+ by_family.setdefault(entry.get("family", "other"), []).append((key, entry))
104
+ for family in sorted(by_family):
105
+ lines.append("%s:" % family)
106
+ for key, entry in sorted(by_family[family]):
107
+ mark = "[installed]" if key in have else " "
108
+ trust = entry.get("trust", "community")
109
+ lines.append(" %s %-16s %-10s %s" % (mark, key, trust, entry.get("summary", "")))
110
+ return lines
111
+
112
+
113
+ def install(key, assume_yes=False, hub_url=None, pip_args=()):
114
+ """Resolve `key` in the catalog and install it (pip for pip/git; images are run, not installed)."""
115
+ entry = connectors(hub_url).get(key)
116
+ if not entry:
117
+ sys.stderr.write("streamwright: unknown connector %r (try: streamwright connectors list)\n" % key)
118
+ return 2
119
+ inst = entry.get("install", {})
120
+ trust = entry.get("trust", "community")
121
+
122
+ if "image" in inst:
123
+ print("%r is distributed as an image, not a Python package:" % key)
124
+ print(" %s" % inst["image"])
125
+ print("Run it with the docker or k8s execution mode (it is not pip-installed).")
126
+ return 0
127
+
128
+ spec = inst.get("pip") or inst.get("git")
129
+ if not spec:
130
+ sys.stderr.write("streamwright: connector %r has no installable source\n" % key)
131
+ return 2
132
+
133
+ if trust != "official" and not assume_yes:
134
+ source = _source_of(inst)
135
+ if not sys.stdin.isatty():
136
+ sys.stderr.write(
137
+ "streamwright: refusing to install %s connector %r non-interactively; pass --yes to confirm (%s)\n"
138
+ % (trust, key, source))
139
+ return 2
140
+ sys.stderr.write(
141
+ "\n WARNING: %r is a %s connector. Installing runs third-party code from:\n %s\n"
142
+ % (key, trust, source))
143
+ answer = input(" Continue? [y/N] ").strip().lower()
144
+ if answer not in ("y", "yes"):
145
+ print("aborted")
146
+ return 1
147
+
148
+ cmd = [sys.executable, "-m", "pip", "install", *pip_args, spec]
149
+ print("+ " + " ".join(cmd))
150
+ return subprocess.call(cmd)