Path: blob/develop/src/sage_setup/autogen/interpreters/internal/utils.py
4086 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."""1213from __future__ import print_function, absolute_import1415import os16import textwrap1718from jinja2 import Environment19from jinja2.runtime import StrictUndefined202122# We share a single jinja2 environment among all templating in this23# file. We use trim_blocks=True (which means that we ignore white24# space after "%}" jinja2 command endings), and set undefined to25# complain if we use an undefined variable.26JINJA_ENV = Environment(trim_blocks=True, undefined=StrictUndefined)2728# Allow 'i' as a shorter alias for the built-in 'indent' filter.29JINJA_ENV.filters['i'] = JINJA_ENV.filters['indent']303132def je(template, **kwargs):33r"""34A convenience method for creating strings with Jinja templates.3536The name je stands for "Jinja evaluate".3738The first argument is the template string; remaining keyword39arguments define Jinja variables.4041If the first character in the template string is a newline, it is42removed (this feature is useful when using multi-line templates defined43with triple-quoted strings -- the first line doesn't have to be on44the same line as the quotes, which would screw up the indentation).4546(This is very inefficient, because it recompiles the Jinja47template on each call; don't use it in situations where48performance is important.)4950EXAMPLES::5152sage: from sage_setup.autogen.interpreters.internal import je53sage: je("{{ a }} > {{ b }} * {{ c }}", a='"a suffusion of yellow"', b=3, c=7)54'"a suffusion of yellow" > 3 * 7'55"""56if template and template[0] == '\n':57template = template[1:]5859# It looks like Jinja2 automatically removes one trailing newline?60if template and template[-1] == '\n':61template = template + '\n'6263tmpl = JINJA_ENV.from_string(template)64return tmpl.render(kwargs)656667def indent_lines(n, text):68r"""69Indent each line in text by ``n`` spaces.7071INPUT:7273- ``n`` -- indentation amount74- ``text`` -- text to indent7576EXAMPLES::7778sage: from sage_setup.autogen.interpreters.internal import indent_lines79sage: indent_lines(3, "foo")80' foo'81sage: indent_lines(3, "foo\nbar")82' foo\n bar'83sage: indent_lines(3, "foo\nbar\n")84' foo\n bar\n'85"""86lines = text.splitlines(True)87spaces = ' ' * n88return ''.join((spaces if line.strip() else '') + line89for line in lines)909192def reindent_lines(n, text):93r"""94Strip any existing indentation on the given text (while keeping95relative indentation) then re-indents the text by ``n`` spaces.9697INPUT:9899- ``n`` -- indentation amount100- ``text`` -- text to indent101102EXAMPLES::103104sage: from sage_setup.autogen.interpreters.internal import reindent_lines105sage: print(reindent_lines(3, " foo\n bar"))106foo107bar108"""109110return indent_lines(n, textwrap.dedent(text))111112113def write_if_changed(fn, value):114r"""115Write value to the file named fn, if value is different than116the current contents.117118EXAMPLES::119120sage: from sage_setup.autogen.interpreters.internal import *121sage: def last_modification(fn): return os.stat(fn).st_mtime122sage: fn = tmp_filename('gen_interp')123sage: write_if_changed(fn, 'Hello, world')124sage: t1 = last_modification(fn)125sage: open(fn).read()126'Hello, world'127sage: sleep(2) # long time128sage: write_if_changed(fn, 'Goodbye, world')129sage: t2 = last_modification(fn)130sage: open(fn).read()131'Goodbye, world'132sage: sleep(2) # long time133sage: write_if_changed(fn, 'Goodbye, world')134sage: t3 = last_modification(fn)135sage: open(fn).read()136'Goodbye, world'137sage: t1 == t2 # long time138False139sage: t2 == t3140True141"""142143old_value = None144try:145with open(fn) as file:146old_value = file.read()147except OSError:148pass149150if value != old_value:151# We try to remove the file, in case it exists. This is to152# automatically break hardlinks... see #5350 for motivation.153try:154os.remove(fn)155except OSError:156pass157158with open(fn, 'w') as file:159file.write(value)160161162