diff --git a/compose/config/config.py b/compose/config/config.py index 47ec177e3..2a6e8437b 100644 --- a/compose/config/config.py +++ b/compose/config/config.py @@ -815,11 +815,12 @@ def finalize_service_volumes(service_dict, environment): if 'volumes' in service_dict: finalized_volumes = [] normalize = environment.get_boolean('COMPOSE_CONVERT_WINDOWS_PATHS') + win_host = environment.get_boolean('COMPOSE_FORCE_WINDOWS_HOST') for v in service_dict['volumes']: if isinstance(v, dict): - finalized_volumes.append(MountSpec.parse(v, normalize)) + finalized_volumes.append(MountSpec.parse(v, normalize, win_host)) else: - finalized_volumes.append(VolumeSpec.parse(v, normalize)) + finalized_volumes.append(VolumeSpec.parse(v, normalize, win_host)) service_dict['volumes'] = finalized_volumes return service_dict diff --git a/compose/config/types.py b/compose/config/types.py index 5e1087858..72e68d345 100644 --- a/compose/config/types.py +++ b/compose/config/types.py @@ -4,6 +4,7 @@ Types for objects parsed from the configuration. from __future__ import absolute_import from __future__ import unicode_literals +import ntpath import os import re from collections import namedtuple @@ -145,9 +146,10 @@ class MountSpec(object): _fields = ['type', 'source', 'target', 'read_only', 'consistency'] @classmethod - def parse(cls, mount_dict, normalize=False): + 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'): - mount_dict['source'] = os.path.normpath(mount_dict['source']) + mount_dict['source'] = normpath(mount_dict['source']) if normalize: mount_dict['source'] = normalize_path_for_engine(mount_dict['source']) @@ -193,6 +195,7 @@ class MountSpec(object): class VolumeSpec(namedtuple('_VolumeSpec', 'external internal mode')): + win32 = False @classmethod def _parse_unix(cls, volume_config): @@ -236,7 +239,7 @@ class VolumeSpec(namedtuple('_VolumeSpec', 'external internal mode')): else: external = parts[0] parts = separate_next_section(parts[1]) - external = os.path.normpath(external) + external = ntpath.normpath(external) internal = parts[0] if len(parts) > 1: if ':' in parts[1]: @@ -249,14 +252,16 @@ class VolumeSpec(namedtuple('_VolumeSpec', 'external internal mode')): if normalize: external = normalize_path_for_engine(external) if external else None - return cls(external, internal, mode) + result = cls(external, internal, mode) + result.win32 = True + return result @classmethod - def parse(cls, volume_config, normalize=False): + def parse(cls, volume_config, normalize=False, win_host=False): """Parse a volume_config path and split it into external:internal[:mode] parts to be returned as a valid VolumeSpec. """ - if IS_WINDOWS_PLATFORM: + if IS_WINDOWS_PLATFORM or win_host: return cls._parse_win32(volume_config, normalize) else: return cls._parse_unix(volume_config) @@ -269,7 +274,7 @@ class VolumeSpec(namedtuple('_VolumeSpec', 'external internal mode')): @property def is_named_volume(self): res = self.external and not self.external.startswith(('.', '/', '~')) - if not IS_WINDOWS_PLATFORM: + if not self.win32: return res return (