import errno
import traceback
import datetime
+import fcntl
from numbers import Number
from .handler import BaseHandlerError, BaseHandler
-__version__ = '0.1.1'
+__version__ = '0.2.1'
log = logging.getLogger(__name__)
# -------------------------------------------------------------------------
def __init__(
- self, lockfile, ctime=None, mtime=None, fcontent=None, simulate=False,
+ self, lockfile, ctime=None, mtime=None, fcontent=None, fd=None, simulate=False,
autoremove=False, appname=None, verbose=0, version=__version__,
base_dir=None, silent=False):
"""
@type mtime: datetime
@param fcontent: the content of the lockfile
@type fcontent: str
+ @param fd: The numeric file descriptor of the lockfile, if opened, if not opened, then None
+ @type fd: int or None
@param simulate: don't execute actions, only display them
@type simulate: bool
@param autoremove: removing the lockfile on deleting the current object
@return: None
"""
+ self._fd = None
+
super(LockObject, self).__init__(
appname=appname, verbose=verbose, version=version,
base_dir=base_dir, initialized=False,
if not os.path.isfile(lockfile):
raise LockObjectError("Lockfile {!r} is not a regular file.".format(lockfile))
+ if fd is not None:
+ self._fd = fd
+
self._lockfile = os.path.realpath(lockfile)
self._fcontent = None
"""The content of the lockfile."""
return self._fcontent
+ # -----------------------------------------------------------
+ @property
+ def fd(self):
+ "The numeric file descriptor of the lockfile."
+ return self._fd
+
# -----------------------------------------------------------
@property
def simulate(self):
res['simulate'] = self.simulate
res['autoremove'] = self.autoremove
res['silent'] = self.silent
+ res['fd'] = self.fd
return res
fields.append("ctime={!r}".format(self.ctime))
fields.append("mtime={!r}".format(self.mtime))
fields.append("fcontent={!r}".format(self.fcontent))
+ fields.append("fd={!r}".format(self.fd))
fields.append("simulate={!r}".format(self.simulate))
fields.append("autoremove={!r}".format(self.autoremove))
fields.append("silent={!r}".format(self.silent))
if not getattr(self, '_initialized', False):
return
+ if self.fd is not None:
+ msg = "Closing file descriptor {} ...".format(self.fd)
+ if self.silent:
+ if self.verbose >= 2:
+ log.debug(msg)
+ else:
+ log.debug(msg)
+ os.close(self.fd)
+ self._fd = None
+
if self.autoremove and self.exists:
msg = "Automatic removing of {!r} ...".format(self.lockfile)
lockretry_max_delay=DEFAULT_LOCKRETRY_MAX_DELAY,
max_lockfile_age=DEFAULT_MAX_LOCKFILE_AGE,
locking_use_pid=DEFAULT_LOCKING_USE_PID,
- appname=None, verbose=0, version=__version__, base_dir=None,
+ stay_opened=True, appname=None, verbose=0, version=__version__, base_dir=None,
simulate=False, sudo=False, quiet=False, silent=False, *targs, **kwargs):
"""
Initialisation of the locking handler object.
can be used to check the validity of the
lockfile
@type locking_use_pid: bool
+ @param stay_opened: should the lockfile stay opened after creation
+ @@type stay_opened: bool
@param appname: name of the current running application
@type appname: str
@param verbose: verbose level
"""
+ self._stay_opened = bool(stay_opened)
+
super(LockHandler, self).__init__(
appname=appname, verbose=verbose, version=version, base_dir=base_dir,
initialized=False, simulate=simulate, sudo=sudo, quiet=quiet,
def locking_use_pid(self, value):
self._locking_use_pid = bool(value)
+ # -----------------------------------------------------------
+ @property
+ def stay_opened(self):
+ """
+ Should the lockfile stay opened after creation. If yes, then it will be closed
+ on deleting the LockObject.
+ """
+ return self._stay_opened
+
+ @stay_opened.setter
+ def stay_opened(self, value):
+ self._stay_opened = bool(value)
+
# -----------------------------------------------------------
@property
def silent(self):
res['max_lockfile_age'] = self.max_lockfile_age
res['locking_use_pid'] = self.locking_use_pid
res['silent'] = self.silent
+ res['stay_opened'] = self.stay_opened
return res
fields.append("max_lockfile_age=%r" % (self.max_lockfile_age))
fields.append("locking_use_pid=%r" % (self.locking_use_pid))
fields.append("silent=%r" % (self.silent))
+ fields.append("stay_opened=%r" % (self.stay_opened))
if fields:
out += ', ' + ", ".join(fields)
# -------------------------------------------------------------------------
def create_lockfile(
self, lockfile, delay_start=None, delay_increase=None, max_delay=None,
- use_pid=None, max_age=None, pid=None, raise_on_fail=True):
+ use_pid=None, max_age=None, pid=None, raise_on_fail=True, stay_opened=None):
"""
Tries to create the given lockfile exclusive.
the lockfile couldn't occupied.
@type raise_on_fail: bool
+ @param stay_opened: should the lockfile stay opened after creation,
+ @@type stay_opened: bool or None
+
@return: a lock object on success, else None
@rtype: LockObject or None
else:
raise LockdirNotWriteableError(lockdir)
+ if stay_opened is None:
+ stay_opened = self.stay_opened
+ else:
+ stay_opened = bool(stay_opened)
+
counter = 0
delay = delay_start
if fd is not None and not self.simulate:
os.write(fd, out)
- os.close(fd)
- fd = None
+ if stay_opened:
+ os.lseek(fd, 0, 0)
+ os.fsync(fd)
+ else:
+ os.close(fd)
+ fd = None
+
+ if fd is not None and self.simulate:
+ fd = None
mtime = datetime.datetime.utcnow()
lock_object = LockObject(
- lockfile, ctime=ctime, mtime=mtime, fcontent=out, simulate=self.simulate,
+ lockfile, ctime=ctime, mtime=mtime, fcontent=out, fd=fd, simulate=self.simulate,
appname=self.appname, verbose=self.verbose, base_dir=self.base_dir, silent=self.silent,
)
fd = None
try:
fd = os.open(lockfile, os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o644)
+ fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
except OSError as e:
msg = "Error on creating lockfile {lfile!r}: {err}".format(
lfile=lockfile, err=e)
# -------------------------------------------------------------------------
def create_lockfile(self, content):
- (fd, filename) = tempfile.mkstemp()
+ (fd, filename) = tempfile.mkstemp(suffix='.lock', prefix='test-', dir=self.lock_dir)
LOG.debug("Created temporary file %r, writing in it.", filename)
content = to_utf8(str(content))
lockdir=self.lock_dir,
)
LOG.debug("Creating lockfile %r ...", self.lock_file)
- locker.create_lockfile(self.lock_basename)
+ lock = locker.create_lockfile(self.lock_basename)
LOG.debug("Removing lockfile %r ...", self.lock_file)
+ lock = None
locker.remove_lockfile(self.lock_basename)
# -------------------------------------------------------------------------
LOG.debug("Creating lockfile %r ...", self.lock_file)
lock = locker.create_lockfile(self.lock_basename)
LOG.debug("PbLock object %%r: %r", lock)
- LOG.debug("PbLock object %%s: %s", str(lock))
+ LOG.debug("PbLock object %%s:\n%s", str(lock))
+ lock = None
finally:
LOG.debug("Removing lockfile %r ...", self.lock_file)
locker.remove_lockfile(self.lock_basename)
tdiff = mtime2 - mtime1
LOG.debug("Got a time difference between mtimes of %0.3f seconds." % (tdiff))
self.assertGreater(mtime2, mtime1)
+ lock = None
finally:
LOG.debug("Removing lockfile %r ...", self.lock_file)
locker.remove_lockfile(self.lock_basename)
lockdir=ldir,
)
with self.assertRaises(LockdirNotExistsError) as cm:
- locker.create_lockfile(self.lock_basename)
+ lock = locker.create_lockfile(self.lock_basename)
+ lock = None
e = cm.exception
LOG.debug(
"%s raised as expected on lockdir = %r: %s.",
lockdir=ldir,
)
with self.assertRaises(LockdirNotWriteableError) as cm:
- locker.create_lockfile(self.lock_basename)
+ lock = locker.create_lockfile(self.lock_basename)
+ lock = None
e = cm.exception
LOG.debug(
"%s raised as expected on lockdir = %r: %s.",
self.assertEqual(lockfile, e.lockfile)
if result:
self.fail("PbLockHandler shouldn't be able to create the lockfile.")
+ result = None
finally:
self.remove_lockfile(lockfile)
LOG.debug(
"%s raised as expected on an invalid lockfile (empty lines): %s",
e.__class__.__name__, e)
+ result = None
finally:
self.remove_lockfile(lockfile)
locker.remove_lockfile(lockfile)
if result:
self.fail("LockHandler should not be able to create the lockfile.")
+ result = None
finally:
self.remove_lockfile(lockfile)
locker.remove_lockfile(lockfile)
if not result:
self.fail("PbLockHandler should be able to create the lockfile.")
+ result = None
finally:
self.remove_lockfile(lockfile)