please icon indicating copy to clipboard operation
please copied to clipboard

Make `str.format` more Pythonic

Open chrisnovakovic opened this issue 3 months ago • 0 comments

str.format has a number of surprising behaviours that aren't Pythonic:

  • Numerical replacement fields use a different syntax to named replacement fields (i.e. ${0}).
  • Automatic and manual replacement field names can be used within the same format string.
  • Replacement field names are passed through if the corresponding keyword argument is undefined.
  • Format strings are allowed to contain unbalanced delimiters.

Some of these were introduced in #3146, along with some regressions that quietly broke substitution and delimiter escaping (see #3356).

Make Please's str.format implementation more Pythonic by aligning it with Bazel's:

  • Permit the use of by-order, by-position or by-name replacement field referencing, but do not allow them to be combined in the same format string.
  • Parse all occurrences of { and } in the format string as if they are delimiters, except for those escaped via {{ and }} - do not pass them through to the return value, and raise errors if they appear inappropriately.
  • Perform more robust checks on replacement field references.

The new error messages are intentionally reminiscent of those returned by Python's str.format, and are identical in some cases.

The correctness and robustness of the new implementation comes at the cost of performance - it is hard to compare them fairly against the degenerate case in src/parse/asp/builtins_bench_test.go, but in the typical case, the new implementation is about 75% slower and performs four times more allocations:

cpu: Intel(R) Xeon(R) E-2246G CPU @ 3.60GHz
BenchmarkStrFormat
BenchmarkStrFormat-12            5117605               257.0 ns/op            80 B/op          2 allocs/op
BenchmarkStrFormatBig
BenchmarkStrFormatBig-12          512914              2474 ns/op            7568 B/op          3 allocs/op
PASS

cpu: Intel(R) Xeon(R) E-2246G CPU @ 3.60GHz
BenchmarkStrFormat
BenchmarkStrFormat-12            3261982               454.1 ns/op           176 B/op          8 allocs/op
BenchmarkStrFormatBig
BenchmarkStrFormatBig-12          358680              9921 ns/op            7712 B/op         12 allocs/op
PASS

chrisnovakovic avatar Sep 21 '25 19:09 chrisnovakovic