diff --git a/launch/launch/substitutions/__init__.py b/launch/launch/substitutions/__init__.py index 1622debaa..fd5770807 100644 --- a/launch/launch/substitutions/__init__.py +++ b/launch/launch/substitutions/__init__.py @@ -31,6 +31,7 @@ from .local_substitution import LocalSubstitution from .not_equals_substitution import NotEqualsSubstitution from .path_join_substitution import PathJoinSubstitution +from .path_join_substitution import PathSubstitution from .python_expression import PythonExpression from .substitution_failure import SubstitutionFailure from .text_substitution import TextSubstitution @@ -55,6 +56,7 @@ 'NotEqualsSubstitution', 'OrSubstitution', 'PathJoinSubstitution', + 'PathSubstitution', 'PythonExpression', 'SubstitutionFailure', 'TextSubstitution', diff --git a/launch/launch/substitutions/path_join_substitution.py b/launch/launch/substitutions/path_join_substitution.py index 27f83328d..6868f1eeb 100644 --- a/launch/launch/substitutions/path_join_substitution.py +++ b/launch/launch/substitutions/path_join_substitution.py @@ -21,13 +21,56 @@ from ..launch_context import LaunchContext from ..substitution import Substitution +<<<<<<< HEAD +======= +from ..utilities import normalize_to_list_of_substitutions +from ..utilities import perform_substitutions +>>>>>>> e716d1f (Add a `/` path join operator for `PathJoinSubstitution` (#868)) class PathJoinSubstitution(Substitution): """Substitution that join paths, in a platform independent way.""" +<<<<<<< HEAD def __init__(self, substitutions: Iterable[Union[Text, Substitution]]) -> None: """Create a PathJoinSubstitution.""" +======= + This takes in a list of path components as substitutions. The substitutions for each path + component are performed and concatenated, and then all path components are joined. + + For example: + + .. code-block:: python + + PathJoinSubstitution([ + EnvironmentVariable('SOME_DIR'), + 'cfg', + ['config_', LaunchConfiguration('map'), '.yml'] + ]) + + Or: + + .. code-block:: python + + cfg_dir = PathJoinSubstitution([EnvironmentVariable('SOME_DIR'), 'cfg']) + cfg_file = cfg_dir / ['config_', LaunchConfiguration('map'), '.yml'] + + If the ``SOME_DIR`` environment variable was set to ``/home/user/dir`` and the ``map`` launch + configuration was set to ``my_map``, this would result in a path equal equivalent to (depending + on the platform): + + .. code-block:: python + + '/home/user/dir/cfg/config_my_map.yml' + """ + + def __init__(self, substitutions: Iterable[SomeSubstitutionsType]) -> None: + """ + Create a PathJoinSubstitution. + + :param substitutions: the list of path component substitutions to join + """ +>>>>>>> e716d1f (Add a `/` path join operator for `PathJoinSubstitution` (#868)) from ..utilities import normalize_to_list_of_substitutions self.__substitutions = normalize_to_list_of_substitutions(substitutions) @@ -41,6 +84,46 @@ def describe(self) -> Text: return f"PathJoin('{' + '.join([s.describe() for s in self.substitutions])}')" def perform(self, context: LaunchContext) -> Text: +<<<<<<< HEAD """Perform the substitution by retrieving the local variable.""" performed_substitutions = [sub.perform(context) for sub in self.__substitutions] return os.path.join(*performed_substitutions) +======= + """Perform the substitutions and join into a path.""" + path_components = [ + perform_substitutions(context, component_substitutions) + for component_substitutions in self.substitutions + ] + return os.path.join(*path_components) + + def __truediv__(self, additional_path: SomeSubstitutionsType) -> 'PathJoinSubstitution': + """Join path substitutions using the / operator, mimicking pathlib.Path operation.""" + return PathJoinSubstitution( + self.substitutions + [normalize_to_list_of_substitutions(additional_path)]) + + +class PathSubstitution(PathJoinSubstitution): + """ + Thin wrapper on PathJoinSubstitution for more pathlib.Path-like construction. + + .. code-block:: python + + PathSubstitution(LaunchConfiguration('base_dir')) / 'sub_dir' / 'file_name' + + Which, for ``base_dir:=/my_dir``, results in (depending on the platform): + + .. code-block:: python + + /my_dir/sub_dir/file_name + + """ + + def __init__(self, path: SomeSubstitutionsType): + """ + Create a PathSubstitution. + + :param path: May be a single text or Substitution element, + or an Iterable of them which are then joined + """ + super().__init__(normalize_to_list_of_substitutions(path)) +>>>>>>> e716d1f (Add a `/` path join operator for `PathJoinSubstitution` (#868)) diff --git a/launch/test/launch/substitutions/test_path_join_substitution.py b/launch/test/launch/substitutions/test_path_join_substitution.py index 0838c7e7c..6e128eb46 100644 --- a/launch/test/launch/substitutions/test_path_join_substitution.py +++ b/launch/test/launch/substitutions/test_path_join_substitution.py @@ -16,10 +16,30 @@ import os +<<<<<<< HEAD from launch.substitutions import PathJoinSubstitution +======= +from launch import LaunchContext +from launch.substitutions import PathJoinSubstitution, PathSubstitution +from launch.substitutions import TextSubstitution +>>>>>>> e716d1f (Add a `/` path join operator for `PathJoinSubstitution` (#868)) def test_path_join(): path = ['asd', 'bsd', 'cds'] sub = PathJoinSubstitution(path) +<<<<<<< HEAD assert sub.perform(None) == os.path.join(*path) +======= + assert sub.perform(context) == os.path.join(*path) + + path = ['path', ['to'], ['my_', TextSubstitution(text='file'), '.yaml']] + sub = PathJoinSubstitution(path) + assert sub.perform(context) == os.path.join('path', 'to', 'my_file.yaml') + + sub = PathSubstitution('some') / 'path' + sub = sub / PathJoinSubstitution(['to', 'some', 'dir']) + sub = sub / (TextSubstitution(text='my_model'), '.xacro') + assert sub.perform(context) == os.path.join( + 'some', 'path', 'to', 'some', 'dir', 'my_model.xacro') +>>>>>>> e716d1f (Add a `/` path join operator for `PathJoinSubstitution` (#868))