posixemulation.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. # -*- coding: utf-8 -*-
  2. r"""
  3. werkzeug.posixemulation
  4. ~~~~~~~~~~~~~~~~~~~~~~~
  5. Provides a POSIX emulation for some features that are relevant to
  6. web applications. The main purpose is to simplify support for
  7. systems such as Windows NT that are not 100% POSIX compatible.
  8. Currently this only implements a :func:`rename` function that
  9. follows POSIX semantics. Eg: if the target file already exists it
  10. will be replaced without asking.
  11. This module was introduced in 0.6.1 and is not a public interface.
  12. It might become one in later versions of Werkzeug.
  13. :copyright: 2007 Pallets
  14. :license: BSD-3-Clause
  15. """
  16. import errno
  17. import os
  18. import random
  19. import sys
  20. import time
  21. from ._compat import to_unicode
  22. from .filesystem import get_filesystem_encoding
  23. can_rename_open_file = False
  24. if os.name == "nt":
  25. try:
  26. import ctypes
  27. _MOVEFILE_REPLACE_EXISTING = 0x1
  28. _MOVEFILE_WRITE_THROUGH = 0x8
  29. _MoveFileEx = ctypes.windll.kernel32.MoveFileExW
  30. def _rename(src, dst):
  31. src = to_unicode(src, get_filesystem_encoding())
  32. dst = to_unicode(dst, get_filesystem_encoding())
  33. if _rename_atomic(src, dst):
  34. return True
  35. retry = 0
  36. rv = False
  37. while not rv and retry < 100:
  38. rv = _MoveFileEx(
  39. src, dst, _MOVEFILE_REPLACE_EXISTING | _MOVEFILE_WRITE_THROUGH
  40. )
  41. if not rv:
  42. time.sleep(0.001)
  43. retry += 1
  44. return rv
  45. # new in Vista and Windows Server 2008
  46. _CreateTransaction = ctypes.windll.ktmw32.CreateTransaction
  47. _CommitTransaction = ctypes.windll.ktmw32.CommitTransaction
  48. _MoveFileTransacted = ctypes.windll.kernel32.MoveFileTransactedW
  49. _CloseHandle = ctypes.windll.kernel32.CloseHandle
  50. can_rename_open_file = True
  51. def _rename_atomic(src, dst):
  52. ta = _CreateTransaction(None, 0, 0, 0, 0, 1000, "Werkzeug rename")
  53. if ta == -1:
  54. return False
  55. try:
  56. retry = 0
  57. rv = False
  58. while not rv and retry < 100:
  59. rv = _MoveFileTransacted(
  60. src,
  61. dst,
  62. None,
  63. None,
  64. _MOVEFILE_REPLACE_EXISTING | _MOVEFILE_WRITE_THROUGH,
  65. ta,
  66. )
  67. if rv:
  68. rv = _CommitTransaction(ta)
  69. break
  70. else:
  71. time.sleep(0.001)
  72. retry += 1
  73. return rv
  74. finally:
  75. _CloseHandle(ta)
  76. except Exception:
  77. def _rename(src, dst):
  78. return False
  79. def _rename_atomic(src, dst):
  80. return False
  81. def rename(src, dst):
  82. # Try atomic or pseudo-atomic rename
  83. if _rename(src, dst):
  84. return
  85. # Fall back to "move away and replace"
  86. try:
  87. os.rename(src, dst)
  88. except OSError as e:
  89. if e.errno != errno.EEXIST:
  90. raise
  91. old = "%s-%08x" % (dst, random.randint(0, sys.maxsize))
  92. os.rename(dst, old)
  93. os.rename(src, dst)
  94. try:
  95. os.unlink(old)
  96. except Exception:
  97. pass
  98. else:
  99. rename = os.rename
  100. can_rename_open_file = True