Package epydoc :: Module util
[hide private]
[frames] | no frames]

Source Code for Module epydoc.util

  1  # epydoc -- Utility functions 
  2  # 
  3  # Copyright (C) 2005 Edward Loper 
  4  # Author: Edward Loper <[email protected]> 
  5  # URL: <http://epydoc.sf.net> 
  6  # 
  7  # $Id: util.py 1431 2007-02-04 02:57:23Z dvarrazzo $ 
  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  ## Python Source Types 
 23  ###################################################################### 
 24   
 25  PY_SRC_EXTENSIONS = ['.py', '.pyw'] 
 26  PY_BIN_EXTENSIONS = ['.pyc', '.so', '.pyd'] 
 27   
28 -def is_module_file(path):
29 # Make sure it's a file name. 30 if not isinstance(path, basestring): 31 return False 32 (dir, filename) = os.path.split(path) 33 (basename, extension) = os.path.splitext(filename) 34 return (os.path.isfile(path) and 35 re.match('[a-zA-Z_]\w*$', basename) and 36 extension in PY_SRC_EXTENSIONS+PY_BIN_EXTENSIONS)
37
38 -def is_src_filename(filename):
39 if not isinstance(filename, basestring): return False 40 return os.path.splitext(filename)[1] in PY_SRC_EXTENSIONS
41
42 -def is_package_dir(dirname):
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 # Make sure it's a directory name. 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 # Make sure it's a valid identifier. (Special case for 55 # "foo/", where os.path.split -> ("foo", "").) 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
68 -def is_pyname(name):
69 return re.match(r"\w+(\.\w+)*$", name)
70
71 -def py_src_filename(filename):
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
83 -def munge_script_name(filename):
84 name = os.path.split(filename)[1] 85 name = re.sub(r'\W', '_', name) 86 return 'script-'+name
87 88 ###################################################################### 89 ## Text Processing 90 ###################################################################### 91
92 -def decode_with_backslashreplace(s):
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 # s.encode('string-escape') is not appropriate here, since it 103 # also adds backslashes to some ascii chars (eg \ and '). 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
154 -def plaintext_to_html(s):
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('&', '&amp;').replace('"', '&quot;') 162 s = s.replace('<', '&lt;').replace('>', '&gt;') 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 # These get converted to hyphenation points later 178 if breakany: str = re.sub('(.)', '\\1\1', str) 179 180 # These get converted to \textbackslash later. 181 str = str.replace('\\', '\0') 182 183 # Expand tabs 184 str = str.expandtabs() 185 186 # These elements need to be backslashed. 187 str = re.sub(r'([#$&%_\${}])', r'\\\1', str) 188 189 # These elements have special names. 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 # replace spaces with non-breaking spaces 198 if nbsp: str = str.replace(' ', '~') 199 200 # Convert \1's to hyphenation points. 201 if breakany: str = str.replace('\1', r'\-') 202 203 return str
204
205 -class RunSubprocessError(OSError):
206 - def __init__(self, cmd, out, err):
207 OSError.__init__(self, '%s failed' % cmd[0]) 208 self.out = out 209 self.err = err
210
211 -def run_subprocess(cmd, data=None):
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 # Under Python 2.4+, use subprocess 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 # Assume that there was an error iff anything was written 238 # to the child's stderr. 239 if err == '': 240 return out, err 241 else: 242 raise RunSubprocessError(cmd, out, err) 243 except ImportError: 244 pass 245 246 # Under Python 2.3 or earlier, on unix, use popen2.Popen3 so we 247 # can access the return value. 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 # Under Python 2.3 or earlier, on non-unix, use os.popen3 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 # Guard for a broken pipe error 275 except IOError, e: 276 raise OSError(e) 277 to_child.close() 278 err = child_err.read() 279 out = from_child.read() 280 # Assume that there was an error iff anything was written 281 # to the child's stderr. 282 if err == '': 283 return out, err 284 else: 285 raise RunSubprocessError(cmd, out, err)
286