doapfiend.utils

Package doapfiend :: Module utils |
[hide private]
[frames] | no frames]

# Source Code for Module doapfiend.utils

      1
      2  """
      3
      4  utils.py
      5  ========
      6
      7  Misc utilities for doapfiend
      8  ----------------------------
      9
     10  General purpose helper functions and classes for doapfiend
     11  You'll probably want to use doaplib for most cases.
     12
     13  License: BSD-2
     14
     15  """
     16
     17  #pylint: disable-msg=C0103
     18
     19  import urllib
     20  import logging
     21  import textwrap
     22  import urlparse
     23  from cStringIO import StringIO
     24  from httplib import HTTPConnection
     25  from urllib2 import build_opener, HTTPError, ProxyHandler
     26
     27  from rdflib import ConjunctiveGraph, Namespace
     28  from rdfalchemy import rdfSubject
     29
     30
     31  FOAF = Namespace("http://xmlns.com/foaf/0.1/")
     32
     33  __docformat__ = 'epytext'
     34
     35  LOG = logging.getLogger('doapfiend')
     36
     37  color = {'normal': "\033[0m",
     38            'bold': "\033[1m",
     39            'underline': "\033[4m",
     40            'blink': "\033[5m",
     41            'reverse': "\033[7m",
     42            'black': "\033[30m",
     43            'red': "\033[31m",
     44            'green': "\033[32m",
     45            'yellow': "\033[33m",
     46            'blue': "\033[34m",
     47            'magenta': "\033[35m",
     48            'cyan': "\033[36m",
     49            'white': "\033[37m"}
     50
     51
     52 -class NotFoundError(Exception):
     53
     54      '''DOAP not found'''
     55
     56      #pylint: disable-msg=W0231
     57 -    def __init__(self, err_msg):
     58          '''Initialize attributes'''
     59          self.err_msg = err_msg
     60
     61 -    def __str__(self):
     62          return repr(self.err_msg)
     63
     64
     65 -def http_filesize(url):
     66      """
     67      Get the size of file without downloading it.
     68      bla bla bla
     69      blaba
     70
     71      @param url: URL of file
     72      @type  url: string
     73
     74      @rtype: string
     75      @return: Size of file
     76
     77      Usage:
     78
     79      >>> http_filesize('http://trac.doapspace.org/test_file.txt')
     80      '160'
     81      """
     82
     83      host, path = urlparse.urlsplit(url)[1:3]
     84      if ':' in host:
     85          # port specified, try to use it
     86          host, port = host.split(':', 1)
     87          try:
     88              port = int(port)
     89          except ValueError:
     90              LOG.error('invalid port number %r' % port)
     91              return False
     92      else:
     93          # no port specified, use default port
     94          port = None
     95      connection = HTTPConnection(host, port=port)
     96      connection.request("HEAD", path)
     97      resp = connection.getresponse()
     98      return resp.getheader('content-length')
     99
    100
    101 -def http_exists(url):
    102      """
    103      A quick way to check if a file exists on the web.
    104
    105      @param url: URL of the document
    106      @type  url: string
    107      @rtype: boolean
    108      @return:  True or False
    109
    110      Usage:
    111
    112      >>> http_exists('http://www.python.org/')
    113      True
    114      >>> http_exists('http://www.python.org/PenguinOnTheTelly')
    115      False
    116      """
    117
    118      host, path = urlparse.urlsplit(url)[1:3]
    119      if ':' in host:
    120          #port specified, try to use it
    121          host, port = host.split(':', 1)
    122          try:
    123              port = int(port)
    124          except ValueError:
    125              LOG.error('invalid port number %r' % port)
    126              return False
    127      else:
    128          #no port specified, use default port
    129          port = None
    130      connection = HTTPConnection(host, port=port)
    131      connection.request("HEAD", path)
    132      resp = connection.getresponse()
    133      if resp.status == 200:       # normal 'found' status
    134          found = True
    135      elif resp.status == 302:     # recurse on temporary redirect
    136          found = http_exists(urlparse.urljoin(url,
    137                             resp.getheader('location', '')))
    138      else:                        # everything else -> not found
    139          LOG.info("Status %d %s : %s" % (resp.status, resp.reason, url))
    140          found = False
    141      return found
    142
    143
    144 -def is_content_type(url_or_file, content_type):
    145      """
    146      Tells whether the URL or pseudofile from urllib.urlopen is of
    147      the required content type.
    148
    149      @param url_or_file: URL or file path
    150      @type url_or_file: string
    151      @param content_type: Content type we're looking for
    152      @type content_type: string
    153
    154      @rtype: boolean
    155      @returns: True if it can return the Content type we want
    156
    157      Usage:
    158
    159      >>> is_content_type('http://doapspace.org/doap/sf/nlyrics.rdf', \
    160              'application/rdf+xml')
    161      True
    162      >>> is_content_type('http://doapspace.org/', 'application/rdf+xml')
    163      False
    164      """
    165      try:
    166          if isinstance(url_or_file, str):
    167              thefile = urllib.urlopen(url_or_file)
    168          else:
    169              thefile = url_or_file
    170          result = thefile.info().gettype() == content_type.lower()
    171          if thefile is not url_or_file:
    172              thefile.close()
    173      except IOError:
    174          result = False
    175      return result
    176
    177
    178 -def pretty_name(field):
    179      """
    180      Convert DOAP element name to pretty printable label
    181
    182      @param field: Text to be formatted
    183      @type field: C{string}
    184
    185      @return: formatted string
    186      @rtype: string
    187      """
    188      if field == 'programming_language':
    189          field = 'Prog. Lang.'
    190      if field == 'created':
    191          field = 'DOAP Created'
    192      field = field.capitalize()
    193      field = field.replace('_', ' ')
    194      field = field.replace('-', ' ')
    195      return field
    196
    197
    198 -def get_n3(xml_text):
    199      '''
    200      Return N3 (Notation 3) text
    201
    202      @param xml_text: XML/RDF
    203      @type xml_text: string
    204      @return: Notation 3
    205      @rtype: string
    206      '''
    207      store = ConjunctiveGraph()
    208      graph = store.parse(StringIO(xml_text), publicID=None, format="xml")
    209      return graph.serialize(format="n3")
    210
    211
    212 -def print_misc_field(label, text):
    213      '''
    214      Print colorized and justified single label value pair
    215
    216      @param label: A label
    217      @type label: string
    218      @param text: Text to print
    219      @type text: string
    220
    221      @rtype: None
    222      @return: Nothing
    223      '''
    224      label = color['bold'] + label + color['normal']
    225      print '%s %s' % (label, text)
    226
    227
    228 -def fetch_file(url, proxy=None):
    229      '''
    230      Download file by URL
    231
    232      @param url: URL of a file
    233      @type url: string
    234
    235      @param proxy: URL of HTTP Proxy
    236      @type proxy: string
    237
    238      @return: File
    239      @rtype: string
    240
    241      '''
    242      if not url.startswith('http://') and not url.startswith('ftp://'):
    243          return open(url, 'r').read()
    244      LOG.debug('Fetching ' + url)
    245      if proxy:
    246          opener = build_opener(ProxyHandler({'http': proxy}))
    247      else:
    248          opener = build_opener()
    249      opener.addheaders = [('Accept', 'application/rdf+xml'),
    250              ('User-agent',
    251               'Mozilla/5.0 (compatible; doapfiend ' +
    252               'http://trac.doapspace.org/doapfiend)')]
    253      try:
    254          result = opener.open(url)
    255      except HTTPError, err_msg:
    256          if err_msg.code == 404:
    257              raise NotFoundError('Not found: %s' % url)
    258          else:
    259              LOG.error(err_msg)
    260      return result.read()
    261
    262 -class DoapPrinter(object):
    263
    264      '''Prints DOAP in human readable text'''
    265
    266 -    def __init__(self, doap, brief=False):
    267          '''Initialize attributes'''
    268          self.brief = brief
    269          self.doap = doap
    270
    271 -    def print_doap(self):
    272          '''
    273          Print DOAP in human readable text, optionally colorized
    274
    275          @rtype: None
    276          @return: Just prints DOAP
    277          '''
    278
    279          self.print_misc()
    280          if self.brief:
    281              return
    282          self.print_people()
    283          self.print_repos()
    284          self.print_releases()
    285
    286 -    def print_misc(self):
    287          '''Prints basic DOAP metadata'''
    288          #Tuples with boolean to indicate single or multiple values
    289          #and line wrap or not
    290          #(name, multiple, wrap)
    291          fields = (('name', False, True), ('shortname', False, True),
    292                  ('homepage', False, False), ('shortdesc', True, True),
    293                  ('description', True, True),
    294                  ('old_homepage', True, False), ('created', False, True),
    295                  ('download_mirror', False, False))
    296
    297          fields_verbose = (('license', True, True),
    298                  ('programming_language', True, True),
    299                  ('bug_database', False, False),
    300                  ('screenshots', False, False), ('oper_sys', True, True),
    301                  ('wiki', True, False), ('download_page', False, False),
    302                  ('mailing_list', True, False))
    303
    304          for fld in fields:
    305              self.print_field(fld)
    306          if not self.brief:
    307              for fld in fields_verbose:
    308                  self.print_field(fld)
    309
    310 -    def print_repos(self):
    311          '''Prints DOAP repository metadata'''
    312          if hasattr(self.doap.cvs_repository, 'module') and \
    313                  self.doap.cvs_repository.module is not None:
    314              print_misc_field('CVS Module:', self.doap.cvs_repository.module)
    315              print_misc_field('CVS Anon:', self.doap.cvs_repository.anon_root)
    316              print_misc_field('CVS Browse:',
    317                      self.doap.cvs_repository.cvs_browse.resUri)
    318          if hasattr(self.doap.svn_repository, 'location') and \
    319                  self.doap.svn_repository.location is not None:
    320              print_misc_field('SVN Location:',
    321                      self.doap.svn_repository.location.resUri)
    322              print_misc_field('SVN Browse:',
    323                      self.doap.svn_repository.svn_browse.resUri)
    324
    325 -    def print_releases(self):
    326          '''Print DOAP package release metadata'''
    327          if hasattr(self.doap, 'releases'):
    328              for release in self.doap.releases:
    329                  print color['bold'] + color['cyan'] + release.name + \
    330                          color['normal']
    331                  print color['cyan'] + '  ' + release.revision + ' ' + \
    332                          color['normal'] + release.created
    333                  for frel in release.file_releases:
    334                      print '   %s' % frel.resUri
    335
    336 -    def print_people(self):
    337          '''Print maintainers, documenters, helpers etc.'''
    338          print_misc_field("Maintainers:",
    339                  ",".join([m[FOAF.name] for m in self.doap.maintainer]))
    340
    341 -    def print_field(self, field):
    342          '''
    343          Print a DOAP element
    344
    345          @param field: A misc DOAP element
    346          @type field: string
    347
    348          @rtype: None
    349          @return: Nothing
    350          '''
    351          name, multi, wrap = field
    352          if not hasattr(self.doap, name):
    353              return
    354          attr = getattr(self.doap, name)
    355          if attr == [] or attr is None:
    356              return
    357          label = '%s' % color['bold'] + pretty_name(name) + \
    358                   color['normal'] + ':'
    359          label = label.ljust(21)
    360          if multi:
    361              #Can have multiple values per attribute
    362              for thing in getattr(self.doap, name):
    363                  if isinstance(thing, rdfSubject):
    364                      text = thing.resUri
    365                  else:
    366                      #unicode object
    367                      text = thing
    368          else:
    369              text = getattr(self.doap, name)
    370              if isinstance(text, rdfSubject):
    371                  text = text.resUri
    372          if wrap:
    373              print textwrap.fill('%s %s' % (label, text),
    374                      initial_indent='',
    375                      subsequent_indent = '              ')
    376
    377          else:
    378              print '%s %s' % (label, text)
    379
    380
    381  if __name__ == '__main__':
    382      import doctest
    383      doctest.testmod()
    384