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.
Files changed (227) hide show
  1. dataface/DATAFACE_SYNTAX.md +15 -4
  2. dataface/__init__.py +70 -23
  3. dataface/agent_api/__init__.py +162 -68
  4. dataface/agent_api/_paths.py +9 -3
  5. dataface/agent_api/docs/yaml-reference.md +273 -239
  6. dataface/agent_api/init.py +1 -1
  7. dataface/agent_api/project_session.py +2 -1
  8. dataface/agent_api/serve.py +113 -0
  9. dataface/agent_api/validate.py +8 -0
  10. dataface/ai/agent.py +28 -2
  11. dataface/cli/_project.py +100 -1
  12. dataface/cli/commands/board_artifact.py +5 -2
  13. dataface/cli/commands/describe.py +7 -3
  14. dataface/cli/commands/docs.py +12 -3
  15. dataface/cli/commands/init.py +7 -14
  16. dataface/cli/commands/inspect.py +6 -1
  17. dataface/cli/commands/mcp_init.py +23 -43
  18. dataface/cli/commands/migrate.py +8 -3
  19. dataface/cli/commands/query.py +11 -8
  20. dataface/cli/commands/render.py +26 -9
  21. dataface/cli/commands/search.py +8 -2
  22. dataface/cli/commands/serve.py +25 -81
  23. dataface/cli/commands/skills_init.py +2 -19
  24. dataface/cli/commands/validate.py +7 -2
  25. dataface/cli/filesystem_project.py +5 -4
  26. dataface/cli/main.py +22 -33
  27. dataface/core/__init__.py +73 -28
  28. dataface/core/aliases.py +27 -0
  29. dataface/core/{compile/colors.py → colors.py} +20 -5
  30. dataface/core/compile/__init__.py +0 -8
  31. dataface/core/compile/channel.py +0 -13
  32. dataface/core/compile/compiler.py +161 -96
  33. dataface/core/compile/data_table.py +7 -2
  34. dataface/core/compile/enrich.py +18 -44
  35. dataface/core/compile/face_warnings.py +46 -8
  36. dataface/core/compile/format.py +47 -1
  37. dataface/core/compile/inherit_graph.py +3 -3
  38. dataface/core/compile/inherit_registry.yaml +1 -1
  39. dataface/core/compile/introspection.py +2 -13
  40. dataface/core/compile/migrations.py +112 -9
  41. dataface/core/compile/models/chart/authored/_base.py +1 -1
  42. dataface/core/compile/models/chart/normalized/__init__.py +2 -0
  43. dataface/core/compile/models/chart/normalized/_base.py +26 -0
  44. dataface/core/compile/models/chart/normalized/kpi.py +0 -4
  45. dataface/core/compile/models/chart/resolved/_base.py +28 -10
  46. dataface/core/compile/models/chart/resolved/callout.py +2 -3
  47. dataface/core/compile/models/chart/resolved/kpi.py +7 -3
  48. dataface/core/compile/models/chart/resolved/table.py +16 -2
  49. dataface/core/compile/models/config.py +5 -0
  50. dataface/core/compile/models/face/authored.py +24 -89
  51. dataface/core/compile/models/markers.py +1 -1
  52. dataface/core/compile/models/primitives.py +175 -2
  53. dataface/core/compile/models/query/authored.py +1 -119
  54. dataface/core/compile/models/refs.py +0 -2
  55. dataface/core/compile/models/source.py +78 -164
  56. dataface/core/compile/models/style/authored/__init__.py +32 -8
  57. dataface/core/compile/models/style/authored/_base.py +126 -69
  58. dataface/core/compile/models/style/authored/table.py +61 -3
  59. dataface/core/compile/models/style/resolved/__init__.py +12 -2
  60. dataface/core/compile/models/style/resolved/_base.py +142 -57
  61. dataface/core/compile/models/style/resolved/_cartesian.py +6 -11
  62. dataface/core/compile/models/style/resolved/_cascade.py +141 -54
  63. dataface/core/compile/models/style/resolved/area.py +1 -1
  64. dataface/core/compile/models/style/resolved/bar.py +1 -1
  65. dataface/core/compile/models/style/resolved/line.py +1 -1
  66. dataface/core/compile/models/style/resolved/scatter.py +3 -2
  67. dataface/core/compile/models/style/theme/__init__.py +36 -14
  68. dataface/core/compile/models/style/theme/_chart_base.py +17 -9
  69. dataface/core/compile/models/style/theme/axis.py +432 -312
  70. dataface/core/compile/models/style/theme/charts.py +10 -6
  71. dataface/core/compile/models/style/theme/kpi.py +2 -1
  72. dataface/core/compile/models/style/theme/page.py +5 -2
  73. dataface/core/compile/models/style/theme/table.py +2 -2
  74. dataface/core/compile/models/style/theme/variables.py +0 -3
  75. dataface/core/compile/models/vega_lite/config.py +1 -1
  76. dataface/core/compile/normalize_charts.py +10 -154
  77. dataface/core/compile/normalize_layout.py +20 -284
  78. dataface/core/compile/normalize_queries.py +24 -513
  79. dataface/core/compile/normalizer.py +4 -10
  80. dataface/core/compile/palette.py +19 -7
  81. dataface/core/compile/resolve/__init__.py +2 -0
  82. dataface/core/compile/resolve/_axes.py +147 -44
  83. dataface/core/compile/resolve/_channels.py +1 -1
  84. dataface/core/compile/resolve/_dispatch.py +1 -1
  85. dataface/core/compile/resolve/_domain.py +49 -32
  86. dataface/core/compile/resolve/_kwargs.py +39 -12
  87. dataface/core/compile/resolve/_marks.py +8 -3
  88. dataface/core/compile/resolve/_palette.py +7 -3
  89. dataface/core/compile/resolve/_table.py +124 -33
  90. dataface/core/compile/resolve/area.py +48 -18
  91. dataface/core/compile/resolve/bar.py +245 -64
  92. dataface/core/compile/resolve/geo.py +10 -3
  93. dataface/core/compile/resolve/heatmap.py +16 -8
  94. dataface/core/compile/resolve/line.py +35 -12
  95. dataface/core/compile/resolve/pie.py +4 -1
  96. dataface/core/compile/resolve/pie_attachment.py +1 -1
  97. dataface/core/compile/resolve/scatter.py +25 -13
  98. dataface/core/compile/resolve/simple.py +25 -2
  99. dataface/core/compile/schema_renderers/prompt.py +1 -1
  100. dataface/core/compile/schema_renderers/textmate_grammar.py +8 -1
  101. dataface/core/compile/source_map.py +211 -28
  102. dataface/core/compile/style_cascade.py +126 -55
  103. dataface/core/compile/validator.py +121 -44
  104. dataface/core/compile/yaml_error_formatter.py +108 -3
  105. dataface/core/connections.py +10 -3
  106. dataface/core/dashboard.py +27 -19
  107. dataface/core/defaults/default_config.yml +4 -0
  108. dataface/core/defaults/themes/_base.yaml +49 -40
  109. dataface/core/defaults/themes/cream.yaml +4 -4
  110. dataface/core/defaults/themes/diagnostics-title-baseline.yaml +1 -1
  111. dataface/core/defaults/themes/diagnostics-title-color-extreme.yaml +1 -1
  112. dataface/core/defaults/themes/diagnostics-title-font-extreme.yaml +1 -1
  113. dataface/core/defaults/themes/diagnostics-title-size-extreme.yaml +1 -1
  114. dataface/core/defaults/themes/diagnostics-title-weight-extreme.yaml +1 -1
  115. dataface/core/defaults/themes/editorial.yaml +9 -6
  116. dataface/core/defaults/themes/neon.yaml +15 -8
  117. dataface/core/defaults/themes/plain.yaml +6 -4
  118. dataface/core/defaults/themes/stark.yaml +24 -7
  119. dataface/core/defaults/themes/vivid.yaml +15 -8
  120. dataface/core/diagnostics/__init__.py +12 -2
  121. dataface/core/diagnostics/codes_compile.py +17 -33
  122. dataface/core/diagnostics/codes_execute.py +149 -0
  123. dataface/core/diagnostics/codes_render.py +144 -31
  124. dataface/core/diagnostics/diagnostic.py +30 -0
  125. dataface/core/diagnostics/hints.py +19 -0
  126. dataface/core/dialects/base.py +17 -0
  127. dataface/core/dialects/postgres.py +14 -0
  128. dataface/core/dialects/redshift.py +5 -0
  129. dataface/core/execute/__init__.py +59 -14
  130. dataface/core/execute/adapters/__init__.py +1 -1
  131. dataface/core/execute/adapters/dbt_adapter.py +4 -1
  132. dataface/core/execute/adapters/dbt_adapter_factory.py +19 -0
  133. dataface/core/execute/adapters/dbt_utils.py +84 -20
  134. dataface/core/execute/adapters/postgres_connection_manager.py +48 -0
  135. dataface/core/execute/adapters/sql_adapter.py +72 -4
  136. dataface/core/execute/cache_keys.py +4 -9
  137. dataface/core/execute/dbt_jinja.py +65 -10
  138. dataface/core/execute/executor.py +94 -19
  139. dataface/core/execute/file_source_materializer.py +185 -32
  140. dataface/core/execute/trivial_local_cache.py +5 -2
  141. dataface/core/font_measure.py +28 -0
  142. dataface/core/fonts.py +8 -5
  143. dataface/core/inspect/__init__.py +46 -7
  144. dataface/core/project.py +19 -3
  145. dataface/core/render/chart/data_table_attachment.py +14 -12
  146. dataface/core/render/chart/emitters/_cartesian.py +21 -18
  147. dataface/core/render/chart/emitters/_channels.py +56 -8
  148. dataface/core/render/chart/emitters/_label_overlap.py +18 -15
  149. dataface/core/render/chart/emitters/_measured_label_padding.py +4 -4
  150. dataface/core/render/chart/emitters/area.py +3 -3
  151. dataface/core/render/chart/emitters/bar.py +60 -35
  152. dataface/core/render/chart/emitters/geo.py +8 -13
  153. dataface/core/render/chart/emitters/heatmap.py +20 -26
  154. dataface/core/render/chart/emitters/line.py +2 -2
  155. dataface/core/render/chart/emitters/scatter.py +7 -30
  156. dataface/core/render/chart/features/baseline.py +22 -9
  157. dataface/core/render/chart/features/endpoint_labels.py +55 -32
  158. dataface/core/render/chart/features/mirror_axis.py +12 -9
  159. dataface/core/render/chart/features/value_labels.py +1 -1
  160. dataface/core/render/chart/kpi.py +11 -90
  161. dataface/core/render/chart/mark_extents.py +339 -0
  162. dataface/core/render/chart/presentation.py +6 -8
  163. dataface/core/render/chart/rendering.py +100 -46
  164. dataface/core/render/chart/session.py +10 -0
  165. dataface/core/render/chart/spec.py +3 -0
  166. dataface/core/render/chart/spec_builders.py +18 -18
  167. dataface/core/render/chart/table.py +81 -36
  168. dataface/core/render/chart/table_support.py +2 -53
  169. dataface/core/render/chart/time_unit_detect.py +3 -2
  170. dataface/core/render/chart/translate.py +82 -44
  171. dataface/core/render/chart/type_inference.py +45 -25
  172. dataface/core/render/chart/validation.py +91 -0
  173. dataface/core/render/chart/vega_lite.py +30 -11
  174. dataface/core/render/chart/vl_field_maps.py +111 -76
  175. dataface/core/render/conditions.py +122 -0
  176. dataface/core/render/controls.py +60 -187
  177. dataface/core/render/converters/chart.py +16 -6
  178. dataface/core/render/data_format.py +1 -1
  179. dataface/core/render/face_resolve.py +7 -0
  180. dataface/core/render/faces.py +97 -32
  181. dataface/core/render/fonts/DFTSerifOldstyleProportional-Regular.woff2 +0 -0
  182. dataface/core/render/fonts/DFTSerifOldstyleTabular-Regular.woff2 +0 -0
  183. dataface/core/render/fonts/SOURCE_CODE_PRO_LICENSE.txt +93 -93
  184. dataface/core/render/fonts/_font_face.css +10 -0
  185. dataface/core/render/layout_sizing.py +313 -238
  186. dataface/core/render/numeral_expr.py +80 -0
  187. dataface/core/render/renderer.py +14 -1
  188. dataface/core/render/sizing.py +42 -105
  189. dataface/core/render/svg_utils.py +60 -14
  190. dataface/core/render/terminal.py +1 -1
  191. dataface/core/render/variables_layout.py +211 -0
  192. dataface/core/render/variables_resolve.py +376 -0
  193. dataface/core/render/variables_strip.py +291 -368
  194. dataface/core/render/warnings/bar_band_width_too_narrow.py +64 -6
  195. dataface/core/render/warnings/base.py +18 -0
  196. dataface/core/render/warnings/layered_chart_shared_y_axis_scale_mismatch.py +25 -3
  197. dataface/core/render/warnings/layout_min_exceeds_height.py +68 -0
  198. dataface/core/render/warnings/likely_currency_or_percent_missing_formatter.py +12 -6
  199. dataface/core/render/warnings/palette_unsupported.py +6 -0
  200. dataface/core/render/warnings/pie_dominant_segment.py +1 -0
  201. dataface/core/render/warnings/pie_too_many_segments.py +1 -0
  202. dataface/core/render/warnings/point_map_out_of_projection.py +1 -0
  203. dataface/core/render/warnings/query_returned_zero_rows.py +1 -0
  204. dataface/core/render/warnings/redundant_encoding.py +20 -1
  205. dataface/core/render/warnings/registry.py +9 -3
  206. dataface/core/render/warnings/temporal_single_point.py +1 -0
  207. dataface/core/render/warnings/too_many_color_categories.py +1 -0
  208. dataface/core/render/warnings/too_many_x_categories.py +18 -2
  209. dataface/core/render/warnings/value_labels_crowd_width.py +9 -0
  210. dataface/core/render/warnings/y_encoding_mostly_null.py +1 -0
  211. dataface/core/render_format.py +18 -0
  212. dataface/core/serve/alias_index.py +16 -22
  213. dataface/core/text/format_d3.py +56 -0
  214. dataface/core/text/numeral_scale.py +218 -0
  215. dataface/core/utils.py +103 -10
  216. dataface/data/schemas/board-resolved/board-resolved.schema.json +8589 -7578
  217. dataface/data/schemas/yaml/0.3.2.json +1 -0
  218. dataface/data/schemas/yaml/manifest.json +7 -0
  219. {dataface-0.3.2.dev22.dist-info → dataface-0.3.2.dev150.dist-info}/METADATA +1 -3
  220. {dataface-0.3.2.dev22.dist-info → dataface-0.3.2.dev150.dist-info}/RECORD +223 -213
  221. dataface/core/inspect/partials/categorical.yml +0 -38
  222. dataface/core/inspect/partials/date.yml +0 -38
  223. dataface/core/inspect/partials/numeric.yml +0 -53
  224. dataface/core/inspect/templates/charts.yml +0 -94
  225. {dataface-0.3.2.dev22.dist-info → dataface-0.3.2.dev150.dist-info}/WHEEL +0 -0
  226. {dataface-0.3.2.dev22.dist-info → dataface-0.3.2.dev150.dist-info}/entry_points.txt +0 -0
  227. {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 for this source type
147
- (e.g. ``".csv"``), including the leading dot.
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(expected_extension):
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"{expected_extension!r} to match source type."
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(LookMLModelsMixin, DatabaseSourceConfig):
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, LookMLModelsMixin):
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 → file path. "
1372
- "Each key is the SQL table name; the value is the path relative to the project root. "
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 → file path. "
1409
- "Each key is the SQL table name; the value is the path relative to the project root. "
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 → file path. "
1444
- "Each key is the SQL table name; the value is the path relative to the project root. "
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
- AxisStylePatch,
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
- ScaleStylePatch,
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
- "AxisStylePatch",
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