view piecrust/environment.py @ 1188:a7c43131d871

bake: Fix file write flushing problem with Python 3.8+ Writing the cache files fails in Python 3.8 because it looks like flushing behaviour has changed. We need to explicitly flush. And even then, in very rare occurrences, it looks like it can still run into racing conditions, so we do a very hacky and ugly "retry" loop when fetching cached data :(
author Ludovic Chabant <ludovic@chabant.com>
date Tue, 15 Jun 2021 22:36:23 -0700
parents a9a592f655e3
children
line wrap: on
line source

import time
import logging
import datetime
import contextlib


logger = logging.getLogger(__name__)


class ExecutionStats:
    def __init__(self):
        self.timers = {}
        self.counters = {}
        self.manifests = {}

    def registerTimer(self, category, *,
                      raise_if_registered=True, time=0):
        if raise_if_registered and category in self.timers:
            raise Exception("Timer '%s' has already been registered." %
                            category)
        self.timers[category] = time

    @contextlib.contextmanager
    def timerScope(self, category):
        start = time.perf_counter()
        yield
        self.timers[category] += time.perf_counter() - start

    def stepTimer(self, category, value):
        self.timers[category] += value

    def stepTimerSince(self, category, since):
        self.stepTimer(category, time.perf_counter() - since)

    def registerCounter(self, category, *, raise_if_registered=True):
        if raise_if_registered and category in self.counters:
            raise Exception("Counter '%s' has already been registered." %
                            category)
        self.counters[category] = 0

    def stepCounter(self, category, inc=1):
        self.counters[category] += inc

    def registerManifest(self, name, *, raise_if_registered=True):
        if raise_if_registered and name in self.manifests:
            raise Exception("Manifest '%s' has already been registered." %
                            name)
        self.manifests[name] = []

    def addManifestEntry(self, name, entry):
        self.manifests[name].append(entry)

    def mergeStats(self, other):
        for oc, ov in other.timers.items():
            v = self.timers.setdefault(oc, 0)
            self.timers[oc] = v + ov
        for oc, ov in other.counters.items():
            v = self.counters.setdefault(oc, 0)
            self.counters[oc] = v + ov
        for oc, ov in other.manifests.items():
            v = self.manifests.setdefault(oc, [])
            self.manifests[oc] = v + ov

    def toData(self):
        return {
            'timers': self.timers.copy(),
            'counters': self.counters.copy(),
            'manifests': self.manifests.copy()}

    def fromData(self, data):
        self.timers = data['timers']
        self.counters = data['counters']
        self.manifests = data['manifests']


class Environment:
    def __init__(self):
        from piecrust.cache import MemCache
        from piecrust.rendering import RenderingContextStack

        self.app = None
        self.start_time = None
        self.start_datetime = None
        self.was_cache_cleaned = False
        self.page_repository = MemCache()
        self.rendered_segments_repository = MemCache()
        self.render_ctx_stack = RenderingContextStack()
        self.fs_cache_only_for_main_page = False
        self.abort_source_use = False
        self._stats = ExecutionStats()

    @property
    def stats(self):
        return self._stats

    def initialize(self, app):
        self.app = app
        self.start_time = time.perf_counter()
        self.start_datetime = datetime.datetime.now()

        self.rendered_segments_repository.fs_cache = \
            app.cache.getCache('renders')

    def _mergeCacheStats(self):
        repos = [
            ('RenderedSegmentsRepo', self.rendered_segments_repository),
            ('PagesRepo', self.page_repository)]
        for name, repo in repos:
            self._stats.counters['%s_hit' % name] = repo._hits
            self._stats.counters['%s_miss' % name] = repo._misses
            self._stats.manifests['%s_missedKeys' % name] = \
                list(repo._missed_keys)


class StandardEnvironment(Environment):
    def __init__(self):
        super(StandardEnvironment, self).__init__()