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.
- d3_format/__init__.py +14 -0
- d3_format/errors.py +19 -0
- d3_format/format.py +551 -0
- d3_format/spec.py +159 -0
- dataface/DATAFACE_SYNTAX.md +1135 -0
- dataface/__init__.py +93 -0
- dataface/_docs_site.py +20 -0
- dataface/_install_hint.py +26 -0
- dataface/agent_api/__init__.py +79 -0
- dataface/agent_api/_init_templates/__init__.py +0 -0
- dataface/agent_api/_init_templates/agents_dft_snippet.md +26 -0
- dataface/agent_api/_init_templates/dataface.yml +15 -0
- dataface/agent_api/_init_templates/faces-dataface.yml +144 -0
- dataface/agent_api/_init_templates/index.md +24 -0
- dataface/agent_api/_paths.py +118 -0
- dataface/agent_api/_project_agents_md.py +43 -0
- dataface/agent_api/_session_store.py +486 -0
- dataface/agent_api/_state.py +28 -0
- dataface/agent_api/chat.py +221 -0
- dataface/agent_api/dashboards.py +257 -0
- dataface/agent_api/describe.py +366 -0
- dataface/agent_api/describe_query.py +120 -0
- dataface/agent_api/docs/__init__.py +25 -0
- dataface/agent_api/docs/_loader.py +292 -0
- dataface/agent_api/docs/yaml-reference.md +2757 -0
- dataface/agent_api/file_refs.py +118 -0
- dataface/agent_api/init.py +126 -0
- dataface/agent_api/inspect.py +128 -0
- dataface/agent_api/mcp_install.py +170 -0
- dataface/agent_api/query.py +274 -0
- dataface/agent_api/schema.py +658 -0
- dataface/agent_api/schema_search.py +284 -0
- dataface/agent_api/search.py +270 -0
- dataface/agent_api/skill_install.py +141 -0
- dataface/agent_api/skill_render.py +90 -0
- dataface/agent_api/skills.py +293 -0
- dataface/agent_api/surface_aliases.yaml +128 -0
- dataface/agent_api/validate.py +175 -0
- dataface/agent_api/validate_query.py +84 -0
- dataface/ai/__init__.py +39 -0
- dataface/ai/agent.py +139 -0
- dataface/ai/context.py +45 -0
- dataface/ai/events.py +62 -0
- dataface/ai/external_mcp.py +610 -0
- dataface/ai/generate_sql.py +96 -0
- dataface/ai/llm.py +403 -0
- dataface/ai/mcp/__init__.py +51 -0
- dataface/ai/mcp/server.py +289 -0
- dataface/ai/memories.py +85 -0
- dataface/ai/prompts.py +177 -0
- dataface/ai/schema_context.py +138 -0
- dataface/ai/skills/before-after-comparison/SKILL.md +102 -0
- dataface/ai/skills/before-after-comparison/examples/before-after-comparison.yml +24 -0
- dataface/ai/skills/dashboard-build/SKILL.md +212 -0
- dataface/ai/skills/dashboard-build/examples/_smoke.yml +15 -0
- dataface/ai/skills/dashboard-design/SKILL.md +182 -0
- dataface/ai/skills/dashboard-review/SKILL.md +113 -0
- dataface/ai/skills/dashboard-structural-review/SKILL.md +173 -0
- dataface/ai/skills/dashboard-visual-review/SKILL.md +139 -0
- dataface/ai/skills/dataface-mcp-setup/SKILL.md +177 -0
- dataface/ai/skills/dataface-troubleshooting/SKILL.md +225 -0
- dataface/ai/skills/drill-down-link/SKILL.md +112 -0
- dataface/ai/skills/drill-down-link/examples/drill-down-link.yml +27 -0
- dataface/ai/skills/faceted-small-multiples/SKILL.md +116 -0
- dataface/ai/skills/faceted-small-multiples/examples/faceted-small-multiples.yml +33 -0
- dataface/ai/skills/filter-bar-with-variables/SKILL.md +105 -0
- dataface/ai/skills/filter-bar-with-variables/examples/filter-bar-with-variables.yml +49 -0
- dataface/ai/skills/kpi-row/SKILL.md +101 -0
- dataface/ai/skills/kpi-row/examples/kpi-row.yml +55 -0
- dataface/ai/skills/report-design/SKILL.md +184 -0
- dataface/ai/skills/single-metric-bignum/SKILL.md +90 -0
- dataface/ai/skills/single-metric-bignum/examples/single-metric-bignum.yml +27 -0
- dataface/ai/skills/table-heavy-ops-dashboard/SKILL.md +114 -0
- dataface/ai/skills/table-heavy-ops-dashboard/examples/table-heavy-ops-dashboard.yml +48 -0
- dataface/ai/skills/time-series-trend/SKILL.md +93 -0
- dataface/ai/skills/time-series-trend/examples/time-series-trend.yml +26 -0
- dataface/ai/skills/top-n-with-detail/SKILL.md +98 -0
- dataface/ai/skills/top-n-with-detail/examples/top-n-with-detail.yml +45 -0
- dataface/ai/skills/two-by-two-grid-overview/SKILL.md +78 -0
- dataface/ai/skills/two-by-two-grid-overview/examples/two-by-two-grid-overview.yml +64 -0
- dataface/ai/tool_schemas.py +132 -0
- dataface/ai/tools/__init__.py +312 -0
- dataface/ai/yaml_utils.py +57 -0
- dataface/cli/__init__.py +3 -0
- dataface/cli/_console.py +48 -0
- dataface/cli/_error_format.py +83 -0
- dataface/cli/_extras.py +190 -0
- dataface/cli/_json_output.py +8 -0
- dataface/cli/_parsing.py +17 -0
- dataface/cli/_version_info.py +56 -0
- dataface/cli/commands/__init__.py +3 -0
- dataface/cli/commands/_agent_input.py +205 -0
- dataface/cli/commands/_agent_server.py +115 -0
- dataface/cli/commands/chat.py +645 -0
- dataface/cli/commands/describe.py +107 -0
- dataface/cli/commands/docs.py +131 -0
- dataface/cli/commands/extension.py +179 -0
- dataface/cli/commands/init.py +240 -0
- dataface/cli/commands/inspect.py +94 -0
- dataface/cli/commands/mcp_init.py +167 -0
- dataface/cli/commands/query.py +386 -0
- dataface/cli/commands/render.py +291 -0
- dataface/cli/commands/schema.py +411 -0
- dataface/cli/commands/search.py +49 -0
- dataface/cli/commands/serve.py +114 -0
- dataface/cli/commands/skills.py +133 -0
- dataface/cli/commands/skills_init.py +161 -0
- dataface/cli/commands/validate.py +63 -0
- dataface/cli/main.py +1501 -0
- dataface/core/__init__.py +75 -0
- dataface/core/compile/__init__.py +244 -0
- dataface/core/compile/_jinja_helpers.py +78 -0
- dataface/core/compile/channel.py +222 -0
- dataface/core/compile/chart_focus.py +101 -0
- dataface/core/compile/chart_resolved.py +169 -0
- dataface/core/compile/chart_type_detection.py +489 -0
- dataface/core/compile/chart_update.py +261 -0
- dataface/core/compile/colors.py +64 -0
- dataface/core/compile/compiler.py +904 -0
- dataface/core/compile/config.py +823 -0
- dataface/core/compile/custom_chart_types.py +208 -0
- dataface/core/compile/data_table_attachment.py +1287 -0
- dataface/core/compile/detect.py +110 -0
- dataface/core/compile/errors.py +302 -0
- dataface/core/compile/filter_injection.py +319 -0
- dataface/core/compile/introspection.py +527 -0
- dataface/core/compile/jinja.py +511 -0
- dataface/core/compile/labels_env.py +52 -0
- dataface/core/compile/markdown.py +154 -0
- dataface/core/compile/meta.py +388 -0
- dataface/core/compile/models/__init__.py +0 -0
- dataface/core/compile/models/chart/__init__.py +0 -0
- dataface/core/compile/models/chart/authored.py +2137 -0
- dataface/core/compile/models/chart/compiled.py +398 -0
- dataface/core/compile/models/config.py +347 -0
- dataface/core/compile/models/face/__init__.py +0 -0
- dataface/core/compile/models/face/authored.py +659 -0
- dataface/core/compile/models/face/compiled.py +522 -0
- dataface/core/compile/models/factories.py +201 -0
- dataface/core/compile/models/markers.py +40 -0
- dataface/core/compile/models/palette.py +36 -0
- dataface/core/compile/models/primitives.py +415 -0
- dataface/core/compile/models/query/__init__.py +0 -0
- dataface/core/compile/models/query/authored.py +246 -0
- dataface/core/compile/models/query/compiled.py +710 -0
- dataface/core/compile/models/refs.py +137 -0
- dataface/core/compile/models/source.py +611 -0
- dataface/core/compile/models/style/__init__.py +0 -0
- dataface/core/compile/models/style/authored.py +481 -0
- dataface/core/compile/models/style/compiled.py +3399 -0
- dataface/core/compile/models/style/merged.py +1682 -0
- dataface/core/compile/models/theme.py +362 -0
- dataface/core/compile/models/variable/__init__.py +0 -0
- dataface/core/compile/models/variable/authored.py +254 -0
- dataface/core/compile/models/vega_lite/__init__.py +0 -0
- dataface/core/compile/models/vega_lite/config.py +510 -0
- dataface/core/compile/models/vega_lite/contracts.py +171 -0
- dataface/core/compile/normalize_charts.py +494 -0
- dataface/core/compile/normalize_layout.py +1000 -0
- dataface/core/compile/normalize_queries.py +297 -0
- dataface/core/compile/normalize_variables.py +489 -0
- dataface/core/compile/normalizer.py +543 -0
- dataface/core/compile/palette.py +1100 -0
- dataface/core/compile/parameterized.py +658 -0
- dataface/core/compile/parser.py +228 -0
- dataface/core/compile/schema.py +20 -0
- dataface/core/compile/schema_renderers/__init__.py +0 -0
- dataface/core/compile/schema_renderers/json_schema.py +163 -0
- dataface/core/compile/schema_renderers/prompt.py +152 -0
- dataface/core/compile/schema_renderers/vscode_schema.py +301 -0
- dataface/core/compile/sizing.py +2126 -0
- dataface/core/compile/sources.py +518 -0
- dataface/core/compile/sql_authoring_lint.py +56 -0
- dataface/core/compile/style_cascade.py +471 -0
- dataface/core/compile/typography.py +299 -0
- dataface/core/compile/validator.py +301 -0
- dataface/core/compile/variables.py +53 -0
- dataface/core/compile/vega_config.py +98 -0
- dataface/core/compile/vega_lite/__init__.py +6 -0
- dataface/core/compile/vega_lite/validation.py +95 -0
- dataface/core/compile/yaml_error_formatter.py +838 -0
- dataface/core/connections.py +38 -0
- dataface/core/dashboard.py +358 -0
- dataface/core/defaults/default_config.yml +101 -0
- dataface/core/defaults/palettes/categorical/category-10-dark.yml +32 -0
- dataface/core/defaults/palettes/categorical/category-10-light.yml +43 -0
- dataface/core/defaults/palettes/categorical/category-10.yml +31 -0
- dataface/core/defaults/palettes/categorical/category-6-tonal-blue.yml +22 -0
- dataface/core/defaults/palettes/categorical/category-6-tonal-brown.yml +29 -0
- dataface/core/defaults/palettes/categorical/category-6-tonal-green.yml +20 -0
- dataface/core/defaults/palettes/categorical/category-6-tonal-orange.yml +21 -0
- dataface/core/defaults/palettes/categorical/category-6-tonal-purple.yml +20 -0
- dataface/core/defaults/palettes/categorical/editorial-10-dark.yml +32 -0
- dataface/core/defaults/palettes/categorical/editorial-10.yml +40 -0
- dataface/core/defaults/palettes/categorical/hero-6.yml +17 -0
- dataface/core/defaults/palettes/categorical/single-blue.yml +11 -0
- dataface/core/defaults/palettes/categorical/tableau.yml +20 -0
- dataface/core/defaults/palettes/data/xkcd_colors.json +3803 -0
- dataface/core/defaults/palettes/diverging/blue-red.yml +25 -0
- dataface/core/defaults/palettes/diverging/coolwarm.yml +24 -0
- dataface/core/defaults/palettes/diverging/crimson-green.yml +23 -0
- dataface/core/defaults/palettes/diverging/orange-teal.yml +23 -0
- dataface/core/defaults/palettes/diverging/sunset.yml +24 -0
- dataface/core/defaults/palettes/scaffold/dft-creams.yml +38 -0
- dataface/core/defaults/palettes/scaffold/dft-grays.yml +53 -0
- dataface/core/defaults/palettes/sequential/amber.yml +22 -0
- dataface/core/defaults/palettes/sequential/blue.yml +22 -0
- dataface/core/defaults/palettes/sequential/brown.yml +22 -0
- dataface/core/defaults/palettes/sequential/gray.yml +22 -0
- dataface/core/defaults/palettes/sequential/green.yml +22 -0
- dataface/core/defaults/palettes/sequential/purple.yml +22 -0
- dataface/core/defaults/palettes/sequential/rust.yml +22 -0
- dataface/core/defaults/palettes/sequential/teal.yml +22 -0
- dataface/core/defaults/palettes/tone/negative.yml +32 -0
- dataface/core/defaults/palettes/tone/positive.yml +22 -0
- dataface/core/defaults/palettes/tone/warning.yml +22 -0
- dataface/core/defaults/themes/_base.yaml +786 -0
- dataface/core/defaults/themes/bi.yaml +16 -0
- dataface/core/defaults/themes/carbong100.yaml +41 -0
- dataface/core/defaults/themes/cream.yaml +122 -0
- dataface/core/defaults/themes/dark.yaml +40 -0
- dataface/core/defaults/themes/diagnostics-title-angle-extreme.yaml +9 -0
- dataface/core/defaults/themes/diagnostics-title-baseline-extreme.yaml +9 -0
- dataface/core/defaults/themes/diagnostics-title-baseline.yaml +24 -0
- dataface/core/defaults/themes/diagnostics-title-center.yaml +8 -0
- dataface/core/defaults/themes/diagnostics-title-color-extreme.yaml +24 -0
- dataface/core/defaults/themes/diagnostics-title-font-extreme.yaml +25 -0
- dataface/core/defaults/themes/diagnostics-title-left.yaml +8 -0
- dataface/core/defaults/themes/diagnostics-title-offset-extreme.yaml +9 -0
- dataface/core/defaults/themes/diagnostics-title-size-extreme.yaml +24 -0
- dataface/core/defaults/themes/diagnostics-title-weight-extreme.yaml +24 -0
- dataface/core/defaults/themes/editorial.yaml +147 -0
- dataface/core/defaults/themes/light.yaml +30 -0
- dataface/core/defaults/themes/looker.yaml +17 -0
- dataface/core/defaults/themes/stark.yaml +134 -0
- dataface/core/errors/__init__.py +67 -0
- dataface/core/errors/codes_compile.py +56 -0
- dataface/core/errors/codes_execute.py +177 -0
- dataface/core/errors/codes_render.py +106 -0
- dataface/core/errors/codes_unknown.py +15 -0
- dataface/core/errors/hints.py +74 -0
- dataface/core/errors/registry.py +42 -0
- dataface/core/errors/structured.py +92 -0
- dataface/core/execute/__init__.py +91 -0
- dataface/core/execute/adapters/__init__.py +49 -0
- dataface/core/execute/adapters/adapter_registry.py +400 -0
- dataface/core/execute/adapters/base.py +245 -0
- dataface/core/execute/adapters/csv_adapter.py +239 -0
- dataface/core/execute/adapters/dbt_adapter.py +283 -0
- dataface/core/execute/adapters/dbt_adapter_factory.py +212 -0
- dataface/core/execute/adapters/dbt_macro_loader.py +95 -0
- dataface/core/execute/adapters/dbt_utils.py +150 -0
- dataface/core/execute/adapters/http_adapter.py +224 -0
- dataface/core/execute/adapters/metricflow_adapter.py +94 -0
- dataface/core/execute/adapters/schema_resolver_adapter.py +144 -0
- dataface/core/execute/adapters/sql_adapter.py +710 -0
- dataface/core/execute/adapters/values_adapter.py +58 -0
- dataface/core/execute/batch.py +744 -0
- dataface/core/execute/cache_backend.py +135 -0
- dataface/core/execute/cache_keys.py +66 -0
- dataface/core/execute/dbt_jinja.py +21 -0
- dataface/core/execute/dialects/__init__.py +121 -0
- dataface/core/execute/dialects/athena.py +75 -0
- dataface/core/execute/dialects/base.py +302 -0
- dataface/core/execute/dialects/bigquery.py +38 -0
- dataface/core/execute/dialects/databricks.py +68 -0
- dataface/core/execute/dialects/duckdb.py +35 -0
- dataface/core/execute/dialects/mysql.py +68 -0
- dataface/core/execute/dialects/postgres.py +39 -0
- dataface/core/execute/dialects/redshift.py +12 -0
- dataface/core/execute/dialects/snowflake.py +51 -0
- dataface/core/execute/dialects/sqlserver.py +92 -0
- dataface/core/execute/duckdb_cache.py +712 -0
- dataface/core/execute/duckdb_config.py +26 -0
- dataface/core/execute/errors.py +213 -0
- dataface/core/execute/executor.py +1249 -0
- dataface/core/execute/parallel.py +162 -0
- dataface/core/execute/setup_sql.py +58 -0
- dataface/core/execute/source_registry.py +72 -0
- dataface/core/execute/source_resolver.py +255 -0
- dataface/core/execute/sql_guard.py +387 -0
- dataface/core/execute/sql_literals.py +199 -0
- dataface/core/fonts.py +52 -0
- dataface/core/inspect/__init__.py +32 -0
- dataface/core/inspect/cache_factory.py +98 -0
- dataface/core/inspect/db_types.py +162 -0
- dataface/core/inspect/dbt_schema.py +96 -0
- dataface/core/inspect/defaults.yml +37 -0
- dataface/core/inspect/fanout_risk.py +109 -0
- dataface/core/inspect/manifest_utils.py +77 -0
- dataface/core/inspect/partials/categorical.yml +40 -0
- dataface/core/inspect/partials/date.yml +40 -0
- dataface/core/inspect/partials/numeric.yml +55 -0
- dataface/core/inspect/partition_types.py +38 -0
- dataface/core/inspect/query_validator.py +975 -0
- dataface/core/inspect/renderer.py +354 -0
- dataface/core/inspect/resolver.py +808 -0
- dataface/core/inspect/search.py +461 -0
- dataface/core/inspect/sources/__init__.py +32 -0
- dataface/core/inspect/sources/dbt.py +738 -0
- dataface/core/inspect/sources/duckdb_utils.py +66 -0
- dataface/core/inspect/templates/__init__.py +1 -0
- dataface/core/inspect/templates/categorical_column.yml +196 -0
- dataface/core/inspect/templates/charts.yml +109 -0
- dataface/core/inspect/templates/date_column.yml +248 -0
- dataface/core/inspect/templates/model.yml +138 -0
- dataface/core/inspect/templates/numeric_column.yml +261 -0
- dataface/core/inspect/templates/quality.yml +80 -0
- dataface/core/inspect/templates/string_column.yml +263 -0
- dataface/core/project_roots.py +165 -0
- dataface/core/render/__init__.py +87 -0
- dataface/core/render/board_links.py +176 -0
- dataface/core/render/chart/__init__.py +27 -0
- dataface/core/render/chart/arc_attached_table.py +251 -0
- dataface/core/render/chart/artifacts.py +16 -0
- dataface/core/render/chart/callout.py +225 -0
- dataface/core/render/chart/decisions.py +358 -0
- dataface/core/render/chart/geo.py +700 -0
- dataface/core/render/chart/kpi.py +916 -0
- dataface/core/render/chart/labels.py +76 -0
- dataface/core/render/chart/pipeline.py +818 -0
- dataface/core/render/chart/presentation.py +36 -0
- dataface/core/render/chart/profile.py +3438 -0
- dataface/core/render/chart/render_single.py +347 -0
- dataface/core/render/chart/renderers.py +193 -0
- dataface/core/render/chart/rendering.py +565 -0
- dataface/core/render/chart/serialization.py +90 -0
- dataface/core/render/chart/spark.py +496 -0
- dataface/core/render/chart/spark_bar.py +370 -0
- dataface/core/render/chart/spec_builders.py +154 -0
- dataface/core/render/chart/standard_renderer.py +2645 -0
- dataface/core/render/chart/table.py +2957 -0
- dataface/core/render/chart/table_support.py +1452 -0
- dataface/core/render/chart/tick_values.py +66 -0
- dataface/core/render/chart/time_unit_detect.py +809 -0
- dataface/core/render/chart/title_overflow.py +157 -0
- dataface/core/render/chart/type_inference.py +122 -0
- dataface/core/render/chart/validation.py +99 -0
- dataface/core/render/chart/vega_lite.py +125 -0
- dataface/core/render/chart/vega_lite_types.py +268 -0
- dataface/core/render/chart/vl_field_maps.py +346 -0
- dataface/core/render/chart_interactivity.py +24 -0
- dataface/core/render/control_registry.py +287 -0
- dataface/core/render/converters/__init__.py +24 -0
- dataface/core/render/converters/chart.py +276 -0
- dataface/core/render/converters/html.py +98 -0
- dataface/core/render/converters/pdf.py +40 -0
- dataface/core/render/converters/png.py +41 -0
- dataface/core/render/errors.py +144 -0
- dataface/core/render/face_api.py +160 -0
- dataface/core/render/faces.py +1194 -0
- dataface/core/render/font_measurement.py +48 -0
- dataface/core/render/font_support.py +197 -0
- dataface/core/render/fonts/DFTSansTabular-Regular.ttf +0 -0
- dataface/core/render/fonts/DFTSansTabular-Regular.woff2 +0 -0
- dataface/core/render/fonts/DFTSerifOldstyleProportional-Regular.ttf +0 -0
- dataface/core/render/fonts/DFTSerifOldstyleTabular-Regular.ttf +0 -0
- dataface/core/render/fonts/InterVariable.ttf +0 -0
- dataface/core/render/fonts/InterVariable.woff2 +0 -0
- dataface/core/render/fonts/NOTO_COLOR_EMOJI_LICENSE.txt +93 -0
- dataface/core/render/fonts/NOTO_EMOJI_LICENSE.txt +93 -0
- dataface/core/render/fonts/NotoColorEmoji-Regular.ttf +0 -0
- dataface/core/render/fonts/NotoColorEmoji-Regular.woff2 +0 -0
- dataface/core/render/fonts/NotoEmoji-Regular.ttf +0 -0
- dataface/core/render/fonts/NotoEmoji-Regular.woff2 +0 -0
- dataface/core/render/fonts/SOURCE_CODE_PRO_LICENSE.txt +93 -0
- dataface/core/render/fonts/SOURCE_SERIF_4_LICENSE.txt +98 -0
- dataface/core/render/fonts/SourceCodePro-Regular.ttf +0 -0
- dataface/core/render/fonts/SourceSerif4-Regular.ttf +0 -0
- dataface/core/render/fonts/_emoji_font_face.css +43 -0
- dataface/core/render/fonts/source-serif-4-variable-latin.woff2 +0 -0
- dataface/core/render/format_utils.py +329 -0
- dataface/core/render/geo_defaults.yml +28 -0
- dataface/core/render/json_format.py +146 -0
- dataface/core/render/layout_sizing.py +865 -0
- dataface/core/render/layouts.py +541 -0
- dataface/core/render/markdown_defaults.yml +16 -0
- dataface/core/render/missing_vars_prompt.py +79 -0
- dataface/core/render/placeholder.py +389 -0
- dataface/core/render/render_result.py +14 -0
- dataface/core/render/renderer.py +467 -0
- dataface/core/render/script_embedding.py +16 -0
- dataface/core/render/svg_utils.py +212 -0
- dataface/core/render/template_loader.py +69 -0
- dataface/core/render/templates/controls/_styles.css +606 -0
- dataface/core/render/templates/controls/checkbox.html +16 -0
- dataface/core/render/templates/controls/date.html +16 -0
- dataface/core/render/templates/controls/number.html +19 -0
- dataface/core/render/templates/controls/readonly.html +9 -0
- dataface/core/render/templates/controls/select.html +21 -0
- dataface/core/render/templates/controls/slider.html +22 -0
- dataface/core/render/templates/controls/text.html +16 -0
- dataface/core/render/templates/scripts/chart_interactivity.js +191 -0
- dataface/core/render/templates/scripts/variables.js +976 -0
- dataface/core/render/templates/svg/grid_pattern.svg +3 -0
- dataface/core/render/templates/svg/styles.css +51 -0
- dataface/core/render/terminal.py +311 -0
- dataface/core/render/terminal_charts.py +563 -0
- dataface/core/render/terminal_defaults.yml +2 -0
- dataface/core/render/terminal_layouts.py +299 -0
- dataface/core/render/terminal_text.py +31 -0
- dataface/core/render/text/__init__.py +1 -0
- dataface/core/render/text/case.py +113 -0
- dataface/core/render/text_format.py +129 -0
- dataface/core/render/utils.py +106 -0
- dataface/core/render/variable_controls.py +946 -0
- dataface/core/render/variable_input_refinement.py +140 -0
- dataface/core/render/warnings/__init__.py +15 -0
- dataface/core/render/warnings/bar_color_1_to_1_with_x.py +80 -0
- dataface/core/render/warnings/base.py +44 -0
- dataface/core/render/warnings/fanout_risk.py +15 -0
- dataface/core/render/warnings/from_query_diagnostic.py +56 -0
- dataface/core/render/warnings/missing_join_predicate.py +13 -0
- dataface/core/render/warnings/query_parse_error.py +14 -0
- dataface/core/render/warnings/query_returned_zero_rows.py +42 -0
- dataface/core/render/warnings/reaggregation.py +14 -0
- dataface/core/render/warnings/registry.py +45 -0
- dataface/core/render/warnings/suppression.py +46 -0
- dataface/core/render/warnings/temporal_single_point.py +63 -0
- dataface/core/render/warnings/unreferenced_chart.py +15 -0
- dataface/core/render/warnings/y_encoding_mostly_null.py +76 -0
- dataface/core/render/yaml_format.py +167 -0
- dataface/core/resolve_face.py +195 -0
- dataface/core/schema/__init__.py +0 -0
- dataface/core/schema/guidance.py +151 -0
- dataface/core/scoped_paths.py +59 -0
- dataface/core/serve/__init__.py +14 -0
- dataface/core/serve/bootstrap.py +39 -0
- dataface/core/serve/embedded.py +57 -0
- dataface/core/serve/port.py +129 -0
- dataface/core/serve/server.py +938 -0
- dataface/core/serve/templates/__init__.py +0 -0
- dataface/core/serve/templates/directory.yml +6 -0
- dataface/core/serve/templates/error.html.j2 +217 -0
- dataface/core/utils.py +121 -0
- dataface/core/validate.py +64 -0
- dataface/integrations/__init__.py +0 -0
- dataface/integrations/highlighting.py +351 -0
- dataface/integrations/markdown.py +537 -0
- dataface/py.typed +0 -0
- dataface-0.1.2.dist-info/METADATA +375 -0
- dataface-0.1.2.dist-info/RECORD +455 -0
- dataface-0.1.2.dist-info/WHEEL +4 -0
- dataface-0.1.2.dist-info/entry_points.txt +2 -0
- dataface-0.1.2.dist-info/licenses/LICENSE +202 -0
- mdsvg/__init__.py +168 -0
- mdsvg/fonts.py +656 -0
- mdsvg/images.py +299 -0
- mdsvg/parser.py +629 -0
- mdsvg/playground.py +284 -0
- mdsvg/py.typed +2 -0
- mdsvg/renderer.py +1623 -0
- mdsvg/style.py +355 -0
- mdsvg/types.py +200 -0
- mdsvg/utils.py +86 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
"""Profile rendering module.
|
|
2
|
+
|
|
3
|
+
Stage: INSPECT
|
|
4
|
+
Purpose: Shared rendering logic for inspect dashboards.
|
|
5
|
+
|
|
6
|
+
This module provides the core rendering function used by both:
|
|
7
|
+
- CLI: `dft inspect <table> --format html`
|
|
8
|
+
- Server: `dft serve` (via core/serve/server.py)
|
|
9
|
+
|
|
10
|
+
The renderer substitutes string {{ }} vars in the template YAML, then
|
|
11
|
+
compiles and renders it as HTML. Schema-shaped panels query the
|
|
12
|
+
LayeredSchemaResolver in-process via the `schema_resolver` face query type.
|
|
13
|
+
|
|
14
|
+
M2 contract: templates contain only string {{ }} vars — no document-level
|
|
15
|
+
{% %} Jinja. Substitution uses simple string replacement for the declared
|
|
16
|
+
set of inspect vars; the schema_resolver compile validator enforces that no
|
|
17
|
+
{{ }} remain after substitution.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import re
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import Any
|
|
23
|
+
|
|
24
|
+
from dataface.core.compile import CompileResult, compile
|
|
25
|
+
from dataface.core.execute import Executor
|
|
26
|
+
from dataface.core.execute.adapters import build_adapter_registry
|
|
27
|
+
from dataface.core.execute.adapters.sql_adapter import SqlAdapter
|
|
28
|
+
from dataface.core.inspect.cache_factory import make_cache_source_for_project
|
|
29
|
+
from dataface.core.inspect.resolver import LayeredSchemaResolver
|
|
30
|
+
from dataface.core.render import render
|
|
31
|
+
|
|
32
|
+
# Valid identifier pattern for SQL safety
|
|
33
|
+
_VALID_IDENTIFIER = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_]*$")
|
|
34
|
+
_PATH_VAR_KEYS = {"connection"}
|
|
35
|
+
|
|
36
|
+
# Source name the renderer registers when no project _sources.yaml exists.
|
|
37
|
+
# Templates reference this name through the `source_name` Jinja var; an
|
|
38
|
+
# explicit project source by the same name wins (register_source uses
|
|
39
|
+
# set-if-absent semantics).
|
|
40
|
+
_DEFAULT_SOURCE_NAME = "warehouse"
|
|
41
|
+
|
|
42
|
+
# The vars the renderer substitutes directly into the template YAML before
|
|
43
|
+
# compile(). schema_resolver fields reject {{ }} Jinja, so these must be
|
|
44
|
+
# literal strings in the compiled YAML. The set is fixed and declared here;
|
|
45
|
+
# adding a new schema_resolver field requires adding it here.
|
|
46
|
+
_SCHEMA_RESOLVER_VARS = ("model", "column", "connection", "source_name", "schema_name")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class InspectProfileCompileError(Exception):
|
|
50
|
+
"""Inspect template failed compilation."""
|
|
51
|
+
|
|
52
|
+
def __init__(self, result: CompileResult) -> None:
|
|
53
|
+
self.result = result
|
|
54
|
+
error_msg = (
|
|
55
|
+
"; ".join(e.message for e in result.errors)
|
|
56
|
+
if result.errors
|
|
57
|
+
else "Unknown error"
|
|
58
|
+
)
|
|
59
|
+
super().__init__(f"Failed to compile profile template: {error_msg}")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def validate_inspect_variables(variables: dict[str, Any]) -> dict[str, Any]:
|
|
63
|
+
"""Validate and sanitize profile variables.
|
|
64
|
+
|
|
65
|
+
Ensures variables are safe to use in SQL queries by:
|
|
66
|
+
- Checking that model/column names are valid SQL identifiers
|
|
67
|
+
- Converting all values to strings
|
|
68
|
+
- Rejecting potentially dangerous inputs
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
variables: Raw variables from URL params or CLI
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
Sanitized variables (all string values)
|
|
75
|
+
|
|
76
|
+
Raises:
|
|
77
|
+
ValueError: If variables contain invalid values
|
|
78
|
+
"""
|
|
79
|
+
sanitized: dict[str, Any] = {}
|
|
80
|
+
|
|
81
|
+
for key, value in variables.items():
|
|
82
|
+
# Convert to string
|
|
83
|
+
str_value = str(value) if value is not None else ""
|
|
84
|
+
|
|
85
|
+
# Validate SQL identifiers (model, column names)
|
|
86
|
+
if (
|
|
87
|
+
key in ("model", "column")
|
|
88
|
+
and str_value
|
|
89
|
+
and not _VALID_IDENTIFIER.match(str_value)
|
|
90
|
+
):
|
|
91
|
+
raise ValueError(
|
|
92
|
+
f"Invalid {key} name: '{str_value}'. "
|
|
93
|
+
"Must be a valid SQL identifier (letters, numbers, underscores)."
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Validate file path variables — no shell metacharacters or traversal
|
|
97
|
+
if key in _PATH_VAR_KEYS and str_value and str_value != ":memory:":
|
|
98
|
+
if re.search(r"[;&|`$(){}]", str_value):
|
|
99
|
+
raise ValueError(
|
|
100
|
+
f"Invalid {key}: '{str_value}'. Contains disallowed characters."
|
|
101
|
+
)
|
|
102
|
+
if ".." in str_value:
|
|
103
|
+
raise ValueError(
|
|
104
|
+
f"Invalid {key}: '{str_value}'. Path traversal is not allowed."
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
sanitized[key] = str_value
|
|
108
|
+
|
|
109
|
+
# The renderer always overrides ``source_name`` / ``schema_name`` from
|
|
110
|
+
# project-root below. Drop any caller-supplied values so a URL like
|
|
111
|
+
# ``?schema_name=anything-goes`` can't smuggle a string into the rendered YAML.
|
|
112
|
+
for stripped_key in ("source_name", "schema_name"):
|
|
113
|
+
sanitized.pop(stripped_key, None)
|
|
114
|
+
|
|
115
|
+
return sanitized
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _substitute_template_vars(template_yaml: str, vars: dict[str, str]) -> str:
|
|
119
|
+
"""Substitute declared inspect vars into the template YAML.
|
|
120
|
+
|
|
121
|
+
Only substitutes the vars named in ``_SCHEMA_RESOLVER_VARS``; the
|
|
122
|
+
schema_resolver compile validator rejects any remaining ``{{ }}`` Jinja
|
|
123
|
+
in its fields, so unknown vars cause a compile error rather than silent
|
|
124
|
+
pass-through.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
template_yaml: Raw template YAML with ``{{ var_name }}`` placeholders.
|
|
128
|
+
vars: Resolved variable values (all string).
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
Template YAML with placeholders replaced by literal values.
|
|
132
|
+
"""
|
|
133
|
+
result = template_yaml
|
|
134
|
+
for key in _SCHEMA_RESOLVER_VARS:
|
|
135
|
+
value = vars.get(key, "")
|
|
136
|
+
result = result.replace("{{ " + key + " }}", value)
|
|
137
|
+
result = result.replace("{{" + key + "}}", value)
|
|
138
|
+
return result
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _resolve_table_schema(
|
|
142
|
+
resolver: LayeredSchemaResolver,
|
|
143
|
+
source_name: str,
|
|
144
|
+
model_name: str,
|
|
145
|
+
) -> str:
|
|
146
|
+
"""Return the schema containing ``model_name``.
|
|
147
|
+
|
|
148
|
+
Two-pass strategy that keeps the render path safe for every dialect:
|
|
149
|
+
|
|
150
|
+
Pass 1 — cache only (via ``resolver.cache``): no adapter calls, works for
|
|
151
|
+
every dialect including non-DuckDB warehouses where the adapter may refuse
|
|
152
|
+
to connect. Returns immediately when the cache has the answer.
|
|
153
|
+
|
|
154
|
+
Pass 2 — full resolver walk (via ``resolver.list_schemas`` /
|
|
155
|
+
``list_tables``): runs only when the cache pass found nothing. The resolver
|
|
156
|
+
uses cache-first per-target logic, so cached tables are still served without
|
|
157
|
+
a second adapter round-trip. Adapter calls happen here for cold-start DuckDB
|
|
158
|
+
tables that were never profiled.
|
|
159
|
+
|
|
160
|
+
First-match semantics: cache iteration order, then dbt-schema order.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
resolver: LayeredSchemaResolver that owns both the cache and the adapter.
|
|
164
|
+
source_name: Named source registered with the adapter registry.
|
|
165
|
+
model_name: Bare table/model name to look up.
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
Schema name string, or ``""`` when no match is found or the source is
|
|
169
|
+
unreachable.
|
|
170
|
+
"""
|
|
171
|
+
if not source_name:
|
|
172
|
+
return ""
|
|
173
|
+
|
|
174
|
+
def _safe_schema(name: str) -> str:
|
|
175
|
+
"""Return ``name`` only when it's a valid SQL identifier, else ``""``."""
|
|
176
|
+
return name if name and _VALID_IDENTIFIER.match(name) else ""
|
|
177
|
+
|
|
178
|
+
# Cache-first pass: when a warm SuperSchemaSource is available, look up the
|
|
179
|
+
# schema without adapter calls. This path is only hit when the caller injects
|
|
180
|
+
# a cache (e.g. Cloud/IDE with dataface-super-schema). The OSS renderer builds
|
|
181
|
+
# without a cache (cache=None), so this block is skipped in the OSS path.
|
|
182
|
+
if resolver.cache is not None:
|
|
183
|
+
cache_schemas = resolver.cache.list_schemas()
|
|
184
|
+
if cache_schemas is not None:
|
|
185
|
+
for schema in cache_schemas.get("schemas", {}):
|
|
186
|
+
cache_tables = resolver.cache.list_tables(schema)
|
|
187
|
+
if cache_tables is not None and model_name in cache_tables.get(
|
|
188
|
+
"tables", {}
|
|
189
|
+
):
|
|
190
|
+
return _safe_schema(schema)
|
|
191
|
+
|
|
192
|
+
# Adapter walk: needed for cold start or when no cache is present.
|
|
193
|
+
try:
|
|
194
|
+
schemas_result = resolver.list_schemas(source_name)
|
|
195
|
+
except Exception: # noqa: BLE001 — renderer best-effort; fall back to ""
|
|
196
|
+
return ""
|
|
197
|
+
schema_names = list(
|
|
198
|
+
schemas_result.get("sources", {}).get(source_name, {}).get("schemas", {})
|
|
199
|
+
)
|
|
200
|
+
for schema in schema_names:
|
|
201
|
+
try:
|
|
202
|
+
tables_result = resolver.list_tables(source_name, schema)
|
|
203
|
+
except Exception: # noqa: BLE001 — renderer best-effort; per-schema
|
|
204
|
+
continue
|
|
205
|
+
tables = (
|
|
206
|
+
tables_result.get("sources", {})
|
|
207
|
+
.get(source_name, {})
|
|
208
|
+
.get("schemas", {})
|
|
209
|
+
.get(schema, {})
|
|
210
|
+
.get("tables", {})
|
|
211
|
+
)
|
|
212
|
+
if model_name in tables:
|
|
213
|
+
return _safe_schema(schema)
|
|
214
|
+
return ""
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def _ensure_default_source(
|
|
218
|
+
adapter_registry: Any,
|
|
219
|
+
connection: str | None,
|
|
220
|
+
dialect: str,
|
|
221
|
+
) -> None:
|
|
222
|
+
"""Register a synthetic ``warehouse`` source when none are configured.
|
|
223
|
+
|
|
224
|
+
The schema_resolver query type requires a named source registered with
|
|
225
|
+
the adapter registry. Inspect-server projects often have no
|
|
226
|
+
``_sources.yaml`` — they're spun up against a single connection. To
|
|
227
|
+
keep the rewire working for that case we synthesize a default named
|
|
228
|
+
source from the active connection. An explicit project-source named
|
|
229
|
+
``warehouse`` always wins (``register_source`` is idempotent on name).
|
|
230
|
+
|
|
231
|
+
Only DuckDB connections are synthesized here — other dialects require
|
|
232
|
+
credentials that cannot be derived from a bare connection URL; callers
|
|
233
|
+
that need schema_resolver on non-DuckDB must configure the source in
|
|
234
|
+
``_sources.yaml``.
|
|
235
|
+
"""
|
|
236
|
+
if adapter_registry.list_sql_sources():
|
|
237
|
+
return
|
|
238
|
+
# Only DuckDB connection URLs are self-contained enough to synthesize a
|
|
239
|
+
# valid source config from. Non-DuckDB dialects (Snowflake, Postgres, etc.)
|
|
240
|
+
# require credentials not present in the URL; callers that need
|
|
241
|
+
# schema_resolver on non-DuckDB must configure the source in _sources.yaml.
|
|
242
|
+
if dialect != "duckdb":
|
|
243
|
+
return
|
|
244
|
+
fallback_connection: str | None = connection
|
|
245
|
+
if fallback_connection is None:
|
|
246
|
+
for adapter in adapter_registry.adapters:
|
|
247
|
+
if isinstance(adapter, SqlAdapter):
|
|
248
|
+
fallback_connection = adapter.connection_string
|
|
249
|
+
break
|
|
250
|
+
if fallback_connection is None:
|
|
251
|
+
return
|
|
252
|
+
# source_config_from_url lives in the private package — use a minimal
|
|
253
|
+
# inline DuckDB config so the renderer works without the profiler.
|
|
254
|
+
adapter_registry.register_source(
|
|
255
|
+
_DEFAULT_SOURCE_NAME,
|
|
256
|
+
{"type": "duckdb", "path": fallback_connection},
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def _resolve_source_name(adapter_registry: Any) -> str:
|
|
261
|
+
"""Pick the source the inspect templates query for schema-shaped data."""
|
|
262
|
+
sources = adapter_registry.list_sql_sources()
|
|
263
|
+
if not sources:
|
|
264
|
+
return ""
|
|
265
|
+
for entry in sources:
|
|
266
|
+
if entry["name"] == _DEFAULT_SOURCE_NAME:
|
|
267
|
+
return _DEFAULT_SOURCE_NAME
|
|
268
|
+
return str(sources[0]["name"])
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def render_inspect_dashboard(
|
|
272
|
+
template_yaml: str,
|
|
273
|
+
variables: dict[str, Any],
|
|
274
|
+
project_root: Path | None = None,
|
|
275
|
+
connection: str | None = None,
|
|
276
|
+
dialect: str = "duckdb",
|
|
277
|
+
) -> str:
|
|
278
|
+
"""Render a profile template to HTML.
|
|
279
|
+
|
|
280
|
+
Substitutes string {{ }} vars into the template YAML (M2: no block-tag
|
|
281
|
+
Jinja), compiles it, and renders it as HTML. ``source_name`` and
|
|
282
|
+
``schema_name`` are resolved from the LayeredSchemaResolver (which uses
|
|
283
|
+
the cache when present, falls through to dbt/adapter when absent);
|
|
284
|
+
caller-supplied values for those keys are silently dropped to prevent URL
|
|
285
|
+
parameter injection.
|
|
286
|
+
|
|
287
|
+
Args:
|
|
288
|
+
template_yaml: YAML template content with string {{ }} placeholders
|
|
289
|
+
variables: Variables to substitute in the template
|
|
290
|
+
project_root: Project root for resolving relative paths
|
|
291
|
+
connection: Database connection string override
|
|
292
|
+
dialect: Database dialect for the registered SQL adapter — also
|
|
293
|
+
picked up by the synthetic ``warehouse`` source when no
|
|
294
|
+
project sources are configured.
|
|
295
|
+
|
|
296
|
+
Returns:
|
|
297
|
+
Rendered HTML string
|
|
298
|
+
|
|
299
|
+
Raises:
|
|
300
|
+
InspectProfileCompileError: If compilation fails
|
|
301
|
+
"""
|
|
302
|
+
safe_vars = validate_inspect_variables(variables)
|
|
303
|
+
|
|
304
|
+
resolved_project_root = project_root or Path.cwd()
|
|
305
|
+
registry_kwargs: dict[str, Any] = {"read_only": True}
|
|
306
|
+
if connection:
|
|
307
|
+
registry_kwargs["connection_string"] = connection
|
|
308
|
+
registry_kwargs["profile_type"] = dialect
|
|
309
|
+
adapter_registry = build_adapter_registry(resolved_project_root, **registry_kwargs)
|
|
310
|
+
_ensure_default_source(adapter_registry, connection, dialect)
|
|
311
|
+
safe_vars["source_name"] = _resolve_source_name(adapter_registry)
|
|
312
|
+
|
|
313
|
+
# Use warm cache when dataface-super-schema is installed; fall back to
|
|
314
|
+
# dbt-only resolver (cache=None) when it is not. The cache enriches
|
|
315
|
+
# schema_resolver panels for non-DuckDB warehouses where the adapter
|
|
316
|
+
# can't connect; without the private package, those panels degrade.
|
|
317
|
+
cache = make_cache_source_for_project(resolved_project_root)
|
|
318
|
+
resolver = LayeredSchemaResolver(
|
|
319
|
+
cache=cache,
|
|
320
|
+
adapter_registry=adapter_registry,
|
|
321
|
+
project_root=resolved_project_root,
|
|
322
|
+
)
|
|
323
|
+
model_name = safe_vars.get("model", "")
|
|
324
|
+
safe_vars["schema_name"] = (
|
|
325
|
+
_resolve_table_schema(resolver, safe_vars["source_name"], model_name)
|
|
326
|
+
if model_name
|
|
327
|
+
else ""
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
# Substitute the schema_resolver vars into the YAML before compile().
|
|
331
|
+
# schema_resolver fields reject {{ }} Jinja — they must be literal strings.
|
|
332
|
+
safe_vars.setdefault("connection", ":memory:")
|
|
333
|
+
substituted_yaml = _substitute_template_vars(template_yaml, safe_vars)
|
|
334
|
+
|
|
335
|
+
result = compile(substituted_yaml, project_dir=project_root)
|
|
336
|
+
if result.errors or not result.face:
|
|
337
|
+
raise InspectProfileCompileError(result=result)
|
|
338
|
+
|
|
339
|
+
executor = Executor(
|
|
340
|
+
result.face,
|
|
341
|
+
adapter_registry=adapter_registry,
|
|
342
|
+
query_registry=result.query_registry,
|
|
343
|
+
project_root=project_root,
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
html_output = render(
|
|
347
|
+
result.face, executor, format="html", variables=safe_vars
|
|
348
|
+
).output
|
|
349
|
+
|
|
350
|
+
if html_output is None:
|
|
351
|
+
return ""
|
|
352
|
+
if isinstance(html_output, bytes):
|
|
353
|
+
return html_output.decode("utf-8")
|
|
354
|
+
return html_output
|