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 crashes when deleting to trash (#1798) #1871
Fix crashes when deleting to trash (#1798) #1871
Conversation
The execute() method of the trash command (in ranger/config/commands.py) used to pass a list of file paths (as strings) to fm.execute_file(). The documentation of the execute_file() method states that the 'files' parameter must not be strings: [...] files: a list of file objects (not strings!) [...] So I changed 'files' to be a list of File objects and that seems to fix the issue. Fixes ranger#1798
I think we need to use the actual |
@toonn So you mean I should do something like this? def find_existing_files(self, file_names):
'''
Find file objects corresponding to the file names that are already in
memory and load those that are not.
'''
result = []
for name in file_names:
path = os.path.abspath(os.path.expanduser(name))
if os.path.isdir(path):
result.append(self.fm.get_directory(path))
else:
dirname = os.path.dirname(path)
directory = self.fm.get_directory(dirname)
directory.load_content_if_outdated()
desired_file = trash.find_file_by_path(directory.files_all, path)
if desired_file is not None:
result.append(desired_file)
continue
# Exit the operation with an error message if the file could
# not be found.
self.fm.notify('Error: path does not exist: ' + name, bad=True)
return None
return result
@staticmethod
def find_file_by_path(files, path):
'''
Search a list of File objects for a file matching path.
Assumes that path is absolute.
'''
if files is None:
return None
for file_object in files:
if file_object.path == path:
return file_object
return None Sorry if I misunderstood you; I am not very familiar with ranger's code base. The problem with my implementation above is that it uses linear search to find files. But I didn't find a similar method for files, so I just do linear search in the corresponding directory, which could be a performance issue when deleting lots of files. Edit: To clarify: This is for the case when the user supplies the file(s) to be deleted with the command as a string (instead of deleting the selection—in that case I am already using the |
Something like that, yeah. I don't think the linear search can be avoided, the files are sorted but the order depends on the sorting method the user has set. Maybe you could group files by directory, turn them into a set and then go through the directory linearly once, checking the set for membership and remembering those file objects? |
Previously the File() constructor was called for every path (if the paths to be moved to trash were supplied after the command instead of deleting the selection, e.g. ":trash a b c"). This commit adds a method paths_to_filesystem_objects() to find the existing objects that ranger has in memory and use those instead.
c6a68f3
to
efa3299
Compare
That's a good idea! I've implemented it. There is another problem though: If you trash lots of files you run into the argument list length limitation of the operating system and this actually crashes ranger.
If there are lots of files, the argument list gets too long, so it should actually be split up into multiple calls to While I don't really think this limitation itself is a problem, the fact that it crashes ranger is (IMO). try:
self.fm.execute_file(files, label='trash')
except OSError as e:
self.fm.notify(str(e), bad=True) However, I am not sure if this is the right place to put it. Maybe it would be better to have the Here are the steps to reproduce the crash and the traceback for reference:
Result: Ranger crashes with the following traceback:
|
The trash command used to crash ranger when passing so may arguments that the argument length limit of the OS is reached. See the discussion in pull request ranger#1871 for steps to reproduce. Now it displays an error message instead of crashing. (It does not move the files to trash though.)
This commit also renames the method to "get_filesystem_objects" for symmetry to "get_directory".
I also "fixed" the crash I mentioned above by displaying an error message instead of crashing. |
I hate the redraw when using trash-put, and never use it happily. So I write a simple command plugin to put the file into trash. |
Not only diff --git a/ranger/gui/widgets/statusbar.py b/ranger/gui/widgets/statusbar.py
index fd44613e..245beb62 100644
--- a/ranger/gui/widgets/statusbar.py
+++ b/ranger/gui/widgets/statusbar.py
@@ -192,11 +192,24 @@ class StatusBar(Widget): # pylint: disable=too-many-instance-attributes
directory = target if target.is_directory else \
target.fm.get_directory(os.path.dirname(target.path))
if directory.vcs and directory.vcs.track:
- if directory.vcs.rootvcs.branch:
- vcsinfo = '({0:s}: {1:s})'.format(
- directory.vcs.rootvcs.repotype, directory.vcs.rootvcs.branch)
- else:
- vcsinfo = '({0:s})'.format(directory.vcs.rootvcs.repotype)
+ try:
+ if directory.vcs.rootvcs.branch:
+ vcsinfo = '({0:s}: {1:s})'.format(
+ directory.vcs.rootvcs.repotype, directory.vcs.rootvcs.branch)
+ else:
+ vcsinfo = '({0:s})'.format(directory.vcs.rootvcs.repotype)
+ except TypeError:
+ root_path = directory.vcs.rootvcs.path
+ for path, fdir in self.fm.directories.items():
+ if not fdir.files:
+ continue
+ for fobj in fdir.files:
+ if path.startswith(root_path):
+ if fobj.is_directory and fobj.vcs:
+ del fobj.vcs
+ fobj.vcsstatus = None
+ return
+
left.add_space()
left.add(vcsinfo, 'vcsinfo') |
Any progress on this? |
Reviews and testing reports welcome. |
It seems to have fixed the issue for me on Arch Linux. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two small fixes needed but I'll do those myself when testing.
Thank you for the contribution! I'm sorry it took so long to see it through, especially because the code quality was so high! |
Has this been fixed? I'm on ranger 1.9.3 and i get the exact same error message |
I changed the keybinding in
|
ISSUE TYPE
RUNTIME ENVIRONMENT
Arch Linux, kernel 5.4.23-1-lts, last full pacman update at: 2020-03-05T08:20:22
Python 3.8.1
v1.9.3-18-g081e7315
(output ofgit describe HEAD
)en_US.UTF-8
CHECKLIST
CONTRIBUTING
document has been read [REQUIRED](
make test_py
outputs is the same before and after the change)(
make test_py
output is the same before and after the change)DESCRIPTION
The
execute()
method of the trash command (inranger/config/commands.py
)used to pass a list of file paths (as strings) to
fm.execute_file()
.The documentation of the
execute_file()
method states that the 'files'parameter must not be a list of strings:
So I changed
files
to be a list ofFile
objects and that seems to fix the issue.Note: I'm not sure if I need to pass any additional parameters to the
File
constructor.Passing just the path seemed to work fine in my tests.
(I'm talking about this line:
files = [File(name) for name in file_names]
).MOTIVATION AND CONTEXT
Relevant issue: #1798
TESTING
I ran
make test_py
as I only changed a python file.