# utils.py vi:ts=4:sw=4:expandtab:
#
# Copyright (c) 2006 Three Rings Design, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. Neither the name of the copyright owner nor the names of contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from twisted.internet import reactor, defer
import shutil, os, stat
class ExecutionFailureContext(object):
def __init__(self, context, failure):
self.executionContext = context
self.originalFailure = failure
class ExecutionUnit(object):
"""
An execution unit to be passed to an OrderedExecutor
"""
def __init__(self, context, callable, *args, **kwargs):
"""
Initialize an ExecutionUnit
@param context: User-specified context
@param callable: Callable function or method
@param *args: Arguments to callable
@param **kwargs: Keyword arguments to callable.
"""
self.context = context
self.callable = (callable, args, kwargs)
class OrderedExecutor(object):
"""
Serialized execution of a list of deferred-returning callables
according to the order in which they are added.
"""
def __init__(self):
self.eunits = []
def appendExecutionUnit(self, eunit):
"""
Append an ExecutionUnit to the end of the ordered list
"""
self.eunits.append(eunit)
def _nextWorker(self):
"""
Callable generator
"""
for eunit in self.eunits:
yield eunit
def _handleFailure(self, failure, context, d):
d.errback(ExecutionFailureContext(context, failure))
def run(self):
"""
Run all callables serially. Stop if an error occurs.
@result A deferred that while fire when all callables have been run,
or an error has occured.
"""
d = defer.Deferred()
work = iter(self._nextWorker())
# In our nested callback, iterate over callables returned by our
# generator
def cb(result):
try:
eunit = work.next()
new = eunit.callable[0](*eunit.callable[1], **eunit.callable[2])
except StopIteration:
# Iteration complete
d.callback(None)
else:
# Add our nested callback to the new deferred
new.addCallback(cb)
new.addErrback(self._handleFailure, eunit.context, d)
# Kick-off the looping deferred calls
try:
cb(None)
except Exception, e:
d.errback(e)
return d
class Error(EnvironmentError):
pass
def copyRecursive(src, dst, symlinks=False):
"""
Recursively copy a directory tree using preserving ownership.
Code adapted from the python shutil.copytree implementation.
"""
names = os.listdir(src)
os.makedirs(dst)
errors = []
for name in names:
srcname = os.path.join(src, name)
dstname = os.path.join(dst, name)
try:
if symlinks and os.path.islink(srcname):
linkto = os.readlink(srcname)
os.symlink(linkto, dstname)
elif os.path.isdir(srcname):
copyRecursive(srcname, dstname, symlinks)
else:
copyWithOwnership(srcname, dstname)
except (IOError, os.error), why:
errors.append((srcname, dstname, why))
except Error, err:
errors.extend(err.args[0])
shutil.copystat(src, dst)
_copyOwnership(src, dst)
if errors:
raise Error, errors
def copyWithOwnership(src, dst):
"""
Teach copy2 to preserve ownership
"""
shutil.copy2(src, dst)
_copyOwnership(src, dst)
def _copyOwnership(src, dst):
"""
Copy uid and gid. Code adapted from the python
shutil.copystat implementation.
"""
st = os.stat(src)
os.chown(dst, st.st_uid, st.st_gid)
syntax highlighted by Code2HTML, v. 0.9.1