Python template safe substitution with the custom double-braces format

2024/10/9 16:24:23

I am trying to substitute variables in the format {{var}} with Python's Template.

from string import Templateclass CustomTemplate(Template):delimiter = '{{'pattern = r'''\{\{(?:(?P<escaped>\{\{)|(?P<named>[_a-z][_a-z0-9]*)\}\}|(?P<braced>[_a-z][_a-z0-9]*)\}\}|(?P<invalid>))'''replacement_dict = {"test": "hello"
}tpl = '''{"unaltered": "{{foo}}","replaced": "{{test}}"
}'''a = CustomTemplate(tpl)
b = a.safe_substitute(replacement_dict)print(b)

The output:

{"unaltered": "{{foo","replaced": "hello"
}

As you can see, the {{foo}} variable (which is not part of the replacement) has the closing brackets chopped off. I think it's the way the regex is written (the closing \}\})?

I want to solve this with Template, not with any other external libraries.

Answer

I'm not sure how you got this to work. On linux, in python 3.4.3 (I thought I got this working with some version of 2.7) I needed to make tpl a string

tpl = '''"unaltered": "{{foo}}","replaced": "{{test}}"
'''

to avoid getting a TypeError

>>> tpl = '''
...     "unaltered": "{{foo}}",
...     "replaced": "{{test}}"
... '''
>>> a = CustomTemplate(tpl)
>>> a.template
'\n    "unaltered": "{{foo}}",\n    "replaced": "{{test}}"\n'
>>> b = a.safe_substitute(replacement_dict)
>>> b
'\n    "unaltered": "{{foo}}",\n    "replaced": "hello"\n'

When I do this, {{foo}} is unaltered.

I tried the above code, and it looks like the code does not actually work with python 2.7.6. I'll see if I can find a way to make it work with 2.7.6, since that seems to be a common version with recent linux distros.

update:

Looks like this was a known bug as of 2007. http://bugs.python.org/issue1686 Far as I can tell, it was applied to python 3.2 in 2010, and python 2.7 in 2014. As far as getting this to work, you can either apply the patch for issue 1686, or you can override safe_substitute() in your class with the actual source code from this patch https://hg.python.org/cpython/file/8a98ee6baa1e/Lib/string.py.

This code works in 2.7.6, and 3.4.3

from string import Template
class CustomTemplate(Template):delimiter = '{{'pattern = r'''\{\{(?:(?P<escaped>\{\{)|(?P<named>[_a-z][_a-z0-9]*)\}\}|(?P<braced>[_a-z][_a-z0-9]*)\}\}|(?P<invalid>))'''def safe_substitute(self, *args, **kws):if len(args) > 1:raise TypeError('Too many positional arguments')if not args:mapping = kwselif kws:mapping = _multimap(kws, args[0])else:mapping = args[0]# Helper function for .sub()def convert(mo):named = mo.group('named') or mo.group('braced')if named is not None:try:# We use this idiom instead of str() because the latter# will fail if val is a Unicode containing non-ASCIIreturn '%s' % (mapping[named],)except KeyError:return mo.group()if mo.group('escaped') is not None:return self.delimiterif mo.group('invalid') is not None:return mo.group()raise ValueError('Unrecognized named group in pattern',self.pattern)return self.pattern.sub(convert, self.template)replacement_dict = {"test": "hello"
}tpl = '''{"escaped": "{{{{","unaltered": "{{foo}}","replaced": "{{test}}","invalid": "{{az"
}'''a = CustomTemplate(tpl)
b = a.safe_substitute(replacement_dict)print (b)

results:

Python 2.7.6 (default, Jun 22 2015, 17:58:13) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import template
{"escaped": "{{","unaltered": "{{foo}}","replaced": "hello","invalid": "{{az"
}
>>> 
https://en.xdnf.cn/q/69999.html

Related Q&A

Emit signal in standard python thread

I have a threaded application where I do have a network thread. The UI-part passes a callback to this thread. The thread is a normal python thread - its NO QThread.Is it possible to emit PyQT Slot with…

Sqlalchemy from_statement() cannot locate column

I am following the sqlalchemy tutorial in http://docs.sqlalchemy.org/en/rel_0_9/orm/tutorial.htmlNevertheless, instead of using a SQLite backend, I am using MySQL. The problem is that when I try to exe…

Python - how to check if weak reference is still available

I am passing some weakrefs from Python into C++ class, but C++ destructors are actively trying to access the ref when the real object is already dead, obviously it crashes...Is there any Python C/API a…

Django using locals() [duplicate]

This question already has answers here:Django template and the locals trick(8 answers)Closed 5 years ago.I am beginner in web development with Django. I have noticed that the locals() function is used …

python ghostscript: RuntimeError: Can not find Ghostscript library (libgs)

When trying to run hello-world exampleimport sys import ghostscriptargs = ["ps2pdf", # actual value doesnt matter"-dNOPAUSE", "-dBATCH", "-dSAFER","-sDEVICE…

what is the default encoding when python Requests post data is string type?

with fhe following codepayload = 工作报告 总体情况:良好 r = requests.post("http://httpbin.org/post", data=payload)what is the default encoding when Requests post data is string type? UTF8…

How to initialize a database connection only once and reuse it in run-time in python?

I am currently working on a huge project, which constantly executes queries. My problem is, that my old code always created a new database connection and cursor, which decreased the speed immensivly. S…

Django - ModelForm: Add a field not belonging to the model

Note: Using django-crispy-forms library for my form. If you have a solution to my problem that involves not using the cripsy_forms library, I accept it all the same. Not trying to be picky just need a …

Row by row processing of a Dask DataFrame

I need to process a large file and to change some values.I would like to do something like that:for index, row in dataFrame.iterrows():foo = doSomeStuffWith(row)lol = doOtherStuffWith(row)dataFrame[col…

Tweepy Why did I receive AttributeError for search [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.This question was caused by a typo or a problem that can no longer be reproduced. While similar q…