@@ -139,11 +139,10 @@ def _classify_volume(volume: str) -> tuple[str, str | None]:
139139 (tilde in particular) into "named" -- an over-rejection once paired with
140140 the reference check below, since neither needs a top-level declaration.
141141
142- A drive-qualified source with a target (`C:\data:/var`) never reaches this
143- split: `_validate_service_volumes` refuses it first, so the leading `C`
144- this would otherwise read as a one-character volume name is not a verdict
145- anyone sees. A drive-shaped entry with no target (`C:\data`) does reach it,
146- and is still classified by the name grammar -- see issue 105.
142+ No drive-shaped entry (`C:\data:/var`, `v:/data`) reaches this split:
143+ `_validate_service_volumes` refuses the family first. So the name grammar
144+ below never sees a one-character source, and the `named` verdict it can
145+ return always names a volume Docker would name too.
147146 """
148147 if ":" not in volume :
149148 return "anonymous" , None
@@ -153,21 +152,22 @@ def _classify_volume(volume: str) -> tuple[str, str | None]:
153152 return "bind" , None
154153
155154
156- # A source Docker reads as a Windows drive path, with a target after it: any
157- # single letter, either separator, then a further colon. Measured against
158- # `docker compose config` v5.1.2, the drive marker is what the letter means --
159- # `C:\data:/var` and `C:/data:/var` are binds on `{source: C:\data, target:
160- # /var}`, and so is `v:/data:ro`, read as `{source: v:/data, target: ro}`
161- # rather than the named volume `v` its spelling suggests. Two letters
162- # (`CC:\data:/var`) is an ordinary named-volume reference instead.
155+ # A short-syntax entry Docker reads as a Windows drive path. The marker is one
156+ # leading letter and a colon, whatever the letter is: measured against `docker
157+ # compose config` v5.1.2, `C:\data:/var` and `v:/data:ro` are both binds whose
158+ # source keeps the colon (`{source: v:/data, target: ro}`, not the named
159+ # volume `v` the spelling suggests), and `C:\data`, `v:/data`, `a:/var`, `v:`
160+ # are all anonymous volumes whose target is the whole string -- the last three
161+ # even when that letter is declared top-level, a declaration Docker ignores.
162+ # Two letters (`CC:\data:/var`) is an ordinary named-volume reference instead.
163163#
164- # The trailing colon is load-bearing: it is what makes the source carry a
165- # colon, which is the thing podman's `-v` cannot take (it splits a spec into
166- # at most source:target:options, measured against podman 4.9.3). Without it --
167- # `C:\data`, `v:/data` -- Docker reads an anonymous volume whose target is the
168- # whole string, a different divergence with its own verdict, tracked in issue
169- # 105 rather than refused here .
170- _WINDOWS_DRIVE_SOURCE = re .compile (r"^[a-zA-Z]:[\\/][^:]* :" )
164+ # podman refuses every mount either reading makes (measured, podman 4.9.3): a
165+ # colon inside a source has nowhere to go in a `-v` spec, which splits into at
166+ # most source:target:options (`invalid option type "/var"`), and a container
167+ # path that is not absolute is refused outright (`invalid container path`).
168+ # So the whole family is a rule-two refusal, and a one-character volume name
169+ # is reachable only through the long form, where Docker honours `source: v` .
170+ _DRIVE_SHAPED_SOURCE = re .compile (r"^[a-zA-Z]:" )
171171
172172
173173_VOLUME_LONG_TYPES = ("bind" , "volume" , "tmpfs" , "image" )
@@ -203,13 +203,14 @@ def _validate_service_volumes(name: str, svc: dict[str, Any]) -> None:
203203 if not isinstance (volume , str ):
204204 msg = f"service { name !r} : volume entry must be a string or mapping"
205205 raise UnsupportedComposeError (msg )
206- if _WINDOWS_DRIVE_SOURCE .match (volume ):
207- # Refused before classification, so the drive colon is never read
208- # as the end of a one-character volume name (measured, podman
209- # 4.9.3: `invalid option type "/var"`) .
206+ if _DRIVE_SHAPED_SOURCE .match (volume ):
207+ # Refused before classification, so a single leading letter is
208+ # never read as a one-character volume name the way Docker never
209+ # reads it either .
210210 msg = (
211- f"service { name !r} : volume { volume !r} : a Windows drive-letter path "
212- "is not supported (podman cannot express it)"
211+ f"service { name !r} : volume { volume !r} : a leading single letter is a Windows drive path "
212+ "to Docker, not a volume name, and podman cannot express the mount it makes "
213+ "(name a one-character volume through the long form instead)"
213214 )
214215 raise UnsupportedComposeError (msg )
215216 kind , _ = _classify_volume (volume )
0 commit comments