Skip to content

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 lains@riseup.net

https://bugs.python.org/issue45379

@ericsnowcurrently
Copy link
Member

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 commented Oct 23, 2021

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

@FFY00
Copy link
Member Author

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

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

Choose a reason for hiding this comment

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

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.

@bedevere-bot
Copy link

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 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 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 October 31, 2021 01:39
```
>>> 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 <lains@riseup.net>
Co-authored-by: Eric Snow <ericsnowcurrently@gmail.com>
Signed-off-by: Filipe Laíns <lains@riseup.net>
Signed-off-by: Filipe Laíns <lains@riseup.net>
Signed-off-by: Filipe Laíns <lains@riseup.net>
Signed-off-by: Filipe Laíns <lains@riseup.net>
Signed-off-by: Filipe Laíns <lains@riseup.net>
Signed-off-by: Filipe Laíns <lains@riseup.net>
Signed-off-by: Filipe Laíns <lains@riseup.net>
Copy link
Member

@ericsnowcurrently ericsnowcurrently left a comment

Choose a reason for hiding this comment

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

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

}
if (err != NULL) {
PyObject *type, *value, *traceback;
PyErr_Fetch(&type, &value, &traceback);
Copy link
Member

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)

Comment on lines +2049 to +2050
{"test_import_frozen_module_null", test_import_frozen_module_null},
{"test_import_frozen_module_none", test_import_frozen_module_none},
Copy link
Member

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

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.

try:
info = _call_with_frames_removed(_imp.find_frozen, fullname)
except ImportError:
return None
Copy link
Member

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.

}
else if (status == FROZEN_BAD_NAME) {
Py_RETURN_NONE;
set_frozen_error(status, name);
Copy link
Member

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 January 12, 2022 19:15
@FFY00
Copy link
Member Author

FFY00 commented Jan 12, 2022

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

@ericsnowcurrently
Copy link
Member

Sorry for the long delay!

@iritkatriel
Copy link
Member

I can't reproduce on a Mac. Is this fixed now?

The example from #29180 (comment):

>>> from ctypes import py_object, pythonapi as capi
>>> capi.PyImport_ImportFrozenModuleObject(py_object(None))
0
>>> quit()

The example from #29180 (comment):

iritkatriel@Irits-MBP cpython % ./python.exe -c "__import__('\\udcff')" 
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ModuleNotFoundError: No module named '\udcff'

@brettcannon brettcannon self-requested a review April 12, 2023 18:31
@brettcannon
Copy link
Member

@FFY00 there's some merge conflicts now.

@brettcannon brettcannon removed their request for review May 25, 2023 18:46
@brettcannon
Copy link
Member

Removing my review request until the merge conflicts are fixed to at least attempt to keep my review queue "small". 😉

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.

7 participants