Repository: biolab/orange3

Issue:
Save Data crashes on no data
##### Orange version
3.16, caused by #3147 

##### Actual behavior
Error that is_spare does not exist for None object.

##### Steps to reproduce the behavior
Connect and disconnect a file to the File widget. You can also connect an empty selection.



Discussion:

Resolving patch:
diff --git a/Orange/widgets/data/owsave.py b/Orange/widgets/data/owsave.py
--- a/Orange/widgets/data/owsave.py
+++ b/Orange/widgets/data/owsave.py
@@ -129,15 +129,18 @@ def dataset(self, data):
         self.data = data
         self.save.setDisabled(data is None)
         self.save_as.setDisabled(data is None)
-        if data is not None:
-            self.save_file()
+        if data is None:
+            return
 
-        self.controls.filetype.clear()
-        if self.data.is_sparse():
-            self.controls.filetype.insertItems(0, [item for item, _, supports_sparse in FILE_TYPES
-                                                   if supports_sparse])
-        else:
-            self.controls.filetype.insertItems(0, [item for item, _, _ in FILE_TYPES])
+        items = [item for item, _, supports_sparse in FILE_TYPES
+                 if supports_sparse or not data.is_sparse()]
+        if items != [self.controls.filetype.itemText(i) for i in
+                     range(self.controls.filetype.count())]:
+            self.controls.filetype.clear()
+            self.controls.filetype.insertItems(0, items)
+            self.update_extension()
+
+        self.save_file()
 
     def save_file_as(self):
         file_name = self.remove_extensions(self.filename) or os.path.join(


Test patch:
diff --git a/Orange/widgets/data/tests/test_owsave.py b/Orange/widgets/data/tests/test_owsave.py
--- a/Orange/widgets/data/tests/test_owsave.py
+++ b/Orange/widgets/data/tests/test_owsave.py
@@ -1,6 +1,6 @@
 # Test methods with long descriptive names can omit docstrings
 # pylint: disable=missing-docstring
-from unittest.mock import patch
+from unittest.mock import patch, Mock
 import itertools
 
 from Orange.data import Table
@@ -82,3 +82,34 @@ def choose_file(a, b, c, d, e, fn=filename, w=writer):
                 with patch("AnyQt.QtWidgets.QFileDialog.getSaveFileName", choose_file):
                     self.widget.save_file_as()
                 self.assertEqual(len(Table(filename)), 150)
+
+    def test_format_combo(self):
+        widget = self.widget
+        filetype = widget.controls.filetype
+
+        widget.save_file = Mock()
+
+        data = Table("iris")
+        sparse_data = Table("iris")
+        sparse_data.is_sparse = Mock(return_value=True)
+
+        self.send_signal(widget.Inputs.data, data)
+        n_nonsparse = filetype.count()
+
+        self.send_signal(widget.Inputs.data, sparse_data)
+        n_sparse = filetype.count()
+        self.assertGreater(n_nonsparse, n_sparse)
+
+        self.send_signal(widget.Inputs.data, sparse_data)
+        self.assertEqual(filetype.count(), n_sparse)
+
+        self.send_signal(widget.Inputs.data, data)
+        self.assertEqual(filetype.count(), n_nonsparse)
+
+        self.send_signal(widget.Inputs.data, None)
+        self.send_signal(widget.Inputs.data, data)
+        self.assertEqual(filetype.count(), n_nonsparse)
+
+        self.send_signal(widget.Inputs.data, None)
+        self.send_signal(widget.Inputs.data, sparse_data)
+        self.assertEqual(filetype.count(), n_sparse)