dataface 0.3.2.dev22__py3-none-any.whl → 0.3.2.dev150__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.
- dataface/DATAFACE_SYNTAX.md +15 -4
- dataface/__init__.py +70 -23
- dataface/agent_api/__init__.py +162 -68
- dataface/agent_api/_paths.py +9 -3
- dataface/agent_api/docs/yaml-reference.md +273 -239
- dataface/agent_api/init.py +1 -1
- dataface/agent_api/project_session.py +2 -1
- dataface/agent_api/serve.py +113 -0
- dataface/agent_api/validate.py +8 -0
- dataface/ai/agent.py +28 -2
- dataface/cli/_project.py +100 -1
- dataface/cli/commands/board_artifact.py +5 -2
- dataface/cli/commands/describe.py +7 -3
- dataface/cli/commands/docs.py +12 -3
- dataface/cli/commands/init.py +7 -14
- dataface/cli/commands/inspect.py +6 -1
- dataface/cli/commands/mcp_init.py +23 -43
- dataface/cli/commands/migrate.py +8 -3
- dataface/cli/commands/query.py +11 -8
- dataface/cli/commands/render.py +26 -9
- dataface/cli/commands/search.py +8 -2
- dataface/cli/commands/serve.py +25 -81
- dataface/cli/commands/skills_init.py +2 -19
- dataface/cli/commands/validate.py +7 -2
- dataface/cli/filesystem_project.py +5 -4
- dataface/cli/main.py +22 -33
- dataface/core/__init__.py +73 -28
- dataface/core/aliases.py +27 -0
- dataface/core/{compile/colors.py → colors.py} +20 -5
- dataface/core/compile/__init__.py +0 -8
- dataface/core/compile/channel.py +0 -13
- dataface/core/compile/compiler.py +161 -96
- dataface/core/compile/data_table.py +7 -2
- dataface/core/compile/enrich.py +18 -44
- dataface/core/compile/face_warnings.py +46 -8
- dataface/core/compile/format.py +47 -1
- dataface/core/compile/inherit_graph.py +3 -3
- dataface/core/compile/inherit_registry.yaml +1 -1
- dataface/core/compile/introspection.py +2 -13
- dataface/core/compile/migrations.py +112 -9
- dataface/core/compile/models/chart/authored/_base.py +1 -1
- dataface/core/compile/models/chart/normalized/__init__.py +2 -0
- dataface/core/compile/models/chart/normalized/_base.py +26 -0
- dataface/core/compile/models/chart/normalized/kpi.py +0 -4
- dataface/core/compile/models/chart/resolved/_base.py +28 -10
- dataface/core/compile/models/chart/resolved/callout.py +2 -3
- dataface/core/compile/models/chart/resolved/kpi.py +7 -3
- dataface/core/compile/models/chart/resolved/table.py +16 -2
- dataface/core/compile/models/config.py +5 -0
- dataface/core/compile/models/face/authored.py +24 -89
- dataface/core/compile/models/markers.py +1 -1
- dataface/core/compile/models/primitives.py +175 -2
- dataface/core/compile/models/query/authored.py +1 -119
- dataface/core/compile/models/refs.py +0 -2
- dataface/core/compile/models/source.py +78 -164
- dataface/core/compile/models/style/authored/__init__.py +32 -8
- dataface/core/compile/models/style/authored/_base.py +126 -69
- dataface/core/compile/models/style/authored/table.py +61 -3
- dataface/core/compile/models/style/resolved/__init__.py +12 -2
- dataface/core/compile/models/style/resolved/_base.py +142 -57
- dataface/core/compile/models/style/resolved/_cartesian.py +6 -11
- dataface/core/compile/models/style/resolved/_cascade.py +141 -54
- dataface/core/compile/models/style/resolved/area.py +1 -1
- dataface/core/compile/models/style/resolved/bar.py +1 -1
- dataface/core/compile/models/style/resolved/line.py +1 -1
- dataface/core/compile/models/style/resolved/scatter.py +3 -2
- dataface/core/compile/models/style/theme/__init__.py +36 -14
- dataface/core/compile/models/style/theme/_chart_base.py +17 -9
- dataface/core/compile/models/style/theme/axis.py +432 -312
- dataface/core/compile/models/style/theme/charts.py +10 -6
- dataface/core/compile/models/style/theme/kpi.py +2 -1
- dataface/core/compile/models/style/theme/page.py +5 -2
- dataface/core/compile/models/style/theme/table.py +2 -2
- dataface/core/compile/models/style/theme/variables.py +0 -3
- dataface/core/compile/models/vega_lite/config.py +1 -1
- dataface/core/compile/normalize_charts.py +10 -154
- dataface/core/compile/normalize_layout.py +20 -284
- dataface/core/compile/normalize_queries.py +24 -513
- dataface/core/compile/normalizer.py +4 -10
- dataface/core/compile/palette.py +19 -7
- dataface/core/compile/resolve/__init__.py +2 -0
- dataface/core/compile/resolve/_axes.py +147 -44
- dataface/core/compile/resolve/_channels.py +1 -1
- dataface/core/compile/resolve/_dispatch.py +1 -1
- dataface/core/compile/resolve/_domain.py +49 -32
- dataface/core/compile/resolve/_kwargs.py +39 -12
- dataface/core/compile/resolve/_marks.py +8 -3
- dataface/core/compile/resolve/_palette.py +7 -3
- dataface/core/compile/resolve/_table.py +124 -33
- dataface/core/compile/resolve/area.py +48 -18
- dataface/core/compile/resolve/bar.py +245 -64
- dataface/core/compile/resolve/geo.py +10 -3
- dataface/core/compile/resolve/heatmap.py +16 -8
- dataface/core/compile/resolve/line.py +35 -12
- dataface/core/compile/resolve/pie.py +4 -1
- dataface/core/compile/resolve/pie_attachment.py +1 -1
- dataface/core/compile/resolve/scatter.py +25 -13
- dataface/core/compile/resolve/simple.py +25 -2
- dataface/core/compile/schema_renderers/prompt.py +1 -1
- dataface/core/compile/schema_renderers/textmate_grammar.py +8 -1
- dataface/core/compile/source_map.py +211 -28
- dataface/core/compile/style_cascade.py +126 -55
- dataface/core/compile/validator.py +121 -44
- dataface/core/compile/yaml_error_formatter.py +108 -3
- dataface/core/connections.py +10 -3
- dataface/core/dashboard.py +27 -19
- dataface/core/defaults/default_config.yml +4 -0
- dataface/core/defaults/themes/_base.yaml +49 -40
- dataface/core/defaults/themes/cream.yaml +4 -4
- dataface/core/defaults/themes/diagnostics-title-baseline.yaml +1 -1
- dataface/core/defaults/themes/diagnostics-title-color-extreme.yaml +1 -1
- dataface/core/defaults/themes/diagnostics-title-font-extreme.yaml +1 -1
- dataface/core/defaults/themes/diagnostics-title-size-extreme.yaml +1 -1
- dataface/core/defaults/themes/diagnostics-title-weight-extreme.yaml +1 -1
- dataface/core/defaults/themes/editorial.yaml +9 -6
- dataface/core/defaults/themes/neon.yaml +15 -8
- dataface/core/defaults/themes/plain.yaml +6 -4
- dataface/core/defaults/themes/stark.yaml +24 -7
- dataface/core/defaults/themes/vivid.yaml +15 -8
- dataface/core/diagnostics/__init__.py +12 -2
- dataface/core/diagnostics/codes_compile.py +17 -33
- dataface/core/diagnostics/codes_execute.py +149 -0
- dataface/core/diagnostics/codes_render.py +144 -31
- dataface/core/diagnostics/diagnostic.py +30 -0
- dataface/core/diagnostics/hints.py +19 -0
- dataface/core/dialects/base.py +17 -0
- dataface/core/dialects/postgres.py +14 -0
- dataface/core/dialects/redshift.py +5 -0
- dataface/core/execute/__init__.py +59 -14
- dataface/core/execute/adapters/__init__.py +1 -1
- dataface/core/execute/adapters/dbt_adapter.py +4 -1
- dataface/core/execute/adapters/dbt_adapter_factory.py +19 -0
- dataface/core/execute/adapters/dbt_utils.py +84 -20
- dataface/core/execute/adapters/postgres_connection_manager.py +48 -0
- dataface/core/execute/adapters/sql_adapter.py +72 -4
- dataface/core/execute/cache_keys.py +4 -9
- dataface/core/execute/dbt_jinja.py +65 -10
- dataface/core/execute/executor.py +94 -19
- dataface/core/execute/file_source_materializer.py +185 -32
- dataface/core/execute/trivial_local_cache.py +5 -2
- dataface/core/font_measure.py +28 -0
- dataface/core/fonts.py +8 -5
- dataface/core/inspect/__init__.py +46 -7
- dataface/core/project.py +19 -3
- dataface/core/render/chart/data_table_attachment.py +14 -12
- dataface/core/render/chart/emitters/_cartesian.py +21 -18
- dataface/core/render/chart/emitters/_channels.py +56 -8
- dataface/core/render/chart/emitters/_label_overlap.py +18 -15
- dataface/core/render/chart/emitters/_measured_label_padding.py +4 -4
- dataface/core/render/chart/emitters/area.py +3 -3
- dataface/core/render/chart/emitters/bar.py +60 -35
- dataface/core/render/chart/emitters/geo.py +8 -13
- dataface/core/render/chart/emitters/heatmap.py +20 -26
- dataface/core/render/chart/emitters/line.py +2 -2
- dataface/core/render/chart/emitters/scatter.py +7 -30
- dataface/core/render/chart/features/baseline.py +22 -9
- dataface/core/render/chart/features/endpoint_labels.py +55 -32
- dataface/core/render/chart/features/mirror_axis.py +12 -9
- dataface/core/render/chart/features/value_labels.py +1 -1
- dataface/core/render/chart/kpi.py +11 -90
- dataface/core/render/chart/mark_extents.py +339 -0
- dataface/core/render/chart/presentation.py +6 -8
- dataface/core/render/chart/rendering.py +100 -46
- dataface/core/render/chart/session.py +10 -0
- dataface/core/render/chart/spec.py +3 -0
- dataface/core/render/chart/spec_builders.py +18 -18
- dataface/core/render/chart/table.py +81 -36
- dataface/core/render/chart/table_support.py +2 -53
- dataface/core/render/chart/time_unit_detect.py +3 -2
- dataface/core/render/chart/translate.py +82 -44
- dataface/core/render/chart/type_inference.py +45 -25
- dataface/core/render/chart/validation.py +91 -0
- dataface/core/render/chart/vega_lite.py +30 -11
- dataface/core/render/chart/vl_field_maps.py +111 -76
- dataface/core/render/conditions.py +122 -0
- dataface/core/render/controls.py +60 -187
- dataface/core/render/converters/chart.py +16 -6
- dataface/core/render/data_format.py +1 -1
- dataface/core/render/face_resolve.py +7 -0
- dataface/core/render/faces.py +97 -32
- dataface/core/render/fonts/DFTSerifOldstyleProportional-Regular.woff2 +0 -0
- dataface/core/render/fonts/DFTSerifOldstyleTabular-Regular.woff2 +0 -0
- dataface/core/render/fonts/SOURCE_CODE_PRO_LICENSE.txt +93 -93
- dataface/core/render/fonts/_font_face.css +10 -0
- dataface/core/render/layout_sizing.py +313 -238
- dataface/core/render/numeral_expr.py +80 -0
- dataface/core/render/renderer.py +14 -1
- dataface/core/render/sizing.py +42 -105
- dataface/core/render/svg_utils.py +60 -14
- dataface/core/render/terminal.py +1 -1
- dataface/core/render/variables_layout.py +211 -0
- dataface/core/render/variables_resolve.py +376 -0
- dataface/core/render/variables_strip.py +291 -368
- dataface/core/render/warnings/bar_band_width_too_narrow.py +64 -6
- dataface/core/render/warnings/base.py +18 -0
- dataface/core/render/warnings/layered_chart_shared_y_axis_scale_mismatch.py +25 -3
- dataface/core/render/warnings/layout_min_exceeds_height.py +68 -0
- dataface/core/render/warnings/likely_currency_or_percent_missing_formatter.py +12 -6
- dataface/core/render/warnings/palette_unsupported.py +6 -0
- dataface/core/render/warnings/pie_dominant_segment.py +1 -0
- dataface/core/render/warnings/pie_too_many_segments.py +1 -0
- dataface/core/render/warnings/point_map_out_of_projection.py +1 -0
- dataface/core/render/warnings/query_returned_zero_rows.py +1 -0
- dataface/core/render/warnings/redundant_encoding.py +20 -1
- dataface/core/render/warnings/registry.py +9 -3
- dataface/core/render/warnings/temporal_single_point.py +1 -0
- dataface/core/render/warnings/too_many_color_categories.py +1 -0
- dataface/core/render/warnings/too_many_x_categories.py +18 -2
- dataface/core/render/warnings/value_labels_crowd_width.py +9 -0
- dataface/core/render/warnings/y_encoding_mostly_null.py +1 -0
- dataface/core/render_format.py +18 -0
- dataface/core/serve/alias_index.py +16 -22
- dataface/core/text/format_d3.py +56 -0
- dataface/core/text/numeral_scale.py +218 -0
- dataface/core/utils.py +103 -10
- dataface/data/schemas/board-resolved/board-resolved.schema.json +8589 -7578
- dataface/data/schemas/yaml/0.3.2.json +1 -0
- dataface/data/schemas/yaml/manifest.json +7 -0
- {dataface-0.3.2.dev22.dist-info → dataface-0.3.2.dev150.dist-info}/METADATA +1 -3
- {dataface-0.3.2.dev22.dist-info → dataface-0.3.2.dev150.dist-info}/RECORD +223 -213
- dataface/core/inspect/partials/categorical.yml +0 -38
- dataface/core/inspect/partials/date.yml +0 -38
- dataface/core/inspect/partials/numeric.yml +0 -53
- dataface/core/inspect/templates/charts.yml +0 -94
- {dataface-0.3.2.dev22.dist-info → dataface-0.3.2.dev150.dist-info}/WHEEL +0 -0
- {dataface-0.3.2.dev22.dist-info → dataface-0.3.2.dev150.dist-info}/entry_points.txt +0 -0
- {dataface-0.3.2.dev22.dist-info → dataface-0.3.2.dev150.dist-info}/licenses/LICENSE +0 -0
|
@@ -43,8 +43,6 @@ def infer_query_type_from_keys(query_dict: dict[str, Any]) -> str:
|
|
|
43
43
|
return "values"
|
|
44
44
|
if "metrics" in query_dict or "dimensions" in query_dict:
|
|
45
45
|
return "metricflow"
|
|
46
|
-
if "explore" in query_dict or "merge" in query_dict:
|
|
47
|
-
return "lookml"
|
|
48
46
|
if "url" in query_dict:
|
|
49
47
|
return "http"
|
|
50
48
|
return "sql"
|
|
@@ -111,29 +111,14 @@ def reject_credential_literals(source_name: str, raw_config: Mapping[str, Any])
|
|
|
111
111
|
)
|
|
112
112
|
|
|
113
113
|
|
|
114
|
-
def _validate_relative_path(class_name: str, field_label: str, value: str) -> str:
|
|
115
|
-
"""Reject an empty, absolute, or root-escaping project-relative path.
|
|
116
|
-
|
|
117
|
-
Delegates the escape rule to ``assert_relpath`` — the single project-relative
|
|
118
|
-
path guard — and re-raises with the class/field context. Shared by every field
|
|
119
|
-
that stores a project-relative filesystem path (LookML model/lookml_path fields
|
|
120
|
-
today).
|
|
121
|
-
"""
|
|
122
|
-
try:
|
|
123
|
-
assert_relpath(value)
|
|
124
|
-
except ValueError as e:
|
|
125
|
-
raise ValueError(f"{class_name}: {field_label} {e}") from e
|
|
126
|
-
return value
|
|
127
|
-
|
|
128
|
-
|
|
129
114
|
def _validate_files_mapping(
|
|
130
|
-
class_name: str, files: dict[str, str], expected_extension: str
|
|
115
|
+
class_name: str, files: dict[str, str], expected_extension: str | tuple[str, ...]
|
|
131
116
|
) -> None:
|
|
132
117
|
"""Validate all keys and values in a file source's ``files:`` mapping.
|
|
133
118
|
|
|
134
119
|
Keys must be valid SQL identifiers (letters, digits, underscores; no
|
|
135
120
|
leading digit). Values must be non-empty relative POSIX paths that do not
|
|
136
|
-
escape above the project root after normalization, and must end in
|
|
121
|
+
escape above the project root after normalization, and must end in one of
|
|
137
122
|
``expected_extension`` (case-insensitive) — parsing dispatches on the
|
|
138
123
|
declared source ``type``, not the file extension, so this is what
|
|
139
124
|
guarantees every servable data file's extension matches its type (and
|
|
@@ -143,12 +128,18 @@ def _validate_files_mapping(
|
|
|
143
128
|
Args:
|
|
144
129
|
class_name: Source config class name (for error messages).
|
|
145
130
|
files: The ``files:`` mapping from the authored YAML.
|
|
146
|
-
expected_extension: The canonical extension
|
|
147
|
-
(e.g. ``".csv"``
|
|
131
|
+
expected_extension: The canonical extension or tuple of extensions for
|
|
132
|
+
this source type (e.g. ``".csv"`` or ``(".json", ".jsonl")``),
|
|
133
|
+
including the leading dot.
|
|
148
134
|
|
|
149
135
|
Raises:
|
|
150
136
|
ValueError: On an invalid key, path value, or extension mismatch.
|
|
151
137
|
"""
|
|
138
|
+
exts = (
|
|
139
|
+
(expected_extension,)
|
|
140
|
+
if isinstance(expected_extension, str)
|
|
141
|
+
else expected_extension
|
|
142
|
+
)
|
|
152
143
|
for table_name, file_path in files.items():
|
|
153
144
|
if not _SQL_IDENTIFIER_RE.match(table_name):
|
|
154
145
|
raise ValueError(
|
|
@@ -160,10 +151,11 @@ def _validate_files_mapping(
|
|
|
160
151
|
assert_relpath(file_path)
|
|
161
152
|
except ValueError as e:
|
|
162
153
|
raise ValueError(f"{class_name}: files[{table_name!r}] {e}") from e
|
|
163
|
-
if not file_path.lower().endswith(
|
|
154
|
+
if not any(file_path.lower().endswith(ext) for ext in exts):
|
|
155
|
+
ext_msg = repr(exts[0]) if len(exts) == 1 else f"one of {list(exts)}"
|
|
164
156
|
raise ValueError(
|
|
165
157
|
f"{class_name}: files[{table_name!r}] path {file_path!r} must end in "
|
|
166
|
-
f"{
|
|
158
|
+
f"{ext_msg} to match source type."
|
|
167
159
|
)
|
|
168
160
|
|
|
169
161
|
|
|
@@ -373,125 +365,12 @@ class DuckDBRetriesConfig(BaseModel):
|
|
|
373
365
|
] = Field(default_factory=_default_retryable_exceptions)
|
|
374
366
|
|
|
375
367
|
|
|
376
|
-
# ============================================================================
|
|
377
|
-
# LOOKML MODELS (nested under warehouse sources)
|
|
378
|
-
# ============================================================================
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
class LookMLModelConfig(BaseModel):
|
|
382
|
-
"""One LookML model: a named collection of explores under a warehouse source.
|
|
383
|
-
|
|
384
|
-
Exactly one selector chooses which explores belong to the collection:
|
|
385
|
-
|
|
386
|
-
- ``lkml``: a specific ``.lkml`` model file. Validated as a relative path
|
|
387
|
-
that does not escape the project root. File-scoping within likeml is not
|
|
388
|
-
yet implemented, so using this selector raises a clear error at compile
|
|
389
|
-
time directing authors to use an explicit ``explores:`` list instead.
|
|
390
|
-
Disk existence is not checked at parse time.
|
|
391
|
-
- ``group_label``: explores tagged with this label in LookML. Accepted at
|
|
392
|
-
parse time but likeml carries no group_label metadata yet, so using it
|
|
393
|
-
raises a clear error at compile time.
|
|
394
|
-
- ``explores``: an explicit list of explore names. The only selector
|
|
395
|
-
enforced at compile time: an explore not in the list is rejected.
|
|
396
|
-
"""
|
|
397
|
-
|
|
398
|
-
model_config = ConfigDict(extra="forbid")
|
|
399
|
-
|
|
400
|
-
lkml: str | None = Field(
|
|
401
|
-
default=None,
|
|
402
|
-
description="Path to a specific .lkml model file, relative to the project root.",
|
|
403
|
-
)
|
|
404
|
-
group_label: str | None = Field(
|
|
405
|
-
default=None,
|
|
406
|
-
description="LookML group_label selecting this model's explores. Not yet enforced at compile time.",
|
|
407
|
-
)
|
|
408
|
-
explores: list[str] | None = Field(
|
|
409
|
-
default=None, description="Explicit list of explore names in this model."
|
|
410
|
-
)
|
|
411
|
-
|
|
412
|
-
@field_validator("lkml")
|
|
413
|
-
@classmethod
|
|
414
|
-
def _validate_lkml(cls, v: str | None) -> str | None:
|
|
415
|
-
if v is None:
|
|
416
|
-
return v
|
|
417
|
-
return _validate_relative_path("LookMLModelConfig", "lkml", v)
|
|
418
|
-
|
|
419
|
-
@model_validator(mode="after")
|
|
420
|
-
def _require_one_selector(self) -> LookMLModelConfig:
|
|
421
|
-
selectors = [
|
|
422
|
-
name
|
|
423
|
-
for name, value in (
|
|
424
|
-
("lkml", self.lkml),
|
|
425
|
-
("group_label", self.group_label),
|
|
426
|
-
("explores", self.explores),
|
|
427
|
-
)
|
|
428
|
-
if value
|
|
429
|
-
]
|
|
430
|
-
if len(selectors) != 1:
|
|
431
|
-
got = ", ".join(selectors) if selectors else "none"
|
|
432
|
-
raise ValueError(
|
|
433
|
-
"LookMLModelConfig must set exactly one of 'lkml', 'group_label', "
|
|
434
|
-
f"'explores' (got {got})"
|
|
435
|
-
)
|
|
436
|
-
return self
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
class LookMLModelsMixin(BaseModel):
|
|
440
|
-
"""Shared nested ``models:`` field for warehouse sources with LookML explores.
|
|
441
|
-
|
|
442
|
-
A warehouse source with ``models:`` also declares ``lookml_path`` — the
|
|
443
|
-
directory of ``.lkml`` files the models' explores are parsed from. Each
|
|
444
|
-
warehouse source owns its own nested ``models:`` map; a model name must be
|
|
445
|
-
unique across the project (one model = one warehouse connection).
|
|
446
|
-
|
|
447
|
-
Example:
|
|
448
|
-
sources:
|
|
449
|
-
bigquery_prod:
|
|
450
|
-
type: bigquery
|
|
451
|
-
project: digital-arbor-400
|
|
452
|
-
dataset: transforms_bi
|
|
453
|
-
lookml_path: lkml
|
|
454
|
-
models:
|
|
455
|
-
finance: {lkml: fivetran.model.lkml}
|
|
456
|
-
core_kpis: {explores: [revenue, goals]}
|
|
457
|
-
"""
|
|
458
|
-
|
|
459
|
-
model_config = ConfigDict(extra="forbid")
|
|
460
|
-
|
|
461
|
-
lookml_path: str | None = Field(
|
|
462
|
-
default=None,
|
|
463
|
-
description="Relative path to the directory of .lkml files backing this source's models.",
|
|
464
|
-
)
|
|
465
|
-
models: dict[str, LookMLModelConfig] = Field(
|
|
466
|
-
default_factory=dict,
|
|
467
|
-
description="Named LookML model collections nested under this source.",
|
|
468
|
-
)
|
|
469
|
-
|
|
470
|
-
@field_validator("lookml_path")
|
|
471
|
-
@classmethod
|
|
472
|
-
def _validate_lookml_path(cls, v: str | None) -> str | None:
|
|
473
|
-
if v is None:
|
|
474
|
-
return v
|
|
475
|
-
return _validate_relative_path("LookMLModelsMixin", "lookml_path", v)
|
|
476
|
-
|
|
477
|
-
@model_validator(mode="after")
|
|
478
|
-
def _require_lookml_path_with_models(self) -> LookMLModelsMixin:
|
|
479
|
-
if self.models and not self.lookml_path:
|
|
480
|
-
raise ValueError(
|
|
481
|
-
"'lookml_path' is required when 'models' is set — it names the "
|
|
482
|
-
"directory of .lkml files the models' explores are parsed from."
|
|
483
|
-
)
|
|
484
|
-
return self
|
|
485
|
-
|
|
486
|
-
|
|
487
368
|
# ============================================================================
|
|
488
369
|
# DATABASE SOURCE CONFIGS
|
|
489
370
|
# ============================================================================
|
|
490
371
|
|
|
491
372
|
|
|
492
|
-
class PostgresSourceConfig(
|
|
493
|
-
AttributedSourceConfig, LookMLModelsMixin, DatabaseSourceConfig
|
|
494
|
-
):
|
|
373
|
+
class PostgresSourceConfig(AttributedSourceConfig, DatabaseSourceConfig):
|
|
495
374
|
"""Postgres source configuration.
|
|
496
375
|
|
|
497
376
|
Field names match dbt profiles.yml exactly.
|
|
@@ -567,9 +446,7 @@ class PostgresSourceConfig(
|
|
|
567
446
|
] = 1
|
|
568
447
|
|
|
569
448
|
|
|
570
|
-
class SnowflakeSourceConfig(
|
|
571
|
-
AttributedSourceConfig, LookMLModelsMixin, DatabaseSourceConfig
|
|
572
|
-
):
|
|
449
|
+
class SnowflakeSourceConfig(AttributedSourceConfig, DatabaseSourceConfig):
|
|
573
450
|
"""Snowflake source configuration.
|
|
574
451
|
|
|
575
452
|
Field names match dbt profiles.yml exactly.
|
|
@@ -702,9 +579,7 @@ class SnowflakeSourceConfig(
|
|
|
702
579
|
] = 0.0
|
|
703
580
|
|
|
704
581
|
|
|
705
|
-
class BigQuerySourceConfig(
|
|
706
|
-
AttributedSourceConfig, LookMLModelsMixin, DatabaseSourceConfig
|
|
707
|
-
):
|
|
582
|
+
class BigQuerySourceConfig(AttributedSourceConfig, DatabaseSourceConfig):
|
|
708
583
|
"""BigQuery source configuration.
|
|
709
584
|
|
|
710
585
|
Field names match dbt profiles.yml exactly.
|
|
@@ -870,12 +745,14 @@ class BigQuerySourceConfig(
|
|
|
870
745
|
return self
|
|
871
746
|
|
|
872
747
|
|
|
873
|
-
class RedshiftSourceConfig(
|
|
874
|
-
AttributedSourceConfig, LookMLModelsMixin, DatabaseSourceConfig
|
|
875
|
-
):
|
|
748
|
+
class RedshiftSourceConfig(AttributedSourceConfig, DatabaseSourceConfig):
|
|
876
749
|
"""Redshift source configuration.
|
|
877
750
|
|
|
878
|
-
Field names match dbt profiles.yml exactly
|
|
751
|
+
Field names match dbt profiles.yml exactly, except `autocommit`: Dataface
|
|
752
|
+
only reads on this connection, and a non-autocommit session holds
|
|
753
|
+
AccessShareLock on every table it read until the connection closes,
|
|
754
|
+
blocking any writer that needs ACCESS EXCLUSIVE. `build_adapter` forces
|
|
755
|
+
it on unconditionally, so it isn't part of the authored surface.
|
|
879
756
|
|
|
880
757
|
Read-only posture: no native driver flag. Bind to a SELECT-only IAM role
|
|
881
758
|
or database user at the Redshift/IAM layer.
|
|
@@ -957,10 +834,6 @@ class RedshiftSourceConfig(
|
|
|
957
834
|
str | None,
|
|
958
835
|
Field(description="AWS region containing the Redshift target."),
|
|
959
836
|
] = None
|
|
960
|
-
autocommit: Annotated[
|
|
961
|
-
bool | None,
|
|
962
|
-
Field(description="Whether Redshift statements autocommit."),
|
|
963
|
-
] = True
|
|
964
837
|
access_key_id: Annotated[
|
|
965
838
|
str | None,
|
|
966
839
|
Field(description="AWS access key identifier."),
|
|
@@ -1025,9 +898,7 @@ class RedshiftSourceConfig(
|
|
|
1025
898
|
] = None
|
|
1026
899
|
|
|
1027
900
|
|
|
1028
|
-
class MySQLSourceConfig(
|
|
1029
|
-
AttributedSourceConfig, LookMLModelsMixin, DatabaseSourceConfig
|
|
1030
|
-
):
|
|
901
|
+
class MySQLSourceConfig(AttributedSourceConfig, DatabaseSourceConfig):
|
|
1031
902
|
"""MySQL source configuration.
|
|
1032
903
|
|
|
1033
904
|
Field names match dbt profiles.yml exactly.
|
|
@@ -1048,9 +919,7 @@ class MySQLSourceConfig(
|
|
|
1048
919
|
password: str = Field(description="MySQL password.")
|
|
1049
920
|
|
|
1050
921
|
|
|
1051
|
-
class TrinoSourceConfig(
|
|
1052
|
-
AttributedSourceConfig, LookMLModelsMixin, DatabaseSourceConfig
|
|
1053
|
-
):
|
|
922
|
+
class TrinoSourceConfig(AttributedSourceConfig, DatabaseSourceConfig):
|
|
1054
923
|
"""Trino / Presto source configuration.
|
|
1055
924
|
|
|
1056
925
|
`database` names the Trino catalog; dbt-trino aliases `catalog` → `database`,
|
|
@@ -1195,7 +1064,7 @@ class TrinoSourceConfig(
|
|
|
1195
1064
|
] = None
|
|
1196
1065
|
|
|
1197
1066
|
|
|
1198
|
-
class DuckDBSourceConfig(
|
|
1067
|
+
class DuckDBSourceConfig(DatabaseSourceConfig):
|
|
1199
1068
|
"""DuckDB source configuration.
|
|
1200
1069
|
|
|
1201
1070
|
Read-only posture: handled in-process. DuckDBAdapter opens file-based DuckDB
|
|
@@ -1326,7 +1195,7 @@ class DuckDBSourceConfig(LookMLModelsMixin, DatabaseSourceConfig):
|
|
|
1326
1195
|
return data
|
|
1327
1196
|
|
|
1328
1197
|
|
|
1329
|
-
class SQLiteSourceConfig(BaseSourceConfig
|
|
1198
|
+
class SQLiteSourceConfig(BaseSourceConfig):
|
|
1330
1199
|
"""SQLite source configuration.
|
|
1331
1200
|
|
|
1332
1201
|
Read-only posture: handled in-process. SqliteAdapter opens SQLite connections
|
|
@@ -1354,6 +1223,9 @@ class CsvSourceConfig(BaseSourceConfig):
|
|
|
1354
1223
|
|
|
1355
1224
|
A file source is a namespace of relations: each key in `files` is a table
|
|
1356
1225
|
name; the value is the path to the CSV file (relative to the project root).
|
|
1226
|
+
Glob patterns (``*`` and ``?``) are supported: every matched file is loaded
|
|
1227
|
+
and its rows are concatenated into one table. All matched files must share
|
|
1228
|
+
the same column schema. A glob that matches no files is an error.
|
|
1357
1229
|
|
|
1358
1230
|
Example:
|
|
1359
1231
|
sources:
|
|
@@ -1362,14 +1234,22 @@ class CsvSourceConfig(BaseSourceConfig):
|
|
|
1362
1234
|
files:
|
|
1363
1235
|
sales: assets/data/sales.csv
|
|
1364
1236
|
returns: assets/data/returns.csv
|
|
1237
|
+
monthly: assets/data/monthly_*.csv
|
|
1365
1238
|
"""
|
|
1366
1239
|
|
|
1367
1240
|
source_category: ClassVar[str] = "file"
|
|
1368
1241
|
type: Literal["csv"] = Field(description="Source type identifier.")
|
|
1369
1242
|
files: dict[str, str] = Field(
|
|
1370
1243
|
description=(
|
|
1371
|
-
"Mapping of table_name →
|
|
1372
|
-
"Each key is the SQL table name
|
|
1244
|
+
"Mapping of table_name → relative path or glob pattern. "
|
|
1245
|
+
"Each key is the SQL table name. "
|
|
1246
|
+
"The value is a path relative to the project root, or a glob "
|
|
1247
|
+
"pattern using ``*`` (matches within a directory) and ``?`` "
|
|
1248
|
+
"(matches one character). "
|
|
1249
|
+
"A glob must match at least one file; all matched files must share "
|
|
1250
|
+
"the same column schema and are concatenated into one table. "
|
|
1251
|
+
"The fan-out limit is ``execution.max_glob_file_count`` in "
|
|
1252
|
+
"``dataface.yml`` (default 1000). "
|
|
1373
1253
|
"Required and must not be empty."
|
|
1374
1254
|
)
|
|
1375
1255
|
)
|
|
@@ -1392,6 +1272,9 @@ class ParquetSourceConfig(BaseSourceConfig):
|
|
|
1392
1272
|
|
|
1393
1273
|
A file source is a namespace of relations: each key in `files` is a table
|
|
1394
1274
|
name; the value is the path to the Parquet file (relative to the project root).
|
|
1275
|
+
Glob patterns (``*`` and ``?``) are supported: every matched file is loaded
|
|
1276
|
+
and its rows are concatenated into one table. All matched files must share
|
|
1277
|
+
the same column schema. A glob that matches no files is an error.
|
|
1395
1278
|
|
|
1396
1279
|
Example:
|
|
1397
1280
|
sources:
|
|
@@ -1399,14 +1282,22 @@ class ParquetSourceConfig(BaseSourceConfig):
|
|
|
1399
1282
|
type: parquet
|
|
1400
1283
|
files:
|
|
1401
1284
|
events: assets/data/events.parquet
|
|
1285
|
+
archive: assets/data/archive_*.parquet
|
|
1402
1286
|
"""
|
|
1403
1287
|
|
|
1404
1288
|
source_category: ClassVar[str] = "file"
|
|
1405
1289
|
type: Literal["parquet"] = Field(description="Source type identifier.")
|
|
1406
1290
|
files: dict[str, str] = Field(
|
|
1407
1291
|
description=(
|
|
1408
|
-
"Mapping of table_name →
|
|
1409
|
-
"Each key is the SQL table name
|
|
1292
|
+
"Mapping of table_name → relative path or glob pattern. "
|
|
1293
|
+
"Each key is the SQL table name. "
|
|
1294
|
+
"The value is a path relative to the project root, or a glob "
|
|
1295
|
+
"pattern using ``*`` (matches within a directory) and ``?`` "
|
|
1296
|
+
"(matches one character). "
|
|
1297
|
+
"A glob must match at least one file; all matched files must share "
|
|
1298
|
+
"the same column schema and are concatenated into one table. "
|
|
1299
|
+
"The fan-out limit is ``execution.max_glob_file_count`` in "
|
|
1300
|
+
"``dataface.yml`` (default 1000). "
|
|
1410
1301
|
"Required and must not be empty."
|
|
1411
1302
|
)
|
|
1412
1303
|
)
|
|
@@ -1427,6 +1318,12 @@ class JsonSourceConfig(BaseSourceConfig):
|
|
|
1427
1318
|
|
|
1428
1319
|
A file source is a namespace of relations: each key in `files` is a table
|
|
1429
1320
|
name; the value is the path to the JSON file (relative to the project root).
|
|
1321
|
+
Both ``.json`` and ``.jsonl`` (newline-delimited JSON) extensions are accepted;
|
|
1322
|
+
the parser is chosen by extension: ``.json`` → standard JSON array/object,
|
|
1323
|
+
``.jsonl`` → newline-delimited JSON (one object per line).
|
|
1324
|
+
Glob patterns (``*`` and ``?``) are supported: every matched file is loaded
|
|
1325
|
+
and its rows are concatenated into one table. A glob that matches no files
|
|
1326
|
+
is an error.
|
|
1430
1327
|
|
|
1431
1328
|
Example:
|
|
1432
1329
|
sources:
|
|
@@ -1434,17 +1331,34 @@ class JsonSourceConfig(BaseSourceConfig):
|
|
|
1434
1331
|
type: json
|
|
1435
1332
|
files:
|
|
1436
1333
|
products: assets/data/products.json
|
|
1334
|
+
events: assets/data/events_*.jsonl
|
|
1437
1335
|
"""
|
|
1438
1336
|
|
|
1439
1337
|
source_category: ClassVar[str] = "file"
|
|
1440
1338
|
type: Literal["json"] = Field(description="Source type identifier.")
|
|
1441
1339
|
files: dict[str, str] = Field(
|
|
1442
1340
|
description=(
|
|
1443
|
-
"Mapping of table_name →
|
|
1444
|
-
"Each key is the SQL table name
|
|
1341
|
+
"Mapping of table_name → relative path or glob pattern. "
|
|
1342
|
+
"Each key is the SQL table name. "
|
|
1343
|
+
"The value is a path relative to the project root, or a glob "
|
|
1344
|
+
"pattern using ``*`` (matches within a directory) and ``?`` "
|
|
1345
|
+
"(matches one character). "
|
|
1346
|
+
"A glob must match at least one file and all matched files are "
|
|
1347
|
+
"concatenated into one table. "
|
|
1348
|
+
"The fan-out limit is ``execution.max_glob_file_count`` in "
|
|
1349
|
+
"``dataface.yml`` (default 1000). "
|
|
1445
1350
|
"Required and must not be empty."
|
|
1446
1351
|
)
|
|
1447
1352
|
)
|
|
1353
|
+
union_by_name: bool = Field(
|
|
1354
|
+
default=False,
|
|
1355
|
+
description=(
|
|
1356
|
+
"When True, rows from different files in a glob set are unified by "
|
|
1357
|
+
"column name: columns absent in a particular file are filled with "
|
|
1358
|
+
"NULL. When False (default), all files in a glob set must share the "
|
|
1359
|
+
"same column schema — a mismatch raises an error."
|
|
1360
|
+
),
|
|
1361
|
+
)
|
|
1448
1362
|
|
|
1449
1363
|
@model_validator(mode="after")
|
|
1450
1364
|
def _require_nonempty_files(self) -> JsonSourceConfig:
|
|
@@ -1453,7 +1367,7 @@ class JsonSourceConfig(BaseSourceConfig):
|
|
|
1453
1367
|
"JsonSourceConfig: files must not be empty — "
|
|
1454
1368
|
"provide at least one table_name → path entry."
|
|
1455
1369
|
)
|
|
1456
|
-
_validate_files_mapping("JsonSourceConfig", self.files, ".json")
|
|
1370
|
+
_validate_files_mapping("JsonSourceConfig", self.files, (".json", ".jsonl"))
|
|
1457
1371
|
return self
|
|
1458
1372
|
|
|
1459
1373
|
|
|
@@ -42,25 +42,36 @@ from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
|
42
42
|
# Regression coverage: test_style_authored_bootstrap_order.py.
|
|
43
43
|
from dataface.core.compile.models.style.authored._base import (
|
|
44
44
|
AreaMarkStylePatch,
|
|
45
|
-
AxisDomainStylePatch,
|
|
46
|
-
AxisElementStylePatch,
|
|
47
45
|
AxisGridStylePatch,
|
|
48
|
-
|
|
46
|
+
AxisLabelStylePatch,
|
|
47
|
+
AxisLineStylePatch,
|
|
49
48
|
AxisTicksStylePatch,
|
|
49
|
+
AxisTitleStylePatch,
|
|
50
|
+
AxisXStylePatch,
|
|
51
|
+
AxisYStylePatch,
|
|
52
|
+
BandAxisStylePatch,
|
|
50
53
|
BarMarkStylePatch,
|
|
54
|
+
BaseAxisGridStylePatch,
|
|
55
|
+
BaseAxisStylePatch,
|
|
56
|
+
BaseScaleStylePatch,
|
|
51
57
|
DataTableStylePatch,
|
|
58
|
+
DimensionLabelStylePatch,
|
|
59
|
+
DimensionTicksStylePatch,
|
|
52
60
|
EndpointLabelsConfig,
|
|
53
61
|
EndpointLabelsConfigPatch,
|
|
54
62
|
GlobalMarksStylePatch,
|
|
55
63
|
LegendStylePatch,
|
|
56
64
|
LineMarkStylePatch,
|
|
65
|
+
MeasureGridStylePatch,
|
|
57
66
|
PaddingStylePatch,
|
|
58
67
|
PointMarkStylePatch,
|
|
59
|
-
|
|
68
|
+
QuantitativeAxisStylePatch,
|
|
69
|
+
ScaleContinuousStylePatch,
|
|
60
70
|
StylePatch,
|
|
61
71
|
TitleStylePatch,
|
|
62
72
|
TooltipStylePatch,
|
|
63
73
|
TotalStylePatch,
|
|
74
|
+
XScaleStylePatch,
|
|
64
75
|
)
|
|
65
76
|
from dataface.core.compile.models.style.authored.area import AreaChartStylePatch
|
|
66
77
|
from dataface.core.compile.models.style.authored.bar import BarChartStylePatch
|
|
@@ -99,6 +110,7 @@ from dataface.core.compile.models.style.authored.table import (
|
|
|
99
110
|
TableColumnConfig,
|
|
100
111
|
TableColumnDefaultsConfig,
|
|
101
112
|
_validate_glyph_pair as _validate_glyph_pair,
|
|
113
|
+
fill_table_column_defaults,
|
|
102
114
|
)
|
|
103
115
|
|
|
104
116
|
__all__ = [
|
|
@@ -106,11 +118,23 @@ __all__ = [
|
|
|
106
118
|
"_validate_glyph_pair",
|
|
107
119
|
"AreaChartStylePatch",
|
|
108
120
|
"AreaMarkStylePatch",
|
|
109
|
-
"AxisDomainStylePatch",
|
|
110
|
-
"AxisElementStylePatch",
|
|
111
121
|
"AxisGridStylePatch",
|
|
112
|
-
"
|
|
122
|
+
"AxisLabelStylePatch",
|
|
123
|
+
"AxisLineStylePatch",
|
|
113
124
|
"AxisTicksStylePatch",
|
|
125
|
+
"AxisTitleStylePatch",
|
|
126
|
+
"AxisXStylePatch",
|
|
127
|
+
"AxisYStylePatch",
|
|
128
|
+
"BandAxisStylePatch",
|
|
129
|
+
"BaseAxisGridStylePatch",
|
|
130
|
+
"BaseAxisStylePatch",
|
|
131
|
+
"BaseScaleStylePatch",
|
|
132
|
+
"DimensionLabelStylePatch",
|
|
133
|
+
"DimensionTicksStylePatch",
|
|
134
|
+
"MeasureGridStylePatch",
|
|
135
|
+
"QuantitativeAxisStylePatch",
|
|
136
|
+
"ScaleContinuousStylePatch",
|
|
137
|
+
"XScaleStylePatch",
|
|
114
138
|
"BarChartStylePatch",
|
|
115
139
|
"BarMarkStylePatch",
|
|
116
140
|
"CalloutChartStylePatch",
|
|
@@ -131,7 +155,6 @@ __all__ = [
|
|
|
131
155
|
"PieChartStylePatch",
|
|
132
156
|
"PointMapChartStylePatch",
|
|
133
157
|
"PointMarkStylePatch",
|
|
134
|
-
"ScaleStylePatch",
|
|
135
158
|
"ScatterChartStylePatch",
|
|
136
159
|
"SparkBarBarStylePatch",
|
|
137
160
|
"SparkBarChartLabelStylePatch",
|
|
@@ -146,6 +169,7 @@ __all__ = [
|
|
|
146
169
|
"TableColumnDefaultsConfig",
|
|
147
170
|
"TitleStylePatch",
|
|
148
171
|
"TooltipStylePatch",
|
|
172
|
+
"fill_table_column_defaults",
|
|
149
173
|
"TotalStylePatch",
|
|
150
174
|
]
|
|
151
175
|
|