File: jinja2.py

package info (click to toggle)
python-django-compressor 4.5.1-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,124 kB
  • sloc: python: 4,906; makefile: 123; javascript: 5
file content (131 lines) | stat: -rw-r--r-- 3,973 bytes parent folder | download | duplicates (2)
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import io

import jinja2
import jinja2.ext
from jinja2 import nodes
from jinja2.ext import Extension
from jinja2.nodes import CallBlock, Call, ExtensionAttribute

from compressor.exceptions import TemplateSyntaxError, TemplateDoesNotExist


def flatten_context(context):
    if hasattr(context, "dicts"):
        context_dict = {}

        for d in context.dicts:
            context_dict.update(d)

        return context_dict

    return context


class SpacelessExtension(Extension):
    """
    Functional "spaceless" extension equivalent to Django's.

    See: https://github.com/django/django/blob/master/django/template/defaulttags.py
    """

    tags = set(["spaceless"])

    def parse(self, parser):
        lineno = next(parser.stream).lineno
        body = parser.parse_statements(["name:endspaceless"], drop_needle=True)

        return nodes.CallBlock(
            self.call_method("_spaceless", []), [], [], body
        ).set_lineno(lineno)

    def _spaceless(self, caller):
        from django.utils.html import strip_spaces_between_tags

        return strip_spaces_between_tags(caller().strip())


def url_for(mod, filename):
    """
    Incomplete emulation of Flask's url_for.
    """
    try:
        from django.contrib.staticfiles.templatetags import staticfiles
    except ImportError:
        # Django 3.0+
        import django.templatetags.static as staticfiles

    if mod == "static":
        return staticfiles.static(filename)

    return ""


class Jinja2Parser:
    COMPRESSOR_ID = "compressor.contrib.jinja2ext.CompressorExtension"

    def __init__(self, charset, env):
        self.charset = charset
        self.env = env

    def parse(self, template_name):
        with io.open(template_name, mode="rb") as file:
            try:
                template = self.env.parse(file.read().decode(self.charset))
            except jinja2.TemplateSyntaxError as e:
                raise TemplateSyntaxError(str(e))
            except jinja2.TemplateNotFound as e:
                raise TemplateDoesNotExist(str(e))

        return template

    def process_template(self, template, context):
        return True

    def get_init_context(self, offline_context):
        # Don't need to add filters and tests to the context, as Jinja2 will
        # automatically look for them in self.env.filters and self.env.tests.
        # This is tested by test_complex and test_templatetag.

        # Allow offline context to override the globals.
        context = self.env.globals.copy()
        context.update(flatten_context(offline_context))

        return context

    def process_node(self, template, context, node):
        pass

    def _render_nodes(self, template, context, nodes):
        compiled_node = self.env.compile(jinja2.nodes.Template(nodes))
        template = jinja2.Template.from_code(self.env, compiled_node, {})
        flat_context = flatten_context(context)

        return template.render(flat_context)

    def render_nodelist(self, template, context, node):
        return self._render_nodes(template, context, node.body)

    def render_node(self, template, context, node):
        return self._render_nodes(template, context, [node])

    def get_nodelist(self, node):
        body = getattr(node, "body", getattr(node, "nodes", []))

        if isinstance(node, jinja2.nodes.If):
            return body + node.else_

        return body

    def walk_nodes(self, node, block_name=None, context=None):
        for node in self.get_nodelist(node):
            if (
                isinstance(node, CallBlock)
                and isinstance(node.call, Call)
                and isinstance(node.call.node, ExtensionAttribute)
                and node.call.node.identifier == self.COMPRESSOR_ID
            ):
                node.call.node.name = "_compress_forced"
                yield node
            else:
                for node in self.walk_nodes(node, block_name=block_name):
                    yield node