Coverage for src/lektor_ng/admin/context.py: 100%

49 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-08-03 19:18 +0000

1from __future__ import annotations 

2 

3from typing import TYPE_CHECKING, Any, NamedTuple 

4 

5from flask import Flask, current_app, g 

6from werkzeug.utils import cached_property 

7 

8from lektor_ng.builder import Builder 

9from lektor_ng.buildfailures import FailureController 

10from lektor_ng.db import Database, Pad, Tree 

11from lektor_ng.environment import Environment 

12from lektor_ng.environment.config import Config 

13from lektor_ng.reporter import CliReporter 

14 

15if TYPE_CHECKING: 

16 from _typeshed import StrPath 

17 

18 

19class LektorInfo(NamedTuple): 

20 env: Environment 

21 output_path: StrPath 

22 verbosity: int = 0 

23 extra_flags: dict[str, str] | None = None 

24 

25 

26class LektorContext(LektorInfo): 

27 """Per-request object which provides the interface to Lektor for the Flask app(s). 

28 

29 This does not provide any logic. It just provides access to the 

30 needed Lektor internals and instances. 

31 """ 

32 

33 @property 

34 def project_id(self) -> str: 

35 return self.env.project.id 

36 

37 @cached_property 

38 def database(self) -> Database: 

39 return Database(self.env) 

40 

41 @cached_property 

42 def pad(self) -> Pad: 

43 return self.database.new_pad() 

44 

45 @cached_property 

46 def tree(self) -> Tree: 

47 return Tree(self.pad) 

48 

49 @property 

50 def config(self) -> Config: 

51 return self.database.config 

52 

53 @cached_property 

54 def builder(self) -> Builder: 

55 return Builder(self.pad, self.output_path, extra_flags=self.extra_flags) 

56 

57 @cached_property 

58 def failure_controller(self) -> FailureController: 

59 return FailureController(self.pad, self.output_path) 

60 

61 def cli_reporter(self) -> CliReporter: 

62 return CliReporter(self.env, verbosity=self.verbosity) 

63 

64 

65class LektorApp(Flask): 

66 """A Flask app that has a lektor_info attribute.""" 

67 

68 def __init__( 

69 self, 

70 lektor_info: LektorInfo, 

71 **kwargs: Any, 

72 ) -> None: 

73 Flask.__init__(self, "lektor_ng.admin", **kwargs) 

74 self.lektor_info = lektor_info 

75 

76 

77def get_lektor_context() -> LektorContext: 

78 if not hasattr(g, "lektor_context"): 

79 assert isinstance(current_app, LektorApp) 

80 lektor_info = current_app.lektor_info 

81 # pylint: disable=assigning-non-slot 

82 g.lektor_context = LektorContext._make(lektor_info) 

83 return g.lektor_context