lektor_ng.admin.context

src/lektor_ng/admin/context.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
from __future__ import annotations

from typing import TYPE_CHECKING, Any, NamedTuple

from flask import Flask, current_app, g
from werkzeug.utils import cached_property

from lektor_ng.builder import Builder
from lektor_ng.buildfailures import FailureController
from lektor_ng.db import Database, Pad, Tree
from lektor_ng.environment import Environment
from lektor_ng.environment.config import Config
from lektor_ng.reporter import CliReporter

if TYPE_CHECKING:
    from _typeshed import StrPath


class LektorInfo(NamedTuple):
    env: Environment
    output_path: StrPath
    verbosity: int = 0
    extra_flags: dict[str, str] | None = None


class LektorContext(LektorInfo):
    """Per-request object which provides the interface to Lektor for the Flask app(s).

    This does not provide any logic.  It just provides access to the
    needed Lektor internals and instances.
    """

    @property
    def project_id(self) -> str:
        return self.env.project.id

    @cached_property
    def database(self) -> Database:
        return Database(self.env)

    @cached_property
    def pad(self) -> Pad:
        return self.database.new_pad()

    @cached_property
    def tree(self) -> Tree:
        return Tree(self.pad)

    @property
    def config(self) -> Config:
        return self.database.config

    @cached_property
    def builder(self) -> Builder:
        return Builder(self.pad, self.output_path, extra_flags=self.extra_flags)

    @cached_property
    def failure_controller(self) -> FailureController:
        return FailureController(self.pad, self.output_path)

    def cli_reporter(self) -> CliReporter:
        return CliReporter(self.env, verbosity=self.verbosity)


class LektorApp(Flask):
    """A Flask app that has a lektor_info attribute."""

    def __init__(
        self,
        lektor_info: LektorInfo,
        **kwargs: Any,
    ) -> None:
        Flask.__init__(self, "lektor_ng.admin", **kwargs)
        self.lektor_info = lektor_info


def get_lektor_context() -> LektorContext:
    if not hasattr(g, "lektor_context"):
        assert isinstance(current_app, LektorApp)
        lektor_info = current_app.lektor_info
        # pylint: disable=assigning-non-slot
        g.lektor_context = LektorContext._make(lektor_info)
    return g.lektor_context