integrated portalocker with htmltmpl

This commit is contained in:
Brian Ewins 2006-09-23 01:17:46 +01:00
parent 6bf282eab6
commit 2107817687
4 changed files with 100 additions and 66 deletions

View File

@ -44,6 +44,7 @@ import cgi # for HTML escaping of variables
import urllib # for URL escaping of variables
import cPickle # for template compilation
import gettext
import portalocker # for locking
INCLUDE_DIR = "inc"
@ -57,25 +58,6 @@ PARAM_ESCAPE = 2
PARAM_GLOBAL = 3
PARAM_GETTEXT_STRING = 1
# Find a way to lock files. Currently implemented only for UNIX and windows.
LOCKTYPE_FCNTL = 1
LOCKTYPE_MSVCRT = 2
LOCKTYPE = None
try:
import fcntl
except:
try:
import msvcrt
except:
LOCKTYPE = None
else:
LOCKTYPE = LOCKTYPE_MSVCRT
else:
LOCKTYPE = LOCKTYPE_FCNTL
LOCK_EX = 1
LOCK_SH = 2
LOCK_UN = 3
##############################################
# CLASS: TemplateManager #
##############################################
@ -130,13 +112,6 @@ class TemplateManager:
The <em>TemplateError</em>exception is raised when the precompiled
template cannot be saved. Precompilation is enabled by default.
Precompilation is available only on UNIX and Windows platforms,
because proper file locking which is necessary to ensure
multitask safe behaviour is platform specific and is not
implemented for other platforms. Attempts to enable precompilation
on the other platforms result in raise of the
<em>TemplateError</em> exception.
@param comments Enable or disable template comments.
This optional parameter can be used to enable or disable
template comments.
@ -159,13 +134,6 @@ class TemplateManager:
self._gettext = gettext
self._debug = debug
# Find what module to use to lock files.
# File locking is necessary for the 'precompile' feature to be
# multitask/thread safe. Currently it works only on UNIX
# and Windows. Anyone willing to implement it on Mac ?
if precompile and not LOCKTYPE:
raise TemplateError, "Template precompilation is not "\
"available on this platform."
self.DEB("INIT DONE")
def prepare(self, file):
@ -260,33 +228,6 @@ class TemplateManager:
"""
if self._debug: print >> sys.stderr, str
def lock_file(self, file, lock):
""" Provide platform independent file locking.
@hidden
"""
fd = file.fileno()
if LOCKTYPE == LOCKTYPE_FCNTL:
if lock == LOCK_SH:
fcntl.flock(fd, fcntl.LOCK_SH)
elif lock == LOCK_EX:
fcntl.flock(fd, fcntl.LOCK_EX)
elif lock == LOCK_UN:
fcntl.flock(fd, fcntl.LOCK_UN)
else:
raise TemplateError, "BUG: bad lock in lock_file"
elif LOCKTYPE == LOCKTYPE_MSVCRT:
if lock == LOCK_SH:
# msvcrt does not support shared locks :-(
msvcrt.locking(fd, msvcrt.LK_LOCK, 1)
elif lock == LOCK_EX:
msvcrt.locking(fd, msvcrt.LK_LOCK, 1)
elif lock == LOCK_UN:
msvcrt.locking(fd, msvcrt.LK_UNLCK, 1)
else:
raise TemplateError, "BUG: bad lock in lock_file"
else:
raise TemplateError, "BUG: bad locktype in lock_file"
def compile(self, file):
""" Compile the template.
@hidden
@ -322,7 +263,7 @@ class TemplateManager:
file = None
try:
file = open(filename, "rb")
self.lock_file(file, LOCK_SH)
portalocker.lock(file, portalocker.LOCK_SH)
precompiled = cPickle.load(file)
except IOError, (errno, errstr):
raise TemplateError, "IO error in load precompiled "\
@ -338,7 +279,7 @@ class TemplateManager:
return precompiled
finally:
if file:
self.lock_file(file, LOCK_UN)
portalocker.unlock(file)
file.close()
if remove_bad and os.path.isfile(filename):
# X: We may lose the original exception here, raising OSError.
@ -369,7 +310,7 @@ class TemplateManager:
file = None
try:
file = open(filename, "wb") # may truncate existing file
self.lock_file(file, LOCK_EX)
portalocker.lock(file, portalocker.LOCK_EX)
BINARY = 1
READABLE = 0
if self._debug:
@ -393,7 +334,7 @@ class TemplateManager:
self.DEB("SAVING PRECOMPILED")
finally:
if file:
self.lock_file(file, LOCK_UN)
portalocker.unlock(file)
file.close()
if remove_bad and os.path.isfile(filename):
# X: We may lose the original exception here, raising OSError.

93
planet/portalocker.py Normal file
View File

@ -0,0 +1,93 @@
# portalocker.py - Cross-platform (posix/nt) API for flock-style file locking.
# Requires python 1.5.2 or better.
# See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/65203/index_txt
# Except where otherwise noted, recipes in the Python Cookbook are
# published under the Python license.
"""Cross-platform (posix/nt) API for flock-style file locking.
Synopsis:
import portalocker
file = open("somefile", "r+")
portalocker.lock(file, portalocker.LOCK_EX)
file.seek(12)
file.write("foo")
file.close()
If you know what you're doing, you may choose to
portalocker.unlock(file)
before closing the file, but why?
Methods:
lock( file, flags )
unlock( file )
Constants:
LOCK_EX
LOCK_SH
LOCK_NB
I learned the win32 technique for locking files from sample code
provided by John Nielsen <nielsenjf@my-deja.com> in the documentation
that accompanies the win32 modules.
Author: Jonathan Feinberg <jdf@pobox.com>
Version: $Id: portalocker.py,v 1.3 2001/05/29 18:47:55 Administrator Exp $
"""
import os
if os.name == 'nt':
import win32con
import win32file
import pywintypes
LOCK_EX = win32con.LOCKFILE_EXCLUSIVE_LOCK
LOCK_SH = 0 # the default
LOCK_NB = win32con.LOCKFILE_FAIL_IMMEDIATELY
# is there any reason not to reuse the following structure?
__overlapped = pywintypes.OVERLAPPED()
elif os.name == 'posix':
import fcntl
LOCK_EX = fcntl.LOCK_EX
LOCK_SH = fcntl.LOCK_SH
LOCK_NB = fcntl.LOCK_NB
else:
raise RuntimeError("PortaLocker only defined for nt and posix platforms")
if os.name == 'nt':
def lock(file, flags):
hfile = win32file._get_osfhandle(file.fileno())
win32file.LockFileEx(hfile, flags, 0, -0x10000, __overlapped)
def unlock(file):
hfile = win32file._get_osfhandle(file.fileno())
win32file.UnlockFileEx(hfile, 0, -0x10000, __overlapped)
elif os.name =='posix':
def lock(file, flags):
fcntl.flock(file.fileno(), flags)
def unlock(file):
fcntl.flock(file.fileno(), fcntl.LOCK_UN)
if __name__ == '__main__':
from time import time, strftime, localtime
import sys
import portalocker
log = open('log.txt', "a+")
portalocker.lock(log, portalocker.LOCK_EX)
timestamp = strftime("%m/%d/%Y %H:%M:%S\n", localtime(time()))
log.write( timestamp )
print "Wrote lines. Hit enter to release lock."
dummy = sys.stdin.readline()
log.close()

View File

@ -225,7 +225,7 @@ def template_info(source):
def run(script, doc, output_file=None, options={}):
""" process an HTMLTMPL file """
manager = htmltmpl.TemplateManager(precompile=(sys.platform.find('win')<0))
manager = htmltmpl.TemplateManager()
template = manager.prepare(script)
tp = htmltmpl.TemplateProcessor(html_escape=0)
for key,value in template_info(doc).items():

View File

@ -32,7 +32,7 @@ class ApplyTest(unittest.TestCase):
for file in ['index.html', 'default.css', 'images/foaf.png']:
path = os.path.join(workdir, file)
self.assertTrue(os.path.exists(path))
self.assertTrue(os.stat(path).st_size > 0)
self.assertTrue(os.stat(path).st_size > 0, file + ' has size 0')
# verify that index.html is well formed, has content, and xml:lang
html = open(os.path.join(workdir, 'index.html'))