2 Useful support routines (for internal use).
4 These functions aren't really Zero Install specific; they're things we might
5 wish were in the standard library.
10 # Copyright (C) 2009, Thomas Leonard
11 # See the README file for details, or visit http://0install.net.
13 from zeroinstall
import _
14 import sys
, os
, logging
16 def find_in_path(prog
):
17 """Search $PATH for prog.
18 If prog is an absolute path, return it unmodified.
19 @param prog: name of executable to find
20 @return: the full path of prog, or None if not found
23 if os
.path
.isabs(prog
): return prog
26 for d
in os
.environ
.get('PATH', '/bin:/usr/bin').split(os
.pathsep
):
27 path
= os
.path
.join(d
, prog
)
28 if os
.path
.isfile(path
):
32 def read_bytes(fd
, nbytes
, null_ok
= False):
33 """Read exactly nbytes from fd.
34 @param fd: file descriptor to read from
35 @param nbytes: number of bytes to read
36 @param null_ok: if True, it's OK to receive EOF immediately (we then return None)
37 @return: the bytes read
38 @raise Exception: if we received less than nbytes of data
42 got
= os
.read(fd
, nbytes
)
44 if null_ok
and not data
:
46 raise Exception(_("Unexpected end-of-stream. Data so far %(data)s; expecting %(bytes)d bytes more.")
47 % {'data': repr(data
), 'bytes': nbytes
})
50 logging
.debug(_("Message received: %r"), data
)
53 def pretty_size(size
):
54 """Format a size for printing.
55 @param size: the size in bytes
56 @type size: int (or None)
57 @return: the formatted size
63 return _('%d bytes') % size
65 for unit
in (_('KB'), _('MB'), _('GB'), _('TB')):
69 return _('%(size).1f %(unit)s') % {'size': size
, 'unit': unit
}
72 """Like shutil.rmtree, except that we also delete read-only items.
73 @param root: the root of the subtree to remove
78 if platform
.system() == 'Windows':
79 for main
, dirs
, files
in os
.walk(root
):
80 for i
in files
+ dirs
:
81 os
.chmod(os
.path
.join(main
, i
), 0o700)
84 for main
, dirs
, files
in os
.walk(root
):
88 def raise_with_traceback(ex
, tb
):
89 """Raise an exception in a way that works on Python 2 and Python 3"""
90 if hasattr(ex
, 'with_traceback'):
91 raise ex
.with_traceback(tb
) # Python 3
92 exec("raise ex, None, tb", {'ex': ex
, 'tb': tb
}) # Python 2
95 def portable_rename(src
, dst
):
96 """Rename 'src' to 'dst', which must be on the same filesystem.
97 On POSIX systems, this operation is atomic.
98 On Windows, do the best we can by deleting dst and then renaming.
100 if os
.name
== "nt" and os
.path
.exists(dst
):
104 if sys
.version_info
[0] > 2:
112 from urllib
import parse
113 return parse
.urlparse(url
)
116 unicode = unicode # (otherwise it can't be imported)
117 basestring
= basestring
119 raw_input = raw_input
123 return urlparse
.urlparse(url
)