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