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

File: Allow selecting files with arbitrary extensions #6894

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
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
87 changes: 64 additions & 23 deletions Orange/widgets/data/owfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,11 @@
class Error(widget.OWWidget.Error):
file_not_found = Msg("File not found.")
missing_reader = Msg("Missing reader.")
select_file_type = Msg("Select file type.")
sheet_error = Msg("Error listing available sheets.")
unknown = Msg("Read error:\n{}")
unknown_select = Msg(
"Read error, possibly due to incorrect choice of file type:\n{}")

UserAdviceMessages = [
widget.Message(
Expand Down Expand Up @@ -264,7 +267,7 @@
self.reader_combo = QComboBox(self)
self.reader_combo.setSizePolicy(Policy.Expanding, Policy.Fixed)
self.reader_combo.setMinimumSize(QSize(100, 1))
self.reader_combo.activated[int].connect(self.select_reader)
self.reader_combo.activated[int].connect(self.on_reader_change)

box.layout().addWidget(self.reader_combo)
layout.addWidget(box, 0, 1)
Expand Down Expand Up @@ -327,6 +330,10 @@
self.recent_paths[0].sheet = self.sheet_combo.currentText()
self.load_data()

def on_reader_change(self, n):
self.select_reader(n)
self.load_data()

def select_reader(self, n):
if self.source != self.LOCAL_FILE:
return # ignore for URL's
Expand All @@ -335,14 +342,11 @@
path = self.recent_paths[0]
if n == 0: # default
path.file_format = None
self.load_data()
elif n <= len(self.available_readers):
reader = self.available_readers[n - 1]
path.file_format = reader.qualified_name()
self.load_data()
else: # the rest include just qualified names
path.file_format = self.reader_combo.itemText(n)
self.load_data()

def _url_set(self):
index = self.url_combo.currentIndex()
Expand Down Expand Up @@ -373,7 +377,9 @@
else:
start_file = self.last_path() or os.path.expanduser("~/")

filename, reader, _ = open_filename_dialog(start_file, None, self.available_readers)
filename, reader, _ = open_filename_dialog(
start_file, None, self.available_readers,
add_all="*")
if not filename:
return
self.add_path(filename)
Expand Down Expand Up @@ -415,20 +421,20 @@
if not url:
return self.Information.no_file_selected

def mark_problematic_reader():
self.reader_combo.setItemData(self.reader_combo.currentIndex(),
QBrush(Qt.red), Qt.ForegroundRole)

try:
self.reader = self._get_reader() # also sets current reader index
assert self.reader is not None
except MissingReaderException:
mark_problematic_reader()
return self.Error.missing_reader
if self.reader_combo.currentIndex() > 0:
return self.Error.missing_reader
else:
return self.Error.select_file_type
except Exception as ex:
mark_problematic_reader()
log.exception(ex)
return lambda x=ex: self.Error.unknown(str(x))
if self.reader_combo.currentIndex() > 0:
return lambda x=ex: self.Error.unknown(str(x))
else:
return lambda x=ex: self.Error.unknown_select(str(x))

try:
self._update_sheet_combo()
Expand All @@ -439,7 +445,6 @@
try:
data = self.reader.read()
except Exception as ex:
mark_problematic_reader()
log.exception(ex)
return lambda x=ex: self.Error.unknown(str(x))
if warnings:
Expand All @@ -455,9 +460,25 @@
return None

def _get_reader(self) -> FileFormat:
"""
Get the reader for the current file.

For local files, this also observes the stored settings and the reader
combo, as follows:

1. If the file format is known (from stored settings), use it and set
the reader combo to the corresponding index (as in settings)
2. Otherwise, detect it from the extension and set the combo to
Auto detect, overriding any previous user-set choice
3. Otherwise, use the current combo state.

Returns:
FileFormat: reader instance
"""
if self.source == self.LOCAL_FILE:
path = self.last_path()
self.reader_combo.setEnabled(True)

if self.recent_paths and self.recent_paths[0].file_format:
qname = self.recent_paths[0].file_format
qname_index = {r.qualified_name(): i for i, r in enumerate(self.available_readers)}
Expand All @@ -471,11 +492,22 @@
try:
reader_class = class_from_qualified_name(qname)
except Exception as ex:
raise MissingReaderException(f'Can not find reader "{qname}"') from ex
raise MissingReaderException(f'Can not fdind reader "{qname}"') from ex
reader = reader_class(path)

else:
self.reader_combo.setCurrentIndex(0)
reader = FileFormat.get_reader(path)
old_idx = self.reader_combo.currentIndex()
try:
self.reader_combo.setCurrentIndex(0)
reader = FileFormat.get_reader(path)
except MissingReaderException:
if old_idx == 0:
raise
# Set the path for the current file format,
# and repeat the call to return the corresponding reader
self.select_reader(old_idx)
return self._get_reader()

Check warning on line 509 in Orange/widgets/data/owfile.py

View check run for this annotation

Codecov / codecov/patch

Orange/widgets/data/owfile.py#L508-L509

Added lines #L508 - L509 were not covered by tests

if self.recent_paths and self.recent_paths[0].sheet:
reader.select_sheet(self.recent_paths[0].sheet)
return reader
Expand Down Expand Up @@ -504,12 +536,21 @@
self.sheet_combo.setCurrentIndex(0)

def _initialize_reader_combo(self):
self.reader_combo.clear()
filters = [format_filter(f) for f in self.available_readers]
self.reader_combo.addItems([DEFAULT_READER_TEXT] + filters)
self.reader_combo.setCurrentIndex(0)
self.reader_combo.setDisabled(True)
# additional readers may be added in self._get_reader()
# Reset to initial state without losing the current index or
# emitting any signals.
combo = self.reader_combo
if not combo.count():
filters = [format_filter(f) for f in self.available_readers]
combo.addItems([DEFAULT_READER_TEXT] + filters)
combo.setCurrentIndex(0)
else:
# additional readers may be added in self._get_reader()
n = len(self.available_readers) + 1
if combo.currentIndex() >= n:
combo.setCurrentIndex(0)
while combo.count() > n:
combo.removeItem(combo.count() - 1)
combo.setDisabled(True)

@staticmethod
def _describe(table):
Expand Down
4 changes: 2 additions & 2 deletions Orange/widgets/data/tests/test_owfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,13 +361,13 @@ def test_reader_custom_tab(self):
outdata = self.get_output(self.widget.Outputs.data)
self.assertEqual(len(outdata), 150) # loaded iris

def test_no_reader_extension(self):
def test_unknown_extension(self):
with named_file("", suffix=".xyz_unknown") as fn:
no_reader = RecentPath(fn, None, None)
self.widget = self.create_widget(OWFile,
stored_settings={"recent_paths": [no_reader]})
self.widget.load_data()
self.assertTrue(self.widget.Error.missing_reader.is_shown())
self.assertTrue(self.widget.Error.select_file_type.is_shown())

def test_fail_sheets(self):
with named_file("", suffix=".failed_sheet") as fn:
Expand Down
Loading