Repository: pypa/auditwheel

Issue:
libpython3.x is skipped but libpython3.xx is included
howdy,

i noticed when running auditwheel repair on a wheel with dependency on libpython3.8.so or libpython3.9.so, they are not added to the wheel libs directory, but libpython3.10.so and libpython3.11.so are added. I'm not very familiar with the project, but i suspect [the regex detecting libpython](https://github.com/pypa/auditwheel/blob/63c30761e6857491af50fbb1922ecfd4c034ef76/src/auditwheel/policy/external_references.py#L11) could be the culprit

Discussion:
codecov[bot]: ## [Codecov](https://codecov.io/gh/pypa/auditwheel/pull/424?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pypa) Report
Patch coverage: **`100.00`**% and project coverage change: **`-1.09`** :warning:
> Comparison is base [(`d270db8`)](https://codecov.io/gh/pypa/auditwheel/commit/d270db8188386891fd1a653a6737ce5553de568c?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pypa) 92.33% compared to head [(`a44c538`)](https://codecov.io/gh/pypa/auditwheel/pull/424?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pypa) 91.24%.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #424      +/-   ##
==========================================
- Coverage   92.33%   91.24%   -1.09%     
==========================================
  Files          23       23              
  Lines        1291     1291              
  Branches      302      302              
==========================================
- Hits         1192     1178      -14     
- Misses         57       67      +10     
- Partials       42       46       +4     
```


| [Impacted Files](https://codecov.io/gh/pypa/auditwheel/pull/424?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pypa) | Coverage Δ | |
|---|---|---|
| [src/auditwheel/policy/external\_references.py](https://codecov.io/gh/pypa/auditwheel/pull/424?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pypa#diff-c3JjL2F1ZGl0d2hlZWwvcG9saWN5L2V4dGVybmFsX3JlZmVyZW5jZXMucHk=) | `97.87% <100.00%> (ø)` | |

... and [4 files with indirect coverage changes](https://codecov.io/gh/pypa/auditwheel/pull/424/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pypa)

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pypa). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pypa)

</details>

[:umbrella: View full report in Codecov by Sentry](https://codecov.io/gh/pypa/auditwheel/pull/424?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pypa).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pypa).


Patch:
diff --git a/src/auditwheel/policy/external_references.py b/src/auditwheel/policy/external_references.py
index 1c05b10..23afde8 100644
--- a/src/auditwheel/policy/external_references.py
+++ b/src/auditwheel/policy/external_references.py
@@ -8,7 +8,7 @@ from ..elfutils import filter_undefined_symbols, is_subdir
 from . import load_policies
 
 log = logging.getLogger(__name__)
-LIBPYTHON_RE = re.compile(r"^libpython\d\.\dm?.so(.\d)*$")
+LIBPYTHON_RE = re.compile(r"^libpython\d+\.\d+m?.so(.\d)*$")
 
 
 def lddtree_external_references(lddtree: dict, wheel_path: str) -> dict:


Test patch:
diff --git a/tests/unit/test_policy.py b/tests/unit/test_policy.py
index 9fa21c9..adbdad8 100644
--- a/tests/unit/test_policy.py
+++ b/tests/unit/test_policy.py
@@ -10,6 +10,7 @@ from auditwheel.policy import (
     get_policy_name,
     get_priority_by_name,
     get_replace_platforms,
+    lddtree_external_references,
 )
 
 
@@ -202,3 +203,32 @@ class TestPolicyAccess:
     def test_get_by_name_duplicate(self):
         with pytest.raises(RuntimeError):
             get_priority_by_name("duplicate")
+
+
+class TestLddTreeExternalReferences:
+    """Tests for lddtree_external_references."""
+
+    def test_filter_libs(self):
+        """Test the nested filter_libs function."""
+        filtered_libs = [
+            "ld-linux-x86_64.so.1",
+            "ld64.so.1",
+            "ld64.so.2",
+            "libpython3.7m.so.1.0",
+            "libpython3.9.so.1.0",
+            "libpython3.10.so.1.0",
+            "libpython999.999.so.1.0",
+        ]
+        unfiltered_libs = ["libfoo.so.1.0", "libbar.so.999.999.999"]
+        libs = filtered_libs + unfiltered_libs
+
+        lddtree = {
+            "realpath": "/path/to/lib",
+            "needed": libs,
+            "libs": {lib: {"needed": [], "realpath": "/path/to/lib"} for lib in libs},
+        }
+        full_external_refs = lddtree_external_references(lddtree, "/path/to/wheel")
+
+        # Assert that each policy only has the unfiltered libs.
+        for policy in full_external_refs:
+            assert set(full_external_refs[policy]["libs"]) == set(unfiltered_libs)