dataface 0.3.1.dev132__py3-none-any.whl → 0.3.2.dev111__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.
- d3_format/spec.py +45 -0
- dataface/__init__.py +70 -23
- dataface/agent_api/__init__.py +165 -67
- dataface/agent_api/_paths.py +9 -3
- dataface/agent_api/board_artifact.py +11 -1
- dataface/agent_api/docs/yaml-reference.md +246 -106
- dataface/agent_api/init.py +1 -1
- dataface/agent_api/project_session.py +2 -1
- dataface/agent_api/serve.py +115 -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 +19 -11
- dataface/cli/commands/search.py +8 -2
- dataface/cli/commands/serve.py +32 -76
- 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 +6 -23
- dataface/core/__init__.py +73 -28
- dataface/core/attribution.py +191 -0
- dataface/core/{compile/colors.py → colors.py} +20 -5
- dataface/core/compile/channel.py +232 -119
- dataface/core/compile/compiler.py +97 -34
- 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/jinja.py +2 -1
- dataface/core/compile/label_data.py +154 -0
- dataface/core/compile/link_keys.py +127 -0
- dataface/core/compile/migrations.py +133 -14
- dataface/core/compile/models/chart/normalized/_base.py +2 -2
- dataface/core/compile/models/chart/normalized/callout.py +2 -2
- dataface/core/compile/models/chart/normalized/kpi.py +0 -4
- dataface/core/compile/models/chart/resolved/_base.py +30 -12
- dataface/core/compile/models/chart/resolved/callout.py +3 -4
- dataface/core/compile/models/chart/resolved/kpi.py +7 -3
- dataface/core/compile/models/chart/resolved/pie.py +55 -1
- dataface/core/compile/models/chart/resolved/table.py +16 -2
- dataface/core/compile/models/config.py +4 -0
- dataface/core/compile/models/markers.py +1 -1
- dataface/core/compile/models/primitives.py +175 -2
- dataface/core/compile/models/query/normalized.py +2 -2
- dataface/core/compile/models/source.py +96 -13
- 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 +13 -3
- dataface/core/compile/models/style/resolved/_base.py +143 -58
- dataface/core/compile/models/style/resolved/_cartesian.py +6 -11
- dataface/core/compile/models/style/resolved/_cascade.py +148 -53
- 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/resolved/table.py +8 -3
- 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/marks.py +1 -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/variable/authored.py +2 -2
- dataface/core/compile/models/vega_lite/config.py +1 -1
- dataface/core/compile/normalize_charts.py +7 -3
- dataface/core/compile/normalize_queries.py +22 -8
- dataface/core/compile/normalize_variables.py +2 -2
- dataface/core/compile/palette.py +1 -1
- dataface/core/compile/resolve/__init__.py +49 -7
- dataface/core/compile/resolve/_axes.py +416 -0
- dataface/core/compile/resolve/_channels.py +120 -0
- dataface/core/compile/resolve/_chart_rows.py +29 -0
- dataface/core/compile/resolve/_dispatch.py +316 -0
- dataface/core/compile/resolve/_domain.py +482 -0
- dataface/core/compile/resolve/_kwargs.py +265 -0
- dataface/core/compile/resolve/_layers.py +241 -0
- dataface/core/compile/resolve/_links.py +66 -0
- dataface/core/compile/resolve/_marks.py +146 -0
- dataface/core/compile/resolve/_palette.py +191 -0
- dataface/core/compile/resolve/_table.py +222 -0
- dataface/core/compile/resolve/area.py +532 -0
- dataface/core/compile/resolve/bar.py +593 -0
- dataface/core/compile/resolve/geo.py +423 -0
- dataface/core/compile/resolve/heatmap.py +121 -0
- dataface/core/compile/resolve/line.py +337 -0
- dataface/core/compile/resolve/pie.py +290 -0
- dataface/core/compile/resolve/pie_attachment.py +170 -0
- dataface/core/compile/resolve/scatter.py +234 -0
- dataface/core/compile/resolve/simple.py +181 -0
- dataface/core/compile/schema_renderers/prompt.py +1 -1
- dataface/core/compile/schema_renderers/textmate_grammar.py +8 -1
- dataface/core/compile/single_series_allocation.py +1 -1
- dataface/core/compile/source_map.py +211 -28
- dataface/core/compile/style_cascade.py +127 -56
- dataface/core/compile/variables.py +82 -2
- dataface/core/compile/yaml_error_formatter.py +1 -1
- dataface/core/connections.py +10 -3
- dataface/core/dashboard.py +24 -18
- dataface/core/defaults/default_config.yml +6 -0
- dataface/core/defaults/themes/_base.yaml +49 -39
- 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 +6 -6
- dataface/core/defaults/themes/neon.yaml +15 -8
- dataface/core/defaults/themes/plain.yaml +6 -4
- dataface/core/defaults/themes/stark.yaml +16 -7
- dataface/core/defaults/themes/vivid.yaml +15 -8
- dataface/core/diagnostics/__init__.py +7 -3
- dataface/core/diagnostics/codes_compile.py +13 -31
- dataface/core/diagnostics/codes_execute.py +102 -0
- dataface/core/diagnostics/codes_render.py +123 -51
- 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/adapter_registry.py +27 -5
- dataface/core/execute/adapters/base.py +9 -5
- dataface/core/execute/adapters/dbt_adapter.py +93 -5
- dataface/core/execute/adapters/dbt_adapter_factory.py +55 -6
- dataface/core/execute/adapters/dbt_utils.py +84 -20
- dataface/core/execute/adapters/duckdb_adapter.py +5 -3
- dataface/core/execute/adapters/http_adapter.py +2 -2
- dataface/core/execute/adapters/native_attribution.py +66 -0
- dataface/core/execute/adapters/postgres_connection_manager.py +48 -0
- dataface/core/execute/adapters/query_header.py +96 -0
- dataface/core/execute/adapters/schema_adapter.py +2 -2
- dataface/core/execute/adapters/sql_adapter.py +108 -13
- dataface/core/execute/adapters/sqlite_adapter.py +5 -3
- dataface/core/execute/adapters/values_adapter.py +2 -2
- dataface/core/execute/cache_keys.py +10 -4
- dataface/core/execute/chart_data_provider.py +1 -1
- dataface/core/execute/chart_resolution.py +278 -0
- dataface/core/execute/dbt_jinja.py +65 -10
- dataface/core/execute/executor.py +69 -16
- dataface/core/execute/recording.py +6 -4
- dataface/core/execute/source_resolver.py +23 -12
- dataface/core/font_measure.py +136 -0
- dataface/core/fonts.py +221 -49
- dataface/core/inspect/__init__.py +46 -6
- dataface/core/project.py +13 -1
- dataface/core/registered_views/variable_planner.py +7 -74
- dataface/core/render/board_replay.py +15 -0
- dataface/core/render/chart/arc_attached_table.py +19 -368
- dataface/core/render/chart/auto_link.py +3 -3
- dataface/core/render/chart/callout.py +1 -1
- dataface/core/render/chart/data_table_attachment.py +15 -13
- dataface/core/render/chart/emitters/_cartesian.py +21 -18
- dataface/core/render/chart/emitters/_channels.py +104 -16
- dataface/core/render/chart/emitters/_label_overlap.py +19 -16
- dataface/core/render/chart/emitters/_measured_label_padding.py +5 -5
- dataface/core/render/chart/emitters/area.py +26 -13
- dataface/core/render/chart/emitters/bar.py +71 -36
- 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 +28 -12
- dataface/core/render/chart/emitters/pie.py +61 -52
- 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 +56 -33
- dataface/core/render/chart/features/mirror_axis.py +12 -9
- dataface/core/render/chart/features/value_labels.py +2 -2
- dataface/core/render/chart/kpi.py +13 -92
- dataface/core/render/chart/presentation.py +6 -8
- dataface/core/render/chart/rendering.py +3 -12
- 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 +122 -117
- dataface/core/render/chart/table_support.py +5 -71
- dataface/core/render/chart/time_unit_detect.py +69 -3
- dataface/core/render/chart/title_overflow.py +1 -1
- dataface/core/render/chart/translate.py +82 -44
- dataface/core/render/chart/type_inference.py +80 -32
- dataface/core/render/chart/validation.py +91 -0
- dataface/core/render/chart/vega_lite.py +77 -256
- dataface/core/render/chart/vl_field_maps.py +111 -76
- dataface/core/render/controls.py +5 -5
- dataface/core/render/converters/chart.py +16 -6
- dataface/core/render/face_resolve.py +64 -17
- dataface/core/render/face_to_dict.py +1 -1
- dataface/core/render/faces.py +90 -45
- dataface/core/render/font_support.py +64 -103
- 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/SourceSerif4Variable.ttf +0 -0
- dataface/core/render/fonts/{_emoji_font_face.css → _font_face.css} +11 -0
- dataface/core/render/layout_sizing.py +384 -333
- dataface/core/render/numeral_expr.py +80 -0
- dataface/core/render/renderer.py +22 -2
- dataface/core/render/sizing.py +26 -57
- dataface/core/render/svg_utils.py +2 -3
- dataface/core/render/template_loader.py +7 -9
- dataface/core/render/templates/page.css +2 -2
- dataface/core/render/templates/svg/styles.css +1 -1
- dataface/core/render/terminal.py +5 -14
- dataface/core/render/terminal_charts.py +45 -46
- dataface/core/render/variables_strip.py +7 -6
- dataface/core/render/warnings/bar_band_width_too_narrow.py +64 -6
- dataface/core/render/warnings/layered_chart_shared_y_axis_scale_mismatch.py +25 -3
- 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 +7 -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 +10 -1
- dataface/core/render/warnings/y_encoding_mostly_null.py +1 -0
- dataface/core/render_format.py +18 -0
- dataface/core/serve/server.py +2 -2
- 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 +9405 -7863
- dataface/data/schemas/yaml/0.3.1.json +1 -0
- dataface/data/schemas/yaml/0.3.2.json +1 -0
- dataface/data/schemas/yaml/manifest.json +14 -0
- {dataface-0.3.1.dev132.dist-info → dataface-0.3.2.dev111.dist-info}/METADATA +1 -1
- {dataface-0.3.1.dev132.dist-info → dataface-0.3.2.dev111.dist-info}/RECORD +249 -219
- mdsvg/__init__.py +5 -3
- mdsvg/fonts.py +130 -35
- mdsvg/renderer.py +258 -37
- mdsvg/style.py +18 -3
- dataface/core/compile/resolve/_resolve.py +0 -3320
- dataface/core/registered_views/link_keys.py +0 -45
- dataface/core/render/chart/labels.py +0 -76
- dataface/core/render/font_measurement.py +0 -35
- dataface/core/render/fonts/SourceSerif4-Regular.ttf +0 -0
- dataface/core/render/shared_y_datasets.py +0 -33
- dataface/core/render/stable_domain.py +0 -365
- {dataface-0.3.1.dev132.dist-info → dataface-0.3.2.dev111.dist-info}/WHEEL +0 -0
- {dataface-0.3.1.dev132.dist-info → dataface-0.3.2.dev111.dist-info}/entry_points.txt +0 -0
- {dataface-0.3.1.dev132.dist-info → dataface-0.3.2.dev111.dist-info}/licenses/LICENSE +0 -0
d3_format/spec.py
CHANGED
|
@@ -49,6 +49,51 @@ class FormatSpec:
|
|
|
49
49
|
trim: bool = False # ~ flag
|
|
50
50
|
type: str = "" # "" means default (g-ish) behavior
|
|
51
51
|
|
|
52
|
+
def __str__(self) -> str:
|
|
53
|
+
"""Serialize back to a d3-format spec string, in grammar order.
|
|
54
|
+
|
|
55
|
+
Omits every default-valued token, so a parsed spec serializes back to
|
|
56
|
+
something an author would write rather than a fully-explicit
|
|
57
|
+
expansion. ``zero`` implies ``fill == "0"`` and ``align == "="`` (the
|
|
58
|
+
collapse in ``parse``), so the zero-pad flag is emitted alone rather
|
|
59
|
+
than alongside a redundant ``0=`` fill and align.
|
|
60
|
+
|
|
61
|
+
Raises:
|
|
62
|
+
ValueError: If ``zero`` is set but ``fill``/``align`` have been
|
|
63
|
+
mutated away from ``"0"``/``"="``. The d3 grammar cannot
|
|
64
|
+
express that combination -- ``parse`` collapses ``zero`` back
|
|
65
|
+
to ``fill="0"``, ``align="="`` unconditionally -- so silently
|
|
66
|
+
keeping the flag and dropping the fill would misrepresent the
|
|
67
|
+
spec. Clear ``zero`` first if an explicit fill/align is wanted.
|
|
68
|
+
"""
|
|
69
|
+
if self.zero and (self.fill != "0" or self.align != "="):
|
|
70
|
+
raise ValueError(
|
|
71
|
+
"FormatSpec has zero=True with fill="
|
|
72
|
+
f"{self.fill!r}, align={self.align!r}; the d3 grammar can "
|
|
73
|
+
"only express zero-pad as fill='0', align='='. Clear zero "
|
|
74
|
+
"if you want this fill/align combination."
|
|
75
|
+
)
|
|
76
|
+
tokens: list[str] = []
|
|
77
|
+
if not self.zero and (self.fill != " " or self.align != ">"):
|
|
78
|
+
tokens.append(self.fill)
|
|
79
|
+
tokens.append(self.align)
|
|
80
|
+
if self.sign != "-":
|
|
81
|
+
tokens.append(self.sign)
|
|
82
|
+
if self.symbol:
|
|
83
|
+
tokens.append(self.symbol)
|
|
84
|
+
if self.zero:
|
|
85
|
+
tokens.append("0")
|
|
86
|
+
if self.width is not None:
|
|
87
|
+
tokens.append(str(self.width))
|
|
88
|
+
if self.comma:
|
|
89
|
+
tokens.append(",")
|
|
90
|
+
if self.precision is not None:
|
|
91
|
+
tokens.append(f".{self.precision}")
|
|
92
|
+
if self.trim:
|
|
93
|
+
tokens.append("~")
|
|
94
|
+
tokens.append(self.type)
|
|
95
|
+
return "".join(tokens)
|
|
96
|
+
|
|
52
97
|
|
|
53
98
|
def parse(spec: str) -> FormatSpec:
|
|
54
99
|
"""Parse a d3-format spec string into a FormatSpec.
|
dataface/__init__.py
CHANGED
|
@@ -28,8 +28,12 @@ Quick Start:
|
|
|
28
28
|
... svg = render(face, executor, format="svg")
|
|
29
29
|
"""
|
|
30
30
|
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
|
|
33
|
+
import importlib
|
|
31
34
|
import logging
|
|
32
35
|
from importlib.metadata import version as _v
|
|
36
|
+
from typing import TYPE_CHECKING, Any
|
|
33
37
|
|
|
34
38
|
__version__ = _v("dataface")
|
|
35
39
|
|
|
@@ -41,29 +45,31 @@ logger = logging.getLogger("dataface")
|
|
|
41
45
|
# Actual handlers should be configured by the application entry points (CLI, serve)
|
|
42
46
|
logger.addHandler(logging.NullHandler())
|
|
43
47
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
48
|
+
if TYPE_CHECKING:
|
|
49
|
+
# Import-time-only: PEP 562 __getattr__ below resolves these lazily at
|
|
50
|
+
# runtime, so `import dataface` (or `import dataface.<anything>`, which
|
|
51
|
+
# always runs this file first) doesn't eagerly pull in the whole
|
|
52
|
+
# compile/execute/render stack just for this convenience re-export.
|
|
53
|
+
from dataface.core import validate as validate
|
|
54
|
+
from dataface.core.compile import (
|
|
55
|
+
AnyQuery as AnyQuery,
|
|
56
|
+
AuthoredFace as AuthoredFace,
|
|
57
|
+
CompilationError as CompilationError,
|
|
58
|
+
CompileResult as CompileResult,
|
|
59
|
+
Face as Face,
|
|
60
|
+
Layout as Layout,
|
|
61
|
+
LayoutItem as LayoutItem,
|
|
62
|
+
Variable as Variable,
|
|
63
|
+
compile as compile,
|
|
64
|
+
compile_file as compile_file,
|
|
65
|
+
)
|
|
66
|
+
from dataface.core.execute import (
|
|
67
|
+
ExecutionError as ExecutionError,
|
|
68
|
+
Executor as Executor,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# `render` shadows `dataface.render` module access — see _LAZY_ATTRS note below.
|
|
72
|
+
from dataface.core.render import RenderError as RenderError, render as render
|
|
67
73
|
|
|
68
74
|
__all__ = [
|
|
69
75
|
# Version
|
|
@@ -90,3 +96,44 @@ __all__ = [
|
|
|
90
96
|
# Validate
|
|
91
97
|
"validate",
|
|
92
98
|
]
|
|
99
|
+
|
|
100
|
+
# name -> (module dotted path, attribute name in that module). PEP 562
|
|
101
|
+
# __getattr__ below resolves these on first access instead of importing the
|
|
102
|
+
# entire compile/execute/render stack just because something did
|
|
103
|
+
# `import dataface` or `import dataface.<anything>` (this file runs first,
|
|
104
|
+
# always, regardless of which submodule was actually requested).
|
|
105
|
+
_LAZY_ATTRS: dict[str, tuple[str, str]] = {
|
|
106
|
+
"validate": ("dataface.core", "validate"),
|
|
107
|
+
"AnyQuery": ("dataface.core.compile", "AnyQuery"),
|
|
108
|
+
"AuthoredFace": ("dataface.core.compile", "AuthoredFace"),
|
|
109
|
+
"CompilationError": ("dataface.core.compile", "CompilationError"),
|
|
110
|
+
"CompileResult": ("dataface.core.compile", "CompileResult"),
|
|
111
|
+
"Face": ("dataface.core.compile", "Face"),
|
|
112
|
+
"Layout": ("dataface.core.compile", "Layout"),
|
|
113
|
+
"LayoutItem": ("dataface.core.compile", "LayoutItem"),
|
|
114
|
+
"Variable": ("dataface.core.compile", "Variable"),
|
|
115
|
+
"compile": ("dataface.core.compile", "compile"),
|
|
116
|
+
"compile_file": ("dataface.core.compile", "compile_file"),
|
|
117
|
+
"ExecutionError": ("dataface.core.execute", "ExecutionError"),
|
|
118
|
+
"Executor": ("dataface.core.execute", "Executor"),
|
|
119
|
+
# `render` shadows `dataface.render` module access (pre-existing, not
|
|
120
|
+
# introduced by laziness) — tests patching render internals import
|
|
121
|
+
# dataface.core.render.renderer directly instead.
|
|
122
|
+
"RenderError": ("dataface.core.render", "RenderError"),
|
|
123
|
+
"render": ("dataface.core.render", "render"),
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def __getattr__(name: str) -> Any:
|
|
128
|
+
try:
|
|
129
|
+
module_path, attr_name = _LAZY_ATTRS[name]
|
|
130
|
+
except KeyError:
|
|
131
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}") from None
|
|
132
|
+
module = importlib.import_module(module_path)
|
|
133
|
+
value = getattr(module, attr_name)
|
|
134
|
+
globals()[name] = value # cache: repeated access is O(1) and `is` identity holds
|
|
135
|
+
return value
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def __dir__() -> list[str]:
|
|
139
|
+
return sorted(set(globals()) | set(_LAZY_ATTRS))
|
dataface/agent_api/__init__.py
CHANGED
|
@@ -36,70 +36,83 @@ rendering) belongs here, not in the CLI or MCP layer. Code review blocks
|
|
|
36
36
|
CLI/MCP files that contain logic beyond parse-and-dispatch.
|
|
37
37
|
"""
|
|
38
38
|
|
|
39
|
-
from
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
from dataface.agent_api
|
|
49
|
-
from dataface.agent_api.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
)
|
|
72
|
-
from dataface.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
from dataface.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
from dataface.core.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
from dataface.core.
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
39
|
+
from __future__ import annotations
|
|
40
|
+
|
|
41
|
+
import importlib
|
|
42
|
+
from typing import TYPE_CHECKING, Any
|
|
43
|
+
|
|
44
|
+
if TYPE_CHECKING:
|
|
45
|
+
# Import-time-only: PEP 562 __getattr__ below resolves these lazily at
|
|
46
|
+
# runtime. Kept here so mypy/pyright still see concrete types instead of
|
|
47
|
+
# falling back to Any on every re-export.
|
|
48
|
+
from dataface.agent_api import skills as skills
|
|
49
|
+
from dataface.agent_api._paths import (
|
|
50
|
+
project_sources_for_face as project_sources_for_face,
|
|
51
|
+
)
|
|
52
|
+
from dataface.agent_api.describe import (
|
|
53
|
+
DescribeFaceArgs as DescribeFaceArgs,
|
|
54
|
+
DescribeFaceResult as DescribeFaceResult,
|
|
55
|
+
describe_face as describe_face,
|
|
56
|
+
)
|
|
57
|
+
from dataface.agent_api.docs import (
|
|
58
|
+
DocsArgs as DocsArgs,
|
|
59
|
+
DocsResult as DocsResult,
|
|
60
|
+
DocsSearchHit as DocsSearchHit,
|
|
61
|
+
Topic as Topic,
|
|
62
|
+
)
|
|
63
|
+
from dataface.agent_api.init import (
|
|
64
|
+
InitResult as InitResult,
|
|
65
|
+
init_project as init_project,
|
|
66
|
+
)
|
|
67
|
+
from dataface.agent_api.migrate import (
|
|
68
|
+
MigrateError as MigrateError,
|
|
69
|
+
MigrateSummary as MigrateSummary,
|
|
70
|
+
migrate_paths as migrate_paths,
|
|
71
|
+
)
|
|
72
|
+
from dataface.agent_api.project_session import ProjectSession as ProjectSession
|
|
73
|
+
from dataface.agent_api.query import (
|
|
74
|
+
QueryFaceResult as QueryFaceResult,
|
|
75
|
+
query_face as query_face,
|
|
76
|
+
)
|
|
77
|
+
from dataface.agent_api.schema_hints import SchemaHints as SchemaHints
|
|
78
|
+
from dataface.agent_api.validate import (
|
|
79
|
+
ValidateDashboardArgs as ValidateDashboardArgs,
|
|
80
|
+
ValidateResult as ValidateResult,
|
|
81
|
+
)
|
|
82
|
+
from dataface.agent_api.validate_query import QueryDiagnostic as QueryDiagnostic
|
|
83
|
+
from dataface.core.attribution import set_surface as set_surface
|
|
84
|
+
from dataface.core.compile.compiler import (
|
|
85
|
+
CompileResult as CompileResult,
|
|
86
|
+
compile as compile,
|
|
87
|
+
)
|
|
88
|
+
from dataface.core.compile.config import (
|
|
89
|
+
ProjectSourcesConfig as ProjectSourcesConfig,
|
|
90
|
+
)
|
|
91
|
+
from dataface.core.dashboard import (
|
|
92
|
+
RenderedDashboard as RenderedDashboard,
|
|
93
|
+
RenderFormat as RenderFormat,
|
|
94
|
+
render_dashboard as render_dashboard,
|
|
95
|
+
)
|
|
96
|
+
from dataface.core.diagnostics import Diagnostic as Diagnostic
|
|
97
|
+
from dataface.core.execute.cache_backend import (
|
|
98
|
+
QueryResultCache as QueryResultCache,
|
|
99
|
+
)
|
|
100
|
+
from dataface.core.execute.observability import (
|
|
101
|
+
WarehouseObserver as WarehouseObserver,
|
|
102
|
+
)
|
|
103
|
+
from dataface.core.execute.source_resolver import (
|
|
104
|
+
AllowlistedSourceResolver as AllowlistedSourceResolver,
|
|
105
|
+
DefaultSourceResolver as DefaultSourceResolver,
|
|
106
|
+
SourceResolver as SourceResolver,
|
|
107
|
+
)
|
|
108
|
+
from dataface.core.fonts import get_fonts_dir as get_fonts_dir
|
|
109
|
+
from dataface.core.project import (
|
|
110
|
+
FaceFile as FaceFile,
|
|
111
|
+
InMemoryFace as InMemoryFace,
|
|
112
|
+
Project as Project,
|
|
113
|
+
)
|
|
114
|
+
from dataface.core.render.board_links import LinkContext as LinkContext
|
|
115
|
+
from dataface.core.render.errors import RenderError as RenderError
|
|
103
116
|
|
|
104
117
|
__all__ = [
|
|
105
118
|
"compile",
|
|
@@ -118,11 +131,13 @@ __all__ = [
|
|
|
118
131
|
"MigrateSummary",
|
|
119
132
|
"Project",
|
|
120
133
|
"ProjectSession",
|
|
134
|
+
"set_surface",
|
|
121
135
|
"ProjectSourcesConfig",
|
|
122
136
|
"QueryDiagnostic",
|
|
123
137
|
"QueryFaceResult",
|
|
124
138
|
"RenderedDashboard",
|
|
125
139
|
"RenderError",
|
|
140
|
+
"RenderFormat",
|
|
126
141
|
"SchemaHints",
|
|
127
142
|
"Topic",
|
|
128
143
|
"ValidateDashboardArgs",
|
|
@@ -134,11 +149,94 @@ __all__ = [
|
|
|
134
149
|
"SourceResolver",
|
|
135
150
|
"WarehouseObserver",
|
|
136
151
|
"describe_face",
|
|
137
|
-
"
|
|
152
|
+
"get_fonts_dir",
|
|
138
153
|
"init_project",
|
|
139
154
|
"migrate_paths",
|
|
140
155
|
"query_face",
|
|
141
156
|
"render_dashboard",
|
|
142
|
-
"schema_hints",
|
|
143
|
-
"validate_query",
|
|
144
157
|
]
|
|
158
|
+
|
|
159
|
+
# `schema_hints` and `validate_query` (the functions) are deliberately NOT
|
|
160
|
+
# re-exported here: both names collide with their own defining submodule
|
|
161
|
+
# (`dataface.agent_api.schema_hints`, `dataface.agent_api.validate_query`).
|
|
162
|
+
# Importing that submodule for any reason binds it onto this package under
|
|
163
|
+
# that exact name as a side effect of Python's own import machinery — a
|
|
164
|
+
# rebind PEP 562 __getattr__ cannot see or prevent — so a lazy alias sharing
|
|
165
|
+
# a submodule's leaf name silently flips from function to module depending on
|
|
166
|
+
# import order. Import the function directly from its submodule instead:
|
|
167
|
+
# `from dataface.agent_api.schema_hints import schema_hints`,
|
|
168
|
+
# `from dataface.agent_api.validate_query import validate_query`.
|
|
169
|
+
|
|
170
|
+
# name -> (module dotted path, attribute name in that module; "" means the
|
|
171
|
+
# module itself). PEP 562 __getattr__ below resolves these on first access
|
|
172
|
+
# instead of importing every submodule (and the compile/execute/dbt_common
|
|
173
|
+
# stack several of them drag in) eagerly at `import dataface.agent_api` time.
|
|
174
|
+
_LAZY_ATTRS: dict[str, tuple[str, str]] = {
|
|
175
|
+
"skills": ("dataface.agent_api.skills", ""),
|
|
176
|
+
"project_sources_for_face": (
|
|
177
|
+
"dataface.agent_api._paths",
|
|
178
|
+
"project_sources_for_face",
|
|
179
|
+
),
|
|
180
|
+
"DescribeFaceArgs": ("dataface.agent_api.describe", "DescribeFaceArgs"),
|
|
181
|
+
"DescribeFaceResult": ("dataface.agent_api.describe", "DescribeFaceResult"),
|
|
182
|
+
"describe_face": ("dataface.agent_api.describe", "describe_face"),
|
|
183
|
+
"DocsArgs": ("dataface.agent_api.docs", "DocsArgs"),
|
|
184
|
+
"DocsResult": ("dataface.agent_api.docs", "DocsResult"),
|
|
185
|
+
"DocsSearchHit": ("dataface.agent_api.docs", "DocsSearchHit"),
|
|
186
|
+
"Topic": ("dataface.agent_api.docs", "Topic"),
|
|
187
|
+
"InitResult": ("dataface.agent_api.init", "InitResult"),
|
|
188
|
+
"init_project": ("dataface.agent_api.init", "init_project"),
|
|
189
|
+
"MigrateError": ("dataface.agent_api.migrate", "MigrateError"),
|
|
190
|
+
"MigrateSummary": ("dataface.agent_api.migrate", "MigrateSummary"),
|
|
191
|
+
"migrate_paths": ("dataface.agent_api.migrate", "migrate_paths"),
|
|
192
|
+
"ProjectSession": ("dataface.agent_api.project_session", "ProjectSession"),
|
|
193
|
+
"QueryFaceResult": ("dataface.agent_api.query", "QueryFaceResult"),
|
|
194
|
+
"query_face": ("dataface.agent_api.query", "query_face"),
|
|
195
|
+
"SchemaHints": ("dataface.agent_api.schema_hints", "SchemaHints"),
|
|
196
|
+
"ValidateDashboardArgs": ("dataface.agent_api.validate", "ValidateDashboardArgs"),
|
|
197
|
+
"ValidateResult": ("dataface.agent_api.validate", "ValidateResult"),
|
|
198
|
+
"QueryDiagnostic": ("dataface.agent_api.validate_query", "QueryDiagnostic"),
|
|
199
|
+
"set_surface": ("dataface.core.attribution", "set_surface"),
|
|
200
|
+
"compile": ("dataface.core.compile.compiler", "compile"),
|
|
201
|
+
"CompileResult": ("dataface.core.compile.compiler", "CompileResult"),
|
|
202
|
+
"ProjectSourcesConfig": ("dataface.core.compile.config", "ProjectSourcesConfig"),
|
|
203
|
+
"RenderedDashboard": ("dataface.core.dashboard", "RenderedDashboard"),
|
|
204
|
+
"RenderFormat": ("dataface.core.render_format", "RenderFormat"),
|
|
205
|
+
"render_dashboard": ("dataface.core.dashboard", "render_dashboard"),
|
|
206
|
+
"Diagnostic": ("dataface.core.diagnostics", "Diagnostic"),
|
|
207
|
+
"QueryResultCache": ("dataface.core.execute.cache_backend", "QueryResultCache"),
|
|
208
|
+
"WarehouseObserver": (
|
|
209
|
+
"dataface.core.execute.observability",
|
|
210
|
+
"WarehouseObserver",
|
|
211
|
+
),
|
|
212
|
+
"AllowlistedSourceResolver": (
|
|
213
|
+
"dataface.core.execute.source_resolver",
|
|
214
|
+
"AllowlistedSourceResolver",
|
|
215
|
+
),
|
|
216
|
+
"DefaultSourceResolver": (
|
|
217
|
+
"dataface.core.execute.source_resolver",
|
|
218
|
+
"DefaultSourceResolver",
|
|
219
|
+
),
|
|
220
|
+
"SourceResolver": ("dataface.core.execute.source_resolver", "SourceResolver"),
|
|
221
|
+
"get_fonts_dir": ("dataface.core.fonts", "get_fonts_dir"),
|
|
222
|
+
"FaceFile": ("dataface.core.project", "FaceFile"),
|
|
223
|
+
"InMemoryFace": ("dataface.core.project", "InMemoryFace"),
|
|
224
|
+
"Project": ("dataface.core.project", "Project"),
|
|
225
|
+
"LinkContext": ("dataface.core.render.board_links", "LinkContext"),
|
|
226
|
+
"RenderError": ("dataface.core.render.errors", "RenderError"),
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def __getattr__(name: str) -> Any:
|
|
231
|
+
try:
|
|
232
|
+
module_path, attr_name = _LAZY_ATTRS[name]
|
|
233
|
+
except KeyError:
|
|
234
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}") from None
|
|
235
|
+
module = importlib.import_module(module_path)
|
|
236
|
+
value = module if not attr_name else getattr(module, attr_name)
|
|
237
|
+
globals()[name] = value # cache: repeated access is O(1) and `is` identity holds
|
|
238
|
+
return value
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def __dir__() -> list[str]:
|
|
242
|
+
return sorted(set(globals()) | set(_LAZY_ATTRS))
|
dataface/agent_api/_paths.py
CHANGED
|
@@ -5,10 +5,10 @@ from __future__ import annotations
|
|
|
5
5
|
import os
|
|
6
6
|
from dataclasses import dataclass
|
|
7
7
|
from pathlib import Path, PurePosixPath
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
8
9
|
|
|
9
10
|
# tach-ignore(agent_api->cli: runtime host-type guard in resolve_face_or_error / _relpath_for_fs_location; the local-filesystem check needs a non-cli signal on Project — deferred)
|
|
10
11
|
from dataface.cli.filesystem_project import FilesystemProject
|
|
11
|
-
from dataface.core.compile.config import ProjectSourcesConfig
|
|
12
12
|
from dataface.core.diagnostics import (
|
|
13
13
|
ERR_FILE_NOT_FOUND,
|
|
14
14
|
ERR_INTERNAL,
|
|
@@ -21,12 +21,18 @@ from dataface.core.project import (
|
|
|
21
21
|
Project,
|
|
22
22
|
ProjectPath,
|
|
23
23
|
assert_relpath,
|
|
24
|
+
posix_relpath,
|
|
24
25
|
)
|
|
25
26
|
from dataface.core.project_roots import (
|
|
27
|
+
DFT_ROOT_MARKERS as DFT_ROOT_MARKERS,
|
|
26
28
|
find_dft_root as find_dft_root,
|
|
27
29
|
find_project_root as find_project_root,
|
|
30
|
+
find_repo_root as find_repo_root,
|
|
28
31
|
)
|
|
29
32
|
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
from dataface.core.compile.config import ProjectSourcesConfig
|
|
35
|
+
|
|
30
36
|
|
|
31
37
|
def resolve_face_relpath(relpath: PurePosixPath, project: Project) -> ProjectPath:
|
|
32
38
|
"""Project-relative face identity -> ProjectPath, with the faces-first retry.
|
|
@@ -83,12 +89,12 @@ def _relpath_for_fs_location(path: Path, project: Project) -> str:
|
|
|
83
89
|
root = project.root
|
|
84
90
|
resolved = path.resolve()
|
|
85
91
|
try:
|
|
86
|
-
return
|
|
92
|
+
return posix_relpath(resolved, root)
|
|
87
93
|
except ValueError:
|
|
88
94
|
pass
|
|
89
95
|
normalized = Path(os.path.normpath(path.absolute()))
|
|
90
96
|
try:
|
|
91
|
-
return
|
|
97
|
+
return posix_relpath(normalized, root)
|
|
92
98
|
except ValueError:
|
|
93
99
|
raise ValueError(f"Path {path!r} is outside project root {root!r}") from None
|
|
94
100
|
|
|
@@ -22,6 +22,7 @@ from pydantic import BaseModel, ConfigDict
|
|
|
22
22
|
|
|
23
23
|
from dataface.agent_api._paths import resolve_face_or_error
|
|
24
24
|
from dataface.core.compile import compile_file
|
|
25
|
+
from dataface.core.compile.variables import normalize_multiselect_values
|
|
25
26
|
from dataface.core.diagnostics import ERR_FILE_NOT_FOUND, Diagnostic
|
|
26
27
|
from dataface.core.diagnostics.base import DatafaceError
|
|
27
28
|
from dataface.core.execute.executor import Executor
|
|
@@ -105,7 +106,16 @@ def emit_board_artifact(
|
|
|
105
106
|
face = compile_result.face
|
|
106
107
|
assert face is not None # compile_result.success guarantees this
|
|
107
108
|
|
|
108
|
-
|
|
109
|
+
# `emit-board` merges its own variables — it never goes through `render()`,
|
|
110
|
+
# whose prologue does this — and this dict is both rendered from and stored
|
|
111
|
+
# verbatim in the recording that `render_board_from_files` replays. `--var`
|
|
112
|
+
# values are always strings, so a scalar is the only shape a caller can
|
|
113
|
+
# supply for a multiselect here; unnarrowed, a bare `| join` would iterate
|
|
114
|
+
# it character by character.
|
|
115
|
+
merged_variables: dict[str, Any] = normalize_multiselect_values(
|
|
116
|
+
{**face.variable_defaults, **(variables or {})},
|
|
117
|
+
face.variable_registry or {},
|
|
118
|
+
)
|
|
109
119
|
|
|
110
120
|
executor = Executor(
|
|
111
121
|
face,
|