| 239 | else: |
| 240 | |
| 241 | def commonpath(paths): |
| 242 | # type: (Sequence[Text]) -> Text |
| 243 | if not paths: |
| 244 | raise ValueError("The paths given must be a non-empty sequence") |
| 245 | if len(paths) == 1: |
| 246 | return paths[0] |
| 247 | if len({os.path.isabs(path) for path in paths}) > 1: |
| 248 | raise ValueError( |
| 249 | "Can't mix absolute and relative paths, given:\n{paths}".format( |
| 250 | paths="\n".join(paths) |
| 251 | ) |
| 252 | ) |
| 253 | if WINDOWS and len({os.path.splitdrive(path)[0] for path in paths}) > 1: |
| 254 | raise ValueError( |
| 255 | "Can't mix paths from different drives, given:\n{paths}".format( |
| 256 | paths="\n".join(paths) |
| 257 | ) |
| 258 | ) |
| 259 | |
| 260 | def components(path): |
| 261 | # type: (Text) -> Iterable[Text] |
| 262 | |
| 263 | pieces = deque() # type: Deque[Text] |
| 264 | |
| 265 | def append(piece): |
| 266 | if piece and piece != ".": |
| 267 | pieces.appendleft(piece) |
| 268 | |
| 269 | head, tail = os.path.split(path) |
| 270 | append(tail) |
| 271 | while head: |
| 272 | if "/" == head: |
| 273 | append(head) |
| 274 | break |
| 275 | head, tail = os.path.split(head) |
| 276 | append(tail) |
| 277 | return pieces |
| 278 | |
| 279 | prefix = [] # type: List[Text] |
| 280 | for atoms in zip(*(components(path) for path in paths)): |
| 281 | if len(set(atoms)) == 1: |
| 282 | prefix.append(atoms[0]) |
| 283 | else: |
| 284 | break |
| 285 | if not prefix: |
| 286 | return "" |
| 287 | return os.path.join(*prefix) |
| 288 | |
| 289 | |
| 290 | def safe_commonpath(paths): |