[pygobject] setup.py: look up pycairo headers without importing the module



commit 5f614b36b8f61caab581a56ccf320fee3177051b
Author: Christoph Reiter <reiter christoph gmail com>
Date:   Sat Apr 9 11:46:58 2022 +0200

    setup.py: look up pycairo headers without importing the module
    
    Up until now pycairo provided a cairo.get_include() helper which
    could be used to find the required include directory matching the module,
    considering various scenarios.
    
    Starting with 3.8 this leads to problems on Windows since CPython on Windows
    will no longer use PATH for the DLL lookup and expects the library user to
    explicitely pass the directory where the cairo DLL can be found.
    
    In a build environment the user has no control over this though, so we have to
    find the include directory without loading/importing pycairo again.
    
    This now uses a combination of importlib.util.find_spec() for finding the module
    and importlib.metadata.distribution() for finding the package version.
    
    Hopefully this covers all cases.

 setup.py | 78 ++++++++++++++++++++++++++--------------------------------------
 1 file changed, 31 insertions(+), 47 deletions(-)
---
diff --git a/setup.py b/setup.py
index 5f801607..f50c3052 100755
--- a/setup.py
+++ b/setup.py
@@ -22,7 +22,6 @@ import sys
 import errno
 import subprocess
 import tarfile
-import sysconfig
 import tempfile
 import posixpath
 
@@ -827,7 +826,9 @@ def get_pycairo_include_dir():
 
     pkg_config_name = "py3cairo"
     min_version = get_version_requirement(pkg_config_name)
-    min_version_info = tuple(int(p) for p in min_version.split("."))
+
+    def parse_version(string):
+        return tuple(int(p) for p in string.split("."))
 
     def check_path(include_dir):
         log.info("pycairo: trying include directory: %r" % include_dir)
@@ -843,61 +844,44 @@ def get_pycairo_include_dir():
             if check_path(p):
                 return p
 
-    def find_new_api():
-        log.info("pycairo: new API")
-        import cairo
-
-        if cairo.version_info < min_version_info:
-            raise DistutilsSetupError(
-                "pycairo >= %s required, %s found." % (
-                    min_version, ".".join(map(str, cairo.version_info))))
-
-        if hasattr(cairo, "get_include"):
-            return [cairo.get_include()]
-        log.info("pycairo: no get_include()")
-        return []
+    def find_python():
+        """This tries to find the pycairo module without importing it"""
 
-    def find_old_api():
-        log.info("pycairo: old API")
+        from importlib.util import find_spec
 
-        import cairo
+        # Find the module path
+        spec = find_spec("cairo")
+        if spec is None:
+            log.info("pycairo: cairo module not found via importlib")
+            return []
+        package_path = os.path.dirname(spec.origin)
 
-        if cairo.version_info < min_version_info:
-            raise DistutilsSetupError(
-                "pycairo >= %s required, %s found." % (
-                    min_version, ".".join(map(str, cairo.version_info))))
-
-        location = os.path.dirname(os.path.abspath(cairo.__path__[0]))
-        log.info("pycairo: found %r" % location)
-
-        def get_sys_path(location, name):
-            # Returns the sysconfig path for a distribution, or None
-            for scheme in sysconfig.get_scheme_names():
-                for path_type in ["platlib", "purelib"]:
-                    path = sysconfig.get_path(path_type, scheme)
-                    try:
-                        if os.path.samefile(path, location):
-                            return sysconfig.get_path(name, scheme)
-                    except EnvironmentError:
-                        pass
+        # With Python 3.8 we can also check the package version
+        try:
+            from importlib.metadata import distribution, PackageNotFoundError
+        except ImportError:
+            # Python <3.8
+            pass
+        else:
+            try:
+                d = distribution("pycairo")
+            except PackageNotFoundError:
+                log.info("pycairo: pycairo distribution not found via importlib")
+            else:
+                if parse_version(d.version) < parse_version(min_version):
+                    raise DistutilsSetupError(
+                        "pycairo >= %s required, %s found (%s)." % (
+                            min_version, d.version, package_path))
 
-        data_path = get_sys_path(location, "data") or sys.prefix
-        return [os.path.join(data_path, "include", "pycairo")]
+        return [os.path.join(package_path, 'include')]
 
     def find_pkg_config():
         log.info("pycairo: pkg-config")
         pkg_config_version_check(pkg_config_name, min_version)
         return pkg_config_parse("--cflags-only-I", pkg_config_name)
 
-    # First the new get_include() API added in >1.15.6
-    include_dir = find_path(find_new_api())
-    if include_dir is not None:
-        return include_dir
-
-    # Then try to find it in the data prefix based on the module path.
-    # This works with many virtualenv/userdir setups, but not all apparently,
-    # see https://gitlab.gnome.org/GNOME/pygobject/issues/150
-    include_dir = find_path(find_old_api())
+    # First look in the current Python installation/venv
+    include_dir = find_path(find_python())
     if include_dir is not None:
         return include_dir
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]