def function(): hasher = hashlib.new("md5") args_hasher = CodeHasher("md5", hasher) args_hasher.update([argc, argv]) LOGGER.debug("Hashing arguments to %s of %i bytes.", name, args_hasher.size) args_digest_before = args_hasher.digest() code_hasher = CodeHasher("md5", hasher) code_hasher.update(func) LOGGER.debug("Hashing function %s in %i bytes.", name, code_hasher.size) key = hasher.hexdigest() LOGGER.debug("Cache key: %s", key) caller_frame = inspect.currentframe().f_back try: return_value, args_mutated = _read_from_cache( key, persist, ignore_hash, func, caller_frame) except (CacheKeyNotFoundError, CachedObjectWasMutatedError): return_value = func(*argc, **argv) args_hasher_after = CodeHasher("md5") args_hasher_after.update([argc, argv]) args_mutated = args_digest_before != args_hasher_after.digest() _write_to_cache(key, return_value, persist, ignore_hash, args_mutated) if args_mutated: st.warning(_build_args_mutated_message(func)) return return_value
def get_or_set_cache(): hasher = hashlib.new("md5") args_hasher = CodeHasher("md5", hasher, hash_funcs) args_hasher.update([args, kwargs]) LOGGER.debug("Hashing arguments to %s of %i bytes.", name, args_hasher.size) code_hasher = CodeHasher("md5", hasher, hash_funcs) code_hasher.update(func) LOGGER.debug("Hashing function %s in %i bytes.", name, code_hasher.size) key = hasher.hexdigest() LOGGER.debug("Cache key: %s", key) caller_frame = inspect.currentframe().f_back try: return_value = _read_from_cache( key, persist, allow_output_mutation, func, caller_frame, hash_funcs ) LOGGER.debug("Cache hit: %s", func) except CacheKeyNotFoundError: LOGGER.debug("Cache miss: %s", func) with _calling_cached_function(): if suppress_st_warning: with suppress_cached_st_function_warning(): return_value = func(*args, **kwargs) else: return_value = func(*args, **kwargs) _write_to_cache( key=key, value=return_value, persist=persist, allow_output_mutation=allow_output_mutation, hash_funcs=hash_funcs, ) return return_value
def wrapped_func(*argc, **argv): """This function wrapper will only call the underlying function in the case of a cache miss. Cached objects are stored in the cache/ directory.""" if not config.get_option('client.caching'): LOGGER.debug('Purposefully skipping cache') return func(*argc, **argv) name = func.__name__ if len(argc) == 0 and len(argv) == 0: message = 'Running %s().' % name else: message = 'Running %s(...).' % name with st.spinner(message): hasher = hashlib.new('md5') args_hasher = CodeHasher('md5', hasher) args_hasher.update([argc, argv]) LOGGER.debug('Hashing arguments to %s of %i bytes.', name, args_hasher.size) args_digest_before = args_hasher.digest() code_hasher = CodeHasher('md5', hasher) code_hasher.update(func) LOGGER.debug('Hashing function %s in %i bytes.', name, code_hasher.size) key = hasher.hexdigest() LOGGER.debug('Cache key: %s', key) caller_frame = inspect.currentframe().f_back try: return_value, args_mutated = _read_from_cache( key, persist, ignore_hash, func, caller_frame) except (CacheKeyNotFoundError, CachedObjectWasMutatedError): return_value = func(*argc, **argv) args_hasher_after = CodeHasher('md5') args_hasher_after.update([argc, argv]) args_mutated = args_digest_before != args_hasher_after.digest() _write_to_cache( key, return_value, persist, ignore_hash, args_mutated) if args_mutated: st.warning(_build_args_mutated_message(func)) return return_value
def get_or_set_cache(): hasher = hashlib.new("md5") args_hasher = CodeHasher("md5", hasher) args_hasher.update([args, kwargs]) LOGGER.debug("Hashing arguments to %s of %i bytes.", name, args_hasher.size) args_digest_before = args_hasher.digest() code_hasher = CodeHasher("md5", hasher) code_hasher.update(func) LOGGER.debug("Hashing function %s in %i bytes.", name, code_hasher.size) key = hasher.hexdigest() LOGGER.debug("Cache key: %s", key) caller_frame = inspect.currentframe().f_back try: return_value, args_mutated = _read_from_cache( key, persist, allow_output_mutation, func, caller_frame) except (CacheKeyNotFoundError, CachedObjectWasMutatedError): with _calling_cached_function(): if suppress_st_warning: with suppress_cached_st_function_warning(): return_value = func(*args, **kwargs) else: return_value = func(*args, **kwargs) args_hasher_after = CodeHasher("md5") args_hasher_after.update([args, kwargs]) args_mutated = args_digest_before != args_hasher_after.digest() _write_to_cache(key, return_value, persist, allow_output_mutation, args_mutated) if args_mutated: # If we're inside a _nested_ cached function, our # _within_cached_function_counter will be non-zero. # Suppress the warning about this. with suppress_cached_st_function_warning(): st.warning(_build_args_mutated_message(func)) return return_value
def get_hash(f, context=None): hasher = CodeHasher("md5") hasher._get_main_script_directory = MagicMock() hasher._get_main_script_directory.return_value = os.getcwd() hasher.update(f, context) return hasher.digest()
def has_changes(self): current_frame = inspect.currentframe() caller_frame = current_frame.f_back current_file = inspect.getfile(current_frame) caller_file = inspect.getfile(caller_frame) real_caller_is_parent_frame = current_file == caller_file if real_caller_is_parent_frame: caller_frame = caller_frame.f_back frameinfo = inspect.getframeinfo(caller_frame) filename, caller_lineno, _, code_context, _ = frameinfo code_context = code_context[0] context_indent = len(code_context) - len(code_context.lstrip()) lines = [] # TODO: Memoize open(filename, 'r') in a way that clears the memoized # version with each run of the user's script. Then use the memoized # text here, in st.echo, and other places. with open(filename, "r") as f: for line in f.readlines()[caller_lineno:]: if line.strip() == "": lines.append(line) indent = len(line) - len(line.lstrip()) if indent <= context_indent: break if line.strip() and not line.lstrip().startswith("#"): lines.append(line) while lines[-1].strip() == "": lines.pop() code_block = "".join(lines) program = textwrap.dedent(code_block) context = Context(dict(caller_frame.f_globals, **caller_frame.f_locals), {}, {}) code = compile(program, filename, "exec") code_hasher = CodeHasher("md5") code_hasher.update(code, context) LOGGER.debug("Hashing block in %i bytes.", code_hasher.size) key = code_hasher.hexdigest() LOGGER.debug("Cache key: %s", key) try: value, _ = _read_from_cache( key, self._persist, self._allow_output_mutation, code, [caller_lineno + 1, caller_lineno + len(lines)], ) self.update(value) except CacheKeyNotFoundError: if self._allow_output_mutation and not self._persist: # If we don't hash the results, we don't need to use exec and just return True. # This way line numbers will be correct. _write_to_cache( key=key, value=self, persist=False, allow_output_mutation=True ) return True exec(code, caller_frame.f_globals, caller_frame.f_locals) _write_to_cache( key=key, value=self, persist=self._persist, allow_output_mutation=self._allow_output_mutation, ) # Return False so that we have control over the execution. return False
def _get_key(obj: Any) -> str: hasher = CodeHasher() hasher.update(obj) return hasher.hexdigest()