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 <edloper@loper.org> 
  5  # URL: <http://epydoc.sf.net> 
  6  # 
  7  # $Id: util.py 1166 2006-04-05 16:49:40Z edloper $ 
  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 (dir, filename) = os.path.split(path) 30 (basename, extension) = os.path.splitext(filename) 31 return (os.path.isfile(path) and 32 re.match('[a-zA-Z_]\w*$', basename) and 33 extension in PY_SRC_EXTENSIONS+PY_BIN_EXTENSIONS)
34
35 -def is_src_filename(filename):
36 if not isinstance(filename, basestring): return False 37 return os.path.splitext(filename)[1] in PY_SRC_EXTENSIONS
38
39 -def is_package_dir(dirname):
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 # Make sure it's a directory. 46 if not os.path.isdir(dirname): 47 return False 48 # Make sure it's a valid identifier. (Special case for 49 # "foo/", where os.path.split -> ("foo", "").) 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
62 -def is_pyname(name):
63 return re.match(r"\w+(\.\w+)*$", name)
64
65 -def py_src_filename(filename):
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
77 -def munge_script_name(filename):
78 name = os.path.split(filename)[1] 79 name = re.sub(r'\W', '_', name) 80 return 'script-'+name
81 82 ###################################################################### 83 ## Text Processing 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 # s.encode('string-escape') is not appropriate here, since it 97 # also adds backslashes to some ascii chars (eg \ and '). 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
148 -def plaintext_to_html(s):
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('&', '&amp;').replace('"', '&quot;') 156 s = s.replace('<', '&lt;').replace('>', '&gt;') 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 # These get converted to hyphenation points later 172 if breakany: str = re.sub('(.)', '\\1\1', str) 173 174 # These get converted to \textbackslash later. 175 str = str.replace('\\', '\0') 176 177 # Expand tabs 178 str = str.expandtabs() 179 180 # These elements need to be backslashed. 181 str = re.sub(r'([#$&%_\${}])', r'\\\1', str) 182 183 # These elements have special names. 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 # replace spaces with non-breaking spaces 192 if nbsp: str = str.replace(' ', '~') 193 194 # Convert \1's to hyphenation points. 195 if breakany: str = str.replace('\1', r'\-') 196 197 return str
198
199 -class RunSubprocessError(OSError):
200 - def __init__(self, cmd, out, err):
201 OSError.__init__(self, '%s failed' % cmd[0]) 202 self.out = out 203 self.err = err
204
205 -def run_subprocess(cmd, data=None):
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 # Under Python 2.4+, use subprocess 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 # Assume that there was an error iff anything was written 232 # to the child's stderr. 233 if err == '': 234 return out, err 235 else: 236 raise RunSubprocessError(cmd, out, err) 237 except ImportError: 238 pass 239 240 # Under Python 2.3 or earlier, on unix, use popen2.Popen3 so we 241 # can access the return value. 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 # Under Python 2.3 or earlier, on non-unix, use os.popen3 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 # Assume that there was an error iff anything was written 271 # to the child's stderr. 272 if err == '': 273 return out, err 274 else: 275 raise RunSubprocessError(cmd, out, err)
276