Merge lp:~paul-eggleton/smart/yocto-smart-fixes into lp:smart

Proposed by Paul-eggleton
Status: Needs review
Proposed branch: lp:~paul-eggleton/smart/yocto-smart-fixes
Merge into: lp:smart
Diff against target: 1794 lines (+730/-71)
26 files modified
smart/backends/rpm/base.py (+36/-7)
smart/backends/rpm/header.py (+12/-5)
smart/backends/rpm/metadata.py (+21/-8)
smart/backends/rpm/pm.py (+17/-0)
smart/cache.py (+72/-3)
smart/ccache.c (+355/-24)
smart/channels/rpm_sys.py (+4/-4)
smart/commands/channel.py (+15/-2)
smart/commands/check.py (+3/-0)
smart/commands/config.py (+10/-1)
smart/commands/download.py (+8/-0)
smart/commands/flag.py (+4/-1)
smart/commands/info.py (+4/-0)
smart/commands/install.py (+4/-0)
smart/commands/mirror.py (+4/-1)
smart/commands/priority.py (+4/-1)
smart/commands/query.py (+51/-0)
smart/commands/reinstall.py (+5/-1)
smart/commands/remove.py (+4/-0)
smart/commands/search.py (+2/-0)
smart/commands/upgrade.py (+3/-0)
smart/control.py (+2/-2)
smart/plugins/detectsys.py (+2/-1)
smart/searcher.py (+11/-3)
smart/transaction.py (+21/-7)
smart/util/optparse.py (+56/-0)
To merge this branch: bzr merge lp:~paul-eggleton/smart/yocto-smart-fixes
Reviewer Review Type Date Requested Status
Smart Package Manager Team Pending
Review via email: mp+147946@code.launchpad.net

Description of the change

Included are most of the patches for Smart developed within the Yocto Project and being carried in OE-core which are now being carried in OpenEmbedded-Core. Each patch has a commit message explaining what it does; if further clarification or adjustments are needed please let me know.

To post a comment you must log in.

Unmerged revisions

1096. By Paul-eggleton

Print a more friendly error if YAML output is requested without PyYAML

Signed-off-by: Paul Eggleton <email address hidden>

1095. By Bogdan Marinescu <email address hidden>

To fix some multilib issues, change the way the RPM backend decides
if two packages can coexist: if they have a different architecture,
automatically assume that they can coexist (which is fundamental for
multilib).

Signed-off-by: Bogdan Marinescu <email address hidden>

1094. By Bogdan Marinescu <email address hidden>

Improve error reporting in smart

Add code to check proper command line arguments for various
smart commands. Exit with error if erroneous/additional arguments
are given in the command line.

Signed-off-by: Bogdan Marinescu <email address hidden>

1093. By Mark Hatle <email address hidden>

backends/rpm: Fix incorrect call to the match function

The match function should take three parameters, name, comparison, version...
The original code was passing it a reference to the object holding the data
instead, which caused the comparison in match to always fail.

Signed-off-by: Mark Hatle <email address hidden>

1092. By Paul-eggleton

backends/rpm: remove creation of /var/tmp

This doesn't appear to be needed, and breaks installation of base-files
in OpenEmbedded (since that is a symlink installed as part of the
package).

Signed-off-by: Paul Eggleton <email address hidden>

1091. By Paul-eggleton

backends/rpm: fix parsing of rpm-md metadata

If assertions are disabled then the queue.pop() wasn't being executed,
leading to requires, recommends etc. not being read properly.

Signed-off-by: Paul Eggleton <email address hidden>

1090. By Paul-eggleton

backends/rpm: add support for setting dependency flags

This is useful for OpenEmbedded so that we can do the equivalent of
the --nolinktos and --noparentdirs rpm command line options.

Signed-off-by: Paul Eggleton <email address hidden>

1089. By Paul-eggleton

backends/rpm: implement rpm-extra-macros option

Allow defining extra macros in the smart configuration to be passed
to rpm before opening the database.

Signed-off-by: Paul Eggleton <email address hidden>

1088. By Paul-eggleton

Handle recommended packages in core and rpm backends

Identify and store recommended packages in the cache, add a query option
to read them and ignore them if they are not present when installing.

Initial identification code from Mark Hatle <email address hidden>.

Signed-off-by: Paul Eggleton <email address hidden>

1087. By Paul-eggleton

Fix smart RPM backend to handle rpm-dbpath/rpm-root properly

Don't assume that if the dbpath starts with / that it is an absolute
path. This matches the behaviour of rpm itself. (If the root path is
specified and does not start with /, rpm will prepend the root path
twice and fail).

Signed-off-by: Paul Eggleton <email address hidden>

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'smart/backends/rpm/base.py'
2--- smart/backends/rpm/base.py 2011-10-06 07:10:20 +0000
3+++ smart/backends/rpm/base.py 2013-02-12 14:46:20 +0000
4@@ -46,7 +46,17 @@
5 "rpm", "getTS", "getArchScore", "getArchColor", "system_provides",
6 "collapse_libc_requires"]
7
8+def rpm_join_dbpath(root, dbpath):
9+ if dbpath.startswith('/') and root:
10+ return os.path.join(root, dbpath[1:])
11+ else:
12+ return os.path.join(root, dbpath)
13+
14 def getTS(new=False):
15+ if sysconf.get("rpm-extra-macros"):
16+ for key, value in sysconf.get("rpm-extra-macros").items():
17+ rpm.addMacro(key, str(value))
18+
19 rpm_root = os.path.abspath(sysconf.get("rpm-root", "/"))
20 if not hasattr(getTS, "ts") or getTS.root != rpm_root:
21 getTS.root = rpm_root
22@@ -56,7 +66,7 @@
23 if not sysconf.get("rpm-check-signatures", False):
24 getTS.ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES)
25 rpm_dbpath = sysconf.get("rpm-dbpath", "var/lib/rpm")
26- dbdir = os.path.join(getTS.root, rpm_dbpath)
27+ dbdir = rpm_join_dbpath(getTS.root, rpm_dbpath)
28 if not os.path.isdir(dbdir):
29 try:
30 os.makedirs(dbdir)
31@@ -72,12 +82,6 @@
32 else:
33 iface.warning(_("Initialized new rpm database at %s")
34 % getTS.root)
35- tmpdir = os.path.join(getTS.root, "var/tmp")
36- if not os.path.isdir(tmpdir):
37- try:
38- os.makedirs(tmpdir)
39- except OSError:
40- pass
41 if new:
42 if sysconf.get("rpm-dbpath"):
43 rpm.addMacro('_dbpath', "/" + sysconf.get("rpm-dbpath"))
44@@ -192,6 +196,29 @@
45 break
46 else:
47 return False
48+ srecs = fk(self.recommends)
49+ orecs = fk(other.recommends)
50+ if srecs != orecs:
51+ for srec in srecs:
52+ if srec.name[0] == "/" or srec in orecs:
53+ continue
54+ for orec in orecs:
55+ if (srec.name == orec.name and
56+ srec.relation == orec.relation and
57+ checkver(srec.version, orec.version)):
58+ break
59+ else:
60+ return False
61+ for orec in orecs:
62+ if orec.name[0] == "/" or orec in srecs:
63+ continue
64+ for srec in srecs:
65+ if (srec.name == orec.name and
66+ srec.relation == orec.relation and
67+ checkver(srec.version, orec.version)):
68+ break
69+ else:
70+ return False
71 return True
72
73 def coexists(self, other):
74@@ -201,6 +228,8 @@
75 return False
76 selfver, selfarch = splitarch(self.version)
77 otherver, otherarch = splitarch(other.version)
78+ if selfarch != otherarch:
79+ return True
80 selfcolor = getArchColor(selfarch)
81 othercolor = getArchColor(otherarch)
82 if (selfcolor and othercolor and selfcolor != othercolor and
83
84=== modified file 'smart/backends/rpm/header.py'
85--- smart/backends/rpm/header.py 2011-10-05 15:09:41 +0000
86+++ smart/backends/rpm/header.py 2013-02-12 14:46:20 +0000
87@@ -292,6 +292,7 @@
88 f = [0]
89 elif type(f) != list:
90 f = [f]
91+ recdict = {}
92 reqdict = {}
93 for i in range(len(n)):
94 ni = n[i]
95@@ -308,10 +309,16 @@
96 # RPMSENSE_SCRIPT_PREUN |
97 # RPMSENSE_SCRIPT_POST |
98 # RPMSENSE_SCRIPT_POSTUN == 7744
99- reqdict[(f[i]&7744 and PreReq or Req,
100- intern(ni), r, vi)] = True
101+ if (f[i]&rpm.RPMSENSE_MISSINGOK):
102+ recdict[(f[i]&7744 and PreReq or Req,
103+ intern(ni), r, vi)] = True
104+ else:
105+ reqdict[(f[i]&7744 and PreReq or Req,
106+ intern(ni), r, vi)] = True
107+ recargs = collapse_libc_requires(recdict.keys())
108 reqargs = collapse_libc_requires(reqdict.keys())
109 else:
110+ recargs = None
111 reqargs = None
112
113 n = h[1054] # RPMTAG_CONFLICTNAME
114@@ -365,7 +372,7 @@
115 versionarch = "%s@%s" % (distversion, arch)
116
117 pkg = self.buildPackage((Pkg, name, versionarch),
118- prvargs, reqargs, upgargs, cnfargs)
119+ prvargs, reqargs, upgargs, cnfargs, recargs)
120 pkg.loaders[self] = offset
121 self._offsets[offset] = pkg
122 self._groups[pkg] = intern(h[rpm.RPMTAG_GROUP])
123@@ -583,8 +590,8 @@
124 def setErrataFlags(self, flagdict):
125 self._flagdict = flagdict
126
127- def buildPackage(self, pkgargs, prvargs, reqargs, upgargs, cnfargs):
128- pkg = Loader.buildPackage(self, pkgargs, prvargs, reqargs, upgargs, cnfargs)
129+ def buildPackage(self, pkgargs, prvargs, reqargs, upgargs, cnfargs, recargs):
130+ pkg = Loader.buildPackage(self, pkgargs, prvargs, reqargs, upgargs, cnfargs, recargs)
131 name = pkgargs[1]
132 if hasattr(self, '_flagdict') and self._flagdict and name in self._flagdict:
133 if sysconf.getReadOnly():
134
135=== modified file 'smart/backends/rpm/metadata.py'
136--- smart/backends/rpm/metadata.py 2011-10-05 15:09:41 +0000
137+++ smart/backends/rpm/metadata.py 2013-02-12 14:46:20 +0000
138@@ -165,6 +165,7 @@
139 distepoch = None
140 info = {}
141 reqdict = {}
142+ recdict = {}
143 prvdict = {}
144 upgdict = {}
145 cnfdict = {}
146@@ -188,7 +189,8 @@
147
148 elif event == "end":
149
150- assert queue.pop() is elem
151+ popped = queue.pop()
152+ assert popped is elem
153
154 if skip:
155 if tag == skip:
156@@ -287,12 +289,16 @@
157
158 lasttag = queue[-1].tag
159 if lasttag == REQUIRES:
160- if elem.get("pre") == "1":
161- reqdict[(RPMPreRequires,
162- ename, erelation, eversion)] = True
163+ if elem.get("missingok") == "1":
164+ recdict[(RPMRequires,
165+ ename, erelation, eversion)] = True
166 else:
167- reqdict[(RPMRequires,
168- ename, erelation, eversion)] = True
169+ if elem.get("pre") == "1":
170+ reqdict[(RPMPreRequires,
171+ ename, erelation, eversion)] = True
172+ else:
173+ reqdict[(RPMRequires,
174+ ename, erelation, eversion)] = True
175
176 elif lasttag == PROVIDES:
177 if ename[0] == "/":
178@@ -326,8 +332,14 @@
179 reqargs = [x for x in reqdict
180 if not ((x[2] is None or "=" in x[2]) and
181 (RPMProvides, x[1], x[3]) in prvdict or
182- system_provides.match(*x[:3]))]
183+ system_provides.match(x[1], x[2], x[3]))]
184 reqargs = collapse_libc_requires(reqargs)
185+
186+ recargs = [x for x in recdict
187+ if not ((x[2] is None or "=" in x[2]) and
188+ (RPMProvides, x[1], x[3]) in prvdict or
189+ system_provides.match(x[1], x[2], x[3]))]
190+
191 prvargs = prvdict.keys()
192 cnfargs = cnfdict.keys()
193 upgargs = upgdict.keys()
194@@ -339,7 +351,7 @@
195 versionarch = "%s@%s" % (distversion, arch)
196
197 pkg = self.buildPackage((RPMPackage, name, versionarch),
198- prvargs, reqargs, upgargs, cnfargs)
199+ prvargs, reqargs, upgargs, cnfargs, recargs)
200 pkg.loaders[self] = info
201
202 # Store the provided files for future usage.
203@@ -362,6 +374,7 @@
204 distepoch = None
205 pkgid = None
206 reqdict.clear()
207+ recdict.clear()
208 prvdict.clear()
209 upgdict.clear()
210 cnfdict.clear()
211
212=== modified file 'smart/backends/rpm/pm.py'
213--- smart/backends/rpm/pm.py 2010-12-17 11:26:28 +0000
214+++ smart/backends/rpm/pm.py 2013-02-12 14:46:20 +0000
215@@ -106,6 +106,23 @@
216 flags |= rpm.RPMTRANS_FLAG_TEST
217 ts.setFlags(flags)
218
219+ dflags = ts.setDFlags(0)
220+ if sysconf.get("rpm-noupgrade", False):
221+ dflags |= rpm.RPMDEPS_FLAG_NOUPGRADE
222+ if sysconf.get("rpm-norequires", False):
223+ dflags |= rpm.RPMDEPS_FLAG_NOREQUIRES
224+ if sysconf.get("rpm-noconflicts", False):
225+ dflags |= rpm.RPMDEPS_FLAG_NOCONFLICTS
226+ if sysconf.get("rpm-noobsoletes", False):
227+ dflags |= rpm.RPMDEPS_FLAG_NOOBSOLETES
228+ if sysconf.get("rpm-noparentdirs", False):
229+ dflags |= rpm.RPMDEPS_FLAG_NOPARENTDIRS
230+ if sysconf.get("rpm-nolinktos", False):
231+ dflags |= rpm.RPMDEPS_FLAG_NOLINKTOS
232+ if sysconf.get("rpm-nosuggest", False):
233+ dflags |= rpm.RPMDEPS_FLAG_NOSUGGEST
234+ ts.setDFlags(dflags)
235+
236 # Set rpm verbosity level.
237 levelname = sysconf.get('rpm-log-level')
238 level = {
239
240=== modified file 'smart/cache.py'
241--- smart/cache.py 2010-08-24 08:15:35 +0000
242+++ smart/cache.py 2013-02-12 14:46:20 +0000
243@@ -32,7 +32,8 @@
244 self.name = name
245 self.version = version
246 self.provides = ()
247- self.requires = ()
248+ self.requires = []
249+ self.recommends = []
250 self.upgrades = ()
251 self.conflicts = ()
252 self.installed = False
253@@ -55,7 +56,9 @@
254 fk([x for x in self.provides if x.name[0] != "/"]) !=
255 fk([x for x in other.provides if x.name[0] != "/"]) or
256 fk([x for x in self.requires if x.name[0] != "/"]) !=
257- fk([x for x in other.requires if x.name[0] != "/"])):
258+ fk([x for x in other.requires if x.name[0] != "/"]) or
259+ fk([x for x in self.recommends if x.name[0] != "/"]) !=
260+ fk([x for x in other.recommends if x.name[0] != "/"])):
261 return False
262 return True
263
264@@ -110,6 +113,7 @@
265 self.version,
266 self.provides,
267 self.requires,
268+ self.recommends,
269 self.upgrades,
270 self.conflicts,
271 self.installed,
272@@ -122,6 +126,7 @@
273 self.version,
274 self.provides,
275 self.requires,
276+ self.recommends,
277 self.upgrades,
278 self.conflicts,
279 self.installed,
280@@ -274,6 +279,7 @@
281 self.version = version
282 self.packages = []
283 self.requiredby = ()
284+ self.recommendedby = ()
285 self.upgradedby = ()
286 self.conflictedby = ()
287
288@@ -401,7 +407,7 @@
289 def loadFileProvides(self, fndict):
290 pass
291
292- def buildPackage(self, pkgargs, prvargs, reqargs, upgargs, cnfargs):
293+ def buildPackage(self, pkgargs, prvargs, reqargs, upgargs, cnfargs, recargs = None):
294 cache = self._cache
295 pkg = pkgargs[0](*pkgargs[1:])
296 relpkgs = []
297@@ -427,6 +433,17 @@
298 relpkgs.append(req.packages)
299 pkg.requires.append(req)
300
301+ if recargs:
302+ pkg.recommends = []
303+ for args in recargs:
304+ rec = cache._objmap.get(args)
305+ if not rec:
306+ rec = args[0](*args[1:])
307+ cache._objmap[args] = rec
308+ cache._recommends.append(rec)
309+ relpkgs.append(rec.packages)
310+ pkg.recommends.append(rec)
311+
312 if upgargs:
313 pkg.upgrades = []
314 for args in upgargs:
315@@ -572,6 +589,7 @@
316 self._packages = []
317 self._provides = []
318 self._requires = []
319+ self._recommends = []
320 self._upgrades = []
321 self._conflicts = []
322 self._objmap = {}
323@@ -581,6 +599,8 @@
324 del prv.packages[:]
325 if prv.requiredby:
326 del prv.requiredby[:]
327+ if prv.recommendedby:
328+ del prv.recommendedby[:]
329 if prv.upgradedby:
330 del prv.upgradedby[:]
331 if prv.conflictedby:
332@@ -589,6 +609,10 @@
333 del req.packages[:]
334 if req.providedby:
335 del req.providedby[:]
336+ for rec in self._recommends:
337+ del rec.packages[:]
338+ if rec.providedby:
339+ del rec.providedby[:]
340 for upg in self._upgrades:
341 del upg.packages[:]
342 if upg.providedby:
343@@ -600,6 +624,7 @@
344 del self._packages[:]
345 del self._provides[:]
346 del self._requires[:]
347+ del self._recommends[:]
348 del self._upgrades[:]
349 del self._conflicts[:]
350 self._objmap.clear()
351@@ -621,6 +646,7 @@
352 packages = {}
353 provides = {}
354 requires = {}
355+ recommends = {}
356 upgrades = {}
357 conflicts = {}
358 objmap = self._objmap
359@@ -646,6 +672,11 @@
360 if req not in requires:
361 objmap[req.getInitArgs()] = req
362 requires[req] = True
363+ for rec in pkg.recommends[:]:
364+ rec.packages.append(pkg)
365+ if rec not in recommends:
366+ objmap[rec.getInitArgs()] = rec
367+ recommends[rec] = True
368 for upg in pkg.upgrades:
369 upg.packages.append(pkg)
370 if upg not in upgrades:
371@@ -659,6 +690,7 @@
372 self._packages[:] = packages.keys()
373 self._provides[:] = provides.keys()
374 self._requires[:] = requires.keys()
375+ self._recommends[:] = recommends.keys()
376 self._upgrades[:] = upgrades.keys()
377 self._conflicts[:] = conflicts.keys()
378
379@@ -710,6 +742,14 @@
380 lst.append(req)
381 else:
382 reqnames[name] = [req]
383+ recnames = {}
384+ for rec in self._recommends:
385+ for name in rec.getMatchNames():
386+ lst = recnames.get(name)
387+ if lst:
388+ lst.append(rec)
389+ else:
390+ recnames[name] = [rec]
391 upgnames = {}
392 for upg in self._upgrades:
393 for name in upg.getMatchNames():
394@@ -739,6 +779,18 @@
395 prv.requiredby.append(req)
396 else:
397 prv.requiredby = [req]
398+ lst = recnames.get(prv.name)
399+ if lst:
400+ for rec in lst:
401+ if rec.matches(prv):
402+ if rec.providedby:
403+ rec.providedby.append(prv)
404+ else:
405+ rec.providedby = [prv]
406+ if prv.recommendedby:
407+ prv.recommendedby.append(rec)
408+ else:
409+ prv.recommendedby = [rec]
410 lst = upgnames.get(prv.name)
411 if lst:
412 for upg in lst:
413@@ -782,6 +834,12 @@
414 else:
415 return [x for x in self._requires if x.name == name]
416
417+ def getRecommends(self, name=None):
418+ if not name:
419+ return self._recommends
420+ else:
421+ return [x for x in self._recommends if x.name == name]
422+
423 def getUpgrades(self, name=None):
424 if not name:
425 return self._upgrades
426@@ -807,6 +865,12 @@
427 for req in self._requires:
428 if prvname in req.getMatchNames() and req.matches(prv):
429 searcher.addResult(req)
430+ if searcher.recommends:
431+ for prv in searcher.recommends:
432+ prvname = prv.name
433+ for req in self._recommends:
434+ if prvname in req.getMatchNames() and req.matches(prv):
435+ searcher.addResult(req)
436 if searcher.upgrades:
437 for prv in searcher.upgrades:
438 prvname = prv.name
439@@ -839,6 +903,7 @@
440 self._packages = state["_packages"]
441 provides = {}
442 requires = {}
443+ recommends = {}
444 upgrades = {}
445 conflicts = {}
446 for pkg in self._packages:
447@@ -848,6 +913,9 @@
448 for req in pkg.requires:
449 req.packages.append(pkg)
450 requires[req] = True
451+ for rec in pkg.recommends:
452+ rec.packages.append(pkg)
453+ recommends[rec] = True
454 for upg in pkg.upgrades:
455 upg.packages.append(pkg)
456 upgrades[upg] = True
457@@ -856,6 +924,7 @@
458 conflicts[cnf] = True
459 self._provides = provides.keys()
460 self._requires = requires.keys()
461+ self._recommends = recommends.keys()
462 self._upgrades = upgrades.keys()
463 self._conflicts = conflicts.keys()
464 self._objmap = {}
465
466=== modified file 'smart/ccache.c'
467--- smart/ccache.c 2009-03-02 12:56:54 +0000
468+++ smart/ccache.c 2013-02-12 14:46:20 +0000
469@@ -82,6 +82,7 @@
470 PyObject *version;
471 PyObject *provides;
472 PyObject *requires;
473+ PyObject *recommends;
474 PyObject *upgrades;
475 PyObject *conflicts;
476 PyObject *installed;
477@@ -96,6 +97,7 @@
478 PyObject *version;
479 PyObject *packages;
480 PyObject *requiredby;
481+ PyObject *recommendedby;
482 PyObject *upgradedby;
483 PyObject *conflictedby;
484 } ProvidesObject;
485@@ -123,6 +125,7 @@
486 PyObject *_packages;
487 PyObject *_provides;
488 PyObject *_requires;
489+ PyObject *_recommends;
490 PyObject *_upgrades;
491 PyObject *_conflicts;
492 PyObject *_objmap;
493@@ -211,7 +214,8 @@
494 Py_INCREF(self->name);
495 Py_INCREF(self->version);
496 self->provides = PyTuple_New(0);
497- self->requires = PyTuple_New(0);
498+ self->requires = PyList_New(0);
499+ self->recommends = PyList_New(0);
500 self->upgrades = PyTuple_New(0);
501 self->conflicts = PyTuple_New(0);
502 Py_INCREF(Py_False);
503@@ -228,6 +232,7 @@
504 {
505 Py_VISIT(self->provides);
506 Py_VISIT(self->requires);
507+ Py_VISIT(self->recommends);
508 Py_VISIT(self->upgrades);
509 Py_VISIT(self->conflicts);
510 Py_VISIT(self->loaders);
511@@ -239,6 +244,7 @@
512 {
513 Py_CLEAR(self->provides);
514 Py_CLEAR(self->requires);
515+ Py_CLEAR(self->recommends);
516 Py_CLEAR(self->upgrades);
517 Py_CLEAR(self->conflicts);
518 Py_CLEAR(self->loaders);
519@@ -252,6 +258,7 @@
520 Py_XDECREF(self->version);
521 Py_XDECREF(self->provides);
522 Py_XDECREF(self->requires);
523+ Py_XDECREF(self->recommends);
524 Py_XDECREF(self->upgrades);
525 Py_XDECREF(self->conflicts);
526 Py_XDECREF(self->installed);
527@@ -453,6 +460,46 @@
528 }
529 }
530
531+ ilen = 0;
532+ jlen = 0;
533+ for (i = 0; i != PyList_GET_SIZE(self->recommends); i++) {
534+ PyObject *item = PyList_GET_ITEM(self->recommends, i);
535+ if (!PyObject_IsInstance(item, (PyObject *)&Depends_Type)) {
536+ PyErr_SetString(PyExc_TypeError, "Depends instance expected");
537+ return NULL;
538+ }
539+ if (STR(((DependsObject *)item)->name)[0] != '/')
540+ ilen += 1;
541+ }
542+ for (j = 0; j != PyList_GET_SIZE(other->recommends); j++) {
543+ PyObject *item = PyList_GET_ITEM(other->recommends, j);
544+ if (!PyObject_IsInstance(item, (PyObject *)&Depends_Type)) {
545+ PyErr_SetString(PyExc_TypeError, "Depends instance expected");
546+ return NULL;
547+ }
548+ if (STR(((DependsObject *)item)->name)[0] != '/')
549+ jlen += 1;
550+ }
551+ if (ilen != jlen) {
552+ ret = Py_False;
553+ goto exit;
554+ }
555+
556+ ilen = PyList_GET_SIZE(self->recommends);
557+ jlen = PyList_GET_SIZE(other->recommends);
558+ for (i = 0; i != ilen; i++) {
559+ PyObject *item = PyList_GET_ITEM(self->recommends, i);
560+ if (STR(((DependsObject *)item)->name)[0] != '/') {
561+ for (j = 0; j != jlen; j++)
562+ if (item == PyList_GET_ITEM(other->recommends, j))
563+ break;
564+ if (j == jlen) {
565+ ret = Py_False;
566+ goto exit;
567+ }
568+ }
569+ }
570+
571 exit:
572 Py_INCREF(ret);
573 return ret;
574@@ -606,13 +653,14 @@
575 static PyObject *
576 Package__getstate__(PackageObject *self, PyObject *args)
577 {
578- PyObject *state = PyTuple_New(10);
579+ PyObject *state = PyTuple_New(11);
580 if (!state) return NULL;
581
582 Py_INCREF(self->name);
583 Py_INCREF(self->version);
584 Py_INCREF(self->provides);
585 Py_INCREF(self->requires);
586+ Py_INCREF(self->recommends);
587 Py_INCREF(self->upgrades);
588 Py_INCREF(self->conflicts);
589 Py_INCREF(self->installed);
590@@ -620,16 +668,17 @@
591 Py_INCREF(self->priority);
592 Py_INCREF(self->loaders);
593
594- PyTuple_SET_ITEM(state, 0, self->name);
595- PyTuple_SET_ITEM(state, 1, self->version);
596- PyTuple_SET_ITEM(state, 2, self->provides);
597- PyTuple_SET_ITEM(state, 3, self->requires);
598- PyTuple_SET_ITEM(state, 4, self->upgrades);
599- PyTuple_SET_ITEM(state, 5, self->conflicts);
600- PyTuple_SET_ITEM(state, 6, self->installed);
601- PyTuple_SET_ITEM(state, 7, self->essential);
602- PyTuple_SET_ITEM(state, 8, self->priority);
603- PyTuple_SET_ITEM(state, 9, self->loaders);
604+ PyTuple_SET_ITEM(state, 0, self->name);
605+ PyTuple_SET_ITEM(state, 1, self->version);
606+ PyTuple_SET_ITEM(state, 2, self->provides);
607+ PyTuple_SET_ITEM(state, 3, self->requires);
608+ PyTuple_SET_ITEM(state, 4, self->recommends);
609+ PyTuple_SET_ITEM(state, 5, self->upgrades);
610+ PyTuple_SET_ITEM(state, 6, self->conflicts);
611+ PyTuple_SET_ITEM(state, 7, self->installed);
612+ PyTuple_SET_ITEM(state, 8, self->essential);
613+ PyTuple_SET_ITEM(state, 9, self->priority);
614+ PyTuple_SET_ITEM(state, 10, self->loaders);
615
616 return state;
617 }
618@@ -637,7 +686,7 @@
619 static PyObject *
620 Package__setstate__(PackageObject *self, PyObject *state)
621 {
622- if (!PyTuple_Check(state) || PyTuple_GET_SIZE(state) != 10) {
623+ if (!PyTuple_Check(state) || PyTuple_GET_SIZE(state) != 11) {
624 PyErr_SetString(StateVersionError, "");
625 return NULL;
626 }
627@@ -645,18 +694,20 @@
628 self->version = PyTuple_GET_ITEM(state, 1);
629 self->provides = PyTuple_GET_ITEM(state, 2);
630 self->requires = PyTuple_GET_ITEM(state, 3);
631- self->upgrades = PyTuple_GET_ITEM(state, 4);
632- self->conflicts = PyTuple_GET_ITEM(state, 5);
633- self->installed = PyTuple_GET_ITEM(state, 6);
634- self->essential = PyTuple_GET_ITEM(state, 7);
635- self->priority = PyTuple_GET_ITEM(state, 8);
636- self->loaders = PyTuple_GET_ITEM(state, 9);
637+ self->recommends = PyTuple_GET_ITEM(state, 4);
638+ self->upgrades = PyTuple_GET_ITEM(state, 5);
639+ self->conflicts = PyTuple_GET_ITEM(state, 6);
640+ self->installed = PyTuple_GET_ITEM(state, 7);
641+ self->essential = PyTuple_GET_ITEM(state, 8);
642+ self->priority = PyTuple_GET_ITEM(state, 9);
643+ self->loaders = PyTuple_GET_ITEM(state, 10);
644
645
646 Py_INCREF(self->name);
647 Py_INCREF(self->version);
648 Py_INCREF(self->provides);
649 Py_INCREF(self->requires);
650+ Py_INCREF(self->recommends);
651 Py_INCREF(self->upgrades);
652 Py_INCREF(self->conflicts);
653 Py_INCREF(self->installed);
654@@ -686,6 +737,7 @@
655 {"version", T_OBJECT, OFF(version), 0, 0},
656 {"provides", T_OBJECT, OFF(provides), 0, 0},
657 {"requires", T_OBJECT, OFF(requires), 0, 0},
658+ {"recommends", T_OBJECT, OFF(recommends), 0, 0},
659 {"upgrades", T_OBJECT, OFF(upgrades), 0, 0},
660 {"conflicts", T_OBJECT, OFF(conflicts), 0, 0},
661 {"installed", T_OBJECT, OFF(installed), 0, 0},
662@@ -750,6 +802,7 @@
663 Py_INCREF(self->version);
664 self->packages = PyList_New(0);
665 self->requiredby = PyTuple_New(0);
666+ self->recommendedby = PyTuple_New(0);
667 self->upgradedby = PyTuple_New(0);
668 self->conflictedby = PyTuple_New(0);
669 return 0;
670@@ -760,6 +813,7 @@
671 {
672 Py_VISIT(self->packages);
673 Py_VISIT(self->requiredby);
674+ Py_VISIT(self->recommendedby);
675 Py_VISIT(self->upgradedby);
676 Py_VISIT(self->conflictedby);
677 return 0;
678@@ -770,6 +824,7 @@
679 {
680 Py_CLEAR(self->packages);
681 Py_CLEAR(self->requiredby);
682+ Py_CLEAR(self->recommendedby);
683 Py_CLEAR(self->upgradedby);
684 Py_CLEAR(self->conflictedby);
685 return 0;
686@@ -782,6 +837,7 @@
687 Py_XDECREF(self->version);
688 Py_XDECREF(self->packages);
689 Py_XDECREF(self->requiredby);
690+ Py_XDECREF(self->recommendedby);
691 Py_XDECREF(self->upgradedby);
692 Py_XDECREF(self->conflictedby);
693 self->ob_type->tp_free((PyObject *)self);
694@@ -960,6 +1016,7 @@
695 {"version", T_OBJECT, OFF(version), 0, 0},
696 {"packages", T_OBJECT, OFF(packages), 0, 0},
697 {"requiredby", T_OBJECT, OFF(requiredby), 0, 0},
698+ {"recommendedby", T_OBJECT, OFF(recommendedby), 0, 0},
699 {"upgradedby", T_OBJECT, OFF(upgradedby), 0, 0},
700 {"conflictedby", T_OBJECT, OFF(conflictedby), 0, 0},
701 {NULL}
702@@ -1555,6 +1612,7 @@
703 PyObject *reqargs;
704 PyObject *upgargs;
705 PyObject *cnfargs;
706+ PyObject *recargs = NULL;
707 PyObject *callargs;
708
709 PyObject *pkg;
710@@ -1574,9 +1632,10 @@
711
712 cache = (CacheObject *)self->_cache;
713
714- if (!PyArg_ParseTuple(args, "O!O&O&O&O&", &PyTuple_Type, &pkgargs,
715+ if (!PyArg_ParseTuple(args, "O!O&O&O&O&|O&", &PyTuple_Type, &pkgargs,
716 mylist, &prvargs, mylist, &reqargs,
717- mylist, &upgargs, mylist, &cnfargs))
718+ mylist, &upgargs, mylist, &cnfargs,
719+ mylist, &recargs))
720 return NULL;
721
722 if (PyTuple_GET_SIZE(pkgargs) < 2) {
723@@ -1701,6 +1760,59 @@
724 }
725 }
726
727+ /* if recargs: */
728+ if (recargs) {
729+ int i = 0;
730+ int len = PyList_GET_SIZE(recargs);
731+ /* pkg.recommends = [] */
732+ Py_DECREF(pkgobj->recommends);
733+ pkgobj->recommends = PyList_New(len);
734+ /* for args in recargs: */
735+ for (; i != len; i++) {
736+ PyObject *args = PyList_GET_ITEM(recargs, i);
737+ DependsObject *recobj;
738+ PyObject *rec;
739+
740+ if (!PyTuple_Check(args)) {
741+ PyErr_SetString(PyExc_TypeError,
742+ "Item in recargs is not a tuple");
743+ return NULL;
744+ }
745+
746+ /* rec = cache._objmap.get(args) */
747+ rec = PyDict_GetItem(cache->_objmap, args);
748+ recobj = (DependsObject *)rec;
749+
750+ /* if not rec: */
751+ if (!rec) {
752+ if (!PyTuple_Check(args) || PyTuple_GET_SIZE(args) < 2) {
753+ PyErr_SetString(PyExc_ValueError, "Invalid recargs tuple");
754+ return NULL;
755+ }
756+ /* rec = args[0](*args[1:]) */
757+ callargs = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args));
758+ rec = PyObject_CallObject(PyTuple_GET_ITEM(args, 0), callargs);
759+ Py_DECREF(callargs);
760+ if (!rec) return NULL;
761+ recobj = (DependsObject *)rec;
762+
763+ /* cache._objmap[args] = rec */
764+ PyDict_SetItem(cache->_objmap, args, rec);
765+ Py_DECREF(rec);
766+
767+ /* cache._recommends.append(rec) */
768+ PyList_Append(cache->_recommends, rec);
769+ }
770+
771+ /* relpkgs.append(rec.packages) */
772+ PyList_Append(relpkgs, recobj->packages);
773+
774+ /* pkg.recommends.append(rec) */
775+ Py_INCREF(rec);
776+ PyList_SET_ITEM(pkgobj->recommends, i, rec);
777+ }
778+ }
779+
780 /* if upgargs: */
781 if (upgargs) {
782 int i = 0;
783@@ -2391,6 +2503,7 @@
784 self->_packages = PyList_New(0);
785 self->_provides = PyList_New(0);
786 self->_requires = PyList_New(0);
787+ self->_recommends = PyList_New(0);
788 self->_upgrades = PyList_New(0);
789 self->_conflicts = PyList_New(0);
790 self->_objmap = PyDict_New();
791@@ -2404,6 +2517,7 @@
792 Py_VISIT(self->_packages);
793 Py_VISIT(self->_provides);
794 Py_VISIT(self->_requires);
795+ Py_VISIT(self->_recommends);
796 Py_VISIT(self->_upgrades);
797 Py_VISIT(self->_conflicts);
798 Py_VISIT(self->_objmap);
799@@ -2417,6 +2531,7 @@
800 Py_CLEAR(self->_packages);
801 Py_CLEAR(self->_provides);
802 Py_CLEAR(self->_requires);
803+ Py_CLEAR(self->_recommends);
804 Py_CLEAR(self->_upgrades);
805 Py_CLEAR(self->_conflicts);
806 Py_CLEAR(self->_objmap);
807@@ -2430,6 +2545,7 @@
808 Py_XDECREF(self->_packages);
809 Py_XDECREF(self->_provides);
810 Py_XDECREF(self->_requires);
811+ Py_XDECREF(self->_recommends);
812 Py_XDECREF(self->_upgrades);
813 Py_XDECREF(self->_conflicts);
814 Py_XDECREF(self->_objmap);
815@@ -2449,6 +2565,8 @@
816 LIST_CLEAR(prvobj->packages);
817 if (PyList_Check(prvobj->requiredby))
818 LIST_CLEAR(prvobj->requiredby);
819+ if (PyList_Check(prvobj->recommendedby))
820+ LIST_CLEAR(prvobj->recommendedby);
821 if (PyList_Check(prvobj->upgradedby))
822 LIST_CLEAR(prvobj->upgradedby);
823 if (PyList_Check(prvobj->conflictedby))
824@@ -2464,6 +2582,16 @@
825 if (PyList_Check(reqobj->providedby))
826 LIST_CLEAR(reqobj->providedby);
827 }
828+ len = PyList_GET_SIZE(self->_recommends);
829+ for (i = 0; i != len; i++) {
830+ DependsObject *reqobj;
831+ PyObject *req;
832+ req = PyList_GET_ITEM(self->_recommends, i);
833+ reqobj = (DependsObject *)req;
834+ LIST_CLEAR(reqobj->packages);
835+ if (PyList_Check(reqobj->providedby))
836+ LIST_CLEAR(reqobj->providedby);
837+ }
838 len = PyList_GET_SIZE(self->_upgrades);
839 for (i = 0; i != len; i++) {
840 DependsObject *upgobj;
841@@ -2487,6 +2615,7 @@
842 LIST_CLEAR(self->_packages);
843 LIST_CLEAR(self->_provides);
844 LIST_CLEAR(self->_requires);
845+ LIST_CLEAR(self->_recommends);
846 LIST_CLEAR(self->_upgrades);
847 LIST_CLEAR(self->_conflicts);
848 PyDict_Clear(self->_objmap);
849@@ -2534,6 +2663,7 @@
850 packages = {}
851 provides = {}
852 requires = {}
853+ recommends = {}
854 upgrades = {}
855 conflicts = {}
856 objmap = self._objmap
857@@ -2541,11 +2671,12 @@
858 PyObject *packages = PyDict_New();
859 PyObject *provides = PyDict_New();
860 PyObject *requires = PyDict_New();
861+ PyObject *recommends = PyDict_New();
862 PyObject *upgrades = PyDict_New();
863 PyObject *conflicts = PyDict_New();
864 PyObject *objmap = self->_objmap;
865 int i, ilen;
866- if (!packages || !provides || !requires || !conflicts)
867+ if (!packages || !provides || !requires || !recommends || !conflicts )
868 return NULL;
869
870 /* for loader in loaders: */
871@@ -2679,6 +2810,30 @@
872 }
873
874 /*
875+ for rec in pkg.recommends:
876+ rec.packages.append(pkg)
877+ if rec not in recommends:
878+ recommends[rec] = True
879+ objmap[rec.getInitArgs()] = rec
880+ */
881+ if (PyList_Check(pkg->recommends)) {
882+ klen = PyList_GET_SIZE(pkg->recommends);
883+ for (k = 0; k != klen; k++) {
884+ PyObject *rec = PyList_GET_ITEM(pkg->recommends, k);
885+ PyList_Append(((DependsObject *)rec)->packages,
886+ (PyObject *)pkg);
887+ if (!PyDict_GetItem(recommends, rec)) {
888+ PyDict_SetItem(recommends, rec, Py_True);
889+ args = PyObject_CallMethod(rec, "getInitArgs",
890+ NULL);
891+ if (!args) return NULL;
892+ PyDict_SetItem(objmap, args, rec);
893+ Py_DECREF(args);
894+ }
895+ }
896+ }
897+
898+ /*
899 for upg in pkg.upgrades:
900 upg.packages.append(pkg)
901 if upg not in upgrades:
902@@ -2747,6 +2902,11 @@
903 self->_requires = PyDict_Keys(requires);
904 Py_DECREF(requires);
905
906+ /* self._recommends[:] = recommends.keys() */
907+ Py_DECREF(self->_recommends);
908+ self->_recommends = PyDict_Keys(recommends);
909+ Py_DECREF(recommends);
910+
911 /* self._upgrades[:] = upgrades.keys() */
912 Py_DECREF(self->_upgrades);
913 self->_upgrades = PyDict_Keys(upgrades);
914@@ -2852,7 +3012,7 @@
915 Cache_linkDeps(CacheObject *self, PyObject *args)
916 {
917 int i, j, len;
918- PyObject *reqnames, *upgnames, *cnfnames;
919+ PyObject *reqnames, *recnames, *upgnames, *cnfnames;
920 PyObject *lst;
921
922 /* reqnames = {} */
923@@ -2896,6 +3056,47 @@
924 Py_DECREF(seq);
925 }
926
927+ /* recnames = {} */
928+ recnames = PyDict_New();
929+ /* for rec in self._recommends: */
930+ len = PyList_GET_SIZE(self->_recommends);
931+ for (i = 0; i != len; i++) {
932+ PyObject *rec = PyList_GET_ITEM(self->_recommends, i);
933+
934+ /* for name in rec.getMatchNames(): */
935+ PyObject *names = PyObject_CallMethod(rec, "getMatchNames", NULL);
936+ PyObject *seq = PySequence_Fast(names, "getMatchNames() returned "
937+ "non-sequence object");
938+ int nameslen;
939+ if (!seq) return NULL;
940+ nameslen = PySequence_Fast_GET_SIZE(seq);
941+ for (j = 0; j != nameslen; j++) {
942+ PyObject *name = PySequence_Fast_GET_ITEM(seq, j);
943+
944+ /* lst = recnames.get(name) */
945+ lst = PyDict_GetItem(recnames, name);
946+
947+ /*
948+ if lst:
949+ lst.append(rec)
950+ else:
951+ recnames[name] = [rec]
952+ */
953+ if (lst) {
954+ PyList_Append(lst, rec);
955+ } else {
956+ lst = PyList_New(1);
957+ Py_INCREF(rec);
958+ PyList_SET_ITEM(lst, 0, rec);
959+ PyDict_SetItem(recnames, name, lst);
960+ Py_DECREF(lst);
961+ }
962+ }
963+
964+ Py_DECREF(names);
965+ Py_DECREF(seq);
966+ }
967+
968 /* upgnames = {} */
969 upgnames = PyDict_New();
970 /* for upg in self._upgrades: */
971@@ -3035,6 +3236,56 @@
972 }
973 }
974
975+ /* lst = recnames.get(prv.name) */
976+ lst = PyDict_GetItem(recnames, prv->name);
977+
978+ /* if lst: */
979+ if (lst) {
980+ /* for rec in lst: */
981+ int reclen = PyList_GET_SIZE(lst);
982+ for (j = 0; j != reclen; j++) {
983+ DependsObject *rec = (DependsObject *)PyList_GET_ITEM(lst, j);
984+ /* if rec.matches(prv): */
985+ PyObject *ret = PyObject_CallMethod((PyObject *)rec, "matches",
986+ "O", (PyObject *)prv);
987+ if (!ret) return NULL;
988+ if (PyObject_IsTrue(ret)) {
989+ /*
990+ if rec.providedby:
991+ rec.providedby.append(prv)
992+ else:
993+ rec.providedby = [prv]
994+ */
995+ if (PyList_Check(rec->providedby)) {
996+ PyList_Append(rec->providedby, (PyObject *)prv);
997+ } else {
998+ PyObject *_lst = PyList_New(1);
999+ Py_INCREF(prv);
1000+ PyList_SET_ITEM(_lst, 0, (PyObject *)prv);
1001+ Py_DECREF(rec->providedby);
1002+ rec->providedby = _lst;
1003+ }
1004+
1005+ /*
1006+ if prv.recommendedby:
1007+ prv.recommendedby.append(prv)
1008+ else:
1009+ prv.recommendedby = [prv]
1010+ */
1011+ if (PyList_Check(prv->recommendedby)) {
1012+ PyList_Append(prv->recommendedby, (PyObject *)rec);
1013+ } else {
1014+ PyObject *_lst = PyList_New(1);
1015+ Py_INCREF(rec);
1016+ PyList_SET_ITEM(_lst, 0, (PyObject *)rec);
1017+ Py_DECREF(prv->recommendedby);
1018+ prv->recommendedby = _lst;
1019+ }
1020+ }
1021+ Py_DECREF(ret);
1022+ }
1023+ }
1024+
1025 /* lst = upgnames.get(prv.name) */
1026 lst = PyDict_GetItem(upgnames, prv->name);
1027
1028@@ -3139,6 +3390,7 @@
1029 }
1030
1031 Py_DECREF(reqnames);
1032+ Py_DECREF(recnames);
1033 Py_DECREF(upgnames);
1034 Py_DECREF(cnfnames);
1035
1036@@ -3215,6 +3467,29 @@
1037 }
1038
1039 PyObject *
1040+Cache_getRecommends(CacheObject *self, PyObject *args)
1041+{
1042+ const char *name = NULL;
1043+ PyObject *lst;
1044+ int i, len;
1045+ if (!PyArg_ParseTuple(args, "|s", &name))
1046+ return NULL;
1047+ if (!name) {
1048+ Py_INCREF(self->_recommends);
1049+ return self->_recommends;
1050+ }
1051+ lst = PyList_New(0);
1052+ len = PyList_GET_SIZE(self->_recommends);
1053+ for (i = 0; i != len; i++) {
1054+ DependsObject *rec =
1055+ (DependsObject*)PyList_GET_ITEM(self->_recommends, i);
1056+ if (strcmp(STR(rec->name), name) == 0)
1057+ PyList_Append(lst, (PyObject *)rec);
1058+ }
1059+ return lst;
1060+}
1061+
1062+PyObject *
1063 Cache_getUpgrades(CacheObject *self, PyObject *args)
1064 {
1065 const char *name = NULL;
1066@@ -3324,6 +3599,38 @@
1067 }
1068 Py_DECREF(lst);
1069
1070+ lst = PyObject_GetAttrString(searcher, "recommends");
1071+ if (lst == NULL || !PyList_Check(lst)) {
1072+ PyErr_SetString(PyExc_TypeError, "Invalid recommends attribute");
1073+ return NULL;
1074+ }
1075+ for (i = 0; i != PyList_GET_SIZE(lst); i++) {
1076+ ProvidesObject *prv = (ProvidesObject *)PyList_GET_ITEM(lst, i);
1077+ for (j = 0; j != PyList_GET_SIZE(self->_recommends); j++) {
1078+ PyObject *rec = PyList_GET_ITEM(self->_recommends, j);
1079+ PyObject *names = PyObject_CallMethod(rec, "getMatchNames", NULL);
1080+ PyObject *seq = PySequence_Fast(names, "getMatchNames() returned "
1081+ "non-sequence object");
1082+ if (seq == NULL) return NULL;
1083+ for (k = 0; k != PySequence_Fast_GET_SIZE(seq); k++) {
1084+ if (strcmp(PyString_AS_STRING(PySequence_Fast_GET_ITEM(seq, k)),
1085+ PyString_AS_STRING(prv->name)) == 0) {
1086+ res = PyObject_CallMethod(rec, "matches", "O", prv);
1087+ if (res == NULL)
1088+ return NULL;
1089+ if (PyObject_IsTrue(res))
1090+ CALLMETHOD(searcher, "addResult", "O", rec);
1091+ Py_DECREF(res);
1092+ break;
1093+ }
1094+ }
1095+
1096+ Py_DECREF(names);
1097+ Py_DECREF(seq);
1098+ }
1099+ }
1100+ Py_DECREF(lst);
1101+
1102 lst = PyObject_GetAttrString(searcher, "upgrades");
1103 if (lst == NULL || !PyList_Check(lst)) {
1104 PyErr_SetString(PyExc_TypeError, "Invalid upgrades attribute");
1105@@ -3420,7 +3727,7 @@
1106 static PyObject *
1107 Cache__setstate__(CacheObject *self, PyObject *state)
1108 {
1109- PyObject *provides, *requires, *upgrades, *conflicts;
1110+ PyObject *provides, *requires, *recommends, *upgrades, *conflicts;
1111 int i, ilen;
1112 int j, jlen;
1113
1114@@ -3452,11 +3759,13 @@
1115 /*
1116 provides = {}
1117 requires = {}
1118+ recommends = {}
1119 upgrades = {}
1120 conflicts = {}
1121 */
1122 provides = PyDict_New();
1123 requires = PyDict_New();
1124+ recommends = PyDict_New();
1125 upgrades = PyDict_New();
1126 conflicts = PyDict_New();
1127
1128@@ -3497,6 +3806,21 @@
1129 }
1130
1131 /*
1132+ for rec in pkg.recommends:
1133+ rec.packages.append(pkg)
1134+ recommends[rec] = True
1135+ */
1136+ if (PyList_Check(pkgobj->recommends)) {
1137+ jlen = PyList_GET_SIZE(pkgobj->recommends);
1138+ for (j = 0; j != jlen; j++) {
1139+ PyObject *rec = PyList_GET_ITEM(pkgobj->recommends, j);
1140+ DependsObject *recobj = (DependsObject *)rec;
1141+ PyList_Append(recobj->packages, pkg);
1142+ PyDict_SetItem(recommends, rec, Py_True);
1143+ }
1144+ }
1145+
1146+ /*
1147 for upg in pkg.upgrades:
1148 upg.packages.append(pkg)
1149 upgrades[upg] = True
1150@@ -3525,6 +3849,7 @@
1151 PyDict_SetItem(conflicts, cnf, Py_True);
1152 }
1153 }
1154+
1155 }
1156
1157 /* self._provides = provides.keys() */
1158@@ -3535,6 +3860,10 @@
1159 self->_requires = PyDict_Keys(requires);
1160 Py_DECREF(requires);
1161
1162+ /* self._recommends = recommends.keys() */
1163+ self->_recommends = PyDict_Keys(recommends);
1164+ Py_DECREF(recommends);
1165+
1166 /* self._upgrades = upgrades.keys() */
1167 self->_upgrades = PyDict_Keys(upgrades);
1168 Py_DECREF(upgrades);
1169@@ -3562,6 +3891,7 @@
1170 {"getPackages", (PyCFunction)Cache_getPackages, METH_VARARGS, NULL},
1171 {"getProvides", (PyCFunction)Cache_getProvides, METH_VARARGS, NULL},
1172 {"getRequires", (PyCFunction)Cache_getRequires, METH_VARARGS, NULL},
1173+ {"getRecommends", (PyCFunction)Cache_getRecommends, METH_VARARGS, NULL},
1174 {"getUpgrades", (PyCFunction)Cache_getUpgrades, METH_VARARGS, NULL},
1175 {"getConflicts", (PyCFunction)Cache_getConflicts, METH_VARARGS, NULL},
1176 {"search", (PyCFunction)Cache_search, METH_O, NULL},
1177@@ -3576,6 +3906,7 @@
1178 {"_packages", T_OBJECT, OFF(_packages), RO, 0},
1179 {"_provides", T_OBJECT, OFF(_provides), RO, 0},
1180 {"_requires", T_OBJECT, OFF(_requires), RO, 0},
1181+ {"_recommends", T_OBJECT, OFF(_recommends), RO, 0},
1182 {"_upgrades", T_OBJECT, OFF(_upgrades), RO, 0},
1183 {"_conflicts", T_OBJECT, OFF(_conflicts), RO, 0},
1184 {"_objmap", T_OBJECT, OFF(_objmap), RO, 0},
1185
1186=== modified file 'smart/channels/rpm_sys.py'
1187--- smart/channels/rpm_sys.py 2009-12-18 14:50:03 +0000
1188+++ smart/channels/rpm_sys.py 2013-02-12 14:46:20 +0000
1189@@ -20,7 +20,7 @@
1190 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1191 #
1192 from smart.backends.rpm.header import RPMDBLoader
1193-from smart.backends.rpm.base import getTS
1194+from smart.backends.rpm.base import getTS, rpm_join_dbpath
1195 from smart.channel import PackageChannel
1196 from smart import *
1197 import os
1198@@ -32,9 +32,9 @@
1199
1200 def fetch(self, fetcher, progress):
1201 getTS() # Make sure the db exists.
1202- path = os.path.join(sysconf.get("rpm-root", "/"),
1203- sysconf.get("rpm-dbpath", "var/lib/rpm"),
1204- "Packages")
1205+ dbdir = rpm_join_dbpath(sysconf.get("rpm-root", "/"),
1206+ sysconf.get("rpm-dbpath", "var/lib/rpm"))
1207+ path = os.path.join(dbdir, "Packages")
1208 digest = os.path.getmtime(path)
1209 if digest == self._digest:
1210 return True
1211
1212=== modified file 'smart/commands/channel.py'
1213--- smart/commands/channel.py 2010-12-06 07:35:59 +0000
1214+++ smart/commands/channel.py 2013-02-12 14:46:20 +0000
1215@@ -157,7 +157,17 @@
1216 opts.show is None and opts.yaml is None):
1217 iface.warning(_("Can't edit channels information."))
1218 raise Error, _("Configuration is in readonly mode.")
1219-
1220+
1221+ # Argument check
1222+ opts.check_args_of_option("set", -1)
1223+ opts.check_args_of_option("remove", -1)
1224+ opts.check_args_of_option("edit", 0)
1225+ opts.check_args_of_option("enable", -1)
1226+ opts.check_args_of_option("disable", -1)
1227+ opts.ensure_action("channel", ["add", "set", "remove", "remove-all",
1228+ "list", "show", "yaml", "enable", "disable"])
1229+ opts.check_remaining_args()
1230+
1231 if opts.add is not None:
1232 if not opts.add and opts.args == ["-"]:
1233 newchannels = []
1234@@ -329,7 +339,10 @@
1235 print
1236
1237 if opts.yaml is not None:
1238- import yaml
1239+ try:
1240+ import yaml
1241+ except ImportError:
1242+ raise Error, _("Please install PyYAML in order to use this function")
1243 yamlchannels = {}
1244 for alias in (opts.yaml or sysconf.get("channels", ())):
1245 channel = sysconf.get(("channels", alias))
1246
1247=== modified file 'smart/commands/check.py'
1248--- smart/commands/check.py 2010-06-22 20:51:32 +0000
1249+++ smart/commands/check.py 2013-02-12 14:46:20 +0000
1250@@ -72,6 +72,9 @@
1251
1252 def main(ctrl, opts, reloadchannels=True):
1253
1254+ # Argument check
1255+ opts.check_args_of_option("channels", 1)
1256+
1257 if sysconf.get("auto-update"):
1258 from smart.commands import update
1259 updateopts = update.parse_options([])
1260
1261=== modified file 'smart/commands/config.py'
1262--- smart/commands/config.py 2010-07-02 13:55:04 +0000
1263+++ smart/commands/config.py 2013-02-12 14:46:20 +0000
1264@@ -80,6 +80,12 @@
1265 globals["false"] = False
1266 globals["no"] = False
1267
1268+ # Check arguments
1269+ opts.check_args_of_option("set", -1)
1270+ opts.check_args_of_option("remove", -1)
1271+ opts.ensure_action("config", ["set", "show", "yaml", "remove"])
1272+ opts.check_remaining_args()
1273+
1274 if opts.set:
1275 for opt in opts.set:
1276 m = SETRE.match(opt)
1277@@ -131,7 +137,10 @@
1278 pprint.pprint(sysconf.get((), hard=True))
1279
1280 if opts.yaml is not None:
1281- import yaml
1282+ try:
1283+ import yaml
1284+ except ImportError:
1285+ raise Error, _("Please install PyYAML in order to use this function")
1286 if opts.yaml:
1287 marker = object()
1288 for opt in opts.yaml:
1289
1290=== modified file 'smart/commands/download.py'
1291--- smart/commands/download.py 2010-06-22 20:51:32 +0000
1292+++ smart/commands/download.py 2013-02-12 14:46:20 +0000
1293@@ -81,6 +81,14 @@
1294
1295 def main(ctrl, opts):
1296
1297+ # Argument check
1298+ opts.check_args_of_option("target", 1)
1299+ opts.check_args_of_option("output", 1)
1300+ opts.check_args_of_option("from_urls", -1)
1301+ opts.check_args_of_option("from_metalink", -1)
1302+ if not opts.args and not opts.from_metalink and not opts.from_urls:
1303+ raise Error, _("no package(s) given")
1304+
1305 packages = []
1306 if opts.args:
1307 if sysconf.get("auto-update"):
1308
1309=== modified file 'smart/commands/flag.py'
1310--- smart/commands/flag.py 2010-12-05 15:14:50 +0000
1311+++ smart/commands/flag.py 2013-02-12 14:46:20 +0000
1312@@ -138,7 +138,10 @@
1313 print
1314
1315 if opts.yaml is not None:
1316- import yaml
1317+ try:
1318+ import yaml
1319+ except ImportError:
1320+ raise Error, _("Please install PyYAML in order to use this function")
1321 yamlflags = {}
1322 for flag in opts.yaml or pkgconf.getFlagNames():
1323 flag = flag.strip()
1324
1325=== modified file 'smart/commands/info.py'
1326--- smart/commands/info.py 2010-06-22 20:51:32 +0000
1327+++ smart/commands/info.py 2013-02-12 14:46:20 +0000
1328@@ -58,6 +58,10 @@
1329
1330 def main(ctrl, opts, reloadchannels=True):
1331
1332+ # Argument check
1333+ if not opts.args:
1334+ raise Error, _("No package(s) given")
1335+
1336 if sysconf.get("auto-update"):
1337 from smart.commands import update
1338 updateopts = update.parse_options([])
1339
1340=== modified file 'smart/commands/install.py'
1341--- smart/commands/install.py 2011-01-30 21:55:29 +0000
1342+++ smart/commands/install.py 2013-02-12 14:46:20 +0000
1343@@ -76,6 +76,10 @@
1344
1345 def main(ctrl, opts):
1346
1347+ # Argument check
1348+ if not opts.args:
1349+ raise Error, _("no package(s) given")
1350+
1351 if opts.explain:
1352 sysconf.set("explain-changesets", True, soft=True)
1353
1354
1355=== modified file 'smart/commands/mirror.py'
1356--- smart/commands/mirror.py 2010-07-02 13:55:04 +0000
1357+++ smart/commands/mirror.py 2013-02-12 14:46:20 +0000
1358@@ -218,7 +218,10 @@
1359 print
1360
1361 if opts.yaml:
1362- import yaml
1363+ try:
1364+ import yaml
1365+ except ImportError:
1366+ raise Error, _("Please install PyYAML in order to use this function")
1367 yamlmirrors = {}
1368 mirrors = sysconf.get("mirrors", ())
1369 for origin in mirrors:
1370
1371=== modified file 'smart/commands/priority.py'
1372--- smart/commands/priority.py 2010-07-02 13:55:04 +0000
1373+++ smart/commands/priority.py 2013-02-12 14:46:20 +0000
1374@@ -117,7 +117,10 @@
1375 print
1376
1377 elif opts.yaml:
1378- import yaml
1379+ try:
1380+ import yaml
1381+ except ImportError:
1382+ raise Error, _("Please install PyYAML in order to use this function")
1383 yamlpriorities = {}
1384 priorities = sysconf.get("package-priorities", {})
1385 for name in opts.args or priorities:
1386
1387=== modified file 'smart/commands/query.py'
1388--- smart/commands/query.py 2010-12-05 15:11:14 +0000
1389+++ smart/commands/query.py 2013-02-12 14:46:20 +0000
1390@@ -107,6 +107,8 @@
1391 help=_("show requires for the given packages"))
1392 parser.add_option("--show-prerequires", action="store_true",
1393 help=_("show requires selecting only pre-dependencies"))
1394+ parser.add_option("--show-recommends", action="store_true",
1395+ help=_("show recommends for the given packages"))
1396 parser.add_option("--show-upgrades", action="store_true",
1397 help=_("show upgrades for the given packages"))
1398 parser.add_option("--show-conflicts", action="store_true",
1399@@ -488,6 +490,19 @@
1400 continue
1401 output.showRequiresProvidedBy(pkg, req,
1402 prv, prvpkg)
1403+ if pkg.recommends and (opts.show_recommends):
1404+ pkg.recommends.sort()
1405+ first = True
1406+ for req in pkg.recommends:
1407+ output.showRecommends(pkg, req)
1408+ if opts.show_providedby and req.providedby:
1409+ for prv in req.providedby:
1410+ prv.packages.sort()
1411+ for prvpkg in prv.packages:
1412+ if opts.installed and not prvpkg.installed:
1413+ continue
1414+ output.showRecommendsProvidedBy(pkg, req,
1415+ prv, prvpkg)
1416 if pkg.upgrades and (opts.show_upgrades or whoupgrades):
1417 pkg.upgrades.sort()
1418 first = True
1419@@ -594,6 +609,12 @@
1420 def showRequiresProvidedBy(self, pkg, req, prv, prvpkg):
1421 pass
1422
1423+ def showRecommends(self, pkg, req):
1424+ pass
1425+
1426+ def showRecommendsProvidedBy(self, pkg, req, prv, prvpkg):
1427+ pass
1428+
1429 def showUpgrades(self, pkg, upg):
1430 pass
1431
1432@@ -619,6 +640,8 @@
1433 self._firstconflictedby = True
1434 self._firstrequires = True
1435 self._firstrequiresprovidedby = True
1436+ self._firstrecommends = True
1437+ self._firstrecommendsprovidedby = True
1438 self._firstupgrades = True
1439 self._firstupgradesprovidedby = True
1440 self._firstconflicts = True
1441@@ -711,6 +734,22 @@
1442 name = str(prvpkg)
1443 print " ", "%s (%s)" % (name, prv)
1444
1445+ def showRecommends(self, pkg, rec):
1446+ if self._firstrecommends:
1447+ self._firstrecommends = False
1448+ print " ", _("Recommends:")
1449+ print " ", rec
1450+
1451+ def showRecommendsProvidedBy(self, pkg, req, prv, prvpkg):
1452+ if self._firstrecommendsprovidedby:
1453+ self._firstrecommendsprovidedby = False
1454+ print " ", _("Provided By:")
1455+ if self.opts.hide_version:
1456+ name = prvpkg.name
1457+ else:
1458+ name = str(prvpkg)
1459+ print " ", "%s (%s)" % (name, prv)
1460+
1461 def showUpgrades(self, pkg, upg):
1462 if self._firstupgrades:
1463 self._firstupgrades = False
1464@@ -797,6 +836,18 @@
1465 self._shown[req, prv] = True
1466 print ' "Requires: %s" -> "Provides: %s";' % (req, prv)
1467
1468+ def showRecommends(self, pkg, req):
1469+ if (pkg, req) not in self._shown:
1470+ self._shown[pkg, req] = True
1471+ print ' "%s" -> "Recommends: %s";' % (pkg, req)
1472+
1473+ def showRecommendsProvidedBy(self, pkg, req, prv, prvpkg):
1474+ self.showPackage(prvpkg)
1475+ self.showProvides(prvpkg, prv)
1476+ if (req, prv) not in self._shown:
1477+ self._shown[req, prv] = True
1478+ print ' "Recommends: %s" -> "Provides: %s";' % (req, prv)
1479+
1480 def showUpgrades(self, pkg, upg):
1481 if (pkg, upg) not in self._shown:
1482 self._shown[pkg, upg] = True
1483
1484=== modified file 'smart/commands/reinstall.py'
1485--- smart/commands/reinstall.py 2010-06-22 20:51:32 +0000
1486+++ smart/commands/reinstall.py 2013-02-12 14:46:20 +0000
1487@@ -68,7 +68,11 @@
1488 return opts
1489
1490 def main(ctrl, opts):
1491-
1492+
1493+ # Argument check
1494+ if not opts.args:
1495+ raise Error, _("no package(s) given")
1496+
1497 if opts.explain:
1498 sysconf.set("explain-changesets", True, soft=True)
1499
1500
1501=== modified file 'smart/commands/remove.py'
1502--- smart/commands/remove.py 2010-06-22 20:51:32 +0000
1503+++ smart/commands/remove.py 2013-02-12 14:46:20 +0000
1504@@ -74,6 +74,10 @@
1505
1506 def main(ctrl, opts):
1507
1508+ # Argument check
1509+ if not opts.args:
1510+ raise Error, _("no package(s) given")
1511+
1512 if opts.explain:
1513 sysconf.set("explain-changesets", True, soft=True)
1514
1515
1516=== modified file 'smart/commands/search.py'
1517--- smart/commands/search.py 2010-11-28 13:12:37 +0000
1518+++ smart/commands/search.py 2013-02-12 14:46:20 +0000
1519@@ -44,6 +44,8 @@
1520 def parse_options(argv):
1521 opts = query.parse_options(argv, usage=USAGE, \
1522 description=DESCRIPTION, examples=EXAMPLES)
1523+ if not argv:
1524+ raise Error, _("Search expression not specified")
1525 opts.name = opts.args
1526 opts.summary = opts.args
1527 opts.description = opts.args
1528
1529=== modified file 'smart/commands/upgrade.py'
1530--- smart/commands/upgrade.py 2010-12-05 15:11:14 +0000
1531+++ smart/commands/upgrade.py 2013-02-12 14:46:20 +0000
1532@@ -91,6 +91,9 @@
1533
1534 def main(ctrl, opts):
1535
1536+ # Argument check
1537+ opts.check_args_of_option("flag", 1)
1538+
1539 if opts.explain:
1540 sysconf.set("explain-changesets", True, soft=True)
1541
1542
1543=== modified file 'smart/control.py'
1544--- smart/control.py 2011-10-05 22:02:21 +0000
1545+++ smart/control.py 2013-02-12 14:46:20 +0000
1546@@ -447,7 +447,7 @@
1547 queue = marked.keys()
1548 while queue:
1549 pkg = queue.pop(0)
1550- for req in pkg.requires:
1551+ for req in pkg.requires + pkg.recommends:
1552 for prv in req.providedby:
1553 for prvpkg in prv.packages:
1554 if (prvpkg.installed and
1555@@ -794,7 +794,7 @@
1556 pkglst = []
1557 for pkg in changeset:
1558 n = 0
1559- for req in pkg.requires:
1560+ for req in pkg.requires + pkg.recommends:
1561 for prv in req.providedby:
1562 for prvpkg in prv.packages:
1563 if changeset.get(prvpkg) is INSTALL:
1564
1565=== modified file 'smart/plugins/detectsys.py'
1566--- smart/plugins/detectsys.py 2011-09-22 20:06:16 +0000
1567+++ smart/plugins/detectsys.py 2013-02-12 14:46:20 +0000
1568@@ -20,10 +20,11 @@
1569 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1570 #
1571 from smart import *
1572+from smart.backends.rpm.base import rpm_join_dbpath
1573 import os
1574
1575 def detectRPMSystem():
1576- dir = os.path.join(sysconf.get("rpm-root", "/"),
1577+ dir = rpm_join_dbpath(sysconf.get("rpm-root", "/"),
1578 sysconf.get("rpm-dbpath", "var/lib/rpm"))
1579 file = os.path.join(dir, "Packages")
1580 if os.path.exists(file):
1581
1582=== modified file 'smart/searcher.py'
1583--- smart/searcher.py 2010-06-22 10:41:16 +0000
1584+++ smart/searcher.py 2013-02-12 14:46:20 +0000
1585@@ -45,9 +45,9 @@
1586
1587 - provides is matched in Provides.search(), for the same reason.
1588
1589- - requires, upgrades, and conflicts don't have special searching
1590- methods. Instead, their usual match() method is given an instance
1591- of the Provides type.
1592+ - requires, recommends, upgrades, and conflicts don't have special
1593+ searching methods. Instead, their usual match() method is given
1594+ an instance of the Provides type.
1595
1596 - group, path, url, and other information which is found by
1597 PackageInfo, is searched by the Loader.search() method and
1598@@ -62,6 +62,7 @@
1599 self.nameversion = []
1600 self.provides = []
1601 self.requires = []
1602+ self.recommends = []
1603 self.upgrades = []
1604 self.conflicts = []
1605 self.path = []
1606@@ -76,6 +77,7 @@
1607 del self.nameversion[:]
1608 del self.provides[:]
1609 del self.requires[:]
1610+ del self.recommends[:]
1611 del self.upgrades[:]
1612 del self.conflicts[:]
1613 del self.path[:]
1614@@ -122,6 +124,8 @@
1615 self.addProvides(s[9:], cutoff)
1616 elif s.startswith("requires:"):
1617 self.addRequires(s[9:])
1618+ elif s.startswith("recommends:"):
1619+ self.addRecommends(s[11:])
1620 elif s.startswith("upgrades:"):
1621 self.addUpgrades(s[9:])
1622 elif s.startswith("conflicts:"):
1623@@ -151,6 +155,7 @@
1624 return s and (
1625 s.startswith("provides:") or
1626 s.startswith("requires:") or
1627+ s.startswith("recommends:") or
1628 s.startswith("upgrades:") or
1629 s.startswith("conflicts:") or
1630 s.startswith("url:") or
1631@@ -182,6 +187,9 @@
1632 def addRequires(self, s):
1633 self.requires.append(self._buildProvides(s))
1634
1635+ def addRecommends(self, s):
1636+ self.recommends.append(self._buildProvides(s))
1637+
1638 def addUpgrades(self, s):
1639 self.upgrades.append(self._buildProvides(s))
1640
1641
1642=== modified file 'smart/transaction.py'
1643--- smart/transaction.py 2010-06-22 17:01:09 +0000
1644+++ smart/transaction.py 2013-02-12 14:46:20 +0000
1645@@ -573,7 +573,7 @@
1646 self._remove(namepkg, changeset, locked, pending, depth)
1647
1648 # Install packages required by this one.
1649- for req in pkg.requires:
1650+ for req in pkg.requires + pkg.recommends:
1651
1652 # Check if someone is already providing it.
1653 prvpkgs = {}
1654@@ -596,8 +596,12 @@
1655
1656 if not prvpkgs:
1657 # No packages provide it at all. Give up.
1658- raise Failed, _("Can't install %s: no package provides %s") % \
1659- (pkg, req)
1660+ if req in pkg.requires:
1661+ raise Failed, _("Can't install %s: no package provides %s") % \
1662+ (pkg, req)
1663+ else:
1664+ # It's only a recommend, skip
1665+ continue
1666
1667 if len(prvpkgs) == 1:
1668 # Don't check locked here. prvpkgs was
1669@@ -1359,7 +1363,7 @@
1670 set = self._changeset
1671
1672 # Check all dependencies needed by this package.
1673- for req in pkg.requires:
1674+ for req in pkg.requires + pkg.recommends:
1675
1676 # Check if any already installed or to be installed
1677 # package will solve the problem.
1678@@ -1424,8 +1428,9 @@
1679
1680 # There are no solutions for the problem.
1681 # Should we really care about it?
1682- if (self._forcerequires or
1683- isinstance(req, PreRequires)):
1684+ if ((self._forcerequires or
1685+ isinstance(req, PreRequires))
1686+ and req in pkg.requires):
1687 raise Error, _("No providers for '%s', "
1688 "required by '%s'") % (req, pkg)
1689
1690@@ -1625,7 +1630,7 @@
1691 return n
1692
1693 def forwardRequires(pkg, map):
1694- for req in pkg.requires:
1695+ for req in pkg.requires + pkg.recommends:
1696 if req not in map:
1697 map[req] = True
1698 for prv in req.providedby:
1699@@ -1794,6 +1799,15 @@
1700 iface.info(_("Unsatisfied dependency: %s requires %s") %
1701 (pkg, req))
1702
1703+ for req in pkg.recommends:
1704+ for prv in req.providedby:
1705+ for prvpkg in prv.packages:
1706+ if prvpkg in relateset:
1707+ break
1708+ else:
1709+ continue
1710+ break
1711+
1712 if not pkg.installed:
1713 continue
1714
1715
1716=== modified file 'smart/util/optparse.py'
1717--- smart/util/optparse.py 2005-06-14 17:41:24 +0000
1718+++ smart/util/optparse.py 2013-02-12 14:46:20 +0000
1719@@ -70,6 +70,8 @@
1720 import types
1721 import textwrap
1722 from gettext import gettext as _
1723+from smart import Error
1724+import re
1725
1726 def _repr(self):
1727 return "<%s at 0x%x: %s>" % (self.__class__.__name__, id(self), self)
1728@@ -708,6 +710,12 @@
1729 self.action, self.dest, opt, value, values, parser)
1730
1731 def take_action(self, action, dest, opt, value, values, parser):
1732+ # Keep all the options in the command line in the '_given_opts' array
1733+ # This will be used later to validate the command line
1734+ given_opts = getattr(parser.values, "_given_opts", [])
1735+ user_opt = re.sub(r"^\-*", "", opt).replace("-", "_")
1736+ given_opts.append(user_opt)
1737+ setattr(parser.values, "_given_opts", given_opts)
1738 if action == "store":
1739 setattr(values, dest, value)
1740 elif action == "store_const":
1741@@ -819,6 +827,54 @@
1742 setattr(self, attr, value)
1743 return getattr(self, attr)
1744
1745+ # Check if the given option has the specified number of arguments
1746+ # Raise an error if the option has an invalid number of arguments
1747+ # A negative number for 'nargs' means "at least |nargs| arguments are needed"
1748+ def check_args_of_option(self, opt, nargs, err=None):
1749+ given_opts = getattr(self, "_given_opts", [])
1750+ if not opt in given_opts:
1751+ return
1752+ values = getattr(self, opt, [])
1753+ if type(values) != type([]):
1754+ return
1755+ if nargs < 0:
1756+ nargs = -nargs
1757+ if len(values) >= nargs:
1758+ return
1759+ if not err:
1760+ if nargs == 1:
1761+ err = _("Option '%s' requires at least one argument") % opt
1762+ else:
1763+ err = _("Option '%s' requires at least %d arguments") % (opt, nargs)
1764+ raise Error, err
1765+ elif nargs == 0:
1766+ if len( values ) == 0:
1767+ return
1768+ raise Error, err
1769+ else:
1770+ if len(values) == nargs:
1771+ return
1772+ if not err:
1773+ if nargs == 1:
1774+ err = _("Option '%s' requires one argument") % opt
1775+ else:
1776+ err = _("Option '%s' requires %d arguments") % (opt, nargs)
1777+ raise Error, err
1778+
1779+ # Check that at least one of the options in 'actlist' was given as an argument
1780+ # to the command 'cmdname'
1781+ def ensure_action(self, cmdname, actlist):
1782+ given_opts = getattr(self, "_given_opts", [])
1783+ for action in actlist:
1784+ if action in given_opts:
1785+ return
1786+ raise Error, _("No action specified for command '%s'") % cmdname
1787+
1788+ # Check if there are any other arguments left after parsing the command line and
1789+ # raise an error if such arguments are found
1790+ def check_remaining_args(self):
1791+ if self.args:
1792+ raise Error, _("Invalid argument(s) '%s'" % str(self.args))
1793
1794 class OptionContainer:
1795

Subscribers

People subscribed via source and target branches