Source code for pkgcore.ebuild.inspect_profile

import argparse
import operator
from collections import defaultdict
from itertools import chain

from snakeoil.cli import arghparse
from snakeoil.osutils import pjoin
from snakeoil.sequences import split_negations

from ..util import commandline
from . import atom, profiles
from .misc import ChunkedDataDict

commands = []
# info: keywords known
# global known flags, etc


class _base(arghparse.ArgparseCommand):
    @staticmethod
    def _validate_args(parser, namespace):
        path = namespace.profile
        if path is None:
            if namespace.repo is not None:
                # default to the repo's main profiles dir
                path = pjoin(namespace.repo.location, "profiles")
            else:
                # default to the configured system profile if none is selected
                path = namespace.config.get_default("domain").profile.profile
        else:
            if namespace.repo is not None and getattr(
                namespace.repo, "location", False
            ):
                if not path.startswith("/"):
                    path = pjoin(namespace.repo.location, "profiles", path)
        try:
            stack = profiles.ProfileStack(arghparse.existent_path(path))
        except argparse.ArgumentTypeError as e:
            parser.error(e)
        if stack.node.repoconfig is None:
            parser.error(f"invalid profile path: {path!r}")
        namespace.profile = stack

    def bind_to_parser(self, parser):
        arghparse.ArgparseCommand.bind_to_parser(self, parser)
        parser.add_argument("profile", help="path to the profile to inspect")
        name = self.__class__.__name__
        kwds = {(f"_{name}_suppress"): arghparse.DelayedDefault.wipe(("domain"), 50)}
        parser.set_defaults(**kwds)
        parser.bind_final_check(self._validate_args)
        self._subclass_bind(parser)

    def _subclass_bind(self, parser):
        """override to add more command line options"""


_register_command = commandline.register_command(commands)


[docs] class parent(_base, metaclass=_register_command): """output the linearized tree of inherited parents later lines override earlier lines """ def __call__(self, namespace, out, err): if namespace.repo is None: out.write("\n".join(x.path for x in namespace.profile.stack)) else: repo_dir = pjoin(namespace.repo.location, "profiles") for x in namespace.profile.stack: out.write(x.path[len(repo_dir) :].lstrip("/"))
[docs] class eapi(_base, metaclass=_register_command): """output EAPI support required for reading this profile""" def __call__(self, namespace, out, err): eapis = set(str(x.eapi) for x in namespace.profile.stack) out.write("\n".join(sorted(eapis)))
[docs] class status(_base, metaclass=_register_command): """output profile status""" def __call__(self, namespace, out, err): profiles_dir = pjoin(namespace.profile.node.repoconfig.location, "profiles") profile_rel_path = namespace.profile.path[len(profiles_dir) :].lstrip("/") arch_profiles = namespace.profile.node.repoconfig.arch_profiles statuses = [ (path, status) for path, status in chain.from_iterable(arch_profiles.values()) if path.startswith(profile_rel_path) ] if len(statuses) > 1: for path, status in sorted(statuses): out.write(f"{path}: {status}") elif statuses: out.write(statuses[0][1])
[docs] class deprecated(_base, metaclass=_register_command): """dump deprecation notices, if any""" def __call__(self, namespace, out, err): for idx, profile in enumerate( x for x in namespace.profile.stack if x.deprecated ): if idx: out.write() out.write(out.bold, out.fg("cyan"), profile.path, out.reset, ":") data = profile.deprecated if data[0]: out.write( " ", out.fg("yellow"), "replacement profile", out.reset, f": {data[0]}", ) if data[1]: out.write(" ", out.fg("yellow"), "deprecation message", out.reset, ":") for line in data[1].split("\n"): out.write(line, prefix=" ")
[docs] class provided(_base, metaclass=_register_command): """list all package.provided packages Note that these are exact versions- if a dep requires a higher version, it's not considered satisfied. """ def __call__(self, namespace, out, err): targets = defaultdict(list) for pkg in namespace.profile.provides_repo: targets[pkg.key].append(pkg) for pkg_name, pkgs in sorted(targets.items(), key=operator.itemgetter(0)): out.write( out.fg("cyan"), pkg_name, out.reset, ": ", ", ".join(x.fullver for x in sorted(pkgs)), )
[docs] class system(_base, metaclass=_register_command): """output the system package set""" def __call__(self, namespace, out, err): for pkg in sorted(namespace.profile.system): out.write(str(pkg))
[docs] class use_expand(_base, metaclass=_register_command): """output the USE_EXPAND configuration for this profile Outputs two fields of interest; USE_EXPAND (pseudo use groups), and USE_EXPAND_HIDDEN which is immutable by user configuration and use deps (primarily used for things like setting the kernel or OS type). """ def __call__(self, namespace, out, err): out.write("flags: ", ", ".join(sorted(namespace.profile.use_expand))) out.write("hidden: ", ", ".join(sorted(namespace.profile.use_expand_hidden)))
[docs] class iuse_effective(_base, metaclass=_register_command): """output the IUSE_EFFECTIVE value for this profile""" def __call__(self, namespace, out, err): if namespace.profile.iuse_effective: out.write(" ".join(sorted(namespace.profile.iuse_effective)))
[docs] class masks(_base, metaclass=_register_command): """inspect package masks""" def __call__(self, namespace, out, err): for mask in sorted(namespace.profile.masks): out.write(str(mask))
[docs] class unmasks(_base, metaclass=_register_command): """inspect package unmasks""" def __call__(self, namespace, out, err): for unmask in sorted(namespace.profile.unmasks): out.write(str(unmask))
[docs] class bashrcs(_base, metaclass=_register_command): """inspect bashrcs""" def __call__(self, namespace, out, err): for bashrc in namespace.profile.bashrcs: out.write(bashrc.path)
[docs] class package_bashrc(_base, metaclass=_register_command): """inspect package.bashrc""" def __call__(self, namespace, out, err): for package, bashrcs in namespace.profile.pkg_bashrcs: bashrcs = ", ".join(s.path for s in bashrcs) out.write(f"{package}: {bashrcs}")
[docs] class keywords(_base, metaclass=_register_command): """inspect package.keywords""" def __call__(self, namespace, out, err): for pkg, keywords in namespace.profile.keywords: out.write(f"{pkg}: {' '.join(keywords)}")
[docs] class accept_keywords(_base, metaclass=_register_command): """inspect package.accept_keywords""" def __call__(self, namespace, out, err): for pkg, keywords in namespace.profile.accept_keywords: out.write(pkg, autoline=False) if keywords: out.write(f": {' '.join(keywords)}") else: out.write()
class _use(_base): def _output_use(self, neg, pos): neg = ("-" + x for x in neg) return " ".join(sorted(chain(neg, pos))) def __call__(self, namespace, out, err): global_use = [] pkg_use = {} for k, v in namespace.use.render_to_dict().items(): if isinstance(k, str): for pkg, neg, pos in v: if isinstance(pkg, atom.atom): pkg_neg, pkg_pos = pkg_use.setdefault(pkg, (set(), set())) pkg_neg.update(neg) pkg_pos.update(pos) matched = pkg_neg.intersection(pkg_pos) pkg_pos.difference_update(matched) pkg_neg.difference_update(matched) else: _, neg, pos = v[0] global_use = (neg, pos) if global_use: out.write(f"*/*: {self._output_use(*global_use)}") if pkg_use: for pkg, (neg, pos) in sorted(pkg_use.items()): if neg or pos: out.write(f"{pkg}: {self._output_use(neg, pos)}")
[docs] class use(_use, metaclass=_register_command): """inspect enabled USE flags Including USE, USE_EXPAND, and package.use settings. """ def __call__(self, namespace, out, err): u = ChunkedDataDict() u.add_bare_global(*split_negations(namespace.profile.use)) u.merge(namespace.profile.pkg_use) namespace.use = u super().__call__(namespace, out, err)
[docs] class masked_use(_use, metaclass=_register_command): """inspect masked use flags""" def __call__(self, namespace, out, err): namespace.use = namespace.profile.masked_use super().__call__(namespace, out, err)
[docs] class stable_masked_use(_use, metaclass=_register_command): """inspect stable masked use flags""" def __call__(self, namespace, out, err): namespace.use = namespace.profile.stable_masked_use super().__call__(namespace, out, err)
[docs] class forced_use(_use, metaclass=_register_command): """inspect forced use flags""" def __call__(self, namespace, out, err): namespace.use = namespace.profile.forced_use super().__call__(namespace, out, err)
[docs] class stable_forced_use(_use, metaclass=_register_command): """inspect stable forced use flags""" def __call__(self, namespace, out, err): namespace.use = namespace.profile.stable_forced_use super().__call__(namespace, out, err)
[docs] class defaults(_base, metaclass=_register_command): """inspect defined configuration for this profile This is data parsed from make.defaults, containing things like ACCEPT_KEYWORDS. """ def _subclass_bind(self, parser): parser.add_argument( "variables", nargs="*", help="if not specified, all settings are displayed" ". If given, output is limited to just those settings if " "they exist", ) def __call__(self, namespace, out, err): var_filter = namespace.variables if var_filter: var_filter = set(var_filter).__contains__ else: var_filter = lambda x: True settings = namespace.profile.default_env vars = sorted(filter(var_filter, settings)) for key in vars: val = settings[key] if not val: continue if isinstance(val, tuple): val = " ".join(val) out.write(f'{key}="{val}"')
[docs] class arch(_base, metaclass=_register_command): """output the arch defined for this profile""" def __call__(self, namespace, out, err): if namespace.profile.arch is not None: out.write(namespace.profile.arch)
[docs] def bind_parser(parser, name): subparsers = parser.add_subparsers(description=f"{name} commands") for command in commands: # Split docstrings into summaries and extended docs. help, _, docs = command.__doc__.partition("\n") subparser = subparsers.add_parser( command.__name__.lower(), help=help, docs=docs ) command().bind_to_parser(subparser)