From 5832d05c65bbbd932fb60844c1afca89e591b292 Mon Sep 17 00:00:00 2001 From: Julien Danjou Date: Thu, 28 Aug 2014 14:52:46 +0200 Subject: Add .gitreview, tox targets and use pbr Change-Id: I9360c3ea4354f296039e11b09cb4e3b0771f87b4 --- .gitignore | 5 + .gitreview | 4 + MANIFEST | 19 ---- MANIFEST.in | 4 - doc/Makefile | 73 ------------- doc/conf.py | 179 -------------------------------- doc/glossary.rst | 15 --- doc/index.rst | 22 ---- doc/lockfile.rst | 276 -------------------------------------------------- doc/source/Makefile | 73 +++++++++++++ doc/source/conf.py | 179 ++++++++++++++++++++++++++++++++ doc/source/index.rst | 275 +++++++++++++++++++++++++++++++++++++++++++++++++ setup.cfg | 33 ++++++ setup.py | 56 +++++----- test-requirements.txt | 2 + tox.ini | 18 +++- 16 files changed, 615 insertions(+), 618 deletions(-) create mode 100644 .gitreview delete mode 100644 MANIFEST delete mode 100644 MANIFEST.in delete mode 100644 doc/Makefile delete mode 100644 doc/conf.py delete mode 100644 doc/glossary.rst delete mode 100644 doc/index.rst delete mode 100644 doc/lockfile.rst create mode 100644 doc/source/Makefile create mode 100644 doc/source/conf.py create mode 100644 doc/source/index.rst create mode 100644 setup.cfg create mode 100644 test-requirements.txt diff --git a/.gitignore b/.gitignore index e4d5530..e678aaa 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,8 @@ dist .tox *.pyc *~ +doc/build +AUTHORS +ChangeLog +*.egg +pylockfile.egg-info \ No newline at end of file diff --git a/.gitreview b/.gitreview new file mode 100644 index 0000000..d6e7fce --- /dev/null +++ b/.gitreview @@ -0,0 +1,4 @@ +[gerrit] +host=review.openstack.org +port=29418 +project=openstack/pylockfile.git diff --git a/MANIFEST b/MANIFEST deleted file mode 100644 index d302eef..0000000 --- a/MANIFEST +++ /dev/null @@ -1,19 +0,0 @@ -2.4.diff -ACKS -LICENSE -MANIFEST -README -RELEASE-NOTES -setup.py -doc/Makefile -doc/conf.py -doc/glossary.rst -doc/index.rst -doc/lockfile.rst -lockfile/__init__.py -lockfile/linklockfile.py -lockfile/mkdirlockfile.py -lockfile/pidlockfile.py -lockfile/sqlitelockfile.py -test/compliancetest.py -test/test_lockfile.py diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 31903eb..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,4 +0,0 @@ -include README RELEASE-NOTES LICENSE MANIFEST setup.py ACKS 2.4.diff -recursive-include doc *.rst conf.py Makefile -recursive-include test *.py -recursive-include lockfile *.py diff --git a/doc/Makefile b/doc/Makefile deleted file mode 100644 index 1b1e8d2..0000000 --- a/doc/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 ' where 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/conf.py b/doc/conf.py deleted file mode 100644 index 64678e9..0000000 --- a/doc/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 = 'lockfile' - -# 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 -# " v 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/. -#html_copy_source = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a 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/glossary.rst b/doc/glossary.rst deleted file mode 100644 index 9401d48..0000000 --- a/doc/glossary.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. _glossary: - -******** -Glossary -******** - -.. if you add new entries, keep the alphabetical sorting! - -.. glossary:: - - context manager - An object which controls the environment seen in a :keyword:`with` - statement by defining :meth:`__enter__` and :meth:`__exit__` methods. - See :pep:`343`. - diff --git a/doc/index.rst b/doc/index.rst deleted file mode 100644 index 9718a68..0000000 --- a/doc/index.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. lockfile documentation master file, created by sphinx-quickstart on Sat Sep 13 17:54:17 2008. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to lockfile's documentation! -==================================== - -Contents: - -.. toctree:: - :maxdepth: 2 - - lockfile.rst - glossary.rst - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - diff --git a/doc/lockfile.rst b/doc/lockfile.rst deleted file mode 100644 index f20d8aa..0000000 --- a/doc/lockfile.rst +++ /dev/null @@ -1,276 +0,0 @@ - -:mod:`lockfile` --- Platform-independent file locking -===================================================== - -.. module:: lockfile - :synopsis: Platform-independent file locking -.. moduleauthor:: Skip Montanaro -.. sectionauthor:: Skip Montanaro - - -.. 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 :keyword:`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 :keyword:`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 :term:`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 -:keyword:`with` statement with a :keyword:`try` statement. The resulting -construct would not be any simpler than just using a :keyword:`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, contact me directly: Skip Montanaro (skip@pobox.com). diff --git a/doc/source/Makefile b/doc/source/Makefile new file mode 100644 index 0000000..1b1e8d2 --- /dev/null +++ b/doc/source/Makefile @@ -0,0 +1,73 @@ +# 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 ' where 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 new file mode 100644 index 0000000..623edcb --- /dev/null +++ b/doc/source/conf.py @@ -0,0 +1,179 @@ +# -*- 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 +# " v 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/. +#html_copy_source = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a 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 new file mode 100644 index 0000000..2ed17dc --- /dev/null +++ b/doc/source/index.rst @@ -0,0 +1,275 @@ + +:mod:`lockfile` --- Platform-independent file locking +===================================================== + +.. module:: lockfile + :synopsis: Platform-independent file locking +.. moduleauthor:: Skip Montanaro +.. sectionauthor:: Skip Montanaro + + +.. 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, contact me directly: Skip Montanaro (skip@pobox.com). diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..b05506f --- /dev/null +++ b/setup.cfg @@ -0,0 +1,33 @@ +[metadata] +name = pylockfile +summary = Platform-independent file locking module +description-file = + README +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 :: 2.6 + 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 diff --git a/setup.py b/setup.py index 14735b7..7363757 100644 --- a/setup.py +++ b/setup.py @@ -1,32 +1,30 @@ #!/usr/bin/env python +# 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. -V = "0.9.1" +# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT +import setuptools -from distutils.core import setup -setup(name='lockfile', - author='Skip Montanaro', - author_email='skip@pobox.com', - url='http://code.google.com/p/pylockfile/', - download_url=('http://code.google.com/p/pylockfile/downloads/' - 'detail?name=lockfile-%s.tar.gz' % V), - version=V, - description="Platform-independent file locking module", - long_description=open("README").read(), - packages=['lockfile'], - license='MIT License', - classifiers=[ - 'Development Status :: 4 - Beta', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: MIT License', - 'Operating System :: MacOS', - 'Operating System :: Microsoft :: Windows :: Windows NT/2000', - 'Operating System :: POSIX', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2.4', - 'Programming Language :: Python :: 2.5', - 'Programming Language :: Python :: 2.6', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.0', - 'Topic :: Software Development :: Libraries :: Python Modules', - ] - ) +# 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'], + pbr=True) diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 0000000..2e087ff --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1,2 @@ +nose +sphinx>=1.1.2,!=1.2.0,<1.3 diff --git a/tox.ini b/tox.ini index 1bb07d8..cf7f1ad 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,22 @@ # content of: tox.ini , put in same dir as setup.py [tox] envlist = py26,py27,py32,py33 + [testenv] -deps = nose +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 + +[flake8] +ignore = E121,E123,E128,E221,E226,E261,E265,E301,E302,E713,F401,F841,W291,W293,W391 +exclude=.venv,.git,.tox,dist,doc +show-source = True -- cgit v1.2.1