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,522 @@
1
+ """Compiled face types — output representation.
2
+
3
+ Stage: COMPILE (Output) / EXECUTE and RENDER (Input)
4
+ Purpose: Layout, Face, and resolved frozen dataclasses.
5
+ """
6
+
7
+ from dataclasses import dataclass
8
+ from typing import Any, Literal, TypeGuard
9
+
10
+ from pydantic import BaseModel, ConfigDict, Field
11
+
12
+ from dataface.core.compile.models.chart.compiled import Chart
13
+ from dataface.core.compile.models.face.authored import LayoutType
14
+ from dataface.core.compile.models.query.compiled import AnyQuery
15
+ from dataface.core.compile.models.style.authored import StylePatch
16
+ from dataface.core.compile.models.style.merged import MergedStyle
17
+ from dataface.core.compile.models.variable.authored import SingleRowBoolProbe, Variable
18
+
19
+ # ============================================================================
20
+ # TYPE ALIASES
21
+ # ============================================================================
22
+
23
+
24
+ # Resolved variable values (not Variable objects)
25
+ # Used for Jinja template resolution - values can be any Python type
26
+ VariableValues = dict[str, Any]
27
+
28
+
29
+ # ============================================================================
30
+ # UNIFIED LAYOUT
31
+ # ============================================================================
32
+
33
+
34
+ class LayoutItem(BaseModel):
35
+ """A single item in a layout.
36
+
37
+ Represents either a chart or a nested face in the layout.
38
+ The unified structure makes iteration simple:
39
+
40
+ for item in layout.items:
41
+ if item.type == "chart":
42
+ render_chart(item.chart)
43
+ else:
44
+ render_face(item.face)
45
+
46
+ Attributes:
47
+ type: Either "chart" or "face"
48
+ chart: Chart if type == "chart"
49
+ face: Face if type == "face"
50
+
51
+ Grid-specific (optional):
52
+ row, col: Grid position (0-based)
53
+ row_span, col_span: Grid span (in grid units)
54
+
55
+ Calculated dimensions (set by sizing module):
56
+ width_fraction: What fraction of parent width (0.0-1.0)
57
+ width: Pixel width
58
+ height: Pixel height
59
+ x: X position in pixels
60
+ y: Y position in pixels
61
+ """
62
+
63
+ type: Literal["chart", "face"] = Field(description="Item type: 'chart' or 'face'.")
64
+ chart: "Chart | None" = Field(
65
+ default=None, description="Compiled chart, present when type == 'chart'."
66
+ )
67
+ face: "Face | None" = Field(
68
+ default=None, description="Compiled face, present when type == 'face'."
69
+ )
70
+
71
+ # Grid positioning (optional) - 0-based, in grid units
72
+ row: int | None = Field(
73
+ default=None, description="Grid row position (0-based; grid layouts only)."
74
+ )
75
+ col: int | None = Field(
76
+ default=None, description="Grid column position (0-based; grid layouts only)."
77
+ )
78
+ row_span: int | None = Field(
79
+ default=None, description="Number of rows to span (grid layouts only)."
80
+ )
81
+ col_span: int | None = Field(
82
+ default=None, description="Number of columns to span (grid layouts only)."
83
+ )
84
+
85
+ # Authored dimensions (from YAML, e.g. "30%" or "200px").
86
+ # user_width: width fraction/px authored on a cols item.
87
+ # layout_height: slot height authored on a rows/wrapper item; resolved to
88
+ # LayoutItem.height (float px) by the sizing pass.
89
+ user_width: str | None = Field(
90
+ default=None,
91
+ description="User-specified width from YAML (e.g., '30%' or '200px').",
92
+ )
93
+ layout_height: str | None = Field(
94
+ default=None,
95
+ description="Layout-wrapper height from YAML (e.g., '30%' or '200px'); "
96
+ "drives slot allocation in the sizing pass.",
97
+ )
98
+
99
+ # Calculated dimensions (set by sizing)
100
+ width_fraction: float = Field(
101
+ default=1.0, description="Fraction of parent width (0.0–1.0); set by sizing."
102
+ )
103
+ width: float = Field(default=0.0, description="Pixel width; set by sizing.")
104
+ height: float = Field(default=0.0, description="Pixel height; set by sizing.")
105
+ x: float = Field(default=0.0, description="X position in pixels; set by sizing.")
106
+ y: float = Field(default=0.0, description="Y position in pixels; set by sizing.")
107
+
108
+ # Layout visibility — evaluated at render time; None means always visible.
109
+ # Accepts: bool (static), str (variable name or Jinja expression),
110
+ # or SingleRowBoolProbe (single-row boolean probe).
111
+ visible: bool | str | SingleRowBoolProbe | None = Field(
112
+ default=None,
113
+ description=(
114
+ "Visibility condition for this layout item. "
115
+ "None/True: always shown. False: always hidden. "
116
+ "str: variable name (truthy check) or Jinja boolean expression. "
117
+ "{query, column}: single-row boolean probe (same contract as variables.disabled)."
118
+ ),
119
+ )
120
+
121
+ # Details (collapsible section) metadata
122
+ details_variable: str | None = Field(
123
+ default=None,
124
+ description="Variable name controlling expanded state (collapsible sections).",
125
+ )
126
+ details_summary: str | None = Field(
127
+ default=None,
128
+ description="Summary text shown when collapsed (collapsible sections).",
129
+ )
130
+ details_expanded_summary: str | None = Field(
131
+ default=None,
132
+ description="Summary text shown when expanded (collapsible sections).",
133
+ )
134
+ description: str | None = Field(
135
+ default=None, description="Optional metadata for AI/context tooltips."
136
+ )
137
+
138
+ # Render-ready sizing (calculated during normalization/sizing)
139
+ calculated_width: float | None = Field(
140
+ default=None,
141
+ description="Calculated pixel width based on 1200px default; set by sizing.",
142
+ )
143
+ calculated_height: float | None = Field(
144
+ default=None, description="Calculated pixel height; set by sizing."
145
+ )
146
+ aspect_ratio: float | None = Field(
147
+ default=None,
148
+ description="Width/height ratio for responsive rendering; set by sizing.",
149
+ )
150
+
151
+ model_config = ConfigDict(extra="forbid")
152
+
153
+
154
+ class Layout(BaseModel):
155
+ """Unified layout structure.
156
+
157
+ Stage: COMPILE output
158
+
159
+ All layout types (rows, cols, grid, tabs) are represented with this
160
+ unified structure, making downstream processing simpler.
161
+
162
+ The type field indicates the original layout type for rendering
163
+ (e.g., rows render vertically, cols horizontally).
164
+
165
+ Attributes:
166
+ type: Layout type (rows, cols, grid, tabs)
167
+ items: List of LayoutItem (charts or nested boards)
168
+
169
+ Grid-specific:
170
+ columns: Number of columns in grid
171
+ gap: Gap between items
172
+
173
+ Tabs-specific:
174
+ tab_titles: Titles for each tab
175
+ default_tab: Index of default active tab
176
+
177
+ Calculated:
178
+ width, height: Total dimensions (set by sizing)
179
+ content_width, content_height: Inner dimensions after padding/margin
180
+
181
+ Example:
182
+ >>> layout = compiled_board.layout
183
+ >>> print(f"Layout type: {layout.type}")
184
+ >>> for item in layout.items:
185
+ ... print(f" - {item.type}: {item.chart.id if item.chart else item.face.id}")
186
+ """
187
+
188
+ type: LayoutType = Field(description="Layout type (rows, cols, grid, tabs).")
189
+ items: list[LayoutItem] = Field(
190
+ default_factory=list,
191
+ description="List of layout items (charts or nested faces).",
192
+ )
193
+
194
+ # Grid-specific
195
+ columns: int | None = Field(
196
+ default=None, description="Number of columns for grid layouts."
197
+ )
198
+ gap: str | None = Field(
199
+ default=None, description="Gap between items for grid layouts (CSS value)."
200
+ )
201
+ row_height: str | None = Field(
202
+ default=None, description="Default row height for grid layouts (CSS value)."
203
+ )
204
+
205
+ # Tabs-specific
206
+ tab_titles: list[str] | None = Field(
207
+ default=None, description="Titles for each tab (tabs layout only)."
208
+ )
209
+ tab_slugs: list[str] | None = Field(
210
+ default=None,
211
+ description="Slugified titles used as URL values (tabs layout only).",
212
+ )
213
+ tab_variable: str | None = Field(
214
+ default=None,
215
+ description="Variable name controlling the active tab (tabs layout only).",
216
+ )
217
+ default_tab: int | None = Field(
218
+ default=None, description="Index of the default active tab (tabs layout only)."
219
+ )
220
+ tab_position: Literal["top", "left"] | None = Field(
221
+ default=None,
222
+ description="Tab bar position: 'top' or 'left' (tabs layout only).",
223
+ )
224
+
225
+ # Calculated dimensions (outer dimensions including padding/margin)
226
+ width: float = Field(
227
+ default=0.0, description="Total calculated pixel width; set by sizing."
228
+ )
229
+ height: float = Field(
230
+ default=0.0, description="Total calculated pixel height; set by sizing."
231
+ )
232
+
233
+ # Calculated content dimensions (inner dimensions after padding/margin)
234
+ content_width: float = Field(
235
+ default=0.0,
236
+ description="Inner pixel width after padding/margin; set by sizing.",
237
+ )
238
+ content_height: float = Field(
239
+ default=0.0,
240
+ description="Inner pixel height after padding/margin; set by sizing.",
241
+ )
242
+
243
+ model_config = ConfigDict(extra="forbid")
244
+
245
+
246
+ # ============================================================================
247
+ # COMPILED FACE
248
+ # ============================================================================
249
+
250
+
251
+ class Face(BaseModel):
252
+ """Compiled face ready for execution and rendering.
253
+
254
+ Stage: COMPILE output / EXECUTE and RENDER input
255
+
256
+ This is the main output of compilation. It contains:
257
+ - Resolved definitions (variables, queries, charts)
258
+ - Unified layout structure
259
+ - Calculated dimensions
260
+ - Applied defaults and metadata
261
+
262
+ Guarantees:
263
+ - id: Always set
264
+ - title: Always set (empty string if not provided)
265
+ - layout: Always present (unified structure)
266
+ - All charts resolved to Chart objects
267
+ - All queries resolved to query objects
268
+
269
+ Attributes:
270
+ id: Unique identifier for this face
271
+ title: Display title
272
+ description: Description text
273
+ variables: Variable definitions (Dict[str, Variable])
274
+ queries: Compiled queries (Dict[str, AnyQuery])
275
+ charts: Compiled charts (Dict[str, Chart])
276
+ layout: Unified layout structure
277
+ variable_defaults: Resolved default values for variables
278
+ style: Face styling
279
+ Example:
280
+ >>> result = compile(yaml_content)
281
+ >>> face = result.face
282
+ >>> print(face.id) # "sales-dataface"
283
+ >>> print(face.title) # "Sales Dataface"
284
+ >>> print(face.layout.type) # "rows"
285
+ >>> for item in face.layout.items:
286
+ ... chart = item.chart
287
+ ... print(f"Chart: {chart.id}, Query: {chart.query_name}")
288
+ """
289
+
290
+ id: str = Field(description="Unique identifier for this face.")
291
+ title: str = Field(
292
+ default="", description="Display title. Empty string if not provided."
293
+ )
294
+ description: str = Field(
295
+ default="", description="Description text. Empty string if not provided."
296
+ )
297
+ tags: list[str] = Field(
298
+ default_factory=list, description="Tags for categorization and search."
299
+ )
300
+ text: str = Field(
301
+ default="", description="Markdown text content for text-only faces."
302
+ )
303
+
304
+ # Definitions
305
+ variables: dict[str, Variable] = Field(
306
+ default_factory=dict,
307
+ description="Local variable definitions for UI controls.",
308
+ )
309
+ queries: dict[str, AnyQuery] = Field(
310
+ default_factory=dict,
311
+ description="Compiled query objects by name.",
312
+ )
313
+ charts: dict[str, Chart] = Field(
314
+ default_factory=dict,
315
+ description="Compiled chart objects by name.",
316
+ )
317
+
318
+ # Source configurations (for resolving named source references)
319
+ sources: dict[str, dict[str, Any]] = Field(
320
+ default_factory=dict,
321
+ description="Source configurations for resolving named source references (e.g., {'profiles': {'type': 'duckdb'}}).",
322
+ )
323
+
324
+ # Unified layout
325
+ layout: Layout = Field(
326
+ description="Unified layout structure containing all charts and nested faces.",
327
+ )
328
+
329
+ # Pre-computed variable defaults from variable_registry
330
+ variable_defaults: VariableValues = Field(
331
+ default_factory=dict,
332
+ description="Pre-computed default values for all variables; only set on root board.",
333
+ )
334
+
335
+ # Global variable registry (only set on root face, contains all variables from entire tree)
336
+ variable_registry: dict[str, Variable] | None = Field(
337
+ default=None,
338
+ description="All variables from the entire face tree; only set on root face.",
339
+ )
340
+
341
+ # Vega-Lite theme (e.g., "dark", "stark", "carbong100")
342
+ theme: str | None = Field(
343
+ default=None,
344
+ description="Theme name (e.g., 'editorial', 'cream', 'stark'). Inherited by nested faces.",
345
+ )
346
+ # Authored style patch — None when the face has no style: block.
347
+ authored_style: StylePatch | None = Field(
348
+ default=None,
349
+ description="Authored face style patch (background, padding, border, etc.).",
350
+ )
351
+ # Board-scoped resolved style — authoritative, produced at compile time.
352
+ resolved_style: MergedStyle = Field(
353
+ description="Board-scoped resolved style; produced at compile time. Render reads this instead of re-merging patches.",
354
+ )
355
+
356
+ @property
357
+ def visible_variables(self) -> "dict[str, Variable]":
358
+ """Return variables with visible=True (available for UI controls)."""
359
+ return {k: v for k, v in self.variables.items() if v.visible}
360
+
361
+ # Card gap toggle (from board YAML)
362
+ card_gap: bool = Field(
363
+ default=False,
364
+ description="When True, adds gap between cards and adjusts page margin.",
365
+ )
366
+
367
+ # Semantic heading level: count of titled ancestors (not structural nesting depth).
368
+ # A titled root face is level=1; a titled face nested under a bare wrapper is still
369
+ # level=2 (only one titled ancestor). Bare wrappers without a title do not advance
370
+ # the counter. Required — every Face is constructed by the normalizer (or by a
371
+ # call site that derives level from parent_level), so a missing value is a bug,
372
+ # not a default. Can be overridden via face.style.title.level.
373
+ level: int = Field(
374
+ description=(
375
+ "Heading level for this face's title (matches H{level}; root face = 1 when "
376
+ "titled, 0 when untitled). Computed from titled-ancestor count; can be "
377
+ "overridden via face.style.title.level."
378
+ ),
379
+ )
380
+
381
+ # Compilation metadata
382
+ meta: dict[str, Any] = Field(
383
+ default_factory=dict,
384
+ description="Compilation metadata (warnings, diagnostics, source info) populated by the normalizer.",
385
+ )
386
+
387
+ model_config = ConfigDict(extra="forbid")
388
+
389
+
390
+ # Resolve forward references (Chart and Face are now in scope)
391
+ LayoutItem.model_rebuild()
392
+ Layout.model_rebuild()
393
+ Face.model_rebuild()
394
+
395
+
396
+ # ============================================================================
397
+ # RESOLVED TYPES (produced by resolve_face() — no lookups needed downstream)
398
+ # ============================================================================
399
+
400
+
401
+ @dataclass(frozen=True)
402
+ class ResolvedLayoutItem:
403
+ """Layout item with static dimension estimates — no executor, no data access."""
404
+
405
+ type: str # "chart" | "face" | "text" | "markdown" | ...
406
+ chart: Chart | None
407
+ # nested face is a forward reference — resolved recursively
408
+ face: "MergedFace | None"
409
+ x: float
410
+ y: float
411
+ width: float
412
+ height: float
413
+ # Carry-through optional fields from LayoutItem
414
+ details_variable: str | None = None
415
+ details_summary: str | None = None
416
+ details_expanded_summary: str | None = None
417
+ description: str | None = None
418
+ visible: "bool | str | SingleRowBoolProbe | None" = None
419
+
420
+
421
+ @dataclass(frozen=True)
422
+ class ResolvedLayout:
423
+ """Layout tree with static dimension estimates.
424
+
425
+ Dimensions come from compile-time static sizing. The render phase applies
426
+ data-aware sizing (executor-driven row counts and Vega render-first heights)
427
+ on top of these estimates.
428
+ """
429
+
430
+ type: str # "rows" | "cols" | "grid" | "tabs"
431
+ items: tuple["ResolvedLayoutItem", ...]
432
+ width: float
433
+ height: float
434
+ content_width: float
435
+ content_height: float
436
+ # Grid-specific
437
+ columns: int | None = None
438
+ gap: float = 0.0
439
+ # Tabs-specific
440
+ tab_titles: tuple[str, ...] = ()
441
+ tab_slugs: tuple[str, ...] = ()
442
+ tab_variable: str | None = None
443
+ default_tab: int = 0
444
+ tab_position: str | None = None
445
+
446
+
447
+ @dataclass(frozen=True)
448
+ class MergedFace:
449
+ """Pure resolved face — deterministic transform from Face + config.
450
+
451
+ Produced by resolve_face() — never constructed directly.
452
+ No executor, no data access, no render-phase artifacts.
453
+ The render layer performs data-aware sizing on top of these static estimates.
454
+
455
+ Board-level config fields (page_padding, card_padding, card_gap) are None
456
+ for nested faces — those values only apply to the root renderable face.
457
+ """
458
+
459
+ # Identity
460
+ id: str
461
+ title: str
462
+ description: str
463
+ tags: tuple[str, ...]
464
+ text: str
465
+ level: int # Heading level (1 = root face, matches H{level})
466
+
467
+ # Style (theme + face overrides already merged).
468
+ style: MergedStyle
469
+
470
+ # Baked board config constants — None for nested faces (root only).
471
+ # config.style.board.margin / card_padding / card_gap
472
+ page_padding: float | None
473
+ card_padding: float | None
474
+ card_gap: float | None
475
+
476
+ # Static dimension estimates (from compile-time layout tree)
477
+ # Render phase applies data-aware sizing on top.
478
+ width: float
479
+ height: float
480
+
481
+ # Resolved Vega-Lite config (pure: theme name → compiled VL config dict)
482
+ vega_config: dict[str, Any]
483
+
484
+ # Layout tree with static item estimates
485
+ layout: ResolvedLayout
486
+
487
+ # Data (unchanged from Face)
488
+ charts: dict[str, Chart]
489
+ variables: dict[str, Variable]
490
+ queries: dict[str, AnyQuery]
491
+ variable_defaults: VariableValues
492
+
493
+ @property
494
+ def visible_variables(self) -> "dict[str, Variable]":
495
+ """Variables with visible=True — available for UI controls."""
496
+ return {k: v for k, v in self.variables.items() if v.visible}
497
+
498
+
499
+ # ============================================================================
500
+ # TYPE GUARD
501
+ # ============================================================================
502
+
503
+
504
+ def is_face(item: Any) -> TypeGuard[Face]:
505
+ """Type guard for Face.
506
+
507
+ After compilation, all faces are Face objects.
508
+ Use this for type narrowing in conditional blocks.
509
+
510
+ Args:
511
+ item: Item to check
512
+
513
+ Returns:
514
+ True if item is a Face
515
+
516
+ Example:
517
+ >>> for item in layout.items:
518
+ ... if item.face and is_face(item.face):
519
+ ... # Type checker knows item.face is Face
520
+ ... print(item.face.layout.type)
521
+ """
522
+ return isinstance(item, Face)