Path: blob/develop/src/sage_setup/autogen/interpreters/internal/utils.py
7380 views
#*****************************************************************************1# Copyright (C) 2009 Carl Witty <[email protected]>2# Copyright (C) 2015 Jeroen Demeyer <[email protected]>3#4# This program is free software: you can redistribute it and/or modify5# it under the terms of the GNU General Public License as published by6# the Free Software Foundation, either version 2 of the License, or7# (at your option) any later version.8# http://www.gnu.org/licenses/9#*****************************************************************************1011"""Miscellaneous utility routines used for the interpreter generator."""121314import os15import textwrap1617from jinja2 import Environment18from jinja2.runtime import StrictUndefined1920# We share a single jinja2 environment among all templating in this21# file. We use trim_blocks=True (which means that we ignore white22# space after "%}" jinja2 command endings), and set undefined to23# complain if we use an undefined variable.24JINJA_ENV = Environment(trim_blocks=True, undefined=StrictUndefined)2526# Allow 'i' as a shorter alias for the built-in 'indent' filter.27JINJA_ENV.filters['i'] = JINJA_ENV.filters['indent']282930def je(template, **kwargs):31r"""32A convenience method for creating strings with Jinja templates.3334The name je stands for "Jinja evaluate".3536The first argument is the template string; remaining keyword37arguments define Jinja variables.3839If the first character in the template string is a newline, it is40removed (this feature is useful when using multi-line templates defined41with triple-quoted strings -- the first line doesn't have to be on42the same line as the quotes, which would screw up the indentation).4344(This is very inefficient, because it recompiles the Jinja45template on each call; don't use it in situations where46performance is important.)4748EXAMPLES::4950sage: from sage_setup.autogen.interpreters.internal import je51sage: je("{{ a }} > {{ b }} * {{ c }}", a='"a suffusion of yellow"', b=3, c=7)52'"a suffusion of yellow" > 3 * 7'53"""54if template and template[0] == '\n':55template = template[1:]5657# It looks like Jinja2 automatically removes one trailing newline?58if template and template[-1] == '\n':59template = template + '\n'6061tmpl = JINJA_ENV.from_string(template)62return tmpl.render(kwargs)636465def indent_lines(n, text):66r"""67Indent each line in text by ``n`` spaces.6869INPUT:7071- ``n`` -- indentation amount72- ``text`` -- text to indent7374EXAMPLES::7576sage: from sage_setup.autogen.interpreters.internal import indent_lines77sage: indent_lines(3, "foo")78' foo'79sage: indent_lines(3, "foo\nbar")80' foo\n bar'81sage: indent_lines(3, "foo\nbar\n")82' foo\n bar\n'83"""84lines = text.splitlines(True)85spaces = ' ' * n86return ''.join((spaces if line.strip() else '') + line87for line in lines)888990def reindent_lines(n, text):91r"""92Strip any existing indentation on the given text (while keeping93relative indentation) then re-indents the text by ``n`` spaces.9495INPUT:9697- ``n`` -- indentation amount98- ``text`` -- text to indent99100EXAMPLES::101102sage: from sage_setup.autogen.interpreters.internal import reindent_lines103sage: print(reindent_lines(3, " foo\n bar"))104foo105bar106"""107108return indent_lines(n, textwrap.dedent(text))109110111def write_if_changed(fn, value):112r"""113Write value to the file named fn, if value is different than114the current contents.115116EXAMPLES::117118sage: from sage_setup.autogen.interpreters.internal import *119sage: def last_modification(fn): return os.stat(fn).st_mtime120sage: fn = tmp_filename('gen_interp')121sage: write_if_changed(fn, 'Hello, world')122sage: t1 = last_modification(fn)123sage: open(fn).read()124'Hello, world'125sage: sleep(2) # long time126sage: write_if_changed(fn, 'Goodbye, world')127sage: t2 = last_modification(fn)128sage: open(fn).read()129'Goodbye, world'130sage: sleep(2) # long time131sage: write_if_changed(fn, 'Goodbye, world')132sage: t3 = last_modification(fn)133sage: open(fn).read()134'Goodbye, world'135sage: t1 == t2 # long time136False137sage: t2 == t3138True139"""140141old_value = None142try:143with open(fn) as file:144old_value = file.read()145except OSError:146pass147148if value != old_value:149# We try to remove the file, in case it exists. This is to150# automatically break hardlinks... see #5350 for motivation.151try:152os.remove(fn)153except OSError:154pass155156with open(fn, 'w') as file:157file.write(value)158159160