Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

string.format() should have a safe_substitute equivalent, to be run consecutively #68737

Closed
azrdev mannequin opened this issue Jul 1, 2015 · 6 comments
Closed

string.format() should have a safe_substitute equivalent, to be run consecutively #68737

azrdev mannequin opened this issue Jul 1, 2015 · 6 comments
Labels
3.7 interpreter-core (Objects, Python, Grammar, and Parser dirs) type-feature A feature request or enhancement

Comments

@azrdev
Copy link
Mannequin

azrdev mannequin commented Jul 1, 2015

BPO 24549
Nosy @ericvsmith, @stevendaprano, @bitdancer

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields:

assignee = None
closed_at = <Date 2017-03-07.18:45:38.575>
created_at = <Date 2015-07-01.20:42:29.626>
labels = ['interpreter-core', 'type-feature', '3.7']
title = 'string.format() should have a safe_substitute equivalent, to be run consecutively'
updated_at = <Date 2017-03-07.18:45:38.574>
user = 'https://bugs.python.org/azrdev'

bugs.python.org fields:

activity = <Date 2017-03-07.18:45:38.574>
actor = 'serhiy.storchaka'
assignee = 'none'
closed = True
closed_date = <Date 2017-03-07.18:45:38.575>
closer = 'serhiy.storchaka'
components = ['Interpreter Core']
creation = <Date 2015-07-01.20:42:29.626>
creator = 'azrdev'
dependencies = []
files = []
hgrepos = []
issue_num = 24549
keywords = []
message_count = 6.0
messages = ['246047', '246048', '246054', '246055', '277607', '277608']
nosy_count = 4.0
nosy_names = ['eric.smith', 'steven.daprano', 'r.david.murray', 'azrdev']
pr_nums = []
priority = 'normal'
resolution = 'rejected'
stage = 'resolved'
status = 'closed'
superseder = None
type = 'enhancement'
url = 'https://bugs.python.org/issue24549'
versions = ['Python 3.7']

@azrdev
Copy link
Mannequin Author

azrdev mannequin commented Jul 1, 2015

"{1} {0}".format('one').format('two')

should return "two one", but throws

IndexError: tuple index out of range

This would allow partial replacements, similar to string.Template.safe_substitute()
I suggest an analog construction (e.g. a method string.safe_format() )

@azrdev azrdev mannequin added stdlib Python modules in the Lib dir type-feature A feature request or enhancement labels Jul 1, 2015
@bitdancer
Copy link
Member

bitdancer commented Jul 1, 2015

Why not use string.Template?

@ericvsmith
Copy link
Member

ericvsmith commented Jul 2, 2015

So let's say your function would be named "safe_format". Then:

"{1} {0}".safe_format('one')

would give: "{1} one".

Then:

"{1} one".safe_format('two')

would be an error, because there's no index "1" in the args tuple.

I can't imagine how you'd implement this function so it would know to start with index 1 on the second call, instead of 0 as usual.

Or maybe it would decrement the index values it doesn't use, so that the result of the first call would be: "{0} one". That seems very complex, especially when you throw in implicit argument numbering, named arguments, format specifiers, etc.

I agree with David that string.Template might be better for you.

On an unrelated note, I think that "IndexError: tuple index out of range" is a horrible error message for this case. I wouldn't mind an enhancement request to make that something like "argument index '1' is out of range, only 1 argument supplied". Or something with better wordsmithing.

@ericvsmith ericvsmith added interpreter-core (Objects, Python, Grammar, and Parser dirs) and removed stdlib Python modules in the Lib dir labels Jul 2, 2015
@stevendaprano
Copy link
Member

stevendaprano commented Jul 2, 2015

I don't think that this behaviour is desirable, certainly not by default. If I write "{1} {0}".format('one') that's clearly a programming error and I should get an exception. Chaining a second .format method call afterwards does not make the first one any less of a mistake.

I think that there may be a good argument to be made for a safe_format with *named* arguments, like string.Template, but not positional arguments. But that would require some discussion, to decide on the correct behaviour. And why not use string.Template in the first place, or make the chained calls a single call?

"{1} {0}".format('one', 'two')

is clearly the most obvious way to do it. A slightly less obvious way:

"{{}} {}".format('one').format('two')

@azrdev
Copy link
Mannequin Author

azrdev mannequin commented Sep 28, 2016

(Sorry for not replying so long, I expected an email notification)

Why not use string.Template?

because it's so slow

I think that there may be a good argument to be made for a safe_format with *named* arguments, like string.Template, but not positional arguments.

Yes!

But that would require some discussion, to decide on the correct behaviour.

What open question(s) do you think of?

For context, I got the idea for this from Qts .arg() which can be chained, see <https://doc.qt.io/qt-5/qstring.html#arg\>.

@ericvsmith
Copy link
Member

ericvsmith commented Sep 28, 2016

> But that would require some discussion, to decide on the correct behaviour.

What open question(s) do you think of?

You need to provide an exact specification of what you want. It's not clear, for example, if you want to support required positional placeholders ({0}, etc.) but optionally missing named placeholders ({foo}).

I'd also suggest implementing this as a function, so you can get some real-world usage out of it. Maybe:

def format_safe(s, *args, **kwargs):

format_safe("{0} {1} {foo} {bar}", 'one', 'two', bar='bar')
->
'one two {foo} bar}'

or whatever you decide the signature and functionality should be.

You can probably subclass from string.Formatter to do some of the heavy lifting for you, so I expect this wouldn't be very hard, depending on what you're really after.

While you might find this function useful, I don't want to get your hopes up that this would be added to core Python as a member of str. I still don't see why this would be better than a single call to .format() that specifies all of the parameters. You'd have to have some use case that doesn't involve chaining for that to make sense (to me). And maybe functools.partial() or some derivative would make sense in that case.

@ericvsmith ericvsmith added the 3.7 label Sep 28, 2016
@ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.7 interpreter-core (Objects, Python, Grammar, and Parser dirs) type-feature A feature request or enhancement
Projects
None yet
Development

No branches or pull requests

4 participants