GrahamDumpleton / wrapt

A Python module for decorators, wrappers and monkey patching.
BSD 2-Clause "Simplified" License
2.03k stars 231 forks source link

post_import_hooks break pkg_resources in wrapt 1.14 #221

Closed eltoder closed 1 year ago

eltoder commented 1 year ago

The simplest way to retrocede this is:

import wrapt
wrapt.register_post_import_hook(lambda m: print('loaded', m), 'asyncio')
import asyncio, pkg_resources
print(pkg_resources.get_provider('asyncio').has_resource('events.py'))

This fails with the following traceback:

Traceback (most recent call last):
  File "t.py", line 4, in <module>
    print(pkg_resources.get_provider('asyncio').has_resource('events.py'))
  File "/home/elt/.local/lib/python3.6/site-packages/pkg_resources/__init__.py", line 1404, in has_resource
    return self._has(self._fn(self.module_path, resource_name))
  File "/home/elt/.local/lib/python3.6/site-packages/pkg_resources/__init__.py", line 1457, in _has
    "Can't perform this operation for unregistered loader type"
NotImplementedError: Can't perform this operation for unregistered loader type

The reason is that pkg_resources.get_provider() could not find a usable provider, because the __loader__ on the asyncio module was left as _ImportHookChainedLoader instead of being set to the standard SourceFileLoader. Leaving wrapt's internal loader is leaking implementation details. This did not happen previously because _ImportHookChainedLoader implemented load_module by calling to the original loader. The original loader set __loader__ to itself. Now that _ImportHookChainedLoader implements create_module, the import machinery sets __loader__ to the wrapt's loader.