def test_control_field_parsing(self): deb822_package = Deb822([ 'Package: python-py2deb', 'Depends: python-deb-pkg-tools, python-pip, python-pip-accel', 'Installed-Size: 42' ]) parsed_info = parse_control_fields(deb822_package) self.assertEqual( parsed_info, { 'Package': 'python-py2deb', 'Depends': RelationshipSet(Relationship(name=u'python-deb-pkg-tools'), Relationship(name=u'python-pip'), Relationship(name=u'python-pip-accel')), 'Installed-Size': 42 }) # Test backwards compatibility with the old interface where `Depends' # like fields were represented as a list of strings (shallow parsed). parsed_info['Depends'] = [unicode(r) for r in parsed_info['Depends']] self.assertEqual(unparse_control_fields(parsed_info), deb822_package) # Test compatibility with fields like `Depends' containing a string. parsed_info['Depends'] = deb822_package['Depends'] self.assertEqual(unparse_control_fields(parsed_info), deb822_package)
def test_control_field_parsing(self): """Test the parsing of control file fields.""" deb822_package = Deb822([ 'Package: python-py2deb', 'Depends: python-deb-pkg-tools, python-pip, python-pip-accel', 'Installed-Size: 42' ]) parsed_info = parse_control_fields(deb822_package) assert parsed_info == { 'Package': 'python-py2deb', 'Depends': RelationshipSet(Relationship(name=u'python-deb-pkg-tools'), Relationship(name=u'python-pip'), Relationship(name=u'python-pip-accel')), 'Installed-Size': 42 } # Test backwards compatibility with the old interface where `Depends' # like fields were represented as a list of strings (shallow parsed). parsed_info['Depends'] = [text_type(r) for r in parsed_info['Depends']] assert unparse_control_fields(parsed_info) == deb822_package # Test compatibility with fields like `Depends' containing a string. parsed_info['Depends'] = deb822_package['Depends'] assert unparse_control_fields(parsed_info) == deb822_package
def scan_packages(repository, packages_file=None, cache=None): """ A reimplementation of the ``dpkg-scanpackages -m`` command in Python. Updates a ``Packages`` file based on the Debian package archive(s) found in the given directory. Uses :py:class:`.PackageCache` to (optionally) speed up the process significantly by caching package metadata and hashes on disk. This explains why this function can be much faster than ``dpkg-scanpackages -m``. :param repository: The pathname of a directory containing Debian package archives (a string). :param packages_file: The pathname of the ``Packages`` file to update (a string). Defaults to the ``Packages`` file in the given directory. :param cache: The :py:class:`.PackageCache` to use (defaults to ``None``). """ # By default the `Packages' file inside the repository is updated. if not packages_file: packages_file = os.path.join(repository, 'Packages') # Update the `Packages' file. timer = Timer() package_archives = glob.glob(os.path.join(repository, '*.deb')) num_packages = len(package_archives) spinner = Spinner(total=num_packages) with open(packages_file, 'wb') as handle: for i, archive in enumerate(optimize_order(package_archives), start=1): fields = dict(inspect_package_fields(archive, cache=cache)) fields.update(get_packages_entry(archive, cache=cache)) deb822_dict = unparse_control_fields(fields) deb822_dict.dump(handle) handle.write(b'\n') spinner.step(label="Scanning package metadata", progress=i) spinner.clear() logger.debug("Wrote %i entries to output Packages file in %s.", num_packages, timer)
def test_control_field_parsing(self): deb822_package = Deb822(['Package: python-py2deb', 'Depends: python-deb-pkg-tools, python-pip, python-pip-accel', 'Installed-Size: 42']) parsed_info = parse_control_fields(deb822_package) self.assertEqual(parsed_info, {'Package': 'python-py2deb', 'Depends': RelationshipSet( Relationship(name=u'python-deb-pkg-tools'), Relationship(name=u'python-pip'), Relationship(name=u'python-pip-accel')), 'Installed-Size': 42}) # Test backwards compatibility with the old interface where `Depends' # like fields were represented as a list of strings (shallow parsed). parsed_info['Depends'] = [unicode(r) for r in parsed_info['Depends']] self.assertEqual(unparse_control_fields(parsed_info), deb822_package) # Test compatibility with fields like `Depends' containing a string. parsed_info['Depends'] = deb822_package['Depends'] self.assertEqual(unparse_control_fields(parsed_info), deb822_package)
def test_control_field_parsing(self): """Test the parsing of control file fields.""" deb822_package = Deb822(['Package: python-py2deb', 'Depends: python-deb-pkg-tools, python-pip, python-pip-accel', 'Installed-Size: 42']) parsed_info = parse_control_fields(deb822_package) assert parsed_info == {'Package': 'python-py2deb', 'Depends': RelationshipSet( Relationship(name=u'python-deb-pkg-tools'), Relationship(name=u'python-pip'), Relationship(name=u'python-pip-accel')), 'Installed-Size': 42} # Test backwards compatibility with the old interface where `Depends' # like fields were represented as a list of strings (shallow parsed). parsed_info['Depends'] = [text_type(r) for r in parsed_info['Depends']] assert unparse_control_fields(parsed_info) == deb822_package # Test compatibility with fields like `Depends' containing a string. parsed_info['Depends'] = deb822_package['Depends'] assert unparse_control_fields(parsed_info) == deb822_package
def scan_packages(repository, packages_file=None, cache=None): """ A reimplementation of the ``dpkg-scanpackages -m`` command in Python. Updates a ``Packages`` file based on the Debian package archive(s) found in the given directory. Uses :class:`.PackageCache` to (optionally) speed up the process significantly by caching package metadata and hashes on disk. This explains why this function can be much faster than ``dpkg-scanpackages -m``. :param repository: The pathname of a directory containing Debian package archives (a string). :param packages_file: The pathname of the ``Packages`` file to update (a string). Defaults to the ``Packages`` file in the given directory. :param cache: The :class:`.PackageCache` to use (defaults to :data:`None`). """ # By default the `Packages' file inside the repository is updated. if not packages_file: packages_file = os.path.join(repository, 'Packages') # Update the `Packages' file. timer = Timer() package_archives = glob.glob(os.path.join(repository, '*.deb')) num_packages = len(package_archives) spinner = Spinner(total=num_packages) with open(packages_file, 'wb') as handle: for i, archive in enumerate(optimize_order(package_archives), start=1): fields = dict(inspect_package_fields(archive, cache=cache)) fields.update(get_packages_entry(archive, cache=cache)) deb822_dict = unparse_control_fields(fields) deb822_dict.dump(handle) handle.write(b'\n') spinner.step(label="Scanning package metadata", progress=i) spinner.clear() logger.debug("Wrote %i entries to output Packages file in %s.", num_packages, timer)
def convert(self): """ Convert current package from Python package to Debian package. :returns: The pathname of the generated ``*.deb`` archive. """ with TemporaryDirectory(prefix='py2deb-build-') as build_directory: # Prepare the absolute pathname of the Python interpreter on the # target system. This pathname will be embedded in the first line # of executable scripts (including the post-installation and # pre-removal scripts). python_executable = '/usr/bin/%s' % python_version() # Unpack the binary distribution archive provided by pip-accel inside our build directory. build_install_prefix = os.path.join( build_directory, self.converter.install_prefix.lstrip('/')) self.converter.pip_accel.bdists.install_binary_dist( members=self.transform_binary_dist(python_executable), prefix=build_install_prefix, python=python_executable, virtualenv_compatible=False, ) # Determine the directory (at build time) where the *.py files for # Python modules are located (the site-packages equivalent). if self.has_custom_install_prefix: build_modules_directory = os.path.join(build_install_prefix, 'lib') else: # The /py*/ pattern below is intended to match both /pythonX.Y/ and /pypyX.Y/. dist_packages_directories = glob.glob( os.path.join(build_install_prefix, 'lib/py*/dist-packages')) if len(dist_packages_directories) != 1: msg = "Expected to find a single 'dist-packages' directory inside converted package!" raise Exception(msg) build_modules_directory = dist_packages_directories[0] # Determine the directory (at installation time) where the *.py # files for Python modules are located. install_modules_directory = os.path.join( '/', os.path.relpath(build_modules_directory, build_directory)) # Execute a user defined command inside the directory where the Python modules are installed. command = self.converter.scripts.get(self.python_name.lower()) if command: execute(command, directory=build_modules_directory, logger=logger) # Determine the package's dependencies, starting with the currently # running version of Python and the Python requirements converted # to Debian packages. dependencies = [python_version()] + self.debian_dependencies # Check if the converted package contains any compiled *.so files. object_files = find_object_files(build_directory) if object_files: # Strip debugging symbols from the object files. strip_object_files(object_files) # Determine system dependencies by analyzing the linkage of the # *.so file(s) found in the converted package. dependencies += find_system_dependencies(object_files) # Make up some control file fields ... :-) architecture = self.determine_package_architecture(object_files) control_fields = unparse_control_fields( dict(package=self.debian_name, version=self.debian_version, maintainer=self.debian_maintainer, description=self.debian_description, architecture=architecture, depends=dependencies, priority='optional', section='python')) # Automatically add the Mercurial global revision id when available. if self.vcs_revision: control_fields['Vcs-Hg'] = self.vcs_revision # Apply user defined control field overrides from `stdeb.cfg'. control_fields = self.load_control_field_overrides(control_fields) # Create the DEBIAN directory. debian_directory = os.path.join(build_directory, 'DEBIAN') os.mkdir(debian_directory) # Generate the DEBIAN/control file. control_file = os.path.join(debian_directory, 'control') logger.debug("Saving control file fields to %s: %s", control_file, control_fields) with open(control_file, 'wb') as handle: control_fields.dump(handle) # Lintian is a useful tool to find mistakes in Debian binary # packages however Lintian checks from the perspective of a package # included in the official Debian repositories. Because py2deb # doesn't and probably never will generate such packages some # messages emitted by Lintian are useless (they merely point out # how the internals of py2deb work). Because of this we silence # `known to be irrelevant' messages from Lintian using overrides. if self.converter.lintian_ignore: overrides_directory = os.path.join( build_directory, 'usr', 'share', 'lintian', 'overrides', ) overrides_file = os.path.join(overrides_directory, self.debian_name) os.makedirs(overrides_directory) with open(overrides_file, 'w') as handle: for tag in self.converter.lintian_ignore: handle.write('%s: %s\n' % (self.debian_name, tag)) # Find the alternatives relevant to the package we're building. alternatives = set( (link, path) for link, path in self.converter.alternatives if os.path.isfile( os.path.join(build_directory, path.lstrip('/')))) # Generate post-installation and pre-removal maintainer scripts. self.generate_maintainer_script( filename=os.path.join(debian_directory, 'postinst'), python_executable=python_executable, function='post_installation_hook', package_name=self.debian_name, alternatives=alternatives, modules_directory=install_modules_directory, namespaces=self.namespaces) self.generate_maintainer_script( filename=os.path.join(debian_directory, 'prerm'), python_executable=python_executable, function='pre_removal_hook', package_name=self.debian_name, alternatives=alternatives, modules_directory=install_modules_directory, namespaces=self.namespaces) # Enable a user defined Python callback to manipulate the resulting # binary package before it's turned into a *.deb archive (e.g. # manipulate the contents or change the package metadata). if self.converter.python_callback: logger.debug("Invoking user defined Python callback ..") self.converter.python_callback(self.converter, self, build_directory) logger.debug("User defined Python callback finished!") return build_package(directory=build_directory, check_package=self.converter.lintian_enabled, copy_files=False)
def convert(self): """ Convert current package from Python package to Debian package. :returns: The pathname of the generated ``*.deb`` archive. """ with TemporaryDirectory(prefix='py2deb-build-') as build_directory: # Prepare the absolute pathname of the Python interpreter on the # target system. This pathname will be embedded in the first line # of executable scripts (including the post-installation and # pre-removal scripts). python_executable = '/usr/bin/%s' % python_version() # Unpack the binary distribution archive provided by pip-accel inside our build directory. build_install_prefix = os.path.join(build_directory, self.converter.install_prefix.lstrip('/')) self.converter.pip_accel.bdists.install_binary_dist( members=self.transform_binary_dist(), prefix=build_install_prefix, python=python_executable, virtualenv_compatible=False, ) # Determine the directory (at build time) where the *.py files for # Python modules are located (the site-packages equivalent). if self.has_custom_install_prefix: build_modules_directory = os.path.join(build_install_prefix, 'lib') else: dist_packages_directories = glob.glob(os.path.join(build_install_prefix, 'lib/python*/dist-packages')) if len(dist_packages_directories) != 1: msg = "Expected to find a single 'dist-packages' directory inside converted package!" raise Exception(msg) build_modules_directory = dist_packages_directories[0] # Determine the directory (at installation time) where the *.py # files for Python modules are located. install_modules_directory = os.path.join('/', os.path.relpath(build_modules_directory, build_directory)) # Execute a user defined command inside the directory where the Python modules are installed. command = self.converter.scripts.get(self.python_name.lower()) if command: execute(command, directory=build_modules_directory, logger=logger) # Determine the package's dependencies, starting with the currently # running version of Python and the Python requirements converted # to Debian packages. dependencies = [python_version()] + self.debian_dependencies # Check if the converted package contains any compiled *.so files. shared_object_files = self.find_shared_object_files(build_directory) if shared_object_files: # Determine system dependencies by analyzing the linkage of the # *.so file(s) found in the converted package. dependencies += self.find_system_dependencies(shared_object_files) # Make up some control file fields ... :-) architecture = self.determine_package_architecture(shared_object_files) control_fields = unparse_control_fields(dict(package=self.debian_name, version=self.debian_version, maintainer=self.debian_maintainer, description=self.debian_description, architecture=architecture, depends=dependencies, priority='optional', section='python')) # Automatically add the Mercurial global revision id when available. if self.vcs_revision: control_fields['Vcs-Hg'] = self.vcs_revision # Apply user defined control field overrides from `stdeb.cfg'. control_fields = self.load_control_field_overrides(control_fields) # Create the DEBIAN directory. debian_directory = os.path.join(build_directory, 'DEBIAN') os.mkdir(debian_directory) # Generate the DEBIAN/control file. control_file = os.path.join(debian_directory, 'control') logger.debug("Saving control file fields to %s: %s", control_file, control_fields) with open(control_file, 'wb') as handle: control_fields.dump(handle) # Lintian is a useful tool to find mistakes in Debian binary # packages however Lintian checks from the perspective of a package # included in the official Debian repositories. Because py2deb # doesn't and probably never will generate such packages some # messages emitted by Lintian are useless (they merely point out # how the internals of py2deb work). Because of this we silence # `known to be irrelevant' messages from Lintian using overrides. overrides_directory = os.path.join(build_directory, 'usr', 'share', 'lintian', 'overrides') overrides_file = os.path.join(overrides_directory, self.debian_name) os.makedirs(overrides_directory) with open(overrides_file, 'w') as handle: for tag in ['debian-changelog-file-missing', 'embedded-javascript-library', 'extra-license-file', 'unknown-control-interpreter', 'vcs-field-uses-unknown-uri-format']: handle.write('%s: %s\n' % (self.debian_name, tag)) # Find the alternatives relevant to the package we're building. alternatives = set((link, path) for link, path in self.converter.alternatives if os.path.isfile(os.path.join(build_directory, path.lstrip('/')))) # Generate post-installation and pre-removal maintainer scripts. self.generate_maintainer_script(filename=os.path.join(debian_directory, 'postinst'), python_executable=python_executable, function='post_installation_hook', package_name=self.debian_name, alternatives=alternatives, modules_directory=install_modules_directory, namespaces=self.namespaces) self.generate_maintainer_script(filename=os.path.join(debian_directory, 'prerm'), python_executable=python_executable, function='pre_removal_hook', package_name=self.debian_name, alternatives=alternatives, modules_directory=install_modules_directory, namespaces=self.namespaces) # Enable a user defined Python callback to manipulate the resulting # binary package before it's turned into a *.deb archive (e.g. # manipulate the contents or change the package metadata). if self.converter.python_callback: logger.debug("Invoking user defined Python callback ..") self.converter.python_callback(self.converter, self, build_directory) logger.debug("User defined Python callback finished!") return build_package(directory=build_directory, check_package=self.converter.lintian_enabled, copy_files=False)
def inspect(path): sys.stdout.write('Inspecting: {0}\n'.format(path)) info = repo.inspect_package_fields(path) info = dict(info) info.update(repo.get_packages_entry(path)) return dict(control.unparse_control_fields(info))