Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FIX] OWSom: Fix crash for data without class variable #6763

Merged
merged 4 commits into from
Mar 22, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Orange/widgets/unsupervised/owsom.py
Original file line number Diff line number Diff line change
Expand Up @@ -1018,7 +1018,8 @@ def make_domain(values, default_grp, offset):
compute_value=GetGroups(id_to_group, default_grp, offset))

if not self.data.domain.class_vars:
class_vars, metas = grp_var, som_attrs
self.data.domain.class_vars = ()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove the above. We consider Domain immutable, so class_vars must not be changed, neither is there any need to change them because that already equal ().

class_vars, metas = (grp_var,), som_attrs
else:
class_vars, metas = (), (grp_var,) + som_attrs
return add_columns(self.data.domain, (), class_vars, metas)
Expand Down
7 changes: 7 additions & 0 deletions Orange/widgets/unsupervised/tests/test_owsom.py
Original file line number Diff line number Diff line change
Expand Up @@ -723,6 +723,13 @@ def test_modified_info(self):
restart_button.click()
self.assertFalse(w.Information.modified.is_shown())

def test_make_domain_without_class_vars(self):
widget = self.widget
self.send_signal(self.widget.Inputs.data, self.iris)
widget.data.domain.class_vars = None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You must not do this. widget.data is a data table, and widget.data.domain describes its columns. By removing classes, the table and its domain are inconsistent, and anything can happen. (Also. domain is internally inconsistent because when class_var is not None, class_vars equals (class_var, ).)

Instead, you must construct a new domain and convert the table.

        data = self.iris.transform(Domain(self.iris.domain.attributes))
        self.send_signal(self.widget.Inputs.data, data)

The first line creates a new data table without class, and the second line sends it to the widget. Then you can remove widget.update_output().

widget.update_output()
self.assertIsNotNone(self.get_output(widget.Outputs.annotated_data))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't test much. You may want to test two things. First, if you want to test that the widget no longer crashes, this line is unnecessary -- it would crash in the previous line (prior to your fix). But a better test would be that the widget constructs a correct domain. The proper test is

    def test_make_domain_without_class_vars(self):
        widget = self.widget
        data = self.iris.transform(Domain(self.iris.domain.attributes))
        self.send_signal(self.widget.Inputs.data, data)

        domain = self.get_output((widget.Outputs.annotated_data)).domain
        self.assertEqual(domain.attributes, data.domain.attributes)
        self.assertEqual(domain.class_var.name, ANNOTATED_DATA_FEATURE_NAME)
        self.assertEqual([var.name for var in domain.metas],
                         ["som_cell", "som_row", "som_col", "som_error"])



class TestComputeValues(unittest.TestCase):
def test_eq_hash(self):
Expand Down
Loading