Add workaround for Python 2.6.4 issue 4978
[portage.git] / pym / _emerge / resolver / output.py
1 # Copyright 2010-2013 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
3
4 """Resolver output display operation.
5 """
6
7 from __future__ import unicode_literals
8
9 __all__ = (
10         "Display",
11         )
12
13 import sys
14
15 import portage
16 from portage import os
17 from portage.dbapi.dep_expand import dep_expand
18 from portage.dep import cpvequal, _repo_separator, _slot_separator
19 from portage.eapi import _get_eapi_attrs
20 from portage.exception import InvalidDependString, SignatureException
21 from portage.package.ebuild.config import _get_feature_flags
22 from portage.package.ebuild._spawn_nofetch import spawn_nofetch
23 from portage.output import ( blue, colorize, create_color_func,
24         darkblue, darkgreen, green, nc_len, teal)
25 bad = create_color_func("BAD")
26 from portage.util import writemsg_stdout
27 from portage.versions import best
28
29 from _emerge.Blocker import Blocker
30 from _emerge.create_world_atom import create_world_atom
31 from _emerge.resolver.output_helpers import ( _DisplayConfig, _tree_display,
32         _PackageCounters, _create_use_string, _format_size, _calc_changelog, PkgInfo)
33 from _emerge.show_invalid_depstring_notice import show_invalid_depstring_notice
34
35 if sys.hexversion >= 0x3000000:
36         basestring = str
37
38
39 class Display(object):
40         """Formats and outputs the depgrah supplied it for merge/re-merge, etc.
41
42         __call__()
43         @param depgraph: list
44         @param favorites: defaults to []
45         @param verbosity: integer, defaults to None
46         """
47
48         def __init__(self):
49                 self.changelogs = []
50                 self.print_msg = []
51                 self.blockers = []
52                 self.counters = _PackageCounters()
53                 self.resolver = None
54                 self.resolved = None
55                 self.vardb = None
56                 self.portdb = None
57                 self.verboseadd = ''
58                 self.oldlp = None
59                 self.myfetchlist = None
60                 self.indent = ''
61                 self.is_new = True
62                 self.cur_use = None
63                 self.cur_iuse = None
64                 self.old_use = ''
65                 self.old_iuse = ''
66                 self.use_expand = None
67                 self.use_expand_hidden = None
68                 self.pkgsettings = None
69                 self.forced_flags = None
70                 self.newlp = None
71                 self.conf = None
72                 self.blocker_style = None
73
74
75         def _blockers(self, pkg):
76                 """Processes pkg for blockers and adds colorized strings to
77                 self.print_msg and self.blockers
78
79                 @param pkg: _emerge.Package.Package instance
80                 @rtype: bool
81                 Modifies class globals: self.blocker_style, self.resolved,
82                         self.print_msg
83                 """
84                 if pkg.satisfied:
85                         self.blocker_style = "PKG_BLOCKER_SATISFIED"
86                         addl = "%s     " % (colorize(self.blocker_style, "b"),)
87                 else:
88                         self.blocker_style = "PKG_BLOCKER"
89                         addl = "%s     " % (colorize(self.blocker_style, "B"),)
90                 addl += self.empty_space_in_brackets()
91                 self.resolved = dep_expand(
92                         str(pkg.atom).lstrip("!"), mydb=self.vardb,
93                         settings=self.pkgsettings
94                         )
95                 if self.conf.columns and self.conf.quiet:
96                         addl += " " + colorize(self.blocker_style, str(self.resolved))
97                 else:
98                         addl = "[%s %s] %s%s" % \
99                                 (colorize(self.blocker_style, "blocks"),
100                                 addl, self.indent,
101                                 colorize(self.blocker_style, str(self.resolved))
102                                 )
103                 block_parents = self.conf.blocker_parents.parent_nodes(pkg)
104                 block_parents = set([pnode[2] for pnode in block_parents])
105                 block_parents = ", ".join(block_parents)
106                 if self.resolved != pkg[2]:
107                         addl += colorize(self.blocker_style,
108                                 " (\"%s\" is blocking %s)") % \
109                                 (str(pkg.atom).lstrip("!"), block_parents)
110                 else:
111                         addl += colorize(self.blocker_style,
112                                 " (is blocking %s)") % block_parents
113                 if isinstance(pkg, Blocker) and pkg.satisfied:
114                         if not self.conf.columns:
115                                 self.print_msg.append(addl)
116                 else:
117                         self.blockers.append(addl)
118
119         def _display_use(self, pkg, myoldbest, myinslotlist):
120                 """ USE flag display
121
122                 @param pkg: _emerge.Package.Package instance
123                 @param myoldbest: list of installed versions
124                 @param myinslotlist: list of installed slots
125                 Modifies class globals: self.forced_flags, self.cur_iuse,
126                         self.old_iuse, self.old_use, self.use_expand
127                 """
128
129                 self.forced_flags = set()
130                 self.forced_flags.update(pkg.use.force)
131                 self.forced_flags.update(pkg.use.mask)
132
133                 self.cur_use = [flag for flag in self.conf.pkg_use_enabled(pkg) \
134                         if flag in pkg.iuse.all]
135                 self.cur_iuse = sorted(pkg.iuse.all)
136
137                 if myoldbest and myinslotlist:
138                         previous_cpv = myoldbest[0].cpv
139                 else:
140                         previous_cpv = pkg.cpv
141                 if self.vardb.cpv_exists(previous_cpv):
142                         previous_pkg = self.vardb.match_pkgs('=' + previous_cpv)[0]
143                         self.old_iuse = sorted(previous_pkg.iuse.all)
144                         self.old_use = previous_pkg.use.enabled
145                         self.is_new = False
146                 else:
147                         self.old_iuse = []
148                         self.old_use = []
149                         self.is_new = True
150
151                 self.old_use = [flag for flag in self.old_use if flag in self.old_iuse]
152
153                 self.use_expand = pkg.use.expand
154                 self.use_expand_hidden = pkg.use.expand_hidden
155                 return
156
157         def include_mask_str(self):
158                 return self.conf.verbosity > 1
159
160         def gen_mask_str(self, pkg):
161                 """
162                 @param pkg: _emerge.Package.Package instance
163                 """
164                 hardmasked = pkg.isHardMasked()
165                 mask_str = " "
166
167                 if hardmasked:
168                         mask_str = colorize("BAD", "#")
169                 else:
170                         keyword_mask = pkg.get_keyword_mask()
171
172                         if keyword_mask is None:
173                                 pass
174                         elif keyword_mask == "missing":
175                                 mask_str = colorize("BAD", "*")
176                         else:
177                                 mask_str = colorize("WARN", "~")
178
179                 return mask_str
180
181         def empty_space_in_brackets(self):
182                 space = ""
183                 if self.include_mask_str():
184                         # add column for mask status
185                         space += " "
186                 return space
187
188         def map_to_use_expand(self, myvals, forced_flags=False,
189                 remove_hidden=True):
190                 """Map use expand variables
191
192                 @param myvals: list
193                 @param forced_flags: bool
194                 @param remove_hidden: bool
195                 @rtype ret dictionary
196                         or ret dict, forced dict.
197                 """
198                 ret = {}
199                 forced = {}
200                 for exp in self.use_expand:
201                         ret[exp] = []
202                         forced[exp] = set()
203                         for val in myvals[:]:
204                                 if val.startswith(exp.lower()+"_"):
205                                         if val in self.forced_flags:
206                                                 forced[exp].add(val[len(exp)+1:])
207                                         ret[exp].append(val[len(exp)+1:])
208                                         myvals.remove(val)
209                 ret["USE"] = myvals
210                 forced["USE"] = [val for val in myvals \
211                         if val in self.forced_flags]
212                 if remove_hidden:
213                         for exp in self.use_expand_hidden:
214                                 ret.pop(exp, None)
215                 if forced_flags:
216                         return ret, forced
217                 return ret
218
219
220         def recheck_hidden(self, pkg):
221                 """ Prevent USE_EXPAND_HIDDEN flags from being hidden if they
222                 are the only thing that triggered reinstallation.
223
224                 @param pkg: _emerge.Package.Package instance
225                 Modifies self.use_expand_hidden, self.use_expand, self.verboseadd
226                 """
227                 reinst_flags_map = {}
228                 reinstall_for_flags = self.conf.reinstall_nodes.get(pkg)
229                 reinst_expand_map = None
230                 if reinstall_for_flags:
231                         reinst_flags_map = self.map_to_use_expand(
232                                 list(reinstall_for_flags), remove_hidden=False)
233                         for k in list(reinst_flags_map):
234                                 if not reinst_flags_map[k]:
235                                         del reinst_flags_map[k]
236                         if not reinst_flags_map.get("USE"):
237                                 reinst_expand_map = reinst_flags_map.copy()
238                                 reinst_expand_map.pop("USE", None)
239                 if reinst_expand_map and \
240                         not set(reinst_expand_map).difference(
241                         self.use_expand_hidden):
242                         self.use_expand_hidden = \
243                                 set(self.use_expand_hidden).difference(
244                                 reinst_expand_map)
245
246                 cur_iuse_map, iuse_forced = \
247                         self.map_to_use_expand(self.cur_iuse, forced_flags=True)
248                 cur_use_map = self.map_to_use_expand(self.cur_use)
249                 old_iuse_map = self.map_to_use_expand(self.old_iuse)
250                 old_use_map = self.map_to_use_expand(self.old_use)
251
252                 use_expand = sorted(self.use_expand)
253                 use_expand.insert(0, "USE")
254                 feature_flags = _get_feature_flags(_get_eapi_attrs(pkg.eapi))
255
256                 for key in use_expand:
257                         if key in self.use_expand_hidden:
258                                 continue
259                         self.verboseadd += _create_use_string(self.conf, key.upper(),
260                                 cur_iuse_map[key], iuse_forced[key],
261                                 cur_use_map[key], old_iuse_map[key],
262                                 old_use_map[key], self.is_new, feature_flags,
263                                 reinst_flags_map.get(key))
264                 return
265
266
267         @staticmethod
268         def pkgprint(pkg_str, pkg_info):
269                 """Colorizes a string acording to pkg_info settings
270
271                 @param pkg_str: string
272                 @param pkg_info: dictionary
273                 @rtype colorized string
274                 """
275                 if pkg_info.merge:
276                         if pkg_info.built:
277                                 if pkg_info.system:
278                                         return colorize("PKG_BINARY_MERGE_SYSTEM", pkg_str)
279                                 elif pkg_info.world:
280                                         return colorize("PKG_BINARY_MERGE_WORLD", pkg_str)
281                                 else:
282                                         return colorize("PKG_BINARY_MERGE", pkg_str)
283                         else:
284                                 if pkg_info.system:
285                                         return colorize("PKG_MERGE_SYSTEM", pkg_str)
286                                 elif pkg_info.world:
287                                         return colorize("PKG_MERGE_WORLD", pkg_str)
288                                 else:
289                                         return colorize("PKG_MERGE", pkg_str)
290                 elif pkg_info.operation == "uninstall":
291                         return colorize("PKG_UNINSTALL", pkg_str)
292                 else:
293                         if pkg_info.system:
294                                 return colorize("PKG_NOMERGE_SYSTEM", pkg_str)
295                         elif pkg_info.world:
296                                 return colorize("PKG_NOMERGE_WORLD", pkg_str)
297                         else:
298                                 return colorize("PKG_NOMERGE", pkg_str)
299
300
301         def verbose_size(self, pkg, repoadd_set, pkg_info):
302                 """Determines the size of the downloads required
303
304                 @param pkg: _emerge.Package.Package instance
305                 @param repoadd_set: set of repos to add
306                 @param pkg_info: dictionary
307                 Modifies class globals: self.myfetchlist, self.counters.totalsize,
308                         self.verboseadd, repoadd_set.
309                 """
310                 mysize = 0
311                 if pkg.type_name in ("binary", "ebuild") and pkg_info.merge:
312                         db = pkg.root_config.trees[
313                                 pkg.root_config.pkg_tree_map[pkg.type_name]].dbapi
314                         kwargs = {}
315                         if pkg.type_name == "ebuild":
316                                 kwargs["useflags"] = pkg_info.use
317                                 kwargs["myrepo"] = pkg.repo
318                         myfilesdict = None
319                         try:
320                                 myfilesdict = db.getfetchsizes(pkg.cpv,
321                                         **portage._native_kwargs(kwargs))
322                         except InvalidDependString as e:
323                                 # FIXME: validate SRC_URI earlier
324                                 depstr, = db.aux_get(pkg.cpv,
325                                         ["SRC_URI"], myrepo=pkg.repo)
326                                 show_invalid_depstring_notice(
327                                         pkg, depstr, str(e))
328                                 raise
329                         except SignatureException:
330                                 # missing/invalid binary package SIZE signature
331                                 pass
332                         if myfilesdict is None:
333                                 myfilesdict = "[empty/missing/bad digest]"
334                         else:
335                                 for myfetchfile in myfilesdict:
336                                         if myfetchfile not in self.myfetchlist:
337                                                 mysize += myfilesdict[myfetchfile]
338                                                 self.myfetchlist.add(myfetchfile)
339                                 if pkg_info.ordered:
340                                         self.counters.totalsize += mysize
341                         self.verboseadd += _format_size(mysize)
342
343                 if self.quiet_repo_display:
344                         # overlay verbose
345                         # assign index for a previous version in the same slot
346                         slot_matches = self.vardb.match_pkgs(pkg.slot_atom)
347                         if slot_matches:
348                                 repo_name_prev = slot_matches[0].repo
349                         else:
350                                 repo_name_prev = None
351
352                         # now use the data to generate output
353                         if pkg.installed or not slot_matches:
354                                 self.repoadd = self.conf.repo_display.repoStr(
355                                         pkg_info.repo_path_real)
356                         else:
357                                 repo_path_prev = None
358                                 if repo_name_prev:
359                                         repo_path_prev = self.portdb.getRepositoryPath(
360                                                 repo_name_prev)
361                                 if repo_path_prev == pkg_info.repo_path_real:
362                                         self.repoadd = self.conf.repo_display.repoStr(
363                                                 pkg_info.repo_path_real)
364                                 else:
365                                         self.repoadd = "%s=>%s" % (
366                                                 self.conf.repo_display.repoStr(repo_path_prev),
367                                                 self.conf.repo_display.repoStr(pkg_info.repo_path_real))
368                         if self.repoadd:
369                                 repoadd_set.add(self.repoadd)
370
371
372         def convert_myoldbest(self, pkg, pkg_info):
373                 """converts and colorizes a version list to a string
374
375                 @param pkg: _emerge.Package.Package instance
376                 @param pkg_info: dictionary
377                 @rtype string.
378                 """
379                 myoldbest = pkg_info.oldbest_list
380                 # Convert myoldbest from a list to a string.
381                 myoldbest_str = ""
382                 if myoldbest:
383                         versions = []
384                         for pos, old_pkg in enumerate(myoldbest):
385                                 key = old_pkg.version
386                                 if key[-3:] == "-r0":
387                                         key = key[:-3]
388                                 if self.conf.verbosity == 3:
389                                         if pkg_info.attr_display.new_slot:
390                                                 key += _slot_separator + old_pkg.slot
391                                                 if old_pkg.slot != old_pkg.sub_slot:
392                                                         key += "/" + old_pkg.sub_slot
393                                         elif any(x.slot + "/" + x.sub_slot != "0/0" for x in myoldbest + [pkg]):
394                                                 key += _slot_separator + old_pkg.slot
395                                                 if old_pkg.slot != old_pkg.sub_slot or \
396                                                         old_pkg.slot == pkg.slot and old_pkg.sub_slot != pkg.sub_slot:
397                                                         key += "/" + old_pkg.sub_slot
398                                         if not self.quiet_repo_display and (self.verbose_main_repo_display or
399                                                 any(x.repo != self.portdb.repositories.mainRepo().name for x in myoldbest + [pkg])):
400                                                 key += _repo_separator + old_pkg.repo
401                                 versions.append(key)
402                         myoldbest_str = blue("["+", ".join(versions)+"]")
403                 return myoldbest_str
404
405         def _append_slot(self, pkg_str, pkg, pkg_info):
406                 """Potentially appends slot and subslot to package string.
407
408                 @param pkg_str: string
409                 @param pkg: _emerge.Package.Package instance
410                 @param pkg_info: dictionary
411                 @rtype string
412                 """
413                 if pkg_info.attr_display.new_slot:
414                         pkg_str += _slot_separator + pkg_info.slot
415                         if pkg_info.slot != pkg_info.sub_slot:
416                                 pkg_str += "/" + pkg_info.sub_slot
417                 elif any(x.slot + "/" + x.sub_slot != "0/0" for x in pkg_info.oldbest_list + [pkg]):
418                         pkg_str += _slot_separator + pkg_info.slot
419                         if pkg_info.slot != pkg_info.sub_slot or \
420                                 any(x.slot == pkg_info.slot and x.sub_slot != pkg_info.sub_slot for x in pkg_info.oldbest_list):
421                                 pkg_str += "/" + pkg_info.sub_slot
422                 return pkg_str
423
424         def _append_repository(self, pkg_str, pkg, pkg_info):
425                 """Potentially appends repository to package string.
426
427                 @param pkg_str: string
428                 @param pkg: _emerge.Package.Package instance
429                 @param pkg_info: dictionary
430                 @rtype string
431                 """
432                 if not self.quiet_repo_display and (self.verbose_main_repo_display or
433                         any(x.repo != self.portdb.repositories.mainRepo().name for x in pkg_info.oldbest_list + [pkg])):
434                         pkg_str += _repo_separator + pkg.repo
435                 return pkg_str
436
437         def _set_non_root_columns(self, pkg, pkg_info):
438                 """sets the indent level and formats the output
439
440                 @param pkg: _emerge.Package.Package instance
441                 @param pkg_info: dictionary
442                 @rtype string
443                 """
444                 ver_str = pkg_info.ver
445                 if self.conf.verbosity == 3:
446                         ver_str = self._append_slot(ver_str, pkg, pkg_info)
447                         ver_str = self._append_repository(ver_str, pkg, pkg_info)
448                 if self.conf.quiet:
449                         myprint = str(pkg_info.attr_display) + " " + self.indent + \
450                                 self.pkgprint(pkg_info.cp, pkg_info)
451                         myprint = myprint+darkblue(" "+ver_str)+" "
452                         myprint = myprint+pkg_info.oldbest
453                         myprint = myprint+darkgreen("to "+pkg.root)
454                         self.verboseadd = None
455                 else:
456                         if not pkg_info.merge:
457                                 myprint = "[%s] %s%s" % \
458                                         (self.pkgprint(pkg_info.operation.ljust(13), pkg_info),
459                                         self.indent, self.pkgprint(pkg.cp, pkg_info))
460                         else:
461                                 myprint = "[%s %s] %s%s" % \
462                                         (self.pkgprint(pkg.type_name, pkg_info),
463                                         pkg_info.attr_display,
464                                         self.indent, self.pkgprint(pkg.cp, pkg_info))
465                         if (self.newlp-nc_len(myprint)) > 0:
466                                 myprint = myprint+(" "*(self.newlp-nc_len(myprint)))
467                         myprint = myprint+" "+darkblue("["+ver_str+"]")+" "
468                         if (self.oldlp-nc_len(myprint)) > 0:
469                                 myprint = myprint+" "*(self.oldlp-nc_len(myprint))
470                         myprint = myprint+pkg_info.oldbest
471                         myprint += darkgreen("to " + pkg.root)
472                 return myprint
473
474
475         def _set_root_columns(self, pkg, pkg_info):
476                 """sets the indent level and formats the output
477
478                 @param pkg: _emerge.Package.Package instance
479                 @param pkg_info: dictionary
480                 @rtype string
481                 Modifies self.verboseadd
482                 """
483                 ver_str = pkg_info.ver
484                 if self.conf.verbosity == 3:
485                         ver_str = self._append_slot(ver_str, pkg, pkg_info)
486                         ver_str = self._append_repository(ver_str, pkg, pkg_info)
487                 if self.conf.quiet:
488                         myprint = str(pkg_info.attr_display) + " " + self.indent + \
489                                 self.pkgprint(pkg_info.cp, pkg_info)
490                         myprint = myprint+" "+green(ver_str)+" "
491                         myprint = myprint+pkg_info.oldbest
492                         self.verboseadd = None
493                 else:
494                         if not pkg_info.merge:
495                                 addl = self.empty_space_in_brackets()
496                                 myprint = "[%s%s] %s%s" % \
497                                         (self.pkgprint(pkg_info.operation.ljust(13), pkg_info),
498                                         addl, self.indent, self.pkgprint(pkg.cp, pkg_info))
499                         else:
500                                 myprint = "[%s %s] %s%s" % \
501                                         (self.pkgprint(pkg.type_name, pkg_info),
502                                         pkg_info.attr_display,
503                                         self.indent, self.pkgprint(pkg.cp, pkg_info))
504                         if (self.newlp-nc_len(myprint)) > 0:
505                                 myprint = myprint+(" "*(self.newlp-nc_len(myprint)))
506                         myprint = myprint+" "+green("["+ver_str+"]")+" "
507                         if (self.oldlp-nc_len(myprint)) > 0:
508                                 myprint = myprint+(" "*(self.oldlp-nc_len(myprint)))
509                         myprint += pkg_info.oldbest
510                 return myprint
511
512
513         def _set_no_columns(self, pkg, pkg_info):
514                 """prints pkg info without column indentation.
515
516                 @param pkg: _emerge.Package.Package instance
517                 @param pkg_info: dictionary
518                 @rtype the updated addl
519                 """
520                 pkg_str = pkg.cpv
521                 if self.conf.verbosity == 3:
522                         pkg_str = self._append_slot(pkg_str, pkg, pkg_info)
523                         pkg_str = self._append_repository(pkg_str, pkg, pkg_info)
524                 if not pkg_info.merge:
525                         addl = self.empty_space_in_brackets()
526                         myprint = "[%s%s] %s%s %s" % \
527                                 (self.pkgprint(pkg_info.operation.ljust(13),
528                                 pkg_info), addl,
529                                 self.indent, self.pkgprint(pkg_str, pkg_info),
530                                 pkg_info.oldbest)
531                 else:
532                         myprint = "[%s %s] %s%s %s" % \
533                                 (self.pkgprint(pkg.type_name, pkg_info),
534                                 pkg_info.attr_display, self.indent,
535                                 self.pkgprint(pkg_str, pkg_info), pkg_info.oldbest)
536                 return myprint
537
538         def print_messages(self, show_repos):
539                 """Performs the actual output printing of the pre-formatted
540                 messages
541
542                 @param show_repos: bool.
543                 """
544                 for msg in self.print_msg:
545                         if isinstance(msg, basestring):
546                                 writemsg_stdout("%s\n" % (msg,), noiselevel=-1)
547                                 continue
548                         myprint, self.verboseadd, repoadd = msg
549                         if self.verboseadd:
550                                 myprint += " " + self.verboseadd
551                         if show_repos and repoadd:
552                                 myprint += " " + teal("[%s]" % repoadd)
553                         writemsg_stdout("%s\n" % (myprint,), noiselevel=-1)
554                 return
555
556
557         def print_blockers(self):
558                 """Performs the actual output printing of the pre-formatted
559                 blocker messages
560                 """
561                 for pkg in self.blockers:
562                         writemsg_stdout("%s\n" % (pkg,), noiselevel=-1)
563                 return
564
565
566         def print_verbose(self, show_repos):
567                 """Prints the verbose output to std_out
568
569                 @param show_repos: bool.
570                 """
571                 writemsg_stdout('\n%s\n' % (self.counters,), noiselevel=-1)
572                 if show_repos:
573                         # Use unicode_literals to force unicode format string so
574                         # that RepoDisplay.__unicode__() is called in python2.
575                         writemsg_stdout("%s" % (self.conf.repo_display,),
576                                 noiselevel=-1)
577                 return
578
579
580         def print_changelog(self):
581                 """Prints the changelog text to std_out
582                 """
583                 for chunk in self.changelogs:
584                         writemsg_stdout(chunk,
585                                 noiselevel=-1)
586
587
588         def get_display_list(self, mylist):
589                 """Determines the display list to process
590
591                 @param mylist
592                 @rtype list
593                 Modifies self.counters.blocks, self.counters.blocks_satisfied,
594
595                 """
596                 unsatisfied_blockers = []
597                 ordered_nodes = []
598                 for pkg in mylist:
599                         if isinstance(pkg, Blocker):
600                                 self.counters.blocks += 1
601                                 if pkg.satisfied:
602                                         ordered_nodes.append(pkg)
603                                         self.counters.blocks_satisfied += 1
604                                 else:
605                                         unsatisfied_blockers.append(pkg)
606                         else:
607                                 ordered_nodes.append(pkg)
608                 if self.conf.tree_display:
609                         display_list = _tree_display(self.conf, ordered_nodes)
610                 else:
611                         display_list = [(pkg, 0, True) for pkg in ordered_nodes]
612                 for pkg in unsatisfied_blockers:
613                         display_list.append((pkg, 0, True))
614                 return display_list
615
616
617         def set_pkg_info(self, pkg, ordered):
618                 """Sets various pkg_info dictionary variables
619
620                 @param pkg: _emerge.Package.Package instance
621                 @param ordered: bool
622                 @rtype pkg_info dictionary
623                 Modifies self.counters.restrict_fetch,
624                         self.counters.restrict_fetch_satisfied
625                 """
626                 pkg_info = PkgInfo()
627                 pkg_info.cp = pkg.cp
628                 pkg_info.ver = self.get_ver_str(pkg)
629                 pkg_info.slot = pkg.slot
630                 pkg_info.sub_slot = pkg.sub_slot
631                 pkg_info.repo_name = pkg.repo
632                 pkg_info.ordered = ordered
633                 pkg_info.operation = pkg.operation
634                 pkg_info.merge = ordered and pkg_info.operation == "merge"
635                 if not pkg_info.merge and pkg_info.operation == "merge":
636                         pkg_info.operation = "nomerge"
637                 pkg_info.built = pkg.type_name != "ebuild"
638                 pkg_info.ebuild_path = None
639                 if ordered:
640                         if pkg_info.merge:
641                                 if pkg.type_name == "binary":
642                                         self.counters.binary += 1
643                         elif pkg_info.operation == "uninstall":
644                                 self.counters.uninst += 1
645                 if pkg.type_name == "ebuild":
646                         pkg_info.ebuild_path = self.portdb.findname(
647                                 pkg.cpv, myrepo=pkg_info.repo_name)
648                         if pkg_info.ebuild_path is None:
649                                 raise AssertionError(
650                                         "ebuild not found for '%s'" % pkg.cpv)
651                         pkg_info.repo_path_real = os.path.dirname(os.path.dirname(
652                                 os.path.dirname(pkg_info.ebuild_path)))
653                 else:
654                         pkg_info.repo_path_real = self.portdb.getRepositoryPath(pkg.repo)
655                 pkg_info.use = list(self.conf.pkg_use_enabled(pkg))
656                 if not pkg.built and pkg.operation == 'merge' and \
657                         'fetch' in pkg.restrict:
658                         if pkg_info.ordered:
659                                 self.counters.restrict_fetch += 1
660                         pkg_info.attr_display.fetch_restrict = True
661                         if not self.portdb.getfetchsizes(pkg.cpv,
662                                 useflags=pkg_info.use, myrepo=pkg.repo):
663                                 pkg_info.attr_display.fetch_restrict_satisfied = True
664                                 if pkg_info.ordered:
665                                         self.counters.restrict_fetch_satisfied += 1
666                         else:
667                                 if pkg_info.ebuild_path is not None:
668                                         self.restrict_fetch_list[pkg] = pkg_info
669                 return pkg_info
670
671
672         def do_changelog(self, pkg, pkg_info):
673                 """Processes and adds the changelog text to the master text for output
674
675                 @param pkg: _emerge.Package.Package instance
676                 @param pkg_info: dictionay
677                 Modifies self.changelogs
678                 """
679                 inst_matches = self.vardb.match(pkg.slot_atom)
680                 if inst_matches:
681                         ebuild_path_cl = pkg_info.ebuild_path
682                         if ebuild_path_cl is None:
683                                 # binary package
684                                 ebuild_path_cl = self.portdb.findname(pkg.cpv, myrepo=pkg.repo)
685                         if ebuild_path_cl is not None:
686                                 self.changelogs.extend(_calc_changelog(
687                                         ebuild_path_cl, inst_matches[0], pkg.cpv))
688                 return
689
690
691         def check_system_world(self, pkg):
692                 """Checks for any occurances of the package in the system or world sets
693
694                 @param pkg: _emerge.Package.Package instance
695                 @rtype system and world booleans
696                 """
697                 root_config = self.conf.roots[pkg.root]
698                 system_set = root_config.sets["system"]
699                 world_set  = root_config.sets["selected"]
700                 system = False
701                 world = False
702                 try:
703                         system = system_set.findAtomForPackage(
704                                 pkg, modified_use=self.conf.pkg_use_enabled(pkg))
705                         world = world_set.findAtomForPackage(
706                                 pkg, modified_use=self.conf.pkg_use_enabled(pkg))
707                         if not (self.conf.oneshot or world) and \
708                                 pkg.root == self.conf.target_root and \
709                                 self.conf.favorites.findAtomForPackage(
710                                         pkg, modified_use=self.conf.pkg_use_enabled(pkg)
711                                         ):
712                                 # Maybe it will be added to world now.
713                                 if create_world_atom(pkg, self.conf.favorites, root_config):
714                                         world = True
715                 except InvalidDependString:
716                         # This is reported elsewhere if relevant.
717                         pass
718                 return system, world
719
720
721         @staticmethod
722         def get_ver_str(pkg):
723                 """Obtains the version string
724                 @param pkg: _emerge.Package.Package instance
725                 @rtype string
726                 """
727                 ver_str = pkg.cpv.version
728                 if ver_str.endswith("-r0"):
729                         ver_str = ver_str[:-3]
730                 return ver_str
731
732
733         def _get_installed_best(self, pkg, pkg_info):
734                 """ we need to use "--emptrytree" testing here rather than
735                 "empty" param testing because "empty"
736                 param is used for -u, where you still *do* want to see when
737                 something is being upgraded.
738
739                 @param pkg: _emerge.Package.Package instance
740                 @param pkg_info: dictionay
741                 @rtype addl, myoldbest: list, myinslotlist: list
742                 Modifies self.counters.reinst, self.counters.new
743
744                 """
745                 myoldbest = []
746                 myinslotlist = None
747                 installed_versions = self.vardb.match_pkgs(pkg.cp)
748                 if self.vardb.cpv_exists(pkg.cpv):
749                         pkg_info.attr_display.replace = True
750                         installed_version = self.vardb.match_pkgs(pkg.cpv)[0]
751                         if installed_version.slot != pkg.slot or installed_version.sub_slot != pkg.sub_slot or \
752                                 not self.quiet_repo_display and installed_version.repo != pkg.repo:
753                                 myoldbest = [installed_version]
754                         if pkg_info.ordered:
755                                 if pkg_info.merge:
756                                         self.counters.reinst += 1
757                 # filter out old-style virtual matches
758                 elif installed_versions and \
759                         installed_versions[0].cp == pkg.cp:
760                         myinslotlist = self.vardb.match_pkgs(pkg.slot_atom)
761                         # If this is the first install of a new-style virtual, we
762                         # need to filter out old-style virtual matches.
763                         if myinslotlist and \
764                                 myinslotlist[0].cp != pkg.cp:
765                                 myinslotlist = None
766                         if myinslotlist:
767                                 myoldbest = myinslotlist[:]
768                                 if not cpvequal(pkg.cpv,
769                                         best([pkg.cpv] + [x.cpv for x in myinslotlist])):
770                                         # Downgrade in slot
771                                         pkg_info.attr_display.new_version = True
772                                         pkg_info.attr_display.downgrade = True
773                                         if pkg_info.ordered:
774                                                 self.counters.downgrades += 1
775                                 else:
776                                         # Update in slot
777                                         pkg_info.attr_display.new_version = True
778                                         if pkg_info.ordered:
779                                                 self.counters.upgrades += 1
780                         else:
781                                 myoldbest = installed_versions
782                                 pkg_info.attr_display.new = True
783                                 pkg_info.attr_display.new_slot = True
784                                 if pkg_info.ordered:
785                                         self.counters.newslot += 1
786                         if self.conf.changelog:
787                                 self.do_changelog(pkg, pkg_info)
788                 else:
789                         pkg_info.attr_display.new = True
790                         if pkg_info.ordered:
791                                 self.counters.new += 1
792                 return myoldbest, myinslotlist
793
794
795         def __call__(self, depgraph, mylist, favorites=None, verbosity=None):
796                 """The main operation to format and display the resolver output.
797
798                 @param depgraph: dependency grah
799                 @param mylist: list of packages being processed
800                 @param favorites: list, defaults to []
801                 @param verbosity: verbose level, defaults to None
802                 Modifies self.conf, self.myfetchlist, self.portdb, self.vardb,
803                         self.pkgsettings, self.verboseadd, self.oldlp, self.newlp,
804                         self.print_msg,
805                 """
806                 if favorites is None:
807                         favorites = []
808                 self.conf = _DisplayConfig(depgraph, mylist, favorites, verbosity)
809                 mylist = self.get_display_list(self.conf.mylist)
810                 # files to fetch list - avoids counting a same file twice
811                 # in size display (verbose mode)
812                 self.myfetchlist = set()
813
814                 self.quiet_repo_display = "--quiet-repo-display" in depgraph._frozen_config.myopts
815                 if self.quiet_repo_display:
816                         # Use this set to detect when all the "repoadd" strings are "[0]"
817                         # and disable the entire repo display in this case.
818                         repoadd_set = set()
819
820                 self.verbose_main_repo_display = "--verbose-main-repo-display" in depgraph._frozen_config.myopts
821                 self.restrict_fetch_list = {}
822
823                 for mylist_index in range(len(mylist)):
824                         pkg, depth, ordered = mylist[mylist_index]
825                         self.portdb = self.conf.trees[pkg.root]["porttree"].dbapi
826                         self.vardb = self.conf.trees[pkg.root]["vartree"].dbapi
827                         self.pkgsettings = self.conf.pkgsettings[pkg.root]
828                         self.indent = " " * depth
829
830                         if isinstance(pkg, Blocker):
831                                 self._blockers(pkg)
832                         else:
833                                 pkg_info = self.set_pkg_info(pkg, ordered)
834                                 pkg_info.oldbest_list, myinslotlist = \
835                                         self._get_installed_best(pkg, pkg_info)
836                                 if ordered and pkg_info.merge and \
837                                         not pkg_info.attr_display.new:
838                                         for arg, atom in depgraph._iter_atoms_for_pkg(pkg):
839                                                 if arg.force_reinstall:
840                                                         pkg_info.attr_display.force_reinstall = True
841                                                         break
842
843                                 self.verboseadd = ""
844                                 if self.quiet_repo_display:
845                                         self.repoadd = None
846                                 self._display_use(pkg, pkg_info.oldbest_list, myinslotlist)
847                                 self.recheck_hidden(pkg)
848                                 if self.conf.verbosity == 3:
849                                         if self.quiet_repo_display:
850                                                 self.verbose_size(pkg, repoadd_set, pkg_info)
851                                         else:
852                                                 self.verbose_size(pkg, None, pkg_info)
853
854                                 self.oldlp = self.conf.columnwidth - 30
855                                 self.newlp = self.oldlp - 30
856                                 pkg_info.oldbest = self.convert_myoldbest(pkg, pkg_info)
857                                 pkg_info.system, pkg_info.world = \
858                                         self.check_system_world(pkg)
859                                 if 'interactive' in pkg.properties and \
860                                         pkg.operation == 'merge':
861                                         pkg_info.attr_display.interactive = True
862                                         if ordered:
863                                                 self.counters.interactive += 1
864
865                                 if self.include_mask_str():
866                                         pkg_info.attr_display.mask = self.gen_mask_str(pkg)
867
868                                 if pkg.root_config.settings["ROOT"] != "/":
869                                         if pkg_info.oldbest:
870                                                 pkg_info.oldbest += " "
871                                         if self.conf.columns:
872                                                 myprint = self._set_non_root_columns(pkg, pkg_info)
873                                         else:
874                                                 pkg_str = pkg.cpv
875                                                 if self.conf.verbosity == 3:
876                                                         pkg_str = self._append_slot(pkg_str, pkg, pkg_info)
877                                                         pkg_str = self._append_repository(pkg_str, pkg, pkg_info)
878                                                 if not pkg_info.merge:
879                                                         addl = self.empty_space_in_brackets()
880                                                         myprint = "[%s%s] " % (
881                                                                 self.pkgprint(pkg_info.operation.ljust(13),
882                                                                 pkg_info), addl,
883                                                                 )
884                                                 else:
885                                                         myprint = "[%s %s] " % (
886                                                                 self.pkgprint(pkg.type_name, pkg_info),
887                                                                 pkg_info.attr_display)
888                                                 myprint += self.indent + \
889                                                         self.pkgprint(pkg_str, pkg_info) + " " + \
890                                                         pkg_info.oldbest + darkgreen("to " + pkg.root)
891                                 else:
892                                         if self.conf.columns:
893                                                 myprint = self._set_root_columns(pkg, pkg_info)
894                                         else:
895                                                 myprint = self._set_no_columns(pkg, pkg_info)
896
897                                 if self.conf.columns and pkg.operation == "uninstall":
898                                         continue
899                                 if self.quiet_repo_display:
900                                         self.print_msg.append((myprint, self.verboseadd, self.repoadd))
901                                 else:
902                                         self.print_msg.append((myprint, self.verboseadd, None))
903
904                 show_repos = self.quiet_repo_display and repoadd_set and repoadd_set != set(["0"])
905
906                 # now finally print out the messages
907                 self.print_messages(show_repos)
908                 self.print_blockers()
909                 if self.conf.verbosity == 3:
910                         self.print_verbose(show_repos)
911                 for pkg, pkg_info in self.restrict_fetch_list.items():
912                         writemsg_stdout("\nFetch instructions for %s:\n" % (pkg.cpv,),
913                                                         noiselevel=-1)
914                         spawn_nofetch(self.conf.trees[pkg.root]["porttree"].dbapi,
915                                 pkg_info.ebuild_path)
916                 if self.conf.changelog:
917                         self.print_changelog()
918
919                 return os.EX_OK