Skip to content

[flake8-pyi] Expand Optional[A] to A | None (PYI016) #18572

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

Merged
merged 9 commits into from
Jun 27, 2025

Conversation

robsdedude
Copy link
Contributor

Summary

Under preview 🧪 I've expanded rule PYI016 to also flag type union duplicates containing None and Optional.

Test Plan

Examples/tests have been added. I've made sure that the existing examples did not change unless preview is enabled.

Relevant Issues

Copy link
Contributor

github-actions bot commented Jun 8, 2025

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

ℹ️ ecosystem check detected linter changes. (+2 -0 violations, +0 -0 fixes in 1 projects; 54 projects unchanged)

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

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

+ libs/core/langchain_core/language_models/llms.py:158:44: PYI016 [*] Duplicate union member `None`
+ libs/core/langchain_core/language_models/llms.py:194:44: PYI016 [*] Duplicate union member `None`

Changes by rule (1 rules affected)

code total + violation - violation + fix - fix
PYI016 2 2 0 0 0

Formatter (stable)

✅ ecosystem check detected no format changes.

Formatter (preview)

✅ ecosystem check detected no format changes.

@robsdedude robsdedude force-pushed the feat/pyi016-optional-none branch from 0fb3f9f to 0e6490a Compare June 9, 2025 20:53
@ntBre ntBre self-requested a review June 9, 2025 20:59
@robsdedude robsdedude marked this pull request as ready for review June 9, 2025 21:03
@robsdedude robsdedude requested a review from AlexWaygood as a code owner June 9, 2025 21:03
@AlexWaygood
Copy link
Member

Nice, the ecosystem hits are both true positives!

@ntBre ntBre added rule Implementing or modifying a lint rule preview Related to preview mode features labels Jun 17, 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.

Nice! This is looking good. I just had a few minor suggestions and a question about Optional[None].

Comment on lines 95 to 98
// Avoid duplicate checks inside `Optional`
&& !(
optional_as_none_in_union_enabled(checker.settings)
&& checker.semantic.inside_optional()
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we move this check inside the rule itself so we don't have to duplicate it? This feels surprising to me, but I tried removing them locally and they're obviously needed somewhere to avoid duplicate diagnostics, as the comment says.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I suppose we could. I was just trying to be consistent with what I saw in the code-base. Just a few lines above there's

// Avoid duplicate checks if the parent is a union, since these rules already
// traverse nested unions.
if !checker.semantic.in_nested_union() {

Copy link
Contributor

Choose a reason for hiding this comment

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

Fair enough!

Comment on lines +458 to +460
struct UnionTraversalOptions {
traverse_optional: bool,
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I think I'm not as against passing bare bool arguments as most people, but if you really want to avoid it, I think the more typical approach is defining a two-variant enum. Maybe something like this:

enum TraverseOptions {
    Yes,
    No,
}

As an even smaller nit here, I think we usually put the docs above the #[derive].

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I went with the struct as it's easier to extend should the need for more config options arise in the future. If you'd rather have it an enum, I'm also fine with that. I just find bare bool arguments hard to read at call sites, so I try to avoid them unless the function name gives a strong clue as to what the flag does.

Should I change it anyway?

Copy link
Contributor

Choose a reason for hiding this comment

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

Are there more options you think might be needed? I guess I was assuming this would be the only one.

In any case, I don't feel too strongly about this, so we can just move ahead with the current version.

Comment on lines +934 to +935
130 |-field39: typing.Optional[None]
130 |+field39: None
Copy link
Contributor

Choose a reason for hiding this comment

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

I was a bit skeptical about this transformation in #18508 (comment) but if you and @AlexWaygood think it's fine, I'm happy with it. I just thought it was likely to be a separate mistake that a safe autofix could hide.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This might become hard to track, but how about making the fix unsafe if both

  1. An Optional was visited
  2. None is (one of) the redundant types in the union

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess it's fine if it would complicate the code, again I don't feel too strongly. And the change is in preview, so we can collect some feedback here.

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! Can you resolve the conflicts?

After that, I can merge after the patch release today.

Comment on lines +458 to +460
struct UnionTraversalOptions {
traverse_optional: bool,
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Are there more options you think might be needed? I guess I was assuming this would be the only one.

In any case, I don't feel too strongly about this, so we can just move ahead with the current version.

Comment on lines +934 to +935
130 |-field39: typing.Optional[None]
130 |+field39: None
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess it's fine if it would complicate the code, again I don't feel too strongly. And the change is in preview, so we can collect some feedback here.

Comment on lines 95 to 98
// Avoid duplicate checks inside `Optional`
&& !(
optional_as_none_in_union_enabled(checker.settings)
&& checker.semantic.inside_optional()
Copy link
Contributor

Choose a reason for hiding this comment

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

Fair enough!

@ntBre ntBre changed the title [flake8_pyi] Expand Optional[A] to A | None in duplicate-union-member (PYI016) [flake8_pyi] Expand Optional[A] to A | None in duplicate-union-member (PYI016) Jun 27, 2025
@ntBre ntBre changed the title [flake8_pyi] Expand Optional[A] to A | None in duplicate-union-member (PYI016) [flake8-pyi] Expand Optional[A] to A | None (PYI016) Jun 27, 2025
@ntBre ntBre enabled auto-merge (squash) June 27, 2025 15:40
@ntBre ntBre merged commit 6802c47 into astral-sh:main Jun 27, 2025
34 checks passed
dcreager added a commit that referenced this pull request Jun 27, 2025
* main:
  [ty] Make tuple instantiations sound (#18987)
  [`flake8-pyi`] Expand `Optional[A]` to `A | None` (`PYI016`) (#18572)
  Convert `OldDiagnostic::noqa_code` to an `Option<String>` (#18946)
  [ty] Fix playground (#18986)
@robsdedude robsdedude deleted the feat/pyi016-optional-none branch June 29, 2025 11:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
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.

3 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy