-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathenvironment.py
117 lines (98 loc) · 3.59 KB
/
environment.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
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
import sys
from lexer import TemplateSyntaxError
from parser import Parser
from lexer import Lexer
from compiler import generate
from utils import concat
from runtime import new_context, Undefined
class Environment:
def __init__(self, autoescape=False):
self.autoescape = autoescape
self.lexer = Lexer(self)
self.undefined = Undefined
def handle_exception(self, exc_info, source_hint=None):
pass
def getitem(self, obj, argument):
try:
return obj[argument]
except (AttributeError, TypeError, LookupError):
if isinstance(argument, str):
try:
return getattr(obj, argument)
except AttributeError:
pass
return self.undefined(obj=obj, name=argument)
def getattr(self, obj, attribute):
try:
return getattr(obj, attribute)
except AttributeError:
pass
try:
return obj[attribute]
except(TypeError, LookupError, AttributeError):
return self.undefined(obj=obj, name=attribute)
def from_string(self, source):
return Template.from_code(self, self.compile(source))
def tokenize(self, source):
return self.lexer.tokenize(source)
def _parse(self, source, name, filename):
return Parser(self, source).parse()
def parse(self, source, name=None, filename=None):
try:
return self._parse(source, name, filename)
except TemplateSyntaxError:
exc_info = sys.exc_info()
self.handle_exception(exc_info, source_hint=source)
def _generate(self, node, name, filename):
return generate(node, self, name)
def compile(self, source, name=None, filename=None):
source_hint = None
try:
if isinstance(source, str):
source_hint = source
source = self._parse(source, name, filename)
source = self._generate(source, name, filename)
with open('output.py', 'w') as f:
f.write(source)
if filename is None:
filename = '<template>'
rv = self._compile(source, filename)
return rv
except TemplateSyntaxError:
exc_info = sys.exc_info()
self.handle_exception(exc_info, source_hint=source_hint)
def _compile(self, source, filename):
return compile(source, filename, 'exec')
class Template:
def __new__(cls, source, autoescape=False):
env = Environment(autoescape)
return env.from_string(source)
@classmethod
def from_code(cls, environment, code):
namespace = {
'environment': environment,
'__file__': code.co_filename
}
exec(code, namespace)
rv = cls._from_namespace(environment, namespace)
return rv
@classmethod
def _from_namespace(cls, environment, namespace):
t = object.__new__(cls)
t.environment = environment
t.name = namespace['name']
t.filename = namespace['__file__']
t.blocks = namespace['blocks']
t.root_render_func = namespace['root']
namespace['environment'] = environment
namespace['__minja_template__'] = t
return t
def render(self, *args, **kwargs):
vars = dict(*args, **kwargs)
try:
ctx = self.new_context(vars)
return concat(self.root_render_func(ctx))
except Exception:
raise
def new_context(self, vars=None):
return new_context(self.environment, self.name, self.blocks, vars)