Skip to content

Commit fbc17a1

Browse files
committed
Merge pull request #1949 from janezd/catch-fit-errors
[FIX] Catch errors during learning in learner widgets (cherry picked from commit af3cb70)
1 parent 782b9ea commit fbc17a1

File tree

6 files changed

+55
-11
lines changed

6 files changed

+55
-11
lines changed

Orange/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ class Model:
8989
def __init__(self, domain=None):
9090
if isinstance(self, Learner):
9191
domain = None
92-
elif not domain:
92+
elif domain is None:
9393
raise ValueError("unspecified domain")
9494
self.domain = domain
9595

Orange/widgets/classify/owlogisticregression.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ def create_learner(self):
7777
def update_model(self):
7878
super().update_model()
7979
coef_table = None
80-
if self.valid_data:
80+
if self.model is not None:
8181
coef_table = create_coef_table(self.model)
8282
self.send("Coefficients", coef_table)
8383

Orange/widgets/classify/owsvmclassification.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,8 @@ def _report_kernel_parameters(self, items):
112112

113113
def update_model(self):
114114
super().update_model()
115-
116115
sv = None
117-
if self.valid_data:
116+
if self.model is not None:
118117
sv = self.data[self.model.skl_model.support_]
119118
self.send("Support vectors", sv)
120119

Orange/widgets/regression/owlinearregression.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ def create_learner(self):
124124
def update_model(self):
125125
super().update_model()
126126
coef_table = None
127-
if self.valid_data:
127+
if self.model is not None:
128128
domain = Domain(
129129
[ContinuousVariable("coef", number_of_decimals=7)],
130130
metas=[StringVariable("name")])

Orange/widgets/utils/owlearnerwidget.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ class OWBaseLearner(OWWidget, metaclass=OWBaseLearnerMeta):
110110

111111
class Error(OWWidget.Error):
112112
data_error = Msg("{}")
113+
fitting_failed = Msg("Fitting failed.\n{}")
113114

114115
class Warning(OWWidget.Warning):
115116
outdated_learner = Msg("Press Apply to submit changes.")
@@ -171,14 +172,22 @@ def update_learner(self):
171172
self.outdated_settings = False
172173
self.Warning.outdated_learner.clear()
173174

175+
def show_fitting_failed(self, exc):
176+
"""Show error when fitting fails.
177+
Derived widgets can override this to show more specific messages."""
178+
self.Error.fitting_failed(str(exc), shown=exc is not None)
179+
174180
def update_model(self):
181+
self.show_fitting_failed(None)
182+
self.model = None
175183
if self.check_data():
176-
self.model = self.learner(self.data)
177-
self.model.name = self.learner_name
178-
self.model.instances = self.data
179-
self.valid_data = True
180-
else:
181-
self.model = None
184+
try:
185+
self.model = self.learner(self.data)
186+
except BaseException as exc:
187+
self.show_fitting_failed(exc)
188+
else:
189+
self.model.name = self.learner_name
190+
self.model.instances = self.data
182191
self.send(self.OUTPUT_MODEL_NAME, self.model)
183192

184193
def check_data(self):
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# pylint: disable=missing-docstring
2+
from Orange.base import Learner, Model
3+
from Orange.data import Table, Domain
4+
from Orange.widgets.utils.owlearnerwidget import OWBaseLearner
5+
from Orange.widgets.tests.base import WidgetTest
6+
7+
8+
class TestOWBaseLearner(WidgetTest):
9+
@classmethod
10+
def setUpClass(cls):
11+
super().setUpClass()
12+
cls.iris = Table("iris")
13+
14+
def test_error_on_learning(self):
15+
"""Check that widget shows error message when learner fails"""
16+
17+
class FailingLearner(Learner):
18+
"""A learner that fails when given data"""
19+
__returns__ = Model
20+
21+
def __call__(self, data, *_):
22+
if data is not None:
23+
raise ValueError("boom")
24+
return Model(Domain([]))
25+
26+
class OWFailingLearner(OWBaseLearner):
27+
"""Widget for the above learner"""
28+
name = learner_name = "foo"
29+
LEARNER = FailingLearner
30+
auto_apply = True
31+
32+
self.widget = self.create_widget(OWFailingLearner)
33+
self.send_signal("Data", self.iris)
34+
self.assertTrue(self.widget.Error.fitting_failed.is_shown())
35+
self.send_signal("Data", None)
36+
self.assertFalse(self.widget.Error.fitting_failed.is_shown())

0 commit comments

Comments
 (0)