diff options
author | ChangBo Guo(gcb) <eric.guo@easystack.cn> | 2017-09-30 11:31:25 +0800 |
---|---|---|
committer | ChangBo Guo(gcb) <eric.guo@easystack.cn> | 2017-09-30 11:34:10 +0800 |
commit | f3453b829a1df7196bf6de375702277ee0c0c5d5 (patch) | |
tree | e67299ecc2fdbd1fd8bd1a3d78dfa8bcf59267d0 | |
parent | 99870bfa02f1ba490da6a59b1499f0f1578c9825 (diff) | |
download | lockfile-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-- | .gitignore | 11 | ||||
-rw-r--r-- | .gitreview | 4 | ||||
-rw-r--r-- | ACKS | 6 | ||||
-rw-r--r-- | LICENSE | 21 | ||||
-rw-r--r-- | README.rst | 44 | ||||
-rw-r--r-- | RELEASE-NOTES | 50 | ||||
-rw-r--r-- | doc/source/Makefile | 73 | ||||
-rw-r--r-- | doc/source/conf.py | 179 | ||||
-rw-r--r-- | doc/source/index.rst | 286 | ||||
-rw-r--r-- | lockfile/__init__.py | 347 | ||||
-rw-r--r-- | lockfile/linklockfile.py | 73 | ||||
-rw-r--r-- | lockfile/mkdirlockfile.py | 84 | ||||
-rw-r--r-- | lockfile/pidlockfile.py | 190 | ||||
-rw-r--r-- | lockfile/sqlitelockfile.py | 156 | ||||
-rw-r--r-- | lockfile/symlinklockfile.py | 70 | ||||
-rw-r--r-- | setup.cfg | 35 | ||||
-rw-r--r-- | setup.py | 29 | ||||
-rw-r--r-- | test-requirements.txt | 5 | ||||
-rw-r--r-- | test/compliancetest.py | 262 | ||||
-rw-r--r-- | test/test_lockfile.py | 41 | ||||
-rw-r--r-- | tox.ini | 27 |
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 @@ -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. @@ -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 |