1
2
3
4
5
6
7
8
9 """
10 Miscellaneous utility functions that are used by multiple modules.
11
12 @group Python source types: is_module_file, is_package_dir, is_pyname,
13 py_src_filename
14 @group Text processing: wordwrap, decode_with_backslashreplace,
15 plaintext_to_html
16 """
17 __docformat__ = 'epytext en'
18
19 import os, os.path, re
20
21
22
23
24
25 PY_SRC_EXTENSIONS = ['.py', '.pyw']
26 PY_BIN_EXTENSIONS = ['.pyc', '.so', '.pyd']
27
37
41
43 """
44 Return true if the given directory is a valid package directory
45 (i.e., it names a directory that contsains a valid __init__ file,
46 and its name is a valid identifier).
47 """
48
49 if not isinstance(dirname, basestring):
50 return False
51 if not os.path.isdir(dirname):
52 return False
53 dirname = os.path.abspath(dirname)
54
55
56 (parent, dir) = os.path.split(dirname)
57 if dir == '': (parent, dir) = os.path.split(parent)
58 if not re.match('\w+$', dir):
59 return False
60
61 for name in os.listdir(dirname):
62 filename = os.path.join(dirname, name)
63 if name.startswith('__init__.') and is_module_file(filename):
64 return True
65 else:
66 return False
67
69 return re.match(r"\w+(\.\w+)*$", name)
70
72 basefile, extension = os.path.splitext(filename)
73 if extension in PY_SRC_EXTENSIONS:
74 return filename
75 else:
76 for ext in PY_SRC_EXTENSIONS:
77 if os.path.isfile('%s%s' % (basefile, ext)):
78 return '%s%s' % (basefile, ext)
79 else:
80 raise ValueError('Could not find a corresponding '
81 'Python source file for %r.' % filename)
82
87
88
89
90
91
93 r"""
94 Convert the given 8-bit string into unicode, treating any
95 character c such that ord(c)<128 as an ascii character, and
96 converting any c such that ord(c)>128 into a backslashed escape
97 sequence.
98
99 >>> decode_with_backslashreplace('abc\xff\xe8')
100 u'abc\\xff\\xe8'
101 """
102
103
104 assert isinstance(s, str)
105 return (s
106 .decode('latin1')
107 .encode('ascii', 'backslashreplace')
108 .decode('ascii'))
109
110 -def wordwrap(str, indent=0, right=75, startindex=0, splitchars=''):
111 """
112 Word-wrap the given string. I.e., add newlines to the string such
113 that any lines that are longer than C{right} are broken into
114 shorter lines (at the first whitespace sequence that occurs before
115 index C{right}). If the given string contains newlines, they will
116 I{not} be removed. Any lines that begin with whitespace will not
117 be wordwrapped.
118
119 @param indent: If specified, then indent each line by this number
120 of spaces.
121 @type indent: C{int}
122 @param right: The right margin for word wrapping. Lines that are
123 longer than C{right} will be broken at the first whitespace
124 sequence before the right margin.
125 @type right: C{int}
126 @param startindex: If specified, then assume that the first line
127 is already preceeded by C{startindex} characters.
128 @type startindex: C{int}
129 @param splitchars: A list of non-whitespace characters which can
130 be used to split a line. (E.g., use '/\\' to allow path names
131 to be split over multiple lines.)
132 @rtype: C{str}
133 """
134 if splitchars:
135 chunks = re.split(r'( +|\n|[^ \n%s]*[%s])' %
136 (re.escape(splitchars), re.escape(splitchars)),
137 str.expandtabs())
138 else:
139 chunks = re.split(r'( +|\n)', str.expandtabs())
140 result = [' '*(indent-startindex)]
141 charindex = max(indent, startindex)
142 for chunknum, chunk in enumerate(chunks):
143 if (charindex+len(chunk) > right and charindex > 0) or chunk == '\n':
144 result.append('\n' + ' '*indent)
145 charindex = indent
146 if chunk[:1] not in ('\n', ' '):
147 result.append(chunk)
148 charindex += len(chunk)
149 else:
150 result.append(chunk)
151 charindex += len(chunk)
152 return ''.join(result).rstrip()+'\n'
153
155 """
156 @return: An HTML string that encodes the given plaintext string.
157 In particular, special characters (such as C{'<'} and C{'&'})
158 are escaped.
159 @rtype: C{string}
160 """
161 s = s.replace('&', '&').replace('"', '"')
162 s = s.replace('<', '<').replace('>', '>')
163 return s
164
165 -def plaintext_to_latex(str, nbsp=0, breakany=0):
166 """
167 @return: A LaTeX string that encodes the given plaintext string.
168 In particular, special characters (such as C{'$'} and C{'_'})
169 are escaped, and tabs are expanded.
170 @rtype: C{string}
171 @param breakany: Insert hyphenation marks, so that LaTeX can
172 break the resulting string at any point. This is useful for
173 small boxes (e.g., the type box in the variable list table).
174 @param nbsp: Replace every space with a non-breaking space
175 (C{'~'}).
176 """
177
178 if breakany: str = re.sub('(.)', '\\1\1', str)
179
180
181 str = str.replace('\\', '\0')
182
183
184 str = str.expandtabs()
185
186
187 str = re.sub(r'([#$&%_\${}])', r'\\\1', str)
188
189
190 str = str.replace('|', '{\\textbar}')
191 str = str.replace('<', '{\\textless}')
192 str = str.replace('>', '{\\textgreater}')
193 str = str.replace('^', '{\\textasciicircum}')
194 str = str.replace('~', '{\\textasciitilde}')
195 str = str.replace('\0', r'{\textbackslash}')
196
197
198 if nbsp: str = str.replace(' ', '~')
199
200
201 if breakany: str = str.replace('\1', r'\-')
202
203 return str
204
207 OSError.__init__(self, '%s failed' % cmd[0])
208 self.out = out
209 self.err = err
210
212 """
213 Execute the command C{cmd} in a subprocess.
214
215 @param cmd: The command to execute, specified as a list
216 of string.
217 @param data: A string containing data to send to the
218 subprocess.
219 @return: A tuple C{(out, err)}.
220 @raise OSError: If there is any problem executing the
221 command, or if its exitval is not 0.
222 """
223 if isinstance(cmd, basestring):
224 cmd = cmd.split()
225
226
227 try:
228 from subprocess import Popen, PIPE
229 pipe = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
230 out, err = pipe.communicate(data)
231 if hasattr(pipe, 'returncode'):
232 if pipe.returncode == 0:
233 return out, err
234 else:
235 raise RunSubprocessError(cmd, out, err)
236 else:
237
238
239 if err == '':
240 return out, err
241 else:
242 raise RunSubprocessError(cmd, out, err)
243 except ImportError:
244 pass
245
246
247
248 import popen2
249 if hasattr(popen2, 'Popen3'):
250 pipe = popen2.Popen3(' '.join(cmd), True)
251 to_child = pipe.tochild
252 from_child = pipe.fromchild
253 child_err = pipe.childerr
254 if data:
255 to_child.write(data)
256 to_child.close()
257 out = err = ''
258 while pipe.poll() is None:
259 out += from_child.read()
260 err += child_err.read()
261 out += from_child.read()
262 err += child_err.read()
263 if pipe.wait() == 0:
264 return out, err
265 else:
266 raise RunSubprocessError(cmd, out, err)
267
268
269 else:
270 to_child, from_child, child_err = os.popen3(' '.join(cmd), 'b')
271 if data:
272 try:
273 to_child.write(data)
274
275 except IOError, e:
276 raise OSError(e)
277 to_child.close()
278 err = child_err.read()
279 out = from_child.read()
280
281
282 if err == '':
283 return out, err
284 else:
285 raise RunSubprocessError(cmd, out, err)
286