Log using the "0install" logger rather than "root"
[zeroinstall/solver.git] / zeroinstall / support / __init__.py
blob1281f4d0e1e3fc8d8873d37269e0d3eb74862d65
1 """
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.
7 @since: 0.27
8 """
10 # Copyright (C) 2009, Thomas Leonard
11 # See the README file for details, or visit http://0install.net.
13 from zeroinstall import _, logger
14 import sys, os
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
21 @since: 0.27
22 """
23 if os.path.isabs(prog): return prog
24 if os.name == "nt":
25 prog += '.exe'
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):
29 return path
30 return None
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
39 """
40 data = b''
41 while nbytes:
42 got = os.read(fd, nbytes)
43 if not got:
44 if null_ok and not data:
45 return None
46 raise Exception(_("Unexpected end-of-stream. Data so far %(data)s; expecting %(bytes)d bytes more.")
47 % {'data': repr(data), 'bytes': nbytes})
48 data += got
49 nbytes -= len(got)
50 logger.debug(_("Message received: %r"), data)
51 return 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
58 @rtype: str
59 @since: 0.27"""
60 if size is None:
61 return '?'
62 if size < 2048:
63 return _('%d bytes') % size
64 size = float(size)
65 for unit in (_('KB'), _('MB'), _('GB'), _('TB')):
66 size /= 1024
67 if size < 2048:
68 break
69 return _('%(size).1f %(unit)s') % {'size': size, 'unit': unit}
71 def ro_rmtree(root):
72 """Like shutil.rmtree, except that we also delete read-only items.
73 @param root: the root of the subtree to remove
74 @type root: str
75 @since: 0.28"""
76 import shutil
77 import platform
78 if (os.getcwd() + os.path.sep).startswith(root + os.path.sep):
79 import warnings
80 warnings.warn("Removing tree ({tree}) containing the current directory ({cwd}) - this will not work on Windows".format(cwd = os.getcwd(), tree = root), stacklevel = 2)
82 if platform.system() == 'Windows':
83 for main, dirs, files in os.walk(root):
84 for i in files + dirs:
85 os.chmod(os.path.join(main, i), 0o700)
86 os.chmod(root, 0o700)
87 else:
88 for main, dirs, files in os.walk(root):
89 os.chmod(main, 0o700)
90 shutil.rmtree(root)
92 def raise_with_traceback(ex, tb):
93 """Raise an exception in a way that works on Python 2 and Python 3"""
94 if hasattr(ex, 'with_traceback'):
95 raise ex # Python 3
96 exec("raise ex, None, tb", {'ex': ex, 'tb': tb}) # Python 2
97 assert 0
99 def portable_rename(src, dst):
100 """Rename 'src' to 'dst', which must be on the same filesystem.
101 On POSIX systems, this operation is atomic.
102 On Windows, do the best we can by deleting dst and then renaming.
103 @since: 1.9"""
104 if os.name == "nt" and os.path.exists(dst):
105 os.unlink(dst)
106 os.rename(src, dst)
108 if sys.version_info[0] > 2:
109 # Python 3
110 unicode = str
111 basestring = str
112 intern = sys.intern
113 raw_input = input
115 def urlparse(url):
116 from urllib import parse
117 return parse.urlparse(url)
118 else:
119 # Python 2
120 unicode = unicode # (otherwise it can't be imported)
121 basestring = basestring
122 intern = intern
123 raw_input = raw_input
125 def urlparse(url):
126 import urlparse
127 return urlparse.urlparse(url)