summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChangBo Guo(gcb) <eric.guo@easystack.cn>2017-09-30 11:31:25 +0800
committerChangBo Guo(gcb) <eric.guo@easystack.cn>2017-09-30 11:34:10 +0800
commitf3453b829a1df7196bf6de375702277ee0c0c5d5 (patch)
treee67299ecc2fdbd1fd8bd1a3d78dfa8bcf59267d0
parent99870bfa02f1ba490da6a59b1499f0f1578c9825 (diff)
downloadlockfile-f3453b829a1df7196bf6de375702277ee0c0c5d5.tar.gz
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
-rw-r--r--.gitignore11
-rw-r--r--.gitreview4
-rw-r--r--ACKS6
-rw-r--r--LICENSE21
-rw-r--r--README.rst44
-rw-r--r--RELEASE-NOTES50
-rw-r--r--doc/source/Makefile73
-rw-r--r--doc/source/conf.py179
-rw-r--r--doc/source/index.rst286
-rw-r--r--lockfile/__init__.py347
-rw-r--r--lockfile/linklockfile.py73
-rw-r--r--lockfile/mkdirlockfile.py84
-rw-r--r--lockfile/pidlockfile.py190
-rw-r--r--lockfile/sqlitelockfile.py156
-rw-r--r--lockfile/symlinklockfile.py70
-rw-r--r--setup.cfg35
-rw-r--r--setup.py29
-rw-r--r--test-requirements.txt5
-rw-r--r--test/compliancetest.py262
-rw-r--r--test/test_lockfile.py41
-rw-r--r--tox.ini27
21 files changed, 9 insertions, 1984 deletions
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index fd5295a..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,11 +0,0 @@
-dist
-.tox
-*.pyc
-*~
-doc/build
-AUTHORS
-ChangeLog
-*.egg
-pylockfile.egg-info
-cover
-.coverage
diff --git a/.gitreview b/.gitreview
deleted file mode 100644
index d6e7fce..0000000
--- a/.gitreview
+++ /dev/null
@@ -1,4 +0,0 @@
-[gerrit]
-host=review.openstack.org
-port=29418
-project=openstack/pylockfile.git
diff --git a/ACKS b/ACKS
deleted file mode 100644
index 44519d1..0000000
--- a/ACKS
+++ /dev/null
@@ -1,6 +0,0 @@
-Thanks to the following people for help with lockfile.
-
- Scott Dial
- Ben Finney
- Frank Niessink
- Konstantin Veretennicov
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index 610c079..0000000
--- a/LICENSE
+++ /dev/null
@@ -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.
diff --git a/README.rst b/README.rst
index 7cbb52e..ca8e82c 100644
--- a/README.rst
+++ b/README.rst
@@ -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
diff --git a/RELEASE-NOTES b/RELEASE-NOTES
deleted file mode 100644
index 8b452ed..0000000
--- a/RELEASE-NOTES
+++ /dev/null
@@ -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.
diff --git a/doc/source/Makefile b/doc/source/Makefile
deleted file mode 100644
index 1b1e8d2..0000000
--- a/doc/source/Makefile
+++ /dev/null
@@ -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 ../../$@ *)
diff --git a/doc/source/conf.py b/doc/source/conf.py
deleted file mode 100644
index 623edcb..0000000
--- a/doc/source/conf.py
+++ /dev/null
@@ -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
diff --git a/doc/source/index.rst b/doc/source/index.rst
deleted file mode 100644
index 0b43c48..0000000
--- a/doc/source/index.rst
+++ /dev/null
@@ -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
diff --git a/lockfile/__init__.py b/lockfile/__init__.py
deleted file mode 100644
index a6f44a5..0000000
--- a/lockfile/__init__.py
+++ /dev/null
@@ -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
diff --git a/lockfile/linklockfile.py b/lockfile/linklockfile.py
deleted file mode 100644
index 2ca9be0..0000000
--- a/lockfile/linklockfile.py
+++ /dev/null
@@ -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)
diff --git a/lockfile/mkdirlockfile.py b/lockfile/mkdirlockfile.py
deleted file mode 100644
index 05a8c96..0000000
--- a/lockfile/mkdirlockfile.py
+++ /dev/null
@@ -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)
diff --git a/lockfile/pidlockfile.py b/lockfile/pidlockfile.py
deleted file mode 100644
index 069e85b..0000000
--- a/lockfile/pidlockfile.py
+++ /dev/null
@@ -1,190 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# pidlockfile.py
-#
-# Copyright © 2008–2009 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
diff --git a/lockfile/sqlitelockfile.py b/lockfile/sqlitelockfile.py
deleted file mode 100644
index f997e24..0000000
--- a/lockfile/sqlitelockfile.py
+++ /dev/null
@@ -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()
diff --git a/lockfile/symlinklockfile.py b/lockfile/symlinklockfile.py
deleted file mode 100644
index 915cf19..0000000
--- a/lockfile/symlinklockfile.py
+++ /dev/null
@@ -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)
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 16ed79f..0000000
--- a/setup.cfg
+++ /dev/null
@@ -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
diff --git a/setup.py b/setup.py
deleted file mode 100644
index 782bb21..0000000
--- a/setup.py
+++ /dev/null
@@ -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)
diff --git a/test-requirements.txt b/test-requirements.txt
deleted file mode 100644
index 446a41f..0000000
--- a/test-requirements.txt
+++ /dev/null
@@ -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
diff --git a/test/compliancetest.py b/test/compliancetest.py
deleted file mode 100644
index bf4e59c..0000000
--- a/test/compliancetest.py
+++ /dev/null
@@ -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
diff --git a/test/test_lockfile.py b/test/test_lockfile.py
deleted file mode 100644
index ca1ed06..0000000
--- a/test/test_lockfile.py
+++ /dev/null
@@ -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
diff --git a/tox.ini b/tox.ini
deleted file mode 100644
index 1178d9a..0000000
--- a/tox.ini
+++ /dev/null
@@ -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