io.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. import contextlib
  2. import logging
  3. import os
  4. LOG = logging.getLogger()
  5. def mkdirs(*dirnames):
  6. for dirname in dirnames:
  7. if not dirname:
  8. continue # '' => curdir
  9. if not os.path.isdir(dirname):
  10. os.makedirs(dirname)
  11. if len(dirnames) == 1:
  12. LOG.debug('mkdir -p "{}"'.format(dirnames[0]))
  13. def syscall(call, nocheck=False):
  14. """Raise Exception in error, unless nocheck==True
  15. """
  16. LOG.info('$(%s)' % repr(call))
  17. rc = os.system(call)
  18. msg = 'Call %r returned %d.' % (call, rc)
  19. if rc:
  20. LOG.warning(msg)
  21. if not nocheck:
  22. raise Exception(msg)
  23. else:
  24. LOG.debug(msg)
  25. return rc
  26. def capture(cmd, nocheck=False):
  27. """Capture output, maybe checking return-code.
  28. Return stdout, fully captured.
  29. Wait for subproc to finish.
  30. Warn if empty.
  31. Raise on non-zero exit-code, unless nocheck.
  32. """
  33. import subprocess
  34. LOG.info('$ {} >'.format(cmd))
  35. proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='ascii')
  36. stdout, stderr = proc.communicate()
  37. rc = proc.returncode
  38. if rc:
  39. msg = '{} <- {!r}:\n{}'.format(rc, cmd, stdout)
  40. if nocheck:
  41. LOG.debug(msg)
  42. else:
  43. raise Exception(msg)
  44. assert stderr is None, '{!r} != None'.format(stderr)
  45. output = stdout
  46. if not output:
  47. msg = '{!r} failed to produce any output.'.format(cmd)
  48. LOG.warning(msg)
  49. return output
  50. def symlink(src, name, force=True):
  51. if os.path.lexists(name):
  52. os.unlink(name)
  53. os.symlink(src, name)
  54. def fix_relative_symlinks(currdir, origdir, recursive=True, relparent='..'):
  55. """
  56. Fix relative symlinks after cp/rsync, assuming they had
  57. been defined relative to 'origdir'.
  58. If 'recursive', then perform this in all (non-symlinked) sub-dirs also.
  59. Skip relative links that point upward shallower than relparent, and warn.
  60. (Always skip absolute symlinks; we assume those already point to persistent space.)
  61. """
  62. if recursive:
  63. for dn in os.listdir(currdir):
  64. if not os.path.islink(dn) and os.path.isdir(dn):
  65. fix_relative_symlinks(os.path.join(currdir, dn), os.path.join(origdir, dn), recursive,
  66. os.path.join('..', relparent))
  67. for fn in os.listdir(currdir):
  68. fn = os.path.join(currdir, fn)
  69. if not os.path.islink(fn):
  70. continue
  71. oldlink = os.readlink(fn)
  72. if os.path.isabs(oldlink):
  73. continue
  74. if not os.path.normpath(oldlink).startswith(relparent):
  75. msg = 'Symlink {}->{} seems to point within the origdir tree. This is unexpected. relparent={}'.format(
  76. fn, oldlink, relparent)
  77. raise Exception(msg)
  78. #LOG.warning(msg)
  79. #continue
  80. newlink = os.path.relpath(os.path.join(origdir, oldlink), currdir)
  81. LOG.debug('Fix symlink to {!r} from {!r}'.format(newlink, oldlink))
  82. symlink(newlink, fn)
  83. def rm(*f):
  84. syscall('rm -f {}'.format(' '.join(f)))
  85. def touch(*paths):
  86. msg = 'touch {!r}'.format(paths)
  87. LOG.debug(msg)
  88. for path in paths:
  89. if os.path.exists(path):
  90. os.utime(path, None)
  91. else:
  92. open(path, 'a').close()
  93. def filesize(fn):
  94. """In bytes.
  95. Raise if fn does not exist.
  96. """
  97. return os.stat(fn).st_size
  98. def exists_and_not_empty(fn):
  99. if not os.path.exists(fn):
  100. return False
  101. if 0 == filesize(fn):
  102. LOG.debug('File {} is empty.'.format(fn))
  103. return False
  104. return True
  105. @contextlib.contextmanager
  106. def cd(newdir):
  107. # https://stackoverflow.com/a/24176022
  108. prevdir = os.getcwd()
  109. LOG.info('CD: %r <- %r' % (newdir, prevdir))
  110. os.chdir(os.path.expanduser(newdir))
  111. try:
  112. yield
  113. finally:
  114. LOG.info('CD: %r -> %r' % (newdir, prevdir))
  115. os.chdir(prevdir)