Skip to content

Commit 882ead7

Browse files
authored
Merge pull request #205 from neutrons/try-catch-export
Wrap export xml in try-except
2 parents a132762 + 387d137 commit 882ead7

6 files changed

Lines changed: 171 additions & 7 deletions

File tree

src/refred/configuration/export_xml_config.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import datetime
22
import logging
33
import os
4-
from typing import Optional
4+
from typing import TYPE_CHECKING, List, Optional
55

66
import lr_reduction
77
import mantid
@@ -12,11 +12,14 @@
1212
GlobalReductionSettingsHandler,
1313
)
1414

15+
if TYPE_CHECKING:
16+
from refred.main import MainGui
17+
1518

1619
class ExportXMLConfig(object):
17-
def __init__(self, parent=None):
20+
def __init__(self, parent: "MainGui"):
1821
self.parent = parent
19-
self.str_array = []
22+
self.str_array: List[str] = []
2023

2124
def header_part(self):
2225
str_array = self.str_array

src/refred/configuration/saving_configuration.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
from typing import TYPE_CHECKING
23

34
from qtpy import QtWidgets
45

@@ -7,11 +8,14 @@
78
from refred.status_message_handler import StatusMessageHandler
89
from refred.utilities import makeSureFileHasExtension
910

11+
if TYPE_CHECKING:
12+
from refred.main import MainGui
13+
1014

1115
class SavingConfiguration(object):
12-
parent = None
16+
"""Class to save the current configuration to an XML file."""
1317

14-
def __init__(self, parent=None, filename=""):
18+
def __init__(self, parent: "MainGui", filename: str = ""):
1519
self.parent = parent
1620
self.filename = filename
1721

@@ -41,7 +45,12 @@ def run(self):
4145

4246
self.parent.path_config = os.path.dirname(self.filename)
4347
self.filename = makeSureFileHasExtension(self.filename)
44-
ExportXMLConfig(parent=self.parent).save(self.filename)
48+
export_config = ExportXMLConfig(parent=self.parent)
49+
try:
50+
export_config.save(self.filename)
51+
except (PermissionError, IsADirectoryError) as e:
52+
StatusMessageHandler(parent=self.parent, message=f"Error: {e}", is_threaded=True)
53+
return
4554

4655
StatusMessageHandler(parent=self.parent, message="Done!", is_threaded=True)
4756

src/refred/interfaces/deadtime_settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,10 @@ def __init__(self, parent: QWidget):
125125
self.ui = load_ui(ui_filename="deadtime_settings.ui", baseinstance=self)
126126
self.options = self.get_state_from_form()
127127

128+
# TODO: Should add default values for these parameters (Glass)
128129
def set_state(self, paralyzable, dead_time, tof_step, use_threshold_ratio, threshold_ratio):
129130
"""
130131
Store options and populate the form
131-
:param apply_correction: If True, dead time correction will be applied
132132
:param paralyzable: If True, a paralyzable correction will be used
133133
:param dead_time: Value of the dead time in micro second
134134
:param tof_step: TOF binning in micro second

src/refred/main.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,13 @@ class MainGui(QtWidgets.QMainWindow):
151151

152152
big_table_data = TableData(REDUCTIONTABLE_MAX_ROWCOUNT)
153153

154+
# TODO: `parent` argument is never used and can probably be removed (Glass)
154155
def __init__(self, parent=None):
155156
if parent is None:
156157
QtWidgets.QMainWindow.__init__(self)
157158
else:
158159
QtWidgets.QMainWindow.__init__(self, parent, QtCore.Qt.Window)
160+
159161
self.ui = load_ui("refred_main_interface.ui", self)
160162

161163
# Get default values for widgets

src/refred/sf_calculator/sf_calculator.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,6 +1054,7 @@ def show_dead_time_dialog(self):
10541054
Pop up dialog for dead time options
10551055
"""
10561056
dt_settings = DeadTimeSettingsView(parent=self)
1057+
# TODO: Missing `use_threshold_ratio` and `threshold_ratio` arguments (Glass)
10571058
dt_settings.set_state(self.paralyzable_deadtime, self.deadtime_value, self.deadtime_tof_step)
10581059
dt_settings.exec_()
10591060

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
from dataclasses import dataclass
2+
from pathlib import Path
3+
from typing import Callable
4+
5+
import pytest
6+
7+
from refred.configuration import saving_configuration
8+
9+
10+
### Helpers to fake the MainGui
11+
@dataclass
12+
class DummyParent:
13+
path_config: str
14+
config_saved: bool = False
15+
16+
17+
def fake_extension_factory(expected_input: Path, sanitized_output: str) -> Callable[[str], str]:
18+
def _fake_extension(path: str) -> str:
19+
assert path == str(expected_input)
20+
return sanitized_output
21+
22+
return _fake_extension
23+
24+
25+
def fake_status_handler_factory(call_collector: list[tuple[str, bool]]):
26+
class _FakeStatusMessageHandler:
27+
def __init__(self, parent: DummyParent, message: str, is_threaded: bool):
28+
call_collector.append((message, is_threaded))
29+
30+
return _FakeStatusMessageHandler
31+
32+
33+
def fake_export_config_factory() -> type:
34+
class _FakeExportXMLConfig:
35+
def __init__(self, parent: DummyParent):
36+
self.parent: DummyParent = parent
37+
38+
def save(self, filename: str):
39+
path = Path(filename)
40+
path.parent.mkdir(parents=True, exist_ok=True)
41+
_ = path.write_text("<Reduction />", encoding="utf-8")
42+
43+
return _FakeExportXMLConfig
44+
45+
46+
def fake_gui_utility_factory(sanitized_path: str) -> type:
47+
class _FakeGuiUtility:
48+
def __init__(self, parent: DummyParent):
49+
self.parent: DummyParent = parent
50+
51+
def new_config_file_loaded(self, config_file_name: str):
52+
assert config_file_name == sanitized_path
53+
self.parent.config_saved = True
54+
55+
def gui_not_modified(self):
56+
pass
57+
58+
return _FakeGuiUtility
59+
60+
61+
### Tests
62+
63+
64+
def test_saving_configuration_good_path(monkeypatch: pytest.MonkeyPatch, tmp_path: Path):
65+
parent = DummyParent(path_config=str(tmp_path))
66+
provided_path = tmp_path / "test_config"
67+
sanitized_path = f"{provided_path}.xml"
68+
69+
monkeypatch.setattr(
70+
saving_configuration,
71+
"makeSureFileHasExtension",
72+
fake_extension_factory(provided_path, sanitized_path),
73+
)
74+
75+
status_messages: list[tuple[str, bool]] = []
76+
monkeypatch.setattr(
77+
saving_configuration,
78+
"StatusMessageHandler",
79+
fake_status_handler_factory(status_messages),
80+
)
81+
82+
monkeypatch.setattr(
83+
saving_configuration,
84+
"ExportXMLConfig",
85+
fake_export_config_factory(),
86+
)
87+
88+
monkeypatch.setattr(
89+
saving_configuration,
90+
"GuiUtility",
91+
fake_gui_utility_factory(sanitized_path),
92+
)
93+
94+
saver = saving_configuration.SavingConfiguration(parent=parent, filename=str(provided_path)) # type: ignore[arg-type]
95+
saver.run()
96+
97+
saved_file = Path(sanitized_path)
98+
assert saved_file.exists()
99+
assert parent.path_config == str(provided_path.parent)
100+
assert parent.config_saved is True
101+
assert status_messages == [("Saving config ...", False), ("Done!", True)]
102+
103+
104+
def test_saving_configuration_permission_error(monkeypatch: pytest.MonkeyPatch, tmp_path: Path):
105+
parent = DummyParent(path_config=str(tmp_path))
106+
provided_path = tmp_path / "forbidden" / "config"
107+
sanitized_path = f"{provided_path}.xml"
108+
109+
monkeypatch.setattr(
110+
saving_configuration,
111+
"makeSureFileHasExtension",
112+
fake_extension_factory(provided_path, sanitized_path),
113+
)
114+
115+
status_messages: list[tuple[str, bool]] = []
116+
monkeypatch.setattr(
117+
saving_configuration,
118+
"StatusMessageHandler",
119+
fake_status_handler_factory(status_messages),
120+
)
121+
122+
class _PermissionErrorExportXMLConfig:
123+
def __init__(self, parent: DummyParent):
124+
self.parent: DummyParent = parent
125+
126+
def save(self, filename: str):
127+
raise PermissionError("No permission to write file.")
128+
129+
monkeypatch.setattr(
130+
saving_configuration,
131+
"ExportXMLConfig",
132+
_PermissionErrorExportXMLConfig,
133+
)
134+
135+
monkeypatch.setattr(
136+
saving_configuration,
137+
"GuiUtility",
138+
fake_gui_utility_factory(sanitized_path),
139+
)
140+
141+
saver = saving_configuration.SavingConfiguration(parent=parent, filename=str(provided_path)) # type: ignore[arg-type]
142+
saver.run()
143+
144+
assert parent.path_config == str(provided_path.parent)
145+
assert parent.config_saved is False
146+
assert status_messages == [
147+
("Saving config ...", False),
148+
("Error: No permission to write file.", True),
149+
]

0 commit comments

Comments
 (0)