Skip to content

Commit 2177d80

Browse files
committed
OWSave: Implement get_filters() for dynamic retrieval of available filters
1 parent a8e251f commit 2177d80

File tree

3 files changed

+42
-15
lines changed

3 files changed

+42
-15
lines changed

Orange/data/io.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,8 @@ def write_file(cls, filename, data):
395395
# the sort order in file open/save combo boxes. Lower is better.
396396
PRIORITY = 10000
397397
OPTIONAL_TYPE_ANNOTATIONS = False
398+
SUPPORT_COMPRESSED = False
399+
SUPPORT_SPARSE_DATA = False
398400

399401
def __init__(self, filename):
400402
"""

Orange/widgets/data/owsave.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import os.path
22

33
from Orange.data.table import Table
4-
from Orange.data.io import TabReader, CSVReader, PickleReader, ExcelReader, \
5-
XlsReader
4+
from Orange.data.io import \
5+
TabReader, CSVReader, PickleReader, ExcelReader, XlsReader, FileFormat
66
from Orange.widgets import gui, widget
77
from Orange.widgets.widget import Input
88
from Orange.widgets.settings import Setting
@@ -22,14 +22,6 @@ class OWSave(OWSaveBase):
2222

2323
settings_version = 2
2424

25-
writers = [TabReader, CSVReader, PickleReader, ExcelReader, XlsReader]
26-
filters = {
27-
**{f"{w.DESCRIPTION} (*{w.EXTENSIONS[0]})": w
28-
for w in writers},
29-
**{f"Compressed {w.DESCRIPTION} (*{w.EXTENSIONS[0]}.gz)": w
30-
for w in writers if w.SUPPORT_COMPRESSED}
31-
}
32-
3325
class Inputs:
3426
data = Input("Data", Table)
3527

@@ -38,6 +30,8 @@ class Error(OWSaveBase.Error):
3830

3931
add_type_annotations = Setting(True)
4032

33+
builtin_order = [TabReader, CSVReader, PickleReader, ExcelReader, XlsReader]
34+
4135
def __init__(self):
4236
super().__init__(2)
4337

@@ -53,6 +47,21 @@ def __init__(self):
5347
self.grid.setRowMinimumHeight(1, 8)
5448
self.adjustSize()
5549

50+
@classmethod
51+
def get_filters(cls):
52+
writers = [format for format in FileFormat.formats
53+
if getattr(format, 'write_file', None)
54+
and getattr(format, "EXTENSIONS", None)]
55+
writers.sort(key=lambda writer: cls.builtin_order.index(writer)
56+
if writer in cls.builtin_order else 99)
57+
58+
return {
59+
**{f"{w.DESCRIPTION} (*{w.EXTENSIONS[0]})": w
60+
for w in writers},
61+
**{f"Compressed {w.DESCRIPTION} (*{w.EXTENSIONS[0]}.gz)": w
62+
for w in writers if w.SUPPORT_COMPRESSED}
63+
}
64+
5665
@Inputs.data
5766
def dataset(self, data):
5867
self.data = data
@@ -137,7 +146,7 @@ def default_valid_filter(self):
137146
valid = self.valid_filters()
138147
if self.data is None or not self.data.is_sparse() \
139148
or (self.filter in valid
140-
and valid[self.filter].SUPPORT_SPARSE_DATA):
149+
and valid[self.filter].SUPPORT_SPARSE_DATA):
141150
return self.filter
142151
return next(iter(valid))
143152

Orange/widgets/data/tests/test_owsave.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from AnyQt.QtWidgets import QFileDialog
99

1010
from Orange.data import Table
11-
from Orange.data.io import TabReader, PickleReader, ExcelReader
11+
from Orange.data.io import TabReader, PickleReader, ExcelReader, FileFormat
1212
from Orange.tests import named_file
1313
from Orange.widgets.data.owsave import OWSave, OWSaveBase
1414
from Orange.widgets.utils.save.tests.test_owsavebase import \
@@ -23,6 +23,15 @@ def _w(s): # pylint: disable=invalid-name
2323
return s.replace("/", os.sep)
2424

2525

26+
class MockFormat(FileFormat):
27+
EXTENSIONS = ('.mock',)
28+
DESCRIPTION = "Mock file format"
29+
30+
@staticmethod
31+
def write_file(filename, data):
32+
pass
33+
34+
2635
class OWSaveTestBase(WidgetTest, SaveWidgetsTestBaseMixin):
2736
def setUp(self):
2837
with open_widget_classes():
@@ -285,7 +294,12 @@ def test_valid_default_filter(self):
285294
widget.data.X = sp.csr_matrix(widget.data.X)
286295
self.assertTrue(
287296
widget.get_filters()[widget.default_valid_filter()]
288-
.SUPPORT_SPARSE_DATA)
297+
.SUPPORT_SPARSE_DATA)
298+
299+
def test_add_on_writers(self):
300+
# test adding file formats after registering the widget
301+
self.assertIn(MockFormat, self.widget.valid_filters().values())
302+
# this test doesn't call it - test_save_uncompressed does
289303

290304
def test_send_report(self):
291305
widget = self.widget
@@ -387,12 +401,14 @@ def test_save_uncompressed(self):
387401

388402
self.send_signal(widget.Inputs.data, self.iris)
389403
widget.save_file_as()
390-
self.assertEqual(len(Table(filename)), 150)
404+
if hasattr(writer, "read"):
405+
self.assertEqual(len(Table(filename)), 150)
391406

392407
if writer.SUPPORT_SPARSE_DATA:
393408
self.send_signal(widget.Inputs.data, spiris)
394409
widget.save_file()
395-
self.assertEqual(len(Table(filename)), 150)
410+
if hasattr(writer, "read"):
411+
self.assertEqual(len(Table(filename)), 150)
396412

397413

398414
@unittest.skipUnless(sys.platform == "linux", "Tests for dialog on Linux")

0 commit comments

Comments
 (0)