def test_conversion_of_package_with_dependencies(self): """ Convert a non trivial Python package with several dependencies. Converts deb-pkg-tools_ to a Debian package archive and sanity checks the result. Performs static checks on the metadata (dependencies) of the resulting package archive. .. _deb-pkg-tools: https://pypi.python.org/pypi/deb-pkg-tools """ # Use a temporary directory as py2deb's repository directory so that we # can easily find the *.deb archive generated by py2deb. with TemporaryDirectory() as directory: # Run the conversion command. py2deb('--repository=%s' % directory, 'deb-pkg-tools==1.22') # Find the generated Debian package archives. archives = glob.glob('%s/*.deb' % directory) logger.debug("Found generated archive(s): %s", archives) # Make sure the expected dependencies have been converted. converted_dependencies = set( parse_filename(a).name for a in archives) expected_dependencies = set([ 'python-cached-property', 'python-chardet', 'python-coloredlogs', 'python-deb-pkg-tools', 'python-debian', 'python-executor', 'python-humanfriendly', 'python-six', ]) assert expected_dependencies.issubset(converted_dependencies) # Use deb-pkg-tools to inspect ... deb-pkg-tools :-) pathname = find_package_archive(archives, 'python-deb-pkg-tools') metadata, contents = inspect_package(pathname) logger.debug("Metadata of generated package: %s", dict(metadata)) logger.debug("Contents of generated package: %s", dict(contents)) # Make sure the dependencies defined in `stdeb.cfg' have been preserved. for configured_dependency in [ 'apt', 'apt-utils', 'dpkg-dev', 'fakeroot', 'gnupg', 'lintian' ]: logger.debug("Checking configured dependency %s ..", configured_dependency) assert metadata['Depends'].matches( configured_dependency) is not None # Make sure the dependencies defined in `setup.py' have been preserved. expected_dependencies = [ 'python-chardet', 'python-coloredlogs', 'python-debian', 'python-executor', 'python-humanfriendly' ] for python_dependency in expected_dependencies: logger.debug("Checking Python dependency %s ..", python_dependency) assert metadata['Depends'].matches( python_dependency) is not None
def test_filename_parsing(self): # Test the happy path. filename = '/var/cache/apt/archives/python2.7_2.7.3-0ubuntu3.4_amd64.deb' components = parse_filename(filename) self.assertEqual(components.filename, filename) self.assertEqual(components.name, 'python2.7') self.assertEqual(components.version, '2.7.3-0ubuntu3.4') self.assertEqual(components.architecture, 'amd64') # Test the unhappy paths. self.assertRaises(ValueError, parse_filename, 'python2.7_2.7.3-0ubuntu3.4_amd64.not-a-deb') self.assertRaises(ValueError, parse_filename, 'python2.7.deb')
def check_converted_pip_accel_packages(self, directory): """ Check a group of packages converted with a custom name and installation prefix. Check the results of :func:`test_conversion_of_isolated_packages()` and :func:`test_conversion_with_configuration_file()`. """ # Find the generated Debian package archives. archives = glob.glob('%s/*.deb' % directory) logger.debug("Found generated archive(s): %s", archives) # Make sure the expected dependencies have been converted. converted_dependencies = set(parse_filename(a).name for a in archives) expected_dependencies = set([ 'pip-accel', 'pip-accel-coloredlogs-renamed', 'pip-accel-humanfriendly', 'pip-accel-pip', ]) assert expected_dependencies.issubset(converted_dependencies) # Use deb-pkg-tools to inspect pip-accel. pathname = find_package_archive(archives, 'pip-accel') metadata, contents = inspect_package(pathname) logger.debug("Metadata of generated package: %s", dict(metadata)) logger.debug("Contents of generated package: %s", dict(contents)) # Make sure the dependencies defined in `setup.py' have been # preserved while their names have been converted. assert metadata['Depends'].matches('pip-accel-coloredlogs-renamed', '0.4.6') assert metadata['Depends'].matches('pip-accel-humanfriendly', '1.6') assert metadata['Depends'].matches('pip-accel-pip', '1.4') assert not metadata['Depends'].matches('pip-accel-pip', '1.3') assert not metadata['Depends'].matches('pip-accel-pip', '1.5') # Make sure the executable script has been installed and is marked as executable. pip_accel_executable = find_file(contents, '/usr/lib/pip-accel/bin/pip-accel') assert pip_accel_executable.permissions == '-rwxr-xr-x' # Verify the existence of some expected files (picked more or less at random). assert find_file(contents, '/usr/lib/pip-accel/lib/pip_accel/__init__.py') assert find_file(contents, '/usr/lib/pip-accel/lib/pip_accel/deps/debian.ini') assert find_file( contents, '/usr/lib/pip-accel/lib/pip_accel-0.12.6*.egg-info/PKG-INFO') # Verify that all files are installed in the custom installation # prefix. We have to ignore directories, otherwise we would start # complaining about the parent directories /, /usr, /usr/lib, etc. paths_to_ignore = ['/usr/share/lintian/overrides/pip-accel'] for filename, properties in contents.items(): if filename not in paths_to_ignore: is_directory = properties.permissions.startswith('d') in_isolated_directory = filename.startswith( '/usr/lib/pip-accel/') assert is_directory or in_isolated_directory
def test_filename_parsing(self): """Test filename parsing.""" # Test the happy path. filename = '/var/cache/apt/archives/python2.7_2.7.3-0ubuntu3.4_amd64.deb' components = parse_filename(filename) assert components.filename == filename assert components.name == 'python2.7' assert components.version == '2.7.3-0ubuntu3.4' assert components.architecture == 'amd64' # Test the unhappy paths. self.assertRaises(ValueError, parse_filename, 'python2.7_2.7.3-0ubuntu3.4_amd64.not-a-deb') self.assertRaises(ValueError, parse_filename, 'python2.7.deb')
def test_conversion_of_package_with_dependencies(self): """ Convert a non trivial Python package with several dependencies. Converts deb-pkg-tools_ to a Debian package archive and sanity checks the result. Performs static checks on the metadata (dependencies) of the resulting package archive. .. _deb-pkg-tools: https://pypi.python.org/pypi/deb-pkg-tools """ # Use a temporary directory as py2deb's repository directory so that we # can easily find the *.deb archive generated by py2deb. with TemporaryDirectory() as directory: # Run the conversion command. py2deb('--repository=%s' % directory, 'deb-pkg-tools==1.22') # Find the generated Debian package archives. archives = glob.glob('%s/*.deb' % directory) logger.debug("Found generated archive(s): %s", archives) # Make sure the expected dependencies have been converted. converted_dependencies = set(parse_filename(a).name for a in archives) expected_dependencies = set([ 'python-cached-property', 'python-chardet', 'python-coloredlogs', 'python-deb-pkg-tools', 'python-debian', 'python-executor', 'python-humanfriendly', 'python-six', ]) assert expected_dependencies.issubset(converted_dependencies) # Use deb-pkg-tools to inspect ... deb-pkg-tools :-) pathname = find_package_archive(archives, 'python-deb-pkg-tools') metadata, contents = inspect_package(pathname) logger.debug("Metadata of generated package: %s", dict(metadata)) logger.debug("Contents of generated package: %s", dict(contents)) # Make sure the dependencies defined in `stdeb.cfg' have been preserved. for configured_dependency in ['apt', 'apt-utils', 'dpkg-dev', 'fakeroot', 'gnupg', 'lintian']: logger.debug("Checking configured dependency %s ..", configured_dependency) assert metadata['Depends'].matches(configured_dependency) is not None # Make sure the dependencies defined in `setup.py' have been preserved. expected_dependencies = [ 'python-chardet', 'python-coloredlogs', 'python-debian', 'python-executor', 'python-humanfriendly' ] for python_dependency in expected_dependencies: logger.debug("Checking Python dependency %s ..", python_dependency) assert metadata['Depends'].matches(python_dependency) is not None
def collect_packages(archives, directory, prompt=True, cache=None): # Find all related packages. related_archives = set() for filename in archives: related_archives.add(parse_filename(filename)) related_archives.update(collect_related_packages(filename, cache=cache)) # Ignore package archives that are already in the target directory. relevant_archives = set() for archive in related_archives: basename = os.path.basename(archive.filename) if not os.path.isfile(os.path.join(directory, basename)): relevant_archives.add(archive) # Interactively move the package archives. if relevant_archives: relevant_archives = sorted(relevant_archives) pluralized = pluralize(len(relevant_archives), "package archive", "package archives") print("Found %s:" % pluralized) for file_to_collect in relevant_archives: print(" - %s" % format_path(file_to_collect.filename)) try: if prompt: # Ask permission to copy the file(s). prompt = "Copy %s to %s? [Y/n] " % (pluralized, format_path(directory)) assert raw_input(prompt).lower() in ('', 'y', 'yes') # Copy the file(s). for file_to_collect in relevant_archives: copy_from = file_to_collect.filename copy_to = os.path.join(directory, os.path.basename(copy_from)) logger.debug("Copying %s -> %s ..", format_path(copy_from), format_path(copy_to)) shutil.copy(copy_from, copy_to) logger.info("Done! Copied %s to %s.", pluralized, format_path(directory)) except (AssertionError, KeyboardInterrupt, EOFError) as e: if isinstance(e, KeyboardInterrupt): # Control-C interrupts the prompt without emitting a newline. We'll # print one manually so the console output doesn't look funny. sys.stderr.write('\n') logger.warning("Not copying archive(s) to %s! (aborted by user)", format_path(directory)) if isinstance(e, KeyboardInterrupt): # Maybe we shouldn't actually swallow Control-C, it can make # for a very unfriendly user experience... :-) raise
def check_converted_pip_accel_packages(self, directory): """ Check a group of packages converted with a custom name and installation prefix. Check the results of :py:func:`test_conversion_of_isolated_packages()` and :py:func:`test_conversion_with_configuration_file()`. """ # Find the generated Debian package archives. archives = glob.glob('%s/*.deb' % directory) logger.debug("Found generated archive(s): %s", archives) # Make sure the expected dependencies have been converted. converted_dependencies = set(parse_filename(a).name for a in archives) expected_dependencies = set([ 'pip-accel', 'pip-accel-coloredlogs-renamed', 'pip-accel-humanfriendly', 'pip-accel-pip', ]) assert expected_dependencies.issubset(converted_dependencies) # Use deb-pkg-tools to inspect pip-accel. pathname = find_package_archive(archives, 'pip-accel') metadata, contents = inspect_package(pathname) logger.debug("Metadata of generated package: %s", dict(metadata)) logger.debug("Contents of generated package: %s", dict(contents)) # Make sure the dependencies defined in `setup.py' have been # preserved while their names have been converted. assert metadata['Depends'].matches('pip-accel-coloredlogs-renamed', '0.4.6') assert metadata['Depends'].matches('pip-accel-humanfriendly', '1.6') assert metadata['Depends'].matches('pip-accel-pip', '1.4') assert not metadata['Depends'].matches('pip-accel-pip', '1.3') assert not metadata['Depends'].matches('pip-accel-pip', '1.5') # Make sure the executable script has been installed and is marked as executable. pip_accel_executable = find_file(contents, '/usr/lib/pip-accel/bin/pip-accel') assert pip_accel_executable.permissions == '-rwxr-xr-x' # Verify the existence of some expected files (picked more or less at random). assert find_file(contents, '/usr/lib/pip-accel/lib/pip_accel/__init__.py') assert find_file(contents, '/usr/lib/pip-accel/lib/pip_accel/deps/debian.ini') assert find_file(contents, '/usr/lib/pip-accel/lib/pip_accel-0.12.6*.egg-info/PKG-INFO') # Verify that all files are installed in the custom installation # prefix. We have to ignore directories, otherwise we would start # complaining about the parent directories /, /usr, /usr/lib, etc. paths_to_ignore = ['/usr/share/lintian/overrides/pip-accel'] for filename, properties in contents.items(): if filename not in paths_to_ignore: is_directory = properties.permissions.startswith('d') in_isolated_directory = filename.startswith('/usr/lib/pip-accel/') assert is_directory or in_isolated_directory
def find_package_archive(available_archives, package_name): """ Find the ``*.deb`` archive of a specific package. :param available_packages: The pathnames of the available package archives (a list of strings). :param package_name: The name of the package whose archive file we're interested in (a string). :returns: The pathname of the package archive (a string). :raises: :exc:`exceptions.AssertionError` if zero or more than one package archive is found. """ matches = [] for pathname in available_archives: if parse_filename(pathname).name == package_name: matches.append(pathname) assert len(matches) == 1, "Expected to match exactly one package archive!" return matches[0]
def find_package_archive(available_archives, package_name): """ Find the ``*.deb`` archive of a specific package. :param available_packages: The pathnames of the available package archives (a list of strings). :param package_name: The name of the package whose archive file we're interested in (a string). :returns: The pathname of the package archive (a string). :raises: :py:exc:`exceptions.AssertionError` if zero or more than one package archive is found. """ matches = [] for pathname in available_archives: if parse_filename(pathname).name == package_name: matches.append(pathname) assert len(matches) == 1, "Expected to match exactly one package archive!" return matches[0]