dataface 0.1.5.dev206__py3-none-any.whl → 0.1.5.dev215__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 (36) hide show
  1. dataface/DATAFACE_SYNTAX.md +1 -1
  2. dataface/agent_api/docs/yaml-reference.md +4 -4
  3. dataface/agent_api/project.py +40 -44
  4. dataface/ai/tools/__init__.py +1 -1
  5. dataface/cli/commands/serve.py +9 -4
  6. dataface/core/compile/__init__.py +5 -1
  7. dataface/core/compile/compiler.py +25 -49
  8. dataface/core/compile/config.py +32 -3
  9. dataface/core/compile/models/query/authored.py +6 -8
  10. dataface/core/compile/models/query/normalized.py +13 -13
  11. dataface/core/compile/normalize_queries.py +6 -6
  12. dataface/core/dashboard.py +13 -4
  13. dataface/core/errors/__init__.py +6 -0
  14. dataface/core/errors/codes_serve.py +36 -0
  15. dataface/core/errors/hints.py +13 -0
  16. dataface/core/execute/adapters/__init__.py +2 -2
  17. dataface/core/execute/adapters/adapter_registry.py +9 -11
  18. dataface/core/execute/adapters/{schema_resolver_adapter.py → schema_adapter.py} +8 -8
  19. dataface/core/inspect/renderer.py +23 -16
  20. dataface/core/inspect/templates/categorical_column.yml +1 -1
  21. dataface/core/inspect/templates/date_column.yml +1 -1
  22. dataface/core/inspect/templates/model.yml +1 -1
  23. dataface/core/inspect/templates/numeric_column.yml +1 -1
  24. dataface/core/inspect/templates/quality.yml +1 -1
  25. dataface/core/inspect/templates/string_column.yml +1 -1
  26. dataface/core/pack/models.py +1 -1
  27. dataface/core/render/face_api.py +2 -2
  28. dataface/core/render/nav.py +9 -5
  29. dataface/core/render/renderer.py +1 -1
  30. dataface/core/serve/bootstrap.py +22 -8
  31. dataface/core/serve/server.py +6 -2
  32. {dataface-0.1.5.dev206.dist-info → dataface-0.1.5.dev215.dist-info}/METADATA +1 -1
  33. {dataface-0.1.5.dev206.dist-info → dataface-0.1.5.dev215.dist-info}/RECORD +36 -35
  34. {dataface-0.1.5.dev206.dist-info → dataface-0.1.5.dev215.dist-info}/WHEEL +0 -0
  35. {dataface-0.1.5.dev206.dist-info → dataface-0.1.5.dev215.dist-info}/entry_points.txt +0 -0
  36. {dataface-0.1.5.dev206.dist-info → dataface-0.1.5.dev215.dist-info}/licenses/LICENSE +0 -0
@@ -346,7 +346,7 @@ queries:
346
346
  - [Bob, 87.1]
347
347
  ```
348
348
 
349
- Query types (`type:` literals): `sql`, `csv`, `http`, `dbt_model`, `metricflow`, `values`. `schema_resolver` is internal-only and not part of the authored surface.
349
+ Query types (`type:` literals): `sql`, `csv`, `http`, `dbt_model`, `metricflow`, `values`. `schema` is internal-only and not part of the authored surface.
350
350
 
351
351
  Common fields (all query types):
352
352
 
@@ -72,7 +72,7 @@ AuthoredQuery definition from YAML.
72
72
 
73
73
  | Field | Type | Optional | Description |
74
74
  |-------|------|:--------:|-------------|
75
- | `type` | enum: "sql", "metricflow", "dbt_model", "http", "csv", "values", "schema_resolver" | ✓ | Query adapter type. Defaults to 'sql'. Options: sql, metricflow, dbt_model, http, csv, values, schema_resolver. |
75
+ | `type` | enum: "sql", "metricflow", "dbt_model", "http", "csv", "values", "schema" | ✓ | Query adapter type. Defaults to 'sql'. Options: sql, metricflow, dbt_model, http, csv, values, schema. |
76
76
  | `source` | str \| dict[str, Any] | ✓ | Database source reference (name string) or inline source config dict. |
77
77
  | `target` | str | ✓ | dbt target name for dbt_model queries (defaults to 'dev'). |
78
78
  | `sql` | str | ✓ | SQL query string. Supports Jinja2 templates referencing variables. |
@@ -92,9 +92,9 @@ AuthoredQuery definition from YAML.
92
92
  | `filter` | dict[str, Any] | ✓ | Row-level filter applied to CSV or values data. |
93
93
  | `delimiter` | str | ✓ | Column delimiter for CSV queries. Default: comma (','). |
94
94
  | `encoding` | str | ✓ | File encoding for CSV queries. Default: UTF-8. |
95
- | `schema` | str | ✓ | Schema name for schema_resolver queries (YAML key: schema). |
96
- | `table` | str | ✓ | Table name for schema_resolver queries. |
97
- | `column` | str | ✓ | Column name for schema_resolver queries. |
95
+ | `schema` | str | ✓ | Schema name for schema queries (YAML key: schema). |
96
+ | `table` | str | ✓ | Table name for schema queries. |
97
+ | `column` | str | ✓ | Column name for schema queries. |
98
98
  | `rows` | list[dict[str, Any]] | ✓ | Inline data rows for values-type queries (list of row dicts). |
99
99
  | `values` | list[list[Any]] | ✓ | Inline column-oriented data for values-type queries (list of lists). |
100
100
  | `description` | str | ✓ | Human-readable description of the query. Used by AI search and tooling. |
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import sys
6
6
  from dataclasses import dataclass
7
+ from functools import cached_property
7
8
  from pathlib import Path
8
9
  from typing import TYPE_CHECKING, Any
9
10
 
@@ -55,7 +56,6 @@ class Project:
55
56
  project_root: Path
56
57
  cache: DuckDBCache | None
57
58
  _owns_registry: bool
58
- _adapter_registry: AdapterRegistry | None
59
59
  _read_only: bool
60
60
  _dbt_project_path: Path | None
61
61
  _connection_string: str | None
@@ -64,8 +64,8 @@ class Project:
64
64
  _duckdb_config: dict[str, Any] | None
65
65
  _allow_external_access_in_readonly: bool
66
66
  _resolver: SourceResolver | None
67
- _sources_cache: ProjectSourcesConfig | None
68
- _warnings_ignore_cache: frozenset[str] | None
67
+ _sources: ProjectSourcesConfig
68
+ _warnings_ignore: frozenset[str]
69
69
 
70
70
  def __init__(
71
71
  self,
@@ -78,7 +78,10 @@ class Project:
78
78
  # We own the registry only when we'll lazy-build it ourselves. An injected
79
79
  # registry belongs to the caller; we use it but don't close it on their behalf.
80
80
  self._owns_registry = adapter_registry is None
81
- self._adapter_registry = adapter_registry
81
+ if adapter_registry is not None:
82
+ # cached_property stores in __dict__; pre-populating makes the descriptor
83
+ # short-circuit the build on first access.
84
+ self.__dict__["adapter_registry"] = adapter_registry
82
85
  self._read_only = True
83
86
  self._dbt_project_path = None
84
87
  self._connection_string = None
@@ -87,8 +90,8 @@ class Project:
87
90
  self._duckdb_config = None
88
91
  self._allow_external_access_in_readonly = False
89
92
  self._resolver = None
90
- self._sources_cache = None
91
- self._warnings_ignore_cache = None
93
+ self._sources = get_project_sources(self.project_root)
94
+ self._warnings_ignore = get_project_warnings_ignore(self.project_root)
92
95
 
93
96
  @classmethod
94
97
  def open(
@@ -133,7 +136,7 @@ class Project:
133
136
  def __exit__(self, *exc_info: object) -> None:
134
137
  self.close()
135
138
 
136
- @property
139
+ @cached_property
137
140
  def adapter_registry(self) -> AdapterRegistry:
138
141
  """Lazily build the registry on first access; cached thereafter.
139
142
 
@@ -141,57 +144,49 @@ class Project:
141
144
  own it (i.e. it was not injected at construction time).
142
145
  When constructed with an injected registry, returns it directly.
143
146
  """
144
- if self._adapter_registry is None:
145
- self._adapter_registry = build_adapter_registry(
146
- self.project_root,
147
- read_only=self._read_only,
148
- dbt_project_path=self._dbt_project_path,
149
- connection_string=self._connection_string,
150
- profile_type=self._dialect,
151
- target=self._target,
152
- duckdb_config=self._duckdb_config,
153
- allow_external_access_in_readonly=self._allow_external_access_in_readonly,
154
- resolver=self._resolver,
155
- )
156
- return self._adapter_registry
147
+ return build_adapter_registry(
148
+ self.project_root,
149
+ read_only=self._read_only,
150
+ dbt_project_path=self._dbt_project_path,
151
+ connection_string=self._connection_string,
152
+ profile_type=self._dialect,
153
+ target=self._target,
154
+ duckdb_config=self._duckdb_config,
155
+ allow_external_access_in_readonly=self._allow_external_access_in_readonly,
156
+ resolver=self._resolver,
157
+ )
157
158
 
158
159
  def refresh(self) -> None:
159
160
  """Policy-free rebuild primitive.
160
161
 
161
162
  For projects opened via ``open()``: closes the current registry (if built)
162
- and clears it so the next access rebuilds from disk. Also clears the
163
- sources and warnings_ignore caches so the next call re-reads from disk.
163
+ and clears it so the next access rebuilds from disk. Also re-reads
164
+ sources and warnings_ignore from disk.
164
165
 
165
166
  For projects constructed with an injected ``adapter_registry``: skips the
166
- registry rebuild (build arguments are not available), but still clears the
167
- config caches.
167
+ registry rebuild (build arguments are not available), but still re-reads
168
+ the config fields.
168
169
  """
169
- if self._owns_registry:
170
- if self._adapter_registry is not None:
171
- self._adapter_registry.close()
172
- self._adapter_registry = None
173
- self._sources_cache = None
174
- self._warnings_ignore_cache = None
170
+ if self._owns_registry and "adapter_registry" in self.__dict__:
171
+ self.adapter_registry.close()
172
+ del self.__dict__["adapter_registry"]
173
+ self._sources = get_project_sources(self.project_root)
174
+ self._warnings_ignore = get_project_warnings_ignore(self.project_root)
175
175
 
176
176
  def close(self) -> None:
177
177
  """Close the adapter registry iff we own it. The cache is the caller's to close."""
178
- if self._owns_registry and self._adapter_registry is not None:
179
- self._adapter_registry.close()
180
- self._adapter_registry = None
181
-
182
- # ── Config accessors ─────────────────────────────────────────────────────
178
+ if self._owns_registry and "adapter_registry" in self.__dict__:
179
+ self.adapter_registry.close()
180
+ del self.__dict__["adapter_registry"]
183
181
 
182
+ # Read-only views: project lifecycle owns these; external writers go through refresh().
183
+ @property
184
184
  def sources(self) -> ProjectSourcesConfig:
185
- """Project-level sources config. Cached on the instance; refresh() invalidates."""
186
- if self._sources_cache is None:
187
- self._sources_cache = get_project_sources(self.project_root)
188
- return self._sources_cache
185
+ return self._sources
189
186
 
187
+ @property
190
188
  def warnings_ignore(self) -> frozenset[str]:
191
- """Warning codes to suppress. Cached on the instance; refresh() invalidates."""
192
- if self._warnings_ignore_cache is None:
193
- self._warnings_ignore_cache = get_project_warnings_ignore(self.project_root)
194
- return self._warnings_ignore_cache
189
+ return self._warnings_ignore
195
190
 
196
191
  # ── Verb forwarders ──────────────────────────────────────────────────────
197
192
 
@@ -300,6 +295,7 @@ class Project:
300
295
  variables=variables,
301
296
  adapter_registry=self.adapter_registry,
302
297
  project_dir=self.project_root,
298
+ project_sources=self.sources,
303
299
  duckdb_cache=self.cache,
304
300
  format=format,
305
301
  use_cache=use_cache,
@@ -308,5 +304,5 @@ class Project:
308
304
  scale=scale,
309
305
  ignore_codes=ignore_codes,
310
306
  max_workers=max_workers,
311
- warnings_ignore=self.warnings_ignore(),
307
+ warnings_ignore=self.warnings_ignore,
312
308
  )
@@ -113,7 +113,7 @@ def _handle_render(args: dict[str, Any], ctx: DatafaceAIContext) -> dict[str, An
113
113
  adapter_registry=ctx.project.adapter_registry,
114
114
  duckdb_cache=ctx.project.cache,
115
115
  server_port=ctx.server_port,
116
- warnings_ignore=ctx.project.warnings_ignore(),
116
+ warnings_ignore=ctx.project.warnings_ignore,
117
117
  ).model_dump(mode="json", exclude_none=True)
118
118
 
119
119
 
@@ -7,6 +7,10 @@ from pathlib import Path
7
7
  import typer
8
8
  import uvicorn
9
9
 
10
+ from dataface.cli._error_format import print_structured_errors
11
+ from dataface.core.compile.errors import DatafaceError
12
+ from dataface.core.errors import DF_SERVE_STARTUP_FAILED
13
+
10
14
 
11
15
  def serve_command(
12
16
  port: int | None = None,
@@ -52,8 +56,8 @@ def serve_command(
52
56
  # Apply DFT_DEFAULT_THEME before anything else — fails fast on invalid names.
53
57
  try:
54
58
  apply_default_theme_from_env()
55
- except ValueError as e:
56
- typer.echo(f"Error: {e}", err=True)
59
+ except DatafaceError as e:
60
+ print_structured_errors([e.to_structured()])
57
61
  raise typer.Exit(1) from None
58
62
 
59
63
  # Resolve port: --port > DFT_PORT > dataface.yml > hash(project_dir)
@@ -108,6 +112,7 @@ def serve_command(
108
112
  except KeyboardInterrupt:
109
113
  typer.echo("\n👋 Server stopped")
110
114
  sys.exit(0)
111
- except Exception as e: # noqa: BLE001
112
- typer.echo(f"Error starting server: {e}", err=True)
115
+ except Exception as e: # noqa: BLE001 — uvicorn surface
116
+ err = DatafaceError.from_code(DF_SERVE_STARTUP_FAILED, detail=str(e))
117
+ print_structured_errors([err.to_structured()])
113
118
  raise typer.Exit(1) from None
@@ -20,7 +20,11 @@ This module is pure - it does NOT import from execute/ or render/.
20
20
  """
21
21
 
22
22
  from dataface.core.compile.chart_focus import focus_on_chart
23
- from dataface.core.compile.compiler import CompileResult, compile, compile_file
23
+ from dataface.core.compile.compiler import (
24
+ CompileResult,
25
+ compile,
26
+ compile_file,
27
+ )
24
28
  from dataface.core.compile.config import (
25
29
  AccessConfig,
26
30
  MetaConfig,
@@ -38,6 +38,10 @@ import yaml
38
38
  from pydantic import ValidationError as PydanticValidationError
39
39
 
40
40
  from dataface.core.compile.authoring_warnings import detect_authoring_warnings
41
+ from dataface.core.compile.config import (
42
+ ProjectSourcesConfig,
43
+ get_project_sources,
44
+ )
41
45
  from dataface.core.compile.errors import (
42
46
  CompilationError,
43
47
  JinjaError,
@@ -80,35 +84,6 @@ def _to_plain_dict(obj: Any) -> Any:
80
84
  return obj
81
85
 
82
86
 
83
- def resolve_project_source_paths(
84
- sources: dict[str, dict[str, Any]],
85
- project_dir: Path,
86
- ) -> dict[str, dict[str, Any]]:
87
- """Resolve relative file/database paths in project-level source configs."""
88
- resolved_sources: dict[str, dict[str, Any]] = {}
89
-
90
- for name, config in sources.items():
91
- resolved_config = dict(config)
92
- source_type = resolved_config.get("type")
93
-
94
- if source_type == "duckdb":
95
- path_value = resolved_config.get("path")
96
- if (
97
- isinstance(path_value, str)
98
- and path_value != ":memory:"
99
- and not Path(path_value).is_absolute()
100
- ):
101
- resolved_config["path"] = str((project_dir / path_value).resolve())
102
- elif source_type in {"csv", "parquet", "json"}:
103
- file_value = resolved_config.get("file")
104
- if isinstance(file_value, str) and not Path(file_value).is_absolute():
105
- resolved_config["file"] = str((project_dir / file_value).resolve())
106
-
107
- resolved_sources[name] = resolved_config
108
-
109
- return resolved_sources
110
-
111
-
112
87
  @dataclass
113
88
  class CompileResult:
114
89
  """Result of compilation.
@@ -160,7 +135,7 @@ def compile(
160
135
  yaml_content: str,
161
136
  options: dict[str, Any] | None = None,
162
137
  base_dir: Path | None = None,
163
- project_dir: Path | None = None,
138
+ project_sources: ProjectSourcesConfig | None = None,
164
139
  ) -> CompileResult:
165
140
  """Compile YAML content to a Face.
166
141
 
@@ -177,7 +152,9 @@ def compile(
177
152
  yaml_content: YAML string to compile
178
153
  options: Optional compilation options
179
154
  base_dir: Base directory for resolving file references
180
- project_dir: Project directory for resolving project-level sources
155
+ project_sources: Project-level sources, ready to use. Callers that
156
+ hold a ``Project`` thread ``project.sources``; one-shot callers
157
+ pre-load via ``get_project_sources(project_dir)``.
181
158
 
182
159
  Returns:
183
160
  CompileResult with compiled face or errors
@@ -234,13 +211,8 @@ def compile(
234
211
  # ════════════════════════════════════════════════════════════════════
235
212
  # STEP 3: Get Default Source and Extract Named Sources
236
213
  # ════════════════════════════════════════════════════════════════════
237
- # Get project-level sources when available so named source references and
238
- # project defaults behave consistently for file-based compiles.
239
- project_sources = None
240
- if project_dir is not None:
241
- from dataface.core.compile.config import get_project_sources
242
-
243
- project_sources = get_project_sources(project_dir)
214
+ # project_sources is supplied by the caller (Project verb or file-based
215
+ # entry point); its sources dict is already path-resolved.
244
216
 
245
217
  # Get the face's default source to pass to query normalization.
246
218
  default_source = face.get_default_source()
@@ -249,12 +221,9 @@ def compile(
249
221
 
250
222
  # Extract named source configurations from the sources section
251
223
  # These are used to resolve source references in queries (e.g., source: profiles)
252
- sources_registry = {}
224
+ sources_registry: dict[str, dict[str, Any]] = {}
253
225
  if project_sources is not None:
254
- assert project_dir is not None
255
- sources_registry.update(
256
- resolve_project_source_paths(project_sources.sources, project_dir)
257
- )
226
+ sources_registry.update(project_sources.sources)
258
227
  sources_registry.update(_extract_sources(face))
259
228
 
260
229
  # ════════════════════════════════════════════════════════════════════
@@ -446,6 +415,7 @@ def compile_file(
446
415
  options: dict[str, Any] | None = None,
447
416
  apply_meta: bool = True,
448
417
  root_path: Path | None = None,
418
+ project_sources: ProjectSourcesConfig | None = None,
449
419
  ) -> CompileResult:
450
420
  """Compile a YAML file to a Face.
451
421
 
@@ -457,6 +427,9 @@ def compile_file(
457
427
  options: Optional compilation options
458
428
  apply_meta: If True, resolve and apply meta.yaml chain (default: True)
459
429
  root_path: Project root for meta resolution (default: auto-detect)
430
+ project_sources: Pre-loaded project sources. When None, ``compile_file``
431
+ loads once from the derived project root. ``Project.render_dashboard``
432
+ threads its cached ``self.sources`` here to avoid the disk read.
460
433
 
461
434
  Returns:
462
435
  CompileResult with compiled face or errors
@@ -541,12 +514,15 @@ def compile_file(
541
514
  e,
542
515
  )
543
516
 
544
- project_dir = root_path
545
- if project_dir is None:
546
- from dataface.core.project_roots import discover_render_context
517
+ if project_sources is None:
518
+ project_dir = root_path
519
+ if project_dir is None:
520
+ from dataface.core.project_roots import discover_render_context
521
+
522
+ compile_boundary = Path(file_path.anchor) if file_path.anchor else None
523
+ project_dir, _ = discover_render_context(file_path.parent, compile_boundary)
547
524
 
548
- compile_boundary = Path(file_path.anchor) if file_path.anchor else None
549
- project_dir, _ = discover_render_context(file_path.parent, compile_boundary)
525
+ project_sources = get_project_sources(project_dir)
550
526
 
551
527
  # Thread meta lint config through to compile for query diagnostics
552
528
  compile_options = dict(options) if options else {}
@@ -557,7 +533,7 @@ def compile_file(
557
533
  yaml_content,
558
534
  options=compile_options,
559
535
  base_dir=file_path.parent,
560
- project_dir=project_dir,
536
+ project_sources=project_sources,
561
537
  )
562
538
  # Stamp file path and add validate next_command on each compile error so
563
539
  # UI surfaces and CLI can surface "dft validate <file>" without re-deriving the path.
@@ -484,10 +484,12 @@ def get_palette(name: str = "default") -> list[str]:
484
484
 
485
485
 
486
486
  def get_project_sources(project_dir: Path) -> ProjectSourcesConfig:
487
- """Get project-level sources configuration.
487
+ """Get project-level sources configuration with file paths absolutized.
488
488
 
489
489
  Reads disk every call — no cache. _sources.yaml / dataface.yml edits are
490
- visible to the next call.
490
+ visible to the next call. Relative ``path:`` (duckdb) and ``file:`` (csv /
491
+ parquet / json) values resolve against ``project_dir`` so callers can pass
492
+ the returned config straight into ``compile()`` without further work.
491
493
  """
492
494
  project_dir = project_dir.resolve()
493
495
  config = get_config()
@@ -542,10 +544,37 @@ def get_project_sources(project_dir: Path) -> ProjectSourcesConfig:
542
544
 
543
545
  return ProjectSourcesConfig(
544
546
  default=default_source,
545
- sources=_normalize_project_sources(all_sources),
547
+ sources=_absolutize_source_paths(
548
+ _normalize_project_sources(all_sources), project_dir
549
+ ),
546
550
  )
547
551
 
548
552
 
553
+ def _absolutize_source_paths(
554
+ sources: dict[str, dict[str, Any]],
555
+ project_dir: Path,
556
+ ) -> dict[str, dict[str, Any]]:
557
+ """Absolutize relative file/database paths against ``project_dir``."""
558
+ resolved: dict[str, dict[str, Any]] = {}
559
+ for name, config in sources.items():
560
+ cfg = dict(config)
561
+ source_type = cfg.get("type")
562
+ if source_type == "duckdb":
563
+ path_value = cfg.get("path")
564
+ if (
565
+ isinstance(path_value, str)
566
+ and path_value != ":memory:"
567
+ and not Path(path_value).is_absolute()
568
+ ):
569
+ cfg["path"] = str((project_dir / path_value).resolve())
570
+ elif source_type in {"csv", "parquet", "json"}:
571
+ file_value = cfg.get("file")
572
+ if isinstance(file_value, str) and not Path(file_value).is_absolute():
573
+ cfg["file"] = str((project_dir / file_value).resolve())
574
+ resolved[name] = cfg
575
+ return resolved
576
+
577
+
549
578
  # ============================================================================
550
579
  # PROJECT WARNINGS IGNORE
551
580
  # ============================================================================
@@ -97,13 +97,11 @@ class AuthoredQuery(BaseModel):
97
97
  model_config = ConfigDict(extra="forbid", populate_by_name=True)
98
98
 
99
99
  type: (
100
- Literal[
101
- "sql", "metricflow", "dbt_model", "http", "csv", "values", "schema_resolver"
102
- ]
100
+ Literal["sql", "metricflow", "dbt_model", "http", "csv", "values", "schema"]
103
101
  | None
104
102
  ) = Field(
105
103
  default=None,
106
- description="Query adapter type. Defaults to 'sql'. Options: sql, metricflow, dbt_model, http, csv, values, schema_resolver.",
104
+ description="Query adapter type. Defaults to 'sql'. Options: sql, metricflow, dbt_model, http, csv, values, schema.",
107
105
  )
108
106
 
109
107
  # Source field - can be a string reference or inline source config
@@ -194,21 +192,21 @@ class AuthoredQuery(BaseModel):
194
192
  description="File encoding for CSV queries. Default: UTF-8.",
195
193
  )
196
194
 
197
- # schema_resolver fields
195
+ # schema fields
198
196
  # alias="schema" so YAML authors write `schema: analytics`; Python code uses
199
197
  # schema_name to avoid shadowing BaseModel.schema (a Pydantic v2 legacy classmethod).
200
198
  schema_name: str | None = Field(
201
199
  default=None,
202
200
  alias="schema",
203
- description="Schema name for schema_resolver queries (YAML key: schema).",
201
+ description="Schema name for schema queries (YAML key: schema).",
204
202
  )
205
203
  table: str | None = Field(
206
204
  default=None,
207
- description="Table name for schema_resolver queries.",
205
+ description="Table name for schema queries.",
208
206
  )
209
207
  column: str | None = Field(
210
208
  default=None,
211
- description="Column name for schema_resolver queries.",
209
+ description="Column name for schema queries.",
212
210
  )
213
211
 
214
212
  # Values fields (inline data)
@@ -494,7 +494,7 @@ class DbtModelQuery(Query):
494
494
  return f"dbt Model: {self.model}"
495
495
 
496
496
 
497
- class SchemaResolverQuery(Query):
497
+ class SchemaQuery(Query):
498
498
  """Query the LayeredSchemaResolver in-process.
499
499
 
500
500
  Dispatches to list_schemas / list_tables / profile_table / profile_column
@@ -537,24 +537,24 @@ class SchemaResolverQuery(Query):
537
537
  def _no_jinja(cls, v: Any) -> Any:
538
538
  if isinstance(v, str) and any(tok in v for tok in ("{{", "{%", "{#")):
539
539
  raise ValueError(
540
- "schema_resolver fields must be literal strings; "
540
+ "schema fields must be literal strings; "
541
541
  "Jinja templates ({{ }}, {% %}, {# #}) are not supported."
542
542
  )
543
543
  return v
544
544
 
545
545
  @model_validator(mode="after")
546
- def _field_prerequisites(self) -> "SchemaResolverQuery":
546
+ def _field_prerequisites(self) -> "SchemaQuery":
547
547
  if self.table is not None and self.schema_name is None:
548
- raise ValueError("schema_resolver: 'table' requires 'schema' to be set")
548
+ raise ValueError("schema: 'table' requires 'schema' to be set")
549
549
  if self.column is not None and self.table is None:
550
550
  raise ValueError(
551
- "schema_resolver: 'column' requires 'table' (and 'schema') to be set"
551
+ "schema: 'column' requires 'table' (and 'schema') to be set"
552
552
  )
553
553
  return self
554
554
 
555
555
  @property
556
556
  def query_type(self) -> str:
557
- return "schema_resolver"
557
+ return "schema"
558
558
 
559
559
  @property
560
560
  def source_description(self) -> str:
@@ -565,7 +565,7 @@ class SchemaResolverQuery(Query):
565
565
  parts.append(self.table)
566
566
  if self.column:
567
567
  parts.append(self.column)
568
- return f"schema_resolver: {'.'.join(parts)}"
568
+ return f"schema: {'.'.join(parts)}"
569
569
 
570
570
 
571
571
  # ============================================================================
@@ -582,7 +582,7 @@ AnyQuery = (
582
582
  | HttpQuery
583
583
  | DbtModelQuery
584
584
  | ValuesQuery
585
- | SchemaResolverQuery
585
+ | SchemaQuery
586
586
  )
587
587
 
588
588
 
@@ -594,7 +594,7 @@ VALID_QUERY_TYPES: set[str] = {
594
594
  "http",
595
595
  "dbt_model",
596
596
  "values",
597
- "schema_resolver",
597
+ "schema",
598
598
  }
599
599
 
600
600
 
@@ -715,13 +715,13 @@ def is_values_query(query: AnyQuery) -> TypeGuard[ValuesQuery]:
715
715
  return query.query_type == "values"
716
716
 
717
717
 
718
- def is_schema_resolver_query(query: AnyQuery) -> TypeGuard[SchemaResolverQuery]:
719
- """Type guard for schema resolver queries.
718
+ def is_schema_query(query: AnyQuery) -> TypeGuard[SchemaQuery]:
719
+ """Type guard for schema queries.
720
720
 
721
721
  Args:
722
722
  query: Any compiled query
723
723
 
724
724
  Returns:
725
- True if query is a SchemaResolverQuery
725
+ True if query is a SchemaQuery
726
726
  """
727
- return query.query_type == "schema_resolver"
727
+ return query.query_type == "schema"
@@ -17,7 +17,7 @@ from dataface.core.compile.models.query.normalized import (
17
17
  DbtModelQuery,
18
18
  HttpQuery,
19
19
  MetricFlowQuery,
20
- SchemaResolverQuery,
20
+ SchemaQuery,
21
21
  SqlQuery,
22
22
  ValuesQuery,
23
23
  is_sql_query,
@@ -62,7 +62,7 @@ def normalize_query(
62
62
  HttpQuery,
63
63
  DbtModelQuery,
64
64
  ValuesQuery,
65
- SchemaResolverQuery,
65
+ SchemaQuery,
66
66
  ),
67
67
  ):
68
68
  return query_def
@@ -90,13 +90,13 @@ def normalize_query(
90
90
  query_type = query_dict.pop("type")
91
91
 
92
92
  # Apply default source if query doesn't have one.
93
- # ValuesQuery is inline data; SchemaResolverQuery's source is a dbt source name,
93
+ # ValuesQuery is inline data; SchemaQuery's source is a dbt source name,
94
94
  # not a connection reference; HttpQuery uses url, not source — all three are excluded.
95
95
  has_source = query_dict.get("source") is not None
96
96
  if (
97
97
  not has_source
98
98
  and default_source
99
- and query_type not in {"values", "dbt_model", "schema_resolver", "http"}
99
+ and query_type not in {"values", "dbt_model", "schema", "http"}
100
100
  ):
101
101
  query_dict["source"] = default_source
102
102
 
@@ -140,8 +140,8 @@ def normalize_query(
140
140
  query = DbtModelQuery(**query_dict)
141
141
  elif query_type == "values":
142
142
  query = ValuesQuery(**query_dict)
143
- elif query_type == "schema_resolver":
144
- query = SchemaResolverQuery(**query_dict)
143
+ elif query_type == "schema":
144
+ query = SchemaQuery(**query_dict)
145
145
  else:
146
146
  # Default to SQL
147
147
  query = SqlQuery(**query_dict)