posixemulation.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  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. can_rename_open_file = False
  23. if os.name == 'nt': # pragma: no cover
  24. _rename = lambda src, dst: False
  25. _rename_atomic = lambda src, dst: False
  26. try:
  27. import ctypes
  28. _MOVEFILE_REPLACE_EXISTING = 0x1
  29. _MOVEFILE_WRITE_THROUGH = 0x8
  30. _MoveFileEx = ctypes.windll.kernel32.MoveFileExW
  31. def _rename(src, dst):
  32. src = to_unicode(src, sys.getfilesystemencoding())
  33. dst = to_unicode(dst, sys.getfilesystemencoding())
  34. if _rename_atomic(src, dst):
  35. return True
  36. retry = 0
  37. rv = False
  38. while not rv and retry < 100:
  39. rv = _MoveFileEx(src, dst, _MOVEFILE_REPLACE_EXISTING |
  40. _MOVEFILE_WRITE_THROUGH)
  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(src, dst, None, None,
  60. _MOVEFILE_REPLACE_EXISTING |
  61. _MOVEFILE_WRITE_THROUGH, ta)
  62. if rv:
  63. rv = _CommitTransaction(ta)
  64. break
  65. else:
  66. time.sleep(0.001)
  67. retry += 1
  68. return rv
  69. finally:
  70. _CloseHandle(ta)
  71. except Exception:
  72. pass
  73. def rename(src, dst):
  74. # Try atomic or pseudo-atomic rename
  75. if _rename(src, dst):
  76. return
  77. # Fall back to "move away and replace"
  78. try:
  79. os.rename(src, dst)
  80. except OSError as e:
  81. if e.errno != errno.EEXIST:
  82. raise
  83. old = "%s-%08x" % (dst, random.randint(0, sys.maxint))
  84. os.rename(dst, old)
  85. os.rename(src, dst)
  86. try:
  87. os.unlink(old)
  88. except Exception:
  89. pass
  90. else:
  91. rename = os.rename
  92. can_rename_open_file = True