-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
[WIP] Support non-type dependencies #9374
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
Conversation
|
||
def iou( | ||
dt: _NDArrayUInt32 | list[float] | list[_EncodedRLE], | ||
gt: _NDArrayUInt32 | list[float] | list[_EncodedRLE], | ||
pyiscrowd: list[int] | _NDArrayUInt8, | ||
) -> list[Any] | _NDArrayFloat64: ... | ||
def merge(rleObjs: list[_EncodedRLE], intersect: int = ...) -> _EncodedRLE: ... | ||
|
||
# ignore an "overlapping overloads" error due to _NDArrayInt32 being an alias for `Incomplete` for now | ||
@overload | ||
def frPyObjects(pyobj: _NDArrayUInt32 | list[list[int]] | list[_EncodedRLE], h: int, w: int) -> list[_EncodedRLE]: ... # type: ignore[misc] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to actually remove the outdated type: ignore
if we're deleting the comment about why the type ignore
used to be needed 😄
def frPyObjects(pyobj: _NDArrayUInt32 | list[list[int]] | list[_EncodedRLE], h: int, w: int) -> list[_EncodedRLE]: ... # type: ignore[misc] | |
def frPyObjects(pyobj: _NDArrayUInt32 | list[list[int]] | list[_EncodedRLE], h: int, w: int) -> list[_EncodedRLE]: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It becomes a different overload error, stubs/pycocotools/pycocotools/mask.pyi:21: error: Overloaded function signatures 1 and 2 overlap with incompatible return types [misc]
See: https://github.com/Avasam/typeshed/actions/runs/3717387421/jobs/6304685925#step:6:114
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, in which case maybe we should keep the comment, but change it to "mypy complains the overloads overlap, but it still works" or something
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sidenote, since pyright 1.1.283 it's possible to split type: ignore
from pyright: ignore
(and have them both work on the same line). So we could watch for obsolete type: ignore comments. I have an upcoming PR for that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Though remember that mypy's --warn-unused-ignores
option doesn't work properly inside typeshed because of the unique special casing mypy has for "typeshed directories"
stubs/pyinstaller/METADATA.toml
Outdated
@@ -1,5 +1,6 @@ | |||
version = "5.7.*" | |||
requires = ["types-setuptools"] | |||
external_requires = ["modulegraph"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now, for security reasons, any external dependencies will fail the stub uploader tests unless they're explicitly included in this whitelist here: https://github.com/typeshed-internal/stub_uploader/blob/bf783eb638a8aed4e1d0ba1143e3f03f399794a0/stub_uploader/metadata.py#L166
It's not ideal and we can think about ways to improve this after we've got the problems on the typeshed side up and running (plus in the medium term, we can just make that list longer) — but for now, we can't add modulegraph
as a dependency, as it's not on that list
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good to keep in mind! Eventually this PR will split-off stub changes (right now they're only included to test). So it shouldn't be an immediate issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having said that, it's interesting that the stub-uploader tests typeshed runs in CI still seem to be passing. Not sure whether that means I'm mistaken or whether it means the tests are buggy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
stub_uploader only looks at requires
field. I think people disliked the suggestion to split the field up, so I kept it the same (there were also some subtleties around what and when to trust, see #8312 (comment) )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hauntsaninja Ah yes, that's something I meant to ask as well and slipped my mind: should it be the same or different field. And if the same, what's the logic for what needs to be installed? Probably the same as stubtest (a link to relevant code there would be appreciated). I already abstracted the logic to get non-type dependencies anyway.
Thanks so much for taking this on! I'll take a proper look tomorrow. |
This comment has been minimized.
This comment has been minimized.
# Ignoring "module is installed, but missing library stubs or py.typed marker" | ||
# because external dependencies may not be marked as typed. | ||
# pyright already flags non-typed external dependencies as warnings. | ||
# TODO: This mutes ALL import errors, can we narrow it down? | ||
"--ignore-missing-imports", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure there's much use for a stubs package having a dependency on a non-types library that doesn't have inline types. Stubs packages should keep dependencies to an absolute minimum, so that they pose minimal security risks and are easy to install/bootstrap. Since the most important purpose of a stubs package is to help with type checking, I'd rather we didn't allow adding dependencies that don't have inline types.
I also think adding this flag may be obscuring the fact that mypy_test.py
is probably ignoring all packages installed in site-packages
due to the --no-site-packages
flag on line 251!
# Ignoring "module is installed, but missing library stubs or py.typed marker" | |
# because external dependencies may not be marked as typed. | |
# pyright already flags non-typed external dependencies as warnings. | |
# TODO: This mutes ALL import errors, can we narrow it down? | |
"--ignore-missing-imports", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason I was including non- py.typed
packages, is because, if I'm not mistaken, pyright will still get the proper type (let's say a class). Although mypy does import everything as Any
in those cases. I only found two cases of this:
comtypes
: used byD3DShot
, so arguably already on its way out. If another stub ever needs it, maybe consider also typingcomtypes
modulegraph
: used byPyInstaller
. HoweverPyInstaller
currently vendorsmodulegraph
, so we can just stub the necessary vendored files (which is already what I did).
What I was thinking on doing, is to add
[mypy-non_type_dependency]
ignore-missing-imports=true
to a config file (like mypy_test
does) so that only those specific non-type imports are muted.
That being said... this counter-argument is pretty weak and a niche/edge case probably not worth supporting and adding complexity. So I'm with you on this one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good point, actually, pyright has the useLibraryCodeForTypes
option. I don't think mypy has anything similar, though, so I still think we should be pretty wary about adding "non-py.typed
non-types dependencies". If we do decide to allow them (which I think would probably require a bit more discussion first, but I'm open to it if it'll help pyright users 🙂), I think it might actually still be better to have to use a type: ignore[import]
each time the library is imported. That way, there's a very visual reminder that this library is "a bit different" and should be used with caution inside typeshed.
ProblemSo, in an ideal world, each stubs package should be tested in a reasonably "isolated" environment. The approach you have currently could lead to false negatives -- for example, Currently we solve this in Potential solution (1)The "principled" way of fixing this issue would be to use an approach similar to Potential solution (1b)If we have to, we could perhaps take a similar approach to the one we use in Potential solution (2)We should maybe try doing the principled thing here before assuming that it will be too slow. But if it is too slow, there's another approach we could try. We could install all requirements for all stubs globally, like you have now, but also run a separate script that detects if a stubs package tries to import something that isn't declared as a dependency in the package's Potential solution (3)We could continue using Note that I'm using |
This is the code typeshed/tests/stubtest_third_party.py Lines 44 to 84 in 62accb3
|
I've just opened #9382 for (Theoretically, the same concerns about false negatives apply to pyright/pytype as they do to |
Totally on the same page as you here. Keeping pyright and pytype relatively simple. The requirements from the mypy test also causes the necessary isolated tests. I'll wait for #9382 (which looks much better than what I would've come up with) and re-use the logic to get external deps. And clean-up. Side question. I know stub uploader uses an allow-list and checks dependencies so that a malicious dependency doesn't slip as included with the stub. But do the stubs actually need to forcibly install non-type dependencies in the first place? Let's say you wanna use some untyped image manipulation library (pyscreeze for example). So you install Plus if you didn't have opencv2 installed (resulting in Unknowns/Anys), you couldn't have used the cv2-based pyscreeze methods anyway. I could see an issue with complex union types that can't be represented with overloads. Where the Unknown/Any would propagate. In other words, I'm saying there might be a difference between "non-type optional dependency referenced just for type if needed, in typeshed" and "non-type dependency that has to be installed, and validated, to avoid bad Unknown/Any propagation". (hopefully that made sense, I'm not sure how to best put this thought to paper) |
Hmm, that's quite an interesting scenario, and one I'm not sure we've fully considered yet! I think we should definitely think hard and consider it carefully before adding non-types dependencies to stubs packages — and that's especially true if it's a huge package like If there are occasions when we really don't want to pull in a large dependency into a stubs package, protocols may sometimes provide another solution. Instead of saying "this function requires a numpy class X", we could instead say "this function requires an object that conforms to this private-to-the-stub protocol that describes the shape of the numpy class X". It might not be pretty... but when has Python- typing ever been pretty? 😆 |
…l-non-type-dependencies
This comment has been minimized.
This comment has been minimized.
FYI, I'll happily merge this now if you revise the PR so that it's just the changes to how we run pyright in CI :) (I'd prefer to leave pytype to a later PR, so that we can ask the pytype maintainers if they can think of a better way of running pytype in CI before we make any changes there.) |
I can do that in a different branch/PR. There's plenty of things here I'd like to keep for testing/PoC |
According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉 |
fi | ||
|
||
$PYTHON_EXECUTABLE tests/stubtest_third_party.py --specified-stubs-only --num-shards 4 --shard-index ${{ matrix.shard-index }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AlexWaygood is this code de-duplication worth making a PR for? (adds a line, but makes the command to run have a single source of truth)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, seems like a nice improvement :)
I'd vote for this control flow, though:
if [ "${{ runner.os }}" = "Linux" ]; then
PYTHON_EXECUTABLE="xvfb-run python"
else
PYTHON_EXECUTABLE="python"
fi
rather than
PYTHON_EXECUTABLE="python"
if [ "${{ runner.os }}" = "Linux" ]; then
PYTHON_EXECUTABLE="xvfb-run python"
fi
I find the former much more readable.
I think most of what I wanted out of this PR have been split-off and done. So I'll close this now (further tests willl probably be done in my fork as to not ping or use the CI uselessly) |
Not complete, I'm using this PR to share code-change and look for feedback. I know I still have commented code here and there. I'll also split it up in smaller PRs once ready, for now having the stub changes are useful.
Basically let's get it working, then I'll get it pretty and ask for a proper review.
ignore_missing_imports=true
to a temp config file to ignoremodule is installed, but missing library stubs or py.typed marker
on dependencies listed inexternal_requires
Actual feature implementation PRs tracked here: #5768 (comment)