weaverstack 0.1.1__py3-none-any.whl

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 (127) hide show
  1. weaver/__init__.py +59 -0
  2. weaver/build_bundle/__init__.py +109 -0
  3. weaver/build_bundle/aliases.py +325 -0
  4. weaver/build_bundle/bundle.py +359 -0
  5. weaver/build_bundle/catalogue_actions.py +275 -0
  6. weaver/build_bundle/changes.py +186 -0
  7. weaver/build_bundle/endpoints.py +83 -0
  8. weaver/build_bundle/executors/__init__.py +69 -0
  9. weaver/build_bundle/executors/alias.py +202 -0
  10. weaver/build_bundle/executors/base.py +132 -0
  11. weaver/build_bundle/executors/folder.py +71 -0
  12. weaver/build_bundle/executors/load_file.py +205 -0
  13. weaver/build_bundle/executors/spark_case.py +26 -0
  14. weaver/build_bundle/executors/spark_schema.py +60 -0
  15. weaver/build_bundle/executors/spark_sql.py +59 -0
  16. weaver/build_bundle/executors/spark_sql_batch.py +57 -0
  17. weaver/build_bundle/executors/spark_table.py +213 -0
  18. weaver/build_bundle/executors/sql_endpoint_refresh.py +34 -0
  19. weaver/build_bundle/executors/tsql.py +81 -0
  20. weaver/build_bundle/incremental.py +288 -0
  21. weaver/build_bundle/installer.py +384 -0
  22. weaver/build_bundle/models.py +288 -0
  23. weaver/build_bundle/payloads.py +34 -0
  24. weaver/build_bundle/physical.py +625 -0
  25. weaver/build_bundle/planner.py +389 -0
  26. weaver/build_bundle/prune.py +620 -0
  27. weaver/build_bundle/report.py +108 -0
  28. weaver/build_bundle/stages.py +196 -0
  29. weaver/build_bundle/targets.py +272 -0
  30. weaver/build_bundle/workflow.py +585 -0
  31. weaver/catalogue/__init__.py +73 -0
  32. weaver/catalogue/builtin.py +238 -0
  33. weaver/catalogue/claims.py +121 -0
  34. weaver/catalogue/projection.py +437 -0
  35. weaver/catalogue/reader.py +152 -0
  36. weaver/catalogue/reconcile.py +231 -0
  37. weaver/catalogue/render.py +410 -0
  38. weaver/catalogue/state.py +660 -0
  39. weaver/catalogue/tables.py +648 -0
  40. weaver/config.py +178 -0
  41. weaver/declaration/__init__.py +171 -0
  42. weaver/declaration/columns.py +223 -0
  43. weaver/declaration/ddl.py +266 -0
  44. weaver/declaration/dependencies.py +544 -0
  45. weaver/declaration/graph.py +240 -0
  46. weaver/declaration/item_dependencies.py +292 -0
  47. weaver/declaration/load.py +191 -0
  48. weaver/declaration/metadata.py +1405 -0
  49. weaver/declaration/model.py +448 -0
  50. weaver/declaration/references.py +294 -0
  51. weaver/declaration/repository.py +959 -0
  52. weaver/declaration/schemas.py +135 -0
  53. weaver/declaration/source.py +674 -0
  54. weaver/declaration/spark_load.py +759 -0
  55. weaver/declaration/sql_shaping.py +591 -0
  56. weaver/declaration/templates/ddl/declared_create_table.sql +64 -0
  57. weaver/declaration/templates/ddl/infer_create_table.sql +97 -0
  58. weaver/declaration/templates/ddl/metadata_column_validation.sql +30 -0
  59. weaver/declaration/templates/load/column_metadata.sql +40 -0
  60. weaver/declaration/templates/load/full_replace_body.sql +21 -0
  61. weaver/declaration/templates/load/install_load_procedure.sql +27 -0
  62. weaver/declaration/templates/load/load_procedure.sql +48 -0
  63. weaver/declaration/templates/load/primary_key_body.sql +113 -0
  64. weaver/declaration/tsql_ddl.py +468 -0
  65. weaver/declaration/tsql_load.py +417 -0
  66. weaver/declaration/warehouse_type_mapping.yml +93 -0
  67. weaver/diagnostics.py +247 -0
  68. weaver/errors.py +61 -0
  69. weaver/etl.py +469 -0
  70. weaver/fabric/__init__.py +107 -0
  71. weaver/fabric/auth.py +137 -0
  72. weaver/fabric/capacity.py +143 -0
  73. weaver/fabric/client.py +147 -0
  74. weaver/fabric/environment.py +460 -0
  75. weaver/fabric/livy.py +478 -0
  76. weaver/fabric/notebooks.py +201 -0
  77. weaver/fabric/onelake.py +263 -0
  78. weaver/fabric/resolution.py +344 -0
  79. weaver/fabric/resources.py +245 -0
  80. weaver/fabric/session.py +148 -0
  81. weaver/fabric/shortcuts.py +120 -0
  82. weaver/fabric/sql.py +118 -0
  83. weaver/fabric/store.py +198 -0
  84. weaver/initialise.py +209 -0
  85. weaver/lakehouse.py +386 -0
  86. weaver/load.py +474 -0
  87. weaver/load_execution.py +483 -0
  88. weaver/load_plan.py +912 -0
  89. weaver/load_report.py +330 -0
  90. weaver/load_resolution.py +386 -0
  91. weaver/locations.py +164 -0
  92. weaver/objects.py +392 -0
  93. weaver/operations.py +757 -0
  94. weaver/physical_wipe.py +369 -0
  95. weaver/push.py +76 -0
  96. weaver/resolution.py +292 -0
  97. weaver/runtime/__init__.py +30 -0
  98. weaver/runtime/folder_load.py +402 -0
  99. weaver/runtime/load_contract.py +245 -0
  100. weaver/runtime/load_result.py +104 -0
  101. weaver/runtime/spark_load.py +152 -0
  102. weaver/runtime/table_load.py +497 -0
  103. weaver/spark/__init__.py +49 -0
  104. weaver/spark/catalogue.py +245 -0
  105. weaver/spark/destination.py +195 -0
  106. weaver/spark/session.py +84 -0
  107. weaver/spark/tokens.py +138 -0
  108. weaver/sql/__init__.py +40 -0
  109. weaver/sql/authentication.py +38 -0
  110. weaver/sql/connection.py +90 -0
  111. weaver/sql/errors.py +25 -0
  112. weaver/sql/execution.py +123 -0
  113. weaver/sql/pool.py +174 -0
  114. weaver/sql/wipe.py +156 -0
  115. weaver/store.py +209 -0
  116. weaver/targets.py +257 -0
  117. weaver/task_logging.py +215 -0
  118. weaver/unbind.py +74 -0
  119. weaver/workspaces.py +175 -0
  120. weaver_cli/__init__.py +12 -0
  121. weaver_cli/__main__.py +7 -0
  122. weaver_cli/main.py +626 -0
  123. weaverstack-0.1.1.dist-info/METADATA +113 -0
  124. weaverstack-0.1.1.dist-info/RECORD +127 -0
  125. weaverstack-0.1.1.dist-info/WHEEL +4 -0
  126. weaverstack-0.1.1.dist-info/entry_points.txt +2 -0
  127. weaverstack-0.1.1.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,191 @@
1
+ """Generated load definitions — the *load* form of a Weaver document source.
2
+
3
+ The sibling of :mod:`weaver.declaration.ddl`, and the division between them is
4
+ the one Weaver is built on: build creates structure, load puts rows in it. A
5
+ source knows how to produce both, because it alone holds its language, kind, ID
6
+ and validated body — and neither generator ever reopens a repository.
7
+
8
+ .. code-block:: text
9
+
10
+ SourceDocument
11
+ knows the parsed contract, source body and language
12
+ creates the executable load definition
13
+
14
+ LoadArtefact
15
+ carries the completed payload into claiming, planning,
16
+ installation, registration and pruning
17
+
18
+ :class:`weaver.etl.LoadArtefact` remains the lifecycle object and does not
19
+ generate itself. It asks for a payload and carries what it gets, which is why
20
+ replacing what this module returns moves exactly the artefacts whose bytes
21
+ changed and nothing else.
22
+
23
+ **Neither generated load is finished here**, and that is the shape both share
24
+ rather than a limitation of either. What a load writes are the *physical*
25
+ target's columns, and they are not knowable while the target is still a
26
+ declaration: a Warehouse table and a Spark SQL table may each leave their shape
27
+ to be inferred at build. So generation produces something destination-free and
28
+ incomplete, and installation finishes it against the table that now exists:
29
+
30
+ .. code-block:: text
31
+
32
+ create_load() a destination-free instruction
33
+ → the target DDL is built
34
+ → the installer reads the physical target's columns
35
+ → the installer renders the executable definition
36
+ → destination tokens are resolved
37
+ → the runnable artefact is installed
38
+
39
+ The two differ only in where that rendering happens. A Warehouse load is an
40
+ *installer script*: it carries the assembly with it and runs it server-side,
41
+ reading ``sys.columns`` and creating the procedure in one execution. A Spark SQL
42
+ load is an *instruction*, because Spark has no way to assemble a program from
43
+ inside one — so the ``load_file`` executor reads the built table's schema,
44
+ renders the program and writes it down.
45
+
46
+ Which sources own a load, and in what form:
47
+
48
+ .. code-block:: text
49
+
50
+ Warehouse table (T-SQL) an installer script for [_].[Load S.N]
51
+ Lakehouse table (Spark SQL) an instruction the installer renders
52
+ Lakehouse table (Python) the authored module itself
53
+ Folder (Python) the authored module itself
54
+
55
+ The two Python forms are not generated at all, and that is the honest answer
56
+ rather than a gap: the author's module *is* the executable artefact, so it is
57
+ deployed verbatim and signed by its own bytes. It also needs no second phase —
58
+ a Python load reads its target's columns when it runs, which is the same
59
+ question answered at the same place. A view owns no load; its definition is its
60
+ query, so there is nothing to run.
61
+ """
62
+
63
+ from __future__ import annotations
64
+
65
+ from dataclasses import dataclass
66
+ from typing import TYPE_CHECKING
67
+
68
+ from .metadata import SPARK_SQL, SQL, TABLE
69
+
70
+ if TYPE_CHECKING:
71
+ from .source import SourceDocument
72
+
73
+ #: The T-SQL load generator's version, and the Spark SQL one's. Separate because
74
+ #: the two evolve independently: a change to the Spark DML has no bearing on what
75
+ #: a Warehouse procedure should contain, and bumping one must not invalidate the
76
+ #: other's artefacts. Each is a *signature salt*, never part of an identity.
77
+ #:
78
+ #: **Raise one whenever its generated output changes.** A signature is the
79
+ #: source's plus this number, so an edit to a generator that leaves both alone
80
+ #: produces different bytes with an unchanged signature — and incremental
81
+ #: selection, correctly, rebuilds nothing. The estate then keeps running the
82
+ #: previous generation's artefacts, which is the failure this exists to prevent
83
+ #: and which cost a Fabric round trip to notice.
84
+ TSQL_LOAD_VERSION = 5
85
+ SPARK_LOAD_VERSION = 7
86
+
87
+ #: What object a generated load installs, in the catalogue's vocabulary. A
88
+ #: Warehouse load is a stored procedure; a Lakehouse load is a file in the
89
+ #: deployed runtime tree.
90
+ PROCEDURE_OBJECT = "stored_procedure"
91
+ FILE_OBJECT = "file"
92
+
93
+ TSQL_LOAD_EXTENSION = ".sql"
94
+ SPARK_LOAD_EXTENSION = ".spark.sql"
95
+
96
+
97
+ @dataclass(frozen=True)
98
+ class GeneratedLoad:
99
+ """One source's generated load payload — installable, not yet executable.
100
+
101
+ ``payload`` is what the bundle carries and what the installer is handed. It
102
+ is deliberately *not* a finished program: a Warehouse load is a script that
103
+ assembles the procedure server-side, and a Spark SQL load is an instruction
104
+ the executor renders once it can see the built table. Calling it a completed
105
+ executable definition would misdescribe both, and invite a reader to write
106
+ the file down unchanged.
107
+
108
+ ``template_version`` is the generator's version, carried out so the artefact
109
+ layer can salt a signature with it without knowing which generator ran. That
110
+ is what makes a change to load generation rebuild exactly the loads it
111
+ changed, and leave deployed Python — signed by its own bytes — alone.
112
+ """
113
+
114
+ object_type: str
115
+ payload: bytes
116
+ template_version: int
117
+ extension: str
118
+
119
+
120
+ def generate_load(document: "SourceDocument") -> GeneratedLoad:
121
+ """The installable load payload for one validated source.
122
+
123
+ Only a table has one. A Folder's load is its authored module and a View has
124
+ no load at all, so neither reaches here — :func:`has_generated_load` is the
125
+ question to ask first.
126
+ """
127
+
128
+ if document.kind != TABLE:
129
+ raise NotImplementedError(
130
+ f"{document.relative_path}: a {document.kind} has no generated load"
131
+ )
132
+ if document.language == SQL:
133
+ return _tsql_load(document)
134
+ if document.language == SPARK_SQL:
135
+ return _spark_load(document)
136
+ raise NotImplementedError(
137
+ f"{document.relative_path}: a {document.language} table's load is its "
138
+ "authored module, which is deployed rather than generated"
139
+ )
140
+
141
+
142
+ def has_generated_load(document: "SourceDocument") -> bool:
143
+ """Whether this source's load is generated rather than deployed verbatim."""
144
+
145
+ return document.kind == TABLE and document.language in (SQL, SPARK_SQL)
146
+
147
+
148
+ def _tsql_load(document: "SourceDocument") -> GeneratedLoad:
149
+ from ..etl import load_procedure_name
150
+ from .tsql_load import generate_tsql_load_script
151
+
152
+ content = generate_tsql_load_script(
153
+ document.document,
154
+ document.sql_body or "",
155
+ procedure_name=load_procedure_name(document.object_id),
156
+ )
157
+ return GeneratedLoad(
158
+ object_type=PROCEDURE_OBJECT,
159
+ payload=content.encode("utf-8"),
160
+ template_version=TSQL_LOAD_VERSION,
161
+ extension=TSQL_LOAD_EXTENSION,
162
+ )
163
+
164
+
165
+ def _spark_load(document: "SourceDocument") -> GeneratedLoad:
166
+ from .spark_load import generate_spark_load_instruction
167
+
168
+ # An instruction, not the program: the columns a load writes are the built
169
+ # table's, and a Spark SQL table may infer its schema at build. The
170
+ # installer reads them and renders the file into place — the same two-phase
171
+ # shape the Warehouse load uses with sys.columns.
172
+ content = generate_spark_load_instruction(
173
+ document.document, document.sql_body or ""
174
+ )
175
+ return GeneratedLoad(
176
+ object_type=FILE_OBJECT,
177
+ payload=content.encode("utf-8"),
178
+ template_version=SPARK_LOAD_VERSION,
179
+ extension=SPARK_LOAD_EXTENSION,
180
+ )
181
+
182
+
183
+ __all__ = [
184
+ "FILE_OBJECT",
185
+ "PROCEDURE_OBJECT",
186
+ "SPARK_LOAD_VERSION",
187
+ "TSQL_LOAD_VERSION",
188
+ "GeneratedLoad",
189
+ "generate_load",
190
+ "has_generated_load",
191
+ ]