def has_changes(self) -> bool: current_frame = inspect.currentframe() assert current_frame is not None 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 filename, caller_lineno, code_context = _get_frame_info(caller_frame) assert code_context is not None 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") hasher = hashlib.new("md5") update_hash( code, hasher=hasher, context=context, hash_reason=HashReason.CACHING_BLOCK, hash_source=code, ) key = hasher.hexdigest() _LOGGER.debug("Cache key: %s", key) try: value, _ = _read_from_cache( mem_cache=self._mem_cache, key=key, persist=self._persist, allow_output_mutation=self._allow_output_mutation, func_or_code=code, ) 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( mem_cache=self._mem_cache, key=key, value=self, persist=False, allow_output_mutation=True, func_or_code=code, ) return True exec(code, caller_frame.f_globals, caller_frame.f_locals) _write_to_cache( mem_cache=self._mem_cache, key=key, value=self, persist=self._persist, allow_output_mutation=self._allow_output_mutation, func_or_code=code, ) # Return False so that we have control over the execution. return False
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, CachedObjectWasMutatedError): 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, self, False, True, None) return True exec(code, caller_frame.f_globals, caller_frame.f_locals) _write_to_cache(key, self, self._persist, self._allow_output_mutation, None) # Return False so that we have control over the execution. return False