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,135 @@
1
+ """Schema Weaver document files — one declared schema per file under ``_schemas``.
2
+
3
+ A repository declares its schemas explicitly rather than letting a two-part
4
+ object ID conjure one on the fly. Every ``Schema.Object`` an object or an alias
5
+ uses implies a schema, and that schema must be declared here or the repository
6
+ is invalid.
7
+
8
+ ::
9
+
10
+ repo/
11
+ _schemas/
12
+ Sales.yml
13
+ Reporting.yml
14
+ Sales__Order.py
15
+ Reporting.OrderReport.sql
16
+
17
+ Each file names exactly one schema, and its filename — without ``.yml`` — must
18
+ match the declared ``Schema ID`` exactly, case included. A schema is a
19
+ repository resource: it is not owned by a Lakehouse, a Warehouse, a tier or an
20
+ object folder, and declaring one does not create anything physical.
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ from dataclasses import dataclass, field, replace
26
+ from typing import Any
27
+
28
+ import yaml
29
+
30
+ from ..errors import DiscoveryError, MetadataError
31
+ from .metadata import _UniqueKeyLoader
32
+
33
+ #: The directory schema Weaver document files live in, relative to the repository root.
34
+ SCHEMAS_DIRECTORY = "_schemas"
35
+ SCHEMA_SUFFIX = ".yml"
36
+
37
+ _SCHEMA_ID = "Schema ID"
38
+ _DESCRIPTION = "Description"
39
+ _ALLOWED_KEYS = {_SCHEMA_ID, _DESCRIPTION}
40
+
41
+
42
+ @dataclass(frozen=True)
43
+ class SchemaSes:
44
+ """One declared schema, kept as a distinct repository resource."""
45
+
46
+ schema_id: str
47
+ description: str | None
48
+ relative_path: str
49
+ #: The declaration's content hash, on the same terms as an object's — it is
50
+ #: what the catalogue records as the signature of a schema row. Empty for a
51
+ #: schema parsed from text rather than read from a file.
52
+ source_hash: str = ""
53
+ raw: dict[str, Any] = field(default_factory=dict)
54
+
55
+
56
+ def schema_id_for_filename(relative_path: str) -> str:
57
+ """The Schema ID a ``_schemas`` filename claims, before the file is read."""
58
+
59
+ filename = relative_path.rsplit("/", 1)[-1]
60
+ return filename[: -len(SCHEMA_SUFFIX)]
61
+
62
+
63
+ def is_schema_file(relative_path: str) -> bool:
64
+ """True for ``_schemas/<name>.yml`` at the repository root."""
65
+
66
+ parts = relative_path.split("/")
67
+ return (
68
+ len(parts) == 2
69
+ and parts[0] == SCHEMAS_DIRECTORY
70
+ and parts[1].endswith(SCHEMA_SUFFIX)
71
+ and len(parts[1]) > len(SCHEMA_SUFFIX)
72
+ )
73
+
74
+
75
+ def read_schema_document(relative_path: str, data: bytes) -> SchemaSes:
76
+ """Parse and validate one schema Weaver document file against its filename."""
77
+
78
+ try:
79
+ text = data.decode("utf-8-sig")
80
+ except UnicodeDecodeError as exc:
81
+ raise DiscoveryError(f"{relative_path}: must be UTF-8 text ({exc})") from exc
82
+
83
+ from .source import content_hash
84
+
85
+ filename_id = schema_id_for_filename(relative_path)
86
+ schema = parse_schema_document(text, relative_path)
87
+ schema = replace(schema, source_hash=content_hash(data))
88
+ if schema.schema_id != filename_id:
89
+ raise DiscoveryError(
90
+ f"{relative_path}: declares Schema ID {schema.schema_id!r} but the filename "
91
+ f"names {filename_id!r} — they must match exactly, case included"
92
+ )
93
+ return schema
94
+
95
+
96
+ def parse_schema_document(text: str, relative_path: str) -> SchemaSes:
97
+ """Parse the YAML of a schema Weaver document file."""
98
+
99
+ try:
100
+ loaded = yaml.load(text, Loader=_UniqueKeyLoader)
101
+ except MetadataError:
102
+ raise
103
+ except yaml.YAMLError as exc:
104
+ raise DiscoveryError(f"{relative_path}: invalid schema YAML: {exc}") from exc
105
+
106
+ if not isinstance(loaded, dict):
107
+ raise DiscoveryError(f"{relative_path}: schema metadata must be a YAML mapping")
108
+
109
+ unknown = {str(key) for key in loaded} - _ALLOWED_KEYS
110
+ if unknown:
111
+ raise DiscoveryError(
112
+ f"{relative_path}: unknown schema key(s): " + ", ".join(sorted(unknown))
113
+ )
114
+
115
+ schema_id = loaded.get(_SCHEMA_ID)
116
+ if not isinstance(schema_id, str) or not schema_id.strip():
117
+ raise DiscoveryError(f"{relative_path}: {_SCHEMA_ID} is required and must be non-empty")
118
+ schema_id = schema_id.strip()
119
+ if "." in schema_id or any(character.isspace() for character in schema_id):
120
+ raise DiscoveryError(
121
+ f"{relative_path}: {_SCHEMA_ID} must be a single bare name, got {schema_id!r}"
122
+ )
123
+
124
+ description = loaded.get(_DESCRIPTION)
125
+ if description is not None:
126
+ if not isinstance(description, str) or not description.strip():
127
+ raise DiscoveryError(f"{relative_path}: {_DESCRIPTION} must be non-empty when present")
128
+ description = description.strip()
129
+
130
+ return SchemaSes(
131
+ schema_id=schema_id,
132
+ description=description,
133
+ relative_path=relative_path,
134
+ raw=dict(loaded),
135
+ )