56

What does '=' alignment mean in the following error message, and why does this code cause it?

>>> "{num:03}".format(num="1")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: '=' alignment not allowed in string format specifier

The code has a subtle problem: the input value "1" is text, not a number. But the error message doesn't appear to have anything to do with that.

Nothing in the error message indicates why “'=' alignment” is relevant, and it does not appear in the code. So what is the significance of emitting that error message?

8 Answers 8

71

Python 3.10 and later

This issue is corrected in Python 3.10 and later:

In string formatting, preceding the width field by '0' no longer affects the default alignment for strings.

The example code no longer raises an error; instead, it gives the result '100' because the default align for non-numeric types is <.

Python earlier than 3.10

The error message occurs because '=' alignment has been implied by the format specifier.

The str.format format spec mini-language parser has decided on the alignment specifier “=” because:

Preceding the width field by a zero ('0') character enables sign-aware zero-padding for numeric types. This is equivalent to a fill character of '0' with an alignment type of '='.

So by specifying 0N as the “zero-padding to N width”, you have implied both “the input is a numeric type”, and “the zeros should go between the sign and the digits”. That latter implication is what is meant by '=' alignment.

Since the value "1" is not numeric, the “=”-alignment handling code raises that exception. The message is written expecting you know what it's talking about because you requested (by implication) the “=” alignment.

Yes, I think that error message needs to be improved. I've raised an issue for that. Thanks to Serhiy Storchaka for fixing this issue! Python 3.10 includes this change.

Sign up to request clarification or add additional context in comments.

Comments

42

A workaround is to use '>' (right justify) padding, which is with the syntax:

[[fill]align][width]

with align being >, fill being 0 and width being 3.

>>> "{num:0>3}".format(num="1")
'001'

The problem was that there is a different 0 in the format specification:

format_spec     ::=  [[fill]align][sign][#][0][width][grouping_option][.precision][type]
#                                          ^^^ This one

That zero just makes fill default to 0 and align to =.

= alignment is specified as:

Forces the padding to be placed after the sign (if any) but before the digits. This is used for printing fields in the form ‘+000000120’. This alignment option is only valid for numeric types. It becomes the default when ‘0’ immediately precedes the field width.

Source (Python 3 docs)

This expects the argument to be an int, as strings don't have signs. So we just manually set it to the normal default of > (right justify).

Also note that 0 just specifies the default values for fill and align. You can change both or just the align.

>>> # fill defaults to '0', align is '>', `0` is set, width is `3`
>>> "{num:>03}".format(num=-1)
'0-1'
>>> # fill is `x`, align is '>', `0` is set (but does nothing), width is `"3"`
>>> "{num:x>03}".format(num=-1)
'x-1'
>>> # fill is `x`, align is '>', `0` is set (but does nothing), width is `"03"` (3)
>>> "{num:x>003}".format(num=-1)
'x-1'

Comments

12

str.__format__ doesn't know what to do with your 03 part. That only works with numbers:

>>> "{num:03}".format(num=1)
'001'

If you actually want to zero-pad a string, you can use rjust:

>>> "1".rjust(3, "0")
'001'

Comments

8

In my case, I was trying to zero-pad a string instead of a number.

The solution was simply to convert the text to a number before applying the padding:

num_as_text = '23'
num_as_num = int(num_as_text)
padded_text = f'{num_as_num:03}'

1 Comment

This is the simplest method - you can condense to one line with f'{int(num_as_text):03}' - switching the string to a int for padding. You could probably take it farther and switch it back to a string if needed.
2

Changed in version 3.10: Preceding the width field by '0' no longer affects the default alignment for strings.

Thanks to Serhiy Storchaka for fixing it.

The OP's code sample runs fine in Python 3.10+. However note that it returns 100 because the default align for non-numeric types is <.

For older Python versions manually add the left alignment character: "{num:<03}".format(num="1")

1 Comment

Thank you for this update. I have edited the accepted answer to include this relevant information.
0

You are trying to insert 'string->"1" where a float->3.44 is required. Remove the quotes "1", i.e. num=1, and it will work

Comments

0

Just for others who google searched and found this Q&A.

This error:

    out_name = track_name_fmt.format(track, song)
ValueError: '=' alignment not allowed in string format specifier

Was fixed by using:

out_name = track_name_fmt.format(int(track), song)

Forcing track to integer solved the problem.

2 Comments

This is an answer to a somewhat different question; it doesn't constitute an answer to this question.
@bignose It's an answer to the error message that might help others who use Google Search Engine. However I despise down votes and will delete answer soon.
-4

This format would be acceptable

"{num}:03".format(num="1")

but the way you have the placeholder specified {num:03} is not. That is an interesting ValueError though, if you remove the : the interesting error is replaced by a standard KeyError.

2 Comments

This doesn't do what OP was intending. They want to left-align a padded string using format.
Ah ok I see now. That makes more sense.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.