We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[20.0.8] import hook assumes existence of `exec_module` but the api is optional according to PEP 302/451
A contained example is https://github.com/asottile/pymonkey

the import machinery in virtualenv 20.0.8 assumes all loaders have an `exec_module` attribute, however that's optional (and not part of PEP 302 at all)

The crash in my case looks like:

```
Traceback (most recent call last):
  File "<frozen importlib._bootstrap>", line 888, in _find_spec
AttributeError: 'PymonkeyImportHook' object has no attribute 'find_spec'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/tmp/pymonkey/testing/pkg1/patchingmod_main.py", line 8, in <module>
    exit(main())
  File "/tmp/pymonkey/pymonkey.py", line 270, in entry
    tuple(patches) + ('--', original_entry_point) + tuple(argv)
  File "/tmp/pymonkey/pymonkey.py", line 258, in main
    return entry.load()()
  File "/tmp/pymonkey/.tox/py36/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2445, in load
    return self.resolve()
  File "/tmp/pymonkey/.tox/py36/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2451, in resolve
    module = __import__(self.module_name, fromlist=['__name__'], level=0)
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 656, in _load_unlocked
  File "<frozen importlib._bootstrap>", line 626, in _load_backward_compatible
  File "/tmp/pymonkey/pymonkey.py", line 173, in load_module
    module = importmod(fullname)
  File "/tmp/pymonkey/pymonkey.py", line 94, in importmod
    return __import__(mod, fromlist=[str('__name__')], level=0)
  File "/tmp/pymonkey/testing/pkg2/targetmod.py", line 4, in <module>
    import setuptools  # Some weird import hook
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 656, in _load_unlocked
  File "<frozen importlib._bootstrap>", line 626, in _load_backward_compatible
  File "/tmp/pymonkey/pymonkey.py", line 173, in load_module
    module = importmod(fullname)
  File "/tmp/pymonkey/pymonkey.py", line 94, in importmod
    return __import__(mod, fromlist=[str('__name__')], level=0)
  File "/tmp/pymonkey/.tox/py36/lib/python3.6/site-packages/setuptools/__init__.py", line 5, in <module>
    import distutils.core
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 656, in _load_unlocked
  File "<frozen importlib._bootstrap>", line 626, in _load_backward_compatible
  File "/tmp/pymonkey/pymonkey.py", line 173, in load_module
    module = importmod(fullname)
  File "/tmp/pymonkey/pymonkey.py", line 94, in importmod
    return __import__(mod, fromlist=[str('__name__')], level=0)
  File "/usr/lib/python3.6/distutils/core.py", line 16, in <module>
    from distutils.dist import Distribution
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 951, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 890, in _find_spec
  File "<frozen importlib._bootstrap>", line 864, in _find_spec_legacy
  File "/tmp/pymonkey/pymonkey.py", line 162, in find_module
    elif self._module_exists(fullname, path):
  File "/tmp/pymonkey/pymonkey.py", line 122, in _module_exists
    getattr(entry, 'find_spec', _noop)(module, path) or
  File "/tmp/pymonkey/.tox/py36/lib/python3.6/site-packages/_virtualenv.py", line 58, in find_spec
    old = spec.loader.exec_module
AttributeError: 'PymonkeyImportHook' object has no attribute 'exec_module'
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/virtualenv/create/via_global_ref/_virtualenv.py`
Content:
```
1 """Patches that are applied at runtime to the virtual environment"""
2 # -*- coding: utf-8 -*-
3 
4 import os
5 import sys
6 
7 VIRTUALENV_PATCH_FILE = os.path.join(__file__)
8 
9 
10 def patch_dist(dist):
11     """
12     Distutils allows user to configure some arguments via a configuration file:
13     https://docs.python.org/3/install/index.html#distutils-configuration-files
14 
15     Some of this arguments though don't make sense in context of the virtual environment files, let's fix them up.
16     """
17     # we cannot allow some install config as that would get packages installed outside of the virtual environment
18     old_parse_config_files = dist.Distribution.parse_config_files
19 
20     def parse_config_files(self, *args, **kwargs):
21         result = old_parse_config_files(self, *args, **kwargs)
22         install = self.get_option_dict("install")
23 
24         if "prefix" in install:  # the prefix governs where to install the libraries
25             install["prefix"] = VIRTUALENV_PATCH_FILE, os.path.abspath(sys.prefix)
26 
27         if "install_scripts" in install:  # the install_scripts governs where to generate console scripts
28             script_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "__SCRIPT_DIR__"))
29             install["install_scripts"] = VIRTUALENV_PATCH_FILE, script_path
30 
31         return result
32 
33     dist.Distribution.parse_config_files = parse_config_files
34 
35 
36 # Import hook that patches some modules to ignore configuration values that break package installation in case
37 # of virtual environments.
38 _DISTUTILS_PATCH = "distutils.dist", "setuptools.dist"
39 if sys.version_info > (3, 4):
40     # https://docs.python.org/3/library/importlib.html#setting-up-an-importer
41     from importlib.abc import MetaPathFinder
42     from importlib.util import find_spec
43     from threading import Lock
44 
45     class _Finder(MetaPathFinder):
46         """A meta path finder that allows patching the imported distutils modules"""
47 
48         fullname = None
49         lock = Lock()
50 
51         def find_spec(self, fullname, path, target=None):
52             if fullname in _DISTUTILS_PATCH and self.fullname is None:
53                 with self.lock:
54                     self.fullname = fullname
55                     try:
56                         spec = find_spec(fullname, path)
57                         if spec is not None:
58                             old = spec.loader.exec_module
59 
60                             def exec_module(module):
61                                 old(module)
62                                 patch_dist(module)
63 
64                             spec.loader.exec_module = exec_module
65                             return spec
66                     finally:
67                         self.fullname = None
68 
69     sys.meta_path.insert(0, _Finder())
70 else:
71     # https://www.python.org/dev/peps/pep-0302/
72     from imp import find_module
73     from pkgutil import ImpImporter, ImpLoader
74 
75     class _VirtualenvImporter(object, ImpImporter):
76         def __init__(self, path=None):
77             object.__init__(self)
78             ImpImporter.__init__(self, path)
79 
80         def find_module(self, fullname, path=None):
81             if fullname in _DISTUTILS_PATCH:
82                 try:
83                     return _VirtualenvLoader(fullname, *find_module(fullname.split(".")[-1], path))
84                 except ImportError:
85                     pass
86             return None
87 
88     class _VirtualenvLoader(object, ImpLoader):
89         def __init__(self, fullname, file, filename, etc):
90             object.__init__(self)
91             ImpLoader.__init__(self, fullname, file, filename, etc)
92 
93         def load_module(self, fullname):
94             module = super(_VirtualenvLoader, self).load_module(fullname)
95             patch_dist(module)
96             module.__loader__ = None  # distlib fallback
97             return module
98 
99     sys.meta_path.append(_VirtualenvImporter())
100
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/virtualenv/create/via_global_ref/_virtualenv.py b/src/virtualenv/create/via_global_ref/_virtualenv.py
--- a/src/virtualenv/create/via_global_ref/_virtualenv.py
+++ b/src/virtualenv/create/via_global_ref/_virtualenv.py
@@ -3,6 +3,7 @@
 
 import os
 import sys
+from copy import deepcopy
 
 VIRTUALENV_PATCH_FILE = os.path.join(__file__)
 
@@ -55,13 +56,24 @@
                     try:
                         spec = find_spec(fullname, path)
                         if spec is not None:
-                            old = spec.loader.exec_module
+                            # https://www.python.org/dev/peps/pep-0451/#how-loading-will-work
+                            spec.loader = deepcopy(spec.loader)  # loaders may be shared, create new that also patches
+                            func_name = "exec_module" if hasattr(spec.loader, "exec_module") else "load_module"
+                            if func_name == "exec_module":  # new API
 
-                            def exec_module(module):
-                                old(module)
-                                patch_dist(module)
+                                def patch_module_load(module):
+                                    old(module)
+                                    patch_dist(module)
 
-                            spec.loader.exec_module = exec_module
+                            else:  # legacy API
+
+                                def patch_module_load(name):
+                                    module = old(name)
+                                    patch_dist(module)
+                                    return module
+
+                            old = getattr(spec.loader, func_name)
+                            setattr(spec.loader, func_name, patch_module_load)
                             return spec
                     finally:
                         self.fullname = None

{"golden_diff": "diff --git a/src/virtualenv/create/via_global_ref/_virtualenv.py b/src/virtualenv/create/via_global_ref/_virtualenv.py\n--- a/src/virtualenv/create/via_global_ref/_virtualenv.py\n+++ b/src/virtualenv/create/via_global_ref/_virtualenv.py\n@@ -3,6 +3,7 @@\n \n import os\n import sys\n+from copy import deepcopy\n \n VIRTUALENV_PATCH_FILE = os.path.join(__file__)\n \n@@ -55,13 +56,24 @@\n                     try:\n                         spec = find_spec(fullname, path)\n                         if spec is not None:\n-                            old = spec.loader.exec_module\n+                            # https://www.python.org/dev/peps/pep-0451/#how-loading-will-work\n+                            spec.loader = deepcopy(spec.loader)  # loaders may be shared, create new that also patches\n+                            func_name = \"exec_module\" if hasattr(spec.loader, \"exec_module\") else \"load_module\"\n+                            if func_name == \"exec_module\":  # new API\n \n-                            def exec_module(module):\n-                                old(module)\n-                                patch_dist(module)\n+                                def patch_module_load(module):\n+                                    old(module)\n+                                    patch_dist(module)\n \n-                            spec.loader.exec_module = exec_module\n+                            else:  # legacy API\n+\n+                                def patch_module_load(name):\n+                                    module = old(name)\n+                                    patch_dist(module)\n+                                    return module\n+\n+                            old = getattr(spec.loader, func_name)\n+                            setattr(spec.loader, func_name, patch_module_load)\n                             return spec\n                     finally:\n                         self.fullname = None\n", "issue": "[20.0.8] import hook assumes existence of `exec_module` but the api is optional according to PEP 302/451\nA contained example is https://github.com/asottile/pymonkey\r\n\r\nthe import machinery in virtualenv 20.0.8 assumes all loaders have an `exec_module` attribute, however that's optional (and not part of PEP 302 at all)\r\n\r\nThe crash in my case looks like:\r\n\r\n```\r\nTraceback (most recent call last):\r\n  File \"<frozen importlib._bootstrap>\", line 888, in _find_spec\r\nAttributeError: 'PymonkeyImportHook' object has no attribute 'find_spec'\r\n\r\nDuring handling of the above exception, another exception occurred:\r\n\r\nTraceback (most recent call last):\r\n  File \"/tmp/pymonkey/testing/pkg1/patchingmod_main.py\", line 8, in <module>\r\n    exit(main())\r\n  File \"/tmp/pymonkey/pymonkey.py\", line 270, in entry\r\n    tuple(patches) + ('--', original_entry_point) + tuple(argv)\r\n  File \"/tmp/pymonkey/pymonkey.py\", line 258, in main\r\n    return entry.load()()\r\n  File \"/tmp/pymonkey/.tox/py36/lib/python3.6/site-packages/pkg_resources/__init__.py\", line 2445, in load\r\n    return self.resolve()\r\n  File \"/tmp/pymonkey/.tox/py36/lib/python3.6/site-packages/pkg_resources/__init__.py\", line 2451, in resolve\r\n    module = __import__(self.module_name, fromlist=['__name__'], level=0)\r\n  File \"<frozen importlib._bootstrap>\", line 971, in _find_and_load\r\n  File \"<frozen importlib._bootstrap>\", line 955, in _find_and_load_unlocked\r\n  File \"<frozen importlib._bootstrap>\", line 656, in _load_unlocked\r\n  File \"<frozen importlib._bootstrap>\", line 626, in _load_backward_compatible\r\n  File \"/tmp/pymonkey/pymonkey.py\", line 173, in load_module\r\n    module = importmod(fullname)\r\n  File \"/tmp/pymonkey/pymonkey.py\", line 94, in importmod\r\n    return __import__(mod, fromlist=[str('__name__')], level=0)\r\n  File \"/tmp/pymonkey/testing/pkg2/targetmod.py\", line 4, in <module>\r\n    import setuptools  # Some weird import hook\r\n  File \"<frozen importlib._bootstrap>\", line 971, in _find_and_load\r\n  File \"<frozen importlib._bootstrap>\", line 955, in _find_and_load_unlocked\r\n  File \"<frozen importlib._bootstrap>\", line 656, in _load_unlocked\r\n  File \"<frozen importlib._bootstrap>\", line 626, in _load_backward_compatible\r\n  File \"/tmp/pymonkey/pymonkey.py\", line 173, in load_module\r\n    module = importmod(fullname)\r\n  File \"/tmp/pymonkey/pymonkey.py\", line 94, in importmod\r\n    return __import__(mod, fromlist=[str('__name__')], level=0)\r\n  File \"/tmp/pymonkey/.tox/py36/lib/python3.6/site-packages/setuptools/__init__.py\", line 5, in <module>\r\n    import distutils.core\r\n  File \"<frozen importlib._bootstrap>\", line 971, in _find_and_load\r\n  File \"<frozen importlib._bootstrap>\", line 955, in _find_and_load_unlocked\r\n  File \"<frozen importlib._bootstrap>\", line 656, in _load_unlocked\r\n  File \"<frozen importlib._bootstrap>\", line 626, in _load_backward_compatible\r\n  File \"/tmp/pymonkey/pymonkey.py\", line 173, in load_module\r\n    module = importmod(fullname)\r\n  File \"/tmp/pymonkey/pymonkey.py\", line 94, in importmod\r\n    return __import__(mod, fromlist=[str('__name__')], level=0)\r\n  File \"/usr/lib/python3.6/distutils/core.py\", line 16, in <module>\r\n    from distutils.dist import Distribution\r\n  File \"<frozen importlib._bootstrap>\", line 971, in _find_and_load\r\n  File \"<frozen importlib._bootstrap>\", line 951, in _find_and_load_unlocked\r\n  File \"<frozen importlib._bootstrap>\", line 890, in _find_spec\r\n  File \"<frozen importlib._bootstrap>\", line 864, in _find_spec_legacy\r\n  File \"/tmp/pymonkey/pymonkey.py\", line 162, in find_module\r\n    elif self._module_exists(fullname, path):\r\n  File \"/tmp/pymonkey/pymonkey.py\", line 122, in _module_exists\r\n    getattr(entry, 'find_spec', _noop)(module, path) or\r\n  File \"/tmp/pymonkey/.tox/py36/lib/python3.6/site-packages/_virtualenv.py\", line 58, in find_spec\r\n    old = spec.loader.exec_module\r\nAttributeError: 'PymonkeyImportHook' object has no attribute 'exec_module'\r\n```\n", "before_files": [{"content": "\"\"\"Patches that are applied at runtime to the virtual environment\"\"\"\n# -*- coding: utf-8 -*-\n\nimport os\nimport sys\n\nVIRTUALENV_PATCH_FILE = os.path.join(__file__)\n\n\ndef patch_dist(dist):\n    \"\"\"\n    Distutils allows user to configure some arguments via a configuration file:\n    https://docs.python.org/3/install/index.html#distutils-configuration-files\n\n    Some of this arguments though don't make sense in context of the virtual environment files, let's fix them up.\n    \"\"\"\n    # we cannot allow some install config as that would get packages installed outside of the virtual environment\n    old_parse_config_files = dist.Distribution.parse_config_files\n\n    def parse_config_files(self, *args, **kwargs):\n        result = old_parse_config_files(self, *args, **kwargs)\n        install = self.get_option_dict(\"install\")\n\n        if \"prefix\" in install:  # the prefix governs where to install the libraries\n            install[\"prefix\"] = VIRTUALENV_PATCH_FILE, os.path.abspath(sys.prefix)\n\n        if \"install_scripts\" in install:  # the install_scripts governs where to generate console scripts\n            script_path = os.path.abspath(os.path.join(os.path.dirname(__file__), \"__SCRIPT_DIR__\"))\n            install[\"install_scripts\"] = VIRTUALENV_PATCH_FILE, script_path\n\n        return result\n\n    dist.Distribution.parse_config_files = parse_config_files\n\n\n# Import hook that patches some modules to ignore configuration values that break package installation in case\n# of virtual environments.\n_DISTUTILS_PATCH = \"distutils.dist\", \"setuptools.dist\"\nif sys.version_info > (3, 4):\n    # https://docs.python.org/3/library/importlib.html#setting-up-an-importer\n    from importlib.abc import MetaPathFinder\n    from importlib.util import find_spec\n    from threading import Lock\n\n    class _Finder(MetaPathFinder):\n        \"\"\"A meta path finder that allows patching the imported distutils modules\"\"\"\n\n        fullname = None\n        lock = Lock()\n\n        def find_spec(self, fullname, path, target=None):\n            if fullname in _DISTUTILS_PATCH and self.fullname is None:\n                with self.lock:\n                    self.fullname = fullname\n                    try:\n                        spec = find_spec(fullname, path)\n                        if spec is not None:\n                            old = spec.loader.exec_module\n\n                            def exec_module(module):\n                                old(module)\n                                patch_dist(module)\n\n                            spec.loader.exec_module = exec_module\n                            return spec\n                    finally:\n                        self.fullname = None\n\n    sys.meta_path.insert(0, _Finder())\nelse:\n    # https://www.python.org/dev/peps/pep-0302/\n    from imp import find_module\n    from pkgutil import ImpImporter, ImpLoader\n\n    class _VirtualenvImporter(object, ImpImporter):\n        def __init__(self, path=None):\n            object.__init__(self)\n            ImpImporter.__init__(self, path)\n\n        def find_module(self, fullname, path=None):\n            if fullname in _DISTUTILS_PATCH:\n                try:\n                    return _VirtualenvLoader(fullname, *find_module(fullname.split(\".\")[-1], path))\n                except ImportError:\n                    pass\n            return None\n\n    class _VirtualenvLoader(object, ImpLoader):\n        def __init__(self, fullname, file, filename, etc):\n            object.__init__(self)\n            ImpLoader.__init__(self, fullname, file, filename, etc)\n\n        def load_module(self, fullname):\n            module = super(_VirtualenvLoader, self).load_module(fullname)\n            patch_dist(module)\n            module.__loader__ = None  # distlib fallback\n            return module\n\n    sys.meta_path.append(_VirtualenvImporter())\n", "path": "src/virtualenv/create/via_global_ref/_virtualenv.py"}], "after_files": [{"content": "\"\"\"Patches that are applied at runtime to the virtual environment\"\"\"\n# -*- coding: utf-8 -*-\n\nimport os\nimport sys\nfrom copy import deepcopy\n\nVIRTUALENV_PATCH_FILE = os.path.join(__file__)\n\n\ndef patch_dist(dist):\n    \"\"\"\n    Distutils allows user to configure some arguments via a configuration file:\n    https://docs.python.org/3/install/index.html#distutils-configuration-files\n\n    Some of this arguments though don't make sense in context of the virtual environment files, let's fix them up.\n    \"\"\"\n    # we cannot allow some install config as that would get packages installed outside of the virtual environment\n    old_parse_config_files = dist.Distribution.parse_config_files\n\n    def parse_config_files(self, *args, **kwargs):\n        result = old_parse_config_files(self, *args, **kwargs)\n        install = self.get_option_dict(\"install\")\n\n        if \"prefix\" in install:  # the prefix governs where to install the libraries\n            install[\"prefix\"] = VIRTUALENV_PATCH_FILE, os.path.abspath(sys.prefix)\n\n        if \"install_scripts\" in install:  # the install_scripts governs where to generate console scripts\n            script_path = os.path.abspath(os.path.join(os.path.dirname(__file__), \"__SCRIPT_DIR__\"))\n            install[\"install_scripts\"] = VIRTUALENV_PATCH_FILE, script_path\n\n        return result\n\n    dist.Distribution.parse_config_files = parse_config_files\n\n\n# Import hook that patches some modules to ignore configuration values that break package installation in case\n# of virtual environments.\n_DISTUTILS_PATCH = \"distutils.dist\", \"setuptools.dist\"\nif sys.version_info > (3, 4):\n    # https://docs.python.org/3/library/importlib.html#setting-up-an-importer\n    from importlib.abc import MetaPathFinder\n    from importlib.util import find_spec\n    from threading import Lock\n\n    class _Finder(MetaPathFinder):\n        \"\"\"A meta path finder that allows patching the imported distutils modules\"\"\"\n\n        fullname = None\n        lock = Lock()\n\n        def find_spec(self, fullname, path, target=None):\n            if fullname in _DISTUTILS_PATCH and self.fullname is None:\n                with self.lock:\n                    self.fullname = fullname\n                    try:\n                        spec = find_spec(fullname, path)\n                        if spec is not None:\n                            # https://www.python.org/dev/peps/pep-0451/#how-loading-will-work\n                            spec.loader = deepcopy(spec.loader)  # loaders may be shared, create new that also patches\n                            func_name = \"exec_module\" if hasattr(spec.loader, \"exec_module\") else \"load_module\"\n                            if func_name == \"exec_module\":  # new API\n\n                                def patch_module_load(module):\n                                    old(module)\n                                    patch_dist(module)\n\n                            else:  # legacy API\n\n                                def patch_module_load(name):\n                                    module = old(name)\n                                    patch_dist(module)\n                                    return module\n\n                            old = getattr(spec.loader, func_name)\n                            setattr(spec.loader, func_name, patch_module_load)\n                            return spec\n                    finally:\n                        self.fullname = None\n\n    sys.meta_path.insert(0, _Finder())\nelse:\n    # https://www.python.org/dev/peps/pep-0302/\n    from imp import find_module\n    from pkgutil import ImpImporter, ImpLoader\n\n    class _VirtualenvImporter(object, ImpImporter):\n        def __init__(self, path=None):\n            object.__init__(self)\n            ImpImporter.__init__(self, path)\n\n        def find_module(self, fullname, path=None):\n            if fullname in _DISTUTILS_PATCH:\n                try:\n                    return _VirtualenvLoader(fullname, *find_module(fullname.split(\".\")[-1], path))\n                except ImportError:\n                    pass\n            return None\n\n    class _VirtualenvLoader(object, ImpLoader):\n        def __init__(self, fullname, file, filename, etc):\n            object.__init__(self)\n            ImpLoader.__init__(self, fullname, file, filename, etc)\n\n        def load_module(self, fullname):\n            module = super(_VirtualenvLoader, self).load_module(fullname)\n            patch_dist(module)\n            module.__loader__ = None  # distlib fallback\n            return module\n\n    sys.meta_path.append(_VirtualenvImporter())\n", "path": "src/virtualenv/create/via_global_ref/_virtualenv.py"}]}