-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
NoneType handling for str.format() with specifiers #18952
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
base: master
Are you sure you want to change the base?
Changes from 10 commits
3fe1aa7
4180b55
49d8fbf
c5d0b71
31542a1
30665c0
feda1d3
5c4e701
2286f46
8b94be8
d4f70fb
e3d002e
c536cab
0d329dd
693486e
cfc6b17
bcf28be
066a0f3
c979af9
d46f562
ade0d1b
35cf276
6949731
442d8c5
e5e2430
daac179
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -52,6 +52,7 @@ | |
AnyType, | ||
Instance, | ||
LiteralType, | ||
NoneType, | ||
TupleType, | ||
Type, | ||
TypeOfAny, | ||
|
@@ -98,7 +99,6 @@ def compile_new_format_re(custom_spec: bool) -> Pattern[str]: | |
|
||
# Conversion (optional) is ! followed by one of letters for forced repr(), str(), or ascii(). | ||
conversion = r"(?P<conversion>![^:])?" | ||
|
||
# Format specification (optional) follows its own mini-language: | ||
if not custom_spec: | ||
# Fill and align is valid for all builtin types. | ||
|
@@ -113,7 +113,6 @@ def compile_new_format_re(custom_spec: bool) -> Pattern[str]: | |
else: | ||
# Custom types can define their own form_spec using __format__(). | ||
format_spec = r"(?P<format_spec>:.*)?" | ||
|
||
return re.compile(field + conversion + format_spec) | ||
|
||
|
||
|
@@ -449,6 +448,24 @@ def perform_special_format_checks( | |
call, | ||
code=codes.STRING_FORMATTING, | ||
) | ||
a_type = get_proper_type(actual_type) | ||
if isinstance(a_type, NoneType): | ||
# Perform type check of alignment specifiers on None | ||
if spec.format_spec and any(c in spec.format_spec for c in "<>^"): | ||
specifierIndex = -1 | ||
for i in range(len("<>^")): | ||
if spec.format_spec[i] in "<>^": | ||
specifierIndex = i | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This logic seems confused -- why are we mixing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could be just specifier_char = next(c for c in spec.format_spec if c in "<>^") and then Aside, please stick to |
||
if specifierIndex > -1: | ||
self.msg.fail( | ||
( | ||
f"Alignment format specifier " | ||
f'"{spec.format_spec[specifierIndex]}" ' | ||
f"is not supported for None" | ||
), | ||
call, | ||
code=codes.STRING_FORMATTING, | ||
) | ||
|
||
def find_replacements_in_call(self, call: CallExpr, keys: list[str]) -> list[Expression]: | ||
"""Find replacement expression for every specifier in str.format() call. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -265,6 +265,16 @@ b'%c' % (123) | |
-- ------------------ | ||
|
||
|
||
[case testFormatCallNoneAlignment] | ||
'{:<1}'.format(None) # E: Alignment format specifier "<" is not supported for None | ||
'{:>1}'.format(None) # E: Alignment format specifier ">" is not supported for None | ||
'{:^1}'.format(None) # E: Alignment format specifier "^" is not supported for None | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add a few more tests - at least with f-strings, with conversion (e.g. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm looking at the case of
which python throws an error but the current implementation does not catch it.
Is there a way to see what value foo represent in MyPy? My understanding is that MyPy is a static tool, so it doesn't have the value of foo. So, technically, there is no way to check this case? So, would it be an option we just raise an warning that there might be an error rather saying that this is an error? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
no, false positives should be avoided at all costs in There are |
||
|
||
'{:<10}'.format('16') # OK | ||
'{:>10}'.format('16') # OK | ||
'{:^10}'.format('16') # OK | ||
[builtins fixtures/primitives.pyi] | ||
|
||
[case testFormatCallParseErrors] | ||
'}'.format() # E: Invalid conversion specifier in format string: unexpected } | ||
'{'.format() # E: Invalid conversion specifier in format string: unmatched { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be cleaner: