This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Default values for string.Template
Type: enhancement Stage: resolved
Components: Library (Lib) Versions: Python 3.4
process
Status: closed Resolution: wont fix
Dependencies: Superseder:
Assigned To: barry Nosy List: barry, eric.araujo, georg.brandl, nitupho, rhettinger, serhiy.storchaka
Priority: normal Keywords: patch

Created on 2011-10-13 21:16 by nitupho, last changed 2022-04-11 14:57 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
string_template_default_values.tar nitupho, 2011-10-13 21:16 string module, string documentation, and diff file
string_template_default_values.patch nitupho, 2011-10-14 17:21 diff
string_template_default_values2.patch nitupho, 2011-10-18 22:51 diff (v.2)
string_template_default_values2-light.patch nitupho, 2011-10-18 22:53 diff (v.2 "light")
Messages (14)
msg145485 - (view) Author: Bfontaine (nitupho) Date: 2011-10-13 21:16
This patch allows you to define default values for a string.Template, which is useful when you need to use a lot some values, but sometimes other values.

for example:

>>> from string import Template
>>> s = Template("${user} made me a ${flavor} cake.", default={"user":"Dennis"})
>>> s.substitute(flavor="vanilla")
'Dennis made me a vanilla cake.'
>>> s.substitute(user="Ken", flavor="chocolate")
'Ken made me chocolate cake.'
msg145531 - (view) Author: Éric Araujo (eric.araujo) * (Python committer) Date: 2011-10-14 15:21
Thanks for your interest in Python.  Can you repost your code as a diff?  (See the devguide for more info)
msg145550 - (view) Author: Bfontaine (nitupho) Date: 2011-10-14 17:21
Here is my code as a diff
msg145698 - (view) Author: Éric Araujo (eric.araujo) * (Python committer) Date: 2011-10-17 14:11
Thanks for the patch.  It mostly looks good; a detailed review follow.
 
+   The constructor takes two arguments, the first is the template string and
+   the second (optional) is a dictionary object which specify which values
+   should be used as default values, if no provided.
Just say “a dictionary”, or even “a mapping”.  There’s also a grammar typo and a bit of redundancy, so I propose: “is a mapping which gives default values”.  What do you think about adding a small example in the examples section later in the file?  It would probably be clearer than English.

       Like :meth:`substitute`, except that if placeholders are missing from
-      *mapping* and *kwds*, instead of raising a :exc:`KeyError` exception, the
-      original placeholder will appear in the resulting string intact.  Also,
-      unlike with :meth:`substitute`, any other appearances of the ``$`` will
-      simply return ``$`` instead of raising :exc:`ValueError`.
+      *mapping*, *kwds* and *default*, instead of raising a :exc:`KeyError`
default is not an argument, so *foo* markup is misleading.  Either use “the default values given to the constructor” or just “self.default”.

+      exception, the original placeholder will appear in the resulting string
+      intact.  Also, unlike with :meth:`substitute`, any other appearances of
+      the ``$`` will simply return ``$`` instead of raising :exc:`ValueError`.
If you don’t mind, I prefer if you have a few very short or too long lines if that helps reducing diff noise (i.e. lines that appear in the diff but are not changed, only rewrapped).

+   .. attribute:: default
+
+      This is the optional dictionary object passed to the constructor's
+      *template* argument.
I’m not a native English speaker, but “passed to” seems wrong here (and in the other attribute’s doc).  I’d say “passed as the *default* argument”.
 
-    def __init__(self, template):
+    def __init__(self, template, default={}):
Binding a mutable object to a local name at compile time is not good: All instances created without *default* argument will share the same dict, so editions to onetemplate.default will change anothertemplate.default too.  The common idiom is to use None as default value and add this:

        self.default = default if default is not None else {}
msg145699 - (view) Author: Éric Araujo (eric.araujo) * (Python committer) Date: 2011-10-17 14:12
Adding Georg, maintainer of the string module, to the nosy list.  If he approves the idea, you can go ahead and complete your patch.
msg145873 - (view) Author: Bfontaine (nitupho) Date: 2011-10-18 22:51
diff updated
msg145874 - (view) Author: Bfontaine (nitupho) Date: 2011-10-18 22:53
A "light" version of diff added (rewrapped content is replaced by "[rewrapping]")
msg145876 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2011-10-18 23:41
This looks like a reasonable use case.  That being said, I question whether the defaults should be attached directly to the template instance or whether they should be part of the substitution method.

FWIW, there already have a couple of other ways to do it:

>>> from string import Template
>>> s = Template("${user} made me a ${flavor} cake.")
>>> default = {"user":"Dennis"}
>>> s.substitute(default, flavor='vanilla')
'Dennis made me a vanilla cake.'
>>> s.substitute(default, user='Ken', flavor='vanilla')
'Ken made me a vanilla cake.'
 

>>> from collections import ChainMap
>>> s.substitute(ChainMap(dict(flavor='vanilla'), default))
'Dennis made me a vanilla cake.'
msg145915 - (view) Author: Bfontaine (nitupho) Date: 2011-10-19 15:44
When you are using a lot of string templates like I am doing, I think it's better if the defaults is attached directly to the template instance.

This:
[<Template0>, <Template1>, <Template2>, <Template3>, ...]
is easier to use than:
[(<Template0>, <defaults0>), (<Template1>, <defaults>1), ...]
msg146620 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2011-10-29 20:13
Barry, any thoughts?
msg147252 - (view) Author: Barry A. Warsaw (barry) * (Python committer) Date: 2011-11-07 19:21
When I need defaults, I make them part of the mapping that gets passed into .substitute() and .safe_substitute().  It doesn't feel to me like it's necessary to attach them to the Template instance.  Also, couldn't you just subclass string.Template if you wanted defaults?  OTOH, since it can be done in a backward compatible way, I guess I'm -0 on the change.
msg163853 - (view) Author: Éric Araujo (eric.araujo) * (Python committer) Date: 2012-06-24 20:38
It looks like a doc update to mention the excellent ChainMap class would be a good thing.

Bfontaine, are you happy with using a Template subclass or changing your code slightly to have defaults outside of the Template objects, or do you still think the class should be changed?  We could run this by the python-ideas mailing list.
msg163919 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2012-06-25 09:45
If the Template defaults should be attached directly to the template instance, then they should be attached directly to the string instance, for classic and modern string formatting. And this obviously is absurd.

I agree with Éric, to mention ChainMap class in the string formatting documentation will be enough.
msg202568 - (view) Author: Barry A. Warsaw (barry) * (Python committer) Date: 2013-11-10 20:22
I'm looking at this issue again with an eye toward Python 3.4.

Raymond describes what I think is a reasonable way to use defaults:

>>> x = Template('$foo $bar')
>>> defaults = dict(foo='one', bar='two')
>>> x.substitute(defaults)
'one two'
>>> x.substitute(defaults, bar='three')
'one three'
>>> x.substitute(defaults, foo='nine', bar='three')
'nine three'

(The implementation actually uses ChainMap.)

Now, to address Bfontaine's complaint about passing around tuples, observe that Template instances are Just Instances, so you can always do this:

>>> x = Template('$foo $bar')
>>> x.defaults = defaults
>>> x.substitute(x.defaults, foo='nine', bar='three')
'nine three'

IOW, just stash your defaults on the instance and pass the instance around.  Does the Template class actually need more built-in support for defaults?  I'm inclined to close this as Won't Fix.
History
Date User Action Args
2022-04-11 14:57:22adminsetgithub: 57382
2016-05-06 10:02:23serhiy.storchakasetstatus: open -> closed
resolution: wont fix
stage: resolved
2013-11-10 20:22:21barrysetmessages: + msg202568
2013-01-07 16:02:14serhiy.storchakasetversions: + Python 3.4, - Python 3.3
2012-06-25 09:45:29serhiy.storchakasetnosy: + serhiy.storchaka
messages: + msg163919
2012-06-24 20:38:31eric.araujosetmessages: + msg163853
2011-11-07 19:21:06barrysetmessages: + msg147252
2011-10-29 20:13:44rhettingersetassignee: rhettinger -> barry

messages: + msg146620
nosy: + barry
2011-10-19 15:44:50nituphosetmessages: + msg145915
2011-10-18 23:41:56rhettingersetassignee: rhettinger

messages: + msg145876
nosy: + rhettinger
2011-10-18 22:53:21nituphosetfiles: + string_template_default_values2-light.patch

messages: + msg145874
2011-10-18 22:51:35nituphosetfiles: + string_template_default_values2.patch

messages: + msg145873
2011-10-17 14:12:20eric.araujosetnosy: + georg.brandl
messages: + msg145699
2011-10-17 14:11:32eric.araujosetmessages: + msg145698
2011-10-14 17:21:30nituphosetfiles: + string_template_default_values.patch
keywords: + patch
messages: + msg145550
2011-10-14 15:21:42eric.araujosetnosy: + eric.araujo

messages: + msg145531
versions: + Python 3.3, - Python 3.2
2011-10-13 21:16:04nituphocreate