async def test(self, *, additional_hooks=None): # noqa: D102 pkg = self.context.pkg args = self.context.args logger.info("Testing Python package in '{args.path}'".format_map( locals())) try: env = await get_command_environment('setup_py', args.build_base, self.context.dependencies) except RuntimeError as e: logger.error(str(e)) return 1 setup_py_data = get_setup_data(self.context.pkg, env) # select the step extension which should perform the python testing if args.python_testing: key = args.python_testing extension = get_python_testing_step_extension(key) else: extensions = get_python_testing_step_extensions() for key, extension in extensions.items(): logger.log(1, "test() by extension '{key}'".format_map(locals())) try: matched = extension.match(self.context, env, setup_py_data) except Exception as e: # noqa: F841 # catch exceptions raised in python testing step extension exc = traceback.format_exc() logger.error( 'Exception in Python testing step extension ' "'{extension.STEP_TYPE}': {e}\n{exc}".format_map( locals())) # skip failing extension, continue with next one continue if matched: break else: logger.warning( "No Python Testing Step extension matched in '{args.path}'" .format_map(locals())) return logger.log(1, "test.step() by extension '{key}'".format_map(locals())) try: return await extension.step(self.context, env, setup_py_data) except Exception as e: # noqa: F841 # catch exceptions raised in python testing step extension exc = traceback.format_exc() logger.error('Exception in Python testing step extension ' "'{extension.STEP_TYPE}': {e}\n{exc}".format_map( locals())) return 1
def test_pytest_match(): extension = PytestPythonTestingStep() env = {} desc = PackageDescriptor('/dev/null') context = TaskContext(pkg=desc, args=None, dependencies=None) desc.name = 'pkg-name' desc.type = 'python' # no test requirements desc.metadata['get_python_setup_options'] = lambda env: {} assert not extension.match(context, env, get_setup_data(desc, env)) # pytest not in tests_require desc.metadata['get_python_setup_options'] = lambda env: { 'tests_require': ['nose'], } assert not extension.match(context, env, get_setup_data(desc, env)) # pytest not in extras_require.test desc.metadata['get_python_setup_options'] = lambda env: { 'extras_require': { 'test': ['nose'] }, } assert not extension.match(context, env, get_setup_data(desc, env)) # pytest in tests_require desc.metadata['get_python_setup_options'] = lambda env: { 'tests_require': ['pytest'], } assert extension.match(context, env, get_setup_data(desc, env)) # pytest in extras_require.test desc.metadata['get_python_setup_options'] = lambda env: { 'extras_require': { 'test': ['pytest'] }, } assert extension.match(context, env, get_setup_data(desc, env))
async def build(self, *, additional_hooks=None): # noqa: D102 pkg = self.context.pkg args = self.context.args logger.info( "Building Python package in '{args.path}'".format_map(locals())) try: env = await get_command_environment( 'setup_py', args.build_base, self.context.dependencies) except RuntimeError as e: logger.error(str(e)) return 1 setup_py_data = get_setup_data(self.context.pkg, env) # `setup.py egg_info` requires the --egg-base to exist os.makedirs(args.build_base, exist_ok=True) # `setup.py develop|install` requires the python lib path to exist python_lib = os.path.join( args.install_base, self._get_python_lib(args)) os.makedirs(python_lib, exist_ok=True) # and being in the PYTHONPATH env = dict(env) env['PYTHONPATH'] = python_lib + os.pathsep + \ env.get('PYTHONPATH', '') if not args.symlink_install: rc = await self._undo_develop(pkg, args, env) if rc and rc.returncode: return rc.returncode # invoke `setup.py install` step with lots of arguments # to avoid placing any files in the source space cmd = [ executable, 'setup.py', 'egg_info', '--egg-base', args.build_base, 'build', '--build-base', os.path.join( args.build_base, 'build'), 'install', '--prefix', args.install_base, '--record', os.path.join(args.build_base, 'install.log'), # prevent installation of dependencies specified in setup.py '--single-version-externally-managed', ] self._append_install_layout(args, cmd) rc = await check_call(self.context, cmd, cwd=args.path, env=env) if rc and rc.returncode: return rc.returncode else: self._undo_install(pkg, args, setup_py_data, python_lib) self._symlinks_in_build(args, setup_py_data) # invoke `setup.py develop` step in build space # to avoid placing any files in the source space # --editable causes this to skip creating/editing the # easy-install.pth file cmd = [ executable, 'setup.py', 'develop', '--prefix', args.install_base, '--editable', '--build-directory', os.path.join(args.build_base, 'build'), '--no-deps', ] if setup_py_data.get('data_files'): cmd += ['install_data', '--install-dir', args.install_base] self._append_install_layout(args, cmd) rc = await check_call( self.context, cmd, cwd=args.build_base, env=env) if rc and rc.returncode: return rc.returncode # explicitly add the build directory to the PYTHONPATH # to maintain the desired order if additional_hooks is None: additional_hooks = [] base_path = args.build_base # if the Python packages are in a subdirectory # that needs to be appended to the build directory package_dir = setup_py_data.get('package_dir') or {} if '' in package_dir: base_path = os.path.join(base_path, package_dir['']) additional_hooks += create_environment_hook( 'pythonpath_develop', Path(base_path), pkg.name, 'PYTHONPATH', base_path, mode='prepend') hooks = create_environment_hooks(args.install_base, pkg.name) create_environment_scripts( pkg, args, default_hooks=hooks, additional_hooks=additional_hooks)
async def build(self): # noqa: D102 args = self.context.args logger.info( "Building ROS package in '{args.path}' with build type " "'ament_python'".format_map(locals())) # reuse Python build task with additional logic extension = PythonBuildTask() extension.set_context(context=self.context) # additional hooks additional_hooks = create_environment_hook( 'ament_prefix_path', Path(args.install_base), self.context.pkg.name, 'AMENT_PREFIX_PATH', '', mode='prepend') # get options from the Python manifest try: env = await get_command_environment( 'setup_py', args.build_base, self.context.dependencies) except RuntimeError as e: logger.error(str(e)) return 1 setup_py_data = get_setup_data(self.context.pkg, env) # check if the package index and manifest are being installed data_files = get_data_files_mapping( setup_py_data.get('data_files', [])) installs_package_index = False installs_package_manifest = False # check if package index and manifest are being installed for source, destination in data_files.items(): if sys.platform == 'win32': destination = Path(destination).as_posix() # work around data files incorrectly defined as not relative if os.path.isabs(source): source = os.path.relpath(source, args.path) if ( destination == 'share/ament_index/resource_index/packages/' + self.context.pkg.name ): installs_package_index = True elif ( source == 'package.xml' and destination == 'share/{self.context.pkg.name}/package.xml' .format_map(locals()) ): installs_package_manifest = True # warn about missing explicit installation # for now implicitly install the marker and the manifest if not installs_package_index: # TODO remove magic helper in the future logger.warn( "Package '{self.context.pkg.name}' doesn't explicitly install " 'a marker in the package index (colcon-ros currently does it ' 'implicitly but that fallback will be removed in the future)' .format_map(locals())) # create package marker in ament resource index create_file( args, 'share/ament_index/resource_index/packages/' '{self.context.pkg.name}'.format_map(locals())) if not installs_package_manifest: # TODO remove magic helper in the future logger.warn( "Package '{self.context.pkg.name}' doesn't explicitly install " "the 'package.xml' file (colcon-ros currently does it " 'implicitly but that fallback will be removed in the future)' .format_map(locals())) # copy / symlink package manifest install( args, 'package.xml', 'share/{self.context.pkg.name}/package.xml' .format_map(locals())) return await extension.build(additional_hooks=additional_hooks)
async def build(self, *, additional_hooks=None): # noqa: D102 pkg = self.context.pkg args = self.context.args logger.info( "Building Python package in '{args.path}'".format_map(locals())) try: env = await get_command_environment( 'setup_py', args.build_base, self.context.dependencies) except RuntimeError as e: logger.error(str(e)) return 1 setup_py_data = get_setup_data(self.context.pkg, env) # `setup.py egg_info` requires the --egg-base to exist os.makedirs(args.build_base, exist_ok=True) # `setup.py develop|install` requires the python lib path to exist python_lib = os.path.join( args.install_base, self._get_python_lib(args)) os.makedirs(python_lib, exist_ok=True) # and being in the PYTHONPATH env = dict(env) env['PYTHONPATH'] = python_lib + os.pathsep + \ env.get('PYTHONPATH', '') if not args.symlink_install: rc = await self._undo_develop(pkg, args, env) if rc and rc.returncode: return rc.returncode # invoke `setup.py install` step with lots of arguments # to avoid placing any files in the source space cmd = [ executable, 'setup.py', 'egg_info', '--egg-base', args.build_base, 'build', '--build-base', os.path.join( args.build_base, 'build'), 'install', '--prefix', args.install_base, '--record', os.path.join(args.build_base, 'install.log'), # prevent installation of dependencies specified in setup.py '--single-version-externally-managed', ] self._append_install_layout(args, cmd) cmd += [ 'bdist_egg', '--dist-dir', os.path.join( args.build_base, 'dist'), ] rc = await check_call(self.context, cmd, cwd=args.path, env=env) if rc and rc.returncode: return rc.returncode else: self._undo_install(pkg, args, setup_py_data, python_lib) self._symlinks_in_build(args, setup_py_data) # invoke `setup.py develop` step in build space # to avoid placing any files in the source space cmd = [ executable, 'setup.py', 'develop', '--prefix', args.install_base, '--no-deps', ] if setup_py_data.get('data_files', []): cmd += ['install_data', '--install-dir', args.install_base] self._append_install_layout(args, cmd) rc = await check_call( self.context, cmd, cwd=args.build_base, env=env) if rc and rc.returncode: return rc.returncode hooks = create_environment_hooks(args.install_base, pkg.name) create_environment_scripts( pkg, args, default_hooks=hooks, additional_hooks=additional_hooks)