test_bits.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. from win32com.server.util import wrap
  2. import pythoncom, sys, os, time, win32api, win32event, tempfile
  3. from win32com.bits import bits
  4. TIMEOUT = 200 # ms
  5. StopEvent = win32event.CreateEvent(None, 0, 0, None)
  6. job_name = 'bits-pywin32-test'
  7. states = dict([(val, (name[13:]))
  8. for name, val in vars(bits).items()
  9. if name.startswith('BG_JOB_STATE_')])
  10. bcm = pythoncom.CoCreateInstance(bits.CLSID_BackgroundCopyManager,
  11. None,
  12. pythoncom.CLSCTX_LOCAL_SERVER,
  13. bits.IID_IBackgroundCopyManager)
  14. class BackgroundJobCallback:
  15. _com_interfaces_ = [bits.IID_IBackgroundCopyCallback]
  16. _public_methods_ = ["JobTransferred", "JobError", "JobModification"]
  17. def JobTransferred(self, job):
  18. print('Job Transferred', job)
  19. job.Complete()
  20. win32event.SetEvent(StopEvent) # exit msg pump
  21. def JobError(self, job, error):
  22. print('Job Error', job, error)
  23. f = error.GetFile()
  24. print('While downloading', f.GetRemoteName())
  25. print('To', f.GetLocalName())
  26. print('The following error happened:')
  27. self._print_error(error)
  28. if f.GetRemoteName().endswith('missing-favicon.ico'):
  29. print('Changing to point to correct file')
  30. f2 = f.QueryInterface(bits.IID_IBackgroundCopyFile2)
  31. favicon = 'http://www.python.org/favicon.ico'
  32. print('Changing RemoteName from', f2.GetRemoteName(), 'to', favicon)
  33. f2.SetRemoteName(favicon)
  34. job.Resume()
  35. else:
  36. job.Cancel()
  37. def _print_error(self, err):
  38. ctx, hresult = err.GetError()
  39. try:
  40. hresult_msg = win32api.FormatMessage(hresult)
  41. except win32api.error:
  42. hresult_msg = ""
  43. print("Context=0x%x, hresult=0x%x (%s)" % (ctx, hresult, hresult_msg))
  44. print(err.GetErrorDescription())
  45. def JobModification(self, job, reserved):
  46. state = job.GetState()
  47. print('Job Modification', job.GetDisplayName(), states.get(state))
  48. # Need to catch TRANSIENT_ERROR here, as JobError doesn't get
  49. # called (apparently) when the error is transient.
  50. if state == bits.BG_JOB_STATE_TRANSIENT_ERROR:
  51. print("Error details:")
  52. err = job.GetError()
  53. self._print_error(err)
  54. job = bcm.CreateJob(job_name, bits.BG_JOB_TYPE_DOWNLOAD)
  55. job.SetNotifyInterface(wrap(BackgroundJobCallback()))
  56. job.SetNotifyFlags(bits.BG_NOTIFY_JOB_TRANSFERRED |
  57. bits.BG_NOTIFY_JOB_ERROR |
  58. bits.BG_NOTIFY_JOB_MODIFICATION)
  59. # The idea here is to intentionally make one of the files fail to be
  60. # downloaded. Then the JobError notification will be triggered, where
  61. # we do fix the failing file by calling SetRemoteName to a valid URL
  62. # and call Resume() on the job, making the job finish successfully.
  63. #
  64. # Note to self: A domain that cannot be resolved will cause
  65. # TRANSIENT_ERROR instead of ERROR, and the JobError notification will
  66. # not be triggered! This can bite you during testing depending on how
  67. # your DNS is configured. For example, if you use OpenDNS.org's DNS
  68. # servers, an invalid hostname will *always* be resolved (they
  69. # redirect you to a search page), so be careful when testing.
  70. job.AddFile('http://www.python.org/favicon.ico', os.path.join(tempfile.gettempdir(), 'bits-favicon.ico'))
  71. job.AddFile('http://www.python.org/missing-favicon.ico', os.path.join(tempfile.gettempdir(), 'bits-missing-favicon.ico'))
  72. for f in job.EnumFiles():
  73. print('Downloading', f.GetRemoteName())
  74. print('To', f.GetLocalName())
  75. job.Resume()
  76. while True:
  77. rc = win32event.MsgWaitForMultipleObjects(
  78. (StopEvent,),
  79. 0,
  80. TIMEOUT,
  81. win32event.QS_ALLEVENTS)
  82. if rc == win32event.WAIT_OBJECT_0:
  83. break
  84. elif rc == win32event.WAIT_OBJECT_0+1:
  85. if pythoncom.PumpWaitingMessages():
  86. break # wm_quit