Skip to content

Conversation

@danparizher
Copy link
Contributor

Summary

Fixed RUF065 (logging-eager-conversion) to only flag str() calls when they perform a simple conversion that can be safely removed. The rule now ignores str() calls with no arguments, multiple arguments, starred arguments, or keyword unpacking, preventing false positives.

Fixes #21315

Problem Analysis

The RUF065 rule was incorrectly flagging all str() calls in logging statements, even when str() was performing actual conversion work beyond simple type coercion. Specifically, the rule flagged:

  • str() with no arguments - which returns an empty string
  • str(b"data", "utf-8") with multiple arguments - which performs encoding conversion
  • str(*args) with starred arguments - which unpacks arguments
  • str(**kwargs) with keyword unpacking - which passes keyword arguments

These cases cannot be safely removed because str() is doing meaningful work (encoding conversion, argument unpacking, etc.), not just redundant type conversion.

The root cause was that the rule only checked if the function was str() without validating the call signature. It didn't distinguish between simple str(value) conversions (which can be removed) and more complex str() calls that perform actual work.

Approach

The fix adds validation to the str() detection logic in logging_eager_conversion.rs:

  1. Check argument count: Only flag str() calls with exactly one positional argument (str_call_args.args.len() == 1)
  2. Check for starred arguments: Ensure the single argument is not starred (!str_call_args.args[0].is_starred_expr())
  3. Check for keyword arguments: Ensure there are no keyword arguments (str_call_args.keywords.is_empty())

This ensures the rule only flags cases like str(value) where str() is truly redundant and can be removed, while ignoring cases where str() performs actual conversion work.

The fix maintains backward compatibility - all existing valid test cases continue to be flagged correctly, while the new edge cases are properly ignored.

if checker.semantic().match_builtin_expr(func.as_ref(), "str")
&& str_call_args.args.len() == 1
&& !str_call_args.args[0].is_starred_expr()
&& str_call_args.keywords.is_empty() =>

Choose a reason for hiding this comment

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

logging.warning("%s", str(object="!")), with one keyword argument, should still be flagged.

@danparizher
Copy link
Contributor Author

I added a test case for str(object="!"), and the new logic seems to work in isolation, but for some reason isn't being detected in the test file. I would appreciate it if someone could help me diagnose the issue!

@ntBre ntBre added bug Something isn't working rule Implementing or modifying a lint rule preview Related to preview mode features labels Nov 10, 2025
Copy link
Contributor

@ntBre ntBre left a comment

Choose a reason for hiding this comment

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

Thanks! I had a suggestion for simplification, which will hopefully help with the test case too.

@danparizher
Copy link
Contributor Author

Thanks! I pushed those changes, but it looks like we're running into the same issue as before. The fix works in isolation but not within the test.

@astral-sh-bot
Copy link

astral-sh-bot bot commented Nov 10, 2025

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

Formatter (stable)

✅ ecosystem check detected no format changes.

Formatter (preview)

✅ ecosystem check detected no format changes.

@ntBre
Copy link
Contributor

ntBre commented Nov 10, 2025

Oh, str is shadowed on line 37 of the test file, so none of the new tests are actually exercising the new logic. I think we should add the new tests to a separate file.

Copy link
Contributor

@ntBre ntBre left a comment

Choose a reason for hiding this comment

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

Thank you!

@ntBre ntBre merged commit 1fd852f into astral-sh:main Nov 10, 2025
38 checks passed
@danparizher danparizher deleted the fix-21315 branch November 10, 2025 23:18
dcreager added a commit that referenced this pull request Nov 11, 2025
* origin/main: (38 commits)
  [ty] Make implicit submodule imports only occur in global scope (#21370)
  [ty] introduce local variables for `from` imports of submodules in `__init__.py(i)` (#21173)
  [`ruff`] Ignore `str()` when not used for simple conversion (`RUF065`) (#21330)
  [ty] implement `typing.NewType` by adding `Type::NewTypeInstance`
  [ty] supress inlay hints for `+1` and `-1` (#21368)
  [ty] Use type context for inference of generic constructors (#20933)
  [ty] Improve generic call expression inference (#21210)
  [ty] supress some trivial expr inlay hints (#21367)
  [`configuration`] Fix unclear error messages for line-length values exceeding `u16::MAX` (#21329)
  [ty] Fix incorrect inference of `enum.auto()` for enums with non-`int` mixins, and imprecise inference of `enum.auto()` for single-member enums (#20541)
  [`refurb`] Detect empty f-strings (`FURB105`) (#21348)
  [ty] provide `import` completion when in `from <name> <name>` statement (#21291)
  [ty] elide redundant inlay hints for function args (#21365)
  Fix syntax error false positive on alternative `match` patterns (#21362)
  Add a new "Opening a PR" section to the contribution guide (#21298)
  [`flake8-simplify`] Fix SIM222 false positive for `tuple(generator) or None` (`SIM222`) (#21187)
  Rebuild ruff binary instead of sharing it across jobs (#21361)
  [ty] Fix `--exclude` and `src.exclude` merging (#21341)
  [ty] Add support for properties that return `Self` (#21335)
  Add upstream linter URL to `ruff linter --output-format=json` (#21316)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working preview Related to preview mode features rule Implementing or modifying a lint rule

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RUF065 should ignore str when not used for conversion

3 participants