jinja

Форк
0
/
test_loader.py 
413 строк · 14.5 Кб
1
import importlib.abc
2
import importlib.machinery
3
import importlib.util
4
import os
5
import shutil
6
import sys
7
import tempfile
8
import time
9
import weakref
10
from pathlib import Path
11

12
import pytest
13

14
from jinja2 import Environment
15
from jinja2 import loaders
16
from jinja2 import PackageLoader
17
from jinja2.exceptions import TemplateNotFound
18
from jinja2.loaders import split_template_path
19

20

21
class TestLoaders:
22
    def test_dict_loader(self, dict_loader):
23
        env = Environment(loader=dict_loader)
24
        tmpl = env.get_template("justdict.html")
25
        assert tmpl.render().strip() == "FOO"
26
        pytest.raises(TemplateNotFound, env.get_template, "missing.html")
27

28
    def test_package_loader(self, package_loader):
29
        env = Environment(loader=package_loader)
30
        tmpl = env.get_template("test.html")
31
        assert tmpl.render().strip() == "BAR"
32
        pytest.raises(TemplateNotFound, env.get_template, "missing.html")
33

34
    def test_filesystem_loader_overlapping_names(self, filesystem_loader):
35
        t2_dir = Path(filesystem_loader.searchpath[0]) / ".." / "templates2"
36
        # Make "foo" show up before "foo/test.html".
37
        filesystem_loader.searchpath.insert(0, t2_dir)
38
        e = Environment(loader=filesystem_loader)
39
        e.get_template("foo")
40
        # This would raise NotADirectoryError if "t2/foo" wasn't skipped.
41
        e.get_template("foo/test.html")
42

43
    def test_choice_loader(self, choice_loader):
44
        env = Environment(loader=choice_loader)
45
        tmpl = env.get_template("justdict.html")
46
        assert tmpl.render().strip() == "FOO"
47
        tmpl = env.get_template("test.html")
48
        assert tmpl.render().strip() == "BAR"
49
        pytest.raises(TemplateNotFound, env.get_template, "missing.html")
50

51
    def test_function_loader(self, function_loader):
52
        env = Environment(loader=function_loader)
53
        tmpl = env.get_template("justfunction.html")
54
        assert tmpl.render().strip() == "FOO"
55
        pytest.raises(TemplateNotFound, env.get_template, "missing.html")
56

57
    def test_prefix_loader(self, prefix_loader):
58
        env = Environment(loader=prefix_loader)
59
        tmpl = env.get_template("a/test.html")
60
        assert tmpl.render().strip() == "BAR"
61
        tmpl = env.get_template("b/justdict.html")
62
        assert tmpl.render().strip() == "FOO"
63
        pytest.raises(TemplateNotFound, env.get_template, "missing")
64

65
    def test_caching(self):
66
        changed = False
67

68
        class TestLoader(loaders.BaseLoader):
69
            def get_source(self, environment, template):
70
                return "foo", None, lambda: not changed
71

72
        env = Environment(loader=TestLoader(), cache_size=-1)
73
        tmpl = env.get_template("template")
74
        assert tmpl is env.get_template("template")
75
        changed = True
76
        assert tmpl is not env.get_template("template")
77
        changed = False
78

79
    def test_no_cache(self):
80
        mapping = {"foo": "one"}
81
        env = Environment(loader=loaders.DictLoader(mapping), cache_size=0)
82
        assert env.get_template("foo") is not env.get_template("foo")
83

84
    def test_limited_size_cache(self):
85
        mapping = {"one": "foo", "two": "bar", "three": "baz"}
86
        loader = loaders.DictLoader(mapping)
87
        env = Environment(loader=loader, cache_size=2)
88
        t1 = env.get_template("one")
89
        t2 = env.get_template("two")
90
        assert t2 is env.get_template("two")
91
        assert t1 is env.get_template("one")
92
        env.get_template("three")
93
        loader_ref = weakref.ref(loader)
94
        assert (loader_ref, "one") in env.cache
95
        assert (loader_ref, "two") not in env.cache
96
        assert (loader_ref, "three") in env.cache
97

98
    def test_cache_loader_change(self):
99
        loader1 = loaders.DictLoader({"foo": "one"})
100
        loader2 = loaders.DictLoader({"foo": "two"})
101
        env = Environment(loader=loader1, cache_size=2)
102
        assert env.get_template("foo").render() == "one"
103
        env.loader = loader2
104
        assert env.get_template("foo").render() == "two"
105

106
    def test_dict_loader_cache_invalidates(self):
107
        mapping = {"foo": "one"}
108
        env = Environment(loader=loaders.DictLoader(mapping))
109
        assert env.get_template("foo").render() == "one"
110
        mapping["foo"] = "two"
111
        assert env.get_template("foo").render() == "two"
112

113
    def test_split_template_path(self):
114
        assert split_template_path("foo/bar") == ["foo", "bar"]
115
        assert split_template_path("./foo/bar") == ["foo", "bar"]
116
        pytest.raises(TemplateNotFound, split_template_path, "../foo")
117

118

119
class TestFileSystemLoader:
120
    searchpath = (Path(__file__) / ".." / "res" / "templates").resolve()
121

122
    @staticmethod
123
    def _test_common(env):
124
        tmpl = env.get_template("test.html")
125
        assert tmpl.render().strip() == "BAR"
126
        tmpl = env.get_template("foo/test.html")
127
        assert tmpl.render().strip() == "FOO"
128
        pytest.raises(TemplateNotFound, env.get_template, "missing.html")
129

130
    def test_searchpath_as_str(self):
131
        filesystem_loader = loaders.FileSystemLoader(str(self.searchpath))
132

133
        env = Environment(loader=filesystem_loader)
134
        self._test_common(env)
135

136
    def test_searchpath_as_pathlib(self):
137
        filesystem_loader = loaders.FileSystemLoader(self.searchpath)
138
        env = Environment(loader=filesystem_loader)
139
        self._test_common(env)
140

141
    def test_searchpath_as_list_including_pathlib(self):
142
        filesystem_loader = loaders.FileSystemLoader(
143
            ["/tmp/templates", self.searchpath]
144
        )
145
        env = Environment(loader=filesystem_loader)
146
        self._test_common(env)
147

148
    def test_caches_template_based_on_mtime(self):
149
        filesystem_loader = loaders.FileSystemLoader(self.searchpath)
150

151
        env = Environment(loader=filesystem_loader)
152
        tmpl1 = env.get_template("test.html")
153
        tmpl2 = env.get_template("test.html")
154
        assert tmpl1 is tmpl2
155

156
        os.utime(self.searchpath / "test.html", (time.time(), time.time()))
157
        tmpl3 = env.get_template("test.html")
158
        assert tmpl1 is not tmpl3
159

160
    @pytest.mark.parametrize(
161
        ("encoding", "expect"),
162
        [
163
            ("utf-8", "文字化け"),
164
            ("iso-8859-1", "æ\x96\x87\xe5\xad\x97\xe5\x8c\x96\xe3\x81\x91"),
165
        ],
166
    )
167
    def test_uses_specified_encoding(self, encoding, expect):
168
        loader = loaders.FileSystemLoader(self.searchpath, encoding=encoding)
169
        e = Environment(loader=loader)
170
        t = e.get_template("mojibake.txt")
171
        assert t.render() == expect
172

173
    def test_filename_normpath(self):
174
        """Nested template names should only contain ``os.sep`` in the
175
        loaded filename.
176
        """
177
        loader = loaders.FileSystemLoader(self.searchpath)
178
        e = Environment(loader=loader)
179
        t = e.get_template("foo/test.html")
180
        assert t.filename == str(self.searchpath / "foo" / "test.html")
181

182

183
class TestModuleLoader:
184
    archive = None
185
    mod_env = None
186

187
    def compile_down(self, prefix_loader, zip="deflated"):
188
        log = []
189
        self.reg_env = Environment(loader=prefix_loader)
190
        if zip is not None:
191
            fd, self.archive = tempfile.mkstemp(suffix=".zip")
192
            os.close(fd)
193
        else:
194
            self.archive = tempfile.mkdtemp()
195
        self.reg_env.compile_templates(self.archive, zip=zip, log_function=log.append)
196
        self.mod_env = Environment(loader=loaders.ModuleLoader(self.archive))
197
        return "".join(log)
198

199
    def teardown_method(self):
200
        if self.archive is not None:
201
            if os.path.isfile(self.archive):
202
                os.remove(self.archive)
203
            else:
204
                shutil.rmtree(self.archive)
205
            self.archive = None
206
            self.mod_env = None
207

208
    def test_log(self, prefix_loader):
209
        log = self.compile_down(prefix_loader)
210
        assert (
211
            'Compiled "a/foo/test.html" as '
212
            "tmpl_a790caf9d669e39ea4d280d597ec891c4ef0404a" in log
213
        )
214
        assert "Finished compiling templates" in log
215
        assert (
216
            'Could not compile "a/syntaxerror.html": '
217
            "Encountered unknown tag 'endif'" in log
218
        )
219

220
    def _test_common(self):
221
        tmpl1 = self.reg_env.get_template("a/test.html")
222
        tmpl2 = self.mod_env.get_template("a/test.html")
223
        assert tmpl1.render() == tmpl2.render()
224

225
        tmpl1 = self.reg_env.get_template("b/justdict.html")
226
        tmpl2 = self.mod_env.get_template("b/justdict.html")
227
        assert tmpl1.render() == tmpl2.render()
228

229
    def test_deflated_zip_compile(self, prefix_loader):
230
        self.compile_down(prefix_loader, zip="deflated")
231
        self._test_common()
232

233
    def test_stored_zip_compile(self, prefix_loader):
234
        self.compile_down(prefix_loader, zip="stored")
235
        self._test_common()
236

237
    def test_filesystem_compile(self, prefix_loader):
238
        self.compile_down(prefix_loader, zip=None)
239
        self._test_common()
240

241
    def test_weak_references(self, prefix_loader):
242
        self.compile_down(prefix_loader)
243
        self.mod_env.get_template("a/test.html")
244
        key = loaders.ModuleLoader.get_template_key("a/test.html")
245
        name = self.mod_env.loader.module.__name__
246

247
        assert hasattr(self.mod_env.loader.module, key)
248
        assert name in sys.modules
249

250
        # unset all, ensure the module is gone from sys.modules
251
        self.mod_env = None
252

253
        try:
254
            import gc
255

256
            gc.collect()
257
        except BaseException:
258
            pass
259

260
        assert name not in sys.modules
261

262
    def test_choice_loader(self, prefix_loader):
263
        self.compile_down(prefix_loader)
264
        self.mod_env.loader = loaders.ChoiceLoader(
265
            [self.mod_env.loader, loaders.DictLoader({"DICT_SOURCE": "DICT_TEMPLATE"})]
266
        )
267
        tmpl1 = self.mod_env.get_template("a/test.html")
268
        assert tmpl1.render() == "BAR"
269
        tmpl2 = self.mod_env.get_template("DICT_SOURCE")
270
        assert tmpl2.render() == "DICT_TEMPLATE"
271

272
    def test_prefix_loader(self, prefix_loader):
273
        self.compile_down(prefix_loader)
274
        self.mod_env.loader = loaders.PrefixLoader(
275
            {
276
                "MOD": self.mod_env.loader,
277
                "DICT": loaders.DictLoader({"test.html": "DICT_TEMPLATE"}),
278
            }
279
        )
280
        tmpl1 = self.mod_env.get_template("MOD/a/test.html")
281
        assert tmpl1.render() == "BAR"
282
        tmpl2 = self.mod_env.get_template("DICT/test.html")
283
        assert tmpl2.render() == "DICT_TEMPLATE"
284

285
    def test_path_as_pathlib(self, prefix_loader):
286
        self.compile_down(prefix_loader)
287

288
        mod_path = self.mod_env.loader.module.__path__[0]
289
        mod_loader = loaders.ModuleLoader(Path(mod_path))
290
        self.mod_env = Environment(loader=mod_loader)
291

292
        self._test_common()
293

294
    def test_supports_pathlib_in_list_of_paths(self, prefix_loader):
295
        self.compile_down(prefix_loader)
296

297
        mod_path = self.mod_env.loader.module.__path__[0]
298
        mod_loader = loaders.ModuleLoader([Path(mod_path), "/tmp/templates"])
299
        self.mod_env = Environment(loader=mod_loader)
300

301
        self._test_common()
302

303

304
@pytest.fixture()
305
def package_dir_loader(monkeypatch):
306
    monkeypatch.syspath_prepend(Path(__file__).parent)
307
    return PackageLoader("res")
308

309

310
@pytest.mark.parametrize(
311
    ("template", "expect"), [("foo/test.html", "FOO"), ("test.html", "BAR")]
312
)
313
def test_package_dir_source(package_dir_loader, template, expect):
314
    source, name, up_to_date = package_dir_loader.get_source(None, template)
315
    assert source.rstrip() == expect
316
    assert name.endswith(os.path.join(*split_template_path(template)))
317
    assert up_to_date()
318

319

320
def test_package_dir_list(package_dir_loader):
321
    templates = package_dir_loader.list_templates()
322
    assert "foo/test.html" in templates
323
    assert "test.html" in templates
324

325

326
@pytest.fixture()
327
def package_file_loader(monkeypatch):
328
    monkeypatch.syspath_prepend(Path(__file__).parent / "res")
329
    return PackageLoader("__init__")
330

331

332
@pytest.mark.parametrize(
333
    ("template", "expect"), [("foo/test.html", "FOO"), ("test.html", "BAR")]
334
)
335
def test_package_file_source(package_file_loader, template, expect):
336
    source, name, up_to_date = package_file_loader.get_source(None, template)
337
    assert source.rstrip() == expect
338
    assert name.endswith(os.path.join(*split_template_path(template)))
339
    assert up_to_date()
340

341

342
def test_package_file_list(package_file_loader):
343
    templates = package_file_loader.list_templates()
344
    assert "foo/test.html" in templates
345
    assert "test.html" in templates
346

347

348
@pytest.fixture()
349
def package_zip_loader(monkeypatch):
350
    package_zip = (Path(__file__) / ".." / "res" / "package.zip").resolve()
351
    monkeypatch.syspath_prepend(package_zip)
352
    return PackageLoader("t_pack")
353

354

355
@pytest.mark.parametrize(
356
    ("template", "expect"), [("foo/test.html", "FOO"), ("test.html", "BAR")]
357
)
358
def test_package_zip_source(package_zip_loader, template, expect):
359
    source, name, up_to_date = package_zip_loader.get_source(None, template)
360
    assert source.rstrip() == expect
361
    assert name.endswith(os.path.join(*split_template_path(template)))
362
    assert up_to_date is None
363

364

365
@pytest.mark.xfail(
366
    sys.implementation.name == "pypy",
367
    reason="zipimporter doesn't have a '_files' attribute",
368
    raises=TypeError,
369
)
370
def test_package_zip_list(package_zip_loader):
371
    assert package_zip_loader.list_templates() == ["foo/test.html", "test.html"]
372

373

374
@pytest.mark.parametrize("package_path", ["", ".", "./"])
375
def test_package_zip_omit_curdir(package_zip_loader, package_path):
376
    """PackageLoader should not add or include "." or "./" in the root
377
    path, it is invalid in zip paths.
378
    """
379
    loader = PackageLoader("t_pack", package_path)
380
    assert loader.package_path == ""
381
    source, _, _ = loader.get_source(None, "templates/foo/test.html")
382
    assert source.rstrip() == "FOO"
383

384

385
def test_pep_451_import_hook():
386
    class ImportHook(importlib.abc.MetaPathFinder, importlib.abc.Loader):
387
        def find_spec(self, name, path=None, target=None):
388
            if name != "res":
389
                return None
390

391
            spec = importlib.machinery.PathFinder.find_spec(name)
392
            return importlib.util.spec_from_file_location(
393
                name,
394
                spec.origin,
395
                loader=self,
396
                submodule_search_locations=spec.submodule_search_locations,
397
            )
398

399
        def create_module(self, spec):
400
            return None  # default behaviour is fine
401

402
        def exec_module(self, module):
403
            return None  # we need this to satisfy the interface, it's wrong
404

405
    # ensure we restore `sys.meta_path` after putting in our loader
406
    before = sys.meta_path[:]
407

408
    try:
409
        sys.meta_path.insert(0, ImportHook())
410
        package_loader = PackageLoader("res")
411
        assert "test.html" in package_loader.list_templates()
412
    finally:
413
        sys.meta_path[:] = before
414

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.