Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sage
Path: blob/develop/src/sage_setup/autogen/interpreters/internal/utils.py
7380 views
1
#*****************************************************************************
2
# Copyright (C) 2009 Carl Witty <[email protected]>
3
# Copyright (C) 2015 Jeroen Demeyer <[email protected]>
4
#
5
# This program is free software: you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation, either version 2 of the License, or
8
# (at your option) any later version.
9
# http://www.gnu.org/licenses/
10
#*****************************************************************************
11
12
"""Miscellaneous utility routines used for the interpreter generator."""
13
14
15
import os
16
import textwrap
17
18
from jinja2 import Environment
19
from jinja2.runtime import StrictUndefined
20
21
# We share a single jinja2 environment among all templating in this
22
# file. We use trim_blocks=True (which means that we ignore white
23
# space after "%}" jinja2 command endings), and set undefined to
24
# complain if we use an undefined variable.
25
JINJA_ENV = Environment(trim_blocks=True, undefined=StrictUndefined)
26
27
# Allow 'i' as a shorter alias for the built-in 'indent' filter.
28
JINJA_ENV.filters['i'] = JINJA_ENV.filters['indent']
29
30
31
def je(template, **kwargs):
32
r"""
33
A convenience method for creating strings with Jinja templates.
34
35
The name je stands for "Jinja evaluate".
36
37
The first argument is the template string; remaining keyword
38
arguments define Jinja variables.
39
40
If the first character in the template string is a newline, it is
41
removed (this feature is useful when using multi-line templates defined
42
with triple-quoted strings -- the first line doesn't have to be on
43
the same line as the quotes, which would screw up the indentation).
44
45
(This is very inefficient, because it recompiles the Jinja
46
template on each call; don't use it in situations where
47
performance is important.)
48
49
EXAMPLES::
50
51
sage: from sage_setup.autogen.interpreters.internal import je
52
sage: je("{{ a }} > {{ b }} * {{ c }}", a='"a suffusion of yellow"', b=3, c=7)
53
'"a suffusion of yellow" > 3 * 7'
54
"""
55
if template and template[0] == '\n':
56
template = template[1:]
57
58
# It looks like Jinja2 automatically removes one trailing newline?
59
if template and template[-1] == '\n':
60
template = template + '\n'
61
62
tmpl = JINJA_ENV.from_string(template)
63
return tmpl.render(kwargs)
64
65
66
def indent_lines(n, text):
67
r"""
68
Indent each line in text by ``n`` spaces.
69
70
INPUT:
71
72
- ``n`` -- indentation amount
73
- ``text`` -- text to indent
74
75
EXAMPLES::
76
77
sage: from sage_setup.autogen.interpreters.internal import indent_lines
78
sage: indent_lines(3, "foo")
79
' foo'
80
sage: indent_lines(3, "foo\nbar")
81
' foo\n bar'
82
sage: indent_lines(3, "foo\nbar\n")
83
' foo\n bar\n'
84
"""
85
lines = text.splitlines(True)
86
spaces = ' ' * n
87
return ''.join((spaces if line.strip() else '') + line
88
for line in lines)
89
90
91
def reindent_lines(n, text):
92
r"""
93
Strip any existing indentation on the given text (while keeping
94
relative indentation) then re-indents the text by ``n`` spaces.
95
96
INPUT:
97
98
- ``n`` -- indentation amount
99
- ``text`` -- text to indent
100
101
EXAMPLES::
102
103
sage: from sage_setup.autogen.interpreters.internal import reindent_lines
104
sage: print(reindent_lines(3, " foo\n bar"))
105
foo
106
bar
107
"""
108
109
return indent_lines(n, textwrap.dedent(text))
110
111
112
def write_if_changed(fn, value):
113
r"""
114
Write value to the file named fn, if value is different than
115
the current contents.
116
117
EXAMPLES::
118
119
sage: from sage_setup.autogen.interpreters.internal import *
120
sage: def last_modification(fn): return os.stat(fn).st_mtime
121
sage: fn = tmp_filename('gen_interp')
122
sage: write_if_changed(fn, 'Hello, world')
123
sage: t1 = last_modification(fn)
124
sage: open(fn).read()
125
'Hello, world'
126
sage: sleep(2) # long time
127
sage: write_if_changed(fn, 'Goodbye, world')
128
sage: t2 = last_modification(fn)
129
sage: open(fn).read()
130
'Goodbye, world'
131
sage: sleep(2) # long time
132
sage: write_if_changed(fn, 'Goodbye, world')
133
sage: t3 = last_modification(fn)
134
sage: open(fn).read()
135
'Goodbye, world'
136
sage: t1 == t2 # long time
137
False
138
sage: t2 == t3
139
True
140
"""
141
142
old_value = None
143
try:
144
with open(fn) as file:
145
old_value = file.read()
146
except OSError:
147
pass
148
149
if value != old_value:
150
# We try to remove the file, in case it exists. This is to
151
# automatically break hardlinks... see #5350 for motivation.
152
try:
153
os.remove(fn)
154
except OSError:
155
pass
156
157
with open(fn, 'w') as file:
158
file.write(value)
159
160