Issue39842
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.
Created on 2020-03-03 23:57 by Marco Sulla, last changed 2022-04-11 14:59 by admin.
Messages (8) | |||
---|---|---|---|
msg363317 - (view) | Author: Marco Sulla (Marco Sulla) * | Date: 2020-03-03 23:57 | |
In `string` module, there's a very little known class `Template`. It implements a very simple template, but it has an interesting method: `safe_substitute()`. `safe_substitute()` permits you to not fill the entire Template at one time. On the contrary, it substitute the placeholders that are passed, and leave the others untouched. I think it could be useful to have a similar method for the format minilanguage. I propose a partial_format() method. === WHY I think this is useful? === This way, you can create subtemplates from a main template. You could want to use the template for creating a bunch of strings, all of them with the same value for some placeholders, but different values for other ones. This way you have *not* to reuse the same main template and substitute every time the placeholders that does not change. `partial_format()` should act as `safe_substitute()`: if some placeholder misses a value, no error will be raised. On the contrary, the placeholder is leaved untouched. Some example: >>> "{} {}".partial_format(1) '1 {}' >>> "{x} {a}".partial_format(a="elephants") '{x} elephants' >>> "{:-f} and {:-f} nights".partial_format(1000) '1000 and {:-f} nights' |
|||
msg363376 - (view) | Author: Eric V. Smith (eric.smith) * | Date: 2020-03-04 18:53 | |
Do you have some concrete use case for this, or is this a speculative feature request? This will do what you want, although it uses some undocumented internals of the _string module. I've only tested it a little, and I've done especially little testing for the error conditions: from _string import formatter_parser, formatter_field_name_split def partial_format(spec, *args, **kwargs): idx = 0 auto_number = False manual_number = False result = [] for literal_text, field_name, format_spec, conversion in formatter_parser(spec): result.append(literal_text) found = False if field_name is None: # We're at the end of the input. break if not field_name: # Auto-numbering fields. if manual_number: raise ValueError( "cannot switch from manual field specification to automatic field numbering" ) auto_number = True try: value = args[idx] idx += 1 found = True except IndexError: pass elif isinstance(field_name, int): # Numbered fields. if auto_number: raise ValueError( "cannot switch from automatic field number to manual field specification" ) manual_number = True try: value = args[field_name] found = True except IndexError: pass else: # Named fields. try: value = kwargs[field_name] found = True except KeyError: pass spec = f":{format_spec}" if format_spec else "" conv = f"!{conversion}" if conversion else "" if found: s = f"{{{conv}{spec}}}" result.append(s.format(value)) else: result.append(f"{{{field_name}{conv}{spec}}}") return "".join(result) |
|||
msg363377 - (view) | Author: Marco Sulla (Marco Sulla) * | Date: 2020-03-04 19:00 | |
> Do you have some concrete use case for this? Yes, for EWA: https://marco-sulla.github.io/ewa/ Since it's a code generator, it uses templates a lot, and much times I feel the need for a partial substitution. In the end I solved with some ugly tricks. Furthermore, if the method exists in the stdlib for `string.Template`, I suppose it was created because it was of some utility. |
|||
msg363378 - (view) | Author: Eric V. Smith (eric.smith) * | Date: 2020-03-04 19:02 | |
I suggest using the version I posted here and see if it meets your needs. It uses the str.format parser to break the string into pieces, so it should be accurate as far as that goes. |
|||
msg363379 - (view) | Author: Serhiy Storchaka (serhiy.storchaka) * | Date: 2020-03-04 19:10 | |
What would "{} {}".partial_format({}) return? It is not possible to implement a "safe" variant of str.format(), because in difference to Template it can call arbitrary code and allows easily to produce arbitrary large strings. Template is more appropriate if the template came from untrusted source or if it is composed by inexperienced user. |
|||
msg363388 - (view) | Author: Marco Sulla (Marco Sulla) * | Date: 2020-03-04 20:43 | |
> What would "{} {}".partial_format({}) return? `str.partial_format()` was proposed exactly to avoid such tricks. > It is not possible to implement a "safe" variant of str.format(), > because in difference to Template it can call arbitrary code If you read the documentation of `Template.safe_substitute()`, you can read also this function is not safe at all. But Python, for example, does not implement private class attributes. Because Python is for adult and consciousness people, no? |
|||
msg363389 - (view) | Author: Marco Sulla (Marco Sulla) * | Date: 2020-03-04 20:44 | |
@Eric V. Smith: that you for your effort, but I'll never use an API marked as private, that is furthermore undocumented. |
|||
msg363391 - (view) | Author: Eric V. Smith (eric.smith) * | Date: 2020-03-04 21:25 | |
Well, I'm the one who made them private, I can make them public if they're useful. But I won't do that if you're not willing to look at it. |
History | |||
---|---|---|---|
Date | User | Action | Args |
2022-04-11 14:59:27 | admin | set | github: 84023 |
2020-03-04 21:25:26 | eric.smith | set | messages: + msg363391 |
2020-03-04 20:44:56 | Marco Sulla | set | messages: + msg363389 |
2020-03-04 20:43:08 | Marco Sulla | set | messages: + msg363388 |
2020-03-04 19:10:16 | serhiy.storchaka | set | nosy:
+ serhiy.storchaka messages: + msg363379 |
2020-03-04 19:02:47 | eric.smith | set | messages: + msg363378 |
2020-03-04 19:00:03 | Marco Sulla | set | messages: + msg363377 |
2020-03-04 18:53:38 | eric.smith | set | nosy:
+ eric.smith messages: + msg363376 |
2020-03-03 23:57:59 | Marco Sulla | create |