Skip to content

[pylint] Fix false positives and negatives with %b format character (PLE1300, PLE1307) - #27560

Merged
ntBre merged 6 commits into
astral-sh:mainfrom
Andrej730:bad-string-format-bytes
Aug 10, 2026
Merged

ntBre merged 6 commits into
astral-sh:mainfrom
Andrej730:bad-string-format-bytes

Conversation

@Andrej730

@Andrej730 Andrej730 commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Noticed case below with possible false positive/negative when handling %b format character by rules https://docs.astral.sh/ruff/rules/bad-string-format-character/ and https://docs.astral.sh/ruff/rules/bad-string-format-type/

# False negative: not flagged by bad-string-format-character
# Runtime: ValueError: unsupported format character 'b' (0x62) at index 7
a = "hello %b" % 25

# False positive: bad-string-format-type
# Runtime: ValueError: unsupported format character 'b' (0x62) at index 7
# It should report nothing, since the problem is not mismatching
# formatter and provided value, but use of invalid format character in general.
a = "hello %b" % "23"

This fix resolves both issues - first case is now reported and second results in no diagnostic.

The root cause for was bad-string-format-character issue was CFormatString parser always parsing %b as Bytes type, while for bytes literals it's actually just an invalid character. Added CFormatContext enum, so parser can parse %b only when invoked from CFormatBytes and not from CFormatString and report issue when finds %b in string literals formatters.

Second issue was caused by bad-string-format-type assuming %b is allowed only for integers, so it was reporting any other type as a mismatch. Now it just ignores %b, since it will be handled by bad-string-format-character.

'b' | 'c' | 'o' | 'x' | 'X' => FormatType::Integer,

For completeness I also refactored FormatType::from in that rule to rely on CFormatType variants instead of hardcoding formatter characters It really just made it more clean without any functional changes:

  • Ascii is now handled explicitly as Repr, instead of assuming it's unknown
  • dropped non-existing formatters n and %, they are present from the original implementation [pylint]: bad-string-format-type #2572 and are not valid formatters (possibly % is artifact from old cformat parser quirks).

Test Plan

Added tests for both rules, updated snapshots. All previous tests pass too.

Dropped 'n' value - seems some kind of stray value, there's no such
formatting pattern.

'%' also doesn't seem to have a chance to really appear as format char,

Both were present from original implementation in PR 2572.
Maybe `%` is some artifact of the cformat parser that was present back
then.
No functional changes, just making bytes and ascii handling more explicit.
@astral-sh-bot
astral-sh-bot Bot requested a review from ntBre August 6, 2026 21:29

@ntBre ntBre left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this makes sense to me. I just had one suggestion to simplify the changes a bit. My other two comments are mainly observations.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the changes in this file are unnecessary because the rule already skips cases where CFormatString::from_str fails, which should now be the case for a string containing "%b".

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, since from_str will be skipping %b, 'b' format character will never actually reach bad_string_format_type.

Though it's still confusing to see 'b' -> Integer here and misleading 'n', '%' chars and hardcoded characters in general, but I guess I can submit it as a refactor separately.

Reverted the changes in 966abd1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see what you mean. I guess if I were starting from scratch, I would try to tie the successful parsing more directly to this narrower processing, like have FormatType::from also call CFormatString::from_str, or something like that. But it also doesn't feel like a high-priority refactor without resolving a bug, at least to me.

Comment thread crates/ruff_python_literal/src/cformat.rs
@ntBre ntBre added the bug Something isn't working label Aug 10, 2026
@astral-sh-bot

astral-sh-bot Bot commented Aug 10, 2026

Copy link
Copy Markdown

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

@Andrej730
Andrej730 requested a review from ntBre August 10, 2026 20:30
@ntBre ntBre changed the title [pylint] Fix false positive/negative with %b formatter [pylint] Fix false positives and negatives with %b format character (PLE1300, PLE1307) Aug 10, 2026

@ntBre ntBre left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@ntBre
ntBre merged commit d23b7e8 into astral-sh:main Aug 10, 2026
47 checks passed
@Andrej730
Andrej730 deleted the bad-string-format-bytes branch August 10, 2026 21:10
George-Ogden pushed a commit to George-Ogden/ruff that referenced this pull request Aug 16, 2026
…er (`PLE1300`, `PLE1307`) (astral-sh#27560)

## Summary

Noticed case below with possible false positive/negative when handling
`%b` format character by rules
https://docs.astral.sh/ruff/rules/bad-string-format-character/ and
https://docs.astral.sh/ruff/rules/bad-string-format-type/

```python
# False negative: not flagged by bad-string-format-character
# Runtime: ValueError: unsupported format character 'b' (0x62) at index 7
a = "hello %b" % 25

# False positive: bad-string-format-type
# Runtime: ValueError: unsupported format character 'b' (0x62) at index 7
# It should report nothing, since the problem is not mismatching
# formatter and provided value, but use of invalid format character in general.
a = "hello %b" % "23"
```

This fix resolves both issues - first case is now reported and second
results in no diagnostic.

The root cause for was `bad-string-format-character` issue was
`CFormatString` parser always parsing `%b` as `Bytes` type, while for
bytes literals it's actually just an invalid character. Added
`CFormatContext` enum, so parser can parse `%b` only when invoked from
`CFormatBytes` and not from `CFormatString` and report issue when finds
`%b` in string literals formatters.

Second issue was caused by `bad-string-format-type` assuming `%b` is
allowed only for integers, so it was reporting any other type as a
mismatch. Now it just ignores `%b`, since it will be handled by
`bad-string-format-character`.


https://github.com/astral-sh/ruff/blob/17a00de2e298612201a8fe30790e9399204af1b9/crates/ruff_linter/src/rules/pylint/rules/bad_string_format_type.rs#L100

For completeness I also refactored `FormatType::from` in that rule to
rely on `CFormatType` variants instead of hardcoding formatter
characters It really just made it more clean without any functional
changes:
- `Ascii` is now handled explicitly as `Repr`, instead of assuming it's
unknown
- dropped non-existing formatters `n` and `%`, they are present from the
original implementation astral-sh#2572 and
are not valid formatters (possibly `%` is artifact from old cformat
parser quirks).


## Test Plan

Added tests for both rules, updated snapshots. All previous tests pass
too.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants