%PDF- %PDF-
| Direktori : /lib/calibre/calibre/utils/ |
| Current File : //lib/calibre/calibre/utils/recycle_bin.py |
#!/usr/bin/env python3
__license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os, shutil, time, sys
from calibre import isbytestring
from calibre.constants import (iswindows, ismacos, filesystem_encoding,
islinux)
recycle = None
if iswindows:
from calibre.utils.ipc import eintr_retry_call
from threading import Lock
from calibre_extensions import winutil
recycler = None
rlock = Lock()
def start_recycler():
global recycler
if recycler is None:
from calibre.utils.ipc.simple_worker import start_pipe_worker
recycler = start_pipe_worker('from calibre.utils.recycle_bin import recycler_main; recycler_main()')
def recycle_path(path):
winutil.move_to_trash(str(path))
def recycler_main():
stdin = getattr(sys.stdin, 'buffer', sys.stdin)
stdout = getattr(sys.stdout, 'buffer', sys.stdout)
while True:
path = eintr_retry_call(stdin.readline)
if not path:
break
try:
path = path.decode('utf-8').rstrip()
except (ValueError, TypeError):
break
try:
recycle_path(path)
except:
eintr_retry_call(stdout.write, b'KO\n')
stdout.flush()
try:
import traceback
traceback.print_exc() # goes to stderr, which is the same as for parent process
except Exception:
pass # Ignore failures to write the traceback, since GUI processes on windows have no stderr
else:
eintr_retry_call(stdout.write, b'OK\n')
stdout.flush()
def delegate_recycle(path):
if '\n' in path:
raise ValueError('Cannot recycle paths that have newlines in them (%r)' % path)
with rlock:
start_recycler()
recycler.stdin.write(path.encode('utf-8'))
recycler.stdin.write(b'\n')
recycler.stdin.flush()
# Theoretically this could be made non-blocking using a
# thread+queue, however the original implementation was blocking,
# so I am leaving it as blocking.
result = eintr_retry_call(recycler.stdout.readline)
if result.rstrip() != b'OK':
raise RuntimeError('recycler failed to recycle: %r' % path)
def recycle(path):
# We have to run the delete to recycle bin in a separate process as the
# morons who wrote SHFileOperation designed it to spin the event loop
# even when no UI is created. And there is no other way to send files
# to the recycle bin on windows. Le Sigh. So we do it in a worker
# process. Unfortunately, if the worker process exits immediately after
# deleting to recycle bin, winblows does not update the recycle bin
# icon. Le Double Sigh. So we use a long lived worker process, that is
# started on first recycle, and sticks around to handle subsequent
# recycles.
if isinstance(path, bytes):
path = path.decode(filesystem_encoding)
path = os.path.abspath(path) # Windows does not like recycling relative paths
return delegate_recycle(path)
elif ismacos:
from calibre_extensions.cocoa import send2trash
def osx_recycle(path):
if isbytestring(path):
path = path.decode(filesystem_encoding)
send2trash(path)
recycle = osx_recycle
elif islinux:
from calibre.utils.linux_trash import send2trash
def fdo_recycle(path):
if isbytestring(path):
path = path.decode(filesystem_encoding)
path = os.path.abspath(path)
send2trash(path)
recycle = fdo_recycle
can_recycle = callable(recycle)
def nuke_recycle():
global can_recycle
can_recycle = False
def restore_recyle():
global can_recycle
can_recycle = callable(recycle)
def delete_file(path, permanent=False):
if not permanent and can_recycle:
try:
recycle(path)
return
except:
import traceback
traceback.print_exc()
os.remove(path)
def delete_tree(path, permanent=False):
if permanent:
try:
# For completely mysterious reasons, sometimes a file is left open
# leading to access errors. If we get an exception, wait and hope
# that whatever has the file (Antivirus, DropBox?) lets go of it.
shutil.rmtree(path)
except:
import traceback
traceback.print_exc()
time.sleep(1)
shutil.rmtree(path)
else:
if can_recycle:
try:
recycle(path)
return
except:
import traceback
traceback.print_exc()
delete_tree(path, permanent=True)