dataface 0.1.2__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 (455) hide show
  1. d3_format/__init__.py +14 -0
  2. d3_format/errors.py +19 -0
  3. d3_format/format.py +551 -0
  4. d3_format/spec.py +159 -0
  5. dataface/DATAFACE_SYNTAX.md +1135 -0
  6. dataface/__init__.py +93 -0
  7. dataface/_docs_site.py +20 -0
  8. dataface/_install_hint.py +26 -0
  9. dataface/agent_api/__init__.py +79 -0
  10. dataface/agent_api/_init_templates/__init__.py +0 -0
  11. dataface/agent_api/_init_templates/agents_dft_snippet.md +26 -0
  12. dataface/agent_api/_init_templates/dataface.yml +15 -0
  13. dataface/agent_api/_init_templates/faces-dataface.yml +144 -0
  14. dataface/agent_api/_init_templates/index.md +24 -0
  15. dataface/agent_api/_paths.py +118 -0
  16. dataface/agent_api/_project_agents_md.py +43 -0
  17. dataface/agent_api/_session_store.py +486 -0
  18. dataface/agent_api/_state.py +28 -0
  19. dataface/agent_api/chat.py +221 -0
  20. dataface/agent_api/dashboards.py +257 -0
  21. dataface/agent_api/describe.py +366 -0
  22. dataface/agent_api/describe_query.py +120 -0
  23. dataface/agent_api/docs/__init__.py +25 -0
  24. dataface/agent_api/docs/_loader.py +292 -0
  25. dataface/agent_api/docs/yaml-reference.md +2757 -0
  26. dataface/agent_api/file_refs.py +118 -0
  27. dataface/agent_api/init.py +126 -0
  28. dataface/agent_api/inspect.py +128 -0
  29. dataface/agent_api/mcp_install.py +170 -0
  30. dataface/agent_api/query.py +274 -0
  31. dataface/agent_api/schema.py +658 -0
  32. dataface/agent_api/schema_search.py +284 -0
  33. dataface/agent_api/search.py +270 -0
  34. dataface/agent_api/skill_install.py +141 -0
  35. dataface/agent_api/skill_render.py +90 -0
  36. dataface/agent_api/skills.py +293 -0
  37. dataface/agent_api/surface_aliases.yaml +128 -0
  38. dataface/agent_api/validate.py +175 -0
  39. dataface/agent_api/validate_query.py +84 -0
  40. dataface/ai/__init__.py +39 -0
  41. dataface/ai/agent.py +139 -0
  42. dataface/ai/context.py +45 -0
  43. dataface/ai/events.py +62 -0
  44. dataface/ai/external_mcp.py +610 -0
  45. dataface/ai/generate_sql.py +96 -0
  46. dataface/ai/llm.py +403 -0
  47. dataface/ai/mcp/__init__.py +51 -0
  48. dataface/ai/mcp/server.py +289 -0
  49. dataface/ai/memories.py +85 -0
  50. dataface/ai/prompts.py +177 -0
  51. dataface/ai/schema_context.py +138 -0
  52. dataface/ai/skills/before-after-comparison/SKILL.md +102 -0
  53. dataface/ai/skills/before-after-comparison/examples/before-after-comparison.yml +24 -0
  54. dataface/ai/skills/dashboard-build/SKILL.md +212 -0
  55. dataface/ai/skills/dashboard-build/examples/_smoke.yml +15 -0
  56. dataface/ai/skills/dashboard-design/SKILL.md +182 -0
  57. dataface/ai/skills/dashboard-review/SKILL.md +113 -0
  58. dataface/ai/skills/dashboard-structural-review/SKILL.md +173 -0
  59. dataface/ai/skills/dashboard-visual-review/SKILL.md +139 -0
  60. dataface/ai/skills/dataface-mcp-setup/SKILL.md +177 -0
  61. dataface/ai/skills/dataface-troubleshooting/SKILL.md +225 -0
  62. dataface/ai/skills/drill-down-link/SKILL.md +112 -0
  63. dataface/ai/skills/drill-down-link/examples/drill-down-link.yml +27 -0
  64. dataface/ai/skills/faceted-small-multiples/SKILL.md +116 -0
  65. dataface/ai/skills/faceted-small-multiples/examples/faceted-small-multiples.yml +33 -0
  66. dataface/ai/skills/filter-bar-with-variables/SKILL.md +105 -0
  67. dataface/ai/skills/filter-bar-with-variables/examples/filter-bar-with-variables.yml +49 -0
  68. dataface/ai/skills/kpi-row/SKILL.md +101 -0
  69. dataface/ai/skills/kpi-row/examples/kpi-row.yml +55 -0
  70. dataface/ai/skills/report-design/SKILL.md +184 -0
  71. dataface/ai/skills/single-metric-bignum/SKILL.md +90 -0
  72. dataface/ai/skills/single-metric-bignum/examples/single-metric-bignum.yml +27 -0
  73. dataface/ai/skills/table-heavy-ops-dashboard/SKILL.md +114 -0
  74. dataface/ai/skills/table-heavy-ops-dashboard/examples/table-heavy-ops-dashboard.yml +48 -0
  75. dataface/ai/skills/time-series-trend/SKILL.md +93 -0
  76. dataface/ai/skills/time-series-trend/examples/time-series-trend.yml +26 -0
  77. dataface/ai/skills/top-n-with-detail/SKILL.md +98 -0
  78. dataface/ai/skills/top-n-with-detail/examples/top-n-with-detail.yml +45 -0
  79. dataface/ai/skills/two-by-two-grid-overview/SKILL.md +78 -0
  80. dataface/ai/skills/two-by-two-grid-overview/examples/two-by-two-grid-overview.yml +64 -0
  81. dataface/ai/tool_schemas.py +132 -0
  82. dataface/ai/tools/__init__.py +312 -0
  83. dataface/ai/yaml_utils.py +57 -0
  84. dataface/cli/__init__.py +3 -0
  85. dataface/cli/_console.py +48 -0
  86. dataface/cli/_error_format.py +83 -0
  87. dataface/cli/_extras.py +190 -0
  88. dataface/cli/_json_output.py +8 -0
  89. dataface/cli/_parsing.py +17 -0
  90. dataface/cli/_version_info.py +56 -0
  91. dataface/cli/commands/__init__.py +3 -0
  92. dataface/cli/commands/_agent_input.py +205 -0
  93. dataface/cli/commands/_agent_server.py +115 -0
  94. dataface/cli/commands/chat.py +645 -0
  95. dataface/cli/commands/describe.py +107 -0
  96. dataface/cli/commands/docs.py +131 -0
  97. dataface/cli/commands/extension.py +179 -0
  98. dataface/cli/commands/init.py +240 -0
  99. dataface/cli/commands/inspect.py +94 -0
  100. dataface/cli/commands/mcp_init.py +167 -0
  101. dataface/cli/commands/query.py +386 -0
  102. dataface/cli/commands/render.py +291 -0
  103. dataface/cli/commands/schema.py +411 -0
  104. dataface/cli/commands/search.py +49 -0
  105. dataface/cli/commands/serve.py +114 -0
  106. dataface/cli/commands/skills.py +133 -0
  107. dataface/cli/commands/skills_init.py +161 -0
  108. dataface/cli/commands/validate.py +63 -0
  109. dataface/cli/main.py +1501 -0
  110. dataface/core/__init__.py +75 -0
  111. dataface/core/compile/__init__.py +244 -0
  112. dataface/core/compile/_jinja_helpers.py +78 -0
  113. dataface/core/compile/channel.py +222 -0
  114. dataface/core/compile/chart_focus.py +101 -0
  115. dataface/core/compile/chart_resolved.py +169 -0
  116. dataface/core/compile/chart_type_detection.py +489 -0
  117. dataface/core/compile/chart_update.py +261 -0
  118. dataface/core/compile/colors.py +64 -0
  119. dataface/core/compile/compiler.py +904 -0
  120. dataface/core/compile/config.py +823 -0
  121. dataface/core/compile/custom_chart_types.py +208 -0
  122. dataface/core/compile/data_table_attachment.py +1287 -0
  123. dataface/core/compile/detect.py +110 -0
  124. dataface/core/compile/errors.py +302 -0
  125. dataface/core/compile/filter_injection.py +319 -0
  126. dataface/core/compile/introspection.py +527 -0
  127. dataface/core/compile/jinja.py +511 -0
  128. dataface/core/compile/labels_env.py +52 -0
  129. dataface/core/compile/markdown.py +154 -0
  130. dataface/core/compile/meta.py +388 -0
  131. dataface/core/compile/models/__init__.py +0 -0
  132. dataface/core/compile/models/chart/__init__.py +0 -0
  133. dataface/core/compile/models/chart/authored.py +2137 -0
  134. dataface/core/compile/models/chart/compiled.py +398 -0
  135. dataface/core/compile/models/config.py +347 -0
  136. dataface/core/compile/models/face/__init__.py +0 -0
  137. dataface/core/compile/models/face/authored.py +659 -0
  138. dataface/core/compile/models/face/compiled.py +522 -0
  139. dataface/core/compile/models/factories.py +201 -0
  140. dataface/core/compile/models/markers.py +40 -0
  141. dataface/core/compile/models/palette.py +36 -0
  142. dataface/core/compile/models/primitives.py +415 -0
  143. dataface/core/compile/models/query/__init__.py +0 -0
  144. dataface/core/compile/models/query/authored.py +246 -0
  145. dataface/core/compile/models/query/compiled.py +710 -0
  146. dataface/core/compile/models/refs.py +137 -0
  147. dataface/core/compile/models/source.py +611 -0
  148. dataface/core/compile/models/style/__init__.py +0 -0
  149. dataface/core/compile/models/style/authored.py +481 -0
  150. dataface/core/compile/models/style/compiled.py +3399 -0
  151. dataface/core/compile/models/style/merged.py +1682 -0
  152. dataface/core/compile/models/theme.py +362 -0
  153. dataface/core/compile/models/variable/__init__.py +0 -0
  154. dataface/core/compile/models/variable/authored.py +254 -0
  155. dataface/core/compile/models/vega_lite/__init__.py +0 -0
  156. dataface/core/compile/models/vega_lite/config.py +510 -0
  157. dataface/core/compile/models/vega_lite/contracts.py +171 -0
  158. dataface/core/compile/normalize_charts.py +494 -0
  159. dataface/core/compile/normalize_layout.py +1000 -0
  160. dataface/core/compile/normalize_queries.py +297 -0
  161. dataface/core/compile/normalize_variables.py +489 -0
  162. dataface/core/compile/normalizer.py +543 -0
  163. dataface/core/compile/palette.py +1100 -0
  164. dataface/core/compile/parameterized.py +658 -0
  165. dataface/core/compile/parser.py +228 -0
  166. dataface/core/compile/schema.py +20 -0
  167. dataface/core/compile/schema_renderers/__init__.py +0 -0
  168. dataface/core/compile/schema_renderers/json_schema.py +163 -0
  169. dataface/core/compile/schema_renderers/prompt.py +152 -0
  170. dataface/core/compile/schema_renderers/vscode_schema.py +301 -0
  171. dataface/core/compile/sizing.py +2126 -0
  172. dataface/core/compile/sources.py +518 -0
  173. dataface/core/compile/sql_authoring_lint.py +56 -0
  174. dataface/core/compile/style_cascade.py +471 -0
  175. dataface/core/compile/typography.py +299 -0
  176. dataface/core/compile/validator.py +301 -0
  177. dataface/core/compile/variables.py +53 -0
  178. dataface/core/compile/vega_config.py +98 -0
  179. dataface/core/compile/vega_lite/__init__.py +6 -0
  180. dataface/core/compile/vega_lite/validation.py +95 -0
  181. dataface/core/compile/yaml_error_formatter.py +838 -0
  182. dataface/core/connections.py +38 -0
  183. dataface/core/dashboard.py +358 -0
  184. dataface/core/defaults/default_config.yml +101 -0
  185. dataface/core/defaults/palettes/categorical/category-10-dark.yml +32 -0
  186. dataface/core/defaults/palettes/categorical/category-10-light.yml +43 -0
  187. dataface/core/defaults/palettes/categorical/category-10.yml +31 -0
  188. dataface/core/defaults/palettes/categorical/category-6-tonal-blue.yml +22 -0
  189. dataface/core/defaults/palettes/categorical/category-6-tonal-brown.yml +29 -0
  190. dataface/core/defaults/palettes/categorical/category-6-tonal-green.yml +20 -0
  191. dataface/core/defaults/palettes/categorical/category-6-tonal-orange.yml +21 -0
  192. dataface/core/defaults/palettes/categorical/category-6-tonal-purple.yml +20 -0
  193. dataface/core/defaults/palettes/categorical/editorial-10-dark.yml +32 -0
  194. dataface/core/defaults/palettes/categorical/editorial-10.yml +40 -0
  195. dataface/core/defaults/palettes/categorical/hero-6.yml +17 -0
  196. dataface/core/defaults/palettes/categorical/single-blue.yml +11 -0
  197. dataface/core/defaults/palettes/categorical/tableau.yml +20 -0
  198. dataface/core/defaults/palettes/data/xkcd_colors.json +3803 -0
  199. dataface/core/defaults/palettes/diverging/blue-red.yml +25 -0
  200. dataface/core/defaults/palettes/diverging/coolwarm.yml +24 -0
  201. dataface/core/defaults/palettes/diverging/crimson-green.yml +23 -0
  202. dataface/core/defaults/palettes/diverging/orange-teal.yml +23 -0
  203. dataface/core/defaults/palettes/diverging/sunset.yml +24 -0
  204. dataface/core/defaults/palettes/scaffold/dft-creams.yml +38 -0
  205. dataface/core/defaults/palettes/scaffold/dft-grays.yml +53 -0
  206. dataface/core/defaults/palettes/sequential/amber.yml +22 -0
  207. dataface/core/defaults/palettes/sequential/blue.yml +22 -0
  208. dataface/core/defaults/palettes/sequential/brown.yml +22 -0
  209. dataface/core/defaults/palettes/sequential/gray.yml +22 -0
  210. dataface/core/defaults/palettes/sequential/green.yml +22 -0
  211. dataface/core/defaults/palettes/sequential/purple.yml +22 -0
  212. dataface/core/defaults/palettes/sequential/rust.yml +22 -0
  213. dataface/core/defaults/palettes/sequential/teal.yml +22 -0
  214. dataface/core/defaults/palettes/tone/negative.yml +32 -0
  215. dataface/core/defaults/palettes/tone/positive.yml +22 -0
  216. dataface/core/defaults/palettes/tone/warning.yml +22 -0
  217. dataface/core/defaults/themes/_base.yaml +786 -0
  218. dataface/core/defaults/themes/bi.yaml +16 -0
  219. dataface/core/defaults/themes/carbong100.yaml +41 -0
  220. dataface/core/defaults/themes/cream.yaml +122 -0
  221. dataface/core/defaults/themes/dark.yaml +40 -0
  222. dataface/core/defaults/themes/diagnostics-title-angle-extreme.yaml +9 -0
  223. dataface/core/defaults/themes/diagnostics-title-baseline-extreme.yaml +9 -0
  224. dataface/core/defaults/themes/diagnostics-title-baseline.yaml +24 -0
  225. dataface/core/defaults/themes/diagnostics-title-center.yaml +8 -0
  226. dataface/core/defaults/themes/diagnostics-title-color-extreme.yaml +24 -0
  227. dataface/core/defaults/themes/diagnostics-title-font-extreme.yaml +25 -0
  228. dataface/core/defaults/themes/diagnostics-title-left.yaml +8 -0
  229. dataface/core/defaults/themes/diagnostics-title-offset-extreme.yaml +9 -0
  230. dataface/core/defaults/themes/diagnostics-title-size-extreme.yaml +24 -0
  231. dataface/core/defaults/themes/diagnostics-title-weight-extreme.yaml +24 -0
  232. dataface/core/defaults/themes/editorial.yaml +147 -0
  233. dataface/core/defaults/themes/light.yaml +30 -0
  234. dataface/core/defaults/themes/looker.yaml +17 -0
  235. dataface/core/defaults/themes/stark.yaml +134 -0
  236. dataface/core/errors/__init__.py +67 -0
  237. dataface/core/errors/codes_compile.py +56 -0
  238. dataface/core/errors/codes_execute.py +177 -0
  239. dataface/core/errors/codes_render.py +106 -0
  240. dataface/core/errors/codes_unknown.py +15 -0
  241. dataface/core/errors/hints.py +74 -0
  242. dataface/core/errors/registry.py +42 -0
  243. dataface/core/errors/structured.py +92 -0
  244. dataface/core/execute/__init__.py +91 -0
  245. dataface/core/execute/adapters/__init__.py +49 -0
  246. dataface/core/execute/adapters/adapter_registry.py +400 -0
  247. dataface/core/execute/adapters/base.py +245 -0
  248. dataface/core/execute/adapters/csv_adapter.py +239 -0
  249. dataface/core/execute/adapters/dbt_adapter.py +283 -0
  250. dataface/core/execute/adapters/dbt_adapter_factory.py +212 -0
  251. dataface/core/execute/adapters/dbt_macro_loader.py +95 -0
  252. dataface/core/execute/adapters/dbt_utils.py +150 -0
  253. dataface/core/execute/adapters/http_adapter.py +224 -0
  254. dataface/core/execute/adapters/metricflow_adapter.py +94 -0
  255. dataface/core/execute/adapters/schema_resolver_adapter.py +144 -0
  256. dataface/core/execute/adapters/sql_adapter.py +710 -0
  257. dataface/core/execute/adapters/values_adapter.py +58 -0
  258. dataface/core/execute/batch.py +744 -0
  259. dataface/core/execute/cache_backend.py +135 -0
  260. dataface/core/execute/cache_keys.py +66 -0
  261. dataface/core/execute/dbt_jinja.py +21 -0
  262. dataface/core/execute/dialects/__init__.py +121 -0
  263. dataface/core/execute/dialects/athena.py +75 -0
  264. dataface/core/execute/dialects/base.py +302 -0
  265. dataface/core/execute/dialects/bigquery.py +38 -0
  266. dataface/core/execute/dialects/databricks.py +68 -0
  267. dataface/core/execute/dialects/duckdb.py +35 -0
  268. dataface/core/execute/dialects/mysql.py +68 -0
  269. dataface/core/execute/dialects/postgres.py +39 -0
  270. dataface/core/execute/dialects/redshift.py +12 -0
  271. dataface/core/execute/dialects/snowflake.py +51 -0
  272. dataface/core/execute/dialects/sqlserver.py +92 -0
  273. dataface/core/execute/duckdb_cache.py +712 -0
  274. dataface/core/execute/duckdb_config.py +26 -0
  275. dataface/core/execute/errors.py +213 -0
  276. dataface/core/execute/executor.py +1249 -0
  277. dataface/core/execute/parallel.py +162 -0
  278. dataface/core/execute/setup_sql.py +58 -0
  279. dataface/core/execute/source_registry.py +72 -0
  280. dataface/core/execute/source_resolver.py +255 -0
  281. dataface/core/execute/sql_guard.py +387 -0
  282. dataface/core/execute/sql_literals.py +199 -0
  283. dataface/core/fonts.py +52 -0
  284. dataface/core/inspect/__init__.py +32 -0
  285. dataface/core/inspect/cache_factory.py +98 -0
  286. dataface/core/inspect/db_types.py +162 -0
  287. dataface/core/inspect/dbt_schema.py +96 -0
  288. dataface/core/inspect/defaults.yml +37 -0
  289. dataface/core/inspect/fanout_risk.py +109 -0
  290. dataface/core/inspect/manifest_utils.py +77 -0
  291. dataface/core/inspect/partials/categorical.yml +40 -0
  292. dataface/core/inspect/partials/date.yml +40 -0
  293. dataface/core/inspect/partials/numeric.yml +55 -0
  294. dataface/core/inspect/partition_types.py +38 -0
  295. dataface/core/inspect/query_validator.py +975 -0
  296. dataface/core/inspect/renderer.py +354 -0
  297. dataface/core/inspect/resolver.py +808 -0
  298. dataface/core/inspect/search.py +461 -0
  299. dataface/core/inspect/sources/__init__.py +32 -0
  300. dataface/core/inspect/sources/dbt.py +738 -0
  301. dataface/core/inspect/sources/duckdb_utils.py +66 -0
  302. dataface/core/inspect/templates/__init__.py +1 -0
  303. dataface/core/inspect/templates/categorical_column.yml +196 -0
  304. dataface/core/inspect/templates/charts.yml +109 -0
  305. dataface/core/inspect/templates/date_column.yml +248 -0
  306. dataface/core/inspect/templates/model.yml +138 -0
  307. dataface/core/inspect/templates/numeric_column.yml +261 -0
  308. dataface/core/inspect/templates/quality.yml +80 -0
  309. dataface/core/inspect/templates/string_column.yml +263 -0
  310. dataface/core/project_roots.py +165 -0
  311. dataface/core/render/__init__.py +87 -0
  312. dataface/core/render/board_links.py +176 -0
  313. dataface/core/render/chart/__init__.py +27 -0
  314. dataface/core/render/chart/arc_attached_table.py +251 -0
  315. dataface/core/render/chart/artifacts.py +16 -0
  316. dataface/core/render/chart/callout.py +225 -0
  317. dataface/core/render/chart/decisions.py +358 -0
  318. dataface/core/render/chart/geo.py +700 -0
  319. dataface/core/render/chart/kpi.py +916 -0
  320. dataface/core/render/chart/labels.py +76 -0
  321. dataface/core/render/chart/pipeline.py +818 -0
  322. dataface/core/render/chart/presentation.py +36 -0
  323. dataface/core/render/chart/profile.py +3438 -0
  324. dataface/core/render/chart/render_single.py +347 -0
  325. dataface/core/render/chart/renderers.py +193 -0
  326. dataface/core/render/chart/rendering.py +565 -0
  327. dataface/core/render/chart/serialization.py +90 -0
  328. dataface/core/render/chart/spark.py +496 -0
  329. dataface/core/render/chart/spark_bar.py +370 -0
  330. dataface/core/render/chart/spec_builders.py +154 -0
  331. dataface/core/render/chart/standard_renderer.py +2645 -0
  332. dataface/core/render/chart/table.py +2957 -0
  333. dataface/core/render/chart/table_support.py +1452 -0
  334. dataface/core/render/chart/tick_values.py +66 -0
  335. dataface/core/render/chart/time_unit_detect.py +809 -0
  336. dataface/core/render/chart/title_overflow.py +157 -0
  337. dataface/core/render/chart/type_inference.py +122 -0
  338. dataface/core/render/chart/validation.py +99 -0
  339. dataface/core/render/chart/vega_lite.py +125 -0
  340. dataface/core/render/chart/vega_lite_types.py +268 -0
  341. dataface/core/render/chart/vl_field_maps.py +346 -0
  342. dataface/core/render/chart_interactivity.py +24 -0
  343. dataface/core/render/control_registry.py +287 -0
  344. dataface/core/render/converters/__init__.py +24 -0
  345. dataface/core/render/converters/chart.py +276 -0
  346. dataface/core/render/converters/html.py +98 -0
  347. dataface/core/render/converters/pdf.py +40 -0
  348. dataface/core/render/converters/png.py +41 -0
  349. dataface/core/render/errors.py +144 -0
  350. dataface/core/render/face_api.py +160 -0
  351. dataface/core/render/faces.py +1194 -0
  352. dataface/core/render/font_measurement.py +48 -0
  353. dataface/core/render/font_support.py +197 -0
  354. dataface/core/render/fonts/DFTSansTabular-Regular.ttf +0 -0
  355. dataface/core/render/fonts/DFTSansTabular-Regular.woff2 +0 -0
  356. dataface/core/render/fonts/DFTSerifOldstyleProportional-Regular.ttf +0 -0
  357. dataface/core/render/fonts/DFTSerifOldstyleTabular-Regular.ttf +0 -0
  358. dataface/core/render/fonts/InterVariable.ttf +0 -0
  359. dataface/core/render/fonts/InterVariable.woff2 +0 -0
  360. dataface/core/render/fonts/NOTO_COLOR_EMOJI_LICENSE.txt +93 -0
  361. dataface/core/render/fonts/NOTO_EMOJI_LICENSE.txt +93 -0
  362. dataface/core/render/fonts/NotoColorEmoji-Regular.ttf +0 -0
  363. dataface/core/render/fonts/NotoColorEmoji-Regular.woff2 +0 -0
  364. dataface/core/render/fonts/NotoEmoji-Regular.ttf +0 -0
  365. dataface/core/render/fonts/NotoEmoji-Regular.woff2 +0 -0
  366. dataface/core/render/fonts/SOURCE_CODE_PRO_LICENSE.txt +93 -0
  367. dataface/core/render/fonts/SOURCE_SERIF_4_LICENSE.txt +98 -0
  368. dataface/core/render/fonts/SourceCodePro-Regular.ttf +0 -0
  369. dataface/core/render/fonts/SourceSerif4-Regular.ttf +0 -0
  370. dataface/core/render/fonts/_emoji_font_face.css +43 -0
  371. dataface/core/render/fonts/source-serif-4-variable-latin.woff2 +0 -0
  372. dataface/core/render/format_utils.py +329 -0
  373. dataface/core/render/geo_defaults.yml +28 -0
  374. dataface/core/render/json_format.py +146 -0
  375. dataface/core/render/layout_sizing.py +865 -0
  376. dataface/core/render/layouts.py +541 -0
  377. dataface/core/render/markdown_defaults.yml +16 -0
  378. dataface/core/render/missing_vars_prompt.py +79 -0
  379. dataface/core/render/placeholder.py +389 -0
  380. dataface/core/render/render_result.py +14 -0
  381. dataface/core/render/renderer.py +467 -0
  382. dataface/core/render/script_embedding.py +16 -0
  383. dataface/core/render/svg_utils.py +212 -0
  384. dataface/core/render/template_loader.py +69 -0
  385. dataface/core/render/templates/controls/_styles.css +606 -0
  386. dataface/core/render/templates/controls/checkbox.html +16 -0
  387. dataface/core/render/templates/controls/date.html +16 -0
  388. dataface/core/render/templates/controls/number.html +19 -0
  389. dataface/core/render/templates/controls/readonly.html +9 -0
  390. dataface/core/render/templates/controls/select.html +21 -0
  391. dataface/core/render/templates/controls/slider.html +22 -0
  392. dataface/core/render/templates/controls/text.html +16 -0
  393. dataface/core/render/templates/scripts/chart_interactivity.js +191 -0
  394. dataface/core/render/templates/scripts/variables.js +976 -0
  395. dataface/core/render/templates/svg/grid_pattern.svg +3 -0
  396. dataface/core/render/templates/svg/styles.css +51 -0
  397. dataface/core/render/terminal.py +311 -0
  398. dataface/core/render/terminal_charts.py +563 -0
  399. dataface/core/render/terminal_defaults.yml +2 -0
  400. dataface/core/render/terminal_layouts.py +299 -0
  401. dataface/core/render/terminal_text.py +31 -0
  402. dataface/core/render/text/__init__.py +1 -0
  403. dataface/core/render/text/case.py +113 -0
  404. dataface/core/render/text_format.py +129 -0
  405. dataface/core/render/utils.py +106 -0
  406. dataface/core/render/variable_controls.py +946 -0
  407. dataface/core/render/variable_input_refinement.py +140 -0
  408. dataface/core/render/warnings/__init__.py +15 -0
  409. dataface/core/render/warnings/bar_color_1_to_1_with_x.py +80 -0
  410. dataface/core/render/warnings/base.py +44 -0
  411. dataface/core/render/warnings/fanout_risk.py +15 -0
  412. dataface/core/render/warnings/from_query_diagnostic.py +56 -0
  413. dataface/core/render/warnings/missing_join_predicate.py +13 -0
  414. dataface/core/render/warnings/query_parse_error.py +14 -0
  415. dataface/core/render/warnings/query_returned_zero_rows.py +42 -0
  416. dataface/core/render/warnings/reaggregation.py +14 -0
  417. dataface/core/render/warnings/registry.py +45 -0
  418. dataface/core/render/warnings/suppression.py +46 -0
  419. dataface/core/render/warnings/temporal_single_point.py +63 -0
  420. dataface/core/render/warnings/unreferenced_chart.py +15 -0
  421. dataface/core/render/warnings/y_encoding_mostly_null.py +76 -0
  422. dataface/core/render/yaml_format.py +167 -0
  423. dataface/core/resolve_face.py +195 -0
  424. dataface/core/schema/__init__.py +0 -0
  425. dataface/core/schema/guidance.py +151 -0
  426. dataface/core/scoped_paths.py +59 -0
  427. dataface/core/serve/__init__.py +14 -0
  428. dataface/core/serve/bootstrap.py +39 -0
  429. dataface/core/serve/embedded.py +57 -0
  430. dataface/core/serve/port.py +129 -0
  431. dataface/core/serve/server.py +938 -0
  432. dataface/core/serve/templates/__init__.py +0 -0
  433. dataface/core/serve/templates/directory.yml +6 -0
  434. dataface/core/serve/templates/error.html.j2 +217 -0
  435. dataface/core/utils.py +121 -0
  436. dataface/core/validate.py +64 -0
  437. dataface/integrations/__init__.py +0 -0
  438. dataface/integrations/highlighting.py +351 -0
  439. dataface/integrations/markdown.py +537 -0
  440. dataface/py.typed +0 -0
  441. dataface-0.1.2.dist-info/METADATA +375 -0
  442. dataface-0.1.2.dist-info/RECORD +455 -0
  443. dataface-0.1.2.dist-info/WHEEL +4 -0
  444. dataface-0.1.2.dist-info/entry_points.txt +2 -0
  445. dataface-0.1.2.dist-info/licenses/LICENSE +202 -0
  446. mdsvg/__init__.py +168 -0
  447. mdsvg/fonts.py +656 -0
  448. mdsvg/images.py +299 -0
  449. mdsvg/parser.py +629 -0
  450. mdsvg/playground.py +284 -0
  451. mdsvg/py.typed +2 -0
  452. mdsvg/renderer.py +1623 -0
  453. mdsvg/style.py +355 -0
  454. mdsvg/types.py +200 -0
  455. mdsvg/utils.py +86 -0
@@ -0,0 +1,134 @@
1
+ # Unified theme: stark
2
+ # Structural root for the built-in theme cascade — every other built-in theme
3
+ # transitively extends this file (`editorial` extends `stark`; `cream` via
4
+ # `editorial`; `carbong100` via `dark`; `dark`/`light`/`bi`/`looker` directly).
5
+ # Stark itself is opt-in for users: "stripped-back / utilitarian / sans-only"
6
+ # — pick it when you want the pre-editorial bones without the refined
7
+ # hierarchy that the shipped `editorial` theme adds on top.
8
+
9
+ extends: _base
10
+ style:
11
+ # ── Colors ─────────────────────────────────────────────────────────────────
12
+ background: '#FFFFFF'
13
+ accent: '#3b82f6'
14
+ muted: '#e5e7eb'
15
+
16
+ font:
17
+ family: &font_sans '''Inter Variable'', Inter, system-ui, sans-serif'
18
+ color: &color_ink dft-grays.heading
19
+
20
+ border:
21
+ color: *color_ink
22
+
23
+ title:
24
+ font:
25
+ family: *font_sans
26
+ color: *color_ink
27
+ subtitle:
28
+ font:
29
+ family: *font_sans
30
+ color: *color_ink
31
+
32
+ charts:
33
+ axis:
34
+ domain:
35
+ color: *color_ink
36
+ ticks:
37
+ color: *color_ink
38
+ grid:
39
+ color: *color_ink
40
+ zero:
41
+ color: *color_ink
42
+ label:
43
+ font:
44
+ color: *color_ink
45
+ title:
46
+ font:
47
+ color: *color_ink
48
+
49
+ legend:
50
+ label:
51
+ font:
52
+ color: *color_ink
53
+ title:
54
+ font:
55
+ color: *color_ink
56
+
57
+ table:
58
+ border:
59
+ color: '#e0e0e0'
60
+ paginator:
61
+ color_active: dft-grays.ink
62
+ color_inactive: dft-grays.inactive
63
+ color_disabled: dft-grays.disabled
64
+ header:
65
+ font:
66
+ color: *color_ink
67
+ spark:
68
+ empty:
69
+ stroke:
70
+ color: '#d1d5db'
71
+
72
+ marks:
73
+ text:
74
+ font:
75
+ family: *font_sans
76
+ color: *color_ink
77
+ rule:
78
+ stroke:
79
+ color: *color_ink
80
+ tick:
81
+ stroke:
82
+ color: *color_ink
83
+
84
+ kpi:
85
+ font:
86
+ family: '''DFT Sans Tabular'', Inter, system-ui, sans-serif'
87
+
88
+ data_table:
89
+ font:
90
+ family: '''DFT Sans Tabular'', Inter, system-ui, sans-serif'
91
+ color: *color_ink
92
+ divider:
93
+ color: *color_ink
94
+ label:
95
+ font:
96
+ color: *color_ink
97
+
98
+ axis_quantitative:
99
+ label:
100
+ font:
101
+ family: '''DFT Sans Tabular'', Inter, system-ui, sans-serif'
102
+
103
+ pie:
104
+ total:
105
+ value:
106
+ font:
107
+ family: '''DFT Sans Tabular'', Inter, system-ui, sans-serif'
108
+ color: *color_ink
109
+ label:
110
+ font:
111
+ color: *color_ink
112
+
113
+ text:
114
+ font:
115
+ family: *font_sans
116
+ color: *color_ink
117
+
118
+ variables:
119
+ font:
120
+ color: *color_ink
121
+ border:
122
+ color: *color_ink
123
+ input:
124
+ border:
125
+ color: *color_ink
126
+
127
+ page:
128
+ background: '#FFFFFF'
129
+
130
+ placeholder:
131
+ overlay:
132
+ background: 'rgba(255, 255, 255, 0.5)'
133
+ font:
134
+ color: '#666666'
@@ -0,0 +1,67 @@
1
+ """Global error registry: codes, doc pointers, and did-you-mean hints.
2
+
3
+ Importing this package side-effect-registers every code module so a single
4
+ ``from dataface.core.errors import REGISTRY`` is enough to look up any code
5
+ by string at runtime.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ # Side-effect imports — required for REGISTRY self-registration.
11
+ from dataface.core.errors.codes_compile import (
12
+ DF_COMPILE_EXTRA_FIELD,
13
+ DF_COMPILE_UNKNOWN_QUERY,
14
+ )
15
+ from dataface.core.errors.codes_execute import (
16
+ DF_EXECUTE_COLUMN_NOT_FOUND,
17
+ DF_EXECUTE_MUTATING_SQL,
18
+ DF_EXECUTE_NO_DEFAULT_SOURCE,
19
+ DF_EXECUTE_SCHEMA_NOT_FOUND,
20
+ DF_EXECUTE_SOURCE_NOT_FOUND,
21
+ DF_EXECUTE_SOURCE_NOT_FOUND_EMPTY,
22
+ DF_EXECUTE_TABLE_NOT_FOUND,
23
+ DF_EXECUTE_UNPARSEABLE_SQL,
24
+ )
25
+ from dataface.core.errors.codes_render import (
26
+ DF_RENDER_BAR_DUPLICATE_ROWS,
27
+ DF_RENDER_FORMAT_UNSUPPORTED,
28
+ DF_RENDER_INPUT_INVALID,
29
+ DF_RENDER_INTERNAL,
30
+ DF_RENDER_KPI_MULTIROW,
31
+ DF_RENDER_NO_LAYOUT,
32
+ DF_RENDER_UNKNOWN_CHART_TYPE,
33
+ )
34
+ from dataface.core.errors.codes_unknown import DF_UNKNOWN_INTERNAL
35
+ from dataface.core.errors.registry import REGISTRY, ErrorCode, build_doc_url
36
+ from dataface.core.errors.structured import (
37
+ NextCommand,
38
+ StructuredError,
39
+ build_structured,
40
+ )
41
+
42
+ __all__ = [
43
+ "ErrorCode",
44
+ "REGISTRY",
45
+ "StructuredError",
46
+ "NextCommand",
47
+ "build_structured",
48
+ "build_doc_url",
49
+ "DF_UNKNOWN_INTERNAL",
50
+ "DF_COMPILE_EXTRA_FIELD",
51
+ "DF_COMPILE_UNKNOWN_QUERY",
52
+ "DF_EXECUTE_COLUMN_NOT_FOUND",
53
+ "DF_EXECUTE_MUTATING_SQL",
54
+ "DF_EXECUTE_NO_DEFAULT_SOURCE",
55
+ "DF_EXECUTE_SCHEMA_NOT_FOUND",
56
+ "DF_EXECUTE_SOURCE_NOT_FOUND",
57
+ "DF_EXECUTE_SOURCE_NOT_FOUND_EMPTY",
58
+ "DF_EXECUTE_TABLE_NOT_FOUND",
59
+ "DF_EXECUTE_UNPARSEABLE_SQL",
60
+ "DF_RENDER_BAR_DUPLICATE_ROWS",
61
+ "DF_RENDER_FORMAT_UNSUPPORTED",
62
+ "DF_RENDER_INPUT_INVALID",
63
+ "DF_RENDER_INTERNAL",
64
+ "DF_RENDER_KPI_MULTIROW",
65
+ "DF_RENDER_NO_LAYOUT",
66
+ "DF_RENDER_UNKNOWN_CHART_TYPE",
67
+ ]
@@ -0,0 +1,56 @@
1
+ """DF-COMPILE-* error codes.
2
+
3
+ Covers compile-domain raise sites migrated off DF-UNKNOWN-INTERNAL:
4
+ - DF-COMPILE-UNKNOWN-QUERY: chart references a query name that does not exist
5
+ - DF-COMPILE-EXTRA-FIELD: YAML contains a field not recognised by the schema
6
+ - DF-COMPILE-SQL-LITERAL-NEWLINES: SQL contains literal \\n from single-quoted YAML
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from dataface.core.errors.registry import REGISTRY, ErrorCode, build_doc_url
12
+
13
+ DF_COMPILE_UNKNOWN_QUERY = REGISTRY.register(
14
+ ErrorCode(
15
+ code="DF-COMPILE-UNKNOWN-QUERY",
16
+ domain="compile",
17
+ message_template=(
18
+ "Chart {chart_name!r} references unknown query {query_name!r}. "
19
+ "Declare the query under `queries:` or fix the typo in `query:`."
20
+ ),
21
+ doc_url=build_doc_url("DF-COMPILE-UNKNOWN-QUERY"),
22
+ docs_topic="compile.errors",
23
+ )
24
+ )
25
+
26
+ DF_COMPILE_EXTRA_FIELD = REGISTRY.register(
27
+ ErrorCode(
28
+ code="DF-COMPILE-EXTRA-FIELD",
29
+ domain="compile",
30
+ message_template=(
31
+ "Unknown field {field_path!r} in face YAML. "
32
+ "Remove it or check the schema for supported keys."
33
+ ),
34
+ doc_url=build_doc_url("DF-COMPILE-EXTRA-FIELD"),
35
+ docs_topic="compile.errors",
36
+ )
37
+ )
38
+
39
+ DF_COMPILE_SQL_LITERAL_NEWLINES = REGISTRY.register(
40
+ ErrorCode(
41
+ code="DF-COMPILE-SQL-LITERAL-NEWLINES",
42
+ domain="compile",
43
+ message_template=(
44
+ "Query {query_name!r} {field_label} contains literal \\n (backslash + n). "
45
+ "Use YAML block scalar `{field_label}: |` to write multiline SQL:\n\n"
46
+ " queries:\n"
47
+ " my_query:\n"
48
+ " {field_label}: |\n"
49
+ " SELECT\n"
50
+ " col\n"
51
+ " FROM t"
52
+ ),
53
+ doc_url=build_doc_url("DF-COMPILE-SQL-LITERAL-NEWLINES"),
54
+ docs_topic="compile.errors",
55
+ )
56
+ )
@@ -0,0 +1,177 @@
1
+ """DF-EXECUTE-* error codes for the execute layer.
2
+
3
+ Covers source-resolution failures in AdapterRegistry — the most common
4
+ class of error agents hit when misconfiguring sources in dataface.yml.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from dataface.core.errors.hints import (
10
+ suggest_close_column,
11
+ suggest_close_schema,
12
+ suggest_close_source,
13
+ suggest_close_table,
14
+ )
15
+ from dataface.core.errors.registry import REGISTRY, ErrorCode, build_doc_url
16
+
17
+ DF_EXECUTE_SOURCE_NOT_FOUND = REGISTRY.register(
18
+ ErrorCode(
19
+ code="DF-EXECUTE-SOURCE-NOT-FOUND",
20
+ domain="execute",
21
+ message_template=(
22
+ "Source {source!r} not found in configured adapters. "
23
+ "Available sources: {available}. "
24
+ "dft sources are declared under `sources:` in your dataface.yml — "
25
+ "the source name is not the dbt project name."
26
+ ),
27
+ doc_url=build_doc_url("DF-EXECUTE-SOURCE-NOT-FOUND"),
28
+ docs_topic="configuration.sources",
29
+ hint_generator=suggest_close_source,
30
+ )
31
+ )
32
+
33
+ DF_EXECUTE_SOURCE_NOT_FOUND_EMPTY = REGISTRY.register(
34
+ ErrorCode(
35
+ code="DF-EXECUTE-SOURCE-NOT-FOUND-EMPTY",
36
+ domain="execute",
37
+ message_template=(
38
+ "Source {source!r} not found. No source profiles are configured. "
39
+ "Declare sources under `sources:` in your dataface.yml."
40
+ ),
41
+ doc_url=build_doc_url("DF-EXECUTE-SOURCE-NOT-FOUND"),
42
+ docs_topic="configuration.sources",
43
+ )
44
+ )
45
+
46
+ DF_EXECUTE_NO_DEFAULT_SOURCE = REGISTRY.register(
47
+ ErrorCode(
48
+ code="DF-EXECUTE-NO-DEFAULT-SOURCE",
49
+ domain="execute",
50
+ message_template=(
51
+ "Source name required: no default DuckDB adapter is configured. "
52
+ "Pass an explicit source name from the `sources:` block in your dataface.yml."
53
+ ),
54
+ doc_url=build_doc_url("DF-EXECUTE-NO-DEFAULT-SOURCE"),
55
+ docs_topic="configuration.sources",
56
+ )
57
+ )
58
+
59
+ DF_EXECUTE_SOURCE_INLINE_FORBIDDEN = REGISTRY.register(
60
+ ErrorCode(
61
+ code="DF-EXECUTE-SOURCE-INLINE-FORBIDDEN",
62
+ domain="execute",
63
+ message_template=(
64
+ "Inline source definition at {yaml_path!r} is not allowed — "
65
+ "use a named source from the project allowlist. "
66
+ "Inline connection parameters: {offending_value!r}."
67
+ ),
68
+ doc_url=build_doc_url("DF-EXECUTE-SOURCE-INLINE-FORBIDDEN"),
69
+ docs_topic="configuration.sources",
70
+ )
71
+ )
72
+
73
+ DF_EXECUTE_SOURCE_CROSS_FILE_FORBIDDEN = REGISTRY.register(
74
+ ErrorCode(
75
+ code="DF-EXECUTE-SOURCE-CROSS-FILE-FORBIDDEN",
76
+ domain="execute",
77
+ message_template=(
78
+ "Cross-file source reference at {yaml_path!r} "
79
+ "(`#` anchor form) is not allowed: {offending_value!r}."
80
+ ),
81
+ doc_url=build_doc_url("DF-EXECUTE-SOURCE-CROSS-FILE-FORBIDDEN"),
82
+ docs_topic="configuration.sources",
83
+ )
84
+ )
85
+
86
+ DF_EXECUTE_SOURCE_INVALID_TYPE = REGISTRY.register(
87
+ ErrorCode(
88
+ code="DF-EXECUTE-SOURCE-INVALID-TYPE",
89
+ domain="execute",
90
+ message_template=(
91
+ "Unknown source type {offending_value!r}. Valid types: {available}."
92
+ ),
93
+ doc_url=build_doc_url("DF-EXECUTE-SOURCE-INVALID-TYPE"),
94
+ docs_topic="configuration.sources",
95
+ hint_generator=suggest_close_source,
96
+ )
97
+ )
98
+
99
+ DF_EXECUTE_SOURCE_MISSING_TYPE = REGISTRY.register(
100
+ ErrorCode(
101
+ code="DF-EXECUTE-SOURCE-MISSING-TYPE",
102
+ domain="execute",
103
+ message_template=(
104
+ "Source is missing the required `type` field: {offending_value!r}."
105
+ ),
106
+ doc_url=build_doc_url("DF-EXECUTE-SOURCE-MISSING-TYPE"),
107
+ docs_topic="configuration.sources",
108
+ )
109
+ )
110
+
111
+ DF_EXECUTE_MUTATING_SQL = REGISTRY.register(
112
+ ErrorCode(
113
+ code="DF-EXECUTE-MUTATING-SQL",
114
+ domain="execute",
115
+ message_template=(
116
+ "dft refuses to execute non-read-only SQL. "
117
+ "Statement: {rejected_node_kind}. Preview: {fragment_preview}."
118
+ ),
119
+ doc_url=build_doc_url("DF-EXECUTE-MUTATING-SQL"),
120
+ docs_topic="security.sql-guard",
121
+ )
122
+ )
123
+
124
+ DF_EXECUTE_UNPARSEABLE_SQL = REGISTRY.register(
125
+ ErrorCode(
126
+ code="DF-EXECUTE-UNPARSEABLE-SQL",
127
+ domain="execute",
128
+ message_template=(
129
+ "dft could not statically validate this SQL: {cause}. "
130
+ "The caller decides whether to defer to runtime or fail closed."
131
+ ),
132
+ doc_url=build_doc_url("DF-EXECUTE-UNPARSEABLE-SQL"),
133
+ docs_topic="security.sql-guard",
134
+ )
135
+ )
136
+
137
+ DF_EXECUTE_SCHEMA_NOT_FOUND = REGISTRY.register(
138
+ ErrorCode(
139
+ code="DF-EXECUTE-SCHEMA-NOT-FOUND",
140
+ domain="execute",
141
+ message_template=(
142
+ "Schema {schema!r} not found in source {source!r}. "
143
+ "Available schemas: {available}."
144
+ ),
145
+ doc_url=build_doc_url("DF-EXECUTE-SCHEMA-NOT-FOUND"),
146
+ docs_topic="configuration.sources",
147
+ hint_generator=suggest_close_schema,
148
+ )
149
+ )
150
+
151
+ DF_EXECUTE_TABLE_NOT_FOUND = REGISTRY.register(
152
+ ErrorCode(
153
+ code="DF-EXECUTE-TABLE-NOT-FOUND",
154
+ domain="execute",
155
+ message_template=(
156
+ "Table {table!r} not found in schema {schema!r} "
157
+ "(source {source!r}). Available tables: {available}."
158
+ ),
159
+ doc_url=build_doc_url("DF-EXECUTE-TABLE-NOT-FOUND"),
160
+ docs_topic="configuration.sources",
161
+ hint_generator=suggest_close_table,
162
+ )
163
+ )
164
+
165
+ DF_EXECUTE_COLUMN_NOT_FOUND = REGISTRY.register(
166
+ ErrorCode(
167
+ code="DF-EXECUTE-COLUMN-NOT-FOUND",
168
+ domain="execute",
169
+ message_template=(
170
+ "Column {col_name!r} not found in table {schema!r}.{table!r} "
171
+ "(source {source!r}). Available columns: {available}."
172
+ ),
173
+ doc_url=build_doc_url("DF-EXECUTE-COLUMN-NOT-FOUND"),
174
+ docs_topic="configuration.sources",
175
+ hint_generator=suggest_close_column,
176
+ )
177
+ )
@@ -0,0 +1,106 @@
1
+ """DF-RENDER-* error codes (wave 1 + bar duplicate rows).
2
+
3
+ The render-domain raise sites migrated in this PR cover the registry-shape-
4
+ proving subset:
5
+ - DF-RENDER-NO-LAYOUT, DF-RENDER-INTERNAL, DF-RENDER-FORMAT-UNSUPPORTED
6
+ (renderer.py)
7
+ - DF-RENDER-UNKNOWN-CHART-TYPE (profile.py x2, standard_renderer.py)
8
+ - DF-RENDER-KPI-MULTIROW (kpi.py)
9
+ - DF-RENDER-BAR-DUPLICATE-ROWS (validation.py)
10
+
11
+ Column-missing and type-mismatch codes are deferred to the consumer task
12
+ (unify-render-error-feedback), which already needs to touch every channel-
13
+ validation raise site to thread structured fields (chart_id, available
14
+ columns, field path) through. The consumer task adds those codes alongside
15
+ its raise-site migration so codes don't ship registered-but-unraised here.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ from dataface.core.errors.hints import suggest_close_chart_type
21
+ from dataface.core.errors.registry import REGISTRY, ErrorCode, build_doc_url
22
+
23
+ DF_RENDER_KPI_MULTIROW = REGISTRY.register(
24
+ ErrorCode(
25
+ code="DF-RENDER-KPI-MULTIROW",
26
+ domain="render",
27
+ message_template=(
28
+ "KPI chart {chart_id!r} expects exactly 1 row, got {row_count}. "
29
+ "Use a query that returns a single row (e.g. SELECT SUM(...) or LIMIT 1)."
30
+ ),
31
+ doc_url=build_doc_url("DF-RENDER-KPI-MULTIROW"),
32
+ docs_topic="charts.kpi",
33
+ )
34
+ )
35
+
36
+ DF_RENDER_BAR_DUPLICATE_ROWS = REGISTRY.register(
37
+ ErrorCode(
38
+ code="DF-RENDER-BAR-DUPLICATE-ROWS",
39
+ domain="render",
40
+ message_template=(
41
+ "{chart_type} chart {chart_id!r} requires pre-aggregated data "
42
+ "with at most one row per plotted key ({field_list}). "
43
+ "Found duplicate rows for {duplicate_preview}. "
44
+ "Aggregate in the query before rendering."
45
+ ),
46
+ doc_url=build_doc_url("DF-RENDER-BAR-DUPLICATE-ROWS"),
47
+ docs_topic="charts.bar",
48
+ )
49
+ )
50
+
51
+ DF_RENDER_UNKNOWN_CHART_TYPE = REGISTRY.register(
52
+ ErrorCode(
53
+ code="DF-RENDER-UNKNOWN-CHART-TYPE",
54
+ domain="render",
55
+ message_template="Unknown chart type: {chart_type!r}",
56
+ doc_url=build_doc_url("DF-RENDER-UNKNOWN-CHART-TYPE"),
57
+ docs_topic="charts.types",
58
+ hint_generator=suggest_close_chart_type,
59
+ )
60
+ )
61
+
62
+ DF_RENDER_FORMAT_UNSUPPORTED = REGISTRY.register(
63
+ ErrorCode(
64
+ code="DF-RENDER-FORMAT-UNSUPPORTED",
65
+ domain="render",
66
+ message_template="Unknown format: {format!r}",
67
+ doc_url=build_doc_url("DF-RENDER-FORMAT-UNSUPPORTED"),
68
+ docs_topic="render.formats",
69
+ )
70
+ )
71
+
72
+ DF_RENDER_NO_LAYOUT = REGISTRY.register(
73
+ ErrorCode(
74
+ code="DF-RENDER-NO-LAYOUT",
75
+ domain="render",
76
+ message_template=(
77
+ "Face defines charts ({charts}) but no layout — would render with no visible "
78
+ "charts. Add a `rows:`/`cols:`/`grid:`/`tabs:` block that references them."
79
+ ),
80
+ doc_url=build_doc_url("DF-RENDER-NO-LAYOUT"),
81
+ docs_topic="faces.layout",
82
+ )
83
+ )
84
+
85
+ DF_RENDER_INTERNAL = REGISTRY.register(
86
+ ErrorCode(
87
+ code="DF-RENDER-INTERNAL",
88
+ domain="render",
89
+ # Field is named `inner_message` rather than `cause` so it doesn't
90
+ # shadow StructuredError.cause (which is computed independently
91
+ # from __cause__).
92
+ message_template="Failed to render dataface: {inner_message}",
93
+ doc_url=build_doc_url("DF-RENDER-INTERNAL"),
94
+ docs_topic="render.errors",
95
+ )
96
+ )
97
+
98
+ DF_RENDER_INPUT_INVALID = REGISTRY.register(
99
+ ErrorCode(
100
+ code="DF-RENDER-INPUT-INVALID",
101
+ domain="render",
102
+ message_template="{message}",
103
+ doc_url=build_doc_url("DF-RENDER-INPUT-INVALID"),
104
+ docs_topic="render.errors",
105
+ )
106
+ )
@@ -0,0 +1,15 @@
1
+ """DF-UNKNOWN-* fallback code for legacy string-message constructors."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataface.core.errors.registry import REGISTRY, ErrorCode, build_doc_url
6
+
7
+ DF_UNKNOWN_INTERNAL = REGISTRY.register(
8
+ ErrorCode(
9
+ code="DF-UNKNOWN-INTERNAL",
10
+ domain="unknown",
11
+ message_template="{message}",
12
+ doc_url=build_doc_url("DF-UNKNOWN-INTERNAL"),
13
+ docs_topic="errors.unknown",
14
+ )
15
+ )
@@ -0,0 +1,74 @@
1
+ """Hint generators for error codes using difflib close-match suggestions.
2
+
3
+ Hint generators are kept generic over a comma-separated `available` string so
4
+ the registry stays a leaf module. The raise site is responsible for passing
5
+ the canonical list (e.g. CHART_TYPE_MAP keys for chart-type hints).
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import difflib
11
+
12
+
13
+ def _suggest_close_match(value: str, available: str, cutoff: float) -> str | None:
14
+ candidates = [c.strip() for c in available.split(",") if c.strip()]
15
+ if not candidates:
16
+ return None
17
+ matches = difflib.get_close_matches(value, candidates, n=1, cutoff=cutoff)
18
+ if matches:
19
+ return f"Did you mean {matches[0]!r}?"
20
+ return None
21
+
22
+
23
+ def suggest_close_chart_type(
24
+ chart_type: str,
25
+ available: str = "",
26
+ **_kwargs: object,
27
+ ) -> str | None:
28
+ """Return a 'Did you mean X?' hint for unknown chart types.
29
+
30
+ `available` is a comma-separated list passed by the raise site (typically
31
+ CHART_TYPE_MAP keys); the registry stays decoupled from the chart module.
32
+ Defaults to "" so a raise site that forgets `available=` still produces a
33
+ typed-and-coded error rather than crashing the hint pathway.
34
+ """
35
+ return _suggest_close_match(chart_type, available, cutoff=0.5)
36
+
37
+
38
+ def suggest_close_source(
39
+ source: str,
40
+ available: str = "",
41
+ **_kwargs: object,
42
+ ) -> str | None:
43
+ """Return a 'Did you mean X?' hint for unknown source names.
44
+
45
+ `available` is a comma-separated list of configured source names.
46
+ """
47
+ return _suggest_close_match(source, available, cutoff=0.5)
48
+
49
+
50
+ def suggest_close_schema(
51
+ schema: str,
52
+ available: str = "",
53
+ **_kwargs: object,
54
+ ) -> str | None:
55
+ """Return a 'Did you mean X?' hint for an unknown schema name."""
56
+ return _suggest_close_match(schema, available, cutoff=0.5)
57
+
58
+
59
+ def suggest_close_table(
60
+ table: str,
61
+ available: str = "",
62
+ **_kwargs: object,
63
+ ) -> str | None:
64
+ """Return a 'Did you mean X?' hint for an unknown table name."""
65
+ return _suggest_close_match(table, available, cutoff=0.5)
66
+
67
+
68
+ def suggest_close_column(
69
+ col_name: str,
70
+ available: str = "",
71
+ **_kwargs: object,
72
+ ) -> str | None:
73
+ """Return a 'Did you mean X?' hint for an unknown column name."""
74
+ return _suggest_close_match(col_name, available, cutoff=0.5)
@@ -0,0 +1,42 @@
1
+ """Global error registry: ErrorCode definitions and lookup."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable
6
+
7
+ from pydantic import BaseModel, ConfigDict
8
+
9
+
10
+ class ErrorCode(BaseModel):
11
+ model_config = ConfigDict(arbitrary_types_allowed=True)
12
+
13
+ code: str
14
+ domain: str
15
+ message_template: str
16
+ doc_url: str
17
+ docs_topic: str | None = None
18
+ hint_generator: Callable[..., str | None] | None = None
19
+
20
+
21
+ class _Registry:
22
+ def __init__(self) -> None:
23
+ self._by_code: dict[str, ErrorCode] = {}
24
+
25
+ def register(self, ec: ErrorCode) -> ErrorCode:
26
+ if ec.code in self._by_code:
27
+ raise ValueError(f"ErrorCode {ec.code!r} already registered")
28
+ self._by_code[ec.code] = ec
29
+ return ec
30
+
31
+ def get(self, code: str) -> ErrorCode:
32
+ return self._by_code[code]
33
+
34
+
35
+ REGISTRY = _Registry()
36
+
37
+ DOC_BASE_URL = "https://docs.dataface.dev/errors"
38
+
39
+
40
+ def build_doc_url(code: str) -> str:
41
+ """Build the canonical doc URL for an error code."""
42
+ return f"{DOC_BASE_URL}/{code}"