The Wayback Machine - https://web.archive.org/web/20220519150505/https://github.com/python/cpython/pull/29180
Skip to content
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

bpo-45379: raise import error on FROZEN_BAD_NAME #29180

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

FFY00
Copy link
Member

@FFY00 FFY00 commented Oct 22, 2021

>>> from ctypes import py_object, pythonapi as capi
>>> capi.PyImport_ImportFrozenModuleObject(py_object(None))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: Invalid frozen object name (None)

Signed-off-by: Filipe Laíns [email protected]

https://bugs.python.org/issue45379

@ericsnowcurrently
Copy link
Member

@ericsnowcurrently ericsnowcurrently commented Oct 23, 2021

Note that the existing behavior has been around for a long time. So we need to be sure this change won't break people.

@FFY00
Copy link
Member Author

@FFY00 FFY00 commented Oct 23, 2021

Yes, I understand. How do you propose doing that?

@FFY00
Copy link
Member Author

@FFY00 FFY00 commented Oct 23, 2021

I did some detective work, it seems almost nobody uses this function, which is not really unexpected 😅. I think the chances of someone using this and passing a None or a null pointer to it are pretty low. Is there something else you'd recommend to check? I honestly have no clue of what I could do other than this.

@ericsnowcurrently
Copy link
Member

@ericsnowcurrently ericsnowcurrently commented Oct 30, 2021

FWIW, I agree that this change in behavior is extremely unlikely to break anyone.

The users of PyImport_ImportFrozenModuleObject() (and PyImport_ImportFrozenModule() and the legacy imp.init_frozen()) are undoubtedly few. Furthermore, passing a "bad" name is a particularly uncommon case. I'd expect passing in NULL or None to be handled before the call. Names that can't be encoded to UTF-8 are probably also rare. Furthermore, if errors are silenced then they are most certainly treated the same as "not found". So the overall likelihood of hitting the error is really low.

As to the severity of hitting the error, it will be clear what the problem is and how to fix it. Callers would have to add code to treat the exception as "not found" if they prefer the existing behavior but I expect they would rather have the exception (to distinguish from "not found").

I suppose it might be worth adding a paragraph to the "Porting to Python 3.11" section of Doc/whatsnew/3.11.rst, just in case.

Copy link
Member

@ericsnowcurrently ericsnowcurrently left a comment

Sorry for the delay, @FFY00! Thanks for working on this.

_imp_find_frozen_impl() should also be updated in the same way. When you do that, FrozenImporter.find_spec() needs to be updated to return None if it hits that ImportError. Otherwise it will cause import to fail instead of trying the next finder on sys.metapath. It would probably make sense to emit a warning in that case. (@brettcannon, what do you think?)

In find_frozen(), we call PyErr_Clear() when emitting FROZEN_BAD_NAME. (There's a long comment there about why.) Now that we're always raising ImportError, it would be helpful to users if we preserve (chain) the error here instead of clearing it.

Also, please add a test for this exception case, both for PyImport_ImportFrozenModuleObject() (via _test_embed) and _imp.find_frozen(). Clearly this case isn't tested or your change would have caused the existing test to fail. 🙂

Other than that I just have a couple comments about explicitly noting the change in behavior.

Python/import.c Show resolved Hide resolved
@bedevere-bot
Copy link

@bedevere-bot bedevere-bot commented Oct 30, 2021

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phrase I have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

And if you don't make the requested changes, you will be put in the comfy chair!

@FFY00
Copy link
Member Author

@FFY00 FFY00 commented Oct 30, 2021

_imp.find_frozen already validates the name. This is done by argument clinic.

_imp_find_frozen(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)

Python 3.11.0a1+ (heads/[bpo-45379](https://bugs.python.org/issue45379)-bad-name:e225f44088, Oct 30 2021, 19:47:09) [GCC 11.1.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import _imp
>>> _imp.find_frozen(None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: find_frozen() argument 1 must be str, not None

Considering this, does FrozenImporter.find_spec still need to be updated? I assume not, right? I mean, I guess we could update it, but that is not tied to any behavior change by this PR, because _imp.find_frozen stays exactly the same.

@FFY00
Copy link
Member Author

@FFY00 FFY00 commented Oct 31, 2021

Nevermind, this can indeed be triggered if we don't get valid unicode.

$ ./python -c "__import__('\\udcff')"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ImportError: Invalid frozen object name ('\udcff')

FFY00 and others added 8 commits Oct 31, 2021
```
>>> from ctypes import py_object, pythonapi as capi
>>> capi.PyImport_ImportFrozenModuleObject(py_object(None))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: Invalid frozen object name (None)
```

Signed-off-by: Filipe Laíns <[email protected]>
Co-authored-by: Eric Snow <[email protected]>
Signed-off-by: Filipe Laíns <[email protected]>
Signed-off-by: Filipe Laíns <[email protected]>
Signed-off-by: Filipe Laíns <[email protected]>
Signed-off-by: Filipe Laíns <[email protected]>
Signed-off-by: Filipe Laíns <[email protected]>
Signed-off-by: Filipe Laíns <[email protected]>
Signed-off-by: Filipe Laíns <[email protected]>
Copy link
Member

@ericsnowcurrently ericsnowcurrently left a comment

Thanks again for working on this. It mostly LGTM. There are just a few minor things to address before we merge this.

@@ -1202,11 +1204,13 @@ set_frozen_error(frozen_status status, PyObject *modname)
Py_UNREACHABLE();
}
if (err != NULL) {
PyObject *type, *value, *traceback;
PyErr_Fetch(&type, &value, &traceback);
Copy link
Member

@ericsnowcurrently ericsnowcurrently Jan 12, 2022

Choose a reason for hiding this comment

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

We want to chain the exception. So do one of the following:

  • drop this line (AFAICS, PyErr_SetImportError() chains the exception if one is set)
  • call _PyErr_ChainExceptions(type, value, traceback) after the PyErr_SetImportError() call (line 1213)

{"test_import_frozen_module_null", test_import_frozen_module_null},
{"test_import_frozen_module_none", test_import_frozen_module_none},
Copy link
Member

@ericsnowcurrently ericsnowcurrently Jan 12, 2022

Choose a reason for hiding this comment

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

There need to be corresponding tests in Lib/test/test_embed.py.

Copy link
Member

@ericsnowcurrently ericsnowcurrently Jan 12, 2022

Choose a reason for hiding this comment

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

Actually, wouldn't the relevant code be more appropriate in Modules/_testcapimodule.c and the tests in Lib/test/test_capi.py (or even Lib/test/test_import/init.py)? As a bonus, you wouldn't need to do so much in the C code that way.

Programs/_testembed.c Show resolved Hide resolved
try:
info = _call_with_frames_removed(_imp.find_frozen, fullname)
except ImportError:
return None
Copy link
Member

@ericsnowcurrently ericsnowcurrently Jan 12, 2022

Choose a reason for hiding this comment

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

Users may find it helpful if we also emitted a warning here.

@@ -2146,7 +2147,8 @@ _imp_find_frozen_impl(PyObject *module, PyObject *name, int withdata)
Py_RETURN_NONE;
}
else if (status == FROZEN_BAD_NAME) {
Py_RETURN_NONE;
set_frozen_error(status, name);
Copy link
Member

@ericsnowcurrently ericsnowcurrently Jan 12, 2022

Choose a reason for hiding this comment

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

This should have a test in Lib/test/test_imp.py. We should also have one in one in Lib/test/test_importlib/frozen/test_finder.py, to verify FrozenImporter.find_spec() returns None in that case.

@ericsnowcurrently ericsnowcurrently removed the request for review from encukou Jan 12, 2022
@FFY00
Copy link
Member Author

@FFY00 FFY00 commented Jan 12, 2022

Thank you for reviewing, I will go over this and rebase later.

@ericsnowcurrently
Copy link
Member

@ericsnowcurrently ericsnowcurrently commented Jan 12, 2022

Sorry for the long delay!

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

Successfully merging this pull request may close these issues.

None yet

4 participants