-
-
Notifications
You must be signed in to change notification settings - Fork 32k
gh-123930: Better error for "from imports" when script shadows module #123929
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
🤖 New build scheduled with the buildbot fleet by @hauntsaninja for commit 53e6931 🤖 If you want to schedule another build, you need to add the 🔨 test-with-refleak-buildbots label again. |
I think this is a good time to add |
I am not sure I completely follow the one about "third party". It seems that you want to trigger this when a module it's using its own name and is not part of the standard library module names, right? But this message can be confusing (the part mentioning "third party") because this can be module foo importing by mistake itself where there is no "third party" intent involved. I would suggest to point out what's happening (the module its importing from itself) more than doing an educated guess of the original intent. |
Yeah, similar to the PR that's in 3.13 #113769, the error is triggered when a) module is still initialising itself, b) attribute access or from import fails because name doesn't exist, c) the module is on I'm little worried, users who run into this are typically really new to Python, may barely know what a module is, so it's nice for something to say "consider renaming". (This confusion is common with third party packages not just stdlib: here's a report from a library I help maintain , here's a discuss thread that was active when I made original PR , many more examples) I'm happy to do whatever, but maybe you can advise me a little bit… Option 1:
The existing message already says "most likely due to a circular import", which is similar to your suggestion. So if I understand correctly, we basically just revert this to existing logic. One question I have is about the exact same logic that was landed in #113769 — I can revert that as well, and if so should it be backported to 3.13(.1) and removed from What's New? Option 2: Maybe there are ways we can reduce false positives. The place I think false positives are most likely are from this suggestion I took: #113769 (comment) / cpython/Objects/moduleobject.c Lines 891 to 898 in aa2e667
If we remove that, I think there are fewer false positives. At the time of the original PR, I tried to find single file scripts that import themselves and it's quite rare. The two examples I was able to find in codebases I contribute to was: 1) some trick to ensure cloudpickle pickled things in main module via reference not value, 2) some awful thing someone was doing in a setup.py. Both cases felt like users knew a lot about what they were doing and if they did a typo getting "consider renaming if you meant to import third party module" wouldn't scare them too much. I'm sure there are other use cases, but if they're niche or advanced I'm quite happy trading in favour of new users who make torch.py / flask.py / requests.py Option 3: Pablo magic happens and you have some clever trick in mind :-)
Thanks for suggestion, maybe open a new issue for this? |
Right, but precisely for those users the concept of "first party" and "third party" will be equally (or potentially more) confusing.
It depends I think. "A circular import" means that there is a cycle in the import mechanism. Obviously the cycle can be of 1 element (which is the case) but thinking of the user group you mentioned previously that's going to sound more confusing that saying "the module it's importing itself" (if we are targetting exactly that case). In any case my suggestion is to remove or rephrase the concept of "third party" becase (1) we don't really know if is third party and (2) a good set of users that can benefit from this error message may find that more confusing that if we speak just in terms of general modules or packages.
I agree with you that importing a name from itself on pourpose it's rare enough that when someone it's doing it on pourpose the slighly confusing error message with a suggestion is not going to scare them away. We should focus as you suggest in the vast group of users that are not doing this knowingly :)
I have new clever tricks in mind for next time we hang out together but unfortunately not for solving this particular problem without introducing crazy potential problems 😅 In any case, this was just a suggestion and I don't feel very strongly against the current error message. If you want to keep it as is I will support it, so don't worry. I just want us to consider this particular challenge and making sure it did not pass without consideration :) |
Sorry, this got a little lost. I've changed the wording. I also plan on backporting this PR to Python 3.13. This is a little questionable, so let me know if you think it's a terrible idea. My reasoning is: the missing helpful message in "from imports" is basically a bug in the new 3.13 feature, Pablo's suggested change in wording could similarly be considered a bug, unifying the two code paths makes it easier to maintain |
The actual change looks good to me (and backporting sounds reasonable), but I picked up another bit of dubious wording from the original feature implementation. |
Thanks for the review! That wording went through a few iterations and this is the best one of them yet :-) |
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.
Some driveby nitpicks :-)
Thanks @hauntsaninja for the PR 🌮🎉.. I'm working now to backport this PR to: 3.13. |
Sorry, @hauntsaninja, I could not cleanly backport this to
|
GH-125937 is a backport of this pull request to the 3.13 branch. |
…module (python#123929) (cherry picked from commit 500f533)
@hauntsaninja Looks like this PR has broken the iOS buildbot; investigating now. |
{ | ||
PyObject *has_location = NULL; | ||
int rc = PyObject_GetOptionalAttr(spec, &_Py_ID(has_location), &has_location); | ||
if (rc <= 0) { | ||
return rc; | ||
} | ||
// If origin is not a location, or doesn't exist, or is not a str), we could consider falling | ||
// If origin is not a location, or doesn't exist, or is not a str, we could consider falling | ||
// back to module.__file__. But the cases in which module.__file__ is not __spec__.origin |
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 I think this may be the source of the problem. On iOS, module.__file__
isn't the same as __spec__.origin
- module.__file__
is the original source of the binary module (in lib-dynload
), in the sense of where Python thinks the library is installed; __spec__.origin
is the actual iOS framework location (in the Frameworks
folder).
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 interesting, I see the special importlib Loader. Thanks for fixing the test!
This is a follow up to #113769. Thanks to @hugovk and @henryiii for mentioning this.
With this PR, we get e.g. the following error message for shadowing stdlib:
and the following error message for shadowing third party:
This PR can mostly be reviewed commit by commit [*].
I try to make the code path in
ceval.c:_PyEval_ImportFrom
closely match the one inmoduleobject.c:_Py_module_getattro_impl
, so as to avoid consistency issues between the two and to make comparing of the logic easier.Note the main semantic difference as a result of the consistency changes is that we now use
__spec__.origin
instead of__file__
in the_PyEval_ImportFrom
code path.[*] there's one decref I missed in an intermediate commit