diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 78eba8df..5a32a625 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,7 +4,8 @@ Changelog *unreleased* ~~~~~~~~~~~~ -No unreleased changes. +* Handle ``OSError`` on non-dynamic executables when attempting to resolve + the glibc version string. 20.4 - 2020-05-19 ~~~~~~~~~~~~~~~~~ diff --git a/packaging/tags.py b/packaging/tags.py index 9064910b..e35bbdad 100644 --- a/packaging/tags.py +++ b/packaging/tags.py @@ -474,8 +474,20 @@ def _glibc_version_string_ctypes(): # main program". This way we can let the linker do the work to figure out # which libc our process is actually using. # - # Note: typeshed is wrong here so we are ignoring this line. - process_namespace = ctypes.CDLL(None) # type: ignore + # We must also handle the special case where the executable is not a + # dynamically linked executable. This can occur when using musl libc, + # for example. In this situation, dlopen() will error, leading to an + # OSError. Interestingly, at least in the case of musl, there is no + # errno set on the OSError. The single string argument used to construct + # OSError comes from libc itself and is therefore not portable to + # hard code here. In any case, failure to call dlopen() means we + # can proceed, so we bail on our attempt. + try: + # Note: typeshed is wrong here so we are ignoring this line. + process_namespace = ctypes.CDLL(None) # type: ignore + except OSError: + return None + try: gnu_get_libc_version = process_namespace.gnu_get_libc_version except AttributeError: diff --git a/tests/test_tags.py b/tests/test_tags.py index 4840e196..0e445f2b 100644 --- a/tests/test_tags.py +++ b/tests/test_tags.py @@ -389,6 +389,13 @@ def test_glibc_version_string_ctypes_missing(self, monkeypatch): monkeypatch.setitem(sys.modules, "ctypes", None) assert tags._glibc_version_string_ctypes() is None + def test_glibc_version_string_ctypes_raise_oserror(self, monkeypatch): + def patched_cdll(name): + raise OSError("Dynamic loading not supported") + + monkeypatch.setattr(ctypes, "CDLL", patched_cdll) + assert tags._glibc_version_string_ctypes() is None + def test_get_config_var_does_not_log(self, monkeypatch): debug = pretend.call_recorder(lambda *a: None) monkeypatch.setattr(tags.logger, "debug", debug)