Skip to content

Commit

Permalink
FileFormats: get_file_readers() replaces readers dict
Browse files Browse the repository at this point in the history
  • Loading branch information
markotoplak committed Oct 4, 2017
1 parent 5c256e0 commit 373e7e8
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 20 deletions.
32 changes: 21 additions & 11 deletions Orange/data/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
_io, is_discrete_values, MISSING_VALUES, Table, Domain, Variable,
DiscreteVariable, StringVariable, ContinuousVariable, TimeVariable,
)
from Orange.util import Registry, flatten, namegen
from Orange.util import Registry, flatten, namegen, deprecated


# Support values longer than 128K (i.e. text contents features)
Expand Down Expand Up @@ -262,7 +262,6 @@ def split(s):
'|'.join(flatten(filter(None, i) for i in Flags.ALL.items()))
))


class FileFormatMeta(Registry):

def __new__(cls, name, bases, attrs):
Expand All @@ -287,7 +286,8 @@ def __new__(cls, name, bases, attrs):

@property
def formats(cls):
return cls.registry.values()
# also sort by description to make order predictable
return iter(sorted(cls.registry.values(), key=lambda x: (x.PRIORITY, x.DESCRIPTION)))

@lru_cache(5)
def _ext_to_attr_if_attr2(cls, attr, attr2):
Expand All @@ -299,7 +299,7 @@ def _ext_to_attr_if_attr2(cls, attr, attr2):
with the lowest priority.
"""
formats = OrderedDict()
for format in sorted(cls.registry.values(), key=lambda x: x.PRIORITY):
for format in cls.formats:
if not hasattr(format, attr2):
continue
for ext in getattr(format, 'EXTENSIONS', []):
Expand All @@ -316,6 +316,7 @@ def writers(cls):
return cls._ext_to_attr_if_attr2('', 'write_file')

@property
@deprecated
def readers(cls):
return cls._ext_to_attr_if_attr2('', 'read')

Expand All @@ -327,6 +328,10 @@ def img_writers(cls):
def graph_writers(cls):
return cls._ext_to_attr_if_attr2('', 'write_graph')

def get_file_readers(cls):
return (f for f in cls.formats
if getattr(f, 'read', None) and f.EXTENSIONS)


class FileFormat(metaclass=FileFormatMeta):
"""
Expand Down Expand Up @@ -354,6 +359,8 @@ def write_file(cls, filename, data):
# Priority when multiple formats support the same extension. Also
# the sort order in file open/save combo boxes. Lower is better.
PRIORITY = 10000
DESCRIPTION = ""
EXTENSIONS = ()

def __init__(self, filename):
"""
Expand Down Expand Up @@ -398,12 +405,13 @@ def get_reader(cls, filename):
-------
FileFormat
"""
for ext, reader in cls.readers.items():
# Skip ambiguous, invalid compression-only extensions added on OSX
if ext in Compression.all:
continue
if fnmatch(path.basename(filename), '*' + ext):
return reader(filename)
for reader in cls.get_file_readers():
for ext in reader.EXTENSIONS:
# Skip ambiguous, invalid compression-only extensions added on OSX
if ext in Compression.all:
continue
if fnmatch(path.basename(filename), '*' + ext):
return reader(filename)

raise IOError('No readers for file "{}"'.format(filename))

Expand Down Expand Up @@ -464,11 +472,13 @@ def locate(cls, filename, search_dirs=('.',)):
if path.exists(filename):
return filename

supported_extensions = set(chain.from_iterable(
r.EXTENSIONS for r in cls.get_file_readers()))
for directory in search_dirs:
absolute_filename = path.join(directory, filename)
if path.exists(absolute_filename):
break
for ext in cls.readers:
for ext in supported_extensions:
if fnmatch(path.basename(filename), '*' + ext):
break
# glob uses fnmatch internally
Expand Down
6 changes: 4 additions & 2 deletions Orange/tests/test_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -1334,9 +1334,11 @@ def test_read_data_calls_reader(self):
table_mock = Mock(data.Table)
reader_instance = Mock(read=Mock(return_value=table_mock))
reader_mock = Mock(return_value=reader_instance)
reader_mock.EXTENSIONS = (".xlsx",)

with patch.dict(data.io.FileFormat.readers,
{'.xlsx': reader_mock}):
old_readers = list(data.io.FileFormat.get_file_readers())
with patch.object(data.io.FileFormat, "get_file_readers",
lambda: [reader_mock] + old_readers):
table = data.Table.from_file("test.xlsx")

reader_mock.assert_called_with("test.xlsx")
Expand Down
3 changes: 1 addition & 2 deletions Orange/widgets/data/owfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,7 @@ def browse_file(self, in_demos=False):
else:
start_file = self.last_path() or os.path.expanduser("~/")

readers = [f for f in FileFormat.formats
if getattr(f, 'read', None) and getattr(f, "EXTENSIONS", None)]
readers = FileFormat.get_file_readers()
filename, reader, _ = get_file_name_open(start_file, None, readers)
if not filename:
return
Expand Down
12 changes: 7 additions & 5 deletions Orange/widgets/utils/filedialogs.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from itertools import chain
import os

from AnyQt.QtCore import QFileInfo, Qt
Expand Down Expand Up @@ -45,11 +46,12 @@ def dialog_formats():
"""
Return readable file types for QFileDialogs.
"""
formats = list(FileFormat.get_file_readers())
all_extensions = sorted(set(chain.from_iterable(
r.EXTENSIONS for r in formats)))
return ("All readable files ({});;".format(
'*' + ' *'.join(FileFormat.readers.keys())) +
";;".join("{} (*{})".format(f.DESCRIPTION, ' *'.join(f.EXTENSIONS))
for f in sorted(set(FileFormat.readers.values()),
key=list(FileFormat.readers.values()).index)))
'*' + ' *'.join(all_extensions)) +
";;".join(format_filter(f) for f in formats))


def get_file_name(start_dir, start_filter, file_formats):
Expand Down Expand Up @@ -118,7 +120,7 @@ def get_file_name_open(start_dir, start_filter, file_formats, title="Open...",
Returns:
(filename, filter, file_format), or `(None, None, None)` on cancel
"""
file_formats = sorted(set(file_formats), key=lambda w: (w.PRIORITY, w.DESCRIPTION))
file_formats = list(file_formats)
filters = [format_filter(f) for f in file_formats]

# add all readable files option
Expand Down

0 comments on commit 373e7e8

Please sign in to comment.