diff options
Diffstat (limited to 'platform_utils.py')
| -rw-r--r-- | platform_utils.py | 126 |
1 files changed, 110 insertions, 16 deletions
diff --git a/platform_utils.py b/platform_utils.py index a3e96531..b2cc2459 100644 --- a/platform_utils.py +++ b/platform_utils.py | |||
| @@ -187,10 +187,10 @@ def symlink(source, link_name): | |||
| 187 | source = _validate_winpath(source) | 187 | source = _validate_winpath(source) |
| 188 | link_name = _validate_winpath(link_name) | 188 | link_name = _validate_winpath(link_name) |
| 189 | target = os.path.join(os.path.dirname(link_name), source) | 189 | target = os.path.join(os.path.dirname(link_name), source) |
| 190 | if os.path.isdir(target): | 190 | if isdir(target): |
| 191 | platform_utils_win32.create_dirsymlink(source, link_name) | 191 | platform_utils_win32.create_dirsymlink(_makelongpath(source), link_name) |
| 192 | else: | 192 | else: |
| 193 | platform_utils_win32.create_filesymlink(source, link_name) | 193 | platform_utils_win32.create_filesymlink(_makelongpath(source), link_name) |
| 194 | else: | 194 | else: |
| 195 | return os.symlink(source, link_name) | 195 | return os.symlink(source, link_name) |
| 196 | 196 | ||
| @@ -220,9 +220,32 @@ def _winpath_is_valid(path): | |||
| 220 | return not drive # "x:" is invalid | 220 | return not drive # "x:" is invalid |
| 221 | 221 | ||
| 222 | 222 | ||
| 223 | def _makelongpath(path): | ||
| 224 | """Return the input path normalized to support the Windows long path syntax | ||
| 225 | ("\\\\?\\" prefix) if needed, i.e. if the input path is longer than the | ||
| 226 | MAX_PATH limit. | ||
| 227 | """ | ||
| 228 | if isWindows(): | ||
| 229 | # Note: MAX_PATH is 260, but, for directories, the maximum value is actually 246. | ||
| 230 | if len(path) < 246: | ||
| 231 | return path | ||
| 232 | if path.startswith(u"\\\\?\\"): | ||
| 233 | return path | ||
| 234 | if not os.path.isabs(path): | ||
| 235 | return path | ||
| 236 | # Append prefix and ensure unicode so that the special longpath syntax | ||
| 237 | # is supported by underlying Win32 API calls | ||
| 238 | return u"\\\\?\\" + os.path.normpath(path) | ||
| 239 | else: | ||
| 240 | return path | ||
| 241 | |||
| 242 | |||
| 223 | def rmtree(path): | 243 | def rmtree(path): |
| 244 | """shutil.rmtree(path) wrapper with support for long paths on Windows. | ||
| 245 | |||
| 246 | Availability: Unix, Windows.""" | ||
| 224 | if isWindows(): | 247 | if isWindows(): |
| 225 | shutil.rmtree(path, onerror=handle_rmtree_error) | 248 | shutil.rmtree(_makelongpath(path), onerror=handle_rmtree_error) |
| 226 | else: | 249 | else: |
| 227 | shutil.rmtree(path) | 250 | shutil.rmtree(path) |
| 228 | 251 | ||
| @@ -234,15 +257,18 @@ def handle_rmtree_error(function, path, excinfo): | |||
| 234 | 257 | ||
| 235 | 258 | ||
| 236 | def rename(src, dst): | 259 | def rename(src, dst): |
| 260 | """os.rename(src, dst) wrapper with support for long paths on Windows. | ||
| 261 | |||
| 262 | Availability: Unix, Windows.""" | ||
| 237 | if isWindows(): | 263 | if isWindows(): |
| 238 | # On Windows, rename fails if destination exists, see | 264 | # On Windows, rename fails if destination exists, see |
| 239 | # https://docs.python.org/2/library/os.html#os.rename | 265 | # https://docs.python.org/2/library/os.html#os.rename |
| 240 | try: | 266 | try: |
| 241 | os.rename(src, dst) | 267 | os.rename(_makelongpath(src), _makelongpath(dst)) |
| 242 | except OSError as e: | 268 | except OSError as e: |
| 243 | if e.errno == errno.EEXIST: | 269 | if e.errno == errno.EEXIST: |
| 244 | os.remove(dst) | 270 | os.remove(_makelongpath(dst)) |
| 245 | os.rename(src, dst) | 271 | os.rename(_makelongpath(src), _makelongpath(dst)) |
| 246 | else: | 272 | else: |
| 247 | raise | 273 | raise |
| 248 | else: | 274 | else: |
| @@ -250,30 +276,98 @@ def rename(src, dst): | |||
| 250 | 276 | ||
| 251 | 277 | ||
| 252 | def remove(path): | 278 | def remove(path): |
| 253 | """Remove (delete) the file path. This is a replacement for os.remove, but | 279 | """Remove (delete) the file path. This is a replacement for os.remove that |
| 254 | allows deleting read-only files on Windows. | 280 | allows deleting read-only files on Windows, with support for long paths and |
| 255 | """ | 281 | for deleting directory symbolic links. |
| 282 | |||
| 283 | Availability: Unix, Windows.""" | ||
| 256 | if isWindows(): | 284 | if isWindows(): |
| 285 | longpath = _makelongpath(path) | ||
| 257 | try: | 286 | try: |
| 258 | os.remove(path) | 287 | os.remove(longpath) |
| 259 | except OSError as e: | 288 | except OSError as e: |
| 260 | if e.errno == errno.EACCES: | 289 | if e.errno == errno.EACCES: |
| 261 | os.chmod(path, stat.S_IWRITE) | 290 | os.chmod(longpath, stat.S_IWRITE) |
| 262 | os.remove(path) | 291 | # Directory symbolic links must be deleted with 'rmdir'. |
| 292 | if islink(longpath) and isdir(longpath): | ||
| 293 | os.rmdir(longpath) | ||
| 294 | else: | ||
| 295 | os.remove(longpath) | ||
| 263 | else: | 296 | else: |
| 264 | raise | 297 | raise |
| 265 | else: | 298 | else: |
| 266 | os.remove(path) | 299 | os.remove(path) |
| 267 | 300 | ||
| 268 | 301 | ||
| 302 | def walk(top, topdown=True, onerror=None, followlinks=False): | ||
| 303 | """os.walk(path) wrapper with support for long paths on Windows. | ||
| 304 | |||
| 305 | Availability: Windows, Unix. | ||
| 306 | """ | ||
| 307 | if isWindows(): | ||
| 308 | return _walk_windows_impl(top, topdown, onerror, followlinks) | ||
| 309 | else: | ||
| 310 | return os.walk(top, topdown, onerror, followlinks) | ||
| 311 | |||
| 312 | |||
| 313 | def _walk_windows_impl(top, topdown, onerror, followlinks): | ||
| 314 | try: | ||
| 315 | names = listdir(top) | ||
| 316 | except error, err: | ||
| 317 | if onerror is not None: | ||
| 318 | onerror(err) | ||
| 319 | return | ||
| 320 | |||
| 321 | dirs, nondirs = [], [] | ||
| 322 | for name in names: | ||
| 323 | if isdir(os.path.join(top, name)): | ||
| 324 | dirs.append(name) | ||
| 325 | else: | ||
| 326 | nondirs.append(name) | ||
| 327 | |||
| 328 | if topdown: | ||
| 329 | yield top, dirs, nondirs | ||
| 330 | for name in dirs: | ||
| 331 | new_path = os.path.join(top, name) | ||
| 332 | if followlinks or not islink(new_path): | ||
| 333 | for x in _walk_windows_impl(new_path, topdown, onerror, followlinks): | ||
| 334 | yield x | ||
| 335 | if not topdown: | ||
| 336 | yield top, dirs, nondirs | ||
| 337 | |||
| 338 | |||
| 339 | def listdir(path): | ||
| 340 | """os.listdir(path) wrapper with support for long paths on Windows. | ||
| 341 | |||
| 342 | Availability: Windows, Unix. | ||
| 343 | """ | ||
| 344 | return os.listdir(_makelongpath(path)) | ||
| 345 | |||
| 346 | |||
| 347 | def rmdir(path): | ||
| 348 | """os.rmdir(path) wrapper with support for long paths on Windows. | ||
| 349 | |||
| 350 | Availability: Windows, Unix. | ||
| 351 | """ | ||
| 352 | os.rmdir(_makelongpath(path)) | ||
| 353 | |||
| 354 | |||
| 355 | def isdir(path): | ||
| 356 | """os.path.isdir(path) wrapper with support for long paths on Windows. | ||
| 357 | |||
| 358 | Availability: Windows, Unix. | ||
| 359 | """ | ||
| 360 | return os.path.isdir(_makelongpath(path)) | ||
| 361 | |||
| 362 | |||
| 269 | def islink(path): | 363 | def islink(path): |
| 270 | """Test whether a path is a symbolic link. | 364 | """os.path.islink(path) wrapper with support for long paths on Windows. |
| 271 | 365 | ||
| 272 | Availability: Windows, Unix. | 366 | Availability: Windows, Unix. |
| 273 | """ | 367 | """ |
| 274 | if isWindows(): | 368 | if isWindows(): |
| 275 | import platform_utils_win32 | 369 | import platform_utils_win32 |
| 276 | return platform_utils_win32.islink(path) | 370 | return platform_utils_win32.islink(_makelongpath(path)) |
| 277 | else: | 371 | else: |
| 278 | return os.path.islink(path) | 372 | return os.path.islink(path) |
| 279 | 373 | ||
| @@ -288,7 +382,7 @@ def readlink(path): | |||
| 288 | """ | 382 | """ |
| 289 | if isWindows(): | 383 | if isWindows(): |
| 290 | import platform_utils_win32 | 384 | import platform_utils_win32 |
| 291 | return platform_utils_win32.readlink(path) | 385 | return platform_utils_win32.readlink(_makelongpath(path)) |
| 292 | else: | 386 | else: |
| 293 | return os.readlink(path) | 387 | return os.readlink(path) |
| 294 | 388 | ||
