Skip to content

Conversation

@nathanjmcdougall
Copy link
Contributor

@nathanjmcdougall nathanjmcdougall commented Sep 20, 2025

See #1297 for motivation.

There's a few places where a conditional check can be simplified but the logic is somewhat subtle. Basically the key thing to bear in mind is that the new _get_shell_name method handles the case where HAS_SHELLINGHAM is false by returning None.

There were a couple calls to shellingham.detect_shell that potentially could have raised ShellDetectionFailure, so I've put this under API ban in favour of the _get_shell_name method.

@github-actions

This comment was marked as outdated.

@github-actions

This comment was marked as outdated.

@nathanjmcdougall nathanjmcdougall marked this pull request as ready for review September 20, 2025 09:50
Copy link
Member

@svlandeg svlandeg left a comment

Choose a reason for hiding this comment

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

The motivation to lazy-load rich_utils was because of performance reasons, as laid out in #1128. Do you see similar performance hits for loading shellingham?

@nathanjmcdougall
Copy link
Contributor Author

Fair question, I think probably I'm being loose with my terminology and write-ups.

Since shellingham is a conditional dependency, it's using conditional import patterns. That was true for rich even without making it lazy-loaded. In #1297, we moved to a different pattern for conditional imports using importlib instead of try-except. I do the same in this PR but for shellingham. I think there are several advantages to having a HAS_SHELLINGHAM variable instead of a possibly-None shellingham import variable (consistency with rich, type inference, monkey patching in the test suite to mock when shellingham isn't available, etc.).

The next thing the PR does is introduce a unified method _get_shell_name for detecting the shell using shellingham, with error handling for if shellingham isn't available. This adds robustness for the cases where there is an uncaught shellingham.ShellDetectionFailure error. Perhaps I ought to add test cases to prove this is the case (i.e. to prove the error no longer propogates).

A side effect of having a _get_shell_name method is that this is only one place in the codebase where shellingham is actually needed, so it makes sense to place shellingham.detect_shell under import ban to encourage the use of the _get_shell_name method instead.

What about the lazy loading? To answer your question regarding performance, I don't think performance is a concern here. However, I think it's the least confusing approach in light of the above, and it now only occurs in a single file (except the test suite, perhaps we should disable the lazy-loading Ruff rules in the test suite since it adds little value there).

@svlandeg svlandeg self-assigned this Sep 22, 2025
@svlandeg svlandeg changed the title Refactor the handling of shellingham lazy-loading ♻️ Refactor the handling of shellingham Oct 3, 2025
Copy link
Member

@svlandeg svlandeg left a comment

Choose a reason for hiding this comment

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

Thanks for the PR @nathanjmcdougall!

I like the idea of having a HAS_SHELLINGHAM var declared in core.py just once, and importing it from there. And using find_spec instead of the try import setup we had before.

The new _get_shell_name also looks good and simplifies various other checks in the code base.

I also agree with putting shellingham.detect_shell under import ban to ensure we always use the new _get_shell_name and avoid re-raising the ShellDetectionFailure.

The only thing I don't necessarily agree with, is enforcing the "lazy loading" of shellingham in the pyproject.toml. It feels a bit unnecessarily rigid, especially if we have to rewrite the test files because of it. shellingham is specified in our requirements-tests.txt and I personally find the code less clean if the imports aren't all at the top - so I'm not in favour of those edits in the 3 test modules.

Alternatively, if we really do want to enforce using shellingham only from within functions, it would be nice if we could exclude the test suite.

@svlandeg svlandeg removed their assignment Oct 3, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Oct 5, 2025

📝 Docs preview

Last commit 88295c1 at: https://e3d99413.typertiangolo.pages.dev

@nathanjmcdougall
Copy link
Contributor Author

Alright, that makes sense to me. I've removed the lazy-loading aspects from the PR.

Copy link
Member

@svlandeg svlandeg left a comment

Choose a reason for hiding this comment

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

This looks good to me - I'll defer to Tiangolo for a final review though.

@github-actions github-actions bot added the conflicts Automatically generated when a PR has a merge conflict label Nov 25, 2025
@github-actions
Copy link
Contributor

This pull request has a merge conflict that needs to be resolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

conflicts Automatically generated when a PR has a merge conflict refactor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants