/[debian-med]/trunk/community/infrastructure/test/cddtasktools.py
ViewVC logotype

Contents of /trunk/community/infrastructure/test/cddtasktools.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1349 - (show annotations) (download) (as text)
Sun Feb 10 20:00:32 2008 UTC (5 years, 3 months ago) by tille
File MIME type: text/x-python
File size: 19017 byte(s)
eems quite similar to Davids work now
1 #!/usr/bin/python
2 # Copyright 2008: Andreas Tille <tille@debian.org>
3 # License: GPL
4
5 # CDD Meta packages are listing a set of Dependencies
6 # These might be fullfilled by the Debian package
7 # set or not.
8 #
9 # This interface provides some classes that contains
10 # all available information about this Dependency ,
11 # like whether it is an official package or not,
12 # in which distribution it is contained
13 # or if it is not contained it obtains information
14 # from tasks file about home page, license, WNPP etc.
15
16 import sys
17 import os
18 import urllib
19 import StringIO
20 import gzip
21
22 from debian_bundle import deb822
23
24 BASEURL = 'http://ftp.debian.org/'
25 REPOS = { 'debian-med' : "svn://svn.debian.org/svn/cdd/projects/med/trunk/debian-med/tasks/",
26 'debian-edu' : "svn://svn.debian.org/svn/debian-edu/trunk/src/debian-edu/tasks/",
27 'debian-science' : "svn://svn.debian.org/svn/cdd/projects/science/trunk/debian-science/tasks/",
28 }
29 HTMLBASE = "/var/lib/gforge/chroot/home/groups"
30
31 def InitTasksFiles(cddname):
32 # Obtain tasks files from SVN of a CDD
33 # cddname can be: debian-med, debian-edu, debian-science
34 # In case you know another CDD that uses the meta package
35 # technology make sure to include the location in the
36 # REPOS dictionary
37 #
38 tasksdir = "%s/%s/data/tasks" % (HTMLBASE, cddname)
39 # Checkout/Update tasks from SVN
40 if os.path.isdir(tasksdir+'/.svn'):
41 os.system("svn up %s %s >> /dev/null" % (REPOS[cddname], tasksdir))
42 else:
43 os.system("mkdir -p %s" % (tasksdir))
44 os.system("svn co %s %s >> /dev/null" % (REPOS[cddname], tasksdir))
45 return tasksdir
46
47 def SplitDescription(description):
48 # Split first line of Description value as short description
49
50 lines = description.splitlines()
51 ShortDesc = lines[0]
52 LongDesc = ''
53 for line in lines[1:]:
54 LongDesc += line.strip() + '\n'
55 return (ShortDesc, LongDesc)
56
57
58 class Task:
59 # This class just stores name and description of a task package
60 # FIXME: This is probably not used anymore and can be deleted
61
62 def __init__(self, cddname=None, taskname=None, shortDesc=None, longDesc=None):
63 self.cddname = cddname
64 self.taskname = taskname
65 self.shortDesc = shortDesc
66 self.longDesc = longDesc
67
68 class DependantPackage:
69 # Hold information about a program that is in dependency list
70 # The
71
72 def __init__(self, cddname=None, taskname=None):
73 self.cddname = cddname # CDD that includes the package in dependency list
74 self.taskname = taskname # Task which includes the Dependency
75 self.pkg = None # Name of dependant package
76 self.dependencytype = None # Values: 'Depends', 'Recommends', 'Suggests'
77 self.dists = [] # Values: 'stable', 'testing', 'unstable', etc.
78 self.component = {} # Values: 'main', 'contrib', 'non-free', 'experimental'
79
80 self.why = None # basically used as comment
81
82 # The following keys will be mostly used for programs that
83 # are not yet existing in Debian and will go to our todo list
84 self.homepage = '#' # Homepage of program
85 self.version = None # Version of program
86 self.responsible = None # E-Mail address of issuer of ITP or some person
87 # who volunteered to care for this program
88 self.license = None # License of program
89 self.section = None # Section of package in the Debian hierarchy
90 self.filename = None # Filename of package in the Debian pool
91 self.wnpp = None # WNPP bug number
92 self.pkgShortDesc = None # Prospective packages should have a description ...
93 self.pkgLongDesc = None # ... which could be copied to (or from if exists)
94 # WNPP bug and finally can be used for packaging
95 self.pkgURL = None # URL of inofficial package
96
97 # sort these objects according to the package name
98 def __cmp__(self, other):
99 # Comparing with None object has to return something reasonable
100 if other == None:
101 return -2
102 # Sort according to package name
103 return cmp(self.pkg, other.pkg)
104
105 class CddDependencies:
106 # Provide a list of depencencies defined in Metapackages
107 # This class concerns _all_ tasks of a CDD and is the most
108 # complete source of information. If only a single task
109 # should be handled by a tool that uses cddtasktools
110 # probably the class TaskDependencies (see below) is
111 # your friend
112
113 def __init__(self, cddname):
114
115 # This Instance of the Available class contains all
116 # information about packages that are avialable in Debian
117 # See below for more information about Available class
118 global available
119
120 if cddname not in REPOS.keys():
121 print >>sys.stderr, "Unknown CDD."
122 return None
123
124 self.cddname = cddname
125 self.tasksdir = InitTasksFiles(self.cddname)
126 self.tasks = {}
127 self.tasknames = []
128 self.available = available
129
130 def GetTasknames(self):
131 for task in os.listdir(self.tasksdir):
132 if os.path.isfile("%s/%s" % (self.tasksdir, task)):
133 self.tasknames.append(task)
134 self.tasknames.sort()
135
136 def GetAllDependencies(self):
137 if self.tasknames == []:
138 self.GetTasknames()
139 for task in self.tasknames:
140 td = TaskDependencies(self.cddname, task)
141 td.GetTaskDependencies()
142 self.tasks[task] = td
143
144 def GetNamesOnlyDict(self, dependencytypes=()):
145 # David Paleino needs for his web tools a dictionary
146 # { taskname : [list of dependencies]}
147 # This will be prepared here from the main
148 # datastructure
149 ret = {}
150 if dependencytypes == ():
151 dependencytypes=('official', 'unofficial', 'prospective')
152 for task in self.tasknames:
153 tdeps = self.tasks[task]
154 list = []
155 for dep in dependencytypes:
156 for tdep in tdeps.dependencies[dep]:
157 list.append(tdep.pkg)
158 ret[task] = list
159 return ret
160
161 def GetListOfDepsForTask(self, task, dependencytypes=()):
162 # David Paleino needs for his web tools a dictionary
163 # [list of dependencies]
164 # This will be prepared here from the main
165 # datastructure
166 ret = []
167 if dependencytypes == ():
168 dependencytypes=('official', 'unofficial', 'prospective')
169 tdeps = self.tasks[task]
170 for dep in dependencytypes:
171 for tdep in tdeps.dependencies[dep]:
172 ret.append(tdep.pkg)
173 return ret
174
175 def GetTaskDescDict(self):
176 # David Paleino needs for his web tools a dictionary
177 # { taskname : { 'Task' : task
178 # 'ShortDesc' : shortdesc
179 # 'LongDesc' : longdesc }
180 # This will be prepared here from the main
181 # datastructure
182 ret = {}
183 for task in self.tasknames:
184 tdeps = self.tasks[task]
185 tdict = {}
186 tdict['Task'] = tdeps.taskPrintedName
187 tdict['ShortDesc'] = tdeps.taskShortDesc
188 tdict['LongDesc'] = tdeps.taskLongDesc
189 ret[task] = tdict
190 return ret
191
192 class TaskDependencies:
193 # List of depencencies defined in one Metapackage
194 def __init__(self, cddname, task):
195 if cddname not in REPOS.keys():
196 print >>sys.stderr, "Unknown CDD."
197 return None
198
199 self.cddname = cddname
200 self.tasksdir = InitTasksFiles(self.cddname)
201 self.taskfile = self.tasksdir+'/'+task
202 if os.path.isfile(self.taskfile):
203 self.task = task
204 else:
205 print >>sys.stderr, "No such task file %s." % self.taskfile
206 return None
207
208 # Dictionary with dependencytype as key and list of DependantPackage
209 # instances
210 self.dependencies = { 'official' : [],
211 'unofficial' : [],
212 'prospective' : []
213 }
214 self.available = available
215
216 # Main information for a task
217 self.taskPrintedName = None
218 self.taskShortDesc = None
219 self.taskLongDesc = None
220
221 def GetTaskDependencies(self):
222 # First obtain information about Packages
223 # available in Debian
224 # This might take some time.
225 # Caching comes to mind, but the script is supposed
226 # to be run not more frequently than mirror pushes
227 # and thus only testing the script might profit from
228 # caching
229 self.available.GetPackageNames()
230
231 # These keys might contain more than one item that
232 # has to be separated to detect the dependency
233 dependency_keys = [ "Depends", "Recommends", "Suggests" ]
234
235 f = file(self.taskfile)
236 for stanza in deb822.Sources.iter_paragraphs(f):
237 # Why and Responsible can be valid for more than one dependency
238 # Store them in strings and use them for all Dependent Package objects
239 why = None
240 responsible = None
241 dep = None
242 for key in stanza:
243 if key == 'Task':
244 self.taskPrintedName = stanza['task']
245 continue
246 if key == 'Description':
247 (self.taskShortDesc, self.taskLongDesc) = SplitDescription(stanza['description'])
248 continue
249 if key == 'Why':
250 why = stanza['why']
251 continue
252 if key == 'Responsible':
253 responsible = stanza['responsible']
254 continue
255
256 debug = 0
257 if key in dependency_keys:
258 debug += 1
259 # turn alternatives ('|') into real depends for this purpose
260 # because we are finally interested in all alternatives
261 dependencies = stanza[key].replace('|',',').split(',')
262 # Collect all dependencies in one line first,
263 # create an object for each later
264 deps_in_one_line = []
265 for dependency in dependencies:
266 deps_in_one_line.append(dependency.strip())
267
268 for dep_in_line in deps_in_one_line:
269 # If there are more than one dependencies in one line
270 # just put the current one into the right list of dependencies
271 # before initiating the next instance
272 if dep != None:
273 self.dependencies[self._FindDependencyType(dep)].append(dep)
274 dep = DependantPackage(self.cddname, self.task)
275 # Store the comments in case they might be usefull for later applications
276 dep.why = why
277 dep.responsible = responsible
278 dep.dependencytype = key
279 dep.pkg = dep_in_line
280 dep.dists.append(self.available.dist)
281 # Find the component the Dependency might be in
282 for component in self.available.components:
283 if dep.pkg in self.available.packages[component].keys():
284 dep.component = component
285 dep.pkgShortDesc = self.available.packages[component][dep.pkg].pkgShortDesc
286 dep.pkgLongDesc = self.available.packages[component][dep.pkg].pkgLongDesc
287 dep.homepage = self.available.packages[component][dep.pkg].homepage
288 dep.version = self.available.packages[component][dep.pkg].version
289 # TODO: Assuming 'unstable' is wrong --> handle list of dists
290 dep.pkgURL = 'http://packages.debian.org/' + 'unstable/' + \
291 self.available.packages[component][dep.pkg].section + \
292 '/' + dep.pkg
293 dep.filename = self.available.packages[component][dep.pkg].filename
294 break # The same package should be only in one component
295 # At least I currently see no reason for having a
296 # package with the same name in main and contrib
297 # at the same time for instance
298 continue
299 # The following keys will be mostly used for programs that
300 # are not yet existing in Debian and will go to our todo list
301 if key == 'homepage':
302 if dep != None:
303 dep.homepage = stanza['homepage']
304 else:
305 print >>stderr, "Dep not initiatet before Homepage %s -> something is wrong." \
306 % stanza['homepage']
307 elif key == 'section':
308 if dep != None:
309 dep.section = stanza['section']
310 else:
311 print >>stderr, "Dep not initiatet before Section %s -> something is wrong." \
312 % stanza['license']
313 elif key == 'License':
314 if dep != None:
315 dep.license = stanza['license']
316 else:
317 print >>stderr, "Dep not initiatet before License %s -> something is wrong." \
318 % stanza['license']
319 elif key == 'WNPP':
320 if dep != None:
321 dep.wnpp = stanza['wnpp']
322 else:
323 print >>stderr, "Dep not initiatet before WNPP %s -> something is wrong." \
324 % stanza['wnpp']
325 elif key == 'Pkg-URL':
326 if dep != None:
327 dep.pkgURL = stanza['pkg-url']
328 else:
329 print >>stderr, "Dep not initiatet before Pkg-URL %s -> something is wrong." \
330 % stanza['pkg-url']
331 elif key == 'Pkg-Description':
332 if dep == None:
333 print >>stderr, "Dep not initiatet before Pkg-Description %s -> something is wrong." \
334 % stanza['pkg-description'].splitlines()[0]
335 else:
336 (dep.pkgShortDesc, dep.pkgLongDesc) = SplitDescription(stanza['pkg-description'])
337 else:
338 print "Unknown key '%s': %s" % (key, stanza[key])
339 if dep != None:
340 self.dependencies[self._FindDependencyType(dep)].append(dep)
341
342 f.close()
343
344 for dependency in self.dependencies.keys():
345 self.dependencies[dependency].sort()
346
347
348 def _FindDependencyType(self, dep):
349 # Return the name of the Dependencytype to append the Dependency to the right list
350 if dep.component != {}:
351 return 'official'
352 if dep.pkgURL != None:
353 return 'unofficial'
354 return 'prospective'
355
356
357 class Available:
358 # Information about available packages
359 #
360 # Usage example:
361 # available = Available( # Initialize instance
362 # dist='testing', # (default='unstable')
363 # components=('main'), # Regard only main, default: main, contrib, non-free
364 # source=1 # Use source package names, default: use binaries
365 # arch='sparc' # (default='i386')
366 # )
367 #
368 # available.GetPackageNames() # Actually parse the Packages files to obtain needed information
369 # # This has to be done at least once. It is verified that the effort
370 # # to obtain package information is not done twice per run
371
372
373 def __init__(self, dist=None, components=(), source=None, arch=None):
374 self.source = 'Packages.gz'
375 if source != None:
376 self.source = 'Sources.gz'
377 self.binary = 'source'
378 if source == None:
379 if arch == None:
380 # use arch=i386 as default because it contains most packages
381 self.binary = 'binary-i386'
382 else:
383 self.binary = 'binary-' + arch
384 self.dist = 'unstable'
385 if dist != None:
386 self.dist = dist
387 self.components = ('main', 'contrib', 'non-free')
388 if components != ():
389 self.components = components
390 # The dictionary packages contains the component as key
391 # The values are dictionaries holding package names as key
392 # and a DependantPackage object as values
393 self.packages = {}
394 for component in self.components:
395 self.packages[component] = {}
396
397 def GetPackageNames(self):
398 # Fetch Packages / Sources file and get list of package names out of it
399
400 # Check whether package names are just known
401 for component in self.packages:
402 if self.packages[component] != {}:
403 # Just got the Package names because at least one component
404 # has non zero elements
405 return
406
407 for component in self.components:
408 f = urllib.urlopen(BASEURL+'/dists/'+self.dist+'/'+component+'/'+self.binary+'/'+self.source)
409 compresseddata = f.read()
410 compressedstream = StringIO.StringIO(compresseddata)
411 g = gzip.GzipFile(fileobj=compressedstream)
412 for stanza in deb822.Sources.iter_paragraphs(g, shared_storage=False):
413 deppkg = DependantPackage()
414 (deppkg.pkgShortDesc, deppkg.pkgLongDesc) = SplitDescription(stanza['description'])
415 try:
416 deppkg.homepage = stanza['homepage']
417 except KeyError:
418 deppkg.homepage = '#' # Not every package has a homepage tag
419 except:
420 deppkg.homepage = '.' # Something else in case unexpected things happen
421 deppkg.version = stanza['version'].split('-')[0]
422 deppkg.section = stanza['section']
423 deppkg.filename = BASEURL+stanza['filename']
424 self.packages[component][stanza['package']] = deppkg
425 f.close()
426
427 available = Available()
428

  ViewVC Help
Powered by ViewVC 1.1.5