1
2 """Helper functions for handling package meta data."""
3
4 __all__ = [
5 'parse_pkg_info',
6 'read_pkg_info'
7 'read_pkg_info_from_egg',
8 'read_pkg_info_from_tar'
9 ]
10
11 import logging
12 import re
13 import sys
14 import tarfile
15 import zipfile
16
17 from os.path import isdir, join
18
19 import turbogears as tg
20
21 from eggbasket.util import has_extension, get_base_url
22
23
24 _line_rx = re.compile(r'^([\w-]+?):\s?(.*)')
25 log = logging.getLogger("eggbasket.controllers")
26
27
32
34 """Convert string to unicode by assuming ISO-8859-15 encoding."""
35 try:
36 return unicode(s, 'iso-8859-15')
37 except UnicodeDecodeError:
38 return unicode(s, 'utf-8')
39
41 """Read PKG-INFO file from (compresse/gzipped/bzipped) tar file."""
42 if not tarfile.is_tarfile(filename):
43 raise UnsupportedFormatError("Unsupported tar file")
44 tar = tarfile.open(filename)
45 try:
46
47 pkg_info_filename = (i.name for i in tar
48 if i.name.endswith('egg-info/PKG-INFO')).next()
49 fo = tar.extractfile(pkg_info_filename)
50 except (StopIteration, tarfile.TarError), exc:
51 raise UnsupportedFormatError("Can't read PKG-INFO from '%s'." % filename)
52 else:
53 pkg_info_src = fo.read()
54 fo.close()
55 tar.close()
56
57 return pkg_info_src
58
60 """Read PKG-INFO file from Python egg file or directory."""
61 if isdir(filename):
62 pkg = open(join(filename, 'EGG-INFO', 'PKG-INFO'))
63 pkg_info_src = fo.read()
64 else:
65 try:
66 pkg = zipfile.ZipFile(filename, 'r')
67 except zipfile.error:
68 raise IOError("Can't read egg/zip file '%s'." % filename)
69 else:
70 try:
71 pkg_info_filename = (name for name in pkg.namelist()
72 if name.endswith('/PKG-INFO')).next()
73 pkg_info_src = pkg.read(pkg_info_filename)
74 except (StopIteration, zipfile.error), exc:
75 raise UnsupportedFormatError(
76 "Can't read PKG-INFO from '%s'." % filename)
77 pkg.close()
78
79 return pkg_info_src
80
82 """Sets field value in pkg_info.
83
84 If field is already set, value will be a list of all values.
85
86 """
87 if field in pkg_info:
88 if not isinstance(pkg_info[field], list):
89 pkg_info[field] = [pkg_info[field]]
90 pkg_info[field].append(value)
91 else:
92 pkg_info[field] = value
93
95 """Parse string with PKG-INFO meta data.
96
97 Returns dictionary with PKG-INFO fields as keys. Mutiple field values
98 are collected in a list value.
99
100 Raises ParseError on any malformatted line.
101
102 """
103 lines = pkg_info_src.split('\n')
104 pkg_info = dict()
105 field = None
106 value = ''
107
108 for i, line in enumerate(lines):
109 if line and line[0] in [' ', '\t']:
110
111 if field:
112 value += '\n' + line.strip()
113 else:
114 raise ParseError('Misplaced continuation line: %s' % line)
115 else:
116 if not line.strip():
117
118 continue
119 mo = _line_rx.match(line)
120 if mo:
121
122 if field:
123 set_pkg_info_field(pkg_info, field, value)
124 field = mo.group(1).lower()
125 value = _to_unicode(mo.group(2).strip())
126 else:
127
128 raise ParseError('Invalid PKG-INFO line: %s' % line)
129 else:
130 set_pkg_info_field(pkg_info, field, value)
131 return pkg_info
132
134 """Read PKG-INFO meta data from given package distribution file.
135
136 If the package file format is not supported, returns empty dict.
137
138 See parse_pkg_info() for more information.
139
140 """
141 try:
142 if has_extension(filename, ('.egg', '.zip')):
143 pkg_info_src = read_pkg_info_from_egg(filename)
144 elif has_extension(filename,
145 ('.tar', 'tar.bz2', 'tar.gz', 'tar.Z', '.tgz')):
146 pkg_info_src = read_pkg_info_from_tar(filename)
147 else:
148 raise UnsupportedFormatError
149 except (IOError, OSError, ParseError, UnsupportedFormatError), exc:
150 log.warning("Could not read PKG-INFO from file '%s': %s", filename, exc)
151 return dict()
152 else:
153 return parse_pkg_info(pkg_info_src)
154
155 if __name__ == '__main__':
156 import pprint
157 pprint.pprint(parse_pkg_info(read_pkg_info_from_egg(sys.argv[1])))
158