posixemulation.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  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: (c) 2014 by the Werkzeug Team, see AUTHORS for more details.
  14. :license: BSD, see LICENSE for more details.
  15. """
  16. import sys
  17. import os
  18. import errno
  19. import time
  20. import random
  21. from ._compat import to_unicode
  22. from .filesystem import get_filesystem_encoding
  23. can_rename_open_file = False
  24. if os.name == 'nt': # pragma: no cover
  25. _rename = lambda src, dst: False
  26. _rename_atomic = lambda src, dst: False
  27. try:
  28. import ctypes
  29. _MOVEFILE_REPLACE_EXISTING = 0x1
  30. _MOVEFILE_WRITE_THROUGH = 0x8
  31. _MoveFileEx = ctypes.windll.kernel32.MoveFileExW
  32. def _rename(src, dst):
  33. src = to_unicode(src, get_filesystem_encoding())
  34. dst = to_unicode(dst, get_filesystem_encoding())
  35. if _rename_atomic(src, dst):
  36. return True
  37. retry = 0
  38. rv = False
  39. while not rv and retry < 100:
  40. rv = _MoveFileEx(src, dst, _MOVEFILE_REPLACE_EXISTING |
  41. _MOVEFILE_WRITE_THROUGH)
  42. if not rv:
  43. time.sleep(0.001)
  44. retry += 1
  45. return rv
  46. # new in Vista and Windows Server 2008
  47. _CreateTransaction = ctypes.windll.ktmw32.CreateTransaction
  48. _CommitTransaction = ctypes.windll.ktmw32.CommitTransaction
  49. _MoveFileTransacted = ctypes.windll.kernel32.MoveFileTransactedW
  50. _CloseHandle = ctypes.windll.kernel32.CloseHandle
  51. can_rename_open_file = True
  52. def _rename_atomic(src, dst):
  53. ta = _CreateTransaction(None, 0, 0, 0, 0, 1000, 'Werkzeug rename')
  54. if ta == -1:
  55. return False
  56. try:
  57. retry = 0
  58. rv = False
  59. while not rv and retry < 100:
  60. rv = _MoveFileTransacted(src, dst, None, None,
  61. _MOVEFILE_REPLACE_EXISTING |
  62. _MOVEFILE_WRITE_THROUGH, ta)
  63. if rv:
  64. rv = _CommitTransaction(ta)
  65. break
  66. else:
  67. time.sleep(0.001)
  68. retry += 1
  69. return rv
  70. finally:
  71. _CloseHandle(ta)
  72. except Exception:
  73. pass
  74. def rename(src, dst):
  75. # Try atomic or pseudo-atomic rename
  76. if _rename(src, dst):
  77. return
  78. # Fall back to "move away and replace"
  79. try:
  80. os.rename(src, dst)
  81. except OSError as e:
  82. if e.errno != errno.EEXIST:
  83. raise
  84. old = "%s-%08x" % (dst, random.randint(0, sys.maxint))
  85. os.rename(dst, old)
  86. os.rename(src, dst)
  87. try:
  88. os.unlink(old)
  89. except Exception:
  90. pass
  91. else:
  92. rename = os.rename
  93. can_rename_open_file = True