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
SystemError on importing module from unloaded package #75059
Comments
It is possible to get SystemError on import (see attached archive). $ ./python -c 'import package.module1'
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/home/serhiy/py/cpython/package/module1.py", line 3, in <module>
from . import module2
SystemError: Parent module 'package' not loaded, cannot perform relative import SystemError means a programming error in interpreter core or extension. It is comparable to an assert in C code, but without immediate crashing. Since this situation can be provoked by user code, it should be replaced with other exception (KeyError, RuntimeError, ImportError, etc). |
I don't think we're that strict with SystemError - once folks are messing about with deleting things from the sys module, they *are* writing their own system level code, and may end up provoking SystemError if they corrupt the interpreter state in the process. |
To summarise what the attached source archive is doing, module1.py is essentially: import sys
del sys.modules(__package__)
from . import module2 So the only way to trigger this is by corrupting the import state, which seems like an appropriate use of SystemError to me. |
I don't know other way to provoke SystemError by Python code. Always if SystemError was leaked this considered a bug and was fixed. When unload package you need to remove its name and names of its submodules from sys.modules. This is a common case. If the submodule is imported at the same time in other thread you can get SystemError (randomly, with very small probability). I think that if the error can't be avoided, SystemError is a wrong exception for this case. |
If there are intermittent concurrent problems associated with this behaviour, I think that may be a sign that the current management of the per-module import locks is inadequate, since it isn't adequately accounting for direct manipulation of sys.modules in user code. Fully resolving that would probably mean ensuring that:
If we did that, then we should consistently get one of the following two orders:
By contrast, at the moment there's a window where the following can happen:
That said, formulating the problem that way does suggest another potential resolution: in this scenario, we could treat "from . import module2" *exactly* the same way we would handle "import package.module2", which would be to just import "package" again, rather than complaining that it "should" already be imported. In addition to being vastly simpler to implement, that approach would have the virtue of also fixing the "del sys.modules(package)" case, not just the potential race condition. |
I concur with Serhiy: SystemError is a smell that C code isn't taking appropriate precautions before dealing with user code or data. |
So this is very old semantics from the Python 2 days. I think Nick's idea of transforming the import to |
OK, so at least for 3.7, we'll replace the SystemError with a recursive import of the missing parent package, just as we'd expect to see with an absolute import. I'm classing this as "Won't fix" for the native import system in 2.7 - folks wanting the fix there will need to switch to using importlib2 (assuming that gets updated to include the fix at some point). That leaves 3.5 and 3.6, and I'd suggest we defer making a decision about whether or not to backport the fix to the maintenance branches until after we see how complex the patch for 3.7 ends up being. |
It is easy to replace the SystemError with a recursive import of the missing parent package. It is enough to remove raising the SystemError. The other effect of this change is that relative import from the top-level module now raises ImportError "attempted relative import with no known parent package" instead of SystemError "Parent module '' not loaded, cannot perform relative import". |
What about other branches? Should we backport this change to them? I think that even if not backport this change we should change SystemError to more appropriate exception. |
The fix is unintrusive enough that I'm +1 for also fixing it in 3.6 and 3.5. Trying to fix it in 2.7 would likely be more trouble than it's worth, but I also wouldn't be opposed to fixing it there if you or anyone else wanted to do it. |
When backported bpo-30876 to 3.5 I found that the lines
don't exist in 3.5. They where added in bpo-26367, but only in the 3.6 branch. The original example in bpo-26367 still returns a module with a wrong name in 3.5. Why bpo-26367 changes were applied to 3.5 only partially? I afraid that the absence of this check may interfere with removing SystemError. |
Ah, this is because bpo-18018 was fixed only in 3.6. bpo-18018 is similar to this issue, it is about confusing SystemError. As a side effect it solved an example from bpo-26367 in C builtin __import__. I think that both raising SystemError and returning a module with a wrong name are bugs that worth to be fixed in 3.5. |
When running tests from installed location, test_import now fails on master, 3.6, and 3.5 (including v3.5.4rc1) with: ====================================================================== Traceback (most recent call last):
File "/Users/nad/Projects/PyDev/active/dev/3x/root/uxd/lib/python3.7/test/test_import/__init__.py", line 704, in test_import_from_unloaded_package
import package2.submodule1
ModuleNotFoundError: No module named 'package2' |
test_concurrency (test.test_import.ImportTests) seems to fail, too, with a similar error:
Apparently, this happens because the LIBSUBDIRS variable in Makefile doesn't include all the subdirectories of test/test_import/data. Adding the following two lines fixed the issue for me: test/test_import/data/package \ Should I submit a patch? |
Please ignore my last message, I didn't notice the existing pull request... Sorry. |
It seems this change broke the 3.5 build on AMD FreeBSD: http://buildbot.python.org/all/builders/AMD64%20FreeBSD%20CURRENT%20Debug%203.5 On the 3.5 branch, the previous revision (19b2890) built and tested successfully on July 26. But once this revision (f9fbed1) was checked in on July 27 the build started failing. I haven't included this fix in 3.5.4. Please fix the buildbot and it'll go into 3.5.5. |
This is a FreeBSD bug. It isn't related to this change. See bpo-31044 for details. |
Okay, that's good news. That means I don't have to accept a fix for it in the 3.5 branch *after* 3.5 transitions to security-fixes-only mode tomorrow ;-) |
Tagging Petr Viktorin and Matthias Klose, as this may impact distro builds of 3.5.4 that run the tests over the installed version rather than directly from the VCS checkout. It would be slightly helpful to not have to carry the "fix Python's self-tests" patch indefinitely, but it's also not likely to be a major cause of patch conflicts with security updates, so it shouldn't matter much either way. |
If the change has been checked into the 3.5 branch, it'll go out with 3.5.5. TBH we should probably stop accepting bug fixes into a branch when it hits rc1 of "last release to accept bugfixes" (e.g. 3.5.4rc1). There are two or three bugfixes in the 3.5 branch that I cherry-picked around when I made 3.5.4 final. I'm going to let them simply ship in 3.5.5 (eventually), even though 3.5.5 shouldn't have any "bug fixes". Maybe we'll be crisper when it comes to 3.6.xrc1 etc. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: