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
@@ -252,8 +252,9 @@ aliases:
252
252
  ```
253
253
 
254
254
  Rules:
255
- - An alias must not collide with a real face file path — the server raises an error at startup if it does.
256
- - Each alias must be unique across the project — two faces claiming the same alias is a startup error.
255
+ - Every alias must be absolute (leading `/`) — a relative entry fails validation on that face.
256
+ - An alias must not collide with a real face file path — `dft serve` raises an error at startup if it does.
257
+ - Each alias must be unique across the project — two faces claiming the same alias is a startup error under `dft serve`. A host with no startup step resolves a duplicate to the first face by slug instead of reporting it.
257
258
  - Aliasing a generated system route (data or inspector view) is allowed: the redirect takes precedence, letting you override what that URL serves.
258
259
 
259
260
  #### Parameterized aliases (capture a path segment into a variable)
@@ -613,7 +614,11 @@ Each block below shows the minimum-viable shape for one chart type. They are sta
613
614
  type: bar
614
615
  x: category
615
616
  y: value
616
- color: group # Optional second dimension; stacks by default
617
+ color: group # Optional second dimension; grouped side by side by default
618
+ style:
619
+ orientation: vertical # vertical = column chart; horizontal = horizontal bar chart.
620
+ # Auto-resolves from x's column type when omitted: categorical x → horizontal.
621
+ stack: zero # see the stack table under "area" — bar takes the same values
617
622
 
618
623
  # line — temporal/ordered x, numeric y
619
624
  type: line
@@ -625,8 +630,14 @@ color: segment # Optional: one line per segment
625
630
  type: area
626
631
  x: date
627
632
  y: value
633
+ color: segment # Required for stacking — stack modes split by this field
628
634
  style:
629
- stack: normalize # 100% stacked
635
+ # stack names the shape you get. All four values work on bar and area alike:
636
+ stack: zero # stacked
637
+ # stack: none # overlapping — bar groups side by side, area overlays.
638
+ # The default when color: is set.
639
+ # stack: normalize # 100% stacked — every column fills the full height
640
+ # stack: center # streamgraph — stacked around a centered baseline
630
641
 
631
642
  # scatter — x and y numeric
632
643
  type: scatter
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
- # Re-export from core for convenience
45
- from dataface.core import validate
46
- from dataface.core.compile import (
47
- AnyQuery,
48
- AuthoredFace,
49
- CompilationError,
50
- CompileResult,
51
- Face,
52
- Layout,
53
- LayoutItem,
54
- Variable,
55
- compile,
56
- compile_file,
57
- )
58
- from dataface.core.execute import (
59
- ExecutionError,
60
- Executor,
61
- )
62
-
63
- # Import from render submodule
64
- # Note: We import `render` function but this shadows `dataface.render` module access
65
- # Tests that need to patch `dataface.core.render.renderer` should import the module directly
66
- from dataface.core.render import RenderError, render
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))
@@ -36,72 +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 dataface.agent_api import skills as skills
40
- from dataface.agent_api._paths import (
41
- project_sources_for_face as project_sources_for_face,
42
- )
43
- from dataface.agent_api.describe import (
44
- DescribeFaceArgs,
45
- DescribeFaceResult,
46
- describe_face,
47
- )
48
- from dataface.agent_api.docs import DocsArgs, DocsResult, DocsSearchHit, Topic
49
- from dataface.agent_api.init import (
50
- InitResult as InitResult,
51
- init_project as init_project,
52
- )
53
- from dataface.agent_api.migrate import (
54
- MigrateError as MigrateError,
55
- MigrateSummary as MigrateSummary,
56
- migrate_paths as migrate_paths,
57
- )
58
- from dataface.agent_api.project_session import ProjectSession as ProjectSession
59
- from dataface.agent_api.query import QueryFaceResult, query_face
60
- from dataface.agent_api.schema_hints import (
61
- SchemaHints as SchemaHints,
62
- schema_hints as schema_hints,
63
- )
64
- from dataface.agent_api.validate import (
65
- ValidateDashboardArgs,
66
- ValidateResult,
67
- )
68
- from dataface.agent_api.validate_query import (
69
- QueryDiagnostic as QueryDiagnostic,
70
- validate_query as validate_query,
71
- )
72
- from dataface.core.attribution import set_surface as set_surface
73
- from dataface.core.compile.compiler import (
74
- CompileResult as CompileResult,
75
- compile as compile,
76
- )
77
- from dataface.core.compile.config import ProjectSourcesConfig as ProjectSourcesConfig
78
- from dataface.core.dashboard import (
79
- RenderedDashboard as RenderedDashboard,
80
- RenderFormat as RenderFormat,
81
- render_dashboard as render_dashboard,
82
- )
83
- from dataface.core.diagnostics import Diagnostic as Diagnostic
84
-
85
- # Execution extension seams: the protocols a composition root injects, plus the
86
- # resolver base impls it subclasses. Re-exported so roots (CLI, MCP, serve,
87
- # Cloud, A lIe, Playground) compose against agent_api, not dataface.core.*.
88
- # AdapterRegistry is intentionally absent — it is built via ProjectSession /
89
- # build_adapter_registry, not subclassed as an extension contract.
90
- from dataface.core.execute.cache_backend import QueryResultCache as QueryResultCache
91
- from dataface.core.execute.observability import WarehouseObserver as WarehouseObserver
92
- from dataface.core.execute.source_resolver import (
93
- AllowlistedSourceResolver as AllowlistedSourceResolver,
94
- DefaultSourceResolver as DefaultSourceResolver,
95
- SourceResolver as SourceResolver,
96
- )
97
- from dataface.core.fonts import get_fonts_dir as get_fonts_dir
98
- from dataface.core.project import (
99
- FaceFile as FaceFile,
100
- InMemoryFace as InMemoryFace,
101
- Project as Project,
102
- )
103
- from dataface.core.render.board_links import LinkContext as LinkContext
104
- from dataface.core.render.errors import RenderError as RenderError
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
105
116
 
106
117
  __all__ = [
107
118
  "compile",
@@ -143,6 +154,89 @@ __all__ = [
143
154
  "migrate_paths",
144
155
  "query_face",
145
156
  "render_dashboard",
146
- "schema_hints",
147
- "validate_query",
148
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))
@@ -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 str(resolved.relative_to(root))
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 str(normalized.relative_to(root))
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