diff --git a/bin/quickpkg b/bin/quickpkg index 520b9bc35e..5c2e65462c 100755 --- a/bin/quickpkg +++ b/bin/quickpkg @@ -71,7 +71,8 @@ def quickpkg_atom(options, infos, arg, eout): eout.eerror(f"Invalid atom: {arg}") infos["missing"].append(arg) return 1 - if atom[:1] == "=" and arg[:1] != "=": + # Check if dep_expand added an operator that wasn't in the original arg + if atom.operator == "=" and not str(arg).startswith("="): # dep_expand() allows missing '=' but it's really invalid eout.eerror(f"Invalid atom: {arg}") infos["missing"].append(arg) diff --git a/lib/_emerge/BlockerCache.py b/lib/_emerge/BlockerCache.py index 940416e83f..6a4ef636d3 100644 --- a/lib/_emerge/BlockerCache.py +++ b/lib/_emerge/BlockerCache.py @@ -2,6 +2,7 @@ # Distributed under the terms of the GNU General Public License v2 import errno +from portage.dep import Atom from portage.util import writemsg from portage.data import secpass import portage @@ -99,10 +100,10 @@ def _load(self): continue invalid_atom = False for atom in atoms: - if not isinstance(atom, str): + if not isinstance(atom, (str, Atom)): invalid_atom = True break - if atom[:1] != "!" or not portage.isvalidatom( + if str(atom)[:1] != "!" or not portage.isvalidatom( atom, allow_blockers=True ): invalid_atom = True diff --git a/lib/_emerge/BlockerDB.py b/lib/_emerge/BlockerDB.py index fe81e4ad52..ca7ecb1b32 100644 --- a/lib/_emerge/BlockerDB.py +++ b/lib/_emerge/BlockerDB.py @@ -76,8 +76,8 @@ def findInstalledBlockers(self, new_pkg): ) continue - blocker_atoms = [atom for atom in atoms if atom.startswith("!")] - blocker_atoms.sort() + blocker_atoms = [atom for atom in atoms if atom.blocker] + blocker_atoms.sort(key=str) blocker_cache[inst_pkg.cpv] = blocker_cache.BlockerData( inst_pkg.counter, blocker_atoms ) @@ -89,7 +89,7 @@ def findInstalledBlockers(self, new_pkg): blocker_atoms = [] for pkg in installed_pkgs: for blocker_atom in blocker_cache[pkg.cpv].atoms: - blocker_atom = blocker_atom.lstrip("!") + blocker_atom = str(blocker_atom).lstrip("!") blocker_atoms.append(blocker_atom) blocker_parents.add(blocker_atom, pkg) @@ -113,7 +113,7 @@ def findInstalledBlockers(self, new_pkg): show_invalid_depstring_notice(new_pkg, atoms) assert False - blocker_atoms = [atom.lstrip("!") for atom in atoms if atom[:1] == "!"] + blocker_atoms = [str(atom).lstrip("!") for atom in atoms if atom.blocker] if blocker_atoms: blocker_atoms = InternalPackageSet(initial_atoms=blocker_atoms) for inst_pkg in installed_pkgs: diff --git a/lib/_emerge/actions.py b/lib/_emerge/actions.py index b9ff685c71..b1a1f7a313 100644 --- a/lib/_emerge/actions.py +++ b/lib/_emerge/actions.py @@ -1183,17 +1183,11 @@ def unresolved_deps(): # visible in the unevaluated form of the atom. In this # case, we must display the unevaluated atom, so that # the user can see the conditional USE deps that would - # otherwise be invisible. Use Atom(str(atom)) to - # test for a package where this case would matter. This - # is not necessarily the same as atom.without_use, - # since Atom(str(atom)) may still contain some - # USE dependencies that remain after evaluation of + # otherwise be invisible. This is not necessarily the same as + # atom.without_use, since the evaluated atom may still contain + # some USE dependencies that remain after evaluation of # conditionals. - if ( - atom.package - and atom != atom.unevaluated_atom - and vardb.match(Atom(str(atom))) - ): + if atom.package and atom != atom.unevaluated_atom and vardb.match(atom): msg.append(f" {atom.unevaluated_atom} ({atom}) pulled in by:") else: msg.append(f" {atom} pulled in by:") @@ -1758,8 +1752,8 @@ def action_deselect(settings, trees, opts, atoms): expanded_atoms = set(atoms) for atom in atoms: - if not atom.startswith(SETPREFIX): - if atom.cp.startswith("null/"): + if not str(atom).startswith(SETPREFIX): + if atom.category == "null": # try to expand category from world set null_cat, pn = portage.catsplit(atom.cp) for world_atom in world_atoms: @@ -1767,7 +1761,7 @@ def action_deselect(settings, trees, opts, atoms): if pn == world_pn: expanded_atoms.add( Atom( - atom.replace("null", cat, 1), + str(atom).replace("null", cat, 1), allow_repo=True, allow_wildcard=True, ) @@ -1780,13 +1774,13 @@ def action_deselect(settings, trees, opts, atoms): discard_atoms = set() for atom in world_set: for arg_atom in expanded_atoms: - if arg_atom.startswith(SETPREFIX): - if atom.startswith(SETPREFIX) and arg_atom == atom: + if str(arg_atom).startswith(SETPREFIX): + if str(atom).startswith(SETPREFIX) and arg_atom == atom: discard_atoms.add(atom) break else: if ( - not atom.startswith(SETPREFIX) + not str(atom).startswith(SETPREFIX) and arg_atom.intersects(atom) and not (arg_atom.slot and not atom.slot) and not (arg_atom.repo and not atom.repo) @@ -1794,13 +1788,13 @@ def action_deselect(settings, trees, opts, atoms): discard_atoms.add(atom) break if discard_atoms: - for atom in sorted(discard_atoms): + for atom in sorted(discard_atoms, key=str): if pretend: action_desc = "Would remove" else: action_desc = "Removing" - if atom.startswith(SETPREFIX): + if str(atom).startswith(SETPREFIX): filename = "world_sets" else: filename = "world" @@ -2103,7 +2097,7 @@ def action_info(settings, trees, myopts, myfiles): if not atom.blocker: atoms.append((x, atom)) - myvars = sorted(set(atoms)) + myvars = sorted(set(atoms), key=lambda t: str(t[0])) cp_map = {} cp_max_len = 0 @@ -2835,7 +2829,7 @@ def binpkg_selection_config(opts, settings): writemsg( "\n!!! The following atoms appear in both the --usepkg-exclude " "and --usepkg-include command line arguments:\n" - "\n %s\n" % ("\n ".join(conflicted_atoms)) + "\n %s\n" % ("\n ".join(str(a) for a in conflicted_atoms)) ) for a in conflicted_atoms: usepkg_exclude.remove(a) @@ -2847,14 +2841,16 @@ def binpkg_selection_config(opts, settings): writemsg( "\n!!! The following --usepkg-exclude atoms are ignored due " "to use of --nobindeps:\n" - "\n %s\n" % ("\n ".join(usepkg_exclude.getAtoms())) + "\n %s\n" + % ("\n ".join(str(a) for a in usepkg_exclude.getAtoms())) ) usepkg_exclude.clear() if not usepkg_include.isEmpty(): writemsg( "\n!!! The following --usepkg-include atoms are ignored due " "to use of --nobindeps:\n" - "\n %s\n" % ("\n ".join(usepkg_include.getAtoms())) + "\n %s\n" + % ("\n ".join(str(a) for a in usepkg_include.getAtoms())) ) usepkg_include.clear() for repo in settings.repositories: @@ -2863,7 +2859,10 @@ def binpkg_selection_config(opts, settings): "\n!!! The following usepkg-exclude atoms for [%s] are " "ignored due to use of --nobindeps:\n" "\n %s\n" - % (repo.name, "\n ".join(repo.usepkg_exclude.getAtoms())) + % ( + repo.name, + "\n ".join(str(a) for a in repo.usepkg_exclude.getAtoms()), + ) ) repo.usepkg_exclude.clear() if not repo.usepkg_include.isEmpty(): @@ -2871,7 +2870,10 @@ def binpkg_selection_config(opts, settings): "\n!!! The following usepkg-include atoms for [%s] are " "ignored due to use of --nobindeps:\n" "\n %s\n" - % (repo.name, "\n ".join(repo.usepkg_include.getAtoms())) + % ( + repo.name, + "\n ".join(str(a) for a in repo.usepkg_include.getAtoms()), + ) ) repo.usepkg_include.clear() @@ -2884,7 +2886,8 @@ def binpkg_selection_config(opts, settings): writemsg( "\n!!! The following usepkg-exclude atoms for [%s] have " "been overridden by the --usepkg-include option:\n" - "\n %s\n" % (repo.name, "\n ".join(conflicted_exclude)) + "\n %s\n" + % (repo.name, "\n ".join(str(a) for a in conflicted_exclude)) ) for a in conflicted_exclude: repo.usepkg_exclude.remove(a) @@ -2895,7 +2898,8 @@ def binpkg_selection_config(opts, settings): writemsg( "\n!!! The following usepkg-include atoms for [%s] have " "been overridden by the --usepkg-exclude option:\n" - "\n %s\n" % (repo.name, "\n ".join(conflicted_include)) + "\n %s\n" + % (repo.name, "\n ".join(str(a) for a in conflicted_include)) ) for a in conflicted_include: repo.usepkg_include.remove(a) @@ -2908,20 +2912,20 @@ def binpkg_selection_config(opts, settings): writemsg( "\n!!! The following atoms appear in both the --getbinpkg-exclude " "and --getbinpkg-include command line arguments:\n" - "\n %s\n" % ("\n ".join(conflicted_atoms)) + "\n %s\n" % ("\n ".join(str(a) for a in conflicted_atoms)) ) for a in conflicted_atoms: getbinpkg_exclude.remove(a) getbinpkg_include.remove(a) if not getbinpkg_exclude.isEmpty(): - opts["--getbinpkg-exclude"] = list(getbinpkg_exclude) + opts["--getbinpkg-exclude"] = [str(a) for a in getbinpkg_exclude] if not getbinpkg_include.isEmpty(): - opts["--getbinpkg-include"] = list(getbinpkg_include) + opts["--getbinpkg-include"] = [str(a) for a in getbinpkg_include] if not usepkg_exclude.isEmpty(): - opts["--usepkg-exclude"] = list(usepkg_exclude) + opts["--usepkg-exclude"] = [str(a) for a in usepkg_exclude] if not usepkg_include.isEmpty(): - opts["--usepkg-include"] = list(usepkg_include) + opts["--usepkg-include"] = [str(a) for a in usepkg_include] def display_missing_pkg_set(root_config, set_name): @@ -4161,11 +4165,11 @@ def emergeexitsig(signum, frame): # look at the ebuilds, since EAPI 4 allows running pkg_info # on non-installed packages valid_atom = dep_expand(x, mydb=vardb) - if valid_atom.cp.split("/")[0] == "null": + if valid_atom.category == "null": valid_atom = dep_expand(x, mydb=portdb) if ( - valid_atom.cp.split("/")[0] == "null" + valid_atom.category == "null" and emerge_config.opts.get("--usepkg") is True ): valid_atom = dep_expand(x, mydb=bindb) diff --git a/lib/_emerge/create_world_atom.py b/lib/_emerge/create_world_atom.py index 8cab942885..4fe325316c 100644 --- a/lib/_emerge/create_world_atom.py +++ b/lib/_emerge/create_world_atom.py @@ -113,7 +113,7 @@ def create_world_atom(pkg, args_set, root_config, before_install=False): # gcc for example. system_atom = sets["system"].findAtomForPackage(pkg) if system_atom: - if not system_atom.cp.startswith("virtual/"): + if system_atom.category != "virtual": return None # System virtuals aren't safe to exclude from world since they can # match multiple old-style virtuals but only one of them will be diff --git a/lib/_emerge/depgraph.py b/lib/_emerge/depgraph.py index a1b68603c5..1aebb344ef 100644 --- a/lib/_emerge/depgraph.py +++ b/lib/_emerge/depgraph.py @@ -192,33 +192,37 @@ def __init__(self, settings, trees, myopts, params, spinner): else: self._required_set_names = {"world"} - atoms = " ".join(myopts.get("--exclude", [])).split() + atoms = " ".join(str(a) for a in myopts.get("--exclude", [])).split() self.excluded_pkgs = WildcardPackageSet(atoms) - atoms = " ".join(myopts.get("--reinstall-atoms", [])).split() + atoms = " ".join(str(a) for a in myopts.get("--reinstall-atoms", [])).split() self.reinstall_atoms = WildcardPackageSet(atoms) - atoms = " ".join(myopts.get("--usepkg-exclude", [])).split() + atoms = " ".join(str(a) for a in myopts.get("--usepkg-exclude", [])).split() self.usepkg_exclude = WildcardPackageSet(atoms, allow_repo=True) - atoms = " ".join(myopts.get("--usepkg-include", [])).split() + atoms = " ".join(str(a) for a in myopts.get("--usepkg-include", [])).split() self.usepkg_include = WildcardPackageSet(atoms, allow_repo=True) - atoms = " ".join(myopts.get("--useoldpkg-atoms", [])).split() + atoms = " ".join(str(a) for a in myopts.get("--useoldpkg-atoms", [])).split() self.useoldpkg_atoms = WildcardPackageSet(atoms) - atoms = " ".join(myopts.get("--rebuild-exclude", [])).split() + atoms = " ".join(str(a) for a in myopts.get("--rebuild-exclude", [])).split() self.rebuild_exclude = WildcardPackageSet(atoms) - atoms = " ".join(myopts.get("--rebuild-ignore", [])).split() + atoms = " ".join(str(a) for a in myopts.get("--rebuild-ignore", [])).split() self.rebuild_ignore = WildcardPackageSet(atoms) self.buildpkg_exclude = InternalPackageSet( - initial_atoms=" ".join(myopts.get("--buildpkg-exclude", [])).split(), + initial_atoms=" ".join( + str(a) for a in myopts.get("--buildpkg-exclude", []) + ).split(), allow_wildcard=True, allow_repo=True, ) for repo in settings.repositories: self.usepkg_exclude.update( - a + _repo_separator + repo.name for a in repo.usepkg_exclude.getAtoms() + str(a) + _repo_separator + repo.name + for a in repo.usepkg_exclude.getAtoms() ) self.usepkg_include.update( - a + _repo_separator + repo.name for a in repo.usepkg_include.getAtoms() + str(a) + _repo_separator + repo.name + for a in repo.usepkg_include.getAtoms() ) self.rebuild_if_new_rev = "--rebuild-if-new-rev" in myopts @@ -2307,12 +2311,12 @@ def _slot_operator_check_reverse_dependencies( for parent, atom in self._dynamic_config._parent_atoms.get(existing_pkg, []): if isinstance(parent, Package): if parent in built_slot_operator_parents: - if hasattr(atom, "_orig_atom"): + if getattr(atom, "orig_atom", False): # If atom is the result of virtual expansion, then - # dereference it to _orig_atom so that it will be correctly + # dereference it to orig_atom so that it will be correctly # handled as a built slot operator dependency when # appropriate (see bug 764764). - atom = atom._orig_atom + atom = atom.orig_atom # This parent may need to be rebuilt, therefore # discard its soname and built slot operator # dependency components which are not necessarily @@ -3671,7 +3675,7 @@ def _add_pkg_soname_deps(self, pkg, allow_unsatisfied=False): continue dep = Dependency( atom=atom, - blocker=False, + blocker=None, depth=depth, parent=pkg, priority=self._priority(cross=self._cross(pkg.root), runtime=True), @@ -4353,8 +4357,8 @@ def _wrapped_add_pkg_dep_string( # from dep_check, map it back to the original, in # order to avoid distortion in places like display # or conflict resolution code. - is_virt = hasattr(atom, "_orig_atom") - atom = getattr(atom, "_orig_atom", atom) + is_virt = getattr(atom, "orig_atom", False) + atom = atom.orig_atom if is_virt else atom if atom.blocker and (dep_priority.optional or dep_priority.ignored): # For --with-bdeps, ignore build-time only blockers @@ -4495,8 +4499,8 @@ def _wrapped_add_pkg_dep_string( # from dep_check, map it back to the original, in # order to avoid distortion in places like display # or conflict resolution code. - is_virt = hasattr(atom, "_orig_atom") - atom = getattr(atom, "_orig_atom", atom) + is_virt = atom.orig_atom is not None + atom = atom.orig_atom if is_virt else atom # This is a GLEP 37 virtual, so its deps are all runtime. mypriority = self._priority(cross=self._cross(pkg.root), runtime=True) @@ -4709,7 +4713,7 @@ def _queue_disjunctive_deps( # Note: Eventually this will check for PROPERTIES=virtual # or whatever other metadata gets implemented for this # purpose. - if x.cp.startswith("virtual/"): + if x.category == "virtual": disjunctions.append(x) else: yield x @@ -5142,8 +5146,10 @@ def _select_files(self, myfiles): if len(expanded_atoms) > 1: number_of_virtuals = 0 for expanded_atom in expanded_atoms: - if expanded_atom.cp.startswith( - ("acct-group/", "acct-user/", "virtual/") + if expanded_atom.category in ( + "acct-group", + "acct-user", + "virtual", ): number_of_virtuals += 1 else: @@ -5173,7 +5179,8 @@ def _select_files(self, myfiles): if virts_p: # Allow the depgraph to choose which virtual. atom = Atom( - null_atom.replace("null/", "virtual/", 1), allow_repo=True + str(null_atom).replace("null/", "virtual/", 1), + allow_repo=True, ) else: atom = null_atom @@ -5334,7 +5341,7 @@ def _resolve(self, myfavorites): pprovideddict = pkgsettings.pprovideddict virtuals = pkgsettings.getvirtuals() - for atom in sorted(arg.pset.getAtoms()): + for atom in sorted(arg.pset.getAtoms(), key=str): self._spinner_update() dep = Dependency(atom=atom, onlydeps=onlydeps, root=myroot, parent=arg) try: @@ -5404,9 +5411,7 @@ def _resolve(self, myfavorites): if not pkg: pprovided_match = False for virt_choice in virtuals.get(atom.cp, []): - expanded_atom = portage.dep.Atom( - atom.replace(atom.cp, virt_choice.cp, 1) - ) + expanded_atom = atom.with_cp(virt_choice.cp) pprovided = pprovideddict.get(expanded_atom.cp) if pprovided and portage.match_from_list( expanded_atom, pprovided @@ -5445,7 +5450,7 @@ def _resolve(self, myfavorites): if atom.cp != pkg.cp: # For old-style virtuals, we need to repeat the # package.provided check against the selected package. - expanded_atom = atom.replace(atom.cp, pkg.cp) + expanded_atom = atom.with_cp(pkg.cp) pprovided = pprovideddict.get(pkg.cp) if pprovided and portage.match_from_list( expanded_atom, pprovided @@ -5946,9 +5951,9 @@ def _select_atoms_highest_available( chain( (id(atom) for atom in mycheck[1]), ( - id(atom._orig_atom) + id(atom.orig_atom) for atom in mycheck[1] - if hasattr(atom, "_orig_atom") + if getattr(atom, "orig_atom", False) ), ) ) @@ -6007,7 +6012,7 @@ def _expand_virt_from_graph(self, root, atom): if not isinstance(atom, Atom): atom = Atom(atom) - if not atom.cp.startswith("virtual/"): + if atom.category != "virtual": yield atom return @@ -6032,7 +6037,7 @@ def _expand_virt_from_graph(self, root, atom): for atoms in rdepend.values(): for atom in atoms: - if hasattr(atom, "_orig_atom"): + if getattr(atom, "orig_atom", False): # Ignore virtual atoms since we're only # interested in expanding the real atoms. continue @@ -6846,7 +6851,7 @@ def _show_unsatisfied_dep( mask_docs = True else: cp_exists = False - if atom.package and not atom.cp.startswith("null/"): + if atom.package and atom.category != "null": for pkg in self._iter_match_pkgs_any(root_config, Atom(atom.cp)): cp_exists = True break @@ -8566,7 +8571,7 @@ def _complete_graph(self, required_sets=None): self._set_args(args) for arg in self._expand_set_args(args, add_to_digraph=True): - for atom in sorted(arg.pset.getAtoms()): + for atom in sorted(arg.pset.getAtoms(), key=str): if not self._add_dep( Dependency( atom=atom, @@ -8826,7 +8831,7 @@ def _validate_blockers(self): if blocker_data is None and blockers is not None: # Re-use the blockers from the graph. - blocker_atoms = sorted(blockers) + blocker_atoms = sorted(blockers, key=str) blocker_data = blocker_cache.BlockerData( pkg.counter, blocker_atoms ) @@ -8879,7 +8884,7 @@ def _validate_blockers(self): show_invalid_depstring_notice(pkg, atoms) return False blocker_atoms = [myatom for myatom in atoms if myatom.blocker] - blocker_atoms.sort() + blocker_atoms.sort(key=str) blocker_cache[cpv] = blocker_cache.BlockerData( pkg.counter, blocker_atoms ) @@ -8938,7 +8943,9 @@ def _validate_blockers(self): atoms = [] for provider_entry in virtuals[blocker.cp]: atoms.append( - Atom(blocker.atom.replace(blocker.cp, provider_entry.cp, 1)) + Atom( + str(blocker.atom).replace(blocker.cp, provider_entry.cp, 1) + ) ) else: atoms = [blocker.atom] @@ -11135,7 +11142,7 @@ def saveNomergeFavorites(self): all_added.append(s) all_added.extend(added_favorites) if all_added: - all_added.sort() + all_added.sort(key=str) skip = False if "--ask" in self._frozen_config.myopts: writemsg_stdout("\n", noiselevel=-1) @@ -11306,7 +11313,7 @@ def _loadResumeCommand(self, resume_data, skip_masked=True, skip_missing=True): # added via _add_pkg() so that they are included in the # digraph (needed at least for --tree display). for arg in self._expand_set_args(args, add_to_digraph=True): - for atom in sorted(arg.pset.getAtoms()): + for atom in sorted(arg.pset.getAtoms(), key=str): pkg, existing_node = self._select_package( arg.root_config.root, atom ) @@ -11614,7 +11621,7 @@ def match_pkgs(self, atom): if ( pkg is not None and atom.sub_slot is None - and pkg.cp.startswith("virtual/") + and pkg.category == "virtual" and ( ( "remove" not in self._depgraph._dynamic_config.myparams @@ -11706,7 +11713,7 @@ def _visible(self, pkg, atom_set, avoid_slot_conflict=True, probe_virt_update=Tr if not self._depgraph._equiv_ebuild_visible(pkg): return False - if pkg.cp.startswith("virtual/"): + if pkg.category == "virtual": if not self._depgraph._virt_deps_visible(pkg, ignore_use=True): return False diff --git a/lib/_emerge/resolver/output.py b/lib/_emerge/resolver/output.py index 5a36dc2faf..5ff8ab68fe 100644 --- a/lib/_emerge/resolver/output.py +++ b/lib/_emerge/resolver/output.py @@ -28,7 +28,7 @@ bad = create_color_func("BAD") from portage._sets.base import InternalPackageSet from portage.util import writemsg_stdout -from portage.versions import best, cpv_getversion +from portage.versions import best from _emerge.Blocker import Blocker from _emerge.create_world_atom import create_world_atom @@ -953,7 +953,7 @@ def format_unmatched_atom(pkg, atom, pkg_use_enabled): def perform_coloring(): atom_str = "" marker_str = "" - for ii, x in enumerate(atom): + for ii, x in enumerate(str(atom)): if ii in highlight: atom_str += colorize("BAD", x) marker_str += "^" @@ -964,7 +964,7 @@ def perform_coloring(): if atom.cp != pkg.cp: # Highlight the cp part only. - ii = atom.find(atom.cp) + ii = str(atom).find(atom.cp) highlight.update(range(ii, ii + len(atom.cp))) return perform_coloring() @@ -982,9 +982,7 @@ def perform_coloring(): if highlight_version: op = atom.operator - ver = None - if atom.cp != atom.cpv: - ver = cpv_getversion(atom.cpv) + ver = atom.version if op == "=*": op = "=" @@ -994,7 +992,7 @@ def perform_coloring(): highlight.update(range(len(op))) if ver is not None: - start = atom.rfind(ver) + start = str(atom).rfind(ver) end = start + len(ver) highlight.update(range(start, end)) @@ -1004,7 +1002,7 @@ def perform_coloring(): slot_str += "/" + atom.sub_slot if atom.slot_operator: slot_str += atom.slot_operator - start = atom.find(slot_str) + start = str(atom).find(slot_str) end = start + len(slot_str) highlight.update(range(start, end)) @@ -1027,7 +1025,7 @@ def perform_coloring(): ) if highlight_use: - ii = atom.find("[") + 1 + ii = str(atom).find("[") + 1 for token in atom.use.tokens: if token.lstrip("-!").rstrip("=?") in highlight_use: highlight.update(range(ii, ii + len(token))) diff --git a/lib/_emerge/resolver/slot_collision.py b/lib/_emerge/resolver/slot_collision.py index 6322fed1dd..20c47d8c45 100644 --- a/lib/_emerge/resolver/slot_collision.py +++ b/lib/_emerge/resolver/slot_collision.py @@ -10,7 +10,7 @@ from portage.output import colorize from portage._sets.base import InternalPackageSet from portage.util import writemsg -from portage.versions import cpv_getversion, vercmp +from portage.versions import vercmp class slot_conflict_handler: @@ -410,8 +410,8 @@ def _prepare_conflict_msg_and_check_for_specificity(self): for ppkg, atom, other_pkg in parents: if atom.cp in best_matches: cmp = vercmp( - cpv_getversion(atom.cpv), - cpv_getversion(best_matches[atom.cp][1].cpv), + atom.version, + best_matches[atom.cp][1].version, ) if ( @@ -519,9 +519,7 @@ def highlight_violations(atom, version, use, slot_violated): colored_idx = set() if version: op = atom.operator - ver = None - if atom.cp != atom.cpv: - ver = cpv_getversion(atom.cpv) + ver = atom.version slot = atom.slot sub_slot = atom.sub_slot slot_operator = atom.slot_operator diff --git a/lib/_emerge/unmerge.py b/lib/_emerge/unmerge.py index 398674c575..956c9992d9 100644 --- a/lib/_emerge/unmerge.py +++ b/lib/_emerge/unmerge.py @@ -122,8 +122,9 @@ def _pkg(cpv): print(f"\nNo packages to {unmerge_action} have been provided.\n") return 1, {} for x in unmerge_files: - arg_parts = x.split("/") - if x[0] not in [".", "/"] and arg_parts[-1][-7:] != ".ebuild": + x_str = str(x) # this could be an Atom, stringify + arg_parts = x_str.split("/") + if x_str[0] not in [".", "/"] and arg_parts[-1][-7:] != ".ebuild": # possible cat/pkg or dep; treat as such candidate_catpkgs.append(x) elif unmerge_action in ["prune", "clean"]: diff --git a/lib/portage/_sets/ProfilePackageSet.py b/lib/portage/_sets/ProfilePackageSet.py index 8ef7a56091..8d81f31765 100644 --- a/lib/portage/_sets/ProfilePackageSet.py +++ b/lib/portage/_sets/ProfilePackageSet.py @@ -40,7 +40,7 @@ def load(self): ], incremental=1, ) - if x[:1] != "*" + if str(x)[:1] != "*" ) def singleBuilder(self, options, settings, trees): diff --git a/lib/portage/_sets/base.py b/lib/portage/_sets/base.py index 2f51137863..020aea6fa0 100644 --- a/lib/portage/_sets/base.py +++ b/lib/portage/_sets/base.py @@ -5,7 +5,6 @@ from portage.exception import InvalidAtom from portage.versions import cpv_getkey - OPERATIONS = ["merge", "unmerge"] @@ -131,13 +130,7 @@ def findAtomForPackage(self, pkg, modified_use=None): if atom.cp == pkg.cp: rev_transform[atom] = atom else: - rev_transform[ - Atom( - atom.replace(atom.cp, pkg.cp, 1), - allow_wildcard=True, - allow_repo=True, - ) - ] = atom + rev_transform[atom.with_cp(pkg.cp)] = atom best_match = best_match_to_list(pkg, iter(rev_transform)) if best_match: return rev_transform[best_match] diff --git a/lib/portage/_sets/files.py b/lib/portage/_sets/files.py index 1b9cc6016f..a2f2d81aa3 100644 --- a/lib/portage/_sets/files.py +++ b/lib/portage/_sets/files.py @@ -77,7 +77,10 @@ def _validate(self, atom): def write(self): write_atomic( self._filename, - "".join(f"{atom}\n" for atom in sorted(chain(self._atoms, self._nonatoms))), + "".join( + f"{atom}\n" + for atom in sorted(chain(self._atoms, self._nonatoms), key=str) + ), ) def load(self): @@ -305,7 +308,9 @@ def _validate(self, atom): return ValidAtomValidator(atom, allow_repo=True) def write(self): - write_atomic(self._filename, "".join(sorted(f"{x}\n" for x in self._atoms))) + write_atomic( + self._filename, "".join(sorted((f"{x}\n" for x in self._atoms), key=str)) + ) def load(self): atoms = [] @@ -401,7 +406,9 @@ def _validate(self, setname): return setname.startswith(SETPREFIX) def write(self): - write_atomic(self._filename, "".join(sorted(f"{x}\n" for x in self._nonatoms))) + write_atomic( + self._filename, "".join(sorted((f"{x}\n" for x in self._nonatoms), key=str)) + ) def load(self): atoms_changed = False diff --git a/lib/portage/_sets/profiles.py b/lib/portage/_sets/profiles.py index 4731a0addd..d4eab65539 100644 --- a/lib/portage/_sets/profiles.py +++ b/lib/portage/_sets/profiles.py @@ -66,7 +66,7 @@ def load(self): noiselevel=-1, ) - self._setAtoms([x[1:] for x in mylist if x[0] == "*"]) + self._setAtoms([str(x)[1:] for x in mylist if str(x)[0] == "*"]) def singleBuilder(self, options, settings, trees): debug = get_boolean(options, "debug", False) diff --git a/lib/portage/binrepo/config.py b/lib/portage/binrepo/config.py index 604246d15e..ede0b564e0 100644 --- a/lib/portage/binrepo/config.py +++ b/lib/portage/binrepo/config.py @@ -67,7 +67,8 @@ def __init__(self, opts): writemsg( "\n!!! The following atoms appear in both the getbinpkg-exclude " "getbinpkg-include lists for binrepo [%s]:\n" - "\n %s\n" % (self.name, "\n ".join(conflicted_atoms)) + "\n %s\n" + % (self.name, "\n ".join(str(a) for a in conflicted_atoms)) ) for a in conflicted_atoms: self.getbinpkg_exclude.remove(a) diff --git a/lib/portage/dbapi/_expand_new_virt.py b/lib/portage/dbapi/_expand_new_virt.py index 378328abdc..b714862253 100644 --- a/lib/portage/dbapi/_expand_new_virt.py +++ b/lib/portage/dbapi/_expand_new_virt.py @@ -16,7 +16,7 @@ def expand_new_virt(vardb, atom): if not isinstance(atom, Atom): atom = Atom(atom) - if not atom.cp.startswith("virtual/"): + if atom.category != "virtual": yield atom return @@ -25,7 +25,7 @@ def expand_new_virt(vardb, atom): while stack: atom = stack.pop() - if atom.blocker or not atom.cp.startswith("virtual/"): + if atom.blocker or atom.category != "virtual": yield atom continue diff --git a/lib/portage/dbapi/bintree.py b/lib/portage/dbapi/bintree.py index bf4e611bed..07c1d89756 100644 --- a/lib/portage/dbapi/bintree.py +++ b/lib/portage/dbapi/bintree.py @@ -1427,9 +1427,9 @@ def _populate_remote( else: gpkg_only = False - atoms = " ".join(getbinpkg_exclude or []).split() + atoms = " ".join(str(a) for a in (getbinpkg_exclude or [])).split() getbinpkg_exclude = WildcardPackageSet(atoms) - atoms = " ".join(getbinpkg_include or []).split() + atoms = " ".join(str(a) for a in (getbinpkg_include or [])).split() getbinpkg_include = WildcardPackageSet(atoms) # Order by descending priority. @@ -1450,7 +1450,8 @@ def _populate_remote( writemsg( "\n!!! The following getbinpkg-exclude atoms for [%s] have " "been overridden by the --getbinpkg-include option:\n" - "\n %s\n" % (repo.name, "\n ".join(conflicted_exclude)) + "\n %s\n" + % (repo.name, "\n ".join(str(a) for a in conflicted_exclude)) ) for a in conflicted_exclude: getbinpkg_exclude_repo.remove(a) @@ -1463,7 +1464,8 @@ def _populate_remote( writemsg( "\n!!! The following getbinpkg-include atoms for [%s] have " "been overridden by the --getbinpkg-exclude option:\n" - "\n %s\n" % (repo.name, "\n ".join(conflicted_include)) + "\n %s\n" + % (repo.name, "\n ".join(str(a) for a in conflicted_include)) ) for a in conflicted_include: getbinpkg_include_repo.remove(a) diff --git a/lib/portage/dbapi/dep_expand.py b/lib/portage/dbapi/dep_expand.py index fd874aad1d..720415ce27 100644 --- a/lib/portage/dbapi/dep_expand.py +++ b/lib/portage/dbapi/dep_expand.py @@ -47,7 +47,7 @@ def dep_expand(mydep, mydb=None, use_cache=1, settings=None): if has_cat: # Optimize most common cases to avoid calling cpv_expand. - if not mydep.cp.startswith("virtual/"): + if mydep.category != "virtual": return mydep if not hasattr(mydb, "cp_list") or mydb.cp_list(mydep.cp): return mydep @@ -55,4 +55,4 @@ def dep_expand(mydep, mydb=None, use_cache=1, settings=None): mydep = mydep.cp expanded = cpv_expand(mydep, mydb=mydb, use_cache=use_cache, settings=settings) - return Atom(orig_dep.replace(mydep, expanded, 1), allow_repo=True) + return Atom(str(orig_dep).replace(mydep, expanded, 1), allow_repo=True) diff --git a/lib/portage/dbapi/vartree.py b/lib/portage/dbapi/vartree.py index 7f392588ef..1f28e68a8b 100644 --- a/lib/portage/dbapi/vartree.py +++ b/lib/portage/dbapi/vartree.py @@ -1963,7 +1963,7 @@ def _acquire_slot_locks(self, db): # Sort atoms so that locks are acquired in a predictable # order, preventing deadlocks with competitors that may # be trying to acquire overlapping locks. - slot_atoms.sort() + slot_atoms.sort(key=str) for slot_atom in slot_atoms: self.vartree.dbapi._slot_lock(slot_atom) self._slot_locks.append(slot_atom) diff --git a/lib/portage/dep/__init__.py b/lib/portage/dep/__init__.py index 9f831fdea4..7e6b163fda 100644 --- a/lib/portage/dep/__init__.py +++ b/lib/portage/dep/__init__.py @@ -47,14 +47,15 @@ _unknown_repo, _vr, catpkgsplit, + cpv_getversion, vercmp, ververify, ) import portage.cache.mappings -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional, Union if TYPE_CHECKING: - import _emerge.Package + from _emerge.Package import Package # \w is [a-zA-Z0-9_] @@ -100,11 +101,7 @@ def _get_slot_dep_re(eapi_attrs: _eapi_attrs) -> re.Pattern: return slot_re -def _match_slot(atom, pkg) -> bool: - """ - @type atom: portage.dep.Atom - @type pkg: _emerge.Package.Package - """ +def _match_slot(atom: "Atom", pkg: "Package") -> bool: if pkg.slot == atom.slot: if not atom.sub_slot: return True @@ -242,7 +239,7 @@ def _get_useflag_re(eapi): return _useflag_re -def cpvequal(cpv1, cpv2): +def cpvequal(cpv1: Union[str, _pkg_str], cpv2: Union[str, _pkg_str]) -> bool: """ Example Usage: >>> from portage.dep import cpvequal @@ -250,10 +247,7 @@ def cpvequal(cpv1, cpv2): >>> True @param cpv1: CategoryPackageVersion (no operators) Example: "sys-apps/portage-2.1" - @type cpv1: String @param cpv2: CategoryPackageVersion (no operators) Example: "sys-apps/portage-2.1" - @type cpv2: String - @rtype: Boolean @return: 1. True if cpv1 = cpv2 2. False Otherwise @@ -465,13 +459,16 @@ def _zap_parens(self, src, dest, disjunction=False): return dest -def paren_enclose(mylist, unevaluated_atom=False, opconvert=False): +_ParenEncloseType = list[Union[str, "Atom", "_ParenEncloseType"]] + + +def paren_enclose( + mylist: _ParenEncloseType, unevaluated_atom: bool = False, opconvert: bool = False +) -> str: """ Convert a list to a string with sublists enclosed with parens. @param mylist: The list - @type mylist: List - @rtype: String @return: The paren enclosed string Example usage: @@ -487,26 +484,26 @@ def paren_enclose(mylist, unevaluated_atom=False, opconvert=False): else: mystrparts.append(f"( {paren_enclose(x)} )") else: - if unevaluated_atom: - x = getattr(x, "unevaluated_atom", x) - mystrparts.append(x) + if unevaluated_atom and isinstance(x, Atom): + x = x.unevaluated_atom + mystrparts.append(str(x)) return " ".join(mystrparts) @lru_cache(1024) def _use_reduce_cached( - depstr, + depstr: str, uselist, masklist, - matchall, + matchall: bool, excludeall, - is_src_uri, - eapi, - opconvert, - flat, + is_src_uri: bool, + eapi: Optional[str], + opconvert: bool, + flat: bool, is_valid_flag, token_class, - matchnone, + matchnone: bool, subset, ): if opconvert and flat: @@ -522,7 +519,7 @@ def _use_reduce_cached( eapi_attrs = _get_eapi_attrs(eapi) useflag_re = _get_useflag_re(eapi) - def is_active(conditional): + def is_active(conditional: str): """ Decides if a given use conditional is active. """ @@ -535,15 +532,13 @@ def is_active(conditional): if is_valid_flag: if not is_valid_flag(flag): - msg = _( - "USE flag '%s' referenced in " + "conditional '%s' is not in IUSE" - ) % (flag, conditional) + msg = f"USE flag '{flag}' referenced in conditional '{conditional}' is not in IUSE" e = InvalidData(msg, category="IUSE.missing") raise InvalidDependString(msg, errors=(e,)) else: if useflag_re.match(flag) is None: raise InvalidDependString( - _("invalid use flag '%s' in conditional '%s'") % (flag, conditional) + f"invalid use flag '{flag}' in conditional '{conditional}'" ) if is_negated and flag in excludeall: @@ -672,7 +667,11 @@ def missing_white_space_check(token, pos): if flat: # In 'flat' mode, we simply merge all lists into a single large one. - if stack[level] and stack[level][-1][-1] == "?": + if ( + stack[level] + and isinstance(stack[level][-1], str) + and stack[level][-1][-1] == "?" + ): # The last token before the '(' that matches the current ')' # was a use conditional. The conditional is removed in any case. # Merge the current list if needed. @@ -884,18 +883,18 @@ def special_append(): def use_reduce( - depstr, + depstr: str, uselist=(), masklist=(), - matchall=False, + matchall: bool = False, excludeall=(), - is_src_uri=False, - eapi=None, - opconvert=False, - flat=False, + is_src_uri: bool = False, + eapi: Optional[str] = None, + opconvert: bool = False, + flat: bool = False, is_valid_flag=None, token_class=None, - matchnone=False, + matchnone: bool = False, subset=None, ): """ @@ -1453,17 +1452,170 @@ def _eval_qa_conditionals(self, use_mask, use_force): ) -class Atom(str): - """ - For compatibility with existing atom string manipulation code, this - class emulates most of the str methods that are useful with atoms. - """ +class Atom: + __slots__ = ( + "_string", + "_cp", + "_cpv", + "_version", + "_repo", + "_slot", + "_sub_slot", + "_slot_operator", + "_operator", + "_blocker_obj", + "_eapi", + "_extended_syntax", + "_build_id", + "_use", + "_without_use", + "_unevaluated_atom", + "_orig_atom", + ) + + def __str__(self) -> str: + return self._string + + def __repr__(self) -> str: + return f"Atom({self._string!r})" + + def __eq__(self, value: object) -> bool: + if isinstance(value, Atom): + return self._string == value._string + if isinstance(value, str): + return self._string == value + return super().__eq__(value) - # Distiguishes package atoms from other atom types - package = True + def __hash__(self) -> int: + return hash(self._string) - # Distiguishes soname atoms from other atom types - soname = False + @property + def category(self) -> str: + """Return the category part of the atom (e.g., 'dev-libs' from 'dev-libs/foo').""" + return self._cp.partition("/")[0] + + @property + def package_name(self) -> str: + """Return the package name part of the atom (e.g., 'foo' from 'dev-libs/foo').""" + return self._cp.split("/")[1] + + @property + def cp(self) -> str: + """Category/Package string.""" + return self._cp + + @property + def cpv(self) -> str: + """Category/Package-Version string.""" + return self._cpv + + @property + def is_versioned(self) -> bool: + """True if atom specifies a version.""" + return self._version is not None + + @property + def version(self) -> Optional[str]: + """Version string.""" + return self._version + + @property + def repo(self) -> Optional[str]: + """Repository name.""" + return self._repo + + @property + def slot(self) -> Optional[str]: + """Slot name.""" + return self._slot + + @property + def sub_slot(self) -> Optional[str]: + """Sub-slot name.""" + return self._sub_slot + + @property + def slot_operator(self) -> Optional[str]: + """Slot operator (= or *).""" + return self._slot_operator + + @property + def operator(self) -> Optional[str]: + """Version operator (=, >=, <, etc.).""" + return self._operator + + @property + def blocker(self) -> Optional["_blocker"]: + """Blocker information.""" + return self._blocker_obj + + @property + def eapi(self) -> Optional[str]: + """EAPI version.""" + return self._eapi + + @property + def extended_syntax(self) -> bool: + """Whether this atom uses extended syntax.""" + return self._extended_syntax + + @property + def build_id(self) -> Optional[int]: + """Build ID.""" + return self._build_id + + @property + def use(self) -> Optional["_use_dep"]: + """USE dependencies.""" + return self._use + + @property + def without_use(self) -> "Atom": + """Atom without USE dependencies.""" + try: + return self._without_use + except AttributeError: + pass + if self._use is None: + if ( + self._unevaluated_atom is not self + and self._unevaluated_atom.use is not None + # unevaluated_atom.use is used for IUSE checks when matching + # packages, so it must not propagate to without_use + ): + result = Atom( + str(self), + allow_wildcard=self._extended_syntax, + allow_repo=self.repo is not None, + ) + else: + result = self + else: + s = str(self) + result = Atom(s[: s.index("[")], allow_repo=self.repo is not None) + self._without_use = result + return result + + @property + def unevaluated_atom(self) -> "Atom": + """The original unevaluated atom.""" + return self._unevaluated_atom + + @property + def orig_atom(self) -> Optional["Atom"]: + """For virtual-expanded atoms, the original pre-expansion atom.""" + return self._orig_atom + + # Type discrimination properties + @property + def package(self) -> bool: + """Distinguishes package atoms from other atom types (like soname atoms).""" + return True + + @property + def soname(self) -> bool: + """Distinguishes soname atoms from other atom types.""" + return False class _blocker: __slots__ = ("overlap",) @@ -1477,9 +1629,6 @@ def __init__(self, forbid=False): def __init__(self, forbid_overlap=False): self.overlap = self._overlap(forbid=forbid_overlap) - def __new__(cls, s, *args, **kwargs): - return str.__new__(cls, s) - def __init__( self, s, @@ -1490,6 +1639,7 @@ def __init__( eapi=None, is_valid_flag=None, allow_build_id=None, + orig_atom=None, ): if isinstance(s, Atom): # This is an efficiency assertion, to ensure that the Atom @@ -1500,12 +1650,12 @@ def __init__( # Avoid TypeError from str.__init__ with PyPy. s = _unicode_decode(s) - str.__init__(s) + self._string = s eapi_attrs = _get_eapi_attrs(eapi) atom_re = _get_atom_re(eapi_attrs) - self.__dict__["eapi"] = eapi + self._eapi = eapi if eapi is not None: # If allow_repo is not set, use default from eapi if allow_repo is None: @@ -1529,8 +1679,8 @@ def __init__( blocker_prefix = s[:1] s = s[1:] else: - blocker = False - self.__dict__["blocker"] = blocker + blocker = None + self._blocker_obj = blocker m = atom_re.match(s) build_id = None extended_syntax = False @@ -1540,7 +1690,7 @@ def __init__( atom_re = _get_atom_wildcard_re(eapi_attrs) m = atom_re.match(s) if m is None: - raise InvalidAtom(self) + raise InvalidAtom(self._string) m_group = m.group if m_group("star") is not None: op = "=*" @@ -1552,15 +1702,15 @@ def __init__( op = None cpv = cp = m_group("simple") if m_group(atom_re.groupindex["simple"] + 3) is not None: - raise InvalidAtom(self) + raise InvalidAtom(self._string) if cpv.find("**") != -1: - raise InvalidAtom(self) + raise InvalidAtom(self._string) slot = m_group("slot") repo = m_group("repo") use_str = None extended_syntax = True else: - raise InvalidAtom(self) + raise InvalidAtom(self._string) elif m.group("op") is not None: m_group = m.group base = atom_re.groupindex["op"] @@ -1579,13 +1729,13 @@ def __init__( build_id = cpv_build_id[len(cpv) + 1 :] if len(build_id) > 1 and build_id[:1] == "0": # Leading zeros are not allowed. - raise InvalidAtom(self) + raise InvalidAtom(self._string) try: build_id = int(build_id) except ValueError: - raise InvalidAtom(self) + raise InvalidAtom(self._string) else: - raise InvalidAtom(self) + raise InvalidAtom(self._string) elif m.group("star") is not None: base = atom_re.groupindex["star"] op = "=*" @@ -1596,7 +1746,7 @@ def __init__( repo = m_group("repo") use_str = m_group("usedeps") if m_group(base + 3) is not None: - raise InvalidAtom(self) + raise InvalidAtom(self._string) elif m.group("simple") is not None: op = None m_group = m.group @@ -1605,77 +1755,64 @@ def __init__( repo = m_group("repo") use_str = m_group("usedeps") if m_group(atom_re.groupindex["simple"] + 2) is not None: - raise InvalidAtom(self) + raise InvalidAtom(self._string) else: raise AssertionError(_("required group not found in atom: '%s'") % self) - self.__dict__["cp"] = cp - try: - self.__dict__["cpv"] = _pkg_str(cpv) - self.__dict__["version"] = self.cpv.version - except InvalidData: - # plain cp, wildcard, or something - self.__dict__["cpv"] = cpv - self.__dict__["version"] = extended_version - self.__dict__["repo"] = repo + self._cp = cp + self._cpv = cpv + if cpv == cp: + # unversioned: simple cp or extended-syntax wildcard + self._version = extended_version + else: + # versioned: strip "cp-" prefix to get the version string + self._version = cpv[len(cp) + 1 :] + self._repo = repo if slot is None: - self.__dict__["slot"] = None - self.__dict__["sub_slot"] = None - self.__dict__["slot_operator"] = None + self._slot = None + self._sub_slot = None + self._slot_operator = None else: slot_re = _get_slot_dep_re(eapi_attrs) slot_match = slot_re.match(slot) if slot_match is None: - raise InvalidAtom(self) + raise InvalidAtom(self._string) if eapi_attrs.slot_operator: - self.__dict__["slot"] = slot_match.group("main_slot") - self.__dict__["sub_slot"] = slot_match.group("sub_slot") - self.__dict__["slot_operator"] = slot_match.group("slot_operator") + self._slot = slot_match.group("main_slot") + self._sub_slot = slot_match.group("sub_slot") + self._slot_operator = slot_match.group("slot_operator") if self.slot is not None and self.slot_operator == "*": - raise InvalidAtom(self) + raise InvalidAtom(self._string) # since both parts are optional, we could theoretically match on nothing if self.slot is None and self.slot_operator is None: - raise InvalidAtom(self) + raise InvalidAtom(self._string) else: - self.__dict__["slot"] = slot - self.__dict__["sub_slot"] = None - self.__dict__["slot_operator"] = None - self.__dict__["operator"] = op - self.__dict__["extended_syntax"] = extended_syntax - self.__dict__["build_id"] = build_id + self._slot = slot + self._sub_slot = None + self._slot_operator = None + self._operator = op + self._extended_syntax = extended_syntax + self._build_id = build_id if not (repo is None or allow_repo): - raise InvalidAtom(self) + raise InvalidAtom(self._string) if use_str is not None: if _use is not None: use = _use else: use = _use_dep(use_str[1:-1].split(","), eapi_attrs) - without_use = Atom( - blocker_prefix + m.group("without_use"), allow_repo=allow_repo - ) else: use = None - if unevaluated_atom is not None and unevaluated_atom.use is not None: - # unevaluated_atom.use is used for IUSE checks when matching - # packages, so it must not propagate to without_use - without_use = Atom( - str(self), - allow_wildcard=allow_wildcard, - allow_repo=allow_repo, - eapi=eapi, - ) - else: - without_use = self - self.__dict__["use"] = use - self.__dict__["without_use"] = without_use + self._use = use if unevaluated_atom: - self.__dict__["unevaluated_atom"] = unevaluated_atom + self._unevaluated_atom = unevaluated_atom else: - self.__dict__["unevaluated_atom"] = self + self._unevaluated_atom = self + + self._orig_atom = orig_atom if eapi is not None: if not isinstance(eapi, str): @@ -1740,19 +1877,26 @@ def slot_operator_built(self) -> bool: """ return self.slot_operator == "=" and self.sub_slot is not None + def with_cp(self, cp: str) -> "Atom": + return Atom( + str(self).replace(self.cp, cp, 1), + allow_wildcard=True, + allow_repo=self.repo is not None, + ) + @property def without_repo(self) -> "Atom": if self.repo is None: return self return Atom( - self.replace(_repo_separator + self.repo, "", 1), allow_wildcard=True + str(self).replace(_repo_separator + self.repo, "", 1), allow_wildcard=True ) @property def without_slot(self) -> "Atom": if self.slot is None and self.slot_operator is None: return self - atom = remove_slot(self) + atom = remove_slot(str(self)) if self.repo is not None: atom += _repo_separator + self.repo if self.use is not None: @@ -1760,7 +1904,7 @@ def without_slot(self) -> "Atom": return Atom(atom, allow_repo=True, allow_wildcard=True) def with_repo(self, repo) -> "Atom": - atom = remove_slot(self) + atom = remove_slot(str(self)) if self.slot is not None or self.slot_operator is not None: atom += _slot_separator if self.slot is not None: @@ -1775,18 +1919,13 @@ def with_repo(self, repo) -> "Atom": return Atom(atom, allow_repo=True, allow_wildcard=True) def with_slot(self, slot) -> "Atom": - atom = remove_slot(self) + _slot_separator + slot + atom = remove_slot(str(self)) + _slot_separator + slot if self.repo is not None: atom += _repo_separator + self.repo if self.use is not None: atom += str(self.use) return Atom(atom, allow_repo=True, allow_wildcard=True) - def __setattr__(self, name, value): - raise AttributeError( - "Atom instances are immutable", self.__class__, name, value - ) - def intersects(self, other: "Atom") -> bool: """ Atoms with different cpv, operator or use attributes cause this method @@ -1827,7 +1966,7 @@ def evaluate_conditionals(self, use: set) -> "Atom": """ if not (self.use and self.use.conditional): return self - atom = remove_slot(self) + atom = remove_slot(str(self)) if self.slot is not None or self.slot_operator is not None: atom += _slot_separator if self.slot is not None: @@ -1843,6 +1982,7 @@ def evaluate_conditionals(self, use: set) -> "Atom": unevaluated_atom=self, allow_repo=(self.repo is not None), _use=use_dep, + orig_atom=self._orig_atom, ) def violated_conditionals( @@ -1862,7 +2002,7 @@ def violated_conditionals( """ if not self.use: return self - atom = remove_slot(self) + atom = remove_slot(str(self)) if self.slot is not None or self.slot_operator is not None: atom += _slot_separator if self.slot is not None: @@ -1878,12 +2018,13 @@ def violated_conditionals( unevaluated_atom=self, allow_repo=(self.repo is not None), _use=use_dep, + orig_atom=self._orig_atom, ) def _eval_qa_conditionals(self, use_mask, use_force): if not (self.use and self.use.conditional): return self - atom = remove_slot(self) + atom = remove_slot(str(self)) if self.slot is not None or self.slot_operator is not None: atom += _slot_separator if self.slot is not None: @@ -1899,6 +2040,7 @@ def _eval_qa_conditionals(self, use_mask, use_force): unevaluated_atom=self, allow_repo=(self.repo is not None), _use=use_dep, + orig_atom=self._orig_atom, ) def __copy__(self): @@ -1910,14 +2052,12 @@ def __deepcopy__(self, memo=None): memo[id(self)] = self return self - def match(self, pkg: "_emerge.Package"): + def match(self, pkg: "Package") -> bool: """ Check if the given package instance matches this atom. @param pkg: a Package instance - @type pkg: Package @return: True if this atom matches pkg, otherwise False - @rtype: bool """ return bool(match_from_list(self, (pkg,))) @@ -2042,7 +2182,7 @@ def clear(self): self._normal.clear() -def get_operator(mydep): +def get_operator(mydep: Union[str, Atom]) -> Optional[str]: """ Return the operator used in a depstring. @@ -2052,18 +2192,24 @@ def get_operator(mydep): '>=' @param mydep: The dep string to check - @type mydep: String - @rtype: String @return: The operator. One of: '~', '=', '>', '<', '=*', '>=', or '<=' + + .. deprecated:: + Use ``Atom.operator`` directly. """ + warnings.warn( + "get_operator() is deprecated, use Atom.operator instead", + UserWarning, + stacklevel=2, + ) if not isinstance(mydep, Atom): mydep = Atom(mydep) return mydep.operator -def dep_getcpv(mydep): +def dep_getcpv(mydep: Union[str, Atom]) -> str: """ Return the category-package-version with any operators/slot specifications stripped off @@ -2072,17 +2218,23 @@ def dep_getcpv(mydep): 'media-libs/test-3.0' @param mydep: The depstring - @type mydep: String - @rtype: String @return: The depstring with the operator removed + + .. deprecated:: + Use ``Atom.cpv`` directly. """ + warnings.warn( + "dep_getcpv() is deprecated, use Atom.cpv instead", + UserWarning, + stacklevel=2, + ) if not isinstance(mydep, Atom): mydep = Atom(mydep) return mydep.cpv -def dep_getslot(mydep): +def dep_getslot(mydep: Union[str, Atom]) -> Optional[str]: """ Retrieve the slot on a depend. @@ -2091,13 +2243,10 @@ def dep_getslot(mydep): '3' @param mydep: The depstring to retrieve the slot of - @type mydep: String - @rtype: String @return: The slot """ - slot = getattr(mydep, "slot", False) - if slot is not False: - return slot + if isinstance(mydep, Atom): + return mydep.slot # remove repo_name if present mydep = mydep.split(_repo_separator)[0] @@ -2111,49 +2260,41 @@ def dep_getslot(mydep): return None -def dep_getrepo(mydep): +def dep_getrepo(mydep: Union[str, Atom]) -> Optional[str]: """ Retrieve the repo on a depend. @param mydep: The depstring to retrieve the repository of - @type mydep: String - @rtype: String @return: The repository name Example usage: >>> dep_getrepo('app-misc/test::repository') 'repository' """ - repo = getattr(mydep, "repo", False) - if repo is not False: - return repo - - metadata = getattr(mydep, "metadata", False) - if metadata: - repo = metadata.get("repository", False) - if repo is not False: - return repo + if isinstance(mydep, Atom): + return mydep.repo + # Handle string case colon = mydep.find(_repo_separator) if colon != -1: bracket = mydep.find("[", colon) if bracket == -1: return mydep[colon + 2 :] return mydep[colon + 2 : bracket] + return None -def remove_slot(mydep): +def remove_slot(mydep: Union[str, Atom]) -> str: """ Removes dep components from the right side of an atom: - slot - use - repo And repo_name from the left side. - - @type mydep: String - @rtype: String """ + if isinstance(mydep, Atom): + mydep = str(mydep) colon = mydep.find(_slot_separator) if colon != -1: mydep = mydep[:colon] @@ -2164,21 +2305,28 @@ def remove_slot(mydep): return mydep -def dep_getusedeps(depend): +def dep_getusedeps(depend: Union[str, Atom]) -> tuple[str, ...]: """ Pull a listing of USE Dependencies out of a dep atom. @param depend: The depstring to process - @type depend: String - @rtype: List - @return: List of use flags ( or [] if no flags exist ) + @return: Tuple of use flags (or () if no flags exist) Example usage: >>> dep_getusedeps('app-misc/test:3[foo,-bar]') ('foo', '-bar') + + .. deprecated:: + Use ``Atom.use.tokens`` directly. """ + warnings.warn( + "dep_getusedeps() is deprecated, use Atom.use.tokens instead", + UserWarning, + stacklevel=2, + ) + depend_str = str(depend) use_list = [] - open_bracket = depend.find("[") + open_bracket = depend_str.find("[") # -1 = failure (think c++ string::npos) comma_separated = False bracket_count = 0 @@ -2189,10 +2337,10 @@ def dep_getusedeps(depend): _("USE Dependency with more " "than one set of brackets: %s") % (depend,) ) - close_bracket = depend.find("]", open_bracket) + close_bracket = depend_str.find("]", open_bracket) if close_bracket == -1: raise InvalidAtom(_("USE Dependency with no closing bracket: %s") % depend) - use = depend[open_bracket + 1 : close_bracket] + use = depend_str[open_bracket + 1 : close_bracket] # foo[1:1] may return '' instead of None, we don't want '' in the result if not use: raise InvalidAtom(_("USE Dependency with " "no use flag ([]): %s") % depend) @@ -2221,7 +2369,7 @@ def dep_getusedeps(depend): use_list.append(use) # Find next use flag - open_bracket = depend.find("[", open_bracket + 1) + open_bracket = depend_str.find("[", open_bracket + 1) return tuple(use_list) @@ -2270,7 +2418,7 @@ def isvalidatom( return False -def isjustname(mypkg): +def isjustname(mypkg: Union[str, Atom]) -> bool: """ Checks to see if the atom is only the package name (no version parts). @@ -2281,8 +2429,6 @@ def isjustname(mypkg): True @param mypkg: The package atom to check - @param mypkg: String or Atom - @rtype: Integer @return: One of the following: 1) False if the package string is not just the package name 2) True if it is @@ -2290,17 +2436,17 @@ def isjustname(mypkg): try: if not isinstance(mypkg, Atom): mypkg = Atom(mypkg) - return mypkg == mypkg.cp + return str(mypkg) == mypkg.cp except InvalidAtom: pass - for x in mypkg.split("-")[-2:]: + for x in str(mypkg).split("-")[-2:]: if ververify(x): return False return True -def isspecific(mypkg): +def isspecific(mypkg: Union[str, Atom]) -> bool: """ Checks to see if a package is in =category/package-version or package-version format. @@ -2312,8 +2458,6 @@ def isspecific(mypkg): True @param mypkg: The package depstring to check against - @type mypkg: String - @rtype: Boolean @return: One of the following: 1) False if the package string is not specific 2) True if it is @@ -2321,7 +2465,7 @@ def isspecific(mypkg): try: if not isinstance(mypkg, Atom): mypkg = Atom(mypkg) - return mypkg != mypkg.cp + return str(mypkg) != mypkg.cp except InvalidAtom: pass @@ -2329,7 +2473,7 @@ def isspecific(mypkg): return not isjustname(mypkg) -def dep_getkey(mydep): +def dep_getkey(mydep: Union[str, Atom]) -> str: """ Return the category/package-name of a depstring. @@ -2338,8 +2482,6 @@ def dep_getkey(mydep): 'media-libs/test' @param mydep: The depstring to retrieve the category/package-name of - @type mydep: String - @rtype: String @return: The package category/package-name """ if not isinstance(mydep, Atom): @@ -2348,15 +2490,12 @@ def dep_getkey(mydep): return mydep.cp -def match_to_list(mypkg, mylist): +def match_to_list(mypkg: Union[str, Atom], mylist: list) -> list: """ Searches list for entries that matches the package. @param mypkg: The package atom to match - @type mypkg: String @param mylist: The list of package atoms to compare against - @type mylist: List - @rtype: List @return: A unique list of package atoms that match the given package atom """ matches = set() @@ -2370,12 +2509,11 @@ def match_to_list(mypkg, mylist): return result -def best_match_to_list(mypkg, mylist): +def best_match_to_list(mypkg: Union[str, Atom], mylist: list) -> Optional[Atom]: """ Returns the most specific entry that matches the package given. @param mypkg: The package atom to check - @type mypkg: String @param mylist: The list of package atoms to check against @type mylist: List @rtype: String @@ -2447,7 +2585,9 @@ def best_match_to_list(mypkg, mylist): cpv_list = [bestm.cpv, mypkg_cpv, x.cpv] def cmp_cpv(cpv1, cpv2): - return vercmp(cpv1.version, cpv2.version) + v1 = getattr(cpv1, "version", None) or cpv_getversion(str(cpv1)) + v2 = getattr(cpv2, "version", None) or cpv_getversion(str(cpv2)) + return vercmp(v1, v2) cpv_list.sort(key=cmp_sort_key(cmp_cpv)) if cpv_list[0] is mypkg_cpv or cpv_list[-1] is mypkg_cpv: @@ -2460,7 +2600,7 @@ def cmp_cpv(cpv1, cpv2): return bestm -def match_from_list(mydep, candidate_list): +def match_from_list(mydep: Union[str, Atom], candidate_list): """ Searches list for entries that matches the package. @@ -2476,12 +2616,11 @@ def match_from_list(mydep, candidate_list): if not candidate_list: return [] - if "!" == mydep[:1]: - if "!" == mydep[1:2]: - mydep = mydep[2:] - else: - mydep = mydep[1:] - if not isinstance(mydep, Atom): + if isinstance(mydep, Atom): + if mydep.blocker: + mydep = Atom(str(mydep).lstrip("!"), allow_wildcard=True, allow_repo=True) + else: + mydep = mydep.lstrip("!") mydep = Atom(mydep, allow_wildcard=True, allow_repo=True) mycpv = mydep.cpv @@ -2495,14 +2634,13 @@ def match_from_list(mydep, candidate_list): cat, pkg, ver, rev = mycpv_cps if mydep == mycpv: raise KeyError( - _("Specific key requires an operator" " (%s) (try adding an '=')") - % (mydep) + f"Specific key requires an operator ({mydep}) (try adding an '=')" ) if ver and rev: operator = mydep.operator if not operator: - writemsg(_("!!! Invalid atom: %s\n") % mydep, noiselevel=-1) + writemsg(f"!!! Invalid atom: {mydep}\n", noiselevel=-1) return [] else: operator = None @@ -2511,11 +2649,17 @@ def match_from_list(mydep, candidate_list): if mydep.extended_syntax: for x in candidate_list: - cp = getattr(x, "cp", None) - if cp is None: + if isinstance(x, Atom): + cp = x.cp + elif hasattr(x, "cp"): + # Package object + cp = x.cp + else: mysplit = catpkgsplit(remove_slot(x)) if mysplit is not None: cp = mysplit[0] + "/" + mysplit[1] + else: + cp = None if cp is None: continue @@ -2530,8 +2674,12 @@ def match_from_list(mydep, candidate_list): ver = mydep.version[1:-1] for x in candidate_list: - x_ver = getattr(x, "version", None) - if x_ver is None: + if isinstance(x, Atom): + x_ver = x.version + elif hasattr(x, "version"): + # Package object + x_ver = x.version + else: xs = catpkgsplit(remove_slot(x)) if xs is None: continue @@ -2541,11 +2689,17 @@ def match_from_list(mydep, candidate_list): elif operator is None: for x in candidate_list: - cp = getattr(x, "cp", None) - if cp is None: + if isinstance(x, Atom): + cp = x.cp + elif hasattr(x, "cp"): + # Package object + cp = x.cp + else: mysplit = catpkgsplit(remove_slot(x)) if mysplit is not None: cp = mysplit[0] + "/" + mysplit[1] + else: + cp = None if cp is None: continue @@ -2555,13 +2709,18 @@ def match_from_list(mydep, candidate_list): elif operator == "=": # Exact match for x in candidate_list: - xcpv = getattr(x, "cpv", None) - if xcpv is None: + if isinstance(x, Atom): + xcpv = x.cpv + elif hasattr(x, "cpv"): + # Package has cpv attribute - use it directly to preserve _pkg_str with build_id + xcpv = x.cpv + else: xcpv = remove_slot(x) if not cpvequal(xcpv, mycpv): continue - if build_id is not None and getattr(xcpv, "build_id", None) != build_id: - continue + if build_id is not None: + if isinstance(xcpv, _pkg_str) and xcpv.build_id != build_id: + continue mylist.append(x) elif operator == "=*": # glob match @@ -2613,8 +2772,12 @@ def match_from_list(mydep, candidate_list): elif operator == "~": # version, any revision, match for x in candidate_list: - xs = getattr(x, "cpv_split", None) - if xs is None: + if isinstance(x, _pkg_str): + xs = x.cpv_split + elif hasattr(x, "cpv"): + # Package object + xs = x.cpv.cpv_split + else: xs = catpkgsplit(remove_slot(x)) if xs is None: raise InvalidData(x) @@ -2695,7 +2858,12 @@ def match_from_list(mydep, candidate_list): candidate_list = mylist mylist = [] for x in candidate_list: - use = getattr(x, "use", None) + # Only package objects have 'use' and 'iuse' attributes + if not hasattr(x, "use"): + mylist.append(x) + continue + + use = x.use if use is not None: if mydep.unevaluated_atom.use and not x.iuse.is_valid_flag( mydep.unevaluated_atom.use.required @@ -2737,8 +2905,12 @@ def match_from_list(mydep, candidate_list): candidate_list = mylist mylist = [] for x in candidate_list: - repo = getattr(x, "repo", False) - if repo is False: + if isinstance(x, Atom): + repo = x.repo + elif hasattr(x, "repo"): + # Package object + repo = x.repo + else: repo = dep_getrepo(x) if repo is not None and repo != _unknown_repo and repo != mydep.repo: continue diff --git a/lib/portage/dep/dep_check.py b/lib/portage/dep/dep_check.py index 9089d0d7fa..98f65fa956 100644 --- a/lib/portage/dep/dep_check.py +++ b/lib/portage/dep/dep_check.py @@ -56,7 +56,7 @@ def _expand_new_virtuals( # example, atoms that appear identical may behave differently # in USE matching, depending on their unevaluated form. Also, # specially generated virtual atoms may appear identical while - # having different _orig_atom attributes. + # having different orig_atom attributes. atom_graph = mytrees.get("atom_graph") parent = mytrees.get("parent") virt_parent = mytrees.get("virt_parent") @@ -169,7 +169,7 @@ def _expand_new_virtuals( matches.reverse() for pkg in matches: # only use new-style matches - if pkg.cp.startswith("virtual/"): + if pkg.category == "virtual": pkgs.append(pkg) mychoices = [] @@ -192,10 +192,11 @@ def _expand_new_virtuals( a = [] for pkg in pkgs: - virt_atom = "=" + pkg.cpv + virt_atom_str = "=" + pkg.cpv if x.unevaluated_atom.use: - virt_atom += str(x.unevaluated_atom.use) - virt_atom = Atom(virt_atom) + virt_atom_str += str(x.unevaluated_atom.use) + # orig_atom is propagated through evaluate_conditionals + virt_atom = Atom(virt_atom_str, orig_atom=x) if parent is None: if myuse is None: virt_atom = virt_atom.evaluate_conditionals( @@ -206,12 +207,10 @@ def _expand_new_virtuals( else: virt_atom = virt_atom.evaluate_conditionals(pkg_use_enabled(parent)) else: - virt_atom = Atom(virt_atom) - - # Allow the depgraph to map this atom back to the - # original, in order to avoid distortion in places - # like display or conflict resolution code. - virt_atom.__dict__["_orig_atom"] = x + # Allow the depgraph to map this atom back to the + # original, in order to avoid distortion in places + # like display or conflict resolution code. + virt_atom = Atom(virt_atom_str, orig_atom=x) # According to GLEP 37, RDEPEND is the only dependency # type that is valid for new-style virtuals. Repoman @@ -255,7 +254,7 @@ def _expand_new_virtuals( # Replace the original atom "x" with "virt_atom" which refers # to the specific version of the virtual whose deps we're - # expanding. The virt_atom._orig_atom attribute is used + # expanding. The virt_atom.orig_atom attribute is used # by depgraph to map virt_atom back to the original atom. # We specifically exclude the original atom "x" from the # the expanded output here, since otherwise it could trigger @@ -580,7 +579,7 @@ def mydbapi_match_pkgs(atom): parent, avail_pkg ): want_update = True - if not slot_atom.cp.startswith("virtual/") and not graph_db.match_pkgs( + if slot_atom.category != "virtual" and not graph_db.match_pkgs( slot_atom ): new_slot_count += 1 @@ -602,7 +601,7 @@ def mydbapi_match_pkgs(atom): all_installed = True for atom in {Atom(atom.cp) for atom in atoms if not atom.blocker}: # New-style virtuals have zero cost to install. - if not vardb.match(atom) and not atom.startswith("virtual/"): + if not vardb.match(atom) and atom.category != "virtual": all_installed = False break all_installed_slots = False @@ -610,9 +609,7 @@ def mydbapi_match_pkgs(atom): all_installed_slots = True for slot_atom in slot_map: # New-style virtuals have zero cost to install. - if not vardb.match(slot_atom) and not slot_atom.startswith( - "virtual/" - ): + if not vardb.match(slot_atom) and slot_atom.category != "virtual": all_installed_slots = False break this_choice.all_installed_slots = all_installed_slots @@ -638,7 +635,7 @@ def mydbapi_match_pkgs(atom): all_in_graph = True for atom in atoms: # New-style virtuals have zero cost to install. - if atom.blocker or atom.cp.startswith("virtual/"): + if atom.blocker or atom.category == "virtual": continue # We check if the matched package has actually been # added to the digraph, in order to distinguish between @@ -1069,7 +1066,7 @@ def dep_wordreduce(mydeplist, mysettings, mydbapi, mode, use_cache=1): ) elif deplist[mypos] == "||": pass - elif token[:1] == "!": + elif str(token).startswith("!"): deplist[mypos] = False else: mykey = deplist[mypos].cp @@ -1097,7 +1094,7 @@ def dep_wordreduce(mydeplist, mysettings, mydbapi, mode, use_cache=1): mydep = mydbapi.match(deplist[mypos], use_cache=use_cache) if mydep is not None: tmp = len(mydep) >= 1 - if deplist[mypos][0] == "!": + if str(deplist[mypos]).startswith("!"): tmp = False deplist[mypos] = tmp else: diff --git a/lib/portage/emaint/modules/world/world.py b/lib/portage/emaint/modules/world/world.py index 46e4286210..cc4f5e4569 100644 --- a/lib/portage/emaint/modules/world/world.py +++ b/lib/portage/emaint/modules/world/world.py @@ -40,9 +40,9 @@ def _check_world(self, onProgress): maxval = len(world_atoms) if onProgress: onProgress(maxval, 0) - for i, atom in enumerate(sorted(world_atoms)): + for i, atom in enumerate(sorted(world_atoms, key=str)): if not isinstance(atom, portage.dep.Atom): - if atom.startswith(SETPREFIX): + if str(atom).startswith(SETPREFIX): s = atom[len(SETPREFIX) :] if s in sets: self.okay.append(atom) diff --git a/lib/portage/env/validators.py b/lib/portage/env/validators.py index 7b4fba756f..96832a8aad 100644 --- a/lib/portage/env/validators.py +++ b/lib/portage/env/validators.py @@ -14,7 +14,7 @@ def PackagesFileValidator(atom): Args: atom: a string representing an atom such as sys-apps/portage-2.1 """ - if atom.startswith("*") or atom.startswith("-"): + if str(atom).startswith("*") or str(atom).startswith("-"): atom = atom[1:] if not isvalidatom(atom): return False diff --git a/lib/portage/glsa.py b/lib/portage/glsa.py index 648159ad80..0359f51e5a 100644 --- a/lib/portage/glsa.py +++ b/lib/portage/glsa.py @@ -331,7 +331,7 @@ def match(atom, dbapi, match_type="default"): @rtype: list of strings @return: a list with the matching versions """ - if atom[2] == "~": + if str(atom)[2] == "~": return revisionMatch(atom, dbapi, match_type=match_type) if match_type == "default" or not hasattr(dbapi, "xmatch"): return dbapi.match(atom) diff --git a/lib/portage/package/ebuild/_config/MaskManager.py b/lib/portage/package/ebuild/_config/MaskManager.py index 67fbb042c2..9837213796 100644 --- a/lib/portage/package/ebuild/_config/MaskManager.py +++ b/lib/portage/package/ebuild/_config/MaskManager.py @@ -71,14 +71,14 @@ def grab_pmask(loc, repo_config): lines = [] repo_lines = grab_pmask(repo.location, repo) removals = frozenset( - line[0][1:] for line in repo_lines if line[0][:1] == "-" + str(line[0])[1:] for line in repo_lines if str(line[0])[:1] == "-" ) matched_removals = set() for master in repo.masters: master_lines = grab_pmask(master.location, master) for line in master_lines: - if line[0] in removals: - matched_removals.add(line[0]) + if str(line[0]) in removals: + matched_removals.add(str(line[0])) # Since we don't stack masters recursively, there aren't any # atoms earlier in the stack to be matched by negative atoms in # master_lines. Also, repo_lines may contain negative atoms diff --git a/lib/portage/repository/config.py b/lib/portage/repository/config.py index 45a4676d9b..205364fd28 100644 --- a/lib/portage/repository/config.py +++ b/lib/portage/repository/config.py @@ -262,7 +262,7 @@ def __init__(self, name, repo_opts, local_config=True): writemsg( "\n!!! The following atoms are invalid in %s attribute for " "repo [%s] (only package names and slot atoms allowed):\n" - "\n %s\n" % (opt, name, "\n ".join(bad_atoms)) + "\n %s\n" % (opt, name, "\n ".join(str(a) for a in bad_atoms)) ) for a in bad_atoms: usepkg_atoms.remove(a) @@ -278,7 +278,7 @@ def __init__(self, name, repo_opts, local_config=True): writemsg( "\n!!! The following atoms appear in both the usepkg-exclude " "usepkg-include lists for repo [%s]:\n" - "\n %s\n" % (name, "\n ".join(conflicted_atoms)) + "\n %s\n" % (name, "\n ".join(str(a) for a in conflicted_atoms)) ) for a in conflicted_atoms: self.usepkg_exclude.remove(a) @@ -1468,7 +1468,7 @@ def config_string(self): elif key in _str_tuple_keys: config_string += "{} = {}\n".format( key.replace("_", "-"), - " ".join(getattr(repo, key)), + " ".join(str(x) for x in getattr(repo, key)), ) elif key in _repo_config_tuple_keys: config_string += "{} = {}\n".format( diff --git a/lib/portage/tests/resolver/ResolverPlayground.py b/lib/portage/tests/resolver/ResolverPlayground.py index bc31d66294..6b66d039ef 100644 --- a/lib/portage/tests/resolver/ResolverPlayground.py +++ b/lib/portage/tests/resolver/ResolverPlayground.py @@ -937,10 +937,10 @@ def compare_with_result(self, result): if got: new_got = [] for cpv in got: - if cpv[:1] == "!": + if str(cpv)[:1] == "!": new_got.append(cpv) continue - new_got.append(cpv.split(_repo_separator)[0]) + new_got.append(str(cpv).split(_repo_separator)[0]) got = new_got if expected: new_expected = [] diff --git a/lib/portage/update.py b/lib/portage/update.py index 6fbcc77477..234bcc7cbb 100644 --- a/lib/portage/update.py +++ b/lib/portage/update.py @@ -17,7 +17,6 @@ from portage.exception import DirectoryNotFound, InvalidAtom, PortageException from portage.localization import _ - ignored_dbentries = ("CONTENTS", "environment.bz2") @@ -97,7 +96,7 @@ def update_dbentry(update_cmd, mycontent, eapi=None, parent=None): if atom.slot_operator is not None: slot_part += atom.slot_operator - split_content[i] = atom.with_slot(slot_part) + split_content[i] = str(atom.with_slot(slot_part)) modified = True if modified: diff --git a/lib/portage/util/__init__.py b/lib/portage/util/__init__.py index 2eeedf2972..3637c33dcb 100644 --- a/lib/portage/util/__init__.py +++ b/lib/portage/util/__init__.py @@ -336,19 +336,20 @@ def stack_lists( if incremental: if token == "-*": new_list.clear() - elif token[:1] == "-": + elif str(token).startswith("-"): matched = False if ignore_repo and not "::" in token: # Let -cat/pkg remove cat/pkg::repo. to_be_removed = [] - token_slice = token[1:] + token_str = str(token) + token_slice = token_str[1:] for atom in new_list: atom_without_repo = atom if atom.repo is not None: # Atom.without_repo instantiates a new Atom, # which is unnecessary here, so use string # replacement instead. - atom_without_repo = atom.replace( + atom_without_repo = str(atom).replace( "::" + atom.repo, "", 1 ) if atom_without_repo == token_slice: @@ -359,7 +360,7 @@ def stack_lists( new_list.pop(atom) else: try: - new_list.pop(token[1:]) + new_list.pop(str(token)[1:]) matched = True except KeyError: pass diff --git a/lib/portage/versions.py b/lib/portage/versions.py index cd91067ac5..f5e3376365 100644 --- a/lib/portage/versions.py +++ b/lib/portage/versions.py @@ -19,9 +19,12 @@ import typing import warnings from functools import lru_cache -from typing import Any, Optional, Union +from typing import TYPE_CHECKING, Any, Optional, Union from collections.abc import Sequence +if TYPE_CHECKING: + from portage.dep import Atom + from portage import _unicode_decode from portage.eapi import _eapi_attrs, _get_eapi_attrs from portage.exception import InvalidData @@ -603,8 +606,11 @@ def cmp_cpv(cpv1: Any, cpv2: Any) -> int: return cmp_sort_key(cmp_cpv) -def catsplit(mydep: str) -> list[str]: - return mydep.split("/", 1) +# it would be better to fix the call sites, +# but most of them are completely untyped, +# and themselves use something like `str | Atom | OtherMonstrosity` +def catsplit(mydep: "Union[str, Atom]") -> list[str]: + return str(mydep).split("/", 1) def best(mymatches: Sequence[Any], eapi: Any = None) -> Any: