+ signal.signal(signal.SIGINT, signal.default_int_handler)
+
+class LockDirectory(object):
+ """
+ Context for locking a directory.
+ """
+ def __init__(self, lockfile, expiry = 3600):
+ self.lockfile = lockfile
+ self.expiry = expiry # by default an hour
+ def __enter__(self):
+ # It's A WAVY
+ for i in range(0, 3):
+ try:
+ os.open(self.lockfile, os.O_CREAT | os.O_EXCL)
+ open(self.lockfile, "w").write("%d" % os.getpid())
+ except OSError as e:
+ if e.errno == errno.EEXIST:
+ # There is a possibility of infinite recursion, but we
+ # expect it to be unlikely, and not harmful if it does happen
+ with LockDirectory(self.lockfile + "_"):
+ # See if we can break the lock
+ try:
+ pid = open(self.lockfile, "r").read().strip()
+ if not os.path.exists("/proc/%s" % pid):
+ # break the lock, try again
+ logging.warning("Breaking orphaned lock at %s", self.lockfile)
+ os.unlink(self.lockfile)
+ continue
+ try:
+ # check if the file is expiry old, if so, break the lock, try again
+ if time.time() - os.stat(self.lockfile).st_mtime > self.expiry:
+ logging.warning("Breaking stale lock at %s", self.lockfile)
+ os.unlink(self.lockfile)
+ continue
+ except OSError as e:
+ if e.errno == errno.ENOENT:
+ continue
+ raise
+ except IOError:
+ # oh hey, it went away; try again
+ continue
+ raise DirectoryLockedError(os.getcwd())
+ elif e.errno == errno.EACCES:
+ raise PermissionsError(os.getcwd())
+ raise
+ return
+ raise DirectoryLockedError(os.getcwd())
+ def __exit__(self, *args):
+ try:
+ os.unlink(self.lockfile)
+ except OSError:
+ pass
+
+def chdir(dir):
+ """
+ Changes a directory, but has special exceptions for certain
+ classes of errors.
+ """
+ try:
+ os.chdir(dir)
+ except OSError as e:
+ if e.errno == errno.EACCES:
+ raise PermissionsError()
+ elif e.errno == errno.ENOENT:
+ raise NoSuchDirectoryError()
+ else: raise e
+
+def dictmap(f, d):
+ """
+ A map function for dictionaries. Only changes values.
+
+ >>> dictmap(lambda x: x + 2, {'a': 1, 'b': 2})
+ {'a': 3, 'b': 4}
+ """
+ return dict((k,f(v)) for k,v in d.items())
+
+def dictkmap(f, d):
+ """
+ A map function for dictionaries that passes key and value.
+
+ >>> dictkmap(lambda x, y: x + y, {1: 4, 3: 4})
+ {1: 5, 3: 7}
+ """
+ return dict((k,f(k,v)) for k,v in d.items())