backports.py 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. """
  2. Backports of fixes for joblib dependencies
  3. """
  4. import os
  5. import time
  6. from distutils.version import LooseVersion
  7. from os.path import basename
  8. from multiprocessing import util
  9. try:
  10. import numpy as np
  11. def make_memmap(filename, dtype='uint8', mode='r+', offset=0,
  12. shape=None, order='C', unlink_on_gc_collect=False):
  13. """Custom memmap constructor compatible with numpy.memmap.
  14. This function:
  15. - is a backport the numpy memmap offset fix (See
  16. https://github.com/numpy/numpy/pull/8443 for more details.
  17. The numpy fix is available starting numpy 1.13)
  18. - adds ``unlink_on_gc_collect``, which specifies explicitly whether
  19. the process re-constructing the memmap owns a reference to the
  20. underlying file. If set to True, it adds a finalizer to the
  21. newly-created memmap that sends a maybe_unlink request for the
  22. memmaped file to resource_tracker.
  23. """
  24. util.debug(
  25. "[MEMMAP READ] creating a memmap (shape {}, filename {}, "
  26. "pid {})".format(shape, basename(filename), os.getpid())
  27. )
  28. mm = np.memmap(filename, dtype=dtype, mode=mode, offset=offset,
  29. shape=shape, order=order)
  30. if LooseVersion(np.__version__) < '1.13':
  31. mm.offset = offset
  32. if unlink_on_gc_collect:
  33. from ._memmapping_reducer import add_maybe_unlink_finalizer
  34. add_maybe_unlink_finalizer(mm)
  35. return mm
  36. except ImportError:
  37. def make_memmap(filename, dtype='uint8', mode='r+', offset=0,
  38. shape=None, order='C', unlink_on_gc_collect=False):
  39. raise NotImplementedError(
  40. "'joblib.backports.make_memmap' should not be used "
  41. 'if numpy is not installed.')
  42. if os.name == 'nt':
  43. # https://github.com/joblib/joblib/issues/540
  44. access_denied_errors = (5, 13)
  45. from os import replace
  46. def concurrency_safe_rename(src, dst):
  47. """Renames ``src`` into ``dst`` overwriting ``dst`` if it exists.
  48. On Windows os.replace can yield permission errors if executed by two
  49. different processes.
  50. """
  51. max_sleep_time = 1
  52. total_sleep_time = 0
  53. sleep_time = 0.001
  54. while total_sleep_time < max_sleep_time:
  55. try:
  56. replace(src, dst)
  57. break
  58. except Exception as exc:
  59. if getattr(exc, 'winerror', None) in access_denied_errors:
  60. time.sleep(sleep_time)
  61. total_sleep_time += sleep_time
  62. sleep_time *= 2
  63. else:
  64. raise
  65. else:
  66. raise
  67. else:
  68. from os import replace as concurrency_safe_rename # noqa