sqllocks-shape-fabric 0.9.1__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 (110) hide show
  1. sqllocks_shape_fabric-0.9.1/LICENSE +21 -0
  2. sqllocks_shape_fabric-0.9.1/PKG-INFO +244 -0
  3. sqllocks_shape_fabric-0.9.1/README.md +211 -0
  4. sqllocks_shape_fabric-0.9.1/pyproject.toml +73 -0
  5. sqllocks_shape_fabric-0.9.1/setup.cfg +4 -0
  6. sqllocks_shape_fabric-0.9.1/src/shape_fabric/__init__.py +84 -0
  7. sqllocks_shape_fabric-0.9.1/src/shape_fabric/_auth.py +103 -0
  8. sqllocks_shape_fabric-0.9.1/src/shape_fabric/_storage.py +124 -0
  9. sqllocks_shape_fabric-0.9.1/src/shape_fabric/_tsql.py +544 -0
  10. sqllocks_shape_fabric-0.9.1/src/shape_fabric/answer_check.py +331 -0
  11. sqllocks_shape_fabric-0.9.1/src/shape_fabric/auth.py +381 -0
  12. sqllocks_shape_fabric-0.9.1/src/shape_fabric/commands.py +1097 -0
  13. sqllocks_shape_fabric-0.9.1/src/shape_fabric/drift_report.py +644 -0
  14. sqllocks_shape_fabric-0.9.1/src/shape_fabric/errors.py +110 -0
  15. sqllocks_shape_fabric-0.9.1/src/shape_fabric/eventhouse.py +286 -0
  16. sqllocks_shape_fabric-0.9.1/src/shape_fabric/eventhouse_writer.py +177 -0
  17. sqllocks_shape_fabric-0.9.1/src/shape_fabric/eventstream.py +98 -0
  18. sqllocks_shape_fabric-0.9.1/src/shape_fabric/eventstream_writer.py +89 -0
  19. sqllocks_shape_fabric-0.9.1/src/shape_fabric/fabric_api.py +279 -0
  20. sqllocks_shape_fabric-0.9.1/src/shape_fabric/kerberos.py +184 -0
  21. sqllocks_shape_fabric-0.9.1/src/shape_fabric/keyvault.py +113 -0
  22. sqllocks_shape_fabric-0.9.1/src/shape_fabric/known_answer.py +1082 -0
  23. sqllocks_shape_fabric-0.9.1/src/shape_fabric/kusto.py +439 -0
  24. sqllocks_shape_fabric-0.9.1/src/shape_fabric/lakehouse.py +179 -0
  25. sqllocks_shape_fabric-0.9.1/src/shape_fabric/livecheck.py +131 -0
  26. sqllocks_shape_fabric-0.9.1/src/shape_fabric/notebook.py +211 -0
  27. sqllocks_shape_fabric-0.9.1/src/shape_fabric/onelake.py +233 -0
  28. sqllocks_shape_fabric-0.9.1/src/shape_fabric/recording.py +487 -0
  29. sqllocks_shape_fabric-0.9.1/src/shape_fabric/scenarios.py +662 -0
  30. sqllocks_shape_fabric-0.9.1/src/shape_fabric/semantic_model.py +319 -0
  31. sqllocks_shape_fabric-0.9.1/src/shape_fabric/semantic_profile.py +221 -0
  32. sqllocks_shape_fabric-0.9.1/src/shape_fabric/semantic_source.py +410 -0
  33. sqllocks_shape_fabric-0.9.1/src/shape_fabric/setup_env.py +52 -0
  34. sqllocks_shape_fabric-0.9.1/src/shape_fabric/sinks.py +595 -0
  35. sqllocks_shape_fabric-0.9.1/src/shape_fabric/source.py +67 -0
  36. sqllocks_shape_fabric-0.9.1/src/shape_fabric/sqldb.py +653 -0
  37. sqllocks_shape_fabric-0.9.1/src/shape_fabric/synapse.py +262 -0
  38. sqllocks_shape_fabric-0.9.1/src/shape_fabric/targets.py +120 -0
  39. sqllocks_shape_fabric-0.9.1/src/shape_fabric/testing.py +1131 -0
  40. sqllocks_shape_fabric-0.9.1/src/shape_fabric/warehouse.py +271 -0
  41. sqllocks_shape_fabric-0.9.1/src/sqllocks_shape_fabric.egg-info/PKG-INFO +244 -0
  42. sqllocks_shape_fabric-0.9.1/src/sqllocks_shape_fabric.egg-info/SOURCES.txt +108 -0
  43. sqllocks_shape_fabric-0.9.1/src/sqllocks_shape_fabric.egg-info/dependency_links.txt +1 -0
  44. sqllocks_shape_fabric-0.9.1/src/sqllocks_shape_fabric.egg-info/entry_points.txt +24 -0
  45. sqllocks_shape_fabric-0.9.1/src/sqllocks_shape_fabric.egg-info/requires.txt +16 -0
  46. sqllocks_shape_fabric-0.9.1/src/sqllocks_shape_fabric.egg-info/top_level.txt +1 -0
  47. sqllocks_shape_fabric-0.9.1/tests/test_auth.py +570 -0
  48. sqllocks_shape_fabric-0.9.1/tests/test_auth_cli.py +446 -0
  49. sqllocks_shape_fabric-0.9.1/tests/test_auth_tokens.py +49 -0
  50. sqllocks_shape_fabric-0.9.1/tests/test_check_answers.py +491 -0
  51. sqllocks_shape_fabric-0.9.1/tests/test_command_conformance.py +83 -0
  52. sqllocks_shape_fabric-0.9.1/tests/test_dax_formats_compat.py +157 -0
  53. sqllocks_shape_fabric-0.9.1/tests/test_demo_targets.py +291 -0
  54. sqllocks_shape_fabric-0.9.1/tests/test_emulator.py +152 -0
  55. sqllocks_shape_fabric-0.9.1/tests/test_eventhouse.py +308 -0
  56. sqllocks_shape_fabric-0.9.1/tests/test_eventhouse_mapping.py +123 -0
  57. sqllocks_shape_fabric-0.9.1/tests/test_eventhouse_schema_cache.py +89 -0
  58. sqllocks_shape_fabric-0.9.1/tests/test_eventhouse_writer.py +303 -0
  59. sqllocks_shape_fabric-0.9.1/tests/test_eventstream.py +51 -0
  60. sqllocks_shape_fabric-0.9.1/tests/test_eventstream_writer.py +66 -0
  61. sqllocks_shape_fabric-0.9.1/tests/test_export_model.py +309 -0
  62. sqllocks_shape_fabric-0.9.1/tests/test_fabric_api_edges.py +166 -0
  63. sqllocks_shape_fabric-0.9.1/tests/test_fabric_commands.py +328 -0
  64. sqllocks_shape_fabric-0.9.1/tests/test_kerberos.py +518 -0
  65. sqllocks_shape_fabric-0.9.1/tests/test_keyvault_names.py +20 -0
  66. sqllocks_shape_fabric-0.9.1/tests/test_known_answer.py +720 -0
  67. sqllocks_shape_fabric-0.9.1/tests/test_kusto_emulator.py +109 -0
  68. sqllocks_shape_fabric-0.9.1/tests/test_kusto_types.py +26 -0
  69. sqllocks_shape_fabric-0.9.1/tests/test_lakehouse.py +145 -0
  70. sqllocks_shape_fabric-0.9.1/tests/test_landing_zone_inputs.py +28 -0
  71. sqllocks_shape_fabric-0.9.1/tests/test_live.py +75 -0
  72. sqllocks_shape_fabric-0.9.1/tests/test_live_commands.py +170 -0
  73. sqllocks_shape_fabric-0.9.1/tests/test_live_git_sync.py +378 -0
  74. sqllocks_shape_fabric-0.9.1/tests/test_live_scale_sinks.py +213 -0
  75. sqllocks_shape_fabric-0.9.1/tests/test_live_semantic_model.py +43 -0
  76. sqllocks_shape_fabric-0.9.1/tests/test_live_synapse.py +76 -0
  77. sqllocks_shape_fabric-0.9.1/tests/test_live_writers.py +187 -0
  78. sqllocks_shape_fabric-0.9.1/tests/test_livecheck.py +167 -0
  79. sqllocks_shape_fabric-0.9.1/tests/test_notebook_inputs.py +45 -0
  80. sqllocks_shape_fabric-0.9.1/tests/test_onelake.py +101 -0
  81. sqllocks_shape_fabric-0.9.1/tests/test_onelake_segments.py +88 -0
  82. sqllocks_shape_fabric-0.9.1/tests/test_optional_extras.py +112 -0
  83. sqllocks_shape_fabric-0.9.1/tests/test_portability.py +38 -0
  84. sqllocks_shape_fabric-0.9.1/tests/test_profile_model.py +390 -0
  85. sqllocks_shape_fabric-0.9.1/tests/test_publish.py +545 -0
  86. sqllocks_shape_fabric-0.9.1/tests/test_publish_report.py +756 -0
  87. sqllocks_shape_fabric-0.9.1/tests/test_recorded.py +83 -0
  88. sqllocks_shape_fabric-0.9.1/tests/test_recording_edges.py +103 -0
  89. sqllocks_shape_fabric-0.9.1/tests/test_scale_sinks.py +162 -0
  90. sqllocks_shape_fabric-0.9.1/tests/test_security_review.py +150 -0
  91. sqllocks_shape_fabric-0.9.1/tests/test_seed_sqlserver_emulator.py +96 -0
  92. sqllocks_shape_fabric-0.9.1/tests/test_semantic_source.py +471 -0
  93. sqllocks_shape_fabric-0.9.1/tests/test_sink_uris.py +33 -0
  94. sqllocks_shape_fabric-0.9.1/tests/test_skeleton.py +9 -0
  95. sqllocks_shape_fabric-0.9.1/tests/test_sql_emulator.py +121 -0
  96. sqllocks_shape_fabric-0.9.1/tests/test_sql_write_path.py +651 -0
  97. sqllocks_shape_fabric-0.9.1/tests/test_sql_write_path_cli.py +345 -0
  98. sqllocks_shape_fabric-0.9.1/tests/test_sqldb.py +428 -0
  99. sqllocks_shape_fabric-0.9.1/tests/test_sqlserver_sink.py +291 -0
  100. sqllocks_shape_fabric-0.9.1/tests/test_sqlserver_sink_emulator.py +301 -0
  101. sqllocks_shape_fabric-0.9.1/tests/test_sqlserver_sink_uri.py +28 -0
  102. sqllocks_shape_fabric-0.9.1/tests/test_storage_edges.py +49 -0
  103. sqllocks_shape_fabric-0.9.1/tests/test_synapse.py +698 -0
  104. sqllocks_shape_fabric-0.9.1/tests/test_tsql_connection_strings.py +35 -0
  105. sqllocks_shape_fabric-0.9.1/tests/test_tsql_create_table.py +45 -0
  106. sqllocks_shape_fabric-0.9.1/tests/test_tsql_types.py +43 -0
  107. sqllocks_shape_fabric-0.9.1/tests/test_uncovered_paths.py +119 -0
  108. sqllocks_shape_fabric-0.9.1/tests/test_warehouse.py +230 -0
  109. sqllocks_shape_fabric-0.9.1/tests/test_write_mode_atomicity.py +79 -0
  110. sqllocks_shape_fabric-0.9.1/tests/test_write_tables_order.py +23 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 SQLLocks (Jonathan Stewart)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,244 @@
1
+ Metadata-Version: 2.4
2
+ Name: sqllocks-shape-fabric
3
+ Version: 0.9.1
4
+ Summary: Shape plugin: Microsoft Fabric, Synapse and Azure Data Factory pipeline integration
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://github.com/sqllocks/shape
7
+ Project-URL: Repository, https://github.com/sqllocks/shape
8
+ Project-URL: Issues, https://github.com/sqllocks/shape/issues
9
+ Project-URL: Documentation, https://github.com/sqllocks/shape/tree/main/plugins/shape-fabric
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3 :: Only
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Topic :: Database
18
+ Requires-Python: >=3.11
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: sqllocks-shape==0.9.1
22
+ Provides-Extra: eventhubs
23
+ Requires-Dist: sqllocks-shape-eventhubs==0.9.1; extra == "eventhubs"
24
+ Provides-Extra: sqlserver
25
+ Requires-Dist: sqllocks-shape-sqlserver==0.9.1; extra == "sqlserver"
26
+ Provides-Extra: entra
27
+ Requires-Dist: azure-identity>=1.16.1; extra == "entra"
28
+ Provides-Extra: onelake
29
+ Requires-Dist: sqllocks-shape[azure]==0.9.1; extra == "onelake"
30
+ Provides-Extra: semantic-link
31
+ Requires-Dist: semantic-link-sempy>=0.8; extra == "semantic-link"
32
+ Dynamic: license-file
33
+
34
+ # sqllocks-shape-fabric
35
+
36
+ Shape plugin: Microsoft Fabric. It holds the two event emitters of `shape emit` (`shape.emitters`),
37
+ the `sqlserver`, `warehouse` and `synapse` sinks (`shape.sinks`), the writers behind the scale router's Fabric
38
+ sinks, `--auth` and credential references ([fabric-auth](../../docs/plugins/fabric-auth.md)), and
39
+ the commands `shape fabric publish|notebook|deploy-notebook|setup|export-model|known-answer|
40
+ check-answers|publish-report` with their top-level aliases ([fabric-commands](../../docs/plugins/fabric-commands.md)).
41
+
42
+ ```
43
+ pip install sqllocks-shape-fabric # Lakehouse, Eventhouse, notebooks, commands
44
+ pip install 'sqllocks-shape-fabric[sqlserver,eventhubs]' # + SQL targets and the Eventstream
45
+ ```
46
+
47
+ `[sqlserver]` brings the SQL Server plugin (and `pyodbc`) for the `sqlserver`, `warehouse` and
48
+ `synapse` sinks and the SQL database writer; `[eventhubs]` brings the Event Hubs plugin for the
49
+ `eventstream` emitter and writer. Without one, that part says which extra to install. The plugins
50
+ are not on PyPI yet: install from the release's wheels with `--find-links` (docs/INSTALL.md).
51
+
52
+ ## `eventstream://`: a Fabric Eventstream
53
+
54
+ Add a **custom endpoint** source to an Eventstream, copy its connection string (it ends in
55
+ `EntityPath=...`) and:
56
+
57
+ ```
58
+ export SHAPE_EVENTSTREAM_CONNECTION_STRING='Endpoint=sb://...;EntityPath=es_...'
59
+ shape emit retail --realtime --rate 2000 --sink eventstream://my-eventstream
60
+ ```
61
+
62
+ The custom endpoint speaks the Event Hubs protocol, so this is the Event Hubs emitter (idempotency
63
+ key `<table>/<seq>` in the property `shape_key`, at-least-once, throttling waited for) with the
64
+ Eventstream's connection string. Sign-in with Microsoft Entra is not available for a custom
65
+ endpoint.
66
+
67
+ ## `eventhouse://`: a Fabric Eventhouse (KQL database)
68
+
69
+ ```
70
+ export SHAPE_EVENTHOUSE_TOKEN=$(az account get-access-token --resource https://<query-uri-host> --query accessToken -o tsv)
71
+ shape emit retail --sink eventhouse://<query-uri-host>/<database>
72
+ ```
73
+
74
+ Events are sent by Kusto **streaming ingestion**, a JSON-lines request per batch (under the 4 MB
75
+ limit). Each Shape table goes to the KQL table of the same name, created from its schema
76
+ (`.create-merge table`, plus a JSON ingestion mapping); put a table name last in the URI to send
77
+ everything to one table. The idempotency key is the pair of columns `_shape_table`,
78
+ `_shape_seq`. Streaming ingestion does not deduplicate, so a batch the runtime repeats after a
79
+ failure is in the table twice: read it with
80
+
81
+ ```python
82
+ from shape_fabric.eventhouse import dedupe_query
83
+
84
+ dedupe_query("order_line") # ['order_line'] | summarize take_any(*) by _shape_table, _shape_seq
85
+ ```
86
+
87
+ Sign-in: `token` option, `SHAPE_EVENTHOUSE_TOKEN`, or Microsoft Entra through `azure-identity`
88
+ (`pip install 'sqllocks-shape-fabric[entra]'`). A 429/503 is waited for (`Retry-After` honoured);
89
+ 400 (a malformed event, or streaming ingestion not enabled) and 401/403 stop the run.
90
+
91
+ ## Writers (Python API)
92
+
93
+ Batch writers take Arrow `RecordBatch` iterators, one table per `write_table` call (or a mapping to
94
+ `write_tables`), and a `credential` (any object with `get_token(scope)`, or a function
95
+ `scope -> token`; `None` means the writer's default). A failure raises `WriteError`; its `.result`
96
+ lists the tables completed before it. Nothing is reported written unless the destination accepted it.
97
+
98
+ | writer | destination | notes |
99
+ |---|---|---|
100
+ | `LakehouseWriter(folder)` | Parquet/CSV/JSONL files in a local folder or OneLake (`abfss://`, `onelake://<ws>/<lakehouse>/Files/..`) | streamed; a file is complete or absent; landing-zone, manifest and done-flag helpers |
101
+ | `SqlDatabaseWriter(connection_string)` | Fabric SQL database, Azure SQL, SQL Server | parameterised `INSERT`s, `batch_size` rows per trip, one transaction per table |
102
+ | `WarehouseWriter(connection_string, staging_path)` | Fabric Warehouse | Parquet staged in OneLake, `COPY INTO`, rows loaded must equal rows staged, staging always removed |
103
+ | `SynapseWriter(connection_string, staging_path)` | Synapse dedicated SQL pool | Parquet staged in ADLS Gen2, `COPY INTO` (managed or signed-in identity), rows loaded must equal rows staged, staging always removed; `distribution` and `index` table options |
104
+ | `EventhouseWriter(uri)` | KQL table | the emitter's Kusto transport (retries, sign-in), a table's own columns |
105
+ | `EventstreamWriter(uri)` | Eventstream | the emitter's transport, flat events with the `_shape_table`/`_shape_seq` key |
106
+
107
+ `write_mode` (SQL, Warehouse, Eventhouse): `create` (default: an existing table is an error), `append`,
108
+ `truncate`, `replace`. In a SQL database and a Warehouse, `replace` drops the old table. The `TRUNCATE` or
109
+ `DROP TABLE` commits together with the new rows, so a failed write keeps the old rows. Names are quoted and checked, values are parameters; the
110
+ one literal that cannot be a parameter (the `COPY INTO` location) is validated against a strict
111
+ character set. `shape_fabric.sinks` has `Sink`-protocol adapters; `shape_fabric.onelake` builds
112
+ OneLake paths.
113
+
114
+ In Eventhouse, `replace` clears the rows and rebuilds the column schema in the existing table,
115
+ retaining its identity and permissions. Streaming ingestion is enabled as for the other write
116
+ modes. It removes old columns and supports changed
117
+ column types. The management steps and streaming writes are not atomic: a failed replacement
118
+ can leave the table empty with an intermediate schema, or with only the rows already accepted.
119
+ `management_timeout` defaults to 600 seconds to allow streamed rows to be sealed before clearing;
120
+ ingestion and queries retain the 100-second `timeout` default.
121
+
122
+ Profile a lakehouse straight from OneLake: `shape profile onelake://<workspace>/<lakehouse>/Tables/<table>`
123
+ (Delta) or `.../Files/<path>`; authentication and `adlfs` handling are core's `abfss://` source.
124
+
125
+ ## `mssql://`: a live SQL Server, Azure SQL or Fabric SQL sink
126
+
127
+ `shape-fabric` registers three `shape.sinks` entries: `sqlserver` (URI schemes `mssql` and
128
+ `sqlserver`, bulk insert into a live database), `warehouse` (`warehouse://`, `COPY INTO`) and
129
+ `synapse` (`synapse://<workspace>.sql.azuresynapse.net/<pool>`, `COPY INTO` a dedicated SQL pool;
130
+ see [fabric-writers](../../docs/plugins/fabric-writers.md#synapse-a-synapse-dedicated-sql-pool)). The
131
+ `sql` sink of core is different: it writes a script file and never connects.
132
+
133
+ ```python
134
+ from shape.plugins.host import PluginHost
135
+
136
+ sink = PluginHost().get("shape.sinks", "sqlserver")
137
+ sink.write(
138
+ "mssql://myserver.database.windows.net/shop?schema=dbo&write_mode=append",
139
+ "customer",
140
+ batches, # batches: an iterable of Arrow RecordBatch
141
+ credential=my_credential, # get_token(scope) or scope -> token (Entra)
142
+ commit_rows=50_000, # optional: commit every 50,000 rows
143
+ )
144
+ ```
145
+
146
+ | URI part or option | meaning |
147
+ |---|---|
148
+ | `mssql://[user[:password]@]host[:port]/database` | the server and database (`sqlserver://` is the same) |
149
+ | `?schema=`, `schema_name=` | SQL schema (default `dbo`; created when missing) |
150
+ | `?write_mode=`, `write_mode=` | `create` (default; an existing table is an error), `append`, `truncate`, `replace`; the `TRUNCATE` or `DROP TABLE` commits together with the new rows, so a failed write keeps the old rows |
151
+ | `?batch_size=`, `batch_size=` | rows per round trip (default 5,000) |
152
+ | `?commit_rows=`, `commit_rows=` | commit every N rows while the batches are consumed, so readers see rows as they arrive; the default is one transaction per call (a failure rolls everything back). With `commit_rows` a failure rolls back only the open chunk and keeps what was committed |
153
+ | `credential=` | Microsoft Entra sign-in; without it the login is `user` and `password` |
154
+ | `connection_string=`, `connection=` | an ODBC/ADO.NET connection string, or an open DB-API connection (never closed by the sink), instead of the URI's host and database |
155
+ | `driver`, `encrypt`, `trust_server_certificate`, `timeout` | connection settings (query or option) |
156
+
157
+ Each call writes one table and is safe to call again for each micro-batch of a stream with
158
+ `write_mode=append`. The batches are consumed one at a time; nothing is held back until the end.
159
+ A password is accepted as an option or in the URI's user information, never in the query string; no
160
+ password, token or connection string appears in an exception message or a log record, and none
161
+ should go on a command line (use an environment variable and pass it as an option, or sign in
162
+ with `--auth` and `--connection-string` on `shape generate --to mssql://...`; see the next section).
163
+
164
+ ## Sign-in and credential references
165
+
166
+ `--auth cli|msi|spn|sql|device-code|fabric`, `--tenant-id`, `--client-id`, `--client-secret REF`,
167
+ `--sql-user`, `--sql-password REF` and `--connection-string` on `shape generate --scale-mode`, `shape emit`,
168
+ `shape stream`, `shape profile` and `shape jobs`; in Python `shape_fabric.auth` (`AuthSettings`,
169
+ `build_credential`, `writer_options`) builds the `credential` every writer takes. Secrets are `env://`,
170
+ `file://` (refused when others can read the file) or `kv://VAULT/SECRET` (`shape_fabric.keyvault`, Azure Key
171
+ Vault over HTTPS) references, never command-line values. See `docs/plugins/fabric-auth.md`.
172
+
173
+ ## Tests
174
+
175
+ Contract tests replay recorded interactions (`tests/fixtures/*.json`, made by `python -m
176
+ shape_fabric.scenarios record <dir>`; secrets are scrubbed, including quoted, braced and JSON
177
+ values, a tape that still holds one is refused when it is written and when it is replayed, and a
178
+ test fails if a tape holds one).
179
+ Emulator tests: `test_sql_emulator.py` and `test_sqlserver_sink_emulator.py` (SQL Server), `test_kusto_emulator.py`. Live tests
180
+ (`test_live_writers.py`, nightly `fabric-live` job) need the `FABRIC_*` secrets listed in the file.
181
+
182
+ Contract tests run on every PR against in-memory fakes (`shape_fabric.testing`; the emitter
183
+ contract is `shape.streaming.emit.contract`). `pytest -m emulator plugins/shape-fabric/tests` runs
184
+ the end-to-end tests against the Event Hubs emulator (as an Eventstream stand-in) and the Kusto
185
+ emulator (`docker compose -f ci/emulators/docker-compose.yml up -d --wait azurite eventhubs && docker
186
+ compose -f ci/emulators/docker-compose.yml up -d kusto`), nightly in CI. `pytest -m live
187
+ plugins/shape-fabric/tests` needs the owner's `FABRIC_*` secrets (`FABRIC_EVENTSTREAM_CONNECTION_STRING`;
188
+ `FABRIC_EVENTHOUSE_URI`, `FABRIC_EVENTHOUSE_TOKEN`); the nightly job runs each only where its secret
189
+ exists.
190
+
191
+ Its version always equals core's (`sqllocks-shape`), and it is released together with core.
192
+ How plugins are written: `docs/plugins/authoring.md` in the repository.
193
+
194
+ ## Which Fabric surfaces this plugin calls
195
+
196
+ [`docs/FABRIC_PLATFORM.md`](../../docs/FABRIC_PLATFORM.md) lists every Fabric and OneLake API, item type and Spark runtime that Shape calls, with its release stage, and describes the live Git sync check (`tests/test_live_git_sync.py`). A test keeps preview APIs out of the code.
197
+
198
+ ## `synapse://`: a Synapse dedicated SQL pool
199
+
200
+ `synapse` (`shape.sinks`, `shape_fabric.SynapseSink`; the writer is `shape_fabric.SynapseWriter`)
201
+ writes a table into a **dedicated SQL pool** of an Azure Synapse workspace. It follows the Warehouse
202
+ writer: it prepares the table with the same T-SQL helpers (same `write_mode` values, the safe `create`
203
+ default, `schema_name`, `columns`, `primary_key`), stages the rows as Parquet files of at most
204
+ `chunk_rows` rows (default 1,000,000) under `<staging_path>/staging/<run>/<table>/`, runs **one**
205
+ `COPY INTO ... WITH (FILE_TYPE = 'PARQUET')` over that folder, checks that the number of rows it
206
+ loaded equals the number staged (otherwise the write fails and a table this call created is dropped),
207
+ and deletes the staged files, also when a step fails.
208
+
209
+ ```
210
+ shape generate retail --to synapse://myws.sql.azuresynapse.net/pool1 \
211
+ --auth cli --sink-config synapse.staging_path=abfss://stage@myacct.dfs.core.windows.net/shape
212
+ ```
213
+
214
+ | URI part or option | meaning |
215
+ |---|---|
216
+ | `synapse://<workspace>.sql.azuresynapse.net/<pool>` | the workspace's SQL endpoint and the dedicated pool. No user part, port or query: a serverless endpoint (`-ondemand`) is refused, and so is a password anywhere in the URI |
217
+ | `staging_path` | **required**: an ADLS Gen2 folder, `abfss://<container>@<account>.dfs.core.windows.net/<folder>`, that the pool can read. `COPY INTO` reads it as `https://<account>.dfs.core.windows.net/<container>/<folder>/...` |
218
+ | `copy_identity` | who `COPY INTO` reads the storage as: `managed_identity` (default; the workspace's managed identity, `CREDENTIAL = (IDENTITY = 'Managed Identity')`) or `signed_in` (the Microsoft Entra identity of the connection, no `CREDENTIAL` clause; refused with a SQL login, which has no such identity) |
219
+ | `distribution` | `ROUND_ROBIN` (default), `REPLICATE` or `HASH(column)`; the column must be one of the table's and is quoted |
220
+ | `index` | `CLUSTERED COLUMNSTORE INDEX` (default) or `HEAP` |
221
+ | `write_mode`, `schema_name`, `chunk_rows`, `columns`, `primary_key`, `schema` | as the Warehouse writer (`schema` creates an empty table from no batches) |
222
+ | `credential`, `connection_string`, `connection` | sign-in, below |
223
+
224
+ `distribution` and `index` apply to a table this call creates; a table that already exists
225
+ (`append`, `truncate`) keeps its own. Synapse applies its own limits to the hash column's type; a
226
+ refusal from the pool fails the write and a table this call created is dropped. A primary key is
227
+ declared `NONCLUSTERED ... NOT ENFORCED` (the pool does not enforce keys). Column types are the
228
+ Warehouse's (`VARCHAR(8000)`, `DATETIME2(6)`, `BIT`, ...; no `(N)VARCHAR(MAX)`, which a clustered
229
+ columnstore index cannot hold), and timestamps are staged in microseconds in UTC.
230
+
231
+ **Sign-in** is the plugin's `--auth` modes ([fabric-auth](../../docs/plugins/fabric-auth.md)): `cli`, `msi`, `spn`,
232
+ `device-code` and `fabric` send a Microsoft Entra token for the SQL connection and for the storage;
233
+ `--auth sql` adds a SQL login to `--connection-string synapse://<workspace>.sql.azuresynapse.net/<pool>`
234
+ (the password a reference, never a command-line value). From Python give `credential=` or a
235
+ `connection_string` with the login. No password, token or connection string appears in an exception
236
+ message or a log record.
237
+
238
+ **What a failure leaves behind.** The load is one `COPY INTO`, committed once the row count has been
239
+ checked. A failure rolls back and drops a table this call created (`replace` has already dropped the
240
+ old table and `truncate` has already emptied it); the staged files are removed in every case (a
241
+ removal that itself fails is a `RuntimeWarning`).
242
+
243
+ Out of scope: serverless SQL pools, Synapse Spark pools and Synapse pipelines (those are
244
+ `integrations/synapse`).
@@ -0,0 +1,211 @@
1
+ # sqllocks-shape-fabric
2
+
3
+ Shape plugin: Microsoft Fabric. It holds the two event emitters of `shape emit` (`shape.emitters`),
4
+ the `sqlserver`, `warehouse` and `synapse` sinks (`shape.sinks`), the writers behind the scale router's Fabric
5
+ sinks, `--auth` and credential references ([fabric-auth](../../docs/plugins/fabric-auth.md)), and
6
+ the commands `shape fabric publish|notebook|deploy-notebook|setup|export-model|known-answer|
7
+ check-answers|publish-report` with their top-level aliases ([fabric-commands](../../docs/plugins/fabric-commands.md)).
8
+
9
+ ```
10
+ pip install sqllocks-shape-fabric # Lakehouse, Eventhouse, notebooks, commands
11
+ pip install 'sqllocks-shape-fabric[sqlserver,eventhubs]' # + SQL targets and the Eventstream
12
+ ```
13
+
14
+ `[sqlserver]` brings the SQL Server plugin (and `pyodbc`) for the `sqlserver`, `warehouse` and
15
+ `synapse` sinks and the SQL database writer; `[eventhubs]` brings the Event Hubs plugin for the
16
+ `eventstream` emitter and writer. Without one, that part says which extra to install. The plugins
17
+ are not on PyPI yet: install from the release's wheels with `--find-links` (docs/INSTALL.md).
18
+
19
+ ## `eventstream://`: a Fabric Eventstream
20
+
21
+ Add a **custom endpoint** source to an Eventstream, copy its connection string (it ends in
22
+ `EntityPath=...`) and:
23
+
24
+ ```
25
+ export SHAPE_EVENTSTREAM_CONNECTION_STRING='Endpoint=sb://...;EntityPath=es_...'
26
+ shape emit retail --realtime --rate 2000 --sink eventstream://my-eventstream
27
+ ```
28
+
29
+ The custom endpoint speaks the Event Hubs protocol, so this is the Event Hubs emitter (idempotency
30
+ key `<table>/<seq>` in the property `shape_key`, at-least-once, throttling waited for) with the
31
+ Eventstream's connection string. Sign-in with Microsoft Entra is not available for a custom
32
+ endpoint.
33
+
34
+ ## `eventhouse://`: a Fabric Eventhouse (KQL database)
35
+
36
+ ```
37
+ export SHAPE_EVENTHOUSE_TOKEN=$(az account get-access-token --resource https://<query-uri-host> --query accessToken -o tsv)
38
+ shape emit retail --sink eventhouse://<query-uri-host>/<database>
39
+ ```
40
+
41
+ Events are sent by Kusto **streaming ingestion**, a JSON-lines request per batch (under the 4 MB
42
+ limit). Each Shape table goes to the KQL table of the same name, created from its schema
43
+ (`.create-merge table`, plus a JSON ingestion mapping); put a table name last in the URI to send
44
+ everything to one table. The idempotency key is the pair of columns `_shape_table`,
45
+ `_shape_seq`. Streaming ingestion does not deduplicate, so a batch the runtime repeats after a
46
+ failure is in the table twice: read it with
47
+
48
+ ```python
49
+ from shape_fabric.eventhouse import dedupe_query
50
+
51
+ dedupe_query("order_line") # ['order_line'] | summarize take_any(*) by _shape_table, _shape_seq
52
+ ```
53
+
54
+ Sign-in: `token` option, `SHAPE_EVENTHOUSE_TOKEN`, or Microsoft Entra through `azure-identity`
55
+ (`pip install 'sqllocks-shape-fabric[entra]'`). A 429/503 is waited for (`Retry-After` honoured);
56
+ 400 (a malformed event, or streaming ingestion not enabled) and 401/403 stop the run.
57
+
58
+ ## Writers (Python API)
59
+
60
+ Batch writers take Arrow `RecordBatch` iterators, one table per `write_table` call (or a mapping to
61
+ `write_tables`), and a `credential` (any object with `get_token(scope)`, or a function
62
+ `scope -> token`; `None` means the writer's default). A failure raises `WriteError`; its `.result`
63
+ lists the tables completed before it. Nothing is reported written unless the destination accepted it.
64
+
65
+ | writer | destination | notes |
66
+ |---|---|---|
67
+ | `LakehouseWriter(folder)` | Parquet/CSV/JSONL files in a local folder or OneLake (`abfss://`, `onelake://<ws>/<lakehouse>/Files/..`) | streamed; a file is complete or absent; landing-zone, manifest and done-flag helpers |
68
+ | `SqlDatabaseWriter(connection_string)` | Fabric SQL database, Azure SQL, SQL Server | parameterised `INSERT`s, `batch_size` rows per trip, one transaction per table |
69
+ | `WarehouseWriter(connection_string, staging_path)` | Fabric Warehouse | Parquet staged in OneLake, `COPY INTO`, rows loaded must equal rows staged, staging always removed |
70
+ | `SynapseWriter(connection_string, staging_path)` | Synapse dedicated SQL pool | Parquet staged in ADLS Gen2, `COPY INTO` (managed or signed-in identity), rows loaded must equal rows staged, staging always removed; `distribution` and `index` table options |
71
+ | `EventhouseWriter(uri)` | KQL table | the emitter's Kusto transport (retries, sign-in), a table's own columns |
72
+ | `EventstreamWriter(uri)` | Eventstream | the emitter's transport, flat events with the `_shape_table`/`_shape_seq` key |
73
+
74
+ `write_mode` (SQL, Warehouse, Eventhouse): `create` (default: an existing table is an error), `append`,
75
+ `truncate`, `replace`. In a SQL database and a Warehouse, `replace` drops the old table. The `TRUNCATE` or
76
+ `DROP TABLE` commits together with the new rows, so a failed write keeps the old rows. Names are quoted and checked, values are parameters; the
77
+ one literal that cannot be a parameter (the `COPY INTO` location) is validated against a strict
78
+ character set. `shape_fabric.sinks` has `Sink`-protocol adapters; `shape_fabric.onelake` builds
79
+ OneLake paths.
80
+
81
+ In Eventhouse, `replace` clears the rows and rebuilds the column schema in the existing table,
82
+ retaining its identity and permissions. Streaming ingestion is enabled as for the other write
83
+ modes. It removes old columns and supports changed
84
+ column types. The management steps and streaming writes are not atomic: a failed replacement
85
+ can leave the table empty with an intermediate schema, or with only the rows already accepted.
86
+ `management_timeout` defaults to 600 seconds to allow streamed rows to be sealed before clearing;
87
+ ingestion and queries retain the 100-second `timeout` default.
88
+
89
+ Profile a lakehouse straight from OneLake: `shape profile onelake://<workspace>/<lakehouse>/Tables/<table>`
90
+ (Delta) or `.../Files/<path>`; authentication and `adlfs` handling are core's `abfss://` source.
91
+
92
+ ## `mssql://`: a live SQL Server, Azure SQL or Fabric SQL sink
93
+
94
+ `shape-fabric` registers three `shape.sinks` entries: `sqlserver` (URI schemes `mssql` and
95
+ `sqlserver`, bulk insert into a live database), `warehouse` (`warehouse://`, `COPY INTO`) and
96
+ `synapse` (`synapse://<workspace>.sql.azuresynapse.net/<pool>`, `COPY INTO` a dedicated SQL pool;
97
+ see [fabric-writers](../../docs/plugins/fabric-writers.md#synapse-a-synapse-dedicated-sql-pool)). The
98
+ `sql` sink of core is different: it writes a script file and never connects.
99
+
100
+ ```python
101
+ from shape.plugins.host import PluginHost
102
+
103
+ sink = PluginHost().get("shape.sinks", "sqlserver")
104
+ sink.write(
105
+ "mssql://myserver.database.windows.net/shop?schema=dbo&write_mode=append",
106
+ "customer",
107
+ batches, # batches: an iterable of Arrow RecordBatch
108
+ credential=my_credential, # get_token(scope) or scope -> token (Entra)
109
+ commit_rows=50_000, # optional: commit every 50,000 rows
110
+ )
111
+ ```
112
+
113
+ | URI part or option | meaning |
114
+ |---|---|
115
+ | `mssql://[user[:password]@]host[:port]/database` | the server and database (`sqlserver://` is the same) |
116
+ | `?schema=`, `schema_name=` | SQL schema (default `dbo`; created when missing) |
117
+ | `?write_mode=`, `write_mode=` | `create` (default; an existing table is an error), `append`, `truncate`, `replace`; the `TRUNCATE` or `DROP TABLE` commits together with the new rows, so a failed write keeps the old rows |
118
+ | `?batch_size=`, `batch_size=` | rows per round trip (default 5,000) |
119
+ | `?commit_rows=`, `commit_rows=` | commit every N rows while the batches are consumed, so readers see rows as they arrive; the default is one transaction per call (a failure rolls everything back). With `commit_rows` a failure rolls back only the open chunk and keeps what was committed |
120
+ | `credential=` | Microsoft Entra sign-in; without it the login is `user` and `password` |
121
+ | `connection_string=`, `connection=` | an ODBC/ADO.NET connection string, or an open DB-API connection (never closed by the sink), instead of the URI's host and database |
122
+ | `driver`, `encrypt`, `trust_server_certificate`, `timeout` | connection settings (query or option) |
123
+
124
+ Each call writes one table and is safe to call again for each micro-batch of a stream with
125
+ `write_mode=append`. The batches are consumed one at a time; nothing is held back until the end.
126
+ A password is accepted as an option or in the URI's user information, never in the query string; no
127
+ password, token or connection string appears in an exception message or a log record, and none
128
+ should go on a command line (use an environment variable and pass it as an option, or sign in
129
+ with `--auth` and `--connection-string` on `shape generate --to mssql://...`; see the next section).
130
+
131
+ ## Sign-in and credential references
132
+
133
+ `--auth cli|msi|spn|sql|device-code|fabric`, `--tenant-id`, `--client-id`, `--client-secret REF`,
134
+ `--sql-user`, `--sql-password REF` and `--connection-string` on `shape generate --scale-mode`, `shape emit`,
135
+ `shape stream`, `shape profile` and `shape jobs`; in Python `shape_fabric.auth` (`AuthSettings`,
136
+ `build_credential`, `writer_options`) builds the `credential` every writer takes. Secrets are `env://`,
137
+ `file://` (refused when others can read the file) or `kv://VAULT/SECRET` (`shape_fabric.keyvault`, Azure Key
138
+ Vault over HTTPS) references, never command-line values. See `docs/plugins/fabric-auth.md`.
139
+
140
+ ## Tests
141
+
142
+ Contract tests replay recorded interactions (`tests/fixtures/*.json`, made by `python -m
143
+ shape_fabric.scenarios record <dir>`; secrets are scrubbed, including quoted, braced and JSON
144
+ values, a tape that still holds one is refused when it is written and when it is replayed, and a
145
+ test fails if a tape holds one).
146
+ Emulator tests: `test_sql_emulator.py` and `test_sqlserver_sink_emulator.py` (SQL Server), `test_kusto_emulator.py`. Live tests
147
+ (`test_live_writers.py`, nightly `fabric-live` job) need the `FABRIC_*` secrets listed in the file.
148
+
149
+ Contract tests run on every PR against in-memory fakes (`shape_fabric.testing`; the emitter
150
+ contract is `shape.streaming.emit.contract`). `pytest -m emulator plugins/shape-fabric/tests` runs
151
+ the end-to-end tests against the Event Hubs emulator (as an Eventstream stand-in) and the Kusto
152
+ emulator (`docker compose -f ci/emulators/docker-compose.yml up -d --wait azurite eventhubs && docker
153
+ compose -f ci/emulators/docker-compose.yml up -d kusto`), nightly in CI. `pytest -m live
154
+ plugins/shape-fabric/tests` needs the owner's `FABRIC_*` secrets (`FABRIC_EVENTSTREAM_CONNECTION_STRING`;
155
+ `FABRIC_EVENTHOUSE_URI`, `FABRIC_EVENTHOUSE_TOKEN`); the nightly job runs each only where its secret
156
+ exists.
157
+
158
+ Its version always equals core's (`sqllocks-shape`), and it is released together with core.
159
+ How plugins are written: `docs/plugins/authoring.md` in the repository.
160
+
161
+ ## Which Fabric surfaces this plugin calls
162
+
163
+ [`docs/FABRIC_PLATFORM.md`](../../docs/FABRIC_PLATFORM.md) lists every Fabric and OneLake API, item type and Spark runtime that Shape calls, with its release stage, and describes the live Git sync check (`tests/test_live_git_sync.py`). A test keeps preview APIs out of the code.
164
+
165
+ ## `synapse://`: a Synapse dedicated SQL pool
166
+
167
+ `synapse` (`shape.sinks`, `shape_fabric.SynapseSink`; the writer is `shape_fabric.SynapseWriter`)
168
+ writes a table into a **dedicated SQL pool** of an Azure Synapse workspace. It follows the Warehouse
169
+ writer: it prepares the table with the same T-SQL helpers (same `write_mode` values, the safe `create`
170
+ default, `schema_name`, `columns`, `primary_key`), stages the rows as Parquet files of at most
171
+ `chunk_rows` rows (default 1,000,000) under `<staging_path>/staging/<run>/<table>/`, runs **one**
172
+ `COPY INTO ... WITH (FILE_TYPE = 'PARQUET')` over that folder, checks that the number of rows it
173
+ loaded equals the number staged (otherwise the write fails and a table this call created is dropped),
174
+ and deletes the staged files, also when a step fails.
175
+
176
+ ```
177
+ shape generate retail --to synapse://myws.sql.azuresynapse.net/pool1 \
178
+ --auth cli --sink-config synapse.staging_path=abfss://stage@myacct.dfs.core.windows.net/shape
179
+ ```
180
+
181
+ | URI part or option | meaning |
182
+ |---|---|
183
+ | `synapse://<workspace>.sql.azuresynapse.net/<pool>` | the workspace's SQL endpoint and the dedicated pool. No user part, port or query: a serverless endpoint (`-ondemand`) is refused, and so is a password anywhere in the URI |
184
+ | `staging_path` | **required**: an ADLS Gen2 folder, `abfss://<container>@<account>.dfs.core.windows.net/<folder>`, that the pool can read. `COPY INTO` reads it as `https://<account>.dfs.core.windows.net/<container>/<folder>/...` |
185
+ | `copy_identity` | who `COPY INTO` reads the storage as: `managed_identity` (default; the workspace's managed identity, `CREDENTIAL = (IDENTITY = 'Managed Identity')`) or `signed_in` (the Microsoft Entra identity of the connection, no `CREDENTIAL` clause; refused with a SQL login, which has no such identity) |
186
+ | `distribution` | `ROUND_ROBIN` (default), `REPLICATE` or `HASH(column)`; the column must be one of the table's and is quoted |
187
+ | `index` | `CLUSTERED COLUMNSTORE INDEX` (default) or `HEAP` |
188
+ | `write_mode`, `schema_name`, `chunk_rows`, `columns`, `primary_key`, `schema` | as the Warehouse writer (`schema` creates an empty table from no batches) |
189
+ | `credential`, `connection_string`, `connection` | sign-in, below |
190
+
191
+ `distribution` and `index` apply to a table this call creates; a table that already exists
192
+ (`append`, `truncate`) keeps its own. Synapse applies its own limits to the hash column's type; a
193
+ refusal from the pool fails the write and a table this call created is dropped. A primary key is
194
+ declared `NONCLUSTERED ... NOT ENFORCED` (the pool does not enforce keys). Column types are the
195
+ Warehouse's (`VARCHAR(8000)`, `DATETIME2(6)`, `BIT`, ...; no `(N)VARCHAR(MAX)`, which a clustered
196
+ columnstore index cannot hold), and timestamps are staged in microseconds in UTC.
197
+
198
+ **Sign-in** is the plugin's `--auth` modes ([fabric-auth](../../docs/plugins/fabric-auth.md)): `cli`, `msi`, `spn`,
199
+ `device-code` and `fabric` send a Microsoft Entra token for the SQL connection and for the storage;
200
+ `--auth sql` adds a SQL login to `--connection-string synapse://<workspace>.sql.azuresynapse.net/<pool>`
201
+ (the password a reference, never a command-line value). From Python give `credential=` or a
202
+ `connection_string` with the login. No password, token or connection string appears in an exception
203
+ message or a log record.
204
+
205
+ **What a failure leaves behind.** The load is one `COPY INTO`, committed once the row count has been
206
+ checked. A failure rolls back and drops a table this call created (`replace` has already dropped the
207
+ old table and `truncate` has already emptied it); the staged files are removed in every case (a
208
+ removal that itself fails is a `RuntimeWarning`).
209
+
210
+ Out of scope: serverless SQL pools, Synapse Spark pools and Synapse pipelines (those are
211
+ `integrations/synapse`).
@@ -0,0 +1,73 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "sqllocks-shape-fabric"
7
+ # Lockstep with core (T-09): scripts/check_plugin_skeletons.py fails when this drifts.
8
+ version = "0.9.1"
9
+ description = "Shape plugin: Microsoft Fabric, Synapse and Azure Data Factory pipeline integration"
10
+ readme = "README.md"
11
+ requires-python = ">=3.11"
12
+ license = "MIT"
13
+ license-files = ["LICENSE"]
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3 :: Only",
17
+ "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Programming Language :: Python :: 3.13",
20
+ "Programming Language :: Python :: 3.14",
21
+ "Operating System :: OS Independent",
22
+ "Topic :: Database",
23
+ ]
24
+ dependencies = ["sqllocks-shape==0.9.1"]
25
+
26
+ [project.urls]
27
+ Homepage = "https://github.com/sqllocks/shape"
28
+ Repository = "https://github.com/sqllocks/shape"
29
+ Issues = "https://github.com/sqllocks/shape/issues"
30
+ Documentation = "https://github.com/sqllocks/shape/tree/main/plugins/shape-fabric"
31
+
32
+ [project.optional-dependencies]
33
+ # The Eventstream emitter and writer: the Event Hubs plugin (azure-eventhub).
34
+ eventhubs = ["sqllocks-shape-eventhubs==0.9.1"]
35
+ # The SQL database, Warehouse, Synapse and SQL Server targets: the SQL Server plugin (pyodbc,
36
+ # which needs unixODBC and the Microsoft ODBC driver on Linux).
37
+ sqlserver = ["sqllocks-shape-sqlserver==0.9.1"]
38
+ # Microsoft Entra sign-in to an Eventhouse instead of a bearer token.
39
+ entra = ["azure-identity>=1.16.1"]
40
+ # Reading and writing OneLake / ADLS Gen2 paths (abfss://, onelake://): adlfs and azure-identity.
41
+ onelake = ["sqllocks-shape[azure]==0.9.1"]
42
+ # Reading a semantic model (semantic-model://, shape profile-model): sempy runs inside a Fabric notebook.
43
+ semantic-link = ["semantic-link-sempy>=0.8"]
44
+
45
+ # Entry points (shape.* groups, plugin API v1) are added here by the work package that
46
+ # implements each part of this plugin; see docs/plugins/authoring.md.
47
+ [project.entry-points."shape.emitters"]
48
+ eventstream = "shape_fabric:EventstreamEmitter"
49
+ eventhouse = "shape_fabric:EventhouseEmitter"
50
+
51
+ [project.entry-points."shape.commands"]
52
+ fabric = "shape_fabric.commands:FabricCommand"
53
+ publish = "shape_fabric.commands:PublishCommand"
54
+ notebook = "shape_fabric.commands:NotebookCommand"
55
+ deploy-notebook = "shape_fabric.commands:DeployNotebookCommand"
56
+ setup-fabric = "shape_fabric.commands:SetupFabricCommand"
57
+ export-model = "shape_fabric.commands:ExportModelCommand"
58
+ profile-model = "shape_fabric.commands:ProfileModelCommand"
59
+ known-answer = "shape_fabric.commands:KnownAnswerCommand"
60
+ check-answers = "shape_fabric.commands:CheckAnswersCommand"
61
+ publish-report = "shape_fabric.commands:PublishReportCommand"
62
+
63
+ [project.entry-points."shape.sinks"]
64
+ sqlserver = "shape_fabric:SqlServerSink"
65
+ warehouse = "shape_fabric:WarehouseSink"
66
+ synapse = "shape_fabric:SynapseSink"
67
+
68
+ [project.entry-points."shape.sources"]
69
+ onelake = "shape_fabric:LakehouseSource"
70
+ semantic-model = "shape_fabric.semantic_source:SemanticModelSource"
71
+
72
+ [tool.setuptools.packages.find]
73
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,84 @@
1
+ """Shape plugin for Fabric, Synapse and Azure Data Factory pipeline integration.
2
+
3
+ * ``shape.emitters``: ``eventstream`` sends events to a Fabric Eventstream custom endpoint
4
+ (``eventstream://<name>``) and ``eventhouse`` to an Eventhouse (KQL database) by streaming
5
+ ingestion (``eventhouse://<query-uri host>/<database>``), for ``shape emit``.
6
+ * ``shape.sources``: ``onelake`` reads a lakehouse Delta table or files by
7
+ ``onelake://<workspace>/<lakehouse>/Tables|Files/...`` (profiling, ``shape profile``), and
8
+ ``semantic-model`` reads a table of a Power BI / Fabric semantic model through ``sempy`` by
9
+ ``semantic-model://<workspace>/<model>/<table>`` (:mod:`shape_fabric.semantic_source`).
10
+ * ``shape.commands``: ``shape profile-model WORKSPACE/MODEL`` profiles every table of a semantic
11
+ model with its declared relationships (:mod:`shape_fabric.semantic_profile`).
12
+ * ``shape.sinks``: ``sqlserver`` writes to a live SQL Server, Azure SQL or Fabric SQL database
13
+ (``mssql://`` and ``sqlserver://`` URIs, bulk insert) and ``warehouse`` to a Fabric Warehouse
14
+ (``warehouse://``, Parquet staged in OneLake then ``COPY INTO``), ``synapse`` to a Synapse
15
+ dedicated SQL pool (``synapse://``, Parquet staged in ADLS Gen2 then ``COPY INTO``).
16
+ * Writers (a Python API; the scale router's sinks are built on it): ``LakehouseWriter`` (files
17
+ in OneLake or a folder), ``SqlDatabaseWriter`` (Fabric SQL database, Azure SQL, SQL Server),
18
+ ``WarehouseWriter`` (Parquet staged in OneLake, then ``COPY INTO``), ``SynapseWriter``
19
+ (the same into a dedicated SQL pool), ``EventhouseWriter`` and
20
+ ``EventstreamWriter``; ``Sink`` adapters in :mod:`shape_fabric.sinks`.
21
+ * :mod:`shape_fabric.targets`: what ``shape demo`` needs of a destination besides writing to it:
22
+ drop a table, remove files, and check that a target answers.
23
+
24
+ Other entry points are added by the work packages that implement them. Imports here are lazy:
25
+ no Azure or ODBC library is loaded until a writer needs it.
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ import importlib
31
+ from typing import TYPE_CHECKING, Any
32
+
33
+ if TYPE_CHECKING:
34
+ from .errors import AuthError as AuthError
35
+ from .errors import WriteError as WriteError
36
+ from .errors import WriteResult as WriteResult
37
+ from .eventhouse import EventhouseEmitter as EventhouseEmitter
38
+ from .eventhouse_writer import EventhouseWriter as EventhouseWriter
39
+ from .eventstream import EventstreamEmitter as EventstreamEmitter
40
+ from .eventstream_writer import EventstreamWriter as EventstreamWriter
41
+ from .lakehouse import LakehouseWriter as LakehouseWriter
42
+ from .sinks import SqlServerSink as SqlServerSink
43
+ from .sinks import SynapseSink as SynapseSink
44
+ from .sinks import WarehouseSink as WarehouseSink
45
+ from .source import LakehouseSource as LakehouseSource
46
+ from .sqldb import SqlDatabaseWriter as SqlDatabaseWriter
47
+ from .synapse import SynapseWriter as SynapseWriter
48
+ from .warehouse import WarehouseWriter as WarehouseWriter
49
+
50
+ SHAPE_API = "1.0"
51
+
52
+ _EXPORTS = {
53
+ "AuthError": "errors",
54
+ "EventhouseEmitter": "eventhouse",
55
+ "EventhouseWriter": "eventhouse_writer",
56
+ "EventstreamEmitter": "eventstream",
57
+ "EventstreamWriter": "eventstream_writer",
58
+ "LakehouseSource": "source",
59
+ "LakehouseWriter": "lakehouse",
60
+ "SqlDatabaseWriter": "sqldb",
61
+ "SqlServerSink": "sinks",
62
+ "SynapseSink": "sinks",
63
+ "SynapseWriter": "synapse",
64
+ "WarehouseSink": "sinks",
65
+ "WarehouseWriter": "warehouse",
66
+ "WriteError": "errors",
67
+ "WriteResult": "errors",
68
+ }
69
+
70
+ __all__ = ["SHAPE_API", *_EXPORTS]
71
+
72
+
73
+ def __getattr__(name: str) -> Any:
74
+ try:
75
+ module = _EXPORTS[name]
76
+ except KeyError:
77
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}") from None
78
+ value = getattr(importlib.import_module(f".{module}", __name__), name)
79
+ globals()[name] = value
80
+ return value
81
+
82
+
83
+ def __dir__() -> list[str]:
84
+ return sorted({*globals(), *_EXPORTS})