""" Implements a locked shelf using fcntl.lockf and shelve.Shelf.
"""

# Copyright (C) 2003 Piers Lauder
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
# Author Contact Details:
#	email:	piers@it.usyd.edu.au
#	post:	Piers Lauder
#		School of Information Technologies
#		Sydney University
#		NSW, 2006, Australia

__author__ = "Piers Lauder, February 2003"
__licence__ = "GPL"

import anydbm, fcntl, os, shelve, time



class DbfilenameShelf(shelve.Shelf):

	def __init__(self, filename, flag='c', protocol=None, writeback=False, binary=None):

		# NB: the lock ought to be obtained here -
		# but anydbm open below seems to break any lock!

		self._lock_fd = -1	# In case exception below

		for retry in (2, 1, 0):
			try:
				db = anydbm.open(filename, flag)
				break
			except anydbm.error, val:
				raise ValueError(val)
			except:
				if not retry:
					raise
				time.sleep(2.0 - (0.9*retry))

		# shelve.Shelf.__init__(self, db, protocol, writeback, binary)
		shelve.Shelf.__init__(self, db)	# Assume defaults, and remain compatible with older version

		# Lock here better than no lock at all...

		if flag != 'r':
			mode, lock = os.O_RDWR, fcntl.LOCK_EX
		else:
			mode, lock = os.O_RDONLY, fcntl.LOCK_SH

		for retry in (1, 0):
			try:
				self._lock_fd = os.open(filename, mode)
				break
			except OSError, val:	# No such file or directory?
				if not retry or os.path.exists(filename):
					raise
				# Ensure filename exists.
				open(filename, 'w').close()

		fcntl.lockf(self._lock_fd, lock)


	def close(self):
		if self._lock_fd == -1:
			return

		shelve.Shelf.close(self)
		fcntl.lockf(self._lock_fd, fcntl.LOCK_UN)
		os.close(self._lock_fd)
		self._lock_fd = -1



def open(filename, flag='c', protocol=None, writeback=False, binary=None):
	"""Open a persistent dictionary for reading and writing.

	The filename parameter is the base filename for the underlying
	database.  As a side-effect, an extension may be added to the
	filename and more than one file may be created.  The optional flag
	parameter has the same interpretation as the flag parameter of
	anydbm.open(). The optional protocol parameter specifies the
	version of the pickle protocol (0, 1, or 2).

	The optional binary parameter is deprecated and may be set to True
	to force the use of binary pickles for serializing data values.

	The database is locked for exclusive access until close.
	"""

	return DbfilenameShelf(filename, flag, protocol, writeback, binary)
# code highlighted using py2html.py version 0.8