mirror of
https://github.com/gryf/ebook-converter.git
synced 2026-01-28 08:45:46 +01:00
Removed lock module.
This commit is contained in:
@@ -11,7 +11,6 @@ from ebook_converter.utils.config_base import (
|
|||||||
StringConfig, json_dumps, json_loads, make_config_dir, plugin_dir, prefs,
|
StringConfig, json_dumps, json_loads, make_config_dir, plugin_dir, prefs,
|
||||||
tweaks, from_json, to_json
|
tweaks, from_json, to_json
|
||||||
)
|
)
|
||||||
from ebook_converter.utils.lock import ExclusiveFile
|
|
||||||
|
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
@@ -225,7 +224,7 @@ class DynamicConfig(dict):
|
|||||||
if clear_current:
|
if clear_current:
|
||||||
self.clear()
|
self.clear()
|
||||||
if os.path.exists(self.file_path):
|
if os.path.exists(self.file_path):
|
||||||
with ExclusiveFile(self.file_path) as f:
|
with open(self.file_path) as f:
|
||||||
raw = f.read()
|
raw = f.read()
|
||||||
if raw:
|
if raw:
|
||||||
try:
|
try:
|
||||||
@@ -241,7 +240,7 @@ class DynamicConfig(dict):
|
|||||||
migrate = bool(d)
|
migrate = bool(d)
|
||||||
if migrate and d:
|
if migrate and d:
|
||||||
raw = json_dumps(d, ignore_unserializable=True)
|
raw = json_dumps(d, ignore_unserializable=True)
|
||||||
with ExclusiveFile(self.file_path) as f:
|
with open(self.file_path) as f:
|
||||||
f.seek(0), f.truncate()
|
f.seek(0), f.truncate()
|
||||||
f.write(raw)
|
f.write(raw)
|
||||||
|
|
||||||
@@ -272,7 +271,7 @@ class DynamicConfig(dict):
|
|||||||
if not os.path.exists(self.file_path):
|
if not os.path.exists(self.file_path):
|
||||||
make_config_dir()
|
make_config_dir()
|
||||||
raw = json_dumps(self)
|
raw = json_dumps(self)
|
||||||
with ExclusiveFile(self.file_path) as f:
|
with open(self.file_path) as f:
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
f.truncate()
|
f.truncate()
|
||||||
f.write(raw)
|
f.write(raw)
|
||||||
@@ -333,7 +332,7 @@ class XMLConfig(dict):
|
|||||||
def refresh(self, clear_current=True):
|
def refresh(self, clear_current=True):
|
||||||
d = {}
|
d = {}
|
||||||
if os.path.exists(self.file_path):
|
if os.path.exists(self.file_path):
|
||||||
with ExclusiveFile(self.file_path) as f:
|
with open(self.file_path) as f:
|
||||||
raw = f.read()
|
raw = f.read()
|
||||||
try:
|
try:
|
||||||
d = self.raw_to_object(raw) if raw.strip() else {}
|
d = self.raw_to_object(raw) if raw.strip() else {}
|
||||||
@@ -392,7 +391,7 @@ class XMLConfig(dict):
|
|||||||
dpath = os.path.dirname(self.file_path)
|
dpath = os.path.dirname(self.file_path)
|
||||||
if not os.path.exists(dpath):
|
if not os.path.exists(dpath):
|
||||||
os.makedirs(dpath, mode=constants_old.CONFIG_DIR_MODE)
|
os.makedirs(dpath, mode=constants_old.CONFIG_DIR_MODE)
|
||||||
with ExclusiveFile(self.file_path) as f:
|
with open(self.file_path) as f:
|
||||||
raw = self.to_raw()
|
raw = self.to_raw()
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
f.truncate()
|
f.truncate()
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ from ebook_converter.constants_old import config_dir
|
|||||||
from ebook_converter.constants_old import filesystem_encoding
|
from ebook_converter.constants_old import filesystem_encoding
|
||||||
from ebook_converter.constants_old import iswindows
|
from ebook_converter.constants_old import iswindows
|
||||||
from ebook_converter.constants_old import preferred_encoding
|
from ebook_converter.constants_old import preferred_encoding
|
||||||
from ebook_converter.utils.lock import ExclusiveFile
|
|
||||||
from ebook_converter.utils.date import isoformat
|
from ebook_converter.utils.date import isoformat
|
||||||
from ebook_converter.utils import iso8601
|
from ebook_converter.utils import iso8601
|
||||||
|
|
||||||
@@ -374,9 +373,9 @@ class Config(ConfigInterface):
|
|||||||
migrate = False
|
migrate = False
|
||||||
path = self.config_file_path
|
path = self.config_file_path
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
with ExclusiveFile(path) as f:
|
with open(path) as f:
|
||||||
try:
|
try:
|
||||||
src = f.read().decode('utf-8')
|
src = f.read()
|
||||||
except ValueError:
|
except ValueError:
|
||||||
print("Failed to parse", path)
|
print("Failed to parse", path)
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
@@ -384,7 +383,7 @@ class Config(ConfigInterface):
|
|||||||
path = path.rpartition('.')[0]
|
path = path.rpartition('.')[0]
|
||||||
try:
|
try:
|
||||||
with open(path, 'rb') as f:
|
with open(path, 'rb') as f:
|
||||||
src = f.read().decode('utf-8')
|
src = f.read()
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
@@ -393,7 +392,7 @@ class Config(ConfigInterface):
|
|||||||
if migrate:
|
if migrate:
|
||||||
new_src = self.option_set.serialize(ans,
|
new_src = self.option_set.serialize(ans,
|
||||||
ignore_unserializable=True)
|
ignore_unserializable=True)
|
||||||
with ExclusiveFile(self.config_file_path) as f:
|
with open(self.config_file_path, 'w') as f:
|
||||||
f.seek(0), f.truncate()
|
f.seek(0), f.truncate()
|
||||||
f.write(new_src)
|
f.write(new_src)
|
||||||
return ans
|
return ans
|
||||||
@@ -405,13 +404,14 @@ class Config(ConfigInterface):
|
|||||||
if not os.path.exists(config_dir):
|
if not os.path.exists(config_dir):
|
||||||
make_config_dir()
|
make_config_dir()
|
||||||
|
|
||||||
with ExclusiveFile(self.config_file_path) as f:
|
with open(self.config_file_path) as f:
|
||||||
src = f.read()
|
src = f.read()
|
||||||
opts = self.option_set.parse_string(src)
|
opts = self.option_set.parse_string(src)
|
||||||
setattr(opts, name, val)
|
setattr(opts, name, val)
|
||||||
src = self.option_set.serialize(opts)
|
src = self.option_set.serialize(opts)
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
f.truncate()
|
f.truncate()
|
||||||
|
__import__('pdb').set_trace()
|
||||||
if isinstance(src, str):
|
if isinstance(src, str):
|
||||||
src = src.encode('utf-8')
|
src = src.encode('utf-8')
|
||||||
f.write(src)
|
f.write(src)
|
||||||
|
|||||||
@@ -1,206 +0,0 @@
|
|||||||
# License: GPLv3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
|
||||||
|
|
||||||
import atexit
|
|
||||||
import errno
|
|
||||||
import os
|
|
||||||
import stat
|
|
||||||
import tempfile
|
|
||||||
import time
|
|
||||||
from functools import partial
|
|
||||||
|
|
||||||
from ebook_converter.constants_old import (
|
|
||||||
__appname__, fcntl, filesystem_encoding, islinux, isosx, iswindows, plugins
|
|
||||||
)
|
|
||||||
from ebook_converter.utils.monotonic import monotonic
|
|
||||||
|
|
||||||
# speedup = plugins['speedup'][0]
|
|
||||||
if iswindows:
|
|
||||||
import msvcrt, win32file, pywintypes, winerror, win32api, win32event
|
|
||||||
from ebook_converter.constants_old import get_windows_username
|
|
||||||
excl_file_mode = stat.S_IREAD | stat.S_IWRITE
|
|
||||||
else:
|
|
||||||
excl_file_mode = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH
|
|
||||||
|
|
||||||
|
|
||||||
def unix_open(path):
|
|
||||||
flags = os.O_RDWR | os.O_CREAT
|
|
||||||
has_cloexec = False
|
|
||||||
#if hasattr(speedup, 'O_CLOEXEC'):
|
|
||||||
# try:
|
|
||||||
# fd = os.open(path, flags | speedup.O_CLOEXEC, excl_file_mode)
|
|
||||||
# has_cloexec = True
|
|
||||||
# except EnvironmentError as err:
|
|
||||||
# # Kernel may not support O_CLOEXEC
|
|
||||||
# if err.errno != errno.EINVAL:
|
|
||||||
# raise
|
|
||||||
|
|
||||||
if not has_cloexec:
|
|
||||||
fd = os.open(path, flags, excl_file_mode)
|
|
||||||
fcntl.fcntl(fd, fcntl.F_SETFD, fcntl.FD_CLOEXEC)
|
|
||||||
return os.fdopen(fd, 'r+b')
|
|
||||||
|
|
||||||
|
|
||||||
def unix_retry(err):
|
|
||||||
return err.errno in (errno.EACCES, errno.EAGAIN, errno.ENOLCK, errno.EINTR)
|
|
||||||
|
|
||||||
|
|
||||||
def windows_open(path):
|
|
||||||
if isinstance(path, bytes):
|
|
||||||
path = path.decode('mbcs')
|
|
||||||
try:
|
|
||||||
h = win32file.CreateFileW(
|
|
||||||
path,
|
|
||||||
win32file.GENERIC_READ |
|
|
||||||
win32file.GENERIC_WRITE, # Open for reading and writing
|
|
||||||
0, # Open exclusive
|
|
||||||
None, # No security attributes, ensures handle is not inherited by children
|
|
||||||
win32file.OPEN_ALWAYS, # If file does not exist, create it
|
|
||||||
win32file.FILE_ATTRIBUTE_NORMAL, # Normal attributes
|
|
||||||
None, # No template file
|
|
||||||
)
|
|
||||||
except pywintypes.error as err:
|
|
||||||
raise WindowsError(err[0], err[2], path)
|
|
||||||
fd = msvcrt.open_osfhandle(h.Detach(), 0)
|
|
||||||
return os.fdopen(fd, 'r+b')
|
|
||||||
|
|
||||||
|
|
||||||
def windows_retry(err):
|
|
||||||
return err.winerror in (
|
|
||||||
winerror.ERROR_SHARING_VIOLATION, winerror.ERROR_LOCK_VIOLATION
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def retry_for_a_time(timeout, sleep_time, func, error_retry, *args):
|
|
||||||
limit = monotonic() + timeout
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
return func(*args)
|
|
||||||
except EnvironmentError as err:
|
|
||||||
if not error_retry(err) or monotonic() > limit:
|
|
||||||
raise
|
|
||||||
time.sleep(sleep_time)
|
|
||||||
|
|
||||||
|
|
||||||
def _lock_file(path, timeout=15, sleep_time=0.2):
|
|
||||||
if iswindows:
|
|
||||||
return retry_for_a_time(
|
|
||||||
timeout, sleep_time, windows_open, windows_retry, path
|
|
||||||
)
|
|
||||||
f = unix_open(path)
|
|
||||||
retry_for_a_time(
|
|
||||||
timeout, sleep_time, fcntl.flock, unix_retry,
|
|
||||||
f.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB
|
|
||||||
)
|
|
||||||
return f
|
|
||||||
|
|
||||||
|
|
||||||
def lock_file(path, timeout=15, sleep_time=0.2):
|
|
||||||
from filelock import FileLock
|
|
||||||
lock = FileLock(path + '.lock', timeout=timeout)
|
|
||||||
with lock:
|
|
||||||
return unix_open(path)
|
|
||||||
|
|
||||||
|
|
||||||
class ExclusiveFile(object):
|
|
||||||
|
|
||||||
def __init__(self, path, timeout=15, sleep_time=0.2):
|
|
||||||
if iswindows and isinstance(path, bytes):
|
|
||||||
path = path.decode(filesystem_encoding)
|
|
||||||
self.path = path
|
|
||||||
self.timeout = timeout
|
|
||||||
self.sleep_time = sleep_time
|
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
self.file = lock_file(self.path, self.timeout, self.sleep_time)
|
|
||||||
return self.file
|
|
||||||
|
|
||||||
def __exit__(self, type, value, traceback):
|
|
||||||
self.file.close()
|
|
||||||
|
|
||||||
|
|
||||||
def _clean_lock_file(file_obj):
|
|
||||||
try:
|
|
||||||
os.remove(file_obj.name)
|
|
||||||
except EnvironmentError:
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
file_obj.close()
|
|
||||||
except EnvironmentError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
if iswindows:
|
|
||||||
|
|
||||||
def create_single_instance_mutex(name, per_user=True):
|
|
||||||
mutexname = '{}-singleinstance-{}-{}'.format(
|
|
||||||
__appname__, (get_windows_username() if per_user else ''), name
|
|
||||||
)
|
|
||||||
mutex = win32event.CreateMutex(None, False, mutexname)
|
|
||||||
if not mutex:
|
|
||||||
return
|
|
||||||
err = win32api.GetLastError()
|
|
||||||
if err == winerror.ERROR_ALREADY_EXISTS:
|
|
||||||
# Close this handle other wise this handle will prevent the mutex
|
|
||||||
# from being deleted when the process that created it exits.
|
|
||||||
win32api.CloseHandle(mutex)
|
|
||||||
return
|
|
||||||
return partial(win32api.CloseHandle, mutex)
|
|
||||||
|
|
||||||
elif islinux:
|
|
||||||
|
|
||||||
def create_single_instance_mutex(name, per_user=True):
|
|
||||||
import socket
|
|
||||||
from ebook_converter.utils.ipc import eintr_retry_call
|
|
||||||
name = '%s-singleinstance-%s-%s' % (
|
|
||||||
__appname__, (os.geteuid() if per_user else ''), name
|
|
||||||
)
|
|
||||||
name = name
|
|
||||||
address = '\0' + name.replace(' ', '_')
|
|
||||||
sock = socket.socket(family=socket.AF_UNIX)
|
|
||||||
try:
|
|
||||||
eintr_retry_call(sock.bind, address)
|
|
||||||
except socket.error as err:
|
|
||||||
if getattr(err, 'errno', None) == errno.EADDRINUSE:
|
|
||||||
return
|
|
||||||
raise
|
|
||||||
fd = sock.fileno()
|
|
||||||
old_flags = fcntl.fcntl(fd, fcntl.F_GETFD)
|
|
||||||
fcntl.fcntl(fd, fcntl.F_SETFD, old_flags | fcntl.FD_CLOEXEC)
|
|
||||||
return sock.close
|
|
||||||
|
|
||||||
else:
|
|
||||||
|
|
||||||
def singleinstance_path(name, per_user=True):
|
|
||||||
name = '%s-singleinstance-%s-%s.lock' % (
|
|
||||||
__appname__, (os.geteuid() if per_user else ''), name
|
|
||||||
)
|
|
||||||
home = os.path.expanduser('~')
|
|
||||||
locs = ['/var/lock', home, tempfile.gettempdir()]
|
|
||||||
if isosx:
|
|
||||||
locs.insert(0, '/Library/Caches')
|
|
||||||
for loc in locs:
|
|
||||||
if os.access(loc, os.W_OK | os.R_OK | os.X_OK):
|
|
||||||
return os.path.join(loc, ('.' if loc is home else '') + name)
|
|
||||||
raise EnvironmentError(
|
|
||||||
'Failed to find a suitable filesystem location for the lock file'
|
|
||||||
)
|
|
||||||
|
|
||||||
def create_single_instance_mutex(name, per_user=True):
|
|
||||||
from ebook_converter.utils.ipc import eintr_retry_call
|
|
||||||
path = singleinstance_path(name, per_user)
|
|
||||||
f = open(path, 'w')
|
|
||||||
try:
|
|
||||||
eintr_retry_call(fcntl.lockf, f.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
||||||
return partial(_clean_lock_file, f)
|
|
||||||
except EnvironmentError as err:
|
|
||||||
if err.errno not in (errno.EAGAIN, errno.EACCES):
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
def singleinstance(name):
|
|
||||||
' Ensure that only a single process holding exists with the specified mutex key '
|
|
||||||
release_mutex = create_single_instance_mutex(name)
|
|
||||||
if release_mutex is None:
|
|
||||||
return False
|
|
||||||
atexit.register(release_mutex)
|
|
||||||
return True
|
|
||||||
Reference in New Issue
Block a user