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)