|
3 | 3 | import collections.abc |
4 | 4 | import itertools |
5 | 5 | import linecache |
| 6 | +import os |
6 | 7 | import sys |
7 | 8 | import textwrap |
8 | 9 | import types |
@@ -1129,6 +1130,11 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None, |
1129 | 1130 | self._str += (". Site initialization is disabled, did you forget to " |
1130 | 1131 | + "add the site-packages directory to sys.path " |
1131 | 1132 | + "or to enable your virtual environment?") |
| 1133 | + elif abi_tag := _find_incompatible_extension_module(module_name): |
| 1134 | + self._str += ( |
| 1135 | + ". Although a module with this name was found for a " |
| 1136 | + f"different Python version ({abi_tag})." |
| 1137 | + ) |
1132 | 1138 | else: |
1133 | 1139 | suggestion = _compute_suggestion_error(exc_value, exc_traceback, module_name) |
1134 | 1140 | if suggestion: |
@@ -1880,3 +1886,28 @@ def _levenshtein_distance(a, b, max_cost): |
1880 | 1886 | # Everything in this row is too big, so bail early. |
1881 | 1887 | return max_cost + 1 |
1882 | 1888 | return result |
| 1889 | + |
| 1890 | + |
| 1891 | +def _find_incompatible_extension_module(module_name): |
| 1892 | + import importlib.machinery |
| 1893 | + import importlib.resources.readers |
| 1894 | + |
| 1895 | + if not module_name or not importlib.machinery.EXTENSION_SUFFIXES: |
| 1896 | + return |
| 1897 | + |
| 1898 | + # We assume the last extension is untagged (eg. .so, .pyd)! |
| 1899 | + # tests.test_traceback.MiscTest.test_find_incompatible_extension_modules |
| 1900 | + # tests that assumption. |
| 1901 | + untagged_suffix = importlib.machinery.EXTENSION_SUFFIXES[-1] |
| 1902 | + |
| 1903 | + parent, _, child = module_name.rpartition('.') |
| 1904 | + if parent: |
| 1905 | + traversable = importlib.resources.files(parent) |
| 1906 | + else: |
| 1907 | + traversable = importlib.resources.readers.MultiplexedPath( |
| 1908 | + *filter(os.path.isdir, sys.path) |
| 1909 | + ) |
| 1910 | + |
| 1911 | + for entry in traversable.iterdir(): |
| 1912 | + if entry.name.startswith(child + '.') and entry.name.endswith(untagged_suffix): |
| 1913 | + return entry.name.removeprefix(child + '.').removesuffix(untagged_suffix) |
0 commit comments