diff --git a/deb_pkg_tools/package.py b/deb_pkg_tools/package.py index 07cd61a..0a6091f 100644 --- a/deb_pkg_tools/package.py +++ b/deb_pkg_tools/package.py @@ -1,7 +1,7 @@ # Debian packaging tools: Package manipulation. # # Author: Peter Odding -# Last Change: April 19, 2020 +# Last Change: April 25, 2020 # URL: https://github.com/xolox/python-deb-pkg-tools """Functions to build and inspect Debian binary package archives (``*.deb`` files).""" @@ -717,21 +717,30 @@ def inspect_package_contents(archive, cache=None): # lrwxrwxrwx root/root 0 2013-09-26 22:29 ./usr/bin/pdb2.7 -> ../lib/python2.7/pdb.py fields = line.split(None, 5) permissions = fields[0] - owner, group = fields[1].split('/') - size = int(fields[2]) + owner, _, group = fields[1].partition('/') + # The third field (index 2) is normally the file size, but for device + # files it gives the comma separated device type major / minor numbers. + # More details: https://github.com/xolox/python-deb-pkg-tools/pull/22 + if fields[2].isdigit(): + device_type = 0, 0 + size = int(fields[2]) + else: + major_nr, _, minor_nr = fields[2].partition(',') + device_type = int(major_nr), int(minor_nr) + size = 0 modified = fields[3] + ' ' + fields[4] pathname = re.sub('^./', '/', fields[5]) pathname, _, target = pathname.partition(' -> ') if not target: pathname, _, target = pathname.partition(' link to ') target = re.sub('^./', '/', target) - contents[pathname] = ArchiveEntry(permissions, owner, group, size, modified, target) + contents[pathname] = ArchiveEntry(permissions, owner, group, size, modified, target, device_type) if cache: entry.set_value(contents) return contents -class ArchiveEntry(collections.namedtuple('ArchiveEntry', 'permissions, owner, group, size, modified, target')): +class ArchiveEntry(collections.namedtuple('ArchiveEntry', 'permissions, owner, group, size, modified, target, device_type')): """ A named tuple with the result of :func:`inspect_package()`. @@ -759,6 +768,23 @@ class ArchiveEntry(collections.namedtuple('ArchiveEntry', 'permissions, owner, g .. :attribute:: modified A string like ``2013-09-26 22:28``. + + .. :attribute:: target + + If the entry represents a symbolic link this field gives the pathname of + the target of the symbolic link. Defaults to an empty string. + + .. :attribute:: device_type + + If the entry represents a device file this field gives the device type + major and minor numbers as a tuple of two integers. Defaults to a tuple + with two zeros. + + .. note:: This defaults to a tuple with two zeros so that + :class:`ArchiveEntry` tuples can be reliably sorted just like + regular tuples (i.e. without getting + :exc:`~exceptions.TypeError` exceptions due to comparisons + between incompatible value types). """ diff --git a/deb_pkg_tools/tests.py b/deb_pkg_tools/tests.py index f9e7a79..8274cff 100644 --- a/deb_pkg_tools/tests.py +++ b/deb_pkg_tools/tests.py @@ -1,7 +1,7 @@ # Debian packaging tools: Automated tests. # # Author: Peter Odding -# Last Change: April 19, 2020 +# Last Change: April 25, 2020 # URL: https://github.com/xolox/python-deb-pkg-tools """Test suite for the `deb-pkg-tools` package.""" @@ -59,6 +59,7 @@ find_system_dependencies, group_by_latest_versions, inspect_package, + inspect_package_contents, parse_filename, ) from deb_pkg_tools.printer import CustomPrettyPrinter @@ -152,6 +153,35 @@ def test_package_cache_invalidation(self): else: self.load_package_cache() + def test_inspect_contents(self): + """Test inspection of package contents.""" + if os.getuid() != 0: + self.skipTest("need superuser privileges") + with Context() as finalizers: + build_directory = finalizers.mkdtemp() + create_control_file(os.path.join(build_directory, 'DEBIAN', 'control'), { + 'Description': 'Bogus value for mandatory field', + 'Maintainer': 'Peter Odding', + 'Package': 'deb-pkg-tools-contents-test', + 'Version': '1', + }) + # Create a regular file entry. + touch(os.path.join(build_directory, 'regular-file-test')) + # Create a device file entry. + execute('cp', '-a', '/dev/null', os.path.join(build_directory, 'device-file-test')) + # Build the package and inspect its contents. + repository_directory = finalizers.mkdtemp() + package_file = build_package(build_directory, repository_directory) + contents = inspect_package_contents(package_file) + # Make sure the device type field is populated for the device file entry. + device_file_entry = contents['/device-file-test'] + assert device_file_entry.device_type[0] > 0 + assert device_file_entry.device_type[1] > 0 + # Make sure the device type field is not populated for the regular entry. + regular_file_entry = contents['/regular-file-test'] + assert regular_file_entry.device_type[0] == 0 + assert regular_file_entry.device_type[1] == 0 + def test_architecture_determination(self): """Make sure discovery of the current build architecture works properly.""" valid_architectures = execute('dpkg-architecture', '-L', capture=True).splitlines()