Farewell pylockfile

We need remove all of the files except the README as required by:
https://docs.openstack.org/infra/manual/drivers.html#step-2-remove-project-content

Depends-On:I4501bc69b6d6b60894008a796d2888ac52bec252
Change-Id: I2cfeaa7fabd16c43e1301560bb2b71c8b98d49e2
This commit is contained in:
ChangBo Guo(gcb) 2017-09-30 11:31:25 +08:00
parent 99870bfa02
commit f3453b829a
21 changed files with 9 additions and 1984 deletions

11
.gitignore vendored
View File

@ -1,11 +0,0 @@
dist
.tox
*.pyc
*~
doc/build
AUTHORS
ChangeLog
*.egg
pylockfile.egg-info
cover
.coverage

View File

@ -1,4 +0,0 @@
[gerrit]
host=review.openstack.org
port=29418
project=openstack/pylockfile.git

6
ACKS
View File

@ -1,6 +0,0 @@
Thanks to the following people for help with lockfile.
Scott Dial
Ben Finney
Frank Niessink
Konstantin Veretennicov

21
LICENSE
View File

@ -1,21 +0,0 @@
This is the MIT license: http://www.opensource.org/licenses/mit-license.php
Copyright (c) 2007 Skip Montanaro.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.

View File

@ -1,41 +1,15 @@
.. warning::
This project is no longer maintained.
**This package is deprecated**. It is highly preferred that instead of
using this code base that instead `fasteners`_ or `oslo.concurrency`_ is
used instead.
The contents of this repository are still available in the Git
source code management system. To see the contents of this
repository before it reached its end of life, please check out the
previous commit with "git checkout HEAD^1".
The lockfile package exports a ``LockFile`` class which provides a simple API
for locking files. Unlike the Windows ``msvcrt.locking`` function, the
``fcntl.lockf`` and ``fcntl.flock`` functions, and the deprecated ``posixfile
module``, the API is identical across both UNIX (including Linux and Mac) and
Windows platforms.
For an alternative project, please see `fasteners`_ or `oslo.concurrency`
The lock mechanism relies on the atomic nature of the link (on UNIX) and
``mkdir`` (on Windows) system calls. An implementation based on SQLite is also
provided, more as a demonstration of the possibilities it provides than as
production-quality code.
Install pylockfile with: ``pip install lockfile``.
* `Documentation <http://docs.openstack.org/developer/pylockfile>`_
* `Source <http://git.openstack.org/cgit/openstack/pylockfile>`_
* `Bugs <http://bugs.launchpad.net/pylockfile>`_
For any questions or comments or further help needed please email
`openstack-dev`_ and prefix your email subject with ``[oslo][pylockfile]`` (for
a faster response).
In version 0.9 the API changed in two significant ways:
* It changed from a module defining several classes to a package containing
several modules, each defining a single class.
* Where classes had been named ``SomethingFileLock`` before the last two words
have been reversed, so that class is now ``SomethingLockFile``.
The previous module-level definitions of ``LinkFileLock``, ``MkdirFileLock``
and ``SQLiteFileLock`` will be retained until the 1.0 release.
For any further questions, please email
openstack-dev@lists.openstack.org or join #openstack-dev on
Freenode.
.. _fasteners: https://pypi.python.org/pypi/fasteners
.. _oslo.concurrency: http://docs.openstack.org/developer/oslo.concurrency/
.. _openstack-dev: http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev

View File

@ -1,50 +0,0 @@
Version 0.9.1
=============
* This release moves the source location to Google Code.
* Threaded support is currently broken. (It might not actually be broken.
It might just be the tests which are broken.)
Version 0.9
===========
* The lockfile module was reorganized into a package.
* The names of the three main classes have changed as follows:
LinkFileLock -> LinkLockFile
MkdirFileLock -> MkdirLockFile
SQLiteFileLock -> SQLiteLockFile
* A PIDLockFile class was added.
Version 0.3
===========
* Fix 2.4.diff file error.
* More documentation updates.
Version 0.2
===========
* Added 2.4.diff file to patch lockfile to work with Python 2.4 (removes use
of with statement).
* Renamed _FileLock base class to LockBase to expose it (and its docstrings)
to pydoc.
* Got rid of time.sleep() calls in tests (thanks to Konstantin
Veretennicov).
* Use thread.get_ident() as the thread discriminator.
* Updated documentation a bit.
* Added RELEASE-NOTES.
Version 0.1
===========
* First release - All basic functionality there.

View File

@ -1,73 +0,0 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d .build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html web pickle htmlhelp latex changes linkcheck
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " pickle to make pickle files (usable by e.g. sphinx-web)"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " changes to make an overview over all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
clean:
-rm -rf .build/*
html:
mkdir -p .build/html .build/doctrees
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) .build/html
@echo
@echo "Build finished. The HTML pages are in .build/html."
pickle:
mkdir -p .build/pickle .build/doctrees
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) .build/pickle
@echo
@echo "Build finished; now you can process the pickle files or run"
@echo " sphinx-web .build/pickle"
@echo "to start the sphinx-web server."
web: pickle
htmlhelp:
mkdir -p .build/htmlhelp .build/doctrees
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) .build/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in .build/htmlhelp."
latex:
mkdir -p .build/latex .build/doctrees
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) .build/latex
@echo
@echo "Build finished; the LaTeX files are in .build/latex."
@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
"run these through (pdf)latex."
changes:
mkdir -p .build/changes .build/doctrees
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) .build/changes
@echo
@echo "The overview file is in .build/changes."
linkcheck:
mkdir -p .build/linkcheck .build/doctrees
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) .build/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in .build/linkcheck/output.txt."
html.zip: html
(cd .build/html ; zip -r ../../$@ *)

View File

@ -1,179 +0,0 @@
# -*- coding: utf-8 -*-
#
# lockfile documentation build configuration file, created by
# sphinx-quickstart on Sat Sep 13 17:54:17 2008.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# The contents of this file are pickled, so don't put values in the namespace
# that aren't pickleable (module imports are okay, they're removed automatically).
#
# All configuration values have a default value; values that are commented out
# serve to show the default value.
import sys, os
# If your extensions are in another directory, add it here. If the directory
# is relative to the documentation root, use os.path.abspath to make it
# absolute, like shown here.
#sys.path.append(os.path.abspath('some/directory'))
# General configuration
# ---------------------
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = ['.templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# General substitutions.
project = 'lockfile'
copyright = '2008, Skip Montanaro'
# The default replacements for |version| and |release|, also used in various
# other places throughout the built documents.
#
# The short X.Y version.
version = '0.3'
# The full version, including alpha/beta/rc tags.
release = '0.3'
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
today_fmt = '%B %d, %Y'
# List of documents that shouldn't be included in the build.
#unused_docs = []
# List of directories, relative to source directories, that shouldn't be searched
# for source files.
#exclude_dirs = []
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# Options for HTML output
# -----------------------
# The style sheet to use for HTML and HTML Help pages. A file of that name
# must exist either in Sphinx' static/ path, or in one of the custom paths
# given in html_static_path.
html_style = 'default.css'
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (within the static path) to place at the top of
# the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
#html_static_path = ['.static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_use_modindex = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, the reST sources are included in the HTML build as _sources/<name>.
#html_copy_source = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = ''
# Output file base name for HTML help builder.
htmlhelp_basename = 'lockfiledoc'
# Options for LaTeX output
# ------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, document class [howto/manual]).
latex_documents = [
('lockfile', 'lockfile.tex', 'lockfile Documentation',
'Skip Montanaro', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# Additional stuff for the LaTeX preamble.
#latex_preamble = ''
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_use_modindex = True

View File

@ -1,286 +0,0 @@
:mod:`lockfile` --- Platform-independent file locking
=====================================================
.. module:: lockfile
:synopsis: Platform-independent file locking
.. moduleauthor:: Skip Montanaro <skip@pobox.com>
.. sectionauthor:: Skip Montanaro <skip@pobox.com>
.. warning::
This package is **deprecated**. It is highly preferred that instead of
using this code base (where appropriate) that instead `fasteners`_ is
used instead. For any questions or comments or further help needed
please email `openstack-dev`_ and prefix your email subject
with ``[oslo][pylockfile]`` (for a faster response).
.. note::
This package is pre-release software. Between versions 0.8 and 0.9 it
was changed from a module to a package. It is quite possible that the
API and implementation will change again in important ways as people test
it and provide feedback and bug fixes. In particular, if the mkdir-based
locking scheme is sufficient for both Windows and Unix platforms, the
link-based scheme may be deleted so that only a single locking scheme is
used, providing cross-platform lockfile cooperation.
.. note::
The implementation uses the `with` statement, both in the tests and in the
main code, so will only work out-of-the-box with Python 2.5 or later.
However, the use of the `with` statement is minimal, so if you apply the
patch in the included 2.4.diff file you can use it with Python 2.4. It's
possible that it will work in Python 2.3 with that patch applied as well,
though the doctest code relies on APIs new in 2.4, so will have to be
rewritten somewhat to allow testing on 2.3. As they say, patches welcome.
``;-)``
The :mod:`lockfile` package exports a :class:`LockFile` class which provides
a simple API for locking files. Unlike the Windows :func:`msvcrt.locking`
function, the Unix :func:`fcntl.flock`, :func:`fcntl.lockf` and the
deprecated :mod:`posixfile` module, the API is identical across both Unix
(including Linux and Mac) and Windows platforms. The lock mechanism relies
on the atomic nature of the :func:`link` (on Unix) and :func:`mkdir` (On
Windows) system calls. It also contains several lock-method-specific
modules: :mod:`lockfile.linklockfile`, :mod:`lockfile.mkdirlockfile`, and
:mod:`lockfile.sqlitelockfile`, each one exporting a single class. For
backwards compatibility with versions before 0.9 the :class:`LinkFileLock`,
:class:`MkdirFileLock` and :class:`SQLiteFileLock` objects are exposed as
attributes of the top-level lockfile package, though this use was deprecated
starting with version 0.9 and will be removed in version 1.0.
.. note::
The current implementation uses :func:`os.link` on Unix, but since that
function is unavailable on Windows it uses :func:`os.mkdir` there. At
this point it's not clear that using the :func:`os.mkdir` method would be
insufficient on Unix systems. If it proves to be adequate on Unix then
the implementation could be simplified and truly cross-platform locking
would be possible.
.. note::
The current implementation doesn't provide for shared vs. exclusive
locks. It should be possible for multiple reader processes to hold the
lock at the same time.
The module defines the following exceptions:
.. exception:: Error
This is the base class for all exceptions raised by the :class:`LockFile`
class.
.. exception:: LockError
This is the base class for all exceptions raised when attempting to lock
a file.
.. exception:: UnlockError
This is the base class for all exceptions raised when attempting to
unlock a file.
.. exception:: LockTimeout
This exception is raised if the :func:`LockFile.acquire` method is
called with a timeout which expires before an existing lock is released.
.. exception:: AlreadyLocked
This exception is raised if the :func:`LockFile.acquire` detects a
file is already locked when in non-blocking mode.
.. exception:: LockFailed
This exception is raised if the :func:`LockFile.acquire` detects some
other condition (such as a non-writable directory) which prevents it from
creating its lock file.
.. exception:: NotLocked
This exception is raised if the file is not locked when
:func:`LockFile.release` is called.
.. exception:: NotMyLock
This exception is raised if the file is locked by another thread or
process when :func:`LockFile.release` is called.
The following classes are provided:
.. class:: linklockfile.LinkLockFile(path, threaded=True)
This class uses the :func:`link(2)` system call as the basic lock
mechanism. *path* is an object in the file system to be locked. It need
not exist, but its directory must exist and be writable at the time the
:func:`acquire` and :func:`release` methods are called. *threaded* is
optional, but when set to :const:`True` locks will be distinguished
between threads in the same process.
.. class:: symlinklockfile.SymlinkLockFile(path, threaded=True)
This class uses the :func:`symlink(2)` system call as the basic lock
mechanism. The parameters have the same meaning and constraints as for
the :class:`LinkLockFile` class.
.. class:: mkdirlockfile.MkdirLockFile(path, threaded=True)
This class uses the :func:`mkdir(2)` system call as the basic lock
mechanism. The parameters have the same meaning and constraints as for
the :class:`LinkLockFile` class.
.. class:: sqlitelockfile.SQLiteLockFile(path, threaded=True)
This class uses the :mod:`sqlite3` module to implement the lock
mechanism. The parameters have the same meaning as for the
:class:`LinkLockFile` class.
.. class:: LockBase(path, threaded=True)
This is the base class for all concrete implementations and is available
at the lockfile package level so programmers can implement other locking
schemes.
.. function:: locked(path, timeout=None)
This function provides a decorator which insures the decorated function
is always called with the lock held.
By default, the :const:`LockFile` object refers to the
:class:`mkdirlockfile.MkdirLockFile` class on Windows. On all other
platforms it refers to the :class:`linklockfile.LinkLockFile` class.
When locking a file the :class:`linklockfile.LinkLockFile` class creates a
uniquely named hard link to an empty lock file. That hard link contains the
hostname, process id, and if locks between threads are distinguished, the
thread identifier. For example, if you want to lock access to a file named
"README", the lock file is named "README.lock". With per-thread locks
enabled the hard link is named HOSTNAME-THREADID-PID. With only per-process
locks enabled the hard link is named HOSTNAME--PID.
When using the :class:`mkdirlockfile.MkdirLockFile` class the lock file is a
directory. Referring to the example above, README.lock will be a directory
and HOSTNAME-THREADID-PID will be an empty file within that directory.
.. seealso::
Module :mod:`msvcrt`
Provides the :func:`locking` function, the standard Windows way of
locking (parts of) a file.
Module :mod:`posixfile`
The deprecated (since Python 1.5) way of locking files on Posix systems.
Module :mod:`fcntl`
Provides the current best way to lock files on Unix systems
(:func:`lockf` and :func:`flock`).
LockFile Objects
----------------
:class:`LockFile` objects support the `context manager` protocol used by the
statement:`with` statement. The timeout option is not supported when used in
this fashion. While support for timeouts could be implemented, there is no
support for handling the eventual :exc:`Timeout` exceptions raised by the
:func:`__enter__` method, so you would have to protect the `with` statement with
a `try` statement. The resulting construct would not be any simpler than just
using a `try` statement in the first place.
:class:`LockFile` has the following user-visible methods:
.. method:: LockFile.acquire(timeout=None)
Lock the file associated with the :class:`LockFile` object. If the
*timeout* is omitted or :const:`None` the caller will block until the
file is unlocked by the object currently holding the lock. If the
*timeout* is zero or a negative number the :exc:`AlreadyLocked` exception
will be raised if the file is currently locked by another process or
thread. If the *timeout* is positive, the caller will block for that
many seconds waiting for the lock to be released. If the lock is not
released within that period the :exc:`LockTimeout` exception will be
raised.
.. method:: LockFile.release()
Unlock the file associated with the :class:`LockFile` object. If the
file is not currently locked, the :exc:`NotLocked` exception is raised.
If the file is locked by another thread or process the :exc:`NotMyLock`
exception is raised.
.. method:: is_locked()
Return the status of the lock on the current file. If any process or
thread (including the current one) is locking the file, :const:`True` is
returned, otherwise :const:`False` is returned.
.. method:: break_lock()
If the file is currently locked, break it.
.. method:: i_am_locking()
Returns true if the caller holds the lock.
Examples
--------
This example is the "hello world" for the :mod:`lockfile` package::
from lockfile import LockFile
lock = LockFile("/some/file/or/other")
with lock:
print lock.path, 'is locked.'
To use this with Python 2.4, you can execute::
from lockfile import LockFile
lock = LockFile("/some/file/or/other")
lock.acquire()
print lock.path, 'is locked.'
lock.release()
If you don't want to wait forever, you might try::
from lockfile import LockFile
lock = LockFile("/some/file/or/other")
while not lock.i_am_locking():
try:
lock.acquire(timeout=60) # wait up to 60 seconds
except LockTimeout:
lock.break_lock()
lock.acquire()
print "I locked", lock.path
lock.release()
You can also insure that a lock is always held when appropriately decorated
functions are called::
from lockfile import locked
@locked("/tmp/mylock")
def func(a, b):
return a + b
Other Libraries
---------------
The idea of implementing advisory locking with a standard API is not new
with :mod:`lockfile`. There are a number of other libraries available:
* locknix - http://pypi.python.org/pypi/locknix - Unix only
* mx.MiscLockFile - from Marc André Lemburg, part of the mx.Base
distribution - cross-platform.
* Twisted - http://twistedmatrix.com/trac/browser/trunk/twisted/python/lockfile.py
* zc.lockfile - http://pypi.python.org/pypi/zc.lockfile
Contacting the Author
---------------------
If you encounter any problems with ``lockfile``, would like help or want to
submit a patch, check http://launchpad.net/pylockfile
.. _fasteners: http://fasteners.readthedocs.org/
.. _openstack-dev: mailto:openstack-dev@lists.openstack.org

View File

@ -1,347 +0,0 @@
# -*- coding: utf-8 -*-
"""
lockfile.py - Platform-independent advisory file locks.
Requires Python 2.5 unless you apply 2.4.diff
Locking is done on a per-thread basis instead of a per-process basis.
Usage:
>>> lock = LockFile('somefile')
>>> try:
... lock.acquire()
... except AlreadyLocked:
... print 'somefile', 'is locked already.'
... except LockFailed:
... print 'somefile', 'can\\'t be locked.'
... else:
... print 'got lock'
got lock
>>> print lock.is_locked()
True
>>> lock.release()
>>> lock = LockFile('somefile')
>>> print lock.is_locked()
False
>>> with lock:
... print lock.is_locked()
True
>>> print lock.is_locked()
False
>>> lock = LockFile('somefile')
>>> # It is okay to lock twice from the same thread...
>>> with lock:
... lock.acquire()
...
>>> # Though no counter is kept, so you can't unlock multiple times...
>>> print lock.is_locked()
False
Exceptions:
Error - base class for other exceptions
LockError - base class for all locking exceptions
AlreadyLocked - Another thread or process already holds the lock
LockFailed - Lock failed for some other reason
UnlockError - base class for all unlocking exceptions
AlreadyUnlocked - File was not locked.
NotMyLock - File was locked but not by the current thread/process
"""
from __future__ import absolute_import
import functools
import os
import socket
import threading
import warnings
# Work with PEP8 and non-PEP8 versions of threading module.
if not hasattr(threading, "current_thread"):
threading.current_thread = threading.currentThread
if not hasattr(threading.Thread, "get_name"):
threading.Thread.get_name = threading.Thread.getName
__all__ = ['Error', 'LockError', 'LockTimeout', 'AlreadyLocked',
'LockFailed', 'UnlockError', 'NotLocked', 'NotMyLock',
'LinkFileLock', 'MkdirFileLock', 'SQLiteFileLock',
'LockBase', 'locked']
class Error(Exception):
"""
Base class for other exceptions.
>>> try:
... raise Error
... except Exception:
... pass
"""
pass
class LockError(Error):
"""
Base class for error arising from attempts to acquire the lock.
>>> try:
... raise LockError
... except Error:
... pass
"""
pass
class LockTimeout(LockError):
"""Raised when lock creation fails within a user-defined period of time.
>>> try:
... raise LockTimeout
... except LockError:
... pass
"""
pass
class AlreadyLocked(LockError):
"""Some other thread/process is locking the file.
>>> try:
... raise AlreadyLocked
... except LockError:
... pass
"""
pass
class LockFailed(LockError):
"""Lock file creation failed for some other reason.
>>> try:
... raise LockFailed
... except LockError:
... pass
"""
pass
class UnlockError(Error):
"""
Base class for errors arising from attempts to release the lock.
>>> try:
... raise UnlockError
... except Error:
... pass
"""
pass
class NotLocked(UnlockError):
"""Raised when an attempt is made to unlock an unlocked file.
>>> try:
... raise NotLocked
... except UnlockError:
... pass
"""
pass
class NotMyLock(UnlockError):
"""Raised when an attempt is made to unlock a file someone else locked.
>>> try:
... raise NotMyLock
... except UnlockError:
... pass
"""
pass
class _SharedBase(object):
def __init__(self, path):
self.path = path
def acquire(self, timeout=None):
"""
Acquire the lock.
* If timeout is omitted (or None), wait forever trying to lock the
file.
* If timeout > 0, try to acquire the lock for that many seconds. If
the lock period expires and the file is still locked, raise
LockTimeout.
* If timeout <= 0, raise AlreadyLocked immediately if the file is
already locked.
"""
raise NotImplemented("implement in subclass")
def release(self):
"""
Release the lock.
If the file is not locked, raise NotLocked.
"""
raise NotImplemented("implement in subclass")
def __enter__(self):
"""
Context manager support.
"""
self.acquire()
return self
def __exit__(self, *_exc):
"""
Context manager support.
"""
self.release()
def __repr__(self):
return "<%s: %r>" % (self.__class__.__name__, self.path)
class LockBase(_SharedBase):
"""Base class for platform-specific lock classes."""
def __init__(self, path, threaded=True, timeout=None):
"""
>>> lock = LockBase('somefile')
>>> lock = LockBase('somefile', threaded=False)
"""
super(LockBase, self).__init__(path)
self.lock_file = os.path.abspath(path) + ".lock"
self.hostname = socket.gethostname()
self.pid = os.getpid()
if threaded:
t = threading.current_thread()
# Thread objects in Python 2.4 and earlier do not have ident
# attrs. Worm around that.
ident = getattr(t, "ident", hash(t))
self.tname = "-%x" % (ident & 0xffffffff)
else:
self.tname = ""
dirname = os.path.dirname(self.lock_file)
# unique name is mostly about the current process, but must
# also contain the path -- otherwise, two adjacent locked
# files conflict (one file gets locked, creating lock-file and
# unique file, the other one gets locked, creating lock-file
# and overwriting the already existing lock-file, then one
# gets unlocked, deleting both lock-file and unique file,
# finally the last lock errors out upon releasing.
self.unique_name = os.path.join(dirname,
"%s%s.%s%s" % (self.hostname,
self.tname,
self.pid,
hash(self.path)))
self.timeout = timeout
def is_locked(self):
"""
Tell whether or not the file is locked.
"""
raise NotImplemented("implement in subclass")
def i_am_locking(self):
"""
Return True if this object is locking the file.
"""
raise NotImplemented("implement in subclass")
def break_lock(self):
"""
Remove a lock. Useful if a locking thread failed to unlock.
"""
raise NotImplemented("implement in subclass")
def __repr__(self):
return "<%s: %r -- %r>" % (self.__class__.__name__, self.unique_name,
self.path)
def _fl_helper(cls, mod, *args, **kwds):
warnings.warn("Import from %s module instead of lockfile package" % mod,
DeprecationWarning, stacklevel=2)
# This is a bit funky, but it's only for awhile. The way the unit tests
# are constructed this function winds up as an unbound method, so it
# actually takes three args, not two. We want to toss out self.
if not isinstance(args[0], str):
# We are testing, avoid the first arg
args = args[1:]
if len(args) == 1 and not kwds:
kwds["threaded"] = True
return cls(*args, **kwds)
def LinkFileLock(*args, **kwds):
"""Factory function provided for backwards compatibility.
Do not use in new code. Instead, import LinkLockFile from the
lockfile.linklockfile module.
"""
from . import linklockfile
return _fl_helper(linklockfile.LinkLockFile, "lockfile.linklockfile",
*args, **kwds)
def MkdirFileLock(*args, **kwds):
"""Factory function provided for backwards compatibility.
Do not use in new code. Instead, import MkdirLockFile from the
lockfile.mkdirlockfile module.
"""
from . import mkdirlockfile
return _fl_helper(mkdirlockfile.MkdirLockFile, "lockfile.mkdirlockfile",
*args, **kwds)
def SQLiteFileLock(*args, **kwds):
"""Factory function provided for backwards compatibility.
Do not use in new code. Instead, import SQLiteLockFile from the
lockfile.mkdirlockfile module.
"""
from . import sqlitelockfile
return _fl_helper(sqlitelockfile.SQLiteLockFile, "lockfile.sqlitelockfile",
*args, **kwds)
def locked(path, timeout=None):
"""Decorator which enables locks for decorated function.
Arguments:
- path: path for lockfile.
- timeout (optional): Timeout for acquiring lock.
Usage:
@locked('/var/run/myname', timeout=0)
def myname(...):
...
"""
def decor(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
lock = FileLock(path, timeout=timeout)
lock.acquire()
try:
return func(*args, **kwargs)
finally:
lock.release()
return wrapper
return decor
if hasattr(os, "link"):
from . import linklockfile as _llf
LockFile = _llf.LinkLockFile
else:
from . import mkdirlockfile as _mlf
LockFile = _mlf.MkdirLockFile
FileLock = LockFile

View File

@ -1,73 +0,0 @@
from __future__ import absolute_import
import time
import os
from . import (LockBase, LockFailed, NotLocked, NotMyLock, LockTimeout,
AlreadyLocked)
class LinkLockFile(LockBase):
"""Lock access to a file using atomic property of link(2).
>>> lock = LinkLockFile('somefile')
>>> lock = LinkLockFile('somefile', threaded=False)
"""
def acquire(self, timeout=None):
try:
open(self.unique_name, "wb").close()
except IOError:
raise LockFailed("failed to create %s" % self.unique_name)
timeout = timeout if timeout is not None else self.timeout
end_time = time.time()
if timeout is not None and timeout > 0:
end_time += timeout
while True:
# Try and create a hard link to it.
try:
os.link(self.unique_name, self.lock_file)
except OSError:
# Link creation failed. Maybe we've double-locked?
nlinks = os.stat(self.unique_name).st_nlink
if nlinks == 2:
# The original link plus the one I created == 2. We're
# good to go.
return
else:
# Otherwise the lock creation failed.
if timeout is not None and time.time() > end_time:
os.unlink(self.unique_name)
if timeout > 0:
raise LockTimeout("Timeout waiting to acquire"
" lock for %s" %
self.path)
else:
raise AlreadyLocked("%s is already locked" %
self.path)
time.sleep(timeout is not None and timeout / 10 or 0.1)
else:
# Link creation succeeded. We're good to go.
return
def release(self):
if not self.is_locked():
raise NotLocked("%s is not locked" % self.path)
elif not os.path.exists(self.unique_name):
raise NotMyLock("%s is locked, but not by me" % self.path)
os.unlink(self.unique_name)
os.unlink(self.lock_file)
def is_locked(self):
return os.path.exists(self.lock_file)
def i_am_locking(self):
return (self.is_locked() and
os.path.exists(self.unique_name) and
os.stat(self.unique_name).st_nlink == 2)
def break_lock(self):
if os.path.exists(self.lock_file):
os.unlink(self.lock_file)

View File

@ -1,84 +0,0 @@
from __future__ import absolute_import, division
import time
import os
import sys
import errno
from . import (LockBase, LockFailed, NotLocked, NotMyLock, LockTimeout,
AlreadyLocked)
class MkdirLockFile(LockBase):
"""Lock file by creating a directory."""
def __init__(self, path, threaded=True, timeout=None):
"""
>>> lock = MkdirLockFile('somefile')
>>> lock = MkdirLockFile('somefile', threaded=False)
"""
LockBase.__init__(self, path, threaded, timeout)
# Lock file itself is a directory. Place the unique file name into
# it.
self.unique_name = os.path.join(self.lock_file,
"%s.%s%s" % (self.hostname,
self.tname,
self.pid))
def acquire(self, timeout=None):
timeout = timeout if timeout is not None else self.timeout
end_time = time.time()
if timeout is not None and timeout > 0:
end_time += timeout
if timeout is None:
wait = 0.1
else:
wait = max(0, timeout / 10)
while True:
try:
os.mkdir(self.lock_file)
except OSError:
err = sys.exc_info()[1]
if err.errno == errno.EEXIST:
# Already locked.
if os.path.exists(self.unique_name):
# Already locked by me.
return
if timeout is not None and time.time() > end_time:
if timeout > 0:
raise LockTimeout("Timeout waiting to acquire"
" lock for %s" %
self.path)
else:
# Someone else has the lock.
raise AlreadyLocked("%s is already locked" %
self.path)
time.sleep(wait)
else:
# Couldn't create the lock for some other reason
raise LockFailed("failed to create %s" % self.lock_file)
else:
open(self.unique_name, "wb").close()
return
def release(self):
if not self.is_locked():
raise NotLocked("%s is not locked" % self.path)
elif not os.path.exists(self.unique_name):
raise NotMyLock("%s is locked, but not by me" % self.path)
os.unlink(self.unique_name)
os.rmdir(self.lock_file)
def is_locked(self):
return os.path.exists(self.lock_file)
def i_am_locking(self):
return (self.is_locked() and
os.path.exists(self.unique_name))
def break_lock(self):
if os.path.exists(self.lock_file):
for name in os.listdir(self.lock_file):
os.unlink(os.path.join(self.lock_file, name))
os.rmdir(self.lock_file)

View File

@ -1,190 +0,0 @@
# -*- coding: utf-8 -*-
# pidlockfile.py
#
# Copyright © 20082009 Ben Finney <ben+python@benfinney.id.au>
#
# This is free software: you may copy, modify, and/or distribute this work
# under the terms of the Python Software Foundation License, version 2 or
# later as published by the Python Software Foundation.
# No warranty expressed or implied. See the file LICENSE.PSF-2 for details.
""" Lockfile behaviour implemented via Unix PID files.
"""
from __future__ import absolute_import
import errno
import os
import time
from . import (LockBase, AlreadyLocked, LockFailed, NotLocked, NotMyLock,
LockTimeout)
class PIDLockFile(LockBase):
""" Lockfile implemented as a Unix PID file.
The lock file is a normal file named by the attribute `path`.
A lock's PID file contains a single line of text, containing
the process ID (PID) of the process that acquired the lock.
>>> lock = PIDLockFile('somefile')
>>> lock = PIDLockFile('somefile')
"""
def __init__(self, path, threaded=False, timeout=None):
# pid lockfiles don't support threaded operation, so always force
# False as the threaded arg.
LockBase.__init__(self, path, False, timeout)
self.unique_name = self.path
def read_pid(self):
""" Get the PID from the lock file.
"""
return read_pid_from_pidfile(self.path)
def is_locked(self):
""" Test if the lock is currently held.
The lock is held if the PID file for this lock exists.
"""
return os.path.exists(self.path)
def i_am_locking(self):
""" Test if the lock is held by the current process.
Returns ``True`` if the current process ID matches the
number stored in the PID file.
"""
return self.is_locked() and os.getpid() == self.read_pid()
def acquire(self, timeout=None):
""" Acquire the lock.
Creates the PID file for this lock, or raises an error if
the lock could not be acquired.
"""
timeout = timeout if timeout is not None else self.timeout
end_time = time.time()
if timeout is not None and timeout > 0:
end_time += timeout
while True:
try:
write_pid_to_pidfile(self.path)
except OSError as exc:
if exc.errno == errno.EEXIST:
# The lock creation failed. Maybe sleep a bit.
if time.time() > end_time:
if timeout is not None and timeout > 0:
raise LockTimeout("Timeout waiting to acquire"
" lock for %s" %
self.path)
else:
raise AlreadyLocked("%s is already locked" %
self.path)
time.sleep(timeout is not None and timeout / 10 or 0.1)
else:
raise LockFailed("failed to create %s" % self.path)
else:
return
def release(self):
""" Release the lock.
Removes the PID file to release the lock, or raises an
error if the current process does not hold the lock.
"""
if not self.is_locked():
raise NotLocked("%s is not locked" % self.path)
if not self.i_am_locking():
raise NotMyLock("%s is locked, but not by me" % self.path)
remove_existing_pidfile(self.path)
def break_lock(self):
""" Break an existing lock.
Removes the PID file if it already exists, otherwise does
nothing.
"""
remove_existing_pidfile(self.path)
def read_pid_from_pidfile(pidfile_path):
""" Read the PID recorded in the named PID file.
Read and return the numeric PID recorded as text in the named
PID file. If the PID file cannot be read, or if the content is
not a valid PID, return ``None``.
"""
pid = None
try:
pidfile = open(pidfile_path, 'r')
except IOError:
pass
else:
# According to the FHS 2.3 section on PID files in /var/run:
#
# The file must consist of the process identifier in
# ASCII-encoded decimal, followed by a newline character.
#
# Programs that read PID files should be somewhat flexible
# in what they accept; i.e., they should ignore extra
# whitespace, leading zeroes, absence of the trailing
# newline, or additional lines in the PID file.
line = pidfile.readline().strip()
try:
pid = int(line)
except ValueError:
pass
pidfile.close()
return pid
def write_pid_to_pidfile(pidfile_path):
""" Write the PID in the named PID file.
Get the numeric process ID (PID) of the current process
and write it to the named file as a line of text.
"""
open_flags = (os.O_CREAT | os.O_EXCL | os.O_WRONLY)
open_mode = 0o644
pidfile_fd = os.open(pidfile_path, open_flags, open_mode)
pidfile = os.fdopen(pidfile_fd, 'w')
# According to the FHS 2.3 section on PID files in /var/run:
#
# The file must consist of the process identifier in
# ASCII-encoded decimal, followed by a newline character. For
# example, if crond was process number 25, /var/run/crond.pid
# would contain three characters: two, five, and newline.
pid = os.getpid()
pidfile.write("%s\n" % pid)
pidfile.close()
def remove_existing_pidfile(pidfile_path):
""" Remove the named PID file if it exists.
Removing a PID file that doesn't already exist puts us in the
desired state, so we ignore the condition if the file does not
exist.
"""
try:
os.remove(pidfile_path)
except OSError as exc:
if exc.errno == errno.ENOENT:
pass
else:
raise

View File

@ -1,156 +0,0 @@
from __future__ import absolute_import, division
import time
import os
try:
unicode
except NameError:
unicode = str
from . import LockBase, NotLocked, NotMyLock, LockTimeout, AlreadyLocked
class SQLiteLockFile(LockBase):
"Demonstrate SQL-based locking."
testdb = None
def __init__(self, path, threaded=True, timeout=None):
"""
>>> lock = SQLiteLockFile('somefile')
>>> lock = SQLiteLockFile('somefile', threaded=False)
"""
LockBase.__init__(self, path, threaded, timeout)
self.lock_file = unicode(self.lock_file)
self.unique_name = unicode(self.unique_name)
if SQLiteLockFile.testdb is None:
import tempfile
_fd, testdb = tempfile.mkstemp()
os.close(_fd)
os.unlink(testdb)
del _fd, tempfile
SQLiteLockFile.testdb = testdb
import sqlite3
self.connection = sqlite3.connect(SQLiteLockFile.testdb)
c = self.connection.cursor()
try:
c.execute("create table locks"
"("
" lock_file varchar(32),"
" unique_name varchar(32)"
")")
except sqlite3.OperationalError:
pass
else:
self.connection.commit()
import atexit
atexit.register(os.unlink, SQLiteLockFile.testdb)
def acquire(self, timeout=None):
timeout = timeout if timeout is not None else self.timeout
end_time = time.time()
if timeout is not None and timeout > 0:
end_time += timeout
if timeout is None:
wait = 0.1
elif timeout <= 0:
wait = 0
else:
wait = timeout / 10
cursor = self.connection.cursor()
while True:
if not self.is_locked():
# Not locked. Try to lock it.
cursor.execute("insert into locks"
" (lock_file, unique_name)"
" values"
" (?, ?)",
(self.lock_file, self.unique_name))
self.connection.commit()
# Check to see if we are the only lock holder.
cursor.execute("select * from locks"
" where unique_name = ?",
(self.unique_name,))
rows = cursor.fetchall()
if len(rows) > 1:
# Nope. Someone else got there. Remove our lock.
cursor.execute("delete from locks"
" where unique_name = ?",
(self.unique_name,))
self.connection.commit()
else:
# Yup. We're done, so go home.
return
else:
# Check to see if we are the only lock holder.
cursor.execute("select * from locks"
" where unique_name = ?",
(self.unique_name,))
rows = cursor.fetchall()
if len(rows) == 1:
# We're the locker, so go home.
return
# Maybe we should wait a bit longer.
if timeout is not None and time.time() > end_time:
if timeout > 0:
# No more waiting.
raise LockTimeout("Timeout waiting to acquire"
" lock for %s" %
self.path)
else:
# Someone else has the lock and we are impatient..
raise AlreadyLocked("%s is already locked" % self.path)
# Well, okay. We'll give it a bit longer.
time.sleep(wait)
def release(self):
if not self.is_locked():
raise NotLocked("%s is not locked" % self.path)
if not self.i_am_locking():
raise NotMyLock("%s is locked, but not by me (by %s)" %
(self.unique_name, self._who_is_locking()))
cursor = self.connection.cursor()
cursor.execute("delete from locks"
" where unique_name = ?",
(self.unique_name,))
self.connection.commit()
def _who_is_locking(self):
cursor = self.connection.cursor()
cursor.execute("select unique_name from locks"
" where lock_file = ?",
(self.lock_file,))
return cursor.fetchone()[0]
def is_locked(self):
cursor = self.connection.cursor()
cursor.execute("select * from locks"
" where lock_file = ?",
(self.lock_file,))
rows = cursor.fetchall()
return not not rows
def i_am_locking(self):
cursor = self.connection.cursor()
cursor.execute("select * from locks"
" where lock_file = ?"
" and unique_name = ?",
(self.lock_file, self.unique_name))
return not not cursor.fetchall()
def break_lock(self):
cursor = self.connection.cursor()
cursor.execute("delete from locks"
" where lock_file = ?",
(self.lock_file,))
self.connection.commit()

View File

@ -1,70 +0,0 @@
from __future__ import absolute_import
import os
import time
from . import (LockBase, NotLocked, NotMyLock, LockTimeout,
AlreadyLocked)
class SymlinkLockFile(LockBase):
"""Lock access to a file using symlink(2)."""
def __init__(self, path, threaded=True, timeout=None):
# super(SymlinkLockFile).__init(...)
LockBase.__init__(self, path, threaded, timeout)
# split it back!
self.unique_name = os.path.split(self.unique_name)[1]
def acquire(self, timeout=None):
# Hopefully unnecessary for symlink.
# try:
# open(self.unique_name, "wb").close()
# except IOError:
# raise LockFailed("failed to create %s" % self.unique_name)
timeout = timeout if timeout is not None else self.timeout
end_time = time.time()
if timeout is not None and timeout > 0:
end_time += timeout
while True:
# Try and create a symbolic link to it.
try:
os.symlink(self.unique_name, self.lock_file)
except OSError:
# Link creation failed. Maybe we've double-locked?
if self.i_am_locking():
# Linked to out unique name. Proceed.
return
else:
# Otherwise the lock creation failed.
if timeout is not None and time.time() > end_time:
if timeout > 0:
raise LockTimeout("Timeout waiting to acquire"
" lock for %s" %
self.path)
else:
raise AlreadyLocked("%s is already locked" %
self.path)
time.sleep(timeout / 10 if timeout is not None else 0.1)
else:
# Link creation succeeded. We're good to go.
return
def release(self):
if not self.is_locked():
raise NotLocked("%s is not locked" % self.path)
elif not self.i_am_locking():
raise NotMyLock("%s is locked, but not by me" % self.path)
os.unlink(self.lock_file)
def is_locked(self):
return os.path.islink(self.lock_file)
def i_am_locking(self):
return (os.path.islink(self.lock_file) and
os.readlink(self.lock_file) == self.unique_name)
def break_lock(self):
if os.path.islink(self.lock_file): # exists && link
os.unlink(self.lock_file)

View File

@ -1,35 +0,0 @@
[metadata]
name = lockfile
summary = Platform-independent file locking module
description-file =
README.rst
author = OpenStack
author-email = openstack-dev@lists.openstack.org
home-page = http://launchpad.net/pylockfile
classifier =
Intended Audience :: Developers
License :: OSI Approved :: MIT License
Operating System :: POSIX :: Linux
Operating System :: MacOS
Operating System :: Microsoft :: Windows :: Windows NT/2000
Operating System :: POSIX
Programming Language :: Python
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.3
Topic :: Software Development :: Libraries :: Python Modules
[files]
packages = lockfile
[pbr]
warnerrors = true
[build_sphinx]
source-dir = doc/source
build-dir = doc/build
all_files = 1
[bdist_wheel]
universal=1

View File

@ -1,29 +0,0 @@
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
import setuptools
# In python < 2.7.4, a lazy loading of package `pbr` will break
# setuptools if some other modules registered functions in `atexit`.
# solution from: http://bugs.python.org/issue15881#msg170215
try:
import multiprocessing # noqa
except ImportError:
pass
setuptools.setup(
setup_requires=['pbr>=1.8'],
pbr=True)

View File

@ -1,5 +0,0 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
nose
sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2

View File

@ -1,262 +0,0 @@
import os
import threading
import shutil
import lockfile
class ComplianceTest(object):
def __init__(self):
self.saved_class = lockfile.LockFile
def _testfile(self):
"""Return platform-appropriate file. Helper for tests."""
import tempfile
return os.path.join(tempfile.gettempdir(), 'trash-%s' % os.getpid())
def setup(self):
lockfile.LockFile = self.class_to_test
def teardown(self):
try:
tf = self._testfile()
if os.path.isdir(tf):
shutil.rmtree(tf)
elif os.path.isfile(tf):
os.unlink(tf)
elif not os.path.exists(tf):
pass
else:
raise SystemError("unrecognized file: %s" % tf)
finally:
lockfile.LockFile = self.saved_class
def _test_acquire_helper(self, tbool):
# As simple as it gets.
lock = lockfile.LockFile(self._testfile(), threaded=tbool)
lock.acquire()
assert lock.i_am_locking()
lock.release()
assert not lock.is_locked()
# def test_acquire_basic_threaded(self):
# self._test_acquire_helper(True)
def test_acquire_basic_unthreaded(self):
self._test_acquire_helper(False)
def _test_acquire_no_timeout_helper(self, tbool):
# No timeout test
e1, e2 = threading.Event(), threading.Event()
t = _in_thread(self._lock_wait_unlock, e1, e2)
e1.wait() # wait for thread t to acquire lock
lock2 = lockfile.LockFile(self._testfile(), threaded=tbool)
assert lock2.is_locked()
if tbool:
assert not lock2.i_am_locking()
else:
assert lock2.i_am_locking()
try:
lock2.acquire(timeout=-1)
except lockfile.AlreadyLocked:
pass
else:
lock2.release()
raise AssertionError("did not raise AlreadyLocked in"
" thread %s" %
threading.current_thread().get_name())
try:
lock2.acquire(timeout=0)
except lockfile.AlreadyLocked:
pass
else:
lock2.release()
raise AssertionError("did not raise AlreadyLocked in"
" thread %s" %
threading.current_thread().get_name())
e2.set() # tell thread t to release lock
t.join()
# def test_acquire_no_timeout_threaded(self):
# self._test_acquire_no_timeout_helper(True)
# def test_acquire_no_timeout_unthreaded(self):
# self._test_acquire_no_timeout_helper(False)
def _test_acquire_timeout_helper(self, tbool):
# Timeout test
e1, e2 = threading.Event(), threading.Event()
t = _in_thread(self._lock_wait_unlock, e1, e2)
e1.wait() # wait for thread t to acquire lock
lock2 = lockfile.LockFile(self._testfile(), threaded=tbool)
assert lock2.is_locked()
try:
lock2.acquire(timeout=0.1)
except lockfile.LockTimeout:
pass
else:
lock2.release()
raise AssertionError("did not raise LockTimeout in thread %s" %
threading.current_thread().get_name())
e2.set()
t.join()
def test_acquire_timeout_threaded(self):
self._test_acquire_timeout_helper(True)
def test_acquire_timeout_unthreaded(self):
self._test_acquire_timeout_helper(False)
def _test_context_timeout_helper(self, tbool):
# Timeout test
e1, e2 = threading.Event(), threading.Event()
t = _in_thread(self._lock_wait_unlock, e1, e2)
e1.wait() # wait for thread t to acquire lock
lock2 = lockfile.LockFile(self._testfile(), threaded=tbool,
timeout=0.2)
assert lock2.is_locked()
try:
lock2.acquire()
except lockfile.LockTimeout:
pass
else:
lock2.release()
raise AssertionError("did not raise LockTimeout in thread %s" %
threading.current_thread().get_name())
e2.set()
t.join()
def test_context_timeout_unthreaded(self):
self._test_context_timeout_helper(False)
def _test_release_basic_helper(self, tbool):
lock = lockfile.LockFile(self._testfile(), threaded=tbool)
lock.acquire()
assert lock.is_locked()
lock.release()
assert not lock.is_locked()
assert not lock.i_am_locking()
try:
lock.release()
except lockfile.NotLocked:
pass
except lockfile.NotMyLock:
raise AssertionError('unexpected exception: %s' %
lockfile.NotMyLock)
else:
raise AssertionError('erroneously unlocked file')
# def test_release_basic_threaded(self):
# self._test_release_basic_helper(True)
def test_release_basic_unthreaded(self):
self._test_release_basic_helper(False)
# def test_release_from_thread(self):
# e1, e2 = threading.Event(), threading.Event()
# t = _in_thread(self._lock_wait_unlock, e1, e2)
# e1.wait()
# lock2 = lockfile.LockFile(self._testfile(), threaded=False)
# assert not lock2.i_am_locking()
# try:
# lock2.release()
# except lockfile.NotMyLock:
# pass
# else:
# raise AssertionError('erroneously unlocked a file locked'
# ' by another thread.')
# e2.set()
# t.join()
def _test_is_locked_helper(self, tbool):
lock = lockfile.LockFile(self._testfile(), threaded=tbool)
lock.acquire(timeout=2)
assert lock.is_locked()
lock.release()
assert not lock.is_locked(), "still locked after release!"
# def test_is_locked_threaded(self):
# self._test_is_locked_helper(True)
def test_is_locked_unthreaded(self):
self._test_is_locked_helper(False)
# def test_i_am_locking_threaded(self):
# self._test_i_am_locking_helper(True)
def test_i_am_locking_unthreaded(self):
self._test_i_am_locking_helper(False)
def _test_i_am_locking_helper(self, tbool):
lock1 = lockfile.LockFile(self._testfile(), threaded=tbool)
assert not lock1.is_locked()
lock1.acquire()
try:
assert lock1.i_am_locking()
lock2 = lockfile.LockFile(self._testfile(), threaded=tbool)
assert lock2.is_locked()
if tbool:
assert not lock2.i_am_locking()
finally:
lock1.release()
def _test_break_lock_helper(self, tbool):
lock = lockfile.LockFile(self._testfile(), threaded=tbool)
lock.acquire()
assert lock.is_locked()
lock2 = lockfile.LockFile(self._testfile(), threaded=tbool)
assert lock2.is_locked()
lock2.break_lock()
assert not lock2.is_locked()
try:
lock.release()
except lockfile.NotLocked:
pass
else:
raise AssertionError('break lock failed')
# def test_break_lock_threaded(self):
# self._test_break_lock_helper(True)
def test_break_lock_unthreaded(self):
self._test_break_lock_helper(False)
def _lock_wait_unlock(self, event1, event2):
"""Lock from another thread. Helper for tests."""
l = lockfile.LockFile(self._testfile())
l.acquire()
try:
event1.set() # we're in,
event2.wait() # wait for boss's permission to leave
finally:
l.release()
def test_enter(self):
lock = lockfile.LockFile(self._testfile())
lock.acquire()
try:
assert lock.is_locked(), "Not locked after acquire!"
finally:
lock.release()
assert not lock.is_locked(), "still locked after release!"
def test_decorator(self):
@lockfile.locked(self._testfile())
def func(a, b):
return a + b
assert func(4, 3) == 7
def _in_thread(func, *args, **kwargs):
"""Execute func(*args, **kwargs) after dt seconds. Helper for tests."""
def _f():
func(*args, **kwargs)
t = threading.Thread(target=_f, name='/*/*')
t.setDaemon(True)
t.start()
return t

View File

@ -1,41 +0,0 @@
import lockfile.linklockfile
import lockfile.mkdirlockfile
import lockfile.pidlockfile
import lockfile.symlinklockfile
from compliancetest import ComplianceTest
class TestLinkLockFile(ComplianceTest):
class_to_test = lockfile.linklockfile.LinkLockFile
class TestSymlinkLockFile(ComplianceTest):
class_to_test = lockfile.symlinklockfile.SymlinkLockFile
class TestMkdirLockFile(ComplianceTest):
class_to_test = lockfile.mkdirlockfile.MkdirLockFile
class TestPIDLockFile(ComplianceTest):
class_to_test = lockfile.pidlockfile.PIDLockFile
# Check backwards compatibility
class TestLinkFileLock(ComplianceTest):
class_to_test = lockfile.LinkFileLock
class TestMkdirFileLock(ComplianceTest):
class_to_test = lockfile.MkdirFileLock
try:
import sqlite3 # noqa
except ImportError:
pass
else:
import lockfile.sqlitelockfile
class TestSQLiteLockFile(ComplianceTest):
class_to_test = lockfile.sqlitelockfile.SQLiteLockFile

27
tox.ini
View File

@ -1,27 +0,0 @@
# content of: tox.ini , put in same dir as setup.py
[tox]
envlist = py27,py34
[testenv]
deps = -r{toxinidir}/test-requirements.txt
commands=nosetests
[testenv:venv]
commands = {posargs}
[testenv:pep8]
deps = flake8
commands = flake8
[testenv:docs]
commands = python setup.py build_sphinx
[testenv:cover]
deps = {[testenv]deps}
coverage
commands =
nosetests --with-coverage --cover-erase --cover-package=lockfile --cover-inclusive []
[flake8]
exclude=.venv,.git,.tox,dist,doc
show-source = True