Skip to content

Commit 942f276

Browse files
authored
Merge pull request #1900 from wkentaro/fix/file-dialog-malformed-json
fix: handle malformed JSON in FileDialogPreview
2 parents 323c9ea + e087e34 commit 942f276

3 files changed

Lines changed: 99 additions & 3 deletions

File tree

labelme/widgets/file_dialog_preview.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,13 @@ def __init__(self, *args, **kwargs):
5353

5454
def onChange(self, path):
5555
if path.lower().endswith(".json"):
56-
with open(path) as f:
57-
data = json.load(f)
58-
self.labelPreview.setText(json.dumps(data, indent=4, sort_keys=False))
56+
try:
57+
with open(path) as f:
58+
data = json.load(f)
59+
text = json.dumps(data, indent=4, sort_keys=False)
60+
except (json.JSONDecodeError, UnicodeDecodeError) as e:
61+
text = f"Cannot preview: {e}"
62+
self.labelPreview.setText(text)
5963
self.labelPreview.label.setAlignment(
6064
QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop
6165
)

tests/conftest.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,20 @@
55
import pytest
66

77

8+
def pytest_addoption(parser: pytest.Parser) -> None:
9+
parser.addoption(
10+
"--show",
11+
action="store_true",
12+
default=False,
13+
help="Show GUI widgets during tests for visual inspection.",
14+
)
15+
16+
17+
@pytest.fixture()
18+
def show(request: pytest.FixtureRequest) -> bool:
19+
return request.config.getoption("--show", default=False)
20+
21+
822
def _create_annotated_nested(data_path: Path) -> None:
923
dst_dir: Path = data_path / "annotated_nested"
1024
dst_dir.mkdir()
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
from __future__ import annotations
2+
3+
import json
4+
from pathlib import Path
5+
6+
import pytest
7+
from pytestqt.qtbot import QtBot
8+
9+
from labelme.widgets.file_dialog_preview import FileDialogPreview
10+
11+
12+
@pytest.fixture()
13+
def _file_preview_widget(qtbot: QtBot) -> FileDialogPreview:
14+
widget = FileDialogPreview()
15+
qtbot.addWidget(widget)
16+
return widget
17+
18+
19+
def _show_and_wait(widget: FileDialogPreview, qtbot: QtBot) -> None:
20+
widget.show()
21+
qtbot.waitExposed(widget)
22+
qtbot.waitUntil(lambda: not widget.isVisible(), timeout=30_000)
23+
24+
25+
@pytest.mark.gui
26+
def test_onChange_valid_json(
27+
_file_preview_widget: FileDialogPreview,
28+
tmp_path: Path,
29+
qtbot: QtBot,
30+
show: bool,
31+
) -> None:
32+
path = tmp_path / "valid.json"
33+
path.write_text(json.dumps({"version": "5.0.0", "shapes": []}))
34+
35+
_file_preview_widget.onChange(str(path))
36+
37+
assert _file_preview_widget.labelPreview.isHidden() is False
38+
assert '"version"' in _file_preview_widget.labelPreview.label.text()
39+
40+
if show:
41+
_show_and_wait(_file_preview_widget, qtbot)
42+
43+
44+
@pytest.mark.gui
45+
@pytest.mark.parametrize(
46+
"content_bytes",
47+
[
48+
b"not valid json {{{{",
49+
bytes(range(256)),
50+
],
51+
ids=["malformed_json", "binary_file"],
52+
)
53+
def test_onChange_bad_json_no_crash(
54+
_file_preview_widget: FileDialogPreview,
55+
tmp_path: Path,
56+
content_bytes: bytes,
57+
qtbot: QtBot,
58+
show: bool,
59+
) -> None:
60+
path = tmp_path / "bad.json"
61+
path.write_bytes(content_bytes)
62+
63+
_file_preview_widget.onChange(str(path))
64+
65+
assert _file_preview_widget.labelPreview.isHidden() is False
66+
assert "Cannot preview" in _file_preview_widget.labelPreview.label.text()
67+
68+
if show:
69+
_show_and_wait(_file_preview_widget, qtbot)
70+
71+
72+
@pytest.mark.gui
73+
def test_onChange_non_json_non_image_hides_preview(
74+
_file_preview_widget: FileDialogPreview,
75+
) -> None:
76+
_file_preview_widget.onChange("/nonexistent/path.txt")
77+
78+
assert _file_preview_widget.labelPreview.isHidden() is True

0 commit comments

Comments
 (0)