Skip to content
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

Applying or ignoring RUF022/RUF023 (__all__ & __slots__ sorting) #13108

Closed
wants to merge 7 commits into from

Conversation

Avasam
Copy link
Collaborator

@Avasam Avasam commented Nov 25, 2024

As per #13090 (comment), here's a PR to discuss whether we want RUF022 in typeshed.

From a typing perspective, this technically changes the type of tuple-literals __all__:

__all__ = ("a", "b")  # tuple[Literal['a'], Literal['b']]
# is not the same as
__all__ = ("b", "a")  # tuple[Literal['b'], Literal['a']]

Whilst the cases where this matters are extremely niche, there is an argument for accuracy.

Essentially the same thing is mentioned in https://docs.astral.sh/ruff/rules/unsorted-dunder-all/#fix-safety

This rule's fix is marked as always being safe, in that it should very rarely alter the semantics of any Python code. However, note that (although it's rare) the value of all could be read by code elsewhere that depends on the exact iteration order of the items in all, in which case this rule's fix could theoretically cause breakage.

Although is that's something we care about, stubtest could probably test for it, which would counter the "pros" of ordering consistency mentioned below as it would be enforced by stubtest and the order defined by implementation.

Another argument is about copying __all__ from source. It might be easier to update/understand/debug if one can copy __all__ from implementation and the order stays the same.


Arguments for applying RUF022 is that autosorting and consistency are generally nice and after the initial commit help avoid conflicts. There's likely a few places in typeshed where we don't follow runtime ordering anyway.


The same question and overall arguments also apply to __slots__ sorting with RUF023

@Avasam Avasam changed the title Applying or ignoring RUF022 Applying or ignoring RUF022/RUF023 (__all__ & __slots__ sorting) Nov 25, 2024

This comment has been minimized.

Comment on lines 39 to 51
__slots__ = (
"namespace_whitelist",
"save_debug_info",
"function_aliases",
"experimental_debug_stripper",
"experimental_io_device",
"experimental_variable_policy",
"experimental_custom_gradients",
"experimental_debug_stripper",
"experimental_image_format",
"experimental_skip_saver",
"experimental_io_device",
"experimental_sharding_callback",
"experimental_skip_saver",
"experimental_variable_policy",
"extra_tags",
"function_aliases",
"namespace_whitelist",
"save_debug_info",
)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

In case you're searching for it, this is the only RUF023 change

Comment on lines -74 to -77
# TODO: Ruff 0.8.0 added sorting of __all__ and __slots_. Validate whether we want this in stubs
"RUF022",
"RUF023",

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

If we decide to ignore, these should go in the .pyi-only ignores.

@AlexWaygood
Copy link
Member

I'm actually sort-of surprised that stubtest isn't freaking out about __all__ being ("foo", "bar") at runtime and ("bar", "foo") in the stub -- I would have expected stubtest to see those as different types (tuple[Literal["foo"], Literal["bar"]] vs tuple[Literal["bar"], Literal["foo"]]) and then complain when we changed the stub to have a different order than exists at runtime. Do we know why stubtest isn't complaining here? Did something in stubtest change, or was I just always wrong about how stubtest checked __all__ when __all__ is a tuple?

This comment has been minimized.

@AlexWaygood
Copy link
Member

AlexWaygood commented Nov 25, 2024

As long as this doesn't affect stubtest's ability to verify which items are in __all__ in the stub and how those compare to the runtime definition (and I don't see how it could affect that), I think I'm weakly in favour of this. I'm sure there are quite a few __all__ definitions in typeshed where we already don't match the order of __all__ at runtime, and we'd have to setup some custom tooling (either as part of stubtest or as a standalone tool) if that's something we wanted to verify. And as @tungol mentioned in #13090 (comment), answering the question "Does this have the same order as at runtime?" is kind of a complicated question for us because of all our __all__ modifications in sys.version_info and sys.platform branches.

@srittau
Copy link
Collaborator

srittau commented Nov 25, 2024

I'm also +0 about this change.

@tungol
Copy link
Contributor

tungol commented Nov 25, 2024

Is there an option to sort list-based __all__ but not tuple-based __all__? The tuple ones are the ones that are technically not the same and also the ones were we don't dynamically add things in branches (but only because we can't, really). Getting stubtest to look for exact matches if __all__ is a tuple would probably be straightforward.

@JelleZijlstra
Copy link
Member

I'd generally favor consistency with the runtime, but I don't really see any practical reason why the order should matter, so we might as well sort them.

@AlexWaygood
Copy link
Member

Is there an option to sort list-based __all__ but not tuple-based __all__?

There isn't. I'd consider implementing such a feature at ruff (I'm a maintainer there), but it feels like a slightly niche thing for Ruff to add a configuration option for. (Outside of typeshed, who would use it? Would it be obvious to anybody why the option exists unless you're a stub author and typing expert?)

@JelleZijlstra
Copy link
Member

I don't think there's a strong reason to treat list and tuple __all__ differently. Yes, in theory you could assign a tuple __all__ to a place with a precise type like tuple[Literal["a"], Literal["b"]], but that seems highly unlikely to matter in practice.

@Avasam
Copy link
Collaborator Author

Avasam commented Nov 25, 2024

Is there an option to sort list-based __all__ but not tuple-based __all__?

it feels like a slightly niche thing for Ruff to add a configuration option for. (Outside of typeshed, who would use it? [...])

The thought crossed my mind as well. But I have to agree, it would be extremely niche. And would only apply to stubs (as to re-expose the same type). The general comment about fix safety from the rule's doc still applies to tuples.

I thought about it some more over dinner, and I can't think of a single legitimate case where not only the order matters, but that you also can't deal with it programmatically (ie: getting all names that are in all-caps instead of the first 5 if you wanted the constants for example, and even that example isn't affected by tuple types). Like it would be so niche that you aren't looking at stubs/types anyway.

@tungol
Copy link
Contributor

tungol commented Nov 25, 2024

Do we know why stubtest isn't complaining here?

I don't know the whole answer, but I stuck a breakpoint in stubtest. The stub.type for asyncio.base_events.__all__ is showing as tuple[builtins.str, builtins.str] which compares favorably with the runtime's type of tuple[Literal['BaseEventLoop'], Literal['Server']]

@Avasam
Copy link
Collaborator Author

Avasam commented Nov 25, 2024

Do we know why stubtest isn't complaining here?

I don't know the whole answer, but I stuck a breakpoint in stubtest. The stub.type for asyncio.base_events.__all__ is showing as tuple[builtins.str, builtins.str]

It's a mypy vs pyright difference on inferred types.
image
So in other words, this doesn't even currently affect stubtest and mypy users.

This comment has been minimized.

@Akuli
Copy link
Collaborator

Akuli commented Nov 25, 2024

I would somewhat prefer using the copy/paste order as is, so that stubs are closer to implementation. As already mentioned, this way it's much easier to see how a long __all__ differs from the __all__ in the implementation. For example, consider reviewing a pull request where one more public member is added to a large module.

Copy link
Contributor

github-actions bot commented Dec 6, 2024

According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉

@AlexWaygood
Copy link
Member

I feel like we should probably close this at this point (though we should probably get rid of the TODO comment in our pyproject.toml file). Since we don't have consensus among the core team, the status quo wins out IMO :-)

@Avasam
Copy link
Collaborator Author

Avasam commented Dec 28, 2024

I'm fine closing this.
Although I should still move the ignores to be stubs-only. I'll do that in a different PR.

Edit: #13322

@Avasam Avasam closed this Dec 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants