# Path: hypebot/core/cache_lib.py
# class _TimedCacheElement(object):
# class LRUCache(object):
#   def __init__(self, key: Any, value: Any):
#   def __init__(self, max_items: int, max_age_secs: int = None):
#   def __str__(self) -> AnyStr:
#   def Get(self, key: Any) -> Any:
#   def Put(self, key: Any, value: Any) -> Any:
#   def Del(self, key: Any) -> None:
#   def Iterate(self) -> Generator[Tuple[Any, Any], None, None]:
#   def Flush(self):
#   def _RemoveStaleElements(self):
# 
# Path: hypebot/core/params_lib.py
# def _ParseChannel(channel):
# def _ParseChannels(channels):
#   def __init__(self, defaults=None):
#   def __str__(self):
#   def Override(self, params):
#   def _AssignValueConvertDict(self, key, value):
#   def _ParseDict(self, params):
#   def __setattr__(self, key, value):
#   def get(self, key, default_value=None):  # pylint: disable=invalid-name
#   def AsDict(self):
#   def Lock(self):
#   def _RaiseIfLocked(self):
# def MergeParams(defaults, *overrides):
# class HypeParams(object):
#   SPECIAL_ATTRS = ['_locked']
#   HAS_DYNAMIC_ATTRIBUTES = True
# 
# Path: hypebot/storage/storage_lib.py
# class HypeTransaction(abc.ABC):
# class HypeStore(abc.ABC):
# class CommitAbortError(RuntimeError):
# class SyncedDict(dict):
# class HypeQueue:
#   def __init__(self, tx_name: AnyStr):
#   def __str__(self) -> AnyStr:
#   def name(self) -> AnyStr:
#   def Commit(self) -> bool:
#   def __init__(self, params):
#   def engine(self):
#   def GetValue(self,
#                key: AnyStr,
#                subkey: AnyStr,
#                tx: Optional[HypeTransaction] = None) -> Optional[AnyStr]:
#   def SetValue(self,
#                key: AnyStr,
#                subkey: AnyStr,
#                value: Union[int, AnyStr],
#                tx: Optional[HypeTransaction] = None) -> None:
#   def DeleteKey(self,
#                 key: AnyStr,
#                 tx: Optional[HypeTransaction] = None) -> None:
#   def GetSubkey(self, subkey: AnyStr, tx: Optional[HypeTransaction] = None
#                ) -> List[Tuple[AnyStr, AnyStr]]:
#   def GetHistoricalValues(
#       self,
#       key: AnyStr,
#       subkey: AnyStr,
#       num_past_values: int,
#       tx: Optional[HypeTransaction] = None) -> List[JsonType]:
#   def PrependValue(self,
#                    key: AnyStr,
#                    subkey: AnyStr,
#                    new_value: JsonType,
#                    max_length: Optional[int] = None,
#                    tx: Optional[HypeTransaction] = None) -> None:
#   def NewTransaction(self, tx_name: AnyStr) -> HypeTransaction:
#   def UpdateValue(self,
#                   key: AnyStr,
#                   subkey: AnyStr,
#                   delta: int,
#                   tx: Optional[HypeTransaction] = None) -> None:
#   def GetJsonValue(self,
#                    key: AnyStr,
#                    subkey: AnyStr,
#                    tx: Optional[HypeTransaction] = None
#                   ) -> (Optional[JsonType]):
#   def SetJsonValue(self,
#                    key: AnyStr,
#                    subkey: AnyStr,
#                    json_value: JsonType,
#                    tx: Optional[HypeTransaction] = None) -> None:
#   def UpdateJson(self,
#                  key: AnyStr,
#                  subkey: AnyStr,
#                  transform_fn: Callable[[JsonType], Any],
#                  success_fn: Callable[[JsonType], bool] = lambda _: True,
#                  is_set: bool = False,
#                  tx: Optional[HypeTransaction] = None) -> bool:
#   def RunInTransaction(self, fn: Callable, *args, **kwargs) -> Any:
#     def _Internal(tx):
#   def __init__(self, store, storage_key, storage_subkey=None):
#   def __delitem__(self, key):
#   def __setitem__(self, key, value):
#   def pop(self, key, default=_POP_SENTINEL):
#   def update(self, *args, **kwargs):
#   def _FlushToStorage(self):
#   def Sync(self):
#   def __init__(self, store: HypeStore, name: Text,
#                scheduler: schedule_lib.HypeScheduler,
#                process_fn: Callable[..., bool]):
#   def Enqueue(self, payload: JsonType, tx: HypeTransaction = None) -> None:
#   def ProcessQueue(self,
#                    batch_size: int = 10,
#                    tx: HypeTransaction = None) -> bool:
#   DEFAULT_PARAMS = params_lib.HypeParams()
#   _POP_SENTINEL = object()
#   _DEFAULT_SUBKEY = 'synced_object'
#   _SUBKEY = 'hype_queue'
# 
# Path: hypebot/hype_types.py



from functools import partial
from typing import Any, AnyStr, List, Optional, Tuple
from absl import logging
from redis.exceptions import WatchError
from hypebot.core import cache_lib
from hypebot.core import params_lib
from hypebot.storage import storage_lib
from hypebot.hype_types import JsonType
import json
import redis
    """Adds full_key to transaction if no pending write for full_key exist."""
    if full_key in (cmd.args for cmd in self._command_buffer):
      raise ValueError(
          'Key %s already has a pending write in %s.' % (full_key, self))
    self.watch(full_key)

  def Commit(self):
    self._ApplyBufferedCommands()
    try:
      status = self._pipe.execute()
      self._pipe.reset()
      # status is a list of return values from the redis server. It's possible
      # one of them actually demonstrates a failure, but most failures either
      # raise or end up with an empty status, so we just return that.
      return status is not None
    except WatchError:
      logging.info('%s Commit failed due to watch failure, retrying', self)
      return False


class RedisStore(storage_lib.HypeStore):
  """Store hype in Redis.

  Note: Redis does not actually have strongly atomic reads, only optimistic
  transactions. Put another way, you can't guarantee a read of two separte keys
  is consistent (one might change before you can read the other). Redis supports
  optimistic transactions using WATCH on any keys you've read, which means that
  given enough time (potentially infinite :^) ), you'll get an atomic read.
  """


# Path: hypebot/core/cache_lib.py
# class _TimedCacheElement(object):
# class LRUCache(object):
#   def __init__(self, key: Any, value: Any):
#   def __init__(self, max_items: int, max_age_secs: int = None):
#   def __str__(self) -> AnyStr:
#   def Get(self, key: Any) -> Any:
#   def Put(self, key: Any, value: Any) -> Any:
#   def Del(self, key: Any) -> None:
#   def Iterate(self) -> Generator[Tuple[Any, Any], None, None]:
#   def Flush(self):
#   def _RemoveStaleElements(self):
# 
# Path: hypebot/core/params_lib.py
# def _ParseChannel(channel):
# def _ParseChannels(channels):
#   def __init__(self, defaults=None):
#   def __str__(self):
#   def Override(self, params):
#   def _AssignValueConvertDict(self, key, value):
#   def _ParseDict(self, params):
#   def __setattr__(self, key, value):
#   def get(self, key, default_value=None):  # pylint: disable=invalid-name
#   def AsDict(self):
#   def Lock(self):
#   def _RaiseIfLocked(self):
# def MergeParams(defaults, *overrides):
# class HypeParams(object):
#   SPECIAL_ATTRS = ['_locked']
#   HAS_DYNAMIC_ATTRIBUTES = True
# 
# Path: hypebot/storage/storage_lib.py
# class HypeTransaction(abc.ABC):
# class HypeStore(abc.ABC):
# class CommitAbortError(RuntimeError):
# class SyncedDict(dict):
# class HypeQueue:
#   def __init__(self, tx_name: AnyStr):
#   def __str__(self) -> AnyStr:
#   def name(self) -> AnyStr:
#   def Commit(self) -> bool:
#   def __init__(self, params):
#   def engine(self):
#   def GetValue(self,
#                key: AnyStr,
#                subkey: AnyStr,
#                tx: Optional[HypeTransaction] = None) -> Optional[AnyStr]:
#   def SetValue(self,
#                key: AnyStr,
#                subkey: AnyStr,
#                value: Union[int, AnyStr],
#                tx: Optional[HypeTransaction] = None) -> None:
#   def DeleteKey(self,
#                 key: AnyStr,
#                 tx: Optional[HypeTransaction] = None) -> None:
#   def GetSubkey(self, subkey: AnyStr, tx: Optional[HypeTransaction] = None
#                ) -> List[Tuple[AnyStr, AnyStr]]:
#   def GetHistoricalValues(
#       self,
#       key: AnyStr,
#       subkey: AnyStr,
#       num_past_values: int,
#       tx: Optional[HypeTransaction] = None) -> List[JsonType]:
#   def PrependValue(self,
#                    key: AnyStr,
#                    subkey: AnyStr,
#                    new_value: JsonType,
#                    max_length: Optional[int] = None,
#                    tx: Optional[HypeTransaction] = None) -> None:
#   def NewTransaction(self, tx_name: AnyStr) -> HypeTransaction:
#   def UpdateValue(self,
#                   key: AnyStr,
#                   subkey: AnyStr,
#                   delta: int,
#                   tx: Optional[HypeTransaction] = None) -> None:
#   def GetJsonValue(self,
#                    key: AnyStr,
#                    subkey: AnyStr,
#                    tx: Optional[HypeTransaction] = None
#                   ) -> (Optional[JsonType]):
#   def SetJsonValue(self,
#                    key: AnyStr,
#                    subkey: AnyStr,
#                    json_value: JsonType,
#                    tx: Optional[HypeTransaction] = None) -> None:
#   def UpdateJson(self,
#                  key: AnyStr,
#                  subkey: AnyStr,
#                  transform_fn: Callable[[JsonType], Any],
#                  success_fn: Callable[[JsonType], bool] = lambda _: True,
#                  is_set: bool = False,
#                  tx: Optional[HypeTransaction] = None) -> bool:
#   def RunInTransaction(self, fn: Callable, *args, **kwargs) -> Any:
#     def _Internal(tx):
#   def __init__(self, store, storage_key, storage_subkey=None):
#   def __delitem__(self, key):
#   def __setitem__(self, key, value):
#   def pop(self, key, default=_POP_SENTINEL):
#   def update(self, *args, **kwargs):
#   def _FlushToStorage(self):
#   def Sync(self):
#   def __init__(self, store: HypeStore, name: Text,
#                scheduler: schedule_lib.HypeScheduler,
#                process_fn: Callable[..., bool]):
#   def Enqueue(self, payload: JsonType, tx: HypeTransaction = None) -> None:
#   def ProcessQueue(self,
#                    batch_size: int = 10,
#                    tx: HypeTransaction = None) -> bool:
#   DEFAULT_PARAMS = params_lib.HypeParams()
#   _POP_SENTINEL = object()
#   _DEFAULT_SUBKEY = 'synced_object'
#   _SUBKEY = 'hype_queue'
# 
# Path: hypebot/hype_types.py



# Path: hypebot/storage/redis_lib.py
from functools import partial
from typing import Any, AnyStr, List, Optional, Tuple
from absl import logging
from redis.exceptions import WatchError
from hypebot.core import cache_lib
from hypebot.core import params_lib
from hypebot.storage import storage_lib
from hypebot.hype_types import JsonType
import json
import redis

    """Adds full_key to transaction if no pending write for full_key exist."""
    if full_key in (cmd.args for cmd in self._command_buffer):
      raise ValueError(
          'Key %s already has a pending write in %s.' % (full_key, self))
    self.watch(full_key)

  def Commit(self):
    self._ApplyBufferedCommands()
    try:
      status = self._pipe.execute()
      self._pipe.reset()
      # status is a list of return values from the redis server. It's possible
      # one of them actually demonstrates a failure, but most failures either
      # raise or end up with an empty status, so we just return that.
      return status is not None
    except WatchError:
      logging.info('%s Commit failed due to watch failure, retrying', self)
      return False


class RedisStore(storage_lib.HypeStore):
  """Store hype in Redis.

  Note: Redis does not actually have strongly atomic reads, only optimistic
  transactions. Put another way, you can't guarantee a read of two separte keys
  is consistent (one might change before you can read the other). Redis supports
  optimistic transactions using WATCH on any keys you've read, which means that
  given enough time (potentially infinite :^) ), you'll get an atomic read.
  """


  DEFAULT_PARAMS = params_lib.MergeParams(