3 ms·
Yes! `textwrap.dedent` is great. On further reflection `wrap` is actually more useful for this kludge (see below). But my point is that that's a whole import fo
by stefco_ 7y ago
Yes! `textwrap.dedent` is great. On further reflection `wrap` is actually more useful for this kludge (see below). But my point is that that's a whole import for a kludge. Compare the f-string ideal (by my standards):
raise ValueError("File exists, not uploading: "
f"{filename} -> {bucket}, {key}")
...which is short enough that it's readable, and it's clear where exactly each variable is going. It's the single obvious solution, so much so that I don't spend a second thinking about it (very Pythonic!). Compare it to using `str.format` with the same continued indentation:
raise ValueError(("File exists, not uploading: {filename} -> "
"{bucket}, {key}").format(filename=filename,
bucket=bucket,
key=key))
Even this minimal example looks terrible! Remember that a lot of exceptions are raised within multiply-nested blocks, and then format pushes things farther to the right (while also ruining your automated string-literal concatenation, hence the extra parentheses), leaving very little room for the format arguments. You can use a more self-consistent and readable indentation strategy:
raise ValueError(
(
"File exists, not uploading: {filename} -> "
"{bucket}, {key}"
).format(filename, bucket, key)
)
This is unquestionably more pleasant to read than the former, but it's 3 times longer than the simple f-string solution, and I would argue it is not any more readable than the f-string for this simple example. My point with having a `str.wrap` builtin is that at least you could use the docstring convention of terminating multi-line strings on a newline, which would get rid of the string concatenation issues while leaving you a consistent (albeit diminished by the "wrap" call) amount of rightward room for the `format` args:
raise ValueError("""File exists, not uploading: {filename} ->
{bucket}, {key}
""".dedent().format(filename=filename,
bucket=bucket, key=key))
Maybe a little bit better than the first one, especially if you're writing a longer docstring and don't want to think about string concatenation. But still a kludge. You can use positional formatting to shorten things up, but the fundamental weakness of `str.format` remains.
- bpchaps 7y agoHere's a clean way to do that: str_fmt = "File exists, not uploading: {filename} -> {bucket}, {key}" fmt_vals = dict(filename=filename, bucket=bucket, key=key) raise ValueError(str_fmt.dedent().format(**fmt_vals))
- stefco_ 7y agoThis is somewhat cleaner, and I also use this idiom when things get ugly with the inline formatting shown above. But my point is that none of these are very elegant for an extremely common use case. Throw this block in the middle of some complex code with a few try/except and raise statements and it still looks confusing. Having two extra temp variables and statements per error in a function that's just doing control flow and wrapping unsafe code can double your local variable count and number of statements across the whole function. AFAIK, there has been no elegant solution to this common problem until f-strings came around; the only decently clean one is using printf-style format strings with the old-style operator, but outside of terseness I find it less readable.
- deleted 7y ago[deleted]
- JoBrad 7y agoAlternate “clean” way, but sort of hacky. raise ValueError(“File exists, not uploading: {filename} -> {bucket}, {key}”.format(**locals()))
- Dylan16807 7y agof-strings are nice, but when the problem is indenting too far, what if you just... didn't do that? raise ValueError(("File exists, not uploading: {filename} -> " "{bucket}, {key}").format(filename=filename, bucket=bucket, key=key))