Skip to content

Commit a91208a

Browse files
committed
tests: fix duckdb changes to get tables
1 parent 49bc22d commit a91208a

File tree

6 files changed

+99
-23
lines changed

6 files changed

+99
-23
lines changed

frontend/src/components/app-config/mcp-config.tsx

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* Copyright 2024 Marimo. All rights reserved. */
22

3-
import { CheckSquareIcon } from "lucide-react";
3+
import { CheckSquareIcon, Loader2, RefreshCwIcon } from "lucide-react";
44
import React from "react";
55
import type { UseFormReturn } from "react-hook-form";
66
import {
@@ -12,6 +12,7 @@ import {
1212
} from "@/components/ui/card";
1313
import { FormField, FormItem } from "@/components/ui/form";
1414
import type { UserConfig } from "@/core/config/config-schema";
15+
import { useMCPRefresh, useMCPStatus } from "../mcp";
1516
import { Button } from "../ui/button";
1617
import { Kbd } from "../ui/kbd";
1718
import { SettingSubtitle } from "./common";
@@ -45,10 +46,38 @@ const PRESET_CONFIGS: PresetConfig[] = [
4546

4647
export const MCPConfig: React.FC<MCPConfigProps> = ({ form, onSubmit }) => {
4748
const { handleClick } = useOpenSettingsToTab();
49+
const { data: status, refetch, isFetching } = useMCPStatus();
50+
const { refresh, isRefreshing } = useMCPRefresh();
51+
52+
const handleRefresh = async () => {
53+
await refresh();
54+
refetch();
55+
};
4856

4957
return (
5058
<div className="flex flex-col gap-4">
51-
<SettingSubtitle>MCP Servers</SettingSubtitle>
59+
<div className="flex items-center justify-between">
60+
<SettingSubtitle>MCP Servers</SettingSubtitle>
61+
<div className="flex items-center gap-2">
62+
{status && (
63+
<span className="text-xs text-muted-foreground">
64+
Status: {status.status}
65+
</span>
66+
)}
67+
<Button
68+
variant="outline"
69+
size="xs"
70+
onClick={handleRefresh}
71+
disabled={isRefreshing || isFetching}
72+
>
73+
{isRefreshing || isFetching ? (
74+
<Loader2 className="h-3 w-3 animate-spin" />
75+
) : (
76+
<RefreshCwIcon className="h-3 w-3" />
77+
)}
78+
</Button>
79+
</div>
80+
</div>
5281
<p className="text-sm text-muted-foreground">
5382
Enable Model Context Protocol (MCP) servers to provide additional
5483
capabilities and data sources for AI features.

frontend/src/components/chat/chat-panel.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ import {
6363
} from "../editor/ai/completion-utils";
6464
import { PanelEmptyState } from "../editor/chrome/panels/empty-state";
6565
import { CopyClipboardIcon } from "../icons/copy-icon";
66+
import { MCPStatusIndicator } from "../mcp";
6667
import { Input } from "../ui/input";
6768
import { Tooltip, TooltipProvider } from "../ui/tooltip";
6869
import { toast } from "../ui/use-toast";
@@ -120,6 +121,7 @@ const ChatHeader: React.FC<ChatHeaderProps> = ({
120121
</Button>
121122
</Tooltip>
122123
<div className="flex items-center gap-2">
124+
<MCPStatusIndicator />
123125
<Tooltip content="AI Settings">
124126
<Button
125127
variant="text"

marimo/_data/get_datasets.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ def _get_databases_from_duckdb_internal(
133133
# databases_dict[database][schema] = [table1, table2, ...]
134134
databases_dict: dict[str, dict[str, list[DataTable]]] = {}
135135

136+
SKIP_TABLES = ["duckdb_functions()", "duckdb_types()", "duckdb_settings()"]
137+
136138
for (
137139
database,
138140
schema,
@@ -141,6 +143,9 @@ def _get_databases_from_duckdb_internal(
141143
column_types,
142144
*_rest,
143145
) in tables_result:
146+
if name in SKIP_TABLES:
147+
continue
148+
144149
assert len(column_names) == len(column_types)
145150
assert isinstance(column_names, list)
146151
assert isinstance(column_types, list)

marimo/_plugins/ui/_impl/tables/narwhals_table.py

Lines changed: 57 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -342,13 +342,22 @@ def _get_stats_internal(self, column: str) -> ColumnStats:
342342
"nulls": col.null_count(),
343343
}
344344

345-
# As of Sep 2025, pyarrow and ibis do not support quantiles
345+
# As of Oct 2025, pyarrow and ibis do not support quantiles
346346
# through narwhals
347-
supports_quantiles = (
347+
supports_numeric_quantiles = (
348+
not frame.implementation.is_pyarrow()
349+
and not frame.implementation.is_ibis()
350+
)
351+
supports_temporal_quantiles = (
348352
not frame.implementation.is_pyarrow()
349353
and not frame.implementation.is_ibis()
350354
)
351355

356+
quantile_interpolation = "nearest"
357+
if frame.implementation.is_duckdb():
358+
# As of Oct 2025, DuckDB does not support "nearest" interpolation
359+
quantile_interpolation = "linear"
360+
352361
if is_narwhals_string_type(dtype):
353362
exprs["unique"] = col.n_unique()
354363
elif dtype == nw.Boolean:
@@ -401,15 +410,25 @@ def _get_stats_internal(self, column: str) -> ColumnStats:
401410
"max": col.max(),
402411
}
403412
)
404-
if supports_quantiles:
413+
if supports_temporal_quantiles:
405414
exprs.update(
406415
{
407416
"mean": col.mean(),
408-
"median": col.quantile(0.5, interpolation="nearest"),
409-
"p5": col.quantile(0.05, interpolation="nearest"),
410-
"p25": col.quantile(0.25, interpolation="nearest"),
411-
"p75": col.quantile(0.75, interpolation="nearest"),
412-
"p95": col.quantile(0.95, interpolation="nearest"),
417+
"median": col.quantile(
418+
0.5, interpolation=quantile_interpolation
419+
),
420+
"p5": col.quantile(
421+
0.05, interpolation=quantile_interpolation
422+
),
423+
"p25": col.quantile(
424+
0.25, interpolation=quantile_interpolation
425+
),
426+
"p75": col.quantile(
427+
0.75, interpolation=quantile_interpolation
428+
),
429+
"p95": col.quantile(
430+
0.95, interpolation=quantile_interpolation
431+
),
413432
}
414433
)
415434
elif is_narwhals_integer_type(dtype):
@@ -423,13 +442,21 @@ def _get_stats_internal(self, column: str) -> ColumnStats:
423442
"median": col.median(),
424443
}
425444
)
426-
if supports_quantiles:
445+
if supports_numeric_quantiles:
427446
exprs.update(
428447
{
429-
"p5": col.quantile(0.05, interpolation="nearest"),
430-
"p25": col.quantile(0.25, interpolation="nearest"),
431-
"p75": col.quantile(0.75, interpolation="nearest"),
432-
"p95": col.quantile(0.95, interpolation="nearest"),
448+
"p5": col.quantile(
449+
0.05, interpolation=quantile_interpolation
450+
),
451+
"p25": col.quantile(
452+
0.25, interpolation=quantile_interpolation
453+
),
454+
"p75": col.quantile(
455+
0.75, interpolation=quantile_interpolation
456+
),
457+
"p95": col.quantile(
458+
0.95, interpolation=quantile_interpolation
459+
),
433460
}
434461
)
435462
elif dtype.is_numeric():
@@ -443,13 +470,21 @@ def _get_stats_internal(self, column: str) -> ColumnStats:
443470
"median": col.median(),
444471
}
445472
)
446-
if supports_quantiles:
473+
if supports_numeric_quantiles:
447474
exprs.update(
448475
{
449-
"p5": col.quantile(0.05, interpolation="nearest"),
450-
"p25": col.quantile(0.25, interpolation="nearest"),
451-
"p75": col.quantile(0.75, interpolation="nearest"),
452-
"p95": col.quantile(0.95, interpolation="nearest"),
476+
"p5": col.quantile(
477+
0.05, interpolation=quantile_interpolation
478+
),
479+
"p25": col.quantile(
480+
0.25, interpolation=quantile_interpolation
481+
),
482+
"p75": col.quantile(
483+
0.75, interpolation=quantile_interpolation
484+
),
485+
"p95": col.quantile(
486+
0.95, interpolation=quantile_interpolation
487+
),
453488
}
454489
)
455490

@@ -461,6 +496,10 @@ def _get_stats_internal(self, column: str) -> ColumnStats:
461496
if key in units:
462497
stats_dict[key] = f"{value} {units[key]}"
463498

499+
# Maybe coerce null count to int
500+
if stats_dict["nulls"] is not None:
501+
stats_dict["nulls"] = int(stats_dict["nulls"])
502+
464503
return ColumnStats(**stats_dict)
465504

466505
def get_bin_values(self, column: str, num_bins: int) -> list[BinValue]:

tests/_cli/test_cli_export.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import pytest
1616

1717
from marimo._dependencies.dependencies import DependencyManager
18+
from marimo._utils.platform import is_windows
1819
from tests._server.templates.utils import normalize_index_html
1920
from tests.mocks import snapshotter
2021

@@ -922,8 +923,8 @@ def test_export_ipynb_with_multiple_definitions(
922923
assert p.stdout.decode() == ""
923924

924925
@pytest.mark.skipif(
925-
not DependencyManager.nbformat.has(),
926-
reason="This test requires nbformat.",
926+
not DependencyManager.nbformat.has() or is_windows(),
927+
reason="This test requires nbformat. Or windows.",
927928
)
928929
def test_export_ipynb_with_errors(
929930
self, temp_marimo_file_with_errors: str

tests/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,4 @@ def assert_serialize_roundtrip(obj: msgspec.Struct) -> None:
3535
serialized = encode_json_bytes(obj)
3636
cls = type(obj)
3737
parsed = parse_raw(serialized, cls)
38-
assert asdict(obj) == asdict(parsed)
38+
assert asdict(obj) == asdict(parsed), f"{asdict(obj)} != {asdict(parsed)}"

0 commit comments

Comments
 (0)