Skip to content

Commit 50031f6

Browse files
committed
generate import handling
1 parent dbfb7ce commit 50031f6

21 files changed

+1573
-201
lines changed

.github/workflows/branchbuild.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ jobs:
2727
2828
- name: Generate cython
2929
run: |
30-
chmod +x ./src/rapidfuzz/generate.sh
31-
./src/rapidfuzz/generate.sh
30+
chmod +x ./src/rapidfuzz/generate_cython.sh
31+
./src/rapidfuzz/generate_cython.sh
3232
3333
- name: build
3434
run: |

.github/workflows/coverage.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ jobs:
2424
2525
- name: Generate cython
2626
run: |
27-
chmod +x ./src/rapidfuzz/generate.sh
28-
./src/rapidfuzz/generate.sh
27+
chmod +x ./src/rapidfuzz/generate_cython.sh
28+
./src/rapidfuzz/generate_cython.sh
2929
3030
# for cython tests inplace installation is required
3131
- name: build

.github/workflows/releasebuild.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ jobs:
2727
# The cythonized files allow installation from the sdist without cython
2828
- name: Generate cython
2929
run: |
30-
chmod +x ./src/rapidfuzz/generate.sh
31-
./src/rapidfuzz/generate.sh
30+
chmod +x ./src/rapidfuzz/generate_cython.sh
31+
./src/rapidfuzz/generate_cython.sh
3232
3333
- name: Build sdist
3434
run: |

pyproject.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ wheel.exclude = [
5555
"**.hpp",
5656
"**.h",
5757
"CMakeLists.txt",
58-
"generate.sh"
58+
"generate_cython.sh"
5959
]
6060
wheel.packages = ["src/rapidfuzz"]
6161
wheel.cmake = false
@@ -162,7 +162,6 @@ select = [
162162
extend-ignore = [
163163
"PLR", # Design related pylint codes
164164
"E501", # Line too long
165-
"PT004", # Use underscore for non-returning fixture (use usefixture instead)
166165
"PTH123", # use pathlib instead of builtin open
167166
]
168167
unfixable = [

src/rapidfuzz/_utils.py

Lines changed: 0 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,10 @@
33

44
from __future__ import annotations
55

6-
import importlib
7-
import os
86
import sys
97
from math import isnan
108
from typing import Any, Callable
119

12-
from rapidfuzz._feature_detector import AVX2, SSE2, supports
13-
1410
pandas_NA = None
1511

1612

@@ -80,66 +76,6 @@ def add_scorer_attrs(func: Any, cached_scorer_call: dict[str, Callable[..., dict
8076
func._RF_OriginalScorer = func
8177

8278

83-
def optional_import_module(module: str) -> Any:
84-
"""
85-
try to import module. Return None on failure
86-
"""
87-
try:
88-
return importlib.import_module(module)
89-
except Exception:
90-
return None
91-
92-
93-
def vectorized_import(name: str) -> tuple[Any, list[Any]]:
94-
"""
95-
import module best fitting for current CPU
96-
"""
97-
if supports(AVX2):
98-
module = optional_import_module(name + "_avx2")
99-
if module is not None:
100-
return module
101-
if supports(SSE2):
102-
module = optional_import_module(name + "_sse2")
103-
if module is not None:
104-
return module
105-
106-
return importlib.import_module(name)
107-
108-
109-
def fallback_import(
110-
module: str,
111-
name: str,
112-
) -> Any:
113-
"""
114-
import library function and possibly fall back to a pure Python version
115-
when no C++ implementation is available
116-
"""
117-
impl = os.environ.get("RAPIDFUZZ_IMPLEMENTATION")
118-
119-
py_mod = importlib.import_module(module + "_py")
120-
py_func = getattr(py_mod, name)
121-
if not py_func:
122-
msg = f"cannot import name {name!r} from {py_mod.__name!r} ({py_mod.__file__})"
123-
raise ImportError(msg)
124-
125-
if impl == "cpp":
126-
cpp_mod = vectorized_import(module + "_cpp")
127-
elif impl == "python":
128-
return py_func
129-
else:
130-
try:
131-
cpp_mod = vectorized_import(module + "_cpp")
132-
except Exception:
133-
return py_func
134-
135-
cpp_func = getattr(cpp_mod, name)
136-
if not cpp_func:
137-
msg = f"cannot import name {name!r} from {cpp_mod.__name!r} ({cpp_mod.__file__})"
138-
raise ImportError(msg)
139-
140-
return cpp_func
141-
142-
14379
default_distance_attribute: dict[str, Callable[..., dict[str, Any]]] = {"get_scorer_flags": _get_scorer_flags_distance}
14480
default_similarity_attribute: dict[str, Callable[..., dict[str, Any]]] = {
14581
"get_scorer_flags": _get_scorer_flags_similarity
Lines changed: 87 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,91 @@
11
# SPDX-License-Identifier: MIT
2-
# Copyright (C) 2022 Max Bachmann
2+
# Copyright (C) 2025 Max Bachmann
33
from __future__ import annotations
44

5-
from rapidfuzz._utils import fallback_import as _fallback_import
5+
import os
6+
from rapidfuzz._feature_detector import AVX2, SSE2, supports
7+
import contextlib
68

7-
_mod = "rapidfuzz.distance.metrics"
8-
distance = _fallback_import(_mod, "damerau_levenshtein_distance")
9-
similarity = _fallback_import(_mod, "damerau_levenshtein_similarity")
10-
normalized_distance = _fallback_import(_mod, "damerau_levenshtein_normalized_distance")
11-
normalized_similarity = _fallback_import(_mod, "damerau_levenshtein_normalized_similarity")
9+
__all__ = ["distance", "similarity", "normalized_distance", "normalized_similarity"]
10+
11+
_impl = os.environ.get("RAPIDFUZZ_IMPLEMENTATION")
12+
if _impl == "cpp":
13+
imported = False
14+
if supports(AVX2):
15+
with contextlib.suppress(ImportError):
16+
from rapidfuzz.distance.metrics_cpp_avx2 import ( # pyright: ignore[reportMissingImports]
17+
damerau_levenshtein_distance as distance,
18+
damerau_levenshtein_similarity as similarity,
19+
damerau_levenshtein_normalized_distance as normalized_distance,
20+
damerau_levenshtein_normalized_similarity as normalized_similarity,
21+
)
22+
23+
imported = True
24+
25+
if not imported and supports(SSE2):
26+
with contextlib.suppress(ImportError):
27+
from rapidfuzz.distance.metrics_cpp_sse2 import ( # pyright: ignore[reportMissingImports]
28+
damerau_levenshtein_distance as distance,
29+
damerau_levenshtein_similarity as similarity,
30+
damerau_levenshtein_normalized_distance as normalized_distance,
31+
damerau_levenshtein_normalized_similarity as normalized_similarity,
32+
)
33+
34+
imported = True
35+
36+
if not imported:
37+
from rapidfuzz.distance.metrics_cpp import ( # pyright: ignore[reportMissingImports]
38+
damerau_levenshtein_distance as distance,
39+
damerau_levenshtein_similarity as similarity,
40+
damerau_levenshtein_normalized_distance as normalized_distance,
41+
damerau_levenshtein_normalized_similarity as normalized_similarity,
42+
)
43+
elif _impl == "python":
44+
from rapidfuzz.distance.metrics_py import (
45+
damerau_levenshtein_distance as distance,
46+
damerau_levenshtein_similarity as similarity,
47+
damerau_levenshtein_normalized_distance as normalized_distance,
48+
damerau_levenshtein_normalized_similarity as normalized_similarity,
49+
)
50+
else:
51+
imported = False
52+
if supports(AVX2):
53+
with contextlib.suppress(ImportError):
54+
from rapidfuzz.distance.metrics_cpp_avx2 import ( # pyright: ignore[reportMissingImports]
55+
damerau_levenshtein_distance as distance,
56+
damerau_levenshtein_similarity as similarity,
57+
damerau_levenshtein_normalized_distance as normalized_distance,
58+
damerau_levenshtein_normalized_similarity as normalized_similarity,
59+
)
60+
61+
imported = True
62+
63+
if not imported and supports(SSE2):
64+
with contextlib.suppress(ImportError):
65+
from rapidfuzz.distance.metrics_cpp_sse2 import ( # pyright: ignore[reportMissingImports]
66+
damerau_levenshtein_distance as distance,
67+
damerau_levenshtein_similarity as similarity,
68+
damerau_levenshtein_normalized_distance as normalized_distance,
69+
damerau_levenshtein_normalized_similarity as normalized_similarity,
70+
)
71+
72+
imported = True
73+
74+
if not imported:
75+
with contextlib.suppress(ImportError):
76+
from rapidfuzz.distance.metrics_cpp import ( # pyright: ignore[reportMissingImports]
77+
damerau_levenshtein_distance as distance,
78+
damerau_levenshtein_similarity as similarity,
79+
damerau_levenshtein_normalized_distance as normalized_distance,
80+
damerau_levenshtein_normalized_similarity as normalized_similarity,
81+
)
82+
83+
imported = True
84+
85+
if not imported:
86+
from rapidfuzz.distance.metrics_py import (
87+
damerau_levenshtein_distance as distance,
88+
damerau_levenshtein_similarity as similarity,
89+
damerau_levenshtein_normalized_distance as normalized_distance,
90+
damerau_levenshtein_normalized_similarity as normalized_similarity,
91+
)

src/rapidfuzz/distance/Hamming.py

Lines changed: 110 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,114 @@
11
# SPDX-License-Identifier: MIT
2-
# Copyright (C) 2022 Max Bachmann
2+
# Copyright (C) 2025 Max Bachmann
33
from __future__ import annotations
44

5-
from rapidfuzz._utils import fallback_import as _fallback_import
5+
import os
6+
from rapidfuzz._feature_detector import AVX2, SSE2, supports
7+
import contextlib
68

7-
_mod = "rapidfuzz.distance.metrics"
8-
distance = _fallback_import(_mod, "hamming_distance")
9-
similarity = _fallback_import(_mod, "hamming_similarity")
10-
normalized_similarity = _fallback_import(_mod, "hamming_normalized_similarity")
11-
normalized_distance = _fallback_import(_mod, "hamming_normalized_distance")
12-
editops = _fallback_import(_mod, "hamming_editops")
13-
opcodes = _fallback_import(_mod, "hamming_opcodes")
9+
__all__ = [
10+
"distance",
11+
"similarity",
12+
"normalized_distance",
13+
"normalized_similarity",
14+
"editops",
15+
"opcodes",
16+
]
17+
18+
_impl = os.environ.get("RAPIDFUZZ_IMPLEMENTATION")
19+
if _impl == "cpp":
20+
imported = False
21+
if supports(AVX2):
22+
with contextlib.suppress(ImportError):
23+
from rapidfuzz.distance.metrics_cpp_avx2 import ( # pyright: ignore[reportMissingImports]
24+
hamming_distance as distance,
25+
hamming_similarity as similarity,
26+
hamming_normalized_distance as normalized_distance,
27+
hamming_normalized_similarity as normalized_similarity,
28+
hamming_editops as editops,
29+
hamming_opcodes as opcodes,
30+
)
31+
32+
imported = True
33+
34+
if not imported and supports(SSE2):
35+
with contextlib.suppress(ImportError):
36+
from rapidfuzz.distance.metrics_cpp_sse2 import ( # pyright: ignore[reportMissingImports]
37+
hamming_distance as distance,
38+
hamming_similarity as similarity,
39+
hamming_normalized_distance as normalized_distance,
40+
hamming_normalized_similarity as normalized_similarity,
41+
hamming_editops as editops,
42+
hamming_opcodes as opcodes,
43+
)
44+
45+
imported = True
46+
47+
if not imported:
48+
from rapidfuzz.distance.metrics_cpp import ( # pyright: ignore[reportMissingImports]
49+
hamming_distance as distance,
50+
hamming_similarity as similarity,
51+
hamming_normalized_distance as normalized_distance,
52+
hamming_normalized_similarity as normalized_similarity,
53+
hamming_editops as editops,
54+
hamming_opcodes as opcodes,
55+
)
56+
elif _impl == "python":
57+
from rapidfuzz.distance.metrics_py import (
58+
hamming_distance as distance,
59+
hamming_similarity as similarity,
60+
hamming_normalized_distance as normalized_distance,
61+
hamming_normalized_similarity as normalized_similarity,
62+
hamming_editops as editops,
63+
hamming_opcodes as opcodes,
64+
)
65+
else:
66+
imported = False
67+
if supports(AVX2):
68+
with contextlib.suppress(ImportError):
69+
from rapidfuzz.distance.metrics_cpp_avx2 import ( # pyright: ignore[reportMissingImports]
70+
hamming_distance as distance,
71+
hamming_similarity as similarity,
72+
hamming_normalized_distance as normalized_distance,
73+
hamming_normalized_similarity as normalized_similarity,
74+
hamming_editops as editops,
75+
hamming_opcodes as opcodes,
76+
)
77+
78+
imported = True
79+
80+
if not imported and supports(SSE2):
81+
with contextlib.suppress(ImportError):
82+
from rapidfuzz.distance.metrics_cpp_sse2 import ( # pyright: ignore[reportMissingImports]
83+
hamming_distance as distance,
84+
hamming_similarity as similarity,
85+
hamming_normalized_distance as normalized_distance,
86+
hamming_normalized_similarity as normalized_similarity,
87+
hamming_editops as editops,
88+
hamming_opcodes as opcodes,
89+
)
90+
91+
imported = True
92+
93+
if not imported:
94+
with contextlib.suppress(ImportError):
95+
from rapidfuzz.distance.metrics_cpp import ( # pyright: ignore[reportMissingImports]
96+
hamming_distance as distance,
97+
hamming_similarity as similarity,
98+
hamming_normalized_distance as normalized_distance,
99+
hamming_normalized_similarity as normalized_similarity,
100+
hamming_editops as editops,
101+
hamming_opcodes as opcodes,
102+
)
103+
104+
imported = True
105+
106+
if not imported:
107+
from rapidfuzz.distance.metrics_py import (
108+
hamming_distance as distance,
109+
hamming_similarity as similarity,
110+
hamming_normalized_distance as normalized_distance,
111+
hamming_normalized_similarity as normalized_similarity,
112+
hamming_editops as editops,
113+
hamming_opcodes as opcodes,
114+
)

0 commit comments

Comments
 (0)