async def test_bundle(): descriptor = PackageDescriptor('some/path') descriptor.name = 'python_package' deps = [DependencyDescriptor('pkg1', metadata={'version_eq': '1.3.2'}), DependencyDescriptor('pkg_in_workspace'), DependencyDescriptor('pkg2', metadata={'version_lt': '1.2'}), 'ignored_pkg'] descriptor.dependencies['run'] = deps workspace = {'pkg_in_workspace': 'path/to/pkg'} task_args = MagicMock(build_base='build/base', install_base='install/base', bundle_base='bundle/base') pip_installer = MagicMock() apt_installer = MagicMock() args = BundlePackageArguments( descriptor, {'pip3': pip_installer, 'apt': apt_installer}, task_args) context = TaskContext(pkg=descriptor, args=args, dependencies=workspace) task = PythonBundleTask() task.set_context(context=context) await task.bundle() pip_installer.add_to_install_list.assert_called_once_with('pkg1==1.3.2') apt_calls = apt_installer.add_to_install_list.call_args_list assert len(apt_calls) == 2 assert apt_calls[0][0][0] == 'libpython3-dev' assert apt_calls[1][0][0] == 'python3-pip'
def test_constructor(): d = DependencyDescriptor('foo') assert d == 'foo' assert str(d) == 'foo' assert d.name == 'foo' assert len(d.metadata) == 0 d = DependencyDescriptor('foo', metadata={'bar': 'baz'}) assert d == 'foo' assert str(d) == 'foo' assert d.name == 'foo' assert len(d.metadata) == 1 assert 'bar' in d.metadata assert d.metadata['bar'] == 'baz'
def identify(self, desc): # noqa: D102 # ignore packages which have been identified with a different type if desc.type is not None and desc.type != 'ros': return # skip paths with an ignore marker file if (desc.path / 'CATKIN_IGNORE').exists(): raise IgnoreLocationException() if (desc.path / 'AMENT_IGNORE').exists(): raise IgnoreLocationException() # parse package manifest and get build type pkg, build_type = get_package_with_build_type(str(desc.path)) if not pkg or not build_type: # if it is not a wet ROS package check for a dry ROS package if (desc.path / 'manifest.xml').exists(): # ignore location to avoid being identified as a CMake package raise IgnoreLocationException() return desc.type = 'ros.{build_type}'.format_map(locals()) # use package name from manifest if not already set # e.g. from external configuration if desc.name is None: desc.name = pkg.name desc.metadata['version'] = pkg.version # get dependencies for d in pkg.build_depends + pkg.buildtool_depends: assert d.evaluated_condition is not None if d.evaluated_condition: desc.dependencies['build'].add( DependencyDescriptor(d.name, metadata=_create_metadata(d))) for d in (pkg.build_export_depends + pkg.buildtool_export_depends + pkg.exec_depends): assert d.evaluated_condition is not None if d.evaluated_condition: desc.dependencies['run'].add( DependencyDescriptor(d.name, metadata=_create_metadata(d))) for d in pkg.test_depends: assert d.evaluated_condition is not None if d.evaluated_condition: desc.dependencies['test'].add( DependencyDescriptor(d.name, metadata=_create_metadata(d)))
def create_dependency_descriptor(requirement_string): """ Create a DependencyDescriptor from a PEP440 compliant string. See https://www.python.org/dev/peps/pep-0440/#version-specifiers :param str requirement_string: a PEP440 compliant requirement string :return: A descriptor with version constraints from the requirement string :rtype: DependencyDescriptor """ symbol_mapping = { '==': 'version_eq', '!=': 'version_neq', '<=': 'version_lte', '>=': 'version_gte', '>': 'version_gt', '<': 'version_lt', } requirement = parse_requirement(requirement_string) metadata = {} for symbol, version in (requirement.constraints or []): if symbol in symbol_mapping: metadata[symbol_mapping[symbol]] = version elif symbol == '~=': metadata['version_gte'] = version metadata['version_lt'] = _next_incompatible_version(version) else: logger.warn( "Ignoring unknown symbol '{symbol}' in '{requirement}'".format( locals())) return DependencyDescriptor(requirement.name, metadata=metadata)
def test_dependencies_changed(self): package_1_desc = PackageDescriptor('fake/path') package_1_desc.name = 'package1' package_1_desc.dependencies['run'] = [DependencyDescriptor('foo')] package_1 = PackageDecorator(package_1_desc) decorators = [package_1] self.assertTrue( package_dependencies_changed(self.path_context, decorators)) update_dependencies_cache(self.path_context) package_1_desc.dependencies['run'] = [ DependencyDescriptor('foo'), 'baz' ] package_1 = PackageDecorator(package_1_desc) decorators = [package_1] self.assertTrue( package_dependencies_changed(self.path_context, decorators))
def test_constructor(): d = DependencyDescriptor('foo') assert d == 'foo' assert str(d) == 'foo' assert d.name == 'foo' assert len(d.metadata) == 0 d = DependencyDescriptor('foo', metadata={'bar': 'baz'}) assert d == 'foo' assert str(d) == 'foo' assert d.name == 'foo' assert len(d.metadata) == 1 assert 'bar' in d.metadata assert d.metadata['bar'] == 'baz' d2 = copy.deepcopy(d) assert d.name == d2.name assert d.metadata == d2.metadata d.metadata['bar'] = 'baz baz' assert d.metadata != d2.metadata
async def test_bundle(): pkg = PackageDescriptor('package/path') pkg.name = 'MyPackageName' pkg.dependencies['run'] = { DependencyDescriptor('source_pkg'), DependencyDescriptor('other_pkg'), DependencyDescriptor('system_pkg') } installers = { 'rdmanifest': MagicMock(), 'system': MagicMock(), 'other': MagicMock() } top_level_args = MagicMock(build_base='build/base', install_base='install/base', bundle_base='bundle/base') args = BundlePackageArguments(pkg, installers, top_level_args) args.ros_distribution = 'kinetic' args.exclude_ros_base = True context = TaskContext(pkg=pkg, args=args, dependencies={}) task = RosBundle() task.set_context(context=context) # Concise read on why it's patched this way. # http://www.voidspace.org.uk/python/mock/patch.html#where-to-patch with patch('colcon_ros_bundle.task.ros_bundle.RosdepWrapper' ) as wrapper: # noqa: E501 wrapper().get_rule.side_effect = _get_rule_side_effect wrapper().resolve.side_effect = _resolve_side_effect await task.bundle() installers['rdmanifest'].add_to_install_list.assert_called_with( 'source_pkg', { 's': 'ource', 'uri': 'rdmanifest' }) installers['other'].add_to_install_list.assert_called_with( 'other_pkg_name') installers['system'].add_to_install_list.assert_called_with( 'system_pkg_name')
def augment_packages( # noqa: D102 self, descs, *, additional_argument_names=None): # get all parsed ROS package manifests global _cached_packages pkgs = {} for desc in descs: if str(desc.path) not in _cached_packages: continue pkg = _cached_packages[str(desc.path)][0] if pkg: pkgs[pkg] = desc # resolve group members and add them to the descriptor dependencies for pkg, desc in pkgs.items(): for group_depend in pkg.group_depends: assert group_depend.evaluated_condition is not None if not group_depend.evaluated_condition: continue group_depend.extract_group_members(pkgs) for name in group_depend.members: desc.dependencies['build'].add(DependencyDescriptor(name)) desc.dependencies['run'].add(DependencyDescriptor(name))
def get_dependencies(self, *, categories=None): """ Get the dependencies for specific categories or for all categories. :param Iterable[str] categories: The names of the specific categories :returns: The dependencies :rtype: set[DependencyDescriptor] :raises AssertionError: if the package name is listed as a dependency """ dependencies = set() if categories is None: categories = self.dependencies.keys() for category in sorted(categories): dependencies |= self.dependencies[category] assert self.name not in dependencies, \ "The package '{self.name}' has a dependency with the same name" \ .format_map(locals()) return {(DependencyDescriptor(d) if d is str else d) for d in dependencies}
def identify(self, desc): # noqa: D102 # ignore packages which have been identified with a different type if desc.type is not None and desc.type != 'ros': return # skip paths with an ignore marker file if (desc.path / 'CATKIN_IGNORE').exists(): raise IgnoreLocationException() if (desc.path / 'AMENT_IGNORE').exists(): raise IgnoreLocationException() # parse package manifest and get build type pkg, build_type = get_package_with_build_type(str(desc.path)) if not pkg or not build_type: # if it is not a wet ROS package check for a dry ROS package if (desc.path / 'manifest.xml').exists(): # ignore location to avoid being identified as a CMake package raise IgnoreLocationException() return # for Python build types ensure that a setup.py file exists if build_type == 'ament_python': setup_py = desc.path / 'setup.py' if not setup_py.is_file(): logger.error( "ROS package '{desc.path}' with build type '{build_type}' " "has no 'setup.py' file".format_map(locals())) raise IgnoreLocationException() desc.type = 'ros.{build_type}'.format_map(locals()) # use package name from manifest if not already set # e.g. from external configuration if desc.name is None: desc.name = pkg.name desc.metadata['version'] = pkg.version # get dependencies for d in pkg.build_depends + pkg.buildtool_depends: assert d.evaluated_condition is not None if d.evaluated_condition: desc.dependencies['build'].add( DependencyDescriptor(d.name, metadata=_create_metadata(d))) for d in (pkg.build_export_depends + pkg.buildtool_export_depends + pkg.exec_depends): assert d.evaluated_condition is not None if d.evaluated_condition: desc.dependencies['run'].add( DependencyDescriptor(d.name, metadata=_create_metadata(d))) for d in pkg.test_depends: assert d.evaluated_condition is not None if d.evaluated_condition: desc.dependencies['test'].add( DependencyDescriptor(d.name, metadata=_create_metadata(d))) # for Python build types ensure that a setup.py file exists if build_type == 'ament_python': setup_cfg = desc.path / 'setup.cfg' for _ in (1, ): # try to get information from setup.cfg file if setup_cfg.is_file(): if is_reading_cfg_sufficient(setup_py): config = get_configuration(setup_cfg) name = config.get('metadata', {}).get('name') if name: options = config.get('options', {}) def getter(env): nonlocal options return options break else: # use information from setup.py file def getter(env): # noqa: F811 nonlocal desc return get_setup_information(desc.path / 'setup.py', env=env) desc.metadata['get_python_setup_options'] = getter