def test_globs(self): for token in ("*", "*/*"): i = parserestrict.parse_match(token) self.assertInstance(i, restriction.AlwaysBool, token) self.assertEqual(len(i), 1) for token in ("*::gentoo", "*/*::gentoo"): i = parserestrict.parse_match(token) self.assertInstance(i, boolean.AndRestriction, token) self.assertEqual(len(i), 2) self.assertInstance(i[0], restricts.RepositoryDep, token.split("::")[1]) self.assertInstance(i[1], restriction.AlwaysBool, token.split("::")[0]) for token in ("foo*::gentoo", "*foo::gentoo"): i = parserestrict.parse_match(token) self.assertInstance(i, boolean.AndRestriction, token) self.assertEqual(len(i), 2) self.assertInstance(i[0], restricts.RepositoryDep, token.split("::")[1]) self.verify_restrict(i[1], "package", token.split("::")[0]) for token, attr, n in ( ("foo/*::gentoo", "category", 0), ("*/foo::gentoo", "package", 1), ): i = parserestrict.parse_match(token) self.assertInstance(i, boolean.AndRestriction, token) self.assertEqual(len(i), 2) self.assertInstance(i[0], restricts.RepositoryDep, token.split("::")[1]) self.verify_restrict(i[1], attr, token.split("::")[0].split("/")[n])
def test_combined(self): assert isinstance(parserestrict.parse_match("dev-util/diffball"), atom), "dev-util/diffball" for token in ("dev-*/util", "dev-*/util*", "dev-a/util*"): i = parserestrict.parse_match(token) assert isinstance(i, boolean.AndRestriction), token assert len(i) == 2 self.verify_restrict(i[0], "category", token.split("/")[0]) self.verify_restrict(i[1], "package", token.split("/")[1])
def test_combined(self): self.assertInstance(parserestrict.parse_match("dev-util/diffball"), atom, "dev-util/diffball") for token in ("dev-*/util", "dev-*/util*", "dev-a/util*"): i = parserestrict.parse_match(token) self.assertInstance(i, boolean.AndRestriction, token) self.assertEqual(len(i), 2) self.verify_restrict(i[0], "category", token.split("/")[0]) self.verify_restrict(i[1], "package", token.split("/")[1])
def test_exceptions(self): for token in ( "!dev-util/diffball", "dev-util/diffball-0.4", "=dev-util/*diffball-0.4*", "::gentoo", ): with pytest.raises(parserestrict.ParseError): parserestrict.parse_match(token)
def test_base_targets(self): repo = SimpleTree({'spork': {'foon': ('1', '1.0.1', '2')}}) installed_repos = SimpleTree({'foo': {'bar': ('1',)}}) for cat in ('', 'spork/'): a = pmerge.parse_target(parse_match(f'={cat}foon-1'), repo, installed_repos) assert len(a) == 1 assert a[0].key == 'spork/foon' assert [x.fullver for x in repo.itermatch(a[0])] == ['1'] a = pmerge.parse_target(parse_match(f'{cat}foon'), repo, installed_repos) assert len(a) == 1 assert a[0].key == 'spork/foon' assert ( sorted(x.fullver for x in repo.itermatch(a[0])) == sorted(['1', '1.0.1', '2']) )
def matches_finalize(targets, namespace): repos = multiplex.tree(*namespace.repos) # If current working dir is in a repo, build a path restriction; otherwise # match everything. if not targets: cwd = os.getcwd() if cwd in repos: targets = [cwd] else: return [] restrictions = [] for target in targets: try: restrictions.append(parserestrict.parse_match(target)) except parserestrict.ParseError as e: if os.path.exists(target): try: restrictions.append(repos.path_restrict(target)) except ValueError as e: argparser.error(e) else: argparser.error(e) if restrictions: return packages.OrRestriction(*restrictions) return []
def test_no_matches(self): repo = SimpleTree({ 'spork': {'foon': ('1',)}, 'spork2': {'foon': ('2',)}}) installed_repos = SimpleTree({'foo': {'bar': ('1',)}}) with pytest.raises(pmerge.NoMatches): pmerge.parse_target(parse_match("foo"), repo, installed_repos)
def _setup_restrictions(namespace): repo = namespace.domain.all_source_repos_raw target_restrictions = [] # If no targets are passed, create a restriction from the current working # directory if inside a known repo. cwd = os.getcwd() if not namespace.targets and cwd in repo: namespace.targets = [cwd] for target in namespace.targets: try: target_restrictions.append(parserestrict.parse_match(target)) except parserestrict.ParseError as e: if os.path.exists(target): try: restrict = repo.path_restrict(target) # toss the repo restriction, keep the rest target_restrictions.append( boolean.AndRestriction(*restrict[1:])) except ValueError as e: argparser.error(e) else: argparser.error(e) if target_restrictions: namespace.restrict.append(boolean.OrRestriction(*target_restrictions)) if namespace.restrict: namespace.restrict = boolean.AndRestriction(*namespace.restrict)
def test_ambiguous(self): repo = SimpleTree({ 'spork': {'foon': ('1',)}, 'spork2': {'foon': ('2',)}}) installed_repos = SimpleTree({'foo': {'bar': ('1',)}}) with pytest.raises(pmerge.AmbiguousQuery): pmerge.parse_target(parse_match("foon"), repo, installed_repos)
def test_globbing(self): repo = SimpleTree({ 'spork': {'foon': ('1',)}, 'spork2': {'foon': ('2',)}}) installed_repos = SimpleTree({'foo': {'bar': ('1',)}}) a = pmerge.parse_target(parse_match('*/foon'), repo, installed_repos) assert len(a) == 2
def package_keywords_splitter(iterable): for line, lineno, path in iterable: v = line.split() try: yield parse_match(v[0]), tuple(v[1:]), line, lineno, path except ParseError as e: logger.warning(f'{path!r}, line {lineno}: parsing error: {e}')
def package_env_splitter(basedir, val): val = val.split() if len(val) == 1: raise ValueError( "package.env files require atoms followed by env file names, got %s" % val) return parse_match(val[0]), tuple( local_source(pjoin(basedir, env_file)) for env_file in val[1:])
def __init__(self, s): if isinstance(s, atom): self._r = s else: try: self._r = parse_match(s) except ParseError: raise InvalidAtomStringError('Incorrect atom: %s' % s)
def convert_to_restrict(sequence, default=packages.AlwaysTrue): """Convert an iterable to a list of atoms, or return the default""" l = [] try: for x in sequence: l.append(parserestrict.parse_match(x)) except parserestrict.ParseError as e: raise argparse.ArgumentError(f"invalid atom: {x!r}: {e}") from e return l or [default]
def test_subslot_package(self): token = 'boost:0/1.54' o = parserestrict.parse_match(token) assert isinstance(o, boolean.AndRestriction), token assert len(o) == 3 slot, _sep, subslot = token.split(":")[1].partition('/') assert isinstance(o[0], restricts.SlotDep), slot assert isinstance(o[1], restricts.SubSlotDep), subslot self.verify_restrict(o[2], "package", token.split(":")[0])
def test_subslot_package(self): token = 'boost:0/1.54' o = parserestrict.parse_match(token) self.assertInstance(o, boolean.AndRestriction, token) self.assertEqual(len(o), 3) slot, _sep, subslot = token.split(":")[1].partition('/') self.assertInstance(o[0], restricts.SlotDep, slot) self.assertInstance(o[1], restricts.SubSlotDep, subslot) self.verify_restrict(o[2], "package", token.split(":")[0])
def test_collision_livefs(self): # test pkg name collision between real and virtual pkgs on livefs # repos, the real pkg will be selected over the virtual installed_repos = SimpleTree({'foo': {'bar': ('1',)}, 'virtual': {'bar': ('0',)}}) repo = SimpleTree({'foo': {'bar': ('1',)}, 'virtual': {'bar': ('1',)}}) a = pmerge.parse_target(parse_match("bar"), repo, installed_repos) assert len(a) == 1 assert a[0].key == 'foo/bar' assert [x.key for x in repo.match(a[0])] == ['foo/bar']
def __call__(self, parser, namespace, values, option_string=None): if isinstance(values, basestring): values = [values] for x in values: if x.startswith('@'): ret = parser._parse_known_args(['--set', x[1:]], namespace) if ret[1]: raise RuntimeError("failed parsing %r, %r, got back %r" % (option_string, values, ret[1])) else: argparse._AppendAction.__call__(self, parser, namespace, parserestrict.parse_match(x), option_string=option_string)
def limiters(): for target in namespace.targets: try: yield parserestrict.parse_match(target) except parserestrict.ParseError as e: if os.path.exists(target): try: yield repo.path_restrict(target) except ValueError as e: parser.error(e) else: parser.error(e)
def test_parse_atom(self): repo = util.SimpleTree({'spork': {'foon': ('1', '1.0.1', '2')}}) livefs_repos = util.SimpleTree({'foo': {'bar': ('1')}}) for cat in ('', 'spork/'): a = pmerge.parse_atom(parse_match('=%sfoon-1' % (cat,)), repo, livefs_repos) self.assertEqual(a.key, 'spork/foon') self.assertEqual([x.fullver for x in repo.itermatch(a)], ['1']) a = pmerge.parse_atom(parse_match('%sfoon' % (cat,)), repo, livefs_repos) self.assertEqual(a.key, 'spork/foon') self.assertEqual(sorted(x.fullver for x in repo.itermatch(a)), sorted(['1', '1.0.1', '2'])) repo = util.SimpleTree({'spork': {'foon': ('1',)}, 'spork2': {'foon': ('2',)}}) self.assertRaises(pmerge.NoMatches, pmerge.parse_atom, parse_match("foo"), repo, livefs_repos) self.assertRaises(pmerge.AmbiguousQuery, pmerge.parse_atom, parse_match("foon"), repo, livefs_repos) # test unicode conversion. a = pmerge.parse_atom(parse_match(u'=spork/foon-1'), repo, livefs_repos) self.assertEqual(a.key, 'spork/foon') self.assertTrue(isinstance(a.key, str)) # test pkg name collisions between real and virtual pkgs on livefs # repos, the real pkg will be selected over the virtual livefs_repos = util.SimpleTree({'foo': {'bar': ('1')}, 'virtual': {'bar': ('0')}}) repo = util.SimpleTree({'foo': {'bar': ('1',)}, 'virtual': {'bar': ('1',)}}) a = pmerge.parse_atom(parse_match("bar"), repo, livefs_repos) self.assertEqual(a.key, 'foo/bar') self.assertTrue(isinstance(a.key, str))
def restrictions(): for target in namespace.targets: try: r = parserestrict.parse_match(target) except parserestrict.ParseError as e: if os.path.exists(target): try: r = _path_restrict(target, namespace) except ValueError as e: parser.error(e) else: parser.error(e) yield _restrict_to_scope(r), r
def test_parse_atom(self): repo = util.SimpleTree({'spork': {'foon': ('1', '1.0.1', '2')}}) for cat in ('', 'spork/'): a = pmerge.parse_atom(parse_match('=%sfoon-1' % (cat,)), repo) self.assertEqual(a.key, 'spork/foon') self.assertEqual([x.fullver for x in repo.itermatch(a)], ['1']) a = pmerge.parse_atom(parse_match('%sfoon' % (cat,)), repo) self.assertEqual(a.key, 'spork/foon') self.assertEqual(sorted(x.fullver for x in repo.itermatch(a)), sorted(['1', '1.0.1', '2'])) repo = util.SimpleTree({'spork': {'foon': ('1',)}, 'spork2': {'foon': ('2',)}}) self.assertRaises(pmerge.NoMatches, pmerge.parse_atom, parse_match("foo"), repo) self.assertRaises(pmerge.AmbiguousQuery, pmerge.parse_atom, parse_match("foon"), repo) # test unicode conversion. a = pmerge.parse_atom(parse_match(u'=spork/foon-1'), repo) self.assertEqual(a.key, 'spork/foon') self.assertTrue(isinstance(a.key, str))
def generic_single_restrict_check(self, iscat): if iscat: sfmts = ["%s/*"] attr = "category" else: sfmts = ["*/%s", "%s"] attr = "package" for sfmt in sfmts: for raw_token in ("package", "*bsdiff", "bsdiff*"): token = sfmt % raw_token i = parserestrict.parse_match(token) self.verify_restrict(i, attr, raw_token)
def __call__(self, parser, namespace, values, option_string=None): sets = [] if isinstance(values, basestring): values = [values] for x in values: if x.startswith('@'): sets.append(x[1:]) else: argparse._AppendAction.__call__( self, parser, namespace, parserestrict.parse_match(x), option_string=option_string) if namespace.targets is None: namespace.targets = [] namespace.sets = sets
def __call__(self, parser, namespace, values, option_string=None): namespace.sets = [] if isinstance(values, basestring): values = [values] for token in values: if token.startswith('@'): namespace.sets.append(token[1:]) else: try: argparse._AppendAction.__call__( self, parser, namespace, (token, parserestrict.parse_match(token)), option_string=option_string) except parserestrict.ParseError as e: parser.error(e) if namespace.targets is None: namespace.targets = []
def __call__(self, parser, namespace, values, option_string=None): sets = [] if isinstance(values, basestring): values = [values] for x in values: if x.startswith('@'): sets.append(x[1:]) else: try: argparse._AppendAction.__call__( self, parser, namespace, (x, parserestrict.parse_match(x)), option_string=option_string) except parserestrict.ParseError as e: parser.error(e) if namespace.targets is None: namespace.targets = [] namespace.sets = sets
def package_env_splitter(basedir, iterable): for line, lineno, path in iterable: val = line.split() if len(val) == 1: logger.warning(f"{path!r}, line {lineno}: missing file reference: {line!r}") continue paths = [] for env_file in val[1:]: fp = pjoin(basedir, env_file) if os.path.exists(fp): paths.append(fp) else: logger.warning(f"{path!r}, line {lineno}: nonexistent file: {fp!r}") try: yield parse_match(val[0]), tuple(paths), line, lineno, path except ParseError as e: logger.warning(f'{path!r}, line {lineno}: parsing error: {e}')
def test_collision_slotted(self): pkgs = [ FakePkg('foo/bar-1.0.1', slot='0'), FakePkg('foo/bar-2.0.2', slot='2'), FakePkg('foon/bar-3.4.5', slot='0'), ] installed_pkgs = [ FakePkg('foo/bar-1.0.0', slot='0'), FakePkg('foo/bar-2.0.1', slot='2'), ] installed_repos = FakeRepo(pkgs=installed_pkgs) repo = FakeRepo(pkgs=pkgs) a = pmerge.parse_target(parse_match("bar:0"), repo, installed_repos) assert len(a) == 1 assert a[0].key == 'foo/bar' assert a[0].match(atom('foo/bar:0')) assert not a[0].match(atom('foo/bar:2'))
def restrictions(): for target in namespace.targets: if os.path.isabs(target) or (os.path.exists(target) and cwd_in_repo): # try to use target as a path restrict if it exists on the filesystem try: scope, restrict = _path_restrict(target, namespace) except ValueError as e: parser.error(e) else: # otherwise assume it's a package restriction of some type try: restrict = parserestrict.parse_match(target) scope = _restrict_to_scope(restrict) except parserestrict.ParseError as e: parser.error(e) yield scope, restrict
def _manifest_validate(parser, namespace): targets = namespace.target if namespace.target else [namespace.cwd] restrictions = [] for target in targets: if os.path.exists(target): try: restrictions.append(namespace.repo.path_restrict(target)) except ValueError as e: manifest.error(e) else: try: restrictions.append(parse_match(target)) except ValueError: manifest.error(f'invalid atom: {target!r}') namespace.restriction = packages.OrRestriction(*restrictions)
def matches_finalize(targets, namespace): if not targets: return [] repos = multiplex.tree(*namespace.repos) restrictions = [] for target in targets: try: restrictions.append(parserestrict.parse_match(target)) except parserestrict.ParseError as e: if os.path.exists(target): try: restrictions.append(repos.path_restrict(target)) except ValueError as e: argparser.error(e) else: argparser.error(e) if restrictions: return packages.OrRestriction(*restrictions) return []
def _digest_validate(parser, namespace): repo = namespace.repo targets = namespace.target restrictions = [] if repo is not None: if not targets: restrictions.append(repo.path_restrict(repo.location)) else: # if we're currently in a known ebuild repo use it, otherwise use all ebuild repos cwd = os.getcwd() repo = namespace.domain.ebuild_repos_raw.repo_match(cwd) if repo is None: repo = namespace.domain.all_ebuild_repos_raw if not targets: try: restrictions.append(repo.path_restrict(cwd)) except ValueError: # we're not in a configured repo so manifest everything restrictions.extend(repo.path_restrict(x.location) for x in repo.trees) if not repo.operations.supports("digests"): digest.error("no repository support for digests") for target in targets: if os.path.exists(target): try: restrictions.append(repo.path_restrict(target)) except ValueError as e: digest.error(e) else: try: restrictions.append(parse_match(target)) except ValueError: digest.error(f"invalid atom: {target!r}") restriction = packages.OrRestriction(*restrictions) namespace.restriction = restriction namespace.repo = repo
def package_keywords_splitter(val): v = val.split() return parse_match(v[0]), tuple(stable_unique(v[1:]))
def package_masks(iterable): for line, lineno, path in iterable: try: yield parse_match(line), line, lineno, path except ParseError as e: logger.warning(f'{path!r}, line {lineno}: parsing error: {e}')
def package_env_splitter(basedir, val): val = val.split() return parse_match(val[0]), local_source(pjoin(basedir, val[1]))
def check_args(parser, namespace): # XXX hack... namespace.checks = sorted(unstable_unique( get_plugins('check', plugins)), key=lambda x: x.__name__) if any((namespace.list_keywords, namespace.list_checks, namespace.list_reporters)): # no need to check any other args return cwd = abspath(os.getcwd()) if namespace.suite is None: # No suite explicitly specified. Use the repo to guess the suite. if namespace.target_repo is None: # Not specified either. Try to find a repo our cwd is in. # The use of a dict here is a hack to deal with one # repo having multiple names in the configuration. candidates = {} for name, suite in namespace.config.pkgcheck_suite.iteritems(): repo = suite.target_repo if repo is None: continue repo_base = getattr(repo, 'location', None) if repo_base is not None and cwd.startswith(repo_base): candidates[repo] = name if len(candidates) == 1: namespace.guessed_suite = True namespace.target_repo = tuple(candidates)[0] if namespace.target_repo is not None: # We have a repo, now find a suite matching it. candidates = list( suite for suite in namespace.config.pkgcheck_suite.itervalues() if suite.target_repo is namespace.target_repo) if len(candidates) == 1: namespace.guessed_suite = True namespace.suite = candidates[0] if namespace.suite is None: # If we have multiple candidates or no candidates we # fall back to the default suite. namespace.suite = namespace.config.get_default('pkgcheck_suite') namespace.default_suite = namespace.suite is not None if namespace.suite is not None: # We have a suite. Lift defaults from it for values that # were not set explicitly: if namespace.checkset is None: namespace.checkset = namespace.suite.checkset # If we were called with no atoms we want to force # cwd-based detection. if namespace.target_repo is None: if namespace.targets: namespace.target_repo = namespace.suite.target_repo elif namespace.suite.target_repo is not None: # No atoms were passed in, so we want to guess # what to scan based on cwd below. That only makes # sense if we are inside the target repo. We still # want to pick the suite's target repo if we are # inside it, in case there is more than one repo # definition with a base that contains our dir. repo_base = getattr(namespace.suite.target_repo, 'location', None) if repo_base is not None and cwd.startswith(repo_base): namespace.target_repo = namespace.suite.target_repo if namespace.target_repo is None: # We have no target repo (not explicitly passed, not from a suite, not # from an earlier guess at the target_repo) so try to guess one. if len(namespace.targets) == 1 and os.path.exists(namespace.targets[0]): target_dir = namespace.targets[0] else: target_dir = cwd target_repo = None for name, repo in namespace.config.repo.iteritems(): repo_base = getattr(repo, 'location', None) if repo_base is not None and target_dir in repo: target_repo = repo if target_repo is None: parser.error( 'no target repo specified and ' 'current directory is not inside a known repo') namespace.target_repo = target_repo if namespace.reporter is None: namespace.reporter = namespace.config.get_default( 'pkgcheck_reporter_factory') if namespace.reporter is None: namespace.reporter = get_plugin('reporter', plugins) if namespace.reporter is None: parser.error( 'no config defined reporter found, nor any default ' 'plugin based reporters') else: func = namespace.config.pkgcheck_reporter_factory.get(namespace.reporter) if func is None: func = list(base.Whitelist([namespace.reporter]).filter( get_plugins('reporter', plugins))) if not func: parser.error( "no reporter matches %r (available: %s)" % ( namespace.reporter, ', '.join(sorted(x.__name__ for x in get_plugins('reporter', plugins))) ) ) elif len(func) > 1: parser.error( "--reporter %r matched multiple reporters, " "must match one. %r" % ( namespace.reporter, tuple(sorted("%s.%s" % (x.__module__, x.__name__) for x in func)) ) ) func = func[0] namespace.reporter = func # search_repo is a multiplex of target_repo and its masters, make sure # they're configured properly in metadata/layout.conf. This is used for # things like visibility checks (it is passed to the checkers in "start"). namespace.search_repo = multiplex.tree(*namespace.target_repo.trees) namespace.repo_bases = [abspath(repo.location) for repo in reversed(namespace.target_repo.trees)] if namespace.targets: limiters = [] repo = namespace.target_repo # read targets from stdin if len(namespace.targets) == 1 and namespace.targets[0] == '-': namespace.targets = [x.strip() for x in sys.stdin.readlines() if x.strip() != ''] # reassign stdin to allow interactivity (currently only works for unix) sys.stdin = open('/dev/tty') for target in namespace.targets: try: limiters.append(parserestrict.parse_match(target)) except parserestrict.ParseError as e: if os.path.exists(target): try: limiters.append(repo.path_restrict(target)) except ValueError as e: parser.error(e) else: parser.error(e) namespace.limiters = limiters else: repo_base = getattr(namespace.target_repo, 'location', None) if not repo_base: parser.error( 'Either specify a target repo that is not multi-tree or ' 'one or more extended atoms to scan ' '("*" for the entire repo).') if cwd not in namespace.target_repo: namespace.limiters = [packages.AlwaysTrue] else: namespace.limiters = [packages.AndRestriction(*namespace.target_repo.path_restrict(cwd))] if namespace.checkset is None: namespace.checkset = namespace.config.get_default('pkgcheck_checkset') if namespace.checkset is not None: namespace.checks = list(namespace.checkset.filter(namespace.checks)) disabled_checks, enabled_checks = ((), ()) if namespace.selected_checks is not None: disabled_checks, enabled_checks = namespace.selected_checks if enabled_checks: whitelist = base.Whitelist(enabled_checks) namespace.checks = list(whitelist.filter(namespace.checks)) if disabled_checks: blacklist = base.Blacklist(disabled_checks) namespace.checks = list(blacklist.filter(namespace.checks)) if not namespace.checks: parser.error('no active checks') namespace.addons = set() def add_addon(addon): if addon not in namespace.addons: namespace.addons.add(addon) for dep in addon.required_addons: add_addon(dep) for check in namespace.checks: add_addon(check) try: for addon in namespace.addons: addon.check_args(parser, namespace) except argparse.ArgumentError as e: if namespace.debug: raise parser.error(str(e))
def test_subslot_atom(self): o = parserestrict.parse_match("dev-libs/boost:0/1.54") self.assertInstance(o, atom, "dev-libs/boost:0/1.54") self.assertTrue(o.slot) self.assertTrue(o.subslot)
def test_slot_atom(self): o = parserestrict.parse_match("sys-devel/automake:1.6") self.assertInstance(o, atom, "sys-devel/automake:1.6") self.assertTrue(o.slot)
def test_use_atom(self): o = parserestrict.parse_match("net-misc/openssh[-X]") self.assertInstance(o, atom, "net-misc/openssh[-X]") self.assertTrue(o.use)
def test_atom_globbed(self): self.assertInstance( parserestrict.parse_match("=sys-devel/gcc-4*"), atom, "=sys-devel/gcc-4*")