diff --git a/changelogs/fragments/62713-add-path_join-filter.yaml b/changelogs/fragments/62713-add-path_join-filter.yaml new file mode 100644 index 00000000000..9b5bf0d8431 --- /dev/null +++ b/changelogs/fragments/62713-add-path_join-filter.yaml @@ -0,0 +1,2 @@ +minor_changes: + - core filters - Adding ``path_join`` filter to the core filters list diff --git a/docs/docsite/rst/user_guide/playbooks_filters.rst b/docs/docsite/rst/user_guide/playbooks_filters.rst index 553c3a87ef6..48e96f7a06c 100644 --- a/docs/docsite/rst/user_guide/playbooks_filters.rst +++ b/docs/docsite/rst/user_guide/playbooks_filters.rst @@ -1308,6 +1308,12 @@ To get the root and extension of a path or filename (new in version 2.0):: # with path == 'nginx.conf' the return would be ('nginx', '.conf') {{ path | splitext }} +To join one or more path components:: + + {{ ('/etc', path, 'subdir', file) | path_join }} + +.. versionadded:: 2.10 + String filters ============== diff --git a/lib/ansible/plugins/filter/core.py b/lib/ansible/plugins/filter/core.py index eecf7eafe80..f83194be4f4 100644 --- a/lib/ansible/plugins/filter/core.py +++ b/lib/ansible/plugins/filter/core.py @@ -581,6 +581,17 @@ def random_mac(value, seed=None): return value + re.sub(r'(..)', r':\1', rnd) +def path_join(paths): + ''' takes a sequence or a string, and return a concatenation + of the different members ''' + if isinstance(paths, string_types): + return os.path.join(paths) + elif is_sequence(paths): + return os.path.join(*paths) + else: + raise AnsibleFilterError("|path_join expects string or sequence, got %s instead." % type(paths)) + + class FilterModule(object): ''' Ansible core jinja2 filters ''' @@ -612,6 +623,7 @@ class FilterModule(object): 'dirname': partial(unicode_wrap, os.path.dirname), 'expanduser': partial(unicode_wrap, os.path.expanduser), 'expandvars': partial(unicode_wrap, os.path.expandvars), + 'path_join': path_join, 'realpath': partial(unicode_wrap, os.path.realpath), 'relpath': partial(unicode_wrap, os.path.relpath), 'splitext': partial(unicode_wrap, os.path.splitext), diff --git a/test/integration/targets/filters/files/foo.txt b/test/integration/targets/filters/files/foo.txt index 203a1be2b22..a5839cee958 100644 --- a/test/integration/targets/filters/files/foo.txt +++ b/test/integration/targets/filters/files/foo.txt @@ -52,6 +52,10 @@ files to exist and are passthrus to the python os.path functions /etc/motd with basename = motd /etc/motd with dirname = /etc +path_join_simple = /etc/subdir/test +path_join_with_slash = /test +path_join_relative = etc/subdir/test + TODO: realpath follows symlinks. There isn't a test for this just now. TODO: add tests for set theory operations like union diff --git a/test/integration/targets/filters/templates/foo.j2 b/test/integration/targets/filters/templates/foo.j2 index 5661723e5ba..55893cc1321 100644 --- a/test/integration/targets/filters/templates/foo.j2 +++ b/test/integration/targets/filters/templates/foo.j2 @@ -46,6 +46,10 @@ files to exist and are passthrus to the python os.path functions /etc/motd with basename = {{ '/etc/motd' | basename }} /etc/motd with dirname = {{ '/etc/motd' | dirname }} +path_join_simple = {{ ('/etc', 'subdir', 'test') | path_join }} +path_join_with_slash = {{ ('/etc', 'subdir', '/test') | path_join }} +path_join_relative = {{ ('etc', 'subdir', 'test') | path_join }} + TODO: realpath follows symlinks. There isn't a test for this just now. TODO: add tests for set theory operations like union