def wrapper(instance, *args, **kwargs): def partial_func(*args, **kwargs): # Ignores non-hashable instances return func(instance, *args, **kwargs) _func = None if hasattr(instance, wrapper.name): _func = getattr(instance, wrapper.name) if not is_caching_enabled(wrapper.name, instance, func=_func, **wrapper.kwargs): return func(instance, *args, **kwargs) cache = instance.__dict__ cached_func = cache.get(wrapper.attrname, _NOT_FOUND) if cached_func is _NOT_FOUND: with wrapper.lock: # check if another thread filled cache while we awaited lock cached_func = cache.get(wrapper.attrname, _NOT_FOUND) if cached_func is _NOT_FOUND: cached_func = lru_cache(maxsize=wrapper.maxsize, typed=wrapper.typed)(partial_func) cache[wrapper.attrname] = cached_func # store function instead of output # Check if object can be hashed hashable = True for arg in args: if not checks.is_hashable(arg): hashable = False break for k, v in kwargs.items(): if not checks.is_hashable(v): hashable = False break if not hashable: # If not, do not invoke lru_cache return func(instance, *args, **kwargs) return cached_func(*args, **kwargs)
def decorated(*args, **kwargs): if defaults.caching: # Check if object can be hashed hashable = True for arg in args: if not checks.is_hashable(arg): hashable = False break for k, v in kwargs.items(): if not checks.is_hashable(v): hashable = False break if not hashable: # If not, do not invoke lru_cache return self.func(instance, *args, **kwargs) return func(instance, *args, **kwargs)
def download(cls: tp.Type[DataT], symbols: tp.Union[tp.Label, tp.Labels], tz_localize: tp.Optional[tp.TimezoneLike] = None, tz_convert: tp.Optional[tp.TimezoneLike] = None, missing_index: tp.Optional[str] = None, missing_columns: tp.Optional[str] = None, wrapper_kwargs: tp.KwargsLike = None, **kwargs) -> DataT: """Download data using `Data.download_symbol`. Args: symbols (hashable or sequence of hashable): One or multiple symbols. !!! note Tuple is considered as a single symbol (since hashable). tz_localize (any): See `Data.from_data`. tz_convert (any): See `Data.from_data`. missing_index (str): See `Data.from_data`. missing_columns (str): See `Data.from_data`. wrapper_kwargs (dict): See `Data.from_data`. **kwargs: Passed to `Data.download_symbol`. If two symbols require different keyword arguments, pass `symbol_dict` for each argument. """ if checks.is_hashable(symbols): symbols = [symbols] elif not checks.is_sequence(symbols): raise TypeError("Symbols must be either hashable or sequence of hashable") data = dict() for s in symbols: # Select keyword arguments for this symbol _kwargs = cls.select_symbol_kwargs(s, kwargs) # Download data for this symbol data[s] = cls.download_symbol(s, **_kwargs) # Create new instance from data return cls.from_data( data, tz_localize=tz_localize, tz_convert=tz_convert, missing_index=missing_index, missing_columns=missing_columns, wrapper_kwargs=wrapper_kwargs, download_kwargs=kwargs )
def test_is_hashable(self): assert checks.is_hashable(2) assert not checks.is_hashable(np.asarray(2))