text: from __future__ import absolute_import
  from __future__ import division
  from __future__ import print_function
  from __future__ import unicode_literals
  from builtins import str
  
  import argparse
  import errno
  import inspect
  import os
  import shutil
  import sys
  import time
  from datetime import timedelta
  from synapseclient.exceptions import *
  
  LOCK_DEFAULT_MAX_AGE = timedelta(hours=2)
  DEFAULT_BLOCKING_TIMEOUT = timedelta(seconds=70)
  CACHE_UNLOCK_WAIT_TIME = 0.5
  
  
  class LockedException(Exception):
      pass
  
  
  class Lock(object):
      """
      Implements a lock by making a directory named [lockname].lock
      """
      SUFFIX = 'lock'
  
      def __init__(self, name, dir=None, max_age=LOCK_DEFAULT_MAX_AGE, default_blocking_timeout=DEFAULT_BLOCKING_TIMEOUT):
          self.name = name
          self.held = False
          self.dir = dir if dir else os.getcwd()
          self.lock_dir_path = os.path.join(self.dir, ".".join([name, Lock.SUFFIX]))
          self.max_age = max_age
          self.default_blocking_timeout = default_blocking_timeout
  
      def get_age(self):
          try:
              return time.time() - os.path.getmtime(self.lock_dir_path)
          except OSError as err:
              if err.errno != errno.ENOENT and err.errno != errno.EACCES:
                  raise
              return 0
  
      def acquire(self, break_old_locks=True):
          """Try to acquire lock. Return True on success or False otherwise"""
          if self.held:
              return True
          try:
              os.makedirs(self.lock_dir_path)
              self.held = True
              # Make sure the modification times are correct
              # On some machines, the modification time could be seconds off
              os.utime(self.lock_dir_path, (0, time.time()))
          except OSError as err:
              if err.errno != errno.EEXIST and err.errno != errno.EACCES:
                  raise
              # already locked...
              if break_old_locks and self.get_age() > self.max_age.total_seconds():
                  sys.stderr.write("Breaking lock who's age is: %s\n" % self.get_age())
                  self.held = True
                  # Make sure the modification times are correct
                  # On some machines, the modification time could be seconds off
                  os.utime(self.lock_dir_path, (0, time.time()))
              else:
                  self.held = False
          return self.held
  
      def blocking_acquire(self, timeout=None, break_old_locks=True):
          if self.held:
              return True
          if timeout is None:
              timeout = self.default_blocking_timeout
          lock_acquired = False
          tryLockStartTime = time.time()
          while time.time() - tryLockStartTime < timeout.total_seconds():
              lock_acquired = self.acquire(break_old_locks)
              if lock_acquired:
                  break
              else:
                  time.sleep(CACHE_UNLOCK_WAIT_TIME)
          if not lock_acquired:
              raise SynapseFileCacheError("Could not obtain a lock on the file cache within timeout: %s  Please try again later" % str(timeout))
  
      def release(self):
          """Release lock or do nothing if lock is not held"""
          if self.held:
              try:
                  shutil.rmtree(self.lock_dir_path)
                  self.held = False
              except OSError as err:
                  if err.errno != errno.ENOENT:
                      raise
  
      ## Make the lock object a Context Manager
      def __enter__(self):
          self.blocking_acquire()
  
      def __exit__(self, exc_type, exc_value, traceback):
          self.release()
  