Skip to content

Recognize Self annotation and self assignment in SLF001#24144

Merged
MichaReiser merged 2 commits intoastral-sh:mainfrom
moktamd:fix/slf001-self-type
Mar 25, 2026
Merged

Recognize Self annotation and self assignment in SLF001#24144
MichaReiser merged 2 commits intoastral-sh:mainfrom
moktamd:fix/slf001-self-type

Conversation

@moktamd
Copy link
Copy Markdown
Contributor

@moktamd moktamd commented Mar 23, 2026

Fixes #24140

SLF001 now suppresses false positives for two additional same-class patterns:

  • Variables annotated as typing.Self (e.g. other: Self)
  • Variables assigned directly from self (e.g. this = self)

Both cases clearly refer to the same class instance and should not trigger a private member access warning.

@MichaReiser
Copy link
Copy Markdown
Member

Thank you. There's a second PR for the same issue. Would you mind taking a quick look at #24148 to see if it does something different from what you do here (or if this PR can be simplfied) that's worth accounting for?

@moktamd
Copy link
Copy Markdown
Contributor Author

moktamd commented Mar 25, 2026

Thanks for the pointer. #24148 is already closed and only covers the Self annotation case (match_annotation).

This PR additionally handles variables assigned directly from self (e.g. this = self) via match_initializer, which is the other common pattern from the issue. Both checks are minimal additions to the existing SameClassInstanceChecker.

@MichaReiser MichaReiser added the rule Implementing or modifying a lint rule label Mar 25, 2026
Copy link
Copy Markdown
Member

@MichaReiser MichaReiser left a comment

Choose a reason for hiding this comment

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

Hmm okay, there are a few cases that aren't correctly handled:

     from typing import Annotated, Self

     class A:
         def __init__(self) -> None:
             self._x = 1

         def f(self, other: Annotated[Self, "meta"]) -> None:
             print(other._x)  # still flagged

Unlike Annotated[A], Annotated[Self] isn't recognized

Allowing self is prone to false positives

 class A:
         @staticmethod
         def f() -> None:
             self = object()
             alias = self
             print(alias._x)  # incorrectly allowed by this patch

@astral-sh-bot
Copy link
Copy Markdown

astral-sh-bot bot commented Mar 25, 2026

ruff-ecosystem results

Linter (stable)

ℹ️ ecosystem check detected linter changes. (+15 -3 violations, +0 -0 fixes in 3 projects; 53 projects unchanged)

apache/airflow (+9 -3 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --no-preview --select ALL

- airflow-core/src/airflow/dag_processing/processor.py:534:9: SLF001 Private member accessed: `_on_child_started`
+ airflow-core/tests/unit/cli/test_cli_parser.py:648:18: S603 `subprocess` call: check for execution of untrusted input
+ airflow-core/tests/unit/cli/test_cli_parser.py:663:18: S603 `subprocess` call: check for execution of untrusted input
+ airflow-core/tests/unit/cli/test_cli_parser.py:678:18: S603 `subprocess` call: check for execution of untrusted input
+ airflow-core/tests/unit/utils/test_process_utils.py:128:19: S603 `subprocess` call: check for execution of untrusted input
+ airflow-core/tests/unit/utils/test_process_utils.py:137:19: S603 `subprocess` call: check for execution of untrusted input
+ airflow-ctl-tests/tests/airflowctl_tests/conftest.py:154:18: S603 `subprocess` call: check for execution of untrusted input
- devel-common/src/tests_common/pytest_plugin.py:936:25: SLF001 Private member accessed: `_make_serdag`
+ scripts/in_container/run_prepare_airflow_distributions.py:73:23: S603 `subprocess` call: check for execution of untrusted input
+ scripts/in_container/verify_providers.py:782:16: S603 `subprocess` call: check for execution of untrusted input
+ task-sdk-integration-tests/tests/task_sdk_tests/conftest.py:364:18: S603 `subprocess` call: check for execution of untrusted input
- task-sdk/src/airflow/sdk/execution_time/supervisor.py:1008:9: SLF001 Private member accessed: `_on_child_started`

bokeh/bokeh (+1 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --no-preview --select ALL

+ tests/codebase/test_python_execution_with_OO.py:45:12: S603 `subprocess` call: check for execution of untrusted input

home-assistant/core (+5 -0 violations, +0 -0 fixes)

+ homeassistant/components/fritz/config_flow.py:201:48: RUF100 [*] Unused `noqa` directive (unused: `SLF001`)
+ homeassistant/components/fritzbox/config_flow.py:151:48: RUF100 [*] Unused `noqa` directive (unused: `SLF001`)
+ homeassistant/components/gogogate2/config_flow.py:78:60: RUF100 [*] Unused `noqa` directive (unused: `SLF001`)
+ homeassistant/components/webostv/config_flow.py:139:48: RUF100 [*] Unused `noqa` directive (unused: `SLF001`)
+ homeassistant/components/yeelight/config_flow.py:148:66: RUF100 [*] Unused `noqa` directive (unused: `SLF001`)

Changes by rule (3 rules affected)

code total + violation - violation + fix - fix
S603 10 10 0 0 0
RUF100 5 5 0 0 0
SLF001 3 0 3 0 0

Linter (preview)

ℹ️ ecosystem check detected linter changes. (+15 -5 violations, +0 -0 fixes in 5 projects; 51 projects unchanged)

apache/airflow (+9 -3 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview --select ALL

- airflow-core/src/airflow/dag_processing/processor.py:534:9: SLF001 Private member accessed: `_on_child_started`
+ airflow-core/tests/unit/cli/test_cli_parser.py:648:18: S603 `subprocess` call: check for execution of untrusted input
+ airflow-core/tests/unit/cli/test_cli_parser.py:663:18: S603 `subprocess` call: check for execution of untrusted input
+ airflow-core/tests/unit/cli/test_cli_parser.py:678:18: S603 `subprocess` call: check for execution of untrusted input
+ airflow-core/tests/unit/utils/test_process_utils.py:128:19: S603 `subprocess` call: check for execution of untrusted input
+ airflow-core/tests/unit/utils/test_process_utils.py:137:19: S603 `subprocess` call: check for execution of untrusted input
+ airflow-ctl-tests/tests/airflowctl_tests/conftest.py:154:18: S603 `subprocess` call: check for execution of untrusted input
- devel-common/src/tests_common/pytest_plugin.py:936:25: SLF001 Private member accessed: `_make_serdag`
+ scripts/in_container/run_prepare_airflow_distributions.py:73:23: S603 `subprocess` call: check for execution of untrusted input
+ scripts/in_container/verify_providers.py:782:16: S603 `subprocess` call: check for execution of untrusted input
+ task-sdk-integration-tests/tests/task_sdk_tests/conftest.py:364:18: S603 `subprocess` call: check for execution of untrusted input
- task-sdk/src/airflow/sdk/execution_time/supervisor.py:1008:9: SLF001 Private member accessed: `_on_child_started`

bokeh/bokeh (+1 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview --select ALL

+ tests/codebase/test_python_execution_with_OO.py:45:12: S603 `subprocess` call: check for execution of untrusted input

langchain-ai/langchain (+0 -1 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

- libs/langchain_v1/tests/unit_tests/agents/middleware/implementations/test_shell_tool.py:451:5: RUF072 [*] Empty `finally` clause

pytest-dev/pytest (+0 -1 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

- testing/test_link_resolve.py:49:5: RUF072 Empty `finally` clause

home-assistant/core (+5 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --no-fix --output-format concise --preview

+ homeassistant/components/fritz/config_flow.py:201:48: RUF100 [*] Unused `noqa` directive (unused: `SLF001`)
+ homeassistant/components/fritzbox/config_flow.py:151:48: RUF100 [*] Unused `noqa` directive (unused: `SLF001`)
+ homeassistant/components/gogogate2/config_flow.py:78:60: RUF100 [*] Unused `noqa` directive (unused: `SLF001`)
+ homeassistant/components/webostv/config_flow.py:139:48: RUF100 [*] Unused `noqa` directive (unused: `SLF001`)
+ homeassistant/components/yeelight/config_flow.py:148:66: RUF100 [*] Unused `noqa` directive (unused: `SLF001`)

Changes by rule (4 rules affected)

code total + violation - violation + fix - fix
S603 10 10 0 0 0
RUF100 5 5 0 0 0
SLF001 3 0 3 0 0
RUF072 2 0 2 0 0

@moktamd
Copy link
Copy Markdown
Contributor Author

moktamd commented Mar 25, 2026

Thanks for the review — both cases fixed:

  1. Annotated[Self, ...]: Refactored to unwrap Annotated before checking for Self, so Annotated[Self, "meta"] is now recognized.

  2. self in static methods: match_initializer now verifies that self resolves to an Argument binding, so self = object() in a @staticmethod won't be treated as the instance parameter.

Added test cases for both.

Copy link
Copy Markdown
Member

@MichaReiser MichaReiser left a comment

Choose a reason for hiding this comment

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

Nice, thank you

moktamd added 2 commits March 25, 2026 16:55
SLF001 now suppresses false positives for:
- Variables annotated as typing.Self (e.g. other: Self)
- Variables assigned directly from self (e.g. this = self)
- Refactor match_annotation to unwrap Annotated before checking Self
- Verify self is an Argument binding in match_initializer
@MichaReiser MichaReiser force-pushed the fix/slf001-self-type branch from b2c02aa to f148ee0 Compare March 25, 2026 15:55
@MichaReiser MichaReiser merged commit 7e8fb8a into astral-sh:main Mar 25, 2026
41 checks passed
carljm added a commit that referenced this pull request Mar 25, 2026
* main:
  [ty] make `test-case` a dev-dependency (#24187)
  [ty] implement cycle normalization for more types to prevent too-many-cycle panics (#24061)
  [ty] Silence all diagnostics in unreachable code (#24179)
  [ty] Intern `InferableTypeVars` (#24161)
  Implement unnecessary-if (RUF050) (#24114)
  Recognize `Self` annotation and `self` assignment in SLF001 (#24144)
  Bump the npm version before publish (#24178)
  [ty] Disallow Self in metaclass and static methods (#23231)
  Use trusted publishing for NPM packages (#24171)
  [ty] Respect non-explicitly defined dataclass params (#24170)
  Add RUF072: warn when using  operator on an f-string (#24162)
  [ty] Check return type of generator functions (#24026)
  Implement useless-finally (RUF-072) (#24165)
  [ty] Add test for a dataclass with a default field converter (#24169)
  [ty] Dataclass field converters (#23088)
  [flake8-bandit] Treat sys.executable as trusted input in S603 (#24106)
  [ty] Add support for `typing.Concatenate` (#23689)
  `ASYNC115`: autofix to use full qualified `anyio.lowlevel` import (#24166)
  [ty] Disallow read-only fields in TypedDict updates (#24128)
  Speed up diagnostic rendering (#24146)
nicopauss pushed a commit to Intersec/lib-common that referenced this pull request Apr 1, 2026
##### [\`v0.15.8\`](https://github.com/astral-sh/ruff/blob/HEAD/CHANGELOG.md#0158)

Released on 2026-03-26.

##### Preview features

- \[`ruff`] New rule `unnecessary-if` (`RUF050`) ([#24114](astral-sh/ruff#24114))
- \[`ruff`] New rule `useless-finally` (`RUF072`) ([#24165](astral-sh/ruff#24165))
- \[`ruff`] New rule `f-string-percent-format` (`RUF073`): warn when using `%` operator on an f-string ([#24162](astral-sh/ruff#24162))
- \[`pyflakes`] Recognize `frozendict` as a builtin for Python 3.15+ ([#24100](astral-sh/ruff#24100))

##### Bug fixes

- \[`flake8-async`] Use fully-qualified `anyio.lowlevel` import in autofix (`ASYNC115`) ([#24166](astral-sh/ruff#24166))
- \[`flake8-bandit`] Check tuple arguments for partial paths in `S607` ([#24080](astral-sh/ruff#24080))
- \[`pyflakes`] Skip `undefined-name` (`F821`) for conditionally deleted variables ([#24088](astral-sh/ruff#24088))
- `E501`/`W505`/formatter: Exclude nested pragma comments from line width calculation ([#24071](astral-sh/ruff#24071))
- Fix `%foo?` parsing in IPython assignment expressions ([#24152](astral-sh/ruff#24152))
- `analyze graph`: resolve string imports that reference attributes, not just modules ([#24058](astral-sh/ruff#24058))

##### Rule changes

- \[`eradicate`] ignore `ty: ignore` comments in `ERA001` ([#24192](astral-sh/ruff#24192))
- \[`flake8-bandit`] Treat `sys.executable` as trusted input in `S603` ([#24106](astral-sh/ruff#24106))
- \[`flake8-self`] Recognize `Self` annotation and `self` assignment in `SLF001` ([#24144](astral-sh/ruff#24144))
- \[`pyflakes`] `F507`: Fix false negative for non-tuple RHS in `%`-formatting ([#24142](astral-sh/ruff#24142))
- \[`refurb`] Parenthesize generator arguments in `FURB142` fixer ([#24200](astral-sh/ruff#24200))

##### Performance

- Speed up diagnostic rendering ([#24146](astral-sh/ruff#24146))

##### Server

- Warn when Markdown files are skipped due to preview being disabled ([#24150](astral-sh/ruff#24150))

##### Documentation

- Clarify `extend-ignore` and `extend-select` settings documentation ([#24064](astral-sh/ruff#24064))
- Mention AI policy in PR template ([#24198](astral-sh/ruff#24198))

##### Other changes

- Use trusted publishing for NPM packages ([#24171](astral-sh/ruff#24171))

##### Contributors

- [@bitloi](https://github.com/bitloi)
- [@Sim-hu](https://github.com/Sim-hu)
- [@mvanhorn](https://github.com/mvanhorn)
- [@chinar-amrutkar](https://github.com/chinar-amrutkar)
- [@markjm](https://github.com/markjm)
- [@RenzoMXD](https://github.com/RenzoMXD)
- [@vivekkhimani](https://github.com/vivekkhimani)
- [@seroperson](https://github.com/seroperson)
- [@moktamd](https://github.com/moktamd)
- [@charliermarsh](https://github.com/charliermarsh)
- [@ntBre](https://github.com/ntBre)
- [@zanieb](https://github.com/zanieb)
- [@dylwil3](https://github.com/dylwil3)
- [@MichaReiser](https://github.com/MichaReiser)

Renovate-Branch: renovate/2024.6-ruff-0.15.x
Change-Id: Ifd4216a963962ffb24a4df69802bc60fcc29628d
Priv-Id: 46d2f61be3a5e65a9fdd2fef998ba41ea3388f12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

rule Implementing or modifying a lint rule

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SLF001: access to private field of another instance of the same class typed as Self

3 participants