示例#1
0
    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
示例#2
0
    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