Passed
Push — master ( ede4b9...d9bb2e )
by Jochen
02:14
created

byceps.blueprints.snippet.templating   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 130
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 72
dl 0
loc 130
rs 10
c 0
b 0
f 0
wmc 16

7 Functions

Rating   Name   Duplication   Size   Complexity  
A url_for_snippet() 0 12 1
A _get_mountpoints() 0 15 2
A render_snippet_as_partial() 0 23 5
A _render_template() 0 7 2
A render_snippet_as_page() 0 16 3
A _load_template_with_globals() 0 8 1
A get_snippet_context() 0 14 2
1
"""
2
byceps.blueprints.snippet.templating
3
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
5
:Copyright: 2006-2019 Jochen Kupperschmidt
6
:License: Modified BSD, see LICENSE for details.
7
"""
8
9
import sys
10
import traceback
11
from typing import Set
12
13
from flask import abort, g, render_template, url_for
14
from jinja2 import TemplateNotFound
15
16
from ...services.snippet import mountpoint_service, service as snippet_service
17
from ...services.snippet.service import SnippetNotFound
18
from ...services.snippet.transfer.models import Mountpoint, Scope
19
from ...util.templating import get_variable_value, load_template
20
21
22
def render_snippet_as_page(version):
23
    """Render the given version of the snippet, or an error page if
24
    that fails.
25
    """
26
    try:
27
        context = get_snippet_context(version)
28
        return render_template('snippet/view.html', **context)
29
    except TemplateNotFound:
30
        abort(404)
31
    except Exception as e:
32
        print('Error in snippet markup:', e, file=sys.stderr)
33
        traceback.print_exc()
34
        context = {
35
            'message': str(e),
36
        }
37
        return render_template('snippet/error.html', **context), 500
38
39
40
def get_snippet_context(version):
41
    """Return the snippet context to insert into the outer template."""
42
    template = _load_template_with_globals(version.body)
43
44
    current_page = get_variable_value(template, 'current_page')
45
    title = version.title
46
    head = _render_template(version.head) if version.head else None
47
    body = template.render()
48
49
    return {
50
        'title': title,
51
        'current_page': current_page,
52
        'head': head,
53
        'body': body,
54
    }
55
56
57
def render_snippet_as_partial(
58
    name, *, scope=None, ignore_if_unknown=False, context=None
59
):
60
    """Render the latest version of the snippet with the given name and
61
    return the result.
62
    """
63
    if scope is None:
64
        scope = Scope.for_site(g.site_id)
65
66
    current_version = snippet_service.find_current_version_of_snippet_with_name(
67
        scope, name
68
    )
69
70
    if current_version is None:
71
        if ignore_if_unknown:
72
            return ''
73
        else:
74
            raise SnippetNotFound(scope, name)
75
76
    if context is None:
77
        context = {}
78
79
    return _render_template(current_version.body, context=context)
80
81
82
def _render_template(source, *, context=None):
83
    template = _load_template_with_globals(source)
84
85
    if context is None:
86
        context = {}
87
88
    return template.render(**context)
89
90
91
def _load_template_with_globals(source):
92
    template_globals = {
93
        'render_snippet': render_snippet_as_partial,
94
        'url_for': url_for,
95
        'url_for_snippet': url_for_snippet,
96
    }
97
98
    return load_template(source, template_globals=template_globals)
99
100
101
def url_for_snippet(endpoint_suffix: str, **kwargs) -> str:
102
    """Render an URL pointing to the snippet document's mountpoint."""
103
    mountpoints = _get_mountpoints()
104
105
    # Endpoint suffix is unique per site.
106
    mountpoints_by_endpoint_suffix = {
107
        mp.endpoint_suffix: mp for mp in mountpoints
108
    }
109
110
    mountpoint = mountpoints_by_endpoint_suffix[endpoint_suffix]
111
112
    return url_for(f'snippet.view', url_path=mountpoint.url_path, **kwargs)
113
114
115
def _get_mountpoints() -> Set[Mountpoint]:
116
    """Return site-specific mountpoints.
117
118
    Preferrably from request-local cache, if available. From the
119
    database if not yet cached.
120
    """
121
    request_context_key = 'snippet_mountpoints'
122
123
    mountpoints_from_request_context = g.get(request_context_key)
124
    if mountpoints_from_request_context:
125
        return mountpoints_from_request_context
126
    else:
127
        mountpoints_from_database = mountpoint_service.get_mountpoints_for_site(g.site_id)
128
        setattr(g, request_context_key, mountpoints_from_database)
129
        return mountpoints_from_database
130