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)
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