From cbd9b295fb295eafd807b851fb91fc5f941f0ead Mon Sep 17 00:00:00 2001 From: TizenOpenSource Date: Tue, 6 Feb 2024 17:28:21 +0900 Subject: [PATCH] Imported Upstream version 1.0.0 --- LICENSE | 31 ++++++++++++++++ PKG-INFO | 14 ++++++-- README.rst | 5 ++- setup.cfg | 17 +++++++++ setup.py | 42 +++++++++++++++------- src/catkin_pkg.egg-info/PKG-INFO | 14 ++++++-- src/catkin_pkg.egg-info/SOURCES.txt | 2 ++ src/catkin_pkg.egg-info/requires.txt | 15 +++++++- src/catkin_pkg/__init__.py | 2 +- src/catkin_pkg/changelog.py | 4 +-- src/catkin_pkg/cli/package_version.py | 4 +-- src/catkin_pkg/cli/prepare_release.py | 17 +++++---- src/catkin_pkg/cmake.py | 4 +-- src/catkin_pkg/package.py | 36 +++++++++++++++++-- src/catkin_pkg/package_version.py | 47 +++++++++++++++++++++--- src/catkin_pkg/packages.py | 24 ++++++++----- src/catkin_pkg/python_setup.py | 17 +++++---- src/catkin_pkg/topological_order.py | 2 +- test/test_changelog.py | 14 ++++---- test/test_flake8.py | 3 ++ test/test_metapackage.py | 14 ++------ test/test_package.py | 51 ++++++++++++++++++++++++--- test/test_package_version.py | 36 ++++++++++++++++--- test/test_packages.py | 15 ++++++++ test/test_templates.py | 3 +- test/test_topological_order.py | 3 +- test/test_workspaces.py | 2 +- 27 files changed, 348 insertions(+), 90 deletions(-) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7855782 --- /dev/null +++ b/LICENSE @@ -0,0 +1,31 @@ +Software License Agreement (BSD License) + +Copyright (c) 2012, Willow Garage, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. +* Neither the name of Willow Garage, Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/PKG-INFO b/PKG-INFO index 9eb06f9..d039684 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,13 +1,21 @@ -Metadata-Version: 1.1 +Metadata-Version: 2.1 Name: catkin_pkg -Version: 0.4.23 +Version: 1.0.0 Summary: catkin package library Home-page: http://wiki.ros.org/catkin_pkg Author: Dirk Thomas Author-email: dthomas@osrfoundation.org +Maintainer: ROS Infrastructure Team License: BSD -Description: Library for retrieving information about catkin packages. +Project-URL: Source code, https://github.com/ros-infrastructure/catkin_pkg +Project-URL: Issue tracker, https://github.com/ros-infrastructure/catkin_pkg/issues Keywords: catkin,ROS Platform: UNKNOWN Classifier: Programming Language :: Python Classifier: License :: OSI Approved :: BSD License +Requires-Python: >=3.6 +Provides-Extra: test +License-File: LICENSE + +Library for retrieving information about catkin packages. + diff --git a/README.rst b/README.rst index 56e7fc7..0be973f 100644 --- a/README.rst +++ b/README.rst @@ -16,6 +16,5 @@ Code & tickets Continuous Integration ---------------------- -+--------------------------------------------------------------------------+--------------------------------------------------------------------+ -| `Build Status `_. | .. image:: https://travis-ci.org/ros-infrastructure/catkin_pkg.png | -+--------------------------------------------------------------------------+--------------------------------------------------------------------+ +.. image:: https://github.com/ros-infrastructure/catkin_pkg/actions/workflows/ci.yaml/badge.svg?branch=master&event=push + :target: https://github.com/ros-infrastructure/catkin_pkg/actions/workflows/ci.yaml?query=branch%3Amaster+event%3Apush diff --git a/setup.cfg b/setup.cfg index 8bfd5a1..bd2d072 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,20 @@ +[flake8] +extend-ignore = C403,C404,C405,D100,D101,D102,D103,D104,D105,D106,D107,D203,D212,D404,I202,W605 +import-order-style = google +max-complexity = 54 +max-line-length = 200 +show-source = true +statistics = true + +[tool:pytest] +junit_suite_name = catkin_pkg +markers = + flake8 + linter + +[coverage:run] +source = catkin_pkg + [egg_info] tag_build = tag_date = 0 diff --git a/setup.py b/setup.py index 3d9a553..23368e4 100755 --- a/setup.py +++ b/setup.py @@ -1,26 +1,16 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import os -import sys from setuptools import setup -install_requires = [ - 'docutils', - 'python-dateutil', - 'pyparsing', -] - -# argparse is part of the standard library since Python 2.7 -if sys.version_info[0] == 2 and sys.version_info[1] < 7: - install_requires.append('argparse') kwargs = { 'name': 'catkin_pkg', # same version as in: # - src/catkin_pkg/__init__.py # - stdeb.cfg - 'version': '0.4.23', + 'version': '1.0.0', 'packages': ['catkin_pkg', 'catkin_pkg.cli'], 'package_dir': {'': 'src'}, 'package_data': {'catkin_pkg': ['templates/*.in']}, @@ -36,16 +26,42 @@ kwargs = { ]}, 'author': 'Dirk Thomas', 'author_email': 'dthomas@osrfoundation.org', + 'maintainer': 'ROS Infrastructure Team', + 'project_urls': { + 'Source code': + 'https://github.com/ros-infrastructure/catkin_pkg', + 'Issue tracker': + 'https://github.com/ros-infrastructure/catkin_pkg/issues', + }, 'url': 'http://wiki.ros.org/catkin_pkg', 'keywords': ['catkin', 'ROS'], 'classifiers': [ 'Programming Language :: Python', 'License :: OSI Approved :: BSD License' ], + 'python_requires': '>=3.6', 'description': 'catkin package library', 'long_description': 'Library for retrieving information about catkin packages.', 'license': 'BSD', - 'install_requires': install_requires, + 'install_requires': [ + 'docutils', + 'python-dateutil', + 'pyparsing', + 'setuptools', + ], + 'extras_require': { + 'test': [ + 'flake8', + 'flake8-blind-except', + 'flake8-builtins', + 'flake8-class-newline', + 'flake8-comprehensions', + 'flake8-deprecated', + 'flake8-docstrings', + 'flake8-import-order', + 'flake8-quotes', + 'pytest', + ]}, } if 'SKIP_PYTHON_MODULES' in os.environ: kwargs['packages'] = [] diff --git a/src/catkin_pkg.egg-info/PKG-INFO b/src/catkin_pkg.egg-info/PKG-INFO index 3663e18..7000e2a 100644 --- a/src/catkin_pkg.egg-info/PKG-INFO +++ b/src/catkin_pkg.egg-info/PKG-INFO @@ -1,13 +1,21 @@ -Metadata-Version: 1.1 +Metadata-Version: 2.1 Name: catkin-pkg -Version: 0.4.23 +Version: 1.0.0 Summary: catkin package library Home-page: http://wiki.ros.org/catkin_pkg Author: Dirk Thomas Author-email: dthomas@osrfoundation.org +Maintainer: ROS Infrastructure Team License: BSD -Description: Library for retrieving information about catkin packages. +Project-URL: Source code, https://github.com/ros-infrastructure/catkin_pkg +Project-URL: Issue tracker, https://github.com/ros-infrastructure/catkin_pkg/issues Keywords: catkin,ROS Platform: UNKNOWN Classifier: Programming Language :: Python Classifier: License :: OSI Approved :: BSD License +Requires-Python: >=3.6 +Provides-Extra: test +License-File: LICENSE + +Library for retrieving information about catkin packages. + diff --git a/src/catkin_pkg.egg-info/SOURCES.txt b/src/catkin_pkg.egg-info/SOURCES.txt index 5c48cf1..9578505 100644 --- a/src/catkin_pkg.egg-info/SOURCES.txt +++ b/src/catkin_pkg.egg-info/SOURCES.txt @@ -1,4 +1,6 @@ +LICENSE README.rst +setup.cfg setup.py src/catkin_pkg/__init__.py src/catkin_pkg/changelog.py diff --git a/src/catkin_pkg.egg-info/requires.txt b/src/catkin_pkg.egg-info/requires.txt index 0340770..8f326a3 100644 --- a/src/catkin_pkg.egg-info/requires.txt +++ b/src/catkin_pkg.egg-info/requires.txt @@ -1,3 +1,16 @@ docutils -pyparsing python-dateutil +pyparsing +setuptools + +[test] +flake8 +flake8-blind-except +flake8-builtins +flake8-class-newline +flake8-comprehensions +flake8-deprecated +flake8-docstrings +flake8-import-order +flake8-quotes +pytest diff --git a/src/catkin_pkg/__init__.py b/src/catkin_pkg/__init__.py index f1ba209..39940e5 100644 --- a/src/catkin_pkg/__init__.py +++ b/src/catkin_pkg/__init__.py @@ -35,4 +35,4 @@ # same version as in: # - setup.py # - stdeb.cfg -__version__ = '0.4.23' +__version__ = '1.0.0' diff --git a/src/catkin_pkg/changelog.py b/src/catkin_pkg/changelog.py index 9198f88..c3f0304 100644 --- a/src/catkin_pkg/changelog.py +++ b/src/catkin_pkg/changelog.py @@ -231,9 +231,9 @@ def processes_changelog_children(changelog, children): elif isinstance(child, docutils.nodes.title) or isinstance(child, docutils.nodes.subtitle): version, date = None, None # See if the title has a text element in it - if len(child.children) > 0 and isinstance(child.children[0], docutils.nodes.Text): + if len(child.children) > 0 and any(isinstance(c, docutils.nodes.Text) for c in child.traverse()): # Extract version and date from (sub-)title - title_text = child.children[0].rawsource + title_text = child.astext() try: version, date = version_and_date_from_title(title_text) except InvalidSectionTitle: diff --git a/src/catkin_pkg/cli/package_version.py b/src/catkin_pkg/cli/package_version.py index f5556ca..62eee18 100644 --- a/src/catkin_pkg/cli/package_version.py +++ b/src/catkin_pkg/cli/package_version.py @@ -33,7 +33,7 @@ def main(): else: # bump the version number new_version = bump_version(version, args.bump) - update_versions(packages.keys(), new_version) + update_versions(packages, new_version) print('%s -> %s' % (version, new_version)) - except Exception as e: + except Exception as e: # noqa: B902 sys.exit(str(e)) diff --git a/src/catkin_pkg/cli/prepare_release.py b/src/catkin_pkg/cli/prepare_release.py index dbf1549..9171adb 100644 --- a/src/catkin_pkg/cli/prepare_release.py +++ b/src/catkin_pkg/cli/prepare_release.py @@ -133,6 +133,7 @@ def check_clean_working_copy(base_path, vcs_type): def commit_files(base_path, vcs_type, packages, packages_with_changelogs, message, dry_run=False): cmd = [_find_executable(vcs_type), 'commit', '-m', message] cmd += [os.path.join(p, PACKAGE_MANIFEST_FILENAME) for p in packages.keys()] + cmd += [s for s in [os.path.join(p, 'setup.py') for p in packages.keys()] if os.path.exists(s)] cmd += [path for path, _, _ in packages_with_changelogs.values()] if not dry_run: try: @@ -266,18 +267,18 @@ def _main(): # complain about packages with upper case character since they won't be releasable with bloom unsupported_pkg_names = [] invalid_pkg_names = [] + valid_build_types = ['catkin', 'ament_cmake', 'ament_python'] for package in packages.values(): - build_types = [export.content for export in package.exports if export.tagname == 'build_type'] - build_type = build_types[0] if build_types else 'catkin' - if build_type not in ('catkin', 'ament_cmake'): + build_types = package.get_unconditional_build_types() + if any(build_type not in valid_build_types for build_type in build_types): unsupported_pkg_names.append(package.name) if package.name != package.name.lower(): invalid_pkg_names.append(package.name) if unsupported_pkg_names: print( fmt( - "@{yf}Warning: the following package are not of build_type catkin or ament_cmake and may require manual steps to release': %s" % - ', '.join([('@{boldon}%s@{boldoff}' % p) for p in sorted(unsupported_pkg_names)]) + "@{yf}Warning: the following package are not of build_type %s and may require manual steps to release': %s" % + (str(valid_build_types), ', '.join([('@{boldon}%s@{boldoff}' % p) for p in sorted(unsupported_pkg_names)])) ), file=sys.stderr) if not args.non_interactive and not prompt_continue('Continue anyway', default=False): raise RuntimeError(fmt('@{rf}Aborted release, verify that unsupported packages are ready to be released or release manually.')) @@ -304,6 +305,10 @@ def _main(): raise RuntimeError(fmt( "@{rf}Invalid metapackage at path '@{boldon}%s@{boldoff}':\n %s\n\nSee requirements for metapackages: %s" % (os.path.abspath(pkg_path), str(e), metapackage.DEFINITION_URL))) + # verify that the setup.py files don't have modifications pending + setup_py_path = os.path.join(pkg_path, 'setup.py') + if os.path.exists(setup_py_path) and has_changes(base_path, setup_py_path, vcs_type): + local_modifications.append(setup_py_path) # fetch current version and verify that all packages have same version number old_version = verify_equal_package_versions(packages.values()) @@ -380,7 +385,7 @@ def _main(): (new_version, ', '.join([('@{boldon}%s@{boldoff}' % p) for p in sorted(missing_changelogs_but_forthcoming.keys())])))) # bump version number - update_versions(packages.keys(), new_version) + update_versions(packages, new_version) print(fmt("@{gf}Bump version@{reset} of all packages from '@{bf}%s@{reset}' to '@{bf}@{boldon}%s@{boldoff}@{reset}'" % (old_version, new_version))) pushed = None diff --git a/src/catkin_pkg/cmake.py b/src/catkin_pkg/cmake.py index 05be78c..11aaf28 100644 --- a/src/catkin_pkg/cmake.py +++ b/src/catkin_pkg/cmake.py @@ -47,7 +47,7 @@ def get_metapackage_cmake_template_path(): return os.path.join(os.path.dirname(__file__), rel_path) -def configure_file(template_file, environment): # noqa: D402 +def configure_file(template_file, environment): """ Evaluate a .in template file used in CMake with configure_file(). @@ -57,7 +57,7 @@ def configure_file(template_file, environment): # noqa: D402 :returns: string with evaluates template :raises: KeyError for placeholders in the template which are not in the environment - """ + """ # noqa: D402 with open(template_file, 'r') as f: template = f.read() return configure_string(template, environment) diff --git a/src/catkin_pkg/package.py b/src/catkin_pkg/package.py index bdc88a3..2843c69 100644 --- a/src/catkin_pkg/package.py +++ b/src/catkin_pkg/package.py @@ -39,6 +39,7 @@ import os import re import sys import xml.dom.minidom as dom +from xml.parsers.expat import ExpatError from catkin_pkg.condition import evaluate_condition @@ -59,6 +60,7 @@ class Package(object): 'version', 'version_compatibility', 'description', + 'plaintext_description', 'maintainers', 'licenses', 'urls', @@ -164,6 +166,18 @@ class Package(object): return build_type_exports[0] raise InvalidPackage('Only one element is permitted.', self.filename) + def get_unconditional_build_types(self): + """ + Return values of export/build_type elements without conditional filtering, or ['catkin'] if unspecified. + + :returns: package build types + :rtype: List[str] + """ + build_type_exports = [e.content for e in self.exports if e.tagname == 'build_type'] + if not build_type_exports: + return ['catkin'] + return build_type_exports + def has_invalid_metapackage_dependencies(self): """ Return True if this package has invalid dependencies for a metapackage. @@ -539,7 +553,7 @@ def has_ros_schema_reference_string(data): data = data.encode('utf-8') try: root = dom.parseString(data) - except Exception: + except ExpatError: # invalid XML return False @@ -594,7 +608,7 @@ def parse_package_string(data, filename=None, warnings=None): data = data.encode('utf-8') try: root = dom.parseString(data) - except Exception as ex: + except ExpatError as ex: raise InvalidPackage('The manifest contains invalid XML:\n%s' % ex, filename) pkg = Package(filename) @@ -623,6 +637,7 @@ def parse_package_string(data, filename=None, warnings=None): # description pkg.description = _get_node_value(_get_node(root, 'description', filename), allow_xml=True, apply_str=False) + pkg.plaintext_description = re.sub(' +(\n+) +', r'\1', _get_node_text(_get_node(root, 'description', filename)), flags=re.MULTILINE) # at least one maintainer, all must have email maintainers = _get_nodes(root, 'maintainer') @@ -803,6 +818,23 @@ def _get_node_value(node, allow_xml=False, apply_str=True): return value +def _get_node_text(node, strip=True): + value = '' + for child in node.childNodes: + if child.nodeType == child.TEXT_NODE: + value += re.sub(r'\s+', ' ', child.data) + elif child.nodeType == child.ELEMENT_NODE: + if child.tagName == 'br': + value += '\n' + else: + value += _get_node_text(child, strip=False) + else: + assert 'unreachable' + if strip: + value = value.strip() + return value + + def _get_node_attr(node, attr, default=False): """:param default: False means value is required.""" if node.hasAttribute(attr): diff --git a/src/catkin_pkg/package_version.py b/src/catkin_pkg/package_version.py index 0368a73..f7d204e 100644 --- a/src/catkin_pkg/package_version.py +++ b/src/catkin_pkg/package_version.py @@ -84,6 +84,28 @@ def _replace_version(package_str, new_version): return new_package_str +def _replace_setup_py_version(setup_py_str, new_version): + """ + Replace the version tag in contents if there is only one instance and it is using a literal as the version. + + :param str package_str: contents of setup.py + :param str new_version: new version number + :returns: new setup.py string + :rtype: str + :raises RuntimeError: + """ + # try to replace contents + new_setup_py_str, number_of_subs = re.subn( + r'version=([\'"])\d+\.\d+\.\d+([\'"]),', + r'version=\g<1>%s\g<2>,' % new_version, + setup_py_str) + if number_of_subs == 0: + raise RuntimeError("Failed to find a normal version statement, e.g.: version='1.2.3',") + if number_of_subs != 1: + raise RuntimeError('Illegal number of version statements: %s' % (number_of_subs)) + return new_setup_py_str + + def _check_for_version_comment(package_str, new_version): """ Check if a comment is present behind the version tag and return it. @@ -101,16 +123,17 @@ def _check_for_version_comment(package_str, new_version): return comment -def update_versions(paths, new_version): +def update_versions(packages, new_version): """ - Bulk replace of version: searches for package.xml files directly in given folders and replaces version tag within. + Bulk replace of version: searches for package.xml and setup.py files directly in given folders and replaces version tag within. - :param list paths: folder names + :param dict packages: dict from folder names to package xml objects in those folders :param str new_version: version string "int.int.int" :raises RuntimeError: if any one package.xml cannot be updated """ files = {} - for path in paths: + for path, package_obj in packages.items(): + # Update any package.xml files. package_path = os.path.join(path, 'package.xml') with open(package_path, 'r') as f: package_str = f.read() @@ -122,6 +145,20 @@ def update_versions(paths, new_version): except RuntimeError as rue: raise RuntimeError('Could not bump version number in file %s: %s' % (package_path, str(rue))) files[package_path] = new_package_str + # Update any setup.py files. + setup_py_path = os.path.join(path, 'setup.py') + if os.path.exists(setup_py_path): + # Only update setup.py for ament_python packages. + build_types = package_obj.get_unconditional_build_types() + if 'ament_python' in build_types: + with open(setup_py_path, 'r') as f: + setup_py_str = f.read() + try: + new_setup_py_str = _replace_setup_py_version(setup_py_str, new_version) + except RuntimeError as exc: + raise RuntimeError('Could not bump version number in file %s: %s' % (setup_py_path, str(exc))) + files[setup_py_path] = new_setup_py_str + # if all replacements successful, write back modified package.xml for package_path, new_package_str in files.items(): with open(package_path, 'w') as f: @@ -140,7 +177,7 @@ def get_forthcoming_label(rst): if len(section.children) > 0 and isinstance(section.children[0], docutils.nodes.title): title = section.children[0] if title and len(title.children) > 0 and isinstance(title.children[0], docutils.nodes.Text): - title_text = title.children[0].rawsource + title_text = title.children[0].astext() if FORTHCOMING_LABEL.lower() in title_text.lower(): if forthcoming_label: raise RuntimeError('Found multiple forthcoming sections') diff --git a/src/catkin_pkg/packages.py b/src/catkin_pkg/packages.py index aa7208b..36b5144 100644 --- a/src/catkin_pkg/packages.py +++ b/src/catkin_pkg/packages.py @@ -40,11 +40,14 @@ from .package import PACKAGE_MANIFEST_FILENAME from .package import parse_package_string -def find_package_paths(basepath, exclude_paths=None, exclude_subspaces=False): +DEFAULT_IGNORE_MARKERS = {'AMENT_IGNORE', 'CATKIN_IGNORE', 'COLCON_IGNORE'} + + +def find_package_paths(basepath, exclude_paths=None, exclude_subspaces=False, ignore_markers=DEFAULT_IGNORE_MARKERS): """ Crawls the filesystem to find package manifest files. - When a subfolder contains either of the following files it is ignored: + When a subfolder contains either of the files mentioned in ``ignore_markers`` it is ignored. By default, these are: - ``AMENT_IGNORE`` - ``CATKIN_IGNORE`` - ``COLCON_IGNORE`` @@ -53,12 +56,13 @@ def find_package_paths(basepath, exclude_paths=None, exclude_subspaces=False): :param exclude_paths: A list of paths which should not be searched, ``list`` :param exclude_subspaces: The flag is subfolders containing a .catkin file should not be searched, ``bool`` + :param ignore_markers: Names of files that indicate that a folder should be ignored, ``set`` :returns: A list of relative paths containing package manifest files ``list`` """ paths = [] real_exclude_paths = [os.path.realpath(p) for p in exclude_paths] if exclude_paths is not None else [] for dirpath, dirnames, filenames in os.walk(basepath, followlinks=True): - if set(dirnames + filenames) & {'AMENT_IGNORE', 'CATKIN_IGNORE', 'COLCON_IGNORE'} or \ + if set(dirnames + filenames) & ignore_markers or \ os.path.realpath(dirpath) in real_exclude_paths or \ (exclude_subspaces and '.catkin' in filenames): del dirnames[:] @@ -72,7 +76,7 @@ def find_package_paths(basepath, exclude_paths=None, exclude_subspaces=False): return paths -def find_packages(basepath, exclude_paths=None, exclude_subspaces=False, warnings=None): +def find_packages(basepath, exclude_paths=None, exclude_subspaces=False, warnings=None, ignore_markers=DEFAULT_IGNORE_MARKERS): """ Crawls the filesystem to find package manifest files and parses them. @@ -80,11 +84,14 @@ def find_packages(basepath, exclude_paths=None, exclude_subspaces=False, warning :param exclude_paths: A list of paths which should not be searched, ``list`` :param exclude_subspaces: The flag is subfolders containing a .catkin file should not be searched, ``bool`` - :param warnings: Print warnings if None or return them in the given list + :param warnings: Print warnings if None or return them in the given list, ``bool`` + :param ignore_markers: Names of files that indicate that a folder should be ignored, ``set`` :returns: A dict mapping relative paths to ``Package`` objects ``dict`` :raises: :exc:RuntimeError` If multiple packages have the same name """ - packages = find_packages_allowing_duplicates(basepath, exclude_paths=exclude_paths, exclude_subspaces=exclude_subspaces, warnings=warnings) + packages = find_packages_allowing_duplicates(basepath, exclude_paths=exclude_paths, + exclude_subspaces=exclude_subspaces, warnings=warnings, + ignore_markers=ignore_markers) package_paths_by_name = {} for path, package in packages.items(): if package.name not in package_paths_by_name: @@ -109,7 +116,7 @@ class _PackageParser(object): return (path, parsed_package), warnings -def find_packages_allowing_duplicates(basepath, exclude_paths=None, exclude_subspaces=False, warnings=None): +def find_packages_allowing_duplicates(basepath, exclude_paths=None, exclude_subspaces=False, warnings=None, ignore_markers=DEFAULT_IGNORE_MARKERS): """ Crawls the filesystem to find package manifest files and parses them. @@ -118,9 +125,10 @@ def find_packages_allowing_duplicates(basepath, exclude_paths=None, exclude_subs :param exclude_subspaces: The flag is subfolders containing a .catkin file should not be searched, ``bool`` :param warnings: Print warnings if None or return them in the given list + :param ignore_markers: Names of files that indicate that a folder should be ignored, ``set`` :returns: A dict mapping relative paths to ``Package`` objects ``dict`` """ - package_paths = find_package_paths(basepath, exclude_paths=exclude_paths, exclude_subspaces=exclude_subspaces) + package_paths = find_package_paths(basepath, exclude_paths=exclude_paths, exclude_subspaces=exclude_subspaces, ignore_markers=ignore_markers) xmls = {} for path in package_paths: diff --git a/src/catkin_pkg/python_setup.py b/src/catkin_pkg/python_setup.py index c18ded8..84bd64d 100644 --- a/src/catkin_pkg/python_setup.py +++ b/src/catkin_pkg/python_setup.py @@ -57,8 +57,9 @@ def generate_distutils_setup(package_xml_path=os.path.curdir, **kwargs): the "url" field. The "description" is taken from the eponymous tag if it does not - exceed 200 characters. If it does "description" contains the - truncated text while "description_long" contains the complete. + exceed 200 characters and has no newlines. If it does "description" + contains the truncated text while "long_description" contains the + complete. All licenses are merged into the "license" field. @@ -98,11 +99,13 @@ def generate_distutils_setup(package_xml_path=os.path.curdir, **kwargs): elif package.urls: data['url'] = package.urls[0].url - if len(package.description) <= 200: - data['description'] = package.description - else: - data['description'] = package.description[:197] + '...' - data['long_description'] = package.description + description = package.plaintext_description.splitlines()[0] + if len(description) > 200: + description = description[:197] + '...' + + data['description'] = description + if description != package.plaintext_description: + data['long_description'] = package.plaintext_description data['license'] = ', '.join(package.licenses) diff --git a/src/catkin_pkg/topological_order.py b/src/catkin_pkg/topological_order.py index 2bb396a..b9b003e 100644 --- a/src/catkin_pkg/topological_order.py +++ b/src/catkin_pkg/topological_order.py @@ -222,7 +222,7 @@ def _reduce_cycle_set(packages_orig): :param packages: A dict mapping package name to ``_PackageDecorator`` objects ``dict`` :returns: A list of package names from the input which could not easily be detected as not being part of a cycle. """ - assert(packages_orig) + assert packages_orig packages = copy.copy(packages_orig) last_depended = None while len(packages) > 0: diff --git a/test/test_changelog.py b/test/test_changelog.py index 047aff9..673d11e 100644 --- a/test/test_changelog.py +++ b/test/test_changelog.py @@ -31,30 +31,30 @@ class TestSectionTitleParsing(unittest.TestCase): def check_0_1_26(content): assert len(content) == 1 - assert type(content[0]) == BulletList + assert type(content[0]) is BulletList assert len(content[0].bullets) == 3 def check_0_1_25(content): assert len(content) == 3 - assert type(content[0]) == BulletList + assert type(content[0]) is BulletList assert len(content[0].bullets) == 5 mtext = content[0].bullets[3] - assert type(mtext) == MixedText + assert type(mtext) is MixedText assert len(mtext.texts) == 2 - assert type(content[1]) == Transition - assert type(content[2]) == MixedText + assert type(content[1]) is Transition + assert type(content[2]) is MixedText def check_0_1_0(content): assert len(content) == 1 - assert type(content[0]) == MixedText + assert type(content[0]) is MixedText assert len(content[0].texts) == 4 def check_0_0_1(content): assert len(content) == 1 - assert type(content[0]) == BulletList + assert type(content[0]) is BulletList assert content[0].bullet_type == 'enumerated' diff --git a/test/test_flake8.py b/test/test_flake8.py index 3c5e5bf..2390a37 100644 --- a/test/test_flake8.py +++ b/test/test_flake8.py @@ -18,8 +18,11 @@ import os import sys from flake8.api.legacy import get_style_guide +import pytest +@pytest.mark.flake8 +@pytest.mark.linter def test_flake8(): # Configure flake8 using the .flake8 file in the root of this repository. style_guide = get_style_guide() diff --git a/test/test_metapackage.py b/test/test_metapackage.py index e5b4ff6..c240826 100644 --- a/test/test_metapackage.py +++ b/test/test_metapackage.py @@ -61,14 +61,6 @@ def assert_warning(warnreg): sys.stderr = orig_stderr -def _validate_metapackage(path, package): - try: - validate_metapackage(path, package) - except Exception: - # print('on package ' + package.name, file=sys.stderr) - raise - - class TestMetapackageValidation(unittest.TestCase): """Tests the metapackage validator.""" @@ -85,12 +77,12 @@ class TestMetapackageValidation(unittest.TestCase): if exc is not None: if excreg is not None: with self.assertRaisesRegex(exc, excreg): - _validate_metapackage(path, package) + validate_metapackage(path, package) else: with self.assertRaises(exc): - _validate_metapackage(path, package) + validate_metapackage(path, package) else: - _validate_metapackage(path, package) + validate_metapackage(path, package) def test_collect_warnings(self): """Tests warnings collection.""" diff --git a/test/test_package.py b/test/test_package.py index b988a9f..12d8ffd 100644 --- a/test/test_package.py +++ b/test/test_package.py @@ -2,8 +2,10 @@ import os.path # Redirect stderr to stdout to suppress output in tests import sys import unittest +from unittest.mock import Mock import xml.dom.minidom as dom +from xml.parsers.expat import ExpatError from catkin_pkg.package import ( _check_known_attributes, @@ -19,8 +21,6 @@ from catkin_pkg.package import ( Person, ) -from mock import Mock - sys.stderr = sys.stdout test_data_dir = os.path.join(os.path.dirname(__file__), 'data', 'package') @@ -337,14 +337,14 @@ class PackageTest(unittest.TestCase): try: create_node('tag', {'key': 'value'}) - except Exception as e: + except ExpatError as e: self.fail('create_node() raised %s "%s" unexpectedly!' % (type(e), str(e))) self.assertRaisesRegex(Exception, 'unbound prefix: line 1, column 0', create_node, 'tag', {'ns:key': 'value'}) try: create_node('tag', {'ns:key': 'value', 'xmlns:ns': 'urn:ns'}) - except Exception as e: + except ExpatError as e: self.fail('create_node() raised %s "%s" unexpectedly!' % (type(e), str(e))) def check(attrs, known, res=[]): @@ -377,6 +377,17 @@ class PackageTest(unittest.TestCase): filename = os.path.join(test_data_dir, 'invalid_package.xml') self.assertRaises(InvalidPackage, parse_package, filename) + def test_parse_package_xhtml_description(self): + filename = os.path.join(test_data_dir, 'xhtml_description.xml') + expected_plaintext_description = None + with open(os.path.join(test_data_dir, 'xhtml_description.txt'), 'r') as f: + # Strip the trailing newline from the data file. + expected_plaintext_description = f.read().rstrip('\n') + package = parse_package(filename) + assert package.description + + assert package.plaintext_description == expected_plaintext_description + def test_parse_package_string(self): filename = os.path.join(test_data_dir, 'valid_package.xml') xml = _get_package_xml(filename)[0] @@ -392,6 +403,38 @@ class PackageTest(unittest.TestCase): assert isinstance(xml, bytes) parse_package_string(xml) + xml_string = """ + + valid_package + 0.1.0 + valid_package description + + valid_package + 0.1.0 + Invalid < character in description + user + BSD + +""" + self.assertRaises(InvalidPackage, parse_package_string, xml_string) + xml_string = """ + + valid_package + 0.1.0 + valid_package description + user + BSD +Unwanted junk +""" + self.assertRaises(InvalidPackage, parse_package_string, xml_string) + def test_has_ros_schema_reference_string(self): self.assertFalse( has_ros_schema_reference_string( diff --git a/test/test_package_version.py b/test/test_package_version.py index 6265347..dddc995 100644 --- a/test/test_package_version.py +++ b/test/test_package_version.py @@ -3,14 +3,14 @@ import os import shutil import tempfile import unittest +from unittest.mock import Mock +from catkin_pkg.package_version import _replace_setup_py_version from catkin_pkg.package_version import _replace_version from catkin_pkg.package_version import bump_version from catkin_pkg.package_version import update_changelog_sections from catkin_pkg.package_version import update_versions -import mock - from .util import in_temporary_directory @@ -42,6 +42,34 @@ class PackageVersionTest(unittest.TestCase): self.assertRaises(RuntimeError, _replace_version, '', '0.1.1') self.assertRaises(RuntimeError, _replace_version, '0.1.10.1.1', '0.1.1') + def test_replace_setup_py_version(self): + self.assertEqual( + 'version="1.0.0",', + _replace_setup_py_version('version="0.0.1",', '1.0.0')) + self.assertEqual( + "version='1.0.0',", + _replace_setup_py_version("version='0.0.1',", '1.0.0')) + self.assertRaises( + RuntimeError, + _replace_setup_py_version, + '', + '1.0.0') + self.assertRaises( + RuntimeError, + _replace_setup_py_version, + "version='0.1'", + '1.0.0') + self.assertRaises( + RuntimeError, + _replace_setup_py_version, + 'version=something,', + '1.0.0') + self.assertRaises( + RuntimeError, + _replace_setup_py_version, + 'version="0.0.1",version="0.0.1",', + '1.0.0') + def test_update_versions(self): try: root_dir = tempfile.mkdtemp() @@ -52,7 +80,7 @@ class PackageVersionTest(unittest.TestCase): with open(os.path.join(sub_dir, 'package.xml'), 'w') as fhand: fhand.write('1.5.4') - update_versions([root_dir, sub_dir], '7.6.5') + update_versions({root_dir: Mock(), sub_dir: Mock()}, '7.6.5') with open(os.path.join(root_dir, 'package.xml'), 'r') as fhand: contents = fhand.read() @@ -69,7 +97,7 @@ class PackageVersionTest(unittest.TestCase): temp_file = os.path.join(directory, 'changelog') missing_changelogs_but_forthcoming = {} # Mock the Changelog object from catkin_pkg - mock_changelog = mock.Mock() + mock_changelog = Mock() # Create a changelog entry with a unicode char. mock_changelog.rst = ('^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n' 'Changelog for package fake_pkg\n' diff --git a/test/test_packages.py b/test/test_packages.py index fdae799..72761d9 100644 --- a/test/test_packages.py +++ b/test/test_packages.py @@ -8,6 +8,9 @@ from catkin_pkg.packages import find_packages_allowing_duplicates from .util import in_temporary_directory +test_data_dir = os.path.join(os.path.dirname(__file__), 'data', 'ignored_packages') + + def _create_pkg_in_dir(path, version='0.1.0'): path = os.path.abspath(path) os.makedirs(path) @@ -58,3 +61,15 @@ def test_find_packages_invalid_version(): exception_message = str(e) assert version in exception_message assert path in exception_message + + +def test_find_no_ignored_packages(): + result = find_packages(test_data_dir) + assert 'catkin_ignore' not in result + assert 'custom_ignore' in result + + +def test_find_no_ignored_packages_with_custom_ignore(): + custom_result = find_packages(test_data_dir, ignore_markers={'CUSTOM_IGNORE'}) + assert 'custom_ignore' not in custom_result + assert 'catkin_ignore' in custom_result diff --git a/test/test_templates.py b/test/test_templates.py index 561ec28..a01806b 100644 --- a/test/test_templates.py +++ b/test/test_templates.py @@ -2,14 +2,13 @@ import os import shutil import tempfile import unittest +from unittest.mock import MagicMock, Mock from catkin_pkg.package import Dependency, Export, PACKAGE_MANIFEST_FILENAME, parse_package, Url from catkin_pkg.package_templates import _create_include_macro, _create_targetlib_args, _safe_write_files, \ create_cmakelists, create_package_files, create_package_xml, PackageTemplate from catkin_pkg.python_setup import generate_distutils_setup -from mock import MagicMock, Mock - def u(line): try: diff --git a/test/test_topological_order.py b/test/test_topological_order.py index 651cb6b..3b3e3bc 100644 --- a/test/test_topological_order.py +++ b/test/test_topological_order.py @@ -2,8 +2,7 @@ from __future__ import print_function import sys import unittest - -from mock import Mock +from unittest.mock import Mock try: from catkin_pkg.topological_order import topological_order_packages, _PackageDecorator, \ diff --git a/test/test_workspaces.py b/test/test_workspaces.py index 9a6f9e4..be5a76a 100644 --- a/test/test_workspaces.py +++ b/test/test_workspaces.py @@ -6,7 +6,7 @@ import tempfile import unittest try: - from catkin_pkg.workspaces import ensure_workspace_marker, get_spaces, order_paths,\ + from catkin_pkg.workspaces import ensure_workspace_marker, get_spaces, order_paths, \ CATKIN_WORKSPACE_MARKER_FILE except ImportError as e: raise ImportError('Please adjust your PYTHONPATH before running this test: %s' % str(e)) -- 2.34.1