PyGWalker: Turn your dataframe into an interactive UI for visual analysis
This report summarizes scanner evidence for Kanaries/pygwalker, a Python project with 15,983 GitHub stars. SynthScan examined 27,408 lines of code across 240 source files, recording 500 pattern matches distributed across 13 syntactic categories. The legacy adjusted pattern signal is 22.0 (Elevated pattern signal); it is not an authorship estimate.
Use the findings to ask whether maintainers can explain intent, verify dependencies, simplify unnecessary scaffolding, and focus review effort. Risk Payload v1 adds ownership, supply-chain, over-engineering, and review-effort vectors when available. These are prompts for human verification, not probabilities, security findings, or measured maintenance costs.
Use each vector to guide manual verification of the underlying findings.
Can maintainers explain intent, assumptions, and trade-offs? 404 finding(s).
Are imports declared and package/API names verified? 31 finding(s).
Are wrappers, guards, and repeated structures necessary? 448 finding(s).
Which changes need focused human review? 500 finding(s).
This chart tracks the legacy adjusted pattern score across scan runs. Changes can reflect source changes, scan coverage, or detector/scoring versions; they do not measure AI adoption or prove that a team refactored code. Compare runs only when their scoring versions are compatible. The dashed line tracks the raw pattern-hit count.
Groups findings by configured severity weight. Severity is not a confidence estimate, security rating, or proof that a package is missing. Verify imports against the project's manifests and inspect each finding in its local design context.
This horizontal bar chart shows where configured pattern scores accumulate across directories. Treat the bars as locations to inspect, not as evidence of authorship or code quality. Use the file-level findings to decide whether a directory needs a focused ownership, dependency, complexity, or review check.
The scanner identified 500 distinct pattern matches across 13 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a detector match to inspect in context. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the configured finding weight (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — legacy comment-context matches receive a ×1.5 score weight. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier under the legacy scoring model.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | app/src/dataSource/index.tsx | 171 | export function getDatasFromKernelByPayload(comm: ICommunication | null) { | CODE |
| LOW | app/src/tools/saveTool.tsx | 18 | function DocumentTextIconWithRedPoint(iconProps) { | CODE |
| LOW | tests/test_dsl_transform.py | 15 | def test_dsl_to_workflow_returns_valid_workflow(): | CODE |
| LOW | tests/test_dsl_transform.py | 22 | def test_dsl_to_workflow_with_fields(): | CODE |
| LOW | tests/test_dsl_transform.py | 34 | def test_vega_to_dsl_returns_expected_keys(): | CODE |
| LOW | tests/test_dsl_transform.py | 52 | def test_import_error_when_no_js_runtime(): | CODE |
| LOW | tests/test_dsl_transform.py | 66 | def test_runtime_initialized_only_once(): | CODE |
| LOW | tests/test_dsl_transform.py | 82 | def test_reset_runtime_closes_existing_contexts(): | CODE |
| LOW⚡ | tests/test_frontend_contracts.py | 192 | def test_frontend_props_interface_covers_python_props_builder(): | CODE |
| LOW⚡ | tests/test_frontend_contracts.py | 198 | def test_python_props_builder_stringifies_frontend_id(): | CODE |
| LOW⚡ | tests/test_frontend_contracts.py | 205 | def test_frontend_comm_maps_cover_python_comm_handler_endpoints(): | CODE |
| LOW⚡ | tests/test_frontend_contracts.py | 215 | def test_frontend_comm_request_map_uses_types_matching_python_request_models(): | CODE |
| LOW⚡ | tests/test_frontend_contracts.py | 225 | def test_frontend_protocol_interfaces_match_pydantic_aliases(): | CODE |
| LOW⚡ | tests/test_frontend_contracts.py | 232 | def test_generated_frontend_protocol_types_are_fresh(): | CODE |
| LOW⚡ | tests/test_frontend_contracts.py | 239 | def test_comm_handler_register_uses_typed_request_models(): | CODE |
| LOW⚡ | tests/test_frontend_contracts.py | 258 | def test_frontend_dev_typescript_source_maps_are_enabled(): | CODE |
| LOW⚡ | tests/test_frontend_contracts.py | 266 | def test_frontend_tracker_loads_segment_only_after_events_opt_in(): | CODE |
| LOW⚡ | tests/test_frontend_contracts.py | 276 | def test_frontend_http_integrations_initialize_communication(): | CODE |
| LOW | tests/test_frontend_contracts.py | 107 | def _comm_handler_request_models(repo_root: Path) -> dict[str, str]: | CODE |
| LOW | tests/test_frontend_contracts.py | 133 | def _comm_handler_register_calls(repo_root: Path) -> list[ast.Call]: | CODE |
| LOW | tests/test_frontend_contracts.py | 146 | def _typescript_interface_keys(repo_root: Path, interface_name: str) -> set[str]: | CODE |
| LOW | tests/test_frontend_contracts.py | 287 | def test_streamlit_entrypoint_refreshes_props_on_reruns(): | CODE |
| LOW | tests/test_frontend_contracts.py | 302 | def test_frontend_component_state_refreshes_when_props_change(): | CODE |
| LOW | tests/test_frontend_contracts.py | 310 | def test_frontend_pure_renderer_uses_local_data_without_kernel_computation(): | CODE |
| LOW | tests/test_frontend_contracts.py | 327 | def test_frontend_modals_are_lazy_loaded_from_entrypoint(): | CODE |
| LOW | tests/test_frontend_contracts.py | 346 | def test_frontend_save_payload_strips_graphic_walker_export_fields(): | CODE |
| LOW | tests/test_frontend_assets.py | 16 | def test_read_frontend_asset_reads_from_templates_dist(monkeypatch, tmp_path): | CODE |
| LOW | tests/test_frontend_assets.py | 25 | def test_read_frontend_asset_reports_compile_command(monkeypatch, tmp_path): | CODE |
| LOW | tests/test_frontend_assets.py | 36 | def test_read_frontend_asset_report_uses_stable_posix_path_with_windows_paths(monkeypatch, tmp_path): | CODE |
| LOW | tests/test_readme_api_reference.py | 19 | def _read_walk_api_table_rows() -> list[dict[str, str]]: | CODE |
| LOW | tests/test_readme_api_reference.py | 47 | def _read_walk_api_table_params() -> list[str]: | CODE |
| LOW | tests/test_readme_api_reference.py | 51 | def _format_signature_default(parameter: inspect.Parameter) -> str: | CODE |
| LOW | tests/test_readme_api_reference.py | 82 | def test_readme_walk_api_table_matches_jupyter_walk_signature(): | CODE |
| LOW | tests/test_readme_api_reference.py | 93 | def test_readme_walk_api_table_defaults_match_jupyter_walk_signature(): | CODE |
| LOW | tests/test_readme_api_reference.py | 103 | def test_readme_walk_api_table_documents_reusable_walker_input(): | CODE |
| LOW | tests/test_readme_api_reference.py | 136 | def test_public_api_docstrings_document_current_dataset_inputs(relative_path, qualified_name, expected_fragments): | CODE |
| LOW | tests/test_readme_api_reference.py | 144 | def test_readme_walk_api_table_marks_legacy_jupyter_envs_deprecated(): | CODE |
| LOW | tests/test_readme_api_reference.py | 176 | def test_translated_readmes_defer_api_reference_to_english_readme(): | CODE |
| LOW | tests/test_readme_api_reference.py | 185 | def test_translated_readmes_do_not_repeat_stale_api_parameters(): | CODE |
| LOW | tests/test_preview_image.py | 4 | def test_preview_image_tool_renders_on_daemon_thread(monkeypatch): | CODE |
| LOW | tests/test_preview_image.py | 34 | def test_preview_image_tool_keeps_worker_alive_after_render_error(monkeypatch): | CODE |
| LOW⚡ | tests/test_pygwalker_core.py | 1746 | def test_to_html_rejects_live_computation_modes(kwargs): | CODE |
| LOW⚡ | tests/test_pygwalker_core.py | 1751 | def test_to_html_rejects_invalid_computation_mode(): | CODE |
| LOW⚡ | tests/test_pygwalker_core.py | 1756 | def test_to_html_allows_disabled_computation_kwargs(monkeypatch): | CODE |
| LOW | tests/test_pygwalker_core.py | 29 | def _expected_legacy_computation_warning(kwargs): | CODE |
| LOW | tests/test_pygwalker_core.py | 71 | def _patch_cloud_computation_parser(monkeypatch): | CODE |
| LOW | tests/test_pygwalker_core.py | 103 | def fake_create_cloud_dataset(self, data_parser, name, is_public, temporary): | CODE |
| LOW | tests/test_pygwalker_core.py | 139 | def test_pygwalker_props_expose_browser_data_path(monkeypatch): | CODE |
| LOW | tests/test_pygwalker_core.py | 158 | def test_pygwalker_props_expose_kernel_data_path(monkeypatch): | CODE |
| LOW | tests/test_pygwalker_core.py | 169 | def test_pygwalker_get_props_falls_back_without_props_builder(monkeypatch): | CODE |
| LOW | tests/test_pygwalker_core.py | 211 | def test_props_tracker_tracks_expected_invocation_fields(): | CODE |
| LOW | tests/test_pygwalker_core.py | 267 | def test_render_manager_preview_handles_parser_exception_and_manual_gid(monkeypatch): | CODE |
| LOW | tests/test_pygwalker_core.py | 312 | def test_render_manager_chart_preview_uses_chart_indexed_workflow(monkeypatch): | CODE |
| LOW | tests/test_pygwalker_core.py | 325 | def fake_render_chart_preview(**kwargs): | CODE |
| LOW | tests/test_pygwalker_core.py | 357 | def test_render_manager_chart_preview_returns_empty_for_mismatched_workflow_list(): | CODE |
| LOW | tests/test_pygwalker_core.py | 375 | def test_jupyter_display_manager_convert_html_displays_iframe(): | CODE |
| LOW | tests/test_pygwalker_core.py | 387 | def test_jupyter_display_manager_uploads_large_classic_jupyter_data(monkeypatch): | CODE |
| LOW | tests/test_pygwalker_core.py | 426 | def test_chart_export_manager_exports_png(monkeypatch): | CODE |
| LOW | tests/test_pygwalker_core.py | 445 | def test_chart_export_manager_exports_svg_variants(): | CODE |
| LOW | tests/test_pygwalker_core.py | 465 | def test_chart_export_manager_display_chart_uses_chart_name_as_default_title(): | CODE |
| 302 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | tests/test_data_parsers.py | 190 | except ImportError: | CODE |
| HIGH | pygwalker/_typing.py | 11 | except ModuleNotFoundError: | CODE |
| HIGH | pygwalker/_typing.py | 17 | except ModuleNotFoundError: | CODE |
| HIGH | pygwalker/_typing.py | 23 | except ModuleNotFoundError: | CODE |
| HIGH | pygwalker/_typing.py | 29 | except ModuleNotFoundError: | CODE |
| HIGH | pygwalker/_typing.py | 35 | except ModuleNotFoundError: | CODE |
| HIGH | pygwalker/data_parsers/base.py | 186 | except Exception: | CODE |
| HIGH | pygwalker/api/webserver.py | 210 | except KeyboardInterrupt: | CODE |
| HIGH | pygwalker/services/preview_image.py | 130 | except Exception: | CODE |
| HIGH | pygwalker/services/track.py | 70 | except Exception: | CODE |
| HIGH | pygwalker/services/kaggle.py | 16 | except Exception: | CODE |
| HIGH | pygwalker/services/data_bridge.py | 84 | except Exception: | CODE |
| HIGH | pygwalker/communications/streamlit_comm.py | 97 | except Exception: | CODE |
| HIGH | pygwalker/communications/streamlit_comm.py | 151 | except Exception: | CODE |
| HIGH | pygwalker/communications/base.py | 15 | except Exception: | CODE |
| HIGH | scripts/dev.py | 117 | except (ProcessLookupError, OSError): | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | pygwalker/jupyter_extension.py | 187 | except Exception as exc: # pragma: no cover - defensive import boundary | CODE |
| LOW | pygwalker/data_parsers/base.py | 186 | except Exception: | CODE |
| LOW | pygwalker/data_parsers/base.py | 230 | except Exception: | CODE |
| LOW | pygwalker/utils/encode.py | 23 | except Exception: | CODE |
| LOW | pygwalker/utils/runtime_env.py | 12 | except Exception: | CODE |
| MEDIUM | pygwalker/utils/runtime_env.py | 4 | def _is_jupyter() -> bool: | CODE |
| LOW | pygwalker/api/webserver.py | 170 | except Exception: | CODE |
| LOW | pygwalker/services/preview_image.py | 118 | except Exception: | CODE |
| LOW⚡ | pygwalker/services/preview_image.py | 130 | except Exception: | CODE |
| LOW⚡ | pygwalker/services/preview_image.py | 143 | except Exception: | CODE |
| MEDIUM | pygwalker/services/preview_image.py | 140 | def _safe_render_next_preview(self): | CODE |
| LOW | pygwalker/services/config.py | 76 | except Exception: | STRING |
| LOW | pygwalker/services/config.py | 151 | except Exception: | CODE |
| LOW | pygwalker/services/track.py | 70 | except Exception: | CODE |
| LOW | pygwalker/services/check_update.py | 109 | except Exception as exc: | CODE |
| LOW⚡ | pygwalker/services/kaggle.py | 16 | except Exception: | CODE |
| LOW | pygwalker/services/kaggle.py | 32 | except Exception: | CODE |
| LOW | pygwalker/services/data_bridge.py | 84 | except Exception: | CODE |
| MEDIUM | pygwalker/services/data_bridge.py | 81 | def warm_kernel_table(self) -> None: | CODE |
| LOW⚡ | pygwalker/services/format_invoke_walk_code.py | 68 | except Exception: | CODE |
| LOW | pygwalker/services/cloud_service.py | 80 | except Exception as e: | CODE |
| LOW | pygwalker/communications/reflex_comm.py | 37 | except Exception as e: | CODE |
| LOW | pygwalker/communications/reflex_comm.py | 77 | except Exception as e: | CODE |
| LOW | pygwalker/communications/streamlit_comm.py | 97 | except Exception: | CODE |
| LOW | pygwalker/communications/streamlit_comm.py | 151 | except Exception: | CODE |
| LOW | pygwalker/communications/base.py | 15 | except Exception: | CODE |
| LOW | pygwalker/communications/base.py | 50 | except Exception as e: | CODE |
| LOW | pygwalker/communications/base.py | 66 | except Exception as e: | CODE |
| LOW | examples/reflex_demo/app/app.py | 34 | except Exception as e: | CODE |
| LOW | examples/reflex_demo/app/app.py | 83 | except Exception as e: | CODE |
| MEDIUM | examples/reflex_demo/app/app.py | 84 | print(f"Error in pygwalker_demo: {e}") | CODE |
| LOW | examples/reflex_demo/app/app.py | 162 | except Exception as e: | CODE |
| LOW | examples/reflex_demo/app/app.py | 174 | except Exception as e: | CODE |
| LOW⚡ | scripts/test-init.py | 17 | except Exception as e: | CODE |
| MEDIUM⚡ | scripts/test-init.py | 18 | print(f"Error: could not download the file\n{e}", file=sys.stderr) | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | pygwalker/__init__.py | 16 | CODE | |
| LOW | pygwalker/__init__.py | 16 | CODE | |
| LOW | pygwalker/__init__.py | 16 | CODE | |
| LOW | pygwalker/__init__.py | 17 | CODE | |
| LOW | pygwalker/__init__.py | 18 | CODE | |
| LOW | pygwalker/__init__.py | 19 | CODE | |
| LOW | pygwalker/__init__.py | 20 | CODE | |
| LOW | pygwalker/__init__.py | 21 | CODE | |
| LOW | pygwalker/jupyter_extension.py | 8 | CODE | |
| LOW | pygwalker/api/streamlit.py | 31 | CODE | |
| LOW | pygwalker/api/webserver.py | 30 | CODE | |
| LOW | pygwalker/api/adapter.py | 13 | CODE | |
| LOW | pygwalker/api/marimo.py | 24 | CODE | |
| LOW | pygwalker/api/anywidget.py | 23 | CODE | |
| LOW | pygwalker/api/jupyter.py | 25 | CODE | |
| LOW | pygwalker/services/data_upload_communication.py | 7 | CODE | |
| LOW | pygwalker/services/data_communication.py | 17 | CODE | |
| LOW | pygwalker/services/desktop_communication.py | 7 | CODE | |
| LOW | pygwalker/services/comm_handler.py | 34 | CODE | |
| LOW | pygwalker/services/spec_communication.py | 15 | CODE | |
| LOW | pygwalker/services/cloud_communication.py | 21 | CODE | |
| LOW | scripts/generate_comm_protocol_ts.py | 1 | CODE | |
| LOW | scripts/dev.py | 24 | CODE | |
| LOW | scripts/local_ci.py | 3 | CODE | |
| LOW | pygwalker_tools/metrics/__init__.py | 1 | CODE | |
| LOW | pygwalker_tools/metrics/__init__.py | 1 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | pygwalker/data_parsers/database_parser.py | 212 | CODE | |
| LOW | pygwalker/data_parsers/database_parser.py | 221 | CODE | |
| LOW | pygwalker/data_parsers/cloud_dataset_parser.py | 70 | CODE | |
| LOW | pygwalker/api/streamlit.py | 356 | CODE | |
| LOW | pygwalker/api/streamlit.py | 104 | CODE | |
| LOW | pygwalker/api/reflex.py | 22 | CODE | |
| LOW | pygwalker/api/webserver.py | 214 | CODE | |
| LOW | pygwalker/api/webserver.py | 314 | CODE | |
| LOW | pygwalker/api/webserver.py | 376 | CODE | |
| LOW | pygwalker/api/adapter.py | 16 | CODE | |
| LOW | pygwalker/api/walker.py | 60 | CODE | |
| LOW | pygwalker/api/html.py | 25 | CODE | |
| LOW | pygwalker/api/html.py | 79 | CODE | |
| LOW | pygwalker/api/html.py | 142 | CODE | |
| LOW | pygwalker/api/marimo.py | 64 | CODE | |
| LOW | pygwalker/api/component.py | 426 | CODE | |
| LOW | pygwalker/api/component.py | 309 | CODE | |
| LOW | pygwalker/api/gradio.py | 15 | CODE | |
| LOW | pygwalker/api/anywidget.py | 63 | CODE | |
| LOW | pygwalker/api/jupyter.py | 119 | CODE | |
| LOW | pygwalker/services/comm_handler.py | 43 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | pygwalker/communications/reflex_comm.py | 49 | # Create a FastAPI sub-application for PyGWalker API | COMMENT |
| MEDIUM | pygwalker/communications/reflex_comm.py | 85 | # Create a sub-application for PyGWalker routes | COMMENT |
| MEDIUM | examples/reflex_demo/app/app.py | 66 | # Create a PyGWalker component | COMMENT |
| MEDIUM | examples/reflex_demo/app/app.py | 156 | # Create a Reflex app with the fixed PyGWalker API transformer | COMMENT |
| MEDIUM | scripts/test-init.sh | 14 | # Create the directory if it doesn't exist | COMMENT |
| MEDIUM⚡ | scripts/test-init.py | 11 | # Create the directory if it doesn't exist | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | pygwalker/__init__.py | 31 | __all__ = ["walk", "render", "table", "to_html", "FieldSpec", "GlobalVarManager", "component", "Walker", "spec"] | CODE |
| LOW | pygwalker/jupyter_extension.py | 207 | __all__ = [ | CODE |
| LOW | pygwalker/data_parsers/database_parser.py | 21 | logger = logging.getLogger(__name__) | CODE |
| LOW | pygwalker/data_parsers/spark_parser.py | 15 | logger = logging.getLogger(__name__) | CODE |
| LOW | pygwalker/data_parsers/cloud_dataset_parser.py | 14 | logger = logging.getLogger(__name__) | CODE |
| LOW | pygwalker/utils/check_walker_params.py | 4 | logger = logging.getLogger(__name__) | CODE |
| LOW | pygwalker/api/html.py | 22 | logger = logging.getLogger(__name__) | CODE |
| LOW | pygwalker/services/spec_manager.py | 28 | def update_vis_spec(self, vis_spec: List[Dict[str, Any]]) -> None: | CODE |
| LOW | pygwalker/services/preview_image.py | 15 | logger = logging.getLogger(__name__) | CODE |
| LOW | pygwalker/services/check_update.py | 25 | logger = logging.getLogger(__name__) | CODE |
| LOW | pygwalker/services/format_invoke_walk_code.py | 12 | logger = logging.getLogger(__name__) | CODE |
| LOW | pygwalker/services/cloud_service.py | 16 | logger = logging.getLogger(__name__) | CODE |
| LOW | …ckages/pygwalker-jupyter/pygwalker_jupyter/__init__.py | 16 | __all__ = ["__version__", "_jupyter_labextension_paths"] | CODE |
| LOW | pygwalker_tools/metrics/__init__.py | 3 | __all__ = ["get_metrics_datas", "MetricsChart"] | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_frontend_contracts.py | 55 | CODE | |
| LOW | tests/test_frontend_contracts.py | 83 | CODE | |
| LOW | tests/test_frontend_contracts.py | 107 | CODE | |
| LOW | tests/test_readme_api_reference.py | 59 | CODE | |
| LOW | pygwalker/api/_walker_reuse.py | 23 | CODE | |
| LOW | pygwalker/api/component.py | 161 | CODE | |
| LOW | pygwalker/api/component.py | 309 | CODE | |
| LOW | pygwalker/services/spec.py | 110 | CODE | |
| LOW | pygwalker/services/spec.py | 132 | CODE | |
| LOW | pygwalker/communications/gradio_comm.py | 51 | CODE | |
| LOW | pygwalker/communications/streamlit_comm.py | 121 | CODE | |
| LOW | scripts/dev.py | 186 | CODE | |
| LOW | pygwalker_tools/metrics/core.py | 177 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | pygwalker/utils/frontend_assets.py | 25 | "or `cd app && yarn build` before enabling dev mode." | CODE |
| HIGH | scripts/dev.py | 7 | * frontend -> ``cd app && yarn dev:build`` (``vite build --watch``) rebuilds | STRING |
| HIGH | scripts/dev.py | 222 | yarn = _resolve_exe("yarn", "Install Node.js 22.x and Yarn 1.x, then run `cd app && yarn install`.") | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| CRITICAL | app/src/utils/communication.tsx | 232 | const domain = window.parent.document.location.host.split(".").slice(-2).join('.'); | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | docs/JUPYTER_EXTENSION_ROADMAP.md | 309 | ## 9. Release and dependency guardrails | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | scripts/test-init.sh | 37 | # Check if the file was downloaded successfully | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | app/src/utils/communication.tsx | 185 | attributeFilter: ["placeholder"], | CODE |