diff --git a/compose/config/types.py b/compose/config/types.py index ff9875218..838fb9f58 100644 --- a/compose/config/types.py +++ b/compose/config/types.py @@ -136,6 +136,20 @@ def normalize_path_for_engine(path): return path.replace('\\', '/') +def normpath(path, win_host=False): + """ Custom path normalizer that handles Compose-specific edge cases like + UNIX paths on Windows hosts and vice-versa. """ + + sysnorm = ntpath.normpath if win_host else os.path.normpath + # If a path looks like a UNIX absolute path on Windows, it probably is; + # we'll need to revert the backslashes to forward slashes after normalization + flip_slashes = path.startswith('/') and IS_WINDOWS_PLATFORM + path = sysnorm(path) + if flip_slashes: + path = path.replace('\\', '/') + return path + + class MountSpec(object): options_map = { 'volume': { @@ -152,12 +166,11 @@ class MountSpec(object): @classmethod def parse(cls, mount_dict, normalize=False, win_host=False): - normpath = ntpath.normpath if win_host else os.path.normpath if mount_dict.get('source'): if mount_dict['type'] == 'tmpfs': raise ConfigurationError('tmpfs mounts can not specify a source') - mount_dict['source'] = normpath(mount_dict['source']) + mount_dict['source'] = normpath(mount_dict['source'], win_host) if normalize: mount_dict['source'] = normalize_path_for_engine(mount_dict['source']) @@ -247,7 +260,7 @@ class VolumeSpec(namedtuple('_VolumeSpec', 'external internal mode')): else: external = parts[0] parts = separate_next_section(parts[1]) - external = ntpath.normpath(external) + external = normpath(external, True) internal = parts[0] if len(parts) > 1: if ':' in parts[1]: diff --git a/tests/unit/config/config_test.py b/tests/unit/config/config_test.py index 08b92a573..1d42c10d5 100644 --- a/tests/unit/config/config_test.py +++ b/tests/unit/config/config_test.py @@ -1291,7 +1291,7 @@ class ConfigTest(unittest.TestCase): assert tmpfs_mount.target == '/tmpfs' assert not tmpfs_mount.is_named_volume - assert host_mount.source == os.path.normpath('/abc') + assert host_mount.source == '/abc' assert host_mount.target == '/xyz' assert not host_mount.is_named_volume