19510349 pkgdepend needs to be PEP 3149 aware
authorDanek Duvall <danek.duvall@oracle.com>
Mon, 04 May 2015 12:33:50 -0700
changeset 3200 4fc46cd7edf1
parent 3199 ee8403e8122b
child 3201 0cba0e0d2469
19510349 pkgdepend needs to be PEP 3149 aware
src/modules/flavor/depthlimitedmf.py
src/pkg/manifests/package:pkg.p5m
src/tests/api/t_dependencies.py
src/tests/cli/t_pkgdep.py
--- a/src/modules/flavor/depthlimitedmf.py	Wed Apr 29 17:55:57 2015 -0700
+++ b/src/modules/flavor/depthlimitedmf.py	Mon May 04 12:33:50 2015 -0700
@@ -27,6 +27,8 @@
 import modulefinder
 import os
 import sys
+if sys.version_info[0] == 3:
+        from importlib.machinery import EXTENSION_SUFFIXES
 
 # A string used as a component of the pkg.depend.runpath value as a special
 # token to determine where to insert the runpath that pkgdepend generates itself
@@ -52,17 +54,24 @@
 
                 self.name = name
                 self.builtin = builtin
-                self.suffixes = [".py", ".pyc", ".pyo", "/__init__.py", ".so",
-                    "module.so"]
+                self.patterns = [ "{0}.py", "{0}.pyc", "{0}.pyo", "{0}/__init__.py" ]
+                if sys.version_info[0] == 2:
+                        self.patterns += [
+                            "{0}.so", "{0}module.so", "64/{0}.so", "64/{0}module.so"
+                        ]
+                else:
+                        self.patterns += \
+                            ["{{0}}{0}".format(s) for s in EXTENSION_SUFFIXES] + \
+                            ["64/{{0}}{0}".format(s) for s in EXTENSION_SUFFIXES]
                 self.dirs = sorted(dirs)
 
         def make_package(self):
                 """Declare that this module is a package."""
 
                 if self.dirs:
-                        self.suffixes = ["/__init__.py"]
+                        self.patterns = ["{0}/__init__.py"]
                 else:
-                        self.suffixes = []
+                        self.patterns = []
 
         def get_package_dirs(self):
                 """Get the directories where this package might be defined."""
@@ -73,11 +82,11 @@
                 """Return all the file names under which this module might be
                 found."""
 
-                return ["{0}{1}".format(self.name, suf) for suf in self.suffixes]
+                return [ pat.format(self.name) for pat in self.patterns ]
 
         def __str__(self):
                 return "name:{0} suffixes:{1} dirs:{2}".format(self.name,
-                    " ".join(self.suffixes), len(self.dirs))
+                    " ".join(self.patterns), len(self.dirs))
 
 
 if __name__ == "__main__":
--- a/src/pkg/manifests/package:pkg.p5m	Wed Apr 29 17:55:57 2015 -0700
+++ b/src/pkg/manifests/package:pkg.p5m	Mon May 04 12:33:50 2015 -0700
@@ -140,7 +140,9 @@
 dir  path=$(PYDIRVP)/pkg/flavor
 file path=$(PYDIRVP)/pkg/flavor/__init__.py
 file path=$(PYDIRVP)/pkg/flavor/base.py
-file path=$(PYDIRVP)/pkg/flavor/depthlimitedmf.py
+# importlib is specific to Python 3 and part of its standard library.
+file path=$(PYDIRVP)/pkg/flavor/depthlimitedmf.py \
+    pkg.depend.bypass-generate=.*importlib.*
 file path=$(PYDIRVP)/pkg/flavor/elf.py
 file path=$(PYDIRVP)/pkg/flavor/hardlink.py
 file path=$(PYDIRVP)/pkg/flavor/python.py
--- a/src/tests/api/t_dependencies.py	Wed Apr 29 17:55:57 2015 -0700
+++ b/src/tests/api/t_dependencies.py	Mon May 04 12:33:50 2015 -0700
@@ -1375,15 +1375,18 @@
 
                 def _check_all_res(res):
                         ds, es, ms, pkg_attrs = res
-                        mod_suffs = ["/__init__.py", ".py", ".pyc", ".pyo",
-                            ".so", "module.so"]
+                        mod_pats = [
+                            "{0}/__init__.py", "{0}.py", "{0}.pyc", "{0}.pyo",
+                            "{0}.so", "{0}module.so",
+                            "64/{0}.so", "64/{0}module.so"
+                        ]
                         mod_names = ["foobar", "misc_test", "os",
                             "search_storage", "minidom"]
                         pkg_names = ["indexer_test", "pkg", "pkg_test", "xml",
                             "dom"]
                         expected_deps = set([("python",)] +
                             [tuple(sorted([
-                                "{0}{1}".format(n,s) for s in mod_suffs
+                                pat.format(n) for pat in mod_pats
                             ]))
                             for n in mod_names] +
                             [("{0}/__init__.py".format(n),) for n in pkg_names])
@@ -1426,15 +1429,18 @@
 
                 def _check_all_res(res):
                         ds, es, ms, pkg_attrs = res
-                        mod_suffs = ["/__init__.py", ".py", ".pyc", ".pyo",
-                            ".so", "module.so"]
+                        mod_pats = [
+                            "{0}/__init__.py", "{0}.py", "{0}.pyc", "{0}.pyo",
+                            "{0}.so", "{0}module.so",
+                            "64/{0}.so", "64/{0}module.so"
+                        ]
                         mod_names = ["foobar", "os", "search_storage",
                             "minidom"]
                         pkg_names = ["indexer_test", "pkg", "pkg_test", "xml",
                             "dom"]
                         expected_deps = set([("python",)] +
                             [tuple(sorted([
-                                "{0}{1}".format(n,s) for s in mod_suffs
+                                pat.format(n) for pat in mod_pats
                             ]))
                             for n in mod_names] +
                             [("{0}/__init__.py".format(n),) for n in pkg_names])
@@ -1481,8 +1487,11 @@
 
                 def _check_all_res(res):
                         ds, es, ms, pkg_attrs = res
-                        mod_suffs = ["/__init__.py", ".py", ".pyc", ".pyo",
-                            ".so", "module.so"]
+                        mod_pats = [
+                            "{0}/__init__.py", "{0}.py", "{0}.pyc", "{0}.pyo",
+                            "{0}.so", "{0}module.so",
+                            "64/{0}.so", "64/{0}module.so"
+                        ]
                         mod_names = ["foobar", "misc_test", "os",
                             "search_storage", "minidom"]
                         pkg_names = ["indexer_test", "pkg", "pkg_test",
@@ -1495,7 +1504,7 @@
 
                         expected_deps = set([("python",)] +
                             [tuple(sorted([
-                                "{0}{1}".format(n,s) for s in mod_suffs
+                                pat.format(n) for pat in mod_pats
                             ]))
                             for n in mod_names] +
                             [("{0}/__init__.py".format(n),) for n in pkg_names])
@@ -1581,9 +1590,11 @@
                 pddp = "pkg.debug.depend.path"
                 pddf = "pkg.debug.depend.file"
 
-                mod_suffs = ["/__init__.py", ".py", ".pyc", ".pyo", ".so",
-                    "module.so"]
-                expected_deps = set(["bar{0}".format(s) for s in mod_suffs])
+                mod_pats = [
+                    "{0}/__init__.py", "{0}.py", "{0}.pyc", "{0}.pyo",
+                    "{0}.so", "{0}module.so", "64/{0}.so", "64/{0}module.so"
+                ]
+                expected_deps = set([pat.format("bar") for pat in mod_pats])
                 if es != []:
                         raise RuntimeError("Got errors in results:" +
                             "\n".join([str(s) for s in es]))
@@ -2027,15 +2038,18 @@
 
                 def _py_check_all_res(res):
                         ds, es, ms, pkg_attrs = res
-                        mod_suffs = ["/__init__.py", ".py", ".pyc", ".pyo",
-                            ".so", "module.so"]
+                        mod_pats = [
+                            "{0}/__init__.py", "{0}.py", "{0}.pyc", "{0}.pyo",
+                            "{0}.so", "{0}module.so",
+                            "64/{0}.so", "64/{0}module.so"
+                        ]
                         mod_names = ["foobar", "misc_test", "os",
                             "search_storage", "minidom"]
                         pkg_names = ["indexer_test", "pkg", "pkg_test",
                             "xml", "dom"]
                         expected_deps = set([("python",)] +
                             [tuple(sorted([
-                                "{0}{1}".format(n,s) for s in mod_suffs
+                                pat.format(n) for pat in mod_pats
                             ]))
                             for n in mod_names] +
                             [("{0}/__init__.py".format(n),) for n in pkg_names])
--- a/src/tests/cli/t_pkgdep.py	Wed Apr 29 17:55:57 2015 -0700
+++ b/src/tests/cli/t_pkgdep.py	Mon May 04 12:33:50 2015 -0700
@@ -519,6 +519,12 @@
 from pkg.misc import EmptyI
 """
 
+        python3_so_text = """\
+#!/usr/bin/python3.4
+
+import zlib
+"""
+
         python_amd_text = """\
 #!/usr/bin/amd64/python{0}
 
@@ -632,6 +638,9 @@
                 necessary to make the results depend on the sys.path that's
                 discovered.
                 """
+
+                v3 = ver.startswith("3.") and "34m"
+
                 vp = self.get_ver_paths(ver, proto_area)
                 self.debug("ver_paths is {0}".format(vp))
                 pkg_path = self.__make_paths("pkg", vp, reason)
@@ -639,9 +648,17 @@
                     "{pfx}.file=indexer.py "
                     "{pfx}.file=indexer.pyc "
                     "{pfx}.file=indexer.pyo "
+                    "{pfx}.file=64/indexer.so "
                     "{pfx}.file=indexer.so "
-                    "{pfx}.file=indexer/__init__.py "
-                    "{pfx}.file=indexermodule.so " +
+                    "{pfx}.file=indexer/__init__.py " +
+                    (v3 and
+                        "{pfx}.file=indexer.abi3.so "
+                        "{pfx}.file=indexer.cpython-{v3}.so "
+                        "{pfx}.file=64/indexer.abi3.so "
+                        "{pfx}.file=64/indexer.cpython-{v3}.so "
+                        or
+                        "{pfx}.file=indexermodule.so "
+                        "{pfx}.file=64/indexermodule.so ") +
                     pkg_path +
                     " {pfx}.reason={reason} "
                     "{pfx}.type=python type=require\n"
@@ -651,8 +668,16 @@
                     "{pfx}.file=misc.pyc "
                     "{pfx}.file=misc.pyo "
                     "{pfx}.file=misc.so "
-                    "{pfx}.file=misc/__init__.py "
-                    "{pfx}.file=miscmodule.so " +
+                    "{pfx}.file=64/misc.so "
+                    "{pfx}.file=misc/__init__.py " +
+                    (v3 and
+                        "{pfx}.file=misc.abi3.so "
+                        "{pfx}.file=misc.cpython-{v3}.so "
+                        "{pfx}.file=64/misc.abi3.so "
+                        "{pfx}.file=64/misc.cpython-{v3}.so "
+                        or
+                        "{pfx}.file=64/miscmodule.so "
+                        "{pfx}.file=miscmodule.so ") +
                     pkg_path +
                     " {pfx}.reason={reason} "
                     "{pfx}.type=python type=require\n"
@@ -667,9 +692,17 @@
                     "{pfx}.file=search_storage.py "
                     "{pfx}.file=search_storage.pyc "
                     "{pfx}.file=search_storage.pyo "
+                    "{pfx}.file=64/search_storage.so "
                     "{pfx}.file=search_storage.so "
-                    "{pfx}.file=search_storage/__init__.py "
-                    "{pfx}.file=search_storagemodule.so " +
+                    "{pfx}.file=search_storage/__init__.py " +
+                    (v3 and
+                        "{pfx}.file=search_storage.abi3.so "
+                        "{pfx}.file=search_storage.cpython-{v3}.so "
+                        "{pfx}.file=64/search_storage.abi3.so "
+                        "{pfx}.file=64/search_storage.cpython-{v3}.so "
+                        or
+                        "{pfx}.file=64/search_storagemodule.so "
+                        "{pfx}.file=search_storagemodule.so ") +
                     pkg_path +
                     " {pfx}.reason={reason} "
                     "{pfx}.type=python type=require\n")
@@ -681,16 +714,24 @@
                             "{pfx}.file=os.pyc "
                             "{pfx}.file=os.pyo "
                             "{pfx}.file=os.so "
-                            "{pfx}.file=os/__init__.py "
-                            "{pfx}.file=osmodule.so " +
+                            "{pfx}.file=64/os.so "
+                            "{pfx}.file=os/__init__.py " +
+                            (v3 and
+                                "{pfx}.file=os.abi3.so "
+                                "{pfx}.file=os.cpython-{v3}.so "
+                                "{pfx}.file=64/os.abi3.so "
+                                "{pfx}.file=64/os.cpython-{v3}.so "
+                                or
+                                "{pfx}.file=64/osmodule.so "
+                                "{pfx}.file=osmodule.so ") +
                             self.__make_paths("", vp, reason) +
                             " {pfx}.reason={reason} "
                             "{pfx}.type=python type=require\n")
                 return res.format(
                     pfx=base.Dependency.DEPEND_DEBUG_PREFIX,
                     dummy_fmri=base.Dependency.DUMMY_FMRI,
-                    reason=reason
-               )
+                    reason=reason, v3=v3
+                )
 
         pyver_other_script_full_manf_1 = """\
 file NOHASH group=bin mode=0755 owner=root path={reason} {run_path}
@@ -709,7 +750,7 @@
                 if ver == py_ver_other:
                         tmp = self.pyver_other_script_full_manf_1
                 else:
-                        raise RuntimeError("Unexcepted version for "
+                        raise RuntimeError("Unexpected version for "
                             "pyver_res_full_manf_1 {0}".format(ver))
                 return tmp + self.make_pyver_python_res(ver, proto, reason,
                     include_os=include_os)
@@ -726,8 +767,20 @@
                 """Generate the expected results when resolving a manifest which
                 contains a file with a non-default version of python."""
 
-                suffixes = (".py", ".pyc", ".pyo", ".so", "/__init__.py",
-                    "module.so")
+                v3 = py_ver_other.startswith("3.") and "34m"
+                patterns = (
+                    "{0}.py", "{0}.pyc", "{0}.pyo",
+                    "{0}.so", "64/{0}.so",
+                    "{0}/__init__.py"
+                )
+                if v3:
+                        patterns += (
+                            "{0}.abi3.so", "{0}.cpython-{v3}.so",
+                            "64/{0}.abi3.so", "64/{0}.cpython-{v3}.so",
+                        )
+                else:
+                        patterns += ("{0}module.so", "64/{0}module.so")
+
                 files = ["indexer", "misc", "search_storage"]
 
                 # These are the paths to the files which the package depends on.
@@ -757,9 +810,9 @@
                 return "depend fmri=pkg:/{res_manf} " + \
                     " ".join(["{pfx}.file=" + rp for rp in rel_paths]) + \
                     " " + " ".join(["{pfx}.path-id=" + ":".join(sorted([
-                        proto_str + f + s
+                        proto_str + pat.format(f, v3=v3)
                         for proto_str in vps
-                        for s in suffixes
+                        for pat in patterns
                         ]))
                         for f in files
                     ]) + " {pfx}.path-id=" + ":".join(sorted(
@@ -768,7 +821,7 @@
                     "{pfx}.reason=usr/lib/python{py_ver}/" + \
                     "vendor-packages/pkg/client/indexer.py " + \
                     "{pfx}.type=script {pfx}.type=python type=require"
-        
+
         pyver_mismatch_results = """\
 depend fmri={dummy_fmri} {pfx}.file=python{default} {pfx}.path=usr/bin {pfx}.reason=usr/lib/python{other}/vendor-packages/pkg/client/indexer.py {pfx}.type=script type=require
 """.format(default=py_ver_default, other=py_ver_other, pfx=base.Dependency.DEPEND_DEBUG_PREFIX, dummy_fmri=base.Dependency.DUMMY_FMRI)
@@ -1701,7 +1754,7 @@
                 # Test that resolve handles multiline actions correctly when
                 # echoing the manifest.  Bug 18740
                 self.pkgfmt(m1_path)
-                
+
                 self.pkgdepend_resolve(" -vm {0}".format(" ".join([m1_path, m2_path,
                         m3_path, m4_path, m5_path, m6_path, m7_path, m8_path])))
                 fh = open(m1_path + ".res")
@@ -2944,6 +2997,41 @@
                     self.output)
                 self.check_res("", self.errout)
 
+        def test_PEP_3149(self):
+                """Test that Python 3 modules importing native modules can find
+                them in the right place, following PEP 3149.
+
+                On Solaris, this means 64-bit only, and with the "m" (pymalloc)
+                flag turned on.
+                """
+
+                # Create a python 3.x file that imports a native module.
+                tp = self.make_manifest(
+                    self.pyver_test_manf_1.format(py_ver="3.4"))
+                fp = "usr/lib/python{0}/vendor-packages/pkg/" \
+                    "client/indexer.py".format("3.4")
+                self.make_proto_text_file(fp, self.python3_so_text)
+
+                # Run generate
+                self.pkgdepend_generate("-m -d {0} {1}".format(
+                    self.test_proto_dir, tp))
+
+                # Take the output, split it into actions, and find exactly one
+                # action which generated a correct dependency on zlib based on
+                # indexer.py.
+                pfx = base.Dependency.DEPEND_DEBUG_PREFIX
+                acts = [
+                    a
+                    for a in (
+                        actions.fromstr(l)
+                        for l in self.output.strip().splitlines()
+                    )
+                    if a.attrs.get(pfx + ".reason") == fp and
+                        "64/zlib.cpython-34m.so" in a.attrs[pfx + ".file"]
+                ]
+                self.assert_(len(acts) == 1)
+
+                self.check_res("", self.errout)
 
 if __name__ == "__main__":
         unittest.main()