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
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
53
54 '''DOAP not found'''
55
56
58 '''Initialize attributes'''
59 self.err_msg = err_msg
60
62 return repr(self.err_msg)
63
64
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
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
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
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
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
129 port = None
130 connection = HTTPConnection(host, port=port)
131 connection.request("HEAD", path)
132 resp = connection.getresponse()
133 if resp.status == 200:
134 found = True
135 elif resp.status == 302:
136 found = http_exists(urlparse.urljoin(url,
137 resp.getheader('location', '')))
138 else:
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
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
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
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
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
263
264 '''Prints DOAP in human readable text'''
265
267 '''Initialize attributes'''
268 self.brief = brief
269 self.doap = doap
270
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
287 '''Prints basic DOAP metadata'''
288
289
290
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
324
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
340
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
362 for thing in getattr(self.doap, name):
363 if isinstance(thing, rdfSubject):
364 text = thing.resUri
365 else:
366
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