modern.py 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. # Copyright 2017 Virgil Dupras
  2. # This software is licensed under the "BSD" License as described in the "LICENSE" file,
  3. # which should be included with this package. The terms are also available at
  4. # http://www.hardcoded.net/licenses/bsd_license
  5. from __future__ import unicode_literals
  6. import os.path as op
  7. from platform import version
  8. import pythoncom
  9. import pywintypes
  10. from win32com.shell import shell, shellcon
  11. from send2trash.util import preprocess_paths
  12. from send2trash.win.IFileOperationProgressSink import create_sink
  13. def send2trash(paths):
  14. paths = preprocess_paths(paths)
  15. if not paths:
  16. return
  17. # convert data type
  18. paths = [str(path, "mbcs") if not isinstance(path, str) else path for path in paths]
  19. # convert to full paths
  20. paths = [op.abspath(path) if not op.isabs(path) else path for path in paths]
  21. # remove the leading \\?\ if present
  22. paths = [path[4:] if path.startswith("\\\\?\\") else path for path in paths]
  23. # Need to initialize the com before using
  24. pythoncom.CoInitialize()
  25. # create instance of file operation object
  26. fileop = pythoncom.CoCreateInstance(
  27. shell.CLSID_FileOperation,
  28. None,
  29. pythoncom.CLSCTX_ALL,
  30. shell.IID_IFileOperation,
  31. )
  32. # default flags to use
  33. flags = shellcon.FOF_NOCONFIRMATION | shellcon.FOF_NOERRORUI | shellcon.FOF_SILENT | shellcon.FOFX_EARLYFAILURE
  34. # determine rest of the flags based on OS version
  35. # use newer recommended flags if available
  36. if int(version().split(".", 1)[0]) >= 8:
  37. flags |= 0x20000000 | 0x00080000 # FOFX_ADDUNDORECORD win 8+ # FOFX_RECYCLEONDELETE win 8+
  38. else:
  39. flags |= shellcon.FOF_ALLOWUNDO
  40. # set the flags
  41. fileop.SetOperationFlags(flags)
  42. # actually try to perform the operation, this section may throw a
  43. # pywintypes.com_error which does not seem to create as nice of an
  44. # error as OSError so wrapping with try to convert
  45. sink = create_sink()
  46. try:
  47. for path in paths:
  48. item = shell.SHCreateItemFromParsingName(path, None, shell.IID_IShellItem)
  49. fileop.DeleteItem(item, sink)
  50. result = fileop.PerformOperations()
  51. aborted = fileop.GetAnyOperationsAborted()
  52. # if non-zero result or aborted throw an exception
  53. if result or aborted:
  54. raise OSError(None, None, paths, result)
  55. except pywintypes.com_error as error:
  56. # convert to standard OS error, allows other code to get a
  57. # normal errno
  58. raise OSError(None, error.strerror, path, error.hresult) from error
  59. finally:
  60. # Need to make sure we call this once fore every init
  61. pythoncom.CoUninitialize()