Skip to content

add TYPE_CHECKING hints for lazy imports in data and filters#6429

Closed
tlambert03 wants to merge 1 commit intoscikit-image:mainfrom
tlambert03:ide-hints
Closed

add TYPE_CHECKING hints for lazy imports in data and filters#6429
tlambert03 wants to merge 1 commit intoscikit-image:mainfrom
tlambert03:ide-hints

Conversation

@tlambert03
Copy link
Copy Markdown
Contributor

Description

#5101 made all imports from data and filters lazy, which is great for import times, but terrible for IDEs.

This PR adds a TYPE_CHECKING clause to each of those modules so linters and IDEs can find the functions again:

current release:
Screen Shot 2022-07-05 at 1 50 39 PM

with this PR:
Screen Shot 2022-07-05 at 1 51 28 PM

Checklist

  • Docstrings for all functions
  • Gallery example in ./doc/examples (new features only)
  • Benchmark in ./benchmarks, if your changes aren't covered by an
    existing benchmark
  • Unit tests
  • Clean style in the spirit of PEP8
  • Descriptive commit messages (see below)

For reviewers

  • Check that the PR title is short, concise, and will make sense 1 year
    later.
  • Check that new functions are imported in corresponding __init__.py.
  • Check that new features, API changes, and deprecations are mentioned in
    doc/release/release_dev.rst.
  • There is a bot to help automate backporting a PR to an older branch. For
    example, to backport to v0.19.x after merging, add the following in a PR
    comment: @meeseeksdev backport to v0.19.x
  • To run benchmarks on a PR, add the run-benchmark label. To rerun, the label
    can be removed and then added again. The benchmark output can be checked in
    the "Actions" tab.

@pep8speaks
Copy link
Copy Markdown

Hello @tlambert03! Thanks for opening this PR. We checked the lines you've touched for PEP 8 issues, and found:

Line 17:80: E501 line too long (82 > 79 characters)

@tlambert03
Copy link
Copy Markdown
Contributor Author

looks like there's a line length linting error error, but I matched the declarations below exactly (which has the same line length). If i fix it here, i think it should also be fixed below to match. lemme know

@grlee77
Copy link
Copy Markdown
Contributor

grlee77 commented Jul 6, 2022

Thanks, @tlambert03. It looks like a nice solution! For reference of other reviewers, TYPE_CHECKING is defined when tools like mypy are running, but not at runtime, so this should not cause import time overhead.

looks like there's a line length linting error error, but I matched the declarations below exactly

yeah, the linter only checks modified lines, so that is possible. Please just fix both

@grlee77
Copy link
Copy Markdown
Contributor

grlee77 commented Jul 6, 2022

pinging @stefanv as this is relevant to SPEC 1 and could warrant mentioning there

@jni
Copy link
Copy Markdown
Member

jni commented Jul 6, 2022

@tlambert03 how does this solution compare to napari's .pyi files? Which would you favour more generally? Either way we should apply it to all modules?

@tlambert03
Copy link
Copy Markdown
Contributor Author

@tlambert03 how does this solution compare to napari's .pyi files? Which would you favour more generally?

I'm struggling to come up with a strong opinion 😂. I guess the reason I did it this way for this PR is that it's the least "invasive" method for this library (which doesn't yet have any .pyi files). My understanding is that if a type stub pyi file exists, static checkers will go straight to it and bypass the corresponding .py file; otherwise they parse the actual .py file. From the perspective of contributors, I think it feels a bit more intuitive to have one file to worry about: if they change something in the lazy imports, they might be motivated to determine whether it also changes something in that big TYPE_CHECKING blurb right above (which looks awfully similar). pyi files by contrast often go unnoticed.

However, pyi files also just look like "plain" python modules, which make them nice to inspect if you have a lot of classes and functions that are dynamically created (not the case here).

I guess i find myself using TYPE_CHECKING clauses if I'm just pointing type checkers to various imports, and pyi files if it otherwise makes the .py completely awful to visually parse

Either way we should apply it to all modules?

I looked around a bit, and I think the only other module is skimage.__init__, and it only has a single missing export, which is the data_dir (or something like that). So, I think this covers most of it

@jni
Copy link
Copy Markdown
Member

jni commented Jul 7, 2022

Oh! I missed that #5101 only applied to filters! 😂 We should fix that! 😂 But yeah not in this PR.

I guess i find myself using TYPE_CHECKING clauses if I'm just pointing type checkers to various imports, and pyi files if it otherwise makes the .py completely awful to visually parse

Ech. For me tbh I find the if TYPE_CHECKING block itself very distracting and ugly, which is why I'd rather have .pyi files, either auto-generated or auto-checked for consistency by CI. The if clause feels like I'm using a CPython implementation detail to make my code behave some specific way, rather than using a Python feature.

btw have you seen PEP-690??? Not that I'm advocating waiting for it 😂 but its very existence is interesting and exciting!

@tlambert03
Copy link
Copy Markdown
Contributor Author

For me tbh I find the if TYPE_CHECKING block itself very distracting and ugly, which is why I'd rather have .pyi files,

see alternative approach building the dynamic imports with a new lazy.attach_stub function here

scientific-python/specs#139 (comment)

do you prefer that?

@leycec
Copy link
Copy Markdown

leycec commented Jul 8, 2022

PEP 690 is, indeed, very exciting for the scientific Python stack – most of which suffers from excrutiating startup times. Sadly, that's orthogonal to the issue at hand. PEP 690 won't land in a stable Python release for another year or two; meanwhile, excrutiating startup times beleaguer us all.

...which is why I'd rather have .pyi files, either auto-generated or auto-checked for consistency by CI.

You think that until you actually use .pyi files. Then the inevitable litany of desynchronization woes, DRY violations, and caching quandaries plague your house with a vicious cycle of useless dev churn. Don't do what Ruby 3 did with RBS, which suddenly made everyone over there jealous of everyone over here.

Instead, embed type hints directly in code and defer to type hint machinery like TYPE_CHECKING directly from code. .pyi files are mostly an obsolete relic of a bygone era when the type-checking community didn't know any better. Now we know. And animated children series tell us that's half the battle. Would television lie?

I should probably admit at this juncture that I unconditionally support everything @tlambert03 does. As the maintainer of a large project that @tlambert03 recently contributed an equally stunning PR to, I am now on Team @tlambert03. I've learned to accept his fearsome prowess.

@tlambert03
Copy link
Copy Markdown
Contributor Author

You think that until you actually use .pyi files. Then the inevitable litany of desynchronization woes, DRY violations, and caching quandaries plague your house with a vicious cycle of useless dev churn.

lol, you crack me up. Im curious what you think about this crazy pattern: scientific-python/lazy-loader#10

It seems to solve the DRY and desynch woes. What are the caching quandaries to look out for here? If someone changes the pyi in a dev environment, should we expect any stale/missing runtime imports?

@alexdesiqueira
Copy link
Copy Markdown
Member

Thank you @tlambert03!
@grlee77 @jni should we wait skimage2 to merge this? If yes, please tag it. If not let me know and I'll push the button 😉 Thanks!

@stefanv
Copy link
Copy Markdown
Member

stefanv commented Jan 9, 2023

This was fixed by #6577 via the pyi-loading mechanism implemented by @tlambert03 (thanks, Talley!).

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.

7 participants