def val(self, default=None): """Get the actual value of this proxy""" full_key = self._get_full_key(None) # Check if this exact key exists. If yes, it's an scalar value. # This check will fail if the user set a 'None' value. # See below for this case. conn = self._conn() value = _to_native(conn.get(full_key)) if value is not None: return json.loads(value) nested = {} for redis_key in conn.scan_iter(full_key + '.*'): value = _to_native(conn.get(redis_key)) # Strip the full key prefix, we're only interested in returning # the children values directly. sub_key = _to_native(redis_key[len(full_key) + 1:]) util.feed_to_nested(nested, sub_key, json.loads(value)) # This is for the case that this key does not exist, or # for the case that the user explicitly set a None value. if len(nested) is 0: # Key really does not exist. if default is None and not self.exists(): return None return default return nested
def _get_sub_keys(conn, full_key): """Helper to yield all sub keys including the own key. Useful to :param conn: (ConnectionPool) Where to get the connection from. :param full_key: (str) """ for redis_key in conn.scan_iter(full_key + ".*"): yield _to_native(redis_key) yield _to_native(full_key)
def iter_children(self): """Iterate over all children including and below this node. :returns: A generator object, yielding ValueProxies. """ own_path = self._get_full_key(None) for redis_key in self._conn().scan_iter(own_path + '.*'): # Split the VAL_TREE_PREFIX away, it will be added again when # creating a new _Proxy. trimmed_key = _to_native(redis_key[len(LOCK_TREE_PREFIX) + 1:]) yield _REGISTRY.proxy_from_registry(trimmed_key, db_name=self._db_name)
def _acquire(self, pipe, key): """Actual implementation of acquire(), called by _find_root_lock :param pipe: (redis.StrictPipe) A redis pipe. :param key: (str) The full key to lock. """ retries = self._acquire_seconds * 20 lock_count = 0 own_pid = os.getpid() own_thread_ident = threading.current_thread().ident while retries > 0: lock_token = _to_native(pipe.get(key)) # Does this lock even exist? if lock_token is None: break # Is it maybe one of our own locks? pid, thread_ident, lock_count = util.parse_lock_token(lock_token) if pid == own_pid and thread_ident == own_thread_ident: break # Cannot lock now. Wait a short time before a retry. time.sleep(0.05) retries -= 1 if retries is 0: raise LockTimeout( "Lock timed out after {} retries".format( self._acquire_seconds * 20 ) ) # Take over and make sure the key expires at some time. # Also increment the lock count. pipe.multi() pipe.set(key, util.build_lock_token(lock_count + 1)) if self._expire_seconds is not None: pipe.expire(key, self._expire_seconds)
def _release(self, pipe, key): """Actual implementation of release(), called by _find_root_lock :param pipe: (redis.Pipe) A redis pipe. :param key: (str) The full key to release. """ lock_token = _to_native(pipe.get(key)) if lock_token is None: # This lock token does not exist anymore. This either means # it expired (okay) or release was called without prior acquire # (which is pretty bad). We can't really tell, so just return. return # We could also check if pid and thread_ident is the same as own, # but that might fail on edge cases where the key expired and # another pid/thread got the lock (validly). We don't check here # since we cannot know for sure. _, _, lock_count = util.parse_lock_token(lock_token) pipe.multi() # These exceptions are supposed to uncover errors in locking logic. if lock_count <= 0: raise InternalError( "Negative or zero lock count ({}) for {} ({})".format( lock_count, key, self ) ) if lock_count == 1: # Remove the key; we're not locking it anymore. pipe.delete(key) else: # We held an recursive lock. Leave our mark. pipe.set(key, util.build_lock_token(lock_count - 1)) if self._expire_seconds is not None: pipe.expire(key, self._expire_seconds)
def _is_locked(pipe, key): """Actual implementation of is_locked""" lock_value = _to_native(pipe.get(key)) return lock_value is not None