Package: python3-minimal
Version: 3.5.1-4

A package marked "Multi-Arch: same" that distributes python modules and
makes use of dh-python will lead to the following crash when multiple
architectures are installed together:

Setting up gir1.2-ibus-1.0:i386 (1.5.11-1) ...
dpkg-query: error: --listfiles needs a valid package name but
'gir1.2-ibus-1.0' is not: ambiguous package name 'gir1.2-ibus-1.0' with
more than one installed instance

Use --help for help about querying packages.
Traceback (most recent call last):
  File "/usr/bin/py3compile", line 290, in <module>
    main()
  File "/usr/bin/py3compile", line 270, in main
    options.force, options.optimize, e_patterns)
  File "/usr/bin/py3compile", line 154, in compile
    for fn, versions_to_compile in filter_files(files, e_patterns,
versions):
  File "/usr/bin/py3compile", line 106, in filter_files
    for fn in files:
  File "/usr/share/python3/debpython/files.py", line 71, in filter_public
    for fn in files:
  File "/usr/share/python3/debpython/files.py", line 53, in from_package
    raise Exception("cannot get content of %s" % package_name)
Exception: cannot get content of gir1.2-ibus-1.0
dpkg: error processing package gir1.2-ibus-1.0:i386 (--configure):
 subprocess installed post-installation script returned error exit status 1

This is a result of invoking py3compile or py3clean on an ambiguously
specified package.  dh-python can and should disambiguate the package by
specifying an architecture and I am submitting a patch to that effect.
However, I believe the correct behavior for py3clean and py3compile would
be to clean or compile only the packages uniquely provided by a given
instance of a package.  This would ensure the correct behavior for python
modules that are distributed exclusively for a specific architecture, as
well as modules that are common to all architectures.  I've attached a
patch that implements this behavior.

Additionally, in the case of an ambiguous package specification, this patch
will turn what is currently a severe failure requiring a human to repair
into
a minor redundancy that will go away over time as packages are built
against a newer dh-python that does not provide ambiguous package
specifications to
py3clean and py3compile.

This would solve or at least help alleviate the following bugs:

bug #770265
bug #810551
bug #812228


Please apply this patch.
=== modified file 'debian/changelog'
--- debian/changelog	2016-06-03 14:42:29 +0000
+++ debian/changelog	2016-09-18 07:15:37 +0000
@@ -1,3 +1,10 @@
+python3-defaults (3.5.1-4.1) UNRELEASED; urgency=medium
+
+  * Non-maintainer upload.
+  * Compiling/cleaning support for Multi-Arch:same.
+
+ -- Joe Crayne <oh.hello....@gmail.com>  Sun, 18 Sep 2016 03:14:34 -0400
+
 python3-defaults (3.5.1-4) unstable; urgency=medium
 
   * Bump standards version.

=== modified file 'debpython/files.py'
--- debpython/files.py	2013-07-14 12:09:52 +0000
+++ debpython/files.py	2016-09-18 06:53:34 +0000
@@ -44,7 +44,11 @@
 
 
 def from_package(package_name, extensions=('.py',)):
-    """Generate *.py file names available in given package."""
+    """Generate *.py file names available in given package.
+
+    This function is deprecated because it is not Multi-Arch aware.
+    Use uniquely_from_package() instead.
+    """
     extensions = tuple(extensions)  # .endswith doesn't like list
     process = Popen("/usr/bin/dpkg -L %s" % package_name,
                     shell=True, stdout=PIPE)
@@ -56,6 +60,56 @@
         if line.endswith(extensions):
             yield line
 
+def uniquely_from_package(package_name, extensions=('.py',)):
+    """Generate *.py file names uniquely provided by a given package.
+
+    The given package should specify an architecture using dpkg's pkg:arch
+    syntax.  If other instances of the package are installed, the contents
+    of those instances will be excluded, most likely causing the resulting
+    list to be empty.
+
+    In the case that the package is not uniquely specified due to the
+    architecture being omited, an architecture will be chosen arbitrarily
+    and no files will be excluded on non-uniqueness grounds.  This behavior
+    makes this function safe to call in the "don't care" scenario when all
+    architecture varients provide the same *.py files and we don't care
+    which one is selected.
+    """
+    pkg,*arch = package_name.split(':')
+    process = Popen("dpkg-query -f'${Architecture}\n' -W %s" % pkg,
+                    shell=True,stdout=PIPE)
+    stdout, stderr = process.communicate()
+    if process.returncode != 0:
+        raise Exception("cannot determine installed architectures for %s" % pkg)
+    stdout = str(stdout, 'utf-8')
+    other_archs = list()
+    for x in stdout.splitlines():
+        if not x in arch:
+            other_archs.append(x)
+    # Now we ensure:
+    #  1. package_name specifies a package and an architecture.
+    #  2. other_archs is a list of architecture instances to exclude from our output.
+    package_name, other_archs = pkg+":"+(arch or other_archs)[0], arch and other_archs
+    def dpkg_list(package_name):
+        process = Popen("dpkg-query -L %s" % package_name,
+                        shell=True,stdout=PIPE)
+        stdout, stderr = process.communicate()
+        if process.returncode != 0:
+            raise Exception("cannot get content of %s" % package_name)
+        stdout = str(stdout,'utf-8')
+        return set(stdout.splitlines())
+    s = dpkg_list(package_name)
+    for a in other_archs:
+        s = s - dpkg_list(pkg+":"+a)
+    # Finally we filter out all but files of the desired extension.
+    # Technically, it would be more efficient to filter this earlier,
+    # but I coded it this way intentionally on the off chance that
+    # dpkg-query may someday support a --list-unique-files option that
+    # could replace all of the code above this.
+    for line in s:
+        if line.endswith(extensions):
+            yield line
+
 
 def filter_directory(files, dname):
     """Generate *.py file names that match given directory."""

=== modified file 'py3clean'
--- py3clean	2014-11-29 17:40:52 +0000
+++ py3clean	2016-09-18 06:56:47 +0000
@@ -193,7 +193,7 @@
 
     if options.package:
         log.info('cleaning package %s', options.package)
-        pfiles = set(dpf.from_package(options.package))
+        pfiles = set(dpf.uniquely_from_package(options.package))
 
     if args:
         log.info('cleaning directories: %s', args)

=== modified file 'py3compile'
--- py3compile	2013-07-14 12:09:52 +0000
+++ py3compile	2016-09-18 06:57:29 +0000
@@ -248,7 +248,7 @@
         compile_versions = debsorted(versions)[:1]
         log.debug('compile versions: %s', versions)
 
-        pkg_files = tuple(dpf.from_package(options.package))
+        pkg_files = tuple(dpf.uniquely_from_package(options.package))
         for item in args:
             e_patterns = get_exclude_patterns(item, options.regexpr,
                                               compile_versions)
@@ -264,7 +264,7 @@
         # no need to limit versions here, it's either pyr mode or version is
         # hardcoded in path / via -V option
         e_patterns = get_exclude_patterns()
-        files = dpf.from_package(options.package)
+        files = dpf.uniquely_from_package(options.package)
         files = dpf.filter_public(files, versions)
         compile(files, versions,
                 options.force, options.optimize, e_patterns)

Reply via email to