Skip to content

Commit c02ebbc

Browse files
committed
Add unix_path_join file operation util.
This simplifies a lot of calls to `'/'.join` in multiple operations and provides a single util function for unix style path joins.
1 parent 63bb0e7 commit c02ebbc

File tree

4 files changed

+36
-13
lines changed

4 files changed

+36
-13
lines changed

pyinfra/operations/files.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
ensure_whole_line_match,
5555
get_timestamp,
5656
sed_replace,
57+
unix_path_join,
5758
)
5859

5960

@@ -482,12 +483,11 @@ def sync(
482483
# Join local as normal (unix, win)
483484
full_filename,
484485
# Join remote as unix like
485-
'/'.join(
486+
unix_path_join(*[
486487
item for item in
487-
# Remove any existing / where we're going to join
488-
(dest.rstrip('/'), remote_dirpath.strip('/'), filename.lstrip('/'))
488+
(dest, remote_dirpath, filename)
489489
if item
490-
),
490+
]),
491491
))
492492

493493
# Ensure the destination directory - if the destination is a link, ensure
@@ -507,7 +507,7 @@ def sync(
507507
# Ensure any remote dirnames
508508
for dirpath, dir_mode in ensure_dirnames:
509509
yield directory(
510-
'/'.join((dest, dirpath)),
510+
unix_path_join(dest, dirpath),
511511
user=user, group=group, mode=dir_mode,
512512
state=state, host=host,
513513
)
@@ -711,7 +711,7 @@ def put(
711711
remote_file = host.get_fact(File, path=dest)
712712

713713
if not remote_file and host.get_fact(Directory, path=dest):
714-
dest = '/'.join((dest.rstrip('/'), os_path.basename(src)))
714+
dest = unix_path_join(dest, os_path.basename(src))
715715

716716
if create_remote_dir:
717717
yield _create_remote_dir(state, host, dest, user, group)
@@ -1004,11 +1004,11 @@ def link(
10041004
link_dirname = os_path.dirname(path)
10051005

10061006
if not os_path.isabs(target):
1007-
target = os_path.normpath('/'.join((link_dirname, target)))
1007+
target = os_path.normpath(unix_path_join(link_dirname, target))
10081008

10091009
if info and not os_path.isabs(info['link_target']):
10101010
info['link_target'] = os_path.normpath(
1011-
'/'.join((link_dirname, info['link_target'])),
1011+
unix_path_join(link_dirname, info['link_target']),
10121012
)
10131013

10141014
changed = False

pyinfra/operations/git.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from pyinfra.facts.git import GitBranch, GitConfig, GitTrackingBranch
1313

1414
from . import files, ssh
15-
from .util.files import chown
15+
from .util.files import chown, unix_path_join
1616

1717

1818
@operation(pipeline_facts={
@@ -49,7 +49,7 @@ def config(
4949
existing_config = host.get_fact(GitConfig)
5050

5151
# Only get the config if the repo exists at this stage
52-
elif host.get_fact(Directory, path='/'.join((repo, '.git'))):
52+
elif host.get_fact(Directory, path=unix_path_join(repo, '.git')):
5353
existing_config = host.get_fact(GitConfig, repo=repo)
5454

5555
if existing_config.get(key) != value:
@@ -117,7 +117,7 @@ def repo(
117117

118118
# Store git commands for directory prefix
119119
git_commands = []
120-
is_repo = host.get_fact(Directory, path='/'.join((dest, '.git')))
120+
is_repo = host.get_fact(Directory, path=unix_path_join(dest, '.git'))
121121

122122
# Cloning new repo?
123123
if not is_repo:
@@ -310,7 +310,10 @@ def worktree(
310310
if not host.get_fact(Directory, path=worktree) and present:
311311

312312
# be sure that `repo` is a GIT repository
313-
if not assume_repo_exists and not host.get_fact(Directory, path='/'.join((repo, '.git'))):
313+
if (
314+
not assume_repo_exists
315+
and not host.get_fact(Directory, path=unix_path_join(repo, '.git'))
316+
):
314317
raise OperationError(
315318
'The following folder is not a valid GIT repository : {0}'.format(repo),
316319
)

pyinfra/operations/util/files.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
from pyinfra.api import QuoteString, StringCommand
66

77

8+
def unix_path_join(*parts):
9+
parts = list(parts)
10+
parts[0:-1] = [part.rstrip('/') for part in parts[0:-1]]
11+
return '/'.join(parts)
12+
13+
814
def ensure_mode_int(mode):
915
# Already an int (/None)?
1016
if isinstance(mode, int) or mode is None:

tests/test_operations_utils.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212
import pytest
1313

1414
from pyinfra.operations.util.compat import fspath
15+
from pyinfra.operations.util.files import unix_path_join
1516

1617

1718
class TestCompatFSPath(TestCase):
18-
1919
def test_fspath(self):
2020
assert '/path/to/file' == fspath('/path/to/file')
2121
with pytest.raises(TypeError):
@@ -70,3 +70,17 @@ def __fspath__(cls, obj):
7070
)
7171
def test_fspath_with_pathlib_object(self):
7272
assert path.join('path', 'to', 'file') == fspath(Path('path/to/file'))
73+
74+
75+
class TestUnixPathJoin(TestCase):
76+
def test_simple_path(self):
77+
assert unix_path_join('home', 'pyinfra') == 'home/pyinfra'
78+
79+
def test_absolute_path(self):
80+
assert unix_path_join('/', 'home', 'pyinfra') == '/home/pyinfra'
81+
82+
def test_multiple_slash_path(self):
83+
assert unix_path_join('/', 'home/', 'pyinfra') == '/home/pyinfra'
84+
85+
def test_end_slash_path(self):
86+
assert unix_path_join('/', 'home', 'pyinfra/') == '/home/pyinfra/'

0 commit comments

Comments
 (0)