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