epydoc.util

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