{"kind":"task","effective_mode":"full","benchmark":{"kind":"benchmark","effective_mode":"full","slug":"longbench-v2","formal_name":"LongBench v2","introduction":"LongBench v2 evaluates deep understanding and reasoning over long contexts through multiple-choice questions. Its official description lists 503 questions spanning tasks such as single-document and multi-document QA and code-repository understanding.","introduction_ja":"","introduction_en":"","category":"Category not supplied","task_count":null,"acquisition_status":"Acquisition status not supplied","official_url":"https://huggingface.co/datasets/zai-org/LongBench-v2","indexing_mode":"noindex","profile":{"resources":[],"task_format":"","scoring":"","metric":"","size":"","answer_access":"","license":"","citation":"","maintainer":"","released":"","why_hard":"","related":[]}},"task_id":"b81583b2-b3ff-529c-8f1a-e53a73278c60","task_key":"train--66fa2734bb02136c067c627a","task_revision_id":"3","upstream_id":"66fa2734bb02136c067c627a","short_description":"In the urls method of the Channel class, what does not determine the final URL…","config":"","split":"train","body":"{\"choice_A\":\"Handling of subdirs: If subdirs is not provided (i.e., None), the method assigns it the default value of **[./]**, ensuring a default list of subdirectories is used.\",\"choice_B\":\"Unknown Channel Handling: If the channel’s canonical name is UNKNOWN_CHANNEL, the method calls the urls method of the DEFAULTS_CHANNEL_NAME and does not proceed further with the current logic.\",\"choice_C\":\"with_credentials Option: When the with_credentials argument is True, the URL will contain the authentication token and, if available, the user authentication details (self.auth) are added to the base URL.\",\"choice_D\":\"Platform Yielding: If self.platform is defined and not equal to \\\"noarch\\\", the method yields both self.platform and \\\"noarch\\\" as platform subdirectories. Otherwise, it yields the provided subdirs.\",\"context\":\"from __future__ import annotations\\n\\nimport logging\\nimport re\\nimport sys\\nfrom functools import lru_cache, wraps\\nfrom os import environ\\nfrom os.path import abspath, basename, dirname, isfile, join\\nfrom pathlib import Path\\nfrom shutil import which\\n\\nfrom . import CondaError\\nfrom .auxlib.compat import Utf8NamedTemporaryFile, shlex_split_unicode\\nfrom .common.compat import isiterable, on_win\\nfrom .common.path import win_path_to_unix\\nfrom .common.url import path_to_url\\nfrom .deprecations import deprecated\\n\\nlog = logging.getLogger(__name__)\\n\\n\\ndef path_identity(path):\\n    \\\"\\\"\\\"Used as a dummy path converter where no conversion necessary\\\"\\\"\\\"\\n    return path\\n\\n\\ndef unix_path_to_win(path, root_prefix=\\\"\\\"):\\n    \\\"\\\"\\\"Convert a path or :-separated string of paths into a Windows representation\\n\\n    Does not add cygdrive.  If you need that, set root_prefix to \\\"/cygdrive\\\"\\n    \\\"\\\"\\\"\\n    if len(path) > 1 and (\\\";\\\" in path or (path[1] == \\\":\\\" and path.count(\\\":\\\") == 1)):\\n        # already a windows path\\n        return path.replace(\\\"/\\\", \\\"\\\\\\\\\\\")\\n    path_re = root_prefix + r'(/[a-zA-Z]/(?:(?![:\\\\s]/)[^:*?\\\"<>])*)'\\n\\n    def _translation(found_path):\\n        group = found_path.group(0)\\n        return \\\"{}:{}\\\".format(\\n            group[len(root_prefix) + 1],\\n            group[len(root_prefix) + 2 :].replace(\\\"/\\\", \\\"\\\\\\\\\\\"),\\n        )\\n\\n    translation = re.sub(path_re, _translation, path)\\n    translation = re.sub(\\n        \\\":([a-zA-Z]):\\\\\\\\\\\\\\\\\\\", lambda match: \\\";\\\" + match.group(0)[1] + \\\":\\\\\\\\\\\", translation\\n    )\\n    return translation\\n\\n\\n@deprecated(\\n    \\\"25.3\\\",\\n    \\\"25.9\\\",\\n    addendum=\\\"Use `conda.common.path.win_path_to_unix` instead.\\\",\\n)\\ndef win_path_to_cygwin(path):\\n    return win_path_to_unix(path, \\\"/cygdrive\\\")\\n\\n\\n@deprecated(\\n    \\\"25.3\\\",\\n    \\\"25.9\\\",\\n    addendum=\\\"Use `conda.utils.unix_path_to_win` instead.\\\",\\n)\\ndef cygwin_path_to_win(path):\\n    return unix_path_to_win(path, \\\"/cygdrive\\\")\\n\\n\\n@deprecated(\\\"25.3\\\", \\\"25.9\\\", addendum=\\\"Unused.\\\")\\ndef translate_stream(stream, translator):\\n    return \\\"\\\\n\\\".join(translator(line) for line in stream.split(\\\"\\\\n\\\"))\\n\\n\\ndef human_bytes(n):\\n    \\\"\\\"\\\"\\n    Return the number of bytes n in more human readable form.\\n\\n    Examples:\\n        >>> human_bytes(42)\\n        '42 B'\\n        >>> human_bytes(1042)\\n        '1 KB'\\n        >>> human_bytes(10004242)\\n        '9.5 MB'\\n        >>> human_bytes(100000004242)\\n        '93.13 GB'\\n    \\\"\\\"\\\"\\n    if n < 1024:\\n        return \\\"%d B\\\" % n\\n    k = n / 1024\\n    if k < 1024:\\n        return \\\"%d KB\\\" % round(k)\\n    m = k / 1024\\n    if m < 1024:\\n        return f\\\"{m:.1f} MB\\\"\\n    g = m / 1024\\n    return f\\\"{g:.2f} GB\\\"\\n\\n\\n# TODO: this should be done in a more extensible way\\n#     (like files for each shell, with some registration mechanism.)\\n\\n# defaults for unix shells.  Note: missing \\\"exe\\\" entry, which should be set to\\n#    either an executable on PATH, or a full path to an executable for a shell\\n_UNIX_SHELL_BASE = dict(\\n    binpath=\\\"/bin/\\\",  # mind the trailing slash.\\n    echo=\\\"echo\\\",\\n    env_script_suffix=\\\".sh\\\",\\n    nul=\\\"2>/dev/null\\\",\\n    path_from=path_identity,\\n    path_to=path_identity,\\n    pathsep=\\\":\\\",\\n    printdefaultenv=\\\"echo $CONDA_DEFAULT_ENV\\\",\\n    printpath=\\\"echo $PATH\\\",\\n    printps1=\\\"echo $CONDA_PROMPT_MODIFIER\\\",\\n    promptvar=\\\"PS1\\\",\\n    sep=\\\"/\\\",\\n    set_var=\\\"export \\\",\\n    shell_args=[\\\"-l\\\", \\\"-c\\\"],\\n    shell_suffix=\\\"\\\",\\n    slash_convert=(\\\"\\\\\\\\\\\", \\\"/\\\"),\\n    source_setup=\\\"source\\\",\\n    test_echo_extra=\\\"\\\",\\n    var_format=\\\"${}\\\",\\n)\\n\\ndeprecated.constant(\\n    \\\"25.3\\\",\\n    \\\"25.9\\\",\\n    \\\"unix_shell_base\\\",\\n    _UNIX_SHELL_BASE,\\n    addendum=\\\"Use `conda.activate` instead.\\\",\\n)\\n\\n_MSYS2_SHELL_BASE = dict(\\n    _UNIX_SHELL_BASE,\\n    path_from=unix_path_to_win,\\n    path_to=win_path_to_unix,\\n    binpath=\\\"/bin/\\\",  # mind the trailing slash.\\n    printpath=\\\"python -c \\\\\\\"import os; print(';'.join(os.environ['PATH'].split(';')[1:]))\\\\\\\" | cygpath --path -f -\\\",  # NOQA\\n)\\n\\ndeprecated.constant(\\n    \\\"25.3\\\",\\n    \\\"25.9\\\",\\n    \\\"msys2_shell_base\\\",\\n    _MSYS2_SHELL_BASE,\\n    addendum=\\\"Use `conda.activate` instead.\\\",\\n)\\n\\nif on_win:\\n    _SHELLS = {\\n        # \\\"powershell.exe\\\": dict(\\n        #    echo=\\\"echo\\\",\\n        #    test_echo_extra=\\\" .\\\",\\n        #    var_format=\\\"${var}\\\",\\n        #    binpath=\\\"/bin/\\\",  # mind the trailing slash.\\n        #    source_setup=\\\"source\\\",\\n        #    nul='2>/dev/null',\\n        #    set_var='export ',\\n        #    shell_suffix=\\\".ps\\\",\\n        #    env_script_suffix=\\\".ps\\\",\\n        #    printps1='echo $PS1',\\n        #    printdefaultenv='echo $CONDA_DEFAULT_ENV',\\n        #    printpath=\\\"echo %PATH%\\\",\\n        #    exe=\\\"powershell.exe\\\",\\n        #    path_from=path_identity,\\n        #    path_to=path_identity,\\n        #    slash_convert = (\\\"/\\\", \\\"\\\\\\\\\\\"),\\n        # ),\\n        \\\"cmd.exe\\\": dict(\\n            echo=\\\"@echo\\\",\\n            var_format=\\\"%{}%\\\",\\n            binpath=\\\"\\\\\\\\Scripts\\\\\\\\\\\",  # mind the trailing slash.\\n            source_setup=\\\"call\\\",\\n            test_echo_extra=\\\"\\\",\\n            nul=\\\"1>NUL 2>&1\\\",\\n            set_var=\\\"set \\\",\\n            shell_suffix=\\\".bat\\\",\\n            env_script_suffix=\\\".bat\\\",\\n            printps1=\\\"@echo %PROMPT%\\\",\\n            promptvar=\\\"PROMPT\\\",\\n            # parens mismatched intentionally.  See http://stackoverflow.com/questions/20691060/how-do-i-echo-a-blank-empty-line-to-the-console-from-a-windows-batch-file # NOQA\\n            printdefaultenv='IF NOT \\\"%CONDA_DEFAULT_ENV%\\\" == \\\"\\\" (\\\\n'\\n            \\\"echo %CONDA_DEFAULT_ENV% ) ELSE (\\\\n\\\"\\n            \\\"echo()\\\",\\n            printpath=\\\"@echo %PATH%\\\",\\n            exe=\\\"cmd.exe\\\",\\n            shell_args=[\\\"/d\\\", \\\"/c\\\"],\\n            path_from=path_identity,\\n            path_to=path_identity,\\n            slash_convert=(\\\"/\\\", \\\"\\\\\\\\\\\"),\\n            sep=\\\"\\\\\\\\\\\",\\n            pathsep=\\\";\\\",\\n        ),\\n        \\\"cygwin\\\": dict(\\n            _UNIX_SHELL_BASE,\\n            exe=\\\"bash.exe\\\",\\n            binpath=\\\"/Scripts/\\\",  # mind the trailing slash.\\n            path_from=cygwin_path_to_win,\\n            path_to=win_path_to_cygwin,\\n        ),\\n        # bash is whichever bash is on PATH.  If using Cygwin, you should use the cygwin\\n        #    entry instead.  The only major difference is that it handle's cygwin's /cygdrive\\n        #    filesystem root.\\n        \\\"bash.exe\\\": dict(\\n            _MSYS2_SHELL_BASE,\\n            exe=\\\"bash.exe\\\",\\n        ),\\n        \\\"bash\\\": dict(\\n            _MSYS2_SHELL_BASE,\\n            exe=\\\"bash\\\",\\n        ),\\n        \\\"sh.exe\\\": dict(\\n            _MSYS2_SHELL_BASE,\\n            exe=\\\"sh.exe\\\",\\n        ),\\n        \\\"zsh.exe\\\": dict(\\n            _MSYS2_SHELL_BASE,\\n            exe=\\\"zsh.exe\\\",\\n        ),\\n        \\\"zsh\\\": dict(\\n            _MSYS2_SHELL_BASE,\\n            exe=\\\"zsh\\\",\\n        ),\\n    }\\n\\nelse:\\n    _SHELLS = {\\n        \\\"bash\\\": dict(\\n            _UNIX_SHELL_BASE,\\n            exe=\\\"bash\\\",\\n        ),\\n        \\\"dash\\\": dict(\\n            _UNIX_SHELL_BASE,\\n            exe=\\\"dash\\\",\\n            source_setup=\\\".\\\",\\n        ),\\n        \\\"zsh\\\": dict(\\n            _UNIX_SHELL_BASE,\\n            exe=\\\"zsh\\\",\\n        ),\\n        \\\"fish\\\": dict(\\n            _UNIX_SHELL_BASE,\\n            exe=\\\"fish\\\",\\n            pathsep=\\\" \\\",\\n        ),\\n    }\\n\\ndeprecated.constant(\\n    \\\"25.3\\\",\\n    \\\"25.9\\\",\\n    \\\"shells\\\",\\n    _SHELLS,\\n    addendum=\\\"Use `conda.activate` instead.\\\",\\n)\\n\\n\\n# ##########################################\\n# put back because of conda build\\n# ##########################################\\n\\nurlpath = url_path = path_to_url\\n\\n\\n@lru_cache(maxsize=None)\\ndef sys_prefix_unfollowed():\\n    \\\"\\\"\\\"Since conda is installed into non-root environments as a symlink only\\n    and because sys.prefix follows symlinks, this function can be used to\\n    get the 'unfollowed' sys.prefix.\\n\\n    This value is usually the same as the prefix of the environment into\\n    which conda has been symlinked. An example of when this is necessary\\n    is when conda looks for external sub-commands in find_commands.py\\n    \\\"\\\"\\\"\\n    try:\\n        frame = next(iter(sys._current_frames().values()))\\n        while frame.f_back:\\n            frame = frame.f_back\\n        code = frame.f_code\\n        filename = code.co_filename\\n        unfollowed = dirname(dirname(filename))\\n    except Exception:\\n        return sys.prefix\\n    return unfollowed\\n\\n\\ndef quote_for_shell(*arguments):\\n    \\\"\\\"\\\"Properly quote arguments for command line passing.\\n\\n    For POSIX uses `shlex.join`, for Windows uses a custom implementation to properly escape\\n    metacharacters.\\n\\n    :param arguments: Arguments to quote.\\n    :type arguments: list of str\\n    :return: Quoted arguments.\\n    :rtype: str\\n    \\\"\\\"\\\"\\n    # [backport] Support passing in a list of strings or args of string.\\n    if len(arguments) == 1 and isiterable(arguments[0]):\\n        arguments = arguments[0]\\n\\n    return _args_join(arguments)\\n\\n\\nif on_win:\\n    # https://ss64.com/nt/syntax-esc.html\\n    # https://docs.microsoft.com/en-us/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way\\n\\n    _RE_UNSAFE = re.compile(r'[\\\"%\\\\s^<>&|]')\\n    _RE_DBL = re.compile(r'([\\\"%])')\\n\\n    def _args_join(args):\\n        \\\"\\\"\\\"Return a shell-escaped string from *args*.\\\"\\\"\\\"\\n\\n        def quote(s):\\n            # derived from shlex.quote\\n            if not s:\\n                return '\\\"\\\"'\\n            # if any unsafe chars are present we must quote\\n            if not _RE_UNSAFE.search(s):\\n                return s\\n            # double escape (\\\" -> \\\"\\\")\\n            s = _RE_DBL.sub(r\\\"\\\\1\\\\1\\\", s)\\n            # quote entire string\\n            return f'\\\"{s}\\\"'\\n\\n        return \\\" \\\".join(quote(arg) for arg in args)\\n\\nelse:\\n    try:\\n        from shlex import join as _args_join\\n    except ImportError:\\n        # [backport] Python <3.8\\n        def _args_join(args):\\n            \\\"\\\"\\\"Return a shell-escaped string from *args*.\\\"\\\"\\\"\\n            from shlex import quote\\n\\n            return \\\" \\\".join(quote(arg) for arg in args)\\n\\n\\n# Ensures arguments are a tuple or a list. Strings are converted\\n# by shlex_split_unicode() which is bad; we warn about it or else\\n# we assert (and fix the code).\\ndef massage_arguments(arguments, errors=\\\"assert\\\"):\\n    # For reference and in-case anything breaks ..\\n    # .. one of the places (run_command in conda_env/utils.py) this\\n    # gets called from used to do this too:\\n    #\\n    #    def escape_for_winpath(p):\\n    #        return p.replace('\\\\\\\\', '\\\\\\\\\\\\\\\\')\\n    #\\n    #    if not isinstance(arguments, list):\\n    #        arguments = list(map(escape_for_winpath, arguments))\\n\\n    if isinstance(arguments, str):\\n        if errors == \\\"assert\\\":\\n            # This should be something like 'conda programming bug', it is an assert\\n            assert False, \\\"Please ensure arguments are not strings\\\"\\n        else:\\n            arguments = shlex_split_unicode(arguments)\\n            log.warning(\\n                \\\"Please ensure arguments is not a string; \\\"\\n                \\\"used `shlex_split_unicode()` on it\\\"\\n            )\\n\\n    if not isiterable(arguments):\\n        arguments = (arguments,)\\n\\n    assert not any(\\n        [isiterable(arg) for arg in arguments]\\n    ), \\\"Individual arguments must not be iterable\\\"  # NOQA\\n    arguments = list(arguments)\\n\\n    return arguments\\n\\n\\ndef wrap_subprocess_call(\\n    root_prefix,\\n    prefix,\\n    dev_mode,\\n    debug_wrapper_scripts,\\n    arguments,\\n    use_system_tmp_path=False,\\n):\\n    arguments = massage_arguments(arguments)\\n    if not use_system_tmp_path:\\n        tmp_prefix = abspath(join(prefix, \\\".tmp\\\"))\\n    else:\\n        tmp_prefix = None\\n    script_caller = None\\n    multiline = False\\n    if len(arguments) == 1 and \\\"\\\\n\\\" in arguments[0]:\\n        multiline = True\\n    if on_win:\\n        comspec = get_comspec()  # fail early with KeyError if undefined\\n        if dev_mode:\\n            from . import CONDA_PACKAGE_ROOT\\n\\n            conda_bat = join(CONDA_PACKAGE_ROOT, \\\"shell\\\", \\\"condabin\\\", \\\"conda.bat\\\")\\n        else:\\n            conda_bat = environ.get(\\n                \\\"CONDA_BAT\\\", abspath(join(root_prefix, \\\"condabin\\\", \\\"conda.bat\\\"))\\n            )\\n        with Utf8NamedTemporaryFile(\\n            mode=\\\"w\\\", prefix=tmp_prefix, suffix=\\\".bat\\\", delete=False\\n        ) as fh:\\n            silencer = \\\"\\\" if debug_wrapper_scripts else \\\"@\\\"\\n            fh.write(f\\\"{silencer}ECHO OFF\\\\n\\\")\\n            fh.write(f\\\"{silencer}SET PYTHONIOENCODING=utf-8\\\\n\\\")\\n            fh.write(f\\\"{silencer}SET PYTHONUTF8=1\\\\n\\\")\\n            fh.write(\\n                f'{silencer}FOR /F \\\"tokens=2 delims=:.\\\" %%A in (\\\\'chcp\\\\') do for %%B in (%%A) do set \\\"_CONDA_OLD_CHCP=%%B\\\"\\\\n'  # noqa\\n            )\\n            fh.write(f\\\"{silencer}chcp 65001 > NUL\\\\n\\\")\\n            if dev_mode:\\n                from . import CONDA_SOURCE_ROOT\\n\\n                fh.write(f\\\"{silencer}SET CONDA_DEV=1\\\\n\\\")\\n                # In dev mode, conda is really:\\n                # 'python -m conda'\\n                # *with* PYTHONPATH set.\\n                fh.write(f\\\"{silencer}SET PYTHONPATH={CONDA_SOURCE_ROOT}\\\\n\\\")\\n                fh.write(f\\\"{silencer}SET CONDA_EXE={sys.executable}\\\\n\\\")\\n                fh.write(f\\\"{silencer}SET _CE_M=-m\\\\n\\\")\\n                fh.write(f\\\"{silencer}SET _CE_CONDA=conda\\\\n\\\")\\n            if debug_wrapper_scripts:\\n                fh.write(\\\"echo *** environment before *** 1>&2\\\\n\\\")\\n                fh.write(\\\"SET 1>&2\\\\n\\\")\\n            # Not sure there is any point in backing this up, nothing will get called with it reset\\n            # after all!\\n            # fh.write(\\\"@FOR /F \\\\\\\"tokens=100\\\\\\\" %%F IN ('chcp') DO @SET CONDA_OLD_CHCP=%%F\\\\n\\\")\\n            # fh.write('@chcp 65001>NUL\\\\n')\\n            fh.write(f'{silencer}CALL \\\"{conda_bat}\\\" activate \\\"{prefix}\\\"\\\\n')\\n            fh.write(f\\\"{silencer}IF %ERRORLEVEL% NEQ 0 EXIT /b %ERRORLEVEL%\\\\n\\\")\\n            if debug_wrapper_scripts:\\n                fh.write(\\\"echo *** environment after *** 1>&2\\\\n\\\")\\n                fh.write(\\\"SET 1>&2\\\\n\\\")\\n            if multiline:\\n                # No point silencing the first line. If that's what's wanted then\\n                # it needs doing for each line and the caller may as well do that.\\n                fh.write(f\\\"{arguments[0]}\\\\n\\\")\\n            else:\\n                assert not any(\\\"\\\\n\\\" in arg for arg in arguments), (\\n                    \\\"Support for scripts where arguments contain newlines not implemented.\\\\n\\\"\\n                    \\\".. requires writing the script to an external file and knowing how to \\\"\\n                    \\\"transform the command-line (e.g. `python -c args` => `python file`) \\\"\\n                    \\\"in a tool dependent way, or attempting something like:\\\\n\\\"\\n                    \\\".. https://stackoverflow.com/a/15032476 (adds unacceptable escaping\\\"\\n                    \\\"requirements)\\\"\\n                )\\n                fh.write(f\\\"{silencer}{quote_for_shell(*arguments)}\\\\n\\\")\\n            fh.write(f\\\"{silencer}IF %ERRORLEVEL% NEQ 0 EXIT /b %ERRORLEVEL%\\\\n\\\")\\n            fh.write(f\\\"{silencer}chcp %_CONDA_OLD_CHCP%>NUL\\\\n\\\")\\n            script_caller = fh.name\\n        command_args = [comspec, \\\"/d\\\", \\\"/c\\\", script_caller]\\n    else:\\n        shell_path = which(\\\"bash\\\") or which(\\\"sh\\\")\\n        if shell_path is None:\\n            raise Exception(\\\"No compatible shell found!\\\")\\n\\n        # During tests, we sometimes like to have a temp env with e.g. an old python in it\\n        # and have it run tests against the very latest development sources. For that to\\n        # work we need extra smarts here, we want it to be instead:\\n        if dev_mode:\\n            conda_exe = [abspath(join(root_prefix, \\\"bin\\\", \\\"python\\\")), \\\"-m\\\", \\\"conda\\\"]\\n            dev_arg = \\\"--dev\\\"\\n            dev_args = [dev_arg]\\n        else:\\n            conda_exe = [\\n                environ.get(\\\"CONDA_EXE\\\", abspath(join(root_prefix, \\\"bin\\\", \\\"conda\\\")))\\n            ]\\n            dev_arg = \\\"\\\"\\n            dev_args = []\\n        with Utf8NamedTemporaryFile(mode=\\\"w\\\", prefix=tmp_prefix, delete=False) as fh:\\n            if dev_mode:\\n                from . import CONDA_SOURCE_ROOT\\n\\n                fh.write(\\\">&2 export PYTHONPATH=\\\" + CONDA_SOURCE_ROOT + \\\"\\\\n\\\")\\n            hook_quoted = quote_for_shell(*conda_exe, \\\"shell.posix\\\", \\\"hook\\\", *dev_args)\\n            if debug_wrapper_scripts:\\n                fh.write(\\\">&2 echo '*** environment before ***'\\\\n>&2 env\\\\n\\\")\\n                fh.write(f'>&2 echo \\\"$({hook_quoted})\\\"\\\\n')\\n            fh.write(f'eval \\\"$({hook_quoted})\\\"\\\\n')\\n            fh.write(f\\\"conda activate {dev_arg} {quote_for_shell(prefix)}\\\\n\\\")\\n            if debug_wrapper_scripts:\\n                fh.write(\\\">&2 echo '*** environment after ***'\\\\n>&2 env\\\\n\\\")\\n            if multiline:\\n                # The ' '.join() is pointless since mutliline is only True when there's 1 arg\\n                # still, if that were to change this would prevent breakage.\\n                fh.write(\\\"{}\\\\n\\\".format(\\\" \\\".join(arguments)))\\n            else:\\n                fh.write(f\\\"{quote_for_shell(*arguments)}\\\\n\\\")\\n            script_caller = fh.name\\n        if debug_wrapper_scripts:\\n            command_args = [shell_path, \\\"-x\\\", script_caller]\\n        else:\\n            command_args = [shell_path, script_caller]\\n\\n    return script_caller, command_args\\n\\n\\ndef get_comspec():\\n    \\\"\\\"\\\"Returns COMSPEC from envvars.\\n\\n    Ensures COMSPEC envvar is set to cmd.exe, if not attempt to find it.\\n\\n    :raises KeyError: COMSPEC is undefined and cannot be found.\\n    :returns: COMSPEC value.\\n    :rtype: str\\n    \\\"\\\"\\\"\\n    if basename(environ.get(\\\"COMSPEC\\\", \\\"\\\")).lower() != \\\"cmd.exe\\\":\\n        for comspec in (\\n            # %SystemRoot%\\\\System32\\\\cmd.exe\\n            environ.get(\\\"SystemRoot\\\")\\n            and join(environ[\\\"SystemRoot\\\"], \\\"System32\\\", \\\"cmd.exe\\\"),\\n            # %windir%\\\\System32\\\\cmd.exe\\n            environ.get(\\\"windir\\\") and join(environ[\\\"windir\\\"], \\\"System32\\\", \\\"cmd.exe\\\"),\\n        ):\\n            if comspec and isfile(comspec):\\n                environ[\\\"COMSPEC\\\"] = comspec\\n                break\\n        else:\\n            log.warning(\\n                \\\"cmd.exe could not be found. Looked in SystemRoot and windir env vars.\\\\n\\\"\\n            )\\n\\n    # fails with KeyError if still undefined\\n    return environ[\\\"COMSPEC\\\"]\\n\\n\\ndef ensure_dir_exists(func):\\n    \\\"\\\"\\\"\\n    Ensures that the directory exists for functions returning\\n    a Path object containing a directory\\n    \\\"\\\"\\\"\\n\\n    @wraps(func)\\n    def wrapper(*args, **kwargs):\\n        result = func(*args, **kwargs)\\n\\n        if isinstance(result, Path):\\n            try:\\n                result.mkdir(parents=True, exist_ok=True)\\n            except OSError as exc:\\n                raise CondaError(\\n                    \\\"Error encountered while attempting to create cache directory.\\\"\\n                    f\\\"\\\\n  Directory: {result}\\\"\\n                    f\\\"\\\\n  Exception: {exc}\\\"\\n                )\\n\\n        return result\\n\\n    return wrapper\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Define the instruction set (constants) for conda operations.\\\"\\\"\\\"\\n\\nfrom logging import getLogger\\nfrom os.path import isfile, join\\n\\nfrom .core.link import UnlinkLinkTransaction\\nfrom .core.package_cache_data import ProgressiveFetchExtract\\nfrom .deprecations import deprecated\\nfrom .exceptions import CondaFileIOError\\nfrom .gateways.disk.link import islink\\n\\nlog = getLogger(__name__)\\n\\n# op codes\\nCHECK_FETCH = \\\"CHECK_FETCH\\\"\\nFETCH = \\\"FETCH\\\"\\nCHECK_EXTRACT = \\\"CHECK_EXTRACT\\\"\\nEXTRACT = \\\"EXTRACT\\\"\\nRM_EXTRACTED = \\\"RM_EXTRACTED\\\"\\nRM_FETCHED = \\\"RM_FETCHED\\\"\\ndeprecated.constant(\\\"24.9\\\", \\\"25.3\\\", \\\"PREFIX\\\", \\\"PREFIX\\\")\\nPRINT = \\\"PRINT\\\"\\nPROGRESS = \\\"PROGRESS\\\"\\nSYMLINK_CONDA = \\\"SYMLINK_CONDA\\\"\\nUNLINK = \\\"UNLINK\\\"\\nLINK = \\\"LINK\\\"\\nUNLINKLINKTRANSACTION = \\\"UNLINKLINKTRANSACTION\\\"\\nPROGRESSIVEFETCHEXTRACT = \\\"PROGRESSIVEFETCHEXTRACT\\\"\\n\\n\\nPROGRESS_COMMANDS = {EXTRACT, RM_EXTRACTED}\\nACTION_CODES = (\\n    CHECK_FETCH,\\n    FETCH,\\n    CHECK_EXTRACT,\\n    EXTRACT,\\n    UNLINK,\\n    LINK,\\n    SYMLINK_CONDA,\\n    RM_EXTRACTED,\\n    RM_FETCHED,\\n)\\n\\n\\ndef PRINT_CMD(state, arg):  # pragma: no cover\\n    if arg.startswith((\\\"Unlinking packages\\\", \\\"Linking packages\\\")):\\n        return\\n    getLogger(\\\"conda.stdout.verbose\\\").info(arg)\\n\\n\\ndef FETCH_CMD(state, package_cache_entry):\\n    raise NotImplementedError()\\n\\n\\ndef EXTRACT_CMD(state, arg):\\n    raise NotImplementedError()\\n\\n\\ndef PROGRESSIVEFETCHEXTRACT_CMD(state, progressive_fetch_extract):  # pragma: no cover\\n    assert isinstance(progressive_fetch_extract, ProgressiveFetchExtract)\\n    progressive_fetch_extract.execute()\\n\\n\\ndef UNLINKLINKTRANSACTION_CMD(state, arg):  # pragma: no cover\\n    unlink_link_transaction = arg\\n    assert isinstance(unlink_link_transaction, UnlinkLinkTransaction)\\n    unlink_link_transaction.execute()\\n\\n\\ndef check_files_in_package(source_dir, files):\\n    for f in files:\\n        source_file = join(source_dir, f)\\n        if isfile(source_file) or islink(source_file):\\n            return True\\n        else:\\n            raise CondaFileIOError(source_file, f\\\"File {f} does not exist in tarball\\\")\\n\\n\\n# Map instruction to command (a python function)\\ncommands = {\\n    PRINT: PRINT_CMD,\\n    FETCH: FETCH_CMD,\\n    PROGRESS: lambda x, y: None,\\n    EXTRACT: EXTRACT_CMD,\\n    RM_EXTRACTED: lambda x, y: None,\\n    RM_FETCHED: lambda x, y: None,\\n    UNLINK: None,\\n    LINK: None,\\n    SYMLINK_CONDA: lambda x, y: None,\\n    UNLINKLINKTRANSACTION: UNLINKLINKTRANSACTION_CMD,\\n    PROGRESSIVEFETCHEXTRACT: PROGRESSIVEFETCHEXTRACT_CMD,\\n}\\n\\n\\nOP_ORDER = (\\n    RM_FETCHED,\\n    FETCH,\\n    RM_EXTRACTED,\\n    EXTRACT,\\n    UNLINK,\\n    LINK,\\n)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"\\nHandle the planning of installs and their execution.\\n\\nNOTE:\\n    conda.install uses canonical package names in its interface functions,\\n    whereas conda.resolve uses package filenames, as those are used as index\\n    keys.  We try to keep fixes to this \\\"impedance mismatch\\\" local to this\\n    module.\\n\\\"\\\"\\\"\\n\\nimport sys\\nfrom collections import defaultdict\\nfrom logging import getLogger\\n\\nfrom boltons.setutils import IndexedSet\\n\\nfrom .base.constants import DEFAULTS_CHANNEL_NAME, UNKNOWN_CHANNEL\\nfrom .base.context import context, reset_context\\nfrom .common.constants import TRACE\\nfrom .common.io import dashlist, env_vars, time_recorder\\nfrom .common.iterators import groupby_to_dict as groupby\\nfrom .core.index import LAST_CHANNEL_URLS\\nfrom .core.link import PrefixSetup, UnlinkLinkTransaction\\nfrom .deprecations import deprecated\\nfrom .instructions import FETCH, LINK, SYMLINK_CONDA, UNLINK\\nfrom .models.channel import Channel, prioritize_channels\\nfrom .models.dist import Dist\\nfrom .models.enums import LinkType\\nfrom .models.match_spec import MatchSpec\\nfrom .models.records import PackageRecord\\nfrom .models.version import normalized_version\\nfrom .utils import human_bytes\\n\\nlog = getLogger(__name__)\\n\\n\\n@deprecated(\\\"24.9\\\", \\\"25.3\\\", addendum=\\\"Unused.\\\")\\ndef print_dists(dists_extras):\\n    fmt = \\\"    %-27s|%17s\\\"\\n    print(fmt % (\\\"package\\\", \\\"build\\\"))\\n    print(fmt % (\\\"-\\\" * 27, \\\"-\\\" * 17))\\n    for prec, extra in dists_extras:\\n        line = fmt % (prec.name + \\\"-\\\" + prec.version, prec.build)\\n        if extra:\\n            line += extra\\n        print(line)\\n\\n\\n@deprecated(\\\"24.9\\\", \\\"25.3\\\", addendum=\\\"Unused.\\\")\\ndef display_actions(\\n    actions, index, show_channel_urls=None, specs_to_remove=(), specs_to_add=()\\n):\\n    prefix = actions.get(\\\"PREFIX\\\")\\n    builder = [\\\"\\\", \\\"## Package Plan ##\\\\n\\\"]\\n    if prefix:\\n        builder.append(f\\\"  environment location: {prefix}\\\")\\n        builder.append(\\\"\\\")\\n    if specs_to_remove:\\n        builder.append(\\n            f\\\"  removed specs: {dashlist(sorted(str(s) for s in specs_to_remove), indent=4)}\\\"\\n        )\\n        builder.append(\\\"\\\")\\n    if specs_to_add:\\n        builder.append(\\n            f\\\"  added / updated specs: {dashlist(sorted(str(s) for s in specs_to_add), indent=4)}\\\"\\n        )\\n        builder.append(\\\"\\\")\\n    print(\\\"\\\\n\\\".join(builder))\\n\\n    if show_channel_urls is None:\\n        show_channel_urls = context.show_channel_urls\\n\\n    def channel_str(rec):\\n        if rec.get(\\\"schannel\\\"):\\n            return rec[\\\"schannel\\\"]\\n        if rec.get(\\\"url\\\"):\\n            return Channel(rec[\\\"url\\\"]).canonical_name\\n        if rec.get(\\\"channel\\\"):\\n            return Channel(rec[\\\"channel\\\"]).canonical_name\\n        return UNKNOWN_CHANNEL\\n\\n    def channel_filt(s):\\n        if show_channel_urls is False:\\n            return \\\"\\\"\\n        if show_channel_urls is None and s == DEFAULTS_CHANNEL_NAME:\\n            return \\\"\\\"\\n        return s\\n\\n    if actions.get(FETCH):\\n        print(\\\"\\\\nThe following packages will be downloaded:\\\\n\\\")\\n\\n        disp_lst = []\\n        for prec in actions[FETCH]:\\n            assert isinstance(prec, PackageRecord)\\n            extra = \\\"%15s\\\" % human_bytes(prec[\\\"size\\\"])\\n            schannel = channel_filt(prec.channel.canonical_name)\\n            if schannel:\\n                extra += \\\"  \\\" + schannel\\n            disp_lst.append((prec, extra))\\n        print_dists(disp_lst)\\n\\n        if index and len(actions[FETCH]) > 1:\\n            num_bytes = sum(prec[\\\"size\\\"] for prec in actions[FETCH])\\n            print(\\\" \\\" * 4 + \\\"-\\\" * 60)\\n            print(\\\" \\\" * 43 + \\\"Total: %14s\\\" % human_bytes(num_bytes))\\n\\n    # package -> [oldver-oldbuild, newver-newbuild]\\n    packages = defaultdict(lambda: list((\\\"\\\", \\\"\\\")))\\n    features = defaultdict(lambda: list((\\\"\\\", \\\"\\\")))\\n    channels = defaultdict(lambda: list((\\\"\\\", \\\"\\\")))\\n    records = defaultdict(lambda: list((None, None)))\\n    linktypes = {}\\n\\n    for prec in actions.get(LINK, []):\\n        assert isinstance(prec, PackageRecord)\\n        pkg = prec[\\\"name\\\"]\\n        channels[pkg][1] = channel_str(prec)\\n        packages[pkg][1] = prec[\\\"version\\\"] + \\\"-\\\" + prec[\\\"build\\\"]\\n        records[pkg][1] = prec\\n        # TODO: this is a lie; may have to give this report after\\n        # UnlinkLinkTransaction.verify()\\n        linktypes[pkg] = LinkType.hardlink\\n        features[pkg][1] = \\\",\\\".join(prec.get(\\\"features\\\") or ())\\n    for prec in actions.get(UNLINK, []):\\n        assert isinstance(prec, PackageRecord)\\n        pkg = prec[\\\"name\\\"]\\n        channels[pkg][0] = channel_str(prec)\\n        packages[pkg][0] = prec[\\\"version\\\"] + \\\"-\\\" + prec[\\\"build\\\"]\\n        records[pkg][0] = prec\\n        features[pkg][0] = \\\",\\\".join(prec.get(\\\"features\\\") or ())\\n\\n    new = {p for p in packages if not packages[p][0]}\\n    removed = {p for p in packages if not packages[p][1]}\\n    # New packages are actually listed in the left-hand column,\\n    # so let's move them over there\\n    for pkg in new:\\n        for var in (packages, features, channels, records):\\n            var[pkg] = var[pkg][::-1]\\n\\n    updated = set()\\n    downgraded = set()\\n    channeled = set()\\n    oldfmt = {}\\n    newfmt = {}\\n    empty = True\\n    if packages:\\n        empty = False\\n        maxpkg = max(len(p) for p in packages) + 1\\n        maxoldver = max(len(p[0]) for p in packages.values())\\n        maxnewver = max(len(p[1]) for p in packages.values())\\n        maxoldfeatures = max(len(p[0]) for p in features.values())\\n        maxnewfeatures = max(len(p[1]) for p in features.values())\\n        maxoldchannels = max(len(channel_filt(p[0])) for p in channels.values())\\n        maxnewchannels = max(len(channel_filt(p[1])) for p in channels.values())\\n        for pkg in packages:\\n            # That's right. I'm using old-style string formatting to generate a\\n            # string with new-style string formatting.\\n            oldfmt[pkg] = f\\\"{{pkg:<{maxpkg}}} {{vers[0]:<{maxoldver}}}\\\"\\n            if maxoldchannels:\\n                oldfmt[pkg] += f\\\" {{channels[0]:<{maxoldchannels}}}\\\"\\n            if features[pkg][0]:\\n                oldfmt[pkg] += f\\\" [{{features[0]:<{maxoldfeatures}}}]\\\"\\n\\n            lt = LinkType(linktypes.get(pkg, LinkType.hardlink))\\n            lt = \\\"\\\" if lt == LinkType.hardlink else (f\\\" ({lt})\\\")\\n            if pkg in removed or pkg in new:\\n                oldfmt[pkg] += lt\\n                continue\\n\\n            newfmt[pkg] = f\\\"{{vers[1]:<{maxnewver}}}\\\"\\n            if maxnewchannels:\\n                newfmt[pkg] += f\\\" {{channels[1]:<{maxnewchannels}}}\\\"\\n            if features[pkg][1]:\\n                newfmt[pkg] += f\\\" [{{features[1]:<{maxnewfeatures}}}]\\\"\\n            newfmt[pkg] += lt\\n\\n            P0 = records[pkg][0]\\n            P1 = records[pkg][1]\\n            pri0 = P0.get(\\\"priority\\\")\\n            pri1 = P1.get(\\\"priority\\\")\\n            if pri0 is None or pri1 is None:\\n                pri0 = pri1 = 1\\n            try:\\n                if str(P1.version) == \\\"custom\\\":\\n                    newver = str(P0.version) != \\\"custom\\\"\\n                    oldver = not newver\\n                else:\\n                    # <= here means that unchanged packages will be put in updated\\n                    N0 = normalized_version(P0.version)\\n                    N1 = normalized_version(P1.version)\\n                    newver = N0 < N1\\n                    oldver = N0 > N1\\n            except TypeError:\\n                newver = P0.version < P1.version\\n                oldver = P0.version > P1.version\\n            oldbld = P0.build_number > P1.build_number\\n            newbld = P0.build_number < P1.build_number\\n            if (\\n                context.channel_priority\\n                and pri1 < pri0\\n                and (oldver or not newver and not newbld)\\n            ):\\n                channeled.add(pkg)\\n            elif newver:\\n                updated.add(pkg)\\n            elif pri1 < pri0 and (oldver or not newver and oldbld):\\n                channeled.add(pkg)\\n            elif oldver:\\n                downgraded.add(pkg)\\n            elif not oldbld:\\n                updated.add(pkg)\\n            else:\\n                downgraded.add(pkg)\\n\\n    arrow = \\\" --> \\\"\\n    lead = \\\" \\\" * 4\\n\\n    def format(s, pkg):\\n        chans = [channel_filt(c) for c in channels[pkg]]\\n        return lead + s.format(\\n            pkg=pkg + \\\":\\\", vers=packages[pkg], channels=chans, features=features[pkg]\\n        )\\n\\n    if new:\\n        print(\\\"\\\\nThe following NEW packages will be INSTALLED:\\\\n\\\")\\n        for pkg in sorted(new):\\n            # New packages have been moved to the \\\"old\\\" column for display\\n            print(format(oldfmt[pkg], pkg))\\n\\n    if removed:\\n        print(\\\"\\\\nThe following packages will be REMOVED:\\\\n\\\")\\n        for pkg in sorted(removed):\\n            print(format(oldfmt[pkg], pkg))\\n\\n    if updated:\\n        print(\\\"\\\\nThe following packages will be UPDATED:\\\\n\\\")\\n        for pkg in sorted(updated):\\n            print(format(oldfmt[pkg] + arrow + newfmt[pkg], pkg))\\n\\n    if channeled:\\n        print(\\n            \\\"\\\\nThe following packages will be SUPERSEDED by a higher-priority channel:\\\\n\\\"\\n        )\\n        for pkg in sorted(channeled):\\n            print(format(oldfmt[pkg] + arrow + newfmt[pkg], pkg))\\n\\n    if downgraded:\\n        print(\\\"\\\\nThe following packages will be DOWNGRADED:\\\\n\\\")\\n        for pkg in sorted(downgraded):\\n            print(format(oldfmt[pkg] + arrow + newfmt[pkg], pkg))\\n\\n    if empty and actions.get(SYMLINK_CONDA):\\n        print(\\\"\\\\nThe following empty environments will be CREATED:\\\\n\\\")\\n        print(actions[\\\"PREFIX\\\"])\\n\\n    print()\\n\\n\\n@deprecated(\\\"24.9\\\", \\\"25.3\\\", addendum=\\\"Unused.\\\")\\ndef add_unlink(actions, dist):\\n    assert isinstance(dist, Dist)\\n    if UNLINK not in actions:\\n        actions[UNLINK] = []\\n    actions[UNLINK].append(dist)\\n\\n\\n@deprecated(\\\"24.9\\\", \\\"25.3\\\", addendum=\\\"Unused.\\\")\\ndef add_defaults_to_specs(r, linked, specs, update=False, prefix=None):\\n    return\\n\\n\\n@deprecated(\\n    \\\"24.9\\\",\\n    \\\"25.3\\\",\\n    addendum=\\\"Use `conda.misc._get_best_prec_match` instead.\\\",\\n)\\ndef _get_best_prec_match(precs):\\n    from .misc import _get_best_prec_match\\n\\n    return _get_best_prec_match(precs)\\n\\n\\n@deprecated(\\n    \\\"24.9\\\",\\n    \\\"25.3\\\",\\n    addendum=\\\"Use `conda.cli.install.revert_actions` instead.\\\",\\n)\\ndef revert_actions(prefix, revision=-1, index=None):\\n    from .cli.install import revert_actions\\n\\n    return revert_actions(prefix, revision, index)\\n\\n\\n@deprecated(\\\"24.9\\\", \\\"25.3\\\", addendum=\\\"Unused.\\\")\\n@time_recorder(\\\"execute_actions\\\")\\ndef execute_actions(actions, index, verbose=False):  # pragma: no cover\\n    plan = _plan_from_actions(actions, index)\\n    execute_instructions(plan, index, verbose)\\n\\n\\n@deprecated(\\\"24.9\\\", \\\"25.3\\\", addendum=\\\"Unused.\\\")\\ndef _plan_from_actions(actions, index):  # pragma: no cover\\n    from .instructions import ACTION_CODES, PREFIX, PRINT, PROGRESS, PROGRESS_COMMANDS\\n\\n    if \\\"op_order\\\" in actions and actions[\\\"op_order\\\"]:\\n        op_order = actions[\\\"op_order\\\"]\\n    else:\\n        op_order = ACTION_CODES\\n\\n    assert PREFIX in actions and actions[PREFIX]\\n    prefix = actions[PREFIX]\\n    plan = [(\\\"PREFIX\\\", f\\\"{prefix}\\\")]\\n\\n    unlink_link_transaction = actions.get(\\\"UNLINKLINKTRANSACTION\\\")\\n    if unlink_link_transaction:\\n        raise RuntimeError()\\n        # progressive_fetch_extract = actions.get('PROGRESSIVEFETCHEXTRACT')\\n        # if progressive_fetch_extract:\\n        #     plan.append((PROGRESSIVEFETCHEXTRACT, progressive_fetch_extract))\\n        # plan.append((UNLINKLINKTRANSACTION, unlink_link_transaction))\\n        # return plan\\n\\n    axn = actions.get(\\\"ACTION\\\") or None\\n    specs = actions.get(\\\"SPECS\\\", [])\\n\\n    log.debug(f\\\"Adding plans for operations: {op_order}\\\")\\n    for op in op_order:\\n        if op not in actions:\\n            log.log(TRACE, f\\\"action {op} not in actions\\\")\\n            continue\\n        if not actions[op]:\\n            log.log(TRACE, f\\\"action {op} has None value\\\")\\n            continue\\n        if \\\"_\\\" not in op:\\n            plan.append((PRINT, f\\\"{op.capitalize()}ing packages ...\\\"))\\n        elif op.startswith(\\\"RM_\\\"):\\n            plan.append(\\n                (PRINT, f\\\"Pruning {op[3:].lower()} packages from the cache ...\\\")\\n            )\\n        if op in PROGRESS_COMMANDS:\\n            plan.append((PROGRESS, \\\"%d\\\" % len(actions[op])))\\n        for arg in actions[op]:\\n            log.debug(f\\\"appending value {arg} for action {op}\\\")\\n            plan.append((op, arg))\\n\\n    plan = _inject_UNLINKLINKTRANSACTION(plan, index, prefix, axn, specs)\\n\\n    return plan\\n\\n\\n@deprecated(\\\"24.9\\\", \\\"25.3\\\", addendum=\\\"Unused.\\\")\\ndef _inject_UNLINKLINKTRANSACTION(plan, index, prefix, axn, specs):  # pragma: no cover\\n    from os.path import isdir\\n\\n    from .core.package_cache_data import ProgressiveFetchExtract\\n    from .instructions import (\\n        LINK,\\n        PROGRESSIVEFETCHEXTRACT,\\n        UNLINK,\\n        UNLINKLINKTRANSACTION,\\n    )\\n    from .models.dist import Dist\\n\\n    # this is only used for conda-build at this point\\n    first_unlink_link_idx = next(\\n        (q for q, p in enumerate(plan) if p[0] in (UNLINK, LINK)), -1\\n    )\\n    if first_unlink_link_idx >= 0:\\n        grouped_instructions = groupby(lambda x: x[0], plan)\\n        unlink_dists = tuple(Dist(d[1]) for d in grouped_instructions.get(UNLINK, ()))\\n        link_dists = tuple(Dist(d[1]) for d in grouped_instructions.get(LINK, ()))\\n        unlink_dists, link_dists = _handle_menuinst(unlink_dists, link_dists)\\n\\n        if isdir(prefix):\\n            unlink_precs = tuple(index[d] for d in unlink_dists)\\n        else:\\n            # there's nothing to unlink in an environment that doesn't exist\\n            # this is a hack for what appears to be a logic error in conda-build\\n            # caught in tests/test_subpackages.py::test_subpackage_recipes[python_test_dep]\\n            unlink_precs = ()\\n        link_precs = tuple(index[d] for d in link_dists)\\n\\n        pfe = ProgressiveFetchExtract(link_precs)\\n        pfe.prepare()\\n\\n        stp = PrefixSetup(prefix, unlink_precs, link_precs, (), specs, ())\\n        plan.insert(\\n            first_unlink_link_idx, (UNLINKLINKTRANSACTION, UnlinkLinkTransaction(stp))\\n        )\\n        plan.insert(first_unlink_link_idx, (PROGRESSIVEFETCHEXTRACT, pfe))\\n    elif axn in (\\\"INSTALL\\\", \\\"CREATE\\\"):\\n        plan.insert(0, (UNLINKLINKTRANSACTION, (prefix, (), (), (), specs)))\\n\\n    return plan\\n\\n\\n@deprecated(\\\"24.9\\\", \\\"25.3\\\", addendum=\\\"Unused.\\\")\\ndef _handle_menuinst(unlink_dists, link_dists):  # pragma: no cover\\n    # Always link/unlink menuinst first/last in case a subsequent\\n    # package tries to import it to create/remove a shortcut\\n\\n    # unlink\\n    menuinst_idx = next(\\n        (q for q, d in enumerate(unlink_dists) if d.name == \\\"menuinst\\\"), None\\n    )\\n    if menuinst_idx is not None:\\n        unlink_dists = (\\n            *unlink_dists[:menuinst_idx],\\n            *unlink_dists[menuinst_idx + 1 :],\\n            *unlink_dists[menuinst_idx : menuinst_idx + 1],\\n        )\\n\\n    # link\\n    menuinst_idx = next(\\n        (q for q, d in enumerate(link_dists) if d.name == \\\"menuinst\\\"), None\\n    )\\n    if menuinst_idx is not None:\\n        link_dists = (\\n            *link_dists[menuinst_idx : menuinst_idx + 1],\\n            *link_dists[:menuinst_idx],\\n            *link_dists[menuinst_idx + 1 :],\\n        )\\n\\n    return unlink_dists, link_dists\\n\\n\\n@deprecated(\\\"24.9\\\", \\\"25.3\\\", addendum=\\\"Unused.\\\")\\n@time_recorder(\\\"install_actions\\\")\\ndef install_actions(\\n    prefix,\\n    index,\\n    specs,\\n    force=False,\\n    only_names=None,\\n    always_copy=False,\\n    pinned=True,\\n    update_deps=True,\\n    prune=False,\\n    channel_priority_map=None,\\n    is_update=False,\\n    minimal_hint=False,\\n):  # pragma: no cover\\n    # this is for conda-build\\n    with env_vars(\\n        {\\n            \\\"CONDA_ALLOW_NON_CHANNEL_URLS\\\": \\\"true\\\",\\n            \\\"CONDA_SOLVER_IGNORE_TIMESTAMPS\\\": \\\"false\\\",\\n        },\\n        reset_context,\\n    ):\\n        from os.path import basename\\n\\n        from .models.channel import Channel\\n        from .models.dist import Dist\\n\\n        if channel_priority_map:\\n            channel_names = IndexedSet(\\n                Channel(url).canonical_name for url in channel_priority_map\\n            )\\n            channels = IndexedSet(Channel(cn) for cn in channel_names)\\n            subdirs = IndexedSet(basename(url) for url in channel_priority_map)\\n        else:\\n            # a hack for when conda-build calls this function without giving channel_priority_map\\n            if LAST_CHANNEL_URLS:\\n                channel_priority_map = prioritize_channels(LAST_CHANNEL_URLS)\\n                channels = IndexedSet(Channel(url) for url in channel_priority_map)\\n                subdirs = (\\n                    IndexedSet(\\n                        subdir for subdir in (c.subdir for c in channels) if subdir\\n                    )\\n                    or context.subdirs\\n                )\\n            else:\\n                channels = subdirs = None\\n\\n        specs = tuple(MatchSpec(spec) for spec in specs)\\n\\n        from .core.prefix_data import PrefixData\\n\\n        PrefixData._cache_.clear()\\n\\n        solver_backend = context.plugin_manager.get_cached_solver_backend()\\n        solver = solver_backend(prefix, channels, subdirs, specs_to_add=specs)\\n        if index:\\n            solver._index = {prec: prec for prec in index.values()}\\n        txn = solver.solve_for_transaction(prune=prune, ignore_pinned=not pinned)\\n        prefix_setup = txn.prefix_setups[prefix]\\n        actions = get_blank_actions(prefix)\\n        actions[\\\"UNLINK\\\"].extend(Dist(prec) for prec in prefix_setup.unlink_precs)\\n        actions[\\\"LINK\\\"].extend(Dist(prec) for prec in prefix_setup.link_precs)\\n        return actions\\n\\n\\n@deprecated(\\\"24.9\\\", \\\"25.3\\\", addendum=\\\"Unused.\\\")\\ndef get_blank_actions(prefix):  # pragma: no cover\\n    from collections import defaultdict\\n\\n    from .instructions import (\\n        CHECK_EXTRACT,\\n        CHECK_FETCH,\\n        EXTRACT,\\n        FETCH,\\n        LINK,\\n        PREFIX,\\n        RM_EXTRACTED,\\n        RM_FETCHED,\\n        SYMLINK_CONDA,\\n        UNLINK,\\n    )\\n\\n    actions = defaultdict(list)\\n    actions[PREFIX] = prefix\\n    actions[\\\"op_order\\\"] = (\\n        CHECK_FETCH,\\n        RM_FETCHED,\\n        FETCH,\\n        CHECK_EXTRACT,\\n        RM_EXTRACTED,\\n        EXTRACT,\\n        UNLINK,\\n        LINK,\\n        SYMLINK_CONDA,\\n    )\\n    return actions\\n\\n\\n@deprecated(\\\"24.9\\\", \\\"25.3\\\")\\n@time_recorder(\\\"execute_plan\\\")\\ndef execute_plan(old_plan, index=None, verbose=False):  # pragma: no cover\\n    \\\"\\\"\\\"Deprecated: This should `conda.instructions.execute_instructions` instead.\\\"\\\"\\\"\\n    plan = _update_old_plan(old_plan)\\n    execute_instructions(plan, index, verbose)\\n\\n\\n@deprecated(\\\"24.9\\\", \\\"25.3\\\")\\ndef execute_instructions(\\n    plan, index=None, verbose=False, _commands=None\\n):  # pragma: no cover\\n    \\\"\\\"\\\"Execute the instructions in the plan\\n    :param plan: A list of (instruction, arg) tuples\\n    :param index: The meta-data index\\n    :param verbose: verbose output\\n    :param _commands: (For testing only) dict mapping an instruction to executable if None\\n    then the default commands will be used\\n    \\\"\\\"\\\"\\n    from .base.context import context\\n    from .instructions import PROGRESS_COMMANDS, commands\\n    from .models.dist import Dist\\n\\n    if _commands is None:\\n        _commands = commands\\n\\n    log.debug(\\\"executing plan %s\\\", plan)\\n\\n    state = {\\\"i\\\": None, \\\"prefix\\\": context.root_prefix, \\\"index\\\": index}\\n\\n    for instruction, arg in plan:\\n        log.debug(\\\" %s(%r)\\\", instruction, arg)\\n\\n        if state[\\\"i\\\"] is not None and instruction in PROGRESS_COMMANDS:\\n            state[\\\"i\\\"] += 1\\n            getLogger(\\\"progress.update\\\").info((Dist(arg).dist_name, state[\\\"i\\\"] - 1))\\n        cmd = _commands[instruction]\\n\\n        if callable(cmd):\\n            cmd(state, arg)\\n\\n        if (\\n            state[\\\"i\\\"] is not None\\n            and instruction in PROGRESS_COMMANDS\\n            and state[\\\"maxval\\\"] == state[\\\"i\\\"]\\n        ):\\n            state[\\\"i\\\"] = None\\n            getLogger(\\\"progress.stop\\\").info(None)\\n\\n\\n@deprecated(\\\"24.9\\\", \\\"25.3\\\")\\ndef _update_old_plan(old_plan):  # pragma: no cover\\n    \\\"\\\"\\\"\\n    Update an old plan object to work with\\n    `conda.instructions.execute_instructions`\\n    \\\"\\\"\\\"\\n    plan = []\\n    for line in old_plan:\\n        if line.startswith(\\\"#\\\"):\\n            continue\\n        if \\\" \\\" not in line:\\n            from .exceptions import ArgumentError\\n\\n            raise ArgumentError(f\\\"The instruction {line!r} takes at least one argument\\\")\\n\\n        instruction, arg = line.split(\\\" \\\", 1)\\n        plan.append((instruction, arg))\\n    return plan\\n\\n\\nif __name__ == \\\"__main__\\\":\\n    # for testing new revert_actions() only\\n    from pprint import pprint\\n\\n    from .cli.install import revert_actions\\n\\n    deprecated.topic(\\\"24.9\\\", \\\"25.3\\\", topic=\\\"`conda.plan` as an entrypoint\\\")\\n\\n    pprint(dict(revert_actions(sys.prefix, int(sys.argv[1]))))\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Conda as a module entry point.\\\"\\\"\\\"\\n\\nimport sys\\n\\nfrom .cli import main\\n\\nsys.exit(main())\\n\\n\\n# file generated by setuptools_scm\\n# don't change, don't track in version control\\nTYPE_CHECKING = False\\nif TYPE_CHECKING:\\n    from typing import Tuple, Union\\n    VERSION_TUPLE = Tuple[Union[int, str], ...]\\nelse:\\n    VERSION_TUPLE = object\\n\\nversion: str\\n__version__: str\\n__version_tuple__: VERSION_TUPLE\\nversion_tuple: VERSION_TUPLE\\n\\n__version__ = version = '24.7.1'\\n__version_tuple__ = version_tuple = (24, 7, 1)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Low-level SAT solver wrapper/interface for the classic solver.\\n\\nSee conda.core.solver.Solver for the high-level API.\\n\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport copy\\nimport itertools\\nfrom collections import defaultdict, deque\\nfrom functools import lru_cache\\nfrom logging import DEBUG, getLogger\\n\\nfrom tqdm import tqdm\\n\\nfrom .auxlib.decorators import memoizemethod\\nfrom .base.constants import MAX_CHANNEL_PRIORITY, ChannelPriority, SatSolverChoice\\nfrom .base.context import context\\nfrom .common.compat import on_win\\nfrom .common.io import dashlist, time_recorder\\nfrom .common.iterators import groupby_to_dict as groupby\\nfrom .common.logic import (\\n    TRUE,\\n    Clauses,\\n    PycoSatSolver,\\n    PyCryptoSatSolver,\\n    PySatSolver,\\n    minimal_unsatisfiable_subset,\\n)\\nfrom .common.toposort import toposort\\nfrom .exceptions import (\\n    CondaDependencyError,\\n    InvalidSpec,\\n    ResolvePackageNotFound,\\n    UnsatisfiableError,\\n)\\nfrom .models.channel import Channel, MultiChannel\\nfrom .models.enums import NoarchType, PackageType\\nfrom .models.match_spec import MatchSpec\\nfrom .models.records import PackageRecord\\nfrom .models.version import VersionOrder\\n\\ntry:\\n    from frozendict import frozendict\\nexcept ImportError:\\n    from ._vendor.frozendict import FrozenOrderedDict as frozendict\\n\\nlog = getLogger(__name__)\\nstdoutlog = getLogger(\\\"conda.stdoutlog\\\")\\n\\n# used in conda build\\nUnsatisfiable = UnsatisfiableError\\nResolvePackageNotFound = ResolvePackageNotFound\\n\\n_sat_solvers = {\\n    SatSolverChoice.PYCOSAT: PycoSatSolver,\\n    SatSolverChoice.PYCRYPTOSAT: PyCryptoSatSolver,\\n    SatSolverChoice.PYSAT: PySatSolver,\\n}\\n\\n\\n@lru_cache(maxsize=None)\\ndef _get_sat_solver_cls(sat_solver_choice=SatSolverChoice.PYCOSAT):\\n    def try_out_solver(sat_solver):\\n        c = Clauses(sat_solver=sat_solver)\\n        required = {c.new_var(), c.new_var()}\\n        c.Require(c.And, *required)\\n        solution = set(c.sat())\\n        if not required.issubset(solution):\\n            raise RuntimeError(f\\\"Wrong SAT solution: {solution}. Required: {required}\\\")\\n\\n    sat_solver = _sat_solvers[sat_solver_choice]\\n    try:\\n        try_out_solver(sat_solver)\\n    except Exception as e:\\n        log.warning(\\n            \\\"Could not run SAT solver through interface '%s'.\\\", sat_solver_choice\\n        )\\n        log.debug(\\\"SAT interface error due to: %s\\\", e, exc_info=True)\\n    else:\\n        log.debug(\\\"Using SAT solver interface '%s'.\\\", sat_solver_choice)\\n        return sat_solver\\n    for sat_solver in _sat_solvers.values():\\n        try:\\n            try_out_solver(sat_solver)\\n        except Exception as e:\\n            log.debug(\\n                \\\"Attempted SAT interface '%s' but unavailable due to: %s\\\",\\n                sat_solver_choice,\\n                e,\\n            )\\n        else:\\n            log.debug(\\\"Falling back to SAT solver interface '%s'.\\\", sat_solver_choice)\\n            return sat_solver\\n    raise CondaDependencyError(\\n        \\\"Cannot run solver. No functioning SAT implementations available.\\\"\\n    )\\n\\n\\ndef exactness_and_number_of_deps(resolve_obj, ms):\\n    \\\"\\\"\\\"Sorting key to emphasize packages that have more strict\\n    requirements. More strict means the reduced index can be reduced\\n    more, so we want to consider these more constrained deps earlier in\\n    reducing the index.\\n    \\\"\\\"\\\"\\n    if ms.strictness == 3:\\n        prec = resolve_obj.find_matches(ms)\\n        value = 3\\n        if prec:\\n            for dep in prec[0].depends:\\n                value += MatchSpec(dep).strictness\\n    else:\\n        value = ms.strictness\\n    return value\\n\\n\\nclass Resolve:\\n    def __init__(self, index, processed=False, channels=()):\\n        self.index = index\\n\\n        self.channels = channels\\n        self._channel_priorities_map = (\\n            self._make_channel_priorities(channels) if channels else {}\\n        )\\n        self._channel_priority = context.channel_priority\\n        self._solver_ignore_timestamps = context.solver_ignore_timestamps\\n\\n        groups = groupby(lambda x: x.name, index.values())\\n        trackers = defaultdict(list)\\n\\n        for name in groups:\\n            unmanageable_precs = [prec for prec in groups[name] if prec.is_unmanageable]\\n            if unmanageable_precs:\\n                log.debug(\\\"restricting to unmanageable packages: %s\\\", name)\\n                groups[name] = unmanageable_precs\\n            tf_precs = (prec for prec in groups[name] if prec.track_features)\\n            for prec in tf_precs:\\n                for feature_name in prec.track_features:\\n                    trackers[feature_name].append(prec)\\n\\n        self.groups = groups  # dict[package_name, list[PackageRecord]]\\n        self.trackers = trackers  # dict[track_feature, set[PackageRecord]]\\n        self._cached_find_matches = {}  # dict[MatchSpec, set[PackageRecord]]\\n        self.ms_depends_ = {}  # dict[PackageRecord, list[MatchSpec]]\\n        self._reduced_index_cache = {}\\n        self._pool_cache = {}\\n        self._strict_channel_cache = {}\\n\\n        self._system_precs = {\\n            _\\n            for _ in index\\n            if (\\n                hasattr(_, \\\"package_type\\\")\\n                and _.package_type == PackageType.VIRTUAL_SYSTEM\\n            )\\n        }\\n\\n        # sorting these in reverse order is effectively prioritizing\\n        # constraint behavior from newer packages. It is applying broadening\\n        # reduction based on the latest packages, which may reduce the space\\n        # more, because more modern packages utilize constraints in more sane\\n        # ways (for example, using run_exports in conda-build 3)\\n        for name, group in self.groups.items():\\n            self.groups[name] = sorted(group, key=self.version_key, reverse=True)\\n\\n    def __hash__(self):\\n        return (\\n            super().__hash__()\\n            ^ hash(frozenset(self.channels))\\n            ^ hash(frozendict(self._channel_priorities_map))\\n            ^ hash(self._channel_priority)\\n            ^ hash(self._solver_ignore_timestamps)\\n            ^ hash(frozendict((k, tuple(v)) for k, v in self.groups.items()))\\n            ^ hash(frozendict((k, tuple(v)) for k, v in self.trackers.items()))\\n            ^ hash(frozendict((k, tuple(v)) for k, v in self.ms_depends_.items()))\\n        )\\n\\n    def default_filter(self, features=None, filter=None):\\n        # TODO: fix this import; this is bad\\n        from .core.subdir_data import make_feature_record\\n\\n        if filter is None:\\n            filter = {}\\n        else:\\n            filter.clear()\\n\\n        filter.update(\\n            {make_feature_record(fstr): False for fstr in self.trackers.keys()}\\n        )\\n        if features:\\n            filter.update({make_feature_record(fstr): True for fstr in features})\\n        return filter\\n\\n    def valid(self, spec_or_prec, filter, optional=True):\\n        \\\"\\\"\\\"Tests if a package, MatchSpec, or a list of both has satisfiable\\n        dependencies, assuming cyclic dependencies are always valid.\\n\\n        Args:\\n            spec_or_prec: a package record, a MatchSpec, or an iterable of these.\\n            filter: a dictionary of (fkey,valid) pairs, used to consider a subset\\n                of dependencies, and to eliminate repeated searches.\\n            optional: if True (default), do not enforce optional specifications\\n                when considering validity. If False, enforce them.\\n\\n        Returns:\\n            True if the full set of dependencies can be satisfied; False otherwise.\\n            If filter is supplied and update is True, it will be updated with the\\n            search results.\\n        \\\"\\\"\\\"\\n\\n        def v_(spec):\\n            return v_ms_(spec) if isinstance(spec, MatchSpec) else v_fkey_(spec)\\n\\n        def v_ms_(ms):\\n            return (\\n                optional\\n                and ms.optional\\n                or any(v_fkey_(fkey) for fkey in self.find_matches(ms))\\n            )\\n\\n        def v_fkey_(prec):\\n            val = filter.get(prec)\\n            if val is None:\\n                filter[prec] = True\\n                try:\\n                    depends = self.ms_depends(prec)\\n                except InvalidSpec:\\n                    val = filter[prec] = False\\n                else:\\n                    val = filter[prec] = all(v_ms_(ms) for ms in depends)\\n            return val\\n\\n        result = v_(spec_or_prec)\\n        return result\\n\\n    def valid2(self, spec_or_prec, filter_out, optional=True):\\n        def is_valid(_spec_or_prec):\\n            if isinstance(_spec_or_prec, MatchSpec):\\n                return is_valid_spec(_spec_or_prec)\\n            else:\\n                return is_valid_prec(_spec_or_prec)\\n\\n        @memoizemethod\\n        def is_valid_spec(_spec):\\n            return (\\n                optional\\n                and _spec.optional\\n                or any(is_valid_prec(_prec) for _prec in self.find_matches(_spec))\\n            )\\n\\n        def is_valid_prec(prec):\\n            val = filter_out.get(prec)\\n            if val is None:\\n                filter_out[prec] = False\\n                try:\\n                    has_valid_deps = all(\\n                        is_valid_spec(ms) for ms in self.ms_depends(prec)\\n                    )\\n                except InvalidSpec:\\n                    val = filter_out[prec] = \\\"invalid dep specs\\\"\\n                else:\\n                    val = filter_out[prec] = (\\n                        False if has_valid_deps else \\\"invalid depends specs\\\"\\n                    )\\n            return not val\\n\\n        return is_valid(spec_or_prec)\\n\\n    def invalid_chains(self, spec, filter, optional=True):\\n        \\\"\\\"\\\"Constructs a set of 'dependency chains' for invalid specs.\\n\\n        A dependency chain is a tuple of MatchSpec objects, starting with\\n        the requested spec, proceeding down the dependency tree, ending at\\n        a specification that cannot be satisfied.\\n\\n        Args:\\n            spec: a package key or MatchSpec\\n            filter: a dictionary of (prec, valid) pairs to be used when\\n                testing for package validity.\\n\\n        Returns:\\n            A tuple of tuples, empty if the MatchSpec is valid.\\n        \\\"\\\"\\\"\\n\\n        def chains_(spec, names):\\n            if spec.name in names:\\n                return\\n            names.add(spec.name)\\n            if self.valid(spec, filter, optional):\\n                return\\n            precs = self.find_matches(spec)\\n            found = False\\n\\n            conflict_deps = set()\\n            for prec in precs:\\n                for m2 in self.ms_depends(prec):\\n                    for x in chains_(m2, names):\\n                        found = True\\n                        yield (spec,) + x\\n                    else:\\n                        conflict_deps.add(m2)\\n            if not found:\\n                conflict_groups = groupby(lambda x: x.name, conflict_deps)\\n                for group in conflict_groups.values():\\n                    yield (spec,) + MatchSpec.union(group)\\n\\n        return chains_(spec, set())\\n\\n    def verify_specs(self, specs):\\n        \\\"\\\"\\\"Perform a quick verification that specs and dependencies are reasonable.\\n\\n        Args:\\n            specs: An iterable of strings or MatchSpec objects to be tested.\\n\\n        Returns:\\n            Nothing, but if there is a conflict, an error is thrown.\\n\\n        Note that this does not attempt to resolve circular dependencies.\\n        \\\"\\\"\\\"\\n        non_tf_specs = []\\n        bad_deps = []\\n        feature_names = set()\\n        for ms in specs:\\n            _feature_names = ms.get_exact_value(\\\"track_features\\\")\\n            if _feature_names:\\n                feature_names.update(_feature_names)\\n            else:\\n                non_tf_specs.append(ms)\\n        bad_deps.extend(\\n            (spec,)\\n            for spec in non_tf_specs\\n            if (not spec.optional and not self.find_matches(spec))\\n        )\\n        if bad_deps:\\n            raise ResolvePackageNotFound(bad_deps)\\n        return tuple(non_tf_specs), feature_names\\n\\n    def _classify_bad_deps(\\n        self, bad_deps, specs_to_add, history_specs, strict_channel_priority\\n    ):\\n        classes = {\\n            \\\"python\\\": set(),\\n            \\\"request_conflict_with_history\\\": set(),\\n            \\\"direct\\\": set(),\\n            \\\"virtual_package\\\": set(),\\n        }\\n        specs_to_add = {MatchSpec(_) for _ in specs_to_add or []}\\n        history_specs = {MatchSpec(_) for _ in history_specs or []}\\n        for chain in bad_deps:\\n            # sometimes chains come in as strings\\n            if (\\n                len(chain) > 1\\n                and chain[-1].name == \\\"python\\\"\\n                and not any(_.name == \\\"python\\\" for _ in specs_to_add)\\n                and any(_[0] for _ in bad_deps if _[0].name == \\\"python\\\")\\n            ):\\n                python_first_specs = [_[0] for _ in bad_deps if _[0].name == \\\"python\\\"]\\n                if python_first_specs:\\n                    python_spec = python_first_specs[0]\\n                    if not (\\n                        set(self.find_matches(python_spec))\\n                        & set(self.find_matches(chain[-1]))\\n                    ):\\n                        classes[\\\"python\\\"].add(\\n                            (\\n                                tuple([chain[0], chain[-1]]),\\n                                str(MatchSpec(python_spec, target=None)),\\n                            )\\n                        )\\n            elif chain[-1].name.startswith(\\\"__\\\"):\\n                version = [_ for _ in self._system_precs if _.name == chain[-1].name]\\n                virtual_package_version = (\\n                    version[0].version if version else \\\"not available\\\"\\n                )\\n                classes[\\\"virtual_package\\\"].add((tuple(chain), virtual_package_version))\\n            elif chain[0] in specs_to_add:\\n                match = False\\n                for spec in history_specs:\\n                    if spec.name == chain[-1].name:\\n                        classes[\\\"request_conflict_with_history\\\"].add(\\n                            (tuple(chain), str(MatchSpec(spec, target=None)))\\n                        )\\n                        match = True\\n\\n                if not match:\\n                    classes[\\\"direct\\\"].add(\\n                        (tuple(chain), str(MatchSpec(chain[0], target=None)))\\n                    )\\n            else:\\n                if len(chain) > 1 or any(\\n                    len(c) >= 1 and c[0] == chain[0] for c in bad_deps\\n                ):\\n                    classes[\\\"direct\\\"].add(\\n                        (tuple(chain), str(MatchSpec(chain[0], target=None)))\\n                    )\\n\\n        if classes[\\\"python\\\"]:\\n            # filter out plain single-entry python conflicts.  The python section explains these.\\n            classes[\\\"direct\\\"] = [\\n                _\\n                for _ in classes[\\\"direct\\\"]\\n                if _[1].startswith(\\\"python \\\") or len(_[0]) > 1\\n            ]\\n        return classes\\n\\n    def find_matches_with_strict(self, ms, strict_channel_priority):\\n        matches = self.find_matches(ms)\\n        if not strict_channel_priority:\\n            return matches\\n        sole_source_channel_name = self._get_strict_channel(ms.name)\\n        return tuple(f for f in matches if f.channel.name == sole_source_channel_name)\\n\\n    def find_conflicts(self, specs, specs_to_add=None, history_specs=None):\\n        if context.unsatisfiable_hints:\\n            if not context.json:\\n                print(\\n                    \\\"\\\\nFound conflicts! Looking for incompatible packages.\\\\n\\\"\\n                    \\\"This can take several minutes.  Press CTRL-C to abort.\\\"\\n                )\\n            bad_deps = self.build_conflict_map(specs, specs_to_add, history_specs)\\n        else:\\n            bad_deps = {}\\n        strict_channel_priority = context.channel_priority == ChannelPriority.STRICT\\n        raise UnsatisfiableError(bad_deps, strict=strict_channel_priority)\\n\\n    def breadth_first_search_for_dep_graph(\\n        self, root_spec, target_name, dep_graph, num_targets=1\\n    ):\\n        \\\"\\\"\\\"Return shorted path from root_spec to target_name\\\"\\\"\\\"\\n        queue = []\\n        queue.append([root_spec])\\n        visited = []\\n        target_paths = []\\n        while queue:\\n            path = queue.pop(0)\\n            node = path[-1]\\n            if node in visited:\\n                continue\\n            visited.append(node)\\n            if node.name == target_name:\\n                if len(target_paths) == 0:\\n                    target_paths.append(path)\\n                if len(target_paths[-1]) == len(path):\\n                    last_spec = MatchSpec.union((path[-1], target_paths[-1][-1]))[0]\\n                    target_paths[-1][-1] = last_spec\\n                else:\\n                    target_paths.append(path)\\n\\n                found_all_targets = len(target_paths) == num_targets and any(\\n                    len(_) != len(path) for _ in queue\\n                )\\n                if len(queue) == 0 or found_all_targets:\\n                    return target_paths\\n            sub_graph = dep_graph\\n            for p in path[0:-1]:\\n                sub_graph = sub_graph[p]\\n            children = [_ for _ in sub_graph.get(node, {})]\\n            if children is None:\\n                continue\\n            for adj in children:\\n                if len(target_paths) < num_targets:\\n                    new_path = list(path)\\n                    new_path.append(adj)\\n                    queue.append(new_path)\\n        return target_paths\\n\\n    def build_graph_of_deps(self, spec):\\n        dep_graph = {spec: {}}\\n        all_deps = set()\\n        queue = [[spec]]\\n        while queue:\\n            path = queue.pop(0)\\n            sub_graph = dep_graph\\n            for p in path:\\n                sub_graph = sub_graph[p]\\n            parent_node = path[-1]\\n            matches = self.find_matches(parent_node)\\n            for mat in matches:\\n                if len(mat.depends) > 0:\\n                    for i in mat.depends:\\n                        new_node = MatchSpec(i)\\n                        sub_graph.update({new_node: {}})\\n                        all_deps.add(new_node)\\n                        new_path = list(path)\\n                        new_path.append(new_node)\\n                        if len(new_path) <= context.unsatisfiable_hints_check_depth:\\n                            queue.append(new_path)\\n        return dep_graph, all_deps\\n\\n    def build_conflict_map(self, specs, specs_to_add=None, history_specs=None):\\n        \\\"\\\"\\\"Perform a deeper analysis on conflicting specifications, by attempting\\n        to find the common dependencies that might be the cause of conflicts.\\n\\n        Args:\\n            specs: An iterable of strings or MatchSpec objects to be tested.\\n            It is assumed that the specs conflict.\\n\\n        Returns:\\n            bad_deps: A list of lists of bad deps\\n\\n        Strategy:\\n            If we're here, we know that the specs conflict. This could be because:\\n            - One spec conflicts with another; e.g.\\n                  ['numpy 1.5*', 'numpy >=1.6']\\n            - One spec conflicts with a dependency of another; e.g.\\n                  ['numpy 1.5*', 'scipy 0.12.0b1']\\n            - Each spec depends on *the same package* but in a different way; e.g.,\\n                  ['A', 'B'] where A depends on numpy 1.5, and B on numpy 1.6.\\n            Technically, all three of these cases can be boiled down to the last\\n            one if we treat the spec itself as one of the \\\"dependencies\\\". There\\n            might be more complex reasons for a conflict, but this code only\\n            considers the ones above.\\n\\n            The purpose of this code, then, is to identify packages (like numpy\\n            above) that all of the specs depend on *but in different ways*. We\\n            then identify the dependency chains that lead to those packages.\\n        \\\"\\\"\\\"\\n        # if only a single package matches the spec use the packages depends\\n        # rather than the spec itself\\n        strict_channel_priority = context.channel_priority == ChannelPriority.STRICT\\n\\n        specs = set(specs) | (specs_to_add or set())\\n        # Remove virtual packages\\n        specs = {spec for spec in specs if not spec.name.startswith(\\\"__\\\")}\\n        if len(specs) == 1:\\n            matches = self.find_matches(next(iter(specs)))\\n            if len(matches) == 1:\\n                specs = set(self.ms_depends(matches[0]))\\n        specs.update({_.to_match_spec() for _ in self._system_precs})\\n        for spec in specs:\\n            self._get_package_pool((spec,))\\n\\n        dep_graph = {}\\n        dep_list = {}\\n        with tqdm(\\n            total=len(specs),\\n            desc=\\\"Building graph of deps\\\",\\n            leave=False,\\n            disable=context.json,\\n        ) as t:\\n            for spec in specs:\\n                t.set_description(f\\\"Examining {spec}\\\")\\n                t.update()\\n                dep_graph_for_spec, all_deps_for_spec = self.build_graph_of_deps(spec)\\n                dep_graph.update(dep_graph_for_spec)\\n                if dep_list.get(spec.name):\\n                    dep_list[spec.name].append(spec)\\n                else:\\n                    dep_list[spec.name] = [spec]\\n                for dep in all_deps_for_spec:\\n                    if dep_list.get(dep.name):\\n                        dep_list[dep.name].append(spec)\\n                    else:\\n                        dep_list[dep.name] = [spec]\\n\\n        chains = []\\n        conflicting_pkgs_pkgs = {}\\n        for k, v in dep_list.items():\\n            set_v = frozenset(v)\\n            # Packages probably conflicts if many specs depend on it\\n            if len(set_v) > 1:\\n                if conflicting_pkgs_pkgs.get(set_v) is None:\\n                    conflicting_pkgs_pkgs[set_v] = [k]\\n                else:\\n                    conflicting_pkgs_pkgs[set_v].append(k)\\n            # Conflict if required virtual package is not present\\n            elif k.startswith(\\\"__\\\") and any(s for s in set_v if s.name != k):\\n                conflicting_pkgs_pkgs[set_v] = [k]\\n\\n        with tqdm(\\n            total=len(specs),\\n            desc=\\\"Determining conflicts\\\",\\n            leave=False,\\n            disable=context.json,\\n        ) as t:\\n            for roots, nodes in conflicting_pkgs_pkgs.items():\\n                t.set_description(\\n                    \\\"Examining conflict for {}\\\".format(\\\" \\\".join(_.name for _ in roots))\\n                )\\n                t.update()\\n                lroots = [_ for _ in roots]\\n                current_shortest_chain = []\\n                shortest_node = None\\n                requested_spec_unsat = frozenset(nodes).intersection(\\n                    {_.name for _ in roots}\\n                )\\n                if requested_spec_unsat:\\n                    chains.append([_ for _ in roots if _.name in requested_spec_unsat])\\n                    shortest_node = chains[-1][0]\\n                    for root in roots:\\n                        if root != chains[0][0]:\\n                            search_node = shortest_node.name\\n                            num_occurances = dep_list[search_node].count(root)\\n                            c = self.breadth_first_search_for_dep_graph(\\n                                root, search_node, dep_graph, num_occurances\\n                            )\\n                            chains.extend(c)\\n                else:\\n                    for node in nodes:\\n                        num_occurances = dep_list[node].count(lroots[0])\\n                        chain = self.breadth_first_search_for_dep_graph(\\n                            lroots[0], node, dep_graph, num_occurances\\n                        )\\n                        chains.extend(chain)\\n                        if len(current_shortest_chain) == 0 or len(chain) < len(\\n                            current_shortest_chain\\n                        ):\\n                            current_shortest_chain = chain\\n                            shortest_node = node\\n                    for root in lroots[1:]:\\n                        num_occurances = dep_list[shortest_node].count(root)\\n                        c = self.breadth_first_search_for_dep_graph(\\n                            root, shortest_node, dep_graph, num_occurances\\n                        )\\n                        chains.extend(c)\\n\\n        bad_deps = self._classify_bad_deps(\\n            chains, specs_to_add, history_specs, strict_channel_priority\\n        )\\n        return bad_deps\\n\\n    def _get_strict_channel(self, package_name):\\n        channel_name = None\\n        try:\\n            channel_name = self._strict_channel_cache[package_name]\\n        except KeyError:\\n            if package_name in self.groups:\\n                all_channel_names = {\\n                    prec.channel.name for prec in self.groups[package_name]\\n                }\\n                by_cp = {\\n                    self._channel_priorities_map.get(cn, 1): cn\\n                    for cn in all_channel_names\\n                }\\n                highest_priority = sorted(by_cp)[\\n                    0\\n                ]  # highest priority is the lowest number\\n                channel_name = self._strict_channel_cache[package_name] = by_cp[\\n                    highest_priority\\n                ]\\n        return channel_name\\n\\n    @memoizemethod\\n    def _broader(self, ms, specs_by_name):\\n        \\\"\\\"\\\"Prevent introduction of matchspecs that broaden our selection of choices.\\\"\\\"\\\"\\n        if not specs_by_name:\\n            return False\\n        return ms.strictness < specs_by_name[0].strictness\\n\\n    def _get_package_pool(self, specs):\\n        specs = frozenset(specs)\\n        if specs in self._pool_cache:\\n            pool = self._pool_cache[specs]\\n        else:\\n            pool = self.get_reduced_index(specs)\\n            grouped_pool = groupby(lambda x: x.name, pool)\\n            pool = {k: set(v) for k, v in grouped_pool.items()}\\n            self._pool_cache[specs] = pool\\n        return pool\\n\\n    @time_recorder(module_name=__name__)\\n    def get_reduced_index(\\n        self, explicit_specs, sort_by_exactness=True, exit_on_conflict=False\\n    ):\\n        # TODO: fix this import; this is bad\\n        from .core.subdir_data import make_feature_record\\n\\n        strict_channel_priority = context.channel_priority == ChannelPriority.STRICT\\n\\n        cache_key = strict_channel_priority, tuple(explicit_specs)\\n        if cache_key in self._reduced_index_cache:\\n            return self._reduced_index_cache[cache_key]\\n\\n        if log.isEnabledFor(DEBUG):\\n            log.debug(\\n                \\\"Retrieving packages for: %s\\\",\\n                dashlist(sorted(str(s) for s in explicit_specs)),\\n            )\\n\\n        explicit_specs, features = self.verify_specs(explicit_specs)\\n        filter_out = {\\n            prec: False if val else \\\"feature not enabled\\\"\\n            for prec, val in self.default_filter(features).items()\\n        }\\n        snames = set()\\n        top_level_spec = None\\n        cp_filter_applied = set()  # values are package names\\n        if sort_by_exactness:\\n            # prioritize specs that are more exact.  Exact specs will evaluate to 3,\\n            #    constrained specs will evaluate to 2, and name only will be 1\\n            explicit_specs = sorted(\\n                list(explicit_specs),\\n                key=lambda x: (exactness_and_number_of_deps(self, x), x.dist_str()),\\n                reverse=True,\\n            )\\n        # tuple because it needs to be hashable\\n        explicit_specs = tuple(explicit_specs)\\n\\n        explicit_spec_package_pool = {}\\n        for s in explicit_specs:\\n            explicit_spec_package_pool[s.name] = explicit_spec_package_pool.get(\\n                s.name, set()\\n            ) | set(self.find_matches(s))\\n\\n        def filter_group(_specs):\\n            # all _specs should be for the same package name\\n            name = next(iter(_specs)).name\\n            group = self.groups.get(name, ())\\n\\n            # implement strict channel priority\\n            if group and strict_channel_priority and name not in cp_filter_applied:\\n                sole_source_channel_name = self._get_strict_channel(name)\\n                for prec in group:\\n                    if prec.channel.name != sole_source_channel_name:\\n                        filter_out[prec] = \\\"removed due to strict channel priority\\\"\\n                cp_filter_applied.add(name)\\n\\n            # Prune packages that don't match any of the patterns,\\n            # have unsatisfiable dependencies, or conflict with the explicit specs\\n            nold = nnew = 0\\n            for prec in group:\\n                if not filter_out.setdefault(prec, False):\\n                    nold += 1\\n                    if (not self.match_any(_specs, prec)) or (\\n                        explicit_spec_package_pool.get(name)\\n                        and prec not in explicit_spec_package_pool[name]\\n                    ):\\n                        filter_out[prec] = (\\n                            f\\\"incompatible with required spec {top_level_spec}\\\"\\n                        )\\n                        continue\\n                    unsatisfiable_dep_specs = set()\\n                    for ms in self.ms_depends(prec):\\n                        if not ms.optional and not any(\\n                            rec\\n                            for rec in self.find_matches(ms)\\n                            if not filter_out.get(rec, False)\\n                        ):\\n                            unsatisfiable_dep_specs.add(ms)\\n                    if unsatisfiable_dep_specs:\\n                        filter_out[prec] = \\\"unsatisfiable dependencies {}\\\".format(\\n                            \\\" \\\".join(str(s) for s in unsatisfiable_dep_specs)\\n                        )\\n                        continue\\n                    filter_out[prec] = False\\n                    nnew += 1\\n\\n            reduced = nnew < nold\\n            if reduced:\\n                log.debug(\\\"%s: pruned from %d -> %d\\\" % (name, nold, nnew))\\n            if any(ms.optional for ms in _specs):\\n                return reduced\\n            elif nnew == 0:\\n                # Indicates that a conflict was found; we can exit early\\n                return None\\n\\n            # Perform the same filtering steps on any dependencies shared across\\n            # *all* packages in the group. Even if just one of the packages does\\n            # not have a particular dependency, it must be ignored in this pass.\\n            # Otherwise, we might do more filtering than we should---and it is\\n            # better to have extra packages here than missing ones.\\n            if reduced or name not in snames:\\n                snames.add(name)\\n\\n                _dep_specs = groupby(\\n                    lambda s: s.name,\\n                    (\\n                        dep_spec\\n                        for prec in group\\n                        if not filter_out.get(prec, False)\\n                        for dep_spec in self.ms_depends(prec)\\n                        if not dep_spec.optional\\n                    ),\\n                )\\n                _dep_specs.pop(\\\"*\\\", None)  # discard track_features specs\\n\\n                for deps_name, deps in sorted(\\n                    _dep_specs.items(), key=lambda x: any(_.optional for _ in x[1])\\n                ):\\n                    if len(deps) >= nnew:\\n                        res = filter_group(set(deps))\\n                        if res:\\n                            reduced = True\\n                        elif res is None:\\n                            # Indicates that a conflict was found; we can exit early\\n                            return None\\n\\n            return reduced\\n\\n        # Iterate on pruning until no progress is made. We've implemented\\n        # what amounts to \\\"double-elimination\\\" here; packages get one additional\\n        # chance after their first \\\"False\\\" reduction. This catches more instances\\n        # where one package's filter affects another. But we don't have to be\\n        # perfect about this, so performance matters.\\n        pruned_to_zero = set()\\n        for _ in range(2):\\n            snames.clear()\\n            slist = deque(explicit_specs)\\n            while slist:\\n                s = slist.popleft()\\n                if filter_group([s]):\\n                    slist.append(s)\\n                else:\\n                    pruned_to_zero.add(s)\\n\\n        if pruned_to_zero and exit_on_conflict:\\n            return {}\\n\\n        # Determine all valid packages in the dependency graph\\n        reduced_index2 = {\\n            prec: prec for prec in (make_feature_record(fstr) for fstr in features)\\n        }\\n        specs_by_name_seed = {}\\n        for s in explicit_specs:\\n            specs_by_name_seed[s.name] = specs_by_name_seed.get(s.name, []) + [s]\\n        for explicit_spec in explicit_specs:\\n            add_these_precs2 = tuple(\\n                prec\\n                for prec in self.find_matches(explicit_spec)\\n                if prec not in reduced_index2 and self.valid2(prec, filter_out)\\n            )\\n\\n            if strict_channel_priority and add_these_precs2:\\n                strict_channel_name = self._get_strict_channel(add_these_precs2[0].name)\\n\\n                add_these_precs2 = tuple(\\n                    prec\\n                    for prec in add_these_precs2\\n                    if prec.channel.name == strict_channel_name\\n                )\\n            reduced_index2.update((prec, prec) for prec in add_these_precs2)\\n\\n            for pkg in add_these_precs2:\\n                # what we have seen is only relevant within the context of a single package\\n                #    that is picked up because of an explicit spec.  We don't want the\\n                #    broadening check to apply across packages at the explicit level; only\\n                #    at the level of deps below that explicit package.\\n                seen_specs = set()\\n                specs_by_name = copy.deepcopy(specs_by_name_seed)\\n\\n                dep_specs = set(self.ms_depends(pkg))\\n                for dep in dep_specs:\\n                    specs = specs_by_name.get(dep.name, [])\\n                    if dep not in specs and (\\n                        not specs or dep.strictness >= specs[0].strictness\\n                    ):\\n                        specs.insert(0, dep)\\n                    specs_by_name[dep.name] = specs\\n\\n                while dep_specs:\\n                    # used for debugging\\n                    # size_index = len(reduced_index2)\\n                    # specs_added = []\\n                    ms = dep_specs.pop()\\n                    seen_specs.add(ms)\\n                    for dep_pkg in (\\n                        _ for _ in self.find_matches(ms) if _ not in reduced_index2\\n                    ):\\n                        if not self.valid2(dep_pkg, filter_out):\\n                            continue\\n\\n                        # expand the reduced index if not using strict channel priority,\\n                        #    or if using it and this package is in the appropriate channel\\n                        if not strict_channel_priority or (\\n                            self._get_strict_channel(dep_pkg.name)\\n                            == dep_pkg.channel.name\\n                        ):\\n                            reduced_index2[dep_pkg] = dep_pkg\\n\\n                            # recurse to deps of this dep\\n                            new_specs = set(self.ms_depends(dep_pkg)) - seen_specs\\n                            for new_ms in new_specs:\\n                                # We do not pull packages into the reduced index due\\n                                # to a track_features dependency. Remember, a feature\\n                                # specifies a \\\"soft\\\" dependency: it must be in the\\n                                # environment, but it is not _pulled_ in. The SAT\\n                                # logic doesn't do a perfect job of capturing this\\n                                # behavior, but keeping these packags out of the\\n                                # reduced index helps. Of course, if _another_\\n                                # package pulls it in by dependency, that's fine.\\n                                if \\\"track_features\\\" not in new_ms and not self._broader(\\n                                    new_ms,\\n                                    tuple(specs_by_name.get(new_ms.name, ())),\\n                                ):\\n                                    dep_specs.add(new_ms)\\n                                    # if new_ms not in dep_specs:\\n                                    #     specs_added.append(new_ms)\\n                                else:\\n                                    seen_specs.add(new_ms)\\n                    # debugging info - see what specs are bringing in the largest blobs\\n                    # if size_index != len(reduced_index2):\\n                    #     print(\\\"MS {} added {} pkgs to index\\\".format(ms,\\n                    #           len(reduced_index2) - size_index))\\n                    # if specs_added:\\n                    #     print(\\\"MS {} added {} specs to further examination\\\".format(ms,\\n                    #                                                                specs_added))\\n\\n        reduced_index2 = frozendict(reduced_index2)\\n        self._reduced_index_cache[cache_key] = reduced_index2\\n        return reduced_index2\\n\\n    def match_any(self, mss, prec):\\n        return any(ms.match(prec) for ms in mss)\\n\\n    def find_matches(self, spec: MatchSpec) -> tuple[PackageRecord]:\\n        res = self._cached_find_matches.get(spec, None)\\n        if res is not None:\\n            return res\\n\\n        spec_name = spec.get_exact_value(\\\"name\\\")\\n        if spec_name:\\n            candidate_precs = self.groups.get(spec_name, ())\\n        elif spec.get_exact_value(\\\"track_features\\\"):\\n            feature_names = spec.get_exact_value(\\\"track_features\\\")\\n            candidate_precs = itertools.chain.from_iterable(\\n                self.trackers.get(feature_name, ()) for feature_name in feature_names\\n            )\\n        else:\\n            candidate_precs = self.index.values()\\n\\n        res = tuple(p for p in candidate_precs if spec.match(p))\\n        self._cached_find_matches[spec] = res\\n        return res\\n\\n    def ms_depends(self, prec: PackageRecord) -> list[MatchSpec]:\\n        deps = self.ms_depends_.get(prec)\\n        if deps is None:\\n            deps = [MatchSpec(d) for d in prec.combined_depends]\\n            deps.extend(MatchSpec(track_features=feat) for feat in prec.features)\\n            self.ms_depends_[prec] = deps\\n        return deps\\n\\n    def version_key(self, prec, vtype=None):\\n        channel = prec.channel\\n        channel_priority = self._channel_priorities_map.get(\\n            channel.name, 1\\n        )  # TODO: ask @mcg1969 why the default value is 1 here  # NOQA\\n        valid = 1 if channel_priority < MAX_CHANNEL_PRIORITY else 0\\n        version_comparator = VersionOrder(prec.get(\\\"version\\\", \\\"\\\"))\\n        build_number = prec.get(\\\"build_number\\\", 0)\\n        build_string = prec.get(\\\"build\\\")\\n        noarch = -int(prec.subdir == \\\"noarch\\\")\\n        if self._channel_priority != ChannelPriority.DISABLED:\\n            vkey = [valid, -channel_priority, version_comparator, build_number, noarch]\\n        else:\\n            vkey = [valid, version_comparator, -channel_priority, build_number, noarch]\\n        if self._solver_ignore_timestamps:\\n            vkey.append(build_string)\\n        else:\\n            vkey.extend((prec.get(\\\"timestamp\\\", 0), build_string))\\n        return vkey\\n\\n    @staticmethod\\n    def _make_channel_priorities(channels):\\n        priorities_map = {}\\n        for priority_counter, chn in enumerate(\\n            itertools.chain.from_iterable(\\n                (Channel(cc) for cc in c._channels)\\n                if isinstance(c, MultiChannel)\\n                else (c,)\\n                for c in (Channel(c) for c in channels)\\n            )\\n        ):\\n            channel_name = chn.name\\n            if channel_name in priorities_map:\\n                continue\\n            priorities_map[channel_name] = min(\\n                priority_counter, MAX_CHANNEL_PRIORITY - 1\\n            )\\n        return priorities_map\\n\\n    def get_pkgs(self, ms, emptyok=False):  # pragma: no cover\\n        # legacy method for conda-build\\n        ms = MatchSpec(ms)\\n        precs = self.find_matches(ms)\\n        if not precs and not emptyok:\\n            raise ResolvePackageNotFound([(ms,)])\\n        return sorted(precs, key=self.version_key)\\n\\n    @staticmethod\\n    def to_sat_name(val):\\n        # val can be a PackageRecord or MatchSpec\\n        if isinstance(val, PackageRecord):\\n            return val.dist_str()\\n        elif isinstance(val, MatchSpec):\\n            return \\\"@s@\\\" + str(val) + (\\\"?\\\" if val.optional else \\\"\\\")\\n        else:\\n            raise NotImplementedError()\\n\\n    @staticmethod\\n    def to_feature_metric_id(prec_dist_str, feat):\\n        return f\\\"@fm@{prec_dist_str}@{feat}\\\"\\n\\n    def push_MatchSpec(self, C, spec):\\n        spec = MatchSpec(spec)\\n        sat_name = self.to_sat_name(spec)\\n        m = C.from_name(sat_name)\\n        if m is not None:\\n            # the spec has already been pushed onto the clauses stack\\n            return sat_name\\n\\n        simple = spec._is_single()\\n        nm = spec.get_exact_value(\\\"name\\\")\\n        tf = frozenset(\\n            _tf\\n            for _tf in (f.strip() for f in spec.get_exact_value(\\\"track_features\\\") or ())\\n            if _tf\\n        )\\n\\n        if nm:\\n            tgroup = libs = self.groups.get(nm, [])\\n        elif tf:\\n            assert len(tf) == 1\\n            k = next(iter(tf))\\n            tgroup = libs = self.trackers.get(k, [])\\n        else:\\n            tgroup = libs = self.index.keys()\\n            simple = False\\n        if not simple:\\n            libs = [fkey for fkey in tgroup if spec.match(fkey)]\\n        if len(libs) == len(tgroup):\\n            if spec.optional:\\n                m = TRUE\\n            elif not simple:\\n                ms2 = MatchSpec(track_features=tf) if tf else MatchSpec(nm)\\n                m = C.from_name(self.push_MatchSpec(C, ms2))\\n        if m is None:\\n            sat_names = [self.to_sat_name(prec) for prec in libs]\\n            if spec.optional:\\n                ms2 = MatchSpec(track_features=tf) if tf else MatchSpec(nm)\\n                sat_names.append(\\\"!\\\" + self.to_sat_name(ms2))\\n            m = C.Any(sat_names)\\n        C.name_var(m, sat_name)\\n        return sat_name\\n\\n    @time_recorder(module_name=__name__)\\n    def gen_clauses(self):\\n        C = Clauses(sat_solver=_get_sat_solver_cls(context.sat_solver))\\n        for name, group in self.groups.items():\\n            group = [self.to_sat_name(prec) for prec in group]\\n            # Create one variable for each package\\n            for sat_name in group:\\n                C.new_var(sat_name)\\n            # Create one variable for the group\\n            m = C.new_var(self.to_sat_name(MatchSpec(name)))\\n\\n            # Exactly one of the package variables, OR\\n            # the negation of the group variable, is true\\n            C.Require(C.ExactlyOne, group + [C.Not(m)])\\n\\n        # If a package is installed, its dependencies must be as well\\n        for prec in self.index.values():\\n            nkey = C.Not(self.to_sat_name(prec))\\n            for ms in self.ms_depends(prec):\\n                # Virtual packages can't be installed, we ignore them\\n                if not ms.name.startswith(\\\"__\\\"):\\n                    C.Require(C.Or, nkey, self.push_MatchSpec(C, ms))\\n\\n        if log.isEnabledFor(DEBUG):\\n            log.debug(\\n                \\\"gen_clauses returning with clause count: %d\\\", C.get_clause_count()\\n            )\\n        return C\\n\\n    def generate_spec_constraints(self, C, specs):\\n        result = [(self.push_MatchSpec(C, ms),) for ms in specs]\\n        if log.isEnabledFor(DEBUG):\\n            log.debug(\\n                \\\"generate_spec_constraints returning with clause count: %d\\\",\\n                C.get_clause_count(),\\n            )\\n        return result\\n\\n    def generate_feature_count(self, C):\\n        result = {\\n            self.push_MatchSpec(C, MatchSpec(track_features=name)): 1\\n            for name in self.trackers.keys()\\n        }\\n        if log.isEnabledFor(DEBUG):\\n            log.debug(\\n                \\\"generate_feature_count returning with clause count: %d\\\",\\n                C.get_clause_count(),\\n            )\\n        return result\\n\\n    def generate_update_count(self, C, specs):\\n        return {\\n            \\\"!\\\" + ms.target: 1 for ms in specs if ms.target and C.from_name(ms.target)\\n        }\\n\\n    def generate_feature_metric(self, C):\\n        eq = {}  # a C.minimize() objective: dict[varname, coeff]\\n        # Given a pair (prec, feature), assign a \\\"1\\\" score IF:\\n        # - The prec is installed\\n        # - The prec does NOT require the feature\\n        # - At least one package in the group DOES require the feature\\n        # - A package that tracks the feature is installed\\n        for name, group in self.groups.items():\\n            prec_feats = {self.to_sat_name(prec): set(prec.features) for prec in group}\\n            active_feats = set.union(*prec_feats.values()).intersection(self.trackers)\\n            for feat in active_feats:\\n                clause_id_for_feature = self.push_MatchSpec(\\n                    C, MatchSpec(track_features=feat)\\n                )\\n                for prec_sat_name, features in prec_feats.items():\\n                    if feat not in features:\\n                        feature_metric_id = self.to_feature_metric_id(\\n                            prec_sat_name, feat\\n                        )\\n                        C.name_var(\\n                            C.And(prec_sat_name, clause_id_for_feature),\\n                            feature_metric_id,\\n                        )\\n                        eq[feature_metric_id] = 1\\n        return eq\\n\\n    def generate_removal_count(self, C, specs):\\n        return {\\\"!\\\" + self.push_MatchSpec(C, ms.name): 1 for ms in specs}\\n\\n    def generate_install_count(self, C, specs):\\n        return {self.push_MatchSpec(C, ms.name): 1 for ms in specs if ms.optional}\\n\\n    def generate_package_count(self, C, missing):\\n        return {self.push_MatchSpec(C, nm): 1 for nm in missing}\\n\\n    def generate_version_metrics(self, C, specs, include0=False):\\n        # each of these are weights saying how well packages match the specs\\n        #    format for each: a C.minimize() objective: dict[varname, coeff]\\n        eqc = {}  # channel\\n        eqv = {}  # version\\n        eqb = {}  # build number\\n        eqa = {}  # arch/noarch\\n        eqt = {}  # timestamp\\n\\n        sdict = {}  # dict[package_name, PackageRecord]\\n\\n        for s in specs:\\n            s = MatchSpec(s)  # needed for testing\\n            sdict.setdefault(s.name, [])\\n            # # TODO: this block is important! can't leave it commented out\\n            # rec = sdict.setdefault(s.name, [])\\n            # if s.target:\\n            #     dist = Dist(s.target)\\n            #     if dist in self.index:\\n            #         if self.index[dist].get('priority', 0) < MAX_CHANNEL_PRIORITY:\\n            #             rec.append(dist)\\n\\n        for name, targets in sdict.items():\\n            pkgs = [(self.version_key(p), p) for p in self.groups.get(name, [])]\\n            pkey = None\\n            # keep in mind that pkgs is already sorted according to version_key (a tuple,\\n            #    so composite sort key).  Later entries in the list are, by definition,\\n            #    greater in some way, so simply comparing with != suffices.\\n            for version_key, prec in pkgs:\\n                if targets and any(prec == t for t in targets):\\n                    continue\\n                if pkey is None:\\n                    ic = iv = ib = it = ia = 0\\n                # valid package, channel priority\\n                elif pkey[0] != version_key[0] or pkey[1] != version_key[1]:\\n                    ic += 1\\n                    iv = ib = it = ia = 0\\n                # version\\n                elif pkey[2] != version_key[2]:\\n                    iv += 1\\n                    ib = it = ia = 0\\n                # build number\\n                elif pkey[3] != version_key[3]:\\n                    ib += 1\\n                    it = ia = 0\\n                # arch/noarch\\n                elif pkey[4] != version_key[4]:\\n                    ia += 1\\n                    it = 0\\n                elif not self._solver_ignore_timestamps and pkey[5] != version_key[5]:\\n                    it += 1\\n\\n                prec_sat_name = self.to_sat_name(prec)\\n                if ic or include0:\\n                    eqc[prec_sat_name] = ic\\n                if iv or include0:\\n                    eqv[prec_sat_name] = iv\\n                if ib or include0:\\n                    eqb[prec_sat_name] = ib\\n                if ia or include0:\\n                    eqa[prec_sat_name] = ia\\n                if it or include0:\\n                    eqt[prec_sat_name] = it\\n                pkey = version_key\\n\\n        return eqc, eqv, eqb, eqa, eqt\\n\\n    def dependency_sort(\\n        self,\\n        must_have: dict[str, PackageRecord],\\n    ) -> list[PackageRecord]:\\n        assert isinstance(must_have, dict)\\n\\n        digraph = {}  # dict[str, set[dependent_package_names]]\\n        for package_name, prec in must_have.items():\\n            if prec in self.index:\\n                digraph[package_name] = {ms.name for ms in self.ms_depends(prec)}\\n\\n        # There are currently at least three special cases to be aware of.\\n        # 1. The `toposort()` function, called below, contains special case code to remove\\n        #    any circular dependency between python and pip.\\n        # 2. conda/plan.py has special case code for menuinst\\n        #       Always link/unlink menuinst first/last on windows in case a subsequent\\n        #       package tries to import it to create/remove a shortcut\\n        # 3. On windows, python noarch packages need an implicit dependency on conda added, if\\n        #    conda is in the list of packages for the environment.  Python noarch packages\\n        #    that have entry points use conda's own conda.exe python entry point binary. If conda\\n        #    is going to be updated during an operation, the unlink / link order matters.\\n        #    See issue #6057.\\n\\n        if on_win and \\\"conda\\\" in digraph:\\n            for package_name, dist in must_have.items():\\n                record = self.index.get(prec)\\n                if hasattr(record, \\\"noarch\\\") and record.noarch == NoarchType.python:\\n                    digraph[package_name].add(\\\"conda\\\")\\n\\n        sorted_keys = toposort(digraph)\\n        must_have = must_have.copy()\\n        # Take all of the items in the sorted keys\\n        # Don't fail if the key does not exist\\n        result = [must_have.pop(key) for key in sorted_keys if key in must_have]\\n        # Take any key that were not sorted\\n        result.extend(must_have.values())\\n        return result\\n\\n    def environment_is_consistent(self, installed):\\n        log.debug(\\\"Checking if the current environment is consistent\\\")\\n        if not installed:\\n            return None, []\\n        sat_name_map = {}  # dict[sat_name, PackageRecord]\\n        specs = []\\n        for prec in installed:\\n            sat_name_map[self.to_sat_name(prec)] = prec\\n            specs.append(MatchSpec(f\\\"{prec.name} {prec.version} {prec.build}\\\"))\\n        r2 = Resolve({prec: prec for prec in installed}, True, channels=self.channels)\\n        C = r2.gen_clauses()\\n        constraints = r2.generate_spec_constraints(C, specs)\\n        solution = C.sat(constraints)\\n        return bool(solution)\\n\\n    def get_conflicting_specs(self, specs, explicit_specs):\\n        if not specs:\\n            return ()\\n\\n        all_specs = set(specs) | set(explicit_specs)\\n        reduced_index = self.get_reduced_index(all_specs)\\n\\n        # Check if satisfiable\\n        def mysat(specs, add_if=False):\\n            constraints = r2.generate_spec_constraints(C, specs)\\n            return C.sat(constraints, add_if)\\n\\n        if reduced_index:\\n            r2 = Resolve(reduced_index, True, channels=self.channels)\\n            C = r2.gen_clauses()\\n            solution = mysat(all_specs, True)\\n        else:\\n            solution = None\\n\\n        if solution:\\n            final_unsat_specs = ()\\n        elif context.unsatisfiable_hints:\\n            r2 = Resolve(self.index, True, channels=self.channels)\\n            C = r2.gen_clauses()\\n            # This first result is just a single unsatisfiable core. There may be several.\\n            final_unsat_specs = tuple(\\n                minimal_unsatisfiable_subset(\\n                    specs, sat=mysat, explicit_specs=explicit_specs\\n                )\\n            )\\n        else:\\n            final_unsat_specs = None\\n        return final_unsat_specs\\n\\n    def bad_installed(self, installed, new_specs):\\n        log.debug(\\\"Checking if the current environment is consistent\\\")\\n        if not installed:\\n            return None, []\\n        sat_name_map = {}  # dict[sat_name, PackageRecord]\\n        specs = []\\n        for prec in installed:\\n            sat_name_map[self.to_sat_name(prec)] = prec\\n            specs.append(MatchSpec(f\\\"{prec.name} {prec.version} {prec.build}\\\"))\\n        new_index = {prec: prec for prec in sat_name_map.values()}\\n        name_map = {p.name: p for p in new_index}\\n        if \\\"python\\\" in name_map and \\\"pip\\\" not in name_map:\\n            python_prec = new_index[name_map[\\\"python\\\"]]\\n            if \\\"pip\\\" in python_prec.depends:\\n                # strip pip dependency from python if not installed in environment\\n                new_deps = [d for d in python_prec.depends if d != \\\"pip\\\"]\\n                python_prec.depends = new_deps\\n        r2 = Resolve(new_index, True, channels=self.channels)\\n        C = r2.gen_clauses()\\n        constraints = r2.generate_spec_constraints(C, specs)\\n        solution = C.sat(constraints)\\n        limit = xtra = None\\n        if not solution or xtra:\\n\\n            def get_(name, snames):\\n                if name not in snames:\\n                    snames.add(name)\\n                    for fn in self.groups.get(name, []):\\n                        for ms in self.ms_depends(fn):\\n                            get_(ms.name, snames)\\n\\n            # New addition: find the largest set of installed packages that\\n            # are consistent with each other, and include those in the\\n            # list of packages to maintain consistency with\\n            snames = set()\\n            eq_optional_c = r2.generate_removal_count(C, specs)\\n            solution, _ = C.minimize(eq_optional_c, C.sat())\\n            snames.update(\\n                sat_name_map[sat_name][\\\"name\\\"]\\n                for sat_name in (C.from_index(s) for s in solution)\\n                if sat_name and sat_name[0] != \\\"!\\\" and \\\"@\\\" not in sat_name\\n            )\\n            # Existing behavior: keep all specs and their dependencies\\n            for spec in new_specs:\\n                get_(MatchSpec(spec).name, snames)\\n            if len(snames) < len(sat_name_map):\\n                limit = snames\\n                xtra = [\\n                    rec\\n                    for sat_name, rec in sat_name_map.items()\\n                    if rec[\\\"name\\\"] not in snames\\n                ]\\n                log.debug(\\n                    \\\"Limiting solver to the following packages: %s\\\", \\\", \\\".join(limit)\\n                )\\n        if xtra:\\n            log.debug(\\\"Packages to be preserved: %s\\\", xtra)\\n        return limit, xtra\\n\\n    def restore_bad(self, pkgs, preserve):\\n        if preserve:\\n            sdict = {prec.name: prec for prec in pkgs}\\n            pkgs.extend(p for p in preserve if p.name not in sdict)\\n\\n    def install_specs(self, specs, installed, update_deps=True):\\n        specs = list(map(MatchSpec, specs))\\n        snames = {s.name for s in specs}\\n        log.debug(\\\"Checking satisfiability of current install\\\")\\n        limit, preserve = self.bad_installed(installed, specs)\\n        for prec in installed:\\n            if prec not in self.index:\\n                continue\\n            name, version, build = prec.name, prec.version, prec.build\\n            schannel = prec.channel.canonical_name\\n            if name in snames or limit is not None and name not in limit:\\n                continue\\n            # If update_deps=True, set the target package in MatchSpec so that\\n            # the solver can minimize the version change. If update_deps=False,\\n            # fix the version and build so that no change is possible.\\n            if update_deps:\\n                # TODO: fix target here\\n                spec = MatchSpec(name=name, target=prec.dist_str())\\n            else:\\n                spec = MatchSpec(\\n                    name=name, version=version, build=build, channel=schannel\\n                )\\n            specs.insert(0, spec)\\n        return tuple(specs), preserve\\n\\n    def install(self, specs, installed=None, update_deps=True, returnall=False):\\n        specs, preserve = self.install_specs(specs, installed or [], update_deps)\\n        pkgs = []\\n        if specs:\\n            pkgs = self.solve(specs, returnall=returnall, _remove=False)\\n        self.restore_bad(pkgs, preserve)\\n        return pkgs\\n\\n    def remove_specs(self, specs, installed):\\n        nspecs = []\\n        # There's an imperfect thing happening here. \\\"specs\\\" nominally contains\\n        # a list of package names or track_feature values to be removed. But\\n        # because of add_defaults_to_specs it may also contain version constraints\\n        # like \\\"python 2.7*\\\", which are *not* asking for python to be removed.\\n        # We need to separate these two kinds of specs here.\\n        for s in map(MatchSpec, specs):\\n            # Since '@' is an illegal version number, this ensures that all of\\n            # these matches will never match an actual package. Combined with\\n            # optional=True, this has the effect of forcing their removal.\\n            if s._is_single():\\n                nspecs.append(MatchSpec(s, version=\\\"@\\\", optional=True))\\n            else:\\n                nspecs.append(MatchSpec(s, optional=True))\\n        snames = {s.name for s in nspecs if s.name}\\n        limit, _ = self.bad_installed(installed, nspecs)\\n        preserve = []\\n        for prec in installed:\\n            nm, ver = prec.name, prec.version\\n            if nm in snames:\\n                continue\\n            elif limit is not None:\\n                preserve.append(prec)\\n            else:\\n                # TODO: fix target here\\n                nspecs.append(\\n                    MatchSpec(\\n                        name=nm,\\n                        version=\\\">=\\\" + ver if ver else None,\\n                        optional=True,\\n                        target=prec.dist_str(),\\n                    )\\n                )\\n        return nspecs, preserve\\n\\n    def remove(self, specs, installed):\\n        specs, preserve = self.remove_specs(specs, installed)\\n        pkgs = self.solve(specs, _remove=True)\\n        self.restore_bad(pkgs, preserve)\\n        return pkgs\\n\\n    @time_recorder(module_name=__name__)\\n    def solve(\\n        self,\\n        specs: list,\\n        returnall: bool = False,\\n        _remove=False,\\n        specs_to_add=None,\\n        history_specs=None,\\n        should_retry_solve=False,\\n    ) -> list[PackageRecord]:\\n        if specs and not isinstance(specs[0], MatchSpec):\\n            specs = tuple(MatchSpec(_) for _ in specs)\\n\\n        specs = set(specs)\\n        if log.isEnabledFor(DEBUG):\\n            dlist = dashlist(\\n                str(\\\"%i: %s target=%s optional=%s\\\" % (i, s, s.target, s.optional))\\n                for i, s in enumerate(specs)\\n            )\\n            log.debug(\\\"Solving for: %s\\\", dlist)\\n\\n        if not specs:\\n            return ()\\n\\n        # Find the compliant packages\\n        log.debug(\\\"Solve: Getting reduced index of compliant packages\\\")\\n        len0 = len(specs)\\n\\n        reduced_index = self.get_reduced_index(\\n            specs, exit_on_conflict=not context.unsatisfiable_hints\\n        )\\n        if not reduced_index:\\n            # something is intrinsically unsatisfiable - either not found or\\n            # not the right version\\n            not_found_packages = set()\\n            wrong_version_packages = set()\\n            for s in specs:\\n                if not self.find_matches(s):\\n                    if s.name in self.groups:\\n                        wrong_version_packages.add(s)\\n                    else:\\n                        not_found_packages.add(s)\\n            if not_found_packages:\\n                raise ResolvePackageNotFound(not_found_packages)\\n            elif wrong_version_packages:\\n                raise UnsatisfiableError(\\n                    [[d] for d in wrong_version_packages], chains=False\\n                )\\n            if should_retry_solve:\\n                # We don't want to call find_conflicts until our last try.\\n                # This jumps back out to conda/cli/install.py, where the\\n                # retries happen\\n                raise UnsatisfiableError({})\\n            else:\\n                self.find_conflicts(specs, specs_to_add, history_specs)\\n\\n        # Check if satisfiable\\n        log.debug(\\\"Solve: determining satisfiability\\\")\\n\\n        def mysat(specs, add_if=False):\\n            constraints = r2.generate_spec_constraints(C, specs)\\n            return C.sat(constraints, add_if)\\n\\n        # Return a solution of packages\\n        def clean(sol):\\n            return [\\n                q\\n                for q in (C.from_index(s) for s in sol)\\n                if q and q[0] != \\\"!\\\" and \\\"@\\\" not in q\\n            ]\\n\\n        def is_converged(solution):\\n            \\\"\\\"\\\"Determine if the SAT problem has converged to a single solution.\\n\\n            This is determined by testing for a SAT solution with the current\\n            clause set and a clause in which at least one of the packages in\\n            the current solution is excluded. If a solution exists the problem\\n            has not converged as multiple solutions still exist.\\n            \\\"\\\"\\\"\\n            psolution = clean(solution)\\n            nclause = tuple(C.Not(C.from_name(q)) for q in psolution)\\n            if C.sat((nclause,), includeIf=False) is None:\\n                return True\\n            return False\\n\\n        r2 = Resolve(reduced_index, True, channels=self.channels)\\n        C = r2.gen_clauses()\\n        solution = mysat(specs, True)\\n        if not solution:\\n            if should_retry_solve:\\n                # we don't want to call find_conflicts until our last try\\n                raise UnsatisfiableError({})\\n            else:\\n                self.find_conflicts(specs, specs_to_add, history_specs)\\n\\n        speco = []  # optional packages\\n        specr = []  # requested packages\\n        speca = []  # all other packages\\n        specm = set(r2.groups)  # missing from specs\\n        for k, s in enumerate(specs):\\n            if s.name in specm:\\n                specm.remove(s.name)\\n            if not s.optional:\\n                (speca if s.target or k >= len0 else specr).append(s)\\n            elif any(r2.find_matches(s)):\\n                s = MatchSpec(s.name, optional=True, target=s.target)\\n                speco.append(s)\\n                speca.append(s)\\n        speca.extend(MatchSpec(s) for s in specm)\\n\\n        if log.isEnabledFor(DEBUG):\\n            log.debug(\\\"Requested specs: %s\\\", dashlist(sorted(str(s) for s in specr)))\\n            log.debug(\\\"Optional specs: %s\\\", dashlist(sorted(str(s) for s in speco)))\\n            log.debug(\\\"All other specs: %s\\\", dashlist(sorted(str(s) for s in speca)))\\n            log.debug(\\\"missing specs: %s\\\", dashlist(sorted(str(s) for s in specm)))\\n\\n        # Removed packages: minimize count\\n        log.debug(\\\"Solve: minimize removed packages\\\")\\n        if _remove:\\n            eq_optional_c = r2.generate_removal_count(C, speco)\\n            solution, obj7 = C.minimize(eq_optional_c, solution)\\n            log.debug(\\\"Package removal metric: %d\\\", obj7)\\n\\n        # Requested packages: maximize versions\\n        log.debug(\\\"Solve: maximize versions of requested packages\\\")\\n        eq_req_c, eq_req_v, eq_req_b, eq_req_a, eq_req_t = r2.generate_version_metrics(\\n            C, specr\\n        )\\n        solution, obj3a = C.minimize(eq_req_c, solution)\\n        solution, obj3 = C.minimize(eq_req_v, solution)\\n        log.debug(\\\"Initial package channel/version metric: %d/%d\\\", obj3a, obj3)\\n\\n        # Track features: minimize feature count\\n        log.debug(\\\"Solve: minimize track_feature count\\\")\\n        eq_feature_count = r2.generate_feature_count(C)\\n        solution, obj1 = C.minimize(eq_feature_count, solution)\\n        log.debug(\\\"Track feature count: %d\\\", obj1)\\n\\n        # Featured packages: minimize number of featureless packages\\n        # installed when a featured alternative is feasible.\\n        # For example, package name foo exists with two built packages. One with\\n        # 'track_features: 'feat1', and one with 'track_features': 'feat2'.\\n        # The previous \\\"Track features\\\" minimization pass has chosen 'feat1' for the\\n        # environment, but not 'feat2'. In this case, the 'feat2' version of foo is\\n        # considered \\\"featureless.\\\"\\n        eq_feature_metric = r2.generate_feature_metric(C)\\n        solution, obj2 = C.minimize(eq_feature_metric, solution)\\n        log.debug(\\\"Package misfeature count: %d\\\", obj2)\\n\\n        # Requested packages: maximize builds\\n        log.debug(\\\"Solve: maximize build numbers of requested packages\\\")\\n        solution, obj4 = C.minimize(eq_req_b, solution)\\n        log.debug(\\\"Initial package build metric: %d\\\", obj4)\\n\\n        # prefer arch packages where available for requested specs\\n        log.debug(\\\"Solve: prefer arch over noarch for requested packages\\\")\\n        solution, noarch_obj = C.minimize(eq_req_a, solution)\\n        log.debug(\\\"Noarch metric: %d\\\", noarch_obj)\\n\\n        # Optional installations: minimize count\\n        if not _remove:\\n            log.debug(\\\"Solve: minimize number of optional installations\\\")\\n            eq_optional_install = r2.generate_install_count(C, speco)\\n            solution, obj49 = C.minimize(eq_optional_install, solution)\\n            log.debug(\\\"Optional package install metric: %d\\\", obj49)\\n\\n        # Dependencies: minimize the number of packages that need upgrading\\n        log.debug(\\\"Solve: minimize number of necessary upgrades\\\")\\n        eq_u = r2.generate_update_count(C, speca)\\n        solution, obj50 = C.minimize(eq_u, solution)\\n        log.debug(\\\"Dependency update count: %d\\\", obj50)\\n\\n        # Remaining packages: maximize versions, then builds\\n        log.debug(\\n            \\\"Solve: maximize versions and builds of indirect dependencies.  \\\"\\n            \\\"Prefer arch over noarch where equivalent.\\\"\\n        )\\n        eq_c, eq_v, eq_b, eq_a, eq_t = r2.generate_version_metrics(C, speca)\\n        solution, obj5a = C.minimize(eq_c, solution)\\n        solution, obj5 = C.minimize(eq_v, solution)\\n        solution, obj6 = C.minimize(eq_b, solution)\\n        solution, obj6a = C.minimize(eq_a, solution)\\n        log.debug(\\n            \\\"Additional package channel/version/build/noarch metrics: %d/%d/%d/%d\\\",\\n            obj5a,\\n            obj5,\\n            obj6,\\n            obj6a,\\n        )\\n\\n        # Prune unnecessary packages\\n        log.debug(\\\"Solve: prune unnecessary packages\\\")\\n        eq_c = r2.generate_package_count(C, specm)\\n        solution, obj7 = C.minimize(eq_c, solution, trymax=True)\\n        log.debug(\\\"Weak dependency count: %d\\\", obj7)\\n\\n        if not is_converged(solution):\\n            # Maximize timestamps\\n            eq_t.update(eq_req_t)\\n            solution, obj6t = C.minimize(eq_t, solution)\\n            log.debug(\\\"Timestamp metric: %d\\\", obj6t)\\n\\n        log.debug(\\\"Looking for alternate solutions\\\")\\n        nsol = 1\\n        psolutions = []\\n        psolution = clean(solution)\\n        psolutions.append(psolution)\\n        while True:\\n            nclause = tuple(C.Not(C.from_name(q)) for q in psolution)\\n            solution = C.sat((nclause,), True)\\n            if solution is None:\\n                break\\n            nsol += 1\\n            if nsol > 10:\\n                log.debug(\\\"Too many solutions; terminating\\\")\\n                break\\n            psolution = clean(solution)\\n            psolutions.append(psolution)\\n\\n        if nsol > 1:\\n            psols2 = list(map(set, psolutions))\\n            common = set.intersection(*psols2)\\n            diffs = [sorted(set(sol) - common) for sol in psols2]\\n            if not context.json:\\n                stdoutlog.info(\\n                    \\\"\\\\nWarning: {} possible package resolutions \\\"\\n                    \\\"(only showing differing packages):{}{}\\\".format(\\n                        \\\">10\\\" if nsol > 10 else nsol,\\n                        dashlist(\\\", \\\".join(diff) for diff in diffs),\\n                        \\\"\\\\n  ... and others\\\" if nsol > 10 else \\\"\\\",\\n                    )\\n                )\\n\\n        # def stripfeat(sol):\\n        #     return sol.split('[')[0]\\n\\n        new_index = {self.to_sat_name(prec): prec for prec in self.index.values()}\\n\\n        if returnall:\\n            if len(psolutions) > 1:\\n                raise RuntimeError()\\n            # TODO: clean up this mess\\n            # return [sorted(Dist(stripfeat(dname)) for dname in psol) for psol in psolutions]\\n            # return [sorted((new_index[sat_name] for sat_name in psol), key=lambda x: x.name)\\n            #         for psol in psolutions]\\n\\n            # return sorted(Dist(stripfeat(dname)) for dname in psolutions[0])\\n        return sorted(\\n            (new_index[sat_name] for sat_name in psolutions[0]), key=lambda x: x.name\\n        )\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Error handling and error reporting.\\\"\\\"\\\"\\n\\nimport os\\nimport sys\\nfrom functools import lru_cache, partial\\nfrom logging import getLogger\\n\\nfrom .common.compat import ensure_text_type, on_win\\n\\nlog = getLogger(__name__)\\n\\n\\nclass ExceptionHandler:\\n    def __call__(self, func, *args, **kwargs):\\n        try:\\n            return func(*args, **kwargs)\\n        except:\\n            _, exc_val, exc_tb = sys.exc_info()\\n            return self.handle_exception(exc_val, exc_tb)\\n\\n    def write_out(self, *content):\\n        from logging import getLogger\\n\\n        from .cli.main import init_loggers\\n\\n        init_loggers()\\n        getLogger(\\\"conda.stderr\\\").info(\\\"\\\\n\\\".join(content))\\n\\n    @property\\n    def http_timeout(self):\\n        from .base.context import context\\n\\n        return context.remote_connect_timeout_secs, context.remote_read_timeout_secs\\n\\n    @property\\n    def user_agent(self):\\n        from .base.context import context\\n\\n        return context.user_agent\\n\\n    @property\\n    def error_upload_url(self):\\n        from .base.context import context\\n\\n        return context.error_upload_url\\n\\n    def handle_exception(self, exc_val, exc_tb):\\n        from errno import ENOSPC\\n\\n        from .exceptions import (\\n            CondaError,\\n            CondaMemoryError,\\n            NoSpaceLeftError,\\n        )\\n\\n        if isinstance(exc_val, CondaError):\\n            if exc_val.reportable:\\n                return self.handle_reportable_application_exception(exc_val, exc_tb)\\n            else:\\n                return self.handle_application_exception(exc_val, exc_tb)\\n        if isinstance(exc_val, EnvironmentError):\\n            if getattr(exc_val, \\\"errno\\\", None) == ENOSPC:\\n                return self.handle_application_exception(\\n                    NoSpaceLeftError(exc_val), exc_tb\\n                )\\n        if isinstance(exc_val, MemoryError):\\n            return self.handle_application_exception(CondaMemoryError(exc_val), exc_tb)\\n        if isinstance(exc_val, KeyboardInterrupt):\\n            self._print_conda_exception(CondaError(\\\"KeyboardInterrupt\\\"), exc_tb)\\n            return 1\\n        if isinstance(exc_val, SystemExit):\\n            return exc_val.code\\n        return self.handle_unexpected_exception(exc_val, exc_tb)\\n\\n    def handle_application_exception(self, exc_val, exc_tb):\\n        self._print_conda_exception(exc_val, exc_tb)\\n        return exc_val.return_code\\n\\n    def _print_conda_exception(self, exc_val, exc_tb):\\n        from .exceptions import print_conda_exception\\n\\n        print_conda_exception(exc_val, exc_tb)\\n\\n    def handle_unexpected_exception(self, exc_val, exc_tb):\\n        error_report = self.get_error_report(exc_val, exc_tb)\\n        self.print_unexpected_error_report(error_report)\\n        self._upload(error_report)\\n        rc = getattr(exc_val, \\\"return_code\\\", None)\\n        return rc if rc is not None else 1\\n\\n    def handle_reportable_application_exception(self, exc_val, exc_tb):\\n        error_report = self.get_error_report(exc_val, exc_tb)\\n        from .base.context import context\\n\\n        if context.json:\\n            error_report.update(exc_val.dump_map())\\n        self.print_expected_error_report(error_report)\\n        self._upload(error_report)\\n        return exc_val.return_code\\n\\n    def get_error_report(self, exc_val, exc_tb):\\n        from .exceptions import CondaError, _format_exc\\n\\n        command = \\\" \\\".join(ensure_text_type(s) for s in sys.argv)\\n        info_dict = {}\\n        if \\\" info\\\" not in command:\\n            # get info_dict, but if we get an exception here too, record it without trampling\\n            # the original exception\\n            try:\\n                from .cli.main_info import get_info_dict\\n\\n                info_dict = get_info_dict()\\n            except Exception as info_e:\\n                info_traceback = _format_exc()\\n                info_dict = {\\n                    \\\"error\\\": repr(info_e),\\n                    \\\"exception_name\\\": info_e.__class__.__name__,\\n                    \\\"exception_type\\\": str(exc_val.__class__),\\n                    \\\"traceback\\\": info_traceback,\\n                }\\n\\n        error_report = {\\n            \\\"error\\\": repr(exc_val),\\n            \\\"exception_name\\\": exc_val.__class__.__name__,\\n            \\\"exception_type\\\": str(exc_val.__class__),\\n            \\\"command\\\": command,\\n            \\\"traceback\\\": _format_exc(exc_val, exc_tb),\\n            \\\"conda_info\\\": info_dict,\\n        }\\n\\n        if isinstance(exc_val, CondaError):\\n            error_report[\\\"conda_error_components\\\"] = exc_val.dump_map()\\n\\n        return error_report\\n\\n    def print_unexpected_error_report(self, error_report):\\n        from .base.context import context\\n\\n        if context.json:\\n            from .cli.common import stdout_json\\n\\n            stdout_json(error_report)\\n        else:\\n            message_builder = []\\n            message_builder.append(\\\"\\\")\\n            message_builder.append(\\n                \\\"# >>>>>>>>>>>>>>>>>>>>>> ERROR REPORT <<<<<<<<<<<<<<<<<<<<<<\\\"\\n            )\\n            message_builder.append(\\\"\\\")\\n            message_builder.extend(\\n                \\\"    \\\" + line for line in error_report[\\\"traceback\\\"].splitlines()\\n            )\\n            message_builder.append(\\\"\\\")\\n            message_builder.append(\\\"`$ {}`\\\".format(error_report[\\\"command\\\"]))\\n            message_builder.append(\\\"\\\")\\n            if error_report[\\\"conda_info\\\"]:\\n                from .cli.main_info import get_env_vars_str, get_main_info_str\\n\\n                try:\\n                    # TODO: Sanitize env vars to remove secrets (e.g credentials for PROXY)\\n                    message_builder.append(get_env_vars_str(error_report[\\\"conda_info\\\"]))\\n                    message_builder.append(\\n                        get_main_info_str(error_report[\\\"conda_info\\\"])\\n                    )\\n                except Exception as e:\\n                    log.warning(\\\"%r\\\", e, exc_info=True)\\n                    message_builder.append(\\\"conda info could not be constructed.\\\")\\n                    message_builder.append(f\\\"{e!r}\\\")\\n            message_builder.extend(\\n                [\\n                    \\\"\\\",\\n                    \\\"An unexpected error has occurred. Conda has prepared the above report.\\\"\\n                    \\\"\\\",\\n                    \\\"If you suspect this error is being caused by a malfunctioning plugin,\\\",\\n                    \\\"consider using the --no-plugins option to turn off plugins.\\\",\\n                    \\\"\\\",\\n                    \\\"Example: conda --no-plugins install <package>\\\",\\n                    \\\"\\\",\\n                    \\\"Alternatively, you can set the CONDA_NO_PLUGINS environment variable on\\\",\\n                    \\\"the command line to run the command without plugins enabled.\\\",\\n                    \\\"\\\",\\n                    \\\"Example: CONDA_NO_PLUGINS=true conda install <package>\\\",\\n                    \\\"\\\",\\n                ]\\n            )\\n            self.write_out(*message_builder)\\n\\n    def print_expected_error_report(self, error_report):\\n        from .base.context import context\\n\\n        if context.json:\\n            from .cli.common import stdout_json\\n\\n            stdout_json(error_report)\\n        else:\\n            message_builder = []\\n            message_builder.append(\\\"\\\")\\n            message_builder.append(\\n                \\\"# >>>>>>>>>>>>>>>>>>>>>> ERROR REPORT <<<<<<<<<<<<<<<<<<<<<<\\\"\\n            )\\n            message_builder.append(\\\"\\\")\\n            message_builder.append(\\\"`$ {}`\\\".format(error_report[\\\"command\\\"]))\\n            message_builder.append(\\\"\\\")\\n            if error_report[\\\"conda_info\\\"]:\\n                from .cli.main_info import get_env_vars_str, get_main_info_str\\n\\n                try:\\n                    # TODO: Sanitize env vars to remove secrets (e.g credentials for PROXY)\\n                    message_builder.append(get_env_vars_str(error_report[\\\"conda_info\\\"]))\\n                    message_builder.append(\\n                        get_main_info_str(error_report[\\\"conda_info\\\"])\\n                    )\\n                except Exception as e:\\n                    log.warning(\\\"%r\\\", e, exc_info=True)\\n                    message_builder.append(\\\"conda info could not be constructed.\\\")\\n                    message_builder.append(f\\\"{e!r}\\\")\\n            message_builder.append(\\\"\\\")\\n            message_builder.append(\\n                \\\"V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V\\\"\\n            )\\n            message_builder.append(\\\"\\\")\\n\\n            message_builder.extend(error_report[\\\"error\\\"].splitlines())\\n            message_builder.append(\\\"\\\")\\n\\n            message_builder.append(\\n                \\\"A reportable application error has occurred. Conda has prepared the above report.\\\"\\n            )\\n            message_builder.append(\\\"\\\")\\n            self.write_out(*message_builder)\\n\\n    # FUTURE: Python 3.8+, replace with functools.cached_property\\n    @property\\n    @lru_cache(maxsize=None)\\n    def _isatty(self):\\n        try:\\n            return os.isatty(0) or on_win\\n        except Exception as e:\\n            log.debug(\\\"%r\\\", e)\\n            return True\\n\\n    def _upload(self, error_report) -> None:\\n        \\\"\\\"\\\"Determine whether or not to upload the error report.\\\"\\\"\\\"\\n        from .base.context import context\\n\\n        post_upload = False\\n        if context.report_errors is False:\\n            # no prompt and no submission\\n            do_upload = False\\n        elif context.report_errors is True or context.always_yes:\\n            # no prompt and submit\\n            do_upload = True\\n        elif context.json or context.quiet or not self._isatty:\\n            # never prompt under these conditions, submit iff always_yes\\n            do_upload = bool(not context.offline and context.always_yes)\\n        else:\\n            # prompt whether to submit\\n            do_upload = self._ask_upload()\\n            post_upload = True\\n\\n        # the upload state is one of the following:\\n        #   - True: upload error report\\n        #   - False: do not upload error report\\n        #   - None: while prompting a timeout occurred\\n\\n        if do_upload:\\n            # user wants report to be submitted\\n            self._execute_upload(error_report)\\n\\n        if post_upload:\\n            # post submission text\\n            self._post_upload(do_upload)\\n\\n    def _ask_upload(self):\\n        from .auxlib.type_coercion import boolify\\n        from .common.io import timeout\\n\\n        try:\\n            do_upload = timeout(\\n                40,\\n                partial(\\n                    input,\\n                    \\\"If submitted, this report will be used by core maintainers to improve\\\\n\\\"\\n                    \\\"future releases of conda.\\\\n\\\"\\n                    \\\"Would you like conda to send this report to the core maintainers? \\\"\\n                    \\\"[y/N]: \\\",\\n                ),\\n            )\\n            return do_upload and boolify(do_upload)\\n        except Exception as e:\\n            log.debug(\\\"%r\\\", e)\\n            return False\\n\\n    def _execute_upload(self, error_report):\\n        import getpass\\n        import json\\n\\n        from .auxlib.entity import EntityEncoder\\n\\n        headers = {\\n            \\\"User-Agent\\\": self.user_agent,\\n        }\\n        _timeout = self.http_timeout\\n        username = getpass.getuser()\\n        error_report[\\\"is_ascii\\\"] = (\\n            True if all(ord(c) < 128 for c in username) else False\\n        )\\n        error_report[\\\"has_spaces\\\"] = True if \\\" \\\" in str(username) else False\\n        data = json.dumps(error_report, sort_keys=True, cls=EntityEncoder) + \\\"\\\\n\\\"\\n        data = data.replace(str(username), \\\"USERNAME_REMOVED\\\")\\n        response = None\\n        try:\\n            # requests does not follow HTTP standards for redirects of non-GET methods\\n            # That is, when following a 301 or 302, it turns a POST into a GET.\\n            # And no way to disable.  WTF\\n            import requests\\n\\n            redirect_counter = 0\\n            url = self.error_upload_url\\n            response = requests.post(\\n                url, headers=headers, timeout=_timeout, data=data, allow_redirects=False\\n            )\\n            response.raise_for_status()\\n            while response.status_code in (301, 302) and response.headers.get(\\n                \\\"Location\\\"\\n            ):\\n                url = response.headers[\\\"Location\\\"]\\n                response = requests.post(\\n                    url,\\n                    headers=headers,\\n                    timeout=_timeout,\\n                    data=data,\\n                    allow_redirects=False,\\n                )\\n                response.raise_for_status()\\n                redirect_counter += 1\\n                if redirect_counter > 15:\\n                    from . import CondaError\\n\\n                    raise CondaError(\\\"Redirect limit exceeded\\\")\\n            log.debug(\\\"upload response status: %s\\\", response and response.status_code)\\n        except Exception as e:  # pragma: no cover\\n            log.info(\\\"%r\\\", e)\\n        try:\\n            if response and response.ok:\\n                self.write_out(\\\"Upload successful.\\\")\\n            else:\\n                self.write_out(\\\"Upload did not complete.\\\")\\n                if response and response.status_code:\\n                    self.write_out(f\\\" HTTP {response.status_code}\\\")\\n        except Exception as e:\\n            log.debug(f\\\"{e!r}\\\")\\n\\n    def _post_upload(self, do_upload):\\n        if do_upload is True:\\n            # report was submitted\\n            self.write_out(\\n                \\\"\\\",\\n                \\\"Thank you for helping to improve conda.\\\",\\n                \\\"Opt-in to always sending reports (and not see this message again)\\\",\\n                \\\"by running\\\",\\n                \\\"\\\",\\n                \\\"    $ conda config --set report_errors true\\\",\\n                \\\"\\\",\\n            )\\n        elif do_upload is None:\\n            # timeout was reached while prompting user\\n            self.write_out(\\n                \\\"\\\",\\n                \\\"Timeout reached. No report sent.\\\",\\n                \\\"\\\",\\n            )\\n        else:\\n            # no report submitted\\n            self.write_out(\\n                \\\"\\\",\\n                \\\"No report sent. To permanently opt-out, use\\\",\\n                \\\"\\\",\\n                \\\"    $ conda config --set report_errors false\\\",\\n                \\\"\\\",\\n            )\\n\\n\\ndef conda_exception_handler(func, *args, **kwargs):\\n    exception_handler = ExceptionHandler()\\n    return_value = exception_handler(func, *args, **kwargs)\\n    return return_value\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Backported exports for conda-build.\\\"\\\"\\\"\\n\\nimport errno\\nimport functools\\nimport os\\nfrom builtins import input  # noqa: F401, UP029\\nfrom io import StringIO  # noqa: F401, for conda-build\\n\\nfrom . import CondaError, plan  # noqa: F401\\nfrom .auxlib.entity import EntityEncoder  # noqa: F401\\nfrom .base.constants import (  # noqa: F401\\n    DEFAULT_CHANNELS,\\n    DEFAULT_CHANNELS_UNIX,\\n    DEFAULT_CHANNELS_WIN,\\n    PREFIX_PLACEHOLDER,\\n)\\nfrom .base.context import (  # noqa: F401\\n    context,\\n    non_x86_machines,\\n    reset_context,\\n    sys_rc_path,\\n)\\nfrom .cli.common import spec_from_line, specs_from_args, specs_from_url  # noqa: F401\\nfrom .cli.conda_argparse import ArgumentParser  # noqa: F401\\nfrom .cli.helpers import (  # noqa: F401\\n    add_parser_channels,\\n    add_parser_prefix,\\n)\\nfrom .common import compat  # noqa: F401\\nfrom .common.compat import on_win  # noqa: F401\\nfrom .common.path import win_path_to_unix  # noqa: F401\\nfrom .common.toposort import _toposort  # noqa: F401\\nfrom .core.index import dist_str_in_index  # noqa: F401\\nfrom .core.index import fetch_index as _fetch_index  # noqa: F401\\nfrom .core.index import get_index as _get_index\\nfrom .core.package_cache_data import ProgressiveFetchExtract, rm_fetched  # noqa: F401\\nfrom .core.prefix_data import delete_prefix_from_linked_data\\nfrom .core.solve import Solver  # noqa: F401\\nfrom .core.subdir_data import cache_fn_url  # noqa: F401\\nfrom .deprecations import deprecated\\nfrom .exceptions import (  # noqa: F401\\n    CondaHTTPError,\\n    CondaOSError,\\n    LinkError,\\n    LockError,\\n    PaddingError,\\n    PathNotFoundError,\\n    UnsatisfiableError,\\n)\\nfrom .gateways.connection.download import TmpDownload  # noqa: F401\\nfrom .gateways.connection.download import download as _download  # noqa: F401\\nfrom .gateways.connection.session import CondaSession  # noqa: F401\\nfrom .gateways.disk.create import TemporaryDirectory  # noqa: F401\\nfrom .gateways.disk.delete import delete_trash, move_to_trash  # noqa: F401\\nfrom .gateways.disk.delete import rm_rf as _rm_rf\\nfrom .gateways.disk.link import lchmod  # noqa: F401\\nfrom .gateways.subprocess import ACTIVE_SUBPROCESSES, subprocess_call  # noqa: F401\\nfrom .misc import untracked, walk_prefix  # noqa: F401\\nfrom .models.channel import Channel, get_conda_build_local_url  # noqa: F401\\nfrom .models.dist import Dist\\nfrom .models.enums import FileMode, PathType  # noqa: F401\\nfrom .models.records import PackageRecord\\nfrom .models.version import VersionOrder, normalized_version  # noqa: F401\\nfrom .plan import display_actions as _display_actions\\nfrom .plan import (  # noqa: F401\\n    execute_actions,\\n    execute_instructions,\\n    execute_plan,\\n    install_actions,\\n)\\nfrom .resolve import (  # noqa: F401\\n    MatchSpec,\\n    Resolve,\\n    ResolvePackageNotFound,\\n    Unsatisfiable,\\n)\\nfrom .utils import human_bytes, unix_path_to_win, url_path  # noqa: F401\\n\\nreset_context()  # initialize context when conda.exports is imported\\n\\n\\nNoPackagesFound = NoPackagesFoundError = ResolvePackageNotFound\\nnon_x86_linux_machines = non_x86_machines\\nget_default_urls = lambda: DEFAULT_CHANNELS  # noqa: E731\\n_PREFIX_PLACEHOLDER = prefix_placeholder = PREFIX_PLACEHOLDER\\narch_name = context.arch_name\\nbinstar_upload = context.anaconda_upload\\nbits = context.bits\\ndefault_prefix = context.default_prefix\\ndefault_python = context.default_python\\nenvs_dirs = context.envs_dirs\\npkgs_dirs = context.pkgs_dirs\\nplatform = context.platform\\nroot_dir = context.root_prefix\\nroot_writable = context.root_writable\\nsubdir = context.subdir\\nconda_build = context.conda_build\\nget_rc_urls = lambda: list(context.channels)  # noqa: E731\\nget_local_urls = lambda: list(get_conda_build_local_url()) or []  # noqa: E731\\nload_condarc = lambda fn: reset_context([fn])  # noqa: E731\\nPaddingError = PaddingError\\nLinkError = LinkError\\nCondaOSError = CondaOSError\\n# PathNotFoundError is the conda 4.4.x name for it - let's plan ahead.\\nCondaFileNotFoundError = PathNotFoundError\\ndeprecated.constant(\\n    \\\"24.3\\\",\\n    \\\"24.9\\\",\\n    \\\"IndexRecord\\\",\\n    PackageRecord,\\n    addendum=\\\"Use `conda.models.records.PackageRecord` instead.\\\",\\n)\\n# Replacements for six exports for compatibility\\nPY3 = True  # noqa: F401\\nstring_types = str  # noqa: F401\\ntext_type = str  # noqa: F401\\n\\n\\n@deprecated(\\n    \\\"25.3\\\",\\n    \\\"25.9\\\",\\n    addendum=\\\"Use builtin `dict.items()` instead.\\\",\\n)\\ndef iteritems(d, **kw):\\n    return iter(d.items(**kw))\\n\\n\\n@deprecated(\\\"25.3\\\", \\\"25.9\\\", addendum=\\\"Unused.\\\")\\nclass Completer:  # pragma: no cover\\n    def get_items(self):\\n        return self._get_items()\\n\\n    def __contains__(self, item):\\n        return True\\n\\n    def __iter__(self):\\n        return iter(self.get_items())\\n\\n\\n@deprecated(\\\"25.3\\\", \\\"25.9\\\", addendum=\\\"Unused.\\\")\\nclass InstalledPackages:\\n    pass\\n\\n\\ndef rm_rf(path, max_retries=5, trash=True):\\n    _rm_rf(path, max_retries, trash)\\n    delete_prefix_from_linked_data(path)\\n\\n\\ndeprecated.constant(\\\"25.3\\\", \\\"25.9\\\", \\\"KEYS\\\", None, addendum=\\\"Unused.\\\")\\ndeprecated.constant(\\\"25.3\\\", \\\"25.9\\\", \\\"KEYS_DIR\\\", None, addendum=\\\"Unused.\\\")\\n\\n\\n@deprecated(\\\"25.3\\\", \\\"25.9\\\", addendum=\\\"Unused.\\\")\\ndef hash_file(_):\\n    return None  # pragma: no cover\\n\\n\\n@deprecated(\\\"25.3\\\", \\\"25.9\\\", addendum=\\\"Unused.\\\")\\ndef verify(_):\\n    return False  # pragma: no cover\\n\\n\\ndef display_actions(\\n    actions, index, show_channel_urls=None, specs_to_remove=(), specs_to_add=()\\n):\\n    if \\\"FETCH\\\" in actions:\\n        actions[\\\"FETCH\\\"] = [index[d] for d in actions[\\\"FETCH\\\"]]\\n    if \\\"LINK\\\" in actions:\\n        actions[\\\"LINK\\\"] = [index[d] for d in actions[\\\"LINK\\\"]]\\n    if \\\"UNLINK\\\" in actions:\\n        actions[\\\"UNLINK\\\"] = [index[d] for d in actions[\\\"UNLINK\\\"]]\\n    index = {prec: prec for prec in index.values()}\\n    return _display_actions(\\n        actions, index, show_channel_urls, specs_to_remove, specs_to_add\\n    )\\n\\n\\ndef get_index(\\n    channel_urls=(),\\n    prepend=True,\\n    platform=None,\\n    use_local=False,\\n    use_cache=False,\\n    unknown=None,\\n    prefix=None,\\n):\\n    index = _get_index(\\n        channel_urls, prepend, platform, use_local, use_cache, unknown, prefix\\n    )\\n    return {Dist(prec): prec for prec in index.values()}\\n\\n\\n@deprecated(\\\"24.3\\\", \\\"24.9\\\", addendum=\\\"Use `conda.core.index.fetch_index` instead.\\\")\\ndef fetch_index(channel_urls, use_cache=False, index=None):\\n    index = _fetch_index(channel_urls, use_cache, index)\\n    return {Dist(prec): prec for prec in index.values()}\\n\\n\\ndef package_cache():\\n    from .core.package_cache_data import PackageCacheData\\n\\n    class package_cache:\\n        def __contains__(self, dist):\\n            return bool(\\n                PackageCacheData.first_writable().get(Dist(dist).to_package_ref(), None)\\n            )\\n\\n        def keys(self):\\n            return (Dist(v) for v in PackageCacheData.first_writable().values())\\n\\n        def __delitem__(self, dist):\\n            PackageCacheData.first_writable().remove(Dist(dist).to_package_ref())\\n\\n    return package_cache()\\n\\n\\n@deprecated(\\\"25.3\\\", \\\"25.9\\\", addendum=\\\"Use `conda.activate` instead.\\\")\\ndef symlink_conda(prefix, root_dir, shell=None):  # pragma: no cover\\n    # do not symlink root env - this clobbers activate incorrectly.\\n    # prefix should always be longer than, or outside the root dir.\\n    if os.path.normcase(os.path.normpath(prefix)) in os.path.normcase(\\n        os.path.normpath(root_dir)\\n    ):\\n        return\\n    if on_win:\\n        where = \\\"condabin\\\"\\n        symlink_fn = functools.partial(win_conda_bat_redirect, shell=shell)\\n    else:\\n        where = \\\"bin\\\"\\n        symlink_fn = os.symlink\\n    if not os.path.isdir(os.path.join(prefix, where)):\\n        os.makedirs(os.path.join(prefix, where))\\n    _symlink_conda_hlp(prefix, root_dir, where, symlink_fn)\\n\\n\\n@deprecated(\\\"25.3\\\", \\\"25.9\\\", addendum=\\\"Use `conda.activate` instead.\\\")\\ndef _symlink_conda_hlp(prefix, root_dir, where, symlink_fn):  # pragma: no cover\\n    scripts = [\\\"conda\\\", \\\"activate\\\", \\\"deactivate\\\"]\\n    prefix_where = os.path.join(prefix, where)\\n    if not os.path.isdir(prefix_where):\\n        os.makedirs(prefix_where)\\n    for f in scripts:\\n        root_file = os.path.join(root_dir, where, f)\\n        prefix_file = os.path.join(prefix_where, f)\\n        try:\\n            # try to kill stale links if they exist\\n            if os.path.lexists(prefix_file):\\n                rm_rf(prefix_file)\\n            # if they're in use, they won't be killed.  Skip making new symlink.\\n            if not os.path.lexists(prefix_file):\\n                symlink_fn(root_file, prefix_file)\\n        except OSError as e:\\n            if os.path.lexists(prefix_file) and (\\n                e.errno in (errno.EPERM, errno.EACCES, errno.EROFS, errno.EEXIST)\\n            ):\\n                # Cannot symlink root_file to prefix_file. Ignoring since link already exists\\n                pass\\n            else:\\n                raise\\n\\n\\nif on_win:  # pragma: no cover\\n\\n    @deprecated(\\\"25.3\\\", \\\"25.9\\\", addendum=\\\"Use `conda.activate` instead.\\\")\\n    def win_conda_bat_redirect(src, dst, shell):\\n        \\\"\\\"\\\"Special function for Windows XP where the `CreateSymbolicLink`\\n        function is not available.\\n\\n        Simply creates a `.bat` file at `dst` which calls `src` together with\\n        all command line arguments.\\n\\n        Works of course only with callable files, e.g. `.bat` or `.exe` files.\\n        \\\"\\\"\\\"\\n        from .utils import _SHELLS\\n\\n        try:\\n            os.makedirs(os.path.dirname(dst))\\n        except OSError as exc:  # Python >2.5\\n            if exc.errno == errno.EEXIST and os.path.isdir(os.path.dirname(dst)):\\n                pass\\n            else:\\n                raise\\n\\n        # bat file redirect\\n        if not os.path.isfile(dst + \\\".bat\\\"):\\n            with open(dst + \\\".bat\\\", \\\"w\\\") as f:\\n                f.write(f'@echo off\\\\ncall \\\"{src}\\\" %*\\\\n')\\n\\n        # TODO: probably need one here for powershell at some point\\n\\n        # This one is for bash/cygwin/msys\\n        # set default shell to bash.exe when not provided, as that's most common\\n        if not shell:\\n            shell = \\\"bash.exe\\\"\\n\\n        # technically these are \\\"links\\\" - but islink doesn't work on win\\n        if not os.path.isfile(dst):\\n            with open(dst, \\\"w\\\") as f:\\n                f.write(\\\"#!/usr/bin/env bash \\\\n\\\")\\n                if src.endswith(\\\"conda\\\"):\\n                    f.write('{} \\\"$@\\\"'.format(_SHELLS[shell][\\\"path_to\\\"](src + \\\".exe\\\")))\\n                else:\\n                    f.write('source {} \\\"$@\\\"'.format(_SHELLS[shell][\\\"path_to\\\"](src)))\\n            # Make the new file executable\\n            # http://stackoverflow.com/a/30463972/1170370\\n            mode = os.stat(dst).st_mode\\n            mode |= (mode & 292) >> 2  # copy R bits to X\\n            os.chmod(dst, mode)\\n\\n\\ndef linked_data(prefix, ignore_channels=False):\\n    \\\"\\\"\\\"Return a dictionary of the linked packages in prefix.\\\"\\\"\\\"\\n    from .core.prefix_data import PrefixData\\n    from .models.dist import Dist\\n\\n    pd = PrefixData(prefix)\\n    return {\\n        Dist(prefix_record): prefix_record\\n        for prefix_record in pd._prefix_records.values()\\n    }\\n\\n\\ndef linked(prefix, ignore_channels=False):\\n    \\\"\\\"\\\"Return the Dists of linked packages in prefix.\\\"\\\"\\\"\\n    from .models.enums import PackageType\\n\\n    conda_package_types = PackageType.conda_package_types()\\n    ld = linked_data(prefix, ignore_channels=ignore_channels).items()\\n    return {\\n        dist\\n        for dist, prefix_rec in ld\\n        if prefix_rec.package_type in conda_package_types\\n    }\\n\\n\\n# exports\\ndef is_linked(prefix, dist):\\n    \\\"\\\"\\\"\\n    Return the install metadata for a linked package in a prefix, or None\\n    if the package is not linked in the prefix.\\n    \\\"\\\"\\\"\\n    # FIXME Functions that begin with `is_` should return True/False\\n    from .core.prefix_data import PrefixData\\n\\n    pd = PrefixData(prefix)\\n    prefix_record = pd.get(dist.name, None)\\n    if prefix_record is None:\\n        return None\\n    elif MatchSpec(dist).match(prefix_record):\\n        return prefix_record\\n    else:\\n        return None\\n\\n\\ndef download(\\n    url,\\n    dst_path,\\n    session=None,\\n    md5sum=None,\\n    urlstxt=False,\\n    retries=3,\\n    sha256=None,\\n    size=None,\\n):\\n    return _download(url, dst_path, md5=md5sum, sha256=sha256, size=size)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Miscellaneous utility functions.\\\"\\\"\\\"\\n\\nimport os\\nimport re\\nimport shutil\\nimport sys\\nfrom collections import defaultdict\\nfrom logging import getLogger\\nfrom os.path import abspath, dirname, exists, isdir, isfile, join, relpath\\n\\nfrom .base.context import context\\nfrom .common.compat import on_mac, on_win, open\\nfrom .common.io import dashlist\\nfrom .common.path import expand\\nfrom .common.url import is_url, join_url, path_to_url\\nfrom .core.index import get_index\\nfrom .core.link import PrefixSetup, UnlinkLinkTransaction\\nfrom .core.package_cache_data import PackageCacheData, ProgressiveFetchExtract\\nfrom .core.prefix_data import PrefixData\\nfrom .exceptions import (\\n    CondaExitZero,\\n    DisallowedPackageError,\\n    DryRunExit,\\n    PackagesNotFoundError,\\n    ParseError,\\n)\\nfrom .gateways.disk.delete import rm_rf\\nfrom .gateways.disk.link import islink, readlink, symlink\\nfrom .models.match_spec import ChannelMatch, MatchSpec\\nfrom .models.prefix_graph import PrefixGraph\\n\\nlog = getLogger(__name__)\\n\\n\\ndef conda_installed_files(prefix, exclude_self_build=False):\\n    \\\"\\\"\\\"\\n    Return the set of files which have been installed (using conda) into\\n    a given prefix.\\n    \\\"\\\"\\\"\\n    res = set()\\n    for meta in PrefixData(prefix).iter_records():\\n        if exclude_self_build and \\\"file_hash\\\" in meta:\\n            continue\\n        res.update(set(meta.get(\\\"files\\\", ())))\\n    return res\\n\\n\\nurl_pat = re.compile(\\n    r\\\"(?:(?P<url_p>.+)(?:[/\\\\\\\\]))?\\\"\\n    r\\\"(?P<fn>[^/\\\\\\\\#]+(?:\\\\.tar\\\\.bz2|\\\\.conda))\\\"\\n    r\\\"(:?#(?P<md5>[0-9a-f]{32}))?$\\\"\\n)\\n\\n\\ndef explicit(\\n    specs, prefix, verbose=False, force_extract=True, index_args=None, index=None\\n):\\n    actions = defaultdict(list)\\n    actions[\\\"PREFIX\\\"] = prefix\\n\\n    fetch_specs = []\\n    for spec in specs:\\n        if spec == \\\"@EXPLICIT\\\":\\n            continue\\n\\n        if not is_url(spec):\\n            \\\"\\\"\\\"\\n            # This does not work because url_to_path does not enforce Windows\\n            # backslashes. Should it? Seems like a dangerous change to make but\\n            # it would be cleaner.\\n            expanded = expand(spec)\\n            urled = path_to_url(expanded)\\n            pathed = url_to_path(urled)\\n            assert pathed == expanded\\n            \\\"\\\"\\\"\\n            spec = path_to_url(expand(spec))\\n\\n        # parse URL\\n        m = url_pat.match(spec)\\n        if m is None:\\n            raise ParseError(f\\\"Could not parse explicit URL: {spec}\\\")\\n        url_p, fn, md5sum = m.group(\\\"url_p\\\"), m.group(\\\"fn\\\"), m.group(\\\"md5\\\")\\n        url = join_url(url_p, fn)\\n        # url_p is everything but the tarball_basename and the md5sum\\n\\n        fetch_specs.append(MatchSpec(url, md5=md5sum) if md5sum else MatchSpec(url))\\n\\n    if context.dry_run:\\n        raise DryRunExit()\\n\\n    pfe = ProgressiveFetchExtract(fetch_specs)\\n    pfe.execute()\\n\\n    if context.download_only:\\n        raise CondaExitZero(\\n            \\\"Package caches prepared. \\\"\\n            \\\"UnlinkLinkTransaction cancelled with --download-only option.\\\"\\n        )\\n\\n    # now make an UnlinkLinkTransaction with the PackageCacheRecords as inputs\\n    # need to add package name to fetch_specs so that history parsing keeps track of them correctly\\n    specs_pcrecs = tuple(\\n        [spec, next(PackageCacheData.query_all(spec), None)] for spec in fetch_specs\\n    )\\n\\n    # Assert that every spec has a PackageCacheRecord\\n    specs_with_missing_pcrecs = [\\n        str(spec) for spec, pcrec in specs_pcrecs if pcrec is None\\n    ]\\n    if specs_with_missing_pcrecs:\\n        if len(specs_with_missing_pcrecs) == len(specs_pcrecs):\\n            raise AssertionError(\\\"No package cache records found\\\")\\n        else:\\n            missing_precs_list = \\\", \\\".join(specs_with_missing_pcrecs)\\n            raise AssertionError(\\n                f\\\"Missing package cache records for: {missing_precs_list}\\\"\\n            )\\n\\n    precs_to_remove = []\\n    prefix_data = PrefixData(prefix)\\n    for q, (spec, pcrec) in enumerate(specs_pcrecs):\\n        new_spec = MatchSpec(spec, name=pcrec.name)\\n        specs_pcrecs[q][0] = new_spec\\n\\n        prec = prefix_data.get(pcrec.name, None)\\n        if prec:\\n            # If we've already got matching specifications, then don't bother re-linking it\\n            if next(prefix_data.query(new_spec), None):\\n                specs_pcrecs[q][0] = None\\n            else:\\n                precs_to_remove.append(prec)\\n\\n    stp = PrefixSetup(\\n        prefix,\\n        precs_to_remove,\\n        tuple(sp[1] for sp in specs_pcrecs if sp[0]),\\n        (),\\n        tuple(sp[0] for sp in specs_pcrecs if sp[0]),\\n        (),\\n    )\\n\\n    txn = UnlinkLinkTransaction(stp)\\n    if not context.json and not context.quiet:\\n        txn.print_transaction_summary()\\n    txn.execute()\\n\\n\\ndef rel_path(prefix, path, windows_forward_slashes=True):\\n    res = path[len(prefix) + 1 :]\\n    if on_win and windows_forward_slashes:\\n        res = res.replace(\\\"\\\\\\\\\\\", \\\"/\\\")\\n    return res\\n\\n\\ndef walk_prefix(prefix, ignore_predefined_files=True, windows_forward_slashes=True):\\n    \\\"\\\"\\\"Return the set of all files in a given prefix directory.\\\"\\\"\\\"\\n    res = set()\\n    prefix = abspath(prefix)\\n    ignore = {\\n        \\\"pkgs\\\",\\n        \\\"envs\\\",\\n        \\\"conda-bld\\\",\\n        \\\"conda-meta\\\",\\n        \\\".conda_lock\\\",\\n        \\\"users\\\",\\n        \\\"LICENSE.txt\\\",\\n        \\\"info\\\",\\n        \\\"conda-recipes\\\",\\n        \\\".index\\\",\\n        \\\".unionfs\\\",\\n        \\\".nonadmin\\\",\\n    }\\n    binignore = {\\\"conda\\\", \\\"activate\\\", \\\"deactivate\\\"}\\n    if on_mac:\\n        ignore.update({\\\"python.app\\\", \\\"Launcher.app\\\"})\\n    for fn in (entry.name for entry in os.scandir(prefix)):\\n        if ignore_predefined_files and fn in ignore:\\n            continue\\n        if isfile(join(prefix, fn)):\\n            res.add(fn)\\n            continue\\n        for root, dirs, files in os.walk(join(prefix, fn)):\\n            should_ignore = ignore_predefined_files and root == join(prefix, \\\"bin\\\")\\n            for fn2 in files:\\n                if should_ignore and fn2 in binignore:\\n                    continue\\n                res.add(relpath(join(root, fn2), prefix))\\n            for dn in dirs:\\n                path = join(root, dn)\\n                if islink(path):\\n                    res.add(relpath(path, prefix))\\n\\n    if on_win and windows_forward_slashes:\\n        return {path.replace(\\\"\\\\\\\\\\\", \\\"/\\\") for path in res}\\n    else:\\n        return res\\n\\n\\ndef untracked(prefix, exclude_self_build=False):\\n    \\\"\\\"\\\"Return (the set) of all untracked files for a given prefix.\\\"\\\"\\\"\\n    conda_files = conda_installed_files(prefix, exclude_self_build)\\n    return {\\n        path\\n        for path in walk_prefix(prefix) - conda_files\\n        if not (\\n            path.endswith(\\\"~\\\")\\n            or on_mac\\n            and path.endswith(\\\".DS_Store\\\")\\n            or path.endswith(\\\".pyc\\\")\\n            and path[:-1] in conda_files\\n        )\\n    }\\n\\n\\ndef touch_nonadmin(prefix):\\n    \\\"\\\"\\\"Creates $PREFIX/.nonadmin if sys.prefix/.nonadmin exists (on Windows).\\\"\\\"\\\"\\n    if on_win and exists(join(context.root_prefix, \\\".nonadmin\\\")):\\n        if not isdir(prefix):\\n            os.makedirs(prefix)\\n        with open(join(prefix, \\\".nonadmin\\\"), \\\"w\\\") as fo:\\n            fo.write(\\\"\\\")\\n\\n\\ndef clone_env(prefix1, prefix2, verbose=True, quiet=False, index_args=None):\\n    \\\"\\\"\\\"Clone existing prefix1 into new prefix2.\\\"\\\"\\\"\\n    untracked_files = untracked(prefix1)\\n\\n    # Discard conda, conda-env and any package that depends on them\\n    filter = {}\\n    found = True\\n    while found:\\n        found = False\\n        for prec in PrefixData(prefix1).iter_records():\\n            name = prec[\\\"name\\\"]\\n            if name in filter:\\n                continue\\n            if name == \\\"conda\\\":\\n                filter[\\\"conda\\\"] = prec\\n                found = True\\n                break\\n            if name == \\\"conda-env\\\":\\n                filter[\\\"conda-env\\\"] = prec\\n                found = True\\n                break\\n            for dep in prec.combined_depends:\\n                if MatchSpec(dep).name in filter:\\n                    filter[name] = prec\\n                    found = True\\n\\n    if filter:\\n        if not quiet:\\n            fh = sys.stderr if context.json else sys.stdout\\n            print(\\n                \\\"The following packages cannot be cloned out of the root environment:\\\",\\n                file=fh,\\n            )\\n            for prec in filter.values():\\n                print(\\\" - \\\" + prec.dist_str(), file=fh)\\n        drecs = {\\n            prec\\n            for prec in PrefixData(prefix1).iter_records()\\n            if prec[\\\"name\\\"] not in filter\\n        }\\n    else:\\n        drecs = {prec for prec in PrefixData(prefix1).iter_records()}\\n\\n    # Resolve URLs for packages that do not have URLs\\n    index = {}\\n    unknowns = [prec for prec in drecs if not prec.get(\\\"url\\\")]\\n    notfound = []\\n    if unknowns:\\n        index_args = index_args or {}\\n        index = get_index(**index_args)\\n\\n        for prec in unknowns:\\n            spec = MatchSpec(name=prec.name, version=prec.version, build=prec.build)\\n            precs = tuple(prec for prec in index.values() if spec.match(prec))\\n            if not precs:\\n                notfound.append(spec)\\n            elif len(precs) > 1:\\n                drecs.remove(prec)\\n                drecs.add(_get_best_prec_match(precs))\\n            else:\\n                drecs.remove(prec)\\n                drecs.add(precs[0])\\n    if notfound:\\n        raise PackagesNotFoundError(notfound)\\n\\n    # Assemble the URL and channel list\\n    urls = {}\\n    for prec in drecs:\\n        urls[prec] = prec[\\\"url\\\"]\\n\\n    precs = tuple(PrefixGraph(urls).graph)\\n    urls = [urls[prec] for prec in precs]\\n\\n    disallowed = tuple(MatchSpec(s) for s in context.disallowed_packages)\\n    for prec in precs:\\n        if any(d.match(prec) for d in disallowed):\\n            raise DisallowedPackageError(prec)\\n\\n    if verbose:\\n        print(\\\"Packages: %d\\\" % len(precs))\\n        print(\\\"Files: %d\\\" % len(untracked_files))\\n\\n    if context.dry_run:\\n        raise DryRunExit()\\n\\n    for f in untracked_files:\\n        src = join(prefix1, f)\\n        dst = join(prefix2, f)\\n        dst_dir = dirname(dst)\\n        if islink(dst_dir) or isfile(dst_dir):\\n            rm_rf(dst_dir)\\n        if not isdir(dst_dir):\\n            os.makedirs(dst_dir)\\n        if islink(src):\\n            symlink(readlink(src), dst)\\n            continue\\n\\n        try:\\n            with open(src, \\\"rb\\\") as fi:\\n                data = fi.read()\\n        except OSError:\\n            continue\\n\\n        try:\\n            s = data.decode(\\\"utf-8\\\")\\n            s = s.replace(prefix1, prefix2)\\n            data = s.encode(\\\"utf-8\\\")\\n        except UnicodeDecodeError:  # data is binary\\n            pass\\n\\n        with open(dst, \\\"wb\\\") as fo:\\n            fo.write(data)\\n        shutil.copystat(src, dst)\\n\\n    actions = explicit(\\n        urls,\\n        prefix2,\\n        verbose=not quiet,\\n        index=index,\\n        force_extract=False,\\n        index_args=index_args,\\n    )\\n    return actions, untracked_files\\n\\n\\ndef _get_best_prec_match(precs):\\n    assert precs\\n    for channel in context.channels:\\n        channel_matcher = ChannelMatch(channel)\\n        prec_matches = tuple(\\n            prec for prec in precs if channel_matcher.match(prec.channel.name)\\n        )\\n        if prec_matches:\\n            break\\n    else:\\n        prec_matches = precs\\n    log.warning(\\\"Multiple packages found: %s\\\", dashlist(prec_matches))\\n    return prec_matches[0]\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Tools to aid in deprecating code.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport sys\\nimport warnings\\nfrom argparse import Action\\nfrom functools import wraps\\nfrom types import ModuleType\\nfrom typing import TYPE_CHECKING\\n\\nif TYPE_CHECKING:\\n    from argparse import ArgumentParser, Namespace\\n    from typing import Any, Callable, ParamSpec, Self, TypeVar\\n\\n    from packaging.version import Version\\n\\n    T = TypeVar(\\\"T\\\")\\n    P = ParamSpec(\\\"P\\\")\\n\\n    ActionType = TypeVar(\\\"ActionType\\\", bound=type[Action])\\n\\nfrom . import __version__\\n\\n\\nclass DeprecatedError(RuntimeError):\\n    pass\\n\\n\\n# inspired by deprecation (https://deprecation.readthedocs.io/en/latest/) and\\n# CPython's warnings._deprecated\\nclass DeprecationHandler:\\n    _version: str | None\\n    _version_tuple: tuple[int, ...] | None\\n    _version_object: Version | None\\n\\n    def __init__(self: Self, version: str) -> None:\\n        \\\"\\\"\\\"Factory to create a deprecation handle for the specified version.\\n\\n        :param version: The version to compare against when checking deprecation statuses.\\n        \\\"\\\"\\\"\\n        self._version = version\\n        # Try to parse the version string as a simple tuple[int, ...] to avoid\\n        # packaging.version import and costlier version comparisons.\\n        self._version_tuple = self._get_version_tuple(version)\\n        self._version_object = None\\n\\n    @staticmethod\\n    def _get_version_tuple(version: str) -> tuple[int, ...] | None:\\n        \\\"\\\"\\\"Return version as non-empty tuple of ints if possible, else None.\\n\\n        :param version: Version string to parse.\\n        \\\"\\\"\\\"\\n        try:\\n            return tuple(int(part) for part in version.strip().split(\\\".\\\")) or None\\n        except (AttributeError, ValueError):\\n            return None\\n\\n    def _version_less_than(self: Self, version: str) -> bool:\\n        \\\"\\\"\\\"Test whether own version is less than the given version.\\n\\n        :param version: Version string to compare against.\\n        \\\"\\\"\\\"\\n        if self._version_tuple and (version_tuple := self._get_version_tuple(version)):\\n            return self._version_tuple < version_tuple\\n\\n        # If self._version or version could not be represented by a simple\\n        # tuple[int, ...], do a more elaborate version parsing and comparison.\\n        # Avoid this import otherwise to reduce import time for conda activate.\\n        from packaging.version import parse\\n\\n        if self._version_object is None:\\n            try:\\n                self._version_object = parse(self._version)  # type: ignore[arg-type]\\n            except TypeError:\\n                # TypeError: self._version could not be parsed\\n                self._version_object = parse(\\\"0.0.0.dev0+placeholder\\\")\\n        return self._version_object < parse(version)\\n\\n    def __call__(\\n        self: Self,\\n        deprecate_in: str,\\n        remove_in: str,\\n        *,\\n        addendum: str | None = None,\\n        stack: int = 0,\\n    ) -> Callable[[Callable[P, T]], Callable[P, T]]:\\n        \\\"\\\"\\\"Deprecation decorator for functions, methods, & classes.\\n\\n        :param deprecate_in: Version in which code will be marked as deprecated.\\n        :param remove_in: Version in which code is expected to be removed.\\n        :param addendum: Optional additional messaging. Useful to indicate what to do instead.\\n        :param stack: Optional stacklevel increment.\\n        \\\"\\\"\\\"\\n\\n        def deprecated_decorator(func: Callable[P, T]) -> Callable[P, T]:\\n            # detect function name and generate message\\n            category, message = self._generate_message(\\n                deprecate_in=deprecate_in,\\n                remove_in=remove_in,\\n                prefix=f\\\"{func.__module__}.{func.__qualname__}\\\",\\n                addendum=addendum,\\n            )\\n\\n            # alert developer that it's time to remove something\\n            if not category:\\n                raise DeprecatedError(message)\\n\\n            # alert user that it's time to remove something\\n            @wraps(func)\\n            def inner(*args: P.args, **kwargs: P.kwargs) -> T:\\n                warnings.warn(message, category, stacklevel=2 + stack)\\n\\n                return func(*args, **kwargs)\\n\\n            return inner\\n\\n        return deprecated_decorator\\n\\n    def argument(\\n        self: Self,\\n        deprecate_in: str,\\n        remove_in: str,\\n        argument: str,\\n        *,\\n        rename: str | None = None,\\n        addendum: str | None = None,\\n        stack: int = 0,\\n    ) -> Callable[[Callable[P, T]], Callable[P, T]]:\\n        \\\"\\\"\\\"Deprecation decorator for keyword arguments.\\n\\n        :param deprecate_in: Version in which code will be marked as deprecated.\\n        :param remove_in: Version in which code is expected to be removed.\\n        :param argument: The argument to deprecate.\\n        :param rename: Optional new argument name.\\n        :param addendum: Optional additional messaging. Useful to indicate what to do instead.\\n        :param stack: Optional stacklevel increment.\\n        \\\"\\\"\\\"\\n\\n        def deprecated_decorator(func: Callable[P, T]) -> Callable[P, T]:\\n            # detect function name and generate message\\n            category, message = self._generate_message(\\n                deprecate_in=deprecate_in,\\n                remove_in=remove_in,\\n                prefix=f\\\"{func.__module__}.{func.__qualname__}({argument})\\\",\\n                # provide a default addendum if renaming and no addendum is provided\\n                addendum=(\\n                    f\\\"Use '{rename}' instead.\\\" if rename and not addendum else addendum\\n                ),\\n            )\\n\\n            # alert developer that it's time to remove something\\n            if not category:\\n                raise DeprecatedError(message)\\n\\n            # alert user that it's time to remove something\\n            @wraps(func)\\n            def inner(*args: P.args, **kwargs: P.kwargs) -> T:\\n                # only warn about argument deprecations if the argument is used\\n                if argument in kwargs:\\n                    warnings.warn(message, category, stacklevel=2 + stack)\\n\\n                    # rename argument deprecations as needed\\n                    value = kwargs.pop(argument, None)\\n                    if rename:\\n                        kwargs.setdefault(rename, value)\\n\\n                return func(*args, **kwargs)\\n\\n            return inner\\n\\n        return deprecated_decorator\\n\\n    def action(\\n        self: Self,\\n        deprecate_in: str,\\n        remove_in: str,\\n        action: ActionType,\\n        *,\\n        addendum: str | None = None,\\n        stack: int = 0,\\n    ) -> ActionType:\\n        \\\"\\\"\\\"Wraps any argparse.Action to issue a deprecation warning.\\\"\\\"\\\"\\n\\n        class DeprecationMixin(Action):\\n            category: type[Warning]\\n            help: str  # override argparse.Action's help type annotation\\n\\n            def __init__(inner_self: Self, *args: Any, **kwargs: Any) -> None:\\n                super().__init__(*args, **kwargs)\\n\\n                category, message = self._generate_message(\\n                    deprecate_in=deprecate_in,\\n                    remove_in=remove_in,\\n                    prefix=(\\n                        # option_string are ordered shortest to longest,\\n                        # use the longest as it's the most descriptive\\n                        f\\\"`{inner_self.option_strings[-1]}`\\\"\\n                        if inner_self.option_strings\\n                        # if not a flag/switch, use the destination itself\\n                        else f\\\"`{inner_self.dest}`\\\"\\n                    ),\\n                    addendum=addendum,\\n                    deprecation_type=FutureWarning,\\n                )\\n\\n                # alert developer that it's time to remove something\\n                if not category:\\n                    raise DeprecatedError(message)\\n\\n                inner_self.category = category\\n                inner_self.help = message\\n\\n            def __call__(\\n                inner_self: Self,\\n                parser: ArgumentParser,\\n                namespace: Namespace,\\n                values: Any,\\n                option_string: str | None = None,\\n            ) -> None:\\n                # alert user that it's time to remove something\\n                warnings.warn(\\n                    inner_self.help,\\n                    inner_self.category,\\n                    stacklevel=7 + stack,\\n                )\\n\\n                super().__call__(parser, namespace, values, option_string)\\n\\n        return type(action.__name__, (DeprecationMixin, action), {})  # type: ignore[return-value]\\n\\n    def module(\\n        self: Self,\\n        deprecate_in: str,\\n        remove_in: str,\\n        *,\\n        addendum: str | None = None,\\n        stack: int = 0,\\n    ) -> None:\\n        \\\"\\\"\\\"Deprecation function for modules.\\n\\n        :param deprecate_in: Version in which code will be marked as deprecated.\\n        :param remove_in: Version in which code is expected to be removed.\\n        :param addendum: Optional additional messaging. Useful to indicate what to do instead.\\n        :param stack: Optional stacklevel increment.\\n        \\\"\\\"\\\"\\n        self.topic(\\n            deprecate_in=deprecate_in,\\n            remove_in=remove_in,\\n            topic=self._get_module(stack)[1],\\n            addendum=addendum,\\n            stack=2 + stack,\\n        )\\n\\n    def constant(\\n        self: Self,\\n        deprecate_in: str,\\n        remove_in: str,\\n        constant: str,\\n        value: Any,\\n        *,\\n        addendum: str | None = None,\\n        stack: int = 0,\\n    ) -> None:\\n        \\\"\\\"\\\"Deprecation function for module constant/global.\\n\\n        :param deprecate_in: Version in which code will be marked as deprecated.\\n        :param remove_in: Version in which code is expected to be removed.\\n        :param constant:\\n        :param value:\\n        :param addendum: Optional additional messaging. Useful to indicate what to do instead.\\n        :param stack: Optional stacklevel increment.\\n        \\\"\\\"\\\"\\n        # detect calling module\\n        module, fullname = self._get_module(stack)\\n        # detect function name and generate message\\n        category, message = self._generate_message(\\n            deprecate_in=deprecate_in,\\n            remove_in=remove_in,\\n            prefix=f\\\"{fullname}.{constant}\\\",\\n            addendum=addendum,\\n        )\\n\\n        # alert developer that it's time to remove something\\n        if not category:\\n            raise DeprecatedError(message)\\n\\n        # patch module level __getattr__ to alert user that it's time to remove something\\n        super_getattr = getattr(module, \\\"__getattr__\\\", None)\\n\\n        def __getattr__(name: str) -> Any:\\n            if name == constant:\\n                warnings.warn(message, category, stacklevel=2 + stack)\\n                return value\\n\\n            if super_getattr:\\n                return super_getattr(name)\\n\\n            raise AttributeError(f\\\"module '{fullname}' has no attribute '{name}'\\\")\\n\\n        module.__getattr__ = __getattr__  # type: ignore[method-assign]\\n\\n    def topic(\\n        self: Self,\\n        deprecate_in: str,\\n        remove_in: str,\\n        *,\\n        topic: str,\\n        addendum: str | None = None,\\n        stack: int = 0,\\n    ) -> None:\\n        \\\"\\\"\\\"Deprecation function for a topic.\\n\\n        :param deprecate_in: Version in which code will be marked as deprecated.\\n        :param remove_in: Version in which code is expected to be removed.\\n        :param topic: The topic being deprecated.\\n        :param addendum: Optional additional messaging. Useful to indicate what to do instead.\\n        :param stack: Optional stacklevel increment.\\n        \\\"\\\"\\\"\\n        # detect function name and generate message\\n        category, message = self._generate_message(\\n            deprecate_in=deprecate_in,\\n            remove_in=remove_in,\\n            prefix=topic,\\n            addendum=addendum,\\n        )\\n\\n        # alert developer that it's time to remove something\\n        if not category:\\n            raise DeprecatedError(message)\\n\\n        # alert user that it's time to remove something\\n        warnings.warn(message, category, stacklevel=2 + stack)\\n\\n    def _get_module(self: Self, stack: int) -> tuple[ModuleType, str]:\\n        \\\"\\\"\\\"Detect the module from which we are being called.\\n\\n        :param stack: The stacklevel increment.\\n        :return: The module and module name.\\n        \\\"\\\"\\\"\\n        try:\\n            frame = sys._getframe(2 + stack)\\n        except IndexError:\\n            # IndexError: 2 + stack is out of range\\n            pass\\n        else:\\n            # Shortcut finding the module by manually inspecting loaded modules.\\n            try:\\n                filename = frame.f_code.co_filename\\n            except AttributeError:\\n                # AttributeError: frame.f_code.co_filename is undefined\\n                pass\\n            else:\\n                # use a copy of sys.modules to avoid RuntimeError during iteration\\n                # see https://github.com/conda/conda/issues/13754\\n                for loaded in tuple(sys.modules.values()):\\n                    if not isinstance(loaded, ModuleType):\\n                        continue\\n                    if not hasattr(loaded, \\\"__file__\\\"):\\n                        continue\\n                    if loaded.__file__ == filename:\\n                        return (loaded, loaded.__name__)\\n\\n            # If above failed, do an expensive import and costly getmodule call.\\n            import inspect\\n\\n            module = inspect.getmodule(frame)\\n            if module is not None:\\n                return (module, module.__name__)\\n\\n        raise DeprecatedError(\\\"unable to determine the calling module\\\")\\n\\n    def _generate_message(\\n        self: Self,\\n        deprecate_in: str,\\n        remove_in: str,\\n        prefix: str,\\n        addendum: str | None,\\n        *,\\n        deprecation_type: type[Warning] = DeprecationWarning,\\n    ) -> tuple[type[Warning] | None, str]:\\n        \\\"\\\"\\\"Generate the standardized deprecation message and determine whether the\\n        deprecation is pending, active, or past.\\n\\n        :param deprecate_in: Version in which code will be marked as deprecated.\\n        :param remove_in: Version in which code is expected to be removed.\\n        :param prefix: The message prefix, usually the function name.\\n        :param addendum: Additional messaging. Useful to indicate what to do instead.\\n        :param deprecation_type: The warning type to use for active deprecations.\\n        :return: The warning category (if applicable) and the message.\\n        \\\"\\\"\\\"\\n        category: type[Warning] | None\\n        if self._version_less_than(deprecate_in):\\n            category = PendingDeprecationWarning\\n            warning = f\\\"is pending deprecation and will be removed in {remove_in}.\\\"\\n        elif self._version_less_than(remove_in):\\n            category = deprecation_type\\n            warning = f\\\"is deprecated and will be removed in {remove_in}.\\\"\\n        else:\\n            category = None\\n            warning = f\\\"was slated for removal in {remove_in}.\\\"\\n\\n        return (\\n            category,\\n            \\\" \\\".join(filter(None, [prefix, warning, addendum])),  # message\\n        )\\n\\n\\ndeprecated = DeprecationHandler(__version__)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Conda exceptions.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport json\\nimport os\\nimport sys\\nfrom datetime import timedelta\\nfrom logging import getLogger\\nfrom os.path import join\\nfrom textwrap import dedent\\nfrom traceback import format_exception, format_exception_only\\nfrom typing import TYPE_CHECKING\\n\\nfrom requests.exceptions import JSONDecodeError\\n\\nfrom . import CondaError, CondaExitZero, CondaMultiError\\nfrom .auxlib.entity import EntityEncoder\\nfrom .auxlib.ish import dals\\nfrom .auxlib.logz import stringify\\nfrom .base.constants import COMPATIBLE_SHELLS, PathConflict, SafetyChecks\\nfrom .common.compat import on_win\\nfrom .common.io import dashlist\\nfrom .common.iterators import groupby_to_dict as groupby\\nfrom .common.signals import get_signal_name\\nfrom .common.url import join_url, maybe_unquote\\nfrom .deprecations import DeprecatedError  # noqa: F401\\nfrom .exception_handler import ExceptionHandler, conda_exception_handler  # noqa: F401\\nfrom .models.channel import Channel\\n\\nif TYPE_CHECKING:\\n    import requests\\n\\nlog = getLogger(__name__)\\n\\n\\n# TODO: for conda-build compatibility only\\n# remove in conda 4.4\\nclass ResolvePackageNotFound(CondaError):\\n    def __init__(self, bad_deps):\\n        # bad_deps is a list of lists\\n        # bad_deps should really be named 'invalid_chains'\\n        self.bad_deps = tuple(dep for deps in bad_deps for dep in deps if dep)\\n        formatted_chains = tuple(\\n            \\\" -> \\\".join(map(str, bad_chain)) for bad_chain in bad_deps\\n        )\\n        self._formatted_chains = formatted_chains\\n        message = \\\"\\\\n\\\" + \\\"\\\\n\\\".join(\\n            (f\\\"  - {bad_chain}\\\") for bad_chain in formatted_chains\\n        )\\n        super().__init__(message)\\n\\n\\nNoPackagesFound = NoPackagesFoundError = ResolvePackageNotFound  # NOQA\\n\\n\\nclass LockError(CondaError):\\n    def __init__(self, message):\\n        msg = f\\\"{message}\\\"\\n        super().__init__(msg)\\n\\n\\nclass ArgumentError(CondaError):\\n    return_code = 2\\n\\n    def __init__(self, message, **kwargs):\\n        super().__init__(message, **kwargs)\\n\\n\\nclass Help(CondaError):\\n    pass\\n\\n\\nclass ActivateHelp(Help):\\n    def __init__(self):\\n        message = dals(\\n            \\\"\\\"\\\"\\n        usage: conda activate [-h] [--[no-]stack] [env_name_or_prefix]\\n\\n        Activate a conda environment.\\n\\n        Options:\\n\\n        positional arguments:\\n          env_name_or_prefix    The environment name or prefix to activate. If the\\n                                prefix is a relative path, it must start with './'\\n                                (or '.\\\\\\\\' on Windows).\\n\\n        optional arguments:\\n          -h, --help            Show this help message and exit.\\n          --stack               Stack the environment being activated on top of the\\n                                previous active environment, rather replacing the\\n                                current active environment with a new one. Currently,\\n                                only the PATH environment variable is stacked. This\\n                                may be enabled implicitly by the 'auto_stack'\\n                                configuration variable.\\n          --no-stack            Do not stack the environment. Overrides 'auto_stack'\\n                                setting.\\n        \\\"\\\"\\\"\\n        )\\n        super().__init__(message)\\n\\n\\nclass DeactivateHelp(Help):\\n    def __init__(self):\\n        message = dals(\\n            \\\"\\\"\\\"\\n        usage: conda deactivate [-h]\\n\\n        Deactivate the current active conda environment.\\n\\n        Options:\\n\\n        optional arguments:\\n          -h, --help            Show this help message and exit.\\n        \\\"\\\"\\\"\\n        )\\n        super().__init__(message)\\n\\n\\nclass GenericHelp(Help):\\n    def __init__(self, command):\\n        message = f\\\"help requested for {command}\\\"\\n        super().__init__(message)\\n\\n\\nclass CondaSignalInterrupt(CondaError):\\n    def __init__(self, signum):\\n        signal_name = get_signal_name(signum)\\n        super().__init__(\\n            \\\"Signal interrupt %(signal_name)s\\\", signal_name=signal_name, signum=signum\\n        )\\n\\n\\nclass TooManyArgumentsError(ArgumentError):\\n    def __init__(\\n        self, expected, received, offending_arguments, optional_message=\\\"\\\", *args\\n    ):\\n        self.expected = expected\\n        self.received = received\\n        self.offending_arguments = offending_arguments\\n        self.optional_message = optional_message\\n\\n        suffix = \\\"s\\\" if received - expected > 1 else \\\"\\\"\\n        msg = \\\"{} Got {} argument{} ({}) but expected {}.\\\".format(\\n            optional_message,\\n            received,\\n            suffix,\\n            \\\", \\\".join(offending_arguments),\\n            expected,\\n        )\\n        super().__init__(msg, *args)\\n\\n\\nclass ClobberError(CondaError):\\n    def __init__(self, message, path_conflict, **kwargs):\\n        self.path_conflict = path_conflict\\n        super().__init__(message, **kwargs)\\n\\n    def __repr__(self):\\n        clz_name = (\\n            \\\"ClobberWarning\\\"\\n            if self.path_conflict == PathConflict.warn\\n            else \\\"ClobberError\\\"\\n        )\\n        return f\\\"{clz_name}: {self}\\\\n\\\"\\n\\n\\nclass BasicClobberError(ClobberError):\\n    def __init__(self, source_path, target_path, context):\\n        message = dals(\\n            \\\"\\\"\\\"\\n        Conda was asked to clobber an existing path.\\n          source path: %(source_path)s\\n          target path: %(target_path)s\\n        \\\"\\\"\\\"\\n        )\\n        if context.path_conflict == PathConflict.prevent:\\n            message += (\\n                \\\"Conda no longer clobbers existing paths without the use of the \\\"\\n                \\\"--clobber option\\\\n.\\\"\\n            )\\n        super().__init__(\\n            message,\\n            context.path_conflict,\\n            target_path=target_path,\\n            source_path=source_path,\\n        )\\n\\n\\nclass KnownPackageClobberError(ClobberError):\\n    def __init__(\\n        self, target_path, colliding_dist_being_linked, colliding_linked_dist, context\\n    ):\\n        message = dals(\\n            \\\"\\\"\\\"\\n        The package '%(colliding_dist_being_linked)s' cannot be installed due to a\\n        path collision for '%(target_path)s'.\\n        This path already exists in the target prefix, and it won't be removed by\\n        an uninstall action in this transaction. The path appears to be coming from\\n        the package '%(colliding_linked_dist)s', which is already installed in the prefix.\\n        \\\"\\\"\\\"\\n        )\\n        if context.path_conflict == PathConflict.prevent:\\n            message += (\\n                \\\"If you'd like to proceed anyway, re-run the command with \\\"\\n                \\\"the `--clobber` flag.\\\\n.\\\"\\n            )\\n        super().__init__(\\n            message,\\n            context.path_conflict,\\n            target_path=target_path,\\n            colliding_dist_being_linked=colliding_dist_being_linked,\\n            colliding_linked_dist=colliding_linked_dist,\\n        )\\n\\n\\nclass UnknownPackageClobberError(ClobberError):\\n    def __init__(self, target_path, colliding_dist_being_linked, context):\\n        message = dals(\\n            \\\"\\\"\\\"\\n        The package '%(colliding_dist_being_linked)s' cannot be installed due to a\\n        path collision for '%(target_path)s'.\\n        This path already exists in the target prefix, and it won't be removed\\n        by an uninstall action in this transaction. The path is one that conda\\n        doesn't recognize. It may have been created by another package manager.\\n        \\\"\\\"\\\"\\n        )\\n        if context.path_conflict == PathConflict.prevent:\\n            message += (\\n                \\\"If you'd like to proceed anyway, re-run the command with \\\"\\n                \\\"the `--clobber` flag.\\\\n.\\\"\\n            )\\n        super().__init__(\\n            message,\\n            context.path_conflict,\\n            target_path=target_path,\\n            colliding_dist_being_linked=colliding_dist_being_linked,\\n        )\\n\\n\\nclass SharedLinkPathClobberError(ClobberError):\\n    def __init__(self, target_path, incompatible_package_dists, context):\\n        message = dals(\\n            \\\"\\\"\\\"\\n        This transaction has incompatible packages due to a shared path.\\n          packages: %(incompatible_packages)s\\n          path: '%(target_path)s'\\n        \\\"\\\"\\\"\\n        )\\n        if context.path_conflict == PathConflict.prevent:\\n            message += (\\n                \\\"If you'd like to proceed anyway, re-run the command with \\\"\\n                \\\"the `--clobber` flag.\\\\n.\\\"\\n            )\\n        super().__init__(\\n            message,\\n            context.path_conflict,\\n            target_path=target_path,\\n            incompatible_packages=\\\", \\\".join(str(d) for d in incompatible_package_dists),\\n        )\\n\\n\\nclass CommandNotFoundError(CondaError):\\n    def __init__(self, command):\\n        activate_commands = {\\n            \\\"activate\\\",\\n            \\\"deactivate\\\",\\n            \\\"run\\\",\\n        }\\n        conda_commands = {\\n            \\\"clean\\\",\\n            \\\"config\\\",\\n            \\\"create\\\",\\n            \\\"--help\\\",  # https://github.com/conda/conda/issues/11585\\n            \\\"info\\\",\\n            \\\"install\\\",\\n            \\\"list\\\",\\n            \\\"package\\\",\\n            \\\"remove\\\",\\n            \\\"search\\\",\\n            \\\"uninstall\\\",\\n            \\\"update\\\",\\n            \\\"upgrade\\\",\\n        }\\n        build_commands = {\\n            \\\"build\\\",\\n            \\\"convert\\\",\\n            \\\"develop\\\",\\n            \\\"index\\\",\\n            \\\"inspect\\\",\\n            \\\"metapackage\\\",\\n            \\\"render\\\",\\n            \\\"skeleton\\\",\\n        }\\n        from .cli.main import init_loggers\\n\\n        init_loggers()\\n        if command in activate_commands:\\n            # TODO: Point users to a page at conda-docs, which explains this context in more detail\\n            builder = [\\n                \\\"Your shell has not been properly configured to use 'conda %(command)s'.\\\"\\n            ]\\n            if on_win:\\n                builder.append(\\n                    dals(\\n                        \\\"\\\"\\\"\\n                If using 'conda %(command)s' from a batch script, change your\\n                invocation to 'CALL conda.bat %(command)s'.\\n                \\\"\\\"\\\"\\n                    )\\n                )\\n            builder.append(\\n                dals(\\n                    \\\"\\\"\\\"\\n            To initialize your shell, run\\n\\n                $ conda init <SHELL_NAME>\\n\\n            Currently supported shells are:%(supported_shells)s\\n\\n            See 'conda init --help' for more information and options.\\n\\n            IMPORTANT: You may need to close and restart your shell after running 'conda init'.\\n            \\\"\\\"\\\"\\n                )\\n                % {\\n                    \\\"supported_shells\\\": dashlist(COMPATIBLE_SHELLS),\\n                }\\n            )\\n            message = \\\"\\\\n\\\".join(builder)\\n        elif command in build_commands:\\n            message = \\\"To use 'conda %(command)s', install conda-build.\\\"\\n        else:\\n            from difflib import get_close_matches\\n\\n            from .cli.find_commands import find_commands\\n\\n            message = \\\"No command 'conda %(command)s'.\\\"\\n            choices = (\\n                activate_commands\\n                | conda_commands\\n                | build_commands\\n                | set(find_commands())\\n            )\\n            close = get_close_matches(command, choices)\\n            if close:\\n                message += f\\\"\\\\nDid you mean 'conda {close[0]}'?\\\"\\n        super().__init__(message, command=command)\\n\\n\\nclass PathNotFoundError(CondaError, OSError):\\n    def __init__(self, path):\\n        message = \\\"%(path)s\\\"\\n        super().__init__(message, path=path)\\n\\n\\nclass DirectoryNotFoundError(CondaError):\\n    def __init__(self, path):\\n        message = \\\"%(path)s\\\"\\n        super().__init__(message, path=path)\\n\\n\\nclass EnvironmentLocationNotFound(CondaError):\\n    def __init__(self, location):\\n        message = \\\"Not a conda environment: %(location)s\\\"\\n        super().__init__(message, location=location)\\n\\n\\nclass EnvironmentNameNotFound(CondaError):\\n    def __init__(self, environment_name):\\n        message = dals(\\n            \\\"\\\"\\\"\\n        Could not find conda environment: %(environment_name)s\\n        You can list all discoverable environments with `conda info --envs`.\\n        \\\"\\\"\\\"\\n        )\\n        super().__init__(message, environment_name=environment_name)\\n\\n\\nclass NoBaseEnvironmentError(CondaError):\\n    def __init__(self):\\n        message = dals(\\n            \\\"\\\"\\\"\\n        This conda installation has no default base environment. Use\\n        'conda create' to create new environments and 'conda activate' to\\n        activate environments.\\n        \\\"\\\"\\\"\\n        )\\n        super().__init__(message)\\n\\n\\nclass DirectoryNotACondaEnvironmentError(CondaError):\\n    def __init__(self, target_directory):\\n        message = dals(\\n            \\\"\\\"\\\"\\n        The target directory exists, but it is not a conda environment.\\n        Use 'conda create' to convert the directory to a conda environment.\\n          target directory: %(target_directory)s\\n        \\\"\\\"\\\"\\n        )\\n        super().__init__(message, target_directory=target_directory)\\n\\n\\nclass CondaEnvironmentError(CondaError, EnvironmentError):\\n    def __init__(self, message, *args):\\n        msg = f\\\"{message}\\\"\\n        super().__init__(msg, *args)\\n\\n\\nclass DryRunExit(CondaExitZero):\\n    def __init__(self):\\n        msg = \\\"Dry run. Exiting.\\\"\\n        super().__init__(msg)\\n\\n\\nclass CondaSystemExit(CondaExitZero, SystemExit):\\n    def __init__(self, *args):\\n        msg = \\\" \\\".join(str(arg) for arg in self.args)\\n        super().__init__(msg)\\n\\n\\nclass PaddingError(CondaError):\\n    def __init__(self, dist, placeholder, placeholder_length):\\n        msg = (\\n            \\\"Placeholder of length '%d' too short in package %s.\\\\n\\\"\\n            \\\"The package must be rebuilt with conda-build > 2.0.\\\"\\n            % (placeholder_length, dist)\\n        )\\n        super().__init__(msg)\\n\\n\\nclass LinkError(CondaError):\\n    def __init__(self, message):\\n        super().__init__(message)\\n\\n\\nclass CondaOSError(CondaError, OSError):\\n    def __init__(self, message, **kwargs):\\n        msg = f\\\"{message}\\\"\\n        super().__init__(msg, **kwargs)\\n\\n\\nclass ProxyError(CondaError):\\n    def __init__(self):\\n        message = dals(\\n            \\\"\\\"\\\"\\n        Conda cannot proceed due to an error in your proxy configuration.\\n        Check for typos and other configuration errors in any '.netrc' file in your home directory,\\n        any environment variables ending in '_PROXY', and any other system-wide proxy\\n        configuration settings.\\n        \\\"\\\"\\\"\\n        )\\n        super().__init__(message)\\n\\n\\nclass CondaIOError(CondaError, IOError):\\n    def __init__(self, message, *args):\\n        msg = f\\\"{message}\\\"\\n        super().__init__(msg)\\n\\n\\nclass CondaFileIOError(CondaIOError):\\n    def __init__(self, filepath, message, *args):\\n        self.filepath = filepath\\n\\n        msg = f\\\"'{filepath}'. {message}\\\"\\n        super().__init__(msg, *args)\\n\\n\\nclass CondaKeyError(CondaError, KeyError):\\n    def __init__(self, key, message, *args):\\n        self.key = key\\n        self.msg = f\\\"{key!r}: {message}\\\"\\n        super().__init__(self.msg, *args)\\n\\n\\nclass ChannelError(CondaError):\\n    pass\\n\\n\\nclass ChannelNotAllowed(ChannelError):\\n    def __init__(self, channel):\\n        channel = Channel(channel)\\n        channel_name = channel.name\\n        channel_url = maybe_unquote(channel.base_url)\\n        message = dals(\\n            \\\"\\\"\\\"\\n        Channel not included in allowlist:\\n          channel name: %(channel_name)s\\n          channel url: %(channel_url)s\\n        \\\"\\\"\\\"\\n        )\\n        super().__init__(message, channel_url=channel_url, channel_name=channel_name)\\n\\n\\nclass UnavailableInvalidChannel(ChannelError):\\n    status_code: str | int\\n\\n    def __init__(\\n        self, channel, status_code, response: requests.models.Response | None = None\\n    ):\\n        # parse channel\\n        channel = Channel(channel)\\n        channel_name = channel.name\\n        channel_url = maybe_unquote(channel.base_url)\\n\\n        # define hardcoded/default reason/message\\n        reason = getattr(response, \\\"reason\\\", None)\\n        message = dals(\\n            \\\"\\\"\\\"\\n            The channel is not accessible or is invalid.\\n\\n            You will need to adjust your conda configuration to proceed.\\n            Use `conda config --show channels` to view your configuration's current state,\\n            and use `conda config --show-sources` to view config file locations.\\n            \\\"\\\"\\\"\\n        )\\n        if channel.scheme == \\\"file\\\":\\n            url = join_url(channel.location, channel.name)\\n            message += dedent(\\n                f\\\"\\\"\\\"\\n                As of conda 4.3, a valid channel must contain a `noarch/repodata.json` and\\n                associated `noarch/repodata.json.bz2` file, even if `noarch/repodata.json` is\\n                empty. Use `conda index {url}`, or create `noarch/repodata.json`\\n                and associated `noarch/repodata.json.bz2`.\\n                \\\"\\\"\\\"\\n            )\\n\\n        # if response includes a valid json body we prefer the reason/message defined there\\n        try:\\n            body = response.json()\\n        except (AttributeError, JSONDecodeError):\\n            body = {}\\n        else:\\n            reason = body.get(\\\"reason\\\", None) or reason\\n            message = body.get(\\\"message\\\", None) or message\\n\\n        # standardize arguments\\n        status_code = status_code or \\\"000\\\"\\n        reason = reason or \\\"UNAVAILABLE OR INVALID\\\"\\n        if isinstance(reason, str):\\n            reason = reason.upper()\\n\\n        self.status_code = status_code\\n\\n        super().__init__(\\n            f\\\"HTTP {status_code} {reason} for channel {channel_name} <{channel_url}>\\\\n\\\\n{message}\\\",\\n            channel_name=channel_name,\\n            channel_url=channel_url,\\n            status_code=status_code,\\n            reason=reason,\\n            response_details=stringify(response, content_max_len=1024) or \\\"\\\",\\n            json=body,\\n        )\\n\\n\\nclass OperationNotAllowed(CondaError):\\n    def __init__(self, message):\\n        super().__init__(message)\\n\\n\\nclass CondaImportError(CondaError, ImportError):\\n    def __init__(self, message):\\n        msg = f\\\"{message}\\\"\\n        super().__init__(msg)\\n\\n\\nclass ParseError(CondaError):\\n    def __init__(self, message):\\n        msg = f\\\"{message}\\\"\\n        super().__init__(msg)\\n\\n\\nclass CouldntParseError(ParseError):\\n    def __init__(self, reason):\\n        self.reason = reason\\n        super().__init__(self.args[0])\\n\\n\\nclass ChecksumMismatchError(CondaError):\\n    def __init__(\\n        self, url, target_full_path, checksum_type, expected_checksum, actual_checksum\\n    ):\\n        message = dals(\\n            \\\"\\\"\\\"\\n        Conda detected a mismatch between the expected content and downloaded content\\n        for url '%(url)s'.\\n          download saved to: %(target_full_path)s\\n          expected %(checksum_type)s: %(expected_checksum)s\\n          actual %(checksum_type)s: %(actual_checksum)s\\n        \\\"\\\"\\\"\\n        )\\n        url = maybe_unquote(url)\\n        super().__init__(\\n            message,\\n            url=url,\\n            target_full_path=target_full_path,\\n            checksum_type=checksum_type,\\n            expected_checksum=expected_checksum,\\n            actual_checksum=actual_checksum,\\n        )\\n\\n\\nclass PackageNotInstalledError(CondaError):\\n    def __init__(self, prefix, package_name):\\n        message = dals(\\n            \\\"\\\"\\\"\\n        Package is not installed in prefix.\\n          prefix: %(prefix)s\\n          package name: %(package_name)s\\n        \\\"\\\"\\\"\\n        )\\n        super().__init__(message, prefix=prefix, package_name=package_name)\\n\\n\\nclass CondaHTTPError(CondaError):\\n    def __init__(\\n        self,\\n        message,\\n        url,\\n        status_code,\\n        reason,\\n        elapsed_time,\\n        response=None,\\n        caused_by=None,\\n    ):\\n        # if response includes a valid json body we prefer the reason/message defined there\\n        try:\\n            body = response.json()\\n        except (AttributeError, JSONDecodeError):\\n            body = {}\\n        else:\\n            reason = body.get(\\\"reason\\\", None) or reason\\n            message = body.get(\\\"message\\\", None) or message\\n\\n        # standardize arguments\\n        url = maybe_unquote(url)\\n        status_code = status_code or \\\"000\\\"\\n        reason = reason or \\\"CONNECTION FAILED\\\"\\n        if isinstance(reason, str):\\n            reason = reason.upper()\\n        elapsed_time = elapsed_time or \\\"-\\\"\\n        if isinstance(elapsed_time, timedelta):\\n            elapsed_time = str(elapsed_time).split(\\\":\\\", 1)[-1]\\n\\n        # extract CF-RAY\\n        try:\\n            cf_ray = response.headers[\\\"CF-RAY\\\"]\\n        except (AttributeError, KeyError):\\n            cf_ray = \\\"\\\"\\n        else:\\n            cf_ray = f\\\"CF-RAY: {cf_ray}\\\\n\\\"\\n\\n        super().__init__(\\n            dals(\\n                f\\\"\\\"\\\"\\n                HTTP {status_code} {reason} for url <{url}>\\n                Elapsed: {elapsed_time}\\n                {cf_ray}\\n                \\\"\\\"\\\"\\n            )\\n            # since message may include newlines don't include in f-string/dals above\\n            + message,\\n            url=url,\\n            status_code=status_code,\\n            reason=reason,\\n            elapsed_time=elapsed_time,\\n            response_details=stringify(response, content_max_len=1024) or \\\"\\\",\\n            json=body,\\n            caused_by=caused_by,\\n        )\\n\\n\\nclass CondaSSLError(CondaError):\\n    pass\\n\\n\\nclass AuthenticationError(CondaError):\\n    pass\\n\\n\\nclass PackagesNotFoundError(CondaError):\\n    def __init__(self, packages, channel_urls=()):\\n        format_list = lambda iterable: \\\"  - \\\" + \\\"\\\\n  - \\\".join(str(x) for x in iterable)\\n\\n        if channel_urls:\\n            message = dals(\\n                \\\"\\\"\\\"\\n            The following packages are not available from current channels:\\n\\n            %(packages_formatted)s\\n\\n            Current channels:\\n\\n            %(channels_formatted)s\\n\\n            To search for alternate channels that may provide the conda package you're\\n            looking for, navigate to\\n\\n                https://anaconda.org\\n\\n            and use the search bar at the top of the page.\\n            \\\"\\\"\\\"\\n            )\\n            from .base.context import context\\n\\n            if context.use_only_tar_bz2:\\n                message += dals(\\n                    \\\"\\\"\\\"\\n                Note: 'use_only_tar_bz2' is enabled. This might be omitting some\\n                packages from the index. Set this option to 'false' and retry.\\n                \\\"\\\"\\\"\\n                )\\n            packages_formatted = format_list(packages)\\n            channels_formatted = format_list(channel_urls)\\n        else:\\n            message = dals(\\n                \\\"\\\"\\\"\\n            The following packages are missing from the target environment:\\n            %(packages_formatted)s\\n            \\\"\\\"\\\"\\n            )\\n            packages_formatted = format_list(packages)\\n            channels_formatted = ()\\n\\n        super().__init__(\\n            message,\\n            packages=packages,\\n            packages_formatted=packages_formatted,\\n            channel_urls=channel_urls,\\n            channels_formatted=channels_formatted,\\n        )\\n\\n\\nclass UnsatisfiableError(CondaError):\\n    \\\"\\\"\\\"An exception to report unsatisfiable dependencies.\\n\\n    Args:\\n        bad_deps: a list of tuples of objects (likely MatchSpecs).\\n        chains: (optional) if True, the tuples are interpreted as chains\\n            of dependencies, from top level to bottom. If False, the tuples\\n            are interpreted as simple lists of conflicting specs.\\n\\n    Returns:\\n        Raises an exception with a formatted message detailing the\\n        unsatisfiable specifications.\\n    \\\"\\\"\\\"\\n\\n    def _format_chain_str(self, bad_deps):\\n        chains = {}\\n        for dep in sorted(bad_deps, key=len, reverse=True):\\n            dep1 = [s.partition(\\\" \\\") for s in dep[1:]]\\n            key = (dep[0],) + tuple(v[0] for v in dep1)\\n            vals = (\\\"\\\",) + tuple(v[2] for v in dep1)\\n            found = False\\n            for key2, csets in chains.items():\\n                if key2[: len(key)] == key:\\n                    for cset, val in zip(csets, vals):\\n                        cset.add(val)\\n                    found = True\\n            if not found:\\n                chains[key] = [{val} for val in vals]\\n        for key, csets in chains.items():\\n            deps = []\\n            for name, cset in zip(key, csets):\\n                if \\\"\\\" not in cset:\\n                    pass\\n                elif len(cset) == 1:\\n                    cset.clear()\\n                else:\\n                    cset.remove(\\\"\\\")\\n                    cset.add(\\\"*\\\")\\n                if name[0] == \\\"@\\\":\\n                    name = \\\"feature:\\\" + name[1:]\\n                deps.append(\\n                    \\\"{} {}\\\".format(name, \\\"|\\\".join(sorted(cset))) if cset else name\\n                )\\n            chains[key] = \\\" -> \\\".join(deps)\\n        return [chains[key] for key in sorted(chains.keys())]\\n\\n    def __init__(self, bad_deps, chains=True, strict=False):\\n        from .models.match_spec import MatchSpec\\n\\n        messages = {\\n            \\\"python\\\": dals(\\n                \\\"\\\"\\\"\\n\\nThe following specifications were found\\nto be incompatible with the existing python installation in your environment:\\n\\nSpecifications:\\\\n{specs}\\n\\nYour python: {ref}\\n\\nIf python is on the left-most side of the chain, that's the version you've asked for.\\nWhen python appears to the right, that indicates that the thing on the left is somehow\\nnot available for the python version you are constrained to. Note that conda will not\\nchange your python version to a different minor version unless you explicitly specify\\nthat.\\n\\n        \\\"\\\"\\\"\\n            ),\\n            \\\"request_conflict_with_history\\\": dals(\\n                \\\"\\\"\\\"\\n\\nThe following specifications were found to be incompatible with a past\\nexplicit spec that is not an explicit spec in this operation ({ref}):\\\\n{specs}\\n\\n                    \\\"\\\"\\\"\\n            ),\\n            \\\"direct\\\": dals(\\n                \\\"\\\"\\\"\\n\\nThe following specifications were found to be incompatible with each other:\\n                    \\\"\\\"\\\"\\n            ),\\n            \\\"virtual_package\\\": dals(\\n                \\\"\\\"\\\"\\n\\nThe following specifications were found to be incompatible with your system:\\\\n{specs}\\n\\nYour installed version is: {ref}\\n\\\"\\\"\\\"\\n            ),\\n        }\\n\\n        msg = \\\"\\\"\\n        self.unsatisfiable = []\\n        if len(bad_deps) == 0:\\n            msg += \\\"\\\"\\\"\\nDid not find conflicting dependencies. If you would like to know which\\npackages conflict ensure that you have enabled unsatisfiable hints.\\n\\nconda config --set unsatisfiable_hints True\\n            \\\"\\\"\\\"\\n        else:\\n            for class_name, dep_class in bad_deps.items():\\n                if dep_class:\\n                    _chains = []\\n                    if class_name == \\\"direct\\\":\\n                        msg += messages[\\\"direct\\\"]\\n                        last_dep_entry = {d[0][-1].name for d in dep_class}\\n                        dep_constraint_map = {}\\n                        for dep in dep_class:\\n                            if dep[0][-1].name in last_dep_entry:\\n                                if not dep_constraint_map.get(dep[0][-1].name):\\n                                    dep_constraint_map[dep[0][-1].name] = []\\n                                dep_constraint_map[dep[0][-1].name].append(dep[0])\\n                        msg += \\\"\\\\nOutput in format: Requested package -> Available versions\\\"\\n                        for dep, chain in dep_constraint_map.items():\\n                            if len(chain) > 1:\\n                                msg += f\\\"\\\\n\\\\nPackage {dep} conflicts for:\\\\n\\\"\\n                                msg += \\\"\\\\n\\\".join(\\n                                    [\\\" -> \\\".join([str(i) for i in c]) for c in chain]\\n                                )\\n                                self.unsatisfiable += [\\n                                    tuple(entries) for entries in chain\\n                                ]\\n                    else:\\n                        for dep_chain, installed_blocker in dep_class:\\n                            # Remove any target values from the MatchSpecs, convert to strings\\n                            dep_chain = [\\n                                str(MatchSpec(dep, target=None)) for dep in dep_chain\\n                            ]\\n                            _chains.append(dep_chain)\\n\\n                        if _chains:\\n                            _chains = self._format_chain_str(_chains)\\n                        else:\\n                            _chains = [\\\", \\\".join(c) for c in _chains]\\n                        msg += messages[class_name].format(\\n                            specs=dashlist(_chains), ref=installed_blocker\\n                        )\\n        if strict:\\n            msg += (\\n                \\\"\\\\nNote that strict channel priority may have removed \\\"\\n                \\\"packages required for satisfiability.\\\"\\n            )\\n\\n        super().__init__(msg)\\n\\n\\nclass RemoveError(CondaError):\\n    def __init__(self, message):\\n        msg = f\\\"{message}\\\"\\n        super().__init__(msg)\\n\\n\\nclass DisallowedPackageError(CondaError):\\n    def __init__(self, package_ref, **kwargs):\\n        from .models.records import PackageRecord\\n\\n        package_ref = PackageRecord.from_objects(package_ref)\\n        message = (\\n            \\\"The package '%(dist_str)s' is disallowed by configuration.\\\\n\\\"\\n            \\\"See 'conda config --show disallowed_packages'.\\\"\\n        )\\n        super().__init__(\\n            message, package_ref=package_ref, dist_str=package_ref.dist_str(), **kwargs\\n        )\\n\\n\\nclass SpecsConfigurationConflictError(CondaError):\\n    def __init__(self, requested_specs, pinned_specs, prefix):\\n        message = dals(\\n            \\\"\\\"\\\"\\n        Requested specs conflict with configured specs.\\n          requested specs: {requested_specs_formatted}\\n          pinned specs: {pinned_specs_formatted}\\n        Use 'conda config --show-sources' to look for 'pinned_specs' and 'track_features'\\n        configuration parameters.  Pinned specs may also be defined in the file\\n        {pinned_specs_path}.\\n        \\\"\\\"\\\"\\n        ).format(\\n            requested_specs_formatted=dashlist(requested_specs, 4),\\n            pinned_specs_formatted=dashlist(pinned_specs, 4),\\n            pinned_specs_path=join(prefix, \\\"conda-meta\\\", \\\"pinned\\\"),\\n        )\\n        super().__init__(\\n            message,\\n            requested_specs=requested_specs,\\n            pinned_specs=pinned_specs,\\n            prefix=prefix,\\n        )\\n\\n\\nclass CondaIndexError(CondaError, IndexError):\\n    def __init__(self, message):\\n        msg = f\\\"{message}\\\"\\n        super().__init__(msg)\\n\\n\\nclass CondaValueError(CondaError, ValueError):\\n    def __init__(self, message, *args, **kwargs):\\n        super().__init__(message, *args, **kwargs)\\n\\n\\nclass CyclicalDependencyError(CondaError, ValueError):\\n    def __init__(self, packages_with_cycles, **kwargs):\\n        from .models.records import PackageRecord\\n\\n        packages_with_cycles = tuple(\\n            PackageRecord.from_objects(p) for p in packages_with_cycles\\n        )\\n        message = f\\\"Cyclic dependencies exist among these items: {dashlist(p.dist_str() for p in packages_with_cycles)}\\\"\\n        super().__init__(message, packages_with_cycles=packages_with_cycles, **kwargs)\\n\\n\\nclass CorruptedEnvironmentError(CondaError):\\n    def __init__(self, environment_location, corrupted_file, **kwargs):\\n        message = dals(\\n            \\\"\\\"\\\"\\n        The target environment has been corrupted. Corrupted environments most commonly\\n        occur when the conda process is force-terminated while in an unlink-link\\n        transaction.\\n          environment location: %(environment_location)s\\n          corrupted file: %(corrupted_file)s\\n        \\\"\\\"\\\"\\n        )\\n        super().__init__(\\n            message,\\n            environment_location=environment_location,\\n            corrupted_file=corrupted_file,\\n            **kwargs,\\n        )\\n\\n\\nclass CondaHistoryError(CondaError):\\n    def __init__(self, message):\\n        msg = f\\\"{message}\\\"\\n        super().__init__(msg)\\n\\n\\nclass CondaUpgradeError(CondaError):\\n    def __init__(self, message):\\n        msg = f\\\"{message}\\\"\\n        super().__init__(msg)\\n\\n\\nclass CondaVerificationError(CondaError):\\n    def __init__(self, message):\\n        super().__init__(message)\\n\\n\\nclass SafetyError(CondaError):\\n    def __init__(self, message):\\n        super().__init__(message)\\n\\n\\nclass CondaMemoryError(CondaError, MemoryError):\\n    def __init__(self, caused_by, **kwargs):\\n        message = \\\"The conda process ran out of memory. Increase system memory and/or try again.\\\"\\n        super().__init__(message, caused_by=caused_by, **kwargs)\\n\\n\\nclass NotWritableError(CondaError, OSError):\\n    def __init__(self, path, errno, **kwargs):\\n        kwargs.update(\\n            {\\n                \\\"path\\\": path,\\n                \\\"errno\\\": errno,\\n            }\\n        )\\n        if on_win:\\n            message = dals(\\n                \\\"\\\"\\\"\\n            The current user does not have write permissions to a required path.\\n              path: %(path)s\\n            \\\"\\\"\\\"\\n            )\\n        else:\\n            message = dals(\\n                \\\"\\\"\\\"\\n            The current user does not have write permissions to a required path.\\n              path: %(path)s\\n              uid: %(uid)s\\n              gid: %(gid)s\\n\\n            If you feel that permissions on this path are set incorrectly, you can manually\\n            change them by executing\\n\\n              $ sudo chown %(uid)s:%(gid)s %(path)s\\n\\n            In general, it's not advisable to use 'sudo conda'.\\n            \\\"\\\"\\\"\\n            )\\n            kwargs.update(\\n                {\\n                    \\\"uid\\\": os.geteuid(),\\n                    \\\"gid\\\": os.getegid(),\\n                }\\n            )\\n        super().__init__(message, **kwargs)\\n        self.errno = errno\\n\\n\\nclass NoWritableEnvsDirError(CondaError):\\n    def __init__(self, envs_dirs, **kwargs):\\n        message = f\\\"No writeable envs directories configured.{dashlist(envs_dirs)}\\\"\\n        super().__init__(message, envs_dirs=envs_dirs, **kwargs)\\n\\n\\nclass NoWritablePkgsDirError(CondaError):\\n    def __init__(self, pkgs_dirs, **kwargs):\\n        message = f\\\"No writeable pkgs directories configured.{dashlist(pkgs_dirs)}\\\"\\n        super().__init__(message, pkgs_dirs=pkgs_dirs, **kwargs)\\n\\n\\nclass EnvironmentNotWritableError(CondaError):\\n    def __init__(self, environment_location, **kwargs):\\n        kwargs.update(\\n            {\\n                \\\"environment_location\\\": environment_location,\\n            }\\n        )\\n        if on_win:\\n            message = dals(\\n                \\\"\\\"\\\"\\n            The current user does not have write permissions to the target environment.\\n              environment location: %(environment_location)s\\n            \\\"\\\"\\\"\\n            )\\n        else:\\n            message = dals(\\n                \\\"\\\"\\\"\\n            The current user does not have write permissions to the target environment.\\n              environment location: %(environment_location)s\\n              uid: %(uid)s\\n              gid: %(gid)s\\n            \\\"\\\"\\\"\\n            )\\n            kwargs.update(\\n                {\\n                    \\\"uid\\\": os.geteuid(),\\n                    \\\"gid\\\": os.getegid(),\\n                }\\n            )\\n        super().__init__(message, **kwargs)\\n\\n\\nclass CondaDependencyError(CondaError):\\n    def __init__(self, message):\\n        super().__init__(message)\\n\\n\\nclass BinaryPrefixReplacementError(CondaError):\\n    def __init__(\\n        self, path, placeholder, new_prefix, original_data_length, new_data_length\\n    ):\\n        message = dals(\\n            \\\"\\\"\\\"\\n        Refusing to replace mismatched data length in binary file.\\n          path: %(path)s\\n          placeholder: %(placeholder)s\\n          new prefix: %(new_prefix)s\\n          original data Length: %(original_data_length)d\\n          new data length: %(new_data_length)d\\n        \\\"\\\"\\\"\\n        )\\n        kwargs = {\\n            \\\"path\\\": path,\\n            \\\"placeholder\\\": placeholder,\\n            \\\"new_prefix\\\": new_prefix,\\n            \\\"original_data_length\\\": original_data_length,\\n            \\\"new_data_length\\\": new_data_length,\\n        }\\n        super().__init__(message, **kwargs)\\n\\n\\nclass InvalidSpec(CondaError, ValueError):\\n    def __init__(self, message: str, **kwargs):\\n        super().__init__(message, **kwargs)\\n\\n\\nclass InvalidVersionSpec(InvalidSpec):\\n    def __init__(self, invalid_spec: str, details: str):\\n        message = \\\"Invalid version '%(invalid_spec)s': %(details)s\\\"\\n        super().__init__(message, invalid_spec=invalid_spec, details=details)\\n\\n\\nclass InvalidMatchSpec(InvalidSpec):\\n    def __init__(self, invalid_spec: str, details: str):\\n        message = \\\"Invalid spec '%(invalid_spec)s': %(details)s\\\"\\n        super().__init__(message, invalid_spec=invalid_spec, details=details)\\n\\n\\nclass EncodingError(CondaError):\\n    def __init__(self, caused_by, **kwargs):\\n        message = (\\n            dals(\\n                \\\"\\\"\\\"\\n        A unicode encoding or decoding error has occurred.\\n        Python 2 is the interpreter under which conda is running in your base environment.\\n        Replacing your base environment with one having Python 3 may help resolve this issue.\\n        If you still have a need for Python 2 environments, consider using 'conda create'\\n        and 'conda activate'.  For example:\\n\\n            $ conda create -n py2 python=2\\n            $ conda activate py2\\n\\n        Error details: %r\\n\\n        \\\"\\\"\\\"\\n            )\\n            % caused_by\\n        )\\n        super().__init__(message, caused_by=caused_by, **kwargs)\\n\\n\\nclass NoSpaceLeftError(CondaError):\\n    def __init__(self, caused_by, **kwargs):\\n        message = \\\"No space left on devices.\\\"\\n        super().__init__(message, caused_by=caused_by, **kwargs)\\n\\n\\nclass CondaEnvException(CondaError):\\n    def __init__(self, message, *args, **kwargs):\\n        msg = f\\\"{message}\\\"\\n        super().__init__(msg, *args, **kwargs)\\n\\n\\nclass EnvironmentFileNotFound(CondaEnvException):\\n    def __init__(self, filename, *args, **kwargs):\\n        msg = f\\\"'{filename}' file not found\\\"\\n        self.filename = filename\\n        super().__init__(msg, *args, **kwargs)\\n\\n\\nclass EnvironmentFileExtensionNotValid(CondaEnvException):\\n    def __init__(self, filename, *args, **kwargs):\\n        msg = f\\\"'{filename}' file extension must be one of '.txt', '.yaml' or '.yml'\\\"\\n        self.filename = filename\\n        super().__init__(msg, *args, **kwargs)\\n\\n\\nclass EnvironmentFileEmpty(CondaEnvException):\\n    def __init__(self, filename, *args, **kwargs):\\n        self.filename = filename\\n        msg = f\\\"'{filename}' is empty\\\"\\n        super().__init__(msg, *args, **kwargs)\\n\\n\\nclass EnvironmentFileNotDownloaded(CondaError):\\n    def __init__(self, username, packagename, *args, **kwargs):\\n        msg = f\\\"{username}/{packagename} file not downloaded\\\"\\n        self.username = username\\n        self.packagename = packagename\\n        super().__init__(msg, *args, **kwargs)\\n\\n\\nclass SpecNotFound(CondaError):\\n    def __init__(self, msg, *args, **kwargs):\\n        super().__init__(msg, *args, **kwargs)\\n\\n\\nclass PluginError(CondaError):\\n    pass\\n\\n\\ndef maybe_raise(error, context):\\n    if isinstance(error, CondaMultiError):\\n        groups = groupby(lambda e: isinstance(e, ClobberError), error.errors)\\n        clobber_errors = groups.get(True, ())\\n        groups = groupby(lambda e: isinstance(e, SafetyError), groups.get(False, ()))\\n        safety_errors = groups.get(True, ())\\n        other_errors = groups.get(False, ())\\n\\n        if (\\n            (safety_errors and context.safety_checks == SafetyChecks.enabled)\\n            or (\\n                clobber_errors\\n                and context.path_conflict == PathConflict.prevent\\n                and not context.clobber\\n            )\\n            or other_errors\\n        ):\\n            raise error\\n        elif (safety_errors and context.safety_checks == SafetyChecks.warn) or (\\n            clobber_errors\\n            and context.path_conflict == PathConflict.warn\\n            and not context.clobber\\n        ):\\n            print_conda_exception(error)\\n\\n    elif isinstance(error, ClobberError):\\n        if context.path_conflict == PathConflict.prevent and not context.clobber:\\n            raise error\\n        elif context.path_conflict == PathConflict.warn and not context.clobber:\\n            print_conda_exception(error)\\n\\n    elif isinstance(error, SafetyError):\\n        if context.safety_checks == SafetyChecks.enabled:\\n            raise error\\n        elif context.safety_checks == SafetyChecks.warn:\\n            print_conda_exception(error)\\n\\n    else:\\n        raise error\\n\\n\\ndef print_conda_exception(exc_val, exc_tb=None):\\n    from .base.context import context\\n\\n    rc = getattr(exc_val, \\\"return_code\\\", None)\\n    if context.debug or (not isinstance(exc_val, DryRunExit) and context.info):\\n        print(_format_exc(exc_val, exc_tb), file=sys.stderr)\\n    elif context.json:\\n        if isinstance(exc_val, DryRunExit):\\n            return\\n        logger = getLogger(\\\"conda.stdout\\\" if rc else \\\"conda.stderr\\\")\\n        exc_json = json.dumps(\\n            exc_val.dump_map(), indent=2, sort_keys=True, cls=EntityEncoder\\n        )\\n        logger.info(f\\\"{exc_json}\\\\n\\\")\\n    else:\\n        stderrlog = getLogger(\\\"conda.stderr\\\")\\n        stderrlog.error(\\\"\\\\n%r\\\\n\\\", exc_val)\\n        # An alternative which would allow us not to reload sys with newly setdefaultencoding()\\n        # is to not use `%r`, e.g.:\\n        # Still, not being able to use `%r` seems too great a price to pay.\\n        # stderrlog.error(\\\"\\\\n\\\" + exc_val.__repr__() + \\\\n\\\")\\n\\n\\ndef _format_exc(exc_val=None, exc_tb=None):\\n    if exc_val is None:\\n        exc_type, exc_val, exc_tb = sys.exc_info()\\n    else:\\n        exc_type = type(exc_val)\\n    if exc_tb:\\n        formatted_exception = format_exception(exc_type, exc_val, exc_tb)\\n    else:\\n        formatted_exception = format_exception_only(exc_type, exc_val)\\n    return \\\"\\\".join(formatted_exception)\\n\\n\\nclass InvalidInstaller(Exception):\\n    def __init__(self, name):\\n        msg = f\\\"Unable to load installer for {name}\\\"\\n        super().__init__(msg)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Collection of conda's high-level APIs.\\\"\\\"\\\"\\n\\nfrom .base.constants import DepsModifier as _DepsModifier\\nfrom .base.constants import UpdateModifier as _UpdateModifier\\nfrom .base.context import context\\nfrom .common.constants import NULL\\nfrom .core.package_cache_data import PackageCacheData as _PackageCacheData\\nfrom .core.prefix_data import PrefixData as _PrefixData\\nfrom .core.subdir_data import SubdirData as _SubdirData\\nfrom .models.channel import Channel\\n\\n#: Flags to enable alternate handling of dependencies.\\nDepsModifier = _DepsModifier\\n\\n#: Flags to enable alternate handling for updates of existing packages in the environment.\\nUpdateModifier = _UpdateModifier\\n\\n\\nclass Solver:\\n    \\\"\\\"\\\"\\n    **Beta** While in beta, expect both major and minor changes across minor releases.\\n\\n    A high-level API to conda's solving logic. Three public methods are provided to access a\\n    solution in various forms.\\n\\n      * :meth:`solve_final_state`\\n      * :meth:`solve_for_diff`\\n      * :meth:`solve_for_transaction`\\n\\n    \\\"\\\"\\\"\\n\\n    def __init__(\\n        self, prefix, channels, subdirs=(), specs_to_add=(), specs_to_remove=()\\n    ):\\n        \\\"\\\"\\\"\\n        **Beta**\\n\\n        Args:\\n            prefix (str):\\n                The conda prefix / environment location for which the :class:`Solver`\\n                is being instantiated.\\n            channels (Sequence[:class:`Channel`]):\\n                A prioritized list of channels to use for the solution.\\n            subdirs (Sequence[str]):\\n                A prioritized list of subdirs to use for the solution.\\n            specs_to_add (set[:class:`MatchSpec`]):\\n                The set of package specs to add to the prefix.\\n            specs_to_remove (set[:class:`MatchSpec`]):\\n                The set of package specs to remove from the prefix.\\n\\n        \\\"\\\"\\\"\\n        solver_backend = context.plugin_manager.get_cached_solver_backend()\\n        self._internal = solver_backend(\\n            prefix, channels, subdirs, specs_to_add, specs_to_remove\\n        )\\n\\n    def solve_final_state(\\n        self,\\n        update_modifier=NULL,\\n        deps_modifier=NULL,\\n        prune=NULL,\\n        ignore_pinned=NULL,\\n        force_remove=NULL,\\n    ):\\n        \\\"\\\"\\\"\\n        **Beta** While in beta, expect both major and minor changes across minor releases.\\n\\n        Gives the final, solved state of the environment.\\n\\n        Args:\\n            deps_modifier (DepsModifier):\\n                An optional flag indicating special solver handling for dependencies. The\\n                default solver behavior is to be as conservative as possible with dependency\\n                updates (in the case the dependency already exists in the environment), while\\n                still ensuring all dependencies are satisfied.  Options include\\n                * NO_DEPS\\n                * ONLY_DEPS\\n                * UPDATE_DEPS\\n                * UPDATE_DEPS_ONLY_DEPS\\n                * FREEZE_INSTALLED\\n            prune (bool):\\n                If ``True``, the solution will not contain packages that were\\n                previously brought into the environment as dependencies but are no longer\\n                required as dependencies and are not user-requested.\\n            ignore_pinned (bool):\\n                If ``True``, the solution will ignore pinned package configuration\\n                for the prefix.\\n            force_remove (bool):\\n                Forces removal of a package without removing packages that depend on it.\\n\\n        Returns:\\n            tuple[PackageRef]:\\n                In sorted dependency order from roots to leaves, the package references for\\n                the solved state of the environment.\\n\\n        \\\"\\\"\\\"\\n        return self._internal.solve_final_state(\\n            update_modifier, deps_modifier, prune, ignore_pinned, force_remove\\n        )\\n\\n    def solve_for_diff(\\n        self,\\n        update_modifier=NULL,\\n        deps_modifier=NULL,\\n        prune=NULL,\\n        ignore_pinned=NULL,\\n        force_remove=NULL,\\n        force_reinstall=False,\\n    ):\\n        \\\"\\\"\\\"\\n        **Beta** While in beta, expect both major and minor changes across minor releases.\\n\\n        Gives the package references to remove from an environment, followed by\\n        the package references to add to an environment.\\n\\n        Args:\\n            deps_modifier (DepsModifier):\\n                See :meth:`solve_final_state`.\\n            prune (bool):\\n                See :meth:`solve_final_state`.\\n            ignore_pinned (bool):\\n                See :meth:`solve_final_state`.\\n            force_remove (bool):\\n                See :meth:`solve_final_state`.\\n            force_reinstall (bool):\\n                For requested specs_to_add that are already satisfied in the environment,\\n                instructs the solver to remove the package and spec from the environment,\\n                and then add it back--possibly with the exact package instance modified,\\n                depending on the spec exactness.\\n\\n        Returns:\\n            tuple[PackageRef], tuple[PackageRef]:\\n                A two-tuple of PackageRef sequences.  The first is the group of packages to\\n                remove from the environment, in sorted dependency order from leaves to roots.\\n                The second is the group of packages to add to the environment, in sorted\\n                dependency order from roots to leaves.\\n\\n        \\\"\\\"\\\"\\n        return self._internal.solve_for_diff(\\n            update_modifier,\\n            deps_modifier,\\n            prune,\\n            ignore_pinned,\\n            force_remove,\\n            force_reinstall,\\n        )\\n\\n    def solve_for_transaction(\\n        self,\\n        update_modifier=NULL,\\n        deps_modifier=NULL,\\n        prune=NULL,\\n        ignore_pinned=NULL,\\n        force_remove=NULL,\\n        force_reinstall=False,\\n    ):\\n        \\\"\\\"\\\"\\n        **Beta** While in beta, expect both major and minor changes across minor releases.\\n\\n        Gives an UnlinkLinkTransaction instance that can be used to execute the solution\\n        on an environment.\\n\\n        Args:\\n            deps_modifier (DepsModifier):\\n                See :meth:`solve_final_state`.\\n            prune (bool):\\n                See :meth:`solve_final_state`.\\n            ignore_pinned (bool):\\n                See :meth:`solve_final_state`.\\n            force_remove (bool):\\n                See :meth:`solve_final_state`.\\n            force_reinstall (bool):\\n                See :meth:`solve_for_diff`.\\n\\n        Returns:\\n            UnlinkLinkTransaction:\\n\\n        \\\"\\\"\\\"\\n        return self._internal.solve_for_transaction(\\n            update_modifier,\\n            deps_modifier,\\n            prune,\\n            ignore_pinned,\\n            force_remove,\\n            force_reinstall,\\n        )\\n\\n\\nclass SubdirData:\\n    \\\"\\\"\\\"\\n    **Beta** While in beta, expect both major and minor changes across minor releases.\\n\\n    High-level management and usage of repodata.json for subdirs.\\n    \\\"\\\"\\\"\\n\\n    def __init__(self, channel):\\n        \\\"\\\"\\\"\\n        **Beta** While in beta, expect both major and minor changes across minor releases.\\n\\n        Args:\\n            channel (str or Channel):\\n                The target subdir for the instance. Must either be a url that includes a subdir\\n                or a :obj:`Channel` that includes a subdir. e.g.:\\n                    * 'https://repo.anaconda.com/pkgs/main/linux-64'\\n                    * Channel('https://repo.anaconda.com/pkgs/main/linux-64')\\n                    * Channel('conda-forge/osx-64')\\n        \\\"\\\"\\\"\\n        channel = Channel(channel)\\n        assert channel.subdir\\n        self._internal = _SubdirData(channel)\\n\\n    def query(self, package_ref_or_match_spec):\\n        \\\"\\\"\\\"\\n        **Beta** While in beta, expect both major and minor changes across minor releases.\\n\\n        Run a query against this specific instance of repodata.\\n\\n        Args:\\n            package_ref_or_match_spec (PackageRef or MatchSpec or str):\\n                Either an exact :obj:`PackageRef` to match against, or a :obj:`MatchSpec`\\n                query object.  A :obj:`str` will be turned into a :obj:`MatchSpec` automatically.\\n\\n        Returns:\\n            tuple[PackageRecord]\\n\\n        \\\"\\\"\\\"\\n        return tuple(self._internal.query(package_ref_or_match_spec))\\n\\n    @staticmethod\\n    def query_all(package_ref_or_match_spec, channels=None, subdirs=None):\\n        \\\"\\\"\\\"\\n        **Beta** While in beta, expect both major and minor changes across minor releases.\\n\\n        Run a query against all repodata instances in channel/subdir matrix.\\n\\n        Args:\\n            package_ref_or_match_spec (PackageRef or MatchSpec or str):\\n                Either an exact :obj:`PackageRef` to match against, or a :obj:`MatchSpec`\\n                query object.  A :obj:`str` will be turned into a :obj:`MatchSpec` automatically.\\n            channels (Iterable[Channel or str] or None):\\n                An iterable of urls for channels or :obj:`Channel` objects. If None, will fall\\n                back to context.channels.\\n            subdirs (Iterable[str] or None):\\n                If None, will fall back to context.subdirs.\\n\\n        Returns:\\n            tuple[PackageRecord]\\n\\n        \\\"\\\"\\\"\\n        return tuple(\\n            _SubdirData.query_all(package_ref_or_match_spec, channels, subdirs)\\n        )\\n\\n    def iter_records(self):\\n        \\\"\\\"\\\"\\n        **Beta** While in beta, expect both major and minor changes across minor releases.\\n\\n        Returns:\\n            Iterable[PackageRecord]: A generator over all records contained in the repodata.json\\n                instance.  Warning: this is a generator that is exhausted on first use.\\n\\n        \\\"\\\"\\\"\\n        return self._internal.iter_records()\\n\\n    def reload(self):\\n        \\\"\\\"\\\"\\n        **Beta** While in beta, expect both major and minor changes across minor releases.\\n\\n        Update the instance with new information. Backing information (i.e. repodata.json)\\n        is lazily downloaded/loaded on first use by the other methods of this class. You\\n        should only use this method if you are *sure* you have outdated data.\\n\\n        Returns:\\n            SubdirData\\n\\n        \\\"\\\"\\\"\\n        self._internal = self._internal.reload()\\n        return self\\n\\n\\nclass PackageCacheData:\\n    \\\"\\\"\\\"\\n    **Beta** While in beta, expect both major and minor changes across minor releases.\\n\\n    High-level management and usage of package caches.\\n    \\\"\\\"\\\"\\n\\n    def __init__(self, pkgs_dir):\\n        \\\"\\\"\\\"\\n        **Beta** While in beta, expect both major and minor changes across minor releases.\\n\\n        Args:\\n            pkgs_dir (str):\\n        \\\"\\\"\\\"\\n        self._internal = _PackageCacheData(pkgs_dir)\\n\\n    def get(self, package_ref, default=NULL):\\n        \\\"\\\"\\\"\\n        **Beta** While in beta, expect both major and minor changes across minor releases.\\n\\n        Args:\\n            package_ref (PackageRef):\\n                A :obj:`PackageRef` instance representing the key for the\\n                :obj:`PackageCacheRecord` being sought.\\n            default: The default value to return if the record does not exist. If not\\n                specified and no record exists, :exc:`KeyError` is raised.\\n\\n        Returns:\\n            PackageCacheRecord\\n\\n        \\\"\\\"\\\"\\n        return self._internal.get(package_ref, default)\\n\\n    def query(self, package_ref_or_match_spec):\\n        \\\"\\\"\\\"\\n        **Beta** While in beta, expect both major and minor changes across minor releases.\\n\\n        Run a query against this specific package cache instance.\\n\\n        Args:\\n            package_ref_or_match_spec (PackageRef or MatchSpec or str):\\n                Either an exact :obj:`PackageRef` to match against, or a :obj:`MatchSpec`\\n                query object.  A :obj:`str` will be turned into a :obj:`MatchSpec` automatically.\\n\\n        Returns:\\n            tuple[PackageCacheRecord]\\n\\n        \\\"\\\"\\\"\\n        return tuple(self._internal.query(package_ref_or_match_spec))\\n\\n    @staticmethod\\n    def query_all(package_ref_or_match_spec, pkgs_dirs=None):\\n        \\\"\\\"\\\"\\n        **Beta** While in beta, expect both major and minor changes across minor releases.\\n\\n        Run a query against all package caches.\\n\\n        Args:\\n            package_ref_or_match_spec (PackageRef or MatchSpec or str):\\n                Either an exact :obj:`PackageRef` to match against, or a :obj:`MatchSpec`\\n                query object.  A :obj:`str` will be turned into a :obj:`MatchSpec` automatically.\\n            pkgs_dirs (Iterable[str] or None):\\n                If None, will fall back to context.pkgs_dirs.\\n\\n        Returns:\\n            tuple[PackageCacheRecord]\\n\\n        \\\"\\\"\\\"\\n        return tuple(_PackageCacheData.query_all(package_ref_or_match_spec, pkgs_dirs))\\n\\n    def iter_records(self):\\n        \\\"\\\"\\\"\\n        **Beta** While in beta, expect both major and minor changes across minor releases.\\n\\n        Returns:\\n            Iterable[PackageCacheRecord]: A generator over all records contained in the package\\n                cache instance.  Warning: this is a generator that is exhausted on first use.\\n\\n        \\\"\\\"\\\"\\n        return self._internal.iter_records()\\n\\n    @property\\n    def is_writable(self):\\n        \\\"\\\"\\\"\\n        **Beta** While in beta, expect both major and minor changes across minor releases.\\n\\n        Indicates if the package cache location is writable or read-only.\\n\\n        Returns:\\n            bool\\n\\n        \\\"\\\"\\\"\\n        return self._internal.is_writable\\n\\n    @staticmethod\\n    def first_writable(pkgs_dirs=None):\\n        \\\"\\\"\\\"\\n        **Beta** While in beta, expect both major and minor changes across minor releases.\\n\\n        Get an instance object for the first writable package cache.\\n\\n        Args:\\n            pkgs_dirs (Iterable[str]):\\n                If None, will fall back to context.pkgs_dirs.\\n\\n        Returns:\\n            PackageCacheData:\\n                An instance for the first writable package cache.\\n\\n        \\\"\\\"\\\"\\n        return PackageCacheData(_PackageCacheData.first_writable(pkgs_dirs).pkgs_dir)\\n\\n    def reload(self):\\n        \\\"\\\"\\\"\\n        **Beta** While in beta, expect both major and minor changes across minor releases.\\n\\n        Update the instance with new information. Backing information (i.e. contents of\\n        the pkgs_dir) is lazily loaded on first use by the other methods of this class. You\\n        should only use this method if you are *sure* you have outdated data.\\n\\n        Returns:\\n            PackageCacheData\\n\\n        \\\"\\\"\\\"\\n        self._internal = self._internal.reload()\\n        return self\\n\\n\\nclass PrefixData:\\n    \\\"\\\"\\\"\\n    **Beta** While in beta, expect both major and minor changes across minor releases.\\n\\n    High-level management and usage of conda environment prefixes.\\n    \\\"\\\"\\\"\\n\\n    def __init__(self, prefix_path):\\n        \\\"\\\"\\\"\\n        **Beta** While in beta, expect both major and minor changes across minor releases.\\n\\n        Args:\\n            prefix_path (str):\\n        \\\"\\\"\\\"\\n        self._internal = _PrefixData(prefix_path)\\n\\n    def get(self, package_ref, default=NULL):\\n        \\\"\\\"\\\"\\n        **Beta** While in beta, expect both major and minor changes across minor releases.\\n\\n        Args:\\n            package_ref (PackageRef):\\n                A :obj:`PackageRef` instance representing the key for the\\n                :obj:`PrefixRecord` being sought.\\n            default: The default value to return if the record does not exist. If not\\n                specified and no record exists, :exc:`KeyError` is raised.\\n\\n        Returns:\\n            PrefixRecord\\n\\n        \\\"\\\"\\\"\\n        return self._internal.get(package_ref.name, default)\\n\\n    def query(self, package_ref_or_match_spec):\\n        \\\"\\\"\\\"\\n        **Beta** While in beta, expect both major and minor changes across minor releases.\\n\\n        Run a query against this specific prefix instance.\\n\\n        Args:\\n            package_ref_or_match_spec (PackageRef or MatchSpec or str):\\n                Either an exact :obj:`PackageRef` to match against, or a :obj:`MatchSpec`\\n                query object.  A :obj:`str` will be turned into a :obj:`MatchSpec` automatically.\\n\\n        Returns:\\n            tuple[PrefixRecord]\\n\\n        \\\"\\\"\\\"\\n        return tuple(self._internal.query(package_ref_or_match_spec))\\n\\n    def iter_records(self):\\n        \\\"\\\"\\\"\\n        **Beta** While in beta, expect both major and minor changes across minor releases.\\n\\n        Returns:\\n            Iterable[PrefixRecord]: A generator over all records contained in the prefix.\\n                Warning: this is a generator that is exhausted on first use.\\n\\n        \\\"\\\"\\\"\\n        return self._internal.iter_records()\\n\\n    @property\\n    def is_writable(self):\\n        \\\"\\\"\\\"\\n        **Beta** While in beta, expect both major and minor changes across minor releases.\\n\\n        Indicates if the prefix is writable or read-only.\\n\\n        Returns:\\n            bool or None:\\n                True if the prefix is writable.  False if read-only.  None if the prefix\\n                does not exist as a conda environment.\\n\\n        \\\"\\\"\\\"\\n        return self._internal.is_writable\\n\\n    def reload(self):\\n        \\\"\\\"\\\"\\n        **Beta** While in beta, expect both major and minor changes across minor releases.\\n\\n        Update the instance with new information. Backing information (i.e. contents of\\n        the conda-meta directory) is lazily loaded on first use by the other methods of this\\n        class. You should only use this method if you are *sure* you have outdated data.\\n\\n        Returns:\\n            PrefixData\\n\\n        \\\"\\\"\\\"\\n        self._internal = self._internal.reload()\\n        return self\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"OS-agnostic, system-level binary package manager.\\\"\\\"\\\"\\n\\nimport os\\nimport sys\\nfrom json import JSONEncoder\\nfrom os.path import abspath, dirname\\n\\ntry:\\n    from ._version import __version__\\nexcept ImportError:\\n    # _version.py is only created after running `pip install`\\n    try:\\n        from setuptools_scm import get_version\\n\\n        __version__ = get_version(root=\\\"..\\\", relative_to=__file__)\\n    except (ImportError, OSError, LookupError):\\n        # ImportError: setuptools_scm isn't installed\\n        # OSError: git isn't installed\\n        # LookupError: setuptools_scm unable to detect version\\n        # Conda abides by CEP-8 which specifies using CalVer, so the dev version is:\\n        #     YY.MM.MICRO.devN+gHASH[.dirty]\\n        __version__ = \\\"0.0.0.dev0+placeholder\\\"\\n\\n\\ntry:\\n    from frozendict import frozendict\\nexcept ImportError:\\n    from ._vendor.frozendict import frozendict\\n\\n__all__ = (\\n    \\\"__name__\\\",\\n    \\\"__version__\\\",\\n    \\\"__author__\\\",\\n    \\\"__email__\\\",\\n    \\\"__license__\\\",\\n    \\\"__summary__\\\",\\n    \\\"__url__\\\",\\n    \\\"CONDA_PACKAGE_ROOT\\\",\\n    \\\"CondaError\\\",\\n    \\\"CondaMultiError\\\",\\n    \\\"CondaExitZero\\\",\\n    \\\"conda_signal_handler\\\",\\n    \\\"__copyright__\\\",\\n)\\n\\n__name__ = \\\"conda\\\"\\n__author__ = \\\"Anaconda, Inc.\\\"\\n__email__ = \\\"conda@continuum.io\\\"\\n__license__ = \\\"BSD-3-Clause\\\"\\n__copyright__ = \\\"Copyright (c) 2012, Anaconda, Inc.\\\"\\n__summary__ = __doc__\\n__url__ = \\\"https://github.com/conda/conda\\\"\\n\\nif os.getenv(\\\"CONDA_ROOT\\\") is None:\\n    os.environ[\\\"CONDA_ROOT\\\"] = sys.prefix\\n\\n#: The conda package directory.\\nCONDA_PACKAGE_ROOT = abspath(dirname(__file__))\\n#: The path within which to find the conda package.\\n#:\\n#: If `conda` is statically installed this is the site-packages. If `conda` is an editable install\\n#: or otherwise uninstalled this is the git repo.\\nCONDA_SOURCE_ROOT = dirname(CONDA_PACKAGE_ROOT)\\n\\n\\nclass CondaError(Exception):\\n    return_code = 1\\n    reportable = False  # Exception may be reported to core maintainers\\n\\n    def __init__(self, message, caused_by=None, **kwargs):\\n        self.message = message\\n        self._kwargs = kwargs\\n        self._caused_by = caused_by\\n        super().__init__(message)\\n\\n    def __repr__(self):\\n        return f\\\"{self.__class__.__name__}: {self}\\\"\\n\\n    def __str__(self):\\n        try:\\n            return str(self.message % self._kwargs)\\n        except Exception:\\n            debug_message = \\\"\\\\n\\\".join(\\n                (\\n                    \\\"class: \\\" + self.__class__.__name__,\\n                    \\\"message:\\\",\\n                    self.message,\\n                    \\\"kwargs:\\\",\\n                    str(self._kwargs),\\n                    \\\"\\\",\\n                )\\n            )\\n            print(debug_message, file=sys.stderr)\\n            raise\\n\\n    def dump_map(self):\\n        result = {k: v for k, v in vars(self).items() if not k.startswith(\\\"_\\\")}\\n        result.update(\\n            exception_type=str(type(self)),\\n            exception_name=self.__class__.__name__,\\n            message=str(self),\\n            error=repr(self),\\n            caused_by=repr(self._caused_by),\\n            **self._kwargs,\\n        )\\n        return result\\n\\n\\nclass CondaMultiError(CondaError):\\n    def __init__(self, errors):\\n        self.errors = errors\\n        super().__init__(None)\\n\\n    def __repr__(self):\\n        errs = []\\n        for e in self.errors:\\n            if isinstance(e, EnvironmentError) and not isinstance(e, CondaError):\\n                errs.append(str(e))\\n            else:\\n                # We avoid Python casting this back to a str()\\n                # by using e.__repr__() instead of repr(e)\\n                # https://github.com/scrapy/cssselect/issues/34\\n                errs.append(e.__repr__())\\n        res = \\\"\\\\n\\\".join(errs)\\n        return res\\n\\n    def __str__(self):\\n        return \\\"\\\\n\\\".join(str(e) for e in self.errors) + \\\"\\\\n\\\"\\n\\n    def dump_map(self):\\n        return dict(\\n            exception_type=str(type(self)),\\n            exception_name=self.__class__.__name__,\\n            errors=tuple(error.dump_map() for error in self.errors),\\n            error=\\\"Multiple Errors Encountered.\\\",\\n        )\\n\\n    def contains(self, exception_class):\\n        return any(isinstance(e, exception_class) for e in self.errors)\\n\\n\\nclass CondaExitZero(CondaError):\\n    return_code = 0\\n\\n\\nACTIVE_SUBPROCESSES = set()\\n\\n\\ndef conda_signal_handler(signum, frame):\\n    # This function is in the base __init__.py so that it can be monkey-patched by other code\\n    #   if downstream conda users so choose.  The biggest danger of monkey-patching is that\\n    #   unlink/link transactions don't get rolled back if interrupted mid-transaction.\\n    for p in ACTIVE_SUBPROCESSES:\\n        if p.poll() is None:\\n            p.send_signal(signum)\\n\\n    from .exceptions import CondaSignalInterrupt\\n\\n    raise CondaSignalInterrupt(signum)\\n\\n\\ndef _default(self, obj):\\n    if isinstance(obj, frozendict):\\n        return dict(obj)\\n    if hasattr(obj, \\\"to_json\\\"):\\n        return obj.to_json()\\n    return _default.default(obj)\\n\\n\\n_default.default = JSONEncoder().default\\nJSONEncoder.default = _default\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Tools interfacing with conda's history file.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport codecs\\nimport logging\\nimport os\\nimport re\\nimport sys\\nimport time\\nimport warnings\\nfrom ast import literal_eval\\nfrom errno import EACCES, EPERM, EROFS\\nfrom itertools import islice\\nfrom operator import itemgetter\\nfrom os.path import isdir, isfile, join\\nfrom textwrap import dedent\\n\\nfrom . import __version__ as CONDA_VERSION\\nfrom .auxlib.ish import dals\\nfrom .base.constants import DEFAULTS_CHANNEL_NAME\\nfrom .base.context import context\\nfrom .common.compat import ensure_text_type, open\\nfrom .common.iterators import groupby_to_dict as groupby\\nfrom .common.path import paths_equal\\nfrom .core.prefix_data import PrefixData\\nfrom .exceptions import CondaHistoryError, NotWritableError\\nfrom .gateways.disk.update import touch\\nfrom .models.dist import dist_str_to_quad\\nfrom .models.match_spec import MatchSpec\\nfrom .models.version import VersionOrder, version_relation_re\\n\\nlog = logging.getLogger(__name__)\\n\\n\\nclass CondaHistoryWarning(Warning):\\n    pass\\n\\n\\ndef write_head(fo):\\n    fo.write(\\\"==> {} <==\\\\n\\\".format(time.strftime(\\\"%Y-%m-%d %H:%M:%S\\\")))\\n    fo.write(\\\"# cmd: {}\\\\n\\\".format(\\\" \\\".join(ensure_text_type(s) for s in sys.argv)))\\n    fo.write(\\n        \\\"# conda version: {}\\\\n\\\".format(\\\".\\\".join(islice(CONDA_VERSION.split(\\\".\\\"), 3)))\\n    )\\n\\n\\ndef is_diff(content):\\n    return any(s.startswith((\\\"-\\\", \\\"+\\\")) for s in content)\\n\\n\\ndef pretty_diff(diff):\\n    added = {}\\n    removed = {}\\n    for s in diff:\\n        fn = s[1:]\\n        name, version, _, channel = dist_str_to_quad(fn)\\n        if channel != DEFAULTS_CHANNEL_NAME:\\n            version += f\\\" ({channel})\\\"\\n        if s.startswith(\\\"-\\\"):\\n            removed[name.lower()] = version\\n        elif s.startswith(\\\"+\\\"):\\n            added[name.lower()] = version\\n    changed = set(added) & set(removed)\\n    for name in sorted(changed):\\n        yield f\\\" {name}  {{{removed[name]} -> {added[name]}}}\\\"\\n    for name in sorted(set(removed) - changed):\\n        yield f\\\"-{name}-{removed[name]}\\\"\\n    for name in sorted(set(added) - changed):\\n        yield f\\\"+{name}-{added[name]}\\\"\\n\\n\\ndef pretty_content(content):\\n    if is_diff(content):\\n        return pretty_diff(content)\\n    else:\\n        return iter(sorted(content))\\n\\n\\nclass History:\\n    com_pat = re.compile(r\\\"#\\\\s*cmd:\\\\s*(.+)\\\")\\n    spec_pat = re.compile(r\\\"#\\\\s*(\\\\w+)\\\\s*specs:\\\\s*(.+)?\\\")\\n    conda_v_pat = re.compile(r\\\"#\\\\s*conda version:\\\\s*(.+)\\\")\\n\\n    def __init__(self, prefix):\\n        self.prefix = prefix\\n        self.meta_dir = join(prefix, \\\"conda-meta\\\")\\n        self.path = join(self.meta_dir, \\\"history\\\")\\n\\n    def __enter__(self):\\n        self.init_log_file()\\n        return self\\n\\n    def __exit__(self, exc_type, exc_value, traceback):\\n        self.update()\\n\\n    def init_log_file(self):\\n        touch(self.path, True)\\n\\n    def file_is_empty(self):\\n        return os.stat(self.path).st_size == 0\\n\\n    def update(self) -> None:\\n        \\\"\\\"\\\"Update the history file (creating a new one if necessary).\\\"\\\"\\\"\\n        try:\\n            try:\\n                last = set(self.get_state())\\n            except CondaHistoryError as e:\\n                warnings.warn(f\\\"Error in {self.path}: {e}\\\", CondaHistoryWarning)\\n                return\\n            pd = PrefixData(self.prefix)\\n            curr = {prefix_rec.dist_str() for prefix_rec in pd.iter_records()}\\n            self.write_changes(last, curr)\\n        except OSError as e:\\n            if e.errno in (EACCES, EPERM, EROFS):\\n                raise NotWritableError(self.path, e.errno)\\n            else:\\n                raise\\n\\n    def parse(self) -> list[tuple[str, set[str], list[str]]]:\\n        \\\"\\\"\\\"Parse the history file.\\n\\n        Return a list of tuples(datetime strings, set of distributions/diffs, comments).\\n\\n        Comments appearing before the first section header (e.g. ``==> 2024-01-01 00:00:00 <==``)\\n        in the history file will be ignored.\\n        \\\"\\\"\\\"\\n        res = []\\n        if not isfile(self.path):\\n            return res\\n        sep_pat = re.compile(r\\\"==>\\\\s*(.+?)\\\\s*<==\\\")\\n        with open(self.path) as f:\\n            lines = f.read().splitlines()\\n        for line in lines:\\n            line = line.strip()\\n            if not line:\\n                continue\\n            m = sep_pat.match(line)\\n            if m:\\n                res.append((m.group(1), set(), []))\\n            elif line.startswith(\\\"#\\\") and res:\\n                res[-1][2].append(line)\\n            elif res:\\n                res[-1][1].add(line)\\n        return res\\n\\n    @staticmethod\\n    def _parse_old_format_specs_string(specs_string):\\n        \\\"\\\"\\\"\\n        Parse specifications string that use conda<4.5 syntax.\\n\\n        Examples\\n        --------\\n          - \\\"param >=1.5.1,<2.0'\\\"\\n          - \\\"python>=3.5.1,jupyter >=1.0.0,<2.0,matplotlib >=1.5.1,<2.0\\\"\\n        \\\"\\\"\\\"\\n        specs = []\\n        for spec in specs_string.split(\\\",\\\"):\\n            # If the spec starts with a version qualifier, then it actually belongs to the\\n            # previous spec. But don't try to join if there was no previous spec.\\n            if version_relation_re.match(spec) and specs:\\n                specs[-1] = \\\",\\\".join([specs[-1], spec])\\n            else:\\n                specs.append(spec)\\n        return specs\\n\\n    @classmethod\\n    def _parse_comment_line(cls, line):\\n        \\\"\\\"\\\"\\n        Parse comment lines in the history file.\\n\\n        These lines can be of command type or action type.\\n\\n        Examples\\n        --------\\n          - \\\"# cmd: /scratch/mc3/bin/conda install -c conda-forge param>=1.5.1,<2.0\\\"\\n          - \\\"# install specs: python>=3.5.1,jupyter >=1.0.0,<2.0,matplotlib >=1.5.1,<2.0\\\"\\n        \\\"\\\"\\\"\\n        item = {}\\n        m = cls.com_pat.match(line)\\n        if m:\\n            argv = m.group(1).split()\\n            if argv[0].endswith(\\\"conda\\\"):\\n                argv[0] = \\\"conda\\\"\\n            item[\\\"cmd\\\"] = argv\\n\\n        m = cls.conda_v_pat.match(line)\\n        if m:\\n            item[\\\"conda_version\\\"] = m.group(1)\\n\\n        m = cls.spec_pat.match(line)\\n        if m:\\n            action, specs_string = m.groups()\\n            specs_string = specs_string or \\\"\\\"\\n            item[\\\"action\\\"] = action\\n\\n            if specs_string.startswith(\\\"[\\\"):\\n                specs = literal_eval(specs_string)\\n            elif \\\"[\\\" not in specs_string:\\n                specs = History._parse_old_format_specs_string(specs_string)\\n\\n            specs = [spec for spec in specs if spec and not spec.endswith(\\\"@\\\")]\\n\\n            if specs and action in (\\\"update\\\", \\\"install\\\", \\\"create\\\"):\\n                item[\\\"update_specs\\\"] = item[\\\"specs\\\"] = specs\\n            elif specs and action in (\\\"remove\\\", \\\"uninstall\\\"):\\n                item[\\\"remove_specs\\\"] = item[\\\"specs\\\"] = specs\\n            elif specs and action in (\\\"neutered\\\",):\\n                item[\\\"neutered_specs\\\"] = item[\\\"specs\\\"] = specs\\n\\n        return item\\n\\n    def get_user_requests(self):\\n        \\\"\\\"\\\"Return a list of user requested items.\\n\\n        Each item is a dict with the following keys:\\n        'date': the date and time running the command\\n        'cmd': a list of argv of the actual command which was run\\n        'action': install/remove/update\\n        'specs': the specs being used\\n        \\\"\\\"\\\"\\n        res = []\\n        for dt, unused_cont, comments in self.parse():\\n            item = {\\\"date\\\": dt}\\n            for line in comments:\\n                comment_items = self._parse_comment_line(line)\\n                item.update(comment_items)\\n\\n            if \\\"cmd\\\" in item:\\n                res.append(item)\\n\\n            dists = groupby(itemgetter(0), unused_cont)\\n            item[\\\"unlink_dists\\\"] = dists.get(\\\"-\\\", ())\\n            item[\\\"link_dists\\\"] = dists.get(\\\"+\\\", ())\\n\\n        conda_versions_from_history = tuple(\\n            x[\\\"conda_version\\\"] for x in res if \\\"conda_version\\\" in x\\n        )\\n        if conda_versions_from_history and not context.allow_conda_downgrades:\\n            minimum_conda_version = sorted(\\n                conda_versions_from_history, key=VersionOrder\\n            )[-1]\\n            minimum_major_minor = \\\".\\\".join(islice(minimum_conda_version.split(\\\".\\\"), 2))\\n            current_major_minor = \\\".\\\".join(islice(CONDA_VERSION.split(\\\".\\\"), 2))\\n            if VersionOrder(current_major_minor) < VersionOrder(minimum_major_minor):\\n                message = dals(\\n                    \\\"\\\"\\\"\\n                This environment has previously been operated on by a conda version that's newer\\n                than the conda currently being used. A newer version of conda is required.\\n                  target environment location: %(target_prefix)s\\n                  current conda version: %(conda_version)s\\n                  minimum conda version: %(minimum_version)s\\n                \\\"\\\"\\\"\\n                ) % {\\n                    \\\"target_prefix\\\": self.prefix,\\n                    \\\"conda_version\\\": CONDA_VERSION,\\n                    \\\"minimum_version\\\": minimum_major_minor,\\n                }\\n                if not paths_equal(self.prefix, context.root_prefix):\\n                    message += dedent(\\n                        \\\"\\\"\\\"\\n                    Update conda and try again.\\n                        $ conda install -p \\\"%(base_prefix)s\\\" \\\"conda>=%(minimum_version)s\\\"\\n                    \\\"\\\"\\\"\\n                    ) % {\\n                        \\\"base_prefix\\\": context.root_prefix,\\n                        \\\"minimum_version\\\": minimum_major_minor,\\n                    }\\n                message += dedent(\\n                    \\\"\\\"\\\"\\n                To work around this restriction, one can also set the config parameter\\n                'allow_conda_downgrades' to False at their own risk.\\n                \\\"\\\"\\\"\\n                )\\n\\n                # TODO: we need to rethink this.  It's fine as a warning to try to get users\\n                #    to avoid breaking their system.  However, right now it is preventing\\n                #    normal conda operation after downgrading conda.\\n                # raise CondaUpgradeError(message)\\n\\n        return res\\n\\n    def get_requested_specs_map(self):\\n        # keys are package names and values are specs\\n        spec_map = {}\\n        for request in self.get_user_requests():\\n            remove_specs = (MatchSpec(spec) for spec in request.get(\\\"remove_specs\\\", ()))\\n            for spec in remove_specs:\\n                spec_map.pop(spec.name, None)\\n            update_specs = (MatchSpec(spec) for spec in request.get(\\\"update_specs\\\", ()))\\n            spec_map.update((s.name, s) for s in update_specs)\\n            # here is where the neutering takes effect, overriding past values\\n            neutered_specs = (\\n                MatchSpec(spec) for spec in request.get(\\\"neutered_specs\\\", ())\\n            )\\n            spec_map.update((s.name, s) for s in neutered_specs)\\n\\n        # Conda hasn't always been good about recording when specs have been removed from\\n        # environments.  If the package isn't installed in the current environment, then we\\n        # shouldn't try to force it here.\\n        prefix_recs = {_.name for _ in PrefixData(self.prefix).iter_records()}\\n        return {name: spec for name, spec in spec_map.items() if name in prefix_recs}\\n\\n    def construct_states(self):\\n        \\\"\\\"\\\"Return a list of tuples(datetime strings, set of distributions).\\\"\\\"\\\"\\n        res = []\\n        cur = set()\\n        for dt, cont, unused_com in self.parse():\\n            if not is_diff(cont):\\n                cur = cont\\n            else:\\n                for s in cont:\\n                    if s.startswith(\\\"-\\\"):\\n                        cur.discard(s[1:])\\n                    elif s.startswith(\\\"+\\\"):\\n                        cur.add(s[1:])\\n                    else:\\n                        raise CondaHistoryError(f\\\"Did not expect: {s}\\\")\\n            res.append((dt, cur.copy()))\\n        return res\\n\\n    def get_state(self, rev=-1):\\n        \\\"\\\"\\\"Return the state, i.e. the set of distributions, for a given revision.\\n\\n        Defaults to latest (which is the same as the current state when\\n        the log file is up-to-date).\\n\\n        Returns a list of dist_strs.\\n        \\\"\\\"\\\"\\n        states = self.construct_states()\\n        if not states:\\n            return set()\\n        times, pkgs = zip(*states)\\n        return pkgs[rev]\\n\\n    def print_log(self):\\n        for i, (date, content, unused_com) in enumerate(self.parse()):\\n            print(\\\"%s  (rev %d)\\\" % (date, i))\\n            for line in pretty_content(content):\\n                print(f\\\"    {line}\\\")\\n            print()\\n\\n    def object_log(self):\\n        result = []\\n        for i, (date, content, unused_com) in enumerate(self.parse()):\\n            # Based on Mateusz's code; provides more details about the\\n            # history event\\n            event = {\\n                \\\"date\\\": date,\\n                \\\"rev\\\": i,\\n                \\\"install\\\": [],\\n                \\\"remove\\\": [],\\n                \\\"upgrade\\\": [],\\n                \\\"downgrade\\\": [],\\n            }\\n            added = {}\\n            removed = {}\\n            if is_diff(content):\\n                for pkg in content:\\n                    name, version, build, channel = dist_str_to_quad(pkg[1:])\\n                    if pkg.startswith(\\\"+\\\"):\\n                        added[name.lower()] = (version, build, channel)\\n                    elif pkg.startswith(\\\"-\\\"):\\n                        removed[name.lower()] = (version, build, channel)\\n\\n                changed = set(added) & set(removed)\\n                for name in sorted(changed):\\n                    old = removed[name]\\n                    new = added[name]\\n                    details = {\\n                        \\\"old\\\": \\\"-\\\".join((name,) + old),\\n                        \\\"new\\\": \\\"-\\\".join((name,) + new),\\n                    }\\n\\n                    if new > old:\\n                        event[\\\"upgrade\\\"].append(details)\\n                    else:\\n                        event[\\\"downgrade\\\"].append(details)\\n\\n                for name in sorted(set(removed) - changed):\\n                    event[\\\"remove\\\"].append(\\\"-\\\".join((name,) + removed[name]))\\n\\n                for name in sorted(set(added) - changed):\\n                    event[\\\"install\\\"].append(\\\"-\\\".join((name,) + added[name]))\\n            else:\\n                for pkg in sorted(content):\\n                    event[\\\"install\\\"].append(pkg)\\n            result.append(event)\\n        return result\\n\\n    def write_changes(self, last_state, current_state):\\n        if not isdir(self.meta_dir):\\n            os.makedirs(self.meta_dir)\\n        with codecs.open(self.path, mode=\\\"ab\\\", encoding=\\\"utf-8\\\") as fo:\\n            write_head(fo)\\n            for fn in sorted(last_state - current_state):\\n                fo.write(f\\\"-{fn}\\\\n\\\")\\n            for fn in sorted(current_state - last_state):\\n                fo.write(f\\\"+{fn}\\\\n\\\")\\n\\n    def write_specs(self, remove_specs=(), update_specs=(), neutered_specs=()):\\n        remove_specs = [str(MatchSpec(s)) for s in remove_specs]\\n        update_specs = [str(MatchSpec(s)) for s in update_specs]\\n        neutered_specs = [str(MatchSpec(s)) for s in neutered_specs]\\n        if any((update_specs, remove_specs, neutered_specs)):\\n            with codecs.open(self.path, mode=\\\"ab\\\", encoding=\\\"utf-8\\\") as fh:\\n                if remove_specs:\\n                    fh.write(f\\\"# remove specs: {remove_specs}\\\\n\\\")\\n                if update_specs:\\n                    fh.write(f\\\"# update specs: {update_specs}\\\\n\\\")\\n                if neutered_specs:\\n                    fh.write(f\\\"# neutered specs: {neutered_specs}\\\\n\\\")\\n\\n\\nif __name__ == \\\"__main__\\\":\\n    from pprint import pprint\\n\\n    # Don't use in context manager mode---it augments the history every time\\n    h = History(sys.prefix)\\n    pprint(h.get_user_requests())\\n    print(h.get_requested_specs_map())\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Conda activate and deactivate logic.\\n\\nImplementation for all shell interface logic exposed via\\n`conda shell.* [activate|deactivate|reactivate|hook|commands]`. This includes a custom argument\\nparser, an abstract shell class, and special path handling for Windows.\\n\\nSee conda.cli.main.main_sourced for the entry point into this module.\\n\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport abc\\nimport json\\nimport ntpath\\nimport os\\nimport posixpath\\nimport re\\nimport sys\\nfrom logging import getLogger\\nfrom os.path import (\\n    abspath,\\n    basename,\\n    dirname,\\n    exists,\\n    expanduser,\\n    expandvars,\\n    isdir,\\n    join,\\n)\\nfrom pathlib import Path\\nfrom shutil import which\\nfrom subprocess import run\\nfrom textwrap import dedent\\nfrom typing import TYPE_CHECKING\\n\\n# Since we have to have configuration context here, anything imported by\\n#   conda.base.context is fair game, but nothing more.\\nfrom . import CONDA_PACKAGE_ROOT, CONDA_SOURCE_ROOT\\nfrom .auxlib.compat import Utf8NamedTemporaryFile\\nfrom .base.constants import (\\n    CONDA_ENV_VARS_UNSET_VAR,\\n    PACKAGE_ENV_VARS_DIR,\\n    PREFIX_STATE_FILE,\\n)\\nfrom .base.context import ROOT_ENV_NAME, context, locate_prefix_by_name\\nfrom .common.compat import FILESYSTEM_ENCODING, on_win\\nfrom .common.path import paths_equal\\nfrom .deprecations import deprecated\\n\\nif TYPE_CHECKING:\\n    from collections.abc import Callable, Iterable\\n\\nlog = getLogger(__name__)\\n\\n\\nclass _Activator(metaclass=abc.ABCMeta):\\n    # Activate and deactivate have three tasks\\n    #   1. Set and unset environment variables\\n    #   2. Execute/source activate.d/deactivate.d scripts\\n    #   3. Update the command prompt\\n    #\\n    # Shells should also use 'reactivate' following conda's install, update, and\\n    #   remove/uninstall commands.\\n    #\\n    # All core logic is in build_activate() or build_deactivate(), and is independent of\\n    # shell type.  Each returns a map containing the keys:\\n    #   export_vars\\n    #   unset_var\\n    #   activate_scripts\\n    #   deactivate_scripts\\n    #\\n    # The value of the CONDA_PROMPT_MODIFIER environment variable holds conda's contribution\\n    #   to the command prompt.\\n    #\\n    # To implement support for a new shell, ideally one would only need to add shell-specific\\n    # information to the __init__ method of this class.\\n\\n    # The following instance variables must be defined by each implementation.\\n    pathsep_join: str\\n    sep: str\\n    path_conversion: Callable[\\n        [str | Iterable[str] | None], str | tuple[str, ...] | None\\n    ]\\n    script_extension: str\\n    #: temporary file's extension, None writes to stdout instead\\n    tempfile_extension: str | None\\n    command_join: str\\n\\n    unset_var_tmpl: str\\n    export_var_tmpl: str\\n    set_var_tmpl: str\\n    run_script_tmpl: str\\n\\n    hook_source_path: Path | None\\n\\n    def __init__(self, arguments=None):\\n        self._raw_arguments = arguments\\n\\n    def get_export_unset_vars(self, export_metavars=True, **kwargs):\\n        \\\"\\\"\\\"\\n        :param export_metavars: whether to export `conda_exe_vars` meta variables.\\n        :param kwargs: environment variables to export.\\n            .. if you pass and set any other variable to None, then it\\n            emits it to the dict with a value of None.\\n\\n        :return: A dict of env vars to export ordered the same way as kwargs.\\n            And a list of env vars to unset.\\n        \\\"\\\"\\\"\\n        unset_vars = []\\n        export_vars = {}\\n\\n        # split provided environment variables into exports vs unsets\\n        for name, value in kwargs.items():\\n            if value is None:\\n                if context.envvars_force_uppercase:\\n                    unset_vars.append(name.upper())\\n                else:\\n                    unset_vars.append(name)\\n\\n            else:\\n                if context.envvars_force_uppercase:\\n                    export_vars[name.upper()] = value\\n                else:\\n                    export_vars[name] = value\\n\\n        if export_metavars:\\n            # split meta variables into exports vs unsets\\n            for name, value in context.conda_exe_vars_dict.items():\\n                if value is None:\\n                    if context.envvars_force_uppercase:\\n                        unset_vars.append(name.upper())\\n                    else:\\n                        unset_vars.append(name)\\n                elif \\\"/\\\" in value or \\\"\\\\\\\\\\\" in value:\\n                    if context.envvars_force_uppercase:\\n                        export_vars[name.upper()] = self.path_conversion(value)\\n                    else:\\n                        export_vars[name] = self.path_conversion(value)\\n                else:\\n                    if context.envvars_force_uppercase:\\n                        export_vars[name.upper()] = value\\n                    else:\\n                        export_vars[name] = value\\n        else:\\n            # unset all meta variables\\n            unset_vars.extend(context.conda_exe_vars_dict)\\n\\n        return export_vars, unset_vars\\n\\n    @deprecated(\\n        \\\"24.9\\\",\\n        \\\"25.3\\\",\\n        addendum=\\\"Use `conda.activate._Activator.get_export_unset_vars` instead.\\\",\\n    )\\n    def add_export_unset_vars(self, export_vars, unset_vars, **kwargs):\\n        new_export_vars, new_unset_vars = self.get_export_unset_vars(**kwargs)\\n        return {\\n            {**(export_vars or {}), **new_export_vars},\\n            [*(unset_vars or []), *new_unset_vars],\\n        }\\n\\n    @deprecated(\\\"24.9\\\", \\\"25.3\\\", addendum=\\\"For testing only. Moved to test suite.\\\")\\n    def get_scripts_export_unset_vars(self, **kwargs) -> tuple[str, str]:\\n        export_vars, unset_vars = self.get_export_unset_vars(**kwargs)\\n        return (\\n            self.command_join.join(\\n                self.export_var_tmpl % (k, v) for k, v in (export_vars or {}).items()\\n            ),\\n            self.command_join.join(\\n                self.unset_var_tmpl % (k) for k in (unset_vars or [])\\n            ),\\n        )\\n\\n    def _finalize(self, commands, ext):\\n        commands = (*commands, \\\"\\\")  # add terminating newline\\n        if ext is None:\\n            return self.command_join.join(commands)\\n        elif ext:\\n            with Utf8NamedTemporaryFile(\\\"w+\\\", suffix=ext, delete=False) as tf:\\n                # the default mode is 'w+b', and universal new lines don't work in that mode\\n                # command_join should account for that\\n                tf.write(self.command_join.join(commands))\\n            return tf.name\\n        else:\\n            raise NotImplementedError()\\n\\n    def activate(self):\\n        if self.stack:\\n            builder_result = self.build_stack(self.env_name_or_prefix)\\n        else:\\n            builder_result = self.build_activate(self.env_name_or_prefix)\\n        return self._finalize(\\n            self._yield_commands(builder_result), self.tempfile_extension\\n        )\\n\\n    def deactivate(self):\\n        return self._finalize(\\n            self._yield_commands(self.build_deactivate()), self.tempfile_extension\\n        )\\n\\n    def reactivate(self):\\n        return self._finalize(\\n            self._yield_commands(self.build_reactivate()), self.tempfile_extension\\n        )\\n\\n    def hook(self, auto_activate_base: bool | None = None) -> str:\\n        builder: list[str] = []\\n        if preamble := self._hook_preamble():\\n            builder.append(preamble)\\n        if self.hook_source_path:\\n            builder.append(self.hook_source_path.read_text())\\n        if (\\n            auto_activate_base is None\\n            and context.auto_activate_base\\n            or auto_activate_base\\n        ):\\n            builder.append(\\\"conda activate base\\\\n\\\")\\n        postamble = self._hook_postamble()\\n        if postamble is not None:\\n            builder.append(postamble)\\n        return \\\"\\\\n\\\".join(builder)\\n\\n    def execute(self):\\n        # return value meant to be written to stdout\\n        self._parse_and_set_args(self._raw_arguments)\\n        return getattr(self, self.command)()\\n\\n    def commands(self):\\n        \\\"\\\"\\\"\\n        Returns a list of possible subcommands that are valid\\n        immediately following `conda` at the command line.\\n        This method is generally only used by tab-completion.\\n        \\\"\\\"\\\"\\n        # Import locally to reduce impact on initialization time.\\n        from .cli.conda_argparse import find_builtin_commands, generate_parser\\n        from .cli.find_commands import find_commands\\n\\n        # return value meant to be written to stdout\\n        # Hidden commands to provide metadata to shells.\\n        return \\\"\\\\n\\\".join(\\n            sorted(\\n                find_builtin_commands(generate_parser()) + tuple(find_commands(True))\\n            )\\n        )\\n\\n    @abc.abstractmethod\\n    def _hook_preamble(self) -> str | None:\\n        # must be implemented in subclass\\n        raise NotImplementedError\\n\\n    def _hook_postamble(self) -> str | None:\\n        return None\\n\\n    def _parse_and_set_args(self, arguments):\\n        def raise_invalid_command_error(actual_command=None):\\n            from .exceptions import ArgumentError\\n\\n            message = (\\n                \\\"'activate', 'deactivate', 'hook', 'commands', or 'reactivate' \\\"\\n                \\\"command must be given\\\"\\n            )\\n            if actual_command:\\n                message += f\\\". Instead got '{actual_command}'.\\\"\\n            raise ArgumentError(message)\\n\\n        if arguments is None or len(arguments) < 1:\\n            raise_invalid_command_error()\\n\\n        command, *arguments = arguments\\n        help_flags = (\\\"-h\\\", \\\"--help\\\", \\\"/?\\\")\\n        non_help_args = tuple(arg for arg in arguments if arg not in help_flags)\\n        help_requested = len(arguments) != len(non_help_args)\\n        remainder_args = list(arg for arg in non_help_args if arg and arg != command)\\n\\n        if not command:\\n            raise_invalid_command_error()\\n        elif help_requested:\\n            from .exceptions import ActivateHelp, DeactivateHelp, GenericHelp\\n\\n            help_classes = {\\n                \\\"activate\\\": ActivateHelp(),\\n                \\\"deactivate\\\": DeactivateHelp(),\\n                \\\"hook\\\": GenericHelp(\\\"hook\\\"),\\n                \\\"commands\\\": GenericHelp(\\\"commands\\\"),\\n                \\\"reactivate\\\": GenericHelp(\\\"reactivate\\\"),\\n            }\\n            raise help_classes[command]\\n        elif command not in (\\n            \\\"activate\\\",\\n            \\\"deactivate\\\",\\n            \\\"reactivate\\\",\\n            \\\"hook\\\",\\n            \\\"commands\\\",\\n        ):\\n            raise_invalid_command_error(actual_command=command)\\n\\n        if command.endswith(\\\"activate\\\") or command == \\\"hook\\\":\\n            try:\\n                dev_idx = remainder_args.index(\\\"--dev\\\")\\n            except ValueError:\\n                context.dev = False\\n            else:\\n                del remainder_args[dev_idx]\\n                context.dev = True\\n\\n        if command == \\\"activate\\\":\\n            self.stack = context.auto_stack and context.shlvl <= context.auto_stack\\n            try:\\n                stack_idx = remainder_args.index(\\\"--stack\\\")\\n            except ValueError:\\n                stack_idx = -1\\n            try:\\n                no_stack_idx = remainder_args.index(\\\"--no-stack\\\")\\n            except ValueError:\\n                no_stack_idx = -1\\n            if stack_idx >= 0 and no_stack_idx >= 0:\\n                from .exceptions import ArgumentError\\n\\n                raise ArgumentError(\\n                    \\\"cannot specify both --stack and --no-stack to \\\" + command\\n                )\\n            if stack_idx >= 0:\\n                self.stack = True\\n                del remainder_args[stack_idx]\\n            if no_stack_idx >= 0:\\n                self.stack = False\\n                del remainder_args[no_stack_idx]\\n            if len(remainder_args) > 1:\\n                from .exceptions import ArgumentError\\n\\n                raise ArgumentError(\\n                    command\\n                    + \\\" does not accept more than one argument:\\\\n\\\"\\n                    + str(remainder_args)\\n                    + \\\"\\\\n\\\"\\n                )\\n            self.env_name_or_prefix = remainder_args and remainder_args[0] or \\\"base\\\"\\n\\n        else:\\n            if remainder_args:\\n                from .exceptions import ArgumentError\\n\\n                raise ArgumentError(\\n                    f\\\"{command} does not accept arguments\\\\nremainder_args: {remainder_args}\\\\n\\\"\\n                )\\n\\n        self.command = command\\n\\n    def _yield_commands(self, cmds_dict):\\n        for key, value in sorted(cmds_dict.get(\\\"export_path\\\", {}).items()):\\n            yield self.export_var_tmpl % (key, value)\\n\\n        for script in cmds_dict.get(\\\"deactivate_scripts\\\", ()):\\n            yield self.run_script_tmpl % script\\n\\n        for key in cmds_dict.get(\\\"unset_vars\\\", ()):\\n            yield self.unset_var_tmpl % key\\n\\n        for key, value in cmds_dict.get(\\\"set_vars\\\", {}).items():\\n            yield self.set_var_tmpl % (key, value)\\n\\n        for key, value in cmds_dict.get(\\\"export_vars\\\", {}).items():\\n            yield self.export_var_tmpl % (key, value)\\n\\n        for script in cmds_dict.get(\\\"activate_scripts\\\", ()):\\n            yield self.run_script_tmpl % script\\n\\n    def build_activate(self, env_name_or_prefix):\\n        return self._build_activate_stack(env_name_or_prefix, False)\\n\\n    def build_stack(self, env_name_or_prefix):\\n        return self._build_activate_stack(env_name_or_prefix, True)\\n\\n    def _build_activate_stack(self, env_name_or_prefix, stack):\\n        # get environment prefix\\n        if re.search(r\\\"\\\\\\\\|/\\\", env_name_or_prefix):\\n            prefix = expand(env_name_or_prefix)\\n            if not isdir(join(prefix, \\\"conda-meta\\\")):\\n                from .exceptions import EnvironmentLocationNotFound\\n\\n                raise EnvironmentLocationNotFound(prefix)\\n        elif env_name_or_prefix in (ROOT_ENV_NAME, \\\"root\\\"):\\n            prefix = context.root_prefix\\n        else:\\n            prefix = locate_prefix_by_name(env_name_or_prefix)\\n\\n        # get prior shlvl and prefix\\n        old_conda_shlvl = int(os.getenv(\\\"CONDA_SHLVL\\\", \\\"\\\").strip() or 0)\\n        old_conda_prefix = os.getenv(\\\"CONDA_PREFIX\\\")\\n\\n        # if the prior active prefix is this prefix we are actually doing a reactivate\\n        if old_conda_prefix == prefix and old_conda_shlvl > 0:\\n            return self.build_reactivate()\\n\\n        activate_scripts = self._get_activate_scripts(prefix)\\n        conda_shlvl = old_conda_shlvl + 1\\n        conda_default_env = self._default_env(prefix)\\n        conda_prompt_modifier = self._prompt_modifier(prefix, conda_default_env)\\n        env_vars = {\\n            name: value\\n            for name, value in self._get_environment_env_vars(prefix).items()\\n            if value != CONDA_ENV_VARS_UNSET_VAR\\n        }\\n\\n        # get clobbered environment variables\\n        clobber_vars = set(env_vars).intersection(os.environ)\\n        overwritten_clobber_vars = [\\n            clobber_var\\n            for clobber_var in clobber_vars\\n            if os.getenv(clobber_var) != env_vars[clobber_var]\\n        ]\\n        if overwritten_clobber_vars:\\n            print(\\n                \\\"WARNING: overwriting environment variables set in the machine\\\",\\n                file=sys.stderr,\\n            )\\n            print(f\\\"overwriting variable {overwritten_clobber_vars}\\\", file=sys.stderr)\\n        for name in clobber_vars:\\n            env_vars[f\\\"__CONDA_SHLVL_{old_conda_shlvl}_{name}\\\"] = os.getenv(name)\\n\\n        if old_conda_shlvl == 0:\\n            export_vars, unset_vars = self.get_export_unset_vars(\\n                path=self.pathsep_join(self._add_prefix_to_path(prefix)),\\n                conda_prefix=prefix,\\n                conda_shlvl=conda_shlvl,\\n                conda_default_env=conda_default_env,\\n                conda_prompt_modifier=conda_prompt_modifier,\\n                **env_vars,\\n            )\\n            deactivate_scripts = ()\\n        elif stack:\\n            export_vars, unset_vars = self.get_export_unset_vars(\\n                path=self.pathsep_join(self._add_prefix_to_path(prefix)),\\n                conda_prefix=prefix,\\n                conda_shlvl=conda_shlvl,\\n                conda_default_env=conda_default_env,\\n                conda_prompt_modifier=conda_prompt_modifier,\\n                **env_vars,\\n                **{\\n                    f\\\"CONDA_PREFIX_{old_conda_shlvl}\\\": old_conda_prefix,\\n                    f\\\"CONDA_STACKED_{conda_shlvl}\\\": \\\"true\\\",\\n                },\\n            )\\n            deactivate_scripts = ()\\n        else:\\n            export_vars, unset_vars = self.get_export_unset_vars(\\n                path=self.pathsep_join(\\n                    self._replace_prefix_in_path(old_conda_prefix, prefix)\\n                ),\\n                conda_prefix=prefix,\\n                conda_shlvl=conda_shlvl,\\n                conda_default_env=conda_default_env,\\n                conda_prompt_modifier=conda_prompt_modifier,\\n                **env_vars,\\n                **{\\n                    f\\\"CONDA_PREFIX_{old_conda_shlvl}\\\": old_conda_prefix,\\n                },\\n            )\\n            deactivate_scripts = self._get_deactivate_scripts(old_conda_prefix)\\n\\n        set_vars = {}\\n        if context.changeps1:\\n            self._update_prompt(set_vars, conda_prompt_modifier)\\n\\n        return {\\n            \\\"unset_vars\\\": unset_vars,\\n            \\\"set_vars\\\": set_vars,\\n            \\\"export_vars\\\": export_vars,\\n            \\\"deactivate_scripts\\\": deactivate_scripts,\\n            \\\"activate_scripts\\\": activate_scripts,\\n        }\\n\\n    def build_deactivate(self):\\n        self._deactivate = True\\n        # query environment\\n        old_conda_prefix = os.getenv(\\\"CONDA_PREFIX\\\")\\n        old_conda_shlvl = int(os.getenv(\\\"CONDA_SHLVL\\\", \\\"\\\").strip() or 0)\\n        if not old_conda_prefix or old_conda_shlvl < 1:\\n            # no active environment, so cannot deactivate; do nothing\\n            return {\\n                \\\"unset_vars\\\": (),\\n                \\\"set_vars\\\": {},\\n                \\\"export_vars\\\": {},\\n                \\\"deactivate_scripts\\\": (),\\n                \\\"activate_scripts\\\": (),\\n            }\\n        deactivate_scripts = self._get_deactivate_scripts(old_conda_prefix)\\n        old_conda_environment_env_vars = self._get_environment_env_vars(\\n            old_conda_prefix\\n        )\\n\\n        new_conda_shlvl = old_conda_shlvl - 1\\n        set_vars = {}\\n        if old_conda_shlvl == 1:\\n            new_path = self.pathsep_join(\\n                self._remove_prefix_from_path(old_conda_prefix)\\n            )\\n            # You might think that you can remove the CONDA_EXE vars with export_metavars=False\\n            # here so that \\\"deactivate means deactivate\\\" but you cannot since the conda shell\\n            # scripts still refer to them and they only set them once at the top. We could change\\n            # that though, the conda() shell function could set them instead of doing it at the\\n            # top.  This would be *much* cleaner. I personally cannot abide that I have\\n            # deactivated conda and anything at all in my env still references it (apart from the\\n            # shell script, we need something I suppose!)\\n            export_vars, unset_vars = self.get_export_unset_vars(\\n                conda_prefix=None,\\n                conda_shlvl=new_conda_shlvl,\\n                conda_default_env=None,\\n                conda_prompt_modifier=None,\\n            )\\n            conda_prompt_modifier = \\\"\\\"\\n            activate_scripts = ()\\n            export_path = {\\n                \\\"PATH\\\": new_path,\\n            }\\n        else:\\n            assert old_conda_shlvl > 1\\n            new_prefix = os.getenv(\\\"CONDA_PREFIX_%d\\\" % new_conda_shlvl)\\n            conda_default_env = self._default_env(new_prefix)\\n            conda_prompt_modifier = self._prompt_modifier(new_prefix, conda_default_env)\\n            new_conda_environment_env_vars = self._get_environment_env_vars(new_prefix)\\n\\n            old_prefix_stacked = \\\"CONDA_STACKED_%d\\\" % old_conda_shlvl in os.environ\\n            new_path = \\\"\\\"\\n\\n            unset_vars = [\\\"CONDA_PREFIX_%d\\\" % new_conda_shlvl]\\n            if old_prefix_stacked:\\n                new_path = self.pathsep_join(\\n                    self._remove_prefix_from_path(old_conda_prefix)\\n                )\\n                unset_vars.append(\\\"CONDA_STACKED_%d\\\" % old_conda_shlvl)\\n            else:\\n                new_path = self.pathsep_join(\\n                    self._replace_prefix_in_path(old_conda_prefix, new_prefix)\\n                )\\n\\n            export_vars, unset_vars2 = self.get_export_unset_vars(\\n                conda_prefix=new_prefix,\\n                conda_shlvl=new_conda_shlvl,\\n                conda_default_env=conda_default_env,\\n                conda_prompt_modifier=conda_prompt_modifier,\\n                **new_conda_environment_env_vars,\\n            )\\n            unset_vars += unset_vars2\\n            export_path = {\\n                \\\"PATH\\\": new_path,\\n            }\\n            activate_scripts = self._get_activate_scripts(new_prefix)\\n\\n        if context.changeps1:\\n            self._update_prompt(set_vars, conda_prompt_modifier)\\n\\n        for env_var in old_conda_environment_env_vars.keys():\\n            unset_vars.append(env_var)\\n            save_var = f\\\"__CONDA_SHLVL_{new_conda_shlvl}_{env_var}\\\"\\n            if save_value := os.getenv(save_var):\\n                export_vars[env_var] = save_value\\n        return {\\n            \\\"unset_vars\\\": unset_vars,\\n            \\\"set_vars\\\": set_vars,\\n            \\\"export_vars\\\": export_vars,\\n            \\\"export_path\\\": export_path,\\n            \\\"deactivate_scripts\\\": deactivate_scripts,\\n            \\\"activate_scripts\\\": activate_scripts,\\n        }\\n\\n    def build_reactivate(self):\\n        self._reactivate = True\\n        conda_prefix = os.getenv(\\\"CONDA_PREFIX\\\")\\n        conda_shlvl = int(os.getenv(\\\"CONDA_SHLVL\\\", \\\"\\\").strip() or 0)\\n        if not conda_prefix or conda_shlvl < 1:\\n            # no active environment, so cannot reactivate; do nothing\\n            return {\\n                \\\"unset_vars\\\": (),\\n                \\\"set_vars\\\": {},\\n                \\\"export_vars\\\": {},\\n                \\\"deactivate_scripts\\\": (),\\n                \\\"activate_scripts\\\": (),\\n            }\\n        conda_default_env = os.getenv(\\n            \\\"CONDA_DEFAULT_ENV\\\", self._default_env(conda_prefix)\\n        )\\n        new_path = self.pathsep_join(\\n            self._replace_prefix_in_path(conda_prefix, conda_prefix)\\n        )\\n        set_vars = {}\\n        conda_prompt_modifier = self._prompt_modifier(conda_prefix, conda_default_env)\\n        if context.changeps1:\\n            self._update_prompt(set_vars, conda_prompt_modifier)\\n\\n        env_vars_to_unset = ()\\n        env_vars_to_export = {\\n            \\\"PATH\\\": new_path,\\n            \\\"CONDA_SHLVL\\\": conda_shlvl,\\n            \\\"CONDA_PROMPT_MODIFIER\\\": self._prompt_modifier(\\n                conda_prefix, conda_default_env\\n            ),\\n        }\\n        conda_environment_env_vars = self._get_environment_env_vars(conda_prefix)\\n        for k, v in conda_environment_env_vars.items():\\n            if v == CONDA_ENV_VARS_UNSET_VAR:\\n                env_vars_to_unset = env_vars_to_unset + (k,)\\n            else:\\n                env_vars_to_export[k] = v\\n        # environment variables are set only to aid transition from conda 4.3 to conda 4.4\\n        return {\\n            \\\"unset_vars\\\": env_vars_to_unset,\\n            \\\"set_vars\\\": set_vars,\\n            \\\"export_vars\\\": env_vars_to_export,\\n            \\\"deactivate_scripts\\\": self._get_deactivate_scripts(conda_prefix),\\n            \\\"activate_scripts\\\": self._get_activate_scripts(conda_prefix),\\n        }\\n\\n    def _get_starting_path_list(self):\\n        # For isolation, running the conda test suite *without* env. var. inheritance\\n        # every so often is a good idea. We should probably make this a pytest fixture\\n        # along with one that tests both hardlink-only and copy-only, but before that\\n        # conda's testsuite needs to be a lot faster!\\n        clean_paths = {\\n            \\\"darwin\\\": \\\"/usr/bin:/bin:/usr/sbin:/sbin\\\",\\n            # You may think 'let us do something more clever here and interpolate\\n            # `%windir%`' but the point here is the the whole env. is cleaned out\\n            \\\"win32\\\": \\\"C:\\\\\\\\Windows\\\\\\\\system32;\\\"\\n            \\\"C:\\\\\\\\Windows;\\\"\\n            \\\"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\Wbem;\\\"\\n            \\\"C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\WindowsPowerShell\\\\\\\\v1.0\\\\\\\\\\\",\\n        }\\n        path = os.getenv(\\n            \\\"PATH\\\",\\n            clean_paths[sys.platform] if sys.platform in clean_paths else \\\"/usr/bin\\\",\\n        )\\n        path_split = path.split(os.pathsep)\\n        return path_split\\n\\n    @deprecated.argument(\\\"24.9\\\", \\\"25.3\\\", \\\"extra_library_bin\\\")\\n    def _get_path_dirs(self, prefix):\\n        if on_win:  # pragma: unix no cover\\n            yield prefix.rstrip(\\\"\\\\\\\\\\\")\\n\\n            # We need to stat(2) for possible environments because\\n            # tests can't be told where to look!\\n            #\\n            # mingw-w64 is a legacy variant used by m2w64-* packages\\n            #\\n            # We could include clang32 and mingw32 variants\\n            variants = []\\n            for variant in [\\\"ucrt64\\\", \\\"clang64\\\", \\\"mingw64\\\", \\\"clangarm64\\\"]:\\n                path = self.sep.join((prefix, \\\"Library\\\", variant))\\n\\n                # MSYS2 /c/\\n                # cygwin /cygdrive/c/\\n                if re.match(\\\"^(/[A-Za-z]/|/cygdrive/[A-Za-z]/).*\\\", prefix):\\n                    path = unix_path_to_native(path, prefix)\\n\\n                if isdir(path):\\n                    variants.append(variant)\\n\\n            if len(variants) > 1:\\n                print(\\n                    f\\\"WARNING: {prefix}: {variants} MSYS2 envs exist: please check your dependencies\\\",\\n                    file=sys.stderr,\\n                )\\n                print(\\n                    f\\\"WARNING: conda list -n {self._default_env(prefix)}\\\",\\n                    file=sys.stderr,\\n                )\\n\\n            if variants:\\n                yield self.sep.join((prefix, \\\"Library\\\", variants[0], \\\"bin\\\"))\\n\\n            yield self.sep.join((prefix, \\\"Library\\\", \\\"mingw-w64\\\", \\\"bin\\\"))\\n            yield self.sep.join((prefix, \\\"Library\\\", \\\"usr\\\", \\\"bin\\\"))\\n            yield self.sep.join((prefix, \\\"Library\\\", \\\"bin\\\"))\\n            yield self.sep.join((prefix, \\\"Scripts\\\"))\\n            yield self.sep.join((prefix, \\\"bin\\\"))\\n        else:\\n            yield self.sep.join((prefix, \\\"bin\\\"))\\n\\n    def _add_prefix_to_path(self, prefix, starting_path_dirs=None):\\n        prefix = self.path_conversion(prefix)\\n        if starting_path_dirs is None:\\n            path_list = list(self.path_conversion(self._get_starting_path_list()))\\n        else:\\n            path_list = list(self.path_conversion(starting_path_dirs))\\n\\n        # If this is the first time we're activating an environment, we need to ensure that\\n        # the condabin directory is included in the path list.\\n        # Under normal conditions, if the shell hook is working correctly, this should\\n        # never trigger.\\n        old_conda_shlvl = int(os.getenv(\\\"CONDA_SHLVL\\\", \\\"\\\").strip() or 0)\\n        if not old_conda_shlvl and not any(p.endswith(\\\"condabin\\\") for p in path_list):\\n            condabin_dir = self.path_conversion(join(context.conda_prefix, \\\"condabin\\\"))\\n            path_list.insert(0, condabin_dir)\\n\\n        path_list[0:0] = list(self.path_conversion(self._get_path_dirs(prefix)))\\n        return tuple(path_list)\\n\\n    def _remove_prefix_from_path(self, prefix, starting_path_dirs=None):\\n        return self._replace_prefix_in_path(prefix, None, starting_path_dirs)\\n\\n    def _replace_prefix_in_path(self, old_prefix, new_prefix, starting_path_dirs=None):\\n        old_prefix = self.path_conversion(old_prefix)\\n        new_prefix = self.path_conversion(new_prefix)\\n        if starting_path_dirs is None:\\n            path_list = list(self.path_conversion(self._get_starting_path_list()))\\n        else:\\n            path_list = list(self.path_conversion(starting_path_dirs))\\n\\n        def index_of_path(paths, test_path):\\n            for q, path in enumerate(paths):\\n                if paths_equal(path, test_path):\\n                    return q\\n            return None\\n\\n        if old_prefix is not None:\\n            prefix_dirs = tuple(self._get_path_dirs(old_prefix))\\n            first_idx = index_of_path(path_list, prefix_dirs[0])\\n            if first_idx is None:\\n                first_idx = 0\\n            else:\\n                prefix_dirs_idx = len(prefix_dirs) - 1\\n                last_idx = None\\n                while last_idx is None and prefix_dirs_idx > -1:\\n                    last_idx = index_of_path(path_list, prefix_dirs[prefix_dirs_idx])\\n                    if last_idx is None:\\n                        print(\\n                            f\\\"Did not find path entry {prefix_dirs[prefix_dirs_idx]}\\\",\\n                            file=sys.stderr,\\n                        )\\n                    prefix_dirs_idx = prefix_dirs_idx - 1\\n                # this compensates for an extra Library/bin dir entry from the interpreter on\\n                #     windows.  If that entry isn't being added, it should have no effect.\\n                library_bin_dir = self.path_conversion(\\n                    self.sep.join((sys.prefix, \\\"Library\\\", \\\"bin\\\"))\\n                )\\n                if path_list[last_idx + 1] == library_bin_dir:\\n                    last_idx += 1\\n                del path_list[first_idx : last_idx + 1]\\n        else:\\n            first_idx = 0\\n\\n        if new_prefix is not None:\\n            path_list[first_idx:first_idx] = list(self._get_path_dirs(new_prefix))\\n\\n        return tuple(path_list)\\n\\n    def _update_prompt(self, set_vars, conda_prompt_modifier):\\n        pass\\n\\n    def _default_env(self, prefix):\\n        if paths_equal(prefix, context.root_prefix):\\n            return \\\"base\\\"\\n        return basename(prefix) if basename(dirname(prefix)) == \\\"envs\\\" else prefix\\n\\n    def _prompt_modifier(self, prefix, conda_default_env):\\n        if context.changeps1:\\n            # Get current environment and prompt stack\\n            env_stack = []\\n            prompt_stack = []\\n            old_shlvl = int(os.getenv(\\\"CONDA_SHLVL\\\", \\\"0\\\").rstrip())\\n            for i in range(1, old_shlvl + 1):\\n                if i == old_shlvl:\\n                    env_i = self._default_env(os.getenv(\\\"CONDA_PREFIX\\\", \\\"\\\"))\\n                else:\\n                    env_i = self._default_env(\\n                        os.getenv(f\\\"CONDA_PREFIX_{i}\\\", \\\"\\\").rstrip()\\n                    )\\n                stacked_i = bool(os.getenv(f\\\"CONDA_STACKED_{i}\\\", \\\"\\\").rstrip())\\n                env_stack.append(env_i)\\n                if not stacked_i:\\n                    prompt_stack = prompt_stack[0:-1]\\n                prompt_stack.append(env_i)\\n\\n            # Modify prompt stack according to pending operation\\n            deactivate = getattr(self, \\\"_deactivate\\\", False)\\n            reactivate = getattr(self, \\\"_reactivate\\\", False)\\n            if deactivate:\\n                prompt_stack = prompt_stack[0:-1]\\n                env_stack = env_stack[0:-1]\\n                stacked = bool(os.getenv(f\\\"CONDA_STACKED_{old_shlvl}\\\", \\\"\\\").rstrip())\\n                if not stacked and env_stack:\\n                    prompt_stack.append(env_stack[-1])\\n            elif reactivate:\\n                pass\\n            else:\\n                stack = getattr(self, \\\"stack\\\", False)\\n                if not stack:\\n                    prompt_stack = prompt_stack[0:-1]\\n                prompt_stack.append(conda_default_env)\\n\\n            conda_stacked_env = \\\",\\\".join(prompt_stack[::-1])\\n\\n            return context.env_prompt.format(\\n                default_env=conda_default_env,\\n                stacked_env=conda_stacked_env,\\n                prefix=prefix,\\n                name=basename(prefix),\\n            )\\n        else:\\n            return \\\"\\\"\\n\\n    def _get_activate_scripts(self, prefix):\\n        _script_extension = self.script_extension\\n        se_len = -len(_script_extension)\\n        try:\\n            paths = (\\n                entry.path\\n                for entry in os.scandir(join(prefix, \\\"etc\\\", \\\"conda\\\", \\\"activate.d\\\"))\\n            )\\n        except OSError:\\n            return ()\\n        return self.path_conversion(\\n            sorted(p for p in paths if p[se_len:] == _script_extension)\\n        )\\n\\n    def _get_deactivate_scripts(self, prefix):\\n        _script_extension = self.script_extension\\n        se_len = -len(_script_extension)\\n        try:\\n            paths = (\\n                entry.path\\n                for entry in os.scandir(join(prefix, \\\"etc\\\", \\\"conda\\\", \\\"deactivate.d\\\"))\\n            )\\n        except OSError:\\n            return ()\\n        return self.path_conversion(\\n            sorted((p for p in paths if p[se_len:] == _script_extension), reverse=True)\\n        )\\n\\n    def _get_environment_env_vars(self, prefix):\\n        env_vars_file = join(prefix, PREFIX_STATE_FILE)\\n        pkg_env_var_dir = join(prefix, PACKAGE_ENV_VARS_DIR)\\n        env_vars = {}\\n\\n        # First get env vars from packages\\n        if exists(pkg_env_var_dir):\\n            for pkg_env_var_path in sorted(\\n                entry.path for entry in os.scandir(pkg_env_var_dir)\\n            ):\\n                with open(pkg_env_var_path) as f:\\n                    env_vars.update(json.loads(f.read()))\\n\\n        # Then get env vars from environment specification\\n        if exists(env_vars_file):\\n            with open(env_vars_file) as f:\\n                prefix_state = json.loads(f.read())\\n                prefix_state_env_vars = prefix_state.get(\\\"env_vars\\\", {})\\n                dup_vars = [\\n                    ev for ev in env_vars.keys() if ev in prefix_state_env_vars.keys()\\n                ]\\n                for dup in dup_vars:\\n                    print(\\n                        \\\"WARNING: duplicate env vars detected. Vars from the environment \\\"\\n                        \\\"will overwrite those from packages\\\",\\n                        file=sys.stderr,\\n                    )\\n                    print(f\\\"variable {dup} duplicated\\\", file=sys.stderr)\\n                env_vars.update(prefix_state_env_vars)\\n\\n        return env_vars\\n\\n\\ndef expand(path):\\n    return abspath(expanduser(expandvars(path)))\\n\\n\\ndef ensure_binary(value):\\n    try:\\n        return value.encode(\\\"utf-8\\\")\\n    except AttributeError:  # pragma: no cover\\n        # AttributeError: '<>' object has no attribute 'encode'\\n        # In this case assume already binary type and do nothing\\n        return value\\n\\n\\ndef ensure_fs_path_encoding(value):\\n    try:\\n        return value.decode(FILESYSTEM_ENCODING)\\n    except AttributeError:\\n        return value\\n\\n\\nclass _Cygpath:\\n    @classmethod\\n    def nt_to_posix(cls, paths: str) -> str:\\n        return cls.RE_UNIX.sub(cls.translate_unix, paths).replace(\\n            ntpath.pathsep, posixpath.pathsep\\n        )\\n\\n    RE_UNIX = re.compile(\\n        r\\\"\\\"\\\"\\n        (?P<drive>[A-Za-z]:)?\\n        (?P<path>[\\\\/\\\\\\\\]+(?:[^:*?\\\\\\\"<>|;]+[\\\\/\\\\\\\\]*)*)\\n        \\\"\\\"\\\",\\n        flags=re.VERBOSE,\\n    )\\n\\n    @staticmethod\\n    def translate_unix(match: re.Match) -> str:\\n        return \\\"/\\\" + (\\n            ((match.group(\\\"drive\\\") or \\\"\\\").lower() + match.group(\\\"path\\\"))\\n            .replace(\\\"\\\\\\\\\\\", \\\"/\\\")\\n            .replace(\\\":\\\", \\\"\\\")  # remove drive letter delimiter\\n            .replace(\\\"//\\\", \\\"/\\\")\\n            .rstrip(\\\"/\\\")\\n        )\\n\\n    @classmethod\\n    def posix_to_nt(cls, paths: str, prefix: str) -> str:\\n        if posixpath.sep not in paths:\\n            # nothing to translate\\n            return paths\\n\\n        if posixpath.pathsep in paths:\\n            return ntpath.pathsep.join(\\n                cls.posix_to_nt(path, prefix) for path in paths.split(posixpath.pathsep)\\n            )\\n        path = paths\\n\\n        # Reverting a Unix path means unpicking MSYS2/Cygwin\\n        # conventions -- in order!\\n        # 1. drive letter forms:\\n        #      /x/here/there - MSYS2\\n        #      /cygdrive/x/here/there - Cygwin\\n        #    transformed to X:\\\\here\\\\there -- note the uppercase drive letter!\\n        # 2. either:\\n        #    a. mount forms:\\n        #         //here/there\\n        #       transformed to \\\\\\\\here\\\\there\\n        #    b. root filesystem forms:\\n        #         /here/there\\n        #       transformed to {prefix}\\\\Library\\\\here\\\\there\\n        # 3. anything else\\n\\n        # continue performing substitutions until a match is found\\n        path, subs = cls.RE_DRIVE.subn(cls.translation_drive, path)\\n        if not subs:\\n            path, subs = cls.RE_MOUNT.subn(cls.translation_mount, path)\\n        if not subs:\\n            path, _ = cls.RE_ROOT.subn(\\n                lambda match: cls.translation_root(match, prefix), path\\n            )\\n\\n        return re.sub(r\\\"/+\\\", r\\\"\\\\\\\\\\\", path)\\n\\n    RE_DRIVE = re.compile(\\n        r\\\"\\\"\\\"\\n        ^\\n        (/cygdrive)?\\n        /(?P<drive>[A-Za-z])\\n        (/+(?P<path>.*)?)?\\n        $\\n        \\\"\\\"\\\",\\n        flags=re.VERBOSE,\\n    )\\n\\n    @staticmethod\\n    def translation_drive(match: re.Match) -> str:\\n        drive = match.group(\\\"drive\\\").upper()\\n        path = match.group(\\\"path\\\") or \\\"\\\"\\n        return f\\\"{drive}:\\\\\\\\{path}\\\"\\n\\n    RE_MOUNT = re.compile(\\n        r\\\"\\\"\\\"\\n        ^\\n        //(\\n            (?P<mount>[^/]+)\\n            (?P<path>/+.*)?\\n        )?\\n        $\\n        \\\"\\\"\\\",\\n        flags=re.VERBOSE,\\n    )\\n\\n    @staticmethod\\n    def translation_mount(match: re.Match) -> str:\\n        mount = match.group(\\\"mount\\\") or \\\"\\\"\\n        path = match.group(\\\"path\\\") or \\\"\\\"\\n        return f\\\"\\\\\\\\\\\\\\\\{mount}{path}\\\"\\n\\n    RE_ROOT = re.compile(\\n        r\\\"\\\"\\\"\\n        ^\\n        (?P<path>/[^:]*)\\n        $\\n        \\\"\\\"\\\",\\n        flags=re.VERBOSE,\\n    )\\n\\n    @staticmethod\\n    def translation_root(match: re.Match, prefix: str) -> str:\\n        path = match.group(\\\"path\\\")\\n        return f\\\"{prefix}\\\\\\\\Library{path}\\\"\\n\\n\\ndef native_path_to_unix(\\n    paths: str | Iterable[str] | None,\\n) -> str | tuple[str, ...] | None:\\n    if paths is None:\\n        return None\\n    elif not on_win:\\n        return path_identity(paths)\\n\\n    # short-circuit if we don't get any paths\\n    paths = paths if isinstance(paths, str) else tuple(paths)\\n    if not paths:\\n        return \\\".\\\" if isinstance(paths, str) else ()\\n\\n    # on windows, uses cygpath to convert windows native paths to posix paths\\n\\n    # It is very easy to end up with a bash in one place and a cygpath in another due to e.g.\\n    # using upstream MSYS2 bash, but with a conda env that does not have bash but does have\\n    # cygpath.  When this happens, we have two different virtual POSIX machines, rooted at\\n    # different points in the Windows filesystem.  We do our path conversions with one and\\n    # expect the results to work with the other.  It does not.\\n\\n    bash = which(\\\"bash\\\")\\n    cygpath = str(Path(bash).parent / \\\"cygpath\\\") if bash else \\\"cygpath\\\"\\n    joined = paths if isinstance(paths, str) else ntpath.pathsep.join(paths)\\n\\n    try:\\n        # if present, use cygpath to convert paths since its more reliable\\n        unix_path = run(\\n            [cygpath, \\\"--unix\\\", \\\"--path\\\", joined],\\n            text=True,\\n            capture_output=True,\\n            check=True,\\n        ).stdout.strip()\\n    except FileNotFoundError:\\n        # fallback logic when cygpath is not available\\n        # i.e. conda without anything else installed\\n        log.warning(\\\"cygpath is not available, fallback to manual path conversion\\\")\\n\\n        unix_path = _Cygpath.nt_to_posix(joined)\\n    except Exception as err:\\n        log.error(\\\"Unexpected cygpath error (%s)\\\", err)\\n        raise\\n\\n    if isinstance(paths, str):\\n        return unix_path\\n    elif not unix_path:\\n        return ()\\n    else:\\n        return tuple(unix_path.split(posixpath.pathsep))\\n\\n\\ndef unix_path_to_native(\\n    paths: str | Iterable[str] | None, prefix: str\\n) -> str | tuple[str, ...] | None:\\n    if paths is None:\\n        return None\\n    elif not on_win:\\n        return path_identity(paths)\\n\\n    # short-circuit if we don't get any paths\\n    paths = paths if isinstance(paths, str) else tuple(paths)\\n    if not paths:\\n        return \\\".\\\" if isinstance(paths, str) else ()\\n\\n    # on windows, uses cygpath to convert posix paths to windows native paths\\n\\n    # It is very easy to end up with a bash in one place and a cygpath in another due to e.g.\\n    # using upstream MSYS2 bash, but with a conda env that does not have bash but does have\\n    # cygpath.  When this happens, we have two different virtual POSIX machines, rooted at\\n    # different points in the Windows filesystem.  We do our path conversions with one and\\n    # expect the results to work with the other.  It does not.\\n\\n    bash = which(\\\"bash\\\")\\n    cygpath = str(Path(bash).parent / \\\"cygpath\\\") if bash else \\\"cygpath\\\"\\n    joined = paths if isinstance(paths, str) else posixpath.pathsep.join(paths)\\n\\n    try:\\n        # if present, use cygpath to convert paths since its more reliable\\n        win_path = run(\\n            [cygpath, \\\"--windows\\\", \\\"--path\\\", joined],\\n            text=True,\\n            capture_output=True,\\n            check=True,\\n        ).stdout.strip()\\n    except FileNotFoundError:\\n        # fallback logic when cygpath is not available\\n        # i.e. conda without anything else installed\\n        log.warning(\\\"cygpath is not available, fallback to manual path conversion\\\")\\n\\n        # The conda prefix can be in a drive letter form\\n        prefix = _Cygpath.posix_to_nt(prefix, prefix)\\n\\n        win_path = _Cygpath.posix_to_nt(joined, prefix)\\n    except Exception as err:\\n        log.error(\\\"Unexpected cygpath error (%s)\\\", err)\\n        raise\\n\\n    if isinstance(paths, str):\\n        return win_path\\n    elif not win_path:\\n        return ()\\n    else:\\n        return tuple(win_path.split(ntpath.pathsep))\\n\\n\\ndef path_identity(paths: str | Iterable[str] | None) -> str | tuple[str, ...] | None:\\n    if paths is None:\\n        return None\\n    elif isinstance(paths, str):\\n        return os.path.normpath(paths)\\n    else:\\n        return tuple(os.path.normpath(path) for path in paths)\\n\\n\\ndef backslash_to_forwardslash(\\n    paths: str | Iterable[str] | None,\\n) -> str | tuple[str, ...] | None:\\n    if paths is None:\\n        return None\\n    elif isinstance(paths, str):\\n        return paths.replace(\\\"\\\\\\\\\\\", \\\"/\\\")\\n    else:\\n        return tuple([path.replace(\\\"\\\\\\\\\\\", \\\"/\\\") for path in paths])\\n\\n\\nclass PosixActivator(_Activator):\\n    pathsep_join = \\\":\\\".join\\n    sep = \\\"/\\\"\\n    path_conversion = staticmethod(native_path_to_unix)\\n    script_extension = \\\".sh\\\"\\n    tempfile_extension = None  # output to stdout\\n    command_join = \\\"\\\\n\\\"\\n\\n    unset_var_tmpl = \\\"unset %s\\\"\\n    export_var_tmpl = \\\"export %s='%s'\\\"\\n    set_var_tmpl = \\\"%s='%s'\\\"\\n    run_script_tmpl = '. \\\"%s\\\"'\\n\\n    hook_source_path = Path(\\n        CONDA_PACKAGE_ROOT,\\n        \\\"shell\\\",\\n        \\\"etc\\\",\\n        \\\"profile.d\\\",\\n        \\\"conda.sh\\\",\\n    )\\n\\n    def _update_prompt(self, set_vars, conda_prompt_modifier):\\n        ps1 = os.getenv(\\\"PS1\\\", \\\"\\\")\\n        if \\\"POWERLINE_COMMAND\\\" in ps1:\\n            # Defer to powerline (https://github.com/powerline/powerline) if it's in use.\\n            return\\n        current_prompt_modifier = os.getenv(\\\"CONDA_PROMPT_MODIFIER\\\")\\n        if current_prompt_modifier:\\n            ps1 = re.sub(re.escape(current_prompt_modifier), r\\\"\\\", ps1)\\n        # Because we're using single-quotes to set shell variables, we need to handle the\\n        # proper escaping of single quotes that are already part of the string.\\n        # Best solution appears to be https://stackoverflow.com/a/1250279\\n        ps1 = ps1.replace(\\\"'\\\", \\\"'\\\\\\\"'\\\\\\\"'\\\")\\n        set_vars.update(\\n            {\\n                \\\"PS1\\\": conda_prompt_modifier + ps1,\\n            }\\n        )\\n\\n    def _hook_preamble(self) -> str:\\n        result = []\\n        for key, value in context.conda_exe_vars_dict.items():\\n            if value is None:\\n                # Using `unset_var_tmpl` would cause issues for people running\\n                # with shell flag -u set (error on unset).\\n                result.append(self.export_var_tmpl % (key, \\\"\\\"))\\n            elif on_win and (\\\"/\\\" in value or \\\"\\\\\\\\\\\" in value):\\n                result.append(f'''export {key}=\\\"$(cygpath '{value}')\\\"''')\\n            else:\\n                result.append(self.export_var_tmpl % (key, value))\\n        return \\\"\\\\n\\\".join(result) + \\\"\\\\n\\\"\\n\\n\\nclass CshActivator(_Activator):\\n    pathsep_join = \\\":\\\".join\\n    sep = \\\"/\\\"\\n    path_conversion = staticmethod(native_path_to_unix)\\n    script_extension = \\\".csh\\\"\\n    tempfile_extension = None  # output to stdout\\n    command_join = \\\";\\\\n\\\"\\n\\n    unset_var_tmpl = \\\"unsetenv %s\\\"\\n    export_var_tmpl = 'setenv %s \\\"%s\\\"'\\n    set_var_tmpl = \\\"set %s='%s'\\\"\\n    run_script_tmpl = 'source \\\"%s\\\"'\\n\\n    hook_source_path = Path(\\n        CONDA_PACKAGE_ROOT,\\n        \\\"shell\\\",\\n        \\\"etc\\\",\\n        \\\"profile.d\\\",\\n        \\\"conda.csh\\\",\\n    )\\n\\n    def _update_prompt(self, set_vars, conda_prompt_modifier):\\n        prompt = os.getenv(\\\"prompt\\\", \\\"\\\")\\n        current_prompt_modifier = os.getenv(\\\"CONDA_PROMPT_MODIFIER\\\")\\n        if current_prompt_modifier:\\n            prompt = re.sub(re.escape(current_prompt_modifier), r\\\"\\\", prompt)\\n        set_vars.update(\\n            {\\n                \\\"prompt\\\": conda_prompt_modifier + prompt,\\n            }\\n        )\\n\\n    def _hook_preamble(self) -> str:\\n        if on_win:\\n            return dedent(\\n                f\\\"\\\"\\\"\\n                setenv CONDA_EXE `cygpath {context.conda_exe}`\\n                setenv _CONDA_ROOT `cygpath {context.conda_prefix}`\\n                setenv _CONDA_EXE `cygpath {context.conda_exe}`\\n                setenv CONDA_PYTHON_EXE `cygpath {sys.executable}`\\n                \\\"\\\"\\\"\\n            ).strip()\\n        else:\\n            return dedent(\\n                f\\\"\\\"\\\"\\n                setenv CONDA_EXE \\\"{context.conda_exe}\\\"\\n                setenv _CONDA_ROOT \\\"{context.conda_prefix}\\\"\\n                setenv _CONDA_EXE \\\"{context.conda_exe}\\\"\\n                setenv CONDA_PYTHON_EXE \\\"{sys.executable}\\\"\\n                \\\"\\\"\\\"\\n            ).strip()\\n\\n\\nclass XonshActivator(_Activator):\\n    pathsep_join = \\\";\\\".join if on_win else \\\":\\\".join\\n    sep = \\\"/\\\"\\n    path_conversion = staticmethod(\\n        backslash_to_forwardslash if on_win else path_identity\\n    )\\n    # 'scripts' really refer to de/activation scripts, not scripts in the language per se\\n    # xonsh can piggy-back activation scripts from other languages depending on the platform\\n    script_extension = \\\".bat\\\" if on_win else \\\".sh\\\"\\n    tempfile_extension = None  # output to stdout\\n    command_join = \\\"\\\\n\\\"\\n\\n    unset_var_tmpl = \\\"del $%s\\\"\\n    export_var_tmpl = \\\"$%s = '%s'\\\"\\n    # TODO: determine if different than export_var_tmpl\\n    set_var_tmpl = \\\"$%s = '%s'\\\"\\n    run_script_tmpl = (\\n        'source-cmd --suppress-skip-message \\\"%s\\\"'\\n        if on_win\\n        else 'source-bash --suppress-skip-message -n \\\"%s\\\"'\\n    )\\n\\n    hook_source_path = Path(CONDA_PACKAGE_ROOT, \\\"shell\\\", \\\"conda.xsh\\\")\\n\\n    def _hook_preamble(self) -> str:\\n        return f'$CONDA_EXE = \\\"{self.path_conversion(context.conda_exe)}\\\"'\\n\\n\\nclass CmdExeActivator(_Activator):\\n    pathsep_join = \\\";\\\".join\\n    sep = \\\"\\\\\\\\\\\"\\n    path_conversion = staticmethod(path_identity)\\n    script_extension = \\\".bat\\\"\\n    tempfile_extension = \\\".bat\\\"\\n    command_join = \\\"\\\\n\\\"\\n\\n    unset_var_tmpl = \\\"@SET %s=\\\"\\n    export_var_tmpl = '@SET \\\"%s=%s\\\"'\\n    # TODO: determine if different than export_var_tmpl\\n    set_var_tmpl = '@SET \\\"%s=%s\\\"'\\n    run_script_tmpl = '@CALL \\\"%s\\\"'\\n\\n    hook_source_path = None\\n\\n    def _hook_preamble(self) -> None:\\n        # TODO: cmd.exe doesn't get a hook function? Or do we need to do something different?\\n        #       Like, for cmd.exe only, put a special directory containing only conda.bat on PATH?\\n        pass\\n\\n\\nclass FishActivator(_Activator):\\n    pathsep_join = '\\\" \\\"'.join\\n    sep = \\\"/\\\"\\n    path_conversion = staticmethod(native_path_to_unix)\\n    script_extension = \\\".fish\\\"\\n    tempfile_extension = None  # output to stdout\\n    command_join = \\\";\\\\n\\\"\\n\\n    unset_var_tmpl = \\\"set -e %s\\\"\\n    export_var_tmpl = 'set -gx %s \\\"%s\\\"'\\n    set_var_tmpl = 'set -g %s \\\"%s\\\"'\\n    run_script_tmpl = 'source \\\"%s\\\"'\\n\\n    hook_source_path = Path(\\n        CONDA_PACKAGE_ROOT,\\n        \\\"shell\\\",\\n        \\\"etc\\\",\\n        \\\"fish\\\",\\n        \\\"conf.d\\\",\\n        \\\"conda.fish\\\",\\n    )\\n\\n    def _hook_preamble(self) -> str:\\n        if on_win:\\n            return dedent(\\n                f\\\"\\\"\\\"\\n                set -gx CONDA_EXE (cygpath \\\"{context.conda_exe}\\\")\\n                set _CONDA_ROOT (cygpath \\\"{context.conda_prefix}\\\")\\n                set _CONDA_EXE (cygpath \\\"{context.conda_exe}\\\")\\n                set -gx CONDA_PYTHON_EXE (cygpath \\\"{sys.executable}\\\")\\n                \\\"\\\"\\\"\\n            ).strip()\\n        else:\\n            return dedent(\\n                f\\\"\\\"\\\"\\n                set -gx CONDA_EXE \\\"{context.conda_exe}\\\"\\n                set _CONDA_ROOT \\\"{context.conda_prefix}\\\"\\n                set _CONDA_EXE \\\"{context.conda_exe}\\\"\\n                set -gx CONDA_PYTHON_EXE \\\"{sys.executable}\\\"\\n                \\\"\\\"\\\"\\n            ).strip()\\n\\n\\nclass PowerShellActivator(_Activator):\\n    pathsep_join = \\\";\\\".join if on_win else \\\":\\\".join\\n    sep = \\\"\\\\\\\\\\\" if on_win else \\\"/\\\"\\n    path_conversion = staticmethod(path_identity)\\n    script_extension = \\\".ps1\\\"\\n    tempfile_extension = None  # output to stdout\\n    command_join = \\\"\\\\n\\\"\\n\\n    unset_var_tmpl = '$Env:%s = \\\"\\\"'\\n    export_var_tmpl = '$Env:%s = \\\"%s\\\"'\\n    set_var_tmpl = '$Env:%s = \\\"%s\\\"'\\n    run_script_tmpl = '. \\\"%s\\\"'\\n\\n    hook_source_path = Path(\\n        CONDA_PACKAGE_ROOT,\\n        \\\"shell\\\",\\n        \\\"condabin\\\",\\n        \\\"conda-hook.ps1\\\",\\n    )\\n\\n    def _hook_preamble(self) -> str:\\n        if context.dev:\\n            return dedent(\\n                f\\\"\\\"\\\"\\n                $Env:PYTHONPATH = \\\"{CONDA_SOURCE_ROOT}\\\"\\n                $Env:CONDA_EXE = \\\"{sys.executable}\\\"\\n                $Env:_CE_M = \\\"-m\\\"\\n                $Env:_CE_CONDA = \\\"conda\\\"\\n                $Env:_CONDA_ROOT = \\\"{CONDA_PACKAGE_ROOT}\\\"\\n                $Env:_CONDA_EXE = \\\"{context.conda_exe}\\\"\\n                $CondaModuleArgs = @{{ChangePs1 = ${context.changeps1}}}\\n                \\\"\\\"\\\"\\n            ).strip()\\n        else:\\n            return dedent(\\n                f\\\"\\\"\\\"\\n                $Env:CONDA_EXE = \\\"{context.conda_exe}\\\"\\n                $Env:_CE_M = \\\"\\\"\\n                $Env:_CE_CONDA = \\\"\\\"\\n                $Env:_CONDA_ROOT = \\\"{context.conda_prefix}\\\"\\n                $Env:_CONDA_EXE = \\\"{context.conda_exe}\\\"\\n                $CondaModuleArgs = @{{ChangePs1 = ${context.changeps1}}}\\n                \\\"\\\"\\\"\\n            ).strip()\\n\\n    def _hook_postamble(self) -> str:\\n        return \\\"Remove-Variable CondaModuleArgs\\\"\\n\\n\\nclass JSONFormatMixin(_Activator):\\n    \\\"\\\"\\\"Returns the necessary values for activation as JSON, so that tools can use them.\\\"\\\"\\\"\\n\\n    pathsep_join = list\\n    tempfile_extension = None  # output to stdout\\n    command_join = list\\n\\n    def _hook_preamble(self):\\n        if context.dev:\\n            return {\\n                \\\"PYTHONPATH\\\": CONDA_SOURCE_ROOT,\\n                \\\"CONDA_EXE\\\": sys.executable,\\n                \\\"_CE_M\\\": \\\"-m\\\",\\n                \\\"_CE_CONDA\\\": \\\"conda\\\",\\n                \\\"_CONDA_ROOT\\\": CONDA_PACKAGE_ROOT,\\n                \\\"_CONDA_EXE\\\": context.conda_exe,\\n            }\\n        else:\\n            return {\\n                \\\"CONDA_EXE\\\": context.conda_exe,\\n                \\\"_CE_M\\\": \\\"\\\",\\n                \\\"_CE_CONDA\\\": \\\"\\\",\\n                \\\"_CONDA_ROOT\\\": context.conda_prefix,\\n                \\\"_CONDA_EXE\\\": context.conda_exe,\\n            }\\n\\n    @deprecated(\\n        \\\"24.9\\\",\\n        \\\"25.3\\\",\\n        addendum=\\\"Use `conda.activate._Activator.get_export_unset_vars` instead.\\\",\\n    )\\n    def get_scripts_export_unset_vars(self, **kwargs):\\n        export_vars, unset_vars = self.get_export_unset_vars(**kwargs)\\n        return export_vars or {}, unset_vars or []\\n\\n    def _finalize(self, commands, ext):\\n        merged = {}\\n        for _cmds in commands:\\n            merged.update(_cmds)\\n\\n        commands = merged\\n        if ext is None:\\n            return json.dumps(commands, indent=2)\\n        elif ext:\\n            with Utf8NamedTemporaryFile(\\\"w+\\\", suffix=ext, delete=False) as tf:\\n                # the default mode is 'w+b', and universal new lines don't work in that mode\\n                # command_join should account for that\\n                json.dump(commands, tf, indent=2)\\n            return tf.name\\n        else:\\n            raise NotImplementedError()\\n\\n    def _yield_commands(self, cmds_dict):\\n        # TODO: _Is_ defining our own object shape here any better than\\n        # just dumping the `cmds_dict`?\\n        path = cmds_dict.get(\\\"export_path\\\", {})\\n        export_vars = cmds_dict.get(\\\"export_vars\\\", {})\\n        # treat PATH specially\\n        if \\\"PATH\\\" in export_vars:\\n            new_path = path.get(\\\"PATH\\\", [])\\n            new_path.extend(export_vars.pop(\\\"PATH\\\"))\\n            path[\\\"PATH\\\"] = new_path\\n\\n        yield {\\n            \\\"path\\\": path,\\n            \\\"vars\\\": {\\n                \\\"export\\\": export_vars,\\n                \\\"unset\\\": cmds_dict.get(\\\"unset_vars\\\", ()),\\n                \\\"set\\\": cmds_dict.get(\\\"set_vars\\\", {}),\\n            },\\n            \\\"scripts\\\": {\\n                \\\"activate\\\": cmds_dict.get(\\\"activate_scripts\\\", ()),\\n                \\\"deactivate\\\": cmds_dict.get(\\\"deactivate_scripts\\\", ()),\\n            },\\n        }\\n\\n\\nactivator_map: dict[str, type[_Activator]] = {\\n    \\\"posix\\\": PosixActivator,\\n    \\\"ash\\\": PosixActivator,\\n    \\\"bash\\\": PosixActivator,\\n    \\\"dash\\\": PosixActivator,\\n    \\\"zsh\\\": PosixActivator,\\n    \\\"csh\\\": CshActivator,\\n    \\\"tcsh\\\": CshActivator,\\n    \\\"xonsh\\\": XonshActivator,\\n    \\\"cmd.exe\\\": CmdExeActivator,\\n    \\\"fish\\\": FishActivator,\\n    \\\"powershell\\\": PowerShellActivator,\\n}\\n\\nformatter_map = {\\n    \\\"json\\\": JSONFormatMixin,\\n}\\n\\n\\ndef _build_activator_cls(shell):\\n    \\\"\\\"\\\"Dynamically construct the activator class.\\n\\n    Detect the base activator and any number of formatters (appended using '+' to the base name).\\n    For example, `posix+json` (as in `conda shell.posix+json activate`) would use the\\n    `PosixActivator` base class and add the `JSONFormatMixin`.\\n    \\\"\\\"\\\"\\n    shell_etc = shell.split(\\\"+\\\")\\n    activator, formatters = shell_etc[0], shell_etc[1:]\\n\\n    bases = [activator_map[activator]]\\n    for f in formatters:\\n        bases.append(formatter_map[f])\\n\\n    cls = type(\\\"Activator\\\", tuple(reversed(bases)), {})\\n    return cls\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"\\nThis file should hold most string literals and magic numbers used throughout the code base.\\nThe exception is if a literal is specifically meant to be private to and isolated within a module.\\nThink of this as a \\\"more static\\\" source of configuration information.\\n\\nAnother important source of \\\"static\\\" configuration is conda/models/enums.py.\\n\\\"\\\"\\\"\\n\\nimport struct\\nfrom enum import Enum, EnumMeta\\nfrom os.path import join\\n\\nfrom ..common.compat import on_win, six_with_metaclass\\n\\nPREFIX_PLACEHOLDER = (\\n    \\\"/opt/anaconda1anaconda2\\\"\\n    # this is intentionally split into parts, such that running\\n    # this program on itself will leave it unchanged\\n    \\\"anaconda3\\\"\\n)\\n\\nmachine_bits = 8 * struct.calcsize(\\\"P\\\")\\n\\nAPP_NAME = \\\"conda\\\"\\n\\nif on_win:  # pragma: no cover\\n    SEARCH_PATH = (\\n        \\\"C:/ProgramData/conda/.condarc\\\",\\n        \\\"C:/ProgramData/conda/condarc\\\",\\n        \\\"C:/ProgramData/conda/condarc.d\\\",\\n    )\\nelse:\\n    SEARCH_PATH = (\\n        \\\"/etc/conda/.condarc\\\",\\n        \\\"/etc/conda/condarc\\\",\\n        \\\"/etc/conda/condarc.d/\\\",\\n        \\\"/var/lib/conda/.condarc\\\",\\n        \\\"/var/lib/conda/condarc\\\",\\n        \\\"/var/lib/conda/condarc.d/\\\",\\n    )\\n\\nSEARCH_PATH += (\\n    \\\"$CONDA_ROOT/.condarc\\\",\\n    \\\"$CONDA_ROOT/condarc\\\",\\n    \\\"$CONDA_ROOT/condarc.d/\\\",\\n    \\\"$XDG_CONFIG_HOME/conda/.condarc\\\",\\n    \\\"$XDG_CONFIG_HOME/conda/condarc\\\",\\n    \\\"$XDG_CONFIG_HOME/conda/condarc.d/\\\",\\n    \\\"~/.config/conda/.condarc\\\",\\n    \\\"~/.config/conda/condarc\\\",\\n    \\\"~/.config/conda/condarc.d/\\\",\\n    \\\"~/.conda/.condarc\\\",\\n    \\\"~/.conda/condarc\\\",\\n    \\\"~/.conda/condarc.d/\\\",\\n    \\\"~/.condarc\\\",\\n    \\\"$CONDA_PREFIX/.condarc\\\",\\n    \\\"$CONDA_PREFIX/condarc\\\",\\n    \\\"$CONDA_PREFIX/condarc.d/\\\",\\n    \\\"$CONDARC\\\",\\n)\\n\\nDEFAULT_CHANNEL_ALIAS = \\\"https://conda.anaconda.org\\\"\\nCONDA_HOMEPAGE_URL = \\\"https://conda.io\\\"\\nERROR_UPLOAD_URL = \\\"https://conda.io/conda-post/unexpected-error\\\"\\nDEFAULTS_CHANNEL_NAME = \\\"defaults\\\"\\n\\nKNOWN_SUBDIRS = PLATFORM_DIRECTORIES = (\\n    \\\"noarch\\\",\\n    \\\"emscripten-wasm32\\\",\\n    \\\"wasi-wasm32\\\",\\n    \\\"freebsd-64\\\",\\n    \\\"linux-32\\\",\\n    \\\"linux-64\\\",\\n    \\\"linux-aarch64\\\",\\n    \\\"linux-armv6l\\\",\\n    \\\"linux-armv7l\\\",\\n    \\\"linux-ppc64\\\",\\n    \\\"linux-ppc64le\\\",\\n    \\\"linux-riscv64\\\",\\n    \\\"linux-s390x\\\",\\n    \\\"osx-64\\\",\\n    \\\"osx-arm64\\\",\\n    \\\"win-32\\\",\\n    \\\"win-64\\\",\\n    \\\"win-arm64\\\",\\n    \\\"zos-z\\\",\\n)\\n\\nRECOGNIZED_URL_SCHEMES = (\\\"http\\\", \\\"https\\\", \\\"ftp\\\", \\\"s3\\\", \\\"file\\\")\\n\\n\\nDEFAULT_CHANNELS_UNIX = (\\n    \\\"https://repo.anaconda.com/pkgs/main\\\",\\n    \\\"https://repo.anaconda.com/pkgs/r\\\",\\n)\\n\\nDEFAULT_CHANNELS_WIN = (\\n    \\\"https://repo.anaconda.com/pkgs/main\\\",\\n    \\\"https://repo.anaconda.com/pkgs/r\\\",\\n    \\\"https://repo.anaconda.com/pkgs/msys2\\\",\\n)\\n\\nDEFAULT_CUSTOM_CHANNELS = {\\n    \\\"pkgs/pro\\\": \\\"https://repo.anaconda.com\\\",\\n}\\n\\nDEFAULT_CHANNELS = DEFAULT_CHANNELS_WIN if on_win else DEFAULT_CHANNELS_UNIX\\n\\nROOT_ENV_NAME = \\\"base\\\"\\nUNUSED_ENV_NAME = \\\"unused-env-name\\\"\\n\\nROOT_NO_RM = (\\n    \\\"python\\\",\\n    \\\"pycosat\\\",\\n    \\\"ruamel.yaml\\\",\\n    \\\"conda\\\",\\n    \\\"openssl\\\",\\n    \\\"requests\\\",\\n)\\n\\nDEFAULT_AGGRESSIVE_UPDATE_PACKAGES = (\\n    \\\"ca-certificates\\\",\\n    \\\"certifi\\\",\\n    \\\"openssl\\\",\\n)\\n\\nif on_win:  # pragma: no cover\\n    COMPATIBLE_SHELLS = (\\n        \\\"bash\\\",\\n        \\\"cmd.exe\\\",\\n        \\\"fish\\\",\\n        \\\"tcsh\\\",\\n        \\\"xonsh\\\",\\n        \\\"zsh\\\",\\n        \\\"powershell\\\",\\n    )\\nelse:\\n    COMPATIBLE_SHELLS = (\\n        \\\"bash\\\",\\n        \\\"fish\\\",\\n        \\\"tcsh\\\",\\n        \\\"xonsh\\\",\\n        \\\"zsh\\\",\\n        \\\"powershell\\\",\\n    )\\n\\n\\n# Maximum priority, reserved for packages we really want to remove\\nMAX_CHANNEL_PRIORITY = 10000\\n\\nCONDA_PACKAGE_EXTENSION_V1 = \\\".tar.bz2\\\"\\nCONDA_PACKAGE_EXTENSION_V2 = \\\".conda\\\"\\nCONDA_PACKAGE_EXTENSIONS = (\\n    CONDA_PACKAGE_EXTENSION_V2,\\n    CONDA_PACKAGE_EXTENSION_V1,\\n)\\nCONDA_PACKAGE_PARTS = tuple(f\\\"{ext}.part\\\" for ext in CONDA_PACKAGE_EXTENSIONS)\\nCONDA_TARBALL_EXTENSION = CONDA_PACKAGE_EXTENSION_V1  # legacy support for conda-build\\nCONDA_TEMP_EXTENSION = \\\".c~\\\"\\nCONDA_TEMP_EXTENSIONS = (CONDA_TEMP_EXTENSION, \\\".trash\\\")\\nCONDA_LOGS_DIR = \\\".logs\\\"\\n\\nUNKNOWN_CHANNEL = \\\"<unknown>\\\"\\nREPODATA_FN = \\\"repodata.json\\\"\\n\\n#: Default name of the notices file on the server we look for\\nNOTICES_FN = \\\"notices.json\\\"\\n\\n#: Name of cache file where read notice IDs are stored\\nNOTICES_CACHE_FN = \\\"notices.cache\\\"\\n\\n#: Determines the subdir for notices cache\\nNOTICES_CACHE_SUBDIR = \\\"notices\\\"\\n\\n#: Determines the subdir for notices cache\\nNOTICES_DECORATOR_DISPLAY_INTERVAL = 86400  # in seconds\\n\\nDRY_RUN_PREFIX = \\\"Dry run action:\\\"\\nPREFIX_NAME_DISALLOWED_CHARS = {\\\"/\\\", \\\" \\\", \\\":\\\", \\\"#\\\"}\\n\\n\\nclass SafetyChecks(Enum):\\n    disabled = \\\"disabled\\\"\\n    warn = \\\"warn\\\"\\n    enabled = \\\"enabled\\\"\\n\\n    def __str__(self):\\n        return self.value\\n\\n\\nclass PathConflict(Enum):\\n    clobber = \\\"clobber\\\"\\n    warn = \\\"warn\\\"\\n    prevent = \\\"prevent\\\"\\n\\n    def __str__(self):\\n        return self.value\\n\\n\\nclass DepsModifier(Enum):\\n    \\\"\\\"\\\"Flags to enable alternate handling of dependencies.\\\"\\\"\\\"\\n\\n    NOT_SET = \\\"not_set\\\"  # default\\n    NO_DEPS = \\\"no_deps\\\"\\n    ONLY_DEPS = \\\"only_deps\\\"\\n\\n    def __str__(self):\\n        return self.value\\n\\n\\nclass UpdateModifier(Enum):\\n    SPECS_SATISFIED_SKIP_SOLVE = \\\"specs_satisfied_skip_solve\\\"\\n    FREEZE_INSTALLED = (\\n        \\\"freeze_installed\\\"  # freeze is a better name for --no-update-deps\\n    )\\n    UPDATE_DEPS = \\\"update_deps\\\"\\n    UPDATE_SPECS = \\\"update_specs\\\"  # default\\n    UPDATE_ALL = \\\"update_all\\\"\\n    # TODO: add REINSTALL_ALL, see https://github.com/conda/conda/issues/6247 and https://github.com/conda/conda/issues/3149  # NOQA\\n\\n    def __str__(self):\\n        return self.value\\n\\n\\nclass ChannelPriorityMeta(EnumMeta):\\n    def __call__(cls, value, *args, **kwargs):\\n        try:\\n            return super().__call__(value, *args, **kwargs)\\n        except ValueError:\\n            if isinstance(value, str):\\n                from ..auxlib.type_coercion import typify\\n\\n                value = typify(value)\\n            if value is True:\\n                value = \\\"flexible\\\"\\n            elif value is False:\\n                value = cls.DISABLED\\n            return super().__call__(value, *args, **kwargs)\\n\\n\\nclass ValueEnum(Enum):\\n    \\\"\\\"\\\"Subclass of enum that returns the value of the enum as its str representation\\\"\\\"\\\"\\n\\n    def __str__(self):\\n        return f\\\"{self.value}\\\"\\n\\n\\nclass ChannelPriority(six_with_metaclass(ChannelPriorityMeta, ValueEnum)):\\n    __name__ = \\\"ChannelPriority\\\"\\n\\n    STRICT = \\\"strict\\\"\\n    # STRICT_OR_FLEXIBLE = 'strict_or_flexible'  # TODO: consider implementing if needed\\n    FLEXIBLE = \\\"flexible\\\"\\n    DISABLED = \\\"disabled\\\"\\n\\n\\nclass SatSolverChoice(ValueEnum):\\n    PYCOSAT = \\\"pycosat\\\"\\n    PYCRYPTOSAT = \\\"pycryptosat\\\"\\n    PYSAT = \\\"pysat\\\"\\n\\n\\n#: The name of the default solver, currently \\\"libmamba\\\"\\nDEFAULT_SOLVER = \\\"libmamba\\\"\\nCLASSIC_SOLVER = \\\"classic\\\"\\n\\n\\nclass NoticeLevel(ValueEnum):\\n    CRITICAL = \\\"critical\\\"\\n    WARNING = \\\"warning\\\"\\n    INFO = \\\"info\\\"\\n\\n\\n# Magic files for permissions determination\\nPACKAGE_CACHE_MAGIC_FILE = \\\"urls.txt\\\"\\nPREFIX_MAGIC_FILE = join(\\\"conda-meta\\\", \\\"history\\\")\\n\\nPREFIX_STATE_FILE = join(\\\"conda-meta\\\", \\\"state\\\")\\nPACKAGE_ENV_VARS_DIR = join(\\\"etc\\\", \\\"conda\\\", \\\"env_vars.d\\\")\\nCONDA_ENV_VARS_UNSET_VAR = \\\"***unset***\\\"\\n\\n\\n# TODO: should be frozendict(), but I don't want to import frozendict from auxlib here.\\nNAMESPACES_MAP = {  # base package name, namespace\\n    \\\"python\\\": \\\"python\\\",\\n    \\\"r\\\": \\\"r\\\",\\n    \\\"r-base\\\": \\\"r\\\",\\n    \\\"mro-base\\\": \\\"r\\\",\\n    \\\"erlang\\\": \\\"erlang\\\",\\n    \\\"java\\\": \\\"java\\\",\\n    \\\"openjdk\\\": \\\"java\\\",\\n    \\\"julia\\\": \\\"julia\\\",\\n    \\\"latex\\\": \\\"latex\\\",\\n    \\\"lua\\\": \\\"lua\\\",\\n    \\\"nodejs\\\": \\\"js\\\",\\n    \\\"perl\\\": \\\"perl\\\",\\n    \\\"php\\\": \\\"php\\\",\\n    \\\"ruby\\\": \\\"ruby\\\",\\n    \\\"m2-base\\\": \\\"m2\\\",\\n    \\\"msys2-conda-epoch\\\": \\\"m2w64\\\",\\n}\\n\\nNAMESPACE_PACKAGE_NAMES = frozenset(NAMESPACES_MAP)\\nNAMESPACES = frozenset(NAMESPACES_MAP.values())\\n\\n# Namespace arbiters of uniqueness\\n#  global: some repository established by Anaconda, Inc. and conda-forge\\n#  python: https://pypi.org/simple\\n#  r: https://cran.r-project.org/web/packages/available_packages_by_name.html\\n#  erlang: https://hex.pm/packages\\n#  java: https://repo1.maven.org/maven2/\\n#  julia: https://pkg.julialang.org/\\n#  latex: https://ctan.org/pkg\\n#  lua: https://luarocks.org/m/root\\n#  js: https://docs.npmjs.com/misc/registry\\n#  pascal: ???\\n#  perl: https://www.cpan.org/modules/01modules.index.html\\n#  php: https://packagist.org/\\n#  ruby: https://rubygems.org/gems\\n#  clojure: https://clojars.org/\\n\\n\\n# Not all python namespace packages are registered on PyPI. If a package\\n# contains files in site-packages, it probably belongs in the python namespace.\\n\\n\\n# Indicates whether or not external plugins (i.e., plugins that aren't shipped\\n# with conda) are enabled\\nNO_PLUGINS = False\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Conda's global configuration object.\\n\\nThe context aggregates all configuration files, environment variables, and command line arguments\\ninto one global stateful object to be used across all of conda.\\n\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport logging\\nimport os\\nimport platform\\nimport struct\\nimport sys\\nfrom collections import defaultdict\\nfrom contextlib import contextmanager\\nfrom errno import ENOENT\\nfrom functools import cached_property, lru_cache\\nfrom itertools import chain\\nfrom os.path import abspath, exists, expanduser, isdir, isfile, join\\nfrom os.path import split as path_split\\nfrom typing import TYPE_CHECKING, Mapping\\n\\nfrom boltons.setutils import IndexedSet\\n\\nfrom .. import CONDA_SOURCE_ROOT\\nfrom .. import __version__ as CONDA_VERSION\\nfrom ..auxlib.decorators import memoizedproperty\\nfrom ..auxlib.ish import dals\\nfrom ..common._os.linux import linux_get_libc_version\\nfrom ..common.compat import NoneType, on_win\\nfrom ..common.configuration import (\\n    Configuration,\\n    ConfigurationLoadError,\\n    ConfigurationType,\\n    EnvRawParameter,\\n    MapParameter,\\n    ParameterLoader,\\n    PrimitiveParameter,\\n    SequenceParameter,\\n    ValidationError,\\n    unique_sequence_map,\\n)\\nfrom ..common.constants import TRACE\\nfrom ..common.iterators import unique\\nfrom ..common.path import expand, paths_equal\\nfrom ..common.url import has_scheme, path_to_url, split_scheme_auth_token\\nfrom ..deprecations import deprecated\\nfrom .constants import (\\n    APP_NAME,\\n    DEFAULT_AGGRESSIVE_UPDATE_PACKAGES,\\n    DEFAULT_CHANNEL_ALIAS,\\n    DEFAULT_CHANNELS,\\n    DEFAULT_CHANNELS_UNIX,\\n    DEFAULT_CHANNELS_WIN,\\n    DEFAULT_CUSTOM_CHANNELS,\\n    DEFAULT_SOLVER,\\n    DEFAULTS_CHANNEL_NAME,\\n    ERROR_UPLOAD_URL,\\n    KNOWN_SUBDIRS,\\n    NO_PLUGINS,\\n    PREFIX_MAGIC_FILE,\\n    PREFIX_NAME_DISALLOWED_CHARS,\\n    REPODATA_FN,\\n    ROOT_ENV_NAME,\\n    SEARCH_PATH,\\n    ChannelPriority,\\n    DepsModifier,\\n    PathConflict,\\n    SafetyChecks,\\n    SatSolverChoice,\\n    UpdateModifier,\\n)\\n\\ntry:\\n    from frozendict import frozendict\\nexcept ImportError:\\n    from .._vendor.frozendict import frozendict\\n\\nif TYPE_CHECKING:\\n    from pathlib import Path\\n    from typing import Literal\\n\\n    from ..common.configuration import Parameter, RawParameter\\n    from ..plugins.manager import CondaPluginManager\\n\\ntry:\\n    os.getcwd()\\nexcept OSError as e:\\n    if e.errno == ENOENT:\\n        # FileNotFoundError can occur when cwd has been deleted out from underneath the process.\\n        # To resolve #6584, let's go with setting cwd to sys.prefix, and see how far we get.\\n        os.chdir(sys.prefix)\\n    else:\\n        raise\\n\\nlog = logging.getLogger(__name__)\\n\\n_platform_map = {\\n    \\\"freebsd13\\\": \\\"freebsd\\\",\\n    \\\"linux2\\\": \\\"linux\\\",\\n    \\\"linux\\\": \\\"linux\\\",\\n    \\\"darwin\\\": \\\"osx\\\",\\n    \\\"win32\\\": \\\"win\\\",\\n    \\\"zos\\\": \\\"zos\\\",\\n}\\nnon_x86_machines = {\\n    \\\"armv6l\\\",\\n    \\\"armv7l\\\",\\n    \\\"aarch64\\\",\\n    \\\"arm64\\\",\\n    \\\"ppc64\\\",\\n    \\\"ppc64le\\\",\\n    \\\"riscv64\\\",\\n    \\\"s390x\\\",\\n}\\n_arch_names = {\\n    32: \\\"x86\\\",\\n    64: \\\"x86_64\\\",\\n}\\n\\nuser_rc_path = abspath(expanduser(\\\"~/.condarc\\\"))\\nsys_rc_path = join(sys.prefix, \\\".condarc\\\")\\n\\n\\ndef user_data_dir(  # noqa: F811\\n    appname: str | None = None,\\n    appauthor: str | None | Literal[False] = None,\\n    version: str | None = None,\\n    roaming: bool = False,\\n):\\n    # Defer platformdirs import to reduce import time for conda activate.\\n    global user_data_dir\\n    try:\\n        from platformdirs import user_data_dir\\n    except ImportError:  # pragma: no cover\\n        from .._vendor.appdirs import user_data_dir\\n    return user_data_dir(appname, appauthor=appauthor, version=version, roaming=roaming)\\n\\n\\ndef mockable_context_envs_dirs(root_writable, root_prefix, _envs_dirs):\\n    if root_writable:\\n        fixed_dirs = [\\n            join(root_prefix, \\\"envs\\\"),\\n            join(\\\"~\\\", \\\".conda\\\", \\\"envs\\\"),\\n        ]\\n    else:\\n        fixed_dirs = [\\n            join(\\\"~\\\", \\\".conda\\\", \\\"envs\\\"),\\n            join(root_prefix, \\\"envs\\\"),\\n        ]\\n    if on_win:\\n        fixed_dirs.append(join(user_data_dir(APP_NAME, APP_NAME), \\\"envs\\\"))\\n    return tuple(IndexedSet(expand(path) for path in (*_envs_dirs, *fixed_dirs)))\\n\\n\\ndef channel_alias_validation(value):\\n    if value and not has_scheme(value):\\n        return f\\\"channel_alias value '{value}' must have scheme/protocol.\\\"\\n    return True\\n\\n\\ndef default_python_default():\\n    ver = sys.version_info\\n    return \\\"%d.%d\\\" % (ver.major, ver.minor)\\n\\n\\ndef default_python_validation(value):\\n    if value:\\n        if len(value) >= 3 and value[1] == \\\".\\\":\\n            try:\\n                value = float(value)\\n                if 2.0 <= value < 4.0:\\n                    return True\\n            except ValueError:  # pragma: no cover\\n                pass\\n    else:\\n        # Set to None or '' meaning no python pinning\\n        return True\\n\\n    return f\\\"default_python value '{value}' not of the form '[23].[0-9][0-9]?' or ''\\\"\\n\\n\\ndef ssl_verify_validation(value):\\n    if isinstance(value, str):\\n        if sys.version_info < (3, 10) and value == \\\"truststore\\\":\\n            return \\\"`ssl_verify: truststore` is only supported on Python 3.10 or later\\\"\\n        elif value != \\\"truststore\\\" and not exists(value):\\n            return (\\n                f\\\"ssl_verify value '{value}' must be a boolean, a path to a \\\"\\n                \\\"certificate bundle file, a path to a directory containing \\\"\\n                \\\"certificates of trusted CAs, or 'truststore' to use the \\\"\\n                \\\"operating system certificate store.\\\"\\n            )\\n    return True\\n\\n\\nclass Context(Configuration):\\n    add_pip_as_python_dependency = ParameterLoader(PrimitiveParameter(True))\\n    allow_conda_downgrades = ParameterLoader(PrimitiveParameter(False))\\n    # allow cyclical dependencies, or raise\\n    allow_cycles = ParameterLoader(PrimitiveParameter(True))\\n    allow_softlinks = ParameterLoader(PrimitiveParameter(False))\\n    auto_update_conda = ParameterLoader(\\n        PrimitiveParameter(True), aliases=(\\\"self_update\\\",)\\n    )\\n    auto_activate_base = ParameterLoader(PrimitiveParameter(True))\\n    auto_stack = ParameterLoader(PrimitiveParameter(0))\\n    notify_outdated_conda = ParameterLoader(PrimitiveParameter(True))\\n    clobber = ParameterLoader(PrimitiveParameter(False))\\n    changeps1 = ParameterLoader(PrimitiveParameter(True))\\n    env_prompt = ParameterLoader(PrimitiveParameter(\\\"({default_env}) \\\"))\\n    create_default_packages = ParameterLoader(\\n        SequenceParameter(PrimitiveParameter(\\\"\\\", element_type=str))\\n    )\\n    register_envs = ParameterLoader(PrimitiveParameter(True))\\n    default_python = ParameterLoader(\\n        PrimitiveParameter(\\n            default_python_default(),\\n            element_type=(str, NoneType),\\n            validation=default_python_validation,\\n        )\\n    )\\n    download_only = ParameterLoader(PrimitiveParameter(False))\\n    enable_private_envs = ParameterLoader(PrimitiveParameter(False))\\n    force_32bit = ParameterLoader(PrimitiveParameter(False))\\n    non_admin_enabled = ParameterLoader(PrimitiveParameter(True))\\n    pip_interop_enabled = ParameterLoader(PrimitiveParameter(False))\\n\\n    # multithreading in various places\\n    _default_threads = ParameterLoader(\\n        PrimitiveParameter(0, element_type=int), aliases=(\\\"default_threads\\\",)\\n    )\\n    # download repodata\\n    _repodata_threads = ParameterLoader(\\n        PrimitiveParameter(0, element_type=int), aliases=(\\\"repodata_threads\\\",)\\n    )\\n    # download packages\\n    _fetch_threads = ParameterLoader(\\n        PrimitiveParameter(0, element_type=int), aliases=(\\\"fetch_threads\\\",)\\n    )\\n    _verify_threads = ParameterLoader(\\n        PrimitiveParameter(0, element_type=int), aliases=(\\\"verify_threads\\\",)\\n    )\\n    # this one actually defaults to 1 - that is handled in the property below\\n    _execute_threads = ParameterLoader(\\n        PrimitiveParameter(0, element_type=int), aliases=(\\\"execute_threads\\\",)\\n    )\\n\\n    # Safety & Security\\n    _aggressive_update_packages = ParameterLoader(\\n        SequenceParameter(\\n            PrimitiveParameter(\\\"\\\", element_type=str), DEFAULT_AGGRESSIVE_UPDATE_PACKAGES\\n        ),\\n        aliases=(\\\"aggressive_update_packages\\\",),\\n    )\\n    safety_checks = ParameterLoader(PrimitiveParameter(SafetyChecks.warn))\\n    extra_safety_checks = ParameterLoader(PrimitiveParameter(False))\\n    _signing_metadata_url_base = ParameterLoader(\\n        PrimitiveParameter(None, element_type=(str, NoneType)),\\n        aliases=(\\\"signing_metadata_url_base\\\",),\\n    )\\n    path_conflict = ParameterLoader(PrimitiveParameter(PathConflict.clobber))\\n\\n    pinned_packages = ParameterLoader(\\n        SequenceParameter(\\n            PrimitiveParameter(\\\"\\\", element_type=str), string_delimiter=\\\"&\\\"\\n        )\\n    )  # TODO: consider a different string delimiter  # NOQA\\n    disallowed_packages = ParameterLoader(\\n        SequenceParameter(\\n            PrimitiveParameter(\\\"\\\", element_type=str), string_delimiter=\\\"&\\\"\\n        ),\\n        aliases=(\\\"disallow\\\",),\\n    )\\n    rollback_enabled = ParameterLoader(PrimitiveParameter(True))\\n    track_features = ParameterLoader(\\n        SequenceParameter(PrimitiveParameter(\\\"\\\", element_type=str))\\n    )\\n    use_index_cache = ParameterLoader(PrimitiveParameter(False))\\n\\n    separate_format_cache = ParameterLoader(PrimitiveParameter(False))\\n\\n    _root_prefix = ParameterLoader(\\n        PrimitiveParameter(\\\"\\\"), aliases=(\\\"root_dir\\\", \\\"root_prefix\\\")\\n    )\\n    _envs_dirs = ParameterLoader(\\n        SequenceParameter(\\n            PrimitiveParameter(\\\"\\\", element_type=str), string_delimiter=os.pathsep\\n        ),\\n        aliases=(\\\"envs_dirs\\\", \\\"envs_path\\\"),\\n        expandvars=True,\\n    )\\n    _pkgs_dirs = ParameterLoader(\\n        SequenceParameter(PrimitiveParameter(\\\"\\\", str)),\\n        aliases=(\\\"pkgs_dirs\\\",),\\n        expandvars=True,\\n    )\\n    _subdir = ParameterLoader(PrimitiveParameter(\\\"\\\"), aliases=(\\\"subdir\\\",))\\n    _subdirs = ParameterLoader(\\n        SequenceParameter(PrimitiveParameter(\\\"\\\", str)), aliases=(\\\"subdirs\\\",)\\n    )\\n\\n    local_repodata_ttl = ParameterLoader(\\n        PrimitiveParameter(1, element_type=(bool, int))\\n    )\\n    # number of seconds to cache repodata locally\\n    #   True/1: respect Cache-Control max-age header\\n    #   False/0: always fetch remote repodata (HTTP 304 responses respected)\\n\\n    # remote connection details\\n    ssl_verify = ParameterLoader(\\n        PrimitiveParameter(\\n            True, element_type=(str, bool), validation=ssl_verify_validation\\n        ),\\n        aliases=(\\\"verify_ssl\\\",),\\n        expandvars=True,\\n    )\\n    client_ssl_cert = ParameterLoader(\\n        PrimitiveParameter(None, element_type=(str, NoneType)),\\n        aliases=(\\\"client_cert\\\",),\\n        expandvars=True,\\n    )\\n    client_ssl_cert_key = ParameterLoader(\\n        PrimitiveParameter(None, element_type=(str, NoneType)),\\n        aliases=(\\\"client_cert_key\\\",),\\n        expandvars=True,\\n    )\\n    proxy_servers = ParameterLoader(\\n        MapParameter(PrimitiveParameter(None, (str, NoneType))), expandvars=True\\n    )\\n    remote_connect_timeout_secs = ParameterLoader(PrimitiveParameter(9.15))\\n    remote_read_timeout_secs = ParameterLoader(PrimitiveParameter(60.0))\\n    remote_max_retries = ParameterLoader(PrimitiveParameter(3))\\n    remote_backoff_factor = ParameterLoader(PrimitiveParameter(1))\\n\\n    add_anaconda_token = ParameterLoader(\\n        PrimitiveParameter(True), aliases=(\\\"add_binstar_token\\\",)\\n    )\\n\\n    _reporters = ParameterLoader(\\n        SequenceParameter(MapParameter(PrimitiveParameter(\\\"\\\", element_type=str))),\\n        aliases=(\\\"reporters\\\",),\\n    )\\n\\n    ####################################################\\n    #               Channel Configuration              #\\n    ####################################################\\n    allow_non_channel_urls = ParameterLoader(PrimitiveParameter(False))\\n    _channel_alias = ParameterLoader(\\n        PrimitiveParameter(DEFAULT_CHANNEL_ALIAS, validation=channel_alias_validation),\\n        aliases=(\\\"channel_alias\\\",),\\n        expandvars=True,\\n    )\\n    channel_priority = ParameterLoader(PrimitiveParameter(ChannelPriority.FLEXIBLE))\\n    _channels = ParameterLoader(\\n        SequenceParameter(\\n            PrimitiveParameter(\\\"\\\", element_type=str), default=(DEFAULTS_CHANNEL_NAME,)\\n        ),\\n        aliases=(\\n            \\\"channels\\\",\\n            \\\"channel\\\",\\n        ),\\n        expandvars=True,\\n    )  # channel for args.channel\\n    channel_settings = ParameterLoader(\\n        SequenceParameter(MapParameter(PrimitiveParameter(\\\"\\\", element_type=str)))\\n    )\\n    _custom_channels = ParameterLoader(\\n        MapParameter(PrimitiveParameter(\\\"\\\", element_type=str), DEFAULT_CUSTOM_CHANNELS),\\n        aliases=(\\\"custom_channels\\\",),\\n        expandvars=True,\\n    )\\n    _custom_multichannels = ParameterLoader(\\n        MapParameter(SequenceParameter(PrimitiveParameter(\\\"\\\", element_type=str))),\\n        aliases=(\\\"custom_multichannels\\\",),\\n        expandvars=True,\\n    )\\n    _default_channels = ParameterLoader(\\n        SequenceParameter(PrimitiveParameter(\\\"\\\", element_type=str), DEFAULT_CHANNELS),\\n        aliases=(\\\"default_channels\\\",),\\n        expandvars=True,\\n    )\\n    _migrated_channel_aliases = ParameterLoader(\\n        SequenceParameter(PrimitiveParameter(\\\"\\\", element_type=str)),\\n        aliases=(\\\"migrated_channel_aliases\\\",),\\n    )\\n    migrated_custom_channels = ParameterLoader(\\n        MapParameter(PrimitiveParameter(\\\"\\\", element_type=str)), expandvars=True\\n    )  # TODO: also take a list of strings\\n    override_channels_enabled = ParameterLoader(PrimitiveParameter(True))\\n    show_channel_urls = ParameterLoader(\\n        PrimitiveParameter(None, element_type=(bool, NoneType))\\n    )\\n    use_local = ParameterLoader(PrimitiveParameter(False))\\n    allowlist_channels = ParameterLoader(\\n        SequenceParameter(PrimitiveParameter(\\\"\\\", element_type=str)),\\n        aliases=(\\\"whitelist_channels\\\",),\\n        expandvars=True,\\n    )\\n    restore_free_channel = ParameterLoader(PrimitiveParameter(False))\\n    repodata_fns = ParameterLoader(\\n        SequenceParameter(\\n            PrimitiveParameter(\\\"\\\", element_type=str),\\n            (\\\"current_repodata.json\\\", REPODATA_FN),\\n        )\\n    )\\n    _use_only_tar_bz2 = ParameterLoader(\\n        PrimitiveParameter(None, element_type=(bool, NoneType)),\\n        aliases=(\\\"use_only_tar_bz2\\\",),\\n    )\\n\\n    always_softlink = ParameterLoader(PrimitiveParameter(False), aliases=(\\\"softlink\\\",))\\n    always_copy = ParameterLoader(PrimitiveParameter(False), aliases=(\\\"copy\\\",))\\n    always_yes = ParameterLoader(\\n        PrimitiveParameter(None, element_type=(bool, NoneType)), aliases=(\\\"yes\\\",)\\n    )\\n    _debug = ParameterLoader(PrimitiveParameter(False), aliases=[\\\"debug\\\"])\\n    _trace = ParameterLoader(PrimitiveParameter(False), aliases=[\\\"trace\\\"])\\n    dev = ParameterLoader(PrimitiveParameter(False))\\n    dry_run = ParameterLoader(PrimitiveParameter(False))\\n    error_upload_url = ParameterLoader(PrimitiveParameter(ERROR_UPLOAD_URL))\\n    force = ParameterLoader(PrimitiveParameter(False))\\n    json = ParameterLoader(PrimitiveParameter(False))\\n    offline = ParameterLoader(PrimitiveParameter(False))\\n    quiet = ParameterLoader(PrimitiveParameter(False))\\n    ignore_pinned = ParameterLoader(PrimitiveParameter(False))\\n    report_errors = ParameterLoader(\\n        PrimitiveParameter(None, element_type=(bool, NoneType))\\n    )\\n    shortcuts = ParameterLoader(PrimitiveParameter(True))\\n    number_channel_notices = ParameterLoader(PrimitiveParameter(5, element_type=int))\\n    shortcuts = ParameterLoader(PrimitiveParameter(True))\\n    shortcuts_only = ParameterLoader(\\n        SequenceParameter(PrimitiveParameter(\\\"\\\", element_type=str)), expandvars=True\\n    )\\n    _verbosity = ParameterLoader(\\n        PrimitiveParameter(0, element_type=int), aliases=(\\\"verbose\\\", \\\"verbosity\\\")\\n    )\\n    experimental = ParameterLoader(SequenceParameter(PrimitiveParameter(\\\"\\\", str)))\\n    no_lock = ParameterLoader(PrimitiveParameter(False))\\n    repodata_use_zst = ParameterLoader(PrimitiveParameter(True))\\n    envvars_force_uppercase = ParameterLoader(PrimitiveParameter(True))\\n\\n    ####################################################\\n    #               Solver Configuration               #\\n    ####################################################\\n    deps_modifier = ParameterLoader(PrimitiveParameter(DepsModifier.NOT_SET))\\n    update_modifier = ParameterLoader(PrimitiveParameter(UpdateModifier.UPDATE_SPECS))\\n    sat_solver = ParameterLoader(PrimitiveParameter(SatSolverChoice.PYCOSAT))\\n    solver_ignore_timestamps = ParameterLoader(PrimitiveParameter(False))\\n    solver = ParameterLoader(\\n        PrimitiveParameter(DEFAULT_SOLVER),\\n        aliases=(\\\"experimental_solver\\\",),\\n    )\\n\\n    # # CLI-only\\n    # no_deps = ParameterLoader(PrimitiveParameter(NULL, element_type=(type(NULL), bool)))\\n    # # CLI-only\\n    # only_deps = ParameterLoader(PrimitiveParameter(NULL, element_type=(type(NULL), bool)))\\n    #\\n    # freeze_installed = ParameterLoader(PrimitiveParameter(False))\\n    # update_deps = ParameterLoader(PrimitiveParameter(False), aliases=('update_dependencies',))\\n    # update_specs = ParameterLoader(PrimitiveParameter(False))\\n    # update_all = ParameterLoader(PrimitiveParameter(False))\\n\\n    force_remove = ParameterLoader(PrimitiveParameter(False))\\n    force_reinstall = ParameterLoader(PrimitiveParameter(False))\\n\\n    target_prefix_override = ParameterLoader(PrimitiveParameter(\\\"\\\"))\\n\\n    unsatisfiable_hints = ParameterLoader(PrimitiveParameter(True))\\n    unsatisfiable_hints_check_depth = ParameterLoader(PrimitiveParameter(2))\\n\\n    # conda_build\\n    bld_path = ParameterLoader(PrimitiveParameter(\\\"\\\"))\\n    anaconda_upload = ParameterLoader(\\n        PrimitiveParameter(None, element_type=(bool, NoneType)),\\n        aliases=(\\\"binstar_upload\\\",),\\n    )\\n    _croot = ParameterLoader(PrimitiveParameter(\\\"\\\"), aliases=(\\\"croot\\\",))\\n    _conda_build = ParameterLoader(\\n        MapParameter(PrimitiveParameter(\\\"\\\", element_type=str)),\\n        aliases=(\\\"conda-build\\\", \\\"conda_build\\\"),\\n    )\\n\\n    ####################################################\\n    #               Plugin Configuration               #\\n    ####################################################\\n\\n    no_plugins = ParameterLoader(PrimitiveParameter(NO_PLUGINS))\\n\\n    def __init__(self, search_path=None, argparse_args=None, **kwargs):\\n        super().__init__(argparse_args=argparse_args)\\n\\n        self._set_search_path(\\n            SEARCH_PATH if search_path is None else search_path,\\n            # for proper search_path templating when --name/--prefix is used\\n            CONDA_PREFIX=determine_target_prefix(self, argparse_args),\\n        )\\n        self._set_env_vars(APP_NAME)\\n        self._set_argparse_args(argparse_args)\\n\\n    def post_build_validation(self):\\n        errors = []\\n        if self.client_ssl_cert_key and not self.client_ssl_cert:\\n            error = ValidationError(\\n                \\\"client_ssl_cert\\\",\\n                self.client_ssl_cert,\\n                \\\"<<merged>>\\\",\\n                \\\"'client_ssl_cert' is required when 'client_ssl_cert_key' \\\"\\n                \\\"is defined\\\",\\n            )\\n            errors.append(error)\\n        if self.always_copy and self.always_softlink:\\n            error = ValidationError(\\n                \\\"always_copy\\\",\\n                self.always_copy,\\n                \\\"<<merged>>\\\",\\n                \\\"'always_copy' and 'always_softlink' are mutually exclusive. \\\"\\n                \\\"Only one can be set to 'True'.\\\",\\n            )\\n            errors.append(error)\\n        return errors\\n\\n    @property\\n    def plugin_manager(self) -> CondaPluginManager:\\n        \\\"\\\"\\\"\\n        This is the preferred way of accessing the ``PluginManager`` object for this application\\n        and is located here to avoid problems with cyclical imports elsewhere in the code.\\n        \\\"\\\"\\\"\\n        from ..plugins.manager import get_plugin_manager\\n\\n        return get_plugin_manager()\\n\\n    @cached_property\\n    def plugins(self) -> PluginConfig:\\n        \\\"\\\"\\\"\\n        Preferred way of accessing settings introduced by the settings plugin hook\\n        \\\"\\\"\\\"\\n        self.plugin_manager.load_settings()\\n        return PluginConfig(self.raw_data)\\n\\n    @property\\n    def conda_build_local_paths(self):\\n        # does file system reads to make sure paths actually exist\\n        return tuple(\\n            unique(\\n                full_path\\n                for full_path in (\\n                    expand(d)\\n                    for d in (\\n                        self._croot,\\n                        self.bld_path,\\n                        self.conda_build.get(\\\"root-dir\\\"),\\n                        join(self.root_prefix, \\\"conda-bld\\\"),\\n                        \\\"~/conda-bld\\\",\\n                    )\\n                    if d\\n                )\\n                if isdir(full_path)\\n            )\\n        )\\n\\n    @property\\n    def conda_build_local_urls(self):\\n        return tuple(path_to_url(p) for p in self.conda_build_local_paths)\\n\\n    @property\\n    def croot(self):\\n        \\\"\\\"\\\"This is where source caches and work folders live\\\"\\\"\\\"\\n        if self._croot:\\n            return abspath(expanduser(self._croot))\\n        elif self.bld_path:\\n            return abspath(expanduser(self.bld_path))\\n        elif \\\"root-dir\\\" in self.conda_build:\\n            return abspath(expanduser(self.conda_build[\\\"root-dir\\\"]))\\n        elif self.root_writable:\\n            return join(self.root_prefix, \\\"conda-bld\\\")\\n        else:\\n            return expand(\\\"~/conda-bld\\\")\\n\\n    @property\\n    def local_build_root(self):\\n        return self.croot\\n\\n    @property\\n    def conda_build(self):\\n        # conda-build needs its config map to be mutable\\n        try:\\n            return self.__conda_build\\n        except AttributeError:\\n            self.__conda_build = __conda_build = dict(self._conda_build)\\n            return __conda_build\\n\\n    @property\\n    def arch_name(self):\\n        m = platform.machine()\\n        if m in non_x86_machines:\\n            return m\\n        else:\\n            return _arch_names[self.bits]\\n\\n    @property\\n    def platform(self):\\n        return _platform_map.get(sys.platform, \\\"unknown\\\")\\n\\n    @property\\n    def default_threads(self) -> int | None:\\n        return self._default_threads or None\\n\\n    @property\\n    def repodata_threads(self) -> int | None:\\n        return self._repodata_threads or self.default_threads\\n\\n    @property\\n    def fetch_threads(self) -> int | None:\\n        \\\"\\\"\\\"\\n        If both are not overriden (0), return experimentally-determined value of 5\\n        \\\"\\\"\\\"\\n        if self._fetch_threads == 0 and self._default_threads == 0:\\n            return 5\\n        return self._fetch_threads or self.default_threads\\n\\n    @property\\n    def verify_threads(self) -> int | None:\\n        if self._verify_threads:\\n            threads = self._verify_threads\\n        elif self.default_threads:\\n            threads = self.default_threads\\n        else:\\n            threads = 1\\n        return threads\\n\\n    @property\\n    def execute_threads(self):\\n        if self._execute_threads:\\n            threads = self._execute_threads\\n        elif self.default_threads:\\n            threads = self.default_threads\\n        else:\\n            threads = 1\\n        return threads\\n\\n    @property\\n    def subdir(self):\\n        if self._subdir:\\n            return self._subdir\\n        return self._native_subdir()\\n\\n    @lru_cache(maxsize=None)\\n    def _native_subdir(self):\\n        m = platform.machine()\\n        if m in non_x86_machines:\\n            return f\\\"{self.platform}-{m}\\\"\\n        elif self.platform == \\\"zos\\\":\\n            return \\\"zos-z\\\"\\n        else:\\n            return \\\"%s-%d\\\" % (self.platform, self.bits)\\n\\n    @property\\n    def subdirs(self):\\n        return self._subdirs or (self.subdir, \\\"noarch\\\")\\n\\n    @memoizedproperty\\n    def known_subdirs(self):\\n        return frozenset((*KNOWN_SUBDIRS, *self.subdirs))\\n\\n    @property\\n    def bits(self):\\n        if self.force_32bit:\\n            return 32\\n        else:\\n            return 8 * struct.calcsize(\\\"P\\\")\\n\\n    @property\\n    @deprecated(\\n        \\\"24.3\\\",\\n        \\\"24.9\\\",\\n        addendum=\\\"Please use `conda.base.context.context.root_prefix` instead.\\\",\\n    )\\n    def root_dir(self) -> os.PathLike:\\n        # root_dir is an alias for root_prefix, we prefer the name \\\"root_prefix\\\"\\n        # because it is more consistent with other names\\n        return self.root_prefix\\n\\n    @property\\n    def root_writable(self):\\n        # rather than using conda.gateways.disk.test.prefix_is_writable\\n        # let's shortcut and assume the root prefix exists\\n        path = join(self.root_prefix, PREFIX_MAGIC_FILE)\\n        if isfile(path):\\n            try:\\n                fh = open(path, \\\"a+\\\")\\n            except OSError as e:\\n                log.debug(e)\\n                return False\\n            else:\\n                fh.close()\\n                return True\\n        return False\\n\\n    @property\\n    def envs_dirs(self):\\n        return mockable_context_envs_dirs(\\n            self.root_writable, self.root_prefix, self._envs_dirs\\n        )\\n\\n    @property\\n    def pkgs_dirs(self):\\n        if self._pkgs_dirs:\\n            return tuple(IndexedSet(expand(p) for p in self._pkgs_dirs))\\n        else:\\n            cache_dir_name = \\\"pkgs32\\\" if context.force_32bit else \\\"pkgs\\\"\\n            fixed_dirs = (\\n                self.root_prefix,\\n                join(\\\"~\\\", \\\".conda\\\"),\\n            )\\n            if on_win:\\n                fixed_dirs += (user_data_dir(APP_NAME, APP_NAME),)\\n            return tuple(\\n                IndexedSet(expand(join(p, cache_dir_name)) for p in (fixed_dirs))\\n            )\\n\\n    @memoizedproperty\\n    def trash_dir(self):\\n        # TODO: this inline import can be cleaned up by moving pkgs_dir write detection logic\\n        from ..core.package_cache_data import PackageCacheData\\n\\n        pkgs_dir = PackageCacheData.first_writable().pkgs_dir\\n        trash_dir = join(pkgs_dir, \\\".trash\\\")\\n        from ..gateways.disk.create import mkdir_p\\n\\n        mkdir_p(trash_dir)\\n        return trash_dir\\n\\n    @property\\n    def default_prefix(self):\\n        if self.active_prefix:\\n            return self.active_prefix\\n        _default_env = os.getenv(\\\"CONDA_DEFAULT_ENV\\\")\\n        if _default_env in (None, ROOT_ENV_NAME, \\\"root\\\"):\\n            return self.root_prefix\\n        elif os.sep in _default_env:\\n            return abspath(_default_env)\\n        else:\\n            for envs_dir in self.envs_dirs:\\n                default_prefix = join(envs_dir, _default_env)\\n                if isdir(default_prefix):\\n                    return default_prefix\\n        return join(self.envs_dirs[0], _default_env)\\n\\n    @property\\n    def active_prefix(self):\\n        return os.getenv(\\\"CONDA_PREFIX\\\")\\n\\n    @property\\n    def shlvl(self):\\n        return int(os.getenv(\\\"CONDA_SHLVL\\\", -1))\\n\\n    @property\\n    def aggressive_update_packages(self):\\n        from ..models.match_spec import MatchSpec\\n\\n        return tuple(MatchSpec(s) for s in self._aggressive_update_packages)\\n\\n    @property\\n    def target_prefix(self):\\n        # used for the prefix that is the target of the command currently being executed\\n        # different from the active prefix, which is sometimes given by -p or -n command line flags\\n        return determine_target_prefix(self)\\n\\n    @memoizedproperty\\n    def root_prefix(self):\\n        if self._root_prefix:\\n            return abspath(expanduser(self._root_prefix))\\n        else:\\n            return self.conda_prefix\\n\\n    @property\\n    def conda_prefix(self):\\n        return abspath(sys.prefix)\\n\\n    @property\\n    @deprecated(\\n        \\\"23.9\\\",\\n        \\\"24.9\\\",\\n        addendum=\\\"Please use `conda.base.context.context.conda_exe_vars_dict` instead\\\",\\n    )\\n    def conda_exe(self):\\n        bin_dir = \\\"Scripts\\\" if on_win else \\\"bin\\\"\\n        exe = \\\"conda.exe\\\" if on_win else \\\"conda\\\"\\n        return join(self.conda_prefix, bin_dir, exe)\\n\\n    @property\\n    def av_data_dir(self):\\n        \\\"\\\"\\\"Where critical artifact verification data (e.g., various public keys) can be found.\\\"\\\"\\\"\\n        # TODO (AV): Find ways to make this user configurable?\\n        return join(self.conda_prefix, \\\"etc\\\", \\\"conda\\\")\\n\\n    @property\\n    def signing_metadata_url_base(self):\\n        \\\"\\\"\\\"Base URL for artifact verification signing metadata (*.root.json, key_mgr.json).\\\"\\\"\\\"\\n        if self._signing_metadata_url_base:\\n            return self._signing_metadata_url_base\\n        else:\\n            return None\\n\\n    @property\\n    def conda_exe_vars_dict(self):\\n        \\\"\\\"\\\"\\n        The vars can refer to each other if necessary since the dict is ordered.\\n        None means unset it.\\n        \\\"\\\"\\\"\\n        if context.dev:\\n            return {\\n                \\\"CONDA_EXE\\\": sys.executable,\\n                # do not confuse with os.path.join, we are joining paths with ; or : delimiters\\n                \\\"PYTHONPATH\\\": os.pathsep.join(\\n                    (CONDA_SOURCE_ROOT, os.environ.get(\\\"PYTHONPATH\\\", \\\"\\\"))\\n                ),\\n                \\\"_CE_M\\\": \\\"-m\\\",\\n                \\\"_CE_CONDA\\\": \\\"conda\\\",\\n                \\\"CONDA_PYTHON_EXE\\\": sys.executable,\\n            }\\n        else:\\n            bin_dir = \\\"Scripts\\\" if on_win else \\\"bin\\\"\\n            exe = \\\"conda.exe\\\" if on_win else \\\"conda\\\"\\n            # I was going to use None to indicate a variable to unset, but that gets tricky with\\n            # error-on-undefined.\\n            return {\\n                \\\"CONDA_EXE\\\": os.path.join(sys.prefix, bin_dir, exe),\\n                \\\"_CE_M\\\": \\\"\\\",\\n                \\\"_CE_CONDA\\\": \\\"\\\",\\n                \\\"CONDA_PYTHON_EXE\\\": sys.executable,\\n            }\\n\\n    @memoizedproperty\\n    def channel_alias(self):\\n        from ..models.channel import Channel\\n\\n        location, scheme, auth, token = split_scheme_auth_token(self._channel_alias)\\n        return Channel(scheme=scheme, auth=auth, location=location, token=token)\\n\\n    @property\\n    def migrated_channel_aliases(self):\\n        from ..models.channel import Channel\\n\\n        return tuple(\\n            Channel(scheme=scheme, auth=auth, location=location, token=token)\\n            for location, scheme, auth, token in (\\n                split_scheme_auth_token(c) for c in self._migrated_channel_aliases\\n            )\\n        )\\n\\n    @property\\n    def prefix_specified(self):\\n        return (\\n            self._argparse_args.get(\\\"prefix\\\") is not None\\n            or self._argparse_args.get(\\\"name\\\") is not None\\n        )\\n\\n    @memoizedproperty\\n    def default_channels(self):\\n        # the format for 'default_channels' is a list of strings that either\\n        #   - start with a scheme\\n        #   - are meant to be prepended with channel_alias\\n        return self.custom_multichannels[DEFAULTS_CHANNEL_NAME]\\n\\n    @memoizedproperty\\n    def custom_multichannels(self):\\n        from ..models.channel import Channel\\n\\n        if (\\n            not on_win\\n            and self.subdir.startswith(\\\"win-\\\")\\n            and self._default_channels == DEFAULT_CHANNELS_UNIX\\n        ):\\n            default_channels = list(DEFAULT_CHANNELS_WIN)\\n        else:\\n            default_channels = list(self._default_channels)\\n\\n        if self.restore_free_channel:\\n            default_channels.insert(1, \\\"https://repo.anaconda.com/pkgs/free\\\")\\n\\n        reserved_multichannel_urls = {\\n            DEFAULTS_CHANNEL_NAME: default_channels,\\n            \\\"local\\\": self.conda_build_local_urls,\\n        }\\n        reserved_multichannels = {\\n            name: tuple(\\n                Channel.make_simple_channel(self.channel_alias, url) for url in urls\\n            )\\n            for name, urls in reserved_multichannel_urls.items()\\n        }\\n        custom_multichannels = {\\n            name: tuple(\\n                Channel.make_simple_channel(self.channel_alias, url) for url in urls\\n            )\\n            for name, urls in self._custom_multichannels.items()\\n        }\\n        return {\\n            name: channels\\n            for name, channels in (\\n                *custom_multichannels.items(),\\n                *reserved_multichannels.items(),  # order maters, reserved overrides custom\\n            )\\n        }\\n\\n    @memoizedproperty\\n    def custom_channels(self):\\n        from ..models.channel import Channel\\n\\n        return {\\n            channel.name: channel\\n            for channel in (\\n                *chain.from_iterable(\\n                    channel for channel in self.custom_multichannels.values()\\n                ),\\n                *(\\n                    Channel.make_simple_channel(self.channel_alias, url, name)\\n                    for name, url in self._custom_channels.items()\\n                ),\\n            )\\n        }\\n\\n    @property\\n    def channels(self):\\n        local_add = (\\\"local\\\",) if self.use_local else ()\\n        if (\\n            self._argparse_args\\n            and \\\"override_channels\\\" in self._argparse_args\\n            and self._argparse_args[\\\"override_channels\\\"]\\n        ):\\n            if not self.override_channels_enabled:\\n                from ..exceptions import OperationNotAllowed\\n\\n                raise OperationNotAllowed(\\\"Overriding channels has been disabled.\\\")\\n            elif not (\\n                self._argparse_args\\n                and \\\"channel\\\" in self._argparse_args\\n                and self._argparse_args[\\\"channel\\\"]\\n            ):\\n                from ..exceptions import ArgumentError\\n\\n                raise ArgumentError(\\n                    \\\"At least one -c / --channel flag must be supplied when using \\\"\\n                    \\\"--override-channels.\\\"\\n                )\\n            else:\\n                return tuple(IndexedSet((*local_add, *self._argparse_args[\\\"channel\\\"])))\\n\\n        # add 'defaults' channel when necessary if --channel is given via the command line\\n        if self._argparse_args and \\\"channel\\\" in self._argparse_args:\\n            # TODO: it's args.channel right now, not channels\\n            argparse_channels = tuple(self._argparse_args[\\\"channel\\\"] or ())\\n            # Add condition to make sure that sure that we add the 'defaults'\\n            # channel only when no channels are defined in condarc\\n            # We needs to get the config_files and then check that they\\n            # don't define channels\\n            channel_in_config_files = any(\\n                \\\"channels\\\" in context.raw_data[rc_file].keys()\\n                for rc_file in self.config_files\\n            )\\n            if argparse_channels and not channel_in_config_files:\\n                return tuple(\\n                    IndexedSet((*local_add, *argparse_channels, DEFAULTS_CHANNEL_NAME))\\n                )\\n\\n        return tuple(IndexedSet((*local_add, *self._channels)))\\n\\n    @property\\n    def config_files(self):\\n        return tuple(\\n            path\\n            for path in context.collect_all()\\n            if path not in (\\\"envvars\\\", \\\"cmd_line\\\")\\n        )\\n\\n    @property\\n    def use_only_tar_bz2(self):\\n        # we avoid importing this at the top to avoid PATH issues.  Ensure that this\\n        #    is only called when use_only_tar_bz2 is first called.\\n        import conda_package_handling.api\\n\\n        use_only_tar_bz2 = False\\n        if self._use_only_tar_bz2 is None:\\n            if self._argparse_args and \\\"use_only_tar_bz2\\\" in self._argparse_args:\\n                use_only_tar_bz2 &= self._argparse_args[\\\"use_only_tar_bz2\\\"]\\n        return (\\n            (\\n                hasattr(conda_package_handling.api, \\\"libarchive_enabled\\\")\\n                and not conda_package_handling.api.libarchive_enabled\\n            )\\n            or self._use_only_tar_bz2\\n            or use_only_tar_bz2\\n        )\\n\\n    @property\\n    def binstar_upload(self):\\n        # backward compatibility for conda-build\\n        return self.anaconda_upload\\n\\n    @property\\n    def trace(self) -> bool:\\n        \\\"\\\"\\\"Alias for context.verbosity >=4.\\\"\\\"\\\"\\n        return self.verbosity >= 4\\n\\n    @property\\n    def debug(self) -> bool:\\n        \\\"\\\"\\\"Alias for context.verbosity >=3.\\\"\\\"\\\"\\n        return self.verbosity >= 3\\n\\n    @property\\n    def info(self) -> bool:\\n        \\\"\\\"\\\"Alias for context.verbosity >=2.\\\"\\\"\\\"\\n        return self.verbosity >= 2\\n\\n    @property\\n    def verbose(self) -> bool:\\n        \\\"\\\"\\\"Alias for context.verbosity >=1.\\\"\\\"\\\"\\n        return self.verbosity >= 1\\n\\n    @property\\n    def verbosity(self) -> int:\\n        \\\"\\\"\\\"Verbosity level.\\n\\n        For cleaner and readable code it is preferable to use the following alias properties:\\n            context.trace\\n            context.debug\\n            context.info\\n            context.verbose\\n            context.log_level\\n        \\\"\\\"\\\"\\n        #                   0 → logging.WARNING, standard output\\n        #           -v    = 1 → logging.WARNING, detailed output\\n        #           -vv   = 2 → logging.INFO\\n        # --debug = -vvv  = 3 → logging.DEBUG\\n        # --trace = -vvvv = 4 → conda.gateways.logging.TRACE\\n        if self._trace:\\n            return 4\\n        elif self._debug:\\n            return 3\\n        else:\\n            return self._verbosity\\n\\n    @property\\n    def log_level(self) -> int:\\n        \\\"\\\"\\\"Map context.verbosity to logging level.\\\"\\\"\\\"\\n        if 4 < self.verbosity:\\n            return logging.NOTSET  # 0\\n        elif 3 < self.verbosity <= 4:\\n            return TRACE  # 5\\n        elif 2 < self.verbosity <= 3:\\n            return logging.DEBUG  # 10\\n        elif 1 < self.verbosity <= 2:\\n            return logging.INFO  # 20\\n        else:\\n            return logging.WARNING  # 30\\n\\n    def solver_user_agent(self):\\n        user_agent = f\\\"solver/{self.solver}\\\"\\n        try:\\n            solver_backend = self.plugin_manager.get_cached_solver_backend()\\n            # Solver.user_agent has to be a static or class method\\n            user_agent += f\\\" {solver_backend.user_agent()}\\\"\\n        except Exception as exc:\\n            log.debug(\\n                \\\"User agent could not be fetched from solver class '%s'.\\\",\\n                self.solver,\\n                exc_info=exc,\\n            )\\n        return user_agent\\n\\n    @memoizedproperty\\n    def user_agent(self):\\n        builder = [f\\\"conda/{CONDA_VERSION} requests/{self.requests_version}\\\"]\\n        builder.append(\\\"{}/{}\\\".format(*self.python_implementation_name_version))\\n        builder.append(\\\"{}/{}\\\".format(*self.platform_system_release))\\n        builder.append(\\\"{}/{}\\\".format(*self.os_distribution_name_version))\\n        if self.libc_family_version[0]:\\n            builder.append(\\\"{}/{}\\\".format(*self.libc_family_version))\\n        if self.solver != \\\"classic\\\":\\n            builder.append(self.solver_user_agent())\\n        return \\\" \\\".join(builder)\\n\\n    @contextmanager\\n    def _override(self, key, value):\\n        \\\"\\\"\\\"\\n        TODO: This might be broken in some ways. Unsure what happens if the `old`\\n        value is a property and gets set to a new value. Or if the new value\\n        overrides the validation logic on the underlying ParameterLoader instance.\\n\\n        Investigate and implement in a safer way.\\n        \\\"\\\"\\\"\\n        old = getattr(self, key)\\n        setattr(self, key, value)\\n        try:\\n            yield\\n        finally:\\n            setattr(self, key, old)\\n\\n    @memoizedproperty\\n    def requests_version(self):\\n        # used in User-Agent as \\\"requests/<version>\\\"\\n        # if unable to detect a version we expect \\\"requests/unknown\\\"\\n        try:\\n            from requests import __version__ as requests_version\\n        except ImportError as err:\\n            # ImportError: requests is not installed\\n            log.error(\\\"Unable to import requests: %s\\\", err)\\n            requests_version = \\\"unknown\\\"\\n        except Exception as err:\\n            log.error(\\\"Error importing requests: %s\\\", err)\\n            requests_version = \\\"unknown\\\"\\n        return requests_version\\n\\n    @memoizedproperty\\n    def python_implementation_name_version(self):\\n        # CPython, Jython\\n        # '2.7.14'\\n        return platform.python_implementation(), platform.python_version()\\n\\n    @memoizedproperty\\n    def platform_system_release(self):\\n        # tuple of system name and release version\\n        #\\n        # `uname -s` Linux, Windows, Darwin, Java\\n        #\\n        # `uname -r`\\n        # '17.4.0' for macOS\\n        # '10' or 'NT' for Windows\\n        return platform.system(), platform.release()\\n\\n    @memoizedproperty\\n    def os_distribution_name_version(self):\\n        # tuple of os distribution name and version\\n        # e.g.\\n        #   'debian', '9'\\n        #   'OSX', '10.13.6'\\n        #   'Windows', '10.0.17134'\\n        platform_name = self.platform_system_release[0]\\n        if platform_name == \\\"Linux\\\":\\n            try:\\n                try:\\n                    import distro\\n                except ImportError:\\n                    from .._vendor import distro\\n\\n                distinfo = distro.id(), distro.version(best=True)\\n            except Exception as e:\\n                log.debug(\\\"%r\\\", e, exc_info=True)\\n                distinfo = (\\\"Linux\\\", \\\"unknown\\\")\\n            distribution_name, distribution_version = distinfo[0], distinfo[1]\\n        elif platform_name == \\\"Darwin\\\":\\n            distribution_name = \\\"OSX\\\"\\n            distribution_version = platform.mac_ver()[0]\\n        else:\\n            distribution_name = platform.system()\\n            distribution_version = platform.version()\\n        return distribution_name, distribution_version\\n\\n    @memoizedproperty\\n    def libc_family_version(self):\\n        # tuple of lic_family and libc_version\\n        # None, None if not on Linux\\n        libc_family, libc_version = linux_get_libc_version()\\n        return libc_family, libc_version\\n\\n    @property\\n    @deprecated(\\\"24.3\\\", \\\"24.9\\\")\\n    def cpu_flags(self):\\n        # DANGER: This is rather slow\\n        info = _get_cpu_info()\\n        return info[\\\"flags\\\"]\\n\\n    @memoizedproperty\\n    @unique_sequence_map(unique_key=\\\"backend\\\")\\n    def reporters(self) -> tuple[Mapping[str, str]]:\\n        \\\"\\\"\\\"\\n        Determine the value of reporters based on other settings and the ``self._reporters``\\n        value itself.\\n        \\\"\\\"\\\"\\n        if not self._reporters:\\n            return (\\n                {\\n                    \\\"backend\\\": \\\"json\\\" if self.json else \\\"console\\\",\\n                    \\\"output\\\": \\\"stdout\\\",\\n                    \\\"verbosity\\\": self.verbosity,\\n                    \\\"quiet\\\": self.quiet,\\n                },\\n            )\\n\\n        return self._reporters\\n\\n    @property\\n    def category_map(self):\\n        return {\\n            \\\"Channel Configuration\\\": (\\n                \\\"channels\\\",\\n                \\\"channel_alias\\\",\\n                \\\"channel_settings\\\",\\n                \\\"default_channels\\\",\\n                \\\"override_channels_enabled\\\",\\n                \\\"allowlist_channels\\\",\\n                \\\"custom_channels\\\",\\n                \\\"custom_multichannels\\\",\\n                \\\"migrated_channel_aliases\\\",\\n                \\\"migrated_custom_channels\\\",\\n                \\\"add_anaconda_token\\\",\\n                \\\"allow_non_channel_urls\\\",\\n                \\\"restore_free_channel\\\",\\n                \\\"repodata_fns\\\",\\n                \\\"use_only_tar_bz2\\\",\\n                \\\"repodata_threads\\\",\\n                \\\"fetch_threads\\\",\\n                \\\"experimental\\\",\\n                \\\"no_lock\\\",\\n                \\\"repodata_use_zst\\\",\\n            ),\\n            \\\"Basic Conda Configuration\\\": (  # TODO: Is there a better category name here?\\n                \\\"envs_dirs\\\",\\n                \\\"pkgs_dirs\\\",\\n                \\\"default_threads\\\",\\n            ),\\n            \\\"Network Configuration\\\": (\\n                \\\"client_ssl_cert\\\",\\n                \\\"client_ssl_cert_key\\\",\\n                \\\"local_repodata_ttl\\\",\\n                \\\"offline\\\",\\n                \\\"proxy_servers\\\",\\n                \\\"remote_connect_timeout_secs\\\",\\n                \\\"remote_max_retries\\\",\\n                \\\"remote_backoff_factor\\\",\\n                \\\"remote_read_timeout_secs\\\",\\n                \\\"ssl_verify\\\",\\n            ),\\n            \\\"Solver Configuration\\\": (\\n                \\\"aggressive_update_packages\\\",\\n                \\\"auto_update_conda\\\",\\n                \\\"channel_priority\\\",\\n                \\\"create_default_packages\\\",\\n                \\\"disallowed_packages\\\",\\n                \\\"force_reinstall\\\",\\n                \\\"pinned_packages\\\",\\n                \\\"pip_interop_enabled\\\",\\n                \\\"track_features\\\",\\n                \\\"solver\\\",\\n            ),\\n            \\\"Package Linking and Install-time Configuration\\\": (\\n                \\\"allow_softlinks\\\",\\n                \\\"always_copy\\\",\\n                \\\"always_softlink\\\",\\n                \\\"path_conflict\\\",\\n                \\\"rollback_enabled\\\",\\n                \\\"safety_checks\\\",\\n                \\\"extra_safety_checks\\\",\\n                \\\"signing_metadata_url_base\\\",\\n                \\\"shortcuts\\\",\\n                \\\"shortcuts_only\\\",\\n                \\\"non_admin_enabled\\\",\\n                \\\"separate_format_cache\\\",\\n                \\\"verify_threads\\\",\\n                \\\"execute_threads\\\",\\n            ),\\n            \\\"Conda-build Configuration\\\": (\\n                \\\"bld_path\\\",\\n                \\\"croot\\\",\\n                \\\"anaconda_upload\\\",\\n                \\\"conda_build\\\",\\n            ),\\n            \\\"Output, Prompt, and Flow Control Configuration\\\": (\\n                \\\"always_yes\\\",\\n                \\\"auto_activate_base\\\",\\n                \\\"auto_stack\\\",\\n                \\\"changeps1\\\",\\n                \\\"env_prompt\\\",\\n                \\\"json\\\",\\n                \\\"notify_outdated_conda\\\",\\n                \\\"quiet\\\",\\n                \\\"report_errors\\\",\\n                \\\"show_channel_urls\\\",\\n                \\\"verbosity\\\",\\n                \\\"unsatisfiable_hints\\\",\\n                \\\"unsatisfiable_hints_check_depth\\\",\\n                \\\"number_channel_notices\\\",\\n                \\\"envvars_force_uppercase\\\",\\n            ),\\n            \\\"CLI-only\\\": (\\n                \\\"deps_modifier\\\",\\n                \\\"update_modifier\\\",\\n                \\\"force\\\",\\n                \\\"force_remove\\\",\\n                \\\"clobber\\\",\\n                \\\"dry_run\\\",\\n                \\\"download_only\\\",\\n                \\\"ignore_pinned\\\",\\n                \\\"use_index_cache\\\",\\n                \\\"use_local\\\",\\n            ),\\n            \\\"Hidden and Undocumented\\\": (\\n                \\\"allow_cycles\\\",  # allow cyclical dependencies, or raise\\n                \\\"allow_conda_downgrades\\\",\\n                \\\"add_pip_as_python_dependency\\\",\\n                \\\"debug\\\",\\n                \\\"trace\\\",\\n                \\\"dev\\\",\\n                \\\"default_python\\\",\\n                \\\"enable_private_envs\\\",\\n                \\\"error_upload_url\\\",  # should remain undocumented\\n                \\\"force_32bit\\\",\\n                \\\"root_prefix\\\",\\n                \\\"sat_solver\\\",\\n                \\\"solver_ignore_timestamps\\\",\\n                \\\"subdir\\\",\\n                \\\"subdirs\\\",\\n                # https://conda.io/docs/config.html#disable-updating-of-dependencies-update-dependencies # NOQA\\n                # I don't think this documentation is correct any longer. # NOQA\\n                \\\"target_prefix_override\\\",\\n                # used to override prefix rewriting, for e.g. building docker containers or RPMs  # NOQA\\n                \\\"register_envs\\\",\\n                # whether to add the newly created prefix to ~/.conda/environments.txt\\n                \\\"reporters\\\",\\n            ),\\n            \\\"Plugin Configuration\\\": (\\\"no_plugins\\\",),\\n        }\\n\\n    def get_descriptions(self):\\n        return self.description_map\\n\\n    @memoizedproperty\\n    def description_map(self):\\n        return frozendict(\\n            add_anaconda_token=dals(\\n                \\\"\\\"\\\"\\n                In conjunction with the anaconda command-line client (installed with\\n                `conda install anaconda-client`), and following logging into an Anaconda\\n                Server API site using `anaconda login`, automatically apply a matching\\n                private token to enable access to private packages and channels.\\n                \\\"\\\"\\\"\\n            ),\\n            # add_pip_as_python_dependency=dals(\\n            #     \\\"\\\"\\\"\\n            #     Add pip, wheel and setuptools as dependencies of python. This ensures pip,\\n            #     wheel and setuptools will always be installed any time python is installed.\\n            #     \\\"\\\"\\\"\\n            # ),\\n            aggressive_update_packages=dals(\\n                \\\"\\\"\\\"\\n                A list of packages that, if installed, are always updated to the latest possible\\n                version.\\n                \\\"\\\"\\\"\\n            ),\\n            allow_non_channel_urls=dals(\\n                \\\"\\\"\\\"\\n                Warn, but do not fail, when conda detects a channel url is not a valid channel.\\n                \\\"\\\"\\\"\\n            ),\\n            allow_softlinks=dals(\\n                \\\"\\\"\\\"\\n                When allow_softlinks is True, conda uses hard-links when possible, and soft-links\\n                (symlinks) when hard-links are not possible, such as when installing on a\\n                different filesystem than the one that the package cache is on. When\\n                allow_softlinks is False, conda still uses hard-links when possible, but when it\\n                is not possible, conda copies files. Individual packages can override\\n                this setting, specifying that certain files should never be soft-linked (see the\\n                no_link option in the build recipe documentation).\\n                \\\"\\\"\\\"\\n            ),\\n            always_copy=dals(\\n                \\\"\\\"\\\"\\n                Register a preference that files be copied into a prefix during install rather\\n                than hard-linked.\\n                \\\"\\\"\\\"\\n            ),\\n            always_softlink=dals(\\n                \\\"\\\"\\\"\\n                Register a preference that files be soft-linked (symlinked) into a prefix during\\n                install rather than hard-linked. The link source is the 'pkgs_dir' package cache\\n                from where the package is being linked. WARNING: Using this option can result in\\n                corruption of long-lived conda environments. Package caches are *caches*, which\\n                means there is some churn and invalidation. With this option, the contents of\\n                environments can be switched out (or erased) via operations on other environments.\\n                \\\"\\\"\\\"\\n            ),\\n            always_yes=dals(\\n                \\\"\\\"\\\"\\n                Automatically choose the 'yes' option whenever asked to proceed with a conda\\n                operation, such as when running `conda install`.\\n                \\\"\\\"\\\"\\n            ),\\n            anaconda_upload=dals(\\n                \\\"\\\"\\\"\\n                Automatically upload packages built with conda build to anaconda.org.\\n                \\\"\\\"\\\"\\n            ),\\n            auto_activate_base=dals(\\n                \\\"\\\"\\\"\\n                Automatically activate the base environment during shell initialization.\\n                \\\"\\\"\\\"\\n            ),\\n            auto_update_conda=dals(\\n                \\\"\\\"\\\"\\n                Automatically update conda when a newer or higher priority version is detected.\\n                \\\"\\\"\\\"\\n            ),\\n            auto_stack=dals(\\n                \\\"\\\"\\\"\\n                Implicitly use --stack when using activate if current level of nesting\\n                (as indicated by CONDA_SHLVL environment variable) is less than or equal to\\n                specified value. 0 or false disables automatic stacking, 1 or true enables\\n                it for one level.\\n                \\\"\\\"\\\"\\n            ),\\n            bld_path=dals(\\n                \\\"\\\"\\\"\\n                The location where conda-build will put built packages. Same as 'croot', but\\n                'croot' takes precedence when both are defined. Also used in construction of the\\n                'local' multichannel.\\n                \\\"\\\"\\\"\\n            ),\\n            changeps1=dals(\\n                \\\"\\\"\\\"\\n                When using activate, change the command prompt ($PS1) to include the\\n                activated environment.\\n                \\\"\\\"\\\"\\n            ),\\n            channel_alias=dals(\\n                \\\"\\\"\\\"\\n                The prepended url location to associate with channel names.\\n                \\\"\\\"\\\"\\n            ),\\n            channel_priority=dals(\\n                \\\"\\\"\\\"\\n                Accepts values of 'strict', 'flexible', and 'disabled'. The default value\\n                is 'flexible'. With strict channel priority, packages in lower priority channels\\n                are not considered if a package with the same name appears in a higher\\n                priority channel. With flexible channel priority, the solver may reach into\\n                lower priority channels to fulfill dependencies, rather than raising an\\n                unsatisfiable error. With channel priority disabled, package version takes\\n                precedence, and the configured priority of channels is used only to break ties.\\n                In previous versions of conda, this parameter was configured as either True or\\n                False. True is now an alias to 'flexible'.\\n                \\\"\\\"\\\"\\n            ),\\n            channels=dals(\\n                \\\"\\\"\\\"\\n                The list of conda channels to include for relevant operations.\\n                \\\"\\\"\\\"\\n            ),\\n            channel_settings=dals(\\n                \\\"\\\"\\\"\\n                A list of mappings that allows overriding certain settings for a single channel.\\n                Each list item should include at least the \\\"channel\\\" key and the setting you would\\n                like to override.\\n                \\\"\\\"\\\"\\n            ),\\n            client_ssl_cert=dals(\\n                \\\"\\\"\\\"\\n                A path to a single file containing a private key and certificate (e.g. .pem\\n                file). Alternately, use client_ssl_cert_key in conjunction with client_ssl_cert\\n                for individual files.\\n                \\\"\\\"\\\"\\n            ),\\n            client_ssl_cert_key=dals(\\n                \\\"\\\"\\\"\\n                Used in conjunction with client_ssl_cert for a matching key file.\\n                \\\"\\\"\\\"\\n            ),\\n            # clobber=dals(\\n            #     \\\"\\\"\\\"\\n            #     Allow clobbering of overlapping file paths within packages, and suppress\\n            #     related warnings. Overrides the path_conflict configuration value when\\n            #     set to 'warn' or 'prevent'.\\n            #     \\\"\\\"\\\"\\n            # ),\\n            # TODO: add shortened link to docs for conda_build at See https://conda.io/docs/user-guide/configuration/use-condarc.html#conda-build-configuration  # NOQA\\n            conda_build=dals(\\n                \\\"\\\"\\\"\\n                General configuration parameters for conda-build.\\n                \\\"\\\"\\\"\\n            ),\\n            # TODO: This is a bad parameter name. Consider an alternate.\\n            create_default_packages=dals(\\n                \\\"\\\"\\\"\\n                Packages that are by default added to a newly created environments.\\n                \\\"\\\"\\\"\\n            ),\\n            croot=dals(\\n                \\\"\\\"\\\"\\n                The location where conda-build will put built packages. Same as 'bld_path', but\\n                'croot' takes precedence when both are defined. Also used in construction of the\\n                'local' multichannel.\\n                \\\"\\\"\\\"\\n            ),\\n            custom_channels=dals(\\n                \\\"\\\"\\\"\\n                A map of key-value pairs where the key is a channel name and the value is\\n                a channel location. Channels defined here override the default\\n                'channel_alias' value. The channel name (key) is not included in the channel\\n                location (value).  For example, to override the location of the 'conda-forge'\\n                channel where the url to repodata is\\n                https://anaconda-repo.dev/packages/conda-forge/linux-64/repodata.json, add an\\n                entry 'conda-forge: https://anaconda-repo.dev/packages'.\\n                \\\"\\\"\\\"\\n            ),\\n            custom_multichannels=dals(\\n                \\\"\\\"\\\"\\n                A multichannel is a metachannel composed of multiple channels. The two reserved\\n                multichannels are 'defaults' and 'local'. The 'defaults' multichannel is\\n                customized using the 'default_channels' parameter. The 'local'\\n                multichannel is a list of file:// channel locations where conda-build stashes\\n                successfully-built packages.  Other multichannels can be defined with\\n                custom_multichannels, where the key is the multichannel name and the value is\\n                a list of channel names and/or channel urls.\\n                \\\"\\\"\\\"\\n            ),\\n            default_channels=dals(\\n                \\\"\\\"\\\"\\n                The list of channel names and/or urls used for the 'defaults' multichannel.\\n                \\\"\\\"\\\"\\n            ),\\n            # default_python=dals(\\n            #     \\\"\\\"\\\"\\n            #     specifies the default major & minor version of Python to be used when\\n            #     building packages with conda-build. Also used to determine the major\\n            #     version of Python (2/3) to be used in new environments. Defaults to\\n            #     the version used by conda itself.\\n            #     \\\"\\\"\\\"\\n            # ),\\n            default_threads=dals(\\n                \\\"\\\"\\\"\\n                Threads to use by default for parallel operations.  Default is None,\\n                which allows operations to choose themselves.  For more specific\\n                control, see the other *_threads parameters:\\n                    * repodata_threads - for fetching/loading repodata\\n                    * verify_threads - for verifying package contents in transactions\\n                    * execute_threads - for carrying out the unlinking and linking steps\\n                \\\"\\\"\\\"\\n            ),\\n            disallowed_packages=dals(\\n                \\\"\\\"\\\"\\n                Package specifications to disallow installing. The default is to allow\\n                all packages.\\n                \\\"\\\"\\\"\\n            ),\\n            download_only=dals(\\n                \\\"\\\"\\\"\\n                Solve an environment and ensure package caches are populated, but exit\\n                prior to unlinking and linking packages into the prefix\\n                \\\"\\\"\\\"\\n            ),\\n            envs_dirs=dals(\\n                \\\"\\\"\\\"\\n                The list of directories to search for named environments. When creating a new\\n                named environment, the environment will be placed in the first writable\\n                location.\\n                \\\"\\\"\\\"\\n            ),\\n            env_prompt=dals(\\n                \\\"\\\"\\\"\\n                Template for prompt modification based on the active environment. Currently\\n                supported template variables are '{prefix}', '{name}', and '{default_env}'.\\n                '{prefix}' is the absolute path to the active environment. '{name}' is the\\n                basename of the active environment prefix. '{default_env}' holds the value\\n                of '{name}' if the active environment is a conda named environment ('-n'\\n                flag), or otherwise holds the value of '{prefix}'. Templating uses python's\\n                str.format() method.\\n                \\\"\\\"\\\"\\n            ),\\n            execute_threads=dals(\\n                \\\"\\\"\\\"\\n                Threads to use when performing the unlink/link transaction.  When not set,\\n                defaults to 1.  This step is pretty strongly I/O limited, and you may not\\n                see much benefit here.\\n                \\\"\\\"\\\"\\n            ),\\n            fetch_threads=dals(\\n                \\\"\\\"\\\"\\n                Threads to use when downloading packages.  When not set,\\n                defaults to None, which uses the default ThreadPoolExecutor behavior.\\n                \\\"\\\"\\\"\\n            ),\\n            force_reinstall=dals(\\n                \\\"\\\"\\\"\\n                Ensure that any user-requested package for the current operation is uninstalled\\n                and reinstalled, even if that package already exists in the environment.\\n                \\\"\\\"\\\"\\n            ),\\n            # force=dals(\\n            #     \\\"\\\"\\\"\\n            #     Override any of conda's objections and safeguards for installing packages and\\n            #     potentially breaking environments. Also re-installs the package, even if the\\n            #     package is already installed. Implies --no-deps.\\n            #     \\\"\\\"\\\"\\n            # ),\\n            # force_32bit=dals(\\n            #     \\\"\\\"\\\"\\n            #     CONDA_FORCE_32BIT should only be used when running conda-build (in order\\n            #     to build 32-bit packages on a 64-bit system).  We don't want to mention it\\n            #     in the documentation, because it can mess up a lot of things.\\n            #     \\\"\\\"\\\"\\n            # ),\\n            json=dals(\\n                \\\"\\\"\\\"\\n                Ensure all output written to stdout is structured json.\\n                \\\"\\\"\\\"\\n            ),\\n            local_repodata_ttl=dals(\\n                \\\"\\\"\\\"\\n                For a value of False or 0, always fetch remote repodata (HTTP 304 responses\\n                respected). For a value of True or 1, respect the HTTP Cache-Control max-age\\n                header. Any other positive integer values is the number of seconds to locally\\n                cache repodata before checking the remote server for an update.\\n                \\\"\\\"\\\"\\n            ),\\n            migrated_channel_aliases=dals(\\n                \\\"\\\"\\\"\\n                A list of previously-used channel_alias values. Useful when switching between\\n                different Anaconda Repository instances.\\n                \\\"\\\"\\\"\\n            ),\\n            migrated_custom_channels=dals(\\n                \\\"\\\"\\\"\\n                A map of key-value pairs where the key is a channel name and the value is\\n                the previous location of the channel.\\n                \\\"\\\"\\\"\\n            ),\\n            # no_deps=dals(\\n            #     \\\"\\\"\\\"\\n            #     Do not install, update, remove, or change dependencies. This WILL lead to broken\\n            #     environments and inconsistent behavior. Use at your own risk.\\n            #     \\\"\\\"\\\"\\n            # ),\\n            no_plugins=dals(\\n                \\\"\\\"\\\"\\n                Disable all currently-registered plugins, except built-in conda plugins.\\n                \\\"\\\"\\\"\\n            ),\\n            non_admin_enabled=dals(\\n                \\\"\\\"\\\"\\n                Allows completion of conda's create, install, update, and remove operations, for\\n                non-privileged (non-root or non-administrator) users.\\n                \\\"\\\"\\\"\\n            ),\\n            notify_outdated_conda=dals(\\n                \\\"\\\"\\\"\\n                Notify if a newer version of conda is detected during a create, install, update,\\n                or remove operation.\\n                \\\"\\\"\\\"\\n            ),\\n            offline=dals(\\n                \\\"\\\"\\\"\\n                Restrict conda to cached download content and file:// based urls.\\n                \\\"\\\"\\\"\\n            ),\\n            override_channels_enabled=dals(\\n                \\\"\\\"\\\"\\n                Permit use of the --override-channels command-line flag.\\n                \\\"\\\"\\\"\\n            ),\\n            path_conflict=dals(\\n                \\\"\\\"\\\"\\n                The method by which conda handle's conflicting/overlapping paths during a\\n                create, install, or update operation. The value must be one of 'clobber',\\n                'warn', or 'prevent'. The '--clobber' command-line flag or clobber\\n                configuration parameter overrides path_conflict set to 'prevent'.\\n                \\\"\\\"\\\"\\n            ),\\n            pinned_packages=dals(\\n                \\\"\\\"\\\"\\n                A list of package specs to pin for every environment resolution.\\n                This parameter is in BETA, and its behavior may change in a future release.\\n                \\\"\\\"\\\"\\n            ),\\n            pip_interop_enabled=dals(\\n                \\\"\\\"\\\"\\n                Allow the conda solver to interact with non-conda-installed python packages.\\n                \\\"\\\"\\\"\\n            ),\\n            pkgs_dirs=dals(\\n                \\\"\\\"\\\"\\n                The list of directories where locally-available packages are linked from at\\n                install time. Packages not locally available are downloaded and extracted\\n                into the first writable directory.\\n                \\\"\\\"\\\"\\n            ),\\n            proxy_servers=dals(\\n                \\\"\\\"\\\"\\n                A mapping to enable proxy settings. Keys can be either (1) a scheme://hostname\\n                form, which will match any request to the given scheme and exact hostname, or\\n                (2) just a scheme, which will match requests to that scheme. Values are are\\n                the actual proxy server, and are of the form\\n                'scheme://[user:password@]host[:port]'. The optional 'user:password' inclusion\\n                enables HTTP Basic Auth with your proxy.\\n                \\\"\\\"\\\"\\n            ),\\n            quiet=dals(\\n                \\\"\\\"\\\"\\n                Disable progress bar display and other output.\\n                \\\"\\\"\\\"\\n            ),\\n            reporters=dals(\\n                \\\"\\\"\\\"\\n                A list of mappings that allow the configuration of one or more output streams\\n                (e.g. stdout or file).\\n                \\\"\\\"\\\"\\n            ),\\n            remote_connect_timeout_secs=dals(\\n                \\\"\\\"\\\"\\n                The number seconds conda will wait for your client to establish a connection\\n                to a remote url resource.\\n                \\\"\\\"\\\"\\n            ),\\n            remote_max_retries=dals(\\n                \\\"\\\"\\\"\\n                The maximum number of retries each HTTP connection should attempt.\\n                \\\"\\\"\\\"\\n            ),\\n            remote_backoff_factor=dals(\\n                \\\"\\\"\\\"\\n                The factor determines the time HTTP connection should wait for attempt.\\n                \\\"\\\"\\\"\\n            ),\\n            remote_read_timeout_secs=dals(\\n                \\\"\\\"\\\"\\n                Once conda has connected to a remote resource and sent an HTTP request, the\\n                read timeout is the number of seconds conda will wait for the server to send\\n                a response.\\n                \\\"\\\"\\\"\\n            ),\\n            repodata_threads=dals(\\n                \\\"\\\"\\\"\\n                Threads to use when downloading and reading repodata.  When not set,\\n                defaults to None, which uses the default ThreadPoolExecutor behavior.\\n                \\\"\\\"\\\"\\n            ),\\n            report_errors=dals(\\n                \\\"\\\"\\\"\\n                Opt in, or opt out, of automatic error reporting to core maintainers. Error\\n                reports are anonymous, with only the error stack trace and information given\\n                by `conda info` being sent.\\n                \\\"\\\"\\\"\\n            ),\\n            restore_free_channel=dals(\\n                \\\"\\\"\\\"\\\"\\n                Add the \\\"free\\\" channel back into defaults, behind \\\"main\\\" in priority. The \\\"free\\\"\\n                channel was removed from the collection of default channels in conda 4.7.0.\\n                \\\"\\\"\\\"\\n            ),\\n            rollback_enabled=dals(\\n                \\\"\\\"\\\"\\n                Should any error occur during an unlink/link transaction, revert any disk\\n                mutations made to that point in the transaction.\\n                \\\"\\\"\\\"\\n            ),\\n            safety_checks=dals(\\n                \\\"\\\"\\\"\\n                Enforce available safety guarantees during package installation.\\n                The value must be one of 'enabled', 'warn', or 'disabled'.\\n                \\\"\\\"\\\"\\n            ),\\n            separate_format_cache=dals(\\n                \\\"\\\"\\\"\\n                Treat .tar.bz2 files as different from .conda packages when\\n                filenames are otherwise similar. This defaults to False, so\\n                that your package cache doesn't churn when rolling out the new\\n                package format. If you'd rather not assume that a .tar.bz2 and\\n                .conda from the same place represent the same content, set this\\n                to True.\\n                \\\"\\\"\\\"\\n            ),\\n            extra_safety_checks=dals(\\n                \\\"\\\"\\\"\\n                Spend extra time validating package contents.  Currently, runs sha256 verification\\n                on every file within each package during installation.\\n                \\\"\\\"\\\"\\n            ),\\n            signing_metadata_url_base=dals(\\n                \\\"\\\"\\\"\\n                Base URL for obtaining trust metadata updates (i.e., the `*.root.json` and\\n                `key_mgr.json` files) used to verify metadata and (eventually) package signatures.\\n                \\\"\\\"\\\"\\n            ),\\n            shortcuts=dals(\\n                \\\"\\\"\\\"\\n                Allow packages to create OS-specific shortcuts (e.g. in the Windows Start\\n                Menu) at install time.\\n                \\\"\\\"\\\"\\n            ),\\n            shortcuts_only=dals(\\n                \\\"\\\"\\\"\\n                Create shortcuts only for the specified package names.\\n                \\\"\\\"\\\"\\n            ),\\n            show_channel_urls=dals(\\n                \\\"\\\"\\\"\\n                Show channel URLs when displaying what is going to be downloaded.\\n                \\\"\\\"\\\"\\n            ),\\n            ssl_verify=dals(\\n                \\\"\\\"\\\"\\n                Conda verifies SSL certificates for HTTPS requests, just like a web\\n                browser. By default, SSL verification is enabled, and conda operations will\\n                fail if a required url's certificate cannot be verified. Setting ssl_verify to\\n                False disables certification verification. The value for ssl_verify can also\\n                be (1) a path to a CA bundle file, (2) a path to a directory containing\\n                certificates of trusted CA, or (3) 'truststore' to use the\\n                operating system certificate store.\\n                \\\"\\\"\\\"\\n            ),\\n            track_features=dals(\\n                \\\"\\\"\\\"\\n                A list of features that are tracked by default. An entry here is similar to\\n                adding an entry to the create_default_packages list.\\n                \\\"\\\"\\\"\\n            ),\\n            repodata_fns=dals(\\n                \\\"\\\"\\\"\\n                Specify filenames for repodata fetching. The default is ('current_repodata.json',\\n                'repodata.json'), which tries a subset of the full index containing only the\\n                latest version for each package, then falls back to repodata.json.  You may\\n                want to specify something else to use an alternate index that has been reduced\\n                somehow.\\n                \\\"\\\"\\\"\\n            ),\\n            use_index_cache=dals(\\n                \\\"\\\"\\\"\\n                Use cache of channel index files, even if it has expired.\\n                \\\"\\\"\\\"\\n            ),\\n            use_only_tar_bz2=dals(\\n                \\\"\\\"\\\"\\n                A boolean indicating that only .tar.bz2 conda packages should be downloaded.\\n                This is forced to True if conda-build is installed and older than 3.18.3,\\n                because older versions of conda break when conda feeds it the new file format.\\n                \\\"\\\"\\\"\\n            ),\\n            verbosity=dals(\\n                \\\"\\\"\\\"\\n                Sets output log level. 0 is warn. 1 is info. 2 is debug. 3 is trace.\\n                \\\"\\\"\\\"\\n            ),\\n            verify_threads=dals(\\n                \\\"\\\"\\\"\\n                Threads to use when performing the transaction verification step.  When not set,\\n                defaults to 1.\\n                \\\"\\\"\\\"\\n            ),\\n            allowlist_channels=dals(\\n                \\\"\\\"\\\"\\n                The exclusive list of channels allowed to be used on the system. Use of any\\n                other channels will result in an error. If conda-build channels are to be\\n                allowed, along with the --use-local command line flag, be sure to include the\\n                'local' channel in the list. If the list is empty or left undefined, no\\n                channel exclusions will be enforced.\\n                \\\"\\\"\\\"\\n            ),\\n            unsatisfiable_hints=dals(\\n                \\\"\\\"\\\"\\n                A boolean to determine if conda should find conflicting packages in the case\\n                of a failed install.\\n                \\\"\\\"\\\"\\n            ),\\n            unsatisfiable_hints_check_depth=dals(\\n                \\\"\\\"\\\"\\n                An integer that specifies how many levels deep to search for unsatisfiable\\n                dependencies. If this number is 1 it will complete the unsatisfiable hints\\n                fastest (but perhaps not the most complete). The higher this number, the\\n                longer the generation of the unsat hint will take. Defaults to 3.\\n                \\\"\\\"\\\"\\n            ),\\n            solver=dals(\\n                \\\"\\\"\\\"\\n                A string to choose between the different solver logics implemented in\\n                conda. A solver logic takes care of turning your requested packages into a\\n                list of specs to add and/or remove from a given environment, based on their\\n                dependencies and specified constraints.\\n                \\\"\\\"\\\"\\n            ),\\n            number_channel_notices=dals(\\n                \\\"\\\"\\\"\\n                Sets the number of channel notices to be displayed when running commands\\n                the \\\"install\\\", \\\"create\\\", \\\"update\\\", \\\"env create\\\", and \\\"env update\\\" . Defaults\\n                to 5. In order to completely suppress channel notices, set this to 0.\\n                \\\"\\\"\\\"\\n            ),\\n            experimental=dals(\\n                \\\"\\\"\\\"\\n                List of experimental features to enable.\\n                \\\"\\\"\\\"\\n            ),\\n            no_lock=dals(\\n                \\\"\\\"\\\"\\n                Disable index cache lock (defaults to enabled).\\n                \\\"\\\"\\\"\\n            ),\\n            repodata_use_zst=dals(\\n                \\\"\\\"\\\"\\n                Disable check for `repodata.json.zst`; use `repodata.json` only.\\n                \\\"\\\"\\\"\\n            ),\\n            envvars_force_uppercase=dals(\\n                \\\"\\\"\\\"\\n                Force uppercase for new environment variable names. Defaults to True.\\n                \\\"\\\"\\\"\\n            ),\\n        )\\n\\n\\ndef reset_context(search_path=SEARCH_PATH, argparse_args=None):\\n    global context\\n\\n    # reset plugin config params\\n    remove_all_plugin_settings()\\n\\n    context.__init__(search_path, argparse_args)\\n    context.__dict__.pop(\\\"_Context__conda_build\\\", None)\\n    from ..models.channel import Channel\\n\\n    Channel._reset_state()\\n    # need to import here to avoid circular dependency\\n    return context\\n\\n\\n@contextmanager\\ndef fresh_context(env=None, search_path=SEARCH_PATH, argparse_args=None, **kwargs):\\n    if env or kwargs:\\n        old_env = os.environ.copy()\\n        os.environ.update(env or {})\\n        os.environ.update(kwargs)\\n    yield reset_context(search_path=search_path, argparse_args=argparse_args)\\n    if env or kwargs:\\n        os.environ.clear()\\n        os.environ.update(old_env)\\n        reset_context()\\n\\n\\nclass ContextStackObject:\\n    def __init__(self, search_path=SEARCH_PATH, argparse_args=None):\\n        self.set_value(search_path, argparse_args)\\n\\n    def set_value(self, search_path=SEARCH_PATH, argparse_args=None):\\n        self.search_path = search_path\\n        self.argparse_args = argparse_args\\n\\n    def apply(self):\\n        reset_context(self.search_path, self.argparse_args)\\n\\n\\nclass ContextStack:\\n    def __init__(self):\\n        self._stack = [ContextStackObject() for _ in range(3)]\\n        self._stack_idx = 0\\n        self._last_search_path = None\\n        self._last_argparse_args = None\\n\\n    def push(self, search_path, argparse_args):\\n        self._stack_idx += 1\\n        old_len = len(self._stack)\\n        if self._stack_idx >= old_len:\\n            self._stack.extend([ContextStackObject() for _ in range(old_len)])\\n        self._stack[self._stack_idx].set_value(search_path, argparse_args)\\n        self.apply()\\n\\n    def apply(self):\\n        if (\\n            self._last_search_path != self._stack[self._stack_idx].search_path\\n            or self._last_argparse_args != self._stack[self._stack_idx].argparse_args\\n        ):\\n            # Expensive:\\n            self._stack[self._stack_idx].apply()\\n            self._last_search_path = self._stack[self._stack_idx].search_path\\n            self._last_argparse_args = self._stack[self._stack_idx].argparse_args\\n\\n    def pop(self):\\n        self._stack_idx -= 1\\n        self._stack[self._stack_idx].apply()\\n\\n    def replace(self, search_path, argparse_args):\\n        self._stack[self._stack_idx].set_value(search_path, argparse_args)\\n        self._stack[self._stack_idx].apply()\\n\\n\\ncontext_stack = ContextStack()\\n\\n\\ndef stack_context(pushing, search_path=SEARCH_PATH, argparse_args=None):\\n    if pushing:\\n        # Fast\\n        context_stack.push(search_path, argparse_args)\\n    else:\\n        # Slow\\n        context_stack.pop()\\n\\n\\n# Default means \\\"The configuration when there are no condarc files present\\\". It is\\n# all the settings and defaults that are built in to the code and *not* the default\\n# value of search_path=SEARCH_PATH. It means search_path=().\\ndef stack_context_default(pushing, argparse_args=None):\\n    return stack_context(pushing, search_path=(), argparse_args=argparse_args)\\n\\n\\ndef replace_context(pushing=None, search_path=SEARCH_PATH, argparse_args=None):\\n    # pushing arg intentionally not used here, but kept for API compatibility\\n    return context_stack.replace(search_path, argparse_args)\\n\\n\\ndef replace_context_default(pushing=None, argparse_args=None):\\n    # pushing arg intentionally not used here, but kept for API compatibility\\n    return context_stack.replace(search_path=(), argparse_args=argparse_args)\\n\\n\\n# Tests that want to only declare 'I support the project-wide default for how to\\n# manage stacking of contexts'. Tests that are known to be careful with context\\n# can use `replace_context_default` which might be faster, though it should\\n# be a stated goal to set conda_tests_ctxt_mgmt_def_pol to replace_context_default\\n# and not to stack_context_default.\\nconda_tests_ctxt_mgmt_def_pol = replace_context_default\\n\\n\\n@deprecated(\\\"24.3\\\", \\\"24.9\\\")\\n@lru_cache(maxsize=None)\\ndef _get_cpu_info():\\n    # DANGER: This is rather slow\\n    from .._vendor.cpuinfo import get_cpu_info\\n\\n    return frozendict(get_cpu_info())\\n\\n\\ndef env_name(prefix):\\n    # counter part to `locate_prefix_by_name()` below\\n    if not prefix:\\n        return None\\n    if paths_equal(prefix, context.root_prefix):\\n        return ROOT_ENV_NAME\\n    maybe_envs_dir, maybe_name = path_split(prefix)\\n    for envs_dir in context.envs_dirs:\\n        if paths_equal(envs_dir, maybe_envs_dir):\\n            return maybe_name\\n    return prefix\\n\\n\\ndef locate_prefix_by_name(name, envs_dirs=None):\\n    \\\"\\\"\\\"Find the location of a prefix given a conda env name.  If the location does not exist, an\\n    error is raised.\\n    \\\"\\\"\\\"\\n    assert name\\n    if name in (ROOT_ENV_NAME, \\\"root\\\"):\\n        return context.root_prefix\\n    if envs_dirs is None:\\n        envs_dirs = context.envs_dirs\\n    for envs_dir in envs_dirs:\\n        if not isdir(envs_dir):\\n            continue\\n        prefix = join(envs_dir, name)\\n        if isdir(prefix):\\n            return abspath(prefix)\\n\\n    from ..exceptions import EnvironmentNameNotFound\\n\\n    raise EnvironmentNameNotFound(name)\\n\\n\\ndef validate_prefix_name(prefix_name: str, ctx: Context, allow_base=True) -> str:\\n    \\\"\\\"\\\"Run various validations to make sure prefix_name is valid\\\"\\\"\\\"\\n    from ..exceptions import CondaValueError\\n\\n    if PREFIX_NAME_DISALLOWED_CHARS.intersection(prefix_name):\\n        raise CondaValueError(\\n            dals(\\n                f\\\"\\\"\\\"\\n                Invalid environment name: {prefix_name!r}\\n                Characters not allowed: {PREFIX_NAME_DISALLOWED_CHARS}\\n                If you are specifying a path to an environment, the `-p`\\n                flag should be used instead.\\n                \\\"\\\"\\\"\\n            )\\n        )\\n\\n    if prefix_name in (ROOT_ENV_NAME, \\\"root\\\"):\\n        if allow_base:\\n            return ctx.root_prefix\\n        else:\\n            raise CondaValueError(\\n                \\\"Use of 'base' as environment name is not allowed here.\\\"\\n            )\\n\\n    else:\\n        from ..exceptions import EnvironmentNameNotFound\\n\\n        try:\\n            return locate_prefix_by_name(prefix_name)\\n        except EnvironmentNameNotFound:\\n            return join(_first_writable_envs_dir(), prefix_name)\\n\\n\\ndef determine_target_prefix(ctx, args=None):\\n    \\\"\\\"\\\"Get the prefix to operate in.  The prefix may not yet exist.\\n\\n    Args:\\n        ctx: the context of conda\\n        args: the argparse args from the command line\\n\\n    Returns: the prefix\\n    Raises: CondaEnvironmentNotFoundError if the prefix is invalid\\n    \\\"\\\"\\\"\\n    argparse_args = args or ctx._argparse_args\\n    try:\\n        prefix_name = argparse_args.name\\n    except AttributeError:\\n        prefix_name = None\\n    try:\\n        prefix_path = argparse_args.prefix\\n    except AttributeError:\\n        prefix_path = None\\n\\n    if prefix_name is not None and not prefix_name.strip():  # pragma: no cover\\n        from ..exceptions import ArgumentError\\n\\n        raise ArgumentError(\\\"Argument --name requires a value.\\\")\\n\\n    if prefix_path is not None and not prefix_path.strip():  # pragma: no cover\\n        from ..exceptions import ArgumentError\\n\\n        raise ArgumentError(\\\"Argument --prefix requires a value.\\\")\\n\\n    if prefix_name is None and prefix_path is None:\\n        return ctx.default_prefix\\n    elif prefix_path is not None:\\n        return expand(prefix_path)\\n    else:\\n        return validate_prefix_name(prefix_name, ctx=ctx)\\n\\n\\ndef _first_writable_envs_dir():\\n    # Calling this function will *create* an envs directory if one does not already\\n    # exist. Any caller should intend to *use* that directory for *writing*, not just reading.\\n    for envs_dir in context.envs_dirs:\\n        if envs_dir == os.devnull:\\n            continue\\n\\n        # The magic file being used here could change in the future.  Don't write programs\\n        # outside this code base that rely on the presence of this file.\\n        # This value is duplicated in conda.gateways.disk.create.create_envs_directory().\\n        envs_dir_magic_file = join(envs_dir, \\\".conda_envs_dir_test\\\")\\n\\n        if isfile(envs_dir_magic_file):\\n            try:\\n                open(envs_dir_magic_file, \\\"a\\\").close()\\n                return envs_dir\\n            except OSError:\\n                log.log(TRACE, \\\"Tried envs_dir but not writable: %s\\\", envs_dir)\\n        else:\\n            from ..gateways.disk.create import create_envs_directory\\n\\n            was_created = create_envs_directory(envs_dir)\\n            if was_created:\\n                return envs_dir\\n\\n    from ..exceptions import NoWritableEnvsDirError\\n\\n    raise NoWritableEnvsDirError(context.envs_dirs)\\n\\n\\ndef get_plugin_config_data(\\n    data: dict[Path, dict[str, RawParameter]],\\n) -> dict[Path, dict[str, RawParameter]]:\\n    \\\"\\\"\\\"\\n    This is used to move everything under the key \\\"plugins\\\" from the provided dictionary\\n    to the top level of the returned dictionary. The returned dictionary is then passed\\n    to :class:`PluginConfig`.\\n    \\\"\\\"\\\"\\n    new_data = defaultdict(dict)\\n\\n    for source, config in data.items():\\n        if plugin_data := config.get(\\\"plugins\\\"):\\n            plugin_data_value = plugin_data.value(None)\\n\\n            if not isinstance(plugin_data_value, Mapping):\\n                continue\\n\\n            for param_name, raw_param in plugin_data_value.items():\\n                new_data[source][param_name] = raw_param\\n\\n        elif source == EnvRawParameter.source:\\n            for env_var, raw_param in config.items():\\n                if env_var.startswith(\\\"plugins_\\\"):\\n                    _, param_name = env_var.split(\\\"plugins_\\\")\\n                    new_data[source][param_name] = raw_param\\n\\n    return new_data\\n\\n\\nclass PluginConfig(metaclass=ConfigurationType):\\n    \\\"\\\"\\\"\\n    Class used to hold settings for conda plugins.\\n\\n    The object created by this class should only be accessed via\\n    :class:`conda.base.context.Context.plugins`.\\n\\n    When this class is updated via the :func:`add_plugin_setting` function it adds new setting\\n    properties which can be accessed later via the context object.\\n\\n    We currently call that function in\\n    :meth:`conda.plugins.manager.CondaPluginManager.load_settings`.\\n    because ``CondaPluginManager`` has access to all registered plugin settings via the settings\\n    plugin hook.\\n    \\\"\\\"\\\"\\n\\n    def __init__(self, data):\\n        self._cache_ = {}\\n        self.raw_data = get_plugin_config_data(data)\\n\\n\\ndef add_plugin_setting(name: str, parameter: Parameter, aliases: tuple[str, ...] = ()):\\n    \\\"\\\"\\\"\\n    Adds a setting to the :class:`PluginConfig` class\\n    \\\"\\\"\\\"\\n    PluginConfig.parameter_names = PluginConfig.parameter_names + (name,)\\n    loader = ParameterLoader(parameter, aliases=aliases)\\n    name = loader._set_name(name)\\n    setattr(PluginConfig, name, loader)\\n\\n\\ndef remove_all_plugin_settings() -> None:\\n    \\\"\\\"\\\"\\n    Removes all attached settings from the :class:`PluginConfig` class\\n    \\\"\\\"\\\"\\n    for name in PluginConfig.parameter_names:\\n        try:\\n            delattr(PluginConfig, name)\\n        except AttributeError:\\n            continue\\n\\n    PluginConfig.parameter_names = tuple()\\n\\n\\ntry:\\n    context = Context((), None)\\nexcept ConfigurationLoadError as e:  # pragma: no cover\\n    print(repr(e), file=sys.stderr)\\n    # Exception handler isn't loaded so use sys.exit\\n    sys.exit(1)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Base exceptions.\\\"\\\"\\\"\\n\\nfrom ..deprecations import deprecated\\n\\ndeprecated.module(\\\"24.3\\\", \\\"24.9\\\", addendum=\\\"Nothing to import.\\\")\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"\\nCode in ``conda.base`` is the lowest level of the application stack.  It is loaded and executed\\nvirtually every time the application is executed. Any code within, and any of its imports, must\\nbe highly performant.\\n\\nConda modules importable from ``conda.base`` are\\n\\n- ``conda._vendor``\\n- ``conda.base``\\n- ``conda.common``\\n\\nModules prohibited from importing ``conda.base`` are:\\n\\n- ``conda._vendor``\\n- ``conda.common``\\n\\nAll other ``conda`` modules may import from ``conda.base``.\\n\\\"\\\"\\\"\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Topological sorting implementation.\\\"\\\"\\\"\\n\\nfrom functools import reduce as _reduce\\nfrom logging import getLogger\\n\\nlog = getLogger(__name__)\\n\\n\\ndef _toposort(data):\\n    \\\"\\\"\\\"Dependencies are expressed as a dictionary whose keys are items\\n    and whose values are a set of dependent items. Output is a list of\\n    sets in topological order. The first set consists of items with no\\n    dependences, each subsequent set consists of items that depend upon\\n    items in the preceding sets.\\n    \\\"\\\"\\\"\\n    # Special case empty input.\\n    if len(data) == 0:\\n        return\\n\\n    # Ignore self dependencies.\\n    for k, v in data.items():\\n        v.discard(k)\\n    # Find all items that don't depend on anything.\\n    extra_items_in_deps = _reduce(set.union, data.values()) - set(data.keys())\\n    # Add empty dependences where needed.\\n    data.update({item: set() for item in extra_items_in_deps})\\n    while True:\\n        ordered = sorted({item for item, dep in data.items() if len(dep) == 0})\\n        if not ordered:\\n            break\\n\\n        for item in ordered:\\n            yield item\\n            data.pop(item, None)\\n\\n        for dep in sorted(data.values()):\\n            dep -= set(ordered)\\n\\n    if len(data) != 0:\\n        from ..exceptions import CondaValueError\\n\\n        msg = \\\"Cyclic dependencies exist among these items: {}\\\"\\n        raise CondaValueError(msg.format(\\\" -> \\\".join(repr(x) for x in data.keys())))\\n\\n\\ndef pop_key(data):\\n    \\\"\\\"\\\"\\n    Pop an item from the graph that has the fewest dependencies in the case of a tie\\n    The winners will be sorted alphabetically\\n    \\\"\\\"\\\"\\n    items = sorted(data.items(), key=lambda item: (len(item[1]), item[0]))\\n    key = items[0][0]\\n\\n    data.pop(key)\\n\\n    for dep in data.values():\\n        dep.discard(key)\\n\\n    return key\\n\\n\\ndef _safe_toposort(data):\\n    \\\"\\\"\\\"Dependencies are expressed as a dictionary whose keys are items\\n    and whose values are a set of dependent items. Output is a list of\\n    sets in topological order. The first set consists of items with no\\n    dependencies, each subsequent set consists of items that depend upon\\n    items in the preceding sets.\\n    \\\"\\\"\\\"\\n    # Special case empty input.\\n    if len(data) == 0:\\n        return\\n\\n    t = _toposort(data)\\n\\n    while True:\\n        try:\\n            value = next(t)\\n            yield value\\n        except ValueError as err:\\n            log.debug(err.args[0])\\n\\n            if not data:\\n                return  # pragma: nocover\\n\\n            yield pop_key(data)\\n\\n            t = _toposort(data)\\n\\n            continue\\n        except StopIteration:\\n            return\\n\\n\\ndef toposort(data, safe=True):\\n    data = {k: set(v) for k, v in data.items()}\\n\\n    if \\\"python\\\" in data:\\n        # Special case: Remove circular dependency between python and pip,\\n        # to ensure python is always installed before anything that needs it.\\n        # For more details:\\n        # - https://github.com/conda/conda/issues/1152\\n        # - https://github.com/conda/conda/pull/1154\\n        # - https://github.com/conda/conda-build/issues/401\\n        # - https://github.com/conda/conda/pull/1614\\n        data[\\\"python\\\"].discard(\\\"pip\\\")\\n\\n    if safe:\\n        return list(_safe_toposort(data))\\n    else:\\n        return list(_toposort(data))\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Replacements for parts of the toolz library.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport collections\\nimport itertools\\nfrom typing import TYPE_CHECKING\\n\\nif TYPE_CHECKING:\\n    from typing import Any, Generator, Sequence\\n\\n\\ndef groupby_to_dict(keyfunc, sequence):\\n    \\\"\\\"\\\"A `toolz`-style groupby implementation.\\n\\n    Returns a dictionary of { key: [group] } instead of iterators.\\n    \\\"\\\"\\\"\\n    result = collections.defaultdict(list)\\n    for key, group in itertools.groupby(sequence, keyfunc):\\n        result[key].extend(group)\\n    return dict(result)\\n\\n\\ndef unique(sequence: Sequence[Any]) -> Generator[Any, None, None]:\\n    \\\"\\\"\\\"A `toolz` inspired `unique` implementation.\\n\\n    Returns a generator of unique elements in the sequence\\n    \\\"\\\"\\\"\\n    seen: set[Any] = set()\\n    yield from (\\n        # seen.add always returns None so we will always return element\\n        seen.add(element) or element\\n        for element in sequence\\n        # only pass along novel elements\\n        if element not in seen\\n    )\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Intercept signals and handle them gracefully.\\\"\\\"\\\"\\n\\nimport signal\\nimport threading\\nfrom contextlib import contextmanager\\nfrom logging import getLogger\\n\\nlog = getLogger(__name__)\\n\\nINTERRUPT_SIGNALS = (\\n    \\\"SIGABRT\\\",\\n    \\\"SIGINT\\\",\\n    \\\"SIGTERM\\\",\\n    \\\"SIGQUIT\\\",\\n    \\\"SIGBREAK\\\",\\n)\\n\\n\\ndef get_signal_name(signum):\\n    \\\"\\\"\\\"\\n    Examples:\\n        >>> from signal import SIGINT\\n        >>> get_signal_name(SIGINT)\\n        'SIGINT'\\n\\n    \\\"\\\"\\\"\\n    return next(\\n        (\\n            k\\n            for k, v in signal.__dict__.items()\\n            if v == signum and k.startswith(\\\"SIG\\\") and not k.startswith(\\\"SIG_\\\")\\n        ),\\n        None,\\n    )\\n\\n\\n@contextmanager\\ndef signal_handler(handler):\\n    # TODO: test and fix windows\\n    #   https://danielkaes.wordpress.com/2009/06/04/how-to-catch-kill-events-with-python/\\n    _thread_local = threading.local()\\n    _thread_local.previous_handlers = []\\n    for signame in INTERRUPT_SIGNALS:\\n        sig = getattr(signal, signame, None)\\n        if sig:\\n            log.debug(\\\"registering handler for %s\\\", signame)\\n            try:\\n                prev_handler = signal.signal(sig, handler)\\n                _thread_local.previous_handlers.append((sig, prev_handler))\\n            except ValueError as e:  # pragma: no cover\\n                # ValueError: signal only works in main thread\\n                log.debug(\\\"%r\\\", e)\\n    try:\\n        yield\\n    finally:\\n        standard_handlers = signal.SIG_IGN, signal.SIG_DFL\\n        for sig, previous_handler in _thread_local.previous_handlers:\\n            if callable(previous_handler) or previous_handler in standard_handlers:\\n                log.debug(\\\"de-registering handler for %s\\\", sig)\\n                signal.signal(sig, previous_handler)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Common I/O utilities.\\\"\\\"\\\"\\n\\nimport json\\nimport logging\\nimport os\\nimport signal\\nimport sys\\nfrom collections import defaultdict\\nfrom concurrent.futures import Executor, Future, ThreadPoolExecutor, _base, as_completed\\nfrom concurrent.futures.thread import _WorkItem\\nfrom contextlib import contextmanager\\nfrom enum import Enum\\nfrom errno import EPIPE, ESHUTDOWN\\nfrom functools import partial, wraps\\nfrom io import BytesIO, StringIO\\nfrom itertools import cycle\\nfrom logging import CRITICAL, WARN, Formatter, StreamHandler, getLogger\\nfrom os.path import dirname, isdir, isfile, join\\nfrom threading import Event, Lock, RLock, Thread\\nfrom time import sleep, time\\n\\nfrom ..auxlib.decorators import memoizemethod\\nfrom ..auxlib.logz import NullHandler\\nfrom ..auxlib.type_coercion import boolify\\nfrom .compat import encode_environment, on_win\\nfrom .constants import NULL\\nfrom .path import expand\\n\\nlog = getLogger(__name__)\\nIS_INTERACTIVE = hasattr(sys.stdout, \\\"isatty\\\") and sys.stdout.isatty()\\n\\n\\nclass DeltaSecondsFormatter(Formatter):\\n    \\\"\\\"\\\"\\n    Logging formatter with additional attributes for run time logging.\\n\\n    Attributes:\\n      `delta_secs`:\\n        Elapsed seconds since last log/format call (or creation of logger).\\n      `relative_created_secs`:\\n        Like `relativeCreated`, time relative to the initialization of the\\n        `logging` module but conveniently scaled to seconds as a `float` value.\\n    \\\"\\\"\\\"\\n\\n    def __init__(self, fmt=None, datefmt=None):\\n        self.prev_time = time()\\n        super().__init__(fmt=fmt, datefmt=datefmt)\\n\\n    def format(self, record):\\n        now = time()\\n        prev_time = self.prev_time\\n        self.prev_time = max(self.prev_time, now)\\n        record.delta_secs = now - prev_time\\n        record.relative_created_secs = record.relativeCreated / 1000\\n        return super().format(record)\\n\\n\\nif boolify(os.environ.get(\\\"CONDA_TIMED_LOGGING\\\")):\\n    _FORMATTER = DeltaSecondsFormatter(\\n        \\\"%(relative_created_secs) 7.2f %(delta_secs) 7.2f \\\"\\n        \\\"%(levelname)s %(name)s:%(funcName)s(%(lineno)d): %(message)s\\\"\\n    )\\nelse:\\n    _FORMATTER = Formatter(\\n        \\\"%(levelname)s %(name)s:%(funcName)s(%(lineno)d): %(message)s\\\"\\n    )\\n\\n\\ndef dashlist(iterable, indent=2):\\n    return \\\"\\\".join(\\\"\\\\n\\\" + \\\" \\\" * indent + \\\"- \\\" + str(x) for x in iterable)\\n\\n\\nclass ContextDecorator:\\n    \\\"\\\"\\\"Base class for a context manager class (implementing __enter__() and __exit__()) that also\\n    makes it a decorator.\\n    \\\"\\\"\\\"\\n\\n    # TODO: figure out how to improve this pattern so e.g. swallow_broken_pipe doesn't have to be instantiated  # NOQA\\n\\n    def __call__(self, f):\\n        @wraps(f)\\n        def decorated(*args, **kwds):\\n            with self:\\n                return f(*args, **kwds)\\n\\n        return decorated\\n\\n\\nclass SwallowBrokenPipe(ContextDecorator):\\n    # Ignore BrokenPipeError and errors related to stdout or stderr being\\n    # closed by a downstream program.\\n\\n    def __enter__(self):\\n        pass\\n\\n    def __exit__(self, exc_type, exc_val, exc_tb):\\n        if (\\n            exc_val\\n            and isinstance(exc_val, EnvironmentError)\\n            and getattr(exc_val, \\\"errno\\\", None)\\n            and exc_val.errno in (EPIPE, ESHUTDOWN)\\n        ):\\n            return True\\n\\n\\nswallow_broken_pipe = SwallowBrokenPipe()\\n\\n\\nclass CaptureTarget(Enum):\\n    \\\"\\\"\\\"Constants used for contextmanager captured.\\n\\n    Used similarly like the constants PIPE, STDOUT for stdlib's subprocess.Popen.\\n    \\\"\\\"\\\"\\n\\n    STRING = -1\\n    STDOUT = -2\\n\\n\\n@contextmanager\\ndef env_vars(var_map=None, callback=None, stack_callback=None):\\n    if var_map is None:\\n        var_map = {}\\n\\n    new_var_map = encode_environment(var_map)\\n    saved_vars = {}\\n    for name, value in new_var_map.items():\\n        saved_vars[name] = os.environ.get(name, NULL)\\n        os.environ[name] = value\\n    try:\\n        if callback:\\n            callback()\\n        if stack_callback:\\n            stack_callback(True)\\n        yield\\n    finally:\\n        for name, value in saved_vars.items():\\n            if value is NULL:\\n                del os.environ[name]\\n            else:\\n                os.environ[name] = value\\n        if callback:\\n            callback()\\n        if stack_callback:\\n            stack_callback(False)\\n\\n\\n@contextmanager\\ndef env_var(name, value, callback=None, stack_callback=None):\\n    # Maybe, but in env_vars, not here:\\n    #    from .compat import ensure_fs_path_encoding\\n    #    d = dict({name: ensure_fs_path_encoding(value)})\\n    d = {name: value}\\n    with env_vars(d, callback=callback, stack_callback=stack_callback) as es:\\n        yield es\\n\\n\\n@contextmanager\\ndef env_unmodified(callback=None):\\n    with env_vars(callback=callback) as es:\\n        yield es\\n\\n\\n@contextmanager\\ndef captured(stdout=CaptureTarget.STRING, stderr=CaptureTarget.STRING):\\n    r\\\"\\\"\\\"Capture outputs of sys.stdout and sys.stderr.\\n\\n    If stdout is STRING, capture sys.stdout as a string,\\n    if stdout is None, do not capture sys.stdout, leaving it untouched,\\n    otherwise redirect sys.stdout to the file-like object given by stdout.\\n\\n    Behave correspondingly for stderr with the exception that if stderr is STDOUT,\\n    redirect sys.stderr to stdout target and set stderr attribute of yielded object to None.\\n\\n    .. code-block:: pycon\\n\\n       >>> from conda.common.io import captured\\n       >>> with captured() as c:\\n       ...     print(\\\"hello world!\\\")\\n       ...\\n       >>> c.stdout\\n       'hello world!\\\\n'\\n\\n    Args:\\n        stdout: capture target for sys.stdout, one of STRING, None, or file-like object\\n        stderr: capture target for sys.stderr, one of STRING, STDOUT, None, or file-like object\\n\\n    Yields:\\n        CapturedText: has attributes stdout, stderr which are either strings, None or the\\n            corresponding file-like function argument.\\n    \\\"\\\"\\\"\\n\\n    def write_wrapper(self, to_write):\\n        # NOTE: This function is not thread-safe.  Using within multi-threading may cause spurious\\n        # behavior of not returning sys.stdout and sys.stderr back to their 'proper' state\\n        # This may have to deal with a *lot* of text.\\n        if hasattr(self, \\\"mode\\\") and \\\"b\\\" in self.mode:\\n            wanted = bytes\\n        elif isinstance(self, BytesIO):\\n            wanted = bytes\\n        else:\\n            wanted = str\\n        if not isinstance(to_write, wanted):\\n            if hasattr(to_write, \\\"decode\\\"):\\n                decoded = to_write.decode(\\\"utf-8\\\")\\n                self.old_write(decoded)\\n            elif hasattr(to_write, \\\"encode\\\"):\\n                b = to_write.encode(\\\"utf-8\\\")\\n                self.old_write(b)\\n        else:\\n            self.old_write(to_write)\\n\\n    class CapturedText:\\n        pass\\n\\n    # sys.stdout.write(u'unicode out')\\n    # sys.stdout.write(bytes('bytes out', encoding='utf-8'))\\n    # sys.stdout.write(str('str out'))\\n    saved_stdout, saved_stderr = sys.stdout, sys.stderr\\n    if stdout == CaptureTarget.STRING:\\n        outfile = StringIO()\\n        outfile.old_write = outfile.write\\n        outfile.write = partial(write_wrapper, outfile)\\n        sys.stdout = outfile\\n    else:\\n        outfile = stdout\\n        if outfile is not None:\\n            sys.stdout = outfile\\n    if stderr == CaptureTarget.STRING:\\n        errfile = StringIO()\\n        errfile.old_write = errfile.write\\n        errfile.write = partial(write_wrapper, errfile)\\n        sys.stderr = errfile\\n    elif stderr == CaptureTarget.STDOUT:\\n        sys.stderr = errfile = outfile\\n    else:\\n        errfile = stderr\\n        if errfile is not None:\\n            sys.stderr = errfile\\n    c = CapturedText()\\n    log.debug(\\\"overtaking stderr and stdout\\\")\\n    try:\\n        yield c\\n    finally:\\n        if stdout == CaptureTarget.STRING:\\n            c.stdout = outfile.getvalue()\\n        else:\\n            c.stdout = outfile\\n        if stderr == CaptureTarget.STRING:\\n            c.stderr = errfile.getvalue()\\n        elif stderr == CaptureTarget.STDOUT:\\n            c.stderr = None\\n        else:\\n            c.stderr = errfile\\n        sys.stdout, sys.stderr = saved_stdout, saved_stderr\\n        log.debug(\\\"stderr and stdout yielding back\\\")\\n\\n\\n@contextmanager\\ndef argv(args_list):\\n    saved_args = sys.argv\\n    sys.argv = args_list\\n    try:\\n        yield\\n    finally:\\n        sys.argv = saved_args\\n\\n\\n@contextmanager\\ndef _logger_lock():\\n    logging._acquireLock()\\n    try:\\n        yield\\n    finally:\\n        logging._releaseLock()\\n\\n\\n@contextmanager\\ndef disable_logger(logger_name):\\n    logr = getLogger(logger_name)\\n    _lvl, _dsbld, _prpgt = logr.level, logr.disabled, logr.propagate\\n    null_handler = NullHandler()\\n    with _logger_lock():\\n        logr.addHandler(null_handler)\\n        logr.setLevel(CRITICAL + 1)\\n        logr.disabled, logr.propagate = True, False\\n    try:\\n        yield\\n    finally:\\n        with _logger_lock():\\n            logr.removeHandler(null_handler)  # restore list logr.handlers\\n            logr.level, logr.disabled = _lvl, _dsbld\\n            logr.propagate = _prpgt\\n\\n\\n@contextmanager\\ndef stderr_log_level(level, logger_name=None):\\n    logr = getLogger(logger_name)\\n    _hndlrs, _lvl, _dsbld, _prpgt = (\\n        logr.handlers,\\n        logr.level,\\n        logr.disabled,\\n        logr.propagate,\\n    )\\n    handler = StreamHandler(sys.stderr)\\n    handler.name = \\\"stderr\\\"\\n    handler.setLevel(level)\\n    handler.setFormatter(_FORMATTER)\\n    with _logger_lock():\\n        logr.setLevel(level)\\n        logr.handlers, logr.disabled, logr.propagate = [], False, False\\n        logr.addHandler(handler)\\n        logr.setLevel(level)\\n    try:\\n        yield\\n    finally:\\n        with _logger_lock():\\n            logr.handlers, logr.level, logr.disabled = _hndlrs, _lvl, _dsbld\\n            logr.propagate = _prpgt\\n\\n\\ndef attach_stderr_handler(\\n    level=WARN,\\n    logger_name=None,\\n    propagate=False,\\n    formatter=None,\\n    filters=None,\\n):\\n    \\\"\\\"\\\"Attach a new `stderr` handler to the given logger and configure both.\\n\\n    This function creates a new StreamHandler that writes to `stderr` and attaches it\\n    to the logger given by `logger_name` (which maybe `None`, in which case the root\\n    logger is used). If the logger already has a handler by the name of `stderr`, it is\\n    removed first.\\n\\n    The given `level` is set **for the handler**, not for the logger; however, this\\n    function also sets the level of the given logger to the minimum of its current\\n    effective level and the new handler level, ensuring that the handler will receive the\\n    required log records, while minimizing the number of unnecessary log events. It also\\n    sets the loggers `propagate` property according to the `propagate` argument.\\n    The `formatter` argument can be used to set the formatter of the handler.\\n    \\\"\\\"\\\"\\n    # get old stderr logger\\n    logr = getLogger(logger_name)\\n    old_stderr_handler = next(\\n        (handler for handler in logr.handlers if handler.name == \\\"stderr\\\"), None\\n    )\\n\\n    # create new stderr logger\\n    new_stderr_handler = StreamHandler(sys.stderr)\\n    new_stderr_handler.name = \\\"stderr\\\"\\n    new_stderr_handler.setLevel(level)\\n    new_stderr_handler.setFormatter(formatter or _FORMATTER)\\n    for filter_ in filters or ():\\n        new_stderr_handler.addFilter(filter_)\\n\\n    # do the switch\\n    with _logger_lock():\\n        if old_stderr_handler:\\n            logr.removeHandler(old_stderr_handler)\\n        logr.addHandler(new_stderr_handler)\\n        if level < logr.getEffectiveLevel():\\n            logr.setLevel(level)\\n        logr.propagate = propagate\\n\\n\\ndef timeout(timeout_secs, func, *args, default_return=None, **kwargs):\\n    \\\"\\\"\\\"Enforce a maximum time for a callable to complete.\\n    Not yet implemented on Windows.\\n    \\\"\\\"\\\"\\n    if on_win:\\n        # Why does Windows have to be so difficult all the time? Kind of gets old.\\n        # Guess we'll bypass Windows timeouts for now.\\n        try:\\n            return func(*args, **kwargs)\\n        except KeyboardInterrupt:  # pragma: no cover\\n            return default_return\\n    else:\\n\\n        class TimeoutException(Exception):\\n            pass\\n\\n        def interrupt(signum, frame):\\n            raise TimeoutException()\\n\\n        signal.signal(signal.SIGALRM, interrupt)\\n        signal.alarm(timeout_secs)\\n\\n        try:\\n            ret = func(*args, **kwargs)\\n            signal.alarm(0)\\n            return ret\\n        except (TimeoutException, KeyboardInterrupt):  # pragma: no cover\\n            return default_return\\n\\n\\nclass Spinner:\\n    \\\"\\\"\\\"\\n    Args:\\n        message (str):\\n            A message to prefix the spinner with. The string ': ' is automatically appended.\\n        enabled (bool):\\n            If False, usage is a no-op.\\n        json (bool):\\n           If True, will not output non-json to stdout.\\n\\n    \\\"\\\"\\\"\\n\\n    # spinner_cycle = cycle(\\\"⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏\\\")\\n    spinner_cycle = cycle(\\\"/-\\\\\\\\|\\\")\\n\\n    def __init__(self, message, enabled=True, json=False, fail_message=\\\"failed\\\\n\\\"):\\n        self.message = message\\n        self.enabled = enabled\\n        self.json = json\\n\\n        self._stop_running = Event()\\n        self._spinner_thread = Thread(target=self._start_spinning)\\n        self._indicator_length = len(next(self.spinner_cycle)) + 1\\n        self.fh = sys.stdout\\n        self.show_spin = enabled and not json and IS_INTERACTIVE\\n        self.fail_message = fail_message\\n\\n    def start(self):\\n        if self.show_spin:\\n            self._spinner_thread.start()\\n        elif not self.json:\\n            self.fh.write(\\\"...working... \\\")\\n            self.fh.flush()\\n\\n    def stop(self):\\n        if self.show_spin:\\n            self._stop_running.set()\\n            self._spinner_thread.join()\\n            self.show_spin = False\\n\\n    def _start_spinning(self):\\n        try:\\n            while not self._stop_running.is_set():\\n                self.fh.write(next(self.spinner_cycle) + \\\" \\\")\\n                self.fh.flush()\\n                sleep(0.10)\\n                self.fh.write(\\\"\\\\b\\\" * self._indicator_length)\\n        except OSError as e:\\n            if e.errno in (EPIPE, ESHUTDOWN):\\n                self.stop()\\n            else:\\n                raise\\n\\n    @swallow_broken_pipe\\n    def __enter__(self):\\n        if not self.json:\\n            sys.stdout.write(f\\\"{self.message}: \\\")\\n            sys.stdout.flush()\\n        self.start()\\n\\n    def __exit__(self, exc_type, exc_val, exc_tb):\\n        self.stop()\\n        if not self.json:\\n            with swallow_broken_pipe:\\n                if exc_type or exc_val:\\n                    sys.stdout.write(self.fail_message)\\n                else:\\n                    sys.stdout.write(\\\"done\\\\n\\\")\\n                sys.stdout.flush()\\n\\n\\nclass ProgressBar:\\n    @classmethod\\n    def get_lock(cls):\\n        # Used only for --json (our own sys.stdout.write/flush calls).\\n        if not hasattr(cls, \\\"_lock\\\"):\\n            cls._lock = RLock()\\n        return cls._lock\\n\\n    def __init__(\\n        self, description, enabled=True, json=False, position=None, leave=True\\n    ):\\n        \\\"\\\"\\\"\\n        Args:\\n            description (str):\\n                The name of the progress bar, shown on left side of output.\\n            enabled (bool):\\n                If False, usage is a no-op.\\n            json (bool):\\n                If true, outputs json progress to stdout rather than a progress bar.\\n                Currently, the json format assumes this is only used for \\\"fetch\\\", which\\n                maintains backward compatibility with conda 4.3 and earlier behavior.\\n        \\\"\\\"\\\"\\n        self.description = description\\n        self.enabled = enabled\\n        self.json = json\\n\\n        if json:\\n            pass\\n        elif enabled:\\n            if IS_INTERACTIVE:\\n                bar_format = \\\"{desc}{bar} | {percentage:3.0f}% \\\"\\n                try:\\n                    self.pbar = self._tqdm(\\n                        desc=description,\\n                        bar_format=bar_format,\\n                        ascii=True,\\n                        total=1,\\n                        file=sys.stdout,\\n                        position=position,\\n                        leave=leave,\\n                    )\\n                except OSError as e:\\n                    if e.errno in (EPIPE, ESHUTDOWN):\\n                        self.enabled = False\\n                    else:\\n                        raise\\n            else:\\n                self.pbar = None\\n                sys.stdout.write(f\\\"{description} ...working...\\\")\\n\\n    def update_to(self, fraction):\\n        try:\\n            if self.enabled:\\n                if self.json:\\n                    with self.get_lock():\\n                        sys.stdout.write(\\n                            f'{{\\\"fetch\\\":\\\"{self.description}\\\",\\\"finished\\\":false,\\\"maxval\\\":1,\\\"progress\\\":{fraction:f}}}\\\\n\\\\0'\\n                        )\\n                elif IS_INTERACTIVE:\\n                    self.pbar.update(fraction - self.pbar.n)\\n                elif fraction == 1:\\n                    sys.stdout.write(\\\" done\\\\n\\\")\\n        except OSError as e:\\n            if e.errno in (EPIPE, ESHUTDOWN):\\n                self.enabled = False\\n            else:\\n                raise\\n\\n    def finish(self):\\n        self.update_to(1)\\n\\n    def refresh(self):\\n        \\\"\\\"\\\"Force refresh i.e. once 100% has been reached\\\"\\\"\\\"\\n        if self.enabled and not self.json and IS_INTERACTIVE:\\n            self.pbar.refresh()\\n\\n    @swallow_broken_pipe\\n    def close(self):\\n        if self.enabled:\\n            if self.json:\\n                with self.get_lock():\\n                    sys.stdout.write(\\n                        f'{{\\\"fetch\\\":\\\"{self.description}\\\",\\\"finished\\\":true,\\\"maxval\\\":1,\\\"progress\\\":1}}\\\\n\\\\0'\\n                    )\\n                    sys.stdout.flush()\\n            elif IS_INTERACTIVE:\\n                self.pbar.close()\\n            else:\\n                sys.stdout.write(\\\" done\\\\n\\\")\\n\\n    @staticmethod\\n    def _tqdm(*args, **kwargs):\\n        \\\"\\\"\\\"Deferred import so it doesn't hit the `conda activate` paths.\\\"\\\"\\\"\\n        from tqdm.auto import tqdm\\n\\n        return tqdm(*args, **kwargs)\\n\\n\\n# use this for debugging, because ProcessPoolExecutor isn't pdb/ipdb friendly\\nclass DummyExecutor(Executor):\\n    def __init__(self):\\n        self._shutdown = False\\n        self._shutdownLock = Lock()\\n\\n    def submit(self, fn, *args, **kwargs):\\n        with self._shutdownLock:\\n            if self._shutdown:\\n                raise RuntimeError(\\\"cannot schedule new futures after shutdown\\\")\\n\\n            f = Future()\\n            try:\\n                result = fn(*args, **kwargs)\\n            except BaseException as e:\\n                f.set_exception(e)\\n            else:\\n                f.set_result(result)\\n\\n            return f\\n\\n    def map(self, func, *iterables):\\n        for iterable in iterables:\\n            for thing in iterable:\\n                yield func(thing)\\n\\n    def shutdown(self, wait=True):\\n        with self._shutdownLock:\\n            self._shutdown = True\\n\\n\\nclass ThreadLimitedThreadPoolExecutor(ThreadPoolExecutor):\\n    def __init__(self, max_workers=10):\\n        super().__init__(max_workers)\\n\\n    def submit(self, fn, *args, **kwargs):\\n        \\\"\\\"\\\"\\n        This is an exact reimplementation of the `submit()` method on the parent class, except\\n        with an added `try/except` around `self._adjust_thread_count()`.  So long as there is at\\n        least one living thread, this thread pool will not throw an exception if threads cannot\\n        be expanded to `max_workers`.\\n\\n        In the implementation, we use \\\"protected\\\" attributes from concurrent.futures (`_base`\\n        and `_WorkItem`). Consider vendoring the whole concurrent.futures library\\n        as an alternative to these protected imports.\\n\\n        https://github.com/agronholm/pythonfutures/blob/3.2.0/concurrent/futures/thread.py#L121-L131  # NOQA\\n        https://github.com/python/cpython/blob/v3.6.4/Lib/concurrent/futures/thread.py#L114-L124\\n        \\\"\\\"\\\"\\n        with self._shutdown_lock:\\n            if self._shutdown:\\n                raise RuntimeError(\\\"cannot schedule new futures after shutdown\\\")\\n\\n            f = _base.Future()\\n            w = _WorkItem(f, fn, args, kwargs)\\n\\n            self._work_queue.put(w)\\n            try:\\n                self._adjust_thread_count()\\n            except RuntimeError:\\n                # RuntimeError: can't start new thread\\n                # See https://github.com/conda/conda/issues/6624\\n                if len(self._threads) > 0:\\n                    # It's ok to not be able to start new threads if we already have at least\\n                    # one thread alive.\\n                    pass\\n                else:\\n                    raise\\n            return f\\n\\n\\nas_completed = as_completed\\n\\n\\ndef get_instrumentation_record_file():\\n    default_record_file = join(\\\"~\\\", \\\".conda\\\", \\\"instrumentation-record.csv\\\")\\n    return expand(\\n        os.environ.get(\\\"CONDA_INSTRUMENTATION_RECORD_FILE\\\", default_record_file)\\n    )\\n\\n\\nclass time_recorder(ContextDecorator):  # pragma: no cover\\n    record_file = get_instrumentation_record_file()\\n    start_time = None\\n    total_call_num = defaultdict(int)\\n    total_run_time = defaultdict(float)\\n\\n    def __init__(self, entry_name=None, module_name=None):\\n        self.entry_name = entry_name\\n        self.module_name = module_name\\n\\n    def _set_entry_name(self, f):\\n        if self.entry_name is None:\\n            if hasattr(f, \\\"__qualname__\\\"):\\n                entry_name = f.__qualname__\\n            else:\\n                entry_name = \\\":\\\" + f.__name__\\n            if self.module_name:\\n                entry_name = \\\".\\\".join((self.module_name, entry_name))\\n            self.entry_name = entry_name\\n\\n    def __call__(self, f):\\n        self._set_entry_name(f)\\n        return super().__call__(f)\\n\\n    def __enter__(self):\\n        enabled = os.environ.get(\\\"CONDA_INSTRUMENTATION_ENABLED\\\")\\n        if enabled and boolify(enabled):\\n            self.start_time = time()\\n        return self\\n\\n    def __exit__(self, exc_type, exc_val, exc_tb):\\n        if self.start_time:\\n            entry_name = self.entry_name\\n            end_time = time()\\n            run_time = end_time - self.start_time\\n            self.total_call_num[entry_name] += 1\\n            self.total_run_time[entry_name] += run_time\\n            self._ensure_dir()\\n            with open(self.record_file, \\\"a\\\") as fh:\\n                fh.write(f\\\"{entry_name},{run_time:f}\\\\n\\\")\\n            # total_call_num = self.total_call_num[entry_name]\\n            # total_run_time = self.total_run_time[entry_name]\\n            # log.debug('%s %9.3f %9.3f %d', entry_name, run_time, total_run_time, total_call_num)\\n\\n    @classmethod\\n    def log_totals(cls):\\n        enabled = os.environ.get(\\\"CONDA_INSTRUMENTATION_ENABLED\\\")\\n        if not (enabled and boolify(enabled)):\\n            return\\n        log.info(\\\"=== time_recorder total time and calls ===\\\")\\n        for entry_name in sorted(cls.total_run_time.keys()):\\n            log.info(\\n                \\\"TOTAL %9.3f % 9d %s\\\",\\n                cls.total_run_time[entry_name],\\n                cls.total_call_num[entry_name],\\n                entry_name,\\n            )\\n\\n    @memoizemethod\\n    def _ensure_dir(self):\\n        if not isdir(dirname(self.record_file)):\\n            os.makedirs(dirname(self.record_file))\\n\\n\\ndef print_instrumentation_data():  # pragma: no cover\\n    record_file = get_instrumentation_record_file()\\n\\n    grouped_data = defaultdict(list)\\n    final_data = {}\\n\\n    if not isfile(record_file):\\n        return\\n\\n    with open(record_file) as fh:\\n        for line in fh:\\n            entry_name, total_time = line.strip().split(\\\",\\\")\\n            grouped_data[entry_name].append(float(total_time))\\n\\n    for entry_name in sorted(grouped_data):\\n        all_times = grouped_data[entry_name]\\n        counts = len(all_times)\\n        total_time = sum(all_times)\\n        average_time = total_time / counts\\n        final_data[entry_name] = {\\n            \\\"counts\\\": counts,\\n            \\\"total_time\\\": total_time,\\n            \\\"average_time\\\": average_time,\\n        }\\n\\n    print(json.dumps(final_data, sort_keys=True, indent=2, separators=(\\\",\\\", \\\": \\\")))\\n\\n\\nif __name__ == \\\"__main__\\\":\\n    print_instrumentation_data()\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"YAML and JSON serialization and deserialization functions.\\\"\\\"\\\"\\n\\nimport functools\\nimport json\\nfrom io import StringIO\\nfrom logging import getLogger\\n\\nimport ruamel.yaml as yaml\\n\\nfrom ..auxlib.entity import EntityEncoder\\n\\nlog = getLogger(__name__)\\n\\n\\n# FUTURE: Python 3.9+, replace with functools.cache\\n@functools.lru_cache(maxsize=None)\\ndef _yaml_round_trip():\\n    parser = yaml.YAML(typ=\\\"rt\\\")\\n    parser.indent(mapping=2, offset=2, sequence=4)\\n    return parser\\n\\n\\n# FUTURE: Python 3.9+, replace with functools.cache\\n@functools.lru_cache(maxsize=None)\\ndef _yaml_safe():\\n    parser = yaml.YAML(typ=\\\"safe\\\", pure=True)\\n    parser.indent(mapping=2, offset=2, sequence=4)\\n    parser.default_flow_style = False\\n    parser.sort_base_mapping_type_on_output = False\\n    return parser\\n\\n\\ndef yaml_round_trip_load(string):\\n    return _yaml_round_trip().load(string)\\n\\n\\ndef yaml_safe_load(string):\\n    \\\"\\\"\\\"\\n    Examples:\\n        >>> yaml_safe_load(\\\"key: value\\\")\\n        {'key': 'value'}\\n\\n    \\\"\\\"\\\"\\n    return _yaml_safe().load(string)\\n\\n\\ndef yaml_round_trip_dump(object, stream=None):\\n    \\\"\\\"\\\"Dump object to string or stream.\\\"\\\"\\\"\\n    ostream = stream or StringIO()\\n    _yaml_round_trip().dump(object, ostream)\\n    if not stream:\\n        return ostream.getvalue()\\n\\n\\ndef yaml_safe_dump(object, stream=None):\\n    \\\"\\\"\\\"Dump object to string or stream.\\\"\\\"\\\"\\n    ostream = stream or StringIO()\\n    _yaml_safe().dump(object, ostream)\\n    if not stream:\\n        return ostream.getvalue()\\n\\n\\ndef json_load(string):\\n    return json.loads(string)\\n\\n\\ndef json_dump(object):\\n    return json.dumps(\\n        object, indent=2, sort_keys=True, separators=(\\\",\\\", \\\": \\\"), cls=EntityEncoder\\n    )\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Common path utilities.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport os\\nimport re\\nimport subprocess\\nfrom functools import lru_cache, reduce\\nfrom itertools import accumulate, chain\\nfrom logging import getLogger\\nfrom os.path import (\\n    abspath,\\n    basename,\\n    expanduser,\\n    expandvars,\\n    join,\\n    normcase,\\n    split,\\n    splitext,\\n)\\nfrom typing import TYPE_CHECKING\\nfrom urllib.parse import urlsplit\\n\\nfrom .. import CondaError\\nfrom .compat import on_win\\n\\nif TYPE_CHECKING:\\n    from typing import Iterable, Sequence\\n\\nlog = getLogger(__name__)\\n\\nPATH_MATCH_REGEX = (\\n    r\\\"\\\\./\\\"  # ./\\n    r\\\"|\\\\.\\\\.\\\"  # ..\\n    r\\\"|~\\\"  # ~\\n    r\\\"|/\\\"  # /\\n    r\\\"|[a-zA-Z]:[/\\\\\\\\]\\\"  # drive letter, colon, forward or backslash\\n    r\\\"|\\\\\\\\\\\\\\\\\\\"  # windows UNC path\\n    r\\\"|//\\\"  # windows UNC path\\n)\\n\\n# any other extension will be mangled by CondaSession.get() as it tries to find\\n# channel names from URLs, through strip_pkg_extension()\\nKNOWN_EXTENSIONS = (\\\".conda\\\", \\\".tar.bz2\\\", \\\".json\\\", \\\".jlap\\\", \\\".json.zst\\\")\\n\\n\\ndef is_path(value):\\n    if \\\"://\\\" in value:\\n        return False\\n    return re.match(PATH_MATCH_REGEX, value)\\n\\n\\ndef expand(path):\\n    return abspath(expanduser(expandvars(path)))\\n\\n\\ndef paths_equal(path1, path2):\\n    \\\"\\\"\\\"\\n    Examples:\\n        >>> paths_equal('/a/b/c', '/a/b/c/d/..')\\n        True\\n\\n    \\\"\\\"\\\"\\n    if on_win:\\n        return normcase(abspath(path1)) == normcase(abspath(path2))\\n    else:\\n        return abspath(path1) == abspath(path2)\\n\\n\\n@lru_cache(maxsize=None)\\ndef url_to_path(url):\\n    \\\"\\\"\\\"Convert a file:// URL to a path.\\n\\n    Relative file URLs (i.e. `file:relative/path`) are not supported.\\n    \\\"\\\"\\\"\\n    if is_path(url):\\n        return url\\n    if not url.startswith(\\\"file://\\\"):  # pragma: no cover\\n        raise CondaError(\\n            f\\\"You can only turn absolute file: urls into paths (not {url})\\\"\\n        )\\n    _, netloc, path, _, _ = urlsplit(url)\\n    from .url import percent_decode\\n\\n    path = percent_decode(path)\\n    if netloc not in (\\\"\\\", \\\"localhost\\\", \\\"127.0.0.1\\\", \\\"::1\\\"):\\n        if not netloc.startswith(\\\"\\\\\\\\\\\\\\\\\\\"):\\n            # The only net location potentially accessible is a Windows UNC path\\n            netloc = \\\"//\\\" + netloc\\n    else:\\n        netloc = \\\"\\\"\\n        # Handle Windows drive letters if present\\n        if re.match(\\\"^/([a-z])[:|]\\\", path, re.I):\\n            path = path[1] + \\\":\\\" + path[3:]\\n    return netloc + path\\n\\n\\ndef tokenized_startswith(test_iterable, startswith_iterable):\\n    return all(t == sw for t, sw in zip(test_iterable, startswith_iterable))\\n\\n\\ndef get_all_directories(files: Iterable[str]) -> list[tuple[str]]:\\n    return sorted({tuple(f.split(\\\"/\\\")[:-1]) for f in files} - {()})\\n\\n\\ndef get_leaf_directories(files: Iterable[str]) -> Sequence[str]:\\n    # give this function a list of files, and it will hand back a list of leaf\\n    # directories to pass to os.makedirs()\\n    directories = get_all_directories(files)\\n    if not directories:\\n        return ()\\n\\n    leaves = []\\n\\n    def _process(x, y):\\n        if not tokenized_startswith(y, x):\\n            leaves.append(x)\\n        return y\\n\\n    last = reduce(_process, directories)\\n\\n    if not leaves:\\n        leaves.append(directories[-1])\\n    elif not tokenized_startswith(last, leaves[-1]):\\n        leaves.append(last)\\n\\n    return tuple(\\\"/\\\".join(leaf) for leaf in leaves)\\n\\n\\ndef explode_directories(child_directories: Iterable[tuple[str, ...]]) -> set[str]:\\n    # get all directories including parents\\n    # child_directories must already be split with os.path.split\\n    return set(\\n        chain.from_iterable(\\n            accumulate(directory, join) for directory in child_directories if directory\\n        )\\n    )\\n\\n\\ndef pyc_path(py_path, python_major_minor_version):\\n    \\\"\\\"\\\"\\n    This must not return backslashes on Windows as that will break\\n    tests and leads to an eventual need to make url_to_path return\\n    backslashes too and that may end up changing files on disc or\\n    to the result of comparisons with the contents of them.\\n    \\\"\\\"\\\"\\n    pyver_string = python_major_minor_version.replace(\\\".\\\", \\\"\\\")\\n    if pyver_string.startswith(\\\"2\\\"):\\n        return py_path + \\\"c\\\"\\n    else:\\n        directory, py_file = split(py_path)\\n        basename_root, extension = splitext(py_file)\\n        pyc_file = (\\n            \\\"__pycache__\\\" + \\\"/\\\" + f\\\"{basename_root}.cpython-{pyver_string}{extension}c\\\"\\n        )\\n        return \\\"{}{}{}\\\".format(directory, \\\"/\\\", pyc_file) if directory else pyc_file\\n\\n\\ndef missing_pyc_files(python_major_minor_version, files):\\n    # returns a tuple of tuples, with the inner tuple being the .py file and the missing .pyc file\\n    py_files = (f for f in files if f.endswith(\\\".py\\\"))\\n    pyc_matches = (\\n        (py_file, pyc_path(py_file, python_major_minor_version)) for py_file in py_files\\n    )\\n    result = tuple(match for match in pyc_matches if match[1] not in files)\\n    return result\\n\\n\\ndef parse_entry_point_def(ep_definition):\\n    cmd_mod, func = ep_definition.rsplit(\\\":\\\", 1)\\n    command, module = cmd_mod.rsplit(\\\"=\\\", 1)\\n    command, module, func = command.strip(), module.strip(), func.strip()\\n    return command, module, func\\n\\n\\ndef get_python_short_path(python_version=None):\\n    if on_win:\\n        return \\\"python.exe\\\"\\n    if python_version and \\\".\\\" not in python_version:\\n        python_version = \\\".\\\".join(python_version)\\n    return join(\\\"bin\\\", \\\"python%s\\\" % (python_version or \\\"\\\"))\\n\\n\\ndef get_python_site_packages_short_path(python_version):\\n    if python_version is None:\\n        return None\\n    elif on_win:\\n        return \\\"Lib/site-packages\\\"\\n    else:\\n        py_ver = get_major_minor_version(python_version)\\n        return f\\\"lib/python{py_ver}/site-packages\\\"\\n\\n\\n_VERSION_REGEX = re.compile(r\\\"[0-9]+\\\\.[0-9]+\\\")\\n\\n\\ndef get_major_minor_version(string, with_dot=True):\\n    # returns None if not found, otherwise two digits as a string\\n    # should work for\\n    #   - 3.5.2\\n    #   - 27\\n    #   - bin/python2.7\\n    #   - lib/python34/site-packages/\\n    # the last two are dangers because windows doesn't have version information there\\n    assert isinstance(string, str)\\n    if string.startswith(\\\"lib/python\\\"):\\n        pythonstr = string.split(\\\"/\\\")[1]\\n        start = len(\\\"python\\\")\\n        if len(pythonstr) < start + 2:\\n            return None\\n        maj_min = pythonstr[start], pythonstr[start + 1 :]\\n    elif string.startswith(\\\"bin/python\\\"):\\n        pythonstr = string.split(\\\"/\\\")[1]\\n        start = len(\\\"python\\\")\\n        if len(pythonstr) < start + 3:\\n            return None\\n        assert pythonstr[start + 1] == \\\".\\\"\\n        maj_min = pythonstr[start], pythonstr[start + 2 :]\\n    else:\\n        match = _VERSION_REGEX.match(string)\\n        if match:\\n            version = match.group(0).split(\\\".\\\")\\n            maj_min = version[0], version[1]\\n        else:\\n            digits = \\\"\\\".join([c for c in string if c.isdigit()])\\n            if len(digits) < 2:\\n                return None\\n            maj_min = digits[0], digits[1:]\\n\\n    return \\\".\\\".join(maj_min) if with_dot else \\\"\\\".join(maj_min)\\n\\n\\ndef get_bin_directory_short_path():\\n    return \\\"Scripts\\\" if on_win else \\\"bin\\\"\\n\\n\\ndef win_path_ok(path):\\n    return path.replace(\\\"/\\\", \\\"\\\\\\\\\\\") if on_win else path\\n\\n\\ndef win_path_double_escape(path):\\n    return path.replace(\\\"\\\\\\\\\\\", \\\"\\\\\\\\\\\\\\\\\\\") if on_win else path\\n\\n\\ndef win_path_backout(path):\\n    # replace all backslashes except those escaping spaces\\n    # if we pass a file url, something like file://\\\\\\\\unc\\\\path\\\\on\\\\win, make sure\\n    #   we clean that up too\\n    return re.sub(r\\\"(\\\\\\\\(?! ))\\\", r\\\"/\\\", path).replace(\\\":////\\\", \\\"://\\\")\\n\\n\\ndef ensure_pad(name, pad=\\\"_\\\"):\\n    \\\"\\\"\\\"\\n\\n    Examples:\\n        >>> ensure_pad('conda')\\n        '_conda_'\\n        >>> ensure_pad('_conda')\\n        '__conda_'\\n        >>> ensure_pad('')\\n        ''\\n\\n    \\\"\\\"\\\"\\n    if not name or name[0] == name[-1] == pad:\\n        return name\\n    else:\\n        return f\\\"{pad}{name}{pad}\\\"\\n\\n\\ndef is_private_env_name(env_name):\\n    \\\"\\\"\\\"\\n\\n    Examples:\\n        >>> is_private_env_name(\\\"_conda\\\")\\n        False\\n        >>> is_private_env_name(\\\"_conda_\\\")\\n        True\\n\\n    \\\"\\\"\\\"\\n    return env_name and env_name[0] == env_name[-1] == \\\"_\\\"\\n\\n\\ndef is_private_env_path(env_path):\\n    \\\"\\\"\\\"\\n\\n    Examples:\\n        >>> is_private_env_path('/some/path/to/envs/_conda_')\\n        True\\n        >>> is_private_env_path('/not/an/envs_dir/_conda_')\\n        False\\n\\n    \\\"\\\"\\\"\\n    if env_path is not None:\\n        envs_directory, env_name = split(env_path)\\n        if basename(envs_directory) != \\\"envs\\\":\\n            return False\\n        return is_private_env_name(env_name)\\n    return False\\n\\n\\ndef right_pad_os_sep(path):\\n    return path if path.endswith(os.sep) else path + os.sep\\n\\n\\ndef split_filename(path_or_url):\\n    dn, fn = split(path_or_url)\\n    return (dn or None, fn) if \\\".\\\" in fn else (path_or_url, None)\\n\\n\\ndef get_python_noarch_target_path(source_short_path, target_site_packages_short_path):\\n    if source_short_path.startswith(\\\"site-packages/\\\"):\\n        sp_dir = target_site_packages_short_path\\n        return source_short_path.replace(\\\"site-packages\\\", sp_dir, 1)\\n    elif source_short_path.startswith(\\\"python-scripts/\\\"):\\n        bin_dir = get_bin_directory_short_path()\\n        return source_short_path.replace(\\\"python-scripts\\\", bin_dir, 1)\\n    else:\\n        return source_short_path\\n\\n\\ndef win_path_to_unix(path, root_prefix=\\\"\\\"):\\n    # If the user wishes to drive conda from MSYS2 itself while also having\\n    # msys2 packages in their environment this allows the path conversion to\\n    # happen relative to the actual shell. The onus is on the user to set\\n    # CYGPATH to e.g. /usr/bin/cygpath.exe (this will be translated to e.g.\\n    # (C:\\\\msys32\\\\usr\\\\bin\\\\cygpath.exe by MSYS2) to ensure this one is used.\\n    if not path:\\n        return \\\"\\\"\\n\\n    # rebind to shutil to avoid triggering the deprecation warning\\n    from shutil import which\\n\\n    bash = which(\\\"bash\\\")\\n    if bash:\\n        cygpath = os.environ.get(\\n            \\\"CYGPATH\\\", os.path.join(os.path.dirname(bash), \\\"cygpath.exe\\\")\\n        )\\n    else:\\n        cygpath = os.environ.get(\\\"CYGPATH\\\", \\\"cygpath.exe\\\")\\n    try:\\n        path = (\\n            subprocess.check_output([cygpath, \\\"-up\\\", path])\\n            .decode(\\\"ascii\\\")\\n            .split(\\\"\\\\n\\\")[0]\\n        )\\n    except Exception as e:\\n        log.debug(f\\\"{e!r}\\\", exc_info=True)\\n\\n        # Convert a path or ;-separated string of paths into a unix representation\\n        # Does not add cygdrive.  If you need that, set root_prefix to \\\"/cygdrive\\\"\\n        def _translation(found_path):  # NOQA\\n            found = (\\n                found_path.group(1)\\n                .replace(\\\"\\\\\\\\\\\", \\\"/\\\")\\n                .replace(\\\":\\\", \\\"\\\")\\n                .replace(\\\"//\\\", \\\"/\\\")\\n            )\\n            return root_prefix + \\\"/\\\" + found\\n\\n        path_re = '(?<![:/^a-zA-Z])([a-zA-Z]:[/\\\\\\\\\\\\\\\\]+(?:[^:*?\\\"<>|]+[/\\\\\\\\\\\\\\\\]+)*[^:*?\\\"<>|;/\\\\\\\\\\\\\\\\]+?(?![a-zA-Z]:))'  # noqa\\n        path = re.sub(path_re, _translation, path).replace(\\\";/\\\", \\\":/\\\")\\n    return path\\n\\n\\ndef which(executable):\\n    \\\"\\\"\\\"Backwards-compatibility wrapper. Use `shutil.which` directly if possible.\\\"\\\"\\\"\\n    from shutil import which\\n\\n    return which(executable)\\n\\n\\ndef strip_pkg_extension(path: str):\\n    \\\"\\\"\\\"\\n    Examples:\\n        >>> strip_pkg_extension(\\\"/path/_license-1.1-py27_1.tar.bz2\\\")\\n        ('/path/_license-1.1-py27_1', '.tar.bz2')\\n        >>> strip_pkg_extension(\\\"/path/_license-1.1-py27_1.conda\\\")\\n        ('/path/_license-1.1-py27_1', '.conda')\\n        >>> strip_pkg_extension(\\\"/path/_license-1.1-py27_1\\\")\\n        ('/path/_license-1.1-py27_1', None)\\n    \\\"\\\"\\\"\\n    # NOTE: not using CONDA_TARBALL_EXTENSION_V1 or CONDA_TARBALL_EXTENSION_V2 to comply with\\n    #       import rules and to avoid a global lookup.\\n    for extension in KNOWN_EXTENSIONS:\\n        if path.endswith(extension):\\n            return path[: -len(extension)], extension\\n    return path, None\\n\\n\\ndef is_package_file(path):\\n    \\\"\\\"\\\"\\n    Examples:\\n        >>> is_package_file(\\\"/path/_license-1.1-py27_1.tar.bz2\\\")\\n        True\\n        >>> is_package_file(\\\"/path/_license-1.1-py27_1.conda\\\")\\n        True\\n        >>> is_package_file(\\\"/path/_license-1.1-py27_1\\\")\\n        False\\n    \\\"\\\"\\\"\\n    # NOTE: not using CONDA_TARBALL_EXTENSION_V1 or CONDA_TARBALL_EXTENSION_V2 to comply with\\n    #       import rules and to avoid a global lookup.\\n    return path[-6:] == \\\".conda\\\" or path[-8:] == \\\".tar.bz2\\\"\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"\\nA generalized application configuration utility.\\n\\nFeatures include:\\n  - lazy eval\\n  - merges configuration files\\n  - parameter type validation, with custom validation\\n  - parameter aliases\\n\\nEasily extensible to other source formats, e.g. json and ini\\n\\n\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport copy\\nimport sys\\nfrom abc import ABCMeta, abstractmethod\\nfrom collections import defaultdict\\nfrom collections.abc import Mapping\\nfrom enum import Enum, EnumMeta\\nfrom functools import wraps\\nfrom itertools import chain\\nfrom logging import getLogger\\nfrom os import environ\\nfrom os.path import expandvars\\nfrom pathlib import Path\\nfrom re import IGNORECASE, VERBOSE, compile\\nfrom string import Template\\nfrom typing import TYPE_CHECKING\\n\\nfrom boltons.setutils import IndexedSet\\nfrom ruamel.yaml.comments import CommentedMap, CommentedSeq\\nfrom ruamel.yaml.reader import ReaderError\\nfrom ruamel.yaml.scanner import ScannerError\\n\\nfrom .. import CondaError, CondaMultiError\\nfrom ..auxlib.collection import AttrDict, first, last\\nfrom ..auxlib.exceptions import ThisShouldNeverHappenError\\nfrom ..auxlib.type_coercion import TypeCoercionError, typify, typify_data_structure\\nfrom ..common.iterators import unique\\nfrom ..deprecations import deprecated\\nfrom .compat import isiterable, primitive_types\\nfrom .constants import NULL\\nfrom .serialize import yaml_round_trip_load\\n\\ntry:\\n    from frozendict import deepfreeze, frozendict\\n    from frozendict import getFreezeConversionMap as _getFreezeConversionMap\\n    from frozendict import register as _register\\n\\n    if Enum not in _getFreezeConversionMap():\\n        # leave enums as is, deepfreeze will flatten it into a dict\\n        # see https://github.com/Marco-Sulla/python-frozendict/issues/98\\n        _register(Enum, lambda x: x)\\n\\n    del _getFreezeConversionMap\\n    del _register\\nexcept ImportError:\\n    from .._vendor.frozendict import frozendict\\n    from ..auxlib.collection import make_immutable as deepfreeze\\n\\nif TYPE_CHECKING:\\n    from re import Match\\n    from typing import Any, Hashable, Iterable, Sequence\\n\\nlog = getLogger(__name__)\\n\\nEMPTY_MAP = frozendict()\\n\\n\\ndef pretty_list(iterable, padding=\\\"  \\\"):  # TODO: move elsewhere in conda.common\\n    if not isiterable(iterable):\\n        iterable = [iterable]\\n    try:\\n        return \\\"\\\\n\\\".join(f\\\"{padding}- {item}\\\" for item in iterable)\\n    except TypeError:\\n        return pretty_list([iterable], padding)\\n\\n\\ndef pretty_map(dictionary, padding=\\\"  \\\"):\\n    return \\\"\\\\n\\\".join(f\\\"{padding}{key}: {value}\\\" for key, value in dictionary.items())\\n\\n\\ndef expand_environment_variables(unexpanded):\\n    if isinstance(unexpanded, (str, bytes)):\\n        return expandvars(unexpanded)\\n    else:\\n        return unexpanded\\n\\n\\nclass ConfigurationError(CondaError):\\n    pass\\n\\n\\nclass ConfigurationLoadError(ConfigurationError):\\n    def __init__(self, path, message_addition=\\\"\\\", **kwargs):\\n        message = \\\"Unable to load configuration file.\\\\n  path: %(path)s\\\\n\\\"\\n        super().__init__(message + message_addition, path=path, **kwargs)\\n\\n\\nclass ValidationError(ConfigurationError):\\n    def __init__(self, parameter_name, parameter_value, source, msg=None, **kwargs):\\n        self.parameter_name = parameter_name\\n        self.parameter_value = parameter_value\\n        self.source = source\\n        super().__init__(msg, **kwargs)\\n\\n\\nclass MultipleKeysError(ValidationError):\\n    def __init__(self, source, keys, preferred_key):\\n        self.source = source\\n        self.keys = keys\\n        msg = (\\n            f\\\"Multiple aliased keys in file {source}:\\\\n\\\"\\n            f\\\"{pretty_list(keys)}\\\\n\\\"\\n            f\\\"Must declare only one. Prefer '{preferred_key}'\\\"\\n        )\\n        super().__init__(preferred_key, None, source, msg=msg)\\n\\n\\nclass InvalidTypeError(ValidationError):\\n    def __init__(\\n        self, parameter_name, parameter_value, source, wrong_type, valid_types, msg=None\\n    ):\\n        self.wrong_type = wrong_type\\n        self.valid_types = valid_types\\n        if msg is None:\\n            msg = (\\n                f\\\"Parameter {parameter_name} = {parameter_value!r} declared in {source} has type {wrong_type}.\\\\n\\\"\\n                f\\\"Valid types:\\\\n{pretty_list(valid_types)}\\\"\\n            )\\n        super().__init__(parameter_name, parameter_value, source, msg=msg)\\n\\n\\nclass CustomValidationError(ValidationError):\\n    def __init__(self, parameter_name, parameter_value, source, custom_message):\\n        super().__init__(\\n            parameter_name,\\n            parameter_value,\\n            source,\\n            msg=(\\n                f\\\"Parameter {parameter_name} = {parameter_value!r} declared in \\\"\\n                f\\\"{source} is invalid.\\\\n{custom_message}\\\"\\n            ),\\n        )\\n\\n\\nclass MultiValidationError(CondaMultiError, ConfigurationError):\\n    def __init__(self, errors, *args, **kwargs):\\n        super().__init__(errors, *args, **kwargs)\\n\\n\\ndef raise_errors(errors):\\n    if not errors:\\n        return True\\n    elif len(errors) == 1:\\n        raise errors[0]\\n    else:\\n        raise MultiValidationError(errors)\\n\\n\\nclass ParameterFlag(Enum):\\n    final = \\\"final\\\"\\n    top = \\\"top\\\"\\n    bottom = \\\"bottom\\\"\\n\\n    def __str__(self):\\n        return f\\\"{self.value}\\\"\\n\\n    @classmethod\\n    def from_name(cls, name):\\n        return cls[name]\\n\\n    @classmethod\\n    def from_value(cls, value):\\n        return cls(value)\\n\\n    @classmethod\\n    def from_string(cls, string):\\n        try:\\n            string = string.strip(\\\"!#\\\")\\n            return cls.from_value(string)\\n        except (ValueError, AttributeError):\\n            return None\\n\\n\\nclass RawParameter(metaclass=ABCMeta):\\n    def __init__(self, source, key, raw_value):\\n        self.source = source\\n        self.key = key\\n        try:\\n            self._raw_value = raw_value.decode(\\\"utf-8\\\")\\n        except AttributeError:\\n            # AttributeError: raw_value is not encoded\\n            self._raw_value = raw_value\\n\\n    def __repr__(self):\\n        return str(vars(self))\\n\\n    @abstractmethod\\n    def value(self, parameter_obj):\\n        raise NotImplementedError()\\n\\n    @abstractmethod\\n    def keyflag(self):\\n        raise NotImplementedError()\\n\\n    @abstractmethod\\n    def valueflags(self, parameter_obj):\\n        raise NotImplementedError()\\n\\n    @classmethod\\n    def make_raw_parameters(cls, source, from_map):\\n        if from_map:\\n            return {key: cls(source, key, from_map[key]) for key in from_map}\\n        return EMPTY_MAP\\n\\n\\nclass EnvRawParameter(RawParameter):\\n    source = \\\"envvars\\\"\\n\\n    def value(self, parameter_obj):\\n        # note: this assumes that EnvRawParameters will only have flat configuration of either\\n        # primitive or sequential type\\n        if hasattr(parameter_obj, \\\"string_delimiter\\\"):\\n            assert isinstance(self._raw_value, str)\\n            string_delimiter = getattr(parameter_obj, \\\"string_delimiter\\\")\\n            # TODO: add stripping of !important, !top, and !bottom\\n            return tuple(\\n                EnvRawParameter(EnvRawParameter.source, self.key, v)\\n                for v in (vv.strip() for vv in self._raw_value.split(string_delimiter))\\n                if v\\n            )\\n        else:\\n            return self.__important_split_value[0].strip()\\n\\n    def keyflag(self):\\n        return ParameterFlag.final if len(self.__important_split_value) >= 2 else None\\n\\n    def valueflags(self, parameter_obj):\\n        if hasattr(parameter_obj, \\\"string_delimiter\\\"):\\n            string_delimiter = getattr(parameter_obj, \\\"string_delimiter\\\")\\n            # TODO: add stripping of !important, !top, and !bottom\\n            return tuple(\\\"\\\" for _ in self._raw_value.split(string_delimiter))\\n        else:\\n            return self.__important_split_value[0].strip()\\n\\n    @property\\n    def __important_split_value(self):\\n        return self._raw_value.split(\\\"!important\\\")\\n\\n    @classmethod\\n    def make_raw_parameters(cls, appname):\\n        keystart = f\\\"{appname.upper()}_\\\"\\n        raw_env = {\\n            k.replace(keystart, \\\"\\\", 1).lower(): v\\n            for k, v in environ.items()\\n            if k.startswith(keystart)\\n        }\\n        return super().make_raw_parameters(EnvRawParameter.source, raw_env)\\n\\n\\nclass ArgParseRawParameter(RawParameter):\\n    source = \\\"cmd_line\\\"\\n\\n    def value(self, parameter_obj):\\n        # note: this assumes ArgParseRawParameter will only have flat configuration of either\\n        # primitive or sequential type\\n        if isiterable(self._raw_value):\\n            children_values = []\\n            for i in range(len(self._raw_value)):\\n                children_values.append(\\n                    ArgParseRawParameter(self.source, self.key, self._raw_value[i])\\n                )\\n            return tuple(children_values)\\n        else:\\n            return deepfreeze(self._raw_value)\\n\\n    def keyflag(self):\\n        return None\\n\\n    def valueflags(self, parameter_obj):\\n        return None if isinstance(parameter_obj, PrimitiveLoadedParameter) else ()\\n\\n    @classmethod\\n    def make_raw_parameters(cls, args_from_argparse):\\n        return super().make_raw_parameters(\\n            ArgParseRawParameter.source, args_from_argparse\\n        )\\n\\n\\nclass YamlRawParameter(RawParameter):\\n    # this class should encapsulate all direct use of ruamel.yaml in this module\\n\\n    def __init__(self, source, key, raw_value, key_comment):\\n        self._key_comment = key_comment\\n        super().__init__(source, key, raw_value)\\n\\n        if isinstance(self._raw_value, CommentedSeq):\\n            value_comments = self._get_yaml_list_comments(self._raw_value)\\n            self._value_flags = tuple(\\n                ParameterFlag.from_string(s) for s in value_comments\\n            )\\n            children_values = []\\n            for i in range(len(self._raw_value)):\\n                children_values.append(\\n                    YamlRawParameter(\\n                        self.source, self.key, self._raw_value[i], value_comments[i]\\n                    )\\n                )\\n            self._value = tuple(children_values)\\n        elif isinstance(self._raw_value, CommentedMap):\\n            value_comments = self._get_yaml_map_comments(self._raw_value)\\n            self._value_flags = {\\n                k: ParameterFlag.from_string(v)\\n                for k, v in value_comments.items()\\n                if v is not None\\n            }\\n            children_values = {}\\n            for k, v in self._raw_value.items():\\n                children_values[k] = YamlRawParameter(\\n                    self.source, self.key, v, value_comments[k]\\n                )\\n            self._value = frozendict(children_values)\\n        elif isinstance(self._raw_value, primitive_types):\\n            self._value_flags = None\\n            self._value = self._raw_value\\n        else:\\n            print(type(self._raw_value), self._raw_value, file=sys.stderr)\\n            raise ThisShouldNeverHappenError()  # pragma: no cover\\n\\n    def value(self, parameter_obj):\\n        return self._value\\n\\n    def keyflag(self):\\n        return ParameterFlag.from_string(self._key_comment)\\n\\n    def valueflags(self, parameter_obj):\\n        return self._value_flags\\n\\n    @staticmethod\\n    def _get_yaml_key_comment(commented_dict, key):\\n        try:\\n            return commented_dict.ca.items[key][2].value.strip()\\n        except (AttributeError, KeyError):\\n            return None\\n\\n    @classmethod\\n    def _get_yaml_list_comments(cls, value):\\n        # value is a ruamel.yaml CommentedSeq, len(value) is the number of lines in the sequence,\\n        # value.ca is the comment object for the sequence and the comments themselves are stored as\\n        # a sparse dict\\n        list_comments = []\\n        for i in range(len(value)):\\n            try:\\n                list_comments.append(cls._get_yaml_list_comment_item(value.ca.items[i]))\\n            except (AttributeError, IndexError, KeyError, TypeError):\\n                list_comments.append(None)\\n        return tuple(list_comments)\\n\\n    @staticmethod\\n    def _get_yaml_list_comment_item(item):\\n        # take the pre_item comment if available\\n        # if not, take the first post_item comment if available\\n        if item[0]:\\n            return item[0].value.strip() or None\\n        else:\\n            return item[1][0].value.strip() or None\\n\\n    @staticmethod\\n    def _get_yaml_map_comments(value):\\n        map_comments = {}\\n        for key in value:\\n            try:\\n                map_comments[key] = value.ca.items[key][2].value.strip() or None\\n            except (AttributeError, KeyError):\\n                map_comments[key] = None\\n        return map_comments\\n\\n    @classmethod\\n    def make_raw_parameters(cls, source, from_map):\\n        if from_map:\\n            return {\\n                key: cls(\\n                    source, key, from_map[key], cls._get_yaml_key_comment(from_map, key)\\n                )\\n                for key in from_map\\n            }\\n        return EMPTY_MAP\\n\\n    @classmethod\\n    def make_raw_parameters_from_file(cls, filepath):\\n        with open(filepath) as fh:\\n            try:\\n                yaml_obj = yaml_round_trip_load(fh)\\n            except ScannerError as err:\\n                mark = err.problem_mark\\n                raise ConfigurationLoadError(\\n                    filepath,\\n                    \\\"  reason: invalid yaml at line %(line)s, column %(column)s\\\",\\n                    line=mark.line,\\n                    column=mark.column,\\n                )\\n            except ReaderError as err:\\n                raise ConfigurationLoadError(\\n                    filepath,\\n                    \\\"  reason: invalid yaml at position %(position)s\\\",\\n                    position=err.position,\\n                )\\n            return cls.make_raw_parameters(filepath, yaml_obj) or EMPTY_MAP\\n\\n\\nclass DefaultValueRawParameter(RawParameter):\\n    \\\"\\\"\\\"Wraps a default value as a RawParameter, for usage in ParameterLoader.\\\"\\\"\\\"\\n\\n    def __init__(self, source, key, raw_value):\\n        super().__init__(source, key, raw_value)\\n\\n        if isinstance(self._raw_value, Mapping):\\n            children_values = {}\\n            for k, v in self._raw_value.items():\\n                children_values[k] = DefaultValueRawParameter(self.source, self.key, v)\\n            self._value = frozendict(children_values)\\n        elif isiterable(self._raw_value):\\n            children_values = []\\n            for i in range(len(self._raw_value)):\\n                children_values.append(\\n                    DefaultValueRawParameter(self.source, self.key, self._raw_value[i])\\n                )\\n            self._value = tuple(children_values)\\n        elif isinstance(self._raw_value, ConfigurationObject):\\n            self._value = self._raw_value\\n            for attr_name, attr_value in vars(self._raw_value).items():\\n                self._value.__setattr__(\\n                    attr_name,\\n                    DefaultValueRawParameter(self.source, self.key, attr_value),\\n                )\\n        elif isinstance(self._raw_value, Enum):\\n            self._value = self._raw_value\\n        elif isinstance(self._raw_value, primitive_types):\\n            self._value = self._raw_value\\n        else:\\n            raise ThisShouldNeverHappenError()  # pragma: no cover\\n\\n    def value(self, parameter_obj):\\n        return self._value\\n\\n    def keyflag(self):\\n        return None\\n\\n    def valueflags(self, parameter_obj):\\n        if isinstance(self._raw_value, Mapping):\\n            return frozendict()\\n        elif isiterable(self._raw_value):\\n            return ()\\n        elif isinstance(self._raw_value, ConfigurationObject):\\n            return None\\n        elif isinstance(self._raw_value, Enum):\\n            return None\\n        elif isinstance(self._raw_value, primitive_types):\\n            return None\\n        else:\\n            raise ThisShouldNeverHappenError()  # pragma: no cover\\n\\n\\n@deprecated(\\\"24.3\\\", \\\"24.9\\\")\\ndef load_file_configs(search_path: Iterable[Path | str], **kwargs) -> dict[Path, dict]:\\n    expanded_paths = Configuration._expand_search_path(search_path, **kwargs)\\n    return dict(Configuration._load_search_path(expanded_paths))\\n\\n\\nclass LoadedParameter(metaclass=ABCMeta):\\n    # (type) describes the type of parameter\\n    _type = None\\n    # (Parameter or type) if the LoadedParameter holds a collection, describes the element held in\\n    # the collection. if not, describes the primitive type held by the LoadedParameter.\\n    _element_type = None\\n\\n    def __init__(self, name, value, key_flag, value_flags, validation=None):\\n        \\\"\\\"\\\"\\n        Represents a Parameter that has been loaded with configuration value.\\n\\n        Args:\\n            name (str): name of the loaded parameter\\n            value (LoadedParameter or primitive): the value of the loaded parameter\\n            key_flag (ParameterFlag or None): priority flag for the parameter itself\\n            value_flags (Any or None): priority flags for the parameter values\\n            validation (callable): Given a parameter value as input, return a boolean indicating\\n                validity, or alternately return a string describing an invalid value.\\n        \\\"\\\"\\\"\\n        self._name = name\\n        self.value = value\\n        self.key_flag = key_flag\\n        self.value_flags = value_flags\\n        self._validation = validation\\n\\n    def __eq__(self, other):\\n        if type(other) is type(self):\\n            return self.value == other.value\\n        return False\\n\\n    def __hash__(self):\\n        return hash(self.value)\\n\\n    def collect_errors(self, instance, typed_value, source=\\\"<<merged>>\\\"):\\n        \\\"\\\"\\\"\\n        Validate a LoadedParameter typed value.\\n\\n        Args:\\n            instance (Configuration): the instance object used to create the LoadedParameter.\\n            typed_value (Any): typed value to validate.\\n            source (str): string description for the source of the typed_value.\\n        \\\"\\\"\\\"\\n        errors = []\\n        if not isinstance(typed_value, self._type):\\n            errors.append(\\n                InvalidTypeError(\\n                    self._name, typed_value, source, type(self.value), self._type\\n                )\\n            )\\n        elif self._validation is not None:\\n            result = self._validation(typed_value)\\n            if result is False:\\n                errors.append(ValidationError(self._name, typed_value, source))\\n            elif isinstance(result, str):\\n                errors.append(\\n                    CustomValidationError(self._name, typed_value, source, result)\\n                )\\n        return errors\\n\\n    def expand(self):\\n        \\\"\\\"\\\"\\n        Recursively expands any environment values in the Loaded Parameter.\\n\\n        Returns: LoadedParameter\\n        \\\"\\\"\\\"\\n        # This is similar to conda.auxlib.type_coercion.typify_data_structure\\n        # It could be DRY-er but that would break SRP.\\n        if isinstance(self.value, Mapping):\\n            new_value = type(self.value)((k, v.expand()) for k, v in self.value.items())\\n        elif isiterable(self.value):\\n            new_value = type(self.value)(v.expand() for v in self.value)\\n        elif isinstance(self.value, ConfigurationObject):\\n            for attr_name, attr_value in vars(self.value).items():\\n                if isinstance(attr_value, LoadedParameter):\\n                    self.value.__setattr__(attr_name, attr_value.expand())\\n            return self.value\\n        else:\\n            new_value = expand_environment_variables(self.value)\\n        self.value = new_value\\n        return self\\n\\n    @abstractmethod\\n    def merge(self, matches):\\n        \\\"\\\"\\\"\\n        Recursively merges matches into one LoadedParameter.\\n\\n        Args:\\n            matches (List<LoadedParameter>): list of matches of this parameter.\\n\\n        Returns: LoadedParameter\\n        \\\"\\\"\\\"\\n        raise NotImplementedError()\\n\\n    def typify(self, source):\\n        \\\"\\\"\\\"\\n        Recursively types a LoadedParameter.\\n\\n        Args:\\n            source (str): string describing the source of the LoadedParameter.\\n\\n        Returns: a primitive, sequence, or map representing the typed value.\\n        \\\"\\\"\\\"\\n        element_type = self._element_type\\n        try:\\n            return LoadedParameter._typify_data_structure(\\n                self.value, source, element_type\\n            )\\n        except TypeCoercionError as e:\\n            msg = str(e)\\n            if issubclass(element_type, Enum):\\n                choices = \\\", \\\".join(\\n                    map(\\\"'{}'\\\".format, element_type.__members__.values())\\n                )\\n                msg += f\\\"\\\\nValid choices for {self._name}: {choices}\\\"\\n            raise CustomValidationError(self._name, e.value, source, msg)\\n\\n    @staticmethod\\n    def _typify_data_structure(value, source, type_hint=None):\\n        if isinstance(value, Mapping):\\n            return type(value)((k, v.typify(source)) for k, v in value.items())\\n        elif isiterable(value):\\n            return type(value)(v.typify(source) for v in value)\\n        elif isinstance(value, ConfigurationObject):\\n            for attr_name, attr_value in vars(value).items():\\n                if isinstance(attr_value, LoadedParameter):\\n                    value.__setattr__(attr_name, attr_value.typify(source))\\n            return value\\n        elif (\\n            isinstance(value, str)\\n            and isinstance(type_hint, type)\\n            and issubclass(type_hint, str)\\n        ):\\n            # This block is necessary because if we fall through to typify(), we end up calling\\n            # .strip() on the str, when sometimes we want to preserve preceding and trailing\\n            # whitespace.\\n            return type_hint(value)\\n        else:\\n            return typify(value, type_hint)\\n\\n    @staticmethod\\n    def _match_key_is_important(loaded_parameter):\\n        return loaded_parameter.key_flag is ParameterFlag.final\\n\\n    @staticmethod\\n    def _first_important_matches(matches):\\n        idx = first(\\n            enumerate(matches),\\n            lambda x: LoadedParameter._match_key_is_important(x[1]),\\n            apply=lambda x: x[0],\\n        )\\n        return matches if idx is None else matches[: idx + 1]\\n\\n\\nclass PrimitiveLoadedParameter(LoadedParameter):\\n    \\\"\\\"\\\"\\n    LoadedParameter type that holds a single python primitive value.\\n\\n    The python primitive types are str, int, float, complex, bool, and NoneType. In addition,\\n    python 2 has long and unicode types.\\n    \\\"\\\"\\\"\\n\\n    def __init__(\\n        self, name, element_type, value, key_flag, value_flags, validation=None\\n    ):\\n        \\\"\\\"\\\"\\n        Args:\\n            element_type (type or tuple[type]): Type-validation of parameter's value.\\n            value (primitive value): primitive python value.\\n        \\\"\\\"\\\"\\n        self._type = element_type\\n        self._element_type = element_type\\n        super().__init__(name, value, key_flag, value_flags, validation)\\n\\n    def __eq__(self, other):\\n        if type(other) is type(self):\\n            return self.value == other.value\\n        return False\\n\\n    def __hash__(self):\\n        return hash(self.value)\\n\\n    def merge(self, matches):\\n        important_match = first(\\n            matches, LoadedParameter._match_key_is_important, default=None\\n        )\\n        if important_match is not None:\\n            return important_match\\n\\n        last_match = last(matches, lambda x: x is not None, default=None)\\n        if last_match is not None:\\n            return last_match\\n        raise ThisShouldNeverHappenError()  # pragma: no cover\\n\\n\\nclass MapLoadedParameter(LoadedParameter):\\n    \\\"\\\"\\\"LoadedParameter type that holds a map (i.e. dict) of LoadedParameters.\\\"\\\"\\\"\\n\\n    _type = frozendict\\n\\n    def __init__(\\n        self, name, value, element_type, key_flag, value_flags, validation=None\\n    ):\\n        \\\"\\\"\\\"\\n        Args:\\n            value (Mapping): Map of string keys to LoadedParameter values.\\n            element_type (Parameter): The Parameter type that is held in value.\\n            value_flags (Mapping): Map of priority value flags.\\n        \\\"\\\"\\\"\\n        self._element_type = element_type\\n        super().__init__(name, value, key_flag, value_flags, validation)\\n\\n    def collect_errors(self, instance, typed_value, source=\\\"<<merged>>\\\"):\\n        errors = super().collect_errors(instance, typed_value, self.value)\\n\\n        # recursively validate the values in the map\\n        if isinstance(self.value, Mapping):\\n            for key, value in self.value.items():\\n                errors.extend(value.collect_errors(instance, typed_value[key], source))\\n        return errors\\n\\n    def merge(self, parameters: Sequence[MapLoadedParameter]) -> MapLoadedParameter:\\n        # get all values up to and including first important_match\\n        # but if no important_match, then all matches are important_matches\\n        parameters = LoadedParameter._first_important_matches(parameters)\\n\\n        # ensure all parameter values are Mappings\\n        for parameter in parameters:\\n            if not isinstance(parameter.value, Mapping):\\n                raise InvalidTypeError(\\n                    self.name,\\n                    parameter.value,\\n                    parameter.source,\\n                    parameter.value.__class__.__name__,\\n                    self._type.__name__,\\n                )\\n\\n        # map keys with final values,\\n        # first key has higher precedence than later ones\\n        final_map = {\\n            key: value\\n            for parameter in reversed(parameters)\\n            for key, value in parameter.value.items()\\n            if parameter.value_flags.get(key) == ParameterFlag.final\\n        }\\n\\n        # map each value by recursively calling merge on any entries with the same key,\\n        # last key has higher precedence than earlier ones\\n        grouped_map = {}\\n        for parameter in parameters:\\n            for key, value in parameter.value.items():\\n                grouped_map.setdefault(key, []).append(value)\\n        merged_map = {\\n            key: values[0].merge(values) for key, values in grouped_map.items()\\n        }\\n\\n        # update merged_map with final_map values\\n        merged_value = frozendict({**merged_map, **final_map})\\n\\n        # create new parameter for the merged values\\n        return MapLoadedParameter(\\n            self._name,\\n            merged_value,\\n            self._element_type,\\n            self.key_flag,\\n            self.value_flags,\\n            validation=self._validation,\\n        )\\n\\n\\nclass SequenceLoadedParameter(LoadedParameter):\\n    \\\"\\\"\\\"LoadedParameter type that holds a sequence (i.e. list) of LoadedParameters.\\\"\\\"\\\"\\n\\n    _type = tuple\\n\\n    def __init__(\\n        self, name, value, element_type, key_flag, value_flags, validation=None\\n    ):\\n        \\\"\\\"\\\"\\n        Args:\\n            value (Sequence): Sequence of LoadedParameter values.\\n            element_type (Parameter): The Parameter type that is held in the sequence.\\n            value_flags (Sequence): Sequence of priority value_flags.\\n        \\\"\\\"\\\"\\n        self._element_type = element_type\\n        super().__init__(name, value, key_flag, value_flags, validation)\\n\\n    def collect_errors(self, instance, typed_value, source=\\\"<<merged>>\\\"):\\n        errors = super().collect_errors(instance, typed_value, self.value)\\n        # recursively collect errors on the elements in the sequence\\n        for idx, element in enumerate(self.value):\\n            errors.extend(element.collect_errors(instance, typed_value[idx], source))\\n        return errors\\n\\n    def merge(self, matches):\\n        # get matches up to and including first important_match\\n        # but if no important_match, then all matches are important_matches\\n        relevant_matches_and_values = tuple(\\n            (match, match.value)\\n            for match in LoadedParameter._first_important_matches(matches)\\n        )\\n        for match, value in relevant_matches_and_values:\\n            if not isinstance(value, tuple):\\n                raise InvalidTypeError(\\n                    self.name,\\n                    value,\\n                    match.source,\\n                    value.__class__.__name__,\\n                    self._type.__name__,\\n                )\\n\\n        # get individual lines from important_matches that were marked important\\n        # these will be prepended to the final result\\n        def get_marked_lines(match, marker):\\n            return (\\n                tuple(\\n                    line\\n                    for line, flag in zip(match.value, match.value_flags)\\n                    if flag is marker\\n                )\\n                if match\\n                else ()\\n            )\\n\\n        top_lines = chain.from_iterable(\\n            get_marked_lines(m, ParameterFlag.top)\\n            for m, _ in relevant_matches_and_values\\n        )\\n\\n        # also get lines that were marked as bottom, but reverse the match order so that lines\\n        # coming earlier will ultimately be last\\n        bottom_lines = tuple(\\n            chain.from_iterable(\\n                get_marked_lines(match, ParameterFlag.bottom)\\n                for match, _ in reversed(relevant_matches_and_values)\\n            )\\n        )\\n\\n        # now, concat all lines, while reversing the matches\\n        #   reverse because elements closer to the end of search path take precedence\\n        all_lines = chain.from_iterable(\\n            v for _, v in reversed(relevant_matches_and_values)\\n        )\\n\\n        # stack top_lines + all_lines, then de-dupe\\n        top_deduped = tuple(unique((*top_lines, *all_lines)))\\n\\n        # take the top-deduped lines, reverse them, and concat with reversed bottom_lines\\n        # this gives us the reverse of the order we want, but almost there\\n        # NOTE: for a line value marked both top and bottom, the bottom marker will win out\\n        #       for the top marker to win out, we'd need one additional de-dupe step\\n        bottom_deduped = tuple(\\n            unique((*reversed(bottom_lines), *reversed(top_deduped)))\\n        )\\n        # just reverse, and we're good to go\\n        merged_values = tuple(reversed(bottom_deduped))\\n\\n        return SequenceLoadedParameter(\\n            self._name,\\n            merged_values,\\n            self._element_type,\\n            self.key_flag,\\n            self.value_flags,\\n            validation=self._validation,\\n        )\\n\\n\\nclass ObjectLoadedParameter(LoadedParameter):\\n    \\\"\\\"\\\"LoadedParameter type that holds a mapping (i.e. object) of LoadedParameters.\\\"\\\"\\\"\\n\\n    _type = object\\n\\n    def __init__(\\n        self, name, value, element_type, key_flag, value_flags, validation=None\\n    ):\\n        \\\"\\\"\\\"\\n        Args:\\n            value (Sequence): Object with LoadedParameter fields.\\n            element_type (object): The Parameter type that is held in the sequence.\\n            value_flags (Sequence): Sequence of priority value_flags.\\n        \\\"\\\"\\\"\\n        self._element_type = element_type\\n        super().__init__(name, value, key_flag, value_flags, validation)\\n\\n    def collect_errors(self, instance, typed_value, source=\\\"<<merged>>\\\"):\\n        errors = super().collect_errors(instance, typed_value, self.value)\\n\\n        # recursively validate the values in the object fields\\n        if isinstance(self.value, ConfigurationObject):\\n            for key, value in vars(self.value).items():\\n                if isinstance(value, LoadedParameter):\\n                    errors.extend(\\n                        value.collect_errors(instance, typed_value[key], source)\\n                    )\\n        return errors\\n\\n    def merge(\\n        self, parameters: Sequence[ObjectLoadedParameter]\\n    ) -> ObjectLoadedParameter:\\n        # get all parameters up to and including first important_match\\n        # but if no important_match, then all parameters are important_matches\\n        parameters = LoadedParameter._first_important_matches(parameters)\\n\\n        # map keys with final values,\\n        # first key has higher precedence than later ones\\n        final_map = {\\n            key: value\\n            for parameter in reversed(parameters)\\n            for key, value in vars(parameter.value).items()\\n            if (\\n                isinstance(value, LoadedParameter)\\n                and parameter.value_flags.get(key) == ParameterFlag.final\\n            )\\n        }\\n\\n        # map each value by recursively calling merge on any entries with the same key,\\n        # last key has higher precedence than earlier ones\\n        grouped_map = {}\\n        for parameter in parameters:\\n            for key, value in vars(parameter.value).items():\\n                grouped_map.setdefault(key, []).append(value)\\n        merged_map = {\\n            key: values[0].merge(values) for key, values in grouped_map.items()\\n        }\\n\\n        # update merged_map with final_map values\\n        merged_value = copy.deepcopy(self._element_type)\\n        for key, value in {**merged_map, **final_map}.items():\\n            merged_value.__setattr__(key, value)\\n\\n        # create new parameter for the merged values\\n        return ObjectLoadedParameter(\\n            self._name,\\n            merged_value,\\n            self._element_type,\\n            self.key_flag,\\n            self.value_flags,\\n            validation=self._validation,\\n        )\\n\\n\\nclass ConfigurationObject:\\n    \\\"\\\"\\\"Dummy class to mark whether a Python object has config parameters within.\\\"\\\"\\\"\\n\\n\\nclass Parameter(metaclass=ABCMeta):\\n    # (type) describes the type of parameter\\n    _type = None\\n    # (Parameter or type) if the Parameter is holds a collection, describes the element held in\\n    # the collection. if not, describes the primitive type held by the Parameter.\\n    _element_type = None\\n\\n    def __init__(self, default, validation=None):\\n        \\\"\\\"\\\"\\n        The Parameter class represents an unloaded configuration parameter, holding type, default\\n        and validation information until the parameter is loaded with a configuration.\\n\\n        Args:\\n            default (Any): the typed, python representation default value given if the Parameter\\n                is not found in a Configuration.\\n            validation (callable): Given a parameter value as input, return a boolean indicating\\n                validity, or alternately return a string describing an invalid value.\\n        \\\"\\\"\\\"\\n        self._default = default\\n        self._validation = validation\\n\\n    @property\\n    def default(self):\\n        \\\"\\\"\\\"Returns a DefaultValueRawParameter that wraps the actual default value.\\\"\\\"\\\"\\n        wrapped_default = DefaultValueRawParameter(\\\"default\\\", \\\"default\\\", self._default)\\n        return self.load(\\\"default\\\", wrapped_default)\\n\\n    def get_all_matches(self, name, names, instance):\\n        \\\"\\\"\\\"\\n        Finds all matches of a Parameter in a Configuration instance\\n\\n        Args:\\n            name (str): canonical name of the parameter to search for\\n            names (tuple(str)): alternative aliases of the parameter\\n            instance (Configuration): instance of the configuration to search within\\n\\n        Returns (List(RawParameter)): matches of the parameter found in the configuration.\\n        \\\"\\\"\\\"\\n        matches = []\\n        multikey_exceptions = []\\n        for filepath, raw_parameters in instance.raw_data.items():\\n            match, error = ParameterLoader.raw_parameters_from_single_source(\\n                name, names, raw_parameters\\n            )\\n            if match is not None:\\n                matches.append(match)\\n            if error:\\n                multikey_exceptions.append(error)\\n        return matches, multikey_exceptions\\n\\n    @abstractmethod\\n    def load(self, name, match):\\n        \\\"\\\"\\\"\\n        Loads a Parameter with the value in a RawParameter.\\n\\n        Args:\\n            name (str): name of the parameter to pass through\\n            match (RawParameter): the value of the RawParameter match\\n\\n        Returns a LoadedParameter\\n        \\\"\\\"\\\"\\n        raise NotImplementedError()\\n\\n    def typify(self, name, source, value):\\n        element_type = self._element_type\\n        try:\\n            return typify_data_structure(value, element_type)\\n        except TypeCoercionError as e:\\n            msg = str(e)\\n            if issubclass(element_type, Enum):\\n                choices = \\\", \\\".join(\\n                    map(\\\"'{}'\\\".format, element_type.__members__.values())\\n                )\\n                msg += f\\\"\\\\nValid choices for {name}: {choices}\\\"\\n            raise CustomValidationError(name, e.value, source, msg)\\n\\n\\nclass PrimitiveParameter(Parameter):\\n    \\\"\\\"\\\"\\n    Parameter type for a Configuration class that holds a single python primitive value.\\n\\n    The python primitive types are str, int, float, complex, bool, and NoneType. In addition,\\n    python 2 has long and unicode types.\\n    \\\"\\\"\\\"\\n\\n    def __init__(self, default, element_type=None, validation=None):\\n        \\\"\\\"\\\"\\n        Args:\\n            default (primitive value): default value if the Parameter is not found.\\n            element_type (type or tuple[type]): Type-validation of parameter's value. If None,\\n                type(default) is used.\\n        \\\"\\\"\\\"\\n        self._type = type(default) if element_type is None else element_type\\n        self._element_type = self._type\\n        super().__init__(default, validation)\\n\\n    def load(self, name, match):\\n        return PrimitiveLoadedParameter(\\n            name,\\n            self._type,\\n            match.value(self._element_type),\\n            match.keyflag(),\\n            match.valueflags(self._element_type),\\n            validation=self._validation,\\n        )\\n\\n\\nclass MapParameter(Parameter):\\n    \\\"\\\"\\\"Parameter type for a Configuration class that holds a map (i.e. dict) of Parameters.\\\"\\\"\\\"\\n\\n    _type = frozendict\\n\\n    def __init__(self, element_type, default=frozendict(), validation=None):\\n        \\\"\\\"\\\"\\n        Args:\\n            element_type (Parameter): The Parameter type held in the MapParameter.\\n            default (Mapping):  The parameter's default value. If None, will be an empty dict.\\n        \\\"\\\"\\\"\\n        self._element_type = element_type\\n        default = default and frozendict(default) or frozendict()\\n        super().__init__(default, validation=validation)\\n\\n    def get_all_matches(self, name, names, instance):\\n        # it also config settings like `proxy_servers: ~`\\n        matches, exceptions = super().get_all_matches(name, names, instance)\\n        matches = tuple(m for m in matches if m._raw_value is not None)\\n        return matches, exceptions\\n\\n    def load(self, name, match):\\n        value = match.value(self._element_type)\\n        if value is None:\\n            return MapLoadedParameter(\\n                name,\\n                frozendict(),\\n                self._element_type,\\n                match.keyflag(),\\n                frozendict(),\\n                validation=self._validation,\\n            )\\n\\n        if not isinstance(value, Mapping):\\n            raise InvalidTypeError(\\n                name, value, match.source, value.__class__.__name__, self._type.__name__\\n            )\\n\\n        loaded_map = {}\\n        for key, child_value in match.value(self._element_type).items():\\n            loaded_child_value = self._element_type.load(name, child_value)\\n            loaded_map[key] = loaded_child_value\\n\\n        return MapLoadedParameter(\\n            name,\\n            frozendict(loaded_map),\\n            self._element_type,\\n            match.keyflag(),\\n            match.valueflags(self._element_type),\\n            validation=self._validation,\\n        )\\n\\n\\nclass SequenceParameter(Parameter):\\n    \\\"\\\"\\\"Parameter type for a Configuration class that holds a sequence (i.e. list) of Parameters.\\\"\\\"\\\"\\n\\n    _type = tuple\\n\\n    def __init__(self, element_type, default=(), validation=None, string_delimiter=\\\",\\\"):\\n        \\\"\\\"\\\"\\n        Args:\\n            element_type (Parameter): The Parameter type that is held in the sequence.\\n            default (Sequence): default value, empty tuple if not given.\\n            string_delimiter (str): separation string used to parse string into sequence.\\n        \\\"\\\"\\\"\\n        self._element_type = element_type\\n        self.string_delimiter = string_delimiter\\n        super().__init__(default, validation)\\n\\n    def get_all_matches(self, name, names, instance):\\n        # this is necessary to handle argparse `action=\\\"append\\\"`, which can't be set to a\\n        #   default value of NULL\\n        # it also config settings like `channels: ~`\\n        matches, exceptions = super().get_all_matches(name, names, instance)\\n        matches = tuple(m for m in matches if m._raw_value is not None)\\n        return matches, exceptions\\n\\n    def load(self, name, match):\\n        value = match.value(self)\\n        if value is None:\\n            return SequenceLoadedParameter(\\n                name,\\n                (),\\n                self._element_type,\\n                match.keyflag(),\\n                (),\\n                validation=self._validation,\\n            )\\n\\n        if not isiterable(value):\\n            raise InvalidTypeError(\\n                name, value, match.source, value.__class__.__name__, self._type.__name__\\n            )\\n\\n        loaded_sequence = []\\n        for child_value in value:\\n            loaded_child_value = self._element_type.load(name, child_value)\\n            loaded_sequence.append(loaded_child_value)\\n\\n        return SequenceLoadedParameter(\\n            name,\\n            tuple(loaded_sequence),\\n            self._element_type,\\n            match.keyflag(),\\n            match.valueflags(self._element_type),\\n            validation=self._validation,\\n        )\\n\\n\\nclass ObjectParameter(Parameter):\\n    \\\"\\\"\\\"Parameter type for a Configuration class that holds an object with Parameter fields.\\\"\\\"\\\"\\n\\n    _type = object\\n\\n    def __init__(self, element_type, default=ConfigurationObject(), validation=None):\\n        \\\"\\\"\\\"\\n        Args:\\n            element_type (object): The object type with parameter fields held in ObjectParameter.\\n            default (Sequence): default value, empty tuple if not given.\\n        \\\"\\\"\\\"\\n        self._element_type = element_type\\n        super().__init__(default, validation)\\n\\n    def get_all_matches(self, name, names, instance):\\n        # it also config settings like `proxy_servers: ~`\\n        matches, exceptions = super().get_all_matches(name, names, instance)\\n        matches = tuple(m for m in matches if m._raw_value is not None)\\n        return matches, exceptions\\n\\n    def load(self, name, match):\\n        value = match.value(self._element_type)\\n        if value is None:\\n            return ObjectLoadedParameter(\\n                name,\\n                None,\\n                self._element_type,\\n                match.keyflag(),\\n                None,\\n                validation=self._validation,\\n            )\\n\\n        if not isinstance(value, (Mapping, ConfigurationObject)):\\n            raise InvalidTypeError(\\n                name, value, match.source, value.__class__.__name__, self._type.__name__\\n            )\\n\\n        # for a default object, extract out the instance variables\\n        if isinstance(value, ConfigurationObject):\\n            value = vars(value)\\n\\n        object_parameter_attrs = {\\n            attr_name: parameter_type\\n            for attr_name, parameter_type in vars(self._element_type).items()\\n            if isinstance(parameter_type, Parameter) and attr_name in value.keys()\\n        }\\n\\n        # recursively load object fields\\n        loaded_attrs = {}\\n        for attr_name, parameter_type in object_parameter_attrs.items():\\n            raw_child_value = value.get(attr_name)\\n            loaded_child_value = parameter_type.load(name, raw_child_value)\\n            loaded_attrs[attr_name] = loaded_child_value\\n\\n        # copy object and replace Parameter with LoadedParameter fields\\n        object_copy = copy.deepcopy(self._element_type)\\n        for attr_name, loaded_child_parameter in loaded_attrs.items():\\n            object_copy.__setattr__(attr_name, loaded_child_parameter)\\n\\n        return ObjectLoadedParameter(\\n            name,\\n            object_copy,\\n            self._element_type,\\n            match.keyflag(),\\n            match.valueflags(self._element_type),\\n            validation=self._validation,\\n        )\\n\\n\\nclass ParameterLoader:\\n    \\\"\\\"\\\"\\n    ParameterLoader class contains the top level logic needed to load a parameter from start to\\n    finish.\\n    \\\"\\\"\\\"\\n\\n    def __init__(self, parameter_type, aliases=(), expandvars=False):\\n        \\\"\\\"\\\"\\n        Args:\\n            parameter_type (Parameter): the type of Parameter that is stored in the loader.\\n            aliases (tuple(str)): alternative aliases for the Parameter\\n            expandvars (bool): whether or not to recursively expand environmental variables.\\n        \\\"\\\"\\\"\\n        self._name = None\\n        self._names = None\\n        self.type = parameter_type\\n        self.aliases = aliases\\n        self._expandvars = expandvars\\n\\n    def _set_name(self, name):\\n        # this is an explicit method, and not a descriptor/setter\\n        # it's meant to be called by the Configuration metaclass\\n        self._name = name\\n        _names = frozenset(x for x in chain(self.aliases, (name,)))\\n        self._names = _names\\n        return name\\n\\n    @property\\n    def name(self):\\n        if self._name is None:\\n            # The Configuration metaclass should call the `_set_name` method.\\n            raise ThisShouldNeverHappenError()  # pragma: no cover\\n        return self._name\\n\\n    @property\\n    def names(self):\\n        if self._names is None:\\n            # The Configuration metaclass should call the `_set_name` method.\\n            raise ThisShouldNeverHappenError()  # pragma: no cover\\n        return self._names\\n\\n    def __get__(self, instance, instance_type):\\n        # strategy is \\\"extract and merge,\\\" which is actually just map and reduce\\n        # extract matches from each source in SEARCH_PATH\\n        # then merge matches together\\n        if self.name in instance._cache_:\\n            return instance._cache_[self.name]\\n\\n        # step 1/2: load config and find top level matches\\n        raw_matches, errors = self.type.get_all_matches(self.name, self.names, instance)\\n\\n        # step 3: parse RawParameters into LoadedParameters\\n        matches = [self.type.load(self.name, match) for match in raw_matches]\\n\\n        # step 4: merge matches\\n        merged = matches[0].merge(matches) if matches else self.type.default\\n\\n        # step 5: typify\\n        # We need to expand any environment variables before type casting.\\n        # Otherwise e.g. `my_bool_var: $BOOL` with BOOL=True would raise a TypeCoercionError.\\n        expanded = merged.expand() if self._expandvars else merged\\n        try:\\n            result = expanded.typify(\\\"<<merged>>\\\")\\n        except CustomValidationError as e:\\n            errors.append(e)\\n        else:\\n            errors.extend(expanded.collect_errors(instance, result, \\\"<<merged>>\\\"))\\n        raise_errors(errors)\\n        instance._cache_[self.name] = result\\n        return result\\n\\n    def _raw_parameters_from_single_source(self, raw_parameters):\\n        return ParameterLoader.raw_parameters_from_single_source(\\n            self.name, self.names, raw_parameters\\n        )\\n\\n    @staticmethod\\n    def raw_parameters_from_single_source(name, names, raw_parameters):\\n        # while supporting parameter name aliases, we enforce that only one definition is given\\n        # per data source\\n        keys = names & frozenset(raw_parameters.keys())\\n        matches = {key: raw_parameters[key] for key in keys}\\n        numkeys = len(keys)\\n        if numkeys == 0:\\n            return None, None\\n        elif numkeys == 1:\\n            return next(iter(matches.values())), None\\n        elif name in keys:\\n            return matches[name], MultipleKeysError(\\n                raw_parameters[next(iter(keys))].source, keys, name\\n            )\\n        else:\\n            return None, MultipleKeysError(\\n                raw_parameters[next(iter(keys))].source, keys, name\\n            )\\n\\n\\nclass ConfigurationType(type):\\n    \\\"\\\"\\\"metaclass for Configuration\\\"\\\"\\\"\\n\\n    def __init__(cls, name, bases, attr):\\n        super().__init__(name, bases, attr)\\n\\n        # call _set_name for each parameter\\n        cls.parameter_names = tuple(\\n            p._set_name(name)\\n            for name, p in cls.__dict__.items()\\n            if isinstance(p, ParameterLoader)\\n        )\\n\\n\\nCONDARC_FILENAMES = (\\\".condarc\\\", \\\"condarc\\\")\\nYAML_EXTENSIONS = (\\\".yml\\\", \\\".yaml\\\")\\n_RE_CUSTOM_EXPANDVARS = compile(\\n    rf\\\"\\\"\\\"\\n    # delimiter and a Python identifier\\n    \\\\$(?P<named>{Template.idpattern}) |\\n\\n    # delimiter and a braced identifier\\n    \\\\${{(?P<braced>{Template.idpattern})}} |\\n\\n    # delimiter padded identifier\\n    %(?P<padded>{Template.idpattern})%\\n    \\\"\\\"\\\",\\n    flags=IGNORECASE | VERBOSE,\\n)\\n\\n\\ndef custom_expandvars(\\n    template: str, mapping: Mapping[str, Any] = {}, /, **kwargs\\n) -> str:\\n    \\\"\\\"\\\"Expand variables in a string.\\n\\n    Inspired by `string.Template` and modified to mirror `os.path.expandvars` functionality\\n    allowing custom variables without mutating `os.environ`.\\n\\n    Expands POSIX and Windows CMD environment variables as follows:\\n\\n    - $VARIABLE → value of VARIABLE\\n    - ${VARIABLE} → value of VARIABLE\\n    - %VARIABLE% → value of VARIABLE\\n\\n    Invalid substitutions are left as-is:\\n\\n    - $MISSING → $MISSING\\n    - ${MISSING} → ${MISSING}\\n    - %MISSING% → %MISSING%\\n    - $$ → $$\\n    - %% → %%\\n    - $ → $\\n    - % → %\\n    \\\"\\\"\\\"\\n    mapping = {**mapping, **kwargs}\\n\\n    def convert(match: Match):\\n        return str(\\n            mapping.get(\\n                match.group(\\\"named\\\") or match.group(\\\"braced\\\") or match.group(\\\"padded\\\"),\\n                match.group(),  # fallback to the original string\\n            )\\n        )\\n\\n    return _RE_CUSTOM_EXPANDVARS.sub(convert, template)\\n\\n\\nclass Configuration(metaclass=ConfigurationType):\\n    def __init__(self, search_path=(), app_name=None, argparse_args=None, **kwargs):\\n        # Currently, __init__ does a **full** disk reload of all files.\\n        # A future improvement would be to cache files that are already loaded.\\n        self.raw_data = {}\\n        self._cache_ = {}\\n        self._reset_callbacks = IndexedSet()\\n        self._validation_errors = defaultdict(list)\\n\\n        self._set_search_path(search_path, **kwargs)\\n        self._set_env_vars(app_name)\\n        self._set_argparse_args(argparse_args)\\n\\n    @staticmethod\\n    def _expand_search_path(\\n        search_path: Iterable[Path | str],\\n        **kwargs,\\n    ) -> Iterable[Path]:\\n        for search in search_path:\\n            # use custom_expandvars instead of os.path.expandvars so additional variables can be\\n            # passed in without mutating os.environ\\n            if isinstance(search, Path):\\n                path = search\\n            else:\\n                template = custom_expandvars(search, environ, **kwargs)\\n                path = Path(template).expanduser()\\n\\n            if path.is_file() and (\\n                path.name in CONDARC_FILENAMES or path.suffix in YAML_EXTENSIONS\\n            ):\\n                yield path\\n            elif path.is_dir():\\n                yield from (\\n                    subpath\\n                    for subpath in sorted(path.iterdir())\\n                    if subpath.is_file() and subpath.suffix in YAML_EXTENSIONS\\n                )\\n\\n    @classmethod\\n    def _load_search_path(\\n        cls,\\n        search_path: Iterable[Path],\\n    ) -> Iterable[tuple[Path, dict]]:\\n        for path in search_path:\\n            try:\\n                yield path, YamlRawParameter.make_raw_parameters_from_file(path)\\n            except ConfigurationLoadError as err:\\n                log.warning(\\n                    \\\"Ignoring configuration file (%s) due to error:\\\\n%s\\\",\\n                    path,\\n                    err,\\n                )\\n\\n    def _set_search_path(self, search_path: Iterable[Path | str], **kwargs):\\n        self._search_path = IndexedSet(self._expand_search_path(search_path, **kwargs))\\n\\n        self._set_raw_data(dict(self._load_search_path(self._search_path)))\\n\\n        self._reset_cache()\\n        return self\\n\\n    def _set_env_vars(self, app_name=None):\\n        self._app_name = app_name\\n\\n        # remove existing source so \\\"insert\\\" order is correct\\n        source = EnvRawParameter.source\\n        if source in self.raw_data:\\n            del self.raw_data[source]\\n\\n        if app_name:\\n            self.raw_data[source] = EnvRawParameter.make_raw_parameters(app_name)\\n\\n        self._reset_cache()\\n        return self\\n\\n    def _set_argparse_args(self, argparse_args):\\n        # the argparse_args we store internally in this class as self._argparse_args\\n        #   will be a mapping type, not a non-`dict` object like argparse_args is natively\\n        if hasattr(argparse_args, \\\"__dict__\\\"):\\n            # the argparse_args from argparse will be an object with a __dict__ attribute\\n            #   and not a mapping type like this method will turn it into\\n            items = vars(argparse_args).items()\\n        elif not argparse_args:\\n            # argparse_args can be initialized as `None`\\n            items = ()\\n        else:\\n            # we're calling this method with argparse_args that are a mapping type, likely\\n            #   already having been processed by this method before\\n            items = argparse_args.items()\\n\\n        self._argparse_args = argparse_args = AttrDict(\\n            {k: v for k, v in items if v is not NULL}\\n        )\\n\\n        # remove existing source so \\\"insert\\\" order is correct\\n        source = ArgParseRawParameter.source\\n        if source in self.raw_data:\\n            del self.raw_data[source]\\n\\n        self.raw_data[source] = ArgParseRawParameter.make_raw_parameters(argparse_args)\\n\\n        self._reset_cache()\\n        return self\\n\\n    def _set_raw_data(self, raw_data: Mapping[Hashable, dict]):\\n        self.raw_data.update(raw_data)\\n        self._reset_cache()\\n        return self\\n\\n    def _reset_cache(self):\\n        self._cache_ = {}\\n        for callback in self._reset_callbacks:\\n            callback()\\n        return self\\n\\n    def register_reset_callaback(self, callback):\\n        self._reset_callbacks.add(callback)\\n\\n    def check_source(self, source):\\n        # this method ends up duplicating much of the logic of Parameter.__get__\\n        # I haven't yet found a way to make it more DRY though\\n        typed_values = {}\\n        validation_errors = []\\n        raw_parameters = self.raw_data[source]\\n        for key in self.parameter_names:\\n            parameter = self.__class__.__dict__[key]\\n            match, multikey_error = parameter._raw_parameters_from_single_source(\\n                raw_parameters\\n            )\\n            if multikey_error:\\n                validation_errors.append(multikey_error)\\n\\n            if match is not None:\\n                loaded_parameter = parameter.type.load(key, match)\\n                # untyped_value = loaded_parameter.value\\n                # if untyped_value is None:\\n                #     if isinstance(parameter, SequenceLoadedParameter):\\n                #         untyped_value = ()\\n                #     elif isinstance(parameter, MapLoadedParameter):\\n                #         untyped_value = {}\\n                try:\\n                    typed_value = loaded_parameter.typify(match.source)\\n                except CustomValidationError as e:\\n                    validation_errors.append(e)\\n                else:\\n                    collected_errors = loaded_parameter.collect_errors(\\n                        self, typed_value, match.source\\n                    )\\n                    if collected_errors:\\n                        validation_errors.extend(collected_errors)\\n                    else:\\n                        typed_values[match.key] = typed_value\\n            else:\\n                # this situation will happen if there is a multikey_error and none of the\\n                # matched keys is the primary key\\n                pass\\n        return typed_values, validation_errors\\n\\n    def validate_all(self):\\n        validation_errors = list(\\n            chain.from_iterable(\\n                self.check_source(source)[1] for source in self.raw_data\\n            )\\n        )\\n        raise_errors(validation_errors)\\n        self.validate_configuration()\\n\\n    @staticmethod\\n    def _collect_validation_error(func, *args, **kwargs):\\n        try:\\n            func(*args, **kwargs)\\n        except ConfigurationError as e:\\n            return (e.errors if hasattr(e, \\\"errors\\\") else e,)\\n        return ()\\n\\n    def validate_configuration(self):\\n        errors = chain.from_iterable(\\n            Configuration._collect_validation_error(getattr, self, name)\\n            for name in self.parameter_names\\n        )\\n        post_errors = self.post_build_validation()\\n        raise_errors(tuple(chain.from_iterable((errors, post_errors))))\\n\\n    def post_build_validation(self):\\n        return ()\\n\\n    def collect_all(self):\\n        typed_values = {}\\n        validation_errors = {}\\n        for source in self.raw_data:\\n            typed_values[source], validation_errors[source] = self.check_source(source)\\n        raise_errors(tuple(chain.from_iterable(validation_errors.values())))\\n        return {k: v for k, v in typed_values.items() if v}\\n\\n    def describe_parameter(self, parameter_name):\\n        # TODO, in Parameter base class, rename element_type to value_type\\n        if parameter_name not in self.parameter_names:\\n            parameter_name = \\\"_\\\" + parameter_name\\n        parameter_loader = self.__class__.__dict__[parameter_name]\\n        parameter = parameter_loader.type\\n        assert isinstance(parameter, Parameter)\\n\\n        # dedupe leading underscore from name\\n        name = parameter_loader.name.lstrip(\\\"_\\\")\\n        aliases = tuple(alias for alias in parameter_loader.aliases if alias != name)\\n\\n        description = self.get_descriptions().get(name, \\\"\\\")\\n        et = parameter._element_type\\n        if type(et) == EnumMeta:  # noqa: E721\\n            et = [et]\\n        if not isiterable(et):\\n            et = [et]\\n\\n        if isinstance(parameter._element_type, Parameter):\\n            element_types = tuple(\\n                _et.__class__.__name__.lower().replace(\\\"parameter\\\", \\\"\\\") for _et in et\\n            )\\n        else:\\n            element_types = tuple(_et.__name__ for _et in et)\\n\\n        details = {\\n            \\\"parameter_type\\\": parameter.__class__.__name__.lower().replace(\\n                \\\"parameter\\\", \\\"\\\"\\n            ),\\n            \\\"name\\\": name,\\n            \\\"aliases\\\": aliases,\\n            \\\"element_types\\\": element_types,\\n            \\\"default_value\\\": parameter.default.typify(\\\"<<describe>>\\\"),\\n            \\\"description\\\": description.replace(\\\"\\\\n\\\", \\\" \\\").strip(),\\n        }\\n        if isinstance(parameter, SequenceParameter):\\n            details[\\\"string_delimiter\\\"] = parameter.string_delimiter\\n        return details\\n\\n    def list_parameters(self):\\n        return tuple(sorted(name.lstrip(\\\"_\\\") for name in self.parameter_names))\\n\\n    def typify_parameter(self, parameter_name, value, source):\\n        # return a tuple with correct parameter name and typed-value\\n        if parameter_name not in self.parameter_names:\\n            parameter_name = \\\"_\\\" + parameter_name\\n        parameter_loader = self.__class__.__dict__[parameter_name]\\n        parameter = parameter_loader.type\\n        assert isinstance(parameter, Parameter)\\n\\n        return parameter.typify(parameter_name, source, value)\\n\\n    def get_descriptions(self):\\n        raise NotImplementedError()\\n\\n\\ndef unique_sequence_map(*, unique_key: str):\\n    \\\"\\\"\\\"\\n    Used to validate properties on :class:`Configuration` subclasses defined as a\\n    ``SequenceParameter(MapParameter())`` where the map contains a single key that\\n    should be regarded as unique. This decorator will handle removing duplicates and\\n    merging to a single sequence.\\n    \\\"\\\"\\\"\\n\\n    def inner_wrap(func):\\n        @wraps(func)\\n        def wrapper(*args, **kwargs):\\n            sequence_map = func(*args, **kwargs)\\n            new_sequence_mapping = {}\\n\\n            for mapping in sequence_map:\\n                unique_key_value = mapping.get(unique_key)\\n\\n                if unique_key_value is None:\\n                    log.error(\\n                        f'Configuration: skipping {mapping} for \\\"{func.__name__}\\\"; unique key '\\n                        f'\\\"{unique_key}\\\" not present on mapping'\\n                    )\\n                    continue\\n\\n                if unique_key_value in new_sequence_mapping:\\n                    log.error(\\n                        f'Configuration: skipping {mapping} for \\\"{func.__name__}\\\"; value '\\n                        f'\\\"{unique_key_value}\\\" already present'\\n                    )\\n                    continue\\n\\n                new_sequence_mapping[unique_key_value] = mapping\\n\\n            return tuple(new_sequence_mapping.values())\\n\\n        return wrapper\\n\\n    return inner_wrap\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"\\nThe basic idea to nest logical expressions is instead of trying to denest\\nthings via distribution, we add new variables. So if we have some logical\\nexpression expr, we replace it with x and add expr <-> x to the clauses,\\nwhere x is a new variable, and expr <-> x is recursively evaluated in the\\nsame way, so that the final clauses are ORs of atoms.\\n\\nTo use this, create a new Clauses object with the max var, for instance, if you\\nalready have [[1, 2, -3]], you would use C = Clause(3).  All functions return\\na new literal, which represents that function, or True or False if the expression\\ncan be resolved fully. They may also add new clauses to C.clauses, which\\nwill then be delivered to the SAT solver.\\n\\nAll functions take atoms as arguments (an atom is an integer, representing a\\nliteral or a negated literal, or boolean constants True or False; that is,\\nit is the callers' responsibility to do the conversion of expressions\\nrecursively. This is done because we do not have data structures\\nrepresenting the various logical classes, only atoms.\\n\\nThe polarity argument can be set to True or False if you know that the literal\\nbeing used will only be used in the positive or the negative, respectively\\n(e.g., you will only use x, not -x).  This will generate fewer clauses. It\\nis probably best if you do not take advantage of this directly, but rather\\nthrough the Require and Prevent functions.\\n\\n\\\"\\\"\\\"\\n\\nfrom itertools import chain\\n\\nfrom ._logic import FALSE, TRUE\\nfrom ._logic import Clauses as _Clauses\\n\\n# TODO: We may want to turn the user-facing {TRUE,FALSE} values into an Enum and\\n#       hide the _logic.{TRUE,FALSE} values as an implementation detail.\\n#       We then have to handle the {TRUE,FALSE} -> _logic.{TRUE,FALSE} conversion\\n#       in Clauses._convert and the inverse _logic.{TRUE,FALSE} -> {TRUE,FALSE}\\n#       conversion in Clauses._eval.\\nTRUE = TRUE\\nFALSE = FALSE\\n\\nPycoSatSolver = \\\"pycosat\\\"\\nPyCryptoSatSolver = \\\"pycryptosat\\\"\\nPySatSolver = \\\"pysat\\\"\\n\\n\\nclass Clauses:\\n    def __init__(self, m=0, sat_solver=PycoSatSolver):\\n        self.names = {}\\n        self.indices = {}\\n        self._clauses = _Clauses(m=m, sat_solver_str=sat_solver)\\n\\n    @property\\n    def m(self):\\n        return self._clauses.m\\n\\n    @property\\n    def unsat(self):\\n        return self._clauses.unsat\\n\\n    def get_clause_count(self):\\n        return self._clauses.get_clause_count()\\n\\n    def as_list(self):\\n        return self._clauses.as_list()\\n\\n    def _check_variable(self, variable):\\n        if 0 < abs(variable) <= self.m:\\n            return variable\\n        raise ValueError(f\\\"SAT variable out of bounds: {variable} (max_var: {self.m})\\\")\\n\\n    def _check_literal(self, literal):\\n        if literal in {TRUE, FALSE}:\\n            return literal\\n        return self._check_variable(literal)\\n\\n    def add_clause(self, clause):\\n        self._clauses.add_clause(map(self._check_variable, self._convert(clause)))\\n\\n    def add_clauses(self, clauses):\\n        for clause in clauses:\\n            self.add_clause(clause)\\n\\n    def name_var(self, m, name):\\n        self._check_literal(m)\\n        nname = \\\"!\\\" + name\\n        self.names[name] = m\\n        self.names[nname] = -m\\n        if m not in {TRUE, FALSE} and m not in self.indices:\\n            self.indices[m] = name\\n            self.indices[-m] = nname\\n        return m\\n\\n    def new_var(self, name=None):\\n        m = self._clauses.new_var()\\n        if name:\\n            self.name_var(m, name)\\n        return m\\n\\n    def from_name(self, name):\\n        return self.names.get(name)\\n\\n    def from_index(self, m):\\n        return self.indices.get(m)\\n\\n    def _assign(self, vals, name=None):\\n        x = self._clauses.assign(vals)\\n        if not name:\\n            return x\\n        if vals in {TRUE, FALSE}:\\n            x = self._clauses.new_var()\\n            self._clauses.add_clause((x,) if vals else (-x,))\\n        return self.name_var(x, name)\\n\\n    def _convert(self, x):\\n        if isinstance(x, (tuple, list)):\\n            return type(x)(map(self._convert, x))\\n        if isinstance(x, int):\\n            return self._check_literal(x)\\n        name = x\\n        try:\\n            return self.names[name]\\n        except KeyError:\\n            raise ValueError(f\\\"Unregistered SAT variable name: {name}\\\")\\n\\n    def _eval(self, func, args, no_literal_args, polarity, name):\\n        args = self._convert(args)\\n        if name is False:\\n            self._clauses.Eval(func, args + no_literal_args, polarity)\\n            return None\\n        vals = func(*(args + no_literal_args), polarity=polarity)\\n        return self._assign(vals, name)\\n\\n    def Prevent(self, what, *args):\\n        return what.__get__(self, Clauses)(*args, polarity=False, name=False)\\n\\n    def Require(self, what, *args):\\n        return what.__get__(self, Clauses)(*args, polarity=True, name=False)\\n\\n    def Not(self, x, polarity=None, name=None):\\n        return self._eval(self._clauses.Not, (x,), (), polarity, name)\\n\\n    def And(self, f, g, polarity=None, name=None):\\n        return self._eval(self._clauses.And, (f, g), (), polarity, name)\\n\\n    def Or(self, f, g, polarity=None, name=None):\\n        return self._eval(self._clauses.Or, (f, g), (), polarity, name)\\n\\n    def Xor(self, f, g, polarity=None, name=None):\\n        return self._eval(self._clauses.Xor, (f, g), (), polarity, name)\\n\\n    def ITE(self, c, t, f, polarity=None, name=None):\\n        \\\"\\\"\\\"If c Then t Else f.\\n\\n        In this function, if any of c, t, or f are True and False the resulting\\n        expression is resolved.\\n        \\\"\\\"\\\"\\n        return self._eval(self._clauses.ITE, (c, t, f), (), polarity, name)\\n\\n    def All(self, iter, polarity=None, name=None):\\n        return self._eval(self._clauses.All, (iter,), (), polarity, name)\\n\\n    def Any(self, vals, polarity=None, name=None):\\n        return self._eval(self._clauses.Any, (list(vals),), (), polarity, name)\\n\\n    def AtMostOne_NSQ(self, vals, polarity=None, name=None):\\n        return self._eval(\\n            self._clauses.AtMostOne_NSQ, (list(vals),), (), polarity, name\\n        )\\n\\n    def AtMostOne_BDD(self, vals, polarity=None, name=None):\\n        return self._eval(\\n            self._clauses.AtMostOne_BDD, (list(vals),), (), polarity, name\\n        )\\n\\n    def AtMostOne(self, vals, polarity=None, name=None):\\n        vals = list(vals)\\n        nv = len(vals)\\n        if nv < 5 - (polarity is not True):\\n            what = self.AtMostOne_NSQ\\n        else:\\n            what = self.AtMostOne_BDD\\n        return self._eval(what, (vals,), (), polarity, name)\\n\\n    def ExactlyOne_NSQ(self, vals, polarity=None, name=None):\\n        return self._eval(\\n            self._clauses.ExactlyOne_NSQ, (list(vals),), (), polarity, name\\n        )\\n\\n    def ExactlyOne_BDD(self, vals, polarity=None, name=None):\\n        return self._eval(\\n            self._clauses.ExactlyOne_BDD, (list(vals),), (), polarity, name\\n        )\\n\\n    def ExactlyOne(self, vals, polarity=None, name=None):\\n        vals = list(vals)\\n        nv = len(vals)\\n        if nv < 2:\\n            what = self.ExactlyOne_NSQ\\n        else:\\n            what = self.ExactlyOne_BDD\\n        return self._eval(what, (vals,), (), polarity, name)\\n\\n    def LinearBound(self, equation, lo, hi, preprocess=True, polarity=None, name=None):\\n        if not isinstance(equation, dict):\\n            # in case of duplicate literal -> coefficient mappings, always take the last one\\n            equation = {named_lit: coeff for coeff, named_lit in equation}\\n        named_literals = list(equation.keys())\\n        coefficients = list(equation.values())\\n        return self._eval(\\n            self._clauses.LinearBound,\\n            (named_literals,),\\n            (coefficients, lo, hi, preprocess),\\n            polarity,\\n            name,\\n        )\\n\\n    def sat(self, additional=None, includeIf=False, names=False, limit=0):\\n        \\\"\\\"\\\"\\n        Calculate a SAT solution for the current clause set.\\n\\n        Returned is the list of those solutions.  When the clauses are\\n        unsatisfiable, an empty list is returned.\\n\\n        \\\"\\\"\\\"\\n        if self.unsat:\\n            return None\\n        if not self.m:\\n            return set() if names else []\\n        if additional:\\n            additional = (tuple(self.names.get(c, c) for c in cc) for cc in additional)\\n        solution = self._clauses.sat(\\n            additional=additional, includeIf=includeIf, limit=limit\\n        )\\n        if solution is None:\\n            return None\\n        if names:\\n            return {\\n                nm\\n                for nm in (self.indices.get(s) for s in solution)\\n                if nm and nm[0] != \\\"!\\\"\\n            }\\n        return solution\\n\\n    def itersolve(self, constraints=None, m=None):\\n        exclude = []\\n        if m is None:\\n            m = self.m\\n        while True:\\n            # We don't use pycosat.itersolve because it is more\\n            # important to limit the number of terms added to the\\n            # exclusion list, in our experience. Once we update\\n            # pycosat to do this, this can use it.\\n            sol = self.sat(chain(constraints, exclude))\\n            if sol is None:\\n                return\\n            yield sol\\n            exclude.append([-k for k in sol if -m <= k <= m])\\n\\n    def minimize(self, objective, bestsol=None, trymax=False):\\n        if not isinstance(objective, dict):\\n            # in case of duplicate literal -> coefficient mappings, always take the last one\\n            objective = {named_lit: coeff for coeff, named_lit in objective}\\n        literals = self._convert(list(objective.keys()))\\n        coeffs = list(objective.values())\\n\\n        return self._clauses.minimize(literals, coeffs, bestsol=bestsol, trymax=trymax)\\n\\n\\ndef minimal_unsatisfiable_subset(clauses, sat, explicit_specs):\\n    \\\"\\\"\\\"\\n    Given a set of clauses, find a minimal unsatisfiable subset (an\\n    unsatisfiable core)\\n\\n    A set is a minimal unsatisfiable subset if no proper subset is\\n    unsatisfiable.  A set of clauses may have many minimal unsatisfiable\\n    subsets of different sizes.\\n\\n    sat should be a function that takes a tuple of clauses and returns True if\\n    the clauses are satisfiable and False if they are not.  The algorithm will\\n    work with any order-reversing function (reversing the order of subset and\\n    the order False < True), that is, any function where (A <= B) iff (sat(B)\\n    <= sat(A)), where A <= B means A is a subset of B and False < True).\\n\\n    \\\"\\\"\\\"\\n    working_set = set()\\n    found_conflicts = set()\\n\\n    if sat(explicit_specs, True) is None:\\n        found_conflicts = set(explicit_specs)\\n    else:\\n        # we succeeded, so we'll add the spec to our future constraints\\n        working_set = set(explicit_specs)\\n\\n    for spec in set(clauses) - working_set:\\n        if (\\n            sat(\\n                working_set\\n                | {\\n                    spec,\\n                },\\n                True,\\n            )\\n            is None\\n        ):\\n            found_conflicts.add(spec)\\n        else:\\n            # we succeeded, so we'll add the spec to our future constraints\\n            working_set.add(spec)\\n\\n    return found_conflicts\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Common constants.\\\"\\\"\\\"\\n\\nfrom ..auxlib import NULL\\n\\n# Use this NULL object when needing to distinguish a value from None\\n# For example, when parsing json, you may need to determine if a json key was given and set\\n#   to null, or the key didn't exist at all.  There could be a bit of potential confusion here,\\n#   because in python null == None, while here I'm defining NULL to mean 'not defined'.\\nNULL = NULL\\n\\n# Custom \\\"trace\\\" logging level for output more verbose than debug logs (logging.DEBUG == 10).\\nTRACE = 5\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\nimport sys\\nfrom array import array\\nfrom itertools import combinations\\nfrom logging import DEBUG, getLogger\\n\\nfrom .constants import TRACE\\n\\nlog = getLogger(__name__)\\n\\n\\nTRUE = sys.maxsize\\nFALSE = -TRUE\\n\\n\\nclass _ClauseList:\\n    \\\"\\\"\\\"Storage for the CNF clauses, represented as a list of tuples of ints.\\\"\\\"\\\"\\n\\n    def __init__(self):\\n        self._clause_list = []\\n        # Methods append and extend are directly bound for performance reasons,\\n        # to avoid call overhead and lookups.\\n        self.append = self._clause_list.append\\n        self.extend = self._clause_list.extend\\n\\n    def get_clause_count(self):\\n        \\\"\\\"\\\"Return number of stored clauses.\\\"\\\"\\\"\\n        return len(self._clause_list)\\n\\n    def save_state(self):\\n        \\\"\\\"\\\"\\n        Get state information to be able to revert temporary additions of\\n        supplementary clauses.  _ClauseList: state is simply the number of clauses.\\n        \\\"\\\"\\\"\\n        return len(self._clause_list)\\n\\n    def restore_state(self, saved_state):\\n        \\\"\\\"\\\"\\n        Restore state saved via `save_state`.\\n        Removes clauses that were added after the state has been saved.\\n        \\\"\\\"\\\"\\n        len_clauses = saved_state\\n        self._clause_list[len_clauses:] = []\\n\\n    def as_list(self):\\n        \\\"\\\"\\\"Return clauses as a list of tuples of ints.\\\"\\\"\\\"\\n        return self._clause_list\\n\\n    def as_array(self):\\n        \\\"\\\"\\\"Return clauses as a flat int array, each clause being terminated by 0.\\\"\\\"\\\"\\n        clause_array = array(\\\"i\\\")\\n        for c in self._clause_list:\\n            clause_array.extend(c)\\n            clause_array.append(0)\\n        return clause_array\\n\\n\\nclass _ClauseArray:\\n    \\\"\\\"\\\"\\n    Storage for the CNF clauses, represented as a flat int array.\\n    Each clause is terminated by int(0).\\n    \\\"\\\"\\\"\\n\\n    def __init__(self):\\n        self._clause_array = array(\\\"i\\\")\\n        # Methods append and extend are directly bound for performance reasons,\\n        # to avoid call overhead and lookups.\\n        self._array_append = self._clause_array.append\\n        self._array_extend = self._clause_array.extend\\n\\n    def extend(self, clauses):\\n        for clause in clauses:\\n            self.append(clause)\\n\\n    def append(self, clause):\\n        self._array_extend(clause)\\n        self._array_append(0)\\n\\n    def get_clause_count(self):\\n        \\\"\\\"\\\"\\n        Return number of stored clauses.\\n        This is an O(n) operation since we don't store the number of clauses\\n        explicitly due to performance reasons (Python interpreter overhead in\\n        self.append).\\n        \\\"\\\"\\\"\\n        return self._clause_array.count(0)\\n\\n    def save_state(self):\\n        \\\"\\\"\\\"\\n        Get state information to be able to revert temporary additions of\\n        supplementary clauses. _ClauseArray: state is the length of the int\\n        array, NOT number of clauses.\\n        \\\"\\\"\\\"\\n        return len(self._clause_array)\\n\\n    def restore_state(self, saved_state):\\n        \\\"\\\"\\\"\\n        Restore state saved via `save_state`.\\n        Removes clauses that were added after the state has been saved.\\n        \\\"\\\"\\\"\\n        len_clause_array = saved_state\\n        self._clause_array[len_clause_array:] = array(\\\"i\\\")\\n\\n    def as_list(self):\\n        \\\"\\\"\\\"Return clauses as a list of tuples of ints.\\\"\\\"\\\"\\n        clause = []\\n        for v in self._clause_array:\\n            if v == 0:\\n                yield tuple(clause)\\n                clause.clear()\\n            else:\\n                clause.append(v)\\n\\n    def as_array(self):\\n        \\\"\\\"\\\"Return clauses as a flat int array, each clause being terminated by 0.\\\"\\\"\\\"\\n        return self._clause_array\\n\\n\\nclass _SatSolver:\\n    \\\"\\\"\\\"Simple wrapper to call a SAT solver given a _ClauseList/_ClauseArray instance.\\\"\\\"\\\"\\n\\n    def __init__(self, **run_kwargs):\\n        self._run_kwargs = run_kwargs or {}\\n        self._clauses = _ClauseList()\\n        # Bind some methods of _clauses to reduce lookups and call overhead.\\n        self.add_clause = self._clauses.append\\n        self.add_clauses = self._clauses.extend\\n\\n    def get_clause_count(self):\\n        return self._clauses.get_clause_count()\\n\\n    def as_list(self):\\n        return self._clauses.as_list()\\n\\n    def save_state(self):\\n        return self._clauses.save_state()\\n\\n    def restore_state(self, saved_state):\\n        return self._clauses.restore_state(saved_state)\\n\\n    def run(self, m, **kwargs):\\n        run_kwargs = self._run_kwargs.copy()\\n        run_kwargs.update(kwargs)\\n        solver = self.setup(m, **run_kwargs)\\n        sat_solution = self.invoke(solver)\\n        solution = self.process_solution(sat_solution)\\n        return solution\\n\\n    def setup(self, m, **kwargs):\\n        \\\"\\\"\\\"Create a solver instance, add the clauses to it, and return it.\\\"\\\"\\\"\\n        raise NotImplementedError()\\n\\n    def invoke(self, solver):\\n        \\\"\\\"\\\"Start the actual SAT solving and return the calculated solution.\\\"\\\"\\\"\\n        raise NotImplementedError()\\n\\n    def process_solution(self, sat_solution):\\n        \\\"\\\"\\\"\\n        Process the solution returned by self.invoke.\\n        Returns a list of satisfied variables or None if no solution is found.\\n        \\\"\\\"\\\"\\n        raise NotImplementedError()\\n\\n\\nclass _PycoSatSolver(_SatSolver):\\n    def setup(self, m, limit=0, **kwargs):\\n        from pycosat import itersolve\\n\\n        # NOTE: The iterative solving isn't actually used here, we just call\\n        #       itersolve to separate setup from the actual run.\\n        return itersolve(self._clauses.as_list(), vars=m, prop_limit=limit)\\n        # If we add support for passing the clauses as an integer stream to the\\n        # solvers, we could also use self._clauses.as_array like this:\\n        # return itersolve(self._clauses.as_array(), vars=m, prop_limit=limit)\\n\\n    def invoke(self, iter_sol):\\n        try:\\n            sat_solution = next(iter_sol)\\n        except StopIteration:\\n            sat_solution = \\\"UNSAT\\\"\\n        del iter_sol\\n        return sat_solution\\n\\n    def process_solution(self, sat_solution):\\n        if sat_solution in (\\\"UNSAT\\\", \\\"UNKNOWN\\\"):\\n            return None\\n        return sat_solution\\n\\n\\nclass _PyCryptoSatSolver(_SatSolver):\\n    def setup(self, m, threads=1, **kwargs):\\n        from pycryptosat import Solver\\n\\n        solver = Solver(threads=threads)\\n        solver.add_clauses(self._clauses.as_list())\\n        return solver\\n\\n    def invoke(self, solver):\\n        sat, sat_solution = solver.solve()\\n        if not sat:\\n            sat_solution = None\\n        return sat_solution\\n\\n    def process_solution(self, solution):\\n        if not solution:\\n            return None\\n        # The first element of the solution is always None.\\n        solution = [i for i, b in enumerate(solution) if b]\\n        return solution\\n\\n\\nclass _PySatSolver(_SatSolver):\\n    def setup(self, m, **kwargs):\\n        from pysat.solvers import Glucose4\\n\\n        solver = Glucose4()\\n        solver.append_formula(self._clauses.as_list())\\n        return solver\\n\\n    def invoke(self, solver):\\n        if not solver.solve():\\n            sat_solution = None\\n        else:\\n            sat_solution = solver.get_model()\\n        solver.delete()\\n        return sat_solution\\n\\n    def process_solution(self, sat_solution):\\n        if sat_solution is None:\\n            solution = None\\n        else:\\n            solution = sat_solution\\n        return solution\\n\\n\\n_sat_solver_str_to_cls = {\\n    \\\"pycosat\\\": _PycoSatSolver,\\n    \\\"pycryptosat\\\": _PyCryptoSatSolver,\\n    \\\"pysat\\\": _PySatSolver,\\n}\\n\\n_sat_solver_cls_to_str = {cls: string for string, cls in _sat_solver_str_to_cls.items()}\\n\\n\\n# Code that uses special cases (generates no clauses) is in ADTs/FEnv.h in\\n# minisatp. Code that generates clauses is in Hardware_clausify.cc (and are\\n# also described in the paper, \\\"Translating Pseudo-Boolean Constraints into\\n# SAT,\\\" Eén and Sörensson).\\nclass Clauses:\\n    def __init__(self, m=0, sat_solver_str=_sat_solver_cls_to_str[_PycoSatSolver]):\\n        self.unsat = False\\n        self.m = m\\n\\n        try:\\n            sat_solver_cls = _sat_solver_str_to_cls[sat_solver_str]\\n        except KeyError:\\n            raise NotImplementedError(f\\\"Unknown SAT solver: {sat_solver_str}\\\")\\n        self._sat_solver = sat_solver_cls()\\n\\n        # Bind some methods of _sat_solver to reduce lookups and call overhead.\\n        self.add_clause = self._sat_solver.add_clause\\n        self.add_clauses = self._sat_solver.add_clauses\\n\\n    def get_clause_count(self):\\n        return self._sat_solver.get_clause_count()\\n\\n    def as_list(self):\\n        return self._sat_solver.as_list()\\n\\n    def new_var(self):\\n        m = self.m + 1\\n        self.m = m\\n        return m\\n\\n    def assign(self, vals):\\n        if isinstance(vals, tuple):\\n            x = self.new_var()\\n            self.add_clauses((-x,) + y for y in vals[0])\\n            self.add_clauses((x,) + y for y in vals[1])\\n            return x\\n        return vals\\n\\n    def Combine(self, args, polarity):\\n        if any(v == FALSE for v in args):\\n            return FALSE\\n        args = [v for v in args if v != TRUE]\\n        nv = len(args)\\n        if nv == 0:\\n            return TRUE\\n        if nv == 1:\\n            return args[0]\\n        if all(isinstance(v, tuple) for v in args):\\n            return (sum((v[0] for v in args), []), sum((v[1] for v in args), []))\\n        else:\\n            return self.All(map(self.assign, args), polarity)\\n\\n    def Eval(self, func, args, polarity):\\n        saved_state = self._sat_solver.save_state()\\n        vals = func(*args, polarity=polarity)\\n        # eval without assignment:\\n        if isinstance(vals, tuple):\\n            self.add_clauses(vals[0])\\n            self.add_clauses(vals[1])\\n        elif vals not in {TRUE, FALSE}:\\n            self.add_clause((vals if polarity else -vals,))\\n        else:\\n            self._sat_solver.restore_state(saved_state)\\n            self.unsat = self.unsat or (vals == TRUE) != polarity\\n\\n    def Prevent(self, func, *args):\\n        self.Eval(func, args, polarity=False)\\n\\n    def Require(self, func, *args):\\n        self.Eval(func, args, polarity=True)\\n\\n    def Not(self, x, polarity=None, add_new_clauses=False):\\n        return -x\\n\\n    def And(self, f, g, polarity, add_new_clauses=False):\\n        if f == FALSE or g == FALSE:\\n            return FALSE\\n        if f == TRUE:\\n            return g\\n        if g == TRUE:\\n            return f\\n        if f == g:\\n            return f\\n        if f == -g:\\n            return FALSE\\n        if g < f:\\n            f, g = g, f\\n        if add_new_clauses:\\n            # This is equivalent to running self.assign(pval, nval) on\\n            # the (pval, nval) tuple we return below. Duplicating the code here\\n            # is an important performance tweak to avoid the costly generator\\n            # expressions and tuple additions in self.assign.\\n            x = self.new_var()\\n            if polarity in (True, None):\\n                self.add_clauses(\\n                    [\\n                        (\\n                            -x,\\n                            f,\\n                        ),\\n                        (\\n                            -x,\\n                            g,\\n                        ),\\n                    ]\\n                )\\n            if polarity in (False, None):\\n                self.add_clauses([(x, -f, -g)])\\n            return x\\n        pval = [(f,), (g,)] if polarity in (True, None) else []\\n        nval = [(-f, -g)] if polarity in (False, None) else []\\n        return pval, nval\\n\\n    def Or(self, f, g, polarity, add_new_clauses=False):\\n        if f == TRUE or g == TRUE:\\n            return TRUE\\n        if f == FALSE:\\n            return g\\n        if g == FALSE:\\n            return f\\n        if f == g:\\n            return f\\n        if f == -g:\\n            return TRUE\\n        if g < f:\\n            f, g = g, f\\n        if add_new_clauses:\\n            x = self.new_var()\\n            if polarity in (True, None):\\n                self.add_clauses([(-x, f, g)])\\n            if polarity in (False, None):\\n                self.add_clauses(\\n                    [\\n                        (\\n                            x,\\n                            -f,\\n                        ),\\n                        (\\n                            x,\\n                            -g,\\n                        ),\\n                    ]\\n                )\\n            return x\\n        pval = [(f, g)] if polarity in (True, None) else []\\n        nval = [(-f,), (-g,)] if polarity in (False, None) else []\\n        return pval, nval\\n\\n    def Xor(self, f, g, polarity, add_new_clauses=False):\\n        if f == FALSE:\\n            return g\\n        if f == TRUE:\\n            return self.Not(g, polarity, add_new_clauses=add_new_clauses)\\n        if g == FALSE:\\n            return f\\n        if g == TRUE:\\n            return -f\\n        if f == g:\\n            return FALSE\\n        if f == -g:\\n            return TRUE\\n        if g < f:\\n            f, g = g, f\\n        if add_new_clauses:\\n            x = self.new_var()\\n            if polarity in (True, None):\\n                self.add_clauses([(-x, f, g), (-x, -f, -g)])\\n            if polarity in (False, None):\\n                self.add_clauses([(x, -f, g), (x, f, -g)])\\n            return x\\n        pval = [(f, g), (-f, -g)] if polarity in (True, None) else []\\n        nval = [(-f, g), (f, -g)] if polarity in (False, None) else []\\n        return pval, nval\\n\\n    def ITE(self, c, t, f, polarity, add_new_clauses=False):\\n        if c == TRUE:\\n            return t\\n        if c == FALSE:\\n            return f\\n        if t == TRUE:\\n            return self.Or(c, f, polarity, add_new_clauses=add_new_clauses)\\n        if t == FALSE:\\n            return self.And(-c, f, polarity, add_new_clauses=add_new_clauses)\\n        if f == FALSE:\\n            return self.And(c, t, polarity, add_new_clauses=add_new_clauses)\\n        if f == TRUE:\\n            return self.Or(t, -c, polarity, add_new_clauses=add_new_clauses)\\n        if t == c:\\n            return self.Or(c, f, polarity, add_new_clauses=add_new_clauses)\\n        if t == -c:\\n            return self.And(-c, f, polarity, add_new_clauses=add_new_clauses)\\n        if f == c:\\n            return self.And(c, t, polarity, add_new_clauses=add_new_clauses)\\n        if f == -c:\\n            return self.Or(t, -c, polarity, add_new_clauses=add_new_clauses)\\n        if t == f:\\n            return t\\n        if t == -f:\\n            return self.Xor(c, f, polarity, add_new_clauses=add_new_clauses)\\n        if t < f:\\n            t, f, c = f, t, -c\\n        # Basically, c ? t : f is equivalent to (c AND t) OR (NOT c AND f)\\n        # The third clause in each group is redundant but assists the unit\\n        # propagation in the SAT solver.\\n        if add_new_clauses:\\n            x = self.new_var()\\n            if polarity in (True, None):\\n                self.add_clauses([(-x, -c, t), (-x, c, f), (-x, t, f)])\\n            if polarity in (False, None):\\n                self.add_clauses([(x, -c, -t), (x, c, -f), (x, -t, -f)])\\n            return x\\n        pval = [(-c, t), (c, f), (t, f)] if polarity in (True, None) else []\\n        nval = [(-c, -t), (c, -f), (-t, -f)] if polarity in (False, None) else []\\n        return pval, nval\\n\\n    def All(self, iter, polarity=None):\\n        vals = set()\\n        for v in iter:\\n            if v == TRUE:\\n                continue\\n            if v == FALSE or -v in vals:\\n                return FALSE\\n            vals.add(v)\\n        nv = len(vals)\\n        if nv == 0:\\n            return TRUE\\n        elif nv == 1:\\n            return next(v for v in vals)\\n        pval = [(v,) for v in vals] if polarity in (True, None) else []\\n        nval = [tuple(-v for v in vals)] if polarity in (False, None) else []\\n        return pval, nval\\n\\n    def Any(self, iter, polarity):\\n        vals = set()\\n        for v in iter:\\n            if v == FALSE:\\n                continue\\n            elif v == TRUE or -v in vals:\\n                return TRUE\\n            vals.add(v)\\n        nv = len(vals)\\n        if nv == 0:\\n            return FALSE\\n        elif nv == 1:\\n            return next(v for v in vals)\\n        pval = [tuple(vals)] if polarity in (True, None) else []\\n        nval = [(-v,) for v in vals] if polarity in (False, None) else []\\n        return pval, nval\\n\\n    def AtMostOne_NSQ(self, vals, polarity):\\n        combos = []\\n        for v1, v2 in combinations(map(self.Not, vals), 2):\\n            combos.append(self.Or(v1, v2, polarity))\\n        return self.Combine(combos, polarity)\\n\\n    def AtMostOne_BDD(self, vals, polarity=None):\\n        lits = list(vals)\\n        coeffs = [1] * len(lits)\\n        return self.LinearBound(lits, coeffs, 0, 1, True, polarity)\\n\\n    def ExactlyOne_NSQ(self, vals, polarity):\\n        vals = list(vals)\\n        v1 = self.AtMostOne_NSQ(vals, polarity)\\n        v2 = self.Any(vals, polarity)\\n        return self.Combine((v1, v2), polarity)\\n\\n    def ExactlyOne_BDD(self, vals, polarity):\\n        lits = list(vals)\\n        coeffs = [1] * len(lits)\\n        return self.LinearBound(lits, coeffs, 1, 1, True, polarity)\\n\\n    def LB_Preprocess(self, lits, coeffs):\\n        equation = []\\n        offset = 0\\n        for coeff, lit in zip(coeffs, lits):\\n            if lit == TRUE:\\n                offset += coeff\\n                continue\\n            if lit == FALSE or coeff == 0:\\n                continue\\n            if coeff < 0:\\n                offset += coeff\\n                coeff, lit = -coeff, -lit\\n            equation.append((coeff, lit))\\n        coeffs, lits = tuple(zip(*sorted(equation))) or ((), ())\\n        return lits, coeffs, offset\\n\\n    def BDD(self, lits, coeffs, nterms, lo, hi, polarity):\\n        # The equation (coeffs x lits) is sorted in\\n        # order of increasing coefficients.\\n        # Then we take advantage of the following recurrence:\\n        #                l      <= S + cN xN <= u\\n        #  => IF xN THEN l - cN <= S         <= u - cN\\n        #           ELSE l      <= S         <= u\\n        # we use memoization to prune common subexpressions\\n        total = sum(c for c in coeffs[:nterms])\\n        target = (nterms - 1, 0, total)\\n        call_stack = [target]\\n        ret = {}\\n        call_stack_append = call_stack.append\\n        call_stack_pop = call_stack.pop\\n        ret_get = ret.get\\n        ITE = self.ITE\\n\\n        csum = 0\\n        while call_stack:\\n            ndx, csum, total = call_stack[-1]\\n            lower_limit = lo - csum\\n            upper_limit = hi - csum\\n            if lower_limit <= 0 and upper_limit >= total:\\n                ret[call_stack_pop()] = TRUE\\n                continue\\n            if lower_limit > total or upper_limit < 0:\\n                ret[call_stack_pop()] = FALSE\\n                continue\\n            LA = lits[ndx]\\n            LC = coeffs[ndx]\\n            ndx -= 1\\n            total -= LC\\n            hi_key = (ndx, csum if LA < 0 else csum + LC, total)\\n            thi = ret_get(hi_key)\\n            if thi is None:\\n                call_stack_append(hi_key)\\n                continue\\n            lo_key = (ndx, csum + LC if LA < 0 else csum, total)\\n            tlo = ret_get(lo_key)\\n            if tlo is None:\\n                call_stack_append(lo_key)\\n                continue\\n            # NOTE: The following ITE call is _the_ hotspot of the Python-side\\n            # computations for the overall minimization run. For performance we\\n            # avoid calling self.assign here via add_new_clauses=True.\\n            # If we want to translate parts of the code to a compiled language,\\n            # self.BDD (+ its downward call stack) is the prime candidate!\\n            ret[call_stack_pop()] = ITE(\\n                abs(LA), thi, tlo, polarity, add_new_clauses=True\\n            )\\n        return ret[target]\\n\\n    def LinearBound(self, lits, coeffs, lo, hi, preprocess, polarity):\\n        if preprocess:\\n            lits, coeffs, offset = self.LB_Preprocess(lits, coeffs)\\n            lo -= offset\\n            hi -= offset\\n        nterms = len(coeffs)\\n        if nterms and coeffs[-1] > hi:\\n            nprune = sum(c > hi for c in coeffs)\\n            log.log(\\n                TRACE, \\\"Eliminating %d/%d terms for bound violation\\\", nprune, nterms\\n            )\\n            nterms -= nprune\\n        else:\\n            nprune = 0\\n        # Tighten bounds\\n        total = sum(c for c in coeffs[:nterms])\\n        if preprocess:\\n            lo = max([lo, 0])\\n            hi = min([hi, total])\\n        if lo > hi:\\n            return FALSE\\n        if nterms == 0:\\n            res = TRUE if lo == 0 else FALSE\\n        else:\\n            res = self.BDD(lits, coeffs, nterms, lo, hi, polarity)\\n        if nprune:\\n            prune = self.All([-a for a in lits[nterms:]], polarity)\\n            res = self.Combine((res, prune), polarity)\\n        return res\\n\\n    def _run_sat(self, m, limit=0):\\n        if log.isEnabledFor(DEBUG):\\n            log.debug(\\\"Invoking SAT with clause count: %s\\\", self.get_clause_count())\\n        solution = self._sat_solver.run(m, limit=limit)\\n        return solution\\n\\n    def sat(self, additional=None, includeIf=False, limit=0):\\n        \\\"\\\"\\\"\\n        Calculate a SAT solution for the current clause set.\\n\\n        Returned is the list of those solutions.  When the clauses are\\n        unsatisfiable, an empty list is returned.\\n\\n        \\\"\\\"\\\"\\n        if self.unsat:\\n            return None\\n        if not self.m:\\n            return []\\n        saved_state = self._sat_solver.save_state()\\n        if additional:\\n\\n            def preproc(eqs):\\n                def preproc_(cc):\\n                    for c in cc:\\n                        if c == FALSE:\\n                            continue\\n                        yield c\\n                        if c == TRUE:\\n                            break\\n\\n                for cc in eqs:\\n                    cc = tuple(preproc_(cc))\\n                    if not cc:\\n                        yield cc\\n                        break\\n                    if cc[-1] != TRUE:\\n                        yield cc\\n\\n            additional = list(preproc(additional))\\n            if additional:\\n                if not additional[-1]:\\n                    return None\\n                self.add_clauses(additional)\\n        solution = self._run_sat(self.m, limit=limit)\\n        if additional and (solution is None or not includeIf):\\n            self._sat_solver.restore_state(saved_state)\\n        return solution\\n\\n    def minimize(self, lits, coeffs, bestsol=None, trymax=False):\\n        \\\"\\\"\\\"\\n        Minimize the objective function given by (coeff, integer) pairs in\\n        zip(coeffs, lits).\\n        The actual minimization is multiobjective: first, we minimize the\\n        largest active coefficient value, then we minimize the sum.\\n        \\\"\\\"\\\"\\n        if bestsol is None or len(bestsol) < self.m:\\n            log.debug(\\\"Clauses added, recomputing solution\\\")\\n            bestsol = self.sat()\\n        if bestsol is None or self.unsat:\\n            log.debug(\\\"Constraints are unsatisfiable\\\")\\n            return bestsol, sum(abs(c) for c in coeffs) + 1 if coeffs else 1\\n        if not coeffs:\\n            log.debug(\\\"Empty objective, trivial solution\\\")\\n            return bestsol, 0\\n\\n        lits, coeffs, offset = self.LB_Preprocess(lits, coeffs)\\n        maxval = max(coeffs)\\n\\n        def peak_val(sol, objective_dict):\\n            return max(objective_dict.get(s, 0) for s in sol)\\n\\n        def sum_val(sol, objective_dict):\\n            return sum(objective_dict.get(s, 0) for s in sol)\\n\\n        lo = 0\\n        try0 = 0\\n        for peak in (True, False) if maxval > 1 else (False,):\\n            if peak:\\n                log.log(TRACE, \\\"Beginning peak minimization\\\")\\n                objval = peak_val\\n            else:\\n                log.log(TRACE, \\\"Beginning sum minimization\\\")\\n                objval = sum_val\\n\\n            objective_dict = {a: c for c, a in zip(coeffs, lits)}\\n            bestval = objval(bestsol, objective_dict)\\n\\n            # If we got lucky and the initial solution is optimal, we still\\n            # need to generate the constraints at least once\\n            hi = bestval\\n            m_orig = self.m\\n            if log.isEnabledFor(DEBUG):\\n                # This is only used for the log message below.\\n                nz = self.get_clause_count()\\n            saved_state = self._sat_solver.save_state()\\n            if trymax and not peak:\\n                try0 = hi - 1\\n\\n            log.log(TRACE, \\\"Initial range (%d,%d)\\\", lo, hi)\\n            while True:\\n                if try0 is None:\\n                    mid = (lo + hi) // 2\\n                else:\\n                    mid = try0\\n                if peak:\\n                    prevent = tuple(a for c, a in zip(coeffs, lits) if c > mid)\\n                    require = tuple(a for c, a in zip(coeffs, lits) if lo <= c <= mid)\\n                    self.Prevent(self.Any, prevent)\\n                    if require:\\n                        self.Require(self.Any, require)\\n                else:\\n                    self.Require(self.LinearBound, lits, coeffs, lo, mid, False)\\n\\n                if log.isEnabledFor(DEBUG):\\n                    log.log(\\n                        TRACE,\\n                        \\\"Bisection attempt: (%d,%d), (%d+%d) clauses\\\",\\n                        lo,\\n                        mid,\\n                        nz,\\n                        self.get_clause_count() - nz,\\n                    )\\n                newsol = self.sat()\\n                if newsol is None:\\n                    lo = mid + 1\\n                    log.log(TRACE, \\\"Bisection failure, new range=(%d,%d)\\\", lo, hi)\\n                    if lo > hi:\\n                        # FIXME: This is not supposed to happen!\\n                        # TODO: Investigate and fix the cause.\\n                        break\\n                    # If this was a failure of the first test after peak minimization,\\n                    # then it means that the peak minimizer is \\\"tight\\\" and we don't need\\n                    # any further constraints.\\n                else:\\n                    done = lo == mid\\n                    bestsol = newsol\\n                    bestval = objval(newsol, objective_dict)\\n                    hi = bestval\\n                    log.log(TRACE, \\\"Bisection success, new range=(%d,%d)\\\", lo, hi)\\n                    if done:\\n                        break\\n                self.m = m_orig\\n                # Since we only ever _add_ clauses and only remove then via\\n                # restore_state, it's fine to test on equality only.\\n                if self._sat_solver.save_state() != saved_state:\\n                    self._sat_solver.restore_state(saved_state)\\n                self.unsat = False\\n                try0 = None\\n\\n            log.debug(\\\"Final %s objective: %d\\\" % (\\\"peak\\\" if peak else \\\"sum\\\", bestval))\\n            if bestval == 0:\\n                break\\n            elif peak:\\n                # Now that we've minimized the peak value, we can drop any terms\\n                # with coefficients larger than this. Furthermore, since we know\\n                # at least one peak will be active, our lower bound for the sum\\n                # equals the peak.\\n                lits = [a for c, a in zip(coeffs, lits) if c <= bestval]\\n                coeffs = [c for c in coeffs if c <= bestval]\\n                try0 = sum_val(bestsol, objective_dict)\\n                lo = bestval\\n            else:\\n                log.debug(\\\"New peak objective: %d\\\" % peak_val(bestsol, objective_dict))\\n\\n        return bestsol, bestval\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Common decorators.\\\"\\\"\\\"\\n\\nimport os\\nfrom functools import wraps\\n\\nfrom ..deprecations import deprecated\\n\\ndeprecated.module(\\\"24.3\\\", \\\"24.9\\\")\\n\\n\\n@deprecated(\\\"24.3\\\", \\\"24.9\\\")\\ndef env_override(envvar_name, convert_empty_to_none=False):\\n    \\\"\\\"\\\"Override the return value of the decorated function with an environment variable.\\n\\n    If convert_empty_to_none is true, if the value of the environment variable\\n    is the empty string, a None value will be returned.\\n    \\\"\\\"\\\"\\n\\n    def decorator(func):\\n        @wraps(func)\\n        def wrapper(*args, **kwargs):\\n            value = os.environ.get(envvar_name, None)\\n\\n            if value is not None:\\n                if value == \\\"\\\" and convert_empty_to_none:\\n                    return None\\n                else:\\n                    return value\\n            else:\\n                return func(*args, **kwargs)\\n\\n        return wrapper\\n\\n    return decorator\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"\\nCode in ``conda.common`` is not conda-specific.  Technically, it sits *aside* the application\\nstack and not *within* the stack.  It is able to stand independently on its own.\\nThe *only* allowed imports of conda code in ``conda.common`` modules are imports of other\\n``conda.common`` modules and imports from ``conda._vendor``.\\n\\nIf objects are needed from other parts of conda, they should be passed directly as arguments to\\nfunctions and methods.\\n\\\"\\\"\\\"\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Common URL utilities.\\\"\\\"\\\"\\n\\nimport codecs\\nimport re\\nimport socket\\nfrom collections import namedtuple\\nfrom functools import lru_cache\\nfrom getpass import getpass\\nfrom os.path import abspath, expanduser\\nfrom urllib.parse import (  # noqa: F401\\n    ParseResult,\\n    quote,\\n    quote_plus,\\n    unquote,\\n    unquote_plus,\\n)\\nfrom urllib.parse import urlparse as _urlparse\\nfrom urllib.parse import urlunparse as _urlunparse  # noqa: F401\\n\\nfrom .compat import on_win\\nfrom .path import split_filename, strip_pkg_extension\\n\\n\\ndef hex_octal_to_int(ho):\\n    ho = ord(ho.upper())\\n    o0 = ord(\\\"0\\\")\\n    o9 = ord(\\\"9\\\")\\n    oA = ord(\\\"A\\\")\\n    oF = ord(\\\"F\\\")\\n    res = (\\n        ho - o0\\n        if ho >= o0 and ho <= o9\\n        else (ho - oA + 10)\\n        if ho >= oA and ho <= oF\\n        else None\\n    )\\n    return res\\n\\n\\n@lru_cache(maxsize=None)\\ndef percent_decode(path):\\n    # This is not fast so avoid when we can.\\n    if \\\"%\\\" not in path:\\n        return path\\n    ranges = []\\n    for m in re.finditer(r\\\"(%[0-9A-F]{2})\\\", path, flags=re.IGNORECASE):\\n        ranges.append((m.start(), m.end()))\\n    if not len(ranges):\\n        return path\\n\\n    # Sorry! Correctness is more important than speed at the moment.\\n    # Should use a map + lambda eventually.\\n    result = b\\\"\\\"\\n    skips = 0\\n    for i, c in enumerate(path):\\n        if skips > 0:\\n            skips -= 1\\n            continue\\n        c = c.encode(\\\"ascii\\\")\\n        emit = c\\n        if c == b\\\"%\\\":\\n            for r in ranges:\\n                if i == r[0]:\\n                    import struct\\n\\n                    emit = struct.pack(\\n                        \\\"B\\\",\\n                        hex_octal_to_int(path[i + 1]) * 16\\n                        + hex_octal_to_int(path[i + 2]),\\n                    )\\n                    skips = 2\\n                    break\\n        if emit:\\n            result += emit\\n    return codecs.utf_8_decode(result)[0]\\n\\n\\nfile_scheme = \\\"file://\\\"\\n\\n# Keeping this around for now, need to combine with the same function in conda/common/path.py\\n\\\"\\\"\\\"\\ndef url_to_path(url):\\n    assert url.startswith(file_scheme), \\\"{} is not a file-scheme URL\\\".format(url)\\n    decoded = percent_decode(url[len(file_scheme):])\\n    if decoded.startswith('/') and decoded[2] == ':':\\n        # A Windows path.\\n        decoded.replace('/', '\\\\\\\\')\\n    return decoded\\n\\\"\\\"\\\"\\n\\n\\n@lru_cache(maxsize=None)\\ndef path_to_url(path):\\n    if not path:\\n        raise ValueError(f\\\"Not allowed: {path!r}\\\")\\n    if path.startswith(file_scheme):\\n        try:\\n            path.decode(\\\"ascii\\\")\\n        except UnicodeDecodeError:\\n            raise ValueError(\\n                f\\\"Non-ascii not allowed for things claiming to be URLs: {path!r}\\\"\\n            )\\n        return path\\n    path = abspath(expanduser(path)).replace(\\\"\\\\\\\\\\\", \\\"/\\\")\\n    # We do not use urljoin here because we want to take our own\\n    # *very* explicit control of how paths get encoded into URLs.\\n    #   We should not follow any RFCs on how to encode and decode\\n    # them, we just need to make sure we can represent them in a\\n    # way that will not cause problems for whatever amount of\\n    # urllib processing we *do* need to do on them (which should\\n    # be none anyway, but I doubt that is the case). I have gone\\n    # for ASCII and % encoding of everything not alphanumeric or\\n    # not in `!'()*-._/:`. This should be pretty save.\\n    #\\n    # To avoid risking breaking the internet, this code only runs\\n    # for `file://` URLs.\\n    #\\n    percent_encode_chars = \\\"!'()*-._/\\\\\\\\:\\\"\\n    percent_encode = lambda s: \\\"\\\".join(\\n        [f\\\"%{ord(c):02X}\\\", c][c < \\\"{\\\" and c.isalnum() or c in percent_encode_chars]\\n        for c in s\\n    )\\n    if any(ord(char) >= 128 for char in path):\\n        path = percent_encode(\\n            path.decode(\\\"unicode-escape\\\")\\n            if hasattr(path, \\\"decode\\\")\\n            else bytes(path, \\\"utf-8\\\").decode(\\\"unicode-escape\\\")\\n        )\\n\\n    # https://blogs.msdn.microsoft.com/ie/2006/12/06/file-uris-in-windows/\\n    if len(path) > 1 and path[1] == \\\":\\\":\\n        path = file_scheme + \\\"/\\\" + path\\n    else:\\n        path = file_scheme + path\\n    return path\\n\\n\\nurl_attrs = (\\n    \\\"scheme\\\",\\n    \\\"path\\\",\\n    \\\"query\\\",\\n    \\\"fragment\\\",\\n    \\\"username\\\",\\n    \\\"password\\\",\\n    \\\"hostname\\\",\\n    \\\"port\\\",\\n)\\n\\n\\nclass Url(namedtuple(\\\"Url\\\", url_attrs)):\\n    \\\"\\\"\\\"\\n    Object used to represent a Url. The string representation of this object is a url string.\\n\\n    This object was inspired by the urllib3 implementation as it gives you a way to construct\\n    URLs from various parts. The motivation behind this object was making something that is\\n    interoperable with built the `urllib.parse.urlparse` function and has more features than\\n    the built-in `ParseResult` object.\\n    \\\"\\\"\\\"\\n\\n    def __new__(\\n        cls,\\n        scheme=None,\\n        path=None,\\n        query=None,\\n        fragment=None,\\n        username=None,\\n        password=None,\\n        hostname=None,\\n        port=None,\\n    ):\\n        if path and not path.startswith(\\\"/\\\"):\\n            path = \\\"/\\\" + path\\n        if scheme:\\n            scheme = scheme.lower()\\n        if hostname:\\n            hostname = hostname.lower()\\n        return super().__new__(\\n            cls, scheme, path, query, fragment, username, password, hostname, port\\n        )\\n\\n    @property\\n    def auth(self):\\n        if self.username and self.password:\\n            return f\\\"{self.username}:{self.password}\\\"\\n        elif self.username:\\n            return self.username\\n\\n    @property\\n    def netloc(self):\\n        if self.port:\\n            return f\\\"{self.hostname}:{self.port}\\\"\\n        return self.hostname\\n\\n    def __str__(self):\\n        scheme, path, query, fragment, username, password, hostname, port = self\\n        url = \\\"\\\"\\n\\n        if scheme:\\n            url += f\\\"{scheme}://\\\"\\n        if password and username:\\n            url += f\\\"{username}:{password}@\\\"\\n        if hostname:\\n            url += hostname\\n        if port:\\n            url += f\\\":{port}\\\"\\n        if path:\\n            url += path\\n        if query:\\n            url += f\\\"?{query}\\\"\\n        if fragment:\\n            url += f\\\"#{fragment}\\\"\\n\\n        return url\\n\\n    def as_dict(self) -> dict:\\n        \\\"\\\"\\\"Provide a public interface for namedtuple's _asdict\\\"\\\"\\\"\\n        return self._asdict()\\n\\n    def replace(self, **kwargs) -> \\\"Url\\\":\\n        \\\"\\\"\\\"Provide a public interface for namedtuple's _replace\\\"\\\"\\\"\\n        return self._replace(**kwargs)\\n\\n    @classmethod\\n    def from_parse_result(cls, parse_result: ParseResult) -> \\\"Url\\\":\\n        values = {fld: getattr(parse_result, fld, \\\"\\\") for fld in url_attrs}\\n        return cls(**values)\\n\\n\\n@lru_cache(maxsize=None)\\ndef urlparse(url: str) -> Url:\\n    if on_win and url.startswith(\\\"file:\\\"):\\n        url.replace(\\\"\\\\\\\\\\\", \\\"/\\\")\\n    # Allows us to pass in strings like 'example.com:8080/path/1'.\\n    if not has_scheme(url):\\n        url = \\\"//\\\" + url\\n    return Url.from_parse_result(_urlparse(url))\\n\\n\\ndef url_to_s3_info(url):\\n    \\\"\\\"\\\"Convert an s3 url to a tuple of bucket and key.\\n\\n    Examples:\\n        >>> url_to_s3_info(\\\"s3://bucket-name.bucket/here/is/the/key\\\")\\n        ('bucket-name.bucket', '/here/is/the/key')\\n    \\\"\\\"\\\"\\n    parsed_url = urlparse(url)\\n    assert parsed_url.scheme == \\\"s3\\\", f\\\"You can only use s3: urls (not {url!r})\\\"\\n    bucket, key = parsed_url.hostname, parsed_url.path\\n    return bucket, key\\n\\n\\ndef is_url(url):\\n    \\\"\\\"\\\"\\n    Examples:\\n        >>> is_url(None)\\n        False\\n        >>> is_url(\\\"s3://some/bucket\\\")\\n        True\\n    \\\"\\\"\\\"\\n    if not url:\\n        return False\\n    try:\\n        return urlparse(url).scheme != \\\"\\\"\\n    except ValueError:\\n        return False\\n\\n\\ndef is_ipv4_address(string_ip):\\n    \\\"\\\"\\\"\\n    Examples:\\n        >>> [is_ipv4_address(ip) for ip in ('8.8.8.8', '192.168.10.10', '255.255.255.255')]\\n        [True, True, True]\\n        >>> [is_ipv4_address(ip) for ip in ('8.8.8', '192.168.10.10.20', '256.255.255.255', '::1')]\\n        [False, False, False, False]\\n    \\\"\\\"\\\"\\n    try:\\n        socket.inet_aton(string_ip)\\n    except OSError:\\n        return False\\n    return string_ip.count(\\\".\\\") == 3\\n\\n\\ndef is_ipv6_address(string_ip):\\n    \\\"\\\"\\\"\\n    Examples:\\n        >> [is_ipv6_address(ip) for ip in ('::1', '2001:db8:85a3::370:7334', '1234:'*7+'1234')]\\n        [True, True, True]\\n        >> [is_ipv6_address(ip) for ip in ('192.168.10.10', '1234:'*8+'1234')]\\n        [False, False]\\n    \\\"\\\"\\\"\\n    try:\\n        socket.inet_pton(socket.AF_INET6, string_ip)\\n    except OSError:\\n        return False\\n    return True\\n\\n\\ndef is_ip_address(string_ip):\\n    \\\"\\\"\\\"\\n    Examples:\\n        >> is_ip_address('192.168.10.10')\\n        True\\n        >> is_ip_address('::1')\\n        True\\n        >> is_ip_address('www.google.com')\\n        False\\n    \\\"\\\"\\\"\\n    return is_ipv4_address(string_ip) or is_ipv6_address(string_ip)\\n\\n\\ndef join(*args):\\n    start = \\\"/\\\" if not args[0] or args[0].startswith(\\\"/\\\") else \\\"\\\"\\n    return start + \\\"/\\\".join(y for y in (x.strip(\\\"/\\\") for x in args if x) if y)\\n\\n\\njoin_url = join\\n\\n\\ndef has_scheme(value):\\n    return re.match(r\\\"[a-z][a-z0-9]{0,11}://\\\", value)\\n\\n\\ndef strip_scheme(url):\\n    \\\"\\\"\\\"\\n    Examples:\\n        >>> strip_scheme(\\\"https://www.conda.io\\\")\\n        'www.conda.io'\\n        >>> strip_scheme(\\\"s3://some.bucket/plus/a/path.ext\\\")\\n        'some.bucket/plus/a/path.ext'\\n    \\\"\\\"\\\"\\n    return url.split(\\\"://\\\", 1)[-1]\\n\\n\\ndef mask_anaconda_token(url):\\n    _, token = split_anaconda_token(url)\\n    return url.replace(token, \\\"<TOKEN>\\\", 1) if token else url\\n\\n\\ndef split_anaconda_token(url):\\n    \\\"\\\"\\\"\\n    Examples:\\n        >>> split_anaconda_token(\\\"https://1.2.3.4/t/tk-123-456/path\\\")\\n        (u'https://1.2.3.4/path', u'tk-123-456')\\n        >>> split_anaconda_token(\\\"https://1.2.3.4/t//path\\\")\\n        (u'https://1.2.3.4/path', u'')\\n        >>> split_anaconda_token(\\\"https://some.domain/api/t/tk-123-456/path\\\")\\n        (u'https://some.domain/api/path', u'tk-123-456')\\n        >>> split_anaconda_token(\\\"https://1.2.3.4/conda/t/tk-123-456/path\\\")\\n        (u'https://1.2.3.4/conda/path', u'tk-123-456')\\n        >>> split_anaconda_token(\\\"https://1.2.3.4/path\\\")\\n        (u'https://1.2.3.4/path', None)\\n        >>> split_anaconda_token(\\\"https://10.2.3.4:8080/conda/t/tk-123-45\\\")\\n        (u'https://10.2.3.4:8080/conda', u'tk-123-45')\\n    \\\"\\\"\\\"\\n    _token_match = re.search(r\\\"/t/([a-zA-Z0-9-]*)\\\", url)\\n    token = _token_match.groups()[0] if _token_match else None\\n    cleaned_url = url.replace(\\\"/t/\\\" + token, \\\"\\\", 1) if token is not None else url\\n    return cleaned_url.rstrip(\\\"/\\\"), token\\n\\n\\ndef split_platform(known_subdirs, url):\\n    \\\"\\\"\\\"\\n\\n    Examples:\\n        >>> from conda.base.constants import KNOWN_SUBDIRS\\n        >>> split_platform(KNOWN_SUBDIRS, \\\"https://1.2.3.4/t/tk-123/linux-ppc64le/path\\\")\\n        (u'https://1.2.3.4/t/tk-123/path', u'linux-ppc64le')\\n\\n    \\\"\\\"\\\"\\n    _platform_match = _split_platform_re(known_subdirs).search(url)\\n    platform = _platform_match.groups()[0] if _platform_match else None\\n    cleaned_url = url.replace(\\\"/\\\" + platform, \\\"\\\", 1) if platform is not None else url\\n    return cleaned_url.rstrip(\\\"/\\\"), platform\\n\\n\\n@lru_cache(maxsize=None)\\ndef _split_platform_re(known_subdirs):\\n    _platform_match_regex = r\\\"/({})(?:/|$)\\\".format(\\n        r\\\"|\\\".join(rf\\\"{d}\\\" for d in known_subdirs)\\n    )\\n    return re.compile(_platform_match_regex, re.IGNORECASE)\\n\\n\\ndef has_platform(url, known_subdirs):\\n    url_no_package_name, _ = split_filename(url)\\n    if not url_no_package_name:\\n        return None\\n    maybe_a_platform = url_no_package_name.rsplit(\\\"/\\\", 1)[-1]\\n    return maybe_a_platform in known_subdirs and maybe_a_platform or None\\n\\n\\ndef split_scheme_auth_token(url):\\n    \\\"\\\"\\\"\\n    Examples:\\n        >>> split_scheme_auth_token(\\\"https://u:p@conda.io/t/x1029384756/more/path\\\")\\n        ('conda.io/more/path', 'https', 'u:p', 'x1029384756')\\n        >>> split_scheme_auth_token(None)\\n        (None, None, None, None)\\n    \\\"\\\"\\\"\\n    if not url:\\n        return None, None, None, None\\n    cleaned_url, token = split_anaconda_token(url)\\n    url_parts = urlparse(cleaned_url)\\n    remainder_url = Url(\\n        hostname=url_parts.hostname,\\n        port=url_parts.port,\\n        path=url_parts.path,\\n        query=url_parts.query,\\n    )\\n\\n    return str(remainder_url), url_parts.scheme, url_parts.auth, token\\n\\n\\ndef split_conda_url_easy_parts(known_subdirs, url):\\n    # scheme, auth, token, platform, package_filename, host, port, path, query\\n    cleaned_url, token = split_anaconda_token(url)\\n    cleaned_url, platform = split_platform(known_subdirs, cleaned_url)\\n    _, ext = strip_pkg_extension(cleaned_url)\\n    cleaned_url, package_filename = (\\n        cleaned_url.rsplit(\\\"/\\\", 1)\\n        if ext and \\\"/\\\" in cleaned_url\\n        else (cleaned_url, None)\\n    )\\n\\n    # TODO: split out namespace using regex\\n    url_parts = urlparse(cleaned_url)\\n\\n    return (\\n        url_parts.scheme,\\n        url_parts.auth,\\n        token,\\n        platform,\\n        package_filename,\\n        url_parts.hostname,\\n        url_parts.port,\\n        url_parts.path,\\n        url_parts.query,\\n    )\\n\\n\\n@lru_cache(maxsize=None)\\ndef get_proxy_username_and_pass(scheme):\\n    username = input(f\\\"\\\\n{scheme} proxy username: \\\")\\n    passwd = getpass(\\\"Password: \\\")\\n    return username, passwd\\n\\n\\ndef add_username_and_password(url: str, username: str, password: str) -> str:\\n    \\\"\\\"\\\"\\n    Inserts `username` and `password` into provided `url`\\n\\n    >>> add_username_and_password('https://anaconda.org', 'TestUser', 'Password')\\n    'https://TestUser:Password@anaconda.org'\\n    \\\"\\\"\\\"\\n    url = urlparse(url)\\n    url_with_auth = url.replace(username=username, password=quote(password, safe=\\\"\\\"))\\n    return str(url_with_auth)\\n\\n\\ndef maybe_add_auth(url: str, auth: str, force=False) -> str:\\n    \\\"\\\"\\\"Add auth if the url doesn't currently have it.\\n\\n    By default, does not replace auth if it already exists.  Setting ``force`` to ``True``\\n    overrides this behavior.\\n\\n    Examples:\\n        >>> maybe_add_auth(\\\"https://www.conda.io\\\", \\\"user:passwd\\\")\\n        'https://user:passwd@www.conda.io'\\n        >>> maybe_add_auth(\\\"https://www.conda.io\\\", \\\"\\\")\\n        'https://www.conda.io'\\n    \\\"\\\"\\\"\\n    if not auth:\\n        return url\\n\\n    url_parts = urlparse(url)\\n    if url_parts.username and url_parts.password and not force:\\n        return url\\n\\n    auth_parts = auth.split(\\\":\\\")\\n    if len(auth_parts) > 1:\\n        url_parts = url_parts.replace(username=auth_parts[0], password=auth_parts[1])\\n\\n    return str(url_parts)\\n\\n\\ndef maybe_unquote(url):\\n    return unquote_plus(remove_auth(url)) if url else url\\n\\n\\ndef remove_auth(url: str) -> str:\\n    \\\"\\\"\\\"Remove embedded authentication from URL.\\n\\n    .. code-block:: pycon\\n\\n       >>> remove_auth(\\\"https://user:password@anaconda.com\\\")\\n       'https://anaconda.com'\\n    \\\"\\\"\\\"\\n    url = urlparse(url)\\n    url_no_auth = url.replace(username=\\\"\\\", password=\\\"\\\")\\n\\n    return str(url_no_auth)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Common compatiblity code.\\\"\\\"\\\"\\n# Try to keep compat small because it's imported by everything\\n# What is compat, and what isn't?\\n# If a piece of code is \\\"general\\\" and used in multiple modules, it goes here.\\n# If it's only used in one module, keep it in that module, preferably near the top.\\n# This module should contain ONLY stdlib imports.\\n\\nimport builtins\\nimport sys\\n\\nfrom ..deprecations import deprecated\\n\\non_win = bool(sys.platform == \\\"win32\\\")\\non_mac = bool(sys.platform == \\\"darwin\\\")\\non_linux = bool(sys.platform == \\\"linux\\\")\\n\\nFILESYSTEM_ENCODING = sys.getfilesystemencoding()\\n\\n# Control some tweakables that will be removed finally.\\nENCODE_ENVIRONMENT = True\\n\\n\\ndef encode_for_env_var(value) -> str:\\n    \\\"\\\"\\\"Environment names and values need to be string.\\\"\\\"\\\"\\n    if isinstance(value, str):\\n        return value\\n    elif isinstance(value, bytes):\\n        return value.decode()\\n    return str(value)\\n\\n\\ndef encode_environment(env):\\n    if ENCODE_ENVIRONMENT:\\n        env = {encode_for_env_var(k): encode_for_env_var(v) for k, v in env.items()}\\n    return env\\n\\n\\n@deprecated(\\\"24.9\\\", \\\"25.3\\\")\\ndef encode_arguments(arguments):\\n    return arguments\\n\\n\\nfrom collections.abc import Iterable\\n\\n\\ndef isiterable(obj):\\n    return not isinstance(obj, str) and isinstance(obj, Iterable)\\n\\n\\n# #############################\\n# other\\n# #############################\\n\\nfrom collections import OrderedDict as odict  # noqa: F401\\n\\n\\ndef open(\\n    file, mode=\\\"r\\\", buffering=-1, encoding=None, errors=None, newline=None, closefd=True\\n):\\n    if \\\"b\\\" in mode:\\n        return builtins.open(\\n            file,\\n            str(mode),\\n            buffering=buffering,\\n            errors=errors,\\n            newline=newline,\\n            closefd=closefd,\\n        )\\n    else:\\n        return builtins.open(\\n            file,\\n            str(mode),\\n            buffering=buffering,\\n            encoding=encoding or \\\"utf-8\\\",\\n            errors=errors,\\n            newline=newline,\\n            closefd=closefd,\\n        )\\n\\n\\ndef six_with_metaclass(meta, *bases):\\n    \\\"\\\"\\\"Create a base class with a metaclass.\\\"\\\"\\\"\\n\\n    # This requires a bit of explanation: the basic idea is to make a dummy\\n    # metaclass for one level of class instantiation that replaces itself with\\n    # the actual metaclass.\\n    class metaclass(type):\\n        def __new__(cls, name, this_bases, d):\\n            return meta(name, bases, d)\\n\\n        @classmethod\\n        def __prepare__(cls, name, this_bases):\\n            return meta.__prepare__(name, bases)\\n\\n    return type.__new__(metaclass, \\\"temporary_class\\\", (), {})\\n\\n\\nNoneType = type(None)\\nprimitive_types = (str, int, float, complex, bool, NoneType)\\n\\n\\ndef ensure_binary(value):\\n    try:\\n        return value.encode(\\\"utf-8\\\")\\n    except AttributeError:  # pragma: no cover\\n        # AttributeError: '<>' object has no attribute 'encode'\\n        # In this case assume already binary type and do nothing\\n        return value\\n\\n\\ndef ensure_text_type(value) -> str:\\n    try:\\n        return value.decode(\\\"utf-8\\\")\\n    except AttributeError:  # pragma: no cover\\n        # AttributeError: '<>' object has no attribute 'decode'\\n        # In this case assume already text_type and do nothing\\n        return value\\n    except UnicodeDecodeError:  # pragma: no cover\\n        from charset_normalizer import from_bytes\\n\\n        return str(from_bytes(value).best())\\n    except UnicodeEncodeError:  # pragma: no cover\\n        # it's already str, so ignore?\\n        # not sure, surfaced with tests/models/test_match_spec.py test_tarball_match_specs\\n        # using py27\\n        return value\\n\\n\\ndef ensure_unicode(value):\\n    try:\\n        return value.decode(\\\"unicode_escape\\\")\\n    except AttributeError:  # pragma: no cover\\n        # AttributeError: '<>' object has no attribute 'decode'\\n        # In this case assume already unicode and do nothing\\n        return value\\n\\n\\ndef ensure_fs_path_encoding(value):\\n    try:\\n        return value.encode(FILESYSTEM_ENCODING)\\n    except AttributeError:\\n        return value\\n    except UnicodeEncodeError:\\n        return value\\n\\n\\ndef ensure_utf8_encoding(value):\\n    try:\\n        return value.encode(\\\"utf-8\\\")\\n    except AttributeError:\\n        return value\\n    except UnicodeEncodeError:\\n        return value\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Common disk utilities.\\\"\\\"\\\"\\n\\nfrom contextlib import contextmanager\\nfrom os import unlink\\n\\nfrom ..auxlib.compat import Utf8NamedTemporaryFile\\nfrom ..deprecations import deprecated\\n\\n\\n@deprecated(\\\"24.3\\\", \\\"24.9\\\", addendum=\\\"Use `tempfile` instead.\\\")\\n@contextmanager\\ndef temporary_content_in_file(content, suffix=\\\"\\\"):\\n    # content returns temporary file path with contents\\n    fh = None\\n    path = None\\n    try:\\n        with Utf8NamedTemporaryFile(mode=\\\"w\\\", delete=False, suffix=suffix) as fh:\\n            path = fh.name\\n            fh.write(content)\\n            fh.flush()\\n            fh.close()\\n            yield path\\n    finally:\\n        if fh is not None:\\n            fh.close()\\n        if path is not None:\\n            unlink(path)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\nfrom __future__ import annotations\\n\\nimport os\\nfrom functools import lru_cache\\nfrom logging import getLogger\\nfrom os.path import exists\\n\\nfrom ..compat import on_linux\\n\\nlog = getLogger(__name__)\\n\\n\\n@lru_cache(maxsize=None)\\ndef linux_get_libc_version() -> tuple[str, str] | tuple[None, None]:\\n    \\\"\\\"\\\"If on linux, returns (libc_family, version), otherwise (None, None).\\\"\\\"\\\"\\n    if not on_linux:\\n        return None, None\\n\\n    for name in (\\\"CS_GNU_LIBC_VERSION\\\", \\\"CS_GNU_LIBPTHREAD_VERSION\\\"):\\n        try:\\n            # check if os.confstr returned None\\n            if value := os.confstr(name):\\n                family, version = value.strip().split(\\\" \\\")\\n                break\\n        except ValueError:\\n            # ValueError: name is not defined in os.confstr_names\\n            # ValueError: value is not of the form \\\"<family> <version>\\\"\\n            pass\\n    else:\\n        family, version = \\\"glibc\\\", \\\"2.5\\\"\\n        log.warning(\\n            \\\"Failed to detect libc family and version, assuming %s/%s\\\",\\n            family,\\n            version,\\n        )\\n\\n    # NPTL is just the name of the threading library, even though the\\n    # version refers to that of uClibc. os.readlink() can help to try to\\n    # figure out a better name instead.\\n    if family == \\\"NPTL\\\":  # pragma: no cover\\n        for clib in (\\n            entry.path for entry in os.scandir(\\\"/lib\\\") if entry.name[:7] == \\\"libc.so\\\"\\n        ):\\n            clib = os.readlink(clib)\\n            if exists(clib):\\n                if clib.startswith(\\\"libuClibc\\\"):\\n                    if version.startswith(\\\"0.\\\"):\\n                        family = \\\"uClibc\\\"\\n                    else:\\n                        family = \\\"uClibc-ng\\\"\\n                    break\\n        else:\\n            # This could be some other C library; it is unlikely though.\\n            family = \\\"uClibc\\\"\\n            log.warning(\\n                \\\"Failed to detect non-glibc family, assuming %s/%s\\\",\\n                family,\\n                version,\\n            )\\n\\n    return family, version\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\nimport os\\nfrom logging import getLogger\\n\\nlog = getLogger(__name__)\\n\\n\\ndef get_free_space_on_unix(dir_name):\\n    st = os.statvfs(dir_name)\\n    return st.f_bavail * st.f_frsize\\n\\n\\ndef is_admin_on_unix():\\n    # http://stackoverflow.com/a/1026626/2127762\\n    return os.geteuid() == 0\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\nfrom enum import IntEnum\\nfrom logging import getLogger\\n\\nfrom ..compat import ensure_binary, on_win\\n\\nlog = getLogger(__name__)\\n\\nif on_win:\\n    from ctypes import (\\n        POINTER,\\n        Structure,\\n        WinError,\\n        byref,\\n        c_char_p,\\n        c_int,\\n        c_ulong,\\n        c_ulonglong,\\n        c_void_p,\\n        c_wchar_p,\\n        pointer,\\n        sizeof,\\n        windll,\\n    )\\n    from ctypes.wintypes import BOOL, DWORD, HANDLE, HINSTANCE, HKEY, HWND\\n\\n    PHANDLE = POINTER(HANDLE)\\n    PDWORD = POINTER(DWORD)\\n    SEE_MASK_NOCLOSEPROCESS = 0x00000040\\n    INFINITE = -1\\n\\n    WaitForSingleObject = windll.kernel32.WaitForSingleObject\\n    WaitForSingleObject.argtypes = (HANDLE, DWORD)\\n    WaitForSingleObject.restype = DWORD\\n\\n    CloseHandle = windll.kernel32.CloseHandle\\n    CloseHandle.argtypes = (HANDLE,)\\n    CloseHandle.restype = BOOL\\n\\n    class ShellExecuteInfo(Structure):\\n        \\\"\\\"\\\"\\n        https://docs.microsoft.com/en-us/windows/desktop/api/shellapi/nf-shellapi-shellexecuteexa\\n        https://docs.microsoft.com/en-us/windows/desktop/api/shellapi/ns-shellapi-_shellexecuteinfoa\\n        \\\"\\\"\\\"\\n\\n        _fields_ = [\\n            (\\\"cbSize\\\", DWORD),\\n            (\\\"fMask\\\", c_ulong),\\n            (\\\"hwnd\\\", HWND),\\n            (\\\"lpVerb\\\", c_char_p),\\n            (\\\"lpFile\\\", c_char_p),\\n            (\\\"lpParameters\\\", c_char_p),\\n            (\\\"lpDirectory\\\", c_char_p),\\n            (\\\"nShow\\\", c_int),\\n            (\\\"hInstApp\\\", HINSTANCE),\\n            (\\\"lpIDList\\\", c_void_p),\\n            (\\\"lpClass\\\", c_char_p),\\n            (\\\"hKeyClass\\\", HKEY),\\n            (\\\"dwHotKey\\\", DWORD),\\n            (\\\"hIcon\\\", HANDLE),\\n            (\\\"hProcess\\\", HANDLE),\\n        ]\\n\\n        def __init__(self, **kwargs):\\n            Structure.__init__(self)\\n            self.cbSize = sizeof(self)\\n            for field_name, field_value in kwargs.items():\\n                if isinstance(field_value, str):\\n                    field_value = ensure_binary(field_value)\\n                setattr(self, field_name, field_value)\\n\\n    PShellExecuteInfo = POINTER(ShellExecuteInfo)\\n    ShellExecuteEx = windll.Shell32.ShellExecuteExA\\n    ShellExecuteEx.argtypes = (PShellExecuteInfo,)\\n    ShellExecuteEx.restype = BOOL\\n\\n\\nclass SW(IntEnum):\\n    HIDE = 0\\n    MAXIMIZE = 3\\n    MINIMIZE = 6\\n    RESTORE = 9\\n    SHOW = 5\\n    SHOWDEFAULT = 10\\n    SHOWMAXIMIZED = 3\\n    SHOWMINIMIZED = 2\\n    SHOWMINNOACTIVE = 7\\n    SHOWNA = 8\\n    SHOWNOACTIVATE = 4\\n    SHOWNORMAL = 1\\n\\n\\nclass ERROR(IntEnum):\\n    ZERO = 0\\n    FILE_NOT_FOUND = 2\\n    PATH_NOT_FOUND = 3\\n    BAD_FORMAT = 11\\n    ACCESS_DENIED = 5\\n    ASSOC_INCOMPLETE = 27\\n    DDE_BUSY = 30\\n    DDE_FAIL = 29\\n    DDE_TIMEOUT = 28\\n    DLL_NOT_FOUND = 32\\n    NO_ASSOC = 31\\n    OOM = 8\\n    SHARE = 26\\n\\n\\ndef get_free_space_on_windows(dir_name):\\n    result = None\\n    free_bytes = c_ulonglong(0)\\n    try:\\n        windll.kernel32.GetDiskFreeSpaceExW(\\n            c_wchar_p(dir_name),\\n            None,\\n            None,\\n            pointer(free_bytes),\\n        )\\n        result = free_bytes.value\\n    except Exception as e:\\n        log.info(\\\"%r\\\", e)\\n    return result\\n\\n\\ndef is_admin_on_windows():  # pragma: unix no cover\\n    # http://stackoverflow.com/a/1026626/2127762\\n    result = False\\n    try:\\n        result = windll.shell32.IsUserAnAdmin() != 0\\n    except Exception as e:  # pragma: no cover\\n        log.info(\\\"%r\\\", e)\\n        # result = 'unknown'\\n    return result\\n\\n\\ndef _wait_and_close_handle(process_handle):\\n    \\\"\\\"\\\"Waits until spawned process finishes and closes the handle for it.\\\"\\\"\\\"\\n    try:\\n        WaitForSingleObject(process_handle, INFINITE)\\n        CloseHandle(process_handle)\\n    except Exception as e:\\n        log.info(\\\"%r\\\", e)\\n\\n\\ndef run_as_admin(args, wait=True):\\n    \\\"\\\"\\\"\\n    Run command line argument list (`args`) with elevated privileges.\\n\\n    If `wait` is True, the process will block until completion.\\n\\n    NOTES:\\n        - no stdin / stdout / stderr pipe support\\n        - does not automatically quote arguments (i.e. for paths that may contain spaces)\\n    See:\\n    - http://stackoverflow.com/a/19719292/1170370 on 20160407 MCS.\\n    - msdn.microsoft.com/en-us/library/windows/desktop/bb762153(v=vs.85).aspx\\n    - https://github.com/ContinuumIO/menuinst/blob/master/menuinst/windows/win_elevate.py\\n    - https://github.com/saltstack/salt-windows-install/blob/master/deps/salt/python/App/Lib/site-packages/win32/Demos/pipes/runproc.py  # NOQA\\n    - https://github.com/twonds/twisted/blob/master/twisted/internet/_dumbwin32proc.py\\n    - https://stackoverflow.com/a/19982092/2127762\\n    - https://www.codeproject.com/Articles/19165/Vista-UAC-The-Definitive-Guide\\n    - https://github.com/JustAMan/pyWinClobber/blob/master/win32elevate.py\\n    \\\"\\\"\\\"\\n    arg0 = args[0]\\n    param_str = \\\" \\\".join(args[1:] if len(args) > 1 else ())\\n    hprocess = None\\n    error_code = None\\n    try:\\n        execute_info = ShellExecuteInfo(\\n            fMask=SEE_MASK_NOCLOSEPROCESS,\\n            hwnd=None,\\n            lpVerb=\\\"runas\\\",\\n            lpFile=arg0,\\n            lpParameters=param_str,\\n            lpDirectory=None,\\n            nShow=SW.HIDE,\\n        )\\n        successful = ShellExecuteEx(byref(execute_info))\\n        hprocess = execute_info.hProcess\\n    except Exception as e:\\n        successful = False\\n        error_code = e\\n        log.info(\\\"%r\\\", e)\\n\\n    if not successful:\\n        error_code = WinError()\\n    elif wait:\\n        _wait_and_close_handle(execute_info.hProcess)\\n\\n    return hprocess, error_code\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\nfrom logging import getLogger\\n\\nfrom ..compat import on_win\\n\\nif on_win:\\n    from .windows import get_free_space_on_windows as get_free_space\\n    from .windows import is_admin_on_windows as is_admin\\nelse:\\n    from .unix import get_free_space_on_unix as get_free_space  # noqa\\n    from .unix import is_admin_on_unix as is_admin  # noqa\\n\\n\\nlog = getLogger(__name__)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Common Python package format utilities.\\\"\\\"\\\"\\n\\nimport platform\\nimport re\\nimport sys\\nimport warnings\\nfrom collections import namedtuple\\nfrom configparser import ConfigParser\\nfrom csv import reader as csv_reader\\nfrom email.parser import HeaderParser\\nfrom errno import ENOENT\\nfrom io import StringIO\\nfrom itertools import chain\\nfrom logging import getLogger\\nfrom os import name as os_name\\nfrom os import scandir, strerror\\nfrom os.path import basename, dirname, isdir, isfile, join, lexists\\nfrom posixpath import normpath as posix_normpath\\n\\nfrom ... import CondaError\\nfrom ...auxlib.decorators import memoizedproperty\\nfrom ..compat import open\\nfrom ..iterators import groupby_to_dict as groupby\\nfrom ..path import (\\n    get_major_minor_version,\\n    get_python_site_packages_short_path,\\n    pyc_path,\\n    win_path_ok,\\n)\\n\\ntry:\\n    from frozendict import frozendict\\nexcept ImportError:\\n    from ..._vendor.frozendict import frozendict\\n\\nlog = getLogger(__name__)\\n\\n# TODO: complete this list\\nPYPI_TO_CONDA = {\\n    \\\"graphviz\\\": \\\"python-graphviz\\\",\\n}\\n# TODO: complete this list\\nPYPI_CONDA_DEPS = {\\n    \\\"graphviz\\\": [\\\"graphviz\\\"],  # What version constraints?\\n}\\n# This regex can process requirement including or not including name.\\n# This is useful for parsing, for example, `Python-Version`\\nPARTIAL_PYPI_SPEC_PATTERN = re.compile(\\n    r\\\"\\\"\\\"\\n    # Text needs to be stripped and all extra spaces replaced by single spaces\\n    (?P<name>^[A-Z0-9][A-Z0-9._-]*)?\\n    \\\\s?\\n    (\\\\[(?P<extras>.*)\\\\])?\\n    \\\\s?\\n    (?P<constraints>\\\\(? \\\\s? ([\\\\w\\\\d<>=!~,\\\\s\\\\.\\\\*+-]*) \\\\s? \\\\)? )?\\n    \\\\s?\\n\\\"\\\"\\\",\\n    re.VERBOSE | re.IGNORECASE,\\n)\\nPY_FILE_RE = re.compile(r\\\"^[^\\\\t\\\\n\\\\r\\\\f\\\\v]+/site-packages/[^\\\\t\\\\n\\\\r\\\\f\\\\v]+\\\\.py$\\\")\\nPySpec = namedtuple(\\\"PySpec\\\", [\\\"name\\\", \\\"extras\\\", \\\"constraints\\\", \\\"marker\\\", \\\"url\\\"])\\n\\n\\nclass MetadataWarning(Warning):\\n    pass\\n\\n\\n# Dist classes\\n# -----------------------------------------------------------------------------\\nclass PythonDistribution:\\n    \\\"\\\"\\\"Base object describing a python distribution based on path to anchor file.\\\"\\\"\\\"\\n\\n    MANIFEST_FILES = ()  # Only one is used, but many names available\\n    REQUIRES_FILES = ()  # Only one is used, but many names available\\n    MANDATORY_FILES = ()\\n    ENTRY_POINTS_FILES = (\\\"entry_points.txt\\\",)\\n\\n    @staticmethod\\n    def init(prefix_path, anchor_file, python_version):\\n        if anchor_file.endswith(\\\".egg-link\\\"):\\n            return PythonEggLinkDistribution(prefix_path, anchor_file, python_version)\\n        elif \\\".dist-info\\\" in anchor_file:\\n            return PythonInstalledDistribution(prefix_path, anchor_file, python_version)\\n        elif anchor_file.endswith(\\\".egg-info\\\"):\\n            anchor_full_path = join(prefix_path, win_path_ok(anchor_file))\\n            sp_reference = basename(anchor_file)\\n            return PythonEggInfoDistribution(\\n                anchor_full_path, python_version, sp_reference\\n            )\\n        elif \\\".egg-info\\\" in anchor_file:\\n            anchor_full_path = join(prefix_path, win_path_ok(dirname(anchor_file)))\\n            sp_reference = basename(dirname(anchor_file))\\n            return PythonEggInfoDistribution(\\n                anchor_full_path, python_version, sp_reference\\n            )\\n        elif \\\".egg\\\" in anchor_file:\\n            anchor_full_path = join(prefix_path, win_path_ok(dirname(anchor_file)))\\n            sp_reference = basename(dirname(anchor_file))\\n            return PythonEggInfoDistribution(\\n                anchor_full_path, python_version, sp_reference\\n            )\\n        else:\\n            raise NotImplementedError()\\n\\n    def __init__(self, anchor_full_path, python_version):\\n        # Don't call PythonDistribution directly. Use the init() static method.\\n        self.anchor_full_path = anchor_full_path\\n        self.python_version = python_version\\n\\n        if anchor_full_path and isfile(anchor_full_path):\\n            self._metadata_dir_full_path = dirname(anchor_full_path)\\n        elif anchor_full_path and isdir(anchor_full_path):\\n            self._metadata_dir_full_path = anchor_full_path\\n        else:\\n            raise RuntimeError(f\\\"Path not found: {anchor_full_path}\\\")\\n\\n        self._check_files()\\n        self._metadata = PythonDistributionMetadata(anchor_full_path)\\n        self._provides_file_data = ()\\n        self._requires_file_data = ()\\n\\n    def _check_files(self):\\n        \\\"\\\"\\\"Check the existence of mandatory files for a given distribution.\\\"\\\"\\\"\\n        for fname in self.MANDATORY_FILES:\\n            if self._metadata_dir_full_path:\\n                fpath = join(self._metadata_dir_full_path, fname)\\n                if not isfile(fpath):\\n                    raise OSError(ENOENT, strerror(ENOENT), fpath)\\n\\n    def _check_path_data(self, path, checksum, size):\\n        \\\"\\\"\\\"Normalizes record data content and format.\\\"\\\"\\\"\\n        if checksum:\\n            assert checksum.startswith(\\\"sha256=\\\"), (\\n                self._metadata_dir_full_path,\\n                path,\\n                checksum,\\n            )\\n            checksum = checksum[7:]\\n        else:\\n            checksum = None\\n        size = int(size) if size else None\\n\\n        return path, checksum, size\\n\\n    @staticmethod\\n    def _parse_requires_file_data(data, global_section=\\\"__global__\\\"):\\n        # https://setuptools.readthedocs.io/en/latest/formats.html#requires-txt\\n        requires = {}\\n        lines = [line.strip() for line in data.split(\\\"\\\\n\\\") if line]\\n\\n        if lines and not (lines[0].startswith(\\\"[\\\") and lines[0].endswith(\\\"]\\\")):\\n            # Add dummy section for unsectioned items\\n            lines = [f\\\"[{global_section}]\\\"] + lines\\n\\n        # Parse sections\\n        for line in lines:\\n            if line.startswith(\\\"[\\\") and line.endswith(\\\"]\\\"):\\n                section = line.strip()[1:-1]\\n                requires[section] = []\\n                continue\\n\\n            if line.strip():\\n                requires[section].append(line.strip())\\n\\n        # Adapt to *standard* requirements (add env markers to requirements)\\n        reqs = []\\n        extras = []\\n        for section, values in requires.items():\\n            if section == global_section:\\n                # This is the global section (same as dist_requires)\\n                reqs.extend(values)\\n            elif section.startswith(\\\":\\\"):\\n                # The section is used as a marker\\n                # Example: \\\":python_version < '3'\\\"\\n                marker = section.replace(\\\":\\\", \\\"; \\\")\\n                new_values = [v + marker for v in values]\\n                reqs.extend(new_values)\\n            else:\\n                # The section is an extra, i.e. \\\"docs\\\", or \\\"tests\\\"...\\n                extras.append(section)\\n                marker = f'; extra == \\\"{section}\\\"'\\n                new_values = [v + marker for v in values]\\n                reqs.extend(new_values)\\n\\n        return frozenset(reqs), extras\\n\\n    @staticmethod\\n    def _parse_entries_file_data(data):\\n        # https://setuptools.readthedocs.io/en/latest/formats.html#entry-points-txt-entry-point-plugin-metadata\\n        # FIXME: Use pkg_resources which provides API for this?\\n        entries_data = {}\\n        config = ConfigParser()\\n        config.optionxform = lambda x: x  # Avoid lowercasing keys\\n        try:\\n            do_read = config.read_file\\n        except AttributeError:\\n            do_read = config.readfp\\n        do_read(StringIO(data))\\n        for section in config.sections():\\n            entries_data[section] = dict(config.items(section))\\n\\n        return entries_data\\n\\n    def _load_requires_provides_file(self):\\n        # https://setuptools.readthedocs.io/en/latest/formats.html#requires-txt\\n        # FIXME: Use pkg_resources which provides API for this?\\n        requires, extras = None, None\\n        for fname in self.REQUIRES_FILES:\\n            fpath = join(self._metadata_dir_full_path, fname)\\n            if isfile(fpath):\\n                with open(fpath) as fh:\\n                    data = fh.read()\\n\\n                requires, extras = self._parse_requires_file_data(data)\\n                self._provides_file_data = extras\\n                self._requires_file_data = requires\\n                break\\n\\n        return requires, extras\\n\\n    @memoizedproperty\\n    def manifest_full_path(self):\\n        manifest_full_path = None\\n        if self._metadata_dir_full_path:\\n            for fname in self.MANIFEST_FILES:\\n                manifest_full_path = join(self._metadata_dir_full_path, fname)\\n                if isfile(manifest_full_path):\\n                    break\\n        return manifest_full_path\\n\\n    def get_paths(self):\\n        \\\"\\\"\\\"\\n        Read the list of installed paths from record or source file.\\n\\n        Example\\n        -------\\n        [(u'skdata/__init__.py', u'sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU', 0),\\n         (u'skdata/diabetes.py', None, None),\\n         ...\\n        ]\\n        \\\"\\\"\\\"\\n        manifest_full_path = self.manifest_full_path\\n        if manifest_full_path:\\n            python_version = self.python_version\\n            sp_dir = get_python_site_packages_short_path(python_version) + \\\"/\\\"\\n            prepend_metadata_dirname = (\\n                basename(manifest_full_path) == \\\"installed-files.txt\\\"\\n            )\\n            if prepend_metadata_dirname:\\n                path_prepender = basename(dirname(manifest_full_path)) + \\\"/\\\"\\n            else:\\n                path_prepender = \\\"\\\"\\n\\n            def process_csv_row(reader):\\n                seen = []\\n                records = []\\n                for row in reader:\\n                    cleaned_path = posix_normpath(f\\\"{sp_dir}{path_prepender}{row[0]}\\\")\\n                    if len(row) == 3:\\n                        checksum, size = row[1:]\\n                        if checksum:\\n                            assert checksum.startswith(\\\"sha256=\\\"), (\\n                                self._metadata_dir_full_path,\\n                                cleaned_path,\\n                                checksum,\\n                            )\\n                            checksum = checksum[7:]\\n                        else:\\n                            checksum = None\\n                        size = int(size) if size else None\\n                    else:\\n                        checksum = size = None\\n                    if cleaned_path not in seen and row[0]:\\n                        seen.append(cleaned_path)\\n                        records.append((cleaned_path, checksum, size))\\n                return tuple(records)\\n\\n            csv_delimiter = \\\",\\\"\\n            with open(manifest_full_path) as csvfile:\\n                record_reader = csv_reader(csvfile, delimiter=csv_delimiter)\\n                # format of each record is (path, checksum, size)\\n                records = process_csv_row(record_reader)\\n            files_set = {record[0] for record in records}\\n\\n            _pyc_path, _py_file_re = pyc_path, PY_FILE_RE\\n            py_ver_mm = get_major_minor_version(python_version, with_dot=False)\\n            missing_pyc_files = (\\n                ff\\n                for ff in (\\n                    _pyc_path(f, py_ver_mm) for f in files_set if _py_file_re.match(f)\\n                )\\n                if ff not in files_set\\n            )\\n            records = sorted(\\n                (*records, *((pf, None, None) for pf in missing_pyc_files))\\n            )\\n            return records\\n\\n        return []\\n\\n    def get_dist_requirements(self):\\n        # FIXME: On some packages, requirements are not added to metadata,\\n        # but on a separate requires.txt, see: python setup.py develop for\\n        # anaconda-client. This is setuptools behavior.\\n        # TODO: what is the dependency_links.txt on the same example?\\n        data = self._metadata.get_dist_requirements()\\n        if self._requires_file_data:\\n            data = self._requires_file_data\\n        elif not data:\\n            self._load_requires_provides_file()\\n            data = self._requires_file_data\\n        return data\\n\\n    def get_python_requirements(self):\\n        return self._metadata.get_python_requirements()\\n\\n    def get_external_requirements(self):\\n        return self._metadata.get_external_requirements()\\n\\n    def get_extra_provides(self):\\n        # FIXME: On some packages, requirements are not added to metadata,\\n        # but on a separate requires.txt, see: python setup.py develop for\\n        # anaconda-client. This is setuptools behavior.\\n        data = self._metadata.get_extra_provides()\\n        if self._provides_file_data:\\n            data = self._provides_file_data\\n        elif not data:\\n            self._load_requires_provides_file()\\n            data = self._provides_file_data\\n\\n        return data\\n\\n    def get_conda_dependencies(self):\\n        \\\"\\\"\\\"\\n        Process metadata fields providing dependency information.\\n\\n        This includes normalizing fields, and evaluating environment markers.\\n        \\\"\\\"\\\"\\n        python_spec = \\\"python {}.*\\\".format(\\\".\\\".join(self.python_version.split(\\\".\\\")[:2]))\\n\\n        def pyspec_to_norm_req(pyspec):\\n            conda_name = pypi_name_to_conda_name(norm_package_name(pyspec.name))\\n            return (\\n                f\\\"{conda_name} {pyspec.constraints}\\\"\\n                if pyspec.constraints\\n                else conda_name\\n            )\\n\\n        reqs = self.get_dist_requirements()\\n        pyspecs = tuple(parse_specification(req) for req in reqs)\\n        marker_groups = groupby(lambda ps: ps.marker.split(\\\"==\\\", 1)[0].strip(), pyspecs)\\n        depends = {pyspec_to_norm_req(pyspec) for pyspec in marker_groups.pop(\\\"\\\", ())}\\n        extras = marker_groups.pop(\\\"extra\\\", ())\\n        execution_context = {\\n            \\\"python_version\\\": self.python_version,\\n        }\\n        depends.update(\\n            pyspec_to_norm_req(pyspec)\\n            for pyspec in chain.from_iterable(marker_groups.values())\\n            if interpret(pyspec.marker, execution_context)\\n        )\\n        constrains = {\\n            pyspec_to_norm_req(pyspec) for pyspec in extras if pyspec.constraints\\n        }\\n        depends.add(python_spec)\\n\\n        return sorted(depends), sorted(constrains)\\n\\n    def get_optional_dependencies(self):\\n        raise NotImplementedError\\n\\n    def get_entry_points(self):\\n        # TODO: need to add entry points, \\\"exports,\\\" and other files that might\\n        # not be in RECORD\\n        for fname in self.ENTRY_POINTS_FILES:\\n            fpath = join(self._metadata_dir_full_path, fname)\\n            if isfile(fpath):\\n                with open(fpath) as fh:\\n                    data = fh.read()\\n        return self._parse_entries_file_data(data)\\n\\n    @property\\n    def name(self):\\n        return self._metadata.name\\n\\n    @property\\n    def norm_name(self):\\n        return norm_package_name(self.name)\\n\\n    @property\\n    def conda_name(self):\\n        return pypi_name_to_conda_name(self.norm_name)\\n\\n    @property\\n    def version(self):\\n        return self._metadata.version\\n\\n\\nclass PythonInstalledDistribution(PythonDistribution):\\n    \\\"\\\"\\\"\\n    Python distribution installed via distutils.\\n\\n    Notes\\n    -----\\n      - https://www.python.org/dev/peps/pep-0376/\\n    \\\"\\\"\\\"\\n\\n    MANIFEST_FILES = (\\\"RECORD\\\",)\\n    REQUIRES_FILES = ()\\n    MANDATORY_FILES = (\\\"METADATA\\\",)\\n    # FIXME: Do this check? Disabled for tests where only Metadata file is stored\\n    # MANDATORY_FILES = ('METADATA', 'RECORD', 'INSTALLER')\\n    ENTRY_POINTS_FILES = ()\\n\\n    is_manageable = True\\n\\n    def __init__(self, prefix_path, anchor_file, python_version):\\n        anchor_full_path = join(prefix_path, win_path_ok(dirname(anchor_file)))\\n        super().__init__(anchor_full_path, python_version)\\n        self.sp_reference = basename(dirname(anchor_file))\\n\\n\\nclass PythonEggInfoDistribution(PythonDistribution):\\n    \\\"\\\"\\\"\\n    Python distribution installed via setuptools.\\n\\n    Notes\\n    -----\\n      - http://peak.telecommunity.com/DevCenter/EggFormats\\n    \\\"\\\"\\\"\\n\\n    MANIFEST_FILES = (\\\"installed-files.txt\\\", \\\"SOURCES\\\", \\\"SOURCES.txt\\\")\\n    REQUIRES_FILES = (\\\"requires.txt\\\", \\\"depends.txt\\\")\\n    MANDATORY_FILES = ()\\n    ENTRY_POINTS_FILES = (\\\"entry_points.txt\\\",)\\n\\n    def __init__(self, anchor_full_path, python_version, sp_reference):\\n        super().__init__(anchor_full_path, python_version)\\n        self.sp_reference = sp_reference\\n\\n    @property\\n    def is_manageable(self):\\n        return (\\n            self.manifest_full_path\\n            and basename(self.manifest_full_path) == \\\"installed-files.txt\\\"\\n        )\\n\\n\\nclass PythonEggLinkDistribution(PythonEggInfoDistribution):\\n    is_manageable = False\\n\\n    def __init__(self, prefix_path, anchor_file, python_version):\\n        anchor_full_path = get_dist_file_from_egg_link(anchor_file, prefix_path)\\n        sp_reference = None  # This can be None in case the egg-info is no longer there\\n        super().__init__(anchor_full_path, python_version, sp_reference)\\n\\n\\n# Python distribution/eggs metadata\\n# -----------------------------------------------------------------------------\\n\\n\\nclass PythonDistributionMetadata:\\n    \\\"\\\"\\\"\\n    Object representing the metada of a Python Distribution given by anchor\\n    file (or directory) path.\\n\\n    This metadata is extracted from a single file. Python distributions might\\n    create additional files that complement this metadata information, but\\n    that is handled at the python distribution level.\\n\\n    Notes\\n    -----\\n      - https://packaging.python.org/specifications/core-metadata/\\n      - Metadata 2.1: https://www.python.org/dev/peps/pep-0566/\\n      - Metadata 2.0: https://www.python.org/dev/peps/pep-0426/ (Withdrawn)\\n      - Metadata 1.2: https://www.python.org/dev/peps/pep-0345/\\n      - Metadata 1.1: https://www.python.org/dev/peps/pep-0314/\\n      - Metadata 1.0: https://www.python.org/dev/peps/pep-0241/\\n    \\\"\\\"\\\"\\n\\n    FILE_NAMES = (\\\"METADATA\\\", \\\"PKG-INFO\\\")\\n\\n    # Python Packages Metadata 2.1\\n    # -----------------------------------------------------------------------------\\n    SINGLE_USE_KEYS = frozendict(\\n        (\\n            (\\\"Metadata-Version\\\", \\\"metadata_version\\\"),\\n            (\\\"Name\\\", \\\"name\\\"),\\n            (\\\"Version\\\", \\\"version\\\"),\\n            # ('Summary', 'summary'),\\n            # ('Description', 'description'),\\n            # ('Description-Content-Type', 'description_content_type'),\\n            # ('Keywords', 'keywords'),\\n            # ('Home-page', 'home_page'),\\n            # ('Download-URL', 'download_url'),\\n            # ('Author', 'author'),\\n            # ('Author-email', 'author_email'),\\n            # ('Maintainer', 'maintainer'),\\n            # ('Maintainer-email', 'maintainer_email'),\\n            (\\\"License\\\", \\\"license\\\"),\\n            # # Deprecated\\n            # ('Obsoleted-By', 'obsoleted_by'),  # Note: See 2.0\\n            # ('Private-Version', 'private_version'),  # Note: See 2.0\\n        )\\n    )\\n    MULTIPLE_USE_KEYS = frozendict(\\n        (\\n            (\\\"Platform\\\", \\\"platform\\\"),\\n            (\\\"Supported-Platform\\\", \\\"supported_platform\\\"),\\n            # ('Classifier', 'classifier'),\\n            (\\\"Requires-Dist\\\", \\\"requires_dist\\\"),\\n            (\\\"Requires-External\\\", \\\"requires_external\\\"),\\n            (\\\"Requires-Python\\\", \\\"requires_python\\\"),\\n            # ('Project-URL', 'project_url'),\\n            (\\\"Provides-Extra\\\", \\\"provides_extra\\\"),\\n            # ('Provides-Dist', 'provides_dist'),\\n            # ('Obsoletes-Dist', 'obsoletes_dist'),\\n            # # Deprecated\\n            # ('Extension', 'extension'),  # Note: See 2.0\\n            # ('Obsoletes', 'obsoletes'),\\n            # ('Provides', 'provides'),\\n            (\\\"Requires\\\", \\\"requires\\\"),\\n            # ('Setup-Requires-Dist', 'setup_requires_dist'),  # Note: See 2.0\\n        )\\n    )\\n\\n    def __init__(self, path):\\n        metadata_path = self._process_path(path, self.FILE_NAMES)\\n        self._path = path\\n        self._data = self._read_metadata(metadata_path)\\n\\n    @staticmethod\\n    def _process_path(path, metadata_filenames):\\n        \\\"\\\"\\\"Find metadata file inside dist-info folder, or check direct file.\\\"\\\"\\\"\\n        metadata_path = None\\n        if path:\\n            if isdir(path):\\n                for fname in metadata_filenames:\\n                    fpath = join(path, fname)\\n                    if isfile(fpath):\\n                        metadata_path = fpath\\n                        break\\n            elif isfile(path):\\n                # '<pkg>.egg-info' file contains metadata directly\\n                filenames = [\\\".egg-info\\\"]\\n                if metadata_filenames:\\n                    filenames.extend(metadata_filenames)\\n                assert any(path.endswith(filename) for filename in filenames)\\n                metadata_path = path\\n            else:\\n                # `path` does not exist\\n                warnings.warn(\\\"Metadata path not found\\\", MetadataWarning)\\n        else:\\n            warnings.warn(\\\"Metadata path not found\\\", MetadataWarning)\\n\\n        return metadata_path\\n\\n    @classmethod\\n    def _message_to_dict(cls, message):\\n        \\\"\\\"\\\"\\n        Convert the RFC-822 headers data into a dictionary.\\n\\n        `message` is an email.parser.Message instance.\\n\\n        The canonical method to transform metadata fields into such a data\\n        structure is as follows:\\n          - The original key-value format should be read with\\n            email.parser.HeaderParser\\n          - All transformed keys should be reduced to lower case. Hyphens\\n            should be replaced with underscores, but otherwise should retain\\n            all other characters\\n          - The transformed value for any field marked with \\\"(Multiple-use\\\")\\n            should be a single list containing all the original values for the\\n            given key\\n          - The Keywords field should be converted to a list by splitting the\\n            original value on whitespace characters\\n          - The message body, if present, should be set to the value of the\\n            description key.\\n          - The result should be stored as a string-keyed dictionary.\\n        \\\"\\\"\\\"\\n        new_data = {}\\n\\n        if message:\\n            for key, value in message.items():\\n                if key in cls.MULTIPLE_USE_KEYS:\\n                    new_key = cls.MULTIPLE_USE_KEYS[key]\\n                    if new_key not in new_data:\\n                        new_data[new_key] = [value]\\n                    else:\\n                        new_data[new_key].append(value)\\n\\n                elif key in cls.SINGLE_USE_KEYS:\\n                    new_key = cls.SINGLE_USE_KEYS[key]\\n                    new_data[new_key] = value\\n\\n            # TODO: Handle license later on for convenience\\n\\n        return new_data\\n\\n    @classmethod\\n    def _read_metadata(cls, fpath):\\n        \\\"\\\"\\\"Read the original format which is stored as RFC-822 headers.\\\"\\\"\\\"\\n        data = {}\\n        if fpath and isfile(fpath):\\n            parser = HeaderParser()\\n\\n            # FIXME: Is this a correct assumption for the encoding?\\n            # This was needed due to some errors on windows\\n            with open(fpath) as fp:\\n                data = parser.parse(fp)\\n\\n        return cls._message_to_dict(data)\\n\\n    def _get_multiple_data(self, keys):\\n        \\\"\\\"\\\"\\n        Helper method to get multiple data values by keys.\\n\\n        Keys is an iterable including the preferred key in order, to include\\n        values of key that might have been replaced (deprecated), for example\\n        keys can be ['requires_dist', 'requires'], where the key 'requires' is\\n        deprecated and replaced by 'requires_dist'.\\n        \\\"\\\"\\\"\\n        data = []\\n        if self._data:\\n            for key in keys:\\n                raw_data = self._data.get(key, [])\\n                for req in raw_data:\\n                    data.append(req.strip())\\n\\n                if data:\\n                    break\\n\\n        return frozenset(data)\\n\\n    def get_dist_requirements(self):\\n        \\\"\\\"\\\"\\n        Changed in version 2.1: The field format specification was relaxed to\\n        accept the syntax used by popular publishing tools.\\n\\n        Each entry contains a string naming some other distutils project\\n        required by this distribution.\\n\\n        The format of a requirement string contains from one to four parts:\\n          - A project name, in the same format as the Name: field. The only\\n            mandatory part.\\n          - A comma-separated list of ‘extra’ names. These are defined by the\\n            required project, referring to specific features which may need\\n            extra dependencies.\\n          - A version specifier. Tools parsing the format should accept\\n            optional parentheses around this, but tools generating it should\\n            not use parentheses.\\n          - An environment marker after a semicolon. This means that the\\n            requirement is only needed in the specified conditions.\\n\\n        This field may be followed by an environment marker after a semicolon.\\n\\n        Example\\n        -------\\n        frozenset(['pkginfo', 'PasteDeploy', 'zope.interface (>3.5.0)',\\n                   'pywin32 >1.0; sys_platform == \\\"win32\\\"'])\\n\\n        Return 'Requires' if 'Requires-Dist' is empty.\\n        \\\"\\\"\\\"\\n        return self._get_multiple_data([\\\"requires_dist\\\", \\\"requires\\\"])\\n\\n    def get_python_requirements(self):\\n        \\\"\\\"\\\"\\n        New in version 1.2.\\n\\n        This field specifies the Python version(s) that the distribution is\\n        guaranteed to be compatible with. Installation tools may look at this\\n        when picking which version of a project to install.\\n\\n        The value must be in the format specified in Version specifiers.\\n\\n        This field may be followed by an environment marker after a semicolon.\\n\\n        Example\\n        -------\\n        frozenset(['>=3', '>2.6,!=3.0.*,!=3.1.*', '~=2.6',\\n                   '>=3; sys_platform == \\\"win32\\\"'])\\n        \\\"\\\"\\\"\\n        return self._get_multiple_data([\\\"requires_python\\\"])\\n\\n    def get_external_requirements(self):\\n        \\\"\\\"\\\"\\n        Changed in version 2.1: The field format specification was relaxed to\\n        accept the syntax used by popular publishing tools.\\n\\n        Each entry contains a string describing some dependency in the system\\n        that the distribution is to be used. This field is intended to serve\\n        as a hint to downstream project maintainers, and has no semantics\\n        which are meaningful to the distutils distribution.\\n\\n        The format of a requirement string is a name of an external dependency,\\n        optionally followed by a version declaration within parentheses.\\n\\n        This field may be followed by an environment marker after a semicolon.\\n\\n        Because they refer to non-Python software releases, version numbers for\\n        this field are not required to conform to the format specified in PEP\\n        440: they should correspond to the version scheme used by the external\\n        dependency.\\n\\n        Notice that there’s is no particular rule on the strings to be used!\\n\\n        Example\\n        -------\\n        frozenset(['C', 'libpng (>=1.5)', 'make; sys_platform != \\\"win32\\\"'])\\n        \\\"\\\"\\\"\\n        return self._get_multiple_data([\\\"requires_external\\\"])\\n\\n    def get_extra_provides(self):\\n        \\\"\\\"\\\"\\n        New in version 2.1.\\n\\n        A string containing the name of an optional feature. Must be a valid\\n        Python identifier. May be used to make a dependency conditional on\\n        hether the optional feature has been requested.\\n\\n        Example\\n        -------\\n        frozenset(['pdf', 'doc', 'test'])\\n        \\\"\\\"\\\"\\n        return self._get_multiple_data([\\\"provides_extra\\\"])\\n\\n    def get_dist_provides(self):\\n        \\\"\\\"\\\"\\n        New in version 1.2.\\n\\n        Changed in version 2.1: The field format specification was relaxed to\\n        accept the syntax used by popular publishing tools.\\n\\n        Each entry contains a string naming a Distutils project which is\\n        contained within this distribution. This field must include the project\\n        identified in the Name field, followed by the version : Name (Version).\\n\\n        A distribution may provide additional names, e.g. to indicate that\\n        multiple projects have been bundled together. For instance, source\\n        distributions of the ZODB project have historically included the\\n        transaction project, which is now available as a separate distribution.\\n        Installing such a source distribution satisfies requirements for both\\n        ZODB and transaction.\\n\\n        A distribution may also provide a “virtual” project name, which does\\n        not correspond to any separately-distributed project: such a name might\\n        be used to indicate an abstract capability which could be supplied by\\n        one of multiple projects. E.g., multiple projects might supply RDBMS\\n        bindings for use by a given ORM: each project might declare that it\\n        provides ORM-bindings, allowing other projects to depend only on having\\n        at most one of them installed.\\n\\n        A version declaration may be supplied and must follow the rules\\n        described in Version specifiers. The distribution’s version number\\n        will be implied if none is specified.\\n\\n        This field may be followed by an environment marker after a semicolon.\\n\\n        Return `Provides` in case `Provides-Dist` is empty.\\n        \\\"\\\"\\\"\\n        return self._get_multiple_data([\\\"provides_dist\\\", \\\"provides\\\"])\\n\\n    def get_dist_obsolete(self):\\n        \\\"\\\"\\\"\\n        New in version 1.2.\\n\\n        Changed in version 2.1: The field format specification was relaxed to\\n        accept the syntax used by popular publishing tools.\\n\\n        Each entry contains a string describing a distutils project’s\\n        distribution which this distribution renders obsolete, meaning that\\n        the two projects should not be installed at the same time.\\n\\n        Version declarations can be supplied. Version numbers must be in the\\n        format specified in Version specifiers [1].\\n\\n        The most common use of this field will be in case a project name\\n        changes, e.g. Gorgon 2.3 gets subsumed into Torqued Python 1.0. When\\n        you install Torqued Python, the Gorgon distribution should be removed.\\n\\n        This field may be followed by an environment marker after a semicolon.\\n\\n        Return `Obsoletes` in case `Obsoletes-Dist` is empty.\\n\\n        Example\\n        -------\\n        frozenset(['Gorgon', \\\"OtherProject (<3.0) ; python_version == '2.7'\\\"])\\n\\n        Notes\\n        -----\\n        - [1] https://packaging.python.org/specifications/version-specifiers/\\n        \\\"\\\"\\\"\\n        return self._get_multiple_data([\\\"obsoletes_dist\\\", \\\"obsoletes\\\"])\\n\\n    def get_classifiers(self):\\n        \\\"\\\"\\\"\\n        Classifiers are described in PEP 301, and the Python Package Index\\n        publishes a dynamic list of currently defined classifiers.\\n\\n        This field may be followed by an environment marker after a semicolon.\\n\\n        Example\\n        -------\\n        frozenset(['Development Status :: 4 - Beta',\\n                   \\\"Environment :: Console (Text Based) ; os_name == \\\"posix\\\"])\\n        \\\"\\\"\\\"\\n        return self._get_multiple_data([\\\"classifier\\\"])\\n\\n    @property\\n    def name(self):\\n        return self._data.get(\\\"name\\\")  # TODO: Check for existence?\\n\\n    @property\\n    def version(self):\\n        return self._data.get(\\\"version\\\")  # TODO: Check for existence?\\n\\n\\n# Helper functions\\n# -----------------------------------------------------------------------------\\ndef norm_package_name(name):\\n    return name.replace(\\\".\\\", \\\"-\\\").replace(\\\"_\\\", \\\"-\\\").lower() if name else \\\"\\\"\\n\\n\\ndef pypi_name_to_conda_name(pypi_name):\\n    return PYPI_TO_CONDA.get(pypi_name, pypi_name) if pypi_name else \\\"\\\"\\n\\n\\ndef norm_package_version(version):\\n    \\\"\\\"\\\"Normalize a version by removing extra spaces and parentheses.\\\"\\\"\\\"\\n    if version:\\n        version = \\\",\\\".join(v.strip() for v in version.split(\\\",\\\")).strip()\\n\\n        if version.startswith(\\\"(\\\") and version.endswith(\\\")\\\"):\\n            version = version[1:-1]\\n\\n        version = \\\"\\\".join(v for v in version if v.strip())\\n    else:\\n        version = \\\"\\\"\\n\\n    return version\\n\\n\\ndef split_spec(spec, sep):\\n    \\\"\\\"\\\"Split a spec by separator and return stripped start and end parts.\\\"\\\"\\\"\\n    parts = spec.rsplit(sep, 1)\\n    spec_start = parts[0].strip()\\n    spec_end = \\\"\\\"\\n    if len(parts) == 2:\\n        spec_end = parts[-1].strip()\\n    return spec_start, spec_end\\n\\n\\ndef parse_specification(spec):\\n    \\\"\\\"\\\"\\n    Parse a requirement from a python distribution metadata and return a\\n    namedtuple with name, extras, constraints, marker and url components.\\n\\n    This method does not enforce strict specifications but extracts the\\n    information which is assumed to be *correct*. As such no errors are raised.\\n\\n    Example\\n    -------\\n    PySpec(name='requests', extras=['security'], constraints='>=3.3.0',\\n           marker='foo >= 2.7 or bar == 1', url=''])\\n    \\\"\\\"\\\"\\n    name, extras, const = spec, [], \\\"\\\"\\n\\n    # Remove excess whitespace\\n    spec = \\\" \\\".join(p for p in spec.split(\\\" \\\") if p).strip()\\n\\n    # Extract marker (Assumes that there can only be one ';' inside the spec)\\n    spec, marker = split_spec(spec, \\\";\\\")\\n\\n    # Extract url (Assumes that there can only be one '@' inside the spec)\\n    spec, url = split_spec(spec, \\\"@\\\")\\n\\n    # Find name, extras and constraints\\n    r = PARTIAL_PYPI_SPEC_PATTERN.match(spec)\\n    if r:\\n        # Normalize name\\n        name = r.group(\\\"name\\\")\\n        name = norm_package_name(name)  # TODO: Do we want this or not?\\n\\n        # Clean extras\\n        extras = r.group(\\\"extras\\\")\\n        extras = [e.strip() for e in extras.split(\\\",\\\") if e] if extras else []\\n\\n        # Clean constraints\\n        const = r.group(\\\"constraints\\\")\\n        const = \\\"\\\".join(c for c in const.split(\\\" \\\") if c).strip()\\n        if const.startswith(\\\"(\\\") and const.endswith(\\\")\\\"):\\n            # Remove parens\\n            const = const[1:-1]\\n        const = const.replace(\\\"-\\\", \\\".\\\")\\n\\n    return PySpec(name=name, extras=extras, constraints=const, marker=marker, url=url)\\n\\n\\ndef get_site_packages_anchor_files(site_packages_path, site_packages_dir):\\n    \\\"\\\"\\\"Get all the anchor files for the site packages directory.\\\"\\\"\\\"\\n    site_packages_anchor_files = set()\\n    for entry in scandir(site_packages_path):\\n        fname = entry.name\\n        anchor_file = None\\n        if fname.endswith(\\\".dist-info\\\"):\\n            anchor_file = \\\"{}/{}/{}\\\".format(site_packages_dir, fname, \\\"RECORD\\\")\\n        elif fname.endswith(\\\".egg-info\\\"):\\n            if isfile(join(site_packages_path, fname)):\\n                anchor_file = f\\\"{site_packages_dir}/{fname}\\\"\\n            else:\\n                anchor_file = \\\"{}/{}/{}\\\".format(site_packages_dir, fname, \\\"PKG-INFO\\\")\\n        elif fname.endswith(\\\".egg\\\"):\\n            if isdir(join(site_packages_path, fname)):\\n                anchor_file = \\\"{}/{}/{}/{}\\\".format(\\n                    site_packages_dir, fname, \\\"EGG-INFO\\\", \\\"PKG-INFO\\\"\\n                )\\n            # FIXME: If it is a .egg file, we need to unzip the content to be\\n            # able. Do this once and leave the directory, and remove the egg\\n            # (which is a zip file in disguise?)\\n        elif fname.endswith(\\\".egg-link\\\"):\\n            anchor_file = f\\\"{site_packages_dir}/{fname}\\\"\\n        elif fname.endswith(\\\".pth\\\"):\\n            continue\\n        else:\\n            continue\\n\\n        if anchor_file:\\n            site_packages_anchor_files.add(anchor_file)\\n\\n    return site_packages_anchor_files\\n\\n\\ndef get_dist_file_from_egg_link(egg_link_file, prefix_path):\\n    \\\"\\\"\\\"Return the egg info file path following an egg link.\\\"\\\"\\\"\\n    egg_info_full_path = None\\n\\n    egg_link_path = join(prefix_path, win_path_ok(egg_link_file))\\n    try:\\n        with open(egg_link_path) as fh:\\n            # See: https://setuptools.readthedocs.io/en/latest/formats.html#egg-links\\n            # \\\"...Each egg-link file should contain a single file or directory name\\n            # with no newlines...\\\"\\n            egg_link_contents = fh.readlines()[0].strip()\\n    except UnicodeDecodeError:\\n        from locale import getpreferredencoding\\n\\n        with open(egg_link_path, encoding=getpreferredencoding()) as fh:\\n            egg_link_contents = fh.readlines()[0].strip()\\n\\n    if lexists(egg_link_contents):\\n        egg_info_fnames = tuple(\\n            name\\n            for name in (entry.name for entry in scandir(egg_link_contents))\\n            if name[-9:] == \\\".egg-info\\\"\\n        )\\n    else:\\n        egg_info_fnames = ()\\n\\n    if egg_info_fnames:\\n        if len(egg_info_fnames) != 1:\\n            raise CondaError(\\n                f\\\"Expected exactly one `egg-info` directory in '{egg_link_contents}', via egg-link '{egg_link_file}'.\\\"\\n                f\\\" Instead found: {egg_info_fnames}.  These are often left over from \\\"\\n                \\\"legacy operations that did not clean up correctly.  Please \\\"\\n                \\\"remove all but one of these.\\\"\\n            )\\n\\n        egg_info_full_path = join(egg_link_contents, egg_info_fnames[0])\\n\\n        if isdir(egg_info_full_path):\\n            egg_info_full_path = join(egg_info_full_path, \\\"PKG-INFO\\\")\\n\\n    if egg_info_full_path is None:\\n        raise OSError(ENOENT, strerror(ENOENT), egg_link_contents)\\n\\n    return egg_info_full_path\\n\\n\\n# See: https://bitbucket.org/pypa/distlib/src/34629e41cdff5c29429c7a4d1569ef5508b56929/distlib/util.py?at=default&fileviewer=file-view-default  # NOQA\\n# ------------------------------------------------------------------------------------------------\\ndef parse_marker(marker_string):\\n    \\\"\\\"\\\"\\n    Parse marker string and return a dictionary containing a marker expression.\\n\\n    The dictionary will contain keys \\\"op\\\", \\\"lhs\\\" and \\\"rhs\\\" for non-terminals in\\n    the expression grammar, or strings. A string contained in quotes is to be\\n    interpreted as a literal string, and a string not contained in quotes is a\\n    variable (such as os_name).\\n    \\\"\\\"\\\"\\n\\n    def marker_var(remaining):\\n        # either identifier, or literal string\\n        m = IDENTIFIER.match(remaining)\\n        if m:\\n            result = m.groups()[0]\\n            remaining = remaining[m.end() :]\\n        elif not remaining:\\n            raise SyntaxError(\\\"unexpected end of input\\\")\\n        else:\\n            q = remaining[0]\\n            if q not in \\\"'\\\\\\\"\\\":\\n                raise SyntaxError(f\\\"invalid expression: {remaining}\\\")\\n            oq = \\\"'\\\\\\\"\\\".replace(q, \\\"\\\")\\n            remaining = remaining[1:]\\n            parts = [q]\\n            while remaining:\\n                # either a string chunk, or oq, or q to terminate\\n                if remaining[0] == q:\\n                    break\\n                elif remaining[0] == oq:\\n                    parts.append(oq)\\n                    remaining = remaining[1:]\\n                else:\\n                    m = STRING_CHUNK.match(remaining)\\n                    if not m:\\n                        raise SyntaxError(f\\\"error in string literal: {remaining}\\\")\\n                    parts.append(m.groups()[0])\\n                    remaining = remaining[m.end() :]\\n            else:\\n                s = \\\"\\\".join(parts)\\n                raise SyntaxError(f\\\"unterminated string: {s}\\\")\\n            parts.append(q)\\n            result = \\\"\\\".join(parts)\\n            remaining = remaining[1:].lstrip()  # skip past closing quote\\n        return result, remaining\\n\\n    def marker_expr(remaining):\\n        if remaining and remaining[0] == \\\"(\\\":\\n            result, remaining = marker(remaining[1:].lstrip())\\n            if remaining[0] != \\\")\\\":\\n                raise SyntaxError(f\\\"unterminated parenthesis: {remaining}\\\")\\n            remaining = remaining[1:].lstrip()\\n        else:\\n            lhs, remaining = marker_var(remaining)\\n            while remaining:\\n                m = MARKER_OP.match(remaining)\\n                if not m:\\n                    break\\n                op = m.groups()[0]\\n                remaining = remaining[m.end() :]\\n                rhs, remaining = marker_var(remaining)\\n                lhs = {\\\"op\\\": op, \\\"lhs\\\": lhs, \\\"rhs\\\": rhs}\\n            result = lhs\\n        return result, remaining\\n\\n    def marker_and(remaining):\\n        lhs, remaining = marker_expr(remaining)\\n        while remaining:\\n            m = AND.match(remaining)\\n            if not m:\\n                break\\n            remaining = remaining[m.end() :]\\n            rhs, remaining = marker_expr(remaining)\\n            lhs = {\\\"op\\\": \\\"and\\\", \\\"lhs\\\": lhs, \\\"rhs\\\": rhs}\\n        return lhs, remaining\\n\\n    def marker(remaining):\\n        lhs, remaining = marker_and(remaining)\\n        while remaining:\\n            m = OR.match(remaining)\\n            if not m:\\n                break\\n            remaining = remaining[m.end() :]\\n            rhs, remaining = marker_and(remaining)\\n            lhs = {\\\"op\\\": \\\"or\\\", \\\"lhs\\\": lhs, \\\"rhs\\\": rhs}\\n        return lhs, remaining\\n\\n    return marker(marker_string)\\n\\n\\n# See:\\n#   https://bitbucket.org/pypa/distlib/src/34629e41cdff5c29429c7a4d1569ef5508b56929/distlib/util.py?at=default&fileviewer=file-view-default  # NOQA\\n#   https://bitbucket.org/pypa/distlib/src/34629e41cdff5c29429c7a4d1569ef5508b56929/distlib/markers.py?at=default&fileviewer=file-view-default  # NOQA\\n# ------------------------------------------------------------------------------------------------\\n#\\n# Requirement parsing code as per PEP 508\\n#\\nIDENTIFIER = re.compile(r\\\"^([\\\\w\\\\.-]+)\\\\s*\\\")\\nVERSION_IDENTIFIER = re.compile(r\\\"^([\\\\w\\\\.*+-]+)\\\\s*\\\")\\nCOMPARE_OP = re.compile(r\\\"^(<=?|>=?|={2,3}|[~!]=)\\\\s*\\\")\\nMARKER_OP = re.compile(r\\\"^((<=?)|(>=?)|={2,3}|[~!]=|in|not\\\\s+in)\\\\s*\\\")\\nOR = re.compile(r\\\"^or\\\\b\\\\s*\\\")\\nAND = re.compile(r\\\"^and\\\\b\\\\s*\\\")\\nNON_SPACE = re.compile(r\\\"(\\\\S+)\\\\s*\\\")\\nSTRING_CHUNK = re.compile(r\\\"([\\\\s\\\\w\\\\.{}()*+#:;,/?!~`@$%^&=|<>\\\\[\\\\]-]+)\\\")\\n\\n\\ndef _is_literal(o):\\n    if not isinstance(o, str) or not o:\\n        return False\\n    return o[0] in \\\"'\\\\\\\"\\\"\\n\\n\\nclass Evaluator:\\n    \\\"\\\"\\\"This class is used to evaluate marker expressions.\\\"\\\"\\\"\\n\\n    operations = {\\n        \\\"==\\\": lambda x, y: x == y,\\n        \\\"===\\\": lambda x, y: x == y,\\n        \\\"~=\\\": lambda x, y: x == y or x > y,\\n        \\\"!=\\\": lambda x, y: x != y,\\n        \\\"<\\\": lambda x, y: x < y,\\n        \\\"<=\\\": lambda x, y: x == y or x < y,\\n        \\\">\\\": lambda x, y: x > y,\\n        \\\">=\\\": lambda x, y: x == y or x > y,\\n        \\\"and\\\": lambda x, y: x and y,\\n        \\\"or\\\": lambda x, y: x or y,\\n        \\\"in\\\": lambda x, y: x in y,\\n        \\\"not in\\\": lambda x, y: x not in y,\\n    }\\n\\n    def evaluate(self, expr, context):\\n        \\\"\\\"\\\"\\n        Evaluate a marker expression returned by the :func:`parse_requirement`\\n        function in the specified context.\\n        \\\"\\\"\\\"\\n        if isinstance(expr, str):\\n            if expr[0] in \\\"'\\\\\\\"\\\":\\n                result = expr[1:-1]\\n            else:\\n                if expr not in context:\\n                    raise SyntaxError(f\\\"unknown variable: {expr}\\\")\\n                result = context[expr]\\n        else:\\n            assert isinstance(expr, dict)\\n            op = expr[\\\"op\\\"]\\n            if op not in self.operations:\\n                raise NotImplementedError(f\\\"op not implemented: {op}\\\")\\n            elhs = expr[\\\"lhs\\\"]\\n            erhs = expr[\\\"rhs\\\"]\\n            if _is_literal(expr[\\\"lhs\\\"]) and _is_literal(expr[\\\"rhs\\\"]):\\n                raise SyntaxError(f\\\"invalid comparison: {elhs} {op} {erhs}\\\")\\n\\n            lhs = self.evaluate(elhs, context)\\n            rhs = self.evaluate(erhs, context)\\n            result = self.operations[op](lhs, rhs)\\n        return result\\n\\n\\n# def update_marker_context(python_version):\\n#     \\\"\\\"\\\"Update default marker context to include environment python version.\\\"\\\"\\\"\\n#     updated_context = DEFAULT_MARKER_CONTEXT.copy()\\n#     context = {\\n#         'python_full_version': python_version,\\n#         'python_version': '.'.join(python_version.split('.')[:2]),\\n#         'extra': '',\\n#     }\\n#     updated_context.update(context)\\n#     return updated_context\\n\\n\\ndef get_default_marker_context():\\n    \\\"\\\"\\\"Return the default context dictionary to use when parsing markers.\\\"\\\"\\\"\\n\\n    def format_full_version(info):\\n        version = f\\\"{info.major}.{info.minor}.{info.micro}\\\"\\n        kind = info.releaselevel\\n        if kind != \\\"final\\\":\\n            version += kind[0] + str(info.serial)\\n        return version\\n\\n    if hasattr(sys, \\\"implementation\\\"):\\n        implementation_version = format_full_version(sys.implementation.version)\\n        implementation_name = sys.implementation.name\\n    else:\\n        implementation_version = \\\"0\\\"\\n        implementation_name = \\\"\\\"\\n\\n    # TODO: we can't use this\\n    result = {\\n        # See: https://www.python.org/dev/peps/pep-0508/#environment-markers\\n        \\\"implementation_name\\\": implementation_name,\\n        \\\"implementation_version\\\": implementation_version,\\n        \\\"os_name\\\": os_name,\\n        \\\"platform_machine\\\": platform.machine(),\\n        \\\"platform_python_implementation\\\": platform.python_implementation(),\\n        \\\"platform_release\\\": platform.release(),\\n        \\\"platform_system\\\": platform.system(),\\n        \\\"platform_version\\\": platform.version(),\\n        \\\"python_full_version\\\": platform.python_version(),\\n        \\\"python_version\\\": \\\".\\\".join(platform.python_version().split(\\\".\\\")[:2]),\\n        \\\"sys_platform\\\": sys.platform,\\n        # See: https://www.python.org/dev/peps/pep-0345/#environment-markers\\n        \\\"os.name\\\": os_name,\\n        \\\"platform.python_implementation\\\": platform.python_implementation(),\\n        \\\"platform.version\\\": platform.version(),\\n        \\\"platform.machine\\\": platform.machine(),\\n        \\\"sys.platform\\\": sys.platform,\\n        \\\"extra\\\": \\\"\\\",\\n    }\\n    return result\\n\\n\\nDEFAULT_MARKER_CONTEXT = get_default_marker_context()\\nevaluator = Evaluator()\\n\\n\\n# FIXME: Should this raise errors, or fail silently or with a warning?\\ndef interpret(marker, execution_context=None):\\n    \\\"\\\"\\\"\\n    Interpret a marker and return a result depending on environment.\\n\\n    :param marker: The marker to interpret.\\n    :type marker: str\\n    :param execution_context: The context used for name lookup.\\n    :type execution_context: mapping\\n    \\\"\\\"\\\"\\n    try:\\n        expr, rest = parse_marker(marker)\\n    except Exception as e:\\n        raise SyntaxError(f\\\"Unable to interpret marker syntax: {marker}: {e}\\\")\\n\\n    if rest and rest[0] != \\\"#\\\":\\n        raise SyntaxError(f\\\"unexpected trailing data in marker: {marker}: {rest}\\\")\\n\\n    context = DEFAULT_MARKER_CONTEXT.copy()\\n    if execution_context:\\n        context.update(execution_context)\\n\\n    return evaluator.evaluate(expr, context)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"\\nThese helpers were originally defined in tests/test_create.py,\\nbut were refactored here so downstream projects can benefit from\\nthem too.\\n\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport json\\nimport os\\nimport sys\\nfrom contextlib import contextmanager\\nfrom functools import lru_cache\\nfrom logging import getLogger\\nfrom os.path import dirname, isdir, join, lexists\\nfrom pathlib import Path\\nfrom random import sample\\nfrom shutil import copyfile, rmtree\\nfrom subprocess import check_output\\nfrom tempfile import gettempdir\\nfrom typing import TYPE_CHECKING\\nfrom uuid import uuid4\\n\\nimport pytest\\n\\nfrom ..auxlib.compat import Utf8NamedTemporaryFile\\nfrom ..auxlib.entity import EntityEncoder\\nfrom ..base.constants import PACKAGE_CACHE_MAGIC_FILE\\nfrom ..base.context import conda_tests_ctxt_mgmt_def_pol, context, reset_context\\nfrom ..cli.conda_argparse import do_call, generate_parser\\nfrom ..cli.main import init_loggers\\nfrom ..common.compat import on_win\\nfrom ..common.io import (\\n    argv,\\n    captured,\\n    dashlist,\\n    disable_logger,\\n    env_var,\\n    stderr_log_level,\\n)\\nfrom ..common.url import path_to_url\\nfrom ..core.package_cache_data import PackageCacheData\\nfrom ..core.prefix_data import PrefixData\\nfrom ..deprecations import deprecated\\nfrom ..exceptions import conda_exception_handler\\nfrom ..gateways.disk.create import mkdir_p\\nfrom ..gateways.disk.delete import rm_rf\\nfrom ..gateways.disk.link import link\\nfrom ..gateways.disk.update import touch\\nfrom ..gateways.logging import DEBUG\\nfrom ..models.match_spec import MatchSpec\\nfrom ..models.records import PackageRecord\\nfrom ..utils import massage_arguments\\n\\nif TYPE_CHECKING:\\n    from typing import Iterator\\n\\n    from ..models.records import PrefixRecord\\n\\nTEST_LOG_LEVEL = DEBUG\\nPYTHON_BINARY = \\\"python.exe\\\" if on_win else \\\"bin/python\\\"\\nBIN_DIRECTORY = \\\"Scripts\\\" if on_win else \\\"bin\\\"\\nUNICODE_CHARACTERS = \\\"ōγђ家固한áêñßôç\\\"\\n# UNICODE_CHARACTERS_RESTRICTED = u\\\"áêñßôç\\\"\\nUNICODE_CHARACTERS_RESTRICTED = \\\"abcdef\\\"\\nwhich_or_where = \\\"which\\\" if not on_win else \\\"where\\\"\\ncp_or_copy = \\\"cp\\\" if not on_win else \\\"copy\\\"\\nenv_or_set = \\\"env\\\" if not on_win else \\\"set\\\"\\n\\n# UNICODE_CHARACTERS = u\\\"12345678abcdef\\\"\\n# UNICODE_CHARACTERS_RESTRICTED = UNICODE_CHARACTERS\\n\\n# When testing for bugs, you may want to change this to a _,\\n# for example to see if a bug is related to spaces in prefixes.\\nSPACER_CHARACTER = \\\" \\\"\\n\\nlog = getLogger(__name__)\\n\\n\\ndef escape_for_winpath(p):\\n    return p.replace(\\\"\\\\\\\\\\\", \\\"\\\\\\\\\\\\\\\\\\\")\\n\\n\\n@lru_cache(maxsize=None)\\n@deprecated(\\\"24.9\\\", \\\"25.3\\\")\\ndef running_a_python_capable_of_unicode_subprocessing():\\n    name = None\\n    # try:\\n    # UNICODE_CHARACTERS + os.sep +\\n    with Utf8NamedTemporaryFile(\\n        mode=\\\"w\\\", suffix=UNICODE_CHARACTERS + \\\".bat\\\", delete=False\\n    ) as batch_file:\\n        batch_file.write(\\\"@echo Hello World\\\\n\\\")\\n        batch_file.write(\\\"@exit 0\\\\n\\\")\\n        name = batch_file.name\\n    if name:\\n        try:\\n            out = check_output(name, cwd=dirname(name), stderr=None, shell=False)\\n            out = out.decode(\\\"utf-8\\\") if hasattr(out, \\\"decode\\\") else out\\n            if out.startswith(\\\"Hello World\\\"):\\n                return True\\n            return False\\n        except Exception:\\n            return False\\n        finally:\\n            os.unlink(name)\\n    return False\\n\\n\\ntmpdir_in_use = None\\n\\n\\n@pytest.fixture(autouse=True)\\n@deprecated(\\n    \\\"24.9\\\",\\n    \\\"25.3\\\",\\n    addendum=\\\"Use `tmp_path`, `conda.testing.path_factory`, or `conda.testing.tmp_env` instead.\\\",\\n)\\ndef set_tmpdir(tmpdir):\\n    global tmpdir_in_use\\n    if not tmpdir:\\n        return tmpdir_in_use\\n    td = tmpdir.strpath\\n    assert os.sep in td\\n    tmpdir_in_use = td\\n\\n\\n@deprecated(\\n    \\\"24.9\\\",\\n    \\\"25.3\\\",\\n    addendum=\\\"Use `tmp_path`, `conda.testing.path_factory`, or `conda.testing.tmp_env` instead.\\\",\\n)\\ndef _get_temp_prefix(name=None, use_restricted_unicode=False):\\n    tmpdir = tmpdir_in_use or gettempdir()\\n    capable = running_a_python_capable_of_unicode_subprocessing()\\n\\n    if not capable or use_restricted_unicode:\\n        RESTRICTED = UNICODE_CHARACTERS_RESTRICTED\\n        random_unicode = \\\"\\\".join(sample(RESTRICTED, len(RESTRICTED)))\\n    else:\\n        random_unicode = \\\"\\\".join(sample(UNICODE_CHARACTERS, len(UNICODE_CHARACTERS)))\\n    tmpdir_name = os.environ.get(\\n        \\\"CONDA_TEST_TMPDIR_NAME\\\",\\n        (str(uuid4())[:4] + SPACER_CHARACTER + random_unicode)\\n        if name is None\\n        else name,\\n    )\\n    prefix = join(tmpdir, tmpdir_name)\\n\\n    # Exit immediately if we cannot use hardlinks, on Windows, we get permissions errors if we use\\n    # sys.executable so instead use the pdb files.\\n    src = sys.executable.replace(\\\".exe\\\", \\\".pdb\\\") if on_win else sys.executable\\n    dst = os.path.join(tmpdir, os.path.basename(sys.executable))\\n\\n    try:\\n        link(src, dst)\\n    except OSError:\\n        print(\\n            f\\\"\\\\nWARNING :: You are testing `conda` with `tmpdir`:-\\\\n           {tmpdir}\\\\n\\\"\\n            f\\\"           not on the same FS as `sys.prefix`:\\\\n           {sys.prefix}\\\\n\\\"\\n            \\\"           this will be slow and unlike the majority of end-user installs.\\\\n\\\"\\n            \\\"           Please pass `--basetemp=<somewhere-else>` instead.\\\"\\n        )\\n    try:\\n        rm_rf(dst)\\n    except Exception as e:\\n        print(e)\\n        pass\\n\\n    return prefix\\n\\n\\n@deprecated(\\n    \\\"24.9\\\",\\n    \\\"25.3\\\",\\n    addendum=\\\"Use `tmp_path`, `conda.testing.path_factory`, or `conda.testing.tmp_env` instead.\\\",\\n)\\ndef make_temp_prefix(name=None, use_restricted_unicode=False, _temp_prefix=None):\\n    \\\"\\\"\\\"\\n    When the env. you are creating will be used to install Python 2.7 on Windows\\n    only a restricted amount of Unicode will work, and probably only those chars\\n    in your current codepage, so the characters in UNICODE_CHARACTERS_RESTRICTED\\n    should probably be randomly generated from that instead. The problem here is\\n    that the current codepage needs to be able to handle 'sys.prefix' otherwise\\n    ntpath will fall over.\\n    \\\"\\\"\\\"\\n    if not _temp_prefix:\\n        _temp_prefix = _get_temp_prefix(\\n            name=name, use_restricted_unicode=use_restricted_unicode\\n        )\\n    try:\\n        os.makedirs(_temp_prefix)\\n    except:\\n        pass\\n    assert isdir(_temp_prefix)\\n    return _temp_prefix\\n\\n\\n@deprecated(\\n    \\\"24.9\\\",\\n    \\\"25.3\\\",\\n    addendum=\\\"Use `tmp_path`, `conda.testing.path_factory`, or `conda.testing.tmp_env` instead.\\\",\\n)\\ndef FORCE_temp_prefix(name=None, use_restricted_unicode=False):\\n    _temp_prefix = _get_temp_prefix(\\n        name=name, use_restricted_unicode=use_restricted_unicode\\n    )\\n    rm_rf(_temp_prefix)\\n    os.makedirs(_temp_prefix)\\n    assert isdir(_temp_prefix)\\n    return _temp_prefix\\n\\n\\nclass Commands:\\n    COMPARE = \\\"compare\\\"\\n    CONFIG = \\\"config\\\"\\n    CLEAN = \\\"clean\\\"\\n    CREATE = \\\"create\\\"\\n    INFO = \\\"info\\\"\\n    INSTALL = \\\"install\\\"\\n    LIST = \\\"list\\\"\\n    REMOVE = \\\"remove\\\"\\n    SEARCH = \\\"search\\\"\\n    UPDATE = \\\"update\\\"\\n    RUN = \\\"run\\\"\\n\\n\\n@deprecated(\\\"23.9\\\", \\\"25.3\\\", addendum=\\\"Use `conda.testing.conda_cli` instead.\\\")\\ndef run_command(command, prefix, *arguments, **kwargs) -> tuple[str, str, int]:\\n    assert isinstance(arguments, tuple), \\\"run_command() arguments must be tuples\\\"\\n    arguments = massage_arguments(arguments)\\n\\n    use_exception_handler = kwargs.get(\\\"use_exception_handler\\\", False)\\n    # These commands require 'dev' mode to be enabled during testing because\\n    # they end up calling run_script() in link.py and that uses wrapper scripts for e.g. activate.\\n    # Setting `dev` means that, in these scripts, conda is executed via:\\n    #   `sys.prefix/bin/python -m conda` (or the Windows equivalent).\\n    # .. and the source code for `conda` is put on `sys.path` via `PYTHONPATH` (a bit gross but\\n    # less so than always requiring `cwd` to be the root of the conda source tree in every case).\\n    # If you do not want this to happen for some test you must pass dev=False as a kwarg, though\\n    # for nearly all tests, you want to make sure you are running *this* conda and not some old\\n    # conda (it was random which you'd get depending on the initial values of PATH and PYTHONPATH\\n    # - and likely more variables - before `dev` came along). Setting CONDA_EXE is not enough\\n    # either because in the 4.5 days that would just run whatever Python was found first on PATH.\\n    command_defaults_to_dev = command in (\\n        Commands.CREATE,\\n        Commands.INSTALL,\\n        Commands.REMOVE,\\n        Commands.RUN,\\n    )\\n    dev = kwargs.get(\\\"dev\\\", True if command_defaults_to_dev else False)\\n    debug = kwargs.get(\\\"debug_wrapper_scripts\\\", False)\\n\\n    p = generate_parser()\\n\\n    if command is Commands.CONFIG:\\n        arguments.append(\\\"--file\\\")\\n        arguments.append(join(prefix, \\\"condarc\\\"))\\n    if command in (\\n        Commands.LIST,\\n        Commands.COMPARE,\\n        Commands.CREATE,\\n        Commands.INSTALL,\\n        Commands.REMOVE,\\n        Commands.UPDATE,\\n        Commands.RUN,\\n    ):\\n        arguments.insert(0, \\\"-p\\\")\\n        arguments.insert(1, prefix)\\n    if command in (Commands.CREATE, Commands.INSTALL, Commands.REMOVE, Commands.UPDATE):\\n        arguments.extend([\\\"-y\\\", \\\"-q\\\"])\\n\\n    arguments.insert(0, command)\\n    if dev:\\n        arguments.insert(1, \\\"--dev\\\")\\n    if debug:\\n        arguments.insert(1, \\\"--debug-wrapper-scripts\\\")\\n\\n    # It would be nice at this point to re-use:\\n    # from ..cli.python_api import run_command as python_api_run_command\\n    # python_api_run_command\\n    # .. but that does not support no_capture and probably more stuff.\\n\\n    args = p.parse_args(arguments)\\n    context._set_argparse_args(args)\\n    init_loggers()\\n    cap_args = () if not kwargs.get(\\\"no_capture\\\") else (None, None)\\n    # list2cmdline is not exact, but it is only informational.\\n    print(\\n        \\\"\\\\n\\\\nEXECUTING COMMAND >>> $ conda {}\\\\n\\\\n\\\".format(\\\" \\\".join(arguments)),\\n        file=sys.stderr,\\n    )\\n    with stderr_log_level(TEST_LOG_LEVEL, \\\"conda\\\"), stderr_log_level(\\n        TEST_LOG_LEVEL, \\\"requests\\\"\\n    ):\\n        with argv([\\\"python_api\\\", *arguments]), captured(*cap_args) as c:\\n            if use_exception_handler:\\n                result = conda_exception_handler(do_call, args, p)\\n            else:\\n                result = do_call(args, p)\\n        stdout = c.stdout\\n        stderr = c.stderr\\n        print(stdout, file=sys.stdout)\\n        print(stderr, file=sys.stderr)\\n\\n    # Unfortunately there are other ways to change context, such as Commands.CREATE --offline.\\n    # You will probably end up playing whack-a-bug here adding more and more the tuple here.\\n    if command in (Commands.CONFIG,):\\n        reset_context([os.path.join(prefix + os.sep, \\\"condarc\\\")], args)\\n    return stdout, stderr, result\\n\\n\\n@deprecated(\\\"24.9\\\", \\\"25.3\\\", addendum=\\\"Use `conda.testing.tmp_env` instead.\\\")\\n@contextmanager\\ndef make_temp_env(*packages, **kwargs) -> Iterator[str]:\\n    name = kwargs.pop(\\\"name\\\", None)\\n    use_restricted_unicode = kwargs.pop(\\\"use_restricted_unicode\\\", False)\\n\\n    prefix = kwargs.pop(\\\"prefix\\\", None) or _get_temp_prefix(\\n        name=name, use_restricted_unicode=use_restricted_unicode\\n    )\\n    clean_prefix = kwargs.pop(\\\"clean_prefix\\\", None)\\n    if clean_prefix:\\n        if os.path.exists(prefix):\\n            rm_rf(prefix)\\n    if not isdir(prefix):\\n        make_temp_prefix(name, use_restricted_unicode, prefix)\\n    with disable_logger(\\\"fetch\\\"):\\n        try:\\n            # try to clear any config that's been set by other tests\\n            # CAUTION :: This does not partake in the context stack management code\\n            #            of env_{var,vars,unmodified} and, when used in conjunction\\n            #            with that code, this *must* be called first.\\n            reset_context([os.path.join(prefix + os.sep, \\\"condarc\\\")])\\n            run_command(Commands.CREATE, prefix, *packages, **kwargs)\\n            yield prefix\\n        finally:\\n            if \\\"CONDA_TEST_SAVE_TEMPS\\\" not in os.environ:\\n                rmtree(prefix, ignore_errors=True)\\n            else:\\n                log.warning(\\n                    f\\\"CONDA_TEST_SAVE_TEMPS :: retaining make_temp_env {prefix}\\\"\\n                )\\n\\n\\n@deprecated(\\\"24.9\\\", \\\"25.3\\\", addendum=\\\"Use `conda.testing.tmp_pkgs_dir` instead.\\\")\\n@contextmanager\\ndef make_temp_package_cache() -> Iterator[str]:\\n    prefix = make_temp_prefix(use_restricted_unicode=on_win)\\n    pkgs_dir = join(prefix, \\\"pkgs\\\")\\n    mkdir_p(pkgs_dir)\\n    touch(join(pkgs_dir, PACKAGE_CACHE_MAGIC_FILE))\\n\\n    try:\\n        with env_var(\\n            \\\"CONDA_PKGS_DIRS\\\",\\n            pkgs_dir,\\n            stack_callback=conda_tests_ctxt_mgmt_def_pol,\\n        ):\\n            assert context.pkgs_dirs == (pkgs_dir,)\\n            yield pkgs_dir\\n    finally:\\n        rmtree(prefix, ignore_errors=True)\\n        PackageCacheData._cache_.pop(pkgs_dir, None)\\n\\n\\n@deprecated(\\\"24.9\\\", \\\"25.3\\\", addendum=\\\"Use `conda.testing.tmp_channel` instead.\\\")\\n@contextmanager\\ndef make_temp_channel(packages) -> Iterator[str]:\\n    package_reqs = [pkg.replace(\\\"-\\\", \\\"=\\\") for pkg in packages]\\n    package_names = [pkg.split(\\\"-\\\")[0] for pkg in packages]\\n\\n    with make_temp_env(*package_reqs) as prefix:\\n        for package in packages:\\n            assert package_is_installed(prefix, package.replace(\\\"-\\\", \\\"=\\\"))\\n        data = [\\n            p for p in PrefixData(prefix).iter_records() if p[\\\"name\\\"] in package_names\\n        ]\\n        run_command(Commands.REMOVE, prefix, *package_names)\\n        for package in packages:\\n            assert not package_is_installed(prefix, package.replace(\\\"-\\\", \\\"=\\\"))\\n\\n    repodata = {\\\"info\\\": {}, \\\"packages\\\": {}}\\n    tarfiles = {}\\n    for package_data in data:\\n        pkg_data = package_data\\n        fname = pkg_data[\\\"fn\\\"]\\n        tarfiles[fname] = join(PackageCacheData.first_writable().pkgs_dir, fname)\\n\\n        pkg_data = pkg_data.dump()\\n        for field in (\\\"url\\\", \\\"channel\\\", \\\"schannel\\\"):\\n            pkg_data.pop(field, None)\\n        repodata[\\\"packages\\\"][fname] = PackageRecord(**pkg_data)\\n\\n    with make_temp_env() as channel:\\n        subchan = join(channel, context.subdir)\\n        noarch_dir = join(channel, \\\"noarch\\\")\\n        channel = path_to_url(channel)\\n        os.makedirs(subchan)\\n        os.makedirs(noarch_dir)\\n        for fname, tar_old_path in tarfiles.items():\\n            tar_new_path = join(subchan, fname)\\n            copyfile(tar_old_path, tar_new_path)\\n\\n        with open(join(subchan, \\\"repodata.json\\\"), \\\"w\\\") as f:\\n            f.write(json.dumps(repodata, cls=EntityEncoder))\\n        with open(join(noarch_dir, \\\"repodata.json\\\"), \\\"w\\\") as f:\\n            f.write(json.dumps({}, cls=EntityEncoder))\\n\\n        yield channel\\n\\n\\n@deprecated(\\n    \\\"24.9\\\", \\\"25.3\\\", addendum=\\\"Use `tmp_path` or `conda.testing.path_factory` instead.\\\"\\n)\\ndef create_temp_location() -> str:\\n    return _get_temp_prefix()\\n\\n\\n@deprecated(\\n    \\\"24.9\\\", \\\"25.3\\\", addendum=\\\"Use `tmp_path` or `conda.testing.path_factory` instead.\\\"\\n)\\n@contextmanager\\ndef tempdir() -> Iterator[str]:\\n    prefix = create_temp_location()\\n    try:\\n        os.makedirs(prefix)\\n        yield prefix\\n    finally:\\n        if lexists(prefix):\\n            rm_rf(prefix)\\n\\n\\n@deprecated(\\\"24.9\\\", \\\"25.3\\\", addendum=\\\"Use `conda.base.context.reset_context` instead.\\\")\\ndef reload_config(prefix) -> None:\\n    prefix_condarc = join(prefix, \\\"condarc\\\")\\n    reset_context([prefix_condarc])\\n\\n\\ndef package_is_installed(\\n    prefix: str | os.PathLike | Path,\\n    spec: str | MatchSpec,\\n) -> PrefixRecord | None:\\n    spec = MatchSpec(spec)\\n    prefix_recs = tuple(PrefixData(str(prefix), pip_interop_enabled=True).query(spec))\\n    if not prefix_recs:\\n        return None\\n    elif len(prefix_recs) > 1:\\n        raise AssertionError(\\n            f\\\"Multiple packages installed.{dashlist(prec.dist_str() for prec in prefix_recs)}\\\"\\n        )\\n    else:\\n        return prefix_recs[0]\\n\\n\\ndef get_shortcut_dir(prefix_for_unix=sys.prefix):\\n    if sys.platform == \\\"win32\\\":\\n        # On Windows, .nonadmin has been historically created by constructor in sys.prefix\\n        user_mode = \\\"user\\\" if Path(sys.prefix, \\\".nonadmin\\\").is_file() else \\\"system\\\"\\n        try:  # menuinst v2\\n            from menuinst.platforms.win_utils.knownfolders import dirs_src\\n\\n            return dirs_src[user_mode][\\\"start\\\"][0]\\n        except ImportError:  # older menuinst versions; TODO: remove\\n            try:\\n                from menuinst.win32 import dirs_src\\n\\n                return dirs_src[user_mode][\\\"start\\\"][0]\\n            except ImportError:\\n                from menuinst.win32 import dirs\\n\\n                return dirs[user_mode][\\\"start\\\"]\\n    # on unix, .nonadmin is only created by menuinst v2 as needed on the target prefix\\n    # it might exist, or might not; if it doesn't, we try to create it\\n    # see https://github.com/conda/menuinst/issues/150\\n    non_admin_file = Path(prefix_for_unix, \\\".nonadmin\\\")\\n    if non_admin_file.is_file():\\n        user_mode = \\\"user\\\"\\n    else:\\n        try:\\n            non_admin_file.touch()\\n        except OSError:\\n            user_mode = \\\"system\\\"\\n        else:\\n            user_mode = \\\"user\\\"\\n            non_admin_file.unlink()\\n\\n    if sys.platform == \\\"darwin\\\":\\n        if user_mode == \\\"user\\\":\\n            return join(os.environ[\\\"HOME\\\"], \\\"Applications\\\")\\n        return \\\"/Applications\\\"\\n    if sys.platform == \\\"linux\\\":\\n        if user_mode == \\\"user\\\":\\n            return join(os.environ[\\\"HOME\\\"], \\\".local\\\", \\\"share\\\", \\\"applications\\\")\\n        return \\\"/usr/share/applications\\\"\\n    raise NotImplementedError(sys.platform)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Helpers for testing the solver.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport collections\\nimport functools\\nimport json\\nimport pathlib\\nfrom tempfile import TemporaryDirectory\\n\\nimport pytest\\n\\nfrom ..base.context import context\\nfrom ..core.solve import Solver\\nfrom ..exceptions import (\\n    PackagesNotFoundError,\\n    ResolvePackageNotFound,\\n    UnsatisfiableError,\\n)\\nfrom ..models.channel import Channel\\nfrom ..models.match_spec import MatchSpec\\nfrom ..models.records import PackageRecord\\nfrom . import helpers\\n\\n\\n@functools.lru_cache\\ndef index_packages(num):\\n    \\\"\\\"\\\"Get the index data of the ``helpers.get_index_r_*`` helpers.\\\"\\\"\\\"\\n    # XXX: get_index_r_X should probably be refactored to avoid loading the environment like this.\\n    get_index = getattr(helpers, f\\\"get_index_r_{num}\\\")\\n    index, _ = get_index(context.subdir)\\n    return list(index.values())\\n\\n\\ndef package_string(record):\\n    return f\\\"{record.channel.name}::{record.name}-{record.version}-{record.build}\\\"\\n\\n\\ndef package_string_set(packages):\\n    \\\"\\\"\\\"Transforms package container in package string set.\\\"\\\"\\\"\\n    return {package_string(record) for record in packages}\\n\\n\\ndef package_dict(packages):\\n    \\\"\\\"\\\"Transforms package container into a dictionary.\\\"\\\"\\\"\\n    return {record.name: record for record in packages}\\n\\n\\nclass SimpleEnvironment:\\n    \\\"\\\"\\\"Helper environment object.\\\"\\\"\\\"\\n\\n    REPO_DATA_KEYS = (\\n        \\\"build\\\",\\n        \\\"build_number\\\",\\n        \\\"depends\\\",\\n        \\\"license\\\",\\n        \\\"md5\\\",\\n        \\\"name\\\",\\n        \\\"sha256\\\",\\n        \\\"size\\\",\\n        \\\"subdir\\\",\\n        \\\"timestamp\\\",\\n        \\\"version\\\",\\n        \\\"track_features\\\",\\n        \\\"features\\\",\\n    )\\n\\n    def __init__(self, path, solver_class, subdirs=context.subdirs):\\n        self._path = pathlib.Path(path)\\n        self._prefix_path = self._path / \\\"prefix\\\"\\n        self._channels_path = self._path / \\\"channels\\\"\\n        self._solver_class = solver_class\\n        self.subdirs = subdirs\\n        self.installed_packages = []\\n        # if repo_packages is a list, the packages will be put in a `test` channel\\n        # if it is a dictionary, it the keys are the channel name and the value\\n        # the channel packages\\n        self.repo_packages: list[str] | dict[str, list[str]] = []\\n\\n    def solver(self, add, remove):\\n        \\\"\\\"\\\"Writes ``repo_packages`` to the disk and creates a solver instance.\\\"\\\"\\\"\\n        channels = []\\n        self._write_installed_packages()\\n        for channel_name, packages in self._channel_packages.items():\\n            self._write_repo_packages(channel_name, packages)\\n            channel = Channel(str(self._channels_path / channel_name))\\n            channels.append(channel)\\n        return self._solver_class(\\n            prefix=self._prefix_path,\\n            subdirs=self.subdirs,\\n            channels=channels,\\n            specs_to_add=add,\\n            specs_to_remove=remove,\\n        )\\n\\n    def solver_transaction(self, add=(), remove=(), as_specs=False):\\n        packages = self.solver(add=add, remove=remove).solve_final_state()\\n        if as_specs:\\n            return packages\\n        return package_string_set(packages)\\n\\n    def install(self, *specs, as_specs=False):\\n        return self.solver_transaction(add=specs, as_specs=as_specs)\\n\\n    def remove(self, *specs, as_specs=False):\\n        return self.solver_transaction(remove=specs, as_specs=as_specs)\\n\\n    @property\\n    def _channel_packages(self):\\n        \\\"\\\"\\\"Helper that unfolds the ``repo_packages`` into a dictionary.\\\"\\\"\\\"\\n        if isinstance(self.repo_packages, dict):\\n            return self.repo_packages\\n        return {\\\"test\\\": self.repo_packages}\\n\\n    def _package_data(self, record):\\n        \\\"\\\"\\\"Turn record into data, to be written in the JSON environment/repo files.\\\"\\\"\\\"\\n        data = {\\n            key: value\\n            for key, value in vars(record).items()\\n            if key in self.REPO_DATA_KEYS\\n        }\\n        if \\\"subdir\\\" not in data:\\n            data[\\\"subdir\\\"] = context.subdir\\n        return data\\n\\n    def _write_installed_packages(self):\\n        if not self.installed_packages:\\n            return\\n        conda_meta = self._prefix_path / \\\"conda-meta\\\"\\n        conda_meta.mkdir(exist_ok=True, parents=True)\\n        # write record files\\n        for record in self.installed_packages:\\n            record_path = (\\n                conda_meta / f\\\"{record.name}-{record.version}-{record.build}.json\\\"\\n            )\\n            record_data = self._package_data(record)\\n            record_data[\\\"channel\\\"] = record.channel.name\\n            record_path.write_text(json.dumps(record_data))\\n        # write history file\\n        history_path = conda_meta / \\\"history\\\"\\n        history_path.write_text(\\n            \\\"\\\\n\\\".join(\\n                (\\n                    \\\"==> 2000-01-01 00:00:00 <==\\\",\\n                    *map(package_string, self.installed_packages),\\n                )\\n            )\\n        )\\n\\n    def _write_repo_packages(self, channel_name, packages):\\n        \\\"\\\"\\\"Write packages to the channel path.\\\"\\\"\\\"\\n        # build package data\\n        package_data = collections.defaultdict(dict)\\n        for record in packages:\\n            package_data[record.subdir][record.fn] = self._package_data(record)\\n        # write repodata\\n        assert set(self.subdirs).issuperset(set(package_data.keys()))\\n        for subdir in self.subdirs:\\n            subdir_path = self._channels_path / channel_name / subdir\\n            subdir_path.mkdir(parents=True, exist_ok=True)\\n            subdir_path.joinpath(\\\"repodata.json\\\").write_text(\\n                json.dumps(\\n                    {\\n                        \\\"info\\\": {\\n                            \\\"subdir\\\": subdir,\\n                        },\\n                        \\\"packages\\\": package_data.get(subdir, {}),\\n                    }\\n                )\\n            )\\n\\n\\ndef empty_prefix():\\n    return TemporaryDirectory(prefix=\\\"conda-test-repo-\\\")\\n\\n\\n@pytest.fixture()\\ndef temp_simple_env(solver_class=Solver) -> SimpleEnvironment:\\n    with empty_prefix() as prefix:\\n        yield SimpleEnvironment(prefix, solver_class)\\n\\n\\nclass SolverTests:\\n    \\\"\\\"\\\"Tests for :py:class:`conda.core.solve.Solver` implementations.\\\"\\\"\\\"\\n\\n    @property\\n    def solver_class(self) -> type[Solver]:\\n        \\\"\\\"\\\"Class under test.\\\"\\\"\\\"\\n        raise NotImplementedError\\n\\n    @property\\n    def tests_to_skip(self):\\n        return {}  # skip reason -> list of tests to skip\\n\\n    @pytest.fixture(autouse=True)\\n    def skip_tests(self, request):\\n        for reason, skip_list in self.tests_to_skip.items():\\n            if request.node.name in skip_list:\\n                pytest.skip(reason)\\n\\n    @pytest.fixture()\\n    def env(self):\\n        with TemporaryDirectory(prefix=\\\"conda-test-repo-\\\") as tmpdir:\\n            self.env = SimpleEnvironment(tmpdir, self.solver_class)\\n            yield self.env\\n            self.env = None\\n\\n    def find_package_in_list(self, packages, **kwargs):\\n        for record in packages:\\n            if all(getattr(record, key) == value for key, value in kwargs.items()):\\n                return record\\n\\n    def find_package(self, **kwargs):\\n        if isinstance(self.env.repo_packages, dict):\\n            if \\\"channel\\\" not in kwargs:\\n                raise ValueError(\\n                    \\\"Repo has multiple channels, the `channel` argument must be specified\\\"\\n                )\\n            packages = self.env.repo_packages[kwargs[\\\"channel\\\"]]\\n        else:\\n            packages = self.env.repo_packages\\n        return self.find_package_in_list(packages, **kwargs)\\n\\n    def assert_unsatisfiable(self, exc_info, entries):\\n        \\\"\\\"\\\"Helper to assert that a :py:class:`conda.exceptions.UnsatisfiableError`\\n        instance as a the specified set of unsatisfiable specifications.\\n        \\\"\\\"\\\"\\n        assert issubclass(exc_info.type, UnsatisfiableError)\\n        if exc_info.type is UnsatisfiableError:\\n            assert (\\n                sorted(\\n                    tuple(map(str, entries)) for entries in exc_info.value.unsatisfiable\\n                )\\n                == entries\\n            )\\n\\n    def test_empty(self, env):\\n        env.repo_packages = index_packages(1)\\n        assert env.install() == set()\\n\\n    def test_iopro_mkl(self, env):\\n        env.repo_packages = index_packages(1)\\n        assert env.install(\\\"iopro 1.4*\\\", \\\"python 2.7*\\\", \\\"numpy 1.7*\\\") == {\\n            \\\"test::iopro-1.4.3-np17py27_p0\\\",\\n            \\\"test::numpy-1.7.1-py27_0\\\",\\n            \\\"test::openssl-1.0.1c-0\\\",\\n            \\\"test::python-2.7.5-0\\\",\\n            \\\"test::readline-6.2-0\\\",\\n            \\\"test::sqlite-3.7.13-0\\\",\\n            \\\"test::system-5.8-1\\\",\\n            \\\"test::tk-8.5.13-0\\\",\\n            \\\"test::unixodbc-2.3.1-0\\\",\\n            \\\"test::zlib-1.2.7-0\\\",\\n            \\\"test::distribute-0.6.36-py27_1\\\",\\n            \\\"test::pip-1.3.1-py27_1\\\",\\n        }\\n\\n    def test_iopro_nomkl(self, env):\\n        env.repo_packages = index_packages(1)\\n        assert env.install(\\n            \\\"iopro 1.4*\\\", \\\"python 2.7*\\\", \\\"numpy 1.7*\\\", MatchSpec(track_features=\\\"mkl\\\")\\n        ) == {\\n            \\\"test::iopro-1.4.3-np17py27_p0\\\",\\n            \\\"test::mkl-rt-11.0-p0\\\",\\n            \\\"test::numpy-1.7.1-py27_p0\\\",\\n            \\\"test::openssl-1.0.1c-0\\\",\\n            \\\"test::python-2.7.5-0\\\",\\n            \\\"test::readline-6.2-0\\\",\\n            \\\"test::sqlite-3.7.13-0\\\",\\n            \\\"test::system-5.8-1\\\",\\n            \\\"test::tk-8.5.13-0\\\",\\n            \\\"test::unixodbc-2.3.1-0\\\",\\n            \\\"test::zlib-1.2.7-0\\\",\\n            \\\"test::distribute-0.6.36-py27_1\\\",\\n            \\\"test::pip-1.3.1-py27_1\\\",\\n        }\\n\\n    def test_mkl(self, env):\\n        env.repo_packages = index_packages(1)\\n        assert env.install(\\\"mkl\\\") == env.install(\\n            \\\"mkl 11*\\\", MatchSpec(track_features=\\\"mkl\\\")\\n        )\\n\\n    def test_accelerate(self, env):\\n        env.repo_packages = index_packages(1)\\n        assert env.install(\\\"accelerate\\\") == env.install(\\n            \\\"accelerate\\\", MatchSpec(track_features=\\\"mkl\\\")\\n        )\\n\\n    def test_scipy_mkl(self, env):\\n        env.repo_packages = index_packages(1)\\n        records = env.install(\\n            \\\"scipy\\\",\\n            \\\"python 2.7*\\\",\\n            \\\"numpy 1.7*\\\",\\n            MatchSpec(track_features=\\\"mkl\\\"),\\n            as_specs=True,\\n        )\\n\\n        for record in records:\\n            if record.name in (\\\"numpy\\\", \\\"scipy\\\"):\\n                assert \\\"mkl\\\" in record.features\\n\\n        assert \\\"test::numpy-1.7.1-py27_p0\\\" in package_string_set(records)\\n        assert \\\"test::scipy-0.12.0-np17py27_p0\\\" in package_string_set(records)\\n\\n    def test_anaconda_nomkl(self, env):\\n        env.repo_packages = index_packages(1)\\n        records = env.install(\\\"anaconda 1.5.0\\\", \\\"python 2.7*\\\", \\\"numpy 1.7*\\\")\\n        assert len(records) == 107\\n        assert \\\"test::scipy-0.12.0-np17py27_0\\\" in records\\n\\n    def test_pseudo_boolean(self, env):\\n        env.repo_packages = index_packages(1)\\n        # The latest version of iopro, 1.5.0, was not built against numpy 1.5\\n        assert env.install(\\\"iopro\\\", \\\"python 2.7*\\\", \\\"numpy 1.5*\\\") == {\\n            \\\"test::iopro-1.4.3-np15py27_p0\\\",\\n            \\\"test::numpy-1.5.1-py27_4\\\",\\n            \\\"test::openssl-1.0.1c-0\\\",\\n            \\\"test::python-2.7.5-0\\\",\\n            \\\"test::readline-6.2-0\\\",\\n            \\\"test::sqlite-3.7.13-0\\\",\\n            \\\"test::system-5.8-1\\\",\\n            \\\"test::tk-8.5.13-0\\\",\\n            \\\"test::unixodbc-2.3.1-0\\\",\\n            \\\"test::zlib-1.2.7-0\\\",\\n            \\\"test::distribute-0.6.36-py27_1\\\",\\n            \\\"test::pip-1.3.1-py27_1\\\",\\n        }\\n        assert env.install(\\n            \\\"iopro\\\", \\\"python 2.7*\\\", \\\"numpy 1.5*\\\", MatchSpec(track_features=\\\"mkl\\\")\\n        ) == {\\n            \\\"test::iopro-1.4.3-np15py27_p0\\\",\\n            \\\"test::mkl-rt-11.0-p0\\\",\\n            \\\"test::numpy-1.5.1-py27_p4\\\",\\n            \\\"test::openssl-1.0.1c-0\\\",\\n            \\\"test::python-2.7.5-0\\\",\\n            \\\"test::readline-6.2-0\\\",\\n            \\\"test::sqlite-3.7.13-0\\\",\\n            \\\"test::system-5.8-1\\\",\\n            \\\"test::tk-8.5.13-0\\\",\\n            \\\"test::unixodbc-2.3.1-0\\\",\\n            \\\"test::zlib-1.2.7-0\\\",\\n            \\\"test::distribute-0.6.36-py27_1\\\",\\n            \\\"test::pip-1.3.1-py27_1\\\",\\n        }\\n\\n    def test_unsat_from_r1(self, env):\\n        env.repo_packages = index_packages(1)\\n\\n        with pytest.raises(UnsatisfiableError) as exc_info:\\n            env.install(\\\"numpy 1.5*\\\", \\\"scipy 0.12.0b1\\\")\\n        self.assert_unsatisfiable(\\n            exc_info,\\n            [\\n                (\\\"numpy=1.5\\\",),\\n                (\\\"scipy==0.12.0b1\\\", \\\"numpy[version='1.6.*|1.7.*']\\\"),\\n            ],\\n        )\\n\\n        with pytest.raises(UnsatisfiableError) as exc_info:\\n            env.install(\\\"numpy 1.5*\\\", \\\"python 3*\\\")\\n        self.assert_unsatisfiable(\\n            exc_info,\\n            [\\n                (\\\"numpy=1.5\\\", \\\"nose\\\", \\\"python=3.3\\\"),\\n                (\\\"numpy=1.5\\\", \\\"python[version='2.6.*|2.7.*']\\\"),\\n                (\\\"python=3\\\",),\\n            ],\\n        )\\n\\n        with pytest.raises((ResolvePackageNotFound, PackagesNotFoundError)) as exc_info:\\n            env.install(\\\"numpy 1.5*\\\", \\\"numpy 1.6*\\\")\\n        if exc_info.type is ResolvePackageNotFound:\\n            assert sorted(map(str, exc_info.value.bad_deps)) == [\\n                \\\"numpy[version='1.5.*,1.6.*']\\\",\\n            ]\\n\\n    def test_unsat_simple(self, env):\\n        env.repo_packages = [\\n            helpers.record(name=\\\"a\\\", depends=[\\\"c >=1,<2\\\"]),\\n            helpers.record(name=\\\"b\\\", depends=[\\\"c >=2,<3\\\"]),\\n            helpers.record(name=\\\"c\\\", version=\\\"1.0\\\"),\\n            helpers.record(name=\\\"c\\\", version=\\\"2.0\\\"),\\n        ]\\n        with pytest.raises(UnsatisfiableError) as exc_info:\\n            env.install(\\\"a\\\", \\\"b\\\")\\n        self.assert_unsatisfiable(\\n            exc_info,\\n            [\\n                (\\\"a\\\", \\\"c[version='>=1,<2']\\\"),\\n                (\\\"b\\\", \\\"c[version='>=2,<3']\\\"),\\n            ],\\n        )\\n\\n    def test_get_dists(self, env):\\n        env.repo_packages = index_packages(1)\\n        records = env.install(\\\"anaconda 1.4.0\\\")\\n        assert \\\"test::anaconda-1.4.0-np17py33_0\\\" in records\\n        assert \\\"test::freetype-2.4.10-0\\\" in records\\n\\n    def test_unsat_shortest_chain_1(self, env):\\n        env.repo_packages = [\\n            helpers.record(name=\\\"a\\\", depends=[\\\"d\\\", \\\"c <1.3.0\\\"]),\\n            helpers.record(name=\\\"b\\\", depends=[\\\"c\\\"]),\\n            helpers.record(\\n                name=\\\"c\\\",\\n                version=\\\"1.3.6\\\",\\n            ),\\n            helpers.record(\\n                name=\\\"c\\\",\\n                version=\\\"1.2.8\\\",\\n            ),\\n            helpers.record(name=\\\"d\\\", depends=[\\\"c >=0.8.0\\\"]),\\n        ]\\n        with pytest.raises(UnsatisfiableError) as exc_info:\\n            env.install(\\\"c=1.3.6\\\", \\\"a\\\", \\\"b\\\")\\n        self.assert_unsatisfiable(\\n            exc_info,\\n            [\\n                (\\\"a\\\", \\\"c[version='<1.3.0']\\\"),\\n                (\\\"a\\\", \\\"d\\\", \\\"c[version='>=0.8.0']\\\"),\\n                (\\\"b\\\", \\\"c\\\"),\\n                (\\\"c=1.3.6\\\",),\\n            ],\\n        )\\n\\n    def test_unsat_shortest_chain_2(self, env):\\n        env.repo_packages = [\\n            helpers.record(name=\\\"a\\\", depends=[\\\"d\\\", \\\"c >=0.8.0\\\"]),\\n            helpers.record(name=\\\"b\\\", depends=[\\\"c\\\"]),\\n            helpers.record(\\n                name=\\\"c\\\",\\n                version=\\\"1.3.6\\\",\\n            ),\\n            helpers.record(\\n                name=\\\"c\\\",\\n                version=\\\"1.2.8\\\",\\n            ),\\n            helpers.record(name=\\\"d\\\", depends=[\\\"c <1.3.0\\\"]),\\n        ]\\n        with pytest.raises(UnsatisfiableError) as exc_info:\\n            env.install(\\\"c=1.3.6\\\", \\\"a\\\", \\\"b\\\")\\n        self.assert_unsatisfiable(\\n            exc_info,\\n            [\\n                (\\\"a\\\", \\\"c[version='>=0.8.0']\\\"),\\n                (\\\"a\\\", \\\"d\\\", \\\"c[version='<1.3.0']\\\"),\\n                (\\\"b\\\", \\\"c\\\"),\\n                (\\\"c=1.3.6\\\",),\\n            ],\\n        )\\n\\n    def test_unsat_shortest_chain_3(self, env):\\n        env.repo_packages = [\\n            helpers.record(name=\\\"a\\\", depends=[\\\"f\\\", \\\"e\\\"]),\\n            helpers.record(name=\\\"b\\\", depends=[\\\"c\\\"]),\\n            helpers.record(\\n                name=\\\"c\\\",\\n                version=\\\"1.3.6\\\",\\n            ),\\n            helpers.record(\\n                name=\\\"c\\\",\\n                version=\\\"1.2.8\\\",\\n            ),\\n            helpers.record(name=\\\"d\\\", depends=[\\\"c >=0.8.0\\\"]),\\n            helpers.record(name=\\\"e\\\", depends=[\\\"c <1.3.0\\\"]),\\n            helpers.record(name=\\\"f\\\", depends=[\\\"d\\\"]),\\n        ]\\n        with pytest.raises(UnsatisfiableError) as exc_info:\\n            env.install(\\\"c=1.3.6\\\", \\\"a\\\", \\\"b\\\")\\n        self.assert_unsatisfiable(\\n            exc_info,\\n            [\\n                (\\\"a\\\", \\\"e\\\", \\\"c[version='<1.3.0']\\\"),\\n                (\\\"b\\\", \\\"c\\\"),\\n                (\\\"c=1.3.6\\\",),\\n            ],\\n        )\\n\\n    def test_unsat_shortest_chain_4(self, env):\\n        env.repo_packages = [\\n            helpers.record(name=\\\"a\\\", depends=[\\\"py =3.7.1\\\"]),\\n            helpers.record(name=\\\"py_req_1\\\"),\\n            helpers.record(name=\\\"py_req_2\\\"),\\n            helpers.record(\\n                name=\\\"py\\\", version=\\\"3.7.1\\\", depends=[\\\"py_req_1\\\", \\\"py_req_2\\\"]\\n            ),\\n            helpers.record(\\n                name=\\\"py\\\", version=\\\"3.6.1\\\", depends=[\\\"py_req_1\\\", \\\"py_req_2\\\"]\\n            ),\\n        ]\\n        with pytest.raises(UnsatisfiableError) as exc_info:\\n            env.install(\\\"a\\\", \\\"py=3.6.1\\\")\\n        self.assert_unsatisfiable(\\n            exc_info,\\n            [\\n                (\\\"a\\\", \\\"py=3.7.1\\\"),\\n                (\\\"py=3.6.1\\\",),\\n            ],\\n        )\\n\\n    def test_unsat_chain(self, env):\\n        # a -> b -> c=1.x -> d=1.x\\n        # e      -> c=2.x -> d=2.x\\n        env.repo_packages = [\\n            helpers.record(name=\\\"a\\\", depends=[\\\"b\\\"]),\\n            helpers.record(name=\\\"b\\\", depends=[\\\"c >=1,<2\\\"]),\\n            helpers.record(name=\\\"c\\\", version=\\\"1.0\\\", depends=[\\\"d >=1,<2\\\"]),\\n            helpers.record(name=\\\"d\\\", version=\\\"1.0\\\"),\\n            helpers.record(name=\\\"e\\\", depends=[\\\"c >=2,<3\\\"]),\\n            helpers.record(name=\\\"c\\\", version=\\\"2.0\\\", depends=[\\\"d >=2,<3\\\"]),\\n            helpers.record(name=\\\"d\\\", version=\\\"2.0\\\"),\\n        ]\\n        with pytest.raises(UnsatisfiableError) as exc_info:\\n            env.install(\\\"a\\\", \\\"e\\\")\\n        self.assert_unsatisfiable(\\n            exc_info,\\n            [\\n                (\\\"a\\\", \\\"b\\\", \\\"c[version='>=1,<2']\\\"),\\n                (\\\"e\\\", \\\"c[version='>=2,<3']\\\"),\\n            ],\\n        )\\n\\n    def test_unsat_any_two_not_three(self, env):\\n        # can install any two of a, b and c but not all three\\n        env.repo_packages = [\\n            helpers.record(name=\\\"a\\\", version=\\\"1.0\\\", depends=[\\\"d >=1,<2\\\"]),\\n            helpers.record(name=\\\"a\\\", version=\\\"2.0\\\", depends=[\\\"d >=2,<3\\\"]),\\n            helpers.record(name=\\\"b\\\", version=\\\"1.0\\\", depends=[\\\"d >=1,<2\\\"]),\\n            helpers.record(name=\\\"b\\\", version=\\\"2.0\\\", depends=[\\\"d >=3,<4\\\"]),\\n            helpers.record(name=\\\"c\\\", version=\\\"1.0\\\", depends=[\\\"d >=2,<3\\\"]),\\n            helpers.record(name=\\\"c\\\", version=\\\"2.0\\\", depends=[\\\"d >=3,<4\\\"]),\\n            helpers.record(name=\\\"d\\\", version=\\\"1.0\\\"),\\n            helpers.record(name=\\\"d\\\", version=\\\"2.0\\\"),\\n            helpers.record(name=\\\"d\\\", version=\\\"3.0\\\"),\\n        ]\\n        # a and b can be installed\\n        installed = env.install(\\\"a\\\", \\\"b\\\", as_specs=True)\\n        assert any(k.name == \\\"a\\\" and k.version == \\\"1.0\\\" for k in installed)\\n        assert any(k.name == \\\"b\\\" and k.version == \\\"1.0\\\" for k in installed)\\n        # a and c can be installed\\n        installed = env.install(\\\"a\\\", \\\"c\\\", as_specs=True)\\n        assert any(k.name == \\\"a\\\" and k.version == \\\"2.0\\\" for k in installed)\\n        assert any(k.name == \\\"c\\\" and k.version == \\\"1.0\\\" for k in installed)\\n        # b and c can be installed\\n        installed = env.install(\\\"b\\\", \\\"c\\\", as_specs=True)\\n        assert any(k.name == \\\"b\\\" and k.version == \\\"2.0\\\" for k in installed)\\n        assert any(k.name == \\\"c\\\" and k.version == \\\"2.0\\\" for k in installed)\\n        # a, b and c cannot be installed\\n        with pytest.raises(UnsatisfiableError) as exc_info:\\n            env.install(\\\"a\\\", \\\"b\\\", \\\"c\\\")\\n        self.assert_unsatisfiable(\\n            exc_info,\\n            [\\n                (\\\"a\\\", \\\"d[version='>=1,<2|>=2,<3']\\\"),\\n                (\\\"b\\\", \\\"d[version='>=1,<2|>=3,<4']\\\"),\\n                (\\\"c\\\", \\\"d[version='>=2,<3|>=3,<4']\\\"),\\n            ],\\n        )\\n\\n    def test_unsat_expand_single(self, env):\\n        env.repo_packages = [\\n            helpers.record(name=\\\"a\\\", depends=[\\\"b\\\", \\\"c\\\"]),\\n            helpers.record(name=\\\"b\\\", depends=[\\\"d >=1,<2\\\"]),\\n            helpers.record(name=\\\"c\\\", depends=[\\\"d >=2,<3\\\"]),\\n            helpers.record(name=\\\"d\\\", version=\\\"1.0\\\"),\\n            helpers.record(name=\\\"d\\\", version=\\\"2.0\\\"),\\n        ]\\n        with pytest.raises(UnsatisfiableError) as exc_info:\\n            env.install(\\\"a\\\")\\n        self.assert_unsatisfiable(\\n            exc_info,\\n            [\\n                (\\\"b\\\", \\\"d[version='>=1,<2']\\\"),\\n                (\\\"c\\\", \\\"d[version='>=2,<3']\\\"),\\n            ],\\n        )\\n\\n    def test_unsat_missing_dep(self, env):\\n        env.repo_packages = [\\n            helpers.record(name=\\\"a\\\", depends=[\\\"b\\\", \\\"c\\\"]),\\n            helpers.record(name=\\\"b\\\", depends=[\\\"c >=2,<3\\\"]),\\n            helpers.record(name=\\\"c\\\", version=\\\"1.0\\\"),\\n        ]\\n        with pytest.raises(UnsatisfiableError) as exc_info:\\n            env.install(\\\"a\\\", \\\"b\\\")\\n        self.assert_unsatisfiable(\\n            exc_info,\\n            [\\n                (\\\"a\\\", \\\"b\\\"),\\n                (\\\"b\\\",),\\n            ],\\n        )\\n\\n    def test_nonexistent(self, env):\\n        with pytest.raises((ResolvePackageNotFound, PackagesNotFoundError)):\\n            env.install(\\\"notarealpackage 2.0*\\\")\\n        with pytest.raises((ResolvePackageNotFound, PackagesNotFoundError)):\\n            env.install(\\\"numpy 1.5\\\")\\n\\n    def test_timestamps_and_deps(self, env):\\n        env.repo_packages = index_packages(1) + [\\n            helpers.record(\\n                name=\\\"mypackage\\\",\\n                version=\\\"1.0\\\",\\n                build=\\\"hash12_0\\\",\\n                timestamp=1,\\n                depends=[\\\"libpng 1.2.*\\\"],\\n            ),\\n            helpers.record(\\n                name=\\\"mypackage\\\",\\n                version=\\\"1.0\\\",\\n                build=\\\"hash15_0\\\",\\n                timestamp=0,\\n                depends=[\\\"libpng 1.5.*\\\"],\\n            ),\\n        ]\\n        # libpng 1.2\\n        records_12 = env.install(\\\"libpng 1.2.*\\\", \\\"mypackage\\\")\\n        assert \\\"test::libpng-1.2.50-0\\\" in records_12\\n        assert \\\"test::mypackage-1.0-hash12_0\\\" in records_12\\n        # libpng 1.5\\n        records_15 = env.install(\\\"libpng 1.5.*\\\", \\\"mypackage\\\")\\n        assert \\\"test::libpng-1.5.13-1\\\" in records_15\\n        assert \\\"test::mypackage-1.0-hash15_0\\\" in records_15\\n        # this is testing that previously installed reqs are not disrupted\\n        # by newer timestamps. regression test of sorts for\\n        #  https://github.com/conda/conda/issues/6271\\n        assert (\\n            env.install(\\\"mypackage\\\", *env.install(\\\"libpng 1.2.*\\\", as_specs=True))\\n            == records_12\\n        )\\n        assert (\\n            env.install(\\\"mypackage\\\", *env.install(\\\"libpng 1.5.*\\\", as_specs=True))\\n            == records_15\\n        )\\n        # unspecified python version should maximize libpng (v1.5),\\n        # even though it has a lower timestamp\\n        assert env.install(\\\"mypackage\\\") == records_15\\n\\n    def test_nonexistent_deps(self, env):\\n        env.repo_packages = index_packages(1) + [\\n            helpers.record(\\n                name=\\\"mypackage\\\",\\n                version=\\\"1.0\\\",\\n                depends=[\\\"nose\\\", \\\"python 3.3*\\\", \\\"notarealpackage 2.0*\\\"],\\n            ),\\n            helpers.record(\\n                name=\\\"mypackage\\\",\\n                version=\\\"1.1\\\",\\n                depends=[\\\"nose\\\", \\\"python 3.3*\\\"],\\n            ),\\n            helpers.record(\\n                name=\\\"anotherpackage\\\",\\n                version=\\\"1.0\\\",\\n                depends=[\\\"nose\\\", \\\"mypackage 1.1\\\"],\\n            ),\\n            helpers.record(\\n                name=\\\"anotherpackage\\\",\\n                version=\\\"2.0\\\",\\n                depends=[\\\"nose\\\", \\\"mypackage\\\"],\\n            ),\\n        ]\\n        # XXX: missing find_matches and reduced_index\\n        assert env.install(\\\"mypackage\\\") == {\\n            \\\"test::mypackage-1.1-0\\\",\\n            \\\"test::nose-1.3.0-py33_0\\\",\\n            \\\"test::openssl-1.0.1c-0\\\",\\n            \\\"test::python-3.3.2-0\\\",\\n            \\\"test::readline-6.2-0\\\",\\n            \\\"test::sqlite-3.7.13-0\\\",\\n            \\\"test::system-5.8-1\\\",\\n            \\\"test::tk-8.5.13-0\\\",\\n            \\\"test::zlib-1.2.7-0\\\",\\n            \\\"test::distribute-0.6.36-py33_1\\\",\\n            \\\"test::pip-1.3.1-py33_1\\\",\\n        }\\n        assert env.install(\\\"anotherpackage 1.0\\\") == {\\n            \\\"test::anotherpackage-1.0-0\\\",\\n            \\\"test::mypackage-1.1-0\\\",\\n            \\\"test::nose-1.3.0-py33_0\\\",\\n            \\\"test::openssl-1.0.1c-0\\\",\\n            \\\"test::python-3.3.2-0\\\",\\n            \\\"test::readline-6.2-0\\\",\\n            \\\"test::sqlite-3.7.13-0\\\",\\n            \\\"test::system-5.8-1\\\",\\n            \\\"test::tk-8.5.13-0\\\",\\n            \\\"test::zlib-1.2.7-0\\\",\\n            \\\"test::distribute-0.6.36-py33_1\\\",\\n            \\\"test::pip-1.3.1-py33_1\\\",\\n        }\\n        assert env.install(\\\"anotherpackage\\\") == {\\n            \\\"test::anotherpackage-2.0-0\\\",\\n            \\\"test::mypackage-1.1-0\\\",\\n            \\\"test::nose-1.3.0-py33_0\\\",\\n            \\\"test::openssl-1.0.1c-0\\\",\\n            \\\"test::python-3.3.2-0\\\",\\n            \\\"test::readline-6.2-0\\\",\\n            \\\"test::sqlite-3.7.13-0\\\",\\n            \\\"test::system-5.8-1\\\",\\n            \\\"test::tk-8.5.13-0\\\",\\n            \\\"test::zlib-1.2.7-0\\\",\\n            \\\"test::distribute-0.6.36-py33_1\\\",\\n            \\\"test::pip-1.3.1-py33_1\\\",\\n        }\\n\\n        # This time, the latest version is messed up\\n        env.repo_packages = index_packages(1) + [\\n            helpers.record(\\n                name=\\\"mypackage\\\",\\n                version=\\\"1.0\\\",\\n                depends=[\\\"nose\\\", \\\"python 3.3*\\\"],\\n            ),\\n            helpers.record(\\n                name=\\\"mypackage\\\",\\n                version=\\\"1.1\\\",\\n                depends=[\\\"nose\\\", \\\"python 3.3*\\\", \\\"notarealpackage 2.0*\\\"],\\n            ),\\n            helpers.record(\\n                name=\\\"anotherpackage\\\",\\n                version=\\\"1.0\\\",\\n                depends=[\\\"nose\\\", \\\"mypackage 1.0\\\"],\\n            ),\\n            helpers.record(\\n                name=\\\"anotherpackage\\\",\\n                version=\\\"2.0\\\",\\n                depends=[\\\"nose\\\", \\\"mypackage\\\"],\\n            ),\\n        ]\\n        # XXX: missing find_matches and reduced_index\\n        assert env.install(\\\"mypackage\\\") == {\\n            \\\"test::mypackage-1.0-0\\\",\\n            \\\"test::nose-1.3.0-py33_0\\\",\\n            \\\"test::openssl-1.0.1c-0\\\",\\n            \\\"test::python-3.3.2-0\\\",\\n            \\\"test::readline-6.2-0\\\",\\n            \\\"test::sqlite-3.7.13-0\\\",\\n            \\\"test::system-5.8-1\\\",\\n            \\\"test::tk-8.5.13-0\\\",\\n            \\\"test::zlib-1.2.7-0\\\",\\n            \\\"test::distribute-0.6.36-py33_1\\\",\\n            \\\"test::pip-1.3.1-py33_1\\\",\\n        }\\n        # TODO: We need UnsatisfiableError here because mamba does not\\n        # have more granular exceptions yet.\\n        with pytest.raises((ResolvePackageNotFound, UnsatisfiableError)):\\n            env.install(\\\"mypackage 1.1\\\")\\n        assert env.install(\\\"anotherpackage 1.0\\\") == {\\n            \\\"test::anotherpackage-1.0-0\\\",\\n            \\\"test::mypackage-1.0-0\\\",\\n            \\\"test::nose-1.3.0-py33_0\\\",\\n            \\\"test::openssl-1.0.1c-0\\\",\\n            \\\"test::python-3.3.2-0\\\",\\n            \\\"test::readline-6.2-0\\\",\\n            \\\"test::sqlite-3.7.13-0\\\",\\n            \\\"test::system-5.8-1\\\",\\n            \\\"test::tk-8.5.13-0\\\",\\n            \\\"test::zlib-1.2.7-0\\\",\\n            \\\"test::distribute-0.6.36-py33_1\\\",\\n            \\\"test::pip-1.3.1-py33_1\\\",\\n        }\\n\\n        # If recursive checking is working correctly, this will give\\n        # anotherpackage 2.0, not anotherpackage 1.0\\n        assert env.install(\\\"anotherpackage\\\") == {\\n            \\\"test::anotherpackage-2.0-0\\\",\\n            \\\"test::mypackage-1.0-0\\\",\\n            \\\"test::nose-1.3.0-py33_0\\\",\\n            \\\"test::openssl-1.0.1c-0\\\",\\n            \\\"test::python-3.3.2-0\\\",\\n            \\\"test::readline-6.2-0\\\",\\n            \\\"test::sqlite-3.7.13-0\\\",\\n            \\\"test::system-5.8-1\\\",\\n            \\\"test::tk-8.5.13-0\\\",\\n            \\\"test::zlib-1.2.7-0\\\",\\n            \\\"test::distribute-0.6.36-py33_1\\\",\\n            \\\"test::pip-1.3.1-py33_1\\\",\\n        }\\n\\n    def test_install_package_with_feature(self, env):\\n        env.repo_packages = index_packages(1) + [\\n            helpers.record(\\n                name=\\\"mypackage\\\",\\n                version=\\\"1.0\\\",\\n                depends=[\\\"python 3.3*\\\"],\\n                features=\\\"feature\\\",\\n            ),\\n            helpers.record(\\n                name=\\\"feature\\\",\\n                version=\\\"1.0\\\",\\n                depends=[\\\"python 3.3*\\\"],\\n                track_features=\\\"feature\\\",\\n            ),\\n        ]\\n        # should not raise\\n        env.install(\\\"mypackage\\\", \\\"feature 1.0\\\")\\n\\n    def test_unintentional_feature_downgrade(self, env):\\n        # See https://github.com/conda/conda/issues/6765\\n        # With the bug in place, this bad build of scipy\\n        # will be selected for install instead of a later\\n        # build of scipy 0.11.0.\\n        good_rec_match = MatchSpec(\\\"channel-1::scipy==0.11.0=np17py33_3\\\")\\n        good_rec = next(\\n            prec for prec in index_packages(1) if good_rec_match.match(prec)\\n        )\\n        bad_deps = tuple(d for d in good_rec.depends if not d.startswith(\\\"numpy\\\"))\\n        bad_rec = PackageRecord.from_objects(\\n            good_rec,\\n            channel=\\\"test\\\",\\n            build=good_rec.build.replace(\\\"_3\\\", \\\"_x0\\\"),\\n            build_number=0,\\n            depends=bad_deps,\\n            fn=good_rec.fn.replace(\\\"_3\\\", \\\"_x0\\\"),\\n            url=good_rec.url.replace(\\\"_3\\\", \\\"_x0\\\"),\\n        )\\n\\n        env.repo_packages = index_packages(1) + [bad_rec]\\n        records = env.install(\\\"scipy 0.11.0\\\")\\n        assert \\\"test::scipy-0.11.0-np17py33_x0\\\" not in records\\n        assert \\\"test::scipy-0.11.0-np17py33_3\\\" in records\\n\\n    def test_circular_dependencies(self, env):\\n        env.repo_packages = index_packages(1) + [\\n            helpers.record(\\n                name=\\\"package1\\\",\\n                depends=[\\\"package2\\\"],\\n            ),\\n            helpers.record(\\n                name=\\\"package2\\\",\\n                depends=[\\\"package1\\\"],\\n            ),\\n        ]\\n        assert (\\n            env.install(\\\"package1\\\", \\\"package2\\\")\\n            == env.install(\\\"package1\\\")\\n            == env.install(\\\"package2\\\")\\n        )\\n\\n    def test_irrational_version(self, env):\\n        env.repo_packages = index_packages(1)\\n        assert env.install(\\\"pytz 2012d\\\", \\\"python 3*\\\") == {\\n            \\\"test::distribute-0.6.36-py33_1\\\",\\n            \\\"test::openssl-1.0.1c-0\\\",\\n            \\\"test::pip-1.3.1-py33_1\\\",\\n            \\\"test::python-3.3.2-0\\\",\\n            \\\"test::pytz-2012d-py33_0\\\",\\n            \\\"test::readline-6.2-0\\\",\\n            \\\"test::sqlite-3.7.13-0\\\",\\n            \\\"test::system-5.8-1\\\",\\n            \\\"test::tk-8.5.13-0\\\",\\n            \\\"test::zlib-1.2.7-0\\\",\\n        }\\n\\n    def test_no_features(self, env):\\n        env.repo_packages = index_packages(1)\\n\\n        assert env.install(\\\"python 2.6*\\\", \\\"numpy 1.6*\\\", \\\"scipy 0.11*\\\") == {\\n            \\\"test::distribute-0.6.36-py26_1\\\",\\n            \\\"test::numpy-1.6.2-py26_4\\\",\\n            \\\"test::openssl-1.0.1c-0\\\",\\n            \\\"test::pip-1.3.1-py26_1\\\",\\n            \\\"test::python-2.6.8-6\\\",\\n            \\\"test::readline-6.2-0\\\",\\n            \\\"test::scipy-0.11.0-np16py26_3\\\",\\n            \\\"test::sqlite-3.7.13-0\\\",\\n            \\\"test::system-5.8-1\\\",\\n            \\\"test::tk-8.5.13-0\\\",\\n            \\\"test::zlib-1.2.7-0\\\",\\n        }\\n        assert env.install(\\n            \\\"python 2.6*\\\", \\\"numpy 1.6*\\\", \\\"scipy 0.11*\\\", MatchSpec(track_features=\\\"mkl\\\")\\n        ) == {\\n            \\\"test::distribute-0.6.36-py26_1\\\",\\n            \\\"test::mkl-rt-11.0-p0\\\",\\n            \\\"test::numpy-1.6.2-py26_p4\\\",\\n            \\\"test::openssl-1.0.1c-0\\\",\\n            \\\"test::pip-1.3.1-py26_1\\\",\\n            \\\"test::python-2.6.8-6\\\",\\n            \\\"test::readline-6.2-0\\\",\\n            \\\"test::scipy-0.11.0-np16py26_p3\\\",\\n            \\\"test::sqlite-3.7.13-0\\\",\\n            \\\"test::system-5.8-1\\\",\\n            \\\"test::tk-8.5.13-0\\\",\\n            \\\"test::zlib-1.2.7-0\\\",\\n        }\\n\\n        env.repo_packages += [\\n            helpers.record(\\n                name=\\\"pandas\\\",\\n                version=\\\"0.12.0\\\",\\n                build=\\\"np16py27_0\\\",\\n                depends=[\\n                    \\\"dateutil\\\",\\n                    \\\"numpy 1.6*\\\",\\n                    \\\"python 2.7*\\\",\\n                    \\\"pytz\\\",\\n                ],\\n            ),\\n            helpers.record(\\n                name=\\\"numpy\\\",\\n                version=\\\"1.6.2\\\",\\n                build=\\\"py27_p5\\\",\\n                build_number=0,\\n                depends=[\\n                    \\\"mkl-rt 11.0\\\",\\n                    \\\"python 2.7\\\",\\n                ],\\n                features=\\\"mkl\\\",\\n            ),\\n        ]\\n        assert env.install(\\\"pandas 0.12.0 np16py27_0\\\", \\\"python 2.7*\\\") == {\\n            \\\"test::dateutil-2.1-py27_1\\\",\\n            \\\"test::distribute-0.6.36-py27_1\\\",\\n            \\\"test::numpy-1.6.2-py27_4\\\",\\n            \\\"test::openssl-1.0.1c-0\\\",\\n            \\\"test::pandas-0.12.0-np16py27_0\\\",\\n            \\\"test::pip-1.3.1-py27_1\\\",\\n            \\\"test::python-2.7.5-0\\\",\\n            \\\"test::pytz-2013b-py27_0\\\",\\n            \\\"test::readline-6.2-0\\\",\\n            \\\"test::six-1.3.0-py27_0\\\",\\n            \\\"test::sqlite-3.7.13-0\\\",\\n            \\\"test::system-5.8-1\\\",\\n            \\\"test::tk-8.5.13-0\\\",\\n            \\\"test::zlib-1.2.7-0\\\",\\n        }\\n        assert env.install(\\n            \\\"pandas 0.12.0 np16py27_0\\\", \\\"python 2.7*\\\", MatchSpec(track_features=\\\"mkl\\\")\\n        ) == {\\n            \\\"test::dateutil-2.1-py27_1\\\",\\n            \\\"test::distribute-0.6.36-py27_1\\\",\\n            \\\"test::mkl-rt-11.0-p0\\\",\\n            \\\"test::numpy-1.6.2-py27_p4\\\",\\n            \\\"test::openssl-1.0.1c-0\\\",\\n            \\\"test::pandas-0.12.0-np16py27_0\\\",\\n            \\\"test::pip-1.3.1-py27_1\\\",\\n            \\\"test::python-2.7.5-0\\\",\\n            \\\"test::pytz-2013b-py27_0\\\",\\n            \\\"test::readline-6.2-0\\\",\\n            \\\"test::six-1.3.0-py27_0\\\",\\n            \\\"test::sqlite-3.7.13-0\\\",\\n            \\\"test::system-5.8-1\\\",\\n            \\\"test::tk-8.5.13-0\\\",\\n            \\\"test::zlib-1.2.7-0\\\",\\n        }\\n\\n    @pytest.mark.xfail(reason=\\\"CONDA_CHANNEL_PRIORITY does not seem to have any effect\\\")\\n    def test_channel_priority_1(self, monkeypatch, env):\\n        # XXX: Test is skipped because CONDA_CHANNEL_PRIORITY does not seems to\\n        #      have any effect. I have also tried conda.common.io.env_var like\\n        #      the other tests but no luck.\\n        env.repo_packages = {}\\n        env.repo_packages[\\\"channel-A\\\"] = []\\n        env.repo_packages[\\\"channel-1\\\"] = index_packages(1)\\n\\n        pandas_0 = self.find_package(\\n            channel=\\\"channel-1\\\",\\n            name=\\\"pandas\\\",\\n            version=\\\"0.10.1\\\",\\n            build=\\\"np17py27_0\\\",\\n        )\\n        env.repo_packages[\\\"channel-A\\\"].append(pandas_0)\\n\\n        # channel-1 has pandas np17py27_1, channel-A only has np17py27_0\\n        # when priority is set, it channel-A should take precedence and\\n        # np17py27_0 be installed, otherwise np17py27_1 should be installed as\\n        # it has a higher build version\\n        monkeypatch.setenv(\\\"CONDA_CHANNEL_PRIORITY\\\", \\\"True\\\")\\n        assert \\\"channel-A::pandas-0.11.0-np16py27_0\\\" in env.install(\\n            \\\"pandas\\\", \\\"python 2.7*\\\", \\\"numpy 1.6*\\\"\\n        )\\n        monkeypatch.setenv(\\\"CONDA_CHANNEL_PRIORITY\\\", \\\"False\\\")\\n        assert \\\"channel-1::pandas-0.11.0-np16py27_1\\\" in env.install(\\n            \\\"pandas\\\", \\\"python 2.7*\\\", \\\"numpy 1.6*\\\"\\n        )\\n        # now lets revert the channels\\n        env.repo_packages = dict(reversed(env.repo_packages.items()))\\n        monkeypatch.setenv(\\\"CONDA_CHANNEL_PRIORITY\\\", \\\"True\\\")\\n        assert \\\"channel-1::pandas-0.11.0-np16py27_1\\\" in env.install(\\n            \\\"pandas\\\", \\\"python 2.7*\\\", \\\"numpy 1.6*\\\"\\n        )\\n\\n    @pytest.mark.xfail(reason=\\\"CONDA_CHANNEL_PRIORITY does not seem to have any effect\\\")\\n    def test_unsat_channel_priority(self, monkeypatch, env):\\n        # XXX: Test is skipped because CONDA_CHANNEL_PRIORITY does not seems to\\n        #      have any effect. I have also tried conda.common.io.env_var like\\n        #      the other tests but no luck.\\n        env.repo_packages = {}\\n        # higher priority\\n        env.repo_packages[\\\"channel-1\\\"] = [\\n            helpers.record(\\n                name=\\\"a\\\",\\n                version=\\\"1.0\\\",\\n                depends=[\\\"c\\\"],\\n            ),\\n            helpers.record(\\n                name=\\\"b\\\",\\n                version=\\\"1.0\\\",\\n                depends=[\\\"c >=2,<3\\\"],\\n            ),\\n            helpers.record(\\n                name=\\\"c\\\",\\n                version=\\\"1.0\\\",\\n            ),\\n        ]\\n        # lower priority, missing c 2.0\\n        env.repo_packages[\\\"channel-2\\\"] = [\\n            helpers.record(\\n                name=\\\"a\\\",\\n                version=\\\"2.0\\\",\\n                depends=[\\\"c\\\"],\\n            ),\\n            helpers.record(\\n                name=\\\"b\\\",\\n                version=\\\"2.0\\\",\\n                depends=[\\\"c >=2,<3\\\"],\\n            ),\\n            helpers.record(\\n                name=\\\"c\\\",\\n                version=\\\"1.0\\\",\\n            ),\\n            helpers.record(\\n                name=\\\"c\\\",\\n                version=\\\"2.0\\\",\\n            ),\\n        ]\\n\\n        monkeypatch.setenv(\\\"CONDA_CHANNEL_PRIORITY\\\", \\\"True\\\")\\n        records = env.install(\\\"a\\\", \\\"b\\\", as_specs=True)\\n        # channel-1 a and b packages (1.0) installed\\n        assert any(k.name == \\\"a\\\" and k.version == \\\"1.0\\\" for k in records)\\n        assert any(k.name == \\\"b\\\" and k.version == \\\"1.0\\\" for k in records)\\n\\n        monkeypatch.setenv(\\\"CONDA_CHANNEL_PRIORITY\\\", \\\"False\\\")\\n        records = env.install(\\\"a\\\", \\\"b\\\", as_specs=True)\\n        # no channel priority, largest version of a and b (2.0) installed\\n        assert any(k.name == \\\"a\\\" and k.version == \\\"2.0\\\" for k in records)\\n        assert any(k.name == \\\"b\\\" and k.version == \\\"2.0\\\" for k in records)\\n\\n        monkeypatch.setenv(\\\"CONDA_CHANNEL_PRIORITY\\\", \\\"True\\\")\\n        with pytest.raises(UnsatisfiableError) as exc_info:\\n            env.install(\\\"a\\\", \\\"b\\\")\\n        self.assert_unsatisfiable(exc_info, [(\\\"b\\\", \\\"c[version='>=2,<3']\\\")])\\n\\n    @pytest.mark.xfail(\\n        reason=\\\"There is some weird global state making \\\"\\n        \\\"this test fail when the whole test suite is run\\\"\\n    )\\n    def test_remove(self, env):\\n        env.repo_packages = index_packages(1)\\n        records = env.install(\\\"pandas\\\", \\\"python 2.7*\\\", as_specs=True)\\n        assert package_string_set(records) == {\\n            \\\"test::dateutil-2.1-py27_1\\\",\\n            \\\"test::distribute-0.6.36-py27_1\\\",\\n            \\\"test::numpy-1.7.1-py27_0\\\",\\n            \\\"test::openssl-1.0.1c-0\\\",\\n            \\\"test::pandas-0.11.0-np17py27_1\\\",\\n            \\\"test::pip-1.3.1-py27_1\\\",\\n            \\\"test::python-2.7.5-0\\\",\\n            \\\"test::pytz-2013b-py27_0\\\",\\n            \\\"test::readline-6.2-0\\\",\\n            \\\"test::scipy-0.12.0-np17py27_0\\\",\\n            \\\"test::six-1.3.0-py27_0\\\",\\n            \\\"test::sqlite-3.7.13-0\\\",\\n            \\\"test::system-5.8-1\\\",\\n            \\\"test::tk-8.5.13-0\\\",\\n            \\\"test::zlib-1.2.7-0\\\",\\n        }\\n\\n        env.installed_packages = records\\n        assert env.remove(\\\"pandas\\\") == {\\n            \\\"test::dateutil-2.1-py27_1\\\",\\n            \\\"test::distribute-0.6.36-py27_1\\\",\\n            \\\"test::numpy-1.7.1-py27_0\\\",\\n            \\\"test::openssl-1.0.1c-0\\\",\\n            \\\"test::pip-1.3.1-py27_1\\\",\\n            \\\"test::python-2.7.5-0\\\",\\n            \\\"test::pytz-2013b-py27_0\\\",\\n            \\\"test::readline-6.2-0\\\",\\n            \\\"test::scipy-0.12.0-np17py27_0\\\",\\n            \\\"test::six-1.3.0-py27_0\\\",\\n            \\\"test::sqlite-3.7.13-0\\\",\\n            \\\"test::system-5.8-1\\\",\\n            \\\"test::tk-8.5.13-0\\\",\\n            \\\"test::zlib-1.2.7-0\\\",\\n        }\\n        assert env.remove(\\\"numpy\\\") == {\\n            \\\"test::dateutil-2.1-py27_1\\\",\\n            \\\"test::distribute-0.6.36-py27_1\\\",\\n            \\\"test::openssl-1.0.1c-0\\\",\\n            \\\"test::pip-1.3.1-py27_1\\\",\\n            \\\"test::python-2.7.5-0\\\",\\n            \\\"test::pytz-2013b-py27_0\\\",\\n            \\\"test::readline-6.2-0\\\",\\n            \\\"test::six-1.3.0-py27_0\\\",\\n            \\\"test::sqlite-3.7.13-0\\\",\\n            \\\"test::system-5.8-1\\\",\\n            \\\"test::tk-8.5.13-0\\\",\\n            \\\"test::zlib-1.2.7-0\\\",\\n        }\\n\\n    def test_surplus_features_1(self, env):\\n        env.repo_packages += [\\n            helpers.record(\\n                name=\\\"feature\\\",\\n                track_features=\\\"feature\\\",\\n            ),\\n            helpers.record(\\n                name=\\\"package1\\\",\\n                features=\\\"feature\\\",\\n            ),\\n            helpers.record(\\n                name=\\\"package2\\\",\\n                version=\\\"1.0\\\",\\n                features=\\\"feature\\\",\\n                depends=[\\\"package1\\\"],\\n            ),\\n            helpers.record(\\n                name=\\\"package2\\\",\\n                version=\\\"2.0\\\",\\n                features=\\\"feature\\\",\\n            ),\\n        ]\\n        assert env.install(\\\"package2\\\", \\\"feature\\\") == {\\n            \\\"test::package2-2.0-0\\\",\\n            \\\"test::feature-1.0-0\\\",\\n        }\\n\\n    def test_surplus_features_2(self, env):\\n        env.repo_packages += [\\n            helpers.record(\\n                name=\\\"feature\\\",\\n                track_features=\\\"feature\\\",\\n            ),\\n            helpers.record(\\n                name=\\\"package1\\\",\\n                features=\\\"feature\\\",\\n            ),\\n            helpers.record(\\n                name=\\\"package2\\\",\\n                version=\\\"1.0\\\",\\n                build_number=0,\\n                features=\\\"feature\\\",\\n                depends=[\\\"package1\\\"],\\n            ),\\n            helpers.record(\\n                name=\\\"package2\\\",\\n                version=\\\"1.0\\\",\\n                build_number=1,\\n                features=\\\"feature\\\",\\n            ),\\n        ]\\n        assert env.install(\\\"package2\\\", \\\"feature\\\") == {\\n            \\\"test::package2-1.0-0\\\",\\n            \\\"test::feature-1.0-0\\\",\\n        }\\n\\n    def test_get_reduced_index_broadening_with_unsatisfiable_early_dep(self, env):\\n        # Test that spec broadening reduction doesn't kill valid solutions\\n        #    In other words, the order of packages in the index should not affect the\\n        #    overall result of the reduced index.\\n        # see discussion at https://github.com/conda/conda/pull/8117#discussion_r249249815\\n        env.repo_packages += [\\n            helpers.record(\\n                name=\\\"a\\\",\\n                version=\\\"1.0\\\",\\n                # not satisfiable. This record should come first, so that its c==2\\n                # constraint tries to mess up the inclusion of the c record below,\\n                # which should be included as part of b's deps, but which is\\n                # broader than this dep.\\n                depends=[\\\"b\\\", \\\"c==2\\\"],\\n            ),\\n            helpers.record(\\n                name=\\\"a\\\",\\n                version=\\\"2.0\\\",\\n                depends=[\\\"b\\\"],\\n            ),\\n            helpers.record(\\n                name=\\\"b\\\",\\n                depends=[\\\"c\\\"],\\n            ),\\n            helpers.record(\\n                name=\\\"c\\\",\\n            ),\\n        ]\\n        assert env.install(\\\"a\\\") == {\\n            \\\"test::a-2.0-0\\\",\\n            \\\"test::b-1.0-0\\\",\\n            \\\"test::c-1.0-0\\\",\\n        }\\n\\n    def test_get_reduced_index_broadening_preferred_solution(self, env):\\n        # test that order of index reduction does not eliminate what should be a preferred solution\\n        #    https://github.com/conda/conda/pull/8117#discussion_r249216068\\n        env.repo_packages += [\\n            helpers.record(\\n                name=\\\"top\\\",\\n                version=\\\"1.0\\\",\\n                # this is the first processed record, and imposes a broadening constraint on bottom\\n                #    if things are overly restricted, we'll end up with bottom 1.5 in our solution\\n                #    instead of the preferred (latest) 2.5\\n                depends=[\\\"middle\\\", \\\"bottom==1.5\\\"],\\n            ),\\n            helpers.record(\\n                name=\\\"top\\\",\\n                version=\\\"2.0\\\",\\n                depends=[\\\"middle\\\"],\\n            ),\\n            helpers.record(\\n                name=\\\"middle\\\",\\n                depends=[\\\"bottom\\\"],\\n            ),\\n            helpers.record(\\n                name=\\\"bottom\\\",\\n                version=\\\"1.5\\\",\\n            ),\\n            helpers.record(\\n                name=\\\"bottom\\\",\\n                version=\\\"2.5\\\",\\n            ),\\n        ]\\n        for record in env.install(\\\"top\\\", as_specs=True):\\n            if record.name == \\\"top\\\":\\n                assert (\\n                    record.version == \\\"2.0\\\"\\n                ), f\\\"top version should be 2.0, but is {record.version}\\\"\\n            elif record.name == \\\"bottom\\\":\\n                assert (\\n                    record.version == \\\"2.5\\\"\\n                ), f\\\"bottom version should be 2.5, but is {record.version}\\\"\\n\\n    def test_arch_preferred_over_noarch_when_otherwise_equal(self, env):\\n        env.repo_packages += [\\n            helpers.record(\\n                name=\\\"package1\\\",\\n                subdir=\\\"noarch\\\",\\n            ),\\n            helpers.record(\\n                name=\\\"package1\\\",\\n            ),\\n        ]\\n        records = env.install(\\\"package1\\\", as_specs=True)\\n        assert len(records) == 1\\n        assert records[0].subdir == context.subdir\\n\\n    def test_noarch_preferred_over_arch_when_version_greater(self, env):\\n        env.repo_packages += [\\n            helpers.record(\\n                name=\\\"package1\\\",\\n                version=\\\"2.0\\\",\\n                subdir=\\\"noarch\\\",\\n            ),\\n            helpers.record(\\n                name=\\\"package1\\\",\\n                version=\\\"1.0\\\",\\n            ),\\n        ]\\n        records = env.install(\\\"package1\\\", as_specs=True)\\n        assert len(records) == 1\\n        assert records[0].subdir == \\\"noarch\\\"\\n\\n    def test_noarch_preferred_over_arch_when_version_greater_dep(self, env):\\n        env.repo_packages += [\\n            helpers.record(\\n                name=\\\"package1\\\",\\n                version=\\\"1.0\\\",\\n            ),\\n            helpers.record(\\n                name=\\\"package1\\\",\\n                version=\\\"2.0\\\",\\n                subdir=\\\"noarch\\\",\\n            ),\\n            helpers.record(\\n                name=\\\"package2\\\",\\n                depends=[\\\"package1\\\"],\\n            ),\\n        ]\\n        records = env.install(\\\"package2\\\", as_specs=True)\\n        package1 = self.find_package_in_list(records, name=\\\"package1\\\")\\n        assert package1.subdir == \\\"noarch\\\"\\n\\n    def test_noarch_preferred_over_arch_when_build_greater(self, env):\\n        env.repo_packages += [\\n            helpers.record(\\n                name=\\\"package1\\\",\\n                build_number=0,\\n            ),\\n            helpers.record(\\n                name=\\\"package1\\\",\\n                build_number=1,\\n                subdir=\\\"noarch\\\",\\n            ),\\n        ]\\n        records = env.install(\\\"package1\\\", as_specs=True)\\n        assert len(records) == 1\\n        assert records[0].subdir == \\\"noarch\\\"\\n\\n    def test_noarch_preferred_over_arch_when_build_greater_dep(self, env):\\n        env.repo_packages += [\\n            helpers.record(\\n                name=\\\"package1\\\",\\n                build_number=0,\\n            ),\\n            helpers.record(\\n                name=\\\"package1\\\",\\n                build_number=1,\\n                subdir=\\\"noarch\\\",\\n            ),\\n            helpers.record(\\n                name=\\\"package2\\\",\\n                depends=[\\\"package1\\\"],\\n            ),\\n        ]\\n        records = env.install(\\\"package2\\\", as_specs=True)\\n        package1 = self.find_package_in_list(records, name=\\\"package1\\\")\\n        assert package1.subdir == \\\"noarch\\\"\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Extends unittest.TestCase to include select pytest fixtures.\\\"\\\"\\\"\\n\\nimport unittest\\n\\nimport pytest\\n\\n\\nclass BaseTestCase(unittest.TestCase):\\n    fixture_names = (\\\"tmpdir\\\",)\\n\\n    @pytest.fixture(autouse=True)\\n    def auto_injector_fixture(self, request):\\n        names = self.fixture_names\\n        for name in names:\\n            setattr(self, name, request.getfixturevalue(name))\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Collection of helper functions used in conda tests.\\\"\\\"\\\"\\n\\nimport json\\nimport os\\nfrom contextlib import contextmanager\\nfrom functools import lru_cache\\nfrom os.path import abspath, dirname, join\\nfrom pathlib import Path\\nfrom tempfile import gettempdir, mkdtemp\\nfrom unittest.mock import patch\\nfrom uuid import uuid4\\n\\nimport pytest\\n\\nfrom ..base.context import conda_tests_ctxt_mgmt_def_pol, context\\nfrom ..common.io import captured as common_io_captured\\nfrom ..common.io import env_var\\nfrom ..core.prefix_data import PrefixData\\nfrom ..core.subdir_data import SubdirData, make_feature_record\\nfrom ..deprecations import deprecated\\nfrom ..gateways.disk.delete import rm_rf\\nfrom ..gateways.disk.read import lexists\\nfrom ..history import History\\nfrom ..models.channel import Channel\\nfrom ..models.records import PackageRecord, PrefixRecord\\nfrom ..resolve import Resolve\\n\\n# The default value will only work if we have installed conda in development mode!\\nTEST_DATA_DIR = os.environ.get(\\n    \\\"CONDA_TEST_DATA_DIR\\\", abspath(join(dirname(__file__), \\\"..\\\", \\\"..\\\", \\\"tests\\\", \\\"data\\\"))\\n)\\nCHANNEL_DIR = CHANNEL_DIR_V1 = abspath(join(TEST_DATA_DIR, \\\"conda_format_repo\\\"))\\nCHANNEL_DIR_V2 = abspath(join(TEST_DATA_DIR, \\\"base_url_channel\\\"))\\nEXPORTED_CHANNELS_DIR = mkdtemp(suffix=\\\"-test-conda-channels\\\")\\n\\n\\nexpected_error_prefix = \\\"Using Anaconda Cloud api site https://api.anaconda.org\\\"\\n\\n\\ndef strip_expected(stderr):\\n    if expected_error_prefix and stderr.startswith(expected_error_prefix):\\n        stderr = stderr[len(expected_error_prefix) :].lstrip()  # noqa\\n    return stderr\\n\\n\\ndef raises(exception, func, string=None):\\n    try:\\n        a = func()\\n    except exception as e:\\n        if string:\\n            assert string in e.args[0]\\n        print(e)\\n        return True\\n    raise Exception(f\\\"did not raise, gave {a}\\\")\\n\\n\\n@contextmanager\\ndef captured(disallow_stderr=True):\\n    # same as common.io.captured but raises Exception if unexpected output was written to stderr\\n    try:\\n        with common_io_captured() as c:\\n            yield c\\n    finally:\\n        c.stderr = strip_expected(c.stderr)\\n        if disallow_stderr and c.stderr:\\n            raise Exception(f\\\"Got stderr output: {c.stderr}\\\")\\n\\n\\n@deprecated(\\n    \\\"24.3\\\",\\n    \\\"24.9\\\",\\n    addendum=\\\"Use `mocker.patch('conda.base.context.Context.active_prefix')` instead.\\\",\\n)\\n@contextmanager\\ndef set_active_prefix(prefix: str) -> None:\\n    old_prefix = os.environ[\\\"CONDA_PREFIX\\\"]\\n\\n    try:\\n        os.environ[\\\"CONDA_PREFIX\\\"] = prefix\\n        yield\\n    finally:\\n        os.environ[\\\"CONDA_PREFIX\\\"] = old_prefix\\n\\n\\ndef assert_equals(a, b, output=\\\"\\\"):\\n    output = f\\\"{a.lower()!r} != {b.lower()!r}\\\" + \\\"\\\\n\\\\n\\\" + output\\n    assert a.lower() == b.lower(), output\\n\\n\\ndef assert_not_in(a, b, output=\\\"\\\"):\\n    assert (\\n        a.lower() not in b.lower()\\n    ), f\\\"{output} {a.lower()!r} should not be found in {b.lower()!r}\\\"\\n\\n\\ndef assert_in(a, b, output=\\\"\\\"):\\n    assert (\\n        a.lower() in b.lower()\\n    ), f\\\"{output} {a.lower()!r} cannot be found in {b.lower()!r}\\\"\\n\\n\\ndef add_subdir(dist_string):\\n    channel_str, package_str = dist_string.split(\\\"::\\\")\\n    channel_str = channel_str + \\\"/\\\" + context.subdir\\n    return \\\"::\\\".join([channel_str, package_str])\\n\\n\\ndef add_subdir_to_iter(iterable):\\n    if isinstance(iterable, dict):\\n        return {add_subdir(k): v for k, v in iterable.items()}\\n    elif isinstance(iterable, list):\\n        return list(map(add_subdir, iterable))\\n    elif isinstance(iterable, set):\\n        return set(map(add_subdir, iterable))\\n    elif isinstance(iterable, tuple):\\n        return tuple(map(add_subdir, iterable))\\n    else:\\n        raise Exception(\\\"Unable to add subdir to object of unknown type.\\\")\\n\\n\\n@contextmanager\\ndef tempdir():\\n    tempdirdir = gettempdir()\\n    dirname = str(uuid4())[:8]\\n    prefix = join(tempdirdir, dirname)\\n    try:\\n        os.makedirs(prefix)\\n        yield prefix\\n    finally:\\n        if lexists(prefix):\\n            rm_rf(prefix)\\n\\n\\ndef supplement_index_with_repodata(index, repodata, channel, priority):\\n    repodata_info = repodata[\\\"info\\\"]\\n    arch = repodata_info.get(\\\"arch\\\")\\n    platform = repodata_info.get(\\\"platform\\\")\\n    subdir = repodata_info.get(\\\"subdir\\\")\\n    if not subdir:\\n        subdir = \\\"{}-{}\\\".format(repodata_info[\\\"platform\\\"], repodata_info[\\\"arch\\\"])\\n    auth = channel.auth\\n    for fn, info in repodata[\\\"packages\\\"].items():\\n        rec = PackageRecord.from_objects(\\n            info,\\n            fn=fn,\\n            arch=arch,\\n            platform=platform,\\n            channel=channel,\\n            subdir=subdir,\\n            # schannel=schannel,\\n            priority=priority,\\n            # url=join_url(channel_url, fn),\\n            auth=auth,\\n        )\\n        index[rec] = rec\\n\\n\\ndef add_feature_records_legacy(index):\\n    all_features = set()\\n    for rec in index.values():\\n        if rec.track_features:\\n            all_features.update(rec.track_features)\\n\\n    for feature_name in all_features:\\n        rec = make_feature_record(feature_name)\\n        index[rec] = rec\\n\\n\\ndef _export_subdir_data_to_repodata(subdir_data: SubdirData):\\n    \\\"\\\"\\\"\\n    This function is only temporary and meant to patch wrong / undesirable\\n    testing behaviour. It should end up being replaced with the new class-based,\\n    backend-agnostic solver tests.\\n    \\\"\\\"\\\"\\n    state = subdir_data._internal_state\\n    subdir = subdir_data.channel.subdir\\n    packages = {}\\n    packages_conda = {}\\n    for pkg in subdir_data.iter_records():\\n        if pkg.timestamp:\\n            # ensure timestamp is dumped as int in milliseconds\\n            # (pkg.timestamp is a kept as a float in seconds)\\n            pkg.__fields__[\\\"timestamp\\\"]._in_dump = True\\n        data = pkg.dump()\\n        if subdir == \\\"noarch\\\" and getattr(pkg, \\\"noarch\\\", None):\\n            data[\\\"subdir\\\"] = \\\"noarch\\\"\\n            data[\\\"platform\\\"] = data[\\\"arch\\\"] = None\\n        if \\\"features\\\" in data:\\n            # Features are deprecated, so they are not implemented\\n            # in modern solvers like mamba. Mamba does implement\\n            # track_features minimization, so we are exposing the\\n            # features as track_features, which seems to make the\\n            # tests pass\\n            data[\\\"track_features\\\"] = data[\\\"features\\\"]\\n            del data[\\\"features\\\"]\\n        if pkg.fn.endswith(\\\".conda\\\"):\\n            packages_conda[pkg.fn] = data\\n        else:\\n            packages[pkg.fn] = data\\n    return {\\n        \\\"_cache_control\\\": state[\\\"_cache_control\\\"],\\n        \\\"_etag\\\": state[\\\"_etag\\\"],\\n        \\\"_mod\\\": state[\\\"_mod\\\"],\\n        \\\"_url\\\": state[\\\"_url\\\"],\\n        \\\"_add_pip\\\": state[\\\"_add_pip\\\"],\\n        \\\"info\\\": {\\n            \\\"subdir\\\": subdir,\\n        },\\n        \\\"packages\\\": packages,\\n        \\\"packages.conda\\\": packages_conda,\\n    }\\n\\n\\ndef _sync_channel_to_disk(subdir_data: SubdirData):\\n    \\\"\\\"\\\"\\n    This function is only temporary and meant to patch wrong / undesirable\\n    testing behaviour. It should end up being replaced with the new class-based,\\n    backend-agnostic solver tests.\\n    \\\"\\\"\\\"\\n    base = Path(EXPORTED_CHANNELS_DIR) / subdir_data.channel.name\\n    subdir_path = base / subdir_data.channel.subdir\\n    subdir_path.mkdir(parents=True, exist_ok=True)\\n    with open(subdir_path / \\\"repodata.json\\\", \\\"w\\\") as f:\\n        json.dump(\\n            _export_subdir_data_to_repodata(subdir_data), f, indent=2, sort_keys=True\\n        )\\n        f.flush()\\n        os.fsync(f.fileno())\\n\\n\\ndef _alias_canonical_channel_name_cache_to_file_prefixed(name, subdir_data=None):\\n    \\\"\\\"\\\"\\n    This function is only temporary and meant to patch wrong / undesirable\\n    testing behaviour. It should end up being replaced with the new class-based,\\n    backend-agnostic solver tests.\\n    \\\"\\\"\\\"\\n    # export repodata state to disk for other solvers to test\\n    if subdir_data is None:\\n        cache_key = Channel(name).url(with_credentials=True), \\\"repodata.json\\\"\\n        subdir_data = SubdirData._cache_.get(cache_key)\\n    if subdir_data:\\n        local_proxy_channel = Channel(f\\\"{EXPORTED_CHANNELS_DIR}/{name}\\\")\\n        SubdirData._cache_[\\n            (local_proxy_channel.url(with_credentials=True), \\\"repodata.json\\\")\\n        ] = subdir_data\\n\\n\\ndef _patch_for_local_exports(name, subdir_data):\\n    \\\"\\\"\\\"\\n    This function is only temporary and meant to patch wrong / undesirable\\n    testing behaviour. It should end up being replaced with the new class-based,\\n    backend-agnostic solver tests.\\n    \\\"\\\"\\\"\\n    _alias_canonical_channel_name_cache_to_file_prefixed(name, subdir_data)\\n\\n    # we need to override the modification time here so the\\n    # cache hits this subdir_data object from the local copy too\\n    # - without this, the legacy solver will use the local dump too\\n    # and there's no need for that extra work\\n    # (check conda.core.subdir_data.SubdirDataType.__call__ for\\n    # details)\\n    _sync_channel_to_disk(subdir_data)\\n    subdir_data._mtime = float(\\\"inf\\\")\\n\\n\\ndef _get_index_r_base(\\n    json_filename_or_packages,\\n    channel_name,\\n    subdir=context.subdir,\\n    add_pip=False,\\n    merge_noarch=False,\\n):\\n    if isinstance(json_filename_or_packages, (str, os.PathLike)):\\n        with open(join(TEST_DATA_DIR, json_filename_or_packages)) as fi:\\n            all_packages = json.load(fi)\\n    elif isinstance(json_filename_or_packages, dict):\\n        all_packages = json_filename_or_packages\\n    else:\\n        raise ValueError(\\\"'json_filename_or_data' must be path-like or dict\\\")\\n\\n    if merge_noarch:\\n        packages = {subdir: all_packages}\\n    else:\\n        packages = {subdir: {}, \\\"noarch\\\": {}}\\n        for key, pkg in all_packages.items():\\n            if pkg.get(\\\"subdir\\\") == \\\"noarch\\\" or pkg.get(\\\"noarch\\\"):\\n                packages[\\\"noarch\\\"][key] = pkg\\n            else:\\n                packages[subdir][key] = pkg\\n\\n    subdir_datas = []\\n    channels = []\\n    for subchannel, subchannel_pkgs in packages.items():\\n        repodata = {\\n            \\\"info\\\": {\\n                \\\"subdir\\\": subchannel,\\n                \\\"arch\\\": context.arch_name,\\n                \\\"platform\\\": context.platform,\\n            },\\n            \\\"packages\\\": subchannel_pkgs,\\n        }\\n\\n        channel = Channel(f\\\"https://conda.anaconda.org/{channel_name}/{subchannel}\\\")\\n        channels.append(channel)\\n        sd = SubdirData(channel)\\n        subdir_datas.append(sd)\\n        with env_var(\\n            \\\"CONDA_ADD_PIP_AS_PYTHON_DEPENDENCY\\\",\\n            str(add_pip).lower(),\\n            stack_callback=conda_tests_ctxt_mgmt_def_pol,\\n        ):\\n            sd._process_raw_repodata_str(json.dumps(repodata))\\n        sd._loaded = True\\n        SubdirData._cache_[channel.url(with_credentials=True)] = sd\\n        _patch_for_local_exports(channel_name, sd)\\n\\n    # this is for the classic solver only, which is fine with a single collapsed index\\n    index = {}\\n    for sd in subdir_datas:\\n        index.update({prec: prec for prec in sd.iter_records()})\\n    r = Resolve(index, channels=channels)\\n\\n    return index, r\\n\\n\\n# this fixture appears to introduce a test-order dependency if cached\\ndef get_index_r_1(subdir=context.subdir, add_pip=True, merge_noarch=False):\\n    return _get_index_r_base(\\n        \\\"index.json\\\",\\n        \\\"channel-1\\\",\\n        subdir=subdir,\\n        add_pip=add_pip,\\n        merge_noarch=merge_noarch,\\n    )\\n\\n\\n@lru_cache(maxsize=None)\\ndef get_index_r_2(subdir=context.subdir, add_pip=True, merge_noarch=False):\\n    return _get_index_r_base(\\n        \\\"index2.json\\\",\\n        \\\"channel-2\\\",\\n        subdir=subdir,\\n        add_pip=add_pip,\\n        merge_noarch=merge_noarch,\\n    )\\n\\n\\n@lru_cache(maxsize=None)\\ndef get_index_r_4(subdir=context.subdir, add_pip=True, merge_noarch=False):\\n    return _get_index_r_base(\\n        \\\"index4.json\\\",\\n        \\\"channel-4\\\",\\n        subdir=subdir,\\n        add_pip=add_pip,\\n        merge_noarch=merge_noarch,\\n    )\\n\\n\\n@lru_cache(maxsize=None)\\ndef get_index_r_5(subdir=context.subdir, add_pip=False, merge_noarch=False):\\n    return _get_index_r_base(\\n        \\\"index5.json\\\",\\n        \\\"channel-5\\\",\\n        subdir=subdir,\\n        add_pip=add_pip,\\n        merge_noarch=merge_noarch,\\n    )\\n\\n\\n@lru_cache(maxsize=None)\\ndef get_index_must_unfreeze(subdir=context.subdir, add_pip=True, merge_noarch=False):\\n    repodata = {\\n        \\\"foobar-1.0-0.tar.bz2\\\": {\\n            \\\"build\\\": \\\"0\\\",\\n            \\\"build_number\\\": 0,\\n            \\\"depends\\\": [\\\"libbar 2.0.*\\\", \\\"libfoo 1.0.*\\\"],\\n            \\\"md5\\\": \\\"11ec1194bcc56b9a53c127142a272772\\\",\\n            \\\"name\\\": \\\"foobar\\\",\\n            \\\"timestamp\\\": 1562861325613,\\n            \\\"version\\\": \\\"1.0\\\",\\n        },\\n        \\\"foobar-2.0-0.tar.bz2\\\": {\\n            \\\"build\\\": \\\"0\\\",\\n            \\\"build_number\\\": 0,\\n            \\\"depends\\\": [\\\"libbar 2.0.*\\\", \\\"libfoo 2.0.*\\\"],\\n            \\\"md5\\\": \\\"f8eb5a7fa1ff6dead4e360631a6cd048\\\",\\n            \\\"name\\\": \\\"foobar\\\",\\n            \\\"version\\\": \\\"2.0\\\",\\n        },\\n        \\\"libbar-1.0-0.tar.bz2\\\": {\\n            \\\"build\\\": \\\"0\\\",\\n            \\\"build_number\\\": 0,\\n            \\\"depends\\\": [],\\n            \\\"md5\\\": \\\"f51f4d48a541b7105b5e343704114f0f\\\",\\n            \\\"name\\\": \\\"libbar\\\",\\n            \\\"timestamp\\\": 1562858881022,\\n            \\\"version\\\": \\\"1.0\\\",\\n        },\\n        \\\"libbar-2.0-0.tar.bz2\\\": {\\n            \\\"build\\\": \\\"0\\\",\\n            \\\"build_number\\\": 0,\\n            \\\"depends\\\": [],\\n            \\\"md5\\\": \\\"27f4e717ed263f909074f64d9cbf935d\\\",\\n            \\\"name\\\": \\\"libbar\\\",\\n            \\\"timestamp\\\": 1562858881748,\\n            \\\"version\\\": \\\"2.0\\\",\\n        },\\n        \\\"libfoo-1.0-0.tar.bz2\\\": {\\n            \\\"build\\\": \\\"0\\\",\\n            \\\"build_number\\\": 0,\\n            \\\"depends\\\": [],\\n            \\\"md5\\\": \\\"ad7c088566ffe2389958daedf8ff312c\\\",\\n            \\\"name\\\": \\\"libfoo\\\",\\n            \\\"timestamp\\\": 1562858763881,\\n            \\\"version\\\": \\\"1.0\\\",\\n        },\\n        \\\"libfoo-2.0-0.tar.bz2\\\": {\\n            \\\"build\\\": \\\"0\\\",\\n            \\\"build_number\\\": 0,\\n            \\\"depends\\\": [],\\n            \\\"md5\\\": \\\"daf7af7086d8f22be49ae11bdc41f332\\\",\\n            \\\"name\\\": \\\"libfoo\\\",\\n            \\\"timestamp\\\": 1562858836924,\\n            \\\"version\\\": \\\"2.0\\\",\\n        },\\n        \\\"qux-1.0-0.tar.bz2\\\": {\\n            \\\"build\\\": \\\"0\\\",\\n            \\\"build_number\\\": 0,\\n            \\\"depends\\\": [\\\"libbar 2.0.*\\\", \\\"libfoo 1.0.*\\\"],\\n            \\\"md5\\\": \\\"18604cbe4f789fe853232eef4babd4f9\\\",\\n            \\\"name\\\": \\\"qux\\\",\\n            \\\"timestamp\\\": 1562861393808,\\n            \\\"version\\\": \\\"1.0\\\",\\n        },\\n        \\\"qux-2.0-0.tar.bz2\\\": {\\n            \\\"build\\\": \\\"0\\\",\\n            \\\"build_number\\\": 0,\\n            \\\"depends\\\": [\\\"libbar 1.0.*\\\", \\\"libfoo 2.0.*\\\"],\\n            \\\"md5\\\": \\\"892aa4b9ec64b67045a46866ef1ea488\\\",\\n            \\\"name\\\": \\\"qux\\\",\\n            \\\"timestamp\\\": 1562861394828,\\n            \\\"version\\\": \\\"2.0\\\",\\n        },\\n    }\\n    _get_index_r_base(\\n        repodata,\\n        \\\"channel-freeze\\\",\\n        subdir=subdir,\\n        add_pip=add_pip,\\n        merge_noarch=merge_noarch,\\n    )\\n\\n\\n# Do not memoize this get_index to allow different CUDA versions to be detected\\ndef get_index_cuda(subdir=context.subdir, add_pip=True, merge_noarch=False):\\n    return _get_index_r_base(\\n        \\\"index.json\\\",\\n        \\\"channel-1\\\",\\n        subdir=subdir,\\n        add_pip=add_pip,\\n        merge_noarch=merge_noarch,\\n    )\\n\\n\\ndef record(\\n    name=\\\"a\\\",\\n    version=\\\"1.0\\\",\\n    depends=None,\\n    build=\\\"0\\\",\\n    build_number=0,\\n    timestamp=0,\\n    channel=None,\\n    **kwargs,\\n):\\n    return PackageRecord(\\n        name=name,\\n        version=version,\\n        depends=depends or [],\\n        build=build,\\n        build_number=build_number,\\n        timestamp=timestamp,\\n        channel=channel,\\n        **kwargs,\\n    )\\n\\n\\ndef _get_solver_base(\\n    channel_id,\\n    tmpdir,\\n    specs_to_add=(),\\n    specs_to_remove=(),\\n    prefix_records=(),\\n    history_specs=(),\\n    add_pip=False,\\n    merge_noarch=False,\\n):\\n    tmpdir = tmpdir.strpath\\n    pd = PrefixData(tmpdir)\\n    pd._PrefixData__prefix_records = {\\n        rec.name: PrefixRecord.from_objects(rec) for rec in prefix_records\\n    }\\n    spec_map = {spec.name: spec for spec in history_specs}\\n    if channel_id == \\\"channel-1\\\":\\n        get_index_r_1(context.subdir, add_pip, merge_noarch)\\n        _alias_canonical_channel_name_cache_to_file_prefixed(\\\"channel-1\\\")\\n        channels = (Channel(f\\\"{EXPORTED_CHANNELS_DIR}/channel-1\\\"),)\\n    elif channel_id == \\\"channel-2\\\":\\n        get_index_r_2(context.subdir, add_pip, merge_noarch)\\n        _alias_canonical_channel_name_cache_to_file_prefixed(\\\"channel-2\\\")\\n        channels = (Channel(f\\\"{EXPORTED_CHANNELS_DIR}/channel-2\\\"),)\\n    elif channel_id == \\\"channel-4\\\":\\n        get_index_r_4(context.subdir, add_pip, merge_noarch)\\n        _alias_canonical_channel_name_cache_to_file_prefixed(\\\"channel-4\\\")\\n        channels = (Channel(f\\\"{EXPORTED_CHANNELS_DIR}/channel-4\\\"),)\\n    elif channel_id == \\\"channel-5\\\":\\n        get_index_r_5(context.subdir, add_pip, merge_noarch)\\n        _alias_canonical_channel_name_cache_to_file_prefixed(\\\"channel-5\\\")\\n        channels = (Channel(f\\\"{EXPORTED_CHANNELS_DIR}/channel-5\\\"),)\\n    elif channel_id == \\\"aggregate-1\\\":\\n        get_index_r_2(context.subdir, add_pip, merge_noarch)\\n        get_index_r_4(context.subdir, add_pip, merge_noarch)\\n        _alias_canonical_channel_name_cache_to_file_prefixed(\\\"channel-2\\\")\\n        _alias_canonical_channel_name_cache_to_file_prefixed(\\\"channel-4\\\")\\n        channels = (\\n            Channel(f\\\"{EXPORTED_CHANNELS_DIR}/channel-2\\\"),\\n            Channel(f\\\"{EXPORTED_CHANNELS_DIR}/channel-4\\\"),\\n        )\\n    elif channel_id == \\\"aggregate-2\\\":\\n        get_index_r_2(context.subdir, add_pip, merge_noarch)\\n        get_index_r_4(context.subdir, add_pip, merge_noarch)\\n        _alias_canonical_channel_name_cache_to_file_prefixed(\\\"channel-4\\\")\\n        _alias_canonical_channel_name_cache_to_file_prefixed(\\\"channel-2\\\")\\n        # This is the only difference with aggregate-1: the priority\\n        channels = (\\n            Channel(f\\\"{EXPORTED_CHANNELS_DIR}/channel-4\\\"),\\n            Channel(f\\\"{EXPORTED_CHANNELS_DIR}/channel-2\\\"),\\n        )\\n    elif channel_id == \\\"must-unfreeze\\\":\\n        get_index_must_unfreeze(context.subdir, add_pip, merge_noarch)\\n        _alias_canonical_channel_name_cache_to_file_prefixed(\\\"channel-freeze\\\")\\n        channels = (Channel(f\\\"{EXPORTED_CHANNELS_DIR}/channel-freeze\\\"),)\\n    elif channel_id == \\\"cuda\\\":\\n        get_index_cuda(context.subdir, add_pip, merge_noarch)\\n        _alias_canonical_channel_name_cache_to_file_prefixed(\\\"channel-1\\\")\\n        channels = (Channel(f\\\"{EXPORTED_CHANNELS_DIR}/channel-1\\\"),)\\n\\n    subdirs = (context.subdir,) if merge_noarch else (context.subdir, \\\"noarch\\\")\\n\\n    with patch.object(\\n        History, \\\"get_requested_specs_map\\\", return_value=spec_map\\n    ), env_var(\\n        \\\"CONDA_ADD_PIP_AS_PYTHON_DEPENDENCY\\\",\\n        str(add_pip).lower(),\\n        stack_callback=conda_tests_ctxt_mgmt_def_pol,\\n    ):\\n        # We need CONDA_ADD_PIP_AS_PYTHON_DEPENDENCY=false here again (it's also in\\n        # get_index_r_*) to cover solver logics that need to load from disk instead of\\n        # hitting the SubdirData cache\\n        yield context.plugin_manager.get_solver_backend()(\\n            tmpdir,\\n            channels,\\n            subdirs,\\n            specs_to_add=specs_to_add,\\n            specs_to_remove=specs_to_remove,\\n        )\\n\\n\\n@contextmanager\\ndef get_solver(\\n    tmpdir,\\n    specs_to_add=(),\\n    specs_to_remove=(),\\n    prefix_records=(),\\n    history_specs=(),\\n    add_pip=False,\\n    merge_noarch=False,\\n):\\n    yield from _get_solver_base(\\n        \\\"channel-1\\\",\\n        tmpdir,\\n        specs_to_add=specs_to_add,\\n        specs_to_remove=specs_to_remove,\\n        prefix_records=prefix_records,\\n        history_specs=history_specs,\\n        add_pip=add_pip,\\n        merge_noarch=merge_noarch,\\n    )\\n\\n\\n@contextmanager\\ndef get_solver_2(\\n    tmpdir,\\n    specs_to_add=(),\\n    specs_to_remove=(),\\n    prefix_records=(),\\n    history_specs=(),\\n    add_pip=False,\\n    merge_noarch=False,\\n):\\n    yield from _get_solver_base(\\n        \\\"channel-2\\\",\\n        tmpdir,\\n        specs_to_add=specs_to_add,\\n        specs_to_remove=specs_to_remove,\\n        prefix_records=prefix_records,\\n        history_specs=history_specs,\\n        add_pip=add_pip,\\n        merge_noarch=merge_noarch,\\n    )\\n\\n\\n@contextmanager\\ndef get_solver_4(\\n    tmpdir,\\n    specs_to_add=(),\\n    specs_to_remove=(),\\n    prefix_records=(),\\n    history_specs=(),\\n    add_pip=False,\\n    merge_noarch=False,\\n):\\n    yield from _get_solver_base(\\n        \\\"channel-4\\\",\\n        tmpdir,\\n        specs_to_add=specs_to_add,\\n        specs_to_remove=specs_to_remove,\\n        prefix_records=prefix_records,\\n        history_specs=history_specs,\\n        add_pip=add_pip,\\n        merge_noarch=merge_noarch,\\n    )\\n\\n\\n@contextmanager\\ndef get_solver_5(\\n    tmpdir,\\n    specs_to_add=(),\\n    specs_to_remove=(),\\n    prefix_records=(),\\n    history_specs=(),\\n    add_pip=False,\\n    merge_noarch=False,\\n):\\n    yield from _get_solver_base(\\n        \\\"channel-5\\\",\\n        tmpdir,\\n        specs_to_add=specs_to_add,\\n        specs_to_remove=specs_to_remove,\\n        prefix_records=prefix_records,\\n        history_specs=history_specs,\\n        add_pip=add_pip,\\n        merge_noarch=merge_noarch,\\n    )\\n\\n\\n@contextmanager\\ndef get_solver_aggregate_1(\\n    tmpdir,\\n    specs_to_add=(),\\n    specs_to_remove=(),\\n    prefix_records=(),\\n    history_specs=(),\\n    add_pip=False,\\n    merge_noarch=False,\\n):\\n    yield from _get_solver_base(\\n        \\\"aggregate-1\\\",\\n        tmpdir,\\n        specs_to_add=specs_to_add,\\n        specs_to_remove=specs_to_remove,\\n        prefix_records=prefix_records,\\n        history_specs=history_specs,\\n        add_pip=add_pip,\\n        merge_noarch=merge_noarch,\\n    )\\n\\n\\n@contextmanager\\ndef get_solver_aggregate_2(\\n    tmpdir,\\n    specs_to_add=(),\\n    specs_to_remove=(),\\n    prefix_records=(),\\n    history_specs=(),\\n    add_pip=False,\\n    merge_noarch=False,\\n):\\n    yield from _get_solver_base(\\n        \\\"aggregate-2\\\",\\n        tmpdir,\\n        specs_to_add=specs_to_add,\\n        specs_to_remove=specs_to_remove,\\n        prefix_records=prefix_records,\\n        history_specs=history_specs,\\n        add_pip=add_pip,\\n        merge_noarch=merge_noarch,\\n    )\\n\\n\\n@contextmanager\\ndef get_solver_must_unfreeze(\\n    tmpdir,\\n    specs_to_add=(),\\n    specs_to_remove=(),\\n    prefix_records=(),\\n    history_specs=(),\\n    add_pip=False,\\n    merge_noarch=False,\\n):\\n    yield from _get_solver_base(\\n        \\\"must-unfreeze\\\",\\n        tmpdir,\\n        specs_to_add=specs_to_add,\\n        specs_to_remove=specs_to_remove,\\n        prefix_records=prefix_records,\\n        history_specs=history_specs,\\n        add_pip=add_pip,\\n        merge_noarch=merge_noarch,\\n    )\\n\\n\\n@contextmanager\\ndef get_solver_cuda(\\n    tmpdir,\\n    specs_to_add=(),\\n    specs_to_remove=(),\\n    prefix_records=(),\\n    history_specs=(),\\n    add_pip=False,\\n    merge_noarch=False,\\n):\\n    yield from _get_solver_base(\\n        \\\"cuda\\\",\\n        tmpdir,\\n        specs_to_add=specs_to_add,\\n        specs_to_remove=specs_to_remove,\\n        prefix_records=prefix_records,\\n        history_specs=history_specs,\\n        add_pip=add_pip,\\n        merge_noarch=merge_noarch,\\n    )\\n\\n\\ndef convert_to_dist_str(solution):\\n    dist_str = []\\n    for prec in solution:\\n        # This is needed to remove the local path prefix in the\\n        # dist_str() calls, otherwise we cannot compare them\\n        canonical_name = prec.channel._Channel__canonical_name\\n        prec.channel._Channel__canonical_name = prec.channel.name\\n        dist_str.append(prec.dist_str())\\n        prec.channel._Channel__canonical_name = canonical_name\\n    return tuple(dist_str)\\n\\n\\n@pytest.fixture()\\ndef solver_class():\\n    return context.plugin_manager.get_solver_backend()\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Collection of pytest fixtures used in conda tests.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport os\\nimport warnings\\nfrom typing import TYPE_CHECKING, Literal, TypeVar\\n\\nimport py\\nimport pytest\\n\\nfrom ..auxlib.ish import dals\\nfrom ..base.context import conda_tests_ctxt_mgmt_def_pol, context, reset_context\\nfrom ..common.configuration import YamlRawParameter\\nfrom ..common.io import env_vars\\nfrom ..common.serialize import yaml_round_trip_load\\nfrom ..core.subdir_data import SubdirData\\nfrom ..gateways.disk.create import TemporaryDirectory\\n\\nif TYPE_CHECKING:\\n    from typing import Iterable\\n\\n    from pytest import FixtureRequest, MonkeyPatch\\n\\n\\n@pytest.fixture(autouse=True)\\ndef suppress_resource_warning():\\n    \\\"\\\"\\\"\\n    Suppress `Unclosed Socket Warning`\\n\\n    It seems urllib3 keeps a socket open to avoid costly recreation costs.\\n\\n    xref: https://github.com/kennethreitz/requests/issues/1882\\n    \\\"\\\"\\\"\\n    warnings.filterwarnings(\\\"ignore\\\", category=ResourceWarning)\\n\\n\\n@pytest.fixture(scope=\\\"function\\\")\\ndef tmpdir(tmpdir, request):\\n    tmpdir = TemporaryDirectory(dir=str(tmpdir))\\n    request.addfinalizer(tmpdir.cleanup)\\n    return py.path.local(tmpdir.name)\\n\\n\\n@pytest.fixture(autouse=True)\\ndef clear_subdir_cache():\\n    SubdirData.clear_cached_local_channel_data()\\n\\n\\n@pytest.fixture(scope=\\\"function\\\")\\ndef disable_channel_notices():\\n    \\\"\\\"\\\"\\n    Fixture that will set \\\"context.number_channel_notices\\\" to 0 and then set\\n    it back to its original value.\\n\\n    This is also a good example of how to override values in the context object.\\n    \\\"\\\"\\\"\\n    yaml_str = dals(\\n        \\\"\\\"\\\"\\n        number_channel_notices: 0\\n        \\\"\\\"\\\"\\n    )\\n    reset_context(())\\n    rd = {\\n        \\\"testdata\\\": YamlRawParameter.make_raw_parameters(\\n            \\\"testdata\\\", yaml_round_trip_load(yaml_str)\\n        )\\n    }\\n    context._set_raw_data(rd)\\n\\n    yield\\n\\n    reset_context(())\\n\\n\\n@pytest.fixture(scope=\\\"function\\\")\\ndef reset_conda_context():\\n    \\\"\\\"\\\"Resets the context object after each test function is run.\\\"\\\"\\\"\\n    yield\\n\\n    reset_context()\\n\\n\\n@pytest.fixture()\\ndef temp_package_cache(tmp_path_factory):\\n    \\\"\\\"\\\"\\n    Used to isolate package or index cache from other tests.\\n    \\\"\\\"\\\"\\n    pkgs_dir = tmp_path_factory.mktemp(\\\"pkgs\\\")\\n    with env_vars(\\n        {\\\"CONDA_PKGS_DIRS\\\": str(pkgs_dir)}, stack_callback=conda_tests_ctxt_mgmt_def_pol\\n    ):\\n        yield pkgs_dir\\n\\n\\n@pytest.fixture(\\n    # allow CI to set the solver backends via the CONDA_TEST_SOLVERS env var\\n    params=os.environ.get(\\\"CONDA_TEST_SOLVERS\\\", \\\"libmamba,classic\\\").split(\\\",\\\")\\n)\\ndef parametrized_solver_fixture(\\n    request: FixtureRequest,\\n    monkeypatch: MonkeyPatch,\\n) -> Iterable[Literal[\\\"libmamba\\\", \\\"classic\\\"]]:\\n    \\\"\\\"\\\"\\n    A parameterized fixture that sets the solver backend to (1) libmamba\\n    and (2) classic for each test. It's using autouse=True, so only import it in\\n    modules that actually need it.\\n\\n    Note that skips and xfails need to be done _inside_ the test body.\\n    Decorators can't be used because they are evaluated before the\\n    fixture has done its work!\\n\\n    So, instead of:\\n\\n        @pytest.mark.skipif(context.solver == \\\"libmamba\\\", reason=\\\"...\\\")\\n        def test_foo():\\n            ...\\n\\n    Do:\\n\\n        def test_foo():\\n            if context.solver == \\\"libmamba\\\":\\n                pytest.skip(\\\"...\\\")\\n            ...\\n    \\\"\\\"\\\"\\n    yield from _solver_helper(request, monkeypatch, request.param)\\n\\n\\n@pytest.fixture\\ndef solver_classic(\\n    request: FixtureRequest,\\n    monkeypatch: MonkeyPatch,\\n) -> Iterable[Literal[\\\"classic\\\"]]:\\n    yield from _solver_helper(request, monkeypatch, \\\"classic\\\")\\n\\n\\n@pytest.fixture\\ndef solver_libmamba(\\n    request: FixtureRequest,\\n    monkeypatch: MonkeyPatch,\\n) -> Iterable[Literal[\\\"libmamba\\\"]]:\\n    yield from _solver_helper(request, monkeypatch, \\\"libmamba\\\")\\n\\n\\nSolver = TypeVar(\\\"Solver\\\", Literal[\\\"libmamba\\\"], Literal[\\\"classic\\\"])\\n\\n\\ndef _solver_helper(\\n    request: FixtureRequest,\\n    monkeypatch: MonkeyPatch,\\n    solver: Solver,\\n) -> Iterable[Solver]:\\n    # clear cached solver backends before & after each test\\n    context.plugin_manager.get_cached_solver_backend.cache_clear()\\n    request.addfinalizer(context.plugin_manager.get_cached_solver_backend.cache_clear)\\n\\n    monkeypatch.setenv(\\\"CONDA_SOLVER\\\", solver)\\n    reset_context()\\n    assert context.solver == solver\\n\\n    yield solver\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n# Attempt to move any conda entries in PATH to the front of it.\\n# IDEs have their own ideas about how PATH should be managed and\\n# they do dumb stuff like add /usr/bin to the front of it\\n# meaning conda takes a submissive role and the wrong stuff\\n# runs (when other conda prefixes get activated they replace\\n# the wrongly placed entries with newer wrongly placed entries).\\n#\\n# Note, there's still condabin to worry about here, and also should\\n# we not remove all traces of conda instead of just this fixup?\\n# Ideally we'd have two modes, 'removed' and 'fixed'. I have seen\\n# condabin come from an entirely different installation than\\n# CONDA_PREFIX too in some instances and that really needs fixing.\\nfrom __future__ import annotations\\n\\nimport json\\nimport os\\nimport sys\\nimport uuid\\nimport warnings\\nfrom contextlib import contextmanager, nullcontext\\nfrom dataclasses import dataclass\\nfrom logging import getLogger\\nfrom os.path import join\\nfrom pathlib import Path\\nfrom shutil import copyfile\\nfrom typing import TYPE_CHECKING, overload\\n\\nimport pytest\\n\\nfrom ..auxlib.entity import EntityEncoder\\nfrom ..base.constants import PACKAGE_CACHE_MAGIC_FILE\\nfrom ..base.context import context, reset_context\\nfrom ..cli.main import main_subshell\\nfrom ..common.compat import on_win\\nfrom ..common.url import path_to_url\\nfrom ..core.package_cache_data import PackageCacheData\\nfrom ..deprecations import deprecated\\nfrom ..exceptions import CondaExitZero\\nfrom ..models.records import PackageRecord\\n\\nif TYPE_CHECKING:\\n    from typing import Iterator\\n\\n    from pytest import CaptureFixture, ExceptionInfo, MonkeyPatch\\n    from pytest_mock import MockerFixture\\n\\nlog = getLogger(__name__)\\n\\n\\n@deprecated(\\n    \\\"24.9\\\",\\n    \\\"25.3\\\",\\n    addendum=\\\"It don't matter which environment the test suite is run from.\\\",\\n)\\ndef conda_ensure_sys_python_is_base_env_python():\\n    # Exit if we try to run tests from a non-base env. The tests end up installing\\n    # menuinst into the env they are called with and that breaks non-base env activation\\n    # as it emits a message to stderr:\\n    # WARNING menuinst_win32:<module>(157): menuinst called from non-root env\\n    # C:\\\\opt\\\\conda\\\\envs\\\\py27\\n    # So lets just sys.exit on that.\\n\\n    if \\\"CONDA_PYTHON_EXE\\\" in os.environ:\\n        if (\\n            Path(os.environ[\\\"CONDA_PYTHON_EXE\\\"]).resolve()\\n            != Path(sys.executable).resolve()\\n        ):\\n            warnings.warn(\\n                \\\"ERROR :: Running tests from a non-base Python interpreter. \\\"\\n                \\\" Tests requires installing menuinst and that causes stderr \\\"\\n                \\\" output when activated.\\\\n\\\"\\n                f\\\"- CONDA_PYTHON_EXE={os.environ['CONDA_PYTHON_EXE']}\\\\n\\\"\\n                f\\\"- sys.executable={sys.executable}\\\"\\n            )\\n\\n            # menuinst only really matters on windows\\n            if on_win:\\n                sys.exit(-1)\\n\\n\\ndef conda_move_to_front_of_PATH():\\n    if \\\"CONDA_PREFIX\\\" in os.environ:\\n        from ..activate import CmdExeActivator, PosixActivator\\n\\n        if os.name == \\\"nt\\\":\\n            activator_cls = CmdExeActivator\\n        else:\\n            activator_cls = PosixActivator\\n        activator = activator_cls()\\n        # But why not just use _replace_prefix_in_path? => because moving\\n        # the entries to the front of PATH is the goal here, not swapping\\n        # x for x (which would be pointless anyway).\\n        p = None\\n        # It might be nice to have a parameterised fixture with choices of:\\n        # 'System default PATH',\\n        # 'IDE default PATH',\\n        # 'Fully activated conda',\\n        # 'PATHly activated conda'\\n        # This will do for now => Note, if you have conda activated multiple\\n        # times it could mask some test failures but _remove_prefix_from_path\\n        # cannot be used multiple times; it will only remove *one* conda\\n        # prefix from the *original* value of PATH, calling it N times will\\n        # just return the same value every time, even if you update PATH.\\n        p = activator._remove_prefix_from_path(os.environ[\\\"CONDA_PREFIX\\\"])\\n\\n        # Replace any non sys.prefix condabin with sys.prefix condabin\\n        new_p = []\\n        found_condabin = False\\n        for pe in p:\\n            if pe.endswith(\\\"condabin\\\"):\\n                if not found_condabin:\\n                    found_condabin = True\\n                    if join(sys.prefix, \\\"condabin\\\") != pe:\\n                        condabin_path = join(sys.prefix, \\\"condabin\\\")\\n                        print(f\\\"Incorrect condabin, swapping {pe} to {condabin_path}\\\")\\n                        new_p.append(condabin_path)\\n                    else:\\n                        new_p.append(pe)\\n            else:\\n                new_p.append(pe)\\n\\n        os.environ[\\\"PATH\\\"] = os.pathsep.join(new_p)\\n        activator = activator_cls()\\n        p = activator._add_prefix_to_path(os.environ[\\\"CONDA_PREFIX\\\"])\\n        os.environ[\\\"PATH\\\"] = os.pathsep.join(p)\\n\\n\\n@dataclass\\nclass CondaCLIFixture:\\n    capsys: CaptureFixture\\n\\n    @overload\\n    def __call__(\\n        self,\\n        *argv: str | os.PathLike | Path,\\n        raises: type[Exception] | tuple[type[Exception], ...],\\n    ) -> tuple[str, str, ExceptionInfo]: ...\\n\\n    @overload\\n    def __call__(self, *argv: str | os.PathLike | Path) -> tuple[str, str, int]: ...\\n\\n    def __call__(\\n        self,\\n        *argv: str | os.PathLike | Path,\\n        raises: type[Exception] | tuple[type[Exception], ...] | None = None,\\n    ) -> tuple[str, str, int | ExceptionInfo]:\\n        \\\"\\\"\\\"Test conda CLI. Mimic what is done in `conda.cli.main.main`.\\n\\n        `conda ...` == `conda_cli(...)`\\n\\n        :param argv: Arguments to parse.\\n        :param raises: Expected exception to intercept. If provided, the raised exception\\n            will be returned instead of exit code (see pytest.raises and pytest.ExceptionInfo).\\n        :return: Command results (stdout, stderr, exit code or pytest.ExceptionInfo).\\n        \\\"\\\"\\\"\\n        # clear output\\n        self.capsys.readouterr()\\n\\n        # ensure arguments are string\\n        argv = tuple(map(str, argv))\\n\\n        # run command\\n        code = None\\n        with pytest.raises(raises) if raises else nullcontext() as exception:\\n            code = main_subshell(*argv)\\n        # capture output\\n        out, err = self.capsys.readouterr()\\n\\n        # restore to prior state\\n        reset_context()\\n\\n        return out, err, exception if raises else code\\n\\n\\n@pytest.fixture\\ndef conda_cli(capsys: CaptureFixture) -> CondaCLIFixture:\\n    \\\"\\\"\\\"Fixture returning CondaCLIFixture instance.\\\"\\\"\\\"\\n    yield CondaCLIFixture(capsys)\\n\\n\\n@dataclass\\nclass PathFactoryFixture:\\n    tmp_path: Path\\n\\n    def __call__(\\n        self,\\n        name: str | None = None,\\n        prefix: str | None = None,\\n        suffix: str | None = None,\\n    ) -> Path:\\n        \\\"\\\"\\\"Unique, non-existent path factory.\\n\\n        Extends pytest's `tmp_path` fixture with a new unique, non-existent path for usage in cases\\n        where we need a temporary path that doesn't exist yet.\\n\\n        :param name: Path name to append to `tmp_path`\\n        :param prefix: Prefix to prepend to unique name generated\\n        :param suffix: Suffix to append to unique name generated\\n        :return: A new unique path\\n        \\\"\\\"\\\"\\n        prefix = prefix or \\\"\\\"\\n        name = name or uuid.uuid4().hex\\n        suffix = suffix or \\\"\\\"\\n        return self.tmp_path / (prefix + name + suffix)\\n\\n\\n@pytest.fixture\\ndef path_factory(tmp_path: Path) -> PathFactoryFixture:\\n    \\\"\\\"\\\"Fixture returning PathFactoryFixture instance.\\\"\\\"\\\"\\n    yield PathFactoryFixture(tmp_path)\\n\\n\\n@dataclass\\nclass TmpEnvFixture:\\n    path_factory: PathFactoryFixture\\n    conda_cli: CondaCLIFixture\\n\\n    @contextmanager\\n    def __call__(\\n        self,\\n        *packages: str,\\n        prefix: str | os.PathLike | None = None,\\n    ) -> Iterator[Path]:\\n        \\\"\\\"\\\"Generate a conda environment with the provided packages.\\n\\n        :param packages: The packages to install into environment\\n        :param prefix: The prefix at which to install the conda environment\\n        :return: The conda environment's prefix\\n        \\\"\\\"\\\"\\n        prefix = Path(prefix or self.path_factory())\\n\\n        self.conda_cli(\\\"create\\\", \\\"--prefix\\\", prefix, *packages, \\\"--yes\\\", \\\"--quiet\\\")\\n        yield prefix\\n\\n        # no need to remove prefix since it is in a temporary directory\\n\\n\\n@pytest.fixture\\ndef tmp_env(\\n    path_factory: PathFactoryFixture,\\n    conda_cli: CondaCLIFixture,\\n) -> TmpEnvFixture:\\n    \\\"\\\"\\\"Fixture returning TmpEnvFixture instance.\\\"\\\"\\\"\\n    yield TmpEnvFixture(path_factory, conda_cli)\\n\\n\\n@dataclass\\nclass TmpChannelFixture:\\n    path_factory: PathFactoryFixture\\n    conda_cli: CondaCLIFixture\\n\\n    @contextmanager\\n    def __call__(self, *packages: str) -> Iterator[tuple[Path, str]]:\\n        # download packages\\n        self.conda_cli(\\n            \\\"create\\\",\\n            f\\\"--prefix={self.path_factory()}\\\",\\n            *packages,\\n            \\\"--yes\\\",\\n            \\\"--quiet\\\",\\n            \\\"--download-only\\\",\\n            raises=CondaExitZero,\\n        )\\n\\n        pkgs_dir = Path(PackageCacheData.first_writable().pkgs_dir)\\n        pkgs_cache = PackageCacheData(pkgs_dir)\\n\\n        channel = self.path_factory()\\n        subdir = channel / context.subdir\\n        subdir.mkdir(parents=True)\\n        noarch = channel / \\\"noarch\\\"\\n        noarch.mkdir(parents=True)\\n\\n        repodata = {\\\"info\\\": {}, \\\"packages\\\": {}}\\n        for package in packages:\\n            for pkg_data in pkgs_cache.query(package):\\n                fname = pkg_data[\\\"fn\\\"]\\n\\n                copyfile(pkgs_dir / fname, subdir / fname)\\n\\n                repodata[\\\"packages\\\"][fname] = PackageRecord(\\n                    **{\\n                        field: value\\n                        for field, value in pkg_data.dump().items()\\n                        if field not in (\\\"url\\\", \\\"channel\\\", \\\"schannel\\\")\\n                    }\\n                )\\n\\n        (subdir / \\\"repodata.json\\\").write_text(json.dumps(repodata, cls=EntityEncoder))\\n        (noarch / \\\"repodata.json\\\").write_text(json.dumps({}, cls=EntityEncoder))\\n\\n        for package in packages:\\n            assert any(PackageCacheData.query_all(package))\\n\\n        yield channel, path_to_url(str(channel))\\n\\n\\n@pytest.fixture\\ndef tmp_channel(\\n    path_factory: PathFactoryFixture,\\n    conda_cli: CondaCLIFixture,\\n) -> TmpChannelFixture:\\n    \\\"\\\"\\\"Fixture returning TmpChannelFixture instance.\\\"\\\"\\\"\\n    yield TmpChannelFixture(path_factory, conda_cli)\\n\\n\\n@pytest.fixture(name=\\\"monkeypatch\\\")\\ndef context_aware_monkeypatch(monkeypatch: MonkeyPatch) -> MonkeyPatch:\\n    \\\"\\\"\\\"A monkeypatch fixture that resets context after each test\\\"\\\"\\\"\\n    yield monkeypatch\\n\\n    # reset context if any CONDA_ variables were set/unset\\n    if conda_vars := [\\n        name\\n        for obj, name, _ in monkeypatch._setitem\\n        if obj is os.environ and name.startswith(\\\"CONDA_\\\")\\n    ]:\\n        log.debug(f\\\"monkeypatch cleanup: undo & reset context: {', '.join(conda_vars)}\\\")\\n        monkeypatch.undo()\\n        # reload context without search paths\\n        reset_context([])\\n\\n\\n@pytest.fixture\\ndef tmp_pkgs_dir(path_factory: PathFactoryFixture, mocker: MockerFixture) -> Path:\\n    pkgs_dir = path_factory() / \\\"pkgs\\\"\\n    pkgs_dir.mkdir(parents=True)\\n    (pkgs_dir / PACKAGE_CACHE_MAGIC_FILE).touch()\\n\\n    mocker.patch(\\n        \\\"conda.base.context.Context.pkgs_dirs\\\",\\n        new_callable=mocker.PropertyMock,\\n        return_value=(pkgs_dir_str := str(pkgs_dir),),\\n    )\\n    assert context.pkgs_dirs == (pkgs_dir_str,)\\n\\n    yield pkgs_dir\\n\\n    PackageCacheData._cache_.pop(pkgs_dir_str, None)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Collection of helper functions used in conda.notices tests.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport datetime\\nimport json\\nimport os\\nimport uuid\\nfrom itertools import chain\\nfrom pathlib import Path\\nfrom typing import TYPE_CHECKING\\n\\nfrom ...models.channel import get_channel_objs\\nfrom ...notices.cache import get_notices_cache_file\\nfrom ...notices.core import get_channel_name_and_urls\\nfrom ...notices.types import ChannelNoticeResponse\\n\\nif TYPE_CHECKING:\\n    from typing import Sequence\\n    from unittest import mock\\n\\n    from ...base.context import Context\\n\\nDEFAULT_NOTICE_MESG = \\\"Here is an example message that will be displayed to users\\\"\\n\\n\\ndef get_test_notices(\\n    messages: Sequence[str],\\n    level: str | None = \\\"info\\\",\\n    created_at: datetime.datetime | None = None,\\n    expired_at: datetime.datetime | None = None,\\n) -> dict:\\n    created_at = created_at or datetime.datetime.now(datetime.timezone.utc)\\n    expired_at = expired_at or created_at + datetime.timedelta(days=7)\\n\\n    return {\\n        \\\"notices\\\": [\\n            {\\n                \\\"id\\\": str(uuid.uuid4()),\\n                \\\"message\\\": message,\\n                \\\"level\\\": level,\\n                \\\"created_at\\\": created_at.isoformat(),\\n                \\\"expired_at\\\": expired_at.isoformat(),\\n            }\\n            for message in messages\\n        ]\\n    }\\n\\n\\ndef add_resp_to_mock(\\n    mock_session: mock.MagicMock,\\n    status_code: int,\\n    messages_json: dict,\\n    raise_exc: bool = False,\\n) -> None:\\n    \\\"\\\"\\\"Adds any number of MockResponse to MagicMock object as side_effects\\\"\\\"\\\"\\n\\n    def forever_404():\\n        while True:\\n            yield MockResponse(404, {})\\n\\n    def one_200():\\n        yield MockResponse(status_code, messages_json, raise_exc=raise_exc)\\n\\n    chn = chain(one_200(), forever_404())\\n    mock_session().get.side_effect = tuple(next(chn) for _ in range(100))\\n\\n\\ndef create_notice_cache_files(\\n    cache_dir: Path,\\n    cache_files: Sequence[str],\\n    messages_json_seq: Sequence[dict],\\n) -> None:\\n    \\\"\\\"\\\"Creates the cache files that we use in tests\\\"\\\"\\\"\\n    for message_json, file in zip(messages_json_seq, cache_files):\\n        with cache_dir.joinpath(file).open(\\\"w\\\") as fp:\\n            json.dump(message_json, fp)\\n\\n\\ndef offset_cache_file_mtime(mtime_offset) -> None:\\n    \\\"\\\"\\\"\\n    Allows for offsetting the mtime of the notices cache file. This is often\\n    used to mock an older creation time the cache file.\\n    \\\"\\\"\\\"\\n    cache_file = get_notices_cache_file()\\n    os.utime(\\n        cache_file,\\n        times=(cache_file.stat().st_atime, cache_file.stat().st_mtime - mtime_offset),\\n    )\\n\\n\\nclass DummyArgs:\\n    \\\"\\\"\\\"Dummy object that sets all kwargs as object properties.\\\"\\\"\\\"\\n\\n    def __init__(self, **kwargs):\\n        self.no_ansi_colors = True\\n\\n        for key, val in kwargs.items():\\n            setattr(self, key, val)\\n\\n\\ndef notices_decorator_assert_message_in_stdout(\\n    captured,\\n    messages: Sequence[str],\\n    dummy_mesg: str | None = None,\\n    not_in: bool = False,\\n):\\n    \\\"\\\"\\\"\\n    Tests a run of notices decorator where we expect to see the messages\\n    print to stdout.\\n    \\\"\\\"\\\"\\n    assert captured.err == \\\"\\\"\\n    assert dummy_mesg in captured.out\\n\\n    for mesg in messages:\\n        if not_in:\\n            assert mesg not in captured.out\\n        else:\\n            assert mesg in captured.out\\n\\n\\nclass MockResponse:\\n    def __init__(self, status_code, json_data, raise_exc=False):\\n        self.status_code = status_code\\n        self.json_data = json_data\\n        self.raise_exc = raise_exc\\n\\n    def json(self):\\n        if self.raise_exc:\\n            raise ValueError(\\\"Error\\\")\\n        return self.json_data\\n\\n\\ndef get_notice_cache_filenames(ctx: Context) -> tuple[str]:\\n    \\\"\\\"\\\"Returns the filenames of the cache files that will be searched for\\\"\\\"\\\"\\n    channel_urls_and_names = get_channel_name_and_urls(get_channel_objs(ctx))\\n\\n    return tuple(\\n        ChannelNoticeResponse.get_cache_key(url, Path(\\\"\\\")).name\\n        for url, name in channel_urls_and_names\\n    )\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Collection of pytest fixtures used in conda.notices tests.\\\"\\\"\\\"\\n\\nfrom pathlib import Path\\nfrom unittest import mock\\n\\nimport pytest\\n\\nfrom ...base.constants import NOTICES_CACHE_SUBDIR\\nfrom ...cli.conda_argparse import generate_parser\\n\\n\\n@pytest.fixture(scope=\\\"function\\\")\\ndef notices_cache_dir(tmpdir):\\n    \\\"\\\"\\\"\\n    Fixture that creates the notices cache dir while also mocking\\n    out a call to user_cache_dir.\\n    \\\"\\\"\\\"\\n    with mock.patch(\\\"conda.notices.cache.user_cache_dir\\\") as user_cache_dir:\\n        user_cache_dir.return_value = tmpdir\\n        cache_dir = Path(tmpdir).joinpath(NOTICES_CACHE_SUBDIR)\\n        cache_dir.mkdir(parents=True, exist_ok=True)\\n\\n        yield cache_dir\\n\\n\\n@pytest.fixture(scope=\\\"function\\\")\\ndef notices_mock_fetch_get_session():\\n    with mock.patch(\\\"conda.notices.fetch.get_session\\\") as mock_get_session:\\n        mock_get_session.return_value = mock.MagicMock()\\n        yield mock_get_session\\n\\n\\n@pytest.fixture(scope=\\\"function\\\")\\ndef conda_notices_args_n_parser():\\n    parser = generate_parser()\\n    args = parser.parse_args([\\\"notices\\\"])\\n\\n    return args, parser\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Collection of pytest fixtures used in conda.gateways tests.\\\"\\\"\\\"\\n\\nimport json\\nimport os\\nimport socket\\nfrom pathlib import Path\\nfrom shutil import which\\n\\nimport pytest\\nfrom xprocess import ProcessStarter\\n\\nMINIO_EXE = which(\\\"minio\\\")\\n\\n\\n# rely on tests not requesting this fixture, and pytest not creating this if\\n# MINIO_EXE was not found\\n@pytest.fixture()\\ndef minio_s3_server(xprocess, tmp_path):\\n    \\\"\\\"\\\"\\n    Mock a local S3 server using `minio`\\n\\n    This requires:\\n    - pytest-xprocess: runs the background process\\n    - minio: the executable must be in PATH\\n\\n    Note, the given S3 server will be EMPTY! The test function needs\\n    to populate it. You can use\\n    `conda.testing.helpers.populate_s3_server` for that.\\n    \\\"\\\"\\\"\\n\\n    class Minio:\\n        # The 'name' below will be the name of the S3 bucket containing\\n        # keys like `noarch/repodata.json`\\n        # see https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html\\n        name = \\\"minio-s3-server\\\"\\n        port = 9000\\n\\n        def __init__(self):\\n            (Path(tmp_path) / self.name).mkdir()\\n\\n        @property\\n        def server_url(self):\\n            return f\\\"{self.endpoint}/{self.name}\\\"\\n\\n        @property\\n        def endpoint(self):\\n            return f\\\"http://localhost:{self.port}\\\"\\n\\n        def populate_bucket(self, endpoint, bucket_name, channel_dir):\\n            \\\"\\\"\\\"Prepare the s3 connection for our minio instance\\\"\\\"\\\"\\n            from boto3.session import Session\\n            from botocore.client import Config\\n\\n            # Make the minio bucket public first\\n            # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/s3-example-bucket-policies.html#set-a-bucket-policy\\n            session = Session()\\n            client = session.client(\\n                \\\"s3\\\",\\n                endpoint_url=endpoint,\\n                aws_access_key_id=\\\"minioadmin\\\",\\n                aws_secret_access_key=\\\"minioadmin\\\",\\n                config=Config(signature_version=\\\"s3v4\\\"),\\n                region_name=\\\"us-east-1\\\",\\n            )\\n            bucket_policy = json.dumps(\\n                {\\n                    \\\"Version\\\": \\\"2012-10-17\\\",\\n                    \\\"Statement\\\": [\\n                        {\\n                            \\\"Sid\\\": \\\"AddPerm\\\",\\n                            \\\"Effect\\\": \\\"Allow\\\",\\n                            \\\"Principal\\\": \\\"*\\\",\\n                            \\\"Action\\\": [\\\"s3:GetObject\\\"],\\n                            \\\"Resource\\\": f\\\"arn:aws:s3:::{bucket_name}/*\\\",\\n                        }\\n                    ],\\n                }\\n            )\\n            client.put_bucket_policy(Bucket=bucket_name, Policy=bucket_policy)\\n\\n            # Minio has to start with an empty directory; once available,\\n            # we can import all channel files by \\\"uploading\\\" them\\n            for current, _, files in os.walk(channel_dir):\\n                for f in files:\\n                    path = Path(current, f)\\n                    key = path.relative_to(channel_dir)\\n                    client.upload_file(\\n                        str(path),\\n                        bucket_name,\\n                        str(key).replace(\\\"\\\\\\\\\\\", \\\"/\\\"),  # MinIO expects Unix paths\\n                        ExtraArgs={\\\"ACL\\\": \\\"public-read\\\"},\\n                    )\\n\\n    print(\\\"Starting mock_s3_server\\\")\\n    minio = Minio()\\n\\n    class Starter(ProcessStarter):\\n        pattern = \\\"MinIO Object Storage Server\\\"\\n        terminate_on_interrupt = True\\n        timeout = 10\\n        args = [\\n            MINIO_EXE,\\n            \\\"server\\\",\\n            f\\\"--address=:{minio.port}\\\",\\n            tmp_path,\\n        ]\\n\\n        def startup_check(self, port=minio.port):\\n            s = socket.socket()\\n            address = \\\"localhost\\\"\\n            error = False\\n            try:\\n                s.connect((address, port))\\n            except Exception as e:\\n                print(\\n                    \\\"something's wrong with %s:%d. Exception is %s\\\" % (address, port, e)\\n                )\\n                error = True\\n            finally:\\n                s.close()\\n\\n            return not error\\n\\n    # ensure process is running and return its logfile\\n    pid, logfile = xprocess.ensure(minio.name, Starter)\\n    print(f\\\"Server (PID: {pid}) log file can be found here: {logfile}\\\")\\n    yield minio\\n    xprocess.getinfo(minio.name).terminate()\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Implements object describing a symbolic link from the base environment to a private environment.\\n\\nSince private environments are an unrealized feature of conda and has been deprecated this data\\nmodel no longer serves a purpose and has also been deprecated.\\n\\\"\\\"\\\"\\n\\nfrom logging import getLogger\\n\\nfrom ..auxlib.entity import Entity, EnumField, StringField\\nfrom ..deprecations import deprecated\\nfrom .enums import LeasedPathType\\n\\nlog = getLogger(__name__)\\n\\n\\n@deprecated(\\\"24.3\\\", \\\"24.9\\\")\\nclass LeasedPathEntry(Entity):\\n    \\\"\\\"\\\"\\n    _path: short path for the leased path, using forward slashes\\n    target_path: the full path to the executable in the private env\\n    target_prefix: the full path to the private environment\\n    leased_path: the full path for the lease in the root prefix\\n    package_name: the package holding the lease\\n    leased_path_type: application_entry_point\\n\\n    \\\"\\\"\\\"\\n\\n    _path = StringField()\\n    target_path = StringField()\\n    target_prefix = StringField()\\n    leased_path = StringField()\\n    package_name = StringField()\\n    leased_path_type = EnumField(LeasedPathType)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Implements the version spec with parsing and comparison logic.\\n\\nObject inheritance:\\n\\n.. autoapi-inheritance-diagram:: BaseSpec VersionSpec BuildNumberMatch\\n   :top-classes: conda.models.version.BaseSpec\\n   :parts: 1\\n\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport operator as op\\nimport re\\nfrom itertools import zip_longest\\nfrom logging import getLogger\\n\\nfrom ..exceptions import InvalidVersionSpec\\n\\nlog = getLogger(__name__)\\n\\n\\ndef normalized_version(version: str) -> VersionOrder:\\n    \\\"\\\"\\\"Parse a version string and return VersionOrder object.\\\"\\\"\\\"\\n    return VersionOrder(version)\\n\\n\\ndef ver_eval(vtest, spec):\\n    return VersionSpec(spec).match(vtest)\\n\\n\\nversion_check_re = re.compile(r\\\"^[\\\\*\\\\.\\\\+!_0-9a-z]+$\\\")\\nversion_split_re = re.compile(\\\"([0-9]+|[*]+|[^0-9*]+)\\\")\\nversion_cache = {}\\n\\n\\nclass SingleStrArgCachingType(type):\\n    def __call__(cls, arg):\\n        if isinstance(arg, cls):\\n            return arg\\n        elif isinstance(arg, str):\\n            try:\\n                return cls._cache_[arg]\\n            except KeyError:\\n                val = cls._cache_[arg] = super().__call__(arg)\\n                return val\\n        else:\\n            return super().__call__(arg)\\n\\n\\nclass VersionOrder(metaclass=SingleStrArgCachingType):\\n    \\\"\\\"\\\"Implement an order relation between version strings.\\n\\n    Version strings can contain the usual alphanumeric characters\\n    (A-Za-z0-9), separated into components by dots and underscores. Empty\\n    segments (i.e. two consecutive dots, a leading/trailing underscore)\\n    are not permitted. An optional epoch number - an integer\\n    followed by '!' - can proceed the actual version string\\n    (this is useful to indicate a change in the versioning\\n    scheme itself). Version comparison is case-insensitive.\\n\\n    Conda supports six types of version strings:\\n    * Release versions contain only integers, e.g. '1.0', '2.3.5'.\\n    * Pre-release versions use additional letters such as 'a' or 'rc',\\n      for example '1.0a1', '1.2.beta3', '2.3.5rc3'.\\n    * Development versions are indicated by the string 'dev',\\n      for example '1.0dev42', '2.3.5.dev12'.\\n    * Post-release versions are indicated by the string 'post',\\n      for example '1.0post1', '2.3.5.post2'.\\n    * Tagged versions have a suffix that specifies a particular\\n      property of interest, e.g. '1.1.parallel'. Tags can be added\\n      to any of the preceding four types. As far as sorting is concerned,\\n      tags are treated like strings in pre-release versions.\\n    * An optional local version string separated by '+' can be appended\\n      to the main (upstream) version string. It is only considered\\n      in comparisons when the main versions are equal, but otherwise\\n      handled in exactly the same manner.\\n\\n    To obtain a predictable version ordering, it is crucial to keep the\\n    version number scheme of a given package consistent over time.\\n    Specifically,\\n    * version strings should always have the same number of components\\n      (except for an optional tag suffix or local version string),\\n    * letters/strings indicating non-release versions should always\\n      occur at the same position.\\n\\n    Before comparison, version strings are parsed as follows:\\n    * They are first split into epoch, version number, and local version\\n      number at '!' and '+' respectively. If there is no '!', the epoch is\\n      set to 0. If there is no '+', the local version is empty.\\n    * The version part is then split into components at '.' and '_'.\\n    * Each component is split again into runs of numerals and non-numerals\\n    * Subcomponents containing only numerals are converted to integers.\\n    * Strings are converted to lower case, with special treatment for 'dev'\\n      and 'post'.\\n    * When a component starts with a letter, the fillvalue 0 is inserted\\n      to keep numbers and strings in phase, resulting in '1.1.a1' == 1.1.0a1'.\\n    * The same is repeated for the local version part.\\n\\n    Examples:\\n        1.2g.beta15.rc  =>  [[0], [1], [2, 'g'], [0, 'beta', 15], [0, 'rc']]\\n        1!2.15.1_ALPHA  =>  [[1], [2], [15], [1, '_alpha']]\\n\\n    The resulting lists are compared lexicographically, where the following\\n    rules are applied to each pair of corresponding subcomponents:\\n    * integers are compared numerically\\n    * strings are compared lexicographically, case-insensitive\\n    * strings are smaller than integers, except\\n    * 'dev' versions are smaller than all corresponding versions of other types\\n    * 'post' versions are greater than all corresponding versions of other types\\n    * if a subcomponent has no correspondent, the missing correspondent is\\n      treated as integer 0 to ensure '1.1' == '1.1.0'.\\n\\n    The resulting order is:\\n           0.4\\n         < 0.4.0\\n         < 0.4.1.rc\\n        == 0.4.1.RC   # case-insensitive comparison\\n         < 0.4.1\\n         < 0.5a1\\n         < 0.5b3\\n         < 0.5C1      # case-insensitive comparison\\n         < 0.5\\n         < 0.9.6\\n         < 0.960923\\n         < 1.0\\n         < 1.1dev1    # special case 'dev'\\n         < 1.1_       # appended underscore is special case for openssl-like versions\\n         < 1.1a1\\n         < 1.1.0dev1  # special case 'dev'\\n        == 1.1.dev1   # 0 is inserted before string\\n         < 1.1.a1\\n         < 1.1.0rc1\\n         < 1.1.0\\n        == 1.1\\n         < 1.1.0post1 # special case 'post'\\n        == 1.1.post1  # 0 is inserted before string\\n         < 1.1post1   # special case 'post'\\n         < 1996.07.12\\n         < 1!0.4.1    # epoch increased\\n         < 1!3.1.1.6\\n         < 2!0.4.1    # epoch increased again\\n\\n    Some packages (most notably openssl) have incompatible version conventions.\\n    In particular, openssl interprets letters as version counters rather than\\n    pre-release identifiers. For openssl, the relation\\n\\n      1.0.1 < 1.0.1a  =>  False  # should be true for openssl\\n\\n    holds, whereas conda packages use the opposite ordering. You can work-around\\n    this problem by appending an underscore to plain version numbers:\\n\\n      1.0.1_ < 1.0.1a =>  True   # ensure correct ordering for openssl\\n    \\\"\\\"\\\"\\n\\n    _cache_ = {}\\n\\n    def __init__(self, vstr: str):\\n        # version comparison is case-insensitive\\n        version = vstr.strip().rstrip().lower()\\n        # basic validity checks\\n        if version == \\\"\\\":\\n            raise InvalidVersionSpec(vstr, \\\"empty version string\\\")\\n        invalid = not version_check_re.match(version)\\n        if invalid and \\\"-\\\" in version and \\\"_\\\" not in version:\\n            # Allow for dashes as long as there are no underscores\\n            # as well, by converting the former to the latter.\\n            version = version.replace(\\\"-\\\", \\\"_\\\")\\n            invalid = not version_check_re.match(version)\\n        if invalid:\\n            raise InvalidVersionSpec(vstr, \\\"invalid character(s)\\\")\\n\\n        # when fillvalue ==  0  =>  1.1 == 1.1.0\\n        # when fillvalue == -1  =>  1.1  < 1.1.0\\n        self.norm_version = version\\n        self.fillvalue = 0\\n\\n        # find epoch\\n        version = version.split(\\\"!\\\")\\n        if len(version) == 1:\\n            # epoch not given => set it to '0'\\n            epoch = [\\\"0\\\"]\\n        elif len(version) == 2:\\n            # epoch given, must be an integer\\n            if not version[0].isdigit():\\n                raise InvalidVersionSpec(vstr, \\\"epoch must be an integer\\\")\\n            epoch = [version[0]]\\n        else:\\n            raise InvalidVersionSpec(vstr, \\\"duplicated epoch separator '!'\\\")\\n\\n        # find local version string\\n        version = version[-1].split(\\\"+\\\")\\n        if len(version) == 1:\\n            # no local version\\n            self.local = []\\n        # Case 2: We have a local version component in version[1]\\n        elif len(version) == 2:\\n            # local version given\\n            self.local = version[1].replace(\\\"_\\\", \\\".\\\").split(\\\".\\\")\\n        else:\\n            raise InvalidVersionSpec(vstr, \\\"duplicated local version separator '+'\\\")\\n\\n        # Error Case: Version is empty because the version string started with +.\\n        # e.g. \\\"+\\\", \\\"1.2\\\", \\\"+a\\\", \\\"+1\\\".\\n        # This is an error because specifying only a local version is invalid.\\n        # version[0] is empty because vstr.split(\\\"+\\\") returns something like ['', '1.2']\\n        if version[0] == \\\"\\\":\\n            raise InvalidVersionSpec(\\n                vstr, \\\"Missing version before local version separator '+'\\\"\\n            )\\n\\n        if version[0][-1] == \\\"_\\\":\\n            # If the last character of version is \\\"-\\\" or \\\"_\\\", don't split that out\\n            # individually. Implements the instructions for openssl-like versions\\n            #   > You can work-around this problem by appending a dash to plain version numbers\\n            split_version = version[0][:-1].replace(\\\"_\\\", \\\".\\\").split(\\\".\\\")\\n            split_version[-1] += \\\"_\\\"\\n        else:\\n            split_version = version[0].replace(\\\"_\\\", \\\".\\\").split(\\\".\\\")\\n        self.version = epoch + split_version\\n\\n        # split components into runs of numerals and non-numerals,\\n        # convert numerals to int, handle special strings\\n        for v in (self.version, self.local):\\n            for k in range(len(v)):\\n                c = version_split_re.findall(v[k])\\n                if not c:\\n                    raise InvalidVersionSpec(vstr, \\\"empty version component\\\")\\n                for j in range(len(c)):\\n                    if c[j].isdigit():\\n                        c[j] = int(c[j])\\n                    elif c[j] == \\\"post\\\":\\n                        # ensure number < 'post' == infinity\\n                        c[j] = float(\\\"inf\\\")\\n                    elif c[j] == \\\"dev\\\":\\n                        # ensure '*' < 'DEV' < '_' < 'a' < number\\n                        # by upper-casing (all other strings are lower case)\\n                        c[j] = \\\"DEV\\\"\\n                if v[k][0].isdigit():\\n                    v[k] = c\\n                else:\\n                    # components shall start with a number to keep numbers and\\n                    # strings in phase => prepend fillvalue\\n                    v[k] = [self.fillvalue] + c\\n\\n    def __str__(self) -> str:\\n        return self.norm_version\\n\\n    def __repr__(self) -> str:\\n        return f'{self.__class__.__name__}(\\\"{self}\\\")'\\n\\n    def _eq(self, t1: list[str], t2: list[str]) -> bool:\\n        for v1, v2 in zip_longest(t1, t2, fillvalue=[]):\\n            for c1, c2 in zip_longest(v1, v2, fillvalue=self.fillvalue):\\n                if c1 != c2:\\n                    return False\\n        return True\\n\\n    def __eq__(self, other: object) -> bool:\\n        if not isinstance(other, VersionOrder):\\n            return False\\n        return self._eq(self.version, other.version) and self._eq(\\n            self.local, other.local\\n        )\\n\\n    def startswith(self, other: object) -> bool:\\n        if not isinstance(other, VersionOrder):\\n            return False\\n        # Tests if the version lists match up to the last element in \\\"other\\\".\\n        if other.local:\\n            if not self._eq(self.version, other.version):\\n                return False\\n            t1 = self.local\\n            t2 = other.local\\n        else:\\n            t1 = self.version\\n            t2 = other.version\\n        nt = len(t2) - 1\\n        if not self._eq(t1[:nt], t2[:nt]):\\n            return False\\n        v1 = [] if len(t1) <= nt else t1[nt]\\n        v2 = t2[nt]\\n        nt = len(v2) - 1\\n        if not self._eq([v1[:nt]], [v2[:nt]]):\\n            return False\\n        c1 = self.fillvalue if len(v1) <= nt else v1[nt]\\n        c2 = v2[nt]\\n        if isinstance(c2, str):\\n            return isinstance(c1, str) and c1.startswith(c2)\\n        return c1 == c2\\n\\n    def __ne__(self, other: object) -> bool:\\n        return not (self == other)\\n\\n    def __lt__(self, other: object) -> bool:\\n        if not isinstance(other, VersionOrder):\\n            return False\\n        for t1, t2 in zip([self.version, self.local], [other.version, other.local]):\\n            for v1, v2 in zip_longest(t1, t2, fillvalue=[]):\\n                for c1, c2 in zip_longest(v1, v2, fillvalue=self.fillvalue):\\n                    if c1 == c2:\\n                        continue\\n                    elif isinstance(c1, str):\\n                        if not isinstance(c2, str):\\n                            # str < int\\n                            return True\\n                    elif isinstance(c2, str):\\n                        # not (int < str)\\n                        return False\\n                    # c1 and c2 have the same type\\n                    return c1 < c2\\n        # self == other\\n        return False\\n\\n    def __gt__(self, other: object) -> bool:\\n        return other < self\\n\\n    def __le__(self, other: object) -> bool:\\n        return not (other < self)\\n\\n    def __ge__(self, other: object) -> bool:\\n        return not (self < other)\\n\\n\\n# each token slurps up leading whitespace, which we strip out.\\nVSPEC_TOKENS = (\\n    r\\\"\\\\s*\\\\^[^$]*[$]|\\\"  # regexes\\n    r\\\"\\\\s*[()|,]|\\\"  # parentheses, logical and, logical or\\n    r\\\"[^()|,]+\\\"\\n)  # everything else\\n\\n\\ndef treeify(spec_str):\\n    \\\"\\\"\\\"\\n    Examples:\\n        >>> treeify(\\\"1.2.3\\\")\\n        '1.2.3'\\n        >>> treeify(\\\"1.2.3,>4.5.6\\\")\\n        (',', '1.2.3', '>4.5.6')\\n        >>> treeify(\\\"1.2.3,4.5.6|<=7.8.9\\\")\\n        ('|', (',', '1.2.3', '4.5.6'), '<=7.8.9')\\n        >>> treeify(\\\"(1.2.3|4.5.6),<=7.8.9\\\")\\n        (',', ('|', '1.2.3', '4.5.6'), '<=7.8.9')\\n        >>> treeify(\\\"((1.5|((1.6|1.7), 1.8), 1.9 |2.0))|2.1\\\")\\n        ('|', '1.5', (',', ('|', '1.6', '1.7'), '1.8', '1.9'), '2.0', '2.1')\\n        >>> treeify(\\\"1.5|(1.6|1.7),1.8,1.9|2.0|2.1\\\")\\n        ('|', '1.5', (',', ('|', '1.6', '1.7'), '1.8', '1.9'), '2.0', '2.1')\\n    \\\"\\\"\\\"\\n    # Converts a VersionSpec expression string into a tuple-based\\n    # expression tree.\\n    assert isinstance(spec_str, str)\\n    tokens = re.findall(VSPEC_TOKENS, f\\\"({spec_str})\\\")\\n    output = []\\n    stack = []\\n\\n    def apply_ops(cstop):\\n        # cstop: operators with lower precedence\\n        while stack and stack[-1] not in cstop:\\n            if len(output) < 2:\\n                raise InvalidVersionSpec(spec_str, \\\"cannot join single expression\\\")\\n            c = stack.pop()\\n            r = output.pop()\\n            # Fuse expressions with the same operator; e.g.,\\n            #   ('|', ('|', a, b), ('|', c, d))becomes\\n            #   ('|', a, b, c d)\\n            # We're playing a bit of a trick here. Instead of checking\\n            # if the left or right entries are tuples, we're counting\\n            # on the fact that if we _do_ see a string instead, its\\n            # first character cannot possibly be equal to the operator.\\n            r = r[1:] if r[0] == c else (r,)\\n            left = output.pop()\\n            left = left[1:] if left[0] == c else (left,)\\n            output.append((c,) + left + r)\\n\\n    for item in tokens:\\n        item = item.strip()\\n        if item == \\\"|\\\":\\n            apply_ops(\\\"(\\\")\\n            stack.append(\\\"|\\\")\\n        elif item == \\\",\\\":\\n            apply_ops(\\\"|(\\\")\\n            stack.append(\\\",\\\")\\n        elif item == \\\"(\\\":\\n            stack.append(\\\"(\\\")\\n        elif item == \\\")\\\":\\n            apply_ops(\\\"(\\\")\\n            if not stack or stack[-1] != \\\"(\\\":\\n                raise InvalidVersionSpec(spec_str, \\\"expression must start with '('\\\")\\n            stack.pop()\\n        else:\\n            output.append(item)\\n    if stack:\\n        raise InvalidVersionSpec(\\n            spec_str, f\\\"unable to convert to expression tree: {stack}\\\"\\n        )\\n    if not output:\\n        raise InvalidVersionSpec(spec_str, \\\"unable to determine version from spec\\\")\\n    return output[0]\\n\\n\\ndef untreeify(spec, _inand=False, depth=0):\\n    \\\"\\\"\\\"\\n    Examples:\\n        >>> untreeify('1.2.3')\\n        '1.2.3'\\n        >>> untreeify((',', '1.2.3', '>4.5.6'))\\n        '1.2.3,>4.5.6'\\n        >>> untreeify(('|', (',', '1.2.3', '4.5.6'), '<=7.8.9'))\\n        '(1.2.3,4.5.6)|<=7.8.9'\\n        >>> untreeify((',', ('|', '1.2.3', '4.5.6'), '<=7.8.9'))\\n        '(1.2.3|4.5.6),<=7.8.9'\\n        >>> untreeify(('|', '1.5', (',', ('|', '1.6', '1.7'), '1.8', '1.9'), '2.0', '2.1'))\\n        '1.5|((1.6|1.7),1.8,1.9)|2.0|2.1'\\n    \\\"\\\"\\\"\\n    if isinstance(spec, tuple):\\n        if spec[0] == \\\"|\\\":\\n            res = \\\"|\\\".join(map(lambda x: untreeify(x, depth=depth + 1), spec[1:]))\\n            if _inand or depth > 0:\\n                res = f\\\"({res})\\\"\\n        else:\\n            res = \\\",\\\".join(\\n                map(lambda x: untreeify(x, _inand=True, depth=depth + 1), spec[1:])\\n            )\\n            if depth > 0:\\n                res = f\\\"({res})\\\"\\n        return res\\n    return spec\\n\\n\\ndef compatible_release_operator(x, y):\\n    return op.__ge__(x, y) and x.startswith(\\n        VersionOrder(\\\".\\\".join(str(y).split(\\\".\\\")[:-1]))\\n    )\\n\\n\\n# This RE matches the operators '==', '!=', '<=', '>=', '<', '>'\\n# followed by a version string. It rejects expressions like\\n# '<= 1.2' (space after operator), '<>1.2' (unknown operator),\\n# and '<=!1.2' (nonsensical operator).\\nversion_relation_re = re.compile(r\\\"^(=|==|!=|<=|>=|<|>|~=)(?![=<>!~])(\\\\S+)$\\\")\\nregex_split_re = re.compile(r\\\".*[()|,^$]\\\")\\nOPERATOR_MAP = {\\n    \\\"==\\\": op.__eq__,\\n    \\\"!=\\\": op.__ne__,\\n    \\\"<=\\\": op.__le__,\\n    \\\">=\\\": op.__ge__,\\n    \\\"<\\\": op.__lt__,\\n    \\\">\\\": op.__gt__,\\n    \\\"=\\\": lambda x, y: x.startswith(y),\\n    \\\"!=startswith\\\": lambda x, y: not x.startswith(y),\\n    \\\"~=\\\": compatible_release_operator,\\n}\\nOPERATOR_START = frozenset((\\\"=\\\", \\\"<\\\", \\\">\\\", \\\"!\\\", \\\"~\\\"))\\n\\n\\nclass BaseSpec:\\n    def __init__(self, spec_str, matcher, is_exact):\\n        self.spec_str = spec_str\\n        self._is_exact = is_exact\\n        self.match = matcher\\n\\n    @property\\n    def spec(self):\\n        return self.spec_str\\n\\n    def is_exact(self):\\n        return self._is_exact\\n\\n    def __eq__(self, other):\\n        try:\\n            other_spec = other.spec\\n        except AttributeError:\\n            other_spec = self.__class__(other).spec\\n        return self.spec == other_spec\\n\\n    def __ne__(self, other):\\n        return not self.__eq__(other)\\n\\n    def __hash__(self):\\n        return hash(self.spec)\\n\\n    def __str__(self):\\n        return self.spec\\n\\n    def __repr__(self):\\n        return f\\\"{self.__class__.__name__}('{self.spec}')\\\"\\n\\n    @property\\n    def raw_value(self):\\n        return self.spec\\n\\n    @property\\n    def exact_value(self):\\n        return self.is_exact() and self.spec or None\\n\\n    def merge(self, other):\\n        raise NotImplementedError()\\n\\n    def regex_match(self, spec_str):\\n        return bool(self.regex.match(spec_str))\\n\\n    def operator_match(self, spec_str):\\n        return self.operator_func(VersionOrder(str(spec_str)), self.matcher_vo)\\n\\n    def any_match(self, spec_str):\\n        return any(s.match(spec_str) for s in self.tup)\\n\\n    def all_match(self, spec_str):\\n        return all(s.match(spec_str) for s in self.tup)\\n\\n    def exact_match(self, spec_str):\\n        return self.spec == spec_str\\n\\n    def always_true_match(self, spec_str):\\n        return True\\n\\n\\nclass VersionSpec(BaseSpec, metaclass=SingleStrArgCachingType):\\n    _cache_ = {}\\n\\n    def __init__(self, vspec):\\n        vspec_str, matcher, is_exact = self.get_matcher(vspec)\\n        super().__init__(vspec_str, matcher, is_exact)\\n\\n    def get_matcher(self, vspec):\\n        if isinstance(vspec, str) and regex_split_re.match(vspec):\\n            vspec = treeify(vspec)\\n\\n        if isinstance(vspec, tuple):\\n            vspec_tree = vspec\\n            _matcher = self.any_match if vspec_tree[0] == \\\"|\\\" else self.all_match\\n            tup = tuple(VersionSpec(s) for s in vspec_tree[1:])\\n            vspec_str = untreeify((vspec_tree[0],) + tuple(t.spec for t in tup))\\n            self.tup = tup\\n            matcher = _matcher\\n            is_exact = False\\n            return vspec_str, matcher, is_exact\\n\\n        vspec_str = str(vspec).strip()\\n        if vspec_str[0] == \\\"^\\\" or vspec_str[-1] == \\\"$\\\":\\n            if vspec_str[0] != \\\"^\\\" or vspec_str[-1] != \\\"$\\\":\\n                raise InvalidVersionSpec(\\n                    vspec_str, \\\"regex specs must start with '^' and end with '$'\\\"\\n                )\\n            self.regex = re.compile(vspec_str)\\n            matcher = self.regex_match\\n            is_exact = False\\n        elif vspec_str[0] in OPERATOR_START:\\n            m = version_relation_re.match(vspec_str)\\n            if m is None:\\n                raise InvalidVersionSpec(vspec_str, \\\"invalid operator\\\")\\n            operator_str, vo_str = m.groups()\\n            if vo_str[-2:] == \\\".*\\\":\\n                if operator_str in (\\\"=\\\", \\\">=\\\"):\\n                    vo_str = vo_str[:-2]\\n                elif operator_str == \\\"!=\\\":\\n                    vo_str = vo_str[:-2]\\n                    operator_str = \\\"!=startswith\\\"\\n                elif operator_str == \\\"~=\\\":\\n                    raise InvalidVersionSpec(vspec_str, \\\"invalid operator with '.*'\\\")\\n                else:\\n                    log.warning(\\n                        \\\"Using .* with relational operator is superfluous and deprecated \\\"\\n                        \\\"and will be removed in a future version of conda. Your spec was \\\"\\n                        f\\\"{vo_str}, but conda is ignoring the .* and treating it as {vo_str[:-2]}\\\"\\n                    )\\n                    vo_str = vo_str[:-2]\\n            try:\\n                self.operator_func = OPERATOR_MAP[operator_str]\\n            except KeyError:\\n                raise InvalidVersionSpec(vspec_str, f\\\"invalid operator: {operator_str}\\\")\\n            self.matcher_vo = VersionOrder(vo_str)\\n            matcher = self.operator_match\\n            is_exact = operator_str == \\\"==\\\"\\n        elif vspec_str == \\\"*\\\":\\n            matcher = self.always_true_match\\n            is_exact = False\\n        elif \\\"*\\\" in vspec_str.rstrip(\\\"*\\\"):\\n            rx = vspec_str.replace(\\\".\\\", r\\\"\\\\.\\\").replace(\\\"+\\\", r\\\"\\\\+\\\").replace(\\\"*\\\", r\\\".*\\\")\\n            rx = rf\\\"^(?:{rx})$\\\"\\n\\n            self.regex = re.compile(rx)\\n            matcher = self.regex_match\\n            is_exact = False\\n        elif vspec_str[-1] == \\\"*\\\":\\n            if vspec_str[-2:] != \\\".*\\\":\\n                vspec_str = vspec_str[:-1] + \\\".*\\\"\\n\\n            # if vspec_str[-1] in OPERATOR_START:\\n            #     m = version_relation_re.match(vspec_str)\\n            #     if m is None:\\n            #         raise InvalidVersionSpecError(vspec_str)\\n            #     operator_str, vo_str = m.groups()\\n            #\\n            #\\n            # else:\\n            #     pass\\n\\n            vo_str = vspec_str.rstrip(\\\"*\\\").rstrip(\\\".\\\")\\n            self.operator_func = VersionOrder.startswith\\n            self.matcher_vo = VersionOrder(vo_str)\\n            matcher = self.operator_match\\n            is_exact = False\\n        elif \\\"@\\\" not in vspec_str:\\n            self.operator_func = OPERATOR_MAP[\\\"==\\\"]\\n            self.matcher_vo = VersionOrder(vspec_str)\\n            matcher = self.operator_match\\n            is_exact = True\\n        else:\\n            matcher = self.exact_match\\n            is_exact = True\\n        return vspec_str, matcher, is_exact\\n\\n    def merge(self, other):\\n        assert isinstance(other, self.__class__)\\n        return self.__class__(\\\",\\\".join(sorted((self.raw_value, other.raw_value))))\\n\\n    def union(self, other):\\n        assert isinstance(other, self.__class__)\\n        options = {self.raw_value, other.raw_value}\\n        # important: we only return a string here because the parens get gobbled otherwise\\n        #    this info is for visual display only, not for feeding into actual matches\\n        return \\\"|\\\".join(sorted(options))\\n\\n\\n# TODO: someday switch out these class names for consistency\\nVersionMatch = VersionSpec\\n\\n\\nclass BuildNumberMatch(BaseSpec, metaclass=SingleStrArgCachingType):\\n    _cache_ = {}\\n\\n    def __init__(self, vspec):\\n        vspec_str, matcher, is_exact = self.get_matcher(vspec)\\n        super().__init__(vspec_str, matcher, is_exact)\\n\\n    def get_matcher(self, vspec):\\n        try:\\n            vspec = int(vspec)\\n        except ValueError:\\n            pass\\n        else:\\n            matcher = self.exact_match\\n            is_exact = True\\n            return vspec, matcher, is_exact\\n\\n        vspec_str = str(vspec).strip()\\n        if vspec_str == \\\"*\\\":\\n            matcher = self.always_true_match\\n            is_exact = False\\n        elif vspec_str.startswith((\\\"=\\\", \\\"<\\\", \\\">\\\", \\\"!\\\")):\\n            m = version_relation_re.match(vspec_str)\\n            if m is None:\\n                raise InvalidVersionSpec(vspec_str, \\\"invalid operator\\\")\\n            operator_str, vo_str = m.groups()\\n            try:\\n                self.operator_func = OPERATOR_MAP[operator_str]\\n            except KeyError:\\n                raise InvalidVersionSpec(vspec_str, f\\\"invalid operator: {operator_str}\\\")\\n            self.matcher_vo = VersionOrder(vo_str)\\n            matcher = self.operator_match\\n\\n            is_exact = operator_str == \\\"==\\\"\\n        elif vspec_str[0] == \\\"^\\\" or vspec_str[-1] == \\\"$\\\":\\n            if vspec_str[0] != \\\"^\\\" or vspec_str[-1] != \\\"$\\\":\\n                raise InvalidVersionSpec(\\n                    vspec_str, \\\"regex specs must start with '^' and end with '$'\\\"\\n                )\\n            self.regex = re.compile(vspec_str)\\n\\n            matcher = self.regex_match\\n            is_exact = False\\n        # if hasattr(spec, 'match'):\\n        #     self.spec = _spec\\n        #     self.match = spec.match\\n        else:\\n            matcher = self.exact_match\\n            is_exact = True\\n        return vspec_str, matcher, is_exact\\n\\n    def merge(self, other):\\n        if self.raw_value != other.raw_value:\\n            raise ValueError(\\n                f\\\"Incompatible component merge:\\\\n  - {self.raw_value!r}\\\\n  - {other.raw_value!r}\\\"\\n            )\\n        return self.raw_value\\n\\n    def union(self, other):\\n        options = {self.raw_value, other.raw_value}\\n        return \\\"|\\\".join(options)\\n\\n    @property\\n    def exact_value(self) -> int | None:\\n        try:\\n            return int(self.raw_value)\\n        except ValueError:\\n            return None\\n\\n    def __str__(self):\\n        return str(self.spec)\\n\\n    def __repr__(self):\\n        return str(self.spec)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Implements the data model for conda packages.\\n\\nA PackageRecord is the record of a package present in a channel. A PackageCache is the record of a\\ndownloaded and cached package. A PrefixRecord is the record of a package installed into a conda\\nenvironment.\\n\\nObject inheritance:\\n\\n.. autoapi-inheritance-diagram:: PackageRecord PackageCacheRecord PrefixRecord\\n   :top-classes: conda.models.records.PackageRecord\\n   :parts: 1\\n\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nfrom os.path import basename, join\\n\\nfrom boltons.timeutils import dt_to_timestamp, isoparse\\n\\nfrom ..auxlib.entity import (\\n    BooleanField,\\n    ComposableField,\\n    DictSafeMixin,\\n    Entity,\\n    EnumField,\\n    IntegerField,\\n    ListField,\\n    NumberField,\\n    StringField,\\n)\\nfrom ..base.context import context\\nfrom ..common.compat import isiterable\\nfrom ..exceptions import PathNotFoundError\\nfrom .channel import Channel\\nfrom .enums import FileMode, LinkType, NoarchType, PackageType, PathType, Platform\\nfrom .match_spec import MatchSpec\\n\\n\\nclass LinkTypeField(EnumField):\\n    def box(self, instance, instance_type, val):\\n        if isinstance(val, str):\\n            val = val.replace(\\\"-\\\", \\\"\\\").replace(\\\"_\\\", \\\"\\\").lower()\\n            if val == \\\"hard\\\":\\n                val = LinkType.hardlink\\n            elif val == \\\"soft\\\":\\n                val = LinkType.softlink\\n        return super().box(instance, instance_type, val)\\n\\n\\nclass NoarchField(EnumField):\\n    def box(self, instance, instance_type, val):\\n        return super().box(instance, instance_type, NoarchType.coerce(val))\\n\\n\\nclass TimestampField(NumberField):\\n    def __init__(self):\\n        super().__init__(default=0, required=False, default_in_dump=False)\\n\\n    @staticmethod\\n    def _make_seconds(val):\\n        if val:\\n            val = val\\n            if val > 253402300799:  # 9999-12-31\\n                val /= (\\n                    1000  # convert milliseconds to seconds; see conda/conda-build#1988\\n                )\\n        return val\\n\\n    @staticmethod\\n    def _make_milliseconds(val):\\n        if val:\\n            if val < 253402300799:  # 9999-12-31\\n                val *= 1000  # convert seconds to milliseconds\\n            val = val\\n        return val\\n\\n    def box(self, instance, instance_type, val):\\n        return self._make_seconds(super().box(instance, instance_type, val))\\n\\n    def dump(self, instance, instance_type, val):\\n        return int(\\n            self._make_milliseconds(super().dump(instance, instance_type, val))\\n        )  # whether in seconds or milliseconds, type must be int (not float) for backward compat\\n\\n    def __get__(self, instance, instance_type):\\n        try:\\n            return super().__get__(instance, instance_type)\\n        except AttributeError:\\n            try:\\n                return int(dt_to_timestamp(isoparse(instance.date)))\\n            except (AttributeError, ValueError):\\n                return 0\\n\\n\\nclass Link(DictSafeMixin, Entity):\\n    source = StringField()\\n    type = LinkTypeField(LinkType, required=False)\\n\\n\\nEMPTY_LINK = Link(source=\\\"\\\")\\n\\n\\nclass _FeaturesField(ListField):\\n    def __init__(self, **kwargs):\\n        super().__init__(str, **kwargs)\\n\\n    def box(self, instance, instance_type, val):\\n        if isinstance(val, str):\\n            val = val.replace(\\\" \\\", \\\",\\\").split(\\\",\\\")\\n        val = tuple(f for f in (ff.strip() for ff in val) if f)\\n        return super().box(instance, instance_type, val)\\n\\n    def dump(self, instance, instance_type, val):\\n        if isiterable(val):\\n            return \\\" \\\".join(val)\\n        else:\\n            return val or ()  # default value is (), and default_in_dump=False\\n\\n\\nclass ChannelField(ComposableField):\\n    def __init__(self, aliases=()):\\n        super().__init__(Channel, required=False, aliases=aliases)\\n\\n    def dump(self, instance, instance_type, val):\\n        if val:\\n            return str(val)\\n        else:\\n            val = instance.channel  # call __get__\\n            return str(val)\\n\\n    def __get__(self, instance, instance_type):\\n        try:\\n            return super().__get__(instance, instance_type)\\n        except AttributeError:\\n            url = instance.url\\n            return self.unbox(instance, instance_type, Channel(url))\\n\\n\\nclass SubdirField(StringField):\\n    def __init__(self):\\n        super().__init__(required=False)\\n\\n    def __get__(self, instance, instance_type):\\n        try:\\n            return super().__get__(instance, instance_type)\\n        except AttributeError:\\n            try:\\n                url = instance.url\\n            except AttributeError:\\n                url = None\\n            if url:\\n                return self.unbox(instance, instance_type, Channel(url).subdir)\\n\\n            try:\\n                platform, arch = instance.platform.name, instance.arch\\n            except AttributeError:\\n                platform, arch = None, None\\n            if platform and not arch:\\n                return self.unbox(instance, instance_type, \\\"noarch\\\")\\n            elif platform:\\n                if \\\"x86\\\" in arch:\\n                    arch = \\\"64\\\" if \\\"64\\\" in arch else \\\"32\\\"\\n                return self.unbox(instance, instance_type, f\\\"{platform}-{arch}\\\")\\n            else:\\n                return self.unbox(instance, instance_type, context.subdir)\\n\\n\\nclass FilenameField(StringField):\\n    def __init__(self, aliases=()):\\n        super().__init__(required=False, aliases=aliases)\\n\\n    def __get__(self, instance, instance_type):\\n        try:\\n            return super().__get__(instance, instance_type)\\n        except AttributeError:\\n            try:\\n                url = instance.url\\n                fn = Channel(url).package_filename\\n                if not fn:\\n                    raise AttributeError()\\n            except AttributeError:\\n                fn = f\\\"{instance.name}-{instance.version}-{instance.build}\\\"\\n            assert fn\\n            return self.unbox(instance, instance_type, fn)\\n\\n\\nclass PackageTypeField(EnumField):\\n    def __init__(self):\\n        super().__init__(\\n            PackageType,\\n            required=False,\\n            nullable=True,\\n            default=None,\\n            default_in_dump=False,\\n        )\\n\\n    def __get__(self, instance, instance_type):\\n        val = super().__get__(instance, instance_type)\\n        if val is None:\\n            # look in noarch field\\n            noarch_val = instance.noarch\\n            if noarch_val:\\n                type_map = {\\n                    NoarchType.generic: PackageType.NOARCH_GENERIC,\\n                    NoarchType.python: PackageType.NOARCH_PYTHON,\\n                }\\n                val = type_map[NoarchType.coerce(noarch_val)]\\n                val = self.unbox(instance, instance_type, val)\\n        return val\\n\\n\\nclass PathData(Entity):\\n    _path = StringField()\\n    prefix_placeholder = StringField(\\n        required=False, nullable=True, default=None, default_in_dump=False\\n    )\\n    file_mode = EnumField(FileMode, required=False, nullable=True)\\n    no_link = BooleanField(\\n        required=False, nullable=True, default=None, default_in_dump=False\\n    )\\n    path_type = EnumField(PathType)\\n\\n    @property\\n    def path(self):\\n        # because I don't have aliases as an option for entity fields yet\\n        return self._path\\n\\n\\nclass PathDataV1(PathData):\\n    # TODO: sha256 and size_in_bytes should be required for all PathType.hardlink, but not for softlink and directory  # NOQA\\n    sha256 = StringField(required=False, nullable=True)\\n    size_in_bytes = IntegerField(required=False, nullable=True)\\n    inode_paths = ListField(str, required=False, nullable=True)\\n\\n    sha256_in_prefix = StringField(required=False, nullable=True)\\n\\n\\nclass PathsData(Entity):\\n    # from info/paths.json\\n    paths_version = IntegerField()\\n    paths = ListField(PathData)\\n\\n\\nclass PackageRecord(DictSafeMixin, Entity):\\n    name = StringField()\\n    version = StringField()\\n    build = StringField(aliases=(\\\"build_string\\\",))\\n    build_number = IntegerField()\\n\\n    # the canonical code abbreviation for PackageRef is `pref`\\n    # fields required to uniquely identifying a package\\n\\n    channel = ChannelField(aliases=(\\\"schannel\\\",))\\n    subdir = SubdirField()\\n    fn = FilenameField(aliases=(\\\"filename\\\",))\\n\\n    md5 = StringField(\\n        default=None, required=False, nullable=True, default_in_dump=False\\n    )\\n    legacy_bz2_md5 = StringField(\\n        default=None, required=False, nullable=True, default_in_dump=False\\n    )\\n    legacy_bz2_size = IntegerField(required=False, nullable=True, default_in_dump=False)\\n    url = StringField(\\n        default=None, required=False, nullable=True, default_in_dump=False\\n    )\\n    sha256 = StringField(\\n        default=None, required=False, nullable=True, default_in_dump=False\\n    )\\n\\n    @property\\n    def schannel(self):\\n        return self.channel.canonical_name\\n\\n    @property\\n    def _pkey(self):\\n        try:\\n            return self.__pkey\\n        except AttributeError:\\n            __pkey = self.__pkey = [\\n                self.channel.canonical_name,\\n                self.subdir,\\n                self.name,\\n                self.version,\\n                self.build_number,\\n                self.build,\\n            ]\\n            # NOTE: fn is included to distinguish between .conda and .tar.bz2 packages\\n            if context.separate_format_cache:\\n                __pkey.append(self.fn)\\n            self.__pkey = tuple(__pkey)\\n            return self.__pkey\\n\\n    def __hash__(self):\\n        try:\\n            return self._hash\\n        except AttributeError:\\n            self._hash = hash(self._pkey)\\n        return self._hash\\n\\n    def __eq__(self, other):\\n        return self._pkey == other._pkey\\n\\n    def dist_str(self):\\n        return \\\"{}{}::{}-{}-{}\\\".format(\\n            self.channel.canonical_name,\\n            (\\\"/\\\" + self.subdir) if self.subdir else \\\"\\\",\\n            self.name,\\n            self.version,\\n            self.build,\\n        )\\n\\n    def dist_fields_dump(self):\\n        return {\\n            \\\"base_url\\\": self.channel.base_url,\\n            \\\"build_number\\\": self.build_number,\\n            \\\"build_string\\\": self.build,\\n            \\\"channel\\\": self.channel.name,\\n            \\\"dist_name\\\": self.dist_str().split(\\\":\\\")[-1],\\n            \\\"name\\\": self.name,\\n            \\\"platform\\\": self.subdir,\\n            \\\"version\\\": self.version,\\n        }\\n\\n    arch = StringField(required=False, nullable=True)  # so legacy\\n    platform = EnumField(Platform, required=False, nullable=True)  # so legacy\\n\\n    depends = ListField(str, default=())\\n    constrains = ListField(str, default=())\\n\\n    track_features = _FeaturesField(required=False, default=(), default_in_dump=False)\\n    features = _FeaturesField(required=False, default=(), default_in_dump=False)\\n\\n    noarch = NoarchField(\\n        NoarchType, required=False, nullable=True, default=None, default_in_dump=False\\n    )  # TODO: rename to package_type\\n    preferred_env = StringField(\\n        required=False, nullable=True, default=None, default_in_dump=False\\n    )\\n\\n    license = StringField(\\n        required=False, nullable=True, default=None, default_in_dump=False\\n    )\\n    license_family = StringField(\\n        required=False, nullable=True, default=None, default_in_dump=False\\n    )\\n    package_type = PackageTypeField()\\n\\n    @property\\n    def is_unmanageable(self):\\n        return self.package_type in PackageType.unmanageable_package_types()\\n\\n    timestamp = TimestampField()\\n\\n    @property\\n    def combined_depends(self):\\n        from .match_spec import MatchSpec\\n\\n        result = {ms.name: ms for ms in MatchSpec.merge(self.depends)}\\n        for spec in self.constrains or ():\\n            ms = MatchSpec(spec)\\n            result[ms.name] = MatchSpec(ms, optional=(ms.name not in result))\\n        return tuple(result.values())\\n\\n    # the canonical code abbreviation for PackageRecord is `prec`, not to be confused with\\n    # PackageCacheRecord (`pcrec`) or PrefixRecord (`prefix_rec`)\\n    #\\n    # important for \\\"choosing\\\" a package (i.e. the solver), listing packages\\n    # (like search), and for verifying downloads\\n    #\\n    # this is the highest level of the record inheritance model that MatchSpec is designed to\\n    # work with\\n\\n    date = StringField(required=False)\\n    size = IntegerField(required=False)\\n\\n    def __str__(self):\\n        return f\\\"{self.channel.canonical_name}/{self.subdir}::{self.name}=={self.version}={self.build}\\\"\\n\\n    def to_match_spec(self):\\n        return MatchSpec(\\n            channel=self.channel,\\n            subdir=self.subdir,\\n            name=self.name,\\n            version=self.version,\\n            build=self.build,\\n        )\\n\\n    def to_simple_match_spec(self):\\n        return MatchSpec(\\n            name=self.name,\\n            version=self.version,\\n        )\\n\\n    @property\\n    def namekey(self):\\n        return \\\"global:\\\" + self.name\\n\\n    def record_id(self):\\n        # WARNING: This is right now only used in link.py _change_report_str(). It is not\\n        #          the official record_id / uid until it gets namespace.  Even then, we might\\n        #          make the format different.  Probably something like\\n        #              channel_name/subdir:namespace:name-version-build_number-build_string\\n        return f\\\"{self.channel.name}/{self.subdir}::{self.name}-{self.version}-{self.build}\\\"\\n\\n    metadata: set[str]\\n\\n    def __init__(self, *args, **kwargs):\\n        super().__init__(*args, **kwargs)\\n        self.metadata = set()\\n\\n\\nclass Md5Field(StringField):\\n    def __init__(self):\\n        super().__init__(required=False, nullable=True)\\n\\n    def __get__(self, instance, instance_type):\\n        try:\\n            return super().__get__(instance, instance_type)\\n        except AttributeError as e:\\n            try:\\n                return instance._calculate_md5sum()\\n            except PathNotFoundError:\\n                raise e\\n\\n\\nclass PackageCacheRecord(PackageRecord):\\n    package_tarball_full_path = StringField()\\n    extracted_package_dir = StringField()\\n\\n    md5 = Md5Field()\\n\\n    @property\\n    def is_fetched(self):\\n        from ..gateways.disk.read import isfile\\n\\n        return isfile(self.package_tarball_full_path)\\n\\n    @property\\n    def is_extracted(self):\\n        from ..gateways.disk.read import isdir, isfile\\n\\n        epd = self.extracted_package_dir\\n        return isdir(epd) and isfile(join(epd, \\\"info\\\", \\\"index.json\\\"))\\n\\n    @property\\n    def tarball_basename(self):\\n        return basename(self.package_tarball_full_path)\\n\\n    def _calculate_md5sum(self):\\n        memoized_md5 = getattr(self, \\\"_memoized_md5\\\", None)\\n        if memoized_md5:\\n            return memoized_md5\\n\\n        from os.path import isfile\\n\\n        if isfile(self.package_tarball_full_path):\\n            from ..gateways.disk.read import compute_sum\\n\\n            md5sum = compute_sum(self.package_tarball_full_path, \\\"md5\\\")\\n            setattr(self, \\\"_memoized_md5\\\", md5sum)\\n            return md5sum\\n\\n\\nclass PrefixRecord(PackageRecord):\\n    package_tarball_full_path = StringField(required=False)\\n    extracted_package_dir = StringField(required=False)\\n\\n    files = ListField(str, default=(), required=False)\\n    paths_data = ComposableField(\\n        PathsData, required=False, nullable=True, default_in_dump=False\\n    )\\n    link = ComposableField(Link, required=False)\\n    # app = ComposableField(App, required=False)\\n\\n    requested_spec = StringField(required=False)\\n\\n    # There have been requests in the past to save remote server auth\\n    # information with the package.  Open to rethinking that though.\\n    auth = StringField(required=False, nullable=True)\\n\\n    # @classmethod\\n    # def load(cls, conda_meta_json_path):\\n    #     return cls()\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"(Legacy) Low-level implementation of a PackageRecord.\\\"\\\"\\\"\\n\\nfrom logging import getLogger\\n\\nfrom ..auxlib.entity import (\\n    ComposableField,\\n    Entity,\\n    EnumField,\\n    ImmutableEntity,\\n    IntegerField,\\n    ListField,\\n    StringField,\\n)\\nfrom .channel import Channel\\nfrom .enums import NoarchType\\nfrom .records import PackageRecord, PathsData\\n\\nlog = getLogger(__name__)\\n\\n\\nclass NoarchField(EnumField):\\n    def box(self, instance, instance_type, val):\\n        return super().box(instance, instance_type, NoarchType.coerce(val))\\n\\n\\nclass Noarch(Entity):\\n    type = NoarchField(NoarchType)\\n    entry_points = ListField(\\n        str, required=False, nullable=True, default=None, default_in_dump=False\\n    )\\n\\n\\nclass PreferredEnv(Entity):\\n    name = StringField()\\n    executable_paths = ListField(str, required=False, nullable=True)\\n    softlink_paths = ListField(str, required=False, nullable=True)\\n\\n\\nclass PackageMetadata(Entity):\\n    # from info/package_metadata.json\\n    package_metadata_version = IntegerField()\\n    noarch = ComposableField(Noarch, required=False, nullable=True)\\n    preferred_env = ComposableField(\\n        PreferredEnv, required=False, nullable=True, default=None, default_in_dump=False\\n    )\\n\\n\\nclass PackageInfo(ImmutableEntity):\\n    # attributes external to the package tarball\\n    extracted_package_dir = StringField()\\n    package_tarball_full_path = StringField()\\n    channel = ComposableField(Channel)\\n    repodata_record = ComposableField(PackageRecord)\\n    url = StringField()\\n\\n    # attributes within the package tarball\\n    icondata = StringField(required=False, nullable=True)\\n    package_metadata = ComposableField(PackageMetadata, required=False, nullable=True)\\n    paths_data = ComposableField(PathsData)\\n\\n    def dist_str(self):\\n        return f\\\"{self.channel.canonical_name}::{self.name}-{self.version}-{self.build}\\\"\\n\\n    @property\\n    def name(self):\\n        return self.repodata_record.name\\n\\n    @property\\n    def version(self):\\n        return self.repodata_record.version\\n\\n    @property\\n    def build(self):\\n        return self.repodata_record.build\\n\\n    @property\\n    def build_number(self):\\n        return self.repodata_record.build_number\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"(Legacy) Low-level implementation of a Channel.\\\"\\\"\\\"\\n\\nimport re\\nfrom logging import getLogger\\nfrom typing import NamedTuple\\n\\nfrom .. import CondaError\\nfrom ..auxlib.entity import Entity, EntityType, IntegerField, StringField\\nfrom ..base.constants import (\\n    CONDA_PACKAGE_EXTENSIONS,\\n    DEFAULTS_CHANNEL_NAME,\\n    UNKNOWN_CHANNEL,\\n)\\nfrom ..base.context import context\\nfrom ..common.compat import ensure_text_type\\nfrom ..common.constants import NULL\\nfrom ..common.url import has_platform, is_url, join_url\\nfrom ..deprecations import deprecated\\nfrom .channel import Channel\\nfrom .package_info import PackageInfo\\nfrom .records import PackageRecord\\n\\nlog = getLogger(__name__)\\n\\n\\nclass DistDetails(NamedTuple):\\n    name: str\\n    version: str\\n    build_string: str\\n    build_number: str\\n    dist_name: str\\n    fmt: str\\n\\n\\ndeprecated.constant(\\n    \\\"24.3\\\",\\n    \\\"24.9\\\",\\n    \\\"IndexRecord\\\",\\n    PackageRecord,\\n    addendum=\\\"Use `conda.models.records.PackageRecord` instead.\\\",\\n)\\n\\n\\nclass DistType(EntityType):\\n    def __call__(cls, *args, **kwargs):\\n        if len(args) == 1 and not kwargs:\\n            value = args[0]\\n            if value in Dist._cache_:\\n                return Dist._cache_[value]\\n            elif isinstance(value, Dist):\\n                dist = value\\n            elif isinstance(value, PackageRecord):\\n                dist = Dist.from_string(\\n                    value.fn, channel_override=value.channel.canonical_name\\n                )\\n            elif hasattr(value, \\\"dist\\\") and isinstance(value.dist, Dist):\\n                dist = value.dist\\n            elif isinstance(value, PackageInfo):\\n                dist = Dist.from_string(\\n                    value.repodata_record.fn,\\n                    channel_override=value.channel.canonical_name,\\n                )\\n            elif isinstance(value, Channel):\\n                dist = Dist.from_url(value.url())\\n            else:\\n                dist = Dist.from_string(value)\\n            Dist._cache_[value] = dist\\n            return dist\\n        else:\\n            return super().__call__(*args, **kwargs)\\n\\n\\ndef strip_extension(original_dist):\\n    for ext in CONDA_PACKAGE_EXTENSIONS:\\n        if original_dist.endswith(ext):\\n            original_dist = original_dist[: -len(ext)]\\n    return original_dist\\n\\n\\ndef split_extension(original_dist):\\n    stripped = strip_extension(original_dist)\\n    return stripped, original_dist[len(stripped) :]\\n\\n\\nclass Dist(Entity, metaclass=DistType):\\n    _cache_ = {}\\n    _lazy_validate = True\\n\\n    channel = StringField(required=False, nullable=True, immutable=True)\\n\\n    dist_name = StringField(immutable=True)\\n    name = StringField(immutable=True)\\n    fmt = StringField(immutable=True)\\n    version = StringField(immutable=True)\\n    build_string = StringField(immutable=True)\\n    build_number = IntegerField(immutable=True)\\n\\n    base_url = StringField(required=False, nullable=True, immutable=True)\\n    platform = StringField(required=False, nullable=True, immutable=True)\\n\\n    def __init__(\\n        self,\\n        channel,\\n        dist_name=None,\\n        name=None,\\n        version=None,\\n        build_string=None,\\n        build_number=None,\\n        base_url=None,\\n        platform=None,\\n        fmt=\\\".tar.bz2\\\",\\n    ):\\n        super().__init__(\\n            channel=channel,\\n            dist_name=dist_name,\\n            name=name,\\n            version=version,\\n            build_string=build_string,\\n            build_number=build_number,\\n            base_url=base_url,\\n            platform=platform,\\n            fmt=fmt,\\n        )\\n\\n    def to_package_ref(self):\\n        return PackageRecord(\\n            channel=self.channel,\\n            subdir=self.platform,\\n            name=self.name,\\n            version=self.version,\\n            build=self.build_string,\\n            build_number=self.build_number,\\n        )\\n\\n    @property\\n    def full_name(self):\\n        return self.__str__()\\n\\n    @property\\n    def build(self):\\n        return self.build_string\\n\\n    @property\\n    def subdir(self):\\n        return self.platform\\n\\n    @property\\n    def pair(self):\\n        return self.channel or DEFAULTS_CHANNEL_NAME, self.dist_name\\n\\n    @property\\n    def quad(self):\\n        # returns: name, version, build_string, channel\\n        parts = self.dist_name.rsplit(\\\"-\\\", 2) + [\\\"\\\", \\\"\\\"]\\n        return parts[0], parts[1], parts[2], self.channel or DEFAULTS_CHANNEL_NAME\\n\\n    def __str__(self):\\n        return f\\\"{self.channel}::{self.dist_name}\\\" if self.channel else self.dist_name\\n\\n    @property\\n    def is_feature_package(self):\\n        return self.dist_name.endswith(\\\"@\\\")\\n\\n    @property\\n    def is_channel(self):\\n        return bool(self.base_url and self.platform)\\n\\n    def to_filename(self, extension=None):\\n        if self.is_feature_package:\\n            return self.dist_name\\n        else:\\n            return self.dist_name + self.fmt\\n\\n    def to_matchspec(self):\\n        return \\\" \\\".join(self.quad[:3])\\n\\n    def to_match_spec(self):\\n        from .match_spec import MatchSpec\\n\\n        base = \\\"=\\\".join(self.quad[:3])\\n        return MatchSpec(f\\\"{self.channel}::{base}\\\" if self.channel else base)\\n\\n    @classmethod\\n    def from_string(cls, string, channel_override=NULL):\\n        string = str(string)\\n\\n        if is_url(string) and channel_override == NULL:\\n            return cls.from_url(string)\\n\\n        if string.endswith(\\\"@\\\"):\\n            return cls(\\n                channel=\\\"@\\\",\\n                name=string,\\n                version=\\\"\\\",\\n                build_string=\\\"\\\",\\n                build_number=0,\\n                dist_name=string,\\n            )\\n\\n        REGEX_STR = (\\n            r\\\"(?:([^\\\\s\\\\[\\\\]]+)::)?\\\"  # optional channel\\n            r\\\"([^\\\\s\\\\[\\\\]]+)\\\"  # 3.x dist\\n            r\\\"(?:\\\\[([a-zA-Z0-9_-]+)\\\\])?\\\"  # with_features_depends\\n        )\\n        channel, original_dist, w_f_d = re.search(REGEX_STR, string).groups()\\n\\n        original_dist, fmt = split_extension(original_dist)\\n\\n        if channel_override != NULL:\\n            channel = channel_override\\n        if not channel:\\n            channel = UNKNOWN_CHANNEL\\n\\n        # enforce dist format\\n        dist_details = cls.parse_dist_name(original_dist)\\n        return cls(\\n            channel=channel,\\n            name=dist_details.name,\\n            version=dist_details.version,\\n            build_string=dist_details.build_string,\\n            build_number=dist_details.build_number,\\n            dist_name=original_dist,\\n            fmt=fmt,\\n        )\\n\\n    @staticmethod\\n    def parse_dist_name(string):\\n        original_string = string\\n        try:\\n            string = ensure_text_type(string)\\n            no_fmt_string, fmt = split_extension(string)\\n\\n            # remove any directory or channel information\\n            if \\\"::\\\" in no_fmt_string:\\n                dist_name = no_fmt_string.rsplit(\\\"::\\\", 1)[-1]\\n            else:\\n                dist_name = no_fmt_string.rsplit(\\\"/\\\", 1)[-1]\\n\\n            parts = dist_name.rsplit(\\\"-\\\", 2)\\n\\n            name = parts[0]\\n            version = parts[1]\\n            build_string = parts[2] if len(parts) >= 3 else \\\"\\\"\\n            build_number_as_string = \\\"\\\".join(\\n                filter(\\n                    lambda x: x.isdigit(),\\n                    (build_string.rsplit(\\\"_\\\")[-1] if build_string else \\\"0\\\"),\\n                )\\n            )\\n            build_number = int(build_number_as_string) if build_number_as_string else 0\\n\\n            return DistDetails(\\n                name, version, build_string, build_number, dist_name, fmt\\n            )\\n\\n        except:\\n            raise CondaError(\\n                f\\\"dist_name is not a valid conda package: {original_string}\\\"\\n            )\\n\\n    @classmethod\\n    def from_url(cls, url):\\n        assert is_url(url), url\\n        if (\\n            not any(url.endswith(ext) for ext in CONDA_PACKAGE_EXTENSIONS)\\n            and \\\"::\\\" not in url\\n        ):\\n            raise CondaError(f\\\"url '{url}' is not a conda package\\\")\\n\\n        dist_details = cls.parse_dist_name(url)\\n        if \\\"::\\\" in url:\\n            url_no_tarball = url.rsplit(\\\"::\\\", 1)[0]\\n            platform = context.subdir\\n            base_url = url_no_tarball.split(\\\"::\\\")[0]\\n            channel = str(Channel(base_url))\\n        else:\\n            url_no_tarball = url.rsplit(\\\"/\\\", 1)[0]\\n            platform = has_platform(url_no_tarball, context.known_subdirs)\\n            base_url = url_no_tarball.rsplit(\\\"/\\\", 1)[0] if platform else url_no_tarball\\n            channel = Channel(base_url).canonical_name if platform else UNKNOWN_CHANNEL\\n\\n        return cls(\\n            channel=channel,\\n            name=dist_details.name,\\n            version=dist_details.version,\\n            build_string=dist_details.build_string,\\n            build_number=dist_details.build_number,\\n            dist_name=dist_details.dist_name,\\n            base_url=base_url,\\n            platform=platform,\\n            fmt=dist_details.fmt,\\n        )\\n\\n    def to_url(self):\\n        if not self.base_url:\\n            return None\\n        filename = self.dist_name + self.fmt\\n        return (\\n            join_url(self.base_url, self.platform, filename)\\n            if self.platform\\n            else join_url(self.base_url, filename)\\n        )\\n\\n    def __key__(self):\\n        return self.channel, self.dist_name\\n\\n    def __lt__(self, other):\\n        assert isinstance(other, self.__class__)\\n        return self.__key__() < other.__key__()\\n\\n    def __gt__(self, other):\\n        assert isinstance(other, self.__class__)\\n        return self.__key__() > other.__key__()\\n\\n    def __le__(self, other):\\n        assert isinstance(other, self.__class__)\\n        return self.__key__() <= other.__key__()\\n\\n    def __ge__(self, other):\\n        assert isinstance(other, self.__class__)\\n        return self.__key__() >= other.__key__()\\n\\n    def __hash__(self):\\n        # dists compare equal regardless of fmt, but fmt is taken into account for\\n        #    object identity\\n        return hash((self.__key__(), self.fmt))\\n\\n    def __eq__(self, other):\\n        return isinstance(other, self.__class__) and self.__key__() == other.__key__()\\n\\n    def __ne__(self, other):\\n        return not self.__eq__(other)\\n\\n    # ############ conda-build compatibility ################\\n\\n    def split(self, sep=None, maxsplit=-1):\\n        assert sep == \\\"::\\\"\\n        return [self.channel, self.dist_name] if self.channel else [self.dist_name]\\n\\n    def rsplit(self, sep=None, maxsplit=-1):\\n        assert sep == \\\"-\\\"\\n        assert maxsplit == 2\\n        name = f\\\"{self.channel}::{self.quad[0]}\\\" if self.channel else self.quad[0]\\n        return name, self.quad[1], self.quad[2]\\n\\n    def startswith(self, match):\\n        return self.dist_name.startswith(match)\\n\\n    def __contains__(self, item):\\n        item = strip_extension(ensure_text_type(item))\\n        return item in self.__str__()\\n\\n    @property\\n    def fn(self):\\n        return self.to_filename()\\n\\n\\ndef dist_str_to_quad(dist_str):\\n    dist_str = strip_extension(dist_str)\\n    if \\\"::\\\" in dist_str:\\n        channel_str, dist_str = dist_str.split(\\\"::\\\", 1)\\n    else:\\n        channel_str = UNKNOWN_CHANNEL\\n    name, version, build = dist_str.rsplit(\\\"-\\\", 2)\\n    return name, version, build, channel_str\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Defines Channel and MultiChannel objects and other channel-related functions.\\n\\nObject inheritance:\\n\\n.. autoapi-inheritance-diagram:: Channel MultiChannel\\n   :top-classes: conda.models.channel.Channel\\n   :parts: 1\\n\\\"\\\"\\\"\\n\\nfrom copy import copy\\nfrom itertools import chain\\nfrom logging import getLogger\\n\\nfrom boltons.setutils import IndexedSet\\n\\nfrom ..base.constants import (\\n    DEFAULTS_CHANNEL_NAME,\\n    MAX_CHANNEL_PRIORITY,\\n    UNKNOWN_CHANNEL,\\n)\\nfrom ..base.context import Context, context\\nfrom ..common.compat import ensure_text_type, isiterable\\nfrom ..common.path import is_package_file, is_path, win_path_backout\\nfrom ..common.url import (\\n    Url,\\n    has_scheme,\\n    is_url,\\n    join_url,\\n    path_to_url,\\n    split_conda_url_easy_parts,\\n    split_platform,\\n    split_scheme_auth_token,\\n    urlparse,\\n)\\n\\nlog = getLogger(__name__)\\n\\n\\nclass ChannelType(type):\\n    \\\"\\\"\\\"\\n    This metaclass does basic caching and enables static constructor method usage with a\\n    single arg.\\n    \\\"\\\"\\\"\\n\\n    def __call__(cls, *args, **kwargs):\\n        if len(args) == 1 and not kwargs:\\n            value = args[0]\\n            if isinstance(value, Channel):\\n                return value\\n            elif value in Channel._cache_:\\n                return Channel._cache_[value]\\n            else:\\n                c = Channel._cache_[value] = Channel.from_value(value)\\n                return c\\n        elif \\\"channels\\\" in kwargs:\\n            # presence of 'channels' kwarg indicates MultiChannel\\n            channels = tuple(cls(**_kwargs) for _kwargs in kwargs[\\\"channels\\\"])\\n            return MultiChannel(kwargs[\\\"name\\\"], channels)\\n        else:\\n            return super().__call__(*args, **kwargs)\\n\\n\\nclass Channel(metaclass=ChannelType):\\n    \\\"\\\"\\\"\\n    Channel:\\n    scheme <> auth <> location <> token <> channel <> subchannel <> platform <> package_filename\\n\\n    Package Spec:\\n    channel <> subchannel <> namespace <> package_name\\n\\n    \\\"\\\"\\\"\\n\\n    _cache_ = {}\\n\\n    @staticmethod\\n    def _reset_state():\\n        Channel._cache_ = {}\\n\\n    def __init__(\\n        self,\\n        scheme=None,\\n        auth=None,\\n        location=None,\\n        token=None,\\n        name=None,\\n        platform=None,\\n        package_filename=None,\\n    ):\\n        self.scheme = scheme\\n        self.auth = auth\\n        self.location = location\\n        self.token = token\\n        self.name = name or \\\"\\\"\\n        self.platform = platform\\n        self.package_filename = package_filename\\n\\n    @property\\n    def channel_location(self):\\n        return self.location\\n\\n    @property\\n    def channel_name(self):\\n        return self.name\\n\\n    @property\\n    def subdir(self):\\n        return self.platform\\n\\n    @staticmethod\\n    def from_url(url):\\n        return parse_conda_channel_url(url)\\n\\n    @staticmethod\\n    def from_channel_name(channel_name):\\n        return _get_channel_for_name(channel_name)\\n\\n    @staticmethod\\n    def from_value(value):\\n        if value in (None, \\\"<unknown>\\\", \\\"None:///<unknown>\\\", \\\"None\\\"):\\n            return Channel(name=UNKNOWN_CHANNEL)\\n        value = ensure_text_type(value)\\n        if has_scheme(value):\\n            if value.startswith(\\\"file:\\\"):\\n                value = win_path_backout(value)\\n            return Channel.from_url(value)\\n        elif is_path(value):\\n            return Channel.from_url(path_to_url(value))\\n        elif is_package_file(value):\\n            if value.startswith(\\\"file:\\\"):\\n                value = win_path_backout(value)\\n            return Channel.from_url(value)\\n        else:\\n            # at this point assume we don't have a bare (non-scheme) url\\n            #   e.g. this would be bad:  repo.anaconda.com/pkgs/free\\n            _stripped, platform = split_platform(context.known_subdirs, value)\\n            if _stripped in context.custom_multichannels:\\n                return MultiChannel(\\n                    _stripped, context.custom_multichannels[_stripped], platform\\n                )\\n            else:\\n                return Channel.from_channel_name(value)\\n\\n    @staticmethod\\n    def make_simple_channel(channel_alias, channel_url, name=None):\\n        ca = channel_alias\\n        test_url, scheme, auth, token = split_scheme_auth_token(channel_url)\\n        if name and scheme:\\n            return Channel(\\n                scheme=scheme,\\n                auth=auth,\\n                location=test_url,\\n                token=token,\\n                name=name.strip(\\\"/\\\"),\\n            )\\n        if scheme:\\n            if ca.location and test_url.startswith(ca.location):\\n                location, name = ca.location, test_url.replace(ca.location, \\\"\\\", 1)\\n            else:\\n                url_parts = urlparse(test_url)\\n                location = str(Url(hostname=url_parts.hostname, port=url_parts.port))\\n                name = url_parts.path or \\\"\\\"\\n            return Channel(\\n                scheme=scheme,\\n                auth=auth,\\n                location=location,\\n                token=token,\\n                name=name.strip(\\\"/\\\"),\\n            )\\n        else:\\n            return Channel(\\n                scheme=ca.scheme,\\n                auth=ca.auth,\\n                location=ca.location,\\n                token=ca.token,\\n                name=name and name.strip(\\\"/\\\") or channel_url.strip(\\\"/\\\"),\\n            )\\n\\n    @property\\n    def canonical_name(self):\\n        try:\\n            return self.__canonical_name\\n        except AttributeError:\\n            pass\\n\\n        for multiname, channels in context.custom_multichannels.items():\\n            for channel in channels:\\n                if self.name == channel.name:\\n                    cn = self.__canonical_name = multiname\\n                    return cn\\n\\n        for that_name in context.custom_channels:\\n            if self.name and tokenized_startswith(\\n                self.name.split(\\\"/\\\"), that_name.split(\\\"/\\\")\\n            ):\\n                cn = self.__canonical_name = self.name\\n                return cn\\n\\n        if any(\\n            alias.location == self.location\\n            for alias in (\\n                context.channel_alias,\\n                *context.migrated_channel_aliases,\\n            )\\n        ):\\n            cn = self.__canonical_name = self.name\\n            return cn\\n\\n        # fall back to the equivalent of self.base_url\\n        # re-defining here because base_url for MultiChannel is None\\n        if self.scheme:\\n            cn = self.__canonical_name = (\\n                f\\\"{self.scheme}://{join_url(self.location, self.name)}\\\"\\n            )\\n            return cn\\n        else:\\n            cn = self.__canonical_name = join_url(self.location, self.name).lstrip(\\\"/\\\")\\n            return cn\\n\\n    def urls(self, with_credentials=False, subdirs=None):\\n        if subdirs is None:\\n            subdirs = context.subdirs\\n\\n        assert isiterable(subdirs), subdirs  # subdirs must be a non-string iterable\\n\\n        if self.canonical_name == UNKNOWN_CHANNEL:\\n            return Channel(DEFAULTS_CHANNEL_NAME).urls(with_credentials, subdirs)\\n\\n        base = [self.location]\\n        if with_credentials and self.token:\\n            base.extend([\\\"t\\\", self.token])\\n        base.append(self.name)\\n        base = join_url(*base)\\n\\n        def _platforms():\\n            if self.platform:\\n                yield self.platform\\n                if self.platform != \\\"noarch\\\":\\n                    yield \\\"noarch\\\"\\n            else:\\n                yield from subdirs\\n\\n        bases = (join_url(base, p) for p in _platforms())\\n        if with_credentials and self.auth:\\n            return [f\\\"{self.scheme}://{self.auth}@{b}\\\" for b in bases]\\n        else:\\n            return [f\\\"{self.scheme}://{b}\\\" for b in bases]\\n\\n    def url(self, with_credentials=False):\\n        if self.canonical_name == UNKNOWN_CHANNEL:\\n            return None\\n\\n        base = [self.location]\\n        if with_credentials and self.token:\\n            base.extend([\\\"t\\\", self.token])\\n        base.append(self.name)\\n        if self.platform:\\n            base.append(self.platform)\\n            if self.package_filename:\\n                base.append(self.package_filename)\\n        else:\\n            first_non_noarch = next(\\n                (s for s in context.subdirs if s != \\\"noarch\\\"), \\\"noarch\\\"\\n            )\\n            base.append(first_non_noarch)\\n\\n        base = join_url(*base)\\n\\n        if with_credentials and self.auth:\\n            return f\\\"{self.scheme}://{self.auth}@{base}\\\"\\n        else:\\n            return f\\\"{self.scheme}://{base}\\\"\\n\\n    @property\\n    def base_url(self):\\n        if self.canonical_name == UNKNOWN_CHANNEL:\\n            return None\\n        return f\\\"{self.scheme}://{join_url(self.location, self.name)}\\\"\\n\\n    @property\\n    def base_urls(self):\\n        return (self.base_url,)\\n\\n    @property\\n    def subdir_url(self):\\n        url = self.url(True)\\n        if self.package_filename and url:\\n            url = url.rsplit(\\\"/\\\", 1)[0]\\n        return url\\n\\n    def __str__(self):\\n        base = self.base_url or self.name\\n        if self.subdir:\\n            return join_url(base, self.subdir)\\n        else:\\n            return base\\n\\n    def __repr__(self):\\n        return 'Channel(\\\"%s\\\")' % (\\n            join_url(self.name, self.subdir) if self.subdir else self.name\\n        )\\n\\n    def __eq__(self, other):\\n        if isinstance(other, Channel):\\n            return self.location == other.location and self.name == other.name\\n        else:\\n            try:\\n                _other = Channel(other)\\n                return self.location == _other.location and self.name == _other.name\\n            except Exception as e:\\n                log.debug(\\\"%r\\\", e)\\n                return False\\n\\n    def __hash__(self):\\n        return hash((self.location, self.name))\\n\\n    def __nonzero__(self):\\n        return any((self.location, self.name))\\n\\n    def __bool__(self):\\n        return self.__nonzero__()\\n\\n    def __json__(self):\\n        return self.__dict__\\n\\n    @property\\n    def url_channel_wtf(self):\\n        return self.base_url, self.canonical_name\\n\\n    def dump(self):\\n        return {\\n            \\\"scheme\\\": self.scheme,\\n            \\\"auth\\\": self.auth,\\n            \\\"location\\\": self.location,\\n            \\\"token\\\": self.token,\\n            \\\"name\\\": self.name,\\n            \\\"platform\\\": self.platform,\\n            \\\"package_filename\\\": self.package_filename,\\n        }\\n\\n\\nclass MultiChannel(Channel):\\n    def __init__(self, name, channels, platform=None):\\n        self.name = name\\n        self.location = None\\n\\n        if platform:\\n            self._channels = tuple(\\n                Channel(**{**channel.dump(), \\\"platform\\\": platform})\\n                for channel in channels\\n            )\\n        else:\\n            self._channels = channels\\n\\n        self.scheme = None\\n        self.auth = None\\n        self.token = None\\n        self.platform = platform\\n        self.package_filename = None\\n\\n    @property\\n    def channel_location(self):\\n        return self.location\\n\\n    @property\\n    def canonical_name(self):\\n        return self.name\\n\\n    def urls(self, with_credentials=False, subdirs=None):\\n        _channels = self._channels\\n        return list(\\n            chain.from_iterable(c.urls(with_credentials, subdirs) for c in _channels)\\n        )\\n\\n    @property\\n    def base_url(self):\\n        return None\\n\\n    @property\\n    def base_urls(self):\\n        return tuple(c.base_url for c in self._channels)\\n\\n    def url(self, with_credentials=False):\\n        return None\\n\\n    def dump(self):\\n        return {\\\"name\\\": self.name, \\\"channels\\\": tuple(c.dump() for c in self._channels)}\\n\\n\\ndef tokenized_startswith(test_iterable, startswith_iterable):\\n    return all(t == sw for t, sw in zip(test_iterable, startswith_iterable))\\n\\n\\ndef tokenized_conda_url_startswith(test_url, startswith_url):\\n    test_url, startswith_url = urlparse(test_url), urlparse(startswith_url)\\n    if (\\n        test_url.hostname != startswith_url.hostname\\n        or test_url.port != startswith_url.port\\n    ):\\n        return False\\n    norm_url_path = lambda url: url.path.strip(\\\"/\\\") or \\\"/\\\"\\n    return tokenized_startswith(\\n        norm_url_path(test_url).split(\\\"/\\\"), norm_url_path(startswith_url).split(\\\"/\\\")\\n    )\\n\\n\\ndef _get_channel_for_name(channel_name):\\n    def _get_channel_for_name_helper(name):\\n        if name in context.custom_channels:\\n            return context.custom_channels[name]\\n        else:\\n            test_name = name.rsplit(\\\"/\\\", 1)[0]  # progressively strip off path segments\\n            if test_name == name:\\n                return None\\n            return _get_channel_for_name_helper(test_name)\\n\\n    _stripped, platform = split_platform(context.known_subdirs, channel_name)\\n    channel = _get_channel_for_name_helper(_stripped)\\n\\n    if channel is not None:\\n        # stripping off path threw information away from channel_name (i.e. any potential subname)\\n        # channel.name *should still be* channel_name\\n        channel = copy(channel)\\n        channel.name = _stripped\\n        if platform:\\n            channel.platform = platform\\n        return channel\\n    else:\\n        ca = context.channel_alias\\n        return Channel(\\n            scheme=ca.scheme,\\n            auth=ca.auth,\\n            location=ca.location,\\n            token=ca.token,\\n            name=_stripped,\\n            platform=platform,\\n        )\\n\\n\\ndef _read_channel_configuration(scheme, host, port, path):\\n    # return location, name, scheme, auth, token\\n\\n    path = path and path.rstrip(\\\"/\\\")\\n    test_url = str(Url(hostname=host, port=port, path=path))\\n\\n    # Step 1. No path given; channel name is None\\n    if not path:\\n        return (\\n            str(Url(hostname=host, port=port)).rstrip(\\\"/\\\"),\\n            None,\\n            scheme or None,\\n            None,\\n            None,\\n        )\\n\\n    # Step 2. migrated_custom_channels matches\\n    for name, location in sorted(\\n        context.migrated_custom_channels.items(), reverse=True, key=lambda x: len(x[0])\\n    ):\\n        location, _scheme, _auth, _token = split_scheme_auth_token(location)\\n        if tokenized_conda_url_startswith(test_url, join_url(location, name)):\\n            # translate location to new location, with new credentials\\n            subname = test_url.replace(join_url(location, name), \\\"\\\", 1).strip(\\\"/\\\")\\n            channel_name = join_url(name, subname)\\n            channel = _get_channel_for_name(channel_name)\\n            return (\\n                channel.location,\\n                channel_name,\\n                channel.scheme,\\n                channel.auth,\\n                channel.token,\\n            )\\n\\n    # Step 3. migrated_channel_aliases matches\\n    for migrated_alias in context.migrated_channel_aliases:\\n        if test_url.startswith(migrated_alias.location):\\n            name = test_url.replace(migrated_alias.location, \\\"\\\", 1).strip(\\\"/\\\")\\n            ca = context.channel_alias\\n            return ca.location, name, ca.scheme, ca.auth, ca.token\\n\\n    # Step 4. custom_channels matches\\n    for name, channel in sorted(\\n        context.custom_channels.items(), reverse=True, key=lambda x: len(x[0])\\n    ):\\n        that_test_url = join_url(channel.location, channel.name)\\n        if tokenized_startswith(test_url.split(\\\"/\\\"), that_test_url.split(\\\"/\\\")):\\n            subname = test_url.replace(that_test_url, \\\"\\\", 1).strip(\\\"/\\\")\\n            return (\\n                channel.location,\\n                join_url(channel.name, subname),\\n                scheme,\\n                channel.auth,\\n                channel.token,\\n            )\\n\\n    # Step 5. channel_alias match\\n    ca = context.channel_alias\\n    if ca.location and tokenized_startswith(\\n        test_url.split(\\\"/\\\"), ca.location.split(\\\"/\\\")\\n    ):\\n        name = test_url.replace(ca.location, \\\"\\\", 1).strip(\\\"/\\\") or None\\n        return ca.location, name, scheme, ca.auth, ca.token\\n\\n    # Step 6. not-otherwise-specified file://-type urls\\n    if host is None:\\n        # this should probably only happen with a file:// type url\\n        assert port is None\\n        location, name = test_url.rsplit(\\\"/\\\", 1)\\n        if not location:\\n            location = \\\"/\\\"\\n        _scheme, _auth, _token = \\\"file\\\", None, None\\n        return location, name, _scheme, _auth, _token\\n\\n    # Step 7. fall through to host:port as channel_location and path as channel_name\\n    #  but bump the first token of paths starting with /conda for compatibility with\\n    #  Anaconda Enterprise Repository software.\\n    bump = None\\n    path_parts = path.strip(\\\"/\\\").split(\\\"/\\\")\\n    if path_parts and path_parts[0] == \\\"conda\\\":\\n        bump, path = \\\"conda\\\", \\\"/\\\".join(path_parts[1:])\\n    return (\\n        str(Url(hostname=host, port=port, path=bump)).rstrip(\\\"/\\\"),\\n        path.strip(\\\"/\\\") or None,\\n        scheme or None,\\n        None,\\n        None,\\n    )\\n\\n\\ndef parse_conda_channel_url(url):\\n    (\\n        scheme,\\n        auth,\\n        token,\\n        platform,\\n        package_filename,\\n        host,\\n        port,\\n        path,\\n        query,\\n    ) = split_conda_url_easy_parts(context.known_subdirs, url)\\n\\n    # recombine host, port, path to get a channel_name and channel_location\\n    (\\n        channel_location,\\n        channel_name,\\n        configured_scheme,\\n        configured_auth,\\n        configured_token,\\n    ) = _read_channel_configuration(scheme, host, port, path)\\n\\n    # if we came out with no channel_location or channel_name, we need to figure it out\\n    # from host, port, path\\n    assert channel_location is not None or channel_name is not None\\n\\n    return Channel(\\n        configured_scheme or \\\"https\\\",\\n        auth or configured_auth,\\n        channel_location,\\n        token or configured_token,\\n        channel_name,\\n        platform,\\n        package_filename,\\n    )\\n\\n\\n# backward compatibility for conda-build\\ndef get_conda_build_local_url():\\n    return (context.local_build_root,)\\n\\n\\ndef prioritize_channels(channels, with_credentials=True, subdirs=None):\\n    # prioritize_channels returns a dict with platform-specific channel\\n    #   urls as the key, and a tuple of canonical channel name and channel priority\\n    #   number as the value\\n    # ('https://conda.anaconda.org/conda-forge/osx-64/', ('conda-forge', 1))\\n    channels = chain.from_iterable(\\n        (Channel(cc) for cc in c._channels) if isinstance(c, MultiChannel) else (c,)\\n        for c in (Channel(c) for c in channels)\\n    )\\n    result = {}\\n    for priority_counter, chn in enumerate(channels):\\n        channel = Channel(chn)\\n        for url in channel.urls(with_credentials, subdirs):\\n            if url in result:\\n                continue\\n            result[url] = (\\n                channel.canonical_name,\\n                min(priority_counter, MAX_CHANNEL_PRIORITY - 1),\\n            )\\n    return result\\n\\n\\ndef all_channel_urls(channels, subdirs=None, with_credentials=True):\\n    result = IndexedSet()\\n    for chn in channels:\\n        channel = Channel(chn)\\n        result.update(channel.urls(with_credentials, subdirs))\\n    return result\\n\\n\\ndef offline_keep(url):\\n    return not context.offline or not is_url(url) or url.startswith(\\\"file:/\\\")\\n\\n\\ndef get_channel_objs(ctx: Context):\\n    \\\"\\\"\\\"Return current channels as Channel objects\\\"\\\"\\\"\\n    return tuple(Channel(chn) for chn in ctx.channels)\\n\\n\\ncontext.register_reset_callaback(Channel._reset_state)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Implements directed graphs to sort and manipulate packages within a prefix.\\n\\nObject inheritance:\\n\\n.. autoapi-inheritance-diagram:: PrefixGraph GeneralGraph\\n   :top-classes: conda.models.prefix_graph.PrefixGraph\\n   :parts: 1\\n\\\"\\\"\\\"\\n\\nfrom collections import defaultdict\\nfrom logging import getLogger\\n\\nfrom boltons.setutils import IndexedSet\\n\\nfrom ..base.context import context\\nfrom ..common.compat import on_win\\nfrom ..exceptions import CyclicalDependencyError\\nfrom .enums import NoarchType\\nfrom .match_spec import MatchSpec\\n\\nlog = getLogger(__name__)\\n\\n\\nclass PrefixGraph:\\n    \\\"\\\"\\\"\\n    A directed graph structure used for sorting packages (prefix_records) in prefixes and\\n    manipulating packages within prefixes (e.g. removing and pruning).\\n\\n    The terminology used for edge direction is \\\"parents\\\" and \\\"children\\\" rather than \\\"successors\\\"\\n    and \\\"predecessors\\\". The parent nodes of a record are those records in the graph that\\n    match the record's \\\"depends\\\" field.  E.g. NodeA depends on NodeB, then NodeA is a child\\n    of NodeB, and NodeB is a parent of NodeA.  Nodes can have zero parents, or more than two\\n    parents.\\n\\n    Most public methods mutate the graph.\\n    \\\"\\\"\\\"\\n\\n    def __init__(self, records, specs=()):\\n        records = tuple(records)\\n        specs = set(specs)\\n        self.graph = graph = {}  # dict[PrefixRecord, set[PrefixRecord]]\\n        self.spec_matches = spec_matches = {}  # dict[PrefixRecord, set[MatchSpec]]\\n        for node in records:\\n            parent_match_specs = tuple(MatchSpec(d) for d in node.depends)\\n            parent_nodes = {\\n                rec for rec in records if any(m.match(rec) for m in parent_match_specs)\\n            }\\n            graph[node] = parent_nodes\\n            matching_specs = IndexedSet(s for s in specs if s.match(node))\\n            if matching_specs:\\n                spec_matches[node] = matching_specs\\n\\n        self._toposort()\\n\\n    def remove_spec(self, spec):\\n        \\\"\\\"\\\"\\n        Remove all matching nodes, and any associated child nodes.\\n\\n        Args:\\n            spec (MatchSpec):\\n\\n        Returns:\\n            tuple[PrefixRecord]: The removed nodes.\\n\\n        \\\"\\\"\\\"\\n        node_matches = {node for node in self.graph if spec.match(node)}\\n\\n        # If the spec was a track_features spec, then we need to also remove every\\n        # package with a feature that matches the track_feature.\\n        for feature_name in spec.get_raw_value(\\\"track_features\\\") or ():\\n            feature_spec = MatchSpec(features=feature_name)\\n            node_matches.update(node for node in self.graph if feature_spec.match(node))\\n\\n        remove_these = set()\\n        for node in node_matches:\\n            remove_these.add(node)\\n            remove_these.update(self.all_descendants(node))\\n        remove_these = tuple(filter(lambda node: node in remove_these, self.graph))\\n        for node in remove_these:\\n            self._remove_node(node)\\n        self._toposort()\\n        return tuple(remove_these)\\n\\n    def remove_youngest_descendant_nodes_with_specs(self):\\n        \\\"\\\"\\\"\\n        A specialized method used to determine only dependencies of requested specs.\\n\\n        Returns:\\n            tuple[PrefixRecord]: The removed nodes.\\n\\n        \\\"\\\"\\\"\\n        graph = self.graph\\n        spec_matches = self.spec_matches\\n        inverted_graph = {\\n            node: {key for key in graph if node in graph[key]} for node in graph\\n        }\\n        youngest_nodes_with_specs = tuple(\\n            node\\n            for node, children in inverted_graph.items()\\n            if not children and node in spec_matches\\n        )\\n        removed_nodes = tuple(\\n            filter(lambda node: node in youngest_nodes_with_specs, self.graph)\\n        )\\n        for node in removed_nodes:\\n            self._remove_node(node)\\n        self._toposort()\\n        return removed_nodes\\n\\n    @property\\n    def records(self):\\n        return iter(self.graph)\\n\\n    def prune(self):\\n        \\\"\\\"\\\"Prune back all packages until all child nodes are anchored by a spec.\\n\\n        Returns:\\n            tuple[PrefixRecord]: The pruned nodes.\\n\\n        \\\"\\\"\\\"\\n        graph = self.graph\\n        spec_matches = self.spec_matches\\n        original_order = tuple(self.graph)\\n\\n        removed_nodes = set()\\n        while True:\\n            inverted_graph = {\\n                node: {key for key in graph if node in graph[key]} for node in graph\\n            }\\n            prunable_nodes = tuple(\\n                node\\n                for node, children in inverted_graph.items()\\n                if not children and node not in spec_matches\\n            )\\n            if not prunable_nodes:\\n                break\\n            for node in prunable_nodes:\\n                removed_nodes.add(node)\\n                self._remove_node(node)\\n\\n        removed_nodes = tuple(\\n            filter(lambda node: node in removed_nodes, original_order)\\n        )\\n        self._toposort()\\n        return removed_nodes\\n\\n    def get_node_by_name(self, name):\\n        return next(rec for rec in self.graph if rec.name == name)\\n\\n    def all_descendants(self, node):\\n        graph = self.graph\\n        inverted_graph = {\\n            node: {key for key in graph if node in graph[key]} for node in graph\\n        }\\n\\n        nodes = [node]\\n        nodes_seen = set()\\n        q = 0\\n        while q < len(nodes):\\n            for child_node in inverted_graph[nodes[q]]:\\n                if child_node not in nodes_seen:\\n                    nodes_seen.add(child_node)\\n                    nodes.append(child_node)\\n            q += 1\\n        return tuple(filter(lambda node: node in nodes_seen, graph))\\n\\n    def all_ancestors(self, node):\\n        graph = self.graph\\n        nodes = [node]\\n        nodes_seen = set()\\n        q = 0\\n        while q < len(nodes):\\n            for parent_node in graph[nodes[q]]:\\n                if parent_node not in nodes_seen:\\n                    nodes_seen.add(parent_node)\\n                    nodes.append(parent_node)\\n            q += 1\\n        return tuple(filter(lambda node: node in nodes_seen, graph))\\n\\n    def _remove_node(self, node):\\n        \\\"\\\"\\\"Removes this node and all edges referencing it.\\\"\\\"\\\"\\n        graph = self.graph\\n        if node not in graph:\\n            raise KeyError(f\\\"node {node} does not exist\\\")\\n        graph.pop(node)\\n        self.spec_matches.pop(node, None)\\n\\n        for node, edges in graph.items():\\n            if node in edges:\\n                edges.remove(node)\\n\\n    def _toposort(self):\\n        graph_copy = {node: IndexedSet(parents) for node, parents in self.graph.items()}\\n        self._toposort_prepare_graph(graph_copy)\\n        if context.allow_cycles:\\n            sorted_nodes = tuple(self._topo_sort_handle_cycles(graph_copy))\\n        else:\\n            sorted_nodes = tuple(self._toposort_raise_on_cycles(graph_copy))\\n        original_graph = self.graph\\n        self.graph = {node: original_graph[node] for node in sorted_nodes}\\n        return sorted_nodes\\n\\n    @classmethod\\n    def _toposort_raise_on_cycles(cls, graph):\\n        if not graph:\\n            return\\n\\n        while True:\\n            no_parent_nodes = IndexedSet(\\n                sorted(\\n                    (node for node, parents in graph.items() if len(parents) == 0),\\n                    key=lambda x: x.name,\\n                )\\n            )\\n            if not no_parent_nodes:\\n                break\\n\\n            for node in no_parent_nodes:\\n                yield node\\n                graph.pop(node, None)\\n\\n            for parents in graph.values():\\n                parents -= no_parent_nodes\\n\\n        if len(graph) != 0:\\n            raise CyclicalDependencyError(tuple(graph))\\n\\n    @classmethod\\n    def _topo_sort_handle_cycles(cls, graph):\\n        # remove edges that point directly back to the node\\n        for k, v in graph.items():\\n            v.discard(k)\\n\\n        # disconnected nodes go first\\n        nodes_that_are_parents = {\\n            node for parents in graph.values() for node in parents\\n        }\\n        nodes_without_parents = (node for node in graph if not graph[node])\\n        disconnected_nodes = sorted(\\n            (\\n                node\\n                for node in nodes_without_parents\\n                if node not in nodes_that_are_parents\\n            ),\\n            key=lambda x: x.name,\\n        )\\n        yield from disconnected_nodes\\n\\n        t = cls._toposort_raise_on_cycles(graph)\\n\\n        while True:\\n            try:\\n                value = next(t)\\n                yield value\\n            except CyclicalDependencyError as e:\\n                # TODO: Turn this into a warning, but without being too annoying with\\n                #       multiple messages.  See https://github.com/conda/conda/issues/4067\\n                log.debug(\\\"%r\\\", e)\\n\\n                yield cls._toposort_pop_key(graph)\\n\\n                t = cls._toposort_raise_on_cycles(graph)\\n                continue\\n\\n            except StopIteration:\\n                return\\n\\n    @staticmethod\\n    def _toposort_pop_key(graph):\\n        \\\"\\\"\\\"\\n        Pop an item from the graph that has the fewest parents.\\n        In the case of a tie, use the node with the alphabetically-first package name.\\n        \\\"\\\"\\\"\\n        node_with_fewest_parents = sorted(\\n            (len(parents), node.dist_str(), node) for node, parents in graph.items()\\n        )[0][2]\\n        graph.pop(node_with_fewest_parents)\\n\\n        for parents in graph.values():\\n            parents.discard(node_with_fewest_parents)\\n\\n        return node_with_fewest_parents\\n\\n    @staticmethod\\n    def _toposort_prepare_graph(graph):\\n        # There are currently at least three special cases to be aware of.\\n\\n        # 1. Remove any circular dependency between python and pip. This typically comes about\\n        #    because of the add_pip_as_python_dependency configuration parameter.\\n        for node in graph:\\n            if node.name == \\\"python\\\":\\n                parents = graph[node]\\n                for parent in tuple(parents):\\n                    if parent.name == \\\"pip\\\":\\n                        parents.remove(parent)\\n\\n        # 2. Special case code for menuinst.\\n        #    Always link/unlink menuinst first/last in case a subsequent\\n        #    package tries to import it to create/remove a shortcut.\\n        menuinst_node = next((node for node in graph if node.name == \\\"menuinst\\\"), None)\\n        python_node = next((node for node in graph if node.name == \\\"python\\\"), None)\\n        if menuinst_node:\\n            # add menuinst as a parent if python is a parent and the node\\n            # isn't a parent of menuinst\\n            assert python_node is not None\\n            menuinst_parents = graph[menuinst_node]\\n            for node, parents in graph.items():\\n                if python_node in parents and node not in menuinst_parents:\\n                    parents.add(menuinst_node)\\n\\n        if on_win:\\n            # 3. On windows, python noarch packages need an implicit dependency on conda added, if\\n            #    conda is in the list of packages for the environment.  Python noarch packages\\n            #    that have entry points use conda's own conda.exe python entry point binary. If\\n            #    conda is going to be updated during an operation, the unlink / link order matters.\\n            #    See issue #6057.\\n            conda_node = next((node for node in graph if node.name == \\\"conda\\\"), None)\\n            if conda_node:\\n                # add conda as a parent if python is a parent and node isn't a parent of conda\\n                conda_parents = graph[conda_node]\\n                for node, parents in graph.items():\\n                    if (\\n                        hasattr(node, \\\"noarch\\\")\\n                        and node.noarch == NoarchType.python\\n                        and node not in conda_parents\\n                    ):\\n                        parents.add(conda_node)\\n\\n\\n#     def dot_repr(self, title=None):  # pragma: no cover\\n#         # graphviz DOT graph description language\\n#\\n#         builder = ['digraph g {']\\n#         if title:\\n#             builder.append('  labelloc=\\\"t\\\";')\\n#             builder.append('  label=\\\"%s\\\";' % title)\\n#         builder.append('  size=\\\"10.5,8\\\";')\\n#         builder.append('  rankdir=BT;')\\n#         for node in self.get_nodes_ordered_from_roots():\\n#             label = \\\"%s %s\\\" % (node.record.name, node.record.version)\\n#             if node.specs:\\n#                 # TODO: combine?\\n#                 spec = next(iter(node.specs))\\n#                 label += \\\"\\\\\\\\n%s\\\" % (\\\"?%s\\\" if spec.optional else \\\"%s\\\") % spec\\n#             if node.is_orphan:\\n#                 shape = \\\"box\\\"\\n#             elif node.is_root:\\n#                 shape = \\\"invhouse\\\"\\n#             elif node.is_leaf:\\n#                 shape = \\\"house\\\"\\n#             else:\\n#                 shape = \\\"ellipse\\\"\\n#             builder.append('  \\\"%s\\\" [label=\\\"%s\\\", shape=%s];' % (node.record.name, label, shape))\\n#             for child in node.required_children:\\n#                 builder.append('    \\\"%s\\\" -> \\\"%s\\\";' % (child.record.name, node.record.name))\\n#             for child in node.optional_children:\\n#                 builder.append('    \\\"%s -> \\\"%s\\\" [color=lightgray];' % (child.record.name,\\n#                                                                        node.record.name))\\n#         builder.append('}')\\n#         return '\\\\n'.join(builder)\\n#\\n#     def format_url(self):  # pragma: no cover\\n#         return \\\"https://condaviz.glitch.me/%s\\\" % url_quote(self.dot_repr())\\n#\\n#     def request_svg(self):  # pragma: no cover\\n#         from tempfile import NamedTemporaryFile\\n#         import requests\\n#         from ..common.compat import ensure_binary\\n#         response = requests.post(\\\"https://condaviz.glitch.me/post\\\",\\n#                                  data={\\\"digraph\\\": self.dot_repr()})\\n#         response.raise_for_status()\\n#         with NamedTemporaryFile(suffix='.svg', delete=False) as fh:\\n#             fh.write(ensure_binary(response.text))\\n#         print(\\\"saved to: %s\\\" % fh.name, file=sys.stderr)\\n#         return fh.name\\n#\\n#     def open_url(self):  # pragma: no cover\\n#         import webbrowser\\n#         from ..common.url import path_to_url\\n#         location = self.request_svg()\\n#         try:\\n#             browser = webbrowser.get(\\\"safari\\\")\\n#         except webbrowser.Error:\\n#             browser = webbrowser.get()\\n#         browser.open_new_tab(path_to_url(location))\\n\\n\\nclass GeneralGraph(PrefixGraph):\\n    \\\"\\\"\\\"\\n    Compared with PrefixGraph, this class takes in more than one record of a given name,\\n    and operates on that graph from the higher view across any matching dependencies.  It is\\n    not a Prefix thing, but more like a \\\"graph of all possible candidates\\\" thing, and is used\\n    for unsatisfiability analysis\\n    \\\"\\\"\\\"\\n\\n    def __init__(self, records, specs=()):\\n        records = tuple(records)\\n        super().__init__(records, specs)\\n        self.specs_by_name = defaultdict(dict)\\n        for node in records:\\n            parent_dict = self.specs_by_name.get(node.name, {})\\n            for dep in tuple(MatchSpec(d) for d in node.depends):\\n                deps = parent_dict.get(dep.name, set())\\n                deps.add(dep)\\n                parent_dict[dep.name] = deps\\n            self.specs_by_name[node.name] = parent_dict\\n\\n        consolidated_graph = {}\\n        # graph is toposorted, so looping over it is in dependency order\\n        for node, parent_nodes in reversed(list(self.graph.items())):\\n            cg = consolidated_graph.get(node.name, set())\\n            cg.update(_.name for _ in parent_nodes)\\n            consolidated_graph[node.name] = cg\\n        self.graph_by_name = consolidated_graph\\n\\n    def breadth_first_search_by_name(self, root_spec, target_spec):\\n        \\\"\\\"\\\"Return shorted path from root_spec to spec_name\\\"\\\"\\\"\\n        queue = []\\n        queue.append([root_spec])\\n        visited = []\\n        while queue:\\n            path = queue.pop(0)\\n            node = path[-1]\\n            if node in visited:\\n                continue\\n            visited.append(node)\\n            if node == target_spec:\\n                return path\\n            children = []\\n            specs = self.specs_by_name.get(node.name)\\n            if specs is None:\\n                continue\\n            for _, deps in specs.items():\\n                children.extend(list(deps))\\n            for adj in children:\\n                if adj.name == target_spec.name and adj.version != target_spec.version:\\n                    pass\\n                else:\\n                    new_path = list(path)\\n                    new_path.append(adj)\\n                    queue.append(new_path)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Collection of enums used throughout conda.\\\"\\\"\\\"\\n\\nimport sys\\nfrom enum import Enum\\nfrom platform import machine\\n\\nfrom ..auxlib.decorators import classproperty\\nfrom ..auxlib.ish import dals\\nfrom ..auxlib.type_coercion import TypeCoercionError, boolify\\nfrom ..deprecations import deprecated\\nfrom ..exceptions import CondaUpgradeError\\n\\n\\nclass Arch(Enum):\\n    x86 = \\\"x86\\\"\\n    x86_64 = \\\"x86_64\\\"\\n    # arm64 is for macOS and Windows\\n    arm64 = \\\"arm64\\\"\\n    armv6l = \\\"armv6l\\\"\\n    armv7l = \\\"armv7l\\\"\\n    # aarch64 is for Linux only\\n    aarch64 = \\\"aarch64\\\"\\n    ppc64 = \\\"ppc64\\\"\\n    ppc64le = \\\"ppc64le\\\"\\n    riscv64 = \\\"riscv64\\\"\\n    s390x = \\\"s390x\\\"\\n    wasm32 = \\\"wasm32\\\"\\n    z = \\\"z\\\"\\n\\n    @classmethod\\n    def from_sys(cls):\\n        if sys.platform == \\\"zos\\\":\\n            return cls[\\\"z\\\"]\\n        return cls[machine()]\\n\\n    def __json__(self):\\n        return self.value\\n\\n\\nclass Platform(Enum):\\n    freebsd = \\\"freebsd\\\"\\n    linux = \\\"linux\\\"\\n    win = \\\"win32\\\"\\n    openbsd = \\\"openbsd5\\\"\\n    osx = \\\"darwin\\\"\\n    zos = \\\"zos\\\"\\n    emscripten = \\\"emscripten\\\"\\n    wasi = \\\"wasi\\\"\\n\\n    @classmethod\\n    def from_sys(cls):\\n        return cls(sys.platform)\\n\\n    def __json__(self):\\n        return self.value\\n\\n\\nclass FileMode(Enum):\\n    text = \\\"text\\\"\\n    binary = \\\"binary\\\"\\n\\n    def __str__(self):\\n        return f\\\"{self.value}\\\"\\n\\n\\nclass LinkType(Enum):\\n    # directory is not a link type, and copy is not a path type\\n    # LinkType is still probably the best name here\\n    hardlink = 1\\n    softlink = 2\\n    copy = 3\\n    directory = 4\\n\\n    def __int__(self):\\n        return self.value\\n\\n    def __str__(self):\\n        return self.name\\n\\n    def __json__(self):\\n        return self.name\\n\\n\\nclass PathType(Enum):\\n    \\\"\\\"\\\"\\n    Refers to if the file in question is hard linked or soft linked. Originally designed to be used\\n    in paths.json\\n    \\\"\\\"\\\"\\n\\n    hardlink = \\\"hardlink\\\"\\n    softlink = \\\"softlink\\\"\\n    directory = \\\"directory\\\"\\n\\n    # these additional types should not be included by conda-build in packages\\n    linked_package_record = (\\n        \\\"linked_package_record\\\"  # a package's .json file in conda-meta\\n    )\\n    pyc_file = \\\"pyc_file\\\"\\n    unix_python_entry_point = \\\"unix_python_entry_point\\\"\\n    windows_python_entry_point_script = \\\"windows_python_entry_point_script\\\"\\n    windows_python_entry_point_exe = \\\"windows_python_entry_point_exe\\\"\\n\\n    @classproperty\\n    def basic_types(self):\\n        return (PathType.hardlink, PathType.softlink, PathType.directory)\\n\\n    def __str__(self):\\n        return self.name\\n\\n    def __json__(self):\\n        return self.name\\n\\n\\nclass LeasedPathType(Enum):\\n    application_entry_point = \\\"application_entry_point\\\"\\n    application_entry_point_windows_exe = \\\"application_entry_point_windows_exe\\\"\\n    application_softlink = \\\"application_softlink\\\"\\n\\n    def __str__(self):\\n        return self.name\\n\\n    def __json__(self):\\n        return self.name\\n\\n\\ndeprecated.constant(\\\"24.3\\\", \\\"24.9\\\", \\\"LeasedPathType\\\", LeasedPathType)\\ndel LeasedPathType\\n\\n\\nclass PackageType(Enum):\\n    NOARCH_GENERIC = \\\"noarch_generic\\\"\\n    NOARCH_PYTHON = \\\"noarch_python\\\"\\n    VIRTUAL_PRIVATE_ENV = \\\"virtual_private_env\\\"\\n    VIRTUAL_PYTHON_WHEEL = \\\"virtual_python_wheel\\\"  # manageable\\n    VIRTUAL_PYTHON_EGG_MANAGEABLE = \\\"virtual_python_egg_manageable\\\"\\n    VIRTUAL_PYTHON_EGG_UNMANAGEABLE = \\\"virtual_python_egg_unmanageable\\\"\\n    VIRTUAL_PYTHON_EGG_LINK = \\\"virtual_python_egg_link\\\"  # unmanageable\\n    VIRTUAL_SYSTEM = \\\"virtual_system\\\"  # virtual packages representing system attributes\\n\\n    @staticmethod\\n    def conda_package_types():\\n        return {\\n            None,\\n            PackageType.NOARCH_GENERIC,\\n            PackageType.NOARCH_PYTHON,\\n        }\\n\\n    @staticmethod\\n    def unmanageable_package_types():\\n        return {\\n            PackageType.VIRTUAL_PYTHON_EGG_UNMANAGEABLE,\\n            PackageType.VIRTUAL_PYTHON_EGG_LINK,\\n            PackageType.VIRTUAL_SYSTEM,\\n        }\\n\\n\\nclass NoarchType(Enum):\\n    generic = \\\"generic\\\"\\n    python = \\\"python\\\"\\n\\n    @staticmethod\\n    def coerce(val):\\n        # what a mess\\n        if isinstance(val, NoarchType):\\n            return val\\n        valtype = getattr(val, \\\"type\\\", None)\\n        if isinstance(valtype, NoarchType):  # see issue #8311\\n            return valtype\\n        if isinstance(val, bool):\\n            val = NoarchType.generic if val else None\\n        if isinstance(val, str):\\n            val = val.lower()\\n            if val == \\\"python\\\":\\n                val = NoarchType.python\\n            elif val == \\\"generic\\\":\\n                val = NoarchType.generic\\n            else:\\n                try:\\n                    val = NoarchType.generic if boolify(val) else None\\n                except TypeCoercionError:\\n                    raise CondaUpgradeError(\\n                        dals(\\n                            f\\\"\\\"\\\"\\n                    The noarch type for this package is set to '{val}'.\\n                    The current version of conda is too old to install this package.\\n                    Please update conda.\\n                    \\\"\\\"\\\"\\n                        )\\n                    )\\n        return val\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"\\nModels are data transfer objects or \\\"light-weight\\\" domain objects with no appreciable logic\\nother than their own validation. Models are used to pass data between layers of the stack. In\\nmany ways they are similar to ORM objects.  Unlike ORM objects, they are NOT themselves allowed\\nto load data from a remote resource.  Thought of another way, they cannot import from\\n``conda.gateways``, but rather ``conda.gateways`` imports from ``conda.models`` as appropriate\\nto create model objects from remote resources.\\n\\nConda modules importable from ``conda.models`` are\\n\\n- ``conda._vendor``\\n- ``conda.common``\\n- ``conda.models``\\n\\n\\\"\\\"\\\"\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Implements the query language for conda packages (a.k.a, MatchSpec).\\n\\nThe MatchSpec is the conda package specification (e.g. `conda==23.3`, `python<3.7`,\\n`cryptography * *_0`) and is used to communicate the desired packages to install.\\n\\\"\\\"\\\"\\n\\nimport re\\nimport warnings\\nfrom abc import ABCMeta, abstractmethod, abstractproperty\\nfrom collections.abc import Mapping\\nfrom functools import reduce\\nfrom itertools import chain\\nfrom logging import getLogger\\nfrom operator import attrgetter\\nfrom os.path import basename\\n\\nfrom ..auxlib.decorators import memoizedproperty\\nfrom ..base.constants import CONDA_PACKAGE_EXTENSION_V1, CONDA_PACKAGE_EXTENSION_V2\\nfrom ..base.context import context\\nfrom ..common.compat import isiterable\\nfrom ..common.io import dashlist\\nfrom ..common.iterators import groupby_to_dict as groupby\\nfrom ..common.path import expand, is_package_file, strip_pkg_extension, url_to_path\\nfrom ..common.url import is_url, path_to_url, unquote\\nfrom ..exceptions import InvalidMatchSpec, InvalidSpec\\nfrom .channel import Channel\\nfrom .version import BuildNumberMatch, VersionSpec\\n\\ntry:\\n    from frozendict import frozendict\\nexcept ImportError:\\n    from ..auxlib.collection import frozendict\\n\\nlog = getLogger(__name__)\\n\\n\\nclass MatchSpecType(type):\\n    def __call__(cls, spec_arg=None, **kwargs):\\n        try:\\n            if spec_arg:\\n                if isinstance(spec_arg, MatchSpec) and not kwargs:\\n                    return spec_arg\\n                elif isinstance(spec_arg, MatchSpec):\\n                    new_kwargs = dict(spec_arg._match_components)\\n                    new_kwargs.setdefault(\\\"optional\\\", spec_arg.optional)\\n                    new_kwargs.setdefault(\\\"target\\\", spec_arg.target)\\n                    new_kwargs[\\\"_original_spec_str\\\"] = spec_arg.original_spec_str\\n                    new_kwargs.update(**kwargs)\\n                    return super().__call__(**new_kwargs)\\n                elif isinstance(spec_arg, str):\\n                    parsed = _parse_spec_str(spec_arg)\\n                    if kwargs:\\n                        parsed = dict(parsed, **kwargs)\\n                        if set(kwargs) - {\\\"optional\\\", \\\"target\\\"}:\\n                            # if kwargs has anything but optional and target,\\n                            # strip out _original_spec_str from parsed\\n                            parsed.pop(\\\"_original_spec_str\\\", None)\\n                    return super().__call__(**parsed)\\n                elif isinstance(spec_arg, Mapping):\\n                    parsed = dict(spec_arg, **kwargs)\\n                    return super().__call__(**parsed)\\n                elif hasattr(spec_arg, \\\"to_match_spec\\\"):\\n                    spec = spec_arg.to_match_spec()\\n                    if kwargs:\\n                        return MatchSpec(spec, **kwargs)\\n                    else:\\n                        return spec\\n                else:\\n                    raise InvalidSpec(\\n                        f\\\"Invalid MatchSpec:\\\\n  spec_arg={spec_arg}\\\\n  kwargs={kwargs}\\\"\\n                    )\\n            else:\\n                return super().__call__(**kwargs)\\n        except InvalidSpec as e:\\n            msg = \\\"\\\"\\n            if spec_arg:\\n                msg += f\\\"{spec_arg}\\\"\\n            if kwargs:\\n                msg += \\\" \\\" + \\\", \\\".join(f\\\"{k}={v}\\\" for k, v in kwargs.items())\\n            raise InvalidMatchSpec(msg, details=e) from e\\n\\n\\nclass MatchSpec(metaclass=MatchSpecType):\\n    \\\"\\\"\\\"The query language for conda packages.\\n\\n    Any of the fields that comprise a :class:`PackageRecord` can be used to compose a\\n    :class:`MatchSpec`.\\n\\n    :class:`MatchSpec` can be composed with keyword arguments, where keys are any of the\\n    attributes of :class:`PackageRecord`.  Values for keyword arguments are the exact values the\\n    attribute should match against.  Many fields can also be matched against non-exact values--by\\n    including wildcard `*` and `>`/`<` ranges--where supported.  Any non-specified field is\\n    the equivalent of a full wildcard match.\\n\\n    :class:`MatchSpec` can also be composed using a single positional argument, with optional\\n    keyword arguments.  Keyword arguments also override any conflicting information provided in\\n    the positional argument.  The positional argument can be either an existing :class:`MatchSpec`\\n    instance or a string.  Conda has historically had several string representations for equivalent\\n    :class:`MatchSpec`s.  This :class:`MatchSpec` should accept any existing valid spec string, and\\n    correctly compose a :class:`MatchSpec` instance.\\n\\n    A series of rules are now followed for creating the canonical string representation of a\\n    :class:`MatchSpec` instance.  The canonical string representation can generically be\\n    represented by\\n\\n        (channel(/subdir):(namespace):)name(version(build))[key1=value1,key2=value2]\\n\\n    where `()` indicate optional fields.  The rules for constructing a canonical string\\n    representation are:\\n\\n    1. `name` (i.e. \\\"package name\\\") is required, but its value can be '*'.  Its position is always\\n       outside the key-value brackets.\\n    2. If `version` is an exact version, it goes outside the key-value brackets and is prepended\\n       by `==`. If `version` is a \\\"fuzzy\\\" value (e.g. `1.11.*`), it goes outside the key-value\\n       brackets with the `.*` left off and is prepended by `=`.  Otherwise `version` is included\\n       inside key-value brackets.\\n    3. If `version` is an exact version, and `build` is an exact value, `build` goes outside\\n       key-value brackets prepended by a `=`.  Otherwise, `build` goes inside key-value brackets.\\n       `build_string` is an alias for `build`.\\n    4. The `namespace` position is being held for a future conda feature.\\n    5. If `channel` is included and is an exact value, a `::` separator is ued between `channel`\\n       and `name`.  `channel` can either be a canonical channel name or a channel url.  In the\\n       canonical string representation, the canonical channel name will always be used.\\n    6. If `channel` is an exact value and `subdir` is an exact value, `subdir` is appended to\\n       `channel` with a `/` separator.  Otherwise, `subdir` is included in the key-value brackets.\\n    7. Key-value brackets can be delimited by comma, space, or comma+space.  Value can optionally\\n       be wrapped in single or double quotes, but must be wrapped if `value` contains a comma,\\n       space, or equal sign.  The canonical format uses comma delimiters and single quotes.\\n    8. When constructing a :class:`MatchSpec` instance from a string, any key-value pair given\\n       inside the key-value brackets overrides any matching parameter given outside the brackets.\\n\\n    When :class:`MatchSpec` attribute values are simple strings, the are interpreted using the\\n    following conventions:\\n\\n      - If the string begins with `^` and ends with `$`, it is converted to a regex.\\n      - If the string contains an asterisk (`*`), it is transformed from a glob to a regex.\\n      - Otherwise, an exact match to the string is sought.\\n\\n\\n    Examples:\\n        >>> str(MatchSpec(name='foo', build='py2*', channel='conda-forge'))\\n        'conda-forge::foo[build=py2*]'\\n        >>> str(MatchSpec('foo 1.0 py27_0'))\\n        'foo==1.0=py27_0'\\n        >>> str(MatchSpec('foo=1.0=py27_0'))\\n        'foo==1.0=py27_0'\\n        >>> str(MatchSpec('conda-forge::foo[version=1.0.*]'))\\n        'conda-forge::foo=1.0'\\n        >>> str(MatchSpec('conda-forge/linux-64::foo>=1.0'))\\n        \\\"conda-forge/linux-64::foo[version='>=1.0']\\\"\\n        >>> str(MatchSpec('*/linux-64::foo>=1.0'))\\n        \\\"foo[subdir=linux-64,version='>=1.0']\\\"\\n\\n    To fully-specify a package with a full, exact spec, the fields\\n      - channel\\n      - subdir\\n      - name\\n      - version\\n      - build\\n    must be given as exact values.  In the future, the namespace field will be added to this list.\\n    Alternatively, an exact spec is given by '*[md5=12345678901234567890123456789012]'\\n    or '*[sha256=f453db4ffe2271ec492a2913af4e61d4a6c118201f07de757df0eff769b65d2e]'.\\n    \\\"\\\"\\\"\\n\\n    FIELD_NAMES = (\\n        \\\"channel\\\",\\n        \\\"subdir\\\",\\n        \\\"name\\\",\\n        \\\"version\\\",\\n        \\\"build\\\",\\n        \\\"build_number\\\",\\n        \\\"track_features\\\",\\n        \\\"features\\\",\\n        \\\"url\\\",\\n        \\\"md5\\\",\\n        \\\"sha256\\\",\\n        \\\"license\\\",\\n        \\\"license_family\\\",\\n        \\\"fn\\\",\\n    )\\n    FIELD_NAMES_SET = frozenset(FIELD_NAMES)\\n    _MATCHER_CACHE = {}\\n\\n    def __init__(self, optional=False, target=None, **kwargs):\\n        self._optional = optional\\n        self._target = target\\n        self._original_spec_str = kwargs.pop(\\\"_original_spec_str\\\", None)\\n        self._match_components = self._build_components(**kwargs)\\n\\n    @classmethod\\n    def from_dist_str(cls, dist_str):\\n        parts = {}\\n        if dist_str[-len(CONDA_PACKAGE_EXTENSION_V2) :] == CONDA_PACKAGE_EXTENSION_V2:\\n            dist_str = dist_str[: -len(CONDA_PACKAGE_EXTENSION_V2)]\\n        elif dist_str[-len(CONDA_PACKAGE_EXTENSION_V1) :] == CONDA_PACKAGE_EXTENSION_V1:\\n            dist_str = dist_str[: -len(CONDA_PACKAGE_EXTENSION_V1)]\\n        if \\\"::\\\" in dist_str:\\n            channel_subdir_str, dist_str = dist_str.split(\\\"::\\\", 1)\\n            if \\\"/\\\" in channel_subdir_str:\\n                channel_str, subdir = channel_subdir_str.rsplit(\\\"/\\\", 1)\\n                if subdir not in context.known_subdirs:\\n                    channel_str = channel_subdir_str\\n                    subdir = None\\n                parts[\\\"channel\\\"] = channel_str\\n                if subdir:\\n                    parts[\\\"subdir\\\"] = subdir\\n            else:\\n                parts[\\\"channel\\\"] = channel_subdir_str\\n\\n        name, version, build = dist_str.rsplit(\\\"-\\\", 2)\\n        parts.update(\\n            {\\n                \\\"name\\\": name,\\n                \\\"version\\\": version,\\n                \\\"build\\\": build,\\n            }\\n        )\\n        return cls(**parts)\\n\\n    def get_exact_value(self, field_name):\\n        v = self._match_components.get(field_name)\\n        return v and v.exact_value\\n\\n    def get_raw_value(self, field_name):\\n        v = self._match_components.get(field_name)\\n        return v and v.raw_value\\n\\n    def get(self, field_name, default=None):\\n        v = self.get_raw_value(field_name)\\n        return default if v is None else v\\n\\n    @property\\n    def is_name_only_spec(self):\\n        return (\\n            len(self._match_components) == 1\\n            and \\\"name\\\" in self._match_components\\n            and self.name != \\\"*\\\"\\n        )\\n\\n    def dist_str(self):\\n        return self.__str__()\\n\\n    @property\\n    def optional(self):\\n        return self._optional\\n\\n    @property\\n    def target(self):\\n        return self._target\\n\\n    @property\\n    def original_spec_str(self):\\n        return self._original_spec_str\\n\\n    def match(self, rec):\\n        \\\"\\\"\\\"\\n        Accepts a `PackageRecord` or a dict, and matches can pull from any field\\n        in that record.  Returns True for a match, and False for no match.\\n        \\\"\\\"\\\"\\n        if isinstance(rec, dict):\\n            # TODO: consider AttrDict instead of PackageRecord\\n            from .records import PackageRecord\\n\\n            rec = PackageRecord.from_objects(rec)\\n        for field_name, v in self._match_components.items():\\n            if not self._match_individual(rec, field_name, v):\\n                return False\\n        return True\\n\\n    def _match_individual(self, record, field_name, match_component):\\n        val = getattr(record, field_name)\\n        try:\\n            return match_component.match(val)\\n        except AttributeError:\\n            return match_component == val\\n\\n    def _is_simple(self):\\n        return (\\n            len(self._match_components) == 1\\n            and self.get_exact_value(\\\"name\\\") is not None\\n        )\\n\\n    def _is_single(self):\\n        return len(self._match_components) == 1\\n\\n    def _to_filename_do_not_use(self):\\n        # WARNING: this is potentially unreliable and use should probably be limited\\n        #   returns None if a filename can't be constructed\\n        fn_field = self.get_exact_value(\\\"fn\\\")\\n        if fn_field:\\n            return fn_field\\n        vals = tuple(self.get_exact_value(x) for x in (\\\"name\\\", \\\"version\\\", \\\"build\\\"))\\n        if not any(x is None for x in vals):\\n            return (\\\"{}-{}-{}\\\".format(*vals)) + CONDA_PACKAGE_EXTENSION_V1\\n        else:\\n            return None\\n\\n    def __repr__(self):\\n        builder = [f'{self.__class__.__name__}(\\\"{self}\\\"']\\n        if self.target:\\n            builder.append(f', target=\\\"{self.target}\\\"')\\n        if self.optional:\\n            builder.append(\\\", optional=True\\\")\\n        builder.append(\\\")\\\")\\n        return \\\"\\\".join(builder)\\n\\n    def __str__(self):\\n        builder = []\\n        brackets = []\\n\\n        channel_matcher = self._match_components.get(\\\"channel\\\")\\n        if channel_matcher and channel_matcher.exact_value:\\n            builder.append(str(channel_matcher))\\n        elif channel_matcher and not channel_matcher.matches_all:\\n            brackets.append(f\\\"channel={str(channel_matcher)}\\\")\\n\\n        subdir_matcher = self._match_components.get(\\\"subdir\\\")\\n        if subdir_matcher:\\n            if channel_matcher and channel_matcher.exact_value:\\n                builder.append(f\\\"/{subdir_matcher}\\\")\\n            else:\\n                brackets.append(f\\\"subdir={subdir_matcher}\\\")\\n\\n        name_matcher = self._match_components.get(\\\"name\\\", \\\"*\\\")\\n        builder.append((\\\"::%s\\\" if builder else \\\"%s\\\") % name_matcher)\\n\\n        version = self._match_components.get(\\\"version\\\")\\n        build = self._match_components.get(\\\"build\\\")\\n        version_exact = False\\n        if version:\\n            version = str(version)\\n            if any(s in version for s in \\\"><$^|,\\\"):\\n                brackets.append(f\\\"version='{version}'\\\")\\n            elif version[:2] in (\\\"!=\\\", \\\"~=\\\"):\\n                if build:\\n                    brackets.append(f\\\"version='{version}'\\\")\\n                else:\\n                    builder.append(version)\\n            elif version[-2:] == \\\".*\\\":\\n                builder.append(\\\"=\\\" + version[:-2])\\n            elif version[-1] == \\\"*\\\":\\n                builder.append(\\\"=\\\" + version[:-1])\\n            elif version.startswith(\\\"==\\\"):\\n                builder.append(version)\\n                version_exact = True\\n            else:\\n                builder.append(\\\"==\\\" + version)\\n                version_exact = True\\n\\n        if build:\\n            build = str(build)\\n            if any(s in build for s in \\\"><$^|,\\\"):\\n                brackets.append(f\\\"build='{build}'\\\")\\n            elif \\\"*\\\" in build:\\n                brackets.append(f\\\"build={build}\\\")\\n            elif version_exact:\\n                builder.append(\\\"=\\\" + build)\\n            else:\\n                brackets.append(f\\\"build={build}\\\")\\n\\n        _skip = {\\\"channel\\\", \\\"subdir\\\", \\\"name\\\", \\\"version\\\", \\\"build\\\"}\\n        if \\\"url\\\" in self._match_components and \\\"fn\\\" in self._match_components:\\n            _skip.add(\\\"fn\\\")\\n        for key in self.FIELD_NAMES:\\n            if key not in _skip and key in self._match_components:\\n                if key == \\\"url\\\" and channel_matcher:\\n                    # skip url in canonical str if channel already included\\n                    continue\\n                value = str(self._match_components[key])\\n                if any(s in value for s in \\\", =\\\"):\\n                    brackets.append(f\\\"{key}='{value}'\\\")\\n                else:\\n                    brackets.append(f\\\"{key}={value}\\\")\\n\\n        if brackets:\\n            builder.append(\\\"[{}]\\\".format(\\\",\\\".join(brackets)))\\n\\n        return \\\"\\\".join(builder)\\n\\n    def __json__(self):\\n        return self.__str__()\\n\\n    def conda_build_form(self):\\n        builder = []\\n        name = self.get_exact_value(\\\"name\\\")\\n        assert name\\n        builder.append(name)\\n\\n        build = self.get_raw_value(\\\"build\\\")\\n        version = self.get_raw_value(\\\"version\\\")\\n\\n        if build:\\n            assert version\\n            builder += [version, build]\\n        elif version:\\n            builder.append(version)\\n\\n        return \\\" \\\".join(builder)\\n\\n    def __eq__(self, other):\\n        if isinstance(other, MatchSpec):\\n            return self._hash_key == other._hash_key\\n        else:\\n            return False\\n\\n    def __hash__(self):\\n        return hash(self._hash_key)\\n\\n    @memoizedproperty\\n    def _hash_key(self):\\n        return self._match_components, self.optional, self.target\\n\\n    def __contains__(self, field):\\n        return field in self._match_components\\n\\n    def _build_components(self, **kwargs):\\n        not_fields = set(kwargs) - MatchSpec.FIELD_NAMES_SET\\n        if not_fields:\\n            raise InvalidMatchSpec(\\n                self._original_spec_str, f\\\"Cannot match on field(s): {not_fields}\\\"\\n            )\\n        _make_component = MatchSpec._make_component\\n        return frozendict(_make_component(key, value) for key, value in kwargs.items())\\n\\n    @staticmethod\\n    def _make_component(field_name, value):\\n        if hasattr(value, \\\"match\\\"):\\n            matcher = value\\n            return field_name, matcher\\n\\n        _MATCHER_CACHE = MatchSpec._MATCHER_CACHE\\n        cache_key = (field_name, value)\\n        cached_matcher = _MATCHER_CACHE.get(cache_key)\\n        if cached_matcher:\\n            return field_name, cached_matcher\\n        if field_name in _implementors:\\n            matcher = _implementors[field_name](value)\\n        else:\\n            matcher = ExactStrMatch(str(value))\\n        _MATCHER_CACHE[(field_name, value)] = matcher\\n        return field_name, matcher\\n\\n    @property\\n    def name(self):\\n        return self.get_exact_value(\\\"name\\\") or \\\"*\\\"\\n\\n    #\\n    # Remaining methods are for back compatibility with conda-build. Do not remove\\n    # without coordination with the conda-build team.\\n    #\\n    @property\\n    def strictness(self):\\n        # With the old MatchSpec, strictness==3 if name, version, and\\n        # build were all specified.\\n        s = sum(f in self._match_components for f in (\\\"name\\\", \\\"version\\\", \\\"build\\\"))\\n        if s < len(self._match_components):\\n            return 3\\n        elif not self.get_exact_value(\\\"name\\\") or \\\"build\\\" in self._match_components:\\n            return 3\\n        elif \\\"version\\\" in self._match_components:\\n            return 2\\n        else:\\n            return 1\\n\\n    @property\\n    def spec(self):\\n        return self.conda_build_form()\\n\\n    @property\\n    def version(self):\\n        # in the old MatchSpec object, version was a VersionSpec, not a str\\n        # so we'll keep that API here\\n        return self._match_components.get(\\\"version\\\")\\n\\n    @property\\n    def fn(self):\\n        val = self.get_raw_value(\\\"fn\\\") or self.get_raw_value(\\\"url\\\")\\n        if val:\\n            val = basename(val)\\n        assert val\\n        return val\\n\\n    @classmethod\\n    def merge(cls, match_specs, union=False):\\n        match_specs = sorted(tuple(cls(s) for s in match_specs if s), key=str)\\n        name_groups = groupby(attrgetter(\\\"name\\\"), match_specs)\\n        unmergeable = name_groups.pop(\\\"*\\\", []) + name_groups.pop(None, [])\\n\\n        merged_specs = []\\n        mergeable_groups = tuple(\\n            chain.from_iterable(\\n                groupby(lambda s: s.optional, group).values()\\n                for group in name_groups.values()\\n            )\\n        )\\n        for group in mergeable_groups:\\n            target_groups = groupby(attrgetter(\\\"target\\\"), group)\\n            target_groups.pop(None, None)\\n            if len(target_groups) > 1:\\n                raise ValueError(f\\\"Incompatible MatchSpec merge:{dashlist(group)}\\\")\\n            merged_specs.append(\\n                reduce(lambda x, y: x._merge(y, union), group)\\n                if len(group) > 1\\n                else group[0]\\n            )\\n        return (*merged_specs, *unmergeable)\\n\\n    @classmethod\\n    def union(cls, match_specs):\\n        return cls.merge(match_specs, union=True)\\n\\n    def _merge(self, other, union=False):\\n        if self.optional != other.optional or self.target != other.target:\\n            raise ValueError(f\\\"Incompatible MatchSpec merge:  - {self}\\\\n  - {other}\\\")\\n\\n        final_components = {}\\n        component_names = set(self._match_components) | set(other._match_components)\\n        for component_name in component_names:\\n            this_component = self._match_components.get(component_name)\\n            that_component = other._match_components.get(component_name)\\n            if this_component is None and that_component is None:\\n                continue\\n            elif this_component is None:\\n                final_components[component_name] = that_component\\n            elif that_component is None:\\n                final_components[component_name] = this_component\\n            else:\\n                if union:\\n                    try:\\n                        final = this_component.union(that_component)\\n                    except (AttributeError, ValueError, TypeError):\\n                        final = f\\\"{this_component}|{that_component}\\\"\\n                else:\\n                    final = this_component.merge(that_component)\\n                final_components[component_name] = final\\n        return self.__class__(\\n            optional=self.optional, target=self.target, **final_components\\n        )\\n\\n\\ndef _parse_version_plus_build(v_plus_b):\\n    \\\"\\\"\\\"This should reliably pull the build string out of a version + build string combo.\\n    Examples:\\n        >>> _parse_version_plus_build(\\\"=1.2.3 0\\\")\\n        ('=1.2.3', '0')\\n        >>> _parse_version_plus_build(\\\"1.2.3=0\\\")\\n        ('1.2.3', '0')\\n        >>> _parse_version_plus_build(\\\">=1.0 , < 2.0 py34_0\\\")\\n        ('>=1.0,<2.0', 'py34_0')\\n        >>> _parse_version_plus_build(\\\">=1.0 , < 2.0 =py34_0\\\")\\n        ('>=1.0,<2.0', 'py34_0')\\n        >>> _parse_version_plus_build(\\\"=1.2.3 \\\")\\n        ('=1.2.3', None)\\n        >>> _parse_version_plus_build(\\\">1.8,<2|==1.7\\\")\\n        ('>1.8,<2|==1.7', None)\\n        >>> _parse_version_plus_build(\\\"* openblas_0\\\")\\n        ('*', 'openblas_0')\\n        >>> _parse_version_plus_build(\\\"* *\\\")\\n        ('*', '*')\\n    \\\"\\\"\\\"\\n    parts = re.search(\\n        r\\\"((?:.+?)[^><!,|]?)(?:(?<![=!|,<>~])(?:[ =])([^-=,|<>~]+?))?$\\\", v_plus_b\\n    )\\n    if parts:\\n        version, build = parts.groups()\\n        build = build and build.strip()\\n    else:\\n        version, build = v_plus_b, None\\n    return version and version.replace(\\\" \\\", \\\"\\\"), build\\n\\n\\ndef _parse_legacy_dist(dist_str):\\n    \\\"\\\"\\\"\\n    Examples:\\n        >>> _parse_legacy_dist(\\\"_license-1.1-py27_1.tar.bz2\\\")\\n        ('_license', '1.1', 'py27_1')\\n        >>> _parse_legacy_dist(\\\"_license-1.1-py27_1\\\")\\n        ('_license', '1.1', 'py27_1')\\n    \\\"\\\"\\\"\\n    dist_str, _ = strip_pkg_extension(dist_str)\\n    name, version, build = dist_str.rsplit(\\\"-\\\", 2)\\n    return name, version, build\\n\\n\\ndef _parse_channel(channel_val):\\n    if not channel_val:\\n        return None, None\\n    chn = Channel(channel_val)\\n    channel_name = chn.name or chn.base_url\\n    return channel_name, chn.subdir\\n\\n\\n_PARSE_CACHE = {}\\n\\n\\ndef _parse_spec_str(spec_str):\\n    cached_result = _PARSE_CACHE.get(spec_str)\\n    if cached_result:\\n        return cached_result\\n\\n    original_spec_str = spec_str\\n\\n    # pre-step for ugly backward compat\\n    if spec_str.endswith(\\\"@\\\"):\\n        feature_name = spec_str[:-1]\\n        return {\\n            \\\"name\\\": \\\"*\\\",\\n            \\\"track_features\\\": (feature_name,),\\n        }\\n\\n    # Step 1. strip '#' comment\\n    if \\\"#\\\" in spec_str:\\n        ndx = spec_str.index(\\\"#\\\")\\n        spec_str, _ = spec_str[:ndx], spec_str[ndx:]\\n        spec_str.strip()\\n\\n    # Step 1.b strip ' if ' anticipating future compatibility issues\\n    spec_split = spec_str.split(\\\" if \\\", 1)\\n    if len(spec_split) > 1:\\n        log.debug(\\\"Ignoring conditional in spec %s\\\", spec_str)\\n    spec_str = spec_split[0]\\n\\n    # Step 2. done if spec_str is a tarball\\n    if is_package_file(spec_str):\\n        # treat as a normal url\\n        if not is_url(spec_str):\\n            spec_str = unquote(path_to_url(expand(spec_str)))\\n\\n        channel = Channel(spec_str)\\n        if channel.subdir:\\n            name, version, build = _parse_legacy_dist(channel.package_filename)\\n            result = {\\n                \\\"channel\\\": channel.canonical_name,\\n                \\\"subdir\\\": channel.subdir,\\n                \\\"name\\\": name,\\n                \\\"version\\\": version,\\n                \\\"build\\\": build,\\n                \\\"fn\\\": channel.package_filename,\\n                \\\"url\\\": spec_str,\\n            }\\n        else:\\n            # url is not a channel\\n            if spec_str.startswith(\\\"file://\\\"):\\n                # We must undo percent-encoding when generating fn.\\n                path_or_url = url_to_path(spec_str)\\n            else:\\n                path_or_url = spec_str\\n\\n            return {\\n                \\\"name\\\": \\\"*\\\",\\n                \\\"fn\\\": basename(path_or_url),\\n                \\\"url\\\": spec_str,\\n            }\\n        return result\\n\\n    # Step 3. strip off brackets portion\\n    brackets = {}\\n    m3 = re.match(r\\\".*(?:(\\\\[.*\\\\]))\\\", spec_str)\\n    if m3:\\n        brackets_str = m3.groups()[0]\\n        spec_str = spec_str.replace(brackets_str, \\\"\\\")\\n        brackets_str = brackets_str[1:-1]\\n        m3b = re.finditer(\\n            r'([a-zA-Z0-9_-]+?)=([\\\"\\\\']?)([^\\\\'\\\"]*?)(\\\\2)(?:[, ]|$)', brackets_str\\n        )\\n        for match in m3b:\\n            key, _, value, _ = match.groups()\\n            if not key or not value:\\n                raise InvalidMatchSpec(\\n                    original_spec_str, \\\"key-value mismatch in brackets\\\"\\n                )\\n            brackets[key] = value\\n\\n    # Step 4. strip off parens portion\\n    m4 = re.match(r\\\".*(?:(\\\\(.*\\\\)))\\\", spec_str)\\n    parens = {}\\n    if m4:\\n        parens_str = m4.groups()[0]\\n        spec_str = spec_str.replace(parens_str, \\\"\\\")\\n        parens_str = parens_str[1:-1]\\n        m4b = re.finditer(\\n            r'([a-zA-Z0-9_-]+?)=([\\\"\\\\']?)([^\\\\'\\\"]*?)(\\\\2)(?:[, ]|$)', parens_str\\n        )\\n        for match in m4b:\\n            key, _, value, _ = match.groups()\\n            parens[key] = value\\n        if \\\"optional\\\" in parens_str:\\n            parens[\\\"optional\\\"] = True\\n\\n    # Step 5. strip off '::' channel and namespace\\n    m5 = spec_str.rsplit(\\\":\\\", 2)\\n    m5_len = len(m5)\\n    if m5_len == 3:\\n        channel_str, namespace, spec_str = m5\\n    elif m5_len == 2:\\n        namespace, spec_str = m5\\n        channel_str = None\\n    elif m5_len:\\n        spec_str = m5[0]\\n        channel_str, namespace = None, None\\n    else:\\n        raise NotImplementedError()\\n    channel, subdir = _parse_channel(channel_str)\\n    if \\\"channel\\\" in brackets:\\n        b_channel, b_subdir = _parse_channel(brackets.pop(\\\"channel\\\"))\\n        if b_channel:\\n            channel = b_channel\\n        if b_subdir:\\n            subdir = b_subdir\\n    if \\\"subdir\\\" in brackets:\\n        subdir = brackets.pop(\\\"subdir\\\")\\n\\n    # Step 6. strip off package name from remaining version + build\\n    m3 = re.match(r\\\"([^ =<>!~]+)?([><!=~ ].+)?\\\", spec_str)\\n    if m3:\\n        name, spec_str = m3.groups()\\n        if name is None:\\n            raise InvalidMatchSpec(\\n                original_spec_str, f\\\"no package name found in '{spec_str}'\\\"\\n            )\\n    else:\\n        raise InvalidMatchSpec(original_spec_str, \\\"no package name found\\\")\\n\\n    # Step 7. otherwise sort out version + build\\n    spec_str = spec_str and spec_str.strip()\\n    # This was an attempt to make MatchSpec('numpy-1.11.0-py27_0') work like we'd want. It's\\n    # not possible though because plenty of packages have names with more than one '-'.\\n    # if spec_str is None and name.count('-') >= 2:\\n    #     name, version, build = _parse_legacy_dist(name)\\n    if spec_str:\\n        if \\\"[\\\" in spec_str:\\n            raise InvalidMatchSpec(\\n                original_spec_str, \\\"multiple brackets sections not allowed\\\"\\n            )\\n\\n        version, build = _parse_version_plus_build(spec_str)\\n\\n        # Catch cases where version ends up as \\\"==\\\" and pass it through so existing error\\n        # handling code can treat it like cases where version ends up being \\\"<=\\\" or \\\">=\\\".\\n        # This is necessary because the \\\"Translation\\\" code below mangles \\\"==\\\" into a empty\\n        # string, which results in an empty version field on \\\"components.\\\" The set of fields\\n        # on components drives future logic which breaks on an empty string but will deal with\\n        # missing versions like \\\"==\\\", \\\"<=\\\", and \\\">=\\\" \\\"correctly.\\\"\\n        #\\n        # All of these \\\"missing version\\\" cases result from match specs like \\\"numpy==\\\",\\n        # \\\"numpy<=\\\", \\\"numpy>=\\\", \\\"numpy= \\\" (with trailing space). Existing code indicates\\n        # these should be treated as an error and an exception raised.\\n        # IMPORTANT: \\\"numpy=\\\" (no trailing space) is treated as valid.\\n        if version == \\\"==\\\" or version == \\\"=\\\":\\n            pass\\n        # Otherwise,\\n        # translate version '=1.2.3' to '1.2.3*'\\n        # is it a simple version starting with '='? i.e. '=1.2.3'\\n        elif version[0] == \\\"=\\\":\\n            test_str = version[1:]\\n            if version[:2] == \\\"==\\\" and build is None:\\n                version = version[2:]\\n            elif not any(c in test_str for c in \\\"=,|\\\"):\\n                if build is None and test_str[-1] != \\\"*\\\":\\n                    version = test_str + \\\"*\\\"\\n                else:\\n                    version = test_str\\n    else:\\n        version, build = None, None\\n\\n    # Step 8. now compile components together\\n    components = {}\\n    components[\\\"name\\\"] = name or \\\"*\\\"\\n\\n    if channel is not None:\\n        components[\\\"channel\\\"] = channel\\n    if subdir is not None:\\n        components[\\\"subdir\\\"] = subdir\\n    if namespace is not None:\\n        # components['namespace'] = namespace\\n        pass\\n    if version is not None:\\n        components[\\\"version\\\"] = version\\n    if build is not None:\\n        components[\\\"build\\\"] = build\\n\\n    # anything in brackets will now strictly override key as set in other area of spec str\\n    # EXCEPT FOR: name\\n    # If we let name in brackets override a name outside of brackets it is possible to write\\n    # MatchSpecs that appear to install one package but actually install a completely different one\\n    # e.g. tensorflow[name=* version=* md5=<hash of pytorch package> ] will APPEAR to install\\n    # tensorflow but actually install pytorch.\\n    if \\\"name\\\" in components and \\\"name\\\" in brackets:\\n        msg = (\\n            f\\\"'name' specified both inside ({brackets['name']}) and outside \\\"\\n            f\\\"({components['name']}) of brackets. The value outside of brackets \\\"\\n            f\\\"({components['name']}) will be used.\\\"\\n        )\\n        warnings.warn(msg, UserWarning)\\n        del brackets[\\\"name\\\"]\\n    components.update(brackets)\\n    components[\\\"_original_spec_str\\\"] = original_spec_str\\n    _PARSE_CACHE[original_spec_str] = components\\n    return components\\n\\n\\nclass MatchInterface(metaclass=ABCMeta):\\n    def __init__(self, value):\\n        self._raw_value = value\\n\\n    @abstractmethod\\n    def match(self, other):\\n        raise NotImplementedError()\\n\\n    def matches(self, value):\\n        return self.match(value)\\n\\n    @property\\n    def raw_value(self):\\n        return self._raw_value\\n\\n    @abstractproperty\\n    def exact_value(self):\\n        \\\"\\\"\\\"If the match value is an exact specification, returns the value.\\n        Otherwise returns None.\\n        \\\"\\\"\\\"\\n        raise NotImplementedError()\\n\\n    def merge(self, other):\\n        if self.raw_value != other.raw_value:\\n            raise ValueError(\\n                f\\\"Incompatible component merge:\\\\n  - {self.raw_value!r}\\\\n  - {other.raw_value!r}\\\"\\n            )\\n        return self.raw_value\\n\\n    def union(self, other):\\n        options = {self.raw_value, other.raw_value}\\n        return \\\"|\\\".join(options)\\n\\n\\nclass _StrMatchMixin:\\n    def __str__(self):\\n        return self._raw_value\\n\\n    def __repr__(self):\\n        return f\\\"{self.__class__.__name__}('{self._raw_value}')\\\"\\n\\n    def __eq__(self, other):\\n        return isinstance(other, self.__class__) and self._raw_value == other._raw_value\\n\\n    def __hash__(self):\\n        return hash(self._raw_value)\\n\\n    @property\\n    def exact_value(self):\\n        return self._raw_value\\n\\n\\nclass ExactStrMatch(_StrMatchMixin, MatchInterface):\\n    __slots__ = (\\\"_raw_value\\\",)\\n\\n    def __init__(self, value):\\n        super().__init__(value)\\n\\n    def match(self, other):\\n        try:\\n            _other_val = other._raw_value\\n        except AttributeError:\\n            _other_val = str(other)\\n        return self._raw_value == _other_val\\n\\n\\nclass ExactLowerStrMatch(ExactStrMatch):\\n    def __init__(self, value):\\n        super().__init__(value.lower())\\n\\n    def match(self, other):\\n        try:\\n            _other_val = other._raw_value\\n        except AttributeError:\\n            _other_val = str(other)\\n        return self._raw_value == _other_val.lower()\\n\\n\\nclass GlobStrMatch(_StrMatchMixin, MatchInterface):\\n    __slots__ = \\\"_raw_value\\\", \\\"_re_match\\\"\\n\\n    def __init__(self, value):\\n        super().__init__(value)\\n        self._re_match = None\\n\\n        try:\\n            if value.startswith(\\\"^\\\") and value.endswith(\\\"$\\\"):\\n                self._re_match = re.compile(value).match\\n            elif \\\"*\\\" in value:\\n                value = re.escape(value).replace(\\\"\\\\\\\\*\\\", r\\\".*\\\")\\n                self._re_match = re.compile(rf\\\"^(?:{value})$\\\").match\\n        except re.error as e:\\n            raise InvalidMatchSpec(\\n                value, f\\\"Contains an invalid regular expression. '{e}'\\\"\\n            )\\n\\n    def match(self, other):\\n        try:\\n            _other_val = other._raw_value\\n        except AttributeError:\\n            _other_val = str(other)\\n\\n        if self._re_match:\\n            return self._re_match(_other_val)\\n        else:\\n            return self._raw_value == _other_val\\n\\n    @property\\n    def exact_value(self):\\n        return self._raw_value if self._re_match is None else None\\n\\n    @property\\n    def matches_all(self):\\n        return self._raw_value == \\\"*\\\"\\n\\n\\nclass GlobLowerStrMatch(GlobStrMatch):\\n    def __init__(self, value):\\n        super().__init__(value.lower())\\n\\n\\nclass SplitStrMatch(MatchInterface):\\n    __slots__ = (\\\"_raw_value\\\",)\\n\\n    def __init__(self, value):\\n        super().__init__(self._convert(value))\\n\\n    def _convert(self, value):\\n        try:\\n            return frozenset(value.replace(\\\" \\\", \\\",\\\").split(\\\",\\\"))\\n        except AttributeError:\\n            if isiterable(value):\\n                return frozenset(value)\\n            raise\\n\\n    def match(self, other):\\n        try:\\n            return other and self._raw_value & other._raw_value\\n        except AttributeError:\\n            return self._raw_value & self._convert(other)\\n\\n    def __repr__(self):\\n        if self._raw_value:\\n            return \\\"{{{}}}\\\".format(\\\", \\\".join(f\\\"'{s}'\\\" for s in sorted(self._raw_value)))\\n        else:\\n            return \\\"set()\\\"\\n\\n    def __str__(self):\\n        # this space delimiting makes me nauseous\\n        return \\\" \\\".join(sorted(self._raw_value))\\n\\n    def __eq__(self, other):\\n        return isinstance(other, self.__class__) and self._raw_value == other._raw_value\\n\\n    def __hash__(self):\\n        return hash(self._raw_value)\\n\\n    @property\\n    def exact_value(self):\\n        return self._raw_value\\n\\n\\nclass FeatureMatch(MatchInterface):\\n    __slots__ = (\\\"_raw_value\\\",)\\n\\n    def __init__(self, value):\\n        super().__init__(self._convert(value))\\n\\n    def _convert(self, value):\\n        if not value:\\n            return frozenset()\\n        elif isinstance(value, str):\\n            return frozenset(\\n                f\\n                for f in (ff.strip() for ff in value.replace(\\\" \\\", \\\",\\\").split(\\\",\\\"))\\n                if f\\n            )\\n        else:\\n            return frozenset(f for f in (ff.strip() for ff in value) if f)\\n\\n    def match(self, other):\\n        other = self._convert(other)\\n        return self._raw_value == other\\n\\n    def __repr__(self):\\n        return \\\"[{}]\\\".format(\\\", \\\".join(f\\\"'{k}'\\\" for k in sorted(self._raw_value)))\\n\\n    def __str__(self):\\n        return \\\" \\\".join(sorted(self._raw_value))\\n\\n    def __eq__(self, other):\\n        return isinstance(other, self.__class__) and self._raw_value == other._raw_value\\n\\n    def __hash__(self):\\n        return hash(self._raw_value)\\n\\n    @property\\n    def exact_value(self):\\n        return self._raw_value\\n\\n\\nclass ChannelMatch(GlobStrMatch):\\n    def __init__(self, value):\\n        self._re_match = None\\n\\n        try:\\n            if isinstance(value, str):\\n                if value.startswith(\\\"^\\\") and value.endswith(\\\"$\\\"):\\n                    self._re_match = re.compile(value).match\\n                elif \\\"*\\\" in value:\\n                    self._re_match = re.compile(\\n                        r\\\"^(?:{})$\\\".format(value.replace(\\\"*\\\", r\\\".*\\\"))\\n                    ).match\\n                else:\\n                    value = Channel(value)\\n        except re.error as e:\\n            raise InvalidMatchSpec(\\n                value, f\\\"Contains an invalid regular expression. '{e}'\\\"\\n            )\\n\\n        super(GlobStrMatch, self).__init__(value)\\n\\n    def match(self, other):\\n        try:\\n            _other_val = Channel(other._raw_value)\\n        except AttributeError:\\n            _other_val = Channel(other)\\n\\n        if self._re_match:\\n            return self._re_match(_other_val.canonical_name)\\n        else:\\n            # assert ChannelMatch('pkgs/free').match('defaults') is False\\n            # assert ChannelMatch('defaults').match('pkgs/free') is True\\n            return self._raw_value.name in (_other_val.name, _other_val.canonical_name)\\n\\n    def __str__(self):\\n        try:\\n            return f\\\"{self._raw_value.name}\\\"\\n        except AttributeError:\\n            return f\\\"{self._raw_value}\\\"\\n\\n    def __repr__(self):\\n        return f\\\"'{self.__str__()}'\\\"\\n\\n\\nclass CaseInsensitiveStrMatch(GlobLowerStrMatch):\\n    def match(self, other):\\n        try:\\n            _other_val = other._raw_value\\n        except AttributeError:\\n            _other_val = str(other)\\n\\n        _other_val = _other_val.lower()\\n        if self._re_match:\\n            return self._re_match(_other_val)\\n        else:\\n            return self._raw_value == _other_val\\n\\n\\n_implementors = {\\n    \\\"channel\\\": ChannelMatch,\\n    \\\"name\\\": GlobLowerStrMatch,\\n    \\\"version\\\": VersionSpec,\\n    \\\"build\\\": GlobStrMatch,\\n    \\\"build_number\\\": BuildNumberMatch,\\n    \\\"track_features\\\": FeatureMatch,\\n    \\\"features\\\": FeatureMatch,\\n    \\\"license\\\": CaseInsensitiveStrMatch,\\n    \\\"license_family\\\": CaseInsensitiveStrMatch,\\n}\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Environment object describing the conda environment.yaml file.\\\"\\\"\\\"\\n\\nimport json\\nimport os\\nimport re\\nfrom itertools import chain\\nfrom os.path import abspath, expanduser, expandvars\\n\\nfrom ..base.context import context\\nfrom ..cli import common, install\\nfrom ..common.iterators import groupby_to_dict as groupby\\nfrom ..common.iterators import unique\\nfrom ..common.serialize import yaml_safe_dump, yaml_safe_load\\nfrom ..core.prefix_data import PrefixData\\nfrom ..exceptions import EnvironmentFileEmpty, EnvironmentFileNotFound\\nfrom ..gateways.connection.download import download_text\\nfrom ..gateways.connection.session import CONDA_SESSION_SCHEMES\\nfrom ..history import History\\nfrom ..models.enums import PackageType\\nfrom ..models.match_spec import MatchSpec\\nfrom ..models.prefix_graph import PrefixGraph\\n\\nVALID_KEYS = (\\\"name\\\", \\\"dependencies\\\", \\\"prefix\\\", \\\"channels\\\", \\\"variables\\\")\\n\\n\\ndef validate_keys(data, kwargs):\\n    \\\"\\\"\\\"Check for unknown keys, remove them and print a warning\\\"\\\"\\\"\\n    invalid_keys = []\\n    new_data = data.copy() if data else {}\\n    for key in data.keys():\\n        if key not in VALID_KEYS:\\n            invalid_keys.append(key)\\n            new_data.pop(key)\\n\\n    if invalid_keys:\\n        filename = kwargs.get(\\\"filename\\\")\\n        verb = \\\"are\\\" if len(invalid_keys) != 1 else \\\"is\\\"\\n        plural = \\\"s\\\" if len(invalid_keys) != 1 else \\\"\\\"\\n        print(\\n            f\\\"\\\\nEnvironmentSectionNotValid: The following section{plural} on \\\"\\n            f\\\"'{filename}' {verb} invalid and will be ignored:\\\"\\n        )\\n        for key in invalid_keys:\\n            print(f\\\" - {key}\\\")\\n        print()\\n\\n    deps = data.get(\\\"dependencies\\\", [])\\n    depsplit = re.compile(r\\\"[<>~\\\\s=]\\\")\\n    is_pip = lambda dep: \\\"pip\\\" in depsplit.split(dep)[0].split(\\\"::\\\")\\n    lists_pip = any(is_pip(dep) for dep in deps if not isinstance(dep, dict))\\n    for dep in deps:\\n        if isinstance(dep, dict) and \\\"pip\\\" in dep and not lists_pip:\\n            print(\\n                \\\"Warning: you have pip-installed dependencies in your environment file, \\\"\\n                \\\"but you do not list pip itself as one of your conda dependencies.  Conda \\\"\\n                \\\"may not use the correct pip to install your packages, and they may end up \\\"\\n                \\\"in the wrong place.  Please add an explicit pip dependency.  I'm adding one\\\"\\n                \\\" for you, but still nagging you.\\\"\\n            )\\n            new_data[\\\"dependencies\\\"].insert(0, \\\"pip\\\")\\n            break\\n    return new_data\\n\\n\\ndef from_environment(\\n    name, prefix, no_builds=False, ignore_channels=False, from_history=False\\n):\\n    \\\"\\\"\\\"\\n        Get ``Environment`` object from prefix\\n    Args:\\n        name: The name of environment\\n        prefix: The path of prefix\\n        no_builds: Whether has build requirement\\n        ignore_channels: whether ignore_channels\\n        from_history: Whether environment file should be based on explicit specs in history\\n\\n    Returns:     Environment object\\n    \\\"\\\"\\\"\\n    pd = PrefixData(prefix, pip_interop_enabled=True)\\n    variables = pd.get_environment_env_vars()\\n\\n    if from_history:\\n        history = History(prefix).get_requested_specs_map()\\n        deps = [str(package) for package in history.values()]\\n        return Environment(\\n            name=name,\\n            dependencies=deps,\\n            channels=list(context.channels),\\n            prefix=prefix,\\n            variables=variables,\\n        )\\n\\n    precs = tuple(PrefixGraph(pd.iter_records()).graph)\\n    grouped_precs = groupby(lambda x: x.package_type, precs)\\n    conda_precs = sorted(\\n        (\\n            *grouped_precs.get(None, ()),\\n            *grouped_precs.get(PackageType.NOARCH_GENERIC, ()),\\n            *grouped_precs.get(PackageType.NOARCH_PYTHON, ()),\\n        ),\\n        key=lambda x: x.name,\\n    )\\n\\n    pip_precs = sorted(\\n        (\\n            *grouped_precs.get(PackageType.VIRTUAL_PYTHON_WHEEL, ()),\\n            *grouped_precs.get(PackageType.VIRTUAL_PYTHON_EGG_MANAGEABLE, ()),\\n            *grouped_precs.get(PackageType.VIRTUAL_PYTHON_EGG_UNMANAGEABLE, ()),\\n        ),\\n        key=lambda x: x.name,\\n    )\\n\\n    if no_builds:\\n        dependencies = [\\\"=\\\".join((a.name, a.version)) for a in conda_precs]\\n    else:\\n        dependencies = [\\\"=\\\".join((a.name, a.version, a.build)) for a in conda_precs]\\n    if pip_precs:\\n        dependencies.append({\\\"pip\\\": [f\\\"{a.name}=={a.version}\\\" for a in pip_precs]})\\n\\n    channels = list(context.channels)\\n    if not ignore_channels:\\n        for prec in conda_precs:\\n            canonical_name = prec.channel.canonical_name\\n            if canonical_name not in channels:\\n                channels.insert(0, canonical_name)\\n    return Environment(\\n        name=name,\\n        dependencies=dependencies,\\n        channels=channels,\\n        prefix=prefix,\\n        variables=variables,\\n    )\\n\\n\\ndef from_yaml(yamlstr, **kwargs):\\n    \\\"\\\"\\\"Load and return a ``Environment`` from a given ``yaml`` string\\\"\\\"\\\"\\n    data = yaml_safe_load(yamlstr)\\n    filename = kwargs.get(\\\"filename\\\")\\n    if data is None:\\n        raise EnvironmentFileEmpty(filename)\\n    data = validate_keys(data, kwargs)\\n\\n    if kwargs is not None:\\n        for key, value in kwargs.items():\\n            data[key] = value\\n    _expand_channels(data)\\n    return Environment(**data)\\n\\n\\ndef _expand_channels(data):\\n    \\\"\\\"\\\"Expands ``Environment`` variables for the channels found in the ``yaml`` data\\\"\\\"\\\"\\n    data[\\\"channels\\\"] = [\\n        os.path.expandvars(channel) for channel in data.get(\\\"channels\\\", [])\\n    ]\\n\\n\\ndef from_file(filename):\\n    \\\"\\\"\\\"Load and return an ``Environment`` from a given file\\\"\\\"\\\"\\n    url_scheme = filename.split(\\\"://\\\", 1)[0]\\n    if url_scheme in CONDA_SESSION_SCHEMES:\\n        yamlstr = download_text(filename)\\n    elif not os.path.exists(filename):\\n        raise EnvironmentFileNotFound(filename)\\n    else:\\n        with open(filename, \\\"rb\\\") as fp:\\n            yamlb = fp.read()\\n            try:\\n                yamlstr = yamlb.decode(\\\"utf-8\\\")\\n            except UnicodeDecodeError:\\n                yamlstr = yamlb.decode(\\\"utf-16\\\")\\n    return from_yaml(yamlstr, filename=filename)\\n\\n\\nclass Dependencies(dict):\\n    \\\"\\\"\\\"A ``dict`` subclass that parses the raw dependencies into a conda and pip list\\\"\\\"\\\"\\n\\n    def __init__(self, raw, *args, **kwargs):\\n        super().__init__(*args, **kwargs)\\n        self.raw = raw\\n        self.parse()\\n\\n    def parse(self):\\n        \\\"\\\"\\\"Parse the raw dependencies into a conda and pip list\\\"\\\"\\\"\\n        if not self.raw:\\n            return\\n\\n        self.update({\\\"conda\\\": []})\\n\\n        for line in self.raw:\\n            if isinstance(line, dict):\\n                self.update(line)\\n            else:\\n                self[\\\"conda\\\"].append(common.arg2spec(line))\\n\\n        if \\\"pip\\\" in self:\\n            if not self[\\\"pip\\\"]:\\n                del self[\\\"pip\\\"]\\n            if not any(MatchSpec(s).name == \\\"pip\\\" for s in self[\\\"conda\\\"]):\\n                self[\\\"conda\\\"].append(\\\"pip\\\")\\n\\n    # TODO only append when it's not already present\\n    def add(self, package_name):\\n        \\\"\\\"\\\"Add a package to the ``Environment``\\\"\\\"\\\"\\n        self.raw.append(package_name)\\n        self.parse()\\n\\n\\nclass Environment:\\n    \\\"\\\"\\\"A class representing an ``environment.yaml`` file\\\"\\\"\\\"\\n\\n    def __init__(\\n        self,\\n        name=None,\\n        filename=None,\\n        channels=None,\\n        dependencies=None,\\n        prefix=None,\\n        variables=None,\\n    ):\\n        self.name = name\\n        self.filename = filename\\n        self.prefix = prefix\\n        self.dependencies = Dependencies(dependencies)\\n        self.variables = variables\\n\\n        if channels is None:\\n            channels = []\\n        self.channels = channels\\n\\n    def add_channels(self, channels):\\n        \\\"\\\"\\\"Add channels to the ``Environment``\\\"\\\"\\\"\\n        self.channels = list(unique(chain.from_iterable((channels, self.channels))))\\n\\n    def remove_channels(self):\\n        \\\"\\\"\\\"Remove all channels from the ``Environment``\\\"\\\"\\\"\\n        self.channels = []\\n\\n    def to_dict(self, stream=None):\\n        \\\"\\\"\\\"Convert information related to the ``Environment`` into a dictionary\\\"\\\"\\\"\\n        d = {\\\"name\\\": self.name}\\n        if self.channels:\\n            d[\\\"channels\\\"] = self.channels\\n        if self.dependencies:\\n            d[\\\"dependencies\\\"] = self.dependencies.raw\\n        if self.variables:\\n            d[\\\"variables\\\"] = self.variables\\n        if self.prefix:\\n            d[\\\"prefix\\\"] = self.prefix\\n        if stream is None:\\n            return d\\n        stream.write(json.dumps(d))\\n\\n    def to_yaml(self, stream=None):\\n        \\\"\\\"\\\"Convert information related to the ``Environment`` into a ``yaml`` string\\\"\\\"\\\"\\n        d = self.to_dict()\\n        out = yaml_safe_dump(d, stream)\\n        if stream is None:\\n            return out\\n\\n    def save(self):\\n        \\\"\\\"\\\"Save the ``Environment`` data to a ``yaml`` file\\\"\\\"\\\"\\n        with open(self.filename, \\\"wb\\\") as fp:\\n            self.to_yaml(stream=fp)\\n\\n\\ndef get_filename(filename):\\n    \\\"\\\"\\\"Expand filename if local path or return the ``url``\\\"\\\"\\\"\\n    url_scheme = filename.split(\\\"://\\\", 1)[0]\\n    if url_scheme in CONDA_SESSION_SCHEMES:\\n        return filename\\n    else:\\n        return abspath(expanduser(expandvars(filename)))\\n\\n\\ndef print_result(args, prefix, result):\\n    \\\"\\\"\\\"Print the result of an install operation\\\"\\\"\\\"\\n    if context.json:\\n        if result[\\\"conda\\\"] is None and result[\\\"pip\\\"] is None:\\n            common.stdout_json_success(\\n                message=\\\"All requested packages already installed.\\\"\\n            )\\n        else:\\n            if result[\\\"conda\\\"] is not None:\\n                actions = result[\\\"conda\\\"]\\n            else:\\n                actions = {}\\n            if result[\\\"pip\\\"] is not None:\\n                actions[\\\"PIP\\\"] = result[\\\"pip\\\"]\\n            common.stdout_json_success(prefix=prefix, actions=actions)\\n    else:\\n        install.print_activate(args.name or prefix)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"\\nFunctions related to core conda functionality that relates to pip\\n\\nNOTE: This modules used to in conda, as conda/pip.py\\n\\\"\\\"\\\"\\n\\nimport os\\nimport re\\nimport sys\\nfrom logging import getLogger\\n\\nfrom ..base.context import context\\nfrom ..common.compat import on_win\\nfrom ..exceptions import CondaEnvException\\nfrom ..gateways.subprocess import any_subprocess\\n\\nlog = getLogger(__name__)\\n\\n\\ndef pip_subprocess(args, prefix, cwd):\\n    \\\"\\\"\\\"Run pip in a subprocess\\\"\\\"\\\"\\n    if on_win:\\n        python_path = os.path.join(prefix, \\\"python.exe\\\")\\n    else:\\n        python_path = os.path.join(prefix, \\\"bin\\\", \\\"python\\\")\\n    run_args = [python_path, \\\"-m\\\", \\\"pip\\\"] + args\\n    stdout, stderr, rc = any_subprocess(run_args, prefix, cwd=cwd)\\n    if not context.quiet and not context.json:\\n        print(\\\"Ran pip subprocess with arguments:\\\")\\n        print(run_args)\\n        print(\\\"Pip subprocess output:\\\")\\n        print(stdout)\\n    if rc != 0:\\n        print(\\\"Pip subprocess error:\\\", file=sys.stderr)\\n        print(stderr, file=sys.stderr)\\n        raise CondaEnvException(\\\"Pip failed\\\")\\n\\n    return stdout, stderr\\n\\n\\ndef get_pip_installed_packages(stdout):\\n    \\\"\\\"\\\"Return the list of pip packages installed based on the command output\\\"\\\"\\\"\\n    m = re.search(r\\\"Successfully installed\\\\ (.*)\\\", stdout)\\n    if m:\\n        return m.group(1).strip().split()\\n    else:\\n        return None\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Define binstar spec.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport re\\nfrom functools import cached_property\\nfrom typing import TYPE_CHECKING\\n\\nfrom ...env.env import from_yaml\\nfrom ...exceptions import EnvironmentFileNotDownloaded\\nfrom ...models.version import normalized_version\\n\\nif TYPE_CHECKING:\\n    from types import ModuleType\\n\\n    from ...env.env import Environment\\n\\nENVIRONMENT_TYPE = \\\"env\\\"\\n\\n\\nclass BinstarSpec:\\n    \\\"\\\"\\\"\\n    spec = BinstarSpec('darth/deathstar')\\n    spec.can_handle() # => True / False\\n    spec.environment # => YAML string\\n    spec.msg # => Error messages\\n    :raises: EnvironmentFileNotDownloaded\\n    \\\"\\\"\\\"\\n\\n    msg = None\\n\\n    def __init__(self, name=None):\\n        self.name = name\\n\\n    def can_handle(self) -> bool:\\n        \\\"\\\"\\\"\\n        Validates loader can process environment definition.\\n        :return: True or False\\n        \\\"\\\"\\\"\\n        # TODO: log information about trying to find the package in binstar.org\\n        if self.valid_name():\\n            if not self.binstar:\\n                self.msg = (\\n                    \\\"Anaconda Client is required to interact with anaconda.org or an \\\"\\n                    \\\"Anaconda API. Please run `conda install anaconda-client -n base`.\\\"\\n                )\\n                return False\\n\\n            return self.package is not None and self.valid_package()\\n        return False\\n\\n    def valid_name(self) -> bool:\\n        \\\"\\\"\\\"\\n        Validates name\\n        :return: True or False\\n        \\\"\\\"\\\"\\n        if re.match(\\\"^(.+)/(.+)$\\\", str(self.name)) is not None:\\n            return True\\n        elif self.name is None:\\n            self.msg = \\\"Can't process without a name\\\"\\n        else:\\n            self.msg = f\\\"Invalid name {self.name!r}, try the format: user/package\\\"\\n        return False\\n\\n    def valid_package(self) -> bool:\\n        \\\"\\\"\\\"\\n        Returns True if package has an environment file\\n        :return: True or False\\n        \\\"\\\"\\\"\\n        return len(self.file_data) > 0\\n\\n    @cached_property\\n    def binstar(self) -> ModuleType:\\n        try:\\n            from binstar_client.utils import get_server_api\\n\\n            return get_server_api()\\n        except ImportError:\\n            pass\\n\\n    @cached_property\\n    def file_data(self) -> list[dict[str, str]]:\\n        return [\\n            data for data in self.package[\\\"files\\\"] if data[\\\"type\\\"] == ENVIRONMENT_TYPE\\n        ]\\n\\n    @cached_property\\n    def environment(self) -> Environment:\\n        versions = [\\n            {\\\"normalized\\\": normalized_version(d[\\\"version\\\"]), \\\"original\\\": d[\\\"version\\\"]}\\n            for d in self.file_data\\n        ]\\n        latest_version = max(versions, key=lambda x: x[\\\"normalized\\\"])[\\\"original\\\"]\\n        file_data = [\\n            data for data in self.package[\\\"files\\\"] if data[\\\"version\\\"] == latest_version\\n        ]\\n        req = self.binstar.download(\\n            self.username, self.packagename, latest_version, file_data[0][\\\"basename\\\"]\\n        )\\n        if req is None:\\n            raise EnvironmentFileNotDownloaded(self.username, self.packagename)\\n        return from_yaml(req.text)\\n\\n    @cached_property\\n    def package(self):\\n        try:\\n            return self.binstar.package(self.username, self.packagename)\\n        except (IndexError, AttributeError):\\n            self.msg = (\\n                f\\\"{self.name} was not found on anaconda.org.\\\\n\\\"\\n                \\\"You may need to be logged in. Try running:\\\\n\\\"\\n                \\\"    anaconda login\\\"\\n            )\\n\\n    @cached_property\\n    def username(self) -> str:\\n        return self.name.split(\\\"/\\\", 1)[0]\\n\\n    @cached_property\\n    def packagename(self) -> str:\\n        return self.name.split(\\\"/\\\", 1)[1]\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Define YAML spec.\\\"\\\"\\\"\\n\\nfrom ...exceptions import EnvironmentFileEmpty, EnvironmentFileNotFound\\nfrom .. import env\\n\\n\\nclass YamlFileSpec:\\n    _environment = None\\n    extensions = {\\\".yaml\\\", \\\".yml\\\"}\\n\\n    def __init__(self, filename=None, **kwargs):\\n        self.filename = filename\\n        self.msg = None\\n\\n    def can_handle(self):\\n        try:\\n            self._environment = env.from_file(self.filename)\\n            return True\\n        except EnvironmentFileNotFound as e:\\n            self.msg = str(e)\\n            return False\\n        except EnvironmentFileEmpty as e:\\n            self.msg = e.message\\n            return False\\n        except TypeError:\\n            self.msg = f\\\"{self.filename} is not a valid yaml file.\\\"\\n            return False\\n\\n    @property\\n    def environment(self):\\n        if not self._environment:\\n            self.can_handle()\\n        return self._environment\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Define requirements.txt spec.\\\"\\\"\\\"\\n\\nimport os\\n\\nfrom ..env import Environment\\n\\n\\nclass RequirementsSpec:\\n    \\\"\\\"\\\"\\n    Reads dependencies from a requirements.txt file\\n    and returns an Environment object from it.\\n    \\\"\\\"\\\"\\n\\n    msg = None\\n    extensions = {\\\".txt\\\"}\\n\\n    def __init__(self, filename=None, name=None, **kwargs):\\n        self.filename = filename\\n        self.name = name\\n        self.msg = None\\n\\n    def _valid_file(self):\\n        if os.path.exists(self.filename):\\n            return True\\n        else:\\n            self.msg = \\\"There is no requirements.txt\\\"\\n            return False\\n\\n    def _valid_name(self):\\n        if self.name is None:\\n            self.msg = \\\"Environment with requirements.txt file needs a name\\\"\\n            return False\\n        else:\\n            return True\\n\\n    def can_handle(self):\\n        return self._valid_file() and self._valid_name()\\n\\n    @property\\n    def environment(self):\\n        dependencies = []\\n        with open(self.filename) as reqfile:\\n            for line in reqfile:\\n                line = line.strip()\\n                if not line or line.startswith(\\\"#\\\"):\\n                    continue\\n                dependencies.append(line)\\n        return Environment(name=self.name, dependencies=dependencies)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\nfrom __future__ import annotations\\n\\nimport os\\nfrom typing import Type, Union\\n\\nfrom ...exceptions import (\\n    EnvironmentFileExtensionNotValid,\\n    EnvironmentFileNotFound,\\n    SpecNotFound,\\n)\\nfrom ...gateways.connection.session import CONDA_SESSION_SCHEMES\\nfrom .binstar import BinstarSpec\\nfrom .requirements import RequirementsSpec\\nfrom .yaml_file import YamlFileSpec\\n\\nFileSpecTypes = Union[Type[YamlFileSpec], Type[RequirementsSpec]]\\n\\n\\ndef get_spec_class_from_file(filename: str) -> FileSpecTypes:\\n    \\\"\\\"\\\"\\n    Determine spec class to use from the provided ``filename``\\n\\n    :raises EnvironmentFileExtensionNotValid | EnvironmentFileNotFound:\\n    \\\"\\\"\\\"\\n    # Check extensions\\n    all_valid_exts = YamlFileSpec.extensions.union(RequirementsSpec.extensions)\\n    _, ext = os.path.splitext(filename)\\n\\n    # First check if file exists and test the known valid extension for specs\\n    file_exists = (\\n        os.path.isfile(filename) or filename.split(\\\"://\\\", 1)[0] in CONDA_SESSION_SCHEMES\\n    )\\n    if file_exists:\\n        if ext == \\\"\\\" or ext not in all_valid_exts:\\n            raise EnvironmentFileExtensionNotValid(filename)\\n        elif ext in YamlFileSpec.extensions:\\n            return YamlFileSpec\\n        elif ext in RequirementsSpec.extensions:\\n            return RequirementsSpec\\n    else:\\n        raise EnvironmentFileNotFound(filename=filename)\\n\\n\\nSpecTypes = Union[BinstarSpec, YamlFileSpec, RequirementsSpec]\\n\\n\\ndef detect(\\n    name: str = None,\\n    filename: str = None,\\n    directory: str = None,\\n    remote_definition: str = None,\\n) -> SpecTypes:\\n    \\\"\\\"\\\"\\n    Return the appropriate spec type to use.\\n\\n    :raises SpecNotFound: Raised if no suitable spec class could be found given the input\\n    :raises EnvironmentFileExtensionNotValid | EnvironmentFileNotFound:\\n    \\\"\\\"\\\"\\n    if remote_definition is not None:\\n        spec = BinstarSpec(name=remote_definition)\\n        if spec.can_handle():\\n            return spec\\n        else:\\n            raise SpecNotFound(spec.msg)\\n\\n    if filename is not None:\\n        spec_class = get_spec_class_from_file(filename)\\n        spec = spec_class(name=name, filename=filename, directory=directory)\\n        if spec.can_handle():\\n            return spec\\n\\n    raise SpecNotFound(spec.msg)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Pip-flavored installer.\\\"\\\"\\\"\\n\\nimport os\\nimport os.path as op\\nfrom logging import getLogger\\n\\nfrom ...auxlib.compat import Utf8NamedTemporaryFile\\nfrom ...base.context import context\\nfrom ...common.io import Spinner\\nfrom ...env.pip_util import get_pip_installed_packages, pip_subprocess\\nfrom ...gateways.connection.session import CONDA_SESSION_SCHEMES\\n\\nlog = getLogger(__name__)\\n\\n\\ndef _pip_install_via_requirements(prefix, specs, args, *_, **kwargs):\\n    \\\"\\\"\\\"\\n    Installs the pip dependencies in specs using a temporary pip requirements file.\\n\\n    Args\\n    ----\\n    prefix: string\\n      The path to the python and pip executables.\\n\\n    specs: iterable of strings\\n      Each element should be a valid pip dependency.\\n      See: https://pip.pypa.io/en/stable/user_guide/#requirements-files\\n           https://pip.pypa.io/en/stable/reference/pip_install/#requirements-file-format\\n    \\\"\\\"\\\"\\n    url_scheme = args.file.split(\\\"://\\\", 1)[0]\\n    if url_scheme in CONDA_SESSION_SCHEMES:\\n        pip_workdir = None\\n    else:\\n        try:\\n            pip_workdir = op.dirname(op.abspath(args.file))\\n            if not os.access(pip_workdir, os.W_OK):\\n                pip_workdir = None\\n        except AttributeError:\\n            pip_workdir = None\\n    requirements = None\\n    try:\\n        # Generate the temporary requirements file\\n        requirements = Utf8NamedTemporaryFile(\\n            mode=\\\"w\\\",\\n            prefix=\\\"condaenv.\\\",\\n            suffix=\\\".requirements.txt\\\",\\n            dir=pip_workdir,\\n            delete=False,\\n        )\\n        requirements.write(\\\"\\\\n\\\".join(specs))\\n        requirements.close()\\n        # pip command line...\\n        # see https://pip.pypa.io/en/stable/cli/pip/#exists-action-option\\n        pip_cmd = [\\\"install\\\", \\\"-U\\\", \\\"-r\\\", requirements.name, \\\"--exists-action=b\\\"]\\n        stdout, stderr = pip_subprocess(pip_cmd, prefix, cwd=pip_workdir)\\n    finally:\\n        # Win/Appveyor does not like it if we use context manager + delete=True.\\n        # So we delete the temporary file in a finally block.\\n        if requirements is not None and op.isfile(requirements.name):\\n            if \\\"CONDA_TEST_SAVE_TEMPS\\\" not in os.environ:\\n                os.remove(requirements.name)\\n            else:\\n                log.warning(\\n                    f\\\"CONDA_TEST_SAVE_TEMPS :: retaining pip requirements.txt {requirements.name}\\\"\\n                )\\n    return get_pip_installed_packages(stdout)\\n\\n\\ndef install(*args, **kwargs):\\n    with Spinner(\\n        \\\"Installing pip dependencies\\\",\\n        not context.verbose and not context.quiet,\\n        context.json,\\n    ):\\n        return _pip_install_via_requirements(*args, **kwargs)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Conda-flavored installer.\\\"\\\"\\\"\\n\\nimport tempfile\\nfrom os.path import basename\\n\\nfrom boltons.setutils import IndexedSet\\n\\nfrom ...base.constants import UpdateModifier\\nfrom ...base.context import context\\nfrom ...common.constants import NULL\\nfrom ...env.env import Environment\\nfrom ...exceptions import UnsatisfiableError\\nfrom ...models.channel import Channel, prioritize_channels\\n\\n\\ndef _solve(prefix, specs, args, env, *_, **kwargs):\\n    \\\"\\\"\\\"Solve the environment\\\"\\\"\\\"\\n    # TODO: support all various ways this happens\\n    # Including 'nodefaults' in the channels list disables the defaults\\n    channel_urls = [chan for chan in env.channels if chan != \\\"nodefaults\\\"]\\n\\n    if \\\"nodefaults\\\" not in env.channels:\\n        channel_urls.extend(context.channels)\\n    _channel_priority_map = prioritize_channels(channel_urls)\\n\\n    channels = IndexedSet(Channel(url) for url in _channel_priority_map)\\n    subdirs = IndexedSet(basename(url) for url in _channel_priority_map)\\n\\n    solver_backend = context.plugin_manager.get_cached_solver_backend()\\n    solver = solver_backend(prefix, channels, subdirs, specs_to_add=specs)\\n    return solver\\n\\n\\ndef dry_run(specs, args, env, *_, **kwargs):\\n    \\\"\\\"\\\"Do a dry run of the environment solve\\\"\\\"\\\"\\n    solver = _solve(tempfile.mkdtemp(), specs, args, env, *_, **kwargs)\\n    pkgs = solver.solve_final_state()\\n    solved_env = Environment(\\n        name=env.name, dependencies=[str(p) for p in pkgs], channels=env.channels\\n    )\\n    return solved_env\\n\\n\\ndef install(prefix, specs, args, env, *_, **kwargs):\\n    \\\"\\\"\\\"Install packages into an environment\\\"\\\"\\\"\\n    solver = _solve(prefix, specs, args, env, *_, **kwargs)\\n\\n    try:\\n        unlink_link_transaction = solver.solve_for_transaction(\\n            prune=getattr(args, \\\"prune\\\", False),\\n            update_modifier=UpdateModifier.FREEZE_INSTALLED,\\n        )\\n    except (UnsatisfiableError, SystemExit):\\n        unlink_link_transaction = solver.solve_for_transaction(\\n            prune=getattr(args, \\\"prune\\\", False), update_modifier=NULL\\n        )\\n\\n    if unlink_link_transaction.nothing_to_do:\\n        return None\\n    unlink_link_transaction.download_and_extract()\\n    unlink_link_transaction.execute()\\n    return unlink_link_transaction._make_legacy_action_groups()[0]\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Dynamic installer loading.\\\"\\\"\\\"\\n\\nimport importlib\\n\\nfrom ...exceptions import InvalidInstaller\\n\\n\\ndef get_installer(name):\\n    \\\"\\\"\\\"\\n        Gets the installer for the given environment.\\n\\n    Raises: InvalidInstaller if unable to load installer\\n    \\\"\\\"\\\"\\n    try:\\n        return importlib.import_module(f\\\"conda.env.installers.{name}\\\")\\n    except ImportError:\\n        raise InvalidInstaller(name)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"\\nDefinition of specific return types for use when defining a conda plugin hook.\\n\\nEach type corresponds to the plugin hook for which it is used.\\n\\n\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nfrom dataclasses import dataclass, field\\nfrom typing import TYPE_CHECKING, NamedTuple\\n\\nfrom requests.auth import AuthBase\\n\\nif TYPE_CHECKING:\\n    from argparse import ArgumentParser, Namespace\\n    from typing import Callable\\n\\n    from ..common.configuration import Parameter\\n    from ..core.solve import Solver\\n    from ..models.match_spec import MatchSpec\\n    from ..models.records import PackageRecord\\n\\n\\n@dataclass\\nclass CondaSubcommand:\\n    \\\"\\\"\\\"\\n    Return type to use when defining a conda subcommand plugin hook.\\n\\n    For details on how this is used, see\\n    :meth:`~conda.plugins.hookspec.CondaSpecs.conda_subcommands`.\\n\\n    :param name: Subcommand name (e.g., ``conda my-subcommand-name``).\\n    :param summary: Subcommand summary, will be shown in ``conda --help``.\\n    :param action: Callable that will be run when the subcommand is invoked.\\n    :param configure_parser: Callable that will be run when the subcommand parser is initialized.\\n    \\\"\\\"\\\"\\n\\n    name: str\\n    summary: str\\n    action: Callable[\\n        [Namespace | tuple[str]],  # arguments\\n        int | None,  # return code\\n    ]\\n    configure_parser: Callable[[ArgumentParser], None] | None = field(default=None)\\n\\n\\nclass CondaVirtualPackage(NamedTuple):\\n    \\\"\\\"\\\"\\n    Return type to use when defining a conda virtual package plugin hook.\\n\\n    For details on how this is used, see\\n    :meth:`~conda.plugins.hookspec.CondaSpecs.conda_virtual_packages`.\\n\\n    :param name: Virtual package name (e.g., ``my_custom_os``).\\n    :param version: Virtual package version (e.g., ``1.2.3``).\\n    :param build: Virtual package build string (e.g., ``x86_64``).\\n    \\\"\\\"\\\"\\n\\n    name: str\\n    version: str | None\\n    build: str | None\\n\\n\\nclass CondaSolver(NamedTuple):\\n    \\\"\\\"\\\"\\n    Return type to use when defining a conda solver plugin hook.\\n\\n    For details on how this is used, see\\n    :meth:`~conda.plugins.hookspec.CondaSpecs.conda_solvers`.\\n\\n    :param name: Solver name (e.g., ``custom-solver``).\\n    :param backend: Type that will be instantiated as the solver backend.\\n    \\\"\\\"\\\"\\n\\n    name: str\\n    backend: type[Solver]\\n\\n\\nclass CondaPreCommand(NamedTuple):\\n    \\\"\\\"\\\"\\n    Return type to use when defining a conda pre-command plugin hook.\\n\\n    For details on how this is used, see\\n    :meth:`~conda.plugins.hookspec.CondaSpecs.conda_pre_commands`.\\n\\n    :param name: Pre-command name (e.g., ``custom_plugin_pre_commands``).\\n    :param action: Callable which contains the code to be run.\\n    :param run_for: Represents the command(s) this will be run on (e.g. ``install`` or ``create``).\\n    \\\"\\\"\\\"\\n\\n    name: str\\n    action: Callable[[str], None]\\n    run_for: set[str]\\n\\n\\nclass CondaPostCommand(NamedTuple):\\n    \\\"\\\"\\\"\\n    Return type to use when defining a conda post-command plugin hook.\\n\\n    For details on how this is used, see\\n    :meth:`~conda.plugins.hookspec.CondaSpecs.conda_post_commands`.\\n\\n    :param name: Post-command name (e.g., ``custom_plugin_post_commands``).\\n    :param action: Callable which contains the code to be run.\\n    :param run_for: Represents the command(s) this will be run on (e.g. ``install`` or ``create``).\\n    \\\"\\\"\\\"\\n\\n    name: str\\n    action: Callable[[str], None]\\n    run_for: set[str]\\n\\n\\nclass ChannelNameMixin:\\n    \\\"\\\"\\\"\\n    Class mixin to make all plugin implementations compatible, e.g. when they\\n    use an existing (e.g. 3rd party) requests authentication handler.\\n\\n    Please use the concrete :class:`~conda.plugins.types.ChannelAuthBase`\\n    in case you're creating an own implementation.\\n    \\\"\\\"\\\"\\n\\n    def __init__(self, channel_name: str, *args, **kwargs):\\n        self.channel_name = channel_name\\n        super().__init__(*args, **kwargs)\\n\\n\\nclass ChannelAuthBase(ChannelNameMixin, AuthBase):\\n    \\\"\\\"\\\"\\n    Base class that we require all plugin implementations to use to be compatible.\\n\\n    Authentication is tightly coupled with individual channels. Therefore, an additional\\n    ``channel_name`` property must be set on the ``requests.auth.AuthBase`` based class.\\n    \\\"\\\"\\\"\\n\\n\\nclass CondaAuthHandler(NamedTuple):\\n    \\\"\\\"\\\"\\n    Return type to use when the defining the conda auth handlers hook.\\n\\n    :param name: Name (e.g., ``basic-auth``). This name should be unique\\n                 and only one may be registered at a time.\\n    :param handler: Type that will be used as the authentication handler\\n                    during network requests.\\n    \\\"\\\"\\\"\\n\\n    name: str\\n    handler: type[ChannelAuthBase]\\n\\n\\nclass CondaHealthCheck(NamedTuple):\\n    \\\"\\\"\\\"\\n    Return type to use when defining conda health checks plugin hook.\\n    \\\"\\\"\\\"\\n\\n    name: str\\n    action: Callable[[str, bool], None]\\n\\n\\n@dataclass\\nclass CondaPreSolve:\\n    \\\"\\\"\\\"\\n    Return type to use when defining a conda pre-solve plugin hook.\\n\\n    For details on how this is used, see\\n    :meth:`~conda.plugins.hookspec.CondaSpecs.conda_pre_solves`.\\n\\n    :param name: Pre-solve name (e.g., ``custom_plugin_pre_solve``).\\n    :param action: Callable which contains the code to be run.\\n    \\\"\\\"\\\"\\n\\n    name: str\\n    action: Callable[[frozenset[MatchSpec], frozenset[MatchSpec]], None]\\n\\n\\n@dataclass\\nclass CondaPostSolve:\\n    \\\"\\\"\\\"\\n    Return type to use when defining a conda post-solve plugin hook.\\n\\n    For details on how this is used, see\\n    :meth:`~conda.plugins.hookspec.CondaSpecs.conda_post_solves`.\\n\\n    :param name: Post-solve name (e.g., ``custom_plugin_post_solve``).\\n    :param action: Callable which contains the code to be run.\\n    \\\"\\\"\\\"\\n\\n    name: str\\n    action: Callable[[str, tuple[PackageRecord, ...], tuple[PackageRecord, ...]], None]\\n\\n\\n@dataclass\\nclass CondaSetting:\\n    \\\"\\\"\\\"\\n    Return type to use when defining a conda setting plugin hook.\\n\\n    For details on how this is used, see\\n    :meth:`~conda.plugins.hookspec.CondaSpecs.conda_settings`.\\n\\n    :param name: name of the setting (e.g., ``config_param``)\\n    :param description: description of the setting that should be targeted\\n                        towards users of the plugin\\n    :param parameter: Parameter instance containing the setting definition\\n    :param aliases: alternative names of the setting\\n    \\\"\\\"\\\"\\n\\n    name: str\\n    description: str\\n    parameter: Parameter\\n    aliases: tuple[str, ...] = tuple()\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Register the classic conda solver.\\\"\\\"\\\"\\n\\nfrom ..base.constants import CLASSIC_SOLVER\\nfrom . import CondaSolver, hookimpl\\n\\n\\n@hookimpl(tryfirst=True)  # make sure the classic solver can't be overwritten\\ndef conda_solvers():\\n    \\\"\\\"\\\"The classic solver as shipped by default in conda.\\\"\\\"\\\"\\n    from ..core.solve import Solver\\n\\n    yield CondaSolver(\\n        name=CLASSIC_SOLVER,\\n        backend=Solver,\\n    )\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"\\nPluggy hook specifications (\\\"hookspecs\\\") to register conda plugins.\\n\\nEach hookspec defined in :class:`~conda.plugins.hookspec.CondaSpecs` contains\\nan example of how to use it.\\n\\n\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nfrom typing import TYPE_CHECKING\\n\\nimport pluggy\\n\\nif TYPE_CHECKING:\\n    from collections.abc import Iterable\\n\\n    from .types import (\\n        CondaAuthHandler,\\n        CondaHealthCheck,\\n        CondaPostCommand,\\n        CondaPostSolve,\\n        CondaPreCommand,\\n        CondaPreSolve,\\n        CondaSetting,\\n        CondaSolver,\\n        CondaSubcommand,\\n        CondaVirtualPackage,\\n    )\\n\\nspec_name = \\\"conda\\\"\\n\\\"\\\"\\\"Name used for organizing conda hook specifications\\\"\\\"\\\"\\n\\n_hookspec = pluggy.HookspecMarker(spec_name)\\n\\\"\\\"\\\"\\nThe conda plugin hook specifications, to be used by developers\\n\\\"\\\"\\\"\\n\\nhookimpl = pluggy.HookimplMarker(spec_name)\\n\\\"\\\"\\\"\\nDecorator used to mark plugin hook implementations\\n\\\"\\\"\\\"\\n\\n\\nclass CondaSpecs:\\n    \\\"\\\"\\\"The conda plugin hookspecs, to be used by developers.\\\"\\\"\\\"\\n\\n    @_hookspec\\n    def conda_solvers(self) -> Iterable[CondaSolver]:\\n        \\\"\\\"\\\"\\n        Register solvers in conda.\\n\\n        **Example:**\\n\\n        .. code-block:: python\\n\\n            import logging\\n\\n            from conda import plugins\\n            from conda.core import solve\\n\\n            log = logging.getLogger(__name__)\\n\\n\\n            class VerboseSolver(solve.Solver):\\n                def solve_final_state(self, *args, **kwargs):\\n                    log.info(\\\"My verbose solver!\\\")\\n                    return super().solve_final_state(*args, **kwargs)\\n\\n\\n            @plugins.hookimpl\\n            def conda_solvers():\\n                yield plugins.CondaSolver(\\n                    name=\\\"verbose-classic\\\",\\n                    backend=VerboseSolver,\\n                )\\n\\n        :return: An iterable of solver entries.\\n        \\\"\\\"\\\"\\n\\n    @_hookspec\\n    def conda_subcommands(self) -> Iterable[CondaSubcommand]:\\n        \\\"\\\"\\\"\\n        Register external subcommands in conda.\\n\\n        **Example:**\\n\\n        .. code-block:: python\\n\\n            from conda import plugins\\n\\n\\n            def example_command(args):\\n                print(\\\"This is an example command!\\\")\\n\\n\\n            @plugins.hookimpl\\n            def conda_subcommands():\\n                yield plugins.CondaSubcommand(\\n                    name=\\\"example\\\",\\n                    summary=\\\"example command\\\",\\n                    action=example_command,\\n                )\\n\\n        :return: An iterable of subcommand entries.\\n        \\\"\\\"\\\"\\n\\n    @_hookspec\\n    def conda_virtual_packages(self) -> Iterable[CondaVirtualPackage]:\\n        \\\"\\\"\\\"\\n        Register virtual packages in Conda.\\n\\n        **Example:**\\n\\n        .. code-block:: python\\n\\n            from conda import plugins\\n\\n\\n            @plugins.hookimpl\\n            def conda_virtual_packages():\\n                yield plugins.CondaVirtualPackage(\\n                    name=\\\"my_custom_os\\\",\\n                    version=\\\"1.2.3\\\",\\n                    build=\\\"x86_64\\\",\\n                )\\n\\n        :return: An iterable of virtual package entries.\\n        \\\"\\\"\\\"\\n\\n    @_hookspec\\n    def conda_pre_commands(self) -> Iterable[CondaPreCommand]:\\n        \\\"\\\"\\\"\\n        Register pre-command functions in conda.\\n\\n        **Example:**\\n\\n        .. code-block:: python\\n\\n           from conda import plugins\\n\\n\\n           def example_pre_command(command):\\n               print(\\\"pre-command action\\\")\\n\\n\\n           @plugins.hookimpl\\n           def conda_pre_commands():\\n               yield plugins.CondaPreCommand(\\n                   name=\\\"example-pre-command\\\",\\n                   action=example_pre_command,\\n                   run_for={\\\"install\\\", \\\"create\\\"},\\n               )\\n        \\\"\\\"\\\"\\n\\n    @_hookspec\\n    def conda_post_commands(self) -> Iterable[CondaPostCommand]:\\n        \\\"\\\"\\\"\\n        Register post-command functions in conda.\\n\\n        **Example:**\\n\\n        .. code-block:: python\\n\\n           from conda import plugins\\n\\n\\n           def example_post_command(command):\\n               print(\\\"post-command action\\\")\\n\\n\\n           @plugins.hookimpl\\n           def conda_post_commands():\\n               yield plugins.CondaPostCommand(\\n                   name=\\\"example-post-command\\\",\\n                   action=example_post_command,\\n                   run_for={\\\"install\\\", \\\"create\\\"},\\n               )\\n        \\\"\\\"\\\"\\n\\n    @_hookspec\\n    def conda_auth_handlers(self) -> Iterable[CondaAuthHandler]:\\n        \\\"\\\"\\\"\\n        Register a conda auth handler derived from the requests API.\\n\\n        This plugin hook allows attaching requests auth handler subclasses,\\n        e.g. when authenticating requests against individual channels hosted\\n        at HTTP/HTTPS services.\\n\\n        **Example:**\\n\\n        .. code-block:: python\\n\\n            import os\\n            from conda import plugins\\n            from requests.auth import AuthBase\\n\\n\\n            class EnvironmentHeaderAuth(AuthBase):\\n                def __init__(self, *args, **kwargs):\\n                    self.username = os.environ[\\\"EXAMPLE_CONDA_AUTH_USERNAME\\\"]\\n                    self.password = os.environ[\\\"EXAMPLE_CONDA_AUTH_PASSWORD\\\"]\\n\\n                def __call__(self, request):\\n                    request.headers[\\\"X-Username\\\"] = self.username\\n                    request.headers[\\\"X-Password\\\"] = self.password\\n                    return request\\n\\n\\n            @plugins.hookimpl\\n            def conda_auth_handlers():\\n                yield plugins.CondaAuthHandler(\\n                    name=\\\"environment-header-auth\\\",\\n                    auth_handler=EnvironmentHeaderAuth,\\n                )\\n        \\\"\\\"\\\"\\n\\n    @_hookspec\\n    def conda_health_checks(self) -> Iterable[CondaHealthCheck]:\\n        \\\"\\\"\\\"\\n        Register health checks for conda doctor.\\n\\n        This plugin hook allows you to add more \\\"health checks\\\" to conda doctor\\n        that you can write to diagnose problems in your conda environment.\\n        Check out the health checks already shipped with conda for inspiration.\\n\\n        **Example:**\\n\\n        .. code-block:: python\\n\\n            from conda import plugins\\n\\n\\n            def example_health_check(prefix: str, verbose: bool):\\n                print(\\\"This is an example health check!\\\")\\n\\n\\n            @plugins.hookimpl\\n            def conda_health_checks():\\n                yield plugins.CondaHealthCheck(\\n                    name=\\\"example-health-check\\\",\\n                    action=example_health_check,\\n                )\\n        \\\"\\\"\\\"\\n\\n    @_hookspec\\n    def conda_pre_solves(self) -> Iterable[CondaPreSolve]:\\n        \\\"\\\"\\\"\\n        Register pre-solve functions in conda that are used in the\\n        general solver API, before the solver processes the package specs in\\n        search of a solution.\\n\\n        **Example:**\\n\\n        .. code-block:: python\\n\\n           from conda import plugins\\n           from conda.models.match_spec import MatchSpec\\n\\n\\n           def example_pre_solve(\\n               specs_to_add: frozenset[MatchSpec],\\n               specs_to_remove: frozenset[MatchSpec],\\n           ):\\n               print(f\\\"Adding {len(specs_to_add)} packages\\\")\\n               print(f\\\"Removing {len(specs_to_remove)} packages\\\")\\n\\n\\n           @plugins.hookimpl\\n           def conda_pre_solves():\\n               yield plugins.CondaPreSolve(\\n                   name=\\\"example-pre-solve\\\",\\n                   action=example_pre_solve,\\n               )\\n        \\\"\\\"\\\"\\n\\n    @_hookspec\\n    def conda_post_solves(self) -> Iterable[CondaPostSolve]:\\n        \\\"\\\"\\\"\\n        Register post-solve functions in conda that are used in the\\n        general solver API, after the solver has provided the package\\n        records to add or remove from the conda environment.\\n\\n        **Example:**\\n\\n        .. code-block:: python\\n\\n           from conda import plugins\\n           from conda.models.records import PackageRecord\\n\\n\\n           def example_post_solve(\\n               repodata_fn: str,\\n               unlink_precs: tuple[PackageRecord, ...],\\n               link_precs: tuple[PackageRecord, ...],\\n           ):\\n               print(f\\\"Uninstalling {len(unlink_precs)} packages\\\")\\n               print(f\\\"Installing {len(link_precs)} packages\\\")\\n\\n\\n           @plugins.hookimpl\\n           def conda_post_solves():\\n               yield plugins.CondaPostSolve(\\n                   name=\\\"example-post-solve\\\",\\n                   action=example_post_solve,\\n               )\\n        \\\"\\\"\\\"\\n\\n    @_hookspec\\n    def conda_settings(self) -> Iterable[CondaSetting]:\\n        \\\"\\\"\\\"\\n        Register new setting\\n\\n        The example below defines a simple string type parameter\\n\\n        **Example:**\\n\\n        .. code-block:: python\\n\\n           from conda import plugins\\n           from conda.common.configuration import PrimitiveParameter, SequenceParameter\\n\\n\\n           @plugins.hookimpl\\n           def conda_settings():\\n               yield plugins.CondaSetting(\\n                   name=\\\"example_option\\\",\\n                   description=\\\"This is an example option\\\",\\n                   parameter=PrimitiveParameter(\\\"default_value\\\", element_type=str),\\n                   aliases=(\\\"example_option_alias\\\",),\\n               )\\n        \\\"\\\"\\\"\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"\\nIn this module, you will find everything relevant to conda's plugin system.\\nIt contains all of the code that plugin authors will use to write plugins,\\nas well as conda's internal implementations of plugins.\\n\\n**Modules relevant for plugin authors**\\n\\n- :mod:`conda.plugins.hookspec`: all available hook specifications are listed here, including\\n  examples of how to use them\\n- :mod:`conda.plugins.types`: important types to use when defining plugin hooks\\n\\n**Modules relevant for internal development**\\n\\n- :mod:`conda.plugins.manager`: includes our custom subclass of pluggy's\\n  `PluginManager <https://pluggy.readthedocs.io/en/stable/api_reference.html#pluggy.PluginManager>`_ class\\n\\n**Modules with internal plugin implementations**\\n\\n- :mod:`conda.plugins.solvers`: implementation of the \\\"classic\\\" solver\\n- :mod:`conda.plugins.subcommands.doctor`: ``conda doctor`` subcommand\\n- :mod:`conda.plugins.virtual_packages`: registers virtual packages in conda\\n\\n\\\"\\\"\\\"  # noqa: E501\\n\\nfrom .hookspec import hookimpl  # noqa: F401\\nfrom .types import (  # noqa: F401\\n    CondaAuthHandler,\\n    CondaHealthCheck,\\n    CondaPostCommand,\\n    CondaPostSolve,\\n    CondaPreCommand,\\n    CondaPreSolve,\\n    CondaSetting,\\n    CondaSolver,\\n    CondaSubcommand,\\n    CondaVirtualPackage,\\n)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"\\nThis module contains a subclass implementation of pluggy's\\n`PluginManager <https://pluggy.readthedocs.io/en/stable/api_reference.html#pluggy.PluginManager>`_.\\n\\nAdditionally, it contains a function we use to construct the ``PluginManager`` object and\\nregister all plugins during conda's startup process.\\n\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport functools\\nimport logging\\nfrom importlib.metadata import distributions\\nfrom inspect import getmodule, isclass\\nfrom typing import TYPE_CHECKING, overload\\n\\nimport pluggy\\n\\nfrom ..auxlib.ish import dals\\nfrom ..base.context import add_plugin_setting, context\\nfrom ..exceptions import CondaValueError, PluginError\\nfrom . import post_solves, solvers, subcommands, virtual_packages\\nfrom .hookspec import CondaSpecs, spec_name\\nfrom .subcommands.doctor import health_checks\\n\\nif TYPE_CHECKING:\\n    from typing import Literal\\n\\n    from requests.auth import AuthBase\\n\\n    from ..common.configuration import ParameterLoader\\n    from ..core.solve import Solver\\n    from ..models.match_spec import MatchSpec\\n    from ..models.records import PackageRecord\\n    from .types import (\\n        CondaAuthHandler,\\n        CondaHealthCheck,\\n        CondaPostCommand,\\n        CondaPostSolve,\\n        CondaPreCommand,\\n        CondaPreSolve,\\n        CondaSetting,\\n        CondaSolver,\\n        CondaSubcommand,\\n        CondaVirtualPackage,\\n    )\\n\\nlog = logging.getLogger(__name__)\\n\\n\\nclass CondaPluginManager(pluggy.PluginManager):\\n    \\\"\\\"\\\"\\n    The conda plugin manager to implement behavior additional to pluggy's default plugin manager.\\n    \\\"\\\"\\\"\\n\\n    #: Cached version of the :meth:`~conda.plugins.manager.CondaPluginManager.get_solver_backend`\\n    #: method.\\n    get_cached_solver_backend = None\\n\\n    def __init__(self, project_name: str | None = None, *args, **kwargs) -> None:\\n        # Setting the default project name to the spec name for ease of use\\n        if project_name is None:\\n            project_name = spec_name\\n        super().__init__(project_name, *args, **kwargs)\\n        # Make the cache containers local to the instances so that the\\n        # reference from cache to the instance gets garbage collected with the instance\\n        self.get_cached_solver_backend = functools.lru_cache(maxsize=None)(\\n            self.get_solver_backend\\n        )\\n\\n    def get_canonical_name(self, plugin: object) -> str:\\n        # detect the fully qualified module name\\n        prefix = \\\"<unknown_module>\\\"\\n        if (module := getmodule(plugin)) and module.__spec__:\\n            prefix = module.__spec__.name\\n\\n        # return the fully qualified name for modules\\n        if module is plugin:\\n            return prefix\\n\\n        # return the fully qualified name for classes\\n        elif isclass(plugin):\\n            return f\\\"{prefix}.{plugin.__qualname__}\\\"\\n\\n        # return the fully qualified name for instances\\n        else:\\n            return f\\\"{prefix}.{plugin.__class__.__qualname__}[{id(plugin)}]\\\"\\n\\n    def register(self, plugin, name: str | None = None) -> str | None:\\n        \\\"\\\"\\\"\\n        Call :meth:`pluggy.PluginManager.register` and return the result or\\n        ignore errors raised, except ``ValueError``, which means the plugin\\n        had already been registered.\\n        \\\"\\\"\\\"\\n        try:\\n            # register plugin but ignore ValueError since that means\\n            # the plugin has already been registered\\n            return super().register(plugin, name=name)\\n        except ValueError:\\n            return None\\n        except Exception as err:\\n            raise PluginError(\\n                f\\\"Error while loading conda plugin: \\\"\\n                f\\\"{name or self.get_canonical_name(plugin)} ({err})\\\"\\n            ) from err\\n\\n    def load_plugins(self, *plugins) -> int:\\n        \\\"\\\"\\\"\\n        Load the provided list of plugins and fail gracefully on error.\\n        The provided list of plugins can either be classes or modules with\\n        :attr:`~conda.plugins.hookimpl`.\\n        \\\"\\\"\\\"\\n        count = 0\\n        for plugin in plugins:\\n            if self.register(plugin):\\n                count += 1\\n        return count\\n\\n    def load_entrypoints(self, group: str, name: str | None = None) -> int:\\n        \\\"\\\"\\\"Load modules from querying the specified setuptools ``group``.\\n\\n        :param str group: Entry point group to load plugins.\\n        :param str name: If given, loads only plugins with the given ``name``.\\n        :rtype: int\\n        :return: The number of plugins loaded by this call.\\n        \\\"\\\"\\\"\\n        count = 0\\n        for dist in distributions():\\n            for entry_point in dist.entry_points:\\n                # skip entry points that don't match the group/name\\n                if entry_point.group != group or (\\n                    name is not None and entry_point.name != name\\n                ):\\n                    continue\\n\\n                # attempt to load plugin from entry point\\n                try:\\n                    plugin = entry_point.load()\\n                except Exception as err:\\n                    # not using exc_info=True here since the CLI loggers are\\n                    # set up after CLI initialization and argument parsing,\\n                    # meaning that it comes too late to properly render\\n                    # a traceback; instead we pass exc_info conditionally on\\n                    # context.verbosity\\n                    log.warning(\\n                        f\\\"Error while loading conda entry point: {entry_point.name} ({err})\\\",\\n                        exc_info=err if context.info else None,\\n                    )\\n                    continue\\n\\n                if self.register(plugin):\\n                    count += 1\\n        return count\\n\\n    @overload\\n    def get_hook_results(\\n        self, name: Literal[\\\"subcommands\\\"]\\n    ) -> list[CondaSubcommand]: ...\\n\\n    @overload\\n    def get_hook_results(\\n        self, name: Literal[\\\"virtual_packages\\\"]\\n    ) -> list[CondaVirtualPackage]: ...\\n\\n    @overload\\n    def get_hook_results(self, name: Literal[\\\"solvers\\\"]) -> list[CondaSolver]: ...\\n\\n    @overload\\n    def get_hook_results(\\n        self, name: Literal[\\\"pre_commands\\\"]\\n    ) -> list[CondaPreCommand]: ...\\n\\n    @overload\\n    def get_hook_results(\\n        self, name: Literal[\\\"post_commands\\\"]\\n    ) -> list[CondaPostCommand]: ...\\n\\n    @overload\\n    def get_hook_results(\\n        self, name: Literal[\\\"auth_handlers\\\"]\\n    ) -> list[CondaAuthHandler]: ...\\n\\n    @overload\\n    def get_hook_results(\\n        self, name: Literal[\\\"health_checks\\\"]\\n    ) -> list[CondaHealthCheck]: ...\\n\\n    @overload\\n    def get_hook_results(self, name: Literal[\\\"pre_solves\\\"]) -> list[CondaPreSolve]: ...\\n\\n    @overload\\n    def get_hook_results(\\n        self, name: Literal[\\\"post_solves\\\"]\\n    ) -> list[CondaPostSolve]: ...\\n\\n    @overload\\n    def get_hook_results(self, name: Literal[\\\"settings\\\"]) -> list[CondaSetting]: ...\\n\\n    def get_hook_results(self, name):\\n        \\\"\\\"\\\"\\n        Return results of the plugin hooks with the given name and\\n        raise an error if there is a conflict.\\n        \\\"\\\"\\\"\\n        specname = f\\\"{self.project_name}_{name}\\\"  # e.g. conda_solvers\\n        hook = getattr(self.hook, specname, None)\\n        if hook is None:\\n            raise PluginError(f\\\"Could not find requested `{name}` plugins\\\")\\n\\n        plugins = [item for items in hook() for item in items]\\n\\n        # Check for invalid names\\n        invalid = [plugin for plugin in plugins if not isinstance(plugin.name, str)]\\n        if invalid:\\n            raise PluginError(\\n                dals(\\n                    f\\\"\\\"\\\"\\n                    Invalid plugin names found:\\n\\n                    {', '.join([str(plugin) for plugin in invalid])}\\n\\n                    Please report this issue to the plugin author(s).\\n                    \\\"\\\"\\\"\\n                )\\n            )\\n        plugins = sorted(plugins, key=lambda plugin: plugin.name)\\n\\n        # Check for conflicts\\n        seen = set()\\n        conflicts = [\\n            plugin for plugin in plugins if plugin.name in seen or seen.add(plugin.name)\\n        ]\\n        if conflicts:\\n            raise PluginError(\\n                dals(\\n                    f\\\"\\\"\\\"\\n                    Conflicting `{name}` plugins found:\\n\\n                    {', '.join([str(conflict) for conflict in conflicts])}\\n\\n                    Multiple conda plugins are registered via the `{specname}` hook.\\n                    Please make sure that you don't have any incompatible plugins installed.\\n                    \\\"\\\"\\\"\\n                )\\n            )\\n        return plugins\\n\\n    def get_solvers(self) -> dict[str, CondaSolver]:\\n        \\\"\\\"\\\"Return a mapping from solver name to solver class.\\\"\\\"\\\"\\n        return {\\n            solver_plugin.name.lower(): solver_plugin\\n            for solver_plugin in self.get_hook_results(\\\"solvers\\\")\\n        }\\n\\n    def get_solver_backend(self, name: str | None = None) -> type[Solver]:\\n        \\\"\\\"\\\"\\n        Get the solver backend with the given name (or fall back to the\\n        name provided in the context).\\n\\n        See ``context.solver`` for more details.\\n\\n        Please use the cached version of this method called\\n        :meth:`get_cached_solver_backend` for high-throughput code paths\\n        which is set up as a instance-specific LRU cache.\\n        \\\"\\\"\\\"\\n        # Some light data validation in case name isn't given.\\n        if name is None:\\n            name = context.solver\\n        name = name.lower()\\n\\n        solvers_mapping = self.get_solvers()\\n\\n        # Look up the solver mapping and fail loudly if it can't\\n        # find the requested solver.\\n        solver_plugin = solvers_mapping.get(name, None)\\n        if solver_plugin is None:\\n            raise CondaValueError(\\n                f\\\"You have chosen a non-default solver backend ({name}) \\\"\\n                f\\\"but it was not recognized. Choose one of: \\\"\\n                f\\\"{', '.join(solvers_mapping)}\\\"\\n            )\\n\\n        return solver_plugin.backend\\n\\n    def get_auth_handler(self, name: str) -> type[AuthBase] | None:\\n        \\\"\\\"\\\"\\n        Get the auth handler with the given name or None\\n        \\\"\\\"\\\"\\n        auth_handlers = self.get_hook_results(\\\"auth_handlers\\\")\\n        matches = tuple(\\n            item for item in auth_handlers if item.name.lower() == name.lower().strip()\\n        )\\n\\n        if len(matches) > 0:\\n            return matches[0].handler\\n        return None\\n\\n    def get_settings(self) -> dict[str, ParameterLoader]:\\n        \\\"\\\"\\\"\\n        Return a mapping of plugin setting name to ParameterLoader class\\n\\n        This method intentionally overwrites any duplicates that may be present\\n        \\\"\\\"\\\"\\n        return {\\n            config_param.name.lower(): (config_param.parameter, config_param.aliases)\\n            for config_param in self.get_hook_results(\\\"settings\\\")\\n        }\\n\\n    def invoke_pre_commands(self, command: str) -> None:\\n        \\\"\\\"\\\"\\n        Invokes ``CondaPreCommand.action`` functions registered with ``conda_pre_commands``.\\n\\n        :param command: name of the command that is currently being invoked\\n        \\\"\\\"\\\"\\n        for hook in self.get_hook_results(\\\"pre_commands\\\"):\\n            if command in hook.run_for:\\n                hook.action(command)\\n\\n    def invoke_post_commands(self, command: str) -> None:\\n        \\\"\\\"\\\"\\n        Invokes ``CondaPostCommand.action`` functions registered with ``conda_post_commands``.\\n\\n        :param command: name of the command that is currently being invoked\\n        \\\"\\\"\\\"\\n        for hook in self.get_hook_results(\\\"post_commands\\\"):\\n            if command in hook.run_for:\\n                hook.action(command)\\n\\n    def disable_external_plugins(self) -> None:\\n        \\\"\\\"\\\"\\n        Disables all currently registered plugins except built-in conda plugins\\n        \\\"\\\"\\\"\\n        for name, plugin in self.list_name_plugin():\\n            if not name.startswith(\\\"conda.plugins.\\\") and not self.is_blocked(name):\\n                self.set_blocked(name)\\n\\n    def get_subcommands(self) -> dict[str, CondaSubcommand]:\\n        return {\\n            subcommand.name.lower(): subcommand\\n            for subcommand in self.get_hook_results(\\\"subcommands\\\")\\n        }\\n\\n    def get_virtual_packages(self) -> tuple[CondaVirtualPackage, ...]:\\n        return tuple(self.get_hook_results(\\\"virtual_packages\\\"))\\n\\n    def invoke_health_checks(self, prefix: str, verbose: bool) -> None:\\n        for hook in self.get_hook_results(\\\"health_checks\\\"):\\n            try:\\n                hook.action(prefix, verbose)\\n            except Exception as err:\\n                log.warning(f\\\"Error running health check: {hook.name} ({err})\\\")\\n                continue\\n\\n    def invoke_pre_solves(\\n        self,\\n        specs_to_add: frozenset[MatchSpec],\\n        specs_to_remove: frozenset[MatchSpec],\\n    ) -> None:\\n        \\\"\\\"\\\"\\n        Invokes ``CondaPreSolve.action`` functions registered with ``conda_pre_solves``.\\n\\n        :param specs_to_add:\\n        :param specs_to_remove:\\n        \\\"\\\"\\\"\\n        for hook in self.get_hook_results(\\\"pre_solves\\\"):\\n            hook.action(specs_to_add, specs_to_remove)\\n\\n    def invoke_post_solves(\\n        self,\\n        repodata_fn: str,\\n        unlink_precs: tuple[PackageRecord, ...],\\n        link_precs: tuple[PackageRecord, ...],\\n    ) -> None:\\n        \\\"\\\"\\\"\\n        Invokes ``CondaPostSolve.action`` functions registered with ``conda_post_solves``.\\n\\n        :param repodata_fn:\\n        :param unlink_precs:\\n        :param link_precs:\\n        \\\"\\\"\\\"\\n        for hook in self.get_hook_results(\\\"post_solves\\\"):\\n            hook.action(repodata_fn, unlink_precs, link_precs)\\n\\n    def load_settings(self) -> None:\\n        \\\"\\\"\\\"\\n        Iterates through all registered settings and adds them to the\\n        :class:`conda.common.configuration.PluginConfig` class.\\n        \\\"\\\"\\\"\\n        for name, (parameter, aliases) in self.get_settings().items():\\n            add_plugin_setting(name, parameter, aliases)\\n\\n\\n@functools.lru_cache(maxsize=None)  # FUTURE: Python 3.9+, replace w/ functools.cache\\ndef get_plugin_manager() -> CondaPluginManager:\\n    \\\"\\\"\\\"\\n    Get a cached version of the :class:`~conda.plugins.manager.CondaPluginManager` instance,\\n    with the built-in and entrypoints provided by the plugins loaded.\\n    \\\"\\\"\\\"\\n    plugin_manager = CondaPluginManager()\\n    plugin_manager.add_hookspecs(CondaSpecs)\\n    plugin_manager.load_plugins(\\n        solvers,\\n        *virtual_packages.plugins,\\n        *subcommands.plugins,\\n        health_checks,\\n        *post_solves.plugins,\\n    )\\n    plugin_manager.load_entrypoints(spec_name)\\n    return plugin_manager\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\nfrom . import doctor\\n\\nplugins = [doctor]\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Implementation for `conda doctor` subcommand.\\nAdds various environment and package checks to detect issues or possible environment\\ncorruption.\\n\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nfrom typing import TYPE_CHECKING\\n\\nfrom ....base.context import context\\nfrom ....cli.helpers import (\\n    add_parser_help,\\n    add_parser_prefix,\\n    add_parser_verbose,\\n)\\nfrom ....deprecations import deprecated\\nfrom ... import CondaSubcommand, hookimpl\\n\\nif TYPE_CHECKING:\\n    from argparse import ArgumentParser, Namespace\\n\\n\\n@deprecated(\\n    \\\"24.3\\\", \\\"24.9\\\", addendum=\\\"Use `conda.base.context.context.target_prefix` instead.\\\"\\n)\\ndef get_prefix(args: Namespace) -> str:\\n    context.__init__(argparse_args=args)\\n    return context.target_prefix\\n\\n\\ndef configure_parser(parser: ArgumentParser):\\n    add_parser_verbose(parser)\\n    add_parser_help(parser)\\n    add_parser_prefix(parser)\\n\\n\\ndef execute(args: Namespace) -> None:\\n    \\\"\\\"\\\"Run registered health_check plugins.\\\"\\\"\\\"\\n    print(f\\\"Environment Health Report for: {context.target_prefix}\\\\n\\\")\\n    context.plugin_manager.invoke_health_checks(context.target_prefix, context.verbose)\\n\\n\\n@hookimpl\\ndef conda_subcommands():\\n    yield CondaSubcommand(\\n        name=\\\"doctor\\\",\\n        summary=\\\"Display a health report for your environment.\\\",\\n        action=execute,\\n        configure_parser=configure_parser,\\n    )\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Backend logic implementation for `conda doctor`.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport json\\nfrom logging import getLogger\\nfrom pathlib import Path\\nfrom typing import TYPE_CHECKING\\n\\nfrom ....base.context import context\\nfrom ....core.envs_manager import get_user_environments_txt_file\\nfrom ....deprecations import deprecated\\nfrom ....exceptions import CondaError\\nfrom ....gateways.disk.read import compute_sum\\nfrom ... import CondaHealthCheck, hookimpl\\n\\nif TYPE_CHECKING:\\n    import os\\n\\nlogger = getLogger(__name__)\\n\\nOK_MARK = \\\"✅\\\"\\nX_MARK = \\\"❌\\\"\\n\\n\\n@deprecated(\\\"24.3\\\", \\\"24.9\\\")\\ndef display_report_heading(prefix: str) -> None:\\n    \\\"\\\"\\\"Displays our report heading.\\\"\\\"\\\"\\n    print(f\\\"Environment Health Report for: {Path(prefix)}\\\\n\\\")\\n\\n\\ndef check_envs_txt_file(prefix: str | os.PathLike | Path) -> bool:\\n    \\\"\\\"\\\"Checks whether the environment is listed in the environments.txt file\\\"\\\"\\\"\\n    prefix = Path(prefix)\\n    envs_txt_file = Path(get_user_environments_txt_file())\\n\\n    def samefile(path1: Path, path2: Path) -> bool:\\n        try:\\n            return path1.samefile(path2)\\n        except FileNotFoundError:\\n            # FileNotFoundError: path doesn't exist\\n            return path1 == path2\\n\\n    try:\\n        for line in envs_txt_file.read_text().splitlines():\\n            stripped_line = line.strip()\\n            if stripped_line and samefile(prefix, Path(stripped_line)):\\n                return True\\n    except (IsADirectoryError, FileNotFoundError, PermissionError) as err:\\n        logger.error(\\n            f\\\"{envs_txt_file} could not be \\\"\\n            f\\\"accessed because of the following error: {err}\\\"\\n        )\\n    return False\\n\\n\\ndef excluded_files_check(filename: str) -> bool:\\n    excluded_extensions = (\\\".pyc\\\", \\\".pyo\\\")\\n    return filename.endswith(excluded_extensions)\\n\\n\\ndef find_packages_with_missing_files(prefix: str | Path) -> dict[str, list[str]]:\\n    \\\"\\\"\\\"Finds packages listed in conda-meta which have missing files.\\\"\\\"\\\"\\n    packages_with_missing_files = {}\\n    prefix = Path(prefix)\\n    for file in (prefix / \\\"conda-meta\\\").glob(\\\"*.json\\\"):\\n        for file_name in json.loads(file.read_text()).get(\\\"files\\\", []):\\n            # Add warnings if json file has missing \\\"files\\\"\\n            if (\\n                not excluded_files_check(file_name)\\n                and not (prefix / file_name).exists()\\n            ):\\n                packages_with_missing_files.setdefault(file.stem, []).append(file_name)\\n    return packages_with_missing_files\\n\\n\\ndef find_altered_packages(prefix: str | Path) -> dict[str, list[str]]:\\n    \\\"\\\"\\\"Finds altered packages\\\"\\\"\\\"\\n    altered_packages = {}\\n\\n    prefix = Path(prefix)\\n    for file in (prefix / \\\"conda-meta\\\").glob(\\\"*.json\\\"):\\n        try:\\n            metadata = json.loads(file.read_text())\\n        except Exception as exc:\\n            logger.error(\\n                f\\\"Could not load the json file {file} because of the following error: {exc}.\\\"\\n            )\\n            continue\\n\\n        try:\\n            paths_data = metadata[\\\"paths_data\\\"]\\n            paths = paths_data[\\\"paths\\\"]\\n        except KeyError:\\n            continue\\n\\n        if paths_data.get(\\\"paths_version\\\") != 1:\\n            continue\\n\\n        for path in paths:\\n            _path = path.get(\\\"_path\\\")\\n            old_sha256 = path.get(\\\"sha256_in_prefix\\\")\\n            if _path is None or old_sha256 is None:\\n                continue\\n\\n            file_location = prefix / _path\\n            if not file_location.is_file():\\n                continue\\n\\n            try:\\n                new_sha256 = compute_sum(file_location, \\\"sha256\\\")\\n            except OSError as err:\\n                raise CondaError(\\n                    f\\\"Could not generate checksum for file {file_location} \\\"\\n                    f\\\"because of the following error: {err}.\\\"\\n                )\\n\\n            if old_sha256 != new_sha256:\\n                altered_packages.setdefault(file.stem, []).append(_path)\\n\\n    return altered_packages\\n\\n\\n@deprecated(\\\"24.3\\\", \\\"24.9\\\")\\ndef display_health_checks(prefix: str, verbose: bool = False) -> None:\\n    \\\"\\\"\\\"Prints health report.\\\"\\\"\\\"\\n    print(f\\\"Environment Health Report for: {prefix}\\\\n\\\")\\n    context.plugin_manager.invoke_health_checks(prefix, verbose)\\n\\n\\ndef missing_files(prefix: str, verbose: bool) -> None:\\n    print(\\\"Missing Files:\\\\n\\\")\\n    missing_files = find_packages_with_missing_files(prefix)\\n    if missing_files:\\n        for package_name, missing_files in missing_files.items():\\n            if verbose:\\n                delimiter = \\\"\\\\n  \\\"\\n                print(f\\\"{package_name}:{delimiter}{delimiter.join(missing_files)}\\\")\\n            else:\\n                print(f\\\"{package_name}: {len(missing_files)}\\\\n\\\")\\n    else:\\n        print(f\\\"{OK_MARK} There are no packages with missing files.\\\\n\\\")\\n\\n\\ndef altered_files(prefix: str, verbose: bool) -> None:\\n    print(\\\"Altered Files:\\\\n\\\")\\n    altered_packages = find_altered_packages(prefix)\\n    if altered_packages:\\n        for package_name, altered_files in altered_packages.items():\\n            if verbose:\\n                delimiter = \\\"\\\\n  \\\"\\n                print(f\\\"{package_name}:{delimiter}{delimiter.join(altered_files)}\\\\n\\\")\\n            else:\\n                print(f\\\"{package_name}: {len(altered_files)}\\\\n\\\")\\n    else:\\n        print(f\\\"{OK_MARK} There are no packages with altered files.\\\\n\\\")\\n\\n\\ndef env_txt_check(prefix: str, verbose: bool) -> None:\\n    present = OK_MARK if check_envs_txt_file(prefix) else X_MARK\\n    print(f\\\"Environment listed in environments.txt file: {present}\\\\n\\\")\\n\\n\\n@hookimpl\\ndef conda_health_checks():\\n    yield CondaHealthCheck(name=\\\"Missing Files\\\", action=missing_files)\\n    yield CondaHealthCheck(name=\\\"Altered Files\\\", action=altered_files)\\n    yield CondaHealthCheck(name=\\\"Environment.txt File Check\\\", action=env_txt_check)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Detect whether this is macOS.\\\"\\\"\\\"\\n\\nimport os\\n\\nfrom ...base.context import context\\nfrom .. import CondaVirtualPackage, hookimpl\\n\\n\\n@hookimpl\\ndef conda_virtual_packages():\\n    if not context.subdir.startswith(\\\"osx-\\\"):\\n        return\\n\\n    yield CondaVirtualPackage(\\\"unix\\\", None, None)\\n\\n    _, dist_version = context.os_distribution_name_version\\n    dist_version = os.environ.get(\\\"CONDA_OVERRIDE_OSX\\\", dist_version)\\n    if dist_version:\\n        yield CondaVirtualPackage(\\\"osx\\\", dist_version, None)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Detect whether this is Linux.\\\"\\\"\\\"\\n\\nimport os\\nimport re\\n\\nfrom ...base.context import context\\nfrom ...common._os.linux import linux_get_libc_version\\nfrom .. import CondaVirtualPackage, hookimpl\\n\\n\\n@hookimpl\\ndef conda_virtual_packages():\\n    if not context.subdir.startswith(\\\"linux-\\\"):\\n        return\\n\\n    yield CondaVirtualPackage(\\\"unix\\\", None, None)\\n\\n    # By convention, the kernel release string should be three or four\\n    # numeric components, separated by dots, followed by vendor-specific\\n    # bits.  For the purposes of versioning the `__linux` virtual package,\\n    # discard everything after the last digit of the third or fourth\\n    # numeric component; note that this breaks version ordering for\\n    # development (`-rcN`) kernels, but that can be a TODO for later.\\n    _, dist_version = context.platform_system_release\\n    dist_version = os.environ.get(\\\"CONDA_OVERRIDE_LINUX\\\", dist_version)\\n    m = re.match(r\\\"\\\\d+\\\\.\\\\d+(\\\\.\\\\d+)?(\\\\.\\\\d+)?\\\", dist_version)\\n    yield CondaVirtualPackage(\\\"linux\\\", m.group() if m else \\\"0\\\", None)\\n\\n    libc_family, libc_version = linux_get_libc_version()\\n    if not (libc_family and libc_version):\\n        # Default to glibc when using CONDA_SUBDIR var\\n        libc_family = \\\"glibc\\\"\\n    libc_version = os.getenv(f\\\"CONDA_OVERRIDE_{libc_family.upper()}\\\", libc_version)\\n    if libc_version:\\n        yield CondaVirtualPackage(libc_family, libc_version, None)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Detect whether this is FeeBSD.\\\"\\\"\\\"\\n\\nfrom ...base.context import context\\nfrom .. import CondaVirtualPackage, hookimpl\\n\\n\\n@hookimpl\\ndef conda_virtual_packages():\\n    if not context.subdir.startswith(\\\"freebsd-\\\"):\\n        return\\n\\n    yield CondaVirtualPackage(\\\"unix\\\", None, None)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Detect whether this is Windows.\\\"\\\"\\\"\\n\\nfrom ...base.context import context\\nfrom .. import CondaVirtualPackage, hookimpl\\n\\n\\n@hookimpl\\ndef conda_virtual_packages():\\n    if not context.subdir.startswith(\\\"win-\\\"):\\n        return\\n\\n    yield CondaVirtualPackage(\\\"win\\\", None, None)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Detect CUDA version.\\\"\\\"\\\"\\n\\nimport ctypes\\nimport functools\\nimport itertools\\nimport multiprocessing\\nimport os\\nimport platform\\nfrom contextlib import suppress\\n\\nfrom .. import CondaVirtualPackage, hookimpl\\n\\n\\ndef cuda_version():\\n    \\\"\\\"\\\"\\n    Attempt to detect the version of CUDA present in the operating system.\\n\\n    On Windows and Linux, the CUDA library is installed by the NVIDIA\\n    driver package, and is typically found in the standard library path,\\n    rather than with the CUDA SDK (which is optional for running CUDA apps).\\n\\n    On macOS, the CUDA library is only installed with the CUDA SDK, and\\n    might not be in the library path.\\n\\n    Returns: version string (e.g., '9.2') or None if CUDA is not found.\\n    \\\"\\\"\\\"\\n    if \\\"CONDA_OVERRIDE_CUDA\\\" in os.environ:\\n        return os.environ[\\\"CONDA_OVERRIDE_CUDA\\\"].strip() or None\\n\\n    # Do not inherit file descriptors and handles from the parent process.\\n    # The `fork` start method should be considered unsafe as it can lead to\\n    # crashes of the subprocess. The `spawn` start method is preferred.\\n    context = multiprocessing.get_context(\\\"spawn\\\")\\n    queue = context.SimpleQueue()\\n    try:\\n        # Spawn a subprocess to detect the CUDA version\\n        detector = context.Process(\\n            target=_cuda_driver_version_detector_target,\\n            args=(queue,),\\n            name=\\\"CUDA driver version detector\\\",\\n            daemon=True,\\n        )\\n        detector.start()\\n        detector.join(timeout=60.0)\\n    finally:\\n        # Always cleanup the subprocess\\n        detector.kill()  # requires Python 3.7+\\n\\n    if queue.empty():\\n        return None\\n\\n    result = queue.get()\\n    return result\\n\\n\\n@functools.lru_cache(maxsize=None)\\ndef cached_cuda_version():\\n    \\\"\\\"\\\"A cached version of the cuda detection system.\\\"\\\"\\\"\\n    return cuda_version()\\n\\n\\n@hookimpl\\ndef conda_virtual_packages():\\n    cuda_version = cached_cuda_version()\\n    if cuda_version is not None:\\n        yield CondaVirtualPackage(\\\"cuda\\\", cuda_version, None)\\n\\n\\ndef _cuda_driver_version_detector_target(queue):\\n    \\\"\\\"\\\"\\n    Attempt to detect the version of CUDA present in the operating system in a\\n    subprocess.\\n\\n    On Windows and Linux, the CUDA library is installed by the NVIDIA\\n    driver package, and is typically found in the standard library path,\\n    rather than with the CUDA SDK (which is optional for running CUDA apps).\\n\\n    On macOS, the CUDA library is only installed with the CUDA SDK, and\\n    might not be in the library path.\\n\\n    Returns: version string (e.g., '9.2') or None if CUDA is not found.\\n             The result is put in the queue rather than a return value.\\n    \\\"\\\"\\\"\\n    # Platform-specific libcuda location\\n    system = platform.system()\\n    if system == \\\"Darwin\\\":\\n        lib_filenames = [\\n            \\\"libcuda.1.dylib\\\",  # check library path first\\n            \\\"libcuda.dylib\\\",\\n            \\\"/usr/local/cuda/lib/libcuda.1.dylib\\\",\\n            \\\"/usr/local/cuda/lib/libcuda.dylib\\\",\\n        ]\\n    elif system == \\\"Linux\\\":\\n        lib_filenames = [\\n            \\\"libcuda.so\\\",  # check library path first\\n            \\\"/usr/lib64/nvidia/libcuda.so\\\",  # RHEL/Centos/Fedora\\n            \\\"/usr/lib/x86_64-linux-gnu/libcuda.so\\\",  # Ubuntu\\n            \\\"/usr/lib/wsl/lib/libcuda.so\\\",  # WSL\\n        ]\\n        # Also add libraries with version suffix `.1`\\n        lib_filenames = list(\\n            itertools.chain.from_iterable((f\\\"{lib}.1\\\", lib) for lib in lib_filenames)\\n        )\\n    elif system == \\\"Windows\\\":\\n        bits = platform.architecture()[0].replace(\\\"bit\\\", \\\"\\\")  # e.g. \\\"64\\\" or \\\"32\\\"\\n        lib_filenames = [f\\\"nvcuda{bits}.dll\\\", \\\"nvcuda.dll\\\"]\\n    else:\\n        queue.put(None)  # CUDA not available for other operating systems\\n        return\\n\\n    # Open library\\n    if system == \\\"Windows\\\":\\n        dll = ctypes.windll\\n    else:\\n        dll = ctypes.cdll\\n    for lib_filename in lib_filenames:\\n        with suppress(Exception):\\n            libcuda = dll.LoadLibrary(lib_filename)\\n            break\\n    else:\\n        queue.put(None)\\n        return\\n\\n    # Empty `CUDA_VISIBLE_DEVICES` can cause `cuInit()` returns `CUDA_ERROR_NO_DEVICE`\\n    # Invalid `CUDA_VISIBLE_DEVICES` can cause `cuInit()` returns `CUDA_ERROR_INVALID_DEVICE`\\n    # Unset this environment variable to avoid these errors\\n    os.environ.pop(\\\"CUDA_VISIBLE_DEVICES\\\", None)\\n\\n    # Get CUDA version\\n    try:\\n        cuInit = libcuda.cuInit\\n        flags = ctypes.c_uint(0)\\n        ret = cuInit(flags)\\n        if ret != 0:\\n            queue.put(None)\\n            return\\n\\n        cuDriverGetVersion = libcuda.cuDriverGetVersion\\n        version_int = ctypes.c_int(0)\\n        ret = cuDriverGetVersion(ctypes.byref(version_int))\\n        if ret != 0:\\n            queue.put(None)\\n            return\\n\\n        # Convert version integer to version string\\n        value = version_int.value\\n        queue.put(f\\\"{value // 1000}.{(value % 1000) // 10}\\\")\\n        return\\n    except Exception:\\n        queue.put(None)\\n        return\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Detect archspec name.\\\"\\\"\\\"\\n\\nimport os\\n\\nfrom .. import CondaVirtualPackage, hookimpl\\n\\n\\n@hookimpl\\ndef conda_virtual_packages():\\n    from ...core.index import get_archspec_name\\n\\n    archspec_name = get_archspec_name()\\n    archspec_name = os.getenv(\\\"CONDA_OVERRIDE_ARCHSPEC\\\", archspec_name)\\n    if archspec_name:\\n        yield CondaVirtualPackage(\\\"archspec\\\", \\\"1\\\", archspec_name)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Expose conda version.\\\"\\\"\\\"\\n\\nfrom .. import CondaVirtualPackage, hookimpl\\n\\n\\n@hookimpl\\ndef conda_virtual_packages():\\n    from ... import __version__\\n\\n    yield CondaVirtualPackage(\\\"conda\\\", __version__, None)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\nfrom __future__ import annotations\\n\\nfrom . import archspec, conda, cuda, freebsd, linux, osx, windows\\n\\n#: The list of virtual package plugins for easier registration with pluggy\\nplugins = [archspec, conda, cuda, freebsd, linux, osx, windows]\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Register signature verification as a post-solve plugin.\\\"\\\"\\\"\\n\\nfrom .. import CondaPostSolve, hookimpl\\n\\n\\n@hookimpl\\ndef conda_post_solves():\\n    from ...trust.signature_verification import signature_verification\\n\\n    yield CondaPostSolve(\\n        name=\\\"signature-verification\\\",\\n        action=signature_verification,\\n    )\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Register the built-in post_solves hook implementations.\\\"\\\"\\\"\\n\\nfrom . import signature_verification\\n\\n#: The list of post-solve plugins for easier registration with pluggy\\nplugins = [signature_verification]\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Implements all conda.notices types.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport hashlib\\nfrom datetime import datetime\\nfrom typing import TYPE_CHECKING, NamedTuple\\n\\nfrom ..base.constants import NoticeLevel\\n\\nif TYPE_CHECKING:\\n    from pathlib import Path\\n    from typing import Sequence\\n\\n#: Value to use for message ID when it is not provided\\nUNDEFINED_MESSAGE_ID = \\\"undefined\\\"\\n\\n\\nclass ChannelNotice(NamedTuple):\\n    \\\"\\\"\\\"Represents an individual channel notice.\\\"\\\"\\\"\\n\\n    id: str\\n    channel_name: str | None\\n    message: str | None\\n    level: NoticeLevel\\n    created_at: datetime | None\\n    expired_at: datetime | None\\n    interval: int | None\\n\\n    def to_dict(self):\\n        return {\\n            \\\"id\\\": self.id,\\n            \\\"channel_name\\\": self.channel_name,\\n            \\\"message\\\": self.message,\\n            \\\"level\\\": self.level.name.lower(),\\n            \\\"created_at\\\": self.created_at.isoformat(),\\n            \\\"expired_at\\\": self.expired_at.isoformat(),\\n            \\\"interval\\\": self.interval,\\n        }\\n\\n\\nclass ChannelNoticeResultSet(NamedTuple):\\n    \\\"\\\"\\\"\\n    Represents a list of a channel notices, plus some accompanying\\n    metadata such as `viewed_channel_notices`.\\n    \\\"\\\"\\\"\\n\\n    #: Channel notices that are included in this particular set\\n    channel_notices: Sequence[ChannelNotice]\\n\\n    #: Total number of channel notices; not just the ones that will be displayed\\n    total_number_channel_notices: int\\n\\n    #: The number of channel notices that have already been viewed\\n    viewed_channel_notices: int\\n\\n\\nclass ChannelNoticeResponse(NamedTuple):\\n    url: str\\n    name: str\\n    json_data: dict | None\\n\\n    @property\\n    def notices(self) -> Sequence[ChannelNotice]:\\n        if self.json_data:\\n            notices = self.json_data.get(\\\"notices\\\", ())\\n\\n            return tuple(\\n                ChannelNotice(\\n                    id=str(notice.get(\\\"id\\\", UNDEFINED_MESSAGE_ID)),\\n                    channel_name=self.name,\\n                    message=notice.get(\\\"message\\\"),\\n                    level=self._parse_notice_level(notice.get(\\\"level\\\")),\\n                    created_at=self._parse_iso_timestamp(notice.get(\\\"created_at\\\")),\\n                    expired_at=self._parse_iso_timestamp(notice.get(\\\"expired_at\\\")),\\n                    interval=notice.get(\\\"interval\\\"),\\n                )\\n                for notice in notices\\n            )\\n\\n        # Default value\\n        return ()\\n\\n    @staticmethod\\n    def _parse_notice_level(level: str | None) -> NoticeLevel:\\n        \\\"\\\"\\\"\\n        We use this to validate notice levels and provide reasonable defaults\\n        if any are invalid.\\n        \\\"\\\"\\\"\\n        try:\\n            return NoticeLevel(level)\\n        except ValueError:\\n            # If we get an invalid value, rather than fail, we simply use a reasonable default\\n            return NoticeLevel(NoticeLevel.INFO)\\n\\n    @staticmethod\\n    def _parse_iso_timestamp(iso_timestamp: str | None) -> datetime | None:\\n        \\\"\\\"\\\"Parse ISO timestamp and fail over to a default value of none.\\\"\\\"\\\"\\n        if iso_timestamp is None:\\n            return None\\n        try:\\n            return datetime.fromisoformat(iso_timestamp)\\n        except ValueError:\\n            return None\\n\\n    @classmethod\\n    def get_cache_key(cls, url: str, cache_dir: Path) -> Path:\\n        \\\"\\\"\\\"Returns where this channel response will be cached by hashing the URL.\\\"\\\"\\\"\\n        bytes_filename = url.encode()\\n        sha256_hash = hashlib.sha256(bytes_filename)\\n        cache_filename = f\\\"{sha256_hash.hexdigest()}.json\\\"\\n\\n        return cache_dir.joinpath(cache_filename)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Handles all display/view logic.\\\"\\\"\\\"\\n\\nimport json\\nfrom typing import Sequence\\n\\nfrom ..base.context import context\\nfrom .types import ChannelNotice\\n\\n\\ndef print_notices(channel_notices: Sequence[ChannelNotice]):\\n    \\\"\\\"\\\"\\n    Accepts a list of channel notice responses and prints a display.\\n\\n    Args:\\n        channel_notices: A sequence of ChannelNotice objects.\\n    \\\"\\\"\\\"\\n    current_channel = None\\n\\n    if context.json:\\n        json_output = json.dumps(\\n            [channel_notice.to_dict() for channel_notice in channel_notices]\\n        )\\n        print(json_output)\\n\\n    else:\\n        for channel_notice in channel_notices:\\n            if current_channel != channel_notice.channel_name:\\n                print()\\n                channel_header = \\\"Channel\\\"\\n                channel_header += (\\n                    f' \\\"{channel_notice.channel_name}\\\" has the following notices:'\\n                )\\n                print(channel_header)\\n                current_channel = channel_notice.channel_name\\n            print_notice_message(channel_notice)\\n            print()\\n\\n\\ndef print_notice_message(notice: ChannelNotice, indent: str = \\\"  \\\") -> None:\\n    \\\"\\\"\\\"Prints a single channel notice.\\\"\\\"\\\"\\n    timestamp = f\\\"{notice.created_at:%c}\\\" if notice.created_at else \\\"\\\"\\n\\n    level = f\\\"[{notice.level}] -- {timestamp}\\\"\\n\\n    print(f\\\"{indent}{level}\\\\n{indent}{notice.message}\\\")\\n\\n\\ndef print_more_notices_message(\\n    total_notices: int, displayed_notices: int, viewed_notices: int\\n) -> None:\\n    \\\"\\\"\\\"Conditionally shows a message informing users how many more message there are.\\\"\\\"\\\"\\n    notices_not_shown = total_notices - viewed_notices - displayed_notices\\n\\n    if notices_not_shown > 0:\\n        if notices_not_shown > 1:\\n            msg = f\\\"There are {notices_not_shown} more messages. To retrieve them run:\\\\n\\\\n\\\"\\n        else:\\n            msg = f\\\"There is {notices_not_shown} more message. To retrieve it run:\\\\n\\\\n\\\"\\n        print(f\\\"{msg}conda notices\\\\n\\\")\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Core conda notices logic.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport logging\\nimport time\\nfrom functools import wraps\\nfrom typing import TYPE_CHECKING\\n\\nfrom ..base.constants import NOTICES_DECORATOR_DISPLAY_INTERVAL, NOTICES_FN\\nfrom ..base.context import context\\nfrom ..models.channel import get_channel_objs\\nfrom . import cache, fetch, views\\nfrom .types import ChannelNoticeResultSet\\n\\nif TYPE_CHECKING:\\n    from typing import Sequence\\n\\n    from ..base.context import Context\\n    from ..models.channel import Channel, MultiChannel\\n    from .types import ChannelNotice, ChannelNoticeResponse\\n\\n# Used below in type hints\\nChannelName = str\\nChannelUrl = str\\n\\nlogger = logging.getLogger(__name__)\\n\\n\\ndef retrieve_notices(\\n    limit: int | None = None,\\n    always_show_viewed: bool = True,\\n    silent: bool = False,\\n) -> ChannelNoticeResultSet:\\n    \\\"\\\"\\\"\\n    Function used for retrieving notices. This is called by the \\\"notices\\\" decorator as well\\n    as the sub-command \\\"notices\\\"\\n\\n    Args:\\n        limit: Limit the number of notices to show (defaults to None).\\n        always_show_viewed: Whether all notices should be shown, not only the unread ones\\n                            (defaults to True).\\n        silent: Whether to use a spinner when fetching and caching notices.\\n    \\\"\\\"\\\"\\n    channel_name_urls = get_channel_name_and_urls(get_channel_objs(context))\\n    channel_notice_responses = fetch.get_notice_responses(\\n        channel_name_urls, silent=silent\\n    )\\n    channel_notices = flatten_notice_responses(channel_notice_responses)\\n    total_number_channel_notices = len(channel_notices)\\n\\n    cache_file = cache.get_notices_cache_file()\\n\\n    # We always want to modify the mtime attribute of the file if we are trying to retrieve notices\\n    # This is used later in \\\"is_channel_notices_cache_expired\\\"\\n    cache_file.touch()\\n\\n    viewed_notices = None\\n    viewed_channel_notices = 0\\n    if not always_show_viewed:\\n        viewed_notices = cache.get_viewed_channel_notice_ids(\\n            cache_file, channel_notices\\n        )\\n        viewed_channel_notices = len(viewed_notices)\\n\\n    channel_notices = filter_notices(\\n        channel_notices, limit=limit, exclude=viewed_notices\\n    )\\n\\n    return ChannelNoticeResultSet(\\n        channel_notices=channel_notices,\\n        viewed_channel_notices=viewed_channel_notices,\\n        total_number_channel_notices=total_number_channel_notices,\\n    )\\n\\n\\ndef display_notices(channel_notice_set: ChannelNoticeResultSet) -> None:\\n    \\\"\\\"\\\"Prints the channel notices to std out.\\\"\\\"\\\"\\n    views.print_notices(channel_notice_set.channel_notices)\\n\\n    # Updates cache database, marking displayed notices as \\\"viewed\\\"\\n    cache_file = cache.get_notices_cache_file()\\n    cache.mark_channel_notices_as_viewed(cache_file, channel_notice_set.channel_notices)\\n\\n    views.print_more_notices_message(\\n        channel_notice_set.total_number_channel_notices,\\n        len(channel_notice_set.channel_notices),\\n        channel_notice_set.viewed_channel_notices,\\n    )\\n\\n\\ndef notices(func):\\n    \\\"\\\"\\\"\\n    Wrapper for \\\"execute\\\" entry points for subcommands.\\n\\n    If channel notices need to be fetched, we do that first and then\\n    run the command normally. We then display these notices at the very\\n    end of the command output so that the user is more likely to see them.\\n\\n    This ordering was specifically done to address the following bug report:\\n        - https://github.com/conda/conda/issues/11847\\n\\n    Args:\\n        func: Function to be decorated\\n    \\\"\\\"\\\"\\n\\n    @wraps(func)\\n    def wrapper(*args, **kwargs):\\n        if is_channel_notices_enabled(context):\\n            channel_notice_set = None\\n\\n            try:\\n                if is_channel_notices_cache_expired():\\n                    channel_notice_set = retrieve_notices(\\n                        limit=context.number_channel_notices,\\n                        always_show_viewed=False,\\n                        silent=True,\\n                    )\\n            except OSError as exc:\\n                # If we encounter any OSError related error, we simply abandon\\n                # fetching notices\\n                logger.error(f\\\"Unable to open cache file: {str(exc)}\\\")\\n\\n            if channel_notice_set is not None:\\n                return_value = func(*args, **kwargs)\\n                display_notices(channel_notice_set)\\n\\n                return return_value\\n\\n        return func(*args, **kwargs)\\n\\n    return wrapper\\n\\n\\ndef get_channel_name_and_urls(\\n    channels: Sequence[Channel | MultiChannel],\\n) -> list[tuple[ChannelUrl, ChannelName]]:\\n    \\\"\\\"\\\"\\n    Return a sequence of Channel URL and name tuples.\\n\\n    This function handles both Channel and MultiChannel object types.\\n    \\\"\\\"\\\"\\n    channel_name_and_urls = []\\n\\n    for channel in channels:\\n        name = channel.name or channel.location\\n\\n        for url in channel.base_urls:\\n            full_url = url.rstrip(\\\"/\\\")\\n            channel_name_and_urls.append((f\\\"{full_url}/{NOTICES_FN}\\\", name))\\n\\n    return channel_name_and_urls\\n\\n\\ndef flatten_notice_responses(\\n    channel_notice_responses: Sequence[ChannelNoticeResponse],\\n) -> Sequence[ChannelNotice]:\\n    return tuple(\\n        notice\\n        for channel in channel_notice_responses\\n        if channel.notices\\n        for notice in channel.notices\\n    )\\n\\n\\ndef filter_notices(\\n    channel_notices: Sequence[ChannelNotice],\\n    limit: int | None = None,\\n    exclude: set[str] | None = None,\\n) -> Sequence[ChannelNotice]:\\n    \\\"\\\"\\\"Perform filtering actions for the provided sequence of ChannelNotice objects.\\\"\\\"\\\"\\n    if exclude:\\n        channel_notices = tuple(\\n            channel_notice\\n            for channel_notice in channel_notices\\n            if channel_notice.id not in exclude\\n        )\\n\\n    if limit is not None:\\n        channel_notices = channel_notices[:limit]\\n\\n    return channel_notices\\n\\n\\ndef is_channel_notices_enabled(ctx: Context) -> bool:\\n    \\\"\\\"\\\"\\n    Determines whether channel notices are enabled and therefore displayed when\\n    invoking the `notices` command decorator.\\n\\n    This only happens when:\\n     - offline is False\\n     - number_channel_notices is greater than 0\\n\\n    Args:\\n        ctx: The conda context object\\n    \\\"\\\"\\\"\\n    return ctx.number_channel_notices > 0 and not ctx.offline and not ctx.json\\n\\n\\ndef is_channel_notices_cache_expired() -> bool:\\n    \\\"\\\"\\\"\\n    Checks to see if the notices cache file we use to keep track of\\n    displayed notices is expired. This involves checking the mtime\\n    attribute of the file. Anything older than what is specified as\\n    the NOTICES_DECORATOR_DISPLAY_INTERVAL is considered expired.\\n    \\\"\\\"\\\"\\n    cache_file = cache.get_notices_cache_file()\\n\\n    cache_file_stat = cache_file.stat()\\n    now = time.time()\\n    seconds_since_checked = now - cache_file_stat.st_mtime\\n\\n    return seconds_since_checked >= NOTICES_DECORATOR_DISPLAY_INTERVAL\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"\\nHandles all caching logic including:\\n  - Retrieving from cache\\n  - Saving to cache\\n  - Determining whether not certain items have expired and need to be refreshed\\n\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport json\\nimport logging\\nimport os\\nfrom datetime import datetime, timezone\\nfrom functools import wraps\\nfrom pathlib import Path\\nfrom typing import TYPE_CHECKING\\n\\ntry:\\n    from platformdirs import user_cache_dir\\nexcept ImportError:  # pragma: no cover\\n    from .._vendor.appdirs import user_cache_dir\\n\\nfrom ..base.constants import APP_NAME, NOTICES_CACHE_FN, NOTICES_CACHE_SUBDIR\\nfrom ..utils import ensure_dir_exists\\nfrom .types import ChannelNoticeResponse\\n\\nif TYPE_CHECKING:\\n    from typing import Sequence\\n\\n    from .types import ChannelNotice\\n\\nlogger = logging.getLogger(__name__)\\n\\n\\ndef cached_response(func):\\n    @wraps(func)\\n    def wrapper(url: str, name: str):\\n        cache_dir = get_notices_cache_dir()\\n        cache_val = get_notice_response_from_cache(url, name, cache_dir)\\n\\n        if cache_val:\\n            return cache_val\\n\\n        return_value = func(url, name)\\n        if return_value is not None:\\n            write_notice_response_to_cache(return_value, cache_dir)\\n\\n        return return_value\\n\\n    return wrapper\\n\\n\\ndef is_notice_response_cache_expired(\\n    channel_notice_response: ChannelNoticeResponse,\\n) -> bool:\\n    \\\"\\\"\\\"\\n    This checks the contents of the cache response to see if it is expired.\\n\\n    If for whatever reason we encounter an exception while parsing the individual\\n    messages, we assume an invalid cache and return true.\\n    \\\"\\\"\\\"\\n    now = datetime.now(timezone.utc)\\n\\n    def is_channel_notice_expired(expired_at: datetime | None) -> bool:\\n        \\\"\\\"\\\"If there is no \\\"expired_at\\\" field present assume it is expired.\\\"\\\"\\\"\\n        if expired_at is None:\\n            return True\\n        return expired_at < now\\n\\n    return any(\\n        is_channel_notice_expired(chn.expired_at)\\n        for chn in channel_notice_response.notices\\n    )\\n\\n\\n@ensure_dir_exists\\ndef get_notices_cache_dir() -> Path:\\n    \\\"\\\"\\\"Returns the location of the notices cache directory as a Path object\\\"\\\"\\\"\\n    cache_dir = user_cache_dir(APP_NAME, appauthor=APP_NAME)\\n\\n    return Path(cache_dir).joinpath(NOTICES_CACHE_SUBDIR)\\n\\n\\ndef get_notices_cache_file() -> Path:\\n    \\\"\\\"\\\"Returns the location of the notices cache file as a Path object\\\"\\\"\\\"\\n    cache_dir = get_notices_cache_dir()\\n    cache_file = cache_dir.joinpath(NOTICES_CACHE_FN)\\n\\n    if not cache_file.is_file():\\n        with open(cache_file, \\\"w\\\") as fp:\\n            fp.write(\\\"\\\")\\n\\n    return cache_file\\n\\n\\ndef get_notice_response_from_cache(\\n    url: str, name: str, cache_dir: Path\\n) -> ChannelNoticeResponse | None:\\n    \\\"\\\"\\\"Retrieves a notice response object from cache if it exists.\\\"\\\"\\\"\\n    cache_key = ChannelNoticeResponse.get_cache_key(url, cache_dir)\\n\\n    if os.path.isfile(cache_key):\\n        with open(cache_key) as fp:\\n            data = json.load(fp)\\n        chn_ntc_resp = ChannelNoticeResponse(url, name, data)\\n\\n        if not is_notice_response_cache_expired(chn_ntc_resp):\\n            return chn_ntc_resp\\n\\n\\ndef write_notice_response_to_cache(\\n    channel_notice_response: ChannelNoticeResponse, cache_dir: Path\\n) -> None:\\n    \\\"\\\"\\\"Writes our notice data to our local cache location.\\\"\\\"\\\"\\n    cache_key = ChannelNoticeResponse.get_cache_key(\\n        channel_notice_response.url, cache_dir\\n    )\\n\\n    with open(cache_key, \\\"w\\\") as fp:\\n        json.dump(channel_notice_response.json_data, fp)\\n\\n\\ndef mark_channel_notices_as_viewed(\\n    cache_file: Path, channel_notices: Sequence[ChannelNotice]\\n) -> None:\\n    \\\"\\\"\\\"Insert channel notice into our database marking it as read.\\\"\\\"\\\"\\n    notice_ids = {chn.id for chn in channel_notices}\\n\\n    with open(cache_file) as fp:\\n        contents: str = fp.read()\\n\\n    contents_unique = set(filter(None, set(contents.splitlines())))\\n    contents_new = contents_unique.union(notice_ids)\\n\\n    # Save new version of cache file\\n    with open(cache_file, \\\"w\\\") as fp:\\n        fp.write(\\\"\\\\n\\\".join(contents_new))\\n\\n\\ndef get_viewed_channel_notice_ids(\\n    cache_file: Path, channel_notices: Sequence[ChannelNotice]\\n) -> set[str]:\\n    \\\"\\\"\\\"Return the ids of the channel notices which have already been seen.\\\"\\\"\\\"\\n    notice_ids = {chn.id for chn in channel_notices}\\n\\n    with open(cache_file) as fp:\\n        contents: str = fp.read()\\n\\n    contents_unique = set(filter(None, set(contents.splitlines())))\\n\\n    return notice_ids.intersection(contents_unique)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Notices network fetch logic.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport logging\\nfrom concurrent.futures import ThreadPoolExecutor\\nfrom typing import TYPE_CHECKING\\n\\nimport requests\\n\\nfrom ..base.context import context\\nfrom ..common.io import Spinner\\nfrom ..gateways.connection.session import get_session\\nfrom .cache import cached_response\\nfrom .types import ChannelNoticeResponse\\n\\nif TYPE_CHECKING:\\n    from typing import Sequence\\n\\nlogger = logging.getLogger(__name__)\\n\\n\\ndef get_notice_responses(\\n    url_and_names: Sequence[tuple[str, str]],\\n    silent: bool = False,\\n    max_workers: int = 10,\\n) -> Sequence[ChannelNoticeResponse]:\\n    \\\"\\\"\\\"\\n    Provided a list of channel notification url/name tuples, return a sequence of\\n    ChannelNoticeResponse objects.\\n\\n    Args:\\n        url_and_names: channel url and the channel name\\n        silent: turn off \\\"loading animation\\\" (defaults to False)\\n        max_workers: increase worker number in thread executor (defaults to 10)\\n    Returns:\\n        Sequence[ChannelNoticeResponse]\\n    \\\"\\\"\\\"\\n    executor = ThreadPoolExecutor(max_workers=max_workers)\\n\\n    with Spinner(\\\"Retrieving notices\\\", enabled=not silent, json=context.json):\\n        return tuple(\\n            filter(\\n                None,\\n                (\\n                    chn_info\\n                    for chn_info in executor.map(\\n                        lambda args: get_channel_notice_response(*args), url_and_names\\n                    )\\n                ),\\n            )\\n        )\\n\\n\\n@cached_response\\ndef get_channel_notice_response(url: str, name: str) -> ChannelNoticeResponse | None:\\n    \\\"\\\"\\\"\\n    Return a channel response object. We use this to wrap the response with\\n    additional channel information to use. If the response was invalid we suppress/log\\n    and error message.\\n    \\\"\\\"\\\"\\n    session = get_session(url)\\n    try:\\n        resp = session.get(\\n            url, allow_redirects=False, timeout=5\\n        )  # timeout: connect, read\\n    except requests.exceptions.Timeout:\\n        logger.info(f\\\"Request timed out for channel: {name} url: {url}\\\")\\n        return\\n    except requests.exceptions.RequestException as exc:\\n        logger.error(f\\\"Request error <{exc}> for channel: {name} url: {url}\\\")\\n        return\\n\\n    try:\\n        if resp.status_code < 300:\\n            return ChannelNoticeResponse(url, name, json_data=resp.json())\\n        else:\\n            logger.info(f\\\"Received {resp.status_code} when trying to GET {url}\\\")\\n    except ValueError:\\n        logger.info(f\\\"Unable to parse JSON data for {url}\\\")\\n        return ChannelNoticeResponse(url, name, json_data=None)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\nfrom .core import notices  # noqa: F401\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"CLI implementation for `conda list`.\\n\\nLists all packages installed into an environment.\\n\\\"\\\"\\\"\\n\\nimport logging\\nimport re\\nfrom argparse import ArgumentParser, Namespace, _SubParsersAction\\nfrom os.path import isdir, isfile\\n\\nlog = logging.getLogger(__name__)\\n\\n\\ndef configure_parser(sub_parsers: _SubParsersAction, **kwargs) -> ArgumentParser:\\n    from ..auxlib.ish import dals\\n    from .helpers import (\\n        add_parser_json,\\n        add_parser_prefix,\\n        add_parser_show_channel_urls,\\n    )\\n\\n    summary = \\\"List installed packages in a conda environment.\\\"\\n    description = summary\\n    epilog = dals(\\n        \\\"\\\"\\\"\\n        Examples:\\n\\n        List all packages in the current environment::\\n\\n            conda list\\n\\n        List all packages in reverse order::\\n\\n            conda list --reverse\\n\\n        List all packages installed into the environment 'myenv'::\\n\\n            conda list -n myenv\\n\\n        List all packages that begin with the letters \\\"py\\\", using regex::\\n\\n            conda list ^py\\n\\n        Save packages for future use::\\n\\n            conda list --export > package-list.txt\\n\\n        Reinstall packages from an export file::\\n\\n            conda create -n myenv --file package-list.txt\\n\\n        \\\"\\\"\\\"\\n    )\\n\\n    p = sub_parsers.add_parser(\\n        \\\"list\\\",\\n        help=summary,\\n        description=description,\\n        epilog=epilog,\\n        **kwargs,\\n    )\\n    add_parser_prefix(p)\\n    add_parser_json(p)\\n    add_parser_show_channel_urls(p)\\n    p.add_argument(\\n        \\\"--reverse\\\",\\n        action=\\\"store_true\\\",\\n        default=False,\\n        help=\\\"List installed packages in reverse order.\\\",\\n    )\\n    p.add_argument(\\n        \\\"-c\\\",\\n        \\\"--canonical\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Output canonical names of packages only.\\\",\\n    )\\n    p.add_argument(\\n        \\\"-f\\\",\\n        \\\"--full-name\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Only search for full names, i.e., ^<regex>$. \\\"\\n        \\\"--full-name NAME is identical to regex '^NAME$'.\\\",\\n    )\\n    p.add_argument(\\n        \\\"--explicit\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"List explicitly all installed conda packages with URL \\\"\\n        \\\"(output may be used by conda create --file).\\\",\\n    )\\n    p.add_argument(\\n        \\\"--md5\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Add MD5 hashsum when using --explicit.\\\",\\n    )\\n    p.add_argument(\\n        \\\"-e\\\",\\n        \\\"--export\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Output explicit, machine-readable requirement strings instead of \\\"\\n        \\\"human-readable lists of packages. This output may be used by \\\"\\n        \\\"conda create --file.\\\",\\n    )\\n    p.add_argument(\\n        \\\"-r\\\",\\n        \\\"--revisions\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"List the revision history.\\\",\\n    )\\n    p.add_argument(\\n        \\\"--no-pip\\\",\\n        action=\\\"store_false\\\",\\n        default=True,\\n        dest=\\\"pip\\\",\\n        help=\\\"Do not include pip-only installed packages.\\\",\\n    )\\n    p.add_argument(\\n        \\\"--auth\\\",\\n        action=\\\"store_false\\\",\\n        default=True,\\n        dest=\\\"remove_auth\\\",\\n        help=\\\"In explicit mode, leave authentication details in package URLs. \\\"\\n        \\\"They are removed by default otherwise.\\\",\\n    )\\n    p.add_argument(\\n        \\\"regex\\\",\\n        action=\\\"store\\\",\\n        nargs=\\\"?\\\",\\n        help=\\\"List only packages matching this regular expression.\\\",\\n    )\\n    p.set_defaults(func=\\\"conda.cli.main_list.execute\\\")\\n\\n    return p\\n\\n\\ndef print_export_header(subdir):\\n    print(\\\"# This file may be used to create an environment using:\\\")\\n    print(\\\"# $ conda create --name <env> --file <this file>\\\")\\n    print(f\\\"# platform: {subdir}\\\")\\n\\n\\ndef get_packages(installed, regex):\\n    pat = re.compile(regex, re.I) if regex else None\\n    for prefix_rec in sorted(installed, key=lambda x: x.name.lower()):\\n        if pat and pat.search(prefix_rec.name) is None:\\n            continue\\n        yield prefix_rec\\n\\n\\ndef list_packages(\\n    prefix,\\n    regex=None,\\n    format=\\\"human\\\",\\n    reverse=False,\\n    show_channel_urls=None,\\n):\\n    from ..base.constants import DEFAULTS_CHANNEL_NAME\\n    from ..base.context import context\\n    from ..core.prefix_data import PrefixData\\n    from .common import disp_features\\n\\n    res = 0\\n\\n    installed = sorted(\\n        PrefixData(prefix, pip_interop_enabled=True).iter_records(),\\n        key=lambda x: x.name,\\n    )\\n\\n    packages = []\\n    for prec in get_packages(installed, regex) if regex else installed:\\n        if format == \\\"canonical\\\":\\n            packages.append(\\n                prec.dist_fields_dump() if context.json else prec.dist_str()\\n            )\\n            continue\\n        if format == \\\"export\\\":\\n            packages.append(\\\"=\\\".join((prec.name, prec.version, prec.build)))\\n            continue\\n\\n        features = set(prec.get(\\\"features\\\") or ())\\n        disp = \\\"%(name)-25s %(version)-15s %(build)15s\\\" % prec\\n        disp += f\\\"  {disp_features(features)}\\\"\\n        schannel = prec.get(\\\"schannel\\\")\\n        show_channel_urls = show_channel_urls or context.show_channel_urls\\n        if (\\n            show_channel_urls\\n            or show_channel_urls is None\\n            and schannel != DEFAULTS_CHANNEL_NAME\\n        ):\\n            disp += f\\\"  {schannel}\\\"\\n\\n        packages.append(disp)\\n\\n    if reverse:\\n        packages = reversed(packages)\\n\\n    result = []\\n    if format == \\\"human\\\":\\n        result = [\\n            f\\\"# packages in environment at {prefix}:\\\",\\n            \\\"#\\\",\\n            \\\"# %-23s %-15s %15s  Channel\\\" % (\\\"Name\\\", \\\"Version\\\", \\\"Build\\\"),\\n        ]\\n    result.extend(packages)\\n\\n    return res, result\\n\\n\\ndef print_packages(\\n    prefix,\\n    regex=None,\\n    format=\\\"human\\\",\\n    reverse=False,\\n    piplist=False,\\n    json=False,\\n    show_channel_urls=None,\\n):\\n    from ..base.context import context\\n    from .common import stdout_json\\n\\n    if not isdir(prefix):\\n        from ..exceptions import EnvironmentLocationNotFound\\n\\n        raise EnvironmentLocationNotFound(prefix)\\n\\n    if not json:\\n        if format == \\\"export\\\":\\n            print_export_header(context.subdir)\\n\\n    exitcode, output = list_packages(\\n        prefix,\\n        regex,\\n        format=format,\\n        reverse=reverse,\\n        show_channel_urls=show_channel_urls,\\n    )\\n    if context.json:\\n        stdout_json(output)\\n\\n    else:\\n        print(\\\"\\\\n\\\".join(map(str, output)))\\n\\n    return exitcode\\n\\n\\ndef print_explicit(prefix, add_md5=False, remove_auth=True):\\n    from ..base.constants import UNKNOWN_CHANNEL\\n    from ..base.context import context\\n    from ..common import url as common_url\\n    from ..core.prefix_data import PrefixData\\n\\n    if not isdir(prefix):\\n        from ..exceptions import EnvironmentLocationNotFound\\n\\n        raise EnvironmentLocationNotFound(prefix)\\n    print_export_header(context.subdir)\\n    print(\\\"@EXPLICIT\\\")\\n    for prefix_record in PrefixData(prefix).iter_records_sorted():\\n        url = prefix_record.get(\\\"url\\\")\\n        if not url or url.startswith(UNKNOWN_CHANNEL):\\n            print(\\\"# no URL for: {}\\\".format(prefix_record[\\\"fn\\\"]))\\n            continue\\n        if remove_auth:\\n            url = common_url.remove_auth(common_url.split_anaconda_token(url)[0])\\n        md5 = prefix_record.get(\\\"md5\\\")\\n        print(url + (f\\\"#{md5}\\\" if add_md5 and md5 else \\\"\\\"))\\n\\n\\ndef execute(args: Namespace, parser: ArgumentParser) -> int:\\n    from ..base.context import context\\n    from ..gateways.disk.test import is_conda_environment\\n    from ..history import History\\n    from .common import stdout_json\\n\\n    prefix = context.target_prefix\\n    if not is_conda_environment(prefix):\\n        from ..exceptions import EnvironmentLocationNotFound\\n\\n        raise EnvironmentLocationNotFound(prefix)\\n\\n    regex = args.regex\\n    if args.full_name:\\n        regex = rf\\\"^{regex}$\\\"\\n\\n    if args.revisions:\\n        h = History(prefix)\\n        if isfile(h.path):\\n            if not context.json:\\n                h.print_log()\\n            else:\\n                stdout_json(h.object_log())\\n        else:\\n            from ..exceptions import PathNotFoundError\\n\\n            raise PathNotFoundError(h.path)\\n        return 0\\n\\n    if args.explicit:\\n        print_explicit(prefix, args.md5, args.remove_auth)\\n        return 0\\n\\n    if args.canonical:\\n        format = \\\"canonical\\\"\\n    elif args.export:\\n        format = \\\"export\\\"\\n    else:\\n        format = \\\"human\\\"\\n\\n    if context.json:\\n        format = \\\"canonical\\\"\\n\\n    return print_packages(\\n        prefix,\\n        regex,\\n        format,\\n        reverse=args.reverse,\\n        piplist=args.pip,\\n        json=context.json,\\n        show_channel_urls=context.show_channel_urls,\\n    )\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"CLI implementation for `conda-env create`.\\n\\nCreates new conda environments with the specified packages.\\n\\\"\\\"\\\"\\n\\nimport json\\nimport os\\nfrom argparse import (\\n    ArgumentParser,\\n    Namespace,\\n    _SubParsersAction,\\n)\\n\\nfrom .. import CondaError\\nfrom ..notices import notices\\n\\n\\ndef configure_parser(sub_parsers: _SubParsersAction, **kwargs) -> ArgumentParser:\\n    from ..auxlib.ish import dals\\n    from .helpers import (\\n        add_output_and_prompt_options,\\n        add_parser_default_packages,\\n        add_parser_networking,\\n        add_parser_platform,\\n        add_parser_prefix,\\n        add_parser_solver,\\n    )\\n\\n    summary = \\\"Create an environment based on an environment definition file.\\\"\\n    description = dals(\\n        f\\\"\\\"\\\"\\n        {summary}\\n\\n        If using an environment.yml file (the default), you can name the\\n        environment in the first line of the file with 'name: envname' or\\n        you can specify the environment name in the CLI command using the\\n        -n/--name argument. The name specified in the CLI will override\\n        the name specified in the environment.yml file.\\n\\n        Unless you are in the directory containing the environment definition\\n        file, use -f to specify the file path of the environment definition\\n        file you want to use.\\n\\n        \\\"\\\"\\\"\\n    )\\n    epilog = dals(\\n        \\\"\\\"\\\"\\n        Examples::\\n\\n            conda env create\\n            conda env create -n envname\\n            conda env create folder/envname\\n            conda env create -f /path/to/environment.yml\\n            conda env create -f /path/to/requirements.txt -n envname\\n            conda env create -f /path/to/requirements.txt -p /home/user/envname\\n\\n        \\\"\\\"\\\"\\n    )\\n\\n    p = sub_parsers.add_parser(\\n        \\\"create\\\",\\n        help=summary,\\n        description=description,\\n        epilog=epilog,\\n        **kwargs,\\n    )\\n    p.add_argument(\\n        \\\"-f\\\",\\n        \\\"--file\\\",\\n        action=\\\"store\\\",\\n        help=\\\"Environment definition file (default: environment.yml)\\\",\\n        default=\\\"environment.yml\\\",\\n    )\\n\\n    # Add name and prefix args\\n    add_parser_prefix(p)\\n\\n    # Add networking args\\n    add_parser_networking(p)\\n\\n    p.add_argument(\\n        \\\"remote_definition\\\",\\n        help=\\\"Remote environment definition / IPython notebook\\\",\\n        action=\\\"store\\\",\\n        default=None,\\n        nargs=\\\"?\\\",\\n    )\\n    add_parser_default_packages(p)\\n    add_output_and_prompt_options(p)\\n    add_parser_solver(p)\\n    add_parser_platform(p)\\n\\n    p.set_defaults(func=\\\"conda.cli.main_env_create.execute\\\")\\n\\n    return p\\n\\n\\n@notices\\ndef execute(args: Namespace, parser: ArgumentParser) -> int:\\n    from ..auxlib.ish import dals\\n    from ..base.context import context, determine_target_prefix\\n    from ..core.prefix_data import PrefixData\\n    from ..env import specs\\n    from ..env.env import get_filename, print_result\\n    from ..env.installers.base import get_installer\\n    from ..exceptions import InvalidInstaller\\n    from ..gateways.disk.delete import rm_rf\\n    from ..misc import touch_nonadmin\\n    from . import install as cli_install\\n\\n    spec = specs.detect(\\n        name=args.name,\\n        filename=get_filename(args.file),\\n        directory=os.getcwd(),\\n        remote_definition=args.remote_definition,\\n    )\\n    env = spec.environment\\n\\n    # FIXME conda code currently requires args to have a name or prefix\\n    # don't overwrite name if it's given. gh-254\\n    if args.prefix is None and args.name is None:\\n        args.name = env.name\\n\\n    prefix = determine_target_prefix(context, args)\\n\\n    if args.yes and prefix != context.root_prefix and os.path.exists(prefix):\\n        rm_rf(prefix)\\n    cli_install.check_prefix(prefix, json=args.json)\\n\\n    # TODO, add capability\\n    # common.ensure_override_channels_requires_channel(args)\\n    # channel_urls = args.channel or ()\\n\\n    result = {\\\"conda\\\": None, \\\"pip\\\": None}\\n\\n    args_packages = (\\n        context.create_default_packages if not args.no_default_packages else []\\n    )\\n\\n    if args.dry_run:\\n        installer_type = \\\"conda\\\"\\n        installer = get_installer(installer_type)\\n\\n        pkg_specs = env.dependencies.get(installer_type, [])\\n        pkg_specs.extend(args_packages)\\n\\n        solved_env = installer.dry_run(pkg_specs, args, env)\\n        if args.json:\\n            print(json.dumps(solved_env.to_dict(), indent=2))\\n        else:\\n            print(solved_env.to_yaml(), end=\\\"\\\")\\n\\n    else:\\n        if args_packages:\\n            installer_type = \\\"conda\\\"\\n            installer = get_installer(installer_type)\\n            result[installer_type] = installer.install(prefix, args_packages, args, env)\\n\\n        if len(env.dependencies.items()) == 0:\\n            installer_type = \\\"conda\\\"\\n            pkg_specs = []\\n            installer = get_installer(installer_type)\\n            result[installer_type] = installer.install(prefix, pkg_specs, args, env)\\n        else:\\n            for installer_type, pkg_specs in env.dependencies.items():\\n                try:\\n                    installer = get_installer(installer_type)\\n                    result[installer_type] = installer.install(\\n                        prefix, pkg_specs, args, env\\n                    )\\n                except InvalidInstaller:\\n                    raise CondaError(\\n                        dals(\\n                            f\\\"\\\"\\\"\\n                            Unable to install package for {installer_type}.\\n\\n                            Please double check and ensure your dependencies file has\\n                            the correct spelling. You might also try installing the\\n                            conda-env-{installer_type} package to see if provides\\n                            the required installer.\\n                            \\\"\\\"\\\"\\n                        )\\n                    )\\n\\n        if env.variables:\\n            pd = PrefixData(prefix)\\n            pd.set_environment_env_vars(env.variables)\\n\\n        touch_nonadmin(prefix)\\n        print_result(args, prefix, result)\\n\\n    return 0\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"CLI implementation for `conda-env config vars`.\\n\\nAllows for configuring conda-env's vars.\\n\\\"\\\"\\\"\\n\\nfrom argparse import (\\n    ArgumentParser,\\n    Namespace,\\n    _SubParsersAction,\\n)\\nfrom os.path import lexists\\n\\nfrom ..base.context import context, determine_target_prefix\\nfrom ..core.prefix_data import PrefixData\\nfrom ..exceptions import EnvironmentLocationNotFound\\n\\n\\ndef configure_parser(sub_parsers: _SubParsersAction, **kwargs) -> ArgumentParser:\\n    from ..auxlib.ish import dals\\n    from .helpers import add_parser_json, add_parser_prefix\\n\\n    var_summary = (\\n        \\\"Interact with environment variables associated with Conda environments.\\\"\\n    )\\n    var_description = var_summary\\n    var_epilog = dals(\\n        \\\"\\\"\\\"\\n        Examples::\\n\\n            conda env config vars list -n my_env\\n            conda env config vars set MY_VAR=something OTHER_THING=ohhhhya\\n            conda env config vars unset MY_VAR\\n\\n        \\\"\\\"\\\"\\n    )\\n\\n    var_parser = sub_parsers.add_parser(\\n        \\\"vars\\\",\\n        help=var_summary,\\n        description=var_description,\\n        epilog=var_epilog,\\n        **kwargs,\\n    )\\n    var_subparser = var_parser.add_subparsers()\\n\\n    list_summary = \\\"List environment variables for a conda environment.\\\"\\n    list_description = list_summary\\n    list_epilog = dals(\\n        \\\"\\\"\\\"\\n        Example::\\n\\n            conda env config vars list -n my_env\\n\\n        \\\"\\\"\\\"\\n    )\\n\\n    list_parser = var_subparser.add_parser(\\n        \\\"list\\\",\\n        help=list_summary,\\n        description=list_description,\\n        epilog=list_epilog,\\n    )\\n    add_parser_prefix(list_parser)\\n    add_parser_json(list_parser)\\n    list_parser.set_defaults(func=\\\"conda.cli.main_env_vars.execute_list\\\")\\n\\n    set_summary = \\\"Set environment variables for a conda environment.\\\"\\n    set_description = set_summary\\n    set_epilog = dals(\\n        \\\"\\\"\\\"\\n        Example::\\n\\n            conda env config vars set MY_VAR=weee\\n\\n        \\\"\\\"\\\"\\n    )\\n\\n    set_parser = var_subparser.add_parser(\\n        \\\"set\\\",\\n        help=set_summary,\\n        description=set_description,\\n        epilog=set_epilog,\\n    )\\n\\n    set_parser.add_argument(\\n        \\\"vars\\\",\\n        action=\\\"store\\\",\\n        nargs=\\\"*\\\",\\n        help=\\\"Environment variables to set in the form <KEY>=<VALUE> separated by spaces\\\",\\n    )\\n    add_parser_prefix(set_parser)\\n    set_parser.set_defaults(func=\\\"conda.cli.main_env_vars.execute_set\\\")\\n\\n    unset_summary = \\\"Unset environment variables for a conda environment.\\\"\\n    unset_description = unset_summary\\n    unset_epilog = dals(\\n        \\\"\\\"\\\"\\n        Example::\\n\\n            conda env config vars unset MY_VAR\\n\\n        \\\"\\\"\\\"\\n    )\\n    unset_parser = var_subparser.add_parser(\\n        \\\"unset\\\",\\n        help=unset_summary,\\n        description=unset_description,\\n        epilog=unset_epilog,\\n    )\\n    unset_parser.add_argument(\\n        \\\"vars\\\",\\n        action=\\\"store\\\",\\n        nargs=\\\"*\\\",\\n        help=\\\"Environment variables to unset in the form <KEY> separated by spaces\\\",\\n    )\\n    add_parser_prefix(unset_parser)\\n    unset_parser.set_defaults(func=\\\"conda.cli.main_env_vars.execute_unset\\\")\\n\\n\\ndef execute_list(args: Namespace, parser: ArgumentParser) -> int:\\n    from . import common\\n\\n    prefix = determine_target_prefix(context, args)\\n    if not lexists(prefix):\\n        raise EnvironmentLocationNotFound(prefix)\\n\\n    pd = PrefixData(prefix)\\n\\n    env_vars = pd.get_environment_env_vars()\\n    if args.json:\\n        common.stdout_json(env_vars)\\n    else:\\n        for k, v in env_vars.items():\\n            print(f\\\"{k} = {v}\\\")\\n\\n    return 0\\n\\n\\ndef execute_set(args: Namespace, parser: ArgumentParser) -> int:\\n    prefix = determine_target_prefix(context, args)\\n    pd = PrefixData(prefix)\\n    if not lexists(prefix):\\n        raise EnvironmentLocationNotFound(prefix)\\n\\n    env_vars_to_add = {}\\n    for var in args.vars:\\n        var_def = var.split(\\\"=\\\")\\n        env_vars_to_add[var_def[0].strip()] = \\\"=\\\".join(var_def[1:]).strip()\\n    pd.set_environment_env_vars(env_vars_to_add)\\n    if prefix == context.active_prefix:\\n        print(\\\"To make your changes take effect please reactivate your environment\\\")\\n\\n    return 0\\n\\n\\ndef execute_unset(args: Namespace, parser: ArgumentParser) -> int:\\n    prefix = determine_target_prefix(context, args)\\n    pd = PrefixData(prefix)\\n    if not lexists(prefix):\\n        raise EnvironmentLocationNotFound(prefix)\\n\\n    vars_to_unset = [var.strip() for var in args.vars]\\n    pd.unset_environment_env_vars(vars_to_unset)\\n    if prefix == context.active_prefix:\\n        print(\\\"To make your changes take effect please reactivate your environment\\\")\\n\\n    return 0\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"CLI implementation for `conda info`.\\n\\nDisplay information about current conda installation.\\n\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport json\\nimport os\\nimport re\\nimport sys\\nfrom argparse import SUPPRESS\\nfrom logging import getLogger\\nfrom os.path import exists, expanduser, isfile, join\\nfrom textwrap import wrap\\nfrom typing import TYPE_CHECKING\\n\\nfrom ..deprecations import deprecated\\n\\nif TYPE_CHECKING:\\n    from argparse import ArgumentParser, Namespace, _SubParsersAction\\n    from typing import Any, Iterable\\n\\n    from ..models.records import PackageRecord\\n\\nlog = getLogger(__name__)\\n\\n\\ndef configure_parser(sub_parsers: _SubParsersAction, **kwargs) -> ArgumentParser:\\n    from ..common.constants import NULL\\n    from .helpers import add_parser_json\\n\\n    summary = \\\"Display information about current conda install.\\\"\\n    description = summary\\n    epilog = \\\"\\\"\\n\\n    p = sub_parsers.add_parser(\\n        \\\"info\\\",\\n        help=summary,\\n        description=description,\\n        epilog=epilog,\\n        **kwargs,\\n    )\\n    add_parser_json(p)\\n    p.add_argument(\\n        \\\"--offline\\\",\\n        action=\\\"store_true\\\",\\n        default=NULL,\\n        help=SUPPRESS,\\n    )\\n    p.add_argument(\\n        \\\"-a\\\",\\n        \\\"--all\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Show all information.\\\",\\n    )\\n    p.add_argument(\\n        \\\"--base\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Display base environment path.\\\",\\n    )\\n    p.add_argument(\\n        \\\"-e\\\",\\n        \\\"--envs\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"List all known conda environments.\\\",\\n    )\\n    p.add_argument(\\n        \\\"-l\\\",\\n        \\\"--license\\\",\\n        action=\\\"store_true\\\",\\n        help=SUPPRESS,\\n    )\\n    p.add_argument(\\n        \\\"-s\\\",\\n        \\\"--system\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"List environment variables.\\\",\\n    )\\n    p.add_argument(\\n        \\\"--root\\\",\\n        action=\\\"store_true\\\",\\n        help=SUPPRESS,\\n        dest=\\\"base\\\",\\n    )\\n    p.add_argument(\\n        \\\"--unsafe-channels\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Display list of channels with tokens exposed.\\\",\\n    )\\n\\n    p.set_defaults(func=\\\"conda.cli.main_info.execute\\\")\\n\\n    return p\\n\\n\\ndef get_user_site() -> list[str]:  # pragma: no cover\\n    \\\"\\\"\\\"\\n    Method used to populate ``site_dirs`` in ``conda info``.\\n\\n    :returns: List of directories.\\n    \\\"\\\"\\\"\\n\\n    from ..common.compat import on_win\\n\\n    site_dirs = []\\n    try:\\n        if not on_win:\\n            if exists(expanduser(\\\"~/.local/lib\\\")):\\n                python_re = re.compile(r\\\"python\\\\d\\\\.\\\\d\\\")\\n                for path in os.listdir(expanduser(\\\"~/.local/lib/\\\")):\\n                    if python_re.match(path):\\n                        site_dirs.append(f\\\"~/.local/lib/{path}\\\")\\n        else:\\n            if \\\"APPDATA\\\" not in os.environ:\\n                return site_dirs\\n            APPDATA = os.environ[\\\"APPDATA\\\"]\\n            if exists(join(APPDATA, \\\"Python\\\")):\\n                site_dirs = [\\n                    join(APPDATA, \\\"Python\\\", i)\\n                    for i in os.listdir(join(APPDATA, \\\"PYTHON\\\"))\\n                ]\\n    except OSError as e:\\n        log.debug(\\\"Error accessing user site directory.\\\\n%r\\\", e)\\n    return site_dirs\\n\\n\\nIGNORE_FIELDS: set[str] = {\\\"files\\\", \\\"auth\\\", \\\"preferred_env\\\", \\\"priority\\\"}\\n\\nSKIP_FIELDS: set[str] = {\\n    *IGNORE_FIELDS,\\n    \\\"name\\\",\\n    \\\"version\\\",\\n    \\\"build\\\",\\n    \\\"build_number\\\",\\n    \\\"channel\\\",\\n    \\\"schannel\\\",\\n    \\\"size\\\",\\n    \\\"fn\\\",\\n    \\\"depends\\\",\\n}\\n\\n\\ndef dump_record(prec: PackageRecord) -> dict[str, Any]:\\n    \\\"\\\"\\\"\\n    Returns a dictionary of key/value pairs from ``prec``.  Keys included in ``IGNORE_FIELDS`` are not returned.\\n\\n    :param prec: A ``PackageRecord`` object.\\n    :returns: A dictionary of elements dumped from ``prec``\\n    \\\"\\\"\\\"\\n    return {k: v for k, v in prec.dump().items() if k not in IGNORE_FIELDS}\\n\\n\\ndef pretty_package(prec: PackageRecord) -> None:\\n    \\\"\\\"\\\"\\n    Pretty prints contents of a ``PackageRecord``\\n\\n    :param prec: A ``PackageRecord``\\n    \\\"\\\"\\\"\\n\\n    from ..utils import human_bytes\\n\\n    pkg = dump_record(prec)\\n    d = {\\n        \\\"file name\\\": prec.fn,\\n        \\\"name\\\": pkg[\\\"name\\\"],\\n        \\\"version\\\": pkg[\\\"version\\\"],\\n        \\\"build string\\\": pkg[\\\"build\\\"],\\n        \\\"build number\\\": pkg[\\\"build_number\\\"],\\n        \\\"channel\\\": str(prec.channel),\\n        \\\"size\\\": human_bytes(pkg[\\\"size\\\"]),\\n    }\\n    for key in sorted(set(pkg.keys()) - SKIP_FIELDS):\\n        d[key] = pkg[key]\\n\\n    print()\\n    header = \\\"{} {} {}\\\".format(d[\\\"name\\\"], d[\\\"version\\\"], d[\\\"build string\\\"])\\n    print(header)\\n    print(\\\"-\\\" * len(header))\\n    for key in d:\\n        print(\\\"%-12s: %s\\\" % (key, d[key]))\\n    print(\\\"dependencies:\\\")\\n    for dep in pkg[\\\"depends\\\"]:\\n        print(f\\\"    {dep}\\\")\\n\\n\\n@deprecated.argument(\\\"24.9\\\", \\\"25.3\\\", \\\"system\\\")\\ndef get_info_dict() -> dict[str, Any]:\\n    \\\"\\\"\\\"\\n    Returns a dictionary of contextual information.\\n\\n    :returns:  Dictionary of conda information to be sent to stdout.\\n    \\\"\\\"\\\"\\n\\n    from .. import CONDA_PACKAGE_ROOT\\n    from .. import __version__ as conda_version\\n    from ..base.context import (\\n        DEFAULT_SOLVER,\\n        context,\\n        env_name,\\n        sys_rc_path,\\n        user_rc_path,\\n    )\\n    from ..common.compat import on_win\\n    from ..common.url import mask_anaconda_token\\n    from ..core.index import _supplement_index_with_system\\n    from ..models.channel import all_channel_urls, offline_keep\\n\\n    try:\\n        from conda_build import __version__ as conda_build_version\\n    except ImportError as err:\\n        # ImportError: conda-build is not installed\\n        log.debug(\\\"Unable to import conda-build: %s\\\", err)\\n        conda_build_version = \\\"not installed\\\"\\n    except Exception as err:\\n        log.error(\\\"Error importing conda-build: %s\\\", err)\\n        conda_build_version = \\\"error\\\"\\n\\n    virtual_pkg_index = {}\\n    _supplement_index_with_system(virtual_pkg_index)\\n    virtual_pkgs = [[p.name, p.version, p.build] for p in virtual_pkg_index.values()]\\n\\n    channels = list(all_channel_urls(context.channels))\\n    if not context.json:\\n        channels = [c + (\\\"\\\" if offline_keep(c) else \\\"  (offline)\\\") for c in channels]\\n    channels = [mask_anaconda_token(c) for c in channels]\\n\\n    netrc_file = os.environ.get(\\\"NETRC\\\")\\n    if not netrc_file:\\n        user_netrc = expanduser(\\\"~/.netrc\\\")\\n        if isfile(user_netrc):\\n            netrc_file = user_netrc\\n\\n    active_prefix_name = env_name(context.active_prefix)\\n\\n    solver = {\\n        \\\"name\\\": context.solver,\\n        \\\"user_agent\\\": context.solver_user_agent(),\\n        \\\"default\\\": context.solver == DEFAULT_SOLVER,\\n    }\\n\\n    info_dict = dict(\\n        platform=context.subdir,\\n        conda_version=conda_version,\\n        conda_env_version=conda_version,\\n        conda_build_version=conda_build_version,\\n        root_prefix=context.root_prefix,\\n        conda_prefix=context.conda_prefix,\\n        av_data_dir=context.av_data_dir,\\n        av_metadata_url_base=context.signing_metadata_url_base,\\n        root_writable=context.root_writable,\\n        pkgs_dirs=context.pkgs_dirs,\\n        envs_dirs=context.envs_dirs,\\n        default_prefix=context.default_prefix,\\n        active_prefix=context.active_prefix,\\n        active_prefix_name=active_prefix_name,\\n        conda_shlvl=context.shlvl,\\n        channels=channels,\\n        user_rc_path=user_rc_path,\\n        rc_path=user_rc_path,\\n        sys_rc_path=sys_rc_path,\\n        # is_foreign=bool(foreign),\\n        offline=context.offline,\\n        envs=[],\\n        python_version=\\\".\\\".join(map(str, sys.version_info)),\\n        requests_version=context.requests_version,\\n        user_agent=context.user_agent,\\n        conda_location=CONDA_PACKAGE_ROOT,\\n        config_files=context.config_files,\\n        netrc_file=netrc_file,\\n        virtual_pkgs=virtual_pkgs,\\n        solver=solver,\\n    )\\n    if on_win:\\n        from ..common._os.windows import is_admin_on_windows\\n\\n        info_dict[\\\"is_windows_admin\\\"] = is_admin_on_windows()\\n    else:\\n        info_dict[\\\"UID\\\"] = os.geteuid()\\n        info_dict[\\\"GID\\\"] = os.getegid()\\n\\n    env_var_keys = {\\n        \\\"CIO_TEST\\\",\\n        \\\"CURL_CA_BUNDLE\\\",\\n        \\\"REQUESTS_CA_BUNDLE\\\",\\n        \\\"SSL_CERT_FILE\\\",\\n        \\\"LD_PRELOAD\\\",\\n    }\\n\\n    # add all relevant env vars, e.g. startswith('CONDA') or endswith('PATH')\\n    env_var_keys.update(v for v in os.environ if v.upper().startswith(\\\"CONDA\\\"))\\n    env_var_keys.update(v for v in os.environ if v.upper().startswith(\\\"PYTHON\\\"))\\n    env_var_keys.update(v for v in os.environ if v.upper().endswith(\\\"PATH\\\"))\\n    env_var_keys.update(v for v in os.environ if v.upper().startswith(\\\"SUDO\\\"))\\n\\n    env_vars = {\\n        ev: os.getenv(ev, os.getenv(ev.lower(), \\\"<not set>\\\")) for ev in env_var_keys\\n    }\\n\\n    proxy_keys = (v for v in os.environ if v.upper().endswith(\\\"PROXY\\\"))\\n    env_vars.update({ev: \\\"<set>\\\" for ev in proxy_keys})\\n\\n    info_dict.update(\\n        {\\n            \\\"sys.version\\\": sys.version,\\n            \\\"sys.prefix\\\": sys.prefix,\\n            \\\"sys.executable\\\": sys.executable,\\n            \\\"site_dirs\\\": get_user_site(),\\n            \\\"env_vars\\\": env_vars,\\n        }\\n    )\\n\\n    return info_dict\\n\\n\\ndef get_env_vars_str(info_dict: dict[str, Any]) -> str:\\n    \\\"\\\"\\\"\\n    Returns a printable string representing environment variables from the dictionary returned by ``get_info_dict``.\\n\\n    :param info_dict:  The returned dictionary from ``get_info_dict()``.\\n    :returns:  String to print.\\n    \\\"\\\"\\\"\\n\\n    builder = []\\n    builder.append(\\\"%23s:\\\" % \\\"environment variables\\\")\\n    env_vars = info_dict.get(\\\"env_vars\\\", {})\\n    for key in sorted(env_vars):\\n        value = wrap(env_vars[key])\\n        first_line = value[0] if len(value) else \\\"\\\"\\n        other_lines = value[1:] if len(value) > 1 else ()\\n        builder.append(\\\"%25s=%s\\\" % (key, first_line))\\n        for val in other_lines:\\n            builder.append(\\\" \\\" * 26 + val)\\n    return \\\"\\\\n\\\".join(builder)\\n\\n\\ndef get_main_info_str(info_dict: dict[str, Any]) -> str:\\n    \\\"\\\"\\\"\\n    Returns a printable string of the contents of ``info_dict``.\\n\\n    :param info_dict:  The output of ``get_info_dict()``.\\n    :returns:  String to print.\\n    \\\"\\\"\\\"\\n\\n    from ..common.compat import on_win\\n\\n    def flatten(lines: Iterable[str]) -> str:\\n        return (\\\"\\\\n\\\" + 26 * \\\" \\\").join(map(str, lines))\\n\\n    def builder():\\n        if info_dict[\\\"active_prefix_name\\\"]:\\n            yield (\\\"active environment\\\", info_dict[\\\"active_prefix_name\\\"])\\n            yield (\\\"active env location\\\", info_dict[\\\"active_prefix\\\"])\\n        else:\\n            yield (\\\"active environment\\\", info_dict[\\\"active_prefix\\\"])\\n\\n        if info_dict[\\\"conda_shlvl\\\"] >= 0:\\n            yield (\\\"shell level\\\", info_dict[\\\"conda_shlvl\\\"])\\n\\n        yield (\\\"user config file\\\", info_dict[\\\"user_rc_path\\\"])\\n        yield (\\\"populated config files\\\", flatten(info_dict[\\\"config_files\\\"]))\\n        yield (\\\"conda version\\\", info_dict[\\\"conda_version\\\"])\\n        yield (\\\"conda-build version\\\", info_dict[\\\"conda_build_version\\\"])\\n        yield (\\\"python version\\\", info_dict[\\\"python_version\\\"])\\n        yield (\\n            \\\"solver\\\",\\n            f\\\"{info_dict['solver']['name']}{' (default)' if info_dict['solver']['default'] else ''}\\\",\\n        )\\n        yield (\\n            \\\"virtual packages\\\",\\n            flatten(\\\"=\\\".join(pkg) for pkg in info_dict[\\\"virtual_pkgs\\\"]),\\n        )\\n        writable = \\\"writable\\\" if info_dict[\\\"root_writable\\\"] else \\\"read only\\\"\\n        yield (\\\"base environment\\\", f\\\"{info_dict['root_prefix']}  ({writable})\\\")\\n        yield (\\\"conda av data dir\\\", info_dict[\\\"av_data_dir\\\"])\\n        yield (\\\"conda av metadata url\\\", info_dict[\\\"av_metadata_url_base\\\"])\\n        yield (\\\"channel URLs\\\", flatten(info_dict[\\\"channels\\\"]))\\n        yield (\\\"package cache\\\", flatten(info_dict[\\\"pkgs_dirs\\\"]))\\n        yield (\\\"envs directories\\\", flatten(info_dict[\\\"envs_dirs\\\"]))\\n        yield (\\\"platform\\\", info_dict[\\\"platform\\\"])\\n        yield (\\\"user-agent\\\", info_dict[\\\"user_agent\\\"])\\n\\n        if on_win:\\n            yield (\\\"administrator\\\", info_dict[\\\"is_windows_admin\\\"])\\n        else:\\n            yield (\\\"UID:GID\\\", f\\\"{info_dict['UID']}:{info_dict['GID']}\\\")\\n\\n        yield (\\\"netrc file\\\", info_dict[\\\"netrc_file\\\"])\\n        yield (\\\"offline mode\\\", info_dict[\\\"offline\\\"])\\n\\n    return \\\"\\\\n\\\".join((\\\"\\\", *(f\\\"{key:>23} : {value}\\\" for key, value in builder()), \\\"\\\"))\\n\\n\\ndef execute(args: Namespace, parser: ArgumentParser) -> int:\\n    \\\"\\\"\\\"\\n    Implements ``conda info`` commands.\\n\\n     * ``conda info``\\n     * ``conda info --base``\\n     * ``conda info <package_spec> ...``\\n     * ``conda info --unsafe-channels``\\n     * ``conda info --envs``\\n     * ``conda info --system``\\n    \\\"\\\"\\\"\\n\\n    from ..base.context import context\\n    from .common import print_envs_list, stdout_json\\n\\n    if args.base:\\n        if context.json:\\n            stdout_json({\\\"root_prefix\\\": context.root_prefix})\\n        else:\\n            print(f\\\"{context.root_prefix}\\\")\\n        return 0\\n\\n    if args.unsafe_channels:\\n        if not context.json:\\n            print(\\\"\\\\n\\\".join(context.channels))\\n        else:\\n            print(json.dumps({\\\"channels\\\": context.channels}))\\n        return 0\\n\\n    options = \\\"envs\\\", \\\"system\\\"\\n\\n    if args.all or context.json:\\n        for option in options:\\n            setattr(args, option, True)\\n    info_dict = get_info_dict()\\n\\n    if (\\n        args.all or all(not getattr(args, opt) for opt in options)\\n    ) and not context.json:\\n        print(get_main_info_str(info_dict) + \\\"\\\\n\\\")\\n\\n    if args.envs:\\n        from ..core.envs_manager import list_all_known_prefixes\\n\\n        info_dict[\\\"envs\\\"] = list_all_known_prefixes()\\n        print_envs_list(info_dict[\\\"envs\\\"], not context.json)\\n\\n    if args.system:\\n        if not context.json:\\n            from .find_commands import find_commands, find_executable\\n\\n            print(f\\\"sys.version: {sys.version[:40]}...\\\")\\n            print(f\\\"sys.prefix: {sys.prefix}\\\")\\n            print(f\\\"sys.executable: {sys.executable}\\\")\\n            print(\\\"conda location: {}\\\".format(info_dict[\\\"conda_location\\\"]))\\n            for cmd in sorted(set(find_commands() + (\\\"build\\\",))):\\n                print(\\\"conda-{}: {}\\\".format(cmd, find_executable(\\\"conda-\\\" + cmd)))\\n            print(\\\"user site dirs: \\\", end=\\\"\\\")\\n            site_dirs = info_dict[\\\"site_dirs\\\"]\\n            if site_dirs:\\n                print(site_dirs[0])\\n            else:\\n                print()\\n            for site_dir in site_dirs[1:]:\\n                print(f\\\"                {site_dir}\\\")\\n            print()\\n\\n            for name, value in sorted(info_dict[\\\"env_vars\\\"].items()):\\n                print(f\\\"{name}: {value}\\\")\\n            print()\\n\\n    if context.json:\\n        stdout_json(info_dict)\\n    return 0\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"CLI implementation for `conda compare`.\\n\\nCompare the packages in an environment with the packages listed in an environment file.\\n\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport logging\\nimport os\\nfrom os.path import abspath, expanduser, expandvars\\nfrom typing import TYPE_CHECKING\\n\\nif TYPE_CHECKING:\\n    from argparse import ArgumentParser, Namespace, _SubParsersAction\\n\\nlog = logging.getLogger(__name__)\\n\\n\\ndef configure_parser(sub_parsers: _SubParsersAction, **kwargs) -> ArgumentParser:\\n    from ..auxlib.ish import dals\\n    from .helpers import add_parser_json, add_parser_prefix\\n\\n    summary = \\\"Compare packages between conda environments.\\\"\\n    description = summary\\n    epilog = dals(\\n        \\\"\\\"\\\"\\n        Examples:\\n\\n        Compare packages in the current environment with respect\\n        to 'environment.yml' located in the current working directory::\\n\\n            conda compare environment.yml\\n\\n        Compare packages installed into the environment 'myenv' with respect\\n        to 'environment.yml' in a different directory::\\n\\n            conda compare -n myenv path/to/file/environment.yml\\n\\n        \\\"\\\"\\\"\\n    )\\n\\n    p = sub_parsers.add_parser(\\n        \\\"compare\\\",\\n        help=summary,\\n        description=description,\\n        epilog=epilog,\\n        **kwargs,\\n    )\\n    add_parser_json(p)\\n    add_parser_prefix(p)\\n    p.add_argument(\\n        \\\"file\\\",\\n        action=\\\"store\\\",\\n        help=\\\"Path to the environment file that is to be compared against.\\\",\\n    )\\n    p.set_defaults(func=\\\"conda.cli.main_compare.execute\\\")\\n\\n    return p\\n\\n\\ndef get_packages(prefix):\\n    from ..core.prefix_data import PrefixData\\n    from ..exceptions import EnvironmentLocationNotFound\\n\\n    if not os.path.isdir(prefix):\\n        raise EnvironmentLocationNotFound(prefix)\\n\\n    return sorted(\\n        PrefixData(prefix, pip_interop_enabled=True).iter_records(),\\n        key=lambda x: x.name,\\n    )\\n\\n\\ndef compare_packages(active_pkgs, specification_pkgs) -> tuple[int, list[str]]:\\n    from ..models.match_spec import MatchSpec\\n\\n    output = []\\n    miss = False\\n    for pkg in specification_pkgs:\\n        pkg_spec = MatchSpec(pkg)\\n        if (name := pkg_spec.name) in active_pkgs:\\n            if not pkg_spec.match(active_pkg := active_pkgs[name]):\\n                miss = True\\n                output.append(\\n                    f\\\"{name} found but mismatch. Specification pkg: {pkg}, \\\"\\n                    f\\\"Running pkg: {active_pkg.name}=={active_pkg.version}={active_pkg.build}\\\"\\n                )\\n        else:\\n            miss = True\\n            output.append(f\\\"{name} not found\\\")\\n    if not miss:\\n        output.append(\\n            \\\"Success. All the packages in the \\\"\\n            \\\"specification file are present in the environment \\\"\\n            \\\"with matching version and build string.\\\"\\n        )\\n    return int(miss), output\\n\\n\\ndef execute(args: Namespace, parser: ArgumentParser) -> int:\\n    from ..base.context import context\\n    from ..env import specs\\n    from ..exceptions import EnvironmentLocationNotFound, SpecNotFound\\n    from ..gateways.connection.session import CONDA_SESSION_SCHEMES\\n    from ..gateways.disk.test import is_conda_environment\\n    from .common import stdout_json\\n\\n    prefix = context.target_prefix\\n    if not is_conda_environment(prefix):\\n        raise EnvironmentLocationNotFound(prefix)\\n\\n    try:\\n        url_scheme = args.file.split(\\\"://\\\", 1)[0]\\n        if url_scheme in CONDA_SESSION_SCHEMES:\\n            filename = args.file\\n        else:\\n            filename = abspath(expanduser(expandvars(args.file)))\\n\\n        spec = specs.detect(name=args.name, filename=filename, directory=os.getcwd())\\n        env = spec.environment\\n\\n        if args.prefix is None and args.name is None:\\n            args.name = env.name\\n    except SpecNotFound:\\n        raise\\n\\n    active_pkgs = {pkg.name: pkg for pkg in get_packages(prefix)}\\n    specification_pkgs = []\\n    if \\\"conda\\\" in env.dependencies:\\n        specification_pkgs = specification_pkgs + env.dependencies[\\\"conda\\\"]\\n    if \\\"pip\\\" in env.dependencies:\\n        specification_pkgs = specification_pkgs + env.dependencies[\\\"pip\\\"]\\n\\n    exitcode, output = compare_packages(active_pkgs, specification_pkgs)\\n\\n    if context.json:\\n        stdout_json(output)\\n    else:\\n        print(\\\"\\\\n\\\".join(map(str, output)))\\n\\n    return exitcode\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"CLI implementation for `conda init`.\\n\\nPrepares the user's profile for running conda, and sets up the conda shell interface.\\n\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nfrom argparse import SUPPRESS\\nfrom logging import getLogger\\nfrom typing import TYPE_CHECKING\\n\\nif TYPE_CHECKING:\\n    from argparse import ArgumentParser, Namespace, _SubParsersAction\\n\\nlog = getLogger(__name__)\\n\\n\\ndef configure_parser(sub_parsers: _SubParsersAction, **kwargs) -> ArgumentParser:\\n    from ..auxlib.ish import dals\\n    from ..base.constants import COMPATIBLE_SHELLS\\n    from ..common.compat import on_win\\n    from ..common.constants import NULL\\n    from .helpers import add_parser_json\\n\\n    summary = \\\"Initialize conda for shell interaction.\\\"\\n    description = summary\\n    epilog = dals(\\n        \\\"\\\"\\\"\\n        Key parts of conda's functionality require that it interact directly with the shell\\n        within which conda is being invoked. The `conda activate` and `conda deactivate` commands\\n        specifically are shell-level commands. That is, they affect the state (e.g. environment\\n        variables) of the shell context being interacted with. Other core commands, like\\n        `conda create` and `conda install`, also necessarily interact with the shell environment.\\n        They're therefore implemented in ways specific to each shell. Each shell must be configured\\n        to make use of them.\\n\\n        This command makes changes to your system that are specific and customized for each shell.\\n        To see the specific files and locations on your system that will be affected before, use\\n        the '--dry-run' flag.  To see the exact changes that are being or will be made to each\\n        location, use the '--verbose' flag.\\n\\n        IMPORTANT: After running `conda init`, most shells will need to be closed and restarted for\\n        changes to take effect.\\n\\n        \\\"\\\"\\\"\\n    )\\n\\n    p = sub_parsers.add_parser(\\n        \\\"init\\\",\\n        help=summary,\\n        description=description,\\n        epilog=epilog,\\n        **kwargs,\\n    )\\n\\n    p.add_argument(\\n        \\\"--dev\\\",\\n        action=\\\"store_true\\\",\\n        help=SUPPRESS,\\n        default=NULL,\\n    )\\n\\n    p.add_argument(\\n        \\\"--all\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Initialize all currently available shells.\\\",\\n        default=NULL,\\n    )\\n\\n    setup_type_group = p.add_argument_group(\\\"setup type\\\")\\n    setup_type_group.add_argument(\\n        \\\"--install\\\",\\n        action=\\\"store_true\\\",\\n        help=SUPPRESS,\\n        default=NULL,\\n    )\\n    setup_type_group.add_argument(\\n        \\\"--user\\\",\\n        action=\\\"store_true\\\",\\n        dest=\\\"user\\\",\\n        help=\\\"Initialize conda for the current user (default).\\\",\\n        default=True,\\n    )\\n    setup_type_group.add_argument(\\n        \\\"--no-user\\\",\\n        action=\\\"store_false\\\",\\n        dest=\\\"user\\\",\\n        help=\\\"Don't initialize conda for the current user.\\\",\\n    )\\n    setup_type_group.add_argument(\\n        \\\"--system\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Initialize conda for all users on the system.\\\",\\n        default=NULL,\\n    )\\n    setup_type_group.add_argument(\\n        \\\"--reverse\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Undo effects of last conda init.\\\",\\n        default=NULL,\\n    )\\n\\n    p.add_argument(\\n        \\\"shells\\\",\\n        nargs=\\\"*\\\",\\n        choices=COMPATIBLE_SHELLS,\\n        metavar=\\\"SHELLS\\\",\\n        help=(\\n            \\\"One or more shells to be initialized. If not given, the default value is 'bash' on \\\"\\n            \\\"unix and 'cmd.exe' & 'powershell' on Windows. Use the '--all' flag to initialize all \\\"\\n            f\\\"shells. Available shells: {sorted(COMPATIBLE_SHELLS)}\\\"\\n        ),\\n        default=[\\\"cmd.exe\\\", \\\"powershell\\\"] if on_win else [\\\"bash\\\"],\\n    )\\n\\n    if on_win:\\n        p.add_argument(\\n            \\\"--anaconda-prompt\\\",\\n            action=\\\"store_true\\\",\\n            help=\\\"Add an 'Anaconda Prompt' icon to your desktop.\\\",\\n            default=NULL,\\n        )\\n\\n    add_parser_json(p)\\n    p.add_argument(\\n        \\\"-d\\\",\\n        \\\"--dry-run\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Only display what would have been done.\\\",\\n    )\\n    p.set_defaults(func=\\\"conda.cli.main_init.execute\\\")\\n\\n    return p\\n\\n\\ndef execute(args: Namespace, parser: ArgumentParser) -> int:\\n    from ..base.constants import COMPATIBLE_SHELLS\\n    from ..base.context import context\\n    from ..common.compat import on_win\\n    from ..core.initialize import initialize, initialize_dev, install\\n    from ..exceptions import ArgumentError\\n\\n    if args.install:\\n        return install(context.conda_prefix)\\n\\n    selected_shells: tuple[str, ...]\\n    if args.all:\\n        selected_shells = COMPATIBLE_SHELLS\\n    else:\\n        selected_shells = tuple(args.shells)\\n\\n    if args.dev:\\n        if len(selected_shells) != 1:\\n            raise ArgumentError(\\\"--dev can only handle one shell at a time right now\\\")\\n        return initialize_dev(selected_shells[0])\\n\\n    else:\\n        for_user = args.user and not args.system\\n        anaconda_prompt = on_win and args.anaconda_prompt\\n        return initialize(\\n            context.conda_prefix,\\n            selected_shells,\\n            for_user,\\n            args.system,\\n            anaconda_prompt,\\n            args.reverse,\\n        )\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Mock CLI implementation for `conda activate`.\\n\\nA mock implementation of the activate shell command for better UX.\\n\\\"\\\"\\\"\\n\\nfrom argparse import SUPPRESS\\n\\nfrom .. import CondaError\\n\\n\\ndef configure_parser(sub_parsers):\\n    p = sub_parsers.add_parser(\\n        \\\"activate\\\",\\n        help=\\\"Activate a conda environment.\\\",\\n    )\\n    p.set_defaults(func=\\\"conda.cli.main_mock_activate.execute\\\")\\n    p.add_argument(\\\"args\\\", action=\\\"store\\\", nargs=\\\"*\\\", help=SUPPRESS)\\n\\n\\ndef execute(args, parser):\\n    raise CondaError(\\\"Run 'conda init' before 'conda activate'\\\")\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Wrapper for running conda CLI commands as a Python API.\\\"\\\"\\\"\\n\\nfrom logging import getLogger\\n\\nfrom ..base.constants import SEARCH_PATH\\nfrom ..base.context import context\\nfrom ..common.io import CaptureTarget, argv, captured\\nfrom ..deprecations import deprecated\\nfrom ..exceptions import conda_exception_handler\\nfrom ..gateways.logging import initialize_std_loggers\\nfrom .conda_argparse import do_call, generate_parser\\n\\ndeprecated.module(\\\"24.3\\\", \\\"24.9\\\", addendum=\\\"Use `conda.testing.conda_cli` instead.\\\")\\n\\nlog = getLogger(__name__)\\n\\n\\nclass Commands:\\n    CLEAN = \\\"clean\\\"\\n    CONFIG = \\\"config\\\"\\n    CREATE = \\\"create\\\"\\n    INFO = \\\"info\\\"\\n    INSTALL = \\\"install\\\"\\n    LIST = \\\"list\\\"\\n    REMOVE = \\\"remove\\\"\\n    SEARCH = \\\"search\\\"\\n    UPDATE = \\\"update\\\"\\n    RUN = \\\"run\\\"\\n    NOTICES = \\\"notices\\\"\\n\\n\\nSTRING = CaptureTarget.STRING\\nSTDOUT = CaptureTarget.STDOUT\\n\\n\\n# Note, a deviated copy of this code appears in tests/test_create.py\\ndef run_command(command, *arguments, **kwargs):\\n    \\\"\\\"\\\"Runs a conda command in-process with a given set of command-line interface arguments.\\n\\n    Differences from the command-line interface:\\n        Always uses --yes flag, thus does not ask for confirmation.\\n\\n    Args:\\n        command: one of the Commands.\\n        *arguments: instructions you would normally pass to the conda command on the command line\\n                    see below for examples. Be very careful to delimit arguments exactly as you\\n                    want them to be delivered. No 'combine then split at spaces' or other\\n                    information destroying processing gets performed on the arguments.\\n        **kwargs: special instructions for programmatic overrides\\n\\n    Keyword Args:\\n        use_exception_handler: defaults to False. False will let the code calling\\n          `run_command` handle all exceptions.  True won't raise when an exception\\n          has occurred, and instead give a non-zero return code\\n        search_path: an optional non-standard search path for configuration information\\n          that overrides the default SEARCH_PATH\\n        stdout: Define capture behavior for stream sys.stdout. Defaults to STRING.\\n          STRING captures as a string.  None leaves stream untouched.\\n          Otherwise redirect to file-like object stdout.\\n        stderr: Define capture behavior for stream sys.stderr. Defaults to STRING.\\n          STRING captures as a string.  None leaves stream untouched.\\n          STDOUT redirects to stdout target and returns None as stderr value.\\n          Otherwise redirect to file-like object stderr.\\n\\n    Returns:\\n        a tuple of stdout, stderr, and return_code.\\n        stdout, stderr are either strings, None or the corresponding file-like function argument.\\n\\n    Examples:\\n        >>> run_command(Commands.CREATE, \\\"-n\\\", \\\"newenv\\\", \\\"python=3\\\", \\\"flask\\\", \\\\\\n                        use_exception_handler=True)\\n        >>> run_command(Commands.CREATE, \\\"-n\\\", \\\"newenv\\\", \\\"python=3\\\", \\\"flask\\\")\\n        >>> run_command(Commands.CREATE, [\\\"-n\\\", \\\"newenv\\\", \\\"python=3\\\", \\\"flask\\\"], search_path=())\\n    \\\"\\\"\\\"\\n    initialize_std_loggers()\\n    use_exception_handler = kwargs.pop(\\\"use_exception_handler\\\", False)\\n    configuration_search_path = kwargs.pop(\\\"search_path\\\", SEARCH_PATH)\\n    stdout = kwargs.pop(\\\"stdout\\\", STRING)\\n    stderr = kwargs.pop(\\\"stderr\\\", STRING)\\n    p = generate_parser()\\n\\n    if arguments and isinstance(arguments[0], list):\\n        arguments = arguments[0]\\n\\n    arguments = list(arguments)\\n    arguments.insert(0, command)\\n\\n    args = p.parse_args(arguments)\\n    args.yes = True  # always skip user confirmation, force setting context.always_yes\\n    context.__init__(\\n        search_path=configuration_search_path,\\n        argparse_args=args,\\n    )\\n\\n    from subprocess import list2cmdline\\n\\n    log.debug(\\\"executing command >>>  conda %s\\\", list2cmdline(arguments))\\n\\n    is_run = arguments[0] == \\\"run\\\"\\n    if is_run:\\n        cap_args = (None, None)\\n    else:\\n        cap_args = (stdout, stderr)\\n    try:\\n        with argv([\\\"python_api\\\", *arguments]), captured(*cap_args) as c:\\n            if use_exception_handler:\\n                result = conda_exception_handler(do_call, args, p)\\n            else:\\n                result = do_call(args, p)\\n        if is_run:\\n            stdout = result.stdout\\n            stderr = result.stderr\\n            result = result.rc\\n        else:\\n            stdout = c.stdout\\n            stderr = c.stderr\\n    except Exception as e:\\n        log.debug(\\\"\\\\n  stdout: %s\\\\n  stderr: %s\\\", stdout, stderr)\\n        e.stdout, e.stderr = stdout, stderr\\n        raise e\\n    return_code = result or 0\\n    log.debug(\\n        \\\"\\\\n  stdout: %s\\\\n  stderr: %s\\\\n  return_code: %s\\\", stdout, stderr, return_code\\n    )\\n    return stdout, stderr, return_code\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"CLI implementation for `conda package`.\\n\\nProvides some low-level tools for creating conda packages.\\n\\\"\\\"\\\"\\n\\nimport hashlib\\nimport json\\nimport os\\nimport re\\nimport tarfile\\nimport tempfile\\nfrom argparse import ArgumentParser, Namespace, _SubParsersAction\\nfrom os.path import abspath, basename, dirname, isdir, isfile, islink, join\\n\\n\\ndef configure_parser(sub_parsers: _SubParsersAction, **kwargs) -> ArgumentParser:\\n    from .helpers import add_parser_prefix\\n\\n    summary = \\\"Create low-level conda packages. (EXPERIMENTAL)\\\"\\n    description = summary\\n    epilog = \\\"\\\"\\n\\n    p = sub_parsers.add_parser(\\n        \\\"package\\\",\\n        help=summary,\\n        description=description,\\n        epilog=epilog,\\n        **kwargs,\\n    )\\n    add_parser_prefix(p)\\n    p.add_argument(\\n        \\\"-w\\\",\\n        \\\"--which\\\",\\n        metavar=\\\"PATH\\\",\\n        nargs=\\\"+\\\",\\n        action=\\\"store\\\",\\n        help=\\\"Given some file's PATH, print which conda package the file came from.\\\",\\n    )\\n    p.add_argument(\\n        \\\"-r\\\",\\n        \\\"--reset\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Remove all untracked files and exit.\\\",\\n    )\\n    p.add_argument(\\n        \\\"-u\\\",\\n        \\\"--untracked\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Display all untracked files and exit.\\\",\\n    )\\n    p.add_argument(\\n        \\\"--pkg-name\\\",\\n        action=\\\"store\\\",\\n        default=\\\"unknown\\\",\\n        help=\\\"Designate package name of the package being created.\\\",\\n    )\\n    p.add_argument(\\n        \\\"--pkg-version\\\",\\n        action=\\\"store\\\",\\n        default=\\\"0.0\\\",\\n        help=\\\"Designate package version of the package being created.\\\",\\n    )\\n    p.add_argument(\\n        \\\"--pkg-build\\\",\\n        action=\\\"store\\\",\\n        default=0,\\n        help=\\\"Designate package build number of the package being created.\\\",\\n    )\\n    p.set_defaults(func=\\\"conda.cli.main_package.execute\\\")\\n\\n    return p\\n\\n\\ndef remove(prefix, files):\\n    \\\"\\\"\\\"Remove files for a given prefix.\\\"\\\"\\\"\\n    dst_dirs = set()\\n    for f in files:\\n        dst = join(prefix, f)\\n        dst_dirs.add(dirname(dst))\\n        os.unlink(dst)\\n\\n    for path in sorted(dst_dirs, key=len, reverse=True):\\n        try:\\n            os.rmdir(path)\\n        except OSError:  # directory might not be empty\\n            pass\\n\\n\\ndef execute(args: Namespace, parser: ArgumentParser) -> int:\\n    from ..base.context import context\\n    from ..misc import untracked\\n\\n    prefix = context.target_prefix\\n\\n    if args.which:\\n        for path in args.which:\\n            for prec in which_package(path):\\n                print(\\\"%-50s  %s\\\" % (path, prec.dist_str()))\\n        return 0\\n\\n    print(\\\"# prefix:\\\", prefix)\\n\\n    if args.reset:\\n        remove(prefix, untracked(prefix))\\n        return 0\\n\\n    if args.untracked:\\n        files = sorted(untracked(prefix))\\n        print(\\\"# untracked files: %d\\\" % len(files))\\n        for fn in files:\\n            print(fn)\\n        return 0\\n\\n    make_tarbz2(\\n        prefix,\\n        name=args.pkg_name.lower(),\\n        version=args.pkg_version,\\n        build_number=int(args.pkg_build),\\n    )\\n    return 0\\n\\n\\ndef get_installed_version(prefix, name):\\n    from ..core.prefix_data import PrefixData\\n\\n    for info in PrefixData(prefix).iter_records():\\n        if info[\\\"name\\\"] == name:\\n            return str(info[\\\"version\\\"])\\n    return None\\n\\n\\ndef create_info(name, version, build_number, requires_py):\\n    from ..base.context import context\\n\\n    d = dict(\\n        name=name,\\n        version=version,\\n        platform=context.platform,\\n        arch=context.arch_name,\\n        build_number=int(build_number),\\n        build=str(build_number),\\n        depends=[],\\n    )\\n    if requires_py:\\n        d[\\\"build\\\"] = (\\\"py%d%d_\\\" % requires_py) + d[\\\"build\\\"]\\n        d[\\\"depends\\\"].append(\\\"python %d.%d*\\\" % requires_py)\\n    return d\\n\\n\\nshebang_pat = re.compile(r\\\"^#!.+$\\\", re.M)\\n\\n\\ndef fix_shebang(tmp_dir, path):\\n    from ..base.constants import PREFIX_PLACEHOLDER\\n\\n    if open(path, \\\"rb\\\").read(2) != \\\"#!\\\":\\n        return False\\n\\n    with open(path) as fi:\\n        data = fi.read()\\n    m = shebang_pat.match(data)\\n    if not (m and \\\"python\\\" in m.group()):\\n        return False\\n\\n    data = shebang_pat.sub(f\\\"#!{PREFIX_PLACEHOLDER}/bin/python\\\", data, count=1)\\n    tmp_path = join(tmp_dir, basename(path))\\n    with open(tmp_path, \\\"w\\\") as fo:\\n        fo.write(data)\\n    os.chmod(tmp_path, int(\\\"755\\\", 8))\\n    return True\\n\\n\\ndef _add_info_dir(t, tmp_dir, files, has_prefix, info):\\n    from ..auxlib.entity import EntityEncoder\\n\\n    info_dir = join(tmp_dir, \\\"info\\\")\\n    os.mkdir(info_dir)\\n    with open(join(info_dir, \\\"files\\\"), \\\"w\\\") as fo:\\n        for f in files:\\n            fo.write(f + \\\"\\\\n\\\")\\n\\n    with open(join(info_dir, \\\"index.json\\\"), \\\"w\\\") as fo:\\n        json.dump(info, fo, indent=2, sort_keys=True, cls=EntityEncoder)\\n\\n    if has_prefix:\\n        with open(join(info_dir, \\\"has_prefix\\\"), \\\"w\\\") as fo:\\n            for f in has_prefix:\\n                fo.write(f + \\\"\\\\n\\\")\\n\\n    for fn in os.listdir(info_dir):\\n        t.add(join(info_dir, fn), \\\"info/\\\" + fn)\\n\\n\\ndef create_conda_pkg(prefix, files, info, tar_path, update_info=None):\\n    \\\"\\\"\\\"Create a conda package and return a list of warnings.\\\"\\\"\\\"\\n    from ..gateways.disk.delete import rmtree\\n\\n    files = sorted(files)\\n    warnings = []\\n    has_prefix = []\\n    tmp_dir = tempfile.mkdtemp()\\n    t = tarfile.open(tar_path, \\\"w:bz2\\\")\\n    h = hashlib.new(\\\"sha1\\\")\\n    for f in files:\\n        assert not (f.startswith(\\\"/\\\") or f.endswith(\\\"/\\\") or \\\"\\\\\\\\\\\" in f or f == \\\"\\\"), f\\n        path = join(prefix, f)\\n        if f.startswith(\\\"bin/\\\") and fix_shebang(tmp_dir, path):\\n            path = join(tmp_dir, basename(path))\\n            has_prefix.append(f)\\n        t.add(path, f)\\n        h.update(f.encode(\\\"utf-8\\\"))\\n        h.update(b\\\"\\\\x00\\\")\\n        if islink(path):\\n            link = os.readlink(path)\\n            if isinstance(link, str):\\n                h.update(bytes(link, \\\"utf-8\\\"))\\n            else:\\n                h.update(link)\\n            if link.startswith(\\\"/\\\"):\\n                warnings.append(f\\\"found symlink to absolute path: {f} -> {link}\\\")\\n        elif isfile(path):\\n            h.update(open(path, \\\"rb\\\").read())\\n            if path.endswith(\\\".egg-link\\\"):\\n                warnings.append(f\\\"found egg link: {f}\\\")\\n\\n    info[\\\"file_hash\\\"] = h.hexdigest()\\n    if update_info:\\n        update_info(info)\\n    _add_info_dir(t, tmp_dir, files, has_prefix, info)\\n    t.close()\\n    rmtree(tmp_dir)\\n    return warnings\\n\\n\\ndef make_tarbz2(prefix, name=\\\"unknown\\\", version=\\\"0.0\\\", build_number=0, files=None):\\n    from ..base.constants import CONDA_PACKAGE_EXTENSION_V1\\n    from ..misc import untracked\\n\\n    if files is None:\\n        files = untracked(prefix)\\n    print(\\\"# files: %d\\\" % len(files))\\n    if len(files) == 0:\\n        print(\\\"# failed: nothing to do\\\")\\n        return None\\n\\n    if any(\\\"/site-packages/\\\" in f for f in files):\\n        python_version = get_installed_version(prefix, \\\"python\\\")\\n        assert python_version is not None\\n        requires_py = tuple(int(x) for x in python_version[:3].split(\\\".\\\"))\\n    else:\\n        requires_py = False\\n\\n    info = create_info(name, version, build_number, requires_py)\\n    tarbz2_fn = (\\\"{name}-{version}-{build}\\\".format(**info)) + CONDA_PACKAGE_EXTENSION_V1\\n    create_conda_pkg(prefix, files, info, tarbz2_fn)\\n    print(\\\"# success\\\")\\n    print(tarbz2_fn)\\n    return tarbz2_fn\\n\\n\\ndef which_package(path):\\n    \\\"\\\"\\\"Return the package containing the path.\\n\\n    Provided the path of a (presumably) conda installed file, iterate over\\n    the conda packages the file came from. Usually the iteration yields\\n    only one package.\\n    \\\"\\\"\\\"\\n    from ..common.path import paths_equal\\n    from ..core.prefix_data import PrefixData\\n\\n    path = abspath(path)\\n    prefix = which_prefix(path)\\n    if prefix is None:\\n        from ..exceptions import CondaVerificationError\\n\\n        raise CondaVerificationError(f\\\"could not determine conda prefix from: {path}\\\")\\n\\n    for prec in PrefixData(prefix).iter_records():\\n        if any(paths_equal(join(prefix, f), path) for f in prec[\\\"files\\\"] or ()):\\n            yield prec\\n\\n\\ndef which_prefix(path):\\n    \\\"\\\"\\\"Return the prefix for the provided path.\\n\\n    Provided the path of a (presumably) conda installed file, return the\\n    environment prefix in which the file in located.\\n    \\\"\\\"\\\"\\n    prefix = abspath(path)\\n    while True:\\n        if isdir(join(prefix, \\\"conda-meta\\\")):\\n            # we found the it, so let's return it\\n            return prefix\\n        if prefix == dirname(prefix):\\n            # we cannot chop off any more directories, so we didn't find it\\n            return None\\n        prefix = dirname(prefix)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"CLI implementation for `conda clean`.\\n\\nRemoves cached package tarballs, index files, package metadata, temporary files, and log files.\\n\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport os\\nimport sys\\nfrom logging import getLogger\\nfrom os.path import isdir, join\\nfrom typing import TYPE_CHECKING\\n\\nif TYPE_CHECKING:\\n    from argparse import ArgumentParser, Namespace, _SubParsersAction\\n    from typing import Any, Iterable\\n\\nlog = getLogger(__name__)\\n\\n\\ndef configure_parser(sub_parsers: _SubParsersAction, **kwargs) -> ArgumentParser:\\n    from ..auxlib.ish import dals\\n    from .actions import ExtendConstAction\\n    from .helpers import add_output_and_prompt_options\\n\\n    summary = \\\"Remove unused packages and caches.\\\"\\n    description = summary\\n    epilog = dals(\\n        \\\"\\\"\\\"\\n        Examples::\\n\\n            conda clean --tarballs\\n        \\\"\\\"\\\"\\n    )\\n\\n    p = sub_parsers.add_parser(\\n        \\\"clean\\\",\\n        help=summary,\\n        description=description,\\n        epilog=epilog,\\n        **kwargs,\\n    )\\n\\n    removal_target_options = p.add_argument_group(\\\"Removal Targets\\\")\\n    removal_target_options.add_argument(\\n        \\\"-a\\\",\\n        \\\"--all\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Remove index cache, lock files, unused cache packages, tarballs, and logfiles.\\\",\\n    )\\n    removal_target_options.add_argument(\\n        \\\"-i\\\",\\n        \\\"--index-cache\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Remove index cache.\\\",\\n    )\\n    removal_target_options.add_argument(\\n        \\\"-p\\\",\\n        \\\"--packages\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Remove unused packages from writable package caches. \\\"\\n        \\\"WARNING: This does not check for packages installed using \\\"\\n        \\\"symlinks back to the package cache.\\\",\\n    )\\n    removal_target_options.add_argument(\\n        \\\"-t\\\",\\n        \\\"--tarballs\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Remove cached package tarballs.\\\",\\n    )\\n    removal_target_options.add_argument(\\n        \\\"-f\\\",\\n        \\\"--force-pkgs-dirs\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Remove *all* writable package caches. This option is not included with the --all \\\"\\n        \\\"flag. WARNING: This will break environments with packages installed using symlinks \\\"\\n        \\\"back to the package cache.\\\",\\n    )\\n    removal_target_options.add_argument(\\n        \\\"-c\\\",  # for tempfile extension (.c~)\\n        \\\"--tempfiles\\\",\\n        const=sys.prefix,\\n        action=ExtendConstAction,\\n        help=(\\n            \\\"Remove temporary files that could not be deleted earlier due to being in-use.  \\\"\\n            \\\"The argument for the --tempfiles flag is a path (or list of paths) to the \\\"\\n            \\\"environment(s) where the tempfiles should be found and removed.\\\"\\n        ),\\n    )\\n    removal_target_options.add_argument(\\n        \\\"-l\\\",\\n        \\\"--logfiles\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Remove log files.\\\",\\n    )\\n\\n    add_output_and_prompt_options(p)\\n\\n    p.set_defaults(func=\\\"conda.cli.main_clean.execute\\\")\\n\\n    return p\\n\\n\\ndef _get_size(*parts: str, warnings: list[str] | None) -> int:\\n    path = join(*parts)\\n    try:\\n        stat = os.lstat(path)\\n    except OSError as e:\\n        if warnings is None:\\n            raise\\n        warnings.append(f\\\"WARNING: {path}: {e}\\\")\\n\\n        # let the user deal with the issue\\n        raise NotImplementedError\\n    else:\\n        # TODO: This doesn't handle packages that have hard links to files within\\n        # themselves, like bin/python3.3 and bin/python3.3m in the Python package\\n        if stat.st_nlink > 1:\\n            raise NotImplementedError\\n\\n        return stat.st_size\\n\\n\\ndef _get_pkgs_dirs(pkg_sizes: dict[str, dict[str, int]]) -> dict[str, tuple[str, ...]]:\\n    return {pkgs_dir: tuple(pkgs) for pkgs_dir, pkgs in pkg_sizes.items()}\\n\\n\\ndef _get_total_size(pkg_sizes: dict[str, dict[str, int]]) -> int:\\n    return sum(sum(pkgs.values()) for pkgs in pkg_sizes.values())\\n\\n\\ndef _rm_rf(*parts: str, quiet: bool, verbose: bool) -> None:\\n    from ..gateways.disk.delete import rm_rf\\n\\n    path = join(*parts)\\n    try:\\n        if rm_rf(path):\\n            if not quiet and verbose:\\n                print(f\\\"Removed {path}\\\")\\n        elif not quiet:\\n            print(f\\\"WARNING: cannot remove, file permissions: {path}\\\")\\n    except OSError as e:\\n        if not quiet:\\n            print(f\\\"WARNING: cannot remove, file permissions: {path}\\\\n{e!r}\\\")\\n        else:\\n            log.info(\\\"%r\\\", e)\\n\\n\\ndef find_tarballs() -> dict[str, Any]:\\n    from ..base.constants import CONDA_PACKAGE_EXTENSIONS, CONDA_PACKAGE_PARTS\\n\\n    warnings: list[str] = []\\n    pkg_sizes: dict[str, dict[str, int]] = {}\\n    for pkgs_dir in find_pkgs_dirs():\\n        # tarballs are files in pkgs_dir\\n        _, _, tars = next(os.walk(pkgs_dir))\\n        for tar in tars:\\n            # tarballs also end in .tar.bz2, .conda, .tar.bz2.part, or .conda.part\\n            if not tar.endswith((*CONDA_PACKAGE_EXTENSIONS, *CONDA_PACKAGE_PARTS)):\\n                continue\\n\\n            # get size\\n            try:\\n                size = _get_size(pkgs_dir, tar, warnings=warnings)\\n            except NotImplementedError:\\n                pass\\n            else:\\n                pkg_sizes.setdefault(pkgs_dir, {})[tar] = size\\n\\n    return {\\n        \\\"warnings\\\": warnings,\\n        \\\"pkg_sizes\\\": pkg_sizes,\\n        \\\"pkgs_dirs\\\": _get_pkgs_dirs(pkg_sizes),\\n        \\\"total_size\\\": _get_total_size(pkg_sizes),\\n    }\\n\\n\\ndef find_pkgs() -> dict[str, Any]:\\n    warnings: list[str] = []\\n    pkg_sizes: dict[str, dict[str, int]] = {}\\n    for pkgs_dir in find_pkgs_dirs():\\n        # pkgs are directories in pkgs_dir\\n        _, pkgs, _ = next(os.walk(pkgs_dir))\\n        for pkg in pkgs:\\n            # pkgs also have an info directory\\n            if not isdir(join(pkgs_dir, pkg, \\\"info\\\")):\\n                continue\\n\\n            # get size\\n            try:\\n                size = sum(\\n                    _get_size(root, file, warnings=warnings)\\n                    for root, _, files in os.walk(join(pkgs_dir, pkg))\\n                    for file in files\\n                )\\n            except NotImplementedError:\\n                pass\\n            else:\\n                pkg_sizes.setdefault(pkgs_dir, {})[pkg] = size\\n\\n    return {\\n        \\\"warnings\\\": warnings,\\n        \\\"pkg_sizes\\\": pkg_sizes,\\n        \\\"pkgs_dirs\\\": _get_pkgs_dirs(pkg_sizes),\\n        \\\"total_size\\\": _get_total_size(pkg_sizes),\\n    }\\n\\n\\ndef rm_pkgs(\\n    pkgs_dirs: dict[str, tuple[str]],\\n    warnings: list[str],\\n    total_size: int,\\n    pkg_sizes: dict[str, dict[str, int]],\\n    *,\\n    quiet: bool,\\n    verbose: bool,\\n    dry_run: bool,\\n    name: str,\\n) -> None:\\n    from ..base.context import context\\n    from ..utils import human_bytes\\n    from .common import confirm_yn\\n\\n    if not quiet and warnings:\\n        for warning in warnings:\\n            print(warning)\\n\\n    if not any(pkgs for pkgs in pkg_sizes.values()):\\n        if not quiet:\\n            print(f\\\"There are no unused {name} to remove.\\\")\\n        return\\n\\n    if not quiet:\\n        if verbose:\\n            print(f\\\"Will remove the following {name}:\\\")\\n            for pkgs_dir, pkgs in pkg_sizes.items():\\n                print(f\\\"  {pkgs_dir}\\\")\\n                print(f\\\"  {'-' * len(pkgs_dir)}\\\")\\n                for pkg, size in pkgs.items():\\n                    print(f\\\"  - {pkg:<40} {human_bytes(size):>10}\\\")\\n                print()\\n            print(\\\"-\\\" * 17)\\n            print(f\\\"Total: {human_bytes(total_size):>10}\\\")\\n            print()\\n        else:\\n            count = sum(len(pkgs) for pkgs in pkg_sizes.values())\\n            print(f\\\"Will remove {count} ({human_bytes(total_size)}) {name}.\\\")\\n\\n    if dry_run:\\n        return\\n    if not context.json or not context.always_yes:\\n        confirm_yn()\\n\\n    for pkgs_dir, pkgs in pkg_sizes.items():\\n        for pkg in pkgs:\\n            _rm_rf(pkgs_dir, pkg, quiet=quiet, verbose=verbose)\\n\\n\\ndef find_index_cache() -> list[str]:\\n    files = []\\n    for pkgs_dir in find_pkgs_dirs():\\n        # caches are directories in pkgs_dir\\n        path = join(pkgs_dir, \\\"cache\\\")\\n        if isdir(path):\\n            files.append(path)\\n    return files\\n\\n\\ndef find_pkgs_dirs() -> list[str]:\\n    from ..core.package_cache_data import PackageCacheData\\n\\n    return [\\n        pc.pkgs_dir for pc in PackageCacheData.writable_caches() if isdir(pc.pkgs_dir)\\n    ]\\n\\n\\ndef find_tempfiles(paths: Iterable[str]) -> list[str]:\\n    from ..base.constants import CONDA_TEMP_EXTENSIONS\\n\\n    tempfiles = []\\n    for path in sorted(set(paths or [sys.prefix])):\\n        # tempfiles are files in path\\n        for root, _, files in os.walk(path):\\n            for file in files:\\n                # tempfiles also end in .c~ or .trash\\n                if not file.endswith(CONDA_TEMP_EXTENSIONS):\\n                    continue\\n\\n                tempfiles.append(join(root, file))\\n\\n    return tempfiles\\n\\n\\ndef find_logfiles() -> list[str]:\\n    from ..base.constants import CONDA_LOGS_DIR\\n\\n    files = []\\n    for pkgs_dir in find_pkgs_dirs():\\n        # .logs are directories in pkgs_dir\\n        path = join(pkgs_dir, CONDA_LOGS_DIR)\\n        if not isdir(path):\\n            continue\\n\\n        try:\\n            # logfiles are files in .logs\\n            _, _, logs = next(os.walk(path))\\n            files.extend([join(path, log) for log in logs])\\n        except StopIteration:\\n            # StopIteration: .logs is empty\\n            pass\\n\\n    return files\\n\\n\\ndef rm_items(\\n    items: list[str],\\n    *,\\n    quiet: bool,\\n    verbose: bool,\\n    dry_run: bool,\\n    name: str,\\n) -> None:\\n    from ..base.context import context\\n    from .common import confirm_yn\\n\\n    if not items:\\n        if not quiet:\\n            print(f\\\"There are no {name} to remove.\\\")\\n        return\\n\\n    if not quiet:\\n        if verbose:\\n            print(f\\\"Will remove the following {name}:\\\")\\n            for item in items:\\n                print(f\\\"  - {item}\\\")\\n            print()\\n        else:\\n            print(f\\\"Will remove {len(items)} {name}.\\\")\\n\\n    if dry_run:\\n        return\\n    if not context.json or not context.always_yes:\\n        confirm_yn()\\n\\n    for item in items:\\n        _rm_rf(item, quiet=quiet, verbose=verbose)\\n\\n\\ndef _execute(args, parser):\\n    from ..base.context import context\\n\\n    json_result = {\\\"success\\\": True}\\n    kwargs = {\\n        \\\"quiet\\\": context.json or context.quiet,\\n        \\\"verbose\\\": context.verbose,\\n        \\\"dry_run\\\": context.dry_run,\\n    }\\n\\n    if args.force_pkgs_dirs:\\n        json_result[\\\"pkgs_dirs\\\"] = pkgs_dirs = find_pkgs_dirs()\\n        rm_items(pkgs_dirs, **kwargs, name=\\\"package cache(s)\\\")\\n\\n        # we return here because all other clean operations target individual parts of\\n        # package caches\\n        return json_result\\n\\n    if not (\\n        args.all\\n        or args.tarballs\\n        or args.index_cache\\n        or args.packages\\n        or args.tempfiles\\n        or args.logfiles\\n    ):\\n        from ..exceptions import ArgumentError\\n\\n        raise ArgumentError(\\n            \\\"At least one removal target must be given. See 'conda clean --help'.\\\"\\n        )\\n\\n    if args.tarballs or args.all:\\n        json_result[\\\"tarballs\\\"] = tars = find_tarballs()\\n        rm_pkgs(**tars, **kwargs, name=\\\"tarball(s)\\\")\\n\\n    if args.index_cache or args.all:\\n        cache = find_index_cache()\\n        json_result[\\\"index_cache\\\"] = {\\\"files\\\": cache}\\n        rm_items(cache, **kwargs, name=\\\"index cache(s)\\\")\\n\\n    if args.packages or args.all:\\n        json_result[\\\"packages\\\"] = pkgs = find_pkgs()\\n        rm_pkgs(**pkgs, **kwargs, name=\\\"package(s)\\\")\\n\\n    if args.tempfiles or args.all:\\n        json_result[\\\"tempfiles\\\"] = tmps = find_tempfiles(args.tempfiles)\\n        rm_items(tmps, **kwargs, name=\\\"tempfile(s)\\\")\\n\\n    if args.logfiles or args.all:\\n        json_result[\\\"logfiles\\\"] = logs = find_logfiles()\\n        rm_items(logs, **kwargs, name=\\\"logfile(s)\\\")\\n\\n    return json_result\\n\\n\\ndef execute(args: Namespace, parser: ArgumentParser) -> int:\\n    from ..base.context import context\\n    from .common import stdout_json\\n\\n    json_result = _execute(args, parser)\\n    if context.json:\\n        stdout_json(json_result)\\n    if args.dry_run:\\n        from ..exceptions import DryRunExit\\n\\n        raise DryRunExit\\n    return 0\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Conda command line interface parsers.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport argparse\\nimport os\\nimport sys\\nfrom argparse import (\\n    SUPPRESS,\\n    RawDescriptionHelpFormatter,\\n)\\nfrom argparse import ArgumentParser as ArgumentParserBase\\nfrom importlib import import_module\\nfrom logging import getLogger\\nfrom subprocess import Popen\\n\\nfrom .. import __version__\\nfrom ..auxlib.compat import isiterable\\nfrom ..auxlib.ish import dals\\nfrom ..base.context import context, sys_rc_path, user_rc_path\\nfrom ..common.compat import on_win\\nfrom ..common.constants import NULL\\nfrom ..deprecations import deprecated\\nfrom .actions import ExtendConstAction, NullCountAction  # noqa: F401\\nfrom .find_commands import find_commands, find_executable\\nfrom .helpers import (  # noqa: F401\\n    add_output_and_prompt_options,\\n    add_parser_channels,\\n    add_parser_create_install_update,\\n    add_parser_default_packages,\\n    add_parser_help,\\n    add_parser_json,\\n    add_parser_known,\\n    add_parser_networking,\\n    add_parser_package_install_options,\\n    add_parser_platform,\\n    add_parser_prefix,\\n    add_parser_prune,\\n    add_parser_pscheck,\\n    add_parser_show_channel_urls,\\n    add_parser_solver,\\n    add_parser_solver_mode,\\n    add_parser_update_modifiers,\\n    add_parser_verbose,\\n)\\nfrom .main_clean import configure_parser as configure_parser_clean\\nfrom .main_compare import configure_parser as configure_parser_compare\\nfrom .main_config import configure_parser as configure_parser_config\\nfrom .main_create import configure_parser as configure_parser_create\\nfrom .main_env import configure_parser as configure_parser_env\\nfrom .main_export import configure_parser as configure_parser_export\\nfrom .main_info import configure_parser as configure_parser_info\\nfrom .main_init import configure_parser as configure_parser_init\\nfrom .main_install import configure_parser as configure_parser_install\\nfrom .main_list import configure_parser as configure_parser_list\\nfrom .main_mock_activate import configure_parser as configure_parser_mock_activate\\nfrom .main_mock_deactivate import configure_parser as configure_parser_mock_deactivate\\nfrom .main_notices import configure_parser as configure_parser_notices\\nfrom .main_package import configure_parser as configure_parser_package\\nfrom .main_remove import configure_parser as configure_parser_remove\\nfrom .main_rename import configure_parser as configure_parser_rename\\nfrom .main_run import configure_parser as configure_parser_run\\nfrom .main_search import configure_parser as configure_parser_search\\nfrom .main_update import configure_parser as configure_parser_update\\n\\nlog = getLogger(__name__)\\n\\nescaped_user_rc_path = user_rc_path.replace(\\\"%\\\", \\\"%%\\\")\\nescaped_sys_rc_path = sys_rc_path.replace(\\\"%\\\", \\\"%%\\\")\\n\\n#: List of built-in commands; these cannot be overridden by plugin subcommands\\nBUILTIN_COMMANDS = {\\n    \\\"activate\\\",  # Mock entry for shell command\\n    \\\"clean\\\",\\n    \\\"compare\\\",\\n    \\\"config\\\",\\n    \\\"create\\\",\\n    \\\"deactivate\\\",  # Mock entry for shell command\\n    \\\"export\\\",\\n    \\\"info\\\",\\n    \\\"init\\\",\\n    \\\"install\\\",\\n    \\\"list\\\",\\n    \\\"package\\\",\\n    \\\"remove\\\",\\n    \\\"rename\\\",\\n    \\\"run\\\",\\n    \\\"search\\\",\\n    \\\"update\\\",\\n    \\\"upgrade\\\",\\n    \\\"notices\\\",\\n}\\n\\n\\ndef generate_pre_parser(**kwargs) -> ArgumentParser:\\n    pre_parser = ArgumentParser(\\n        description=\\\"conda is a tool for managing and deploying applications,\\\"\\n        \\\" environments and packages.\\\",\\n        **kwargs,\\n    )\\n\\n    add_parser_verbose(pre_parser)\\n    pre_parser.add_argument(\\n        \\\"--json\\\",\\n        action=\\\"store_true\\\",\\n        default=NULL,\\n        help=SUPPRESS,\\n    )\\n    pre_parser.add_argument(\\n        \\\"--no-plugins\\\",\\n        action=\\\"store_true\\\",\\n        default=NULL,\\n        help=\\\"Disable all plugins that are not built into conda.\\\",\\n    )\\n\\n    return pre_parser\\n\\n\\ndef generate_parser(**kwargs) -> ArgumentParser:\\n    parser = generate_pre_parser(**kwargs)\\n\\n    parser.add_argument(\\n        \\\"-V\\\",\\n        \\\"--version\\\",\\n        action=\\\"version\\\",\\n        version=f\\\"conda {__version__}\\\",\\n        help=\\\"Show the conda version number and exit.\\\",\\n    )\\n\\n    sub_parsers = parser.add_subparsers(\\n        metavar=\\\"COMMAND\\\",\\n        title=\\\"commands\\\",\\n        description=\\\"The following built-in and plugins subcommands are available.\\\",\\n        dest=\\\"cmd\\\",\\n        action=_GreedySubParsersAction,\\n        required=True,\\n    )\\n\\n    configure_parser_mock_activate(sub_parsers)\\n    configure_parser_mock_deactivate(sub_parsers)\\n    configure_parser_clean(sub_parsers)\\n    configure_parser_compare(sub_parsers)\\n    configure_parser_config(sub_parsers)\\n    configure_parser_create(sub_parsers)\\n    configure_parser_env(sub_parsers)\\n    configure_parser_export(sub_parsers)\\n    configure_parser_info(sub_parsers)\\n    configure_parser_init(sub_parsers)\\n    configure_parser_install(sub_parsers)\\n    configure_parser_list(sub_parsers)\\n    configure_parser_notices(sub_parsers)\\n    configure_parser_package(sub_parsers)\\n    configure_parser_remove(sub_parsers, aliases=[\\\"uninstall\\\"])\\n    configure_parser_rename(sub_parsers)\\n    configure_parser_run(sub_parsers)\\n    configure_parser_search(sub_parsers)\\n    configure_parser_update(sub_parsers, aliases=[\\\"upgrade\\\"])\\n    configure_parser_plugins(sub_parsers)\\n\\n    return parser\\n\\n\\ndef do_call(args: argparse.Namespace, parser: ArgumentParser):\\n    \\\"\\\"\\\"\\n    Serves as the primary entry point for commands referred to in this file and for\\n    all registered plugin subcommands.\\n    \\\"\\\"\\\"\\n    # let's see if during the parsing phase it was discovered that the\\n    # called command was in fact a plugin subcommand\\n    if plugin_subcommand := getattr(args, \\\"_plugin_subcommand\\\", None):\\n        # pass on the rest of the plugin specific args or fall back to\\n        # the whole discovered arguments\\n        context.plugin_manager.invoke_pre_commands(plugin_subcommand.name)\\n        result = plugin_subcommand.action(getattr(args, \\\"_args\\\", args))\\n        context.plugin_manager.invoke_post_commands(plugin_subcommand.name)\\n    elif name := getattr(args, \\\"_executable\\\", None):\\n        # run the subcommand from executables; legacy path\\n        deprecated.topic(\\n            \\\"23.3\\\",\\n            \\\"25.3\\\",\\n            topic=\\\"Loading conda subcommands via executables\\\",\\n            addendum=\\\"Use the plugin system instead.\\\",\\n        )\\n        executable = find_executable(f\\\"conda-{name}\\\")\\n        if not executable:\\n            from ..exceptions import CommandNotFoundError\\n\\n            raise CommandNotFoundError(name)\\n        return _exec([executable, *args._args], os.environ)\\n    else:\\n        # let's call the subcommand the old-fashioned way via the assigned func..\\n        module_name, func_name = args.func.rsplit(\\\".\\\", 1)\\n        # func_name should always be 'execute'\\n        module = import_module(module_name)\\n        command = module_name.split(\\\".\\\")[-1].replace(\\\"main_\\\", \\\"\\\")\\n\\n        context.plugin_manager.invoke_pre_commands(command)\\n        result = getattr(module, func_name)(args, parser)\\n        context.plugin_manager.invoke_post_commands(command)\\n    return result\\n\\n\\ndef find_builtin_commands(parser):\\n    # ArgumentParser doesn't have an API for getting back what subparsers\\n    # exist, so we need to use internal properties to do so.\\n    return tuple(parser._subparsers._group_actions[0].choices.keys())\\n\\n\\nclass ArgumentParser(ArgumentParserBase):\\n    def __init__(self, *args, add_help=True, **kwargs):\\n        kwargs.setdefault(\\\"formatter_class\\\", RawDescriptionHelpFormatter)\\n        super().__init__(*args, add_help=False, **kwargs)\\n\\n        if add_help:\\n            add_parser_help(self)\\n\\n    def _check_value(self, action, value):\\n        # extend to properly handle when we accept multiple choices and the default is a list\\n        if action.choices is not None and isiterable(value):\\n            for element in value:\\n                super()._check_value(action, element)\\n        else:\\n            super()._check_value(action, value)\\n\\n    def parse_args(self, *args, override_args=None, **kwargs):\\n        parsed_args = super().parse_args(*args, **kwargs)\\n        for name, value in (override_args or {}).items():\\n            if value is not NULL and getattr(parsed_args, name, NULL) is NULL:\\n                setattr(parsed_args, name, value)\\n        return parsed_args\\n\\n\\nclass _GreedySubParsersAction(argparse._SubParsersAction):\\n    \\\"\\\"\\\"A custom subparser action to conditionally act as a greedy consumer.\\n\\n    This is a workaround since argparse.REMAINDER does not work as expected,\\n    see https://github.com/python/cpython/issues/61252.\\n    \\\"\\\"\\\"\\n\\n    def __call__(self, parser, namespace, values, option_string=None):\\n        super().__call__(parser, namespace, values, option_string)\\n\\n        parser = self._name_parser_map[values[0]]\\n\\n        # if the parser has a greedy=True attribute we want to consume all arguments\\n        # i.e. all unknown args should be passed to the subcommand as is\\n        if getattr(parser, \\\"greedy\\\", False):\\n            try:\\n                unknown = getattr(namespace, argparse._UNRECOGNIZED_ARGS_ATTR)\\n                delattr(namespace, argparse._UNRECOGNIZED_ARGS_ATTR)\\n            except AttributeError:\\n                unknown = ()\\n\\n            # underscore prefixed indicating this is not a normal argparse argument\\n            namespace._args = tuple(unknown)\\n\\n    def _get_subactions(self):\\n        \\\"\\\"\\\"Sort actions for subcommands to appear alphabetically in help blurb.\\\"\\\"\\\"\\n        return sorted(self._choices_actions, key=lambda action: action.dest)\\n\\n\\ndef _exec(executable_args, env_vars):\\n    return (_exec_win if on_win else _exec_unix)(executable_args, env_vars)\\n\\n\\ndef _exec_win(executable_args, env_vars):\\n    p = Popen(executable_args, env=env_vars)\\n    try:\\n        p.communicate()\\n    except KeyboardInterrupt:\\n        p.wait()\\n    finally:\\n        sys.exit(p.returncode)\\n\\n\\ndef _exec_unix(executable_args, env_vars):\\n    os.execvpe(executable_args[0], executable_args, env_vars)\\n\\n\\ndef configure_parser_plugins(sub_parsers) -> None:\\n    \\\"\\\"\\\"\\n    For each of the provided plugin-based subcommands, we'll create\\n    a new subparser for an improved help printout and calling the\\n    :meth:`~conda.plugins.types.CondaSubcommand.configure_parser`\\n    with the newly created subcommand specific argument parser.\\n    \\\"\\\"\\\"\\n    plugin_subcommands = context.plugin_manager.get_subcommands()\\n    for name, plugin_subcommand in plugin_subcommands.items():\\n        # if the name of the plugin-based subcommand overlaps a built-in\\n        # subcommand, we print an error\\n        if name in BUILTIN_COMMANDS:\\n            log.error(\\n                dals(\\n                    f\\\"\\\"\\\"\\n                    The plugin '{name}' is trying to override the built-in command\\n                    with the same name, which is not allowed.\\n\\n                    Please uninstall the plugin to stop seeing this error message.\\n                    \\\"\\\"\\\"\\n                )\\n            )\\n            continue\\n\\n        parser = sub_parsers.add_parser(\\n            name,\\n            description=plugin_subcommand.summary,\\n            help=plugin_subcommand.summary,\\n            add_help=False,  # defer to subcommand's help processing\\n        )\\n\\n        # case 1: plugin extends the parser\\n        if plugin_subcommand.configure_parser:\\n            plugin_subcommand.configure_parser(parser)\\n\\n            # attempt to add standard help processing, will fail if plugin defines their own\\n            try:\\n                add_parser_help(parser)\\n            except argparse.ArgumentError:\\n                pass\\n\\n        # case 2: plugin has their own parser, see _GreedySubParsersAction\\n        else:\\n            parser.greedy = True\\n\\n        # underscore prefixed indicating this is not a normal argparse argument\\n        parser.set_defaults(_plugin_subcommand=plugin_subcommand)\\n\\n    if context.no_plugins:\\n        return\\n\\n    # Ignore the legacy `conda-env` entrypoints since we already register `env`\\n    # as a subcommand in `generate_parser` above\\n    legacy = set(find_commands()).difference(plugin_subcommands) - {\\\"env\\\"}\\n\\n    for name in legacy:\\n        # if the name of the plugin-based subcommand overlaps a built-in\\n        # subcommand, we print an error\\n        if name in BUILTIN_COMMANDS:\\n            log.error(\\n                dals(\\n                    f\\\"\\\"\\\"\\n                    The (legacy) plugin '{name}' is trying to override the built-in command\\n                    with the same name, which is not allowed.\\n\\n                    Please uninstall the plugin to stop seeing this error message.\\n                    \\\"\\\"\\\"\\n                )\\n            )\\n            continue\\n\\n        parser = sub_parsers.add_parser(\\n            name,\\n            description=f\\\"See `conda {name} --help`.\\\",\\n            help=f\\\"See `conda {name} --help`.\\\",\\n            add_help=False,  # defer to subcommand's help processing\\n        )\\n\\n        # case 3: legacy plugins are always greedy\\n        parser.greedy = True\\n\\n        parser.set_defaults(_executable=name)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"CLI implementation for `conda update`.\\n\\nUpdates the specified packages in an existing environment.\\n\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport sys\\nfrom typing import TYPE_CHECKING\\n\\nfrom ..notices import notices\\n\\nif TYPE_CHECKING:\\n    from argparse import ArgumentParser, Namespace, _SubParsersAction\\n\\n\\ndef configure_parser(sub_parsers: _SubParsersAction, **kwargs) -> ArgumentParser:\\n    from ..auxlib.ish import dals\\n    from ..common.constants import NULL\\n    from .helpers import (\\n        add_parser_create_install_update,\\n        add_parser_prune,\\n        add_parser_solver,\\n        add_parser_update_modifiers,\\n    )\\n\\n    summary = \\\"Update conda packages to the latest compatible version.\\\"\\n    description = dals(\\n        f\\\"\\\"\\\"\\n        {summary}\\n\\n        This command accepts a list of package names and updates them to the latest\\n        versions that are compatible with all other packages in the environment.\\n\\n        Conda attempts to install the newest versions of the requested packages. To\\n        accomplish this, it may update some packages that are already installed, or\\n        install additional packages. To prevent existing packages from updating,\\n        use the --no-update-deps option. This may force conda to install older\\n        versions of the requested packages, and it does not prevent additional\\n        dependency packages from being installed.\\n        \\\"\\\"\\\"\\n    )\\n    epilog = dals(\\n        \\\"\\\"\\\"\\n        Examples:\\n\\n            conda update -n myenv scipy\\n\\n        \\\"\\\"\\\"\\n    )\\n\\n    p = sub_parsers.add_parser(\\n        \\\"update\\\",\\n        help=summary,\\n        description=description,\\n        epilog=epilog,\\n        **kwargs,\\n    )\\n    solver_mode_options, package_install_options, _ = add_parser_create_install_update(\\n        p\\n    )\\n\\n    add_parser_prune(solver_mode_options)\\n    add_parser_solver(solver_mode_options)\\n    solver_mode_options.add_argument(\\n        \\\"--force-reinstall\\\",\\n        action=\\\"store_true\\\",\\n        default=NULL,\\n        help=\\\"Ensure that any user-requested package for the current operation is uninstalled and \\\"\\n        \\\"reinstalled, even if that package already exists in the environment.\\\",\\n    )\\n    add_parser_update_modifiers(solver_mode_options)\\n\\n    package_install_options.add_argument(\\n        \\\"--clobber\\\",\\n        action=\\\"store_true\\\",\\n        default=NULL,\\n        help=\\\"Allow clobbering of overlapping file paths within packages, \\\"\\n        \\\"and suppress related warnings.\\\",\\n    )\\n    p.set_defaults(func=\\\"conda.cli.main_update.execute\\\")\\n\\n    return p\\n\\n\\n@notices\\ndef execute(args: Namespace, parser: ArgumentParser) -> int:\\n    from ..base.context import context\\n    from .install import install\\n\\n    if context.force:\\n        print(\\n            \\\"\\\\n\\\\n\\\"\\n            \\\"WARNING: The --force flag will be removed in a future conda release.\\\\n\\\"\\n            \\\"         See 'conda update --help' for details about the --force-reinstall\\\\n\\\"\\n            \\\"         and --clobber flags.\\\\n\\\"\\n            \\\"\\\\n\\\",\\n            file=sys.stderr,\\n        )\\n\\n    install(args, parser, \\\"update\\\")\\n    return 0\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"CLI implementation for `conda remove`.\\n\\nRemoves the specified packages from an existing environment.\\n\\\"\\\"\\\"\\n\\nimport logging\\nfrom argparse import ArgumentParser, Namespace, _SubParsersAction\\nfrom os.path import isfile, join\\n\\nfrom .common import confirm_yn\\n\\nlog = logging.getLogger(__name__)\\n\\n\\ndef configure_parser(sub_parsers: _SubParsersAction, **kwargs) -> ArgumentParser:\\n    from ..auxlib.ish import dals\\n    from ..common.constants import NULL\\n    from .actions import NullCountAction\\n    from .helpers import (\\n        add_output_and_prompt_options,\\n        add_parser_channels,\\n        add_parser_networking,\\n        add_parser_prefix,\\n        add_parser_prune,\\n        add_parser_pscheck,\\n        add_parser_solver,\\n    )\\n\\n    summary = \\\"Remove a list of packages from a specified conda environment. \\\"\\n    description = dals(\\n        f\\\"\\\"\\\"\\n        {summary}\\n\\n        Use `--all` flag to remove all packages and the environment itself.\\n\\n        This command will also remove any package that depends on any of the\\n        specified packages as well---unless a replacement can be found without\\n        that dependency. If you wish to skip this dependency checking and remove\\n        just the requested packages, add the '--force' option. Note however that\\n        this may result in a broken environment, so use this with caution.\\n        \\\"\\\"\\\"\\n    )\\n    epilog = dals(\\n        \\\"\\\"\\\"\\n        Examples:\\n\\n        Remove the package 'scipy' from the currently-active environment::\\n\\n            conda remove scipy\\n\\n        Remove a list of packages from an environment 'myenv'::\\n\\n            conda remove -n myenv scipy curl wheel\\n\\n        Remove all packages from environment `myenv` and the environment itself::\\n\\n            conda remove -n myenv --all\\n\\n        Remove all packages from the environment `myenv` but retain the environment::\\n\\n            conda remove -n myenv --all --keep-env\\n\\n        \\\"\\\"\\\"\\n    )\\n\\n    p = sub_parsers.add_parser(\\n        \\\"remove\\\",\\n        help=summary,\\n        description=description,\\n        epilog=epilog,\\n        **kwargs,\\n    )\\n    add_parser_pscheck(p)\\n\\n    add_parser_prefix(p)\\n    add_parser_channels(p)\\n\\n    solver_mode_options = p.add_argument_group(\\\"Solver Mode Modifiers\\\")\\n    solver_mode_options.add_argument(\\n        \\\"--features\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Remove features (instead of packages).\\\",\\n    )\\n    solver_mode_options.add_argument(\\n        \\\"--force-remove\\\",\\n        \\\"--force\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Forces removal of a package without removing packages that depend on it. \\\"\\n        \\\"Using this option will usually leave your environment in a broken and \\\"\\n        \\\"inconsistent state.\\\",\\n        dest=\\\"force_remove\\\",\\n    )\\n    solver_mode_options.add_argument(\\n        \\\"--no-pin\\\",\\n        action=\\\"store_true\\\",\\n        dest=\\\"ignore_pinned\\\",\\n        default=NULL,\\n        help=\\\"Ignore pinned package(s) that apply to the current operation. \\\"\\n        \\\"These pinned packages might come from a .condarc file or a file in \\\"\\n        \\\"<TARGET_ENVIRONMENT>/conda-meta/pinned.\\\",\\n    )\\n    add_parser_prune(solver_mode_options)\\n    add_parser_solver(solver_mode_options)\\n\\n    add_parser_networking(p)\\n    add_output_and_prompt_options(p)\\n\\n    p.add_argument(\\n        \\\"--all\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Remove all packages, i.e., the entire environment.\\\",\\n    )\\n    p.add_argument(\\n        \\\"--keep-env\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Used with `--all`, delete all packages but keep the environment.\\\",\\n    )\\n    p.add_argument(\\n        \\\"package_names\\\",\\n        metavar=\\\"package_name\\\",\\n        action=\\\"store\\\",\\n        nargs=\\\"*\\\",\\n        help=\\\"Package names to remove from the environment.\\\",\\n    )\\n    p.add_argument(\\n        \\\"--dev\\\",\\n        action=NullCountAction,\\n        help=\\\"Use `sys.executable -m conda` in wrapper scripts instead of CONDA_EXE. \\\"\\n        \\\"This is mainly for use during tests where we test new conda sources \\\"\\n        \\\"against old Python versions.\\\",\\n        dest=\\\"dev\\\",\\n        default=NULL,\\n    )\\n\\n    p.set_defaults(func=\\\"conda.cli.main_remove.execute\\\")\\n\\n    return p\\n\\n\\ndef execute(args: Namespace, parser: ArgumentParser) -> int:\\n    from ..base.context import context\\n    from ..core.envs_manager import unregister_env\\n    from ..core.link import PrefixSetup, UnlinkLinkTransaction\\n    from ..core.prefix_data import PrefixData\\n    from ..exceptions import (\\n        CondaEnvironmentError,\\n        CondaValueError,\\n        DirectoryNotACondaEnvironmentError,\\n        PackagesNotFoundError,\\n    )\\n    from ..gateways.disk.delete import path_is_clean, rm_rf\\n    from ..models.match_spec import MatchSpec\\n    from .common import check_non_admin, specs_from_args\\n    from .install import handle_txn\\n\\n    if not (args.all or args.package_names):\\n        raise CondaValueError(\\n            \\\"no package names supplied,\\\\n\\\"\\n            '       try \\\"conda remove -h\\\" for more details'\\n        )\\n\\n    prefix = context.target_prefix\\n    check_non_admin()\\n\\n    if args.all and prefix == context.default_prefix:\\n        msg = \\\"cannot remove current environment. deactivate and run conda remove again\\\"\\n        raise CondaEnvironmentError(msg)\\n\\n    if args.all and path_is_clean(prefix):\\n        # full environment removal was requested, but environment doesn't exist anyway\\n\\n        # .. but you know what? If you call `conda remove --all` you'd expect the dir\\n        # not to exist afterwards, would you not? If not (fine, I can see the argument\\n        # about deleting people's work in envs being a very bad thing indeed), but if\\n        # being careful is the goal it would still be nice if after `conda remove --all`\\n        # to be able to do `conda create` on the same environment name.\\n        #\\n        # try:\\n        #     rm_rf(prefix, clean_empty_parents=True)\\n        # except:\\n        #     log.warning(\\\"Failed rm_rf() of partially existent env {}\\\".format(prefix))\\n\\n        return 0\\n\\n    if args.all:\\n        if prefix == context.root_prefix:\\n            raise CondaEnvironmentError(\\n                \\\"cannot remove root environment, add -n NAME or -p PREFIX option\\\"\\n            )\\n        if not isfile(join(prefix, \\\"conda-meta\\\", \\\"history\\\")):\\n            raise DirectoryNotACondaEnvironmentError(prefix)\\n        if not args.json:\\n            print(f\\\"\\\\nRemove all packages in environment {prefix}:\\\\n\\\")\\n\\n        if \\\"package_names\\\" in args:\\n            stp = PrefixSetup(\\n                target_prefix=prefix,\\n                unlink_precs=tuple(PrefixData(prefix).iter_records()),\\n                link_precs=(),\\n                remove_specs=(),\\n                update_specs=(),\\n                neutered_specs={},\\n            )\\n            txn = UnlinkLinkTransaction(stp)\\n            try:\\n                handle_txn(txn, prefix, args, False, True)\\n            except PackagesNotFoundError:\\n                if not args.json:\\n                    print(\\n                        f\\\"No packages found in {prefix}. Continuing environment removal\\\"\\n                    )\\n        if not context.dry_run:\\n            if not args.keep_env:\\n                if not args.json:\\n                    confirm_yn(\\n                        f\\\"Everything found within the environment ({prefix}), including any conda environment configurations and any non-conda files, will be deleted. Do you wish to continue?\\\\n\\\",\\n                        default=\\\"no\\\",\\n                        dry_run=False,\\n                    )\\n                rm_rf(prefix, clean_empty_parents=True)\\n                unregister_env(prefix)\\n\\n        return 0\\n\\n    else:\\n        if args.features:\\n            specs = tuple(MatchSpec(track_features=f) for f in set(args.package_names))\\n        else:\\n            specs = specs_from_args(args.package_names)\\n        channel_urls = ()\\n        subdirs = ()\\n        solver_backend = context.plugin_manager.get_cached_solver_backend()\\n        solver = solver_backend(prefix, channel_urls, subdirs, specs_to_remove=specs)\\n        txn = solver.solve_for_transaction()\\n        handle_txn(txn, prefix, args, False, True)\\n        return 0\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Utilities for finding executables and `conda-*` commands.\\\"\\\"\\\"\\n\\nimport os\\nimport re\\nimport sys\\nimport sysconfig\\nfrom functools import lru_cache\\nfrom os.path import basename, expanduser, isfile, join\\n\\nfrom ..common.compat import on_win\\n\\n\\ndef find_executable(executable, include_others=True):\\n    # backwards compatibility\\n    global dir_paths\\n\\n    if include_others:\\n        from ..utils import sys_prefix_unfollowed\\n\\n        prefixes = [sys_prefix_unfollowed()]\\n        if sys.prefix != prefixes[0]:\\n            prefixes.append(sys.prefix)\\n        dir_paths = [join(p, basename(sysconfig.get_path(\\\"scripts\\\"))) for p in prefixes]\\n        # Is this still needed?\\n        if on_win:\\n            dir_paths.append(\\\"C:\\\\\\\\cygwin\\\\\\\\bin\\\")\\n    else:\\n        dir_paths = []\\n\\n    dir_paths.extend(os.environ.get(\\\"PATH\\\", \\\"\\\").split(os.pathsep))\\n\\n    for dir_path in dir_paths:\\n        if on_win:\\n            for ext in (\\\".exe\\\", \\\".bat\\\", \\\"\\\"):\\n                path = join(dir_path, executable + ext)\\n                if isfile(path):\\n                    return path\\n        else:\\n            path = join(dir_path, executable)\\n            if isfile(expanduser(path)):\\n                return expanduser(path)\\n    return None\\n\\n\\n@lru_cache(maxsize=None)\\ndef find_commands(include_others=True):\\n    if include_others:\\n        from ..utils import sys_prefix_unfollowed\\n\\n        prefixes = [sys_prefix_unfollowed()]\\n        if sys.prefix != prefixes[0]:\\n            prefixes.append(sys.prefix)\\n        dir_paths = [join(p, basename(sysconfig.get_path(\\\"scripts\\\"))) for p in prefixes]\\n        # Is this still needed?\\n        if on_win:\\n            dir_paths.append(\\\"C:\\\\\\\\cygwin\\\\\\\\bin\\\")\\n    else:\\n        dir_paths = []\\n\\n    dir_paths.extend(os.environ.get(\\\"PATH\\\", \\\"\\\").split(os.pathsep))\\n\\n    if on_win:\\n        pat = re.compile(r\\\"conda-([\\\\w\\\\-]+)(\\\\.(exe|bat))?$\\\")\\n    else:\\n        pat = re.compile(r\\\"conda-([\\\\w\\\\-]+)$\\\")\\n\\n    res = set()\\n    for dir_path in dir_paths:\\n        try:\\n            for entry in os.scandir(dir_path):\\n                m = pat.match(entry.name)\\n                if m and entry.is_file():\\n                    res.add(m.group(1))\\n        except (FileNotFoundError, NotADirectoryError, PermissionError, OSError):\\n            # FileNotFoundError: path doesn't exist\\n            # NotADirectoryError: path is not a directory\\n            # PermissionError: user doesn't have read access\\n            # OSError: [WinError 123] The filename, directory name, or volume\\n            # label syntax is incorrect\\n            continue\\n    return tuple(sorted(res))\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"CLI implementation for `conda create`.\\n\\nCreates new conda environments with the specified packages.\\n\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nfrom argparse import _StoreTrueAction\\nfrom logging import getLogger\\nfrom os.path import isdir\\nfrom typing import TYPE_CHECKING\\n\\nfrom ..deprecations import deprecated\\nfrom ..notices import notices\\n\\nif TYPE_CHECKING:\\n    from argparse import ArgumentParser, Namespace, _SubParsersAction\\n\\nlog = getLogger(__name__)\\n\\n\\ndef configure_parser(sub_parsers: _SubParsersAction, **kwargs) -> ArgumentParser:\\n    from ..auxlib.ish import dals\\n    from ..common.constants import NULL\\n    from .actions import NullCountAction\\n    from .helpers import (\\n        add_parser_create_install_update,\\n        add_parser_default_packages,\\n        add_parser_platform,\\n        add_parser_solver,\\n    )\\n\\n    summary = \\\"Create a new conda environment from a list of specified packages. \\\"\\n    description = dals(\\n        f\\\"\\\"\\\"\\n        {summary}\\n\\n        To use the newly-created environment, use 'conda activate envname'.\\n        This command requires either the -n NAME or -p PREFIX option.\\n        \\\"\\\"\\\"\\n    )\\n    epilog = dals(\\n        \\\"\\\"\\\"\\n        Examples:\\n\\n        Create an environment containing the package 'sqlite'::\\n\\n            conda create -n myenv sqlite\\n\\n        Create an environment (env2) as a clone of an existing environment (env1)::\\n\\n            conda create -n env2 --clone path/to/file/env1\\n\\n        \\\"\\\"\\\"\\n    )\\n    p = sub_parsers.add_parser(\\n        \\\"create\\\",\\n        help=summary,\\n        description=description,\\n        epilog=epilog,\\n        **kwargs,\\n    )\\n    p.add_argument(\\n        \\\"--clone\\\",\\n        action=\\\"store\\\",\\n        help=\\\"Create a new environment as a copy of an existing local environment.\\\",\\n        metavar=\\\"ENV\\\",\\n    )\\n    solver_mode_options, _, channel_options = add_parser_create_install_update(p)\\n    add_parser_default_packages(solver_mode_options)\\n    add_parser_platform(channel_options)\\n    add_parser_solver(solver_mode_options)\\n    p.add_argument(\\n        \\\"-m\\\",\\n        \\\"--mkdir\\\",\\n        action=deprecated.action(\\n            \\\"24.9\\\",\\n            \\\"25.3\\\",\\n            _StoreTrueAction,\\n            addendum=\\\"Redundant argument.\\\",\\n        ),\\n    )\\n    p.add_argument(\\n        \\\"--dev\\\",\\n        action=NullCountAction,\\n        help=\\\"Use `sys.executable -m conda` in wrapper scripts instead of CONDA_EXE. \\\"\\n        \\\"This is mainly for use during tests where we test new conda sources \\\"\\n        \\\"against old Python versions.\\\",\\n        dest=\\\"dev\\\",\\n        default=NULL,\\n    )\\n    p.set_defaults(func=\\\"conda.cli.main_create.execute\\\")\\n\\n    return p\\n\\n\\n@notices\\ndef execute(args: Namespace, parser: ArgumentParser) -> int:\\n    import os\\n    from tempfile import mktemp\\n\\n    from ..base.constants import UNUSED_ENV_NAME\\n    from ..base.context import context\\n    from ..common.path import paths_equal\\n    from ..exceptions import ArgumentError, CondaValueError\\n    from ..gateways.disk.delete import rm_rf\\n    from ..gateways.disk.test import is_conda_environment\\n    from .common import confirm_yn\\n    from .install import install\\n\\n    if not args.name and not args.prefix:\\n        if context.dry_run:\\n            args.prefix = os.path.join(mktemp(), UNUSED_ENV_NAME)\\n            context.__init__(argparse_args=args)\\n        else:\\n            raise ArgumentError(\\n                \\\"one of the arguments -n/--name -p/--prefix is required\\\"\\n            )\\n\\n    if is_conda_environment(context.target_prefix):\\n        if paths_equal(context.target_prefix, context.root_prefix):\\n            raise CondaValueError(\\\"The target prefix is the base prefix. Aborting.\\\")\\n        if context.dry_run:\\n            # Taking the \\\"easy\\\" way out, rather than trying to fake removing\\n            # the existing environment before creating a new one.\\n            raise CondaValueError(\\n                \\\"Cannot `create --dry-run` with an existing conda environment\\\"\\n            )\\n        confirm_yn(\\n            f\\\"WARNING: A conda environment already exists at '{context.target_prefix}'\\\\n\\\"\\n            \\\"Remove existing environment\\\",\\n            default=\\\"no\\\",\\n            dry_run=False,\\n        )\\n        log.info(\\\"Removing existing environment %s\\\", context.target_prefix)\\n        rm_rf(context.target_prefix)\\n    elif isdir(context.target_prefix):\\n        confirm_yn(\\n            f\\\"WARNING: A directory already exists at the target location '{context.target_prefix}'\\\\n\\\"\\n            \\\"but it is not a conda environment.\\\\n\\\"\\n            \\\"Continue creating environment\\\",\\n            default=\\\"no\\\",\\n            dry_run=False,\\n        )\\n\\n    return install(args, parser, \\\"create\\\")\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"PEP 621 compatible entry point used when `conda init` has not updated the user shell profile.\\\"\\\"\\\"\\n\\nimport os\\nimport sys\\nfrom logging import getLogger\\n\\nlog = getLogger(__name__)\\n\\n\\ndef pip_installed_post_parse_hook(args, p):\\n    from .. import CondaError\\n\\n    if args.cmd not in (\\\"init\\\", \\\"info\\\"):\\n        raise CondaError(\\n            \\\"Conda has not been initialized.\\\\n\\\"\\n            \\\"\\\\n\\\"\\n            \\\"To enable full conda functionality, please run 'conda init'.\\\\n\\\"\\n            \\\"For additional information, see 'conda init --help'.\\\\n\\\"\\n        )\\n\\n\\ndef main(*args, **kwargs):\\n    from .main import main\\n\\n    os.environ[\\\"CONDA_PIP_UNINITIALIZED\\\"] = \\\"true\\\"\\n    kwargs[\\\"post_parse_hook\\\"] = pip_installed_post_parse_hook\\n    return main(*args, **kwargs)\\n\\n\\nif __name__ == \\\"__main__\\\":\\n    sys.exit(main())\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"CLI implementation for `conda config`.\\n\\nAllows for programmatically interacting with conda's configuration files (e.g., `~/.condarc`).\\n\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport json\\nimport os\\nimport sys\\nfrom argparse import SUPPRESS\\nfrom collections.abc import Mapping, Sequence\\nfrom itertools import chain\\nfrom logging import getLogger\\nfrom os.path import isfile, join\\nfrom pathlib import Path\\nfrom textwrap import wrap\\nfrom typing import TYPE_CHECKING\\n\\nif TYPE_CHECKING:\\n    from argparse import ArgumentParser, Namespace, _SubParsersAction\\n    from typing import Any\\n\\n\\ndef configure_parser(sub_parsers: _SubParsersAction, **kwargs) -> ArgumentParser:\\n    from ..auxlib.ish import dals\\n    from ..base.constants import CONDA_HOMEPAGE_URL\\n    from ..base.context import context, sys_rc_path, user_rc_path\\n    from ..common.constants import NULL\\n    from .helpers import add_parser_json\\n\\n    escaped_user_rc_path = user_rc_path.replace(\\\"%\\\", \\\"%%\\\")\\n    escaped_sys_rc_path = sys_rc_path.replace(\\\"%\\\", \\\"%%\\\")\\n\\n    summary = \\\"Modify configuration values in .condarc.\\\"\\n    description = dals(\\n        f\\\"\\\"\\\"\\n        {summary}\\n\\n        This is modeled after the git config command.  Writes to the user .condarc\\n        file ({escaped_user_rc_path}) by default. Use the\\n        --show-sources flag to display all identified configuration locations on\\n        your computer.\\n\\n        \\\"\\\"\\\"\\n    )\\n    epilog = dals(\\n        f\\\"\\\"\\\"\\n        See `conda config --describe` or {CONDA_HOMEPAGE_URL}/docs/config.html\\n        for details on all the options that can go in .condarc.\\n\\n        Examples:\\n\\n        Display all configuration values as calculated and compiled::\\n\\n            conda config --show\\n\\n        Display all identified configuration sources::\\n\\n            conda config --show-sources\\n\\n        Print the descriptions of all available configuration\\n        options to your command line::\\n\\n            conda config --describe\\n\\n        Print the description for the \\\"channel_priority\\\" configuration\\n        option to your command line::\\n\\n            conda config --describe channel_priority\\n\\n        Add the conda-canary channel::\\n\\n            conda config --add channels conda-canary\\n\\n        Set the output verbosity to level 3 (highest) for\\n        the current activate environment::\\n\\n            conda config --set verbosity 3 --env\\n\\n        Add the 'conda-forge' channel as a backup to 'defaults'::\\n\\n            conda config --append channels conda-forge\\n\\n        \\\"\\\"\\\"\\n    )\\n\\n    p = sub_parsers.add_parser(\\n        \\\"config\\\",\\n        help=summary,\\n        description=description,\\n        epilog=epilog,\\n        **kwargs,\\n    )\\n    add_parser_json(p)\\n\\n    # TODO: use argparse.FileType\\n    config_file_location_group = p.add_argument_group(\\n        \\\"Config File Location Selection\\\",\\n        f\\\"Without one of these flags, the user config file at '{escaped_user_rc_path}' is used.\\\",\\n    )\\n    location = config_file_location_group.add_mutually_exclusive_group()\\n    location.add_argument(\\n        \\\"--system\\\",\\n        action=\\\"store_true\\\",\\n        help=f\\\"Write to the system .condarc file at '{escaped_sys_rc_path}'.\\\",\\n    )\\n    location.add_argument(\\n        \\\"--env\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Write to the active conda environment .condarc file ({}). \\\"\\n        \\\"If no environment is active, write to the user config file ({}).\\\"\\n        \\\"\\\".format(\\n            context.active_prefix or \\\"<no active environment>\\\",\\n            escaped_user_rc_path,\\n        ),\\n    )\\n    location.add_argument(\\\"--file\\\", action=\\\"store\\\", help=\\\"Write to the given file.\\\")\\n\\n    # XXX: Does this really have to be mutually exclusive. I think the below\\n    # code will work even if it is a regular group (although combination of\\n    # --add and --remove with the same keys will not be well-defined).\\n    _config_subcommands = p.add_argument_group(\\\"Config Subcommands\\\")\\n    config_subcommands = _config_subcommands.add_mutually_exclusive_group()\\n    config_subcommands.add_argument(\\n        \\\"--show\\\",\\n        nargs=\\\"*\\\",\\n        default=None,\\n        help=\\\"Display configuration values as calculated and compiled. \\\"\\n        \\\"If no arguments given, show information for all configuration values.\\\",\\n    )\\n    config_subcommands.add_argument(\\n        \\\"--show-sources\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Display all identified configuration sources.\\\",\\n    )\\n    config_subcommands.add_argument(\\n        \\\"--validate\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Validate all configuration sources. Iterates over all .condarc files \\\"\\n        \\\"and checks for parsing errors.\\\",\\n    )\\n    config_subcommands.add_argument(\\n        \\\"--describe\\\",\\n        nargs=\\\"*\\\",\\n        default=None,\\n        help=\\\"Describe given configuration parameters. If no arguments given, show \\\"\\n        \\\"information for all configuration parameters.\\\",\\n    )\\n    config_subcommands.add_argument(\\n        \\\"--write-default\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Write the default configuration to a file. \\\"\\n        \\\"Equivalent to `conda config --describe > ~/.condarc`.\\\",\\n    )\\n\\n    _config_modifiers = p.add_argument_group(\\\"Config Modifiers\\\")\\n    config_modifiers = _config_modifiers.add_mutually_exclusive_group()\\n    config_modifiers.add_argument(\\n        \\\"--get\\\",\\n        nargs=\\\"*\\\",\\n        action=\\\"store\\\",\\n        help=\\\"Get a configuration value.\\\",\\n        default=None,\\n        metavar=\\\"KEY\\\",\\n    )\\n    config_modifiers.add_argument(\\n        \\\"--append\\\",\\n        nargs=2,\\n        action=\\\"append\\\",\\n        help=\\\"\\\"\\\"Add one configuration value to the end of a list key.\\\"\\\"\\\",\\n        default=[],\\n        metavar=(\\\"KEY\\\", \\\"VALUE\\\"),\\n    )\\n    config_modifiers.add_argument(\\n        \\\"--prepend\\\",\\n        \\\"--add\\\",\\n        nargs=2,\\n        action=\\\"append\\\",\\n        help=\\\"\\\"\\\"Add one configuration value to the beginning of a list key.\\\"\\\"\\\",\\n        default=[],\\n        metavar=(\\\"KEY\\\", \\\"VALUE\\\"),\\n    )\\n    config_modifiers.add_argument(\\n        \\\"--set\\\",\\n        nargs=2,\\n        action=\\\"append\\\",\\n        help=\\\"\\\"\\\"Set a boolean or string key.\\\"\\\"\\\",\\n        default=[],\\n        metavar=(\\\"KEY\\\", \\\"VALUE\\\"),\\n    )\\n    config_modifiers.add_argument(\\n        \\\"--remove\\\",\\n        nargs=2,\\n        action=\\\"append\\\",\\n        help=\\\"\\\"\\\"Remove a configuration value from a list key.\\n                This removes all instances of the value.\\\"\\\"\\\",\\n        default=[],\\n        metavar=(\\\"KEY\\\", \\\"VALUE\\\"),\\n    )\\n    config_modifiers.add_argument(\\n        \\\"--remove-key\\\",\\n        action=\\\"append\\\",\\n        help=\\\"\\\"\\\"Remove a configuration key (and all its values).\\\"\\\"\\\",\\n        default=[],\\n        metavar=\\\"KEY\\\",\\n    )\\n    config_modifiers.add_argument(\\n        \\\"--stdin\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Apply configuration information given in yaml format piped through stdin.\\\",\\n    )\\n\\n    p.add_argument(\\n        \\\"-f\\\",\\n        \\\"--force\\\",\\n        action=\\\"store_true\\\",\\n        default=NULL,\\n        help=SUPPRESS,  # TODO: No longer used.  Remove in a future release.\\n    )\\n\\n    p.set_defaults(func=\\\"conda.cli.main_config.execute\\\")\\n\\n    return p\\n\\n\\ndef execute(args: Namespace, parser: ArgumentParser) -> int:\\n    from .. import CondaError\\n    from ..exceptions import CouldntParseError\\n\\n    try:\\n        return execute_config(args, parser)\\n    except (CouldntParseError, NotImplementedError) as e:\\n        raise CondaError(e)\\n\\n\\ndef format_dict(d):\\n    from ..common.compat import isiterable\\n    from ..common.configuration import pretty_list, pretty_map\\n\\n    lines = []\\n    for k, v in d.items():\\n        if isinstance(v, Mapping):\\n            if v:\\n                lines.append(f\\\"{k}:\\\")\\n                lines.append(pretty_map(v))\\n            else:\\n                lines.append(f\\\"{k}: {{}}\\\")\\n        elif isiterable(v):\\n            if v:\\n                lines.append(f\\\"{k}:\\\")\\n                lines.append(pretty_list(v))\\n            else:\\n                lines.append(f\\\"{k}: []\\\")\\n        else:\\n            lines.append(\\\"{}: {}\\\".format(k, v if v is not None else \\\"None\\\"))\\n    return lines\\n\\n\\ndef parameter_description_builder(name):\\n    from ..auxlib.entity import EntityEncoder\\n    from ..base.context import context\\n    from ..common.serialize import yaml_round_trip_dump\\n\\n    builder = []\\n    details = context.describe_parameter(name)\\n    aliases = details[\\\"aliases\\\"]\\n    string_delimiter = details.get(\\\"string_delimiter\\\")\\n    element_types = details[\\\"element_types\\\"]\\n    default_value_str = json.dumps(details[\\\"default_value\\\"], cls=EntityEncoder)\\n\\n    if details[\\\"parameter_type\\\"] == \\\"primitive\\\":\\n        builder.append(\\n            \\\"{} ({})\\\".format(name, \\\", \\\".join(sorted({et for et in element_types})))\\n        )\\n    else:\\n        builder.append(\\n            \\\"{} ({}: {})\\\".format(\\n                name,\\n                details[\\\"parameter_type\\\"],\\n                \\\", \\\".join(sorted({et for et in element_types})),\\n            )\\n        )\\n\\n    if aliases:\\n        builder.append(\\\"  aliases: {}\\\".format(\\\", \\\".join(aliases)))\\n    if string_delimiter:\\n        builder.append(f\\\"  env var string delimiter: '{string_delimiter}'\\\")\\n\\n    builder.extend(\\\"  \\\" + line for line in wrap(details[\\\"description\\\"], 70))\\n\\n    builder.append(\\\"\\\")\\n    builder = [\\\"# \\\" + line for line in builder]\\n\\n    builder.extend(\\n        yaml_round_trip_dump({name: json.loads(default_value_str)}).strip().split(\\\"\\\\n\\\")\\n    )\\n\\n    builder = [\\\"# \\\" + line for line in builder]\\n    builder.append(\\\"\\\")\\n    return builder\\n\\n\\ndef describe_all_parameters():\\n    from ..base.context import context\\n\\n    builder = []\\n    skip_categories = (\\\"CLI-only\\\", \\\"Hidden and Undocumented\\\")\\n    for category, parameter_names in context.category_map.items():\\n        if category in skip_categories:\\n            continue\\n        builder.append(\\\"# ######################################################\\\")\\n        builder.append(f\\\"# ## {category:^48} ##\\\")\\n        builder.append(\\\"# ######################################################\\\")\\n        builder.append(\\\"\\\")\\n        builder.extend(\\n            chain.from_iterable(\\n                parameter_description_builder(name) for name in parameter_names\\n            )\\n        )\\n        builder.append(\\\"\\\")\\n    return \\\"\\\\n\\\".join(builder)\\n\\n\\ndef print_config_item(key, value):\\n    stdout_write = getLogger(\\\"conda.stdout\\\").info\\n    if isinstance(value, (dict,)):\\n        for k, v in value.items():\\n            print_config_item(key + \\\".\\\" + k, v)\\n    elif isinstance(value, (bool, int, str)):\\n        stdout_write(\\\" \\\".join((\\\"--set\\\", key, str(value))))\\n    elif isinstance(value, (list, tuple)):\\n        # Note, since `conda config --add` prepends, print `--add` commands in\\n        # reverse order (using repr), so that entering them in this order will\\n        # recreate the same file.\\n        numitems = len(value)\\n        for q, item in enumerate(reversed(value)):\\n            if key == \\\"channels\\\" and q in (0, numitems - 1):\\n                stdout_write(\\n                    \\\" \\\".join(\\n                        (\\n                            \\\"--add\\\",\\n                            key,\\n                            repr(item),\\n                            \\\"  # lowest priority\\\" if q == 0 else \\\"  # highest priority\\\",\\n                        )\\n                    )\\n                )\\n            else:\\n                stdout_write(\\\" \\\".join((\\\"--add\\\", key, repr(item))))\\n\\n\\ndef _get_key(\\n    key: str,\\n    config: dict,\\n    *,\\n    json: dict[str, Any] = {},\\n    warnings: list[str] = [],\\n) -> None:\\n    from ..base.context import context\\n\\n    key_parts = key.split(\\\".\\\")\\n\\n    if key_parts[0] not in context.list_parameters():\\n        if context.json:\\n            warnings.append(f\\\"Unknown key: {key_parts[0]!r}\\\")\\n        else:\\n            print(f\\\"Unknown key: {key_parts[0]!r}\\\", file=sys.stderr)\\n        return\\n\\n    sub_config = config\\n    try:\\n        for part in key_parts:\\n            sub_config = sub_config[part]\\n    except KeyError:\\n        # KeyError: part not found, nothing to get\\n        pass\\n    else:\\n        if context.json:\\n            json[key] = sub_config\\n        else:\\n            print_config_item(key, sub_config)\\n\\n\\ndef _set_key(key: str, item: Any, config: dict) -> None:\\n    from ..base.context import context\\n\\n    key_parts = key.split(\\\".\\\")\\n    try:\\n        parameter_type = context.describe_parameter(key_parts[0])[\\\"parameter_type\\\"]\\n    except KeyError:\\n        # KeyError: key_parts[0] is an unknown parameter\\n        from ..exceptions import CondaKeyError\\n\\n        raise CondaKeyError(key, \\\"unknown parameter\\\")\\n\\n    if parameter_type == \\\"primitive\\\" and len(key_parts) == 1:\\n        (key,) = key_parts\\n        config[key] = context.typify_parameter(key, item, \\\"--set parameter\\\")\\n    elif parameter_type == \\\"map\\\" and len(key_parts) == 2:\\n        key, subkey = key_parts\\n        config.setdefault(key, {})[subkey] = item\\n    else:\\n        from ..exceptions import CondaKeyError\\n\\n        raise CondaKeyError(key, \\\"invalid parameter\\\")\\n\\n\\ndef _remove_item(key: str, item: Any, config: dict) -> None:\\n    from ..base.context import context\\n\\n    key_parts = key.split(\\\".\\\")\\n    try:\\n        parameter_type = context.describe_parameter(key_parts[0])[\\\"parameter_type\\\"]\\n    except KeyError:\\n        # KeyError: key_parts[0] is an unknown parameter\\n        from ..exceptions import CondaKeyError\\n\\n        raise CondaKeyError(key, \\\"unknown parameter\\\")\\n\\n    if parameter_type == \\\"sequence\\\" and len(key_parts) == 1:\\n        (key,) = key_parts\\n        if key not in config:\\n            if key != \\\"channels\\\":\\n                from ..exceptions import CondaKeyError\\n\\n                raise CondaKeyError(key, \\\"undefined in config\\\")\\n            config[key] = [\\\"defaults\\\"]\\n\\n        if item not in config[key]:\\n            from ..exceptions import CondaKeyError\\n\\n            raise CondaKeyError(key, f\\\"value {item!r} not present in config\\\")\\n        config[key] = [i for i in config[key] if i != item]\\n    else:\\n        from ..exceptions import CondaKeyError\\n\\n        raise CondaKeyError(key, \\\"invalid parameter\\\")\\n\\n\\ndef _remove_key(key: str, config: dict) -> None:\\n    key_parts = key.split(\\\".\\\")\\n\\n    sub_config = config\\n    try:\\n        for part in key_parts[:-1]:\\n            sub_config = sub_config[part]\\n        del sub_config[key_parts[-1]]\\n    except KeyError:\\n        # KeyError: part not found, nothing to remove\\n        from ..exceptions import CondaKeyError\\n\\n        raise CondaKeyError(key, \\\"undefined in config\\\")\\n\\n\\ndef _read_rc(path: str | os.PathLike | Path) -> dict:\\n    from ..common.serialize import yaml_round_trip_load\\n\\n    try:\\n        return yaml_round_trip_load(Path(path).read_text()) or {}\\n    except FileNotFoundError:\\n        # FileNotFoundError: path does not exist\\n        return {}\\n\\n\\ndef _write_rc(path: str | os.PathLike | Path, config: dict) -> None:\\n    from .. import CondaError\\n    from ..base.constants import (\\n        ChannelPriority,\\n        DepsModifier,\\n        PathConflict,\\n        SafetyChecks,\\n        SatSolverChoice,\\n        UpdateModifier,\\n    )\\n    from ..common.serialize import yaml, yaml_round_trip_dump\\n\\n    # Add representers for enums.\\n    # Because a representer cannot be added for the base Enum class (it must be added for\\n    # each specific Enum subclass - and because of import rules), I don't know of a better\\n    # location to do this.\\n    def enum_representer(dumper, data):\\n        return dumper.represent_str(str(data))\\n\\n    yaml.representer.RoundTripRepresenter.add_representer(\\n        SafetyChecks, enum_representer\\n    )\\n    yaml.representer.RoundTripRepresenter.add_representer(\\n        PathConflict, enum_representer\\n    )\\n    yaml.representer.RoundTripRepresenter.add_representer(\\n        DepsModifier, enum_representer\\n    )\\n    yaml.representer.RoundTripRepresenter.add_representer(\\n        UpdateModifier, enum_representer\\n    )\\n    yaml.representer.RoundTripRepresenter.add_representer(\\n        ChannelPriority, enum_representer\\n    )\\n    yaml.representer.RoundTripRepresenter.add_representer(\\n        SatSolverChoice, enum_representer\\n    )\\n\\n    try:\\n        Path(path).write_text(yaml_round_trip_dump(config))\\n    except OSError as e:\\n        raise CondaError(f\\\"Cannot write to condarc file at {path}\\\\nCaused by {e!r}\\\")\\n\\n\\ndef set_keys(*args: tuple[str, Any], path: str | os.PathLike | Path) -> None:\\n    config = _read_rc(path)\\n    for key, value in args:\\n        _set_key(key, value, config)\\n    _write_rc(path, config)\\n\\n\\ndef execute_config(args, parser):\\n    from .. import CondaError\\n    from ..auxlib.entity import EntityEncoder\\n    from ..base.context import context, sys_rc_path, user_rc_path\\n    from ..common.io import timeout\\n    from ..common.iterators import groupby_to_dict as groupby\\n    from ..common.serialize import yaml_round_trip_load\\n\\n    stdout_write = getLogger(\\\"conda.stdout\\\").info\\n    stderr_write = getLogger(\\\"conda.stderr\\\").info\\n    json_warnings = []\\n    json_get = {}\\n\\n    if args.show_sources:\\n        if context.json:\\n            stdout_write(\\n                json.dumps(\\n                    {\\n                        str(source): values\\n                        for source, values in context.collect_all().items()\\n                    },\\n                    sort_keys=True,\\n                    indent=2,\\n                    separators=(\\\",\\\", \\\": \\\"),\\n                    cls=EntityEncoder,\\n                )\\n            )\\n        else:\\n            lines = []\\n            for source, reprs in context.collect_all().items():\\n                lines.append(f\\\"==> {source} <==\\\")\\n                lines.extend(format_dict(reprs))\\n                lines.append(\\\"\\\")\\n            stdout_write(\\\"\\\\n\\\".join(lines))\\n        return\\n\\n    if args.show is not None:\\n        if args.show:\\n            paramater_names = args.show\\n            all_names = context.list_parameters()\\n            not_params = set(paramater_names) - set(all_names)\\n            if not_params:\\n                from ..common.io import dashlist\\n                from ..exceptions import ArgumentError\\n\\n                raise ArgumentError(\\n                    f\\\"Invalid configuration parameters: {dashlist(not_params)}\\\"\\n                )\\n        else:\\n            paramater_names = context.list_parameters()\\n\\n        d = {key: getattr(context, key) for key in paramater_names}\\n        if context.json:\\n            stdout_write(\\n                json.dumps(\\n                    d,\\n                    sort_keys=True,\\n                    indent=2,\\n                    separators=(\\\",\\\", \\\": \\\"),\\n                    cls=EntityEncoder,\\n                )\\n            )\\n        else:\\n            # Add in custom formatting\\n            if \\\"custom_channels\\\" in d:\\n                d[\\\"custom_channels\\\"] = {\\n                    channel.name: f\\\"{channel.scheme}://{channel.location}\\\"\\n                    for channel in d[\\\"custom_channels\\\"].values()\\n                }\\n            if \\\"custom_multichannels\\\" in d:\\n                from ..common.io import dashlist\\n\\n                d[\\\"custom_multichannels\\\"] = {\\n                    multichannel_name: dashlist(channels, indent=4)\\n                    for multichannel_name, channels in d[\\\"custom_multichannels\\\"].items()\\n                }\\n            if \\\"channel_settings\\\" in d:\\n                ident = \\\" \\\" * 4\\n                d[\\\"channel_settings\\\"] = tuple(\\n                    f\\\"\\\\n{ident}\\\".join(format_dict(mapping))\\n                    for mapping in d[\\\"channel_settings\\\"]\\n                )\\n\\n            stdout_write(\\\"\\\\n\\\".join(format_dict(d)))\\n        context.validate_configuration()\\n        return\\n\\n    if args.describe is not None:\\n        if args.describe:\\n            paramater_names = args.describe\\n            all_names = context.list_parameters()\\n            not_params = set(paramater_names) - set(all_names)\\n            if not_params:\\n                from ..common.io import dashlist\\n                from ..exceptions import ArgumentError\\n\\n                raise ArgumentError(\\n                    f\\\"Invalid configuration parameters: {dashlist(not_params)}\\\"\\n                )\\n            if context.json:\\n                stdout_write(\\n                    json.dumps(\\n                        [context.describe_parameter(name) for name in paramater_names],\\n                        sort_keys=True,\\n                        indent=2,\\n                        separators=(\\\",\\\", \\\": \\\"),\\n                        cls=EntityEncoder,\\n                    )\\n                )\\n            else:\\n                builder = []\\n                builder.extend(\\n                    chain.from_iterable(\\n                        parameter_description_builder(name) for name in paramater_names\\n                    )\\n                )\\n                stdout_write(\\\"\\\\n\\\".join(builder))\\n        else:\\n            if context.json:\\n                skip_categories = (\\\"CLI-only\\\", \\\"Hidden and Undocumented\\\")\\n                paramater_names = sorted(\\n                    chain.from_iterable(\\n                        parameter_names\\n                        for category, parameter_names in context.category_map.items()\\n                        if category not in skip_categories\\n                    )\\n                )\\n                stdout_write(\\n                    json.dumps(\\n                        [context.describe_parameter(name) for name in paramater_names],\\n                        sort_keys=True,\\n                        indent=2,\\n                        separators=(\\\",\\\", \\\": \\\"),\\n                        cls=EntityEncoder,\\n                    )\\n                )\\n            else:\\n                stdout_write(describe_all_parameters())\\n        return\\n\\n    if args.validate:\\n        context.validate_all()\\n        return\\n\\n    if args.system:\\n        rc_path = sys_rc_path\\n    elif args.env:\\n        if context.active_prefix:\\n            rc_path = join(context.active_prefix, \\\".condarc\\\")\\n        else:\\n            rc_path = user_rc_path\\n    elif args.file:\\n        rc_path = args.file\\n    else:\\n        rc_path = user_rc_path\\n\\n    if args.write_default:\\n        if isfile(rc_path):\\n            with open(rc_path) as fh:\\n                data = fh.read().strip()\\n            if data:\\n                raise CondaError(\\n                    f\\\"The file '{rc_path}' \\\"\\n                    \\\"already contains configuration information.\\\\n\\\"\\n                    \\\"Remove the file to proceed.\\\\n\\\"\\n                    \\\"Use `conda config --describe` to display default configuration.\\\"\\n                )\\n\\n        with open(rc_path, \\\"w\\\") as fh:\\n            fh.write(describe_all_parameters())\\n        return\\n\\n    # read existing condarc\\n    if os.path.exists(rc_path):\\n        with open(rc_path) as fh:\\n            # round trip load required because... we need to round trip\\n            rc_config = yaml_round_trip_load(fh) or {}\\n    elif os.path.exists(sys_rc_path):\\n        # In case the considered rc file doesn't exist, fall back to the system rc\\n        with open(sys_rc_path) as fh:\\n            rc_config = yaml_round_trip_load(fh) or {}\\n    else:\\n        rc_config = {}\\n\\n    grouped_paramaters = groupby(\\n        lambda p: context.describe_parameter(p)[\\\"parameter_type\\\"],\\n        context.list_parameters(),\\n    )\\n    sequence_parameters = grouped_paramaters[\\\"sequence\\\"]\\n    map_parameters = grouped_paramaters[\\\"map\\\"]\\n\\n    # Get\\n    if args.get is not None:\\n        context.validate_all()\\n\\n        for key in args.get or sorted(rc_config.keys()):\\n            _get_key(key, rc_config, json=json_get, warnings=json_warnings)\\n\\n    if args.stdin:\\n        content = timeout(5, sys.stdin.read)\\n        if not content:\\n            return\\n        try:\\n            # round trip load required because... we need to round trip\\n            parsed = yaml_round_trip_load(content)\\n            rc_config.update(parsed)\\n        except Exception:  # pragma: no cover\\n            from ..exceptions import ParseError\\n\\n            raise ParseError(f\\\"invalid yaml content:\\\\n{content}\\\")\\n\\n    # prepend, append, add\\n    for arg, prepend in zip((args.prepend, args.append), (True, False)):\\n        for key, item in arg:\\n            key, subkey = key.split(\\\".\\\", 1) if \\\".\\\" in key else (key, None)\\n            if key == \\\"channels\\\" and key not in rc_config:\\n                rc_config[key] = [\\\"defaults\\\"]\\n            if key in sequence_parameters:\\n                arglist = rc_config.setdefault(key, [])\\n            elif key in map_parameters:\\n                arglist = rc_config.setdefault(key, {}).setdefault(subkey, [])\\n            else:\\n                from ..exceptions import CondaValueError\\n\\n                raise CondaValueError(f\\\"Key '{key}' is not a known sequence parameter.\\\")\\n            if not (isinstance(arglist, Sequence) and not isinstance(arglist, str)):\\n                from ..exceptions import CouldntParseError\\n\\n                bad = rc_config[key].__class__.__name__\\n                raise CouldntParseError(f\\\"key {key!r} should be a list, not {bad}.\\\")\\n            if item in arglist:\\n                message_key = key + \\\".\\\" + subkey if subkey is not None else key\\n                # Right now, all list keys should not contain duplicates\\n                message = \\\"Warning: '{}' already in '{}' list, moving to the {}\\\".format(\\n                    item, message_key, \\\"top\\\" if prepend else \\\"bottom\\\"\\n                )\\n                if subkey is None:\\n                    arglist = rc_config[key] = [p for p in arglist if p != item]\\n                else:\\n                    arglist = rc_config[key][subkey] = [p for p in arglist if p != item]\\n                if not context.json:\\n                    stderr_write(message)\\n                else:\\n                    json_warnings.append(message)\\n            arglist.insert(0 if prepend else len(arglist), item)\\n\\n    # Set\\n    for key, item in args.set:\\n        _set_key(key, item, rc_config)\\n\\n    # Remove\\n    for key, item in args.remove:\\n        _remove_item(key, item, rc_config)\\n\\n    # Remove Key\\n    for key in args.remove_key:\\n        _remove_key(key, rc_config)\\n\\n    # config.rc_keys\\n    if not args.get:\\n        _write_rc(rc_path, rc_config)\\n\\n    if context.json:\\n        from .common import stdout_json_success\\n\\n        stdout_json_success(rc_path=rc_path, warnings=json_warnings, get=json_get)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Entry point for all conda subcommands.\\\"\\\"\\\"\\n\\nimport sys\\n\\nfrom ..deprecations import deprecated\\n\\n\\n@deprecated.argument(\\n    \\\"24.3\\\",\\n    \\\"24.9\\\",\\n    \\\"context\\\",\\n    addendum=\\\"The context is a global state, no need to pass it around.\\\",\\n)\\ndef init_loggers():\\n    import logging\\n\\n    from ..base.context import context\\n    from ..gateways.logging import initialize_logging, set_log_level\\n\\n    initialize_logging()\\n\\n    # silence logging info to avoid interfering with JSON output\\n    if context.json:\\n        for logger in (\\\"conda.stdout.verbose\\\", \\\"conda.stdoutlog\\\", \\\"conda.stderrlog\\\"):\\n            logging.getLogger(logger).setLevel(logging.CRITICAL + 10)\\n\\n    # set log_level\\n    set_log_level(context.log_level)\\n\\n\\n@deprecated(\\n    \\\"24.3\\\",\\n    \\\"24.9\\\",\\n    addendum=\\\"Use `conda.cli.conda_argparse.generate_parser` instead.\\\",\\n)\\ndef generate_parser(*args, **kwargs):\\n    \\\"\\\"\\\"\\n    Some code paths import this function directly from this module instead\\n    of from conda_argparse. We add the forwarder for backwards compatibility.\\n    \\\"\\\"\\\"\\n    from .conda_argparse import generate_parser\\n\\n    return generate_parser(*args, **kwargs)\\n\\n\\ndef main_subshell(*args, post_parse_hook=None, **kwargs):\\n    \\\"\\\"\\\"Entrypoint for the \\\"subshell\\\" invocation of CLI interface. E.g. `conda create`.\\\"\\\"\\\"\\n    # defer import here so it doesn't hit the 'conda shell.*' subcommands paths\\n    from ..base.context import context\\n    from .conda_argparse import do_call, generate_parser, generate_pre_parser\\n\\n    args = args or [\\\"--help\\\"]\\n\\n    pre_parser = generate_pre_parser(add_help=False)\\n    pre_args, _ = pre_parser.parse_known_args(args)\\n\\n    # the arguments that we want to pass to the main parser later on\\n    override_args = {\\n        \\\"json\\\": pre_args.json,\\n        \\\"debug\\\": pre_args.debug,\\n        \\\"trace\\\": pre_args.trace,\\n        \\\"verbosity\\\": pre_args.verbosity,\\n    }\\n\\n    context.__init__(argparse_args=pre_args)\\n    if context.no_plugins:\\n        context.plugin_manager.disable_external_plugins()\\n\\n    # reinitialize in case any of the entrypoints modified the context\\n    context.__init__(argparse_args=pre_args)\\n\\n    parser = generate_parser(add_help=True)\\n    args = parser.parse_args(args, override_args=override_args, namespace=pre_args)\\n\\n    context.__init__(argparse_args=args)\\n    init_loggers()\\n\\n    # used with main_pip.py\\n    if post_parse_hook:\\n        post_parse_hook(args, parser)\\n\\n    exit_code = do_call(args, parser)\\n    if isinstance(exit_code, int):\\n        return exit_code\\n    elif hasattr(exit_code, \\\"rc\\\"):\\n        return exit_code.rc\\n\\n\\ndef main_sourced(shell, *args, **kwargs):\\n    \\\"\\\"\\\"Entrypoint for the \\\"sourced\\\" invocation of CLI interface. E.g. `conda activate`.\\\"\\\"\\\"\\n    shell = shell.replace(\\\"shell.\\\", \\\"\\\", 1)\\n\\n    # This is called any way later in conda.activate, so no point in removing it\\n    from ..base.context import context\\n\\n    context.__init__()\\n\\n    from ..activate import _build_activator_cls\\n\\n    try:\\n        activator_cls = _build_activator_cls(shell)\\n    except KeyError:\\n        from ..exceptions import CondaError\\n\\n        raise CondaError(f\\\"{shell} is not a supported shell.\\\")\\n\\n    activator = activator_cls(args)\\n    print(activator.execute(), end=\\\"\\\")\\n    return 0\\n\\n\\ndef main(*args, **kwargs):\\n    # conda.common.compat contains only stdlib imports\\n    from ..common.compat import ensure_text_type\\n    from ..exception_handler import conda_exception_handler\\n\\n    # cleanup argv\\n    args = args or sys.argv[1:]  # drop executable/script\\n    args = tuple(ensure_text_type(s) for s in args)\\n\\n    if args and args[0].strip().startswith(\\\"shell.\\\"):\\n        main = main_sourced\\n    else:\\n        main = main_subshell\\n\\n    return conda_exception_handler(main, *args, **kwargs)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"\\nCollection of helper functions to standardize reused CLI arguments.\\n\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nfrom argparse import SUPPRESS, _HelpAction\\nfrom typing import TYPE_CHECKING\\n\\nif TYPE_CHECKING:\\n    from argparse import ArgumentParser, _ArgumentGroup, _MutuallyExclusiveGroup\\n\\ntry:\\n    from argparse import BooleanOptionalAction\\nexcept ImportError:\\n    # Python < 3.9\\n    from argparse import Action\\n\\n    class BooleanOptionalAction(Action):\\n        # from Python 3.9+ argparse.py\\n        def __init__(\\n            self,\\n            option_strings,\\n            dest,\\n            default=None,\\n            type=None,\\n            choices=None,\\n            required=False,\\n            help=None,\\n            metavar=None,\\n        ):\\n            _option_strings = []\\n            for option_string in option_strings:\\n                _option_strings.append(option_string)\\n\\n                if option_string.startswith(\\\"--\\\"):\\n                    option_string = \\\"--no-\\\" + option_string[2:]\\n                    _option_strings.append(option_string)\\n\\n            super().__init__(\\n                option_strings=_option_strings,\\n                dest=dest,\\n                nargs=0,\\n                default=default,\\n                type=type,\\n                choices=choices,\\n                required=required,\\n                help=help,\\n                metavar=metavar,\\n            )\\n\\n        def __call__(self, parser, namespace, values, option_string=None):\\n            if option_string in self.option_strings:\\n                setattr(namespace, self.dest, not option_string.startswith(\\\"--no-\\\"))\\n\\n        def format_usage(self):\\n            return \\\" | \\\".join(self.option_strings)\\n\\n\\ndef add_parser_create_install_update(p, prefix_required=False):\\n    from ..common.constants import NULL\\n\\n    add_parser_prefix(p, prefix_required)\\n    channel_options = add_parser_channels(p)\\n    solver_mode_options = add_parser_solver_mode(p)\\n    package_install_options = add_parser_package_install_options(p)\\n    add_parser_networking(p)\\n\\n    output_and_prompt_options = add_output_and_prompt_options(p)\\n    output_and_prompt_options.add_argument(\\n        \\\"--download-only\\\",\\n        action=\\\"store_true\\\",\\n        default=NULL,\\n        help=\\\"Solve an environment and ensure package caches are populated, but exit \\\"\\n        \\\"prior to unlinking and linking packages into the prefix.\\\",\\n    )\\n    add_parser_show_channel_urls(output_and_prompt_options)\\n\\n    add_parser_pscheck(p)\\n    add_parser_known(p)\\n\\n    # Add the file kwarg. We don't use {action=\\\"store\\\", nargs='*'} as we don't\\n    # want to gobble up all arguments after --file.\\n    p.add_argument(\\n        \\\"--file\\\",\\n        default=[],\\n        action=\\\"append\\\",\\n        help=\\\"Read package versions from the given file. Repeated file \\\"\\n        \\\"specifications can be passed (e.g. --file=file1 --file=file2).\\\",\\n    )\\n    p.add_argument(\\n        \\\"packages\\\",\\n        metavar=\\\"package_spec\\\",\\n        action=\\\"store\\\",\\n        nargs=\\\"*\\\",\\n        help=\\\"List of packages to install or update in the conda environment.\\\",\\n    )\\n\\n    return solver_mode_options, package_install_options, channel_options\\n\\n\\ndef add_parser_pscheck(p: ArgumentParser) -> None:\\n    p.add_argument(\\\"--force-pscheck\\\", action=\\\"store_true\\\", help=SUPPRESS)\\n\\n\\ndef add_parser_show_channel_urls(p: ArgumentParser | _ArgumentGroup) -> None:\\n    from ..common.constants import NULL\\n\\n    p.add_argument(\\n        \\\"--show-channel-urls\\\",\\n        action=\\\"store_true\\\",\\n        dest=\\\"show_channel_urls\\\",\\n        default=NULL,\\n        help=\\\"Show channel urls. \\\"\\n        \\\"Overrides the value given by `conda config --show show_channel_urls`.\\\",\\n    )\\n    p.add_argument(\\n        \\\"--no-show-channel-urls\\\",\\n        action=\\\"store_false\\\",\\n        dest=\\\"show_channel_urls\\\",\\n        help=SUPPRESS,\\n    )\\n\\n\\ndef add_parser_help(p: ArgumentParser) -> None:\\n    \\\"\\\"\\\"\\n    So we can use consistent capitalization and periods in the help. You must\\n    use the add_help=False argument to ArgumentParser or add_parser to use\\n    this. Add this first to be consistent with the default argparse output.\\n\\n    \\\"\\\"\\\"\\n    p.add_argument(\\n        \\\"-h\\\",\\n        \\\"--help\\\",\\n        action=_HelpAction,\\n        help=\\\"Show this help message and exit.\\\",\\n    )\\n\\n\\ndef add_parser_prefix(\\n    p: ArgumentParser,\\n    prefix_required: bool = False,\\n) -> _MutuallyExclusiveGroup:\\n    target_environment_group = p.add_argument_group(\\\"Target Environment Specification\\\")\\n    npgroup = target_environment_group.add_mutually_exclusive_group(\\n        required=prefix_required\\n    )\\n    npgroup.add_argument(\\n        \\\"-n\\\",\\n        \\\"--name\\\",\\n        action=\\\"store\\\",\\n        help=\\\"Name of environment.\\\",\\n        metavar=\\\"ENVIRONMENT\\\",\\n    )\\n    npgroup.add_argument(\\n        \\\"-p\\\",\\n        \\\"--prefix\\\",\\n        action=\\\"store\\\",\\n        help=\\\"Full path to environment location (i.e. prefix).\\\",\\n        metavar=\\\"PATH\\\",\\n    )\\n    return npgroup\\n\\n\\ndef add_parser_json(p: ArgumentParser) -> _ArgumentGroup:\\n    from ..common.constants import NULL\\n\\n    output_and_prompt_options = p.add_argument_group(\\n        \\\"Output, Prompt, and Flow Control Options\\\"\\n    )\\n    output_and_prompt_options.add_argument(\\n        \\\"--json\\\",\\n        action=\\\"store_true\\\",\\n        default=NULL,\\n        help=\\\"Report all output as json. Suitable for using conda programmatically.\\\",\\n    )\\n    add_parser_verbose(output_and_prompt_options)\\n    output_and_prompt_options.add_argument(\\n        \\\"-q\\\",\\n        \\\"--quiet\\\",\\n        action=\\\"store_true\\\",\\n        default=NULL,\\n        help=\\\"Do not display progress bar.\\\",\\n    )\\n    return output_and_prompt_options\\n\\n\\ndef add_output_and_prompt_options(p: ArgumentParser) -> _ArgumentGroup:\\n    from ..common.constants import NULL\\n\\n    output_and_prompt_options = add_parser_json(p)\\n    output_and_prompt_options.add_argument(\\n        \\\"-d\\\",\\n        \\\"--dry-run\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Only display what would have been done.\\\",\\n    )\\n    output_and_prompt_options.add_argument(\\n        \\\"-y\\\",\\n        \\\"--yes\\\",\\n        action=\\\"store_true\\\",\\n        default=NULL,\\n        help=\\\"Sets any confirmation values to 'yes' automatically. \\\"\\n        \\\"Users will not be asked to confirm any adding, deleting, backups, etc.\\\",\\n    )\\n    return output_and_prompt_options\\n\\n\\ndef add_parser_channels(p: ArgumentParser) -> _ArgumentGroup:\\n    from ..common.constants import NULL\\n\\n    channel_customization_options = p.add_argument_group(\\\"Channel Customization\\\")\\n    channel_customization_options.add_argument(\\n        \\\"-c\\\",\\n        \\\"--channel\\\",\\n        # beware conda-build uses this (currently or in the past?)\\n        # if ever renaming to \\\"channels\\\" consider removing context.channels alias to channel\\n        dest=\\\"channel\\\",\\n        action=\\\"append\\\",\\n        help=(\\n            \\\"Additional channel to search for packages. These are URLs searched in the order \\\"\\n            \\\"they are given (including local directories using the 'file://' syntax or \\\"\\n            \\\"simply a path like '/home/conda/mychan' or '../mychan'). Then, the defaults \\\"\\n            \\\"or channels from .condarc are searched (unless --override-channels is given). \\\"\\n            \\\"You can use 'defaults' to get the default packages for conda. You can also \\\"\\n            \\\"use any name and the .condarc channel_alias value will be prepended. The \\\"\\n            \\\"default channel_alias is https://conda.anaconda.org/.\\\"\\n        ),\\n    )\\n    channel_customization_options.add_argument(\\n        \\\"--use-local\\\",\\n        action=\\\"store_true\\\",\\n        default=NULL,\\n        help=\\\"Use locally built packages. Identical to '-c local'.\\\",\\n    )\\n    channel_customization_options.add_argument(\\n        \\\"--override-channels\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"\\\"\\\"Do not search default or .condarc channels.  Requires --channel.\\\"\\\"\\\",\\n    )\\n    channel_customization_options.add_argument(\\n        \\\"--repodata-fn\\\",\\n        action=\\\"append\\\",\\n        dest=\\\"repodata_fns\\\",\\n        help=(\\n            \\\"Specify file name of repodata on the remote server where your channels \\\"\\n            \\\"are configured or within local backups. Conda will try whatever you \\\"\\n            \\\"specify, but will ultimately fall back to repodata.json if your specs are \\\"\\n            \\\"not satisfiable with what you specify here. This is used to employ repodata \\\"\\n            \\\"that is smaller and reduced in time scope. You may pass this flag more than \\\"\\n            \\\"once. Leftmost entries are tried first, and the fallback to repodata.json \\\"\\n            \\\"is added for you automatically. For more information, see \\\"\\n            \\\"conda config --describe repodata_fns.\\\"\\n        ),\\n    )\\n    channel_customization_options.add_argument(\\n        \\\"--experimental\\\",\\n        action=\\\"append\\\",\\n        choices=[\\\"jlap\\\", \\\"lock\\\"],\\n        help=\\\"jlap: Download incremental package index data from repodata.jlap; implies 'lock'. \\\"\\n        \\\"lock: use locking when reading, updating index (repodata.json) cache. Now enabled.\\\",\\n    )\\n    channel_customization_options.add_argument(\\n        \\\"--no-lock\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Disable locking when reading, updating index (repodata.json) cache. \\\",\\n    )\\n\\n    channel_customization_options.add_argument(\\n        \\\"--repodata-use-zst\\\",\\n        action=BooleanOptionalAction,\\n        dest=\\\"repodata_use_zst\\\",\\n        default=NULL,\\n        help=\\\"Check for/do not check for repodata.json.zst. Enabled by default.\\\",\\n    )\\n    return channel_customization_options\\n\\n\\ndef add_parser_solver_mode(p: ArgumentParser) -> _ArgumentGroup:\\n    from ..base.constants import DepsModifier\\n    from ..common.constants import NULL\\n\\n    solver_mode_options = p.add_argument_group(\\\"Solver Mode Modifiers\\\")\\n    deps_modifiers = solver_mode_options.add_mutually_exclusive_group()\\n    solver_mode_options.add_argument(\\n        \\\"--strict-channel-priority\\\",\\n        action=\\\"store_const\\\",\\n        dest=\\\"channel_priority\\\",\\n        default=NULL,\\n        const=\\\"strict\\\",\\n        help=\\\"Packages in lower priority channels are not considered if a package \\\"\\n        \\\"with the same name appears in a higher priority channel.\\\",\\n    )\\n    solver_mode_options.add_argument(\\n        \\\"--channel-priority\\\",\\n        action=\\\"store_true\\\",\\n        dest=\\\"channel_priority\\\",\\n        default=NULL,\\n        help=SUPPRESS,\\n    )\\n    solver_mode_options.add_argument(\\n        \\\"--no-channel-priority\\\",\\n        action=\\\"store_const\\\",\\n        dest=\\\"channel_priority\\\",\\n        default=NULL,\\n        const=\\\"disabled\\\",\\n        help=\\\"Package version takes precedence over channel priority. \\\"\\n        \\\"Overrides the value given by `conda config --show channel_priority`.\\\",\\n    )\\n    deps_modifiers.add_argument(\\n        \\\"--no-deps\\\",\\n        action=\\\"store_const\\\",\\n        const=DepsModifier.NO_DEPS,\\n        dest=\\\"deps_modifier\\\",\\n        help=\\\"Do not install, update, remove, or change dependencies. This WILL lead \\\"\\n        \\\"to broken environments and inconsistent behavior. Use at your own risk.\\\",\\n        default=NULL,\\n    )\\n    deps_modifiers.add_argument(\\n        \\\"--only-deps\\\",\\n        action=\\\"store_const\\\",\\n        const=DepsModifier.ONLY_DEPS,\\n        dest=\\\"deps_modifier\\\",\\n        help=\\\"Only install dependencies.\\\",\\n        default=NULL,\\n    )\\n    solver_mode_options.add_argument(\\n        \\\"--no-pin\\\",\\n        action=\\\"store_true\\\",\\n        dest=\\\"ignore_pinned\\\",\\n        default=NULL,\\n        help=\\\"Ignore pinned file.\\\",\\n    )\\n    return solver_mode_options\\n\\n\\ndef add_parser_update_modifiers(solver_mode_options: ArgumentParser):\\n    from ..base.constants import UpdateModifier\\n    from ..common.constants import NULL\\n\\n    update_modifiers = solver_mode_options.add_mutually_exclusive_group()\\n    update_modifiers.add_argument(\\n        \\\"--freeze-installed\\\",\\n        \\\"--no-update-deps\\\",\\n        action=\\\"store_const\\\",\\n        const=UpdateModifier.FREEZE_INSTALLED,\\n        dest=\\\"update_modifier\\\",\\n        default=NULL,\\n        help=\\\"Do not update or change already-installed dependencies.\\\",\\n    )\\n    update_modifiers.add_argument(\\n        \\\"--update-deps\\\",\\n        action=\\\"store_const\\\",\\n        const=UpdateModifier.UPDATE_DEPS,\\n        dest=\\\"update_modifier\\\",\\n        default=NULL,\\n        help=\\\"Update dependencies that have available updates.\\\",\\n    )\\n    update_modifiers.add_argument(\\n        \\\"-S\\\",\\n        \\\"--satisfied-skip-solve\\\",\\n        action=\\\"store_const\\\",\\n        const=UpdateModifier.SPECS_SATISFIED_SKIP_SOLVE,\\n        dest=\\\"update_modifier\\\",\\n        default=NULL,\\n        help=\\\"Exit early and do not run the solver if the requested specs are satisfied. \\\"\\n        \\\"Also skips aggressive updates as configured by the \\\"\\n        \\\"'aggressive_update_packages' config setting. Use \\\"\\n        \\\"'conda config --describe aggressive_update_packages' to view your setting. \\\"\\n        \\\"--satisfied-skip-solve is similar to the default behavior of 'pip install'.\\\",\\n    )\\n    update_modifiers.add_argument(\\n        \\\"--update-all\\\",\\n        \\\"--all\\\",\\n        action=\\\"store_const\\\",\\n        const=UpdateModifier.UPDATE_ALL,\\n        dest=\\\"update_modifier\\\",\\n        help=\\\"Update all installed packages in the environment.\\\",\\n        default=NULL,\\n    )\\n    update_modifiers.add_argument(\\n        \\\"--update-specs\\\",\\n        action=\\\"store_const\\\",\\n        const=UpdateModifier.UPDATE_SPECS,\\n        dest=\\\"update_modifier\\\",\\n        help=\\\"Update based on provided specifications.\\\",\\n        default=NULL,\\n    )\\n\\n\\ndef add_parser_prune(p: ArgumentParser) -> None:\\n    from ..common.constants import NULL\\n\\n    p.add_argument(\\n        \\\"--prune\\\",\\n        action=\\\"store_true\\\",\\n        default=NULL,\\n        help=SUPPRESS,\\n    )\\n\\n\\ndef add_parser_solver(p: ArgumentParser) -> None:\\n    \\\"\\\"\\\"\\n    Add a command-line flag for alternative solver backends.\\n\\n    See ``context.solver`` for more info.\\n    \\\"\\\"\\\"\\n    from ..base.context import context\\n    from ..common.constants import NULL\\n\\n    group = p.add_mutually_exclusive_group()\\n    group.add_argument(\\n        \\\"--solver\\\",\\n        dest=\\\"solver\\\",\\n        choices=context.plugin_manager.get_solvers(),\\n        help=\\\"Choose which solver backend to use.\\\",\\n        default=NULL,\\n    )\\n\\n\\ndef add_parser_networking(p: ArgumentParser) -> _ArgumentGroup:\\n    from ..common.constants import NULL\\n\\n    networking_options = p.add_argument_group(\\\"Networking Options\\\")\\n    networking_options.add_argument(\\n        \\\"-C\\\",\\n        \\\"--use-index-cache\\\",\\n        action=\\\"store_true\\\",\\n        default=False,\\n        help=\\\"Use cache of channel index files, even if it has expired. This is useful \\\"\\n        \\\"if you don't want conda to check whether a new version of the repodata \\\"\\n        \\\"file exists, which will save bandwidth.\\\",\\n    )\\n    networking_options.add_argument(\\n        \\\"-k\\\",\\n        \\\"--insecure\\\",\\n        action=\\\"store_false\\\",\\n        dest=\\\"ssl_verify\\\",\\n        default=NULL,\\n        help='Allow conda to perform \\\"insecure\\\" SSL connections and transfers. '\\n        \\\"Equivalent to setting 'ssl_verify' to 'false'.\\\",\\n    )\\n    networking_options.add_argument(\\n        \\\"--offline\\\",\\n        action=\\\"store_true\\\",\\n        default=NULL,\\n        help=\\\"Offline mode. Don't connect to the Internet.\\\",\\n    )\\n    return networking_options\\n\\n\\ndef add_parser_package_install_options(p: ArgumentParser) -> _ArgumentGroup:\\n    from ..common.constants import NULL\\n\\n    package_install_options = p.add_argument_group(\\n        \\\"Package Linking and Install-time Options\\\"\\n    )\\n    package_install_options.add_argument(\\n        \\\"-f\\\",\\n        \\\"--force\\\",\\n        action=\\\"store_true\\\",\\n        default=NULL,\\n        help=SUPPRESS,\\n    )\\n    package_install_options.add_argument(\\n        \\\"--copy\\\",\\n        action=\\\"store_true\\\",\\n        default=NULL,\\n        help=\\\"Install all packages using copies instead of hard- or soft-linking.\\\",\\n    )\\n    package_install_options.add_argument(\\n        \\\"--shortcuts\\\",\\n        action=\\\"store_true\\\",\\n        help=SUPPRESS,\\n        dest=\\\"shortcuts\\\",\\n        default=NULL,\\n    )\\n    package_install_options.add_argument(\\n        \\\"--no-shortcuts\\\",\\n        action=\\\"store_false\\\",\\n        help=\\\"Don't install start menu shortcuts\\\",\\n        dest=\\\"shortcuts\\\",\\n        default=NULL,\\n    )\\n    package_install_options.add_argument(\\n        \\\"--shortcuts-only\\\",\\n        action=\\\"append\\\",\\n        help=\\\"Install shortcuts only for this package name. Can be used several times.\\\",\\n        dest=\\\"shortcuts_only\\\",\\n    )\\n    return package_install_options\\n\\n\\ndef add_parser_known(p: ArgumentParser) -> None:\\n    p.add_argument(\\n        \\\"--unknown\\\",\\n        action=\\\"store_true\\\",\\n        default=False,\\n        dest=\\\"unknown\\\",\\n        help=SUPPRESS,\\n    )\\n\\n\\ndef add_parser_default_packages(p: ArgumentParser) -> None:\\n    p.add_argument(\\n        \\\"--no-default-packages\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Ignore create_default_packages in the .condarc file.\\\",\\n    )\\n\\n\\ndef add_parser_platform(parser):\\n    from ..base.constants import KNOWN_SUBDIRS\\n    from ..common.constants import NULL\\n\\n    parser.add_argument(\\n        \\\"--subdir\\\",\\n        \\\"--platform\\\",\\n        default=NULL,\\n        dest=\\\"subdir\\\",\\n        choices=[s for s in KNOWN_SUBDIRS if s != \\\"noarch\\\"],\\n        metavar=\\\"SUBDIR\\\",\\n        help=\\\"Use packages built for this platform. \\\"\\n        \\\"The new environment will be configured to remember this choice. \\\"\\n        \\\"Should be formatted like 'osx-64', 'linux-32', 'win-64', and so on. \\\"\\n        \\\"Defaults to the current (native) platform.\\\",\\n    )\\n\\n\\ndef add_parser_verbose(parser: ArgumentParser | _ArgumentGroup) -> None:\\n    from ..common.constants import NULL\\n    from .actions import NullCountAction\\n\\n    parser.add_argument(\\n        \\\"-v\\\",\\n        \\\"--verbose\\\",\\n        action=NullCountAction,\\n        help=(\\n            \\\"Can be used multiple times. Once for detailed output, twice for INFO logging, \\\"\\n            \\\"thrice for DEBUG logging, four times for TRACE logging.\\\"\\n        ),\\n        dest=\\\"verbosity\\\",\\n        default=NULL,\\n    )\\n    parser.add_argument(\\n        \\\"--debug\\\",\\n        action=\\\"store_true\\\",\\n        help=SUPPRESS,\\n        default=NULL,\\n    )\\n    parser.add_argument(\\n        \\\"--trace\\\",\\n        action=\\\"store_true\\\",\\n        help=SUPPRESS,\\n        default=NULL,\\n    )\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"CLI implementation for `conda-env config`.\\n\\nAllows for programmatically interacting with conda-env's configuration files (e.g., `~/.condarc`).\\n\\\"\\\"\\\"\\n\\nfrom argparse import ArgumentParser, Namespace, _SubParsersAction\\n\\n\\ndef configure_parser(sub_parsers: _SubParsersAction, **kwargs) -> ArgumentParser:\\n    from ..auxlib.ish import dals\\n    from .main_env_vars import configure_parser as configure_vars_parser\\n\\n    summary = \\\"Configure a conda environment.\\\"\\n    description = summary\\n    epilog = dals(\\n        \\\"\\\"\\\"\\n        Examples::\\n\\n            conda env config vars list\\n            conda env config --append channels conda-forge\\n\\n        \\\"\\\"\\\"\\n    )\\n\\n    p = sub_parsers.add_parser(\\n        \\\"config\\\",\\n        help=summary,\\n        description=description,\\n        epilog=epilog,\\n        **kwargs,\\n    )\\n    p.set_defaults(func=\\\"conda.cli.main_env_config.execute\\\")\\n    config_subparser = p.add_subparsers()\\n    configure_vars_parser(config_subparser)\\n\\n    return p\\n\\n\\ndef execute(args: Namespace, parser: ArgumentParser) -> int:\\n    parser.parse_args([\\\"env\\\", \\\"config\\\", \\\"--help\\\"])\\n\\n    return 0\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"CLI implementation for `conda export`.\\n\\nDumps specified environment package specifications to the screen.\\n\\\"\\\"\\\"\\n\\nfrom argparse import (\\n    ArgumentParser,\\n    Namespace,\\n    _SubParsersAction,\\n)\\n\\nfrom ..common.configuration import YAML_EXTENSIONS\\nfrom ..exceptions import CondaValueError\\n\\n\\ndef configure_parser(sub_parsers: _SubParsersAction, **kwargs) -> ArgumentParser:\\n    from ..auxlib.ish import dals\\n    from .helpers import add_parser_json, add_parser_prefix\\n\\n    summary = \\\"Export a given environment\\\"\\n    description = summary\\n    epilog = dals(\\n        \\\"\\\"\\\"\\n        Examples::\\n\\n            conda export\\n            conda export --file FILE_NAME\\n\\n        \\\"\\\"\\\"\\n    )\\n\\n    p = sub_parsers.add_parser(\\n        \\\"export\\\",\\n        help=summary,\\n        description=description,\\n        epilog=epilog,\\n        **kwargs,\\n    )\\n\\n    p.add_argument(\\n        \\\"-c\\\",\\n        \\\"--channel\\\",\\n        action=\\\"append\\\",\\n        help=\\\"Additional channel to include in the export\\\",\\n    )\\n\\n    p.add_argument(\\n        \\\"--override-channels\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Do not include .condarc channels\\\",\\n    )\\n    add_parser_prefix(p)\\n\\n    p.add_argument(\\n        \\\"-f\\\",\\n        \\\"--file\\\",\\n        default=None,\\n        required=False,\\n        help=(\\n            \\\"File name or path for the exported environment. \\\"\\n            \\\"Note: This will silently overwrite any existing file \\\"\\n            \\\"of the same name in the current directory.\\\"\\n        ),\\n    )\\n\\n    p.add_argument(\\n        \\\"--no-builds\\\",\\n        default=False,\\n        action=\\\"store_true\\\",\\n        required=False,\\n        help=\\\"Remove build specification from dependencies\\\",\\n    )\\n\\n    p.add_argument(\\n        \\\"--ignore-channels\\\",\\n        default=False,\\n        action=\\\"store_true\\\",\\n        required=False,\\n        help=\\\"Do not include channel names with package names.\\\",\\n    )\\n    add_parser_json(p)\\n\\n    p.add_argument(\\n        \\\"--from-history\\\",\\n        default=False,\\n        action=\\\"store_true\\\",\\n        required=False,\\n        help=\\\"Build environment spec from explicit specs in history\\\",\\n    )\\n    p.set_defaults(func=\\\"conda.cli.main_export.execute\\\")\\n\\n    return p\\n\\n\\n# TODO Make this aware of channels that were used to install packages\\ndef execute(args: Namespace, parser: ArgumentParser) -> int:\\n    from ..base.context import context, determine_target_prefix, env_name\\n    from ..env.env import from_environment\\n    from .common import stdout_json\\n\\n    prefix = determine_target_prefix(context, args)\\n    env = from_environment(\\n        env_name(prefix),\\n        prefix,\\n        no_builds=args.no_builds,\\n        ignore_channels=args.ignore_channels,\\n        from_history=args.from_history,\\n    )\\n\\n    if args.override_channels:\\n        env.remove_channels()\\n\\n    if args.channel is not None:\\n        env.add_channels(args.channel)\\n\\n    if args.file is None:\\n        stdout_json(env.to_dict()) if args.json else print(env.to_yaml(), end=\\\"\\\")\\n    else:\\n        filename = args.file\\n        # check for the proper file extension; otherwise when the export file is used later,\\n        # the user will get a file parsing error\\n        if not filename.endswith(YAML_EXTENSIONS):\\n            raise CondaValueError(\\n                f\\\"Export files must have a valid extension {YAML_EXTENSIONS}: {filename}\\\"\\n            )\\n        fp = open(args.file, \\\"wb\\\")\\n        env.to_dict(stream=fp) if args.json else env.to_yaml(stream=fp)\\n        fp.close()\\n\\n    return 0\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"CLI implementation for `conda install`.\\n\\nInstalls the specified packages into an existing environment.\\n\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport sys\\nfrom argparse import _StoreTrueAction\\nfrom typing import TYPE_CHECKING\\n\\nfrom ..deprecations import deprecated\\nfrom ..notices import notices\\n\\nif TYPE_CHECKING:\\n    from argparse import ArgumentParser, Namespace, _SubParsersAction\\n\\n\\ndef configure_parser(sub_parsers: _SubParsersAction, **kwargs) -> ArgumentParser:\\n    from ..auxlib.ish import dals\\n    from ..common.constants import NULL\\n    from .actions import NullCountAction\\n    from .helpers import (\\n        add_parser_create_install_update,\\n        add_parser_prune,\\n        add_parser_solver,\\n        add_parser_update_modifiers,\\n    )\\n\\n    summary = \\\"Install a list of packages into a specified conda environment.\\\"\\n    description = dals(\\n        f\\\"\\\"\\\"\\n        {summary}\\n\\n        This command accepts a list of package specifications (e.g, bitarray=0.8)\\n        and installs a set of packages consistent with those specifications and\\n        compatible with the underlying environment. If full compatibility cannot\\n        be assured, an error is reported and the environment is not changed.\\n\\n        Conda attempts to install the newest versions of the requested packages. To\\n        accomplish this, it may update some packages that are already installed, or\\n        install additional packages. To prevent existing packages from updating,\\n        use the --freeze-installed option. This may force conda to install older\\n        versions of the requested packages, and it does not prevent additional\\n        dependency packages from being installed.\\n\\n        If you wish to skip dependency checking altogether, use the '--no-deps'\\n        option. This may result in an environment with incompatible packages, so\\n        this option must be used with great caution.\\n\\n        conda can also be called with a list of explicit conda package filenames\\n        (e.g. ./lxml-3.2.0-py27_0.tar.bz2). Using conda in this mode implies the\\n        --no-deps option, and should likewise be used with great caution. Explicit\\n        filenames and package specifications cannot be mixed in a single command.\\n        \\\"\\\"\\\"\\n    )\\n    epilog = dals(\\n        \\\"\\\"\\\"\\n        Examples:\\n\\n        Install the package 'scipy' into the currently-active environment::\\n\\n            conda install scipy\\n\\n        Install a list of packages into an environment, myenv::\\n\\n            conda install -n myenv scipy curl wheel\\n\\n        Install a specific version of 'python' into an environment, myenv::\\n\\n            conda install -p path/to/myenv python=3.11\\n\\n        \\\"\\\"\\\"\\n    )\\n\\n    p = sub_parsers.add_parser(\\n        \\\"install\\\",\\n        help=summary,\\n        description=description,\\n        epilog=epilog,\\n        **kwargs,\\n    )\\n    p.add_argument(\\n        \\\"--revision\\\",\\n        action=\\\"store\\\",\\n        help=\\\"Revert to the specified REVISION.\\\",\\n        metavar=\\\"REVISION\\\",\\n    )\\n\\n    solver_mode_options, package_install_options, _ = add_parser_create_install_update(\\n        p\\n    )\\n\\n    add_parser_prune(solver_mode_options)\\n    add_parser_solver(solver_mode_options)\\n    solver_mode_options.add_argument(\\n        \\\"--force-reinstall\\\",\\n        action=\\\"store_true\\\",\\n        default=NULL,\\n        help=\\\"Ensure that any user-requested package for the current operation is uninstalled and \\\"\\n        \\\"reinstalled, even if that package already exists in the environment.\\\",\\n    )\\n    add_parser_update_modifiers(solver_mode_options)\\n    package_install_options.add_argument(\\n        \\\"-m\\\",\\n        \\\"--mkdir\\\",\\n        action=deprecated.action(\\n            \\\"24.9\\\",\\n            \\\"25.3\\\",\\n            _StoreTrueAction,\\n            addendum=\\\"Use `conda create` instead.\\\",\\n        ),\\n    )\\n    package_install_options.add_argument(\\n        \\\"--clobber\\\",\\n        action=\\\"store_true\\\",\\n        default=NULL,\\n        help=\\\"Allow clobbering (i.e. overwriting) of overlapping file paths \\\"\\n        \\\"within packages and suppress related warnings.\\\",\\n    )\\n    p.add_argument(\\n        \\\"--dev\\\",\\n        action=NullCountAction,\\n        help=\\\"Use `sys.executable -m conda` in wrapper scripts instead of CONDA_EXE. \\\"\\n        \\\"This is mainly for use during tests where we test new conda sources \\\"\\n        \\\"against old Python versions.\\\",\\n        dest=\\\"dev\\\",\\n        default=NULL,\\n    )\\n    p.set_defaults(func=\\\"conda.cli.main_install.execute\\\")\\n\\n    return p\\n\\n\\n@notices\\ndef execute(args: Namespace, parser: ArgumentParser) -> int:\\n    from ..base.context import context\\n    from .install import install\\n\\n    if context.force:\\n        print(\\n            \\\"\\\\n\\\\n\\\"\\n            \\\"WARNING: The --force flag will be removed in a future conda release.\\\\n\\\"\\n            \\\"         See 'conda install --help' for details about the --force-reinstall\\\\n\\\"\\n            \\\"         and --clobber flags.\\\\n\\\"\\n            \\\"\\\\n\\\",\\n            file=sys.stderr,\\n        )\\n\\n    return install(args, parser, \\\"install\\\")\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"CLI implementation for `conda search`.\\n\\nQuery channels for packages matching the provided package spec.\\n\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nfrom argparse import SUPPRESS\\nfrom collections import defaultdict\\nfrom datetime import datetime, timezone\\nfrom typing import TYPE_CHECKING\\n\\nif TYPE_CHECKING:\\n    from argparse import ArgumentParser, Namespace, _SubParsersAction\\n\\n    from ..models.records import PackageRecord\\n\\n\\ndef configure_parser(sub_parsers: _SubParsersAction, **kwargs) -> ArgumentParser:\\n    from ..auxlib.ish import dals\\n    from ..common.constants import NULL\\n    from .helpers import (\\n        add_parser_channels,\\n        add_parser_json,\\n        add_parser_known,\\n        add_parser_networking,\\n    )\\n\\n    summary = \\\"Search for packages and display associated information using the MatchSpec format.\\\"\\n    description = dals(\\n        f\\\"\\\"\\\"\\n        {summary}\\n\\n        MatchSpec is a query language for conda packages.\\n        \\\"\\\"\\\"\\n    )\\n    epilog = dals(\\n        \\\"\\\"\\\"\\n        Examples:\\n\\n        Search for a specific package named 'scikit-learn'::\\n\\n            conda search scikit-learn\\n\\n        Search for packages containing 'scikit' in the package name::\\n\\n            conda search *scikit*\\n\\n        Note that your shell may expand '*' before handing the command over to conda.\\n        Therefore, it is sometimes necessary to use single or double quotes around the query::\\n\\n            conda search '*scikit'\\n            conda search \\\"*scikit*\\\"\\n\\n        Search for packages for 64-bit Linux (by default, packages for your current\\n        platform are shown)::\\n\\n            conda search numpy[subdir=linux-64]\\n\\n        Search for a specific version of a package::\\n\\n            conda search 'numpy>=1.12'\\n\\n        Search for a package on a specific channel::\\n\\n            conda search conda-forge::numpy\\n            conda search 'numpy[channel=conda-forge, subdir=osx-64]'\\n        \\\"\\\"\\\"\\n    )\\n\\n    p = sub_parsers.add_parser(\\n        \\\"search\\\",\\n        help=summary,\\n        description=description,\\n        epilog=epilog,\\n        **kwargs,\\n    )\\n    p.add_argument(\\n        \\\"--envs\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Search all of the current user's environments. If run as Administrator \\\"\\n        \\\"(on Windows) or UID 0 (on unix), search all known environments on the system.\\\",\\n    )\\n    p.add_argument(\\n        \\\"-i\\\",\\n        \\\"--info\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Provide detailed information about each package.\\\",\\n    )\\n    p.add_argument(\\n        \\\"--subdir\\\",\\n        \\\"--platform\\\",\\n        action=\\\"store\\\",\\n        dest=\\\"subdir\\\",\\n        help=\\\"Search the given subdir. Should be formatted like 'osx-64', 'linux-32', \\\"\\n        \\\"'win-64', and so on. The default is to search the current platform.\\\",\\n        default=NULL,\\n    )\\n    p.add_argument(\\n        \\\"--skip-flexible-search\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Do not perform flexible search if initial search fails.\\\",\\n    )\\n    p.add_argument(\\n        \\\"match_spec\\\",\\n        default=\\\"*\\\",\\n        nargs=\\\"?\\\",\\n        help=SUPPRESS,\\n    )\\n    p.add_argument(\\n        \\\"--canonical\\\",\\n        action=\\\"store_true\\\",\\n        help=SUPPRESS,\\n    )\\n    p.add_argument(\\n        \\\"-f\\\",\\n        \\\"--full-name\\\",\\n        action=\\\"store_true\\\",\\n        help=SUPPRESS,\\n    )\\n    p.add_argument(\\n        \\\"--names-only\\\",\\n        action=\\\"store_true\\\",\\n        help=SUPPRESS,\\n    )\\n    add_parser_known(p)\\n    p.add_argument(\\n        \\\"-o\\\",\\n        \\\"--outdated\\\",\\n        action=\\\"store_true\\\",\\n        help=SUPPRESS,\\n    )\\n    p.add_argument(\\n        \\\"--spec\\\",\\n        action=\\\"store_true\\\",\\n        help=SUPPRESS,\\n    )\\n    p.add_argument(\\n        \\\"--reverse-dependency\\\",\\n        action=\\\"store_true\\\",\\n        # help=\\\"Perform a reverse dependency search. Use 'conda search package --info' \\\"\\n        #      \\\"to see the dependencies of a package.\\\",\\n        help=SUPPRESS,  # TODO: re-enable once we have --reverse-dependency working again\\n    )\\n\\n    add_parser_channels(p)\\n    add_parser_networking(p)\\n    add_parser_json(p)\\n    p.set_defaults(func=\\\"conda.cli.main_search.execute\\\")\\n\\n    return p\\n\\n\\ndef execute(args: Namespace, parser: ArgumentParser) -> int:\\n    \\\"\\\"\\\"\\n    Implements `conda search` commands.\\n\\n    `conda search <spec>` searches channels for packages.\\n    `conda search <spec> --envs` searches environments for packages.\\n\\n    \\\"\\\"\\\"\\n    from ..base.context import context\\n    from ..cli.common import stdout_json\\n    from ..common.io import Spinner\\n    from ..core.envs_manager import query_all_prefixes\\n    from ..core.index import calculate_channel_urls\\n    from ..core.subdir_data import SubdirData\\n    from ..models.match_spec import MatchSpec\\n    from ..models.records import PackageRecord\\n    from ..models.version import VersionOrder\\n\\n    spec = MatchSpec(args.match_spec)\\n    if spec.get_exact_value(\\\"subdir\\\"):\\n        subdirs = (spec.get_exact_value(\\\"subdir\\\"),)\\n    else:\\n        subdirs = context.subdirs\\n\\n    if args.envs:\\n        with Spinner(\\n            f\\\"Searching environments for {spec}\\\",\\n            not context.verbose and not context.quiet,\\n            context.json,\\n        ):\\n            prefix_matches = query_all_prefixes(spec)\\n            ordered_result = tuple(\\n                {\\n                    \\\"location\\\": prefix,\\n                    \\\"package_records\\\": tuple(\\n                        sorted(\\n                            (\\n                                PackageRecord.from_objects(prefix_rec)\\n                                for prefix_rec in prefix_recs\\n                            ),\\n                            key=lambda prec: prec._pkey,\\n                        )\\n                    ),\\n                }\\n                for prefix, prefix_recs in prefix_matches\\n            )\\n        if context.json:\\n            stdout_json(ordered_result)\\n        elif args.info:\\n            for pkg_group in ordered_result:\\n                for prec in pkg_group[\\\"package_records\\\"]:\\n                    pretty_record(prec)\\n        else:\\n            builder = [\\n                \\\"# %-13s %15s %15s  %-20s %-20s\\\"\\n                % (\\n                    \\\"Name\\\",\\n                    \\\"Version\\\",\\n                    \\\"Build\\\",\\n                    \\\"Channel\\\",\\n                    \\\"Location\\\",\\n                )\\n            ]\\n            for pkg_group in ordered_result:\\n                for prec in pkg_group[\\\"package_records\\\"]:\\n                    builder.append(\\n                        \\\"%-15s %15s %15s  %-20s %-20s\\\"\\n                        % (\\n                            prec.name,\\n                            prec.version,\\n                            prec.build,\\n                            prec.channel.name,\\n                            pkg_group[\\\"location\\\"],\\n                        )\\n                    )\\n            print(\\\"\\\\n\\\".join(builder))\\n        return 0\\n\\n    with Spinner(\\n        \\\"Loading channels\\\",\\n        not context.verbose and not context.quiet,\\n        context.json,\\n    ):\\n        spec_channel = spec.get_exact_value(\\\"channel\\\")\\n        channel_urls = (spec_channel,) if spec_channel else context.channels\\n\\n        matches = sorted(\\n            SubdirData.query_all(spec, channel_urls, subdirs),\\n            key=lambda rec: (rec.name, VersionOrder(rec.version), rec.build),\\n        )\\n    if not matches and not args.skip_flexible_search and spec.get_exact_value(\\\"name\\\"):\\n        flex_spec = MatchSpec(spec, name=f\\\"*{spec.name}*\\\")\\n        if not context.json:\\n            print(f\\\"No match found for: {spec}. Search: {flex_spec}\\\")\\n        matches = sorted(\\n            SubdirData.query_all(flex_spec, channel_urls, subdirs),\\n            key=lambda rec: (rec.name, VersionOrder(rec.version), rec.build),\\n        )\\n\\n    if not matches:\\n        channels_urls = tuple(\\n            calculate_channel_urls(\\n                channel_urls=context.channels,\\n                prepend=not args.override_channels,\\n                platform=subdirs[0],\\n                use_local=args.use_local,\\n            )\\n        )\\n        from ..exceptions import PackagesNotFoundError\\n\\n        raise PackagesNotFoundError((str(spec),), channels_urls)\\n\\n    if context.json:\\n        json_obj = defaultdict(list)\\n        for match in matches:\\n            json_obj[match.name].append(match)\\n        stdout_json(json_obj)\\n\\n    elif args.info:\\n        for record in matches:\\n            pretty_record(record)\\n\\n    else:\\n        builder = [\\n            \\\"# %-18s %15s %15s  %-20s\\\"\\n            % (\\n                \\\"Name\\\",\\n                \\\"Version\\\",\\n                \\\"Build\\\",\\n                \\\"Channel\\\",\\n            )\\n        ]\\n        for record in matches:\\n            builder.append(\\n                \\\"%-20s %15s %15s  %-20s\\\"\\n                % (\\n                    record.name,\\n                    record.version,\\n                    record.build,\\n                    record.channel.name,\\n                )\\n            )\\n        print(\\\"\\\\n\\\".join(builder))\\n    return 0\\n\\n\\ndef pretty_record(record: PackageRecord) -> None:\\n    \\\"\\\"\\\"\\n    Pretty prints a `PackageRecord`.\\n\\n    :param record:  The `PackageRecord` object to print.\\n    \\\"\\\"\\\"\\n    from ..common.io import dashlist\\n    from ..utils import human_bytes\\n\\n    def push_line(display_name, attr_name):\\n        value = getattr(record, attr_name, None)\\n        if value is not None:\\n            builder.append(\\\"%-12s: %s\\\" % (display_name, value))\\n\\n    builder = []\\n    builder.append(record.name + \\\" \\\" + record.version + \\\" \\\" + record.build)\\n    builder.append(\\\"-\\\" * len(builder[0]))\\n\\n    push_line(\\\"file name\\\", \\\"fn\\\")\\n    push_line(\\\"name\\\", \\\"name\\\")\\n    push_line(\\\"version\\\", \\\"version\\\")\\n    push_line(\\\"build\\\", \\\"build\\\")\\n    push_line(\\\"build number\\\", \\\"build_number\\\")\\n    size = getattr(record, \\\"size\\\", None)\\n    if size is not None:\\n        builder.append(\\\"%-12s: %s\\\" % (\\\"size\\\", human_bytes(size)))\\n    push_line(\\\"license\\\", \\\"license\\\")\\n    push_line(\\\"subdir\\\", \\\"subdir\\\")\\n    push_line(\\\"url\\\", \\\"url\\\")\\n    push_line(\\\"md5\\\", \\\"md5\\\")\\n    if record.timestamp:\\n        date_str = datetime.fromtimestamp(record.timestamp, timezone.utc).strftime(\\n            \\\"%Y-%m-%d %H:%M:%S %Z\\\"\\n        )\\n        builder.append(\\\"%-12s: %s\\\" % (\\\"timestamp\\\", date_str))\\n    if record.track_features:\\n        builder.append(\\n            \\\"%-12s: %s\\\" % (\\\"track_features\\\", dashlist(record.track_features))\\n        )\\n    if record.constrains:\\n        builder.append(\\\"%-12s: %s\\\" % (\\\"constraints\\\", dashlist(record.constrains)))\\n    builder.append(\\n        \\\"%-12s: %s\\\"\\n        % (\\\"dependencies\\\", dashlist(record.depends) if record.depends else \\\"[]\\\")\\n    )\\n    builder.append(\\\"\\\\n\\\")\\n    print(\\\"\\\\n\\\".join(builder))\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Entry point for all conda-env subcommands.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nfrom argparse import ArgumentParser\\nfrom typing import TYPE_CHECKING\\n\\nfrom ..deprecations import deprecated\\nfrom . import main_export\\n\\nif TYPE_CHECKING:\\n    from argparse import Namespace, _SubParsersAction\\n\\n\\ndef configure_parser(sub_parsers: _SubParsersAction | None, **kwargs) -> ArgumentParser:\\n    from . import (\\n        main_env_config,\\n        main_env_create,\\n        main_env_list,\\n        main_env_remove,\\n        main_env_update,\\n    )\\n\\n    # This is a backport for the deprecated `conda_env`, see `conda_env.cli.main`\\n    if sub_parsers is None:\\n        deprecated.topic(\\n            \\\"24.9\\\",\\n            \\\"25.3\\\",\\n            topic=\\\"'conda_env'\\\",\\n        )\\n        p = ArgumentParser()\\n\\n    else:\\n        p = sub_parsers.add_parser(\\n            \\\"env\\\",\\n            **kwargs,\\n        )\\n\\n    env_parsers = p.add_subparsers(\\n        metavar=\\\"command\\\",\\n        dest=\\\"cmd\\\",\\n    )\\n    main_env_config.configure_parser(env_parsers)\\n    main_env_create.configure_parser(env_parsers)\\n    main_export.configure_parser(env_parsers)\\n    main_env_list.configure_parser(env_parsers)\\n    main_env_remove.configure_parser(env_parsers)\\n    main_env_update.configure_parser(env_parsers)\\n\\n    p.set_defaults(func=\\\"conda.cli.main_env.execute\\\")\\n    return p\\n\\n\\ndef execute(args: Namespace, parser: ArgumentParser) -> int:\\n    parser.parse_args([\\\"env\\\", \\\"--help\\\"])\\n\\n    return 0\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Mock CLI implementation for `conda deactivate`.\\n\\nA mock implementation of the deactivate shell command for better UX.\\n\\\"\\\"\\\"\\n\\nfrom .. import CondaError\\n\\n\\ndef configure_parser(sub_parsers):\\n    p = sub_parsers.add_parser(\\n        \\\"deactivate\\\",\\n        help=\\\"Deactivate the current active conda environment.\\\",\\n    )\\n    p.set_defaults(func=\\\"conda.cli.main_mock_deactivate.execute\\\")\\n\\n\\ndef execute(args, parser):\\n    raise CondaError(\\\"Run 'conda init' before 'conda deactivate'\\\")\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"CLI implementation for `conda run`.\\n\\nRuns the provided command within the specified environment.\\n\\\"\\\"\\\"\\n\\nimport os\\nimport sys\\nfrom argparse import REMAINDER, ArgumentParser, Namespace, _SubParsersAction\\nfrom logging import getLogger\\n\\n\\ndef configure_parser(sub_parsers: _SubParsersAction, **kwargs) -> ArgumentParser:\\n    from ..auxlib.ish import dals\\n    from ..common.constants import NULL\\n    from .actions import NullCountAction\\n    from .helpers import add_parser_prefix, add_parser_verbose\\n\\n    summary = \\\"Run an executable in a conda environment.\\\"\\n    description = summary\\n    epilog = dals(\\n        \\\"\\\"\\\"\\n        Example::\\n\\n        $ conda create -y -n my-python-env python=3\\n        $ conda run -n my-python-env python --version\\n        \\\"\\\"\\\"\\n    )\\n\\n    p = sub_parsers.add_parser(\\n        \\\"run\\\",\\n        help=summary,\\n        description=description,\\n        epilog=epilog,\\n        **kwargs,\\n    )\\n\\n    add_parser_prefix(p)\\n    add_parser_verbose(p)\\n\\n    p.add_argument(\\n        \\\"--dev\\\",\\n        action=NullCountAction,\\n        help=\\\"Sets `CONDA_EXE` to `python -m conda`, assuming the current \\\"\\n        \\\"working directory contains the root of conda development sources. \\\"\\n        \\\"This is mainly for use during tests where we test new conda sources \\\"\\n        \\\"against old Python versions.\\\",\\n        dest=\\\"dev\\\",\\n        default=NULL,\\n    )\\n\\n    p.add_argument(\\n        \\\"--debug-wrapper-scripts\\\",\\n        action=NullCountAction,\\n        help=\\\"When this is set, where implemented, the shell wrapper scripts\\\"\\n        \\\"will use the echo command to print debugging information to \\\"\\n        \\\"stderr (standard error).\\\",\\n        dest=\\\"debug_wrapper_scripts\\\",\\n        default=NULL,\\n    )\\n    p.add_argument(\\n        \\\"--cwd\\\",\\n        help=\\\"Current working directory for command to run in. Defaults to \\\"\\n        \\\"the user's current working directory if no directory is specified.\\\",\\n        default=os.getcwd(),\\n    )\\n    p.add_argument(\\n        \\\"--no-capture-output\\\",\\n        \\\"--live-stream\\\",\\n        action=\\\"store_true\\\",\\n        help=\\\"Don't capture stdout/stderr (standard out/standard error).\\\",\\n        default=False,\\n    )\\n\\n    p.add_argument(\\n        \\\"executable_call\\\",\\n        nargs=REMAINDER,\\n        help=\\\"Executable name, with additional arguments to be passed to the executable \\\"\\n        \\\"on invocation.\\\",\\n    )\\n\\n    p.set_defaults(func=\\\"conda.cli.main_run.execute\\\")\\n\\n    return p\\n\\n\\ndef execute(args: Namespace, parser: ArgumentParser) -> int:\\n    from ..base.context import context\\n    from ..common.compat import encode_environment\\n    from ..gateways.disk.delete import rm_rf\\n    from ..gateways.subprocess import subprocess_call\\n    from ..utils import wrap_subprocess_call\\n    from .common import validate_prefix\\n\\n    # create run script\\n    script, command = wrap_subprocess_call(\\n        context.root_prefix,\\n        validate_prefix(context.target_prefix),  # ensure prefix exists\\n        args.dev,\\n        args.debug_wrapper_scripts,\\n        args.executable_call,\\n        use_system_tmp_path=True,\\n    )\\n\\n    # run script\\n    response = subprocess_call(\\n        command,\\n        env=encode_environment(os.environ.copy()),\\n        path=args.cwd,\\n        raise_on_error=False,\\n        capture_output=not args.no_capture_output,\\n    )\\n\\n    # display stdout/stderr if it was captured\\n    if not args.no_capture_output:\\n        if response.stdout:\\n            print(response.stdout, file=sys.stdout)\\n        if response.stderr:\\n            print(response.stderr, file=sys.stderr)\\n\\n    # log error\\n    if response.rc != 0:\\n        log = getLogger(__name__)\\n        log.error(\\n            f\\\"`conda run {' '.join(args.executable_call)}` failed. (See above for error)\\\"\\n        )\\n\\n    # remove script\\n    if \\\"CONDA_TEST_SAVE_TEMPS\\\" not in os.environ:\\n        rm_rf(script)\\n    else:\\n        log = getLogger(__name__)\\n        log.warning(f\\\"CONDA_TEST_SAVE_TEMPS :: retaining main_run script {script}\\\")\\n\\n    return response.rc\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"CLI implementation for `conda-env remove`.\\n\\nRemoves the specified conda environment.\\n\\\"\\\"\\\"\\n\\nfrom argparse import (\\n    ArgumentParser,\\n    Namespace,\\n    _SubParsersAction,\\n)\\n\\n\\ndef configure_parser(sub_parsers: _SubParsersAction, **kwargs) -> ArgumentParser:\\n    from ..auxlib.ish import dals\\n    from .helpers import (\\n        add_output_and_prompt_options,\\n        add_parser_prefix,\\n        add_parser_solver,\\n    )\\n\\n    summary = \\\"Remove an environment.\\\"\\n    description = dals(\\n        f\\\"\\\"\\\"\\n        {summary}\\n\\n        Removes a provided environment.  You must deactivate the existing\\n        environment before you can remove it.\\n\\n        \\\"\\\"\\\"\\n    )\\n    epilog = dals(\\n        \\\"\\\"\\\"\\n        Examples::\\n\\n            conda env remove --name FOO\\n            conda env remove -n FOO\\n\\n        \\\"\\\"\\\"\\n    )\\n\\n    p = sub_parsers.add_parser(\\n        \\\"remove\\\",\\n        help=summary,\\n        description=description,\\n        epilog=epilog,\\n        **kwargs,\\n    )\\n\\n    add_parser_prefix(p)\\n    add_parser_solver(p)\\n    add_output_and_prompt_options(p)\\n\\n    p.set_defaults(func=\\\"conda.cli.main_env_remove.execute\\\")\\n\\n    return p\\n\\n\\ndef execute(args: Namespace, parser: ArgumentParser) -> int:\\n    from ..base.context import context\\n    from ..cli.main_remove import execute as remove\\n\\n    args = vars(args)\\n    args.update(\\n        {\\n            \\\"all\\\": True,\\n            \\\"channel\\\": None,\\n            \\\"features\\\": None,\\n            \\\"override_channels\\\": None,\\n            \\\"use_local\\\": None,\\n            \\\"use_cache\\\": None,\\n            \\\"offline\\\": None,\\n            \\\"force\\\": True,\\n            \\\"pinned\\\": None,\\n            \\\"keep_env\\\": False,\\n        }\\n    )\\n    args = Namespace(**args)\\n\\n    context.__init__(argparse_args=args)\\n\\n    remove(args, parser)\\n\\n    return 0\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Conda package installation logic.\\n\\nCore logic for `conda [create|install|update|remove]` commands.\\n\\nSee conda.cli.main_create, conda.cli.main_install, conda.cli.main_update, and\\nconda.cli.main_remove for the entry points into this module.\\n\\\"\\\"\\\"\\n\\nimport os\\nfrom logging import getLogger\\nfrom os.path import abspath, basename, exists, isdir, isfile, join\\nfrom pathlib import Path\\n\\nfrom boltons.setutils import IndexedSet\\n\\nfrom .. import CondaError\\nfrom ..auxlib.ish import dals\\nfrom ..base.constants import REPODATA_FN, ROOT_ENV_NAME, DepsModifier, UpdateModifier\\nfrom ..base.context import context, locate_prefix_by_name\\nfrom ..common.constants import NULL\\nfrom ..common.io import Spinner\\nfrom ..common.path import is_package_file, paths_equal\\nfrom ..core.index import (\\n    _supplement_index_with_prefix,\\n    calculate_channel_urls,\\n    get_index,\\n)\\nfrom ..core.link import PrefixSetup, UnlinkLinkTransaction\\nfrom ..core.prefix_data import PrefixData\\nfrom ..core.solve import diff_for_unlink_link_precs\\nfrom ..exceptions import (\\n    CondaExitZero,\\n    CondaImportError,\\n    CondaIndexError,\\n    CondaOSError,\\n    CondaSystemExit,\\n    CondaValueError,\\n    DirectoryNotACondaEnvironmentError,\\n    DirectoryNotFoundError,\\n    DryRunExit,\\n    EnvironmentLocationNotFound,\\n    NoBaseEnvironmentError,\\n    OperationNotAllowed,\\n    PackageNotInstalledError,\\n    PackagesNotFoundError,\\n    ResolvePackageNotFound,\\n    SpecsConfigurationConflictError,\\n    TooManyArgumentsError,\\n    UnsatisfiableError,\\n)\\nfrom ..gateways.disk.create import mkdir_p\\nfrom ..gateways.disk.delete import delete_trash, path_is_clean\\nfrom ..history import History\\nfrom ..misc import _get_best_prec_match, clone_env, explicit, touch_nonadmin\\nfrom ..models.match_spec import MatchSpec\\nfrom ..models.prefix_graph import PrefixGraph\\nfrom . import common\\nfrom .common import check_non_admin\\nfrom .main_config import set_keys\\n\\nlog = getLogger(__name__)\\nstderrlog = getLogger(\\\"conda.stderr\\\")\\n\\n\\ndef check_prefix(prefix, json=False):\\n    if os.pathsep in prefix:\\n        raise CondaValueError(\\n            f\\\"Cannot create a conda environment with '{os.pathsep}' in the prefix. Aborting.\\\"\\n        )\\n    name = basename(prefix)\\n    error = None\\n    if name == ROOT_ENV_NAME:\\n        error = f\\\"'{name}' is a reserved environment name\\\"\\n    if exists(prefix):\\n        if isdir(prefix) and \\\"conda-meta\\\" not in tuple(\\n            entry.name for entry in os.scandir(prefix)\\n        ):\\n            return None\\n        error = f\\\"prefix already exists: {prefix}\\\"\\n\\n    if error:\\n        raise CondaValueError(error, json)\\n\\n    if \\\" \\\" in prefix:\\n        stderrlog.warning(\\n            \\\"WARNING: A space was detected in your requested environment path:\\\\n\\\"\\n            f\\\"'{prefix}'\\\\n\\\"\\n            \\\"Spaces in paths can sometimes be problematic. To minimize issues,\\\\n\\\"\\n            \\\"make sure you activate your environment before running any executables!\\\\n\\\"\\n        )\\n\\n\\ndef clone(src_arg, dst_prefix, json=False, quiet=False, index_args=None):\\n    if os.sep in src_arg:\\n        src_prefix = abspath(src_arg)\\n        if not isdir(src_prefix):\\n            raise DirectoryNotFoundError(src_arg)\\n    else:\\n        src_prefix = locate_prefix_by_name(src_arg)\\n\\n    if not json:\\n        print(f\\\"Source:      {src_prefix}\\\")\\n        print(f\\\"Destination: {dst_prefix}\\\")\\n\\n    actions, untracked_files = clone_env(\\n        src_prefix, dst_prefix, verbose=not json, quiet=quiet, index_args=index_args\\n    )\\n\\n    if json:\\n        common.stdout_json_success(\\n            actions=actions,\\n            untracked_files=list(untracked_files),\\n            src_prefix=src_prefix,\\n            dst_prefix=dst_prefix,\\n        )\\n\\n\\ndef print_activate(env_name_or_prefix):  # pragma: no cover\\n    if not context.quiet and not context.json:\\n        if \\\" \\\" in env_name_or_prefix:\\n            env_name_or_prefix = f'\\\"{env_name_or_prefix}\\\"'\\n        message = dals(\\n            f\\\"\\\"\\\"\\n        #\\n        # To activate this environment, use\\n        #\\n        #     $ conda activate {env_name_or_prefix}\\n        #\\n        # To deactivate an active environment, use\\n        #\\n        #     $ conda deactivate\\n        \\\"\\\"\\\"\\n        )\\n        print(message)  # TODO: use logger\\n\\n\\ndef get_revision(arg, json=False):\\n    try:\\n        return int(arg)\\n    except ValueError:\\n        raise CondaValueError(f\\\"expected revision number, not: '{arg}'\\\", json)\\n\\n\\ndef install(args, parser, command=\\\"install\\\"):\\n    \\\"\\\"\\\"Logic for `conda install`, `conda update`, and `conda create`.\\\"\\\"\\\"\\n    context.validate_configuration()\\n    check_non_admin()\\n    # this is sort of a hack.  current_repodata.json may not have any .tar.bz2 files,\\n    #    because it deduplicates records that exist as both formats.  Forcing this to\\n    #    repodata.json ensures that .tar.bz2 files are available\\n    if context.use_only_tar_bz2:\\n        args.repodata_fns = (\\\"repodata.json\\\",)\\n\\n    newenv = bool(command == \\\"create\\\")\\n    isupdate = bool(command == \\\"update\\\")\\n    isinstall = bool(command == \\\"install\\\")\\n    isremove = bool(command == \\\"remove\\\")\\n    prefix = context.target_prefix\\n    if context.force_32bit and prefix == context.root_prefix:\\n        raise CondaValueError(\\\"cannot use CONDA_FORCE_32BIT=1 in base env\\\")\\n    if isupdate and not (\\n        args.file\\n        or args.packages\\n        or context.update_modifier == UpdateModifier.UPDATE_ALL\\n    ):\\n        raise CondaValueError(\\n            \\\"\\\"\\\"no package names supplied\\n# Example: conda update -n myenv scipy\\n\\\"\\\"\\\"\\n        )\\n\\n    if newenv:\\n        check_prefix(prefix, json=context.json)\\n        if context.subdir != context._native_subdir():\\n            # We will only allow a different subdir if it's specified by global\\n            # configuration, environment variable or command line argument. IOW,\\n            # prevent a non-base env configured for a non-native subdir from leaking\\n            # its subdir to a newer env.\\n            context_sources = context.collect_all()\\n            if context_sources.get(\\\"cmd_line\\\", {}).get(\\\"subdir\\\") == context.subdir:\\n                pass  # this is ok\\n            elif context_sources.get(\\\"envvars\\\", {}).get(\\\"subdir\\\") == context.subdir:\\n                pass  # this is ok too\\n            # config does not come from envvars or cmd_line, it must be a file\\n            # that's ok as long as it's a base env or a global file\\n            elif not paths_equal(context.active_prefix, context.root_prefix):\\n                # this is only ok as long as it's base environment\\n                active_env_config = next(\\n                    (\\n                        config\\n                        for path, config in context_sources.items()\\n                        if paths_equal(context.active_prefix, path.parent)\\n                    ),\\n                    None,\\n                )\\n                if active_env_config.get(\\\"subdir\\\") == context.subdir:\\n                    # In practice this never happens; the subdir info is not even\\n                    # loaded from the active env for conda create :shrug:\\n                    msg = dals(\\n                        f\\\"\\\"\\\"\\n                        Active environment configuration ({context.active_prefix}) is\\n                        implicitly requesting a non-native platform ({context.subdir}).\\n                        Please deactivate first or explicitly request the platform via\\n                        the --platform=[value] command line flag.\\n                        \\\"\\\"\\\"\\n                    )\\n                    raise OperationNotAllowed(msg)\\n            log.info(\\n                \\\"Creating new environment for a non-native platform %s\\\",\\n                context.subdir,\\n            )\\n    elif isdir(prefix):\\n        delete_trash(prefix)\\n        if not isfile(join(prefix, \\\"conda-meta\\\", \\\"history\\\")):\\n            if paths_equal(prefix, context.conda_prefix):\\n                raise NoBaseEnvironmentError()\\n            else:\\n                if not path_is_clean(prefix):\\n                    raise DirectoryNotACondaEnvironmentError(prefix)\\n        else:\\n            # fall-through expected under normal operation\\n            pass\\n    elif getattr(args, \\\"mkdir\\\", False):\\n        # --mkdir is deprecated and marked for removal in conda 25.3\\n        try:\\n            mkdir_p(prefix)\\n        except OSError as e:\\n            raise CondaOSError(f\\\"Could not create directory: {prefix}\\\", caused_by=e)\\n    else:\\n        raise EnvironmentLocationNotFound(prefix)\\n\\n    args_packages = [s.strip(\\\"\\\\\\\"'\\\") for s in args.packages]\\n    if newenv and not args.no_default_packages:\\n        # Override defaults if they are specified at the command line\\n        names = [MatchSpec(pkg).name for pkg in args_packages]\\n        for default_package in context.create_default_packages:\\n            if MatchSpec(default_package).name not in names:\\n                args_packages.append(default_package)\\n\\n    index_args = {\\n        \\\"use_cache\\\": args.use_index_cache,\\n        \\\"channel_urls\\\": context.channels,\\n        \\\"unknown\\\": args.unknown,\\n        \\\"prepend\\\": not args.override_channels,\\n        \\\"use_local\\\": args.use_local,\\n    }\\n\\n    num_cp = sum(is_package_file(s) for s in args_packages)\\n    if num_cp:\\n        if num_cp == len(args_packages):\\n            explicit(args_packages, prefix, verbose=not context.quiet)\\n            if newenv:\\n                touch_nonadmin(prefix)\\n                print_activate(args.name or prefix)\\n            return\\n        else:\\n            raise CondaValueError(\\n                \\\"cannot mix specifications with conda package filenames\\\"\\n            )\\n\\n    specs = []\\n    if args.file:\\n        for fpath in args.file:\\n            try:\\n                specs.extend(common.specs_from_url(fpath, json=context.json))\\n            except UnicodeError:\\n                raise CondaError(\\n                    \\\"Error reading file, file should be a text file containing\\\"\\n                    \\\" packages \\\\nconda create --help for details\\\"\\n                )\\n        if \\\"@EXPLICIT\\\" in specs:\\n            explicit(specs, prefix, verbose=not context.quiet, index_args=index_args)\\n            if newenv:\\n                touch_nonadmin(prefix)\\n                print_activate(args.name or prefix)\\n            return\\n    specs.extend(common.specs_from_args(args_packages, json=context.json))\\n\\n    if isinstall and args.revision:\\n        get_revision(args.revision, json=context.json)\\n    elif isinstall and not (args.file or args_packages):\\n        raise CondaValueError(\\n            \\\"too few arguments, must supply command line package specs or --file\\\"\\n        )\\n\\n    # for 'conda update', make sure the requested specs actually exist in the prefix\\n    # and that they are name-only specs\\n    if isupdate and context.update_modifier != UpdateModifier.UPDATE_ALL:\\n        prefix_data = PrefixData(prefix)\\n        for spec in specs:\\n            spec = MatchSpec(spec)\\n            if not spec.is_name_only_spec:\\n                raise CondaError(\\n                    f\\\"Invalid spec for 'conda update': {spec}\\\\n\\\"\\n                    \\\"Use 'conda install' instead.\\\"\\n                )\\n            if not prefix_data.get(spec.name, None):\\n                raise PackageNotInstalledError(prefix, spec.name)\\n\\n    if newenv and args.clone:\\n        if args.packages:\\n            raise TooManyArgumentsError(\\n                0,\\n                len(args.packages),\\n                list(args.packages),\\n                \\\"did not expect any arguments for --clone\\\",\\n            )\\n\\n        clone(\\n            args.clone,\\n            prefix,\\n            json=context.json,\\n            quiet=context.quiet,\\n            index_args=index_args,\\n        )\\n        touch_nonadmin(prefix)\\n        print_activate(args.name or prefix)\\n        return\\n\\n    repodata_fns = args.repodata_fns\\n    if not repodata_fns:\\n        repodata_fns = list(context.repodata_fns)\\n    if REPODATA_FN not in repodata_fns:\\n        repodata_fns.append(REPODATA_FN)\\n\\n    args_set_update_modifier = (\\n        hasattr(args, \\\"update_modifier\\\") and args.update_modifier != NULL\\n    )\\n    # This helps us differentiate between an update, the --freeze-installed option, and the retry\\n    # behavior in our initial fast frozen solve\\n    _should_retry_unfrozen = (\\n        not args_set_update_modifier\\n        or args.update_modifier\\n        not in (UpdateModifier.FREEZE_INSTALLED, UpdateModifier.UPDATE_SPECS)\\n    ) and not newenv\\n\\n    for repodata_fn in repodata_fns:\\n        try:\\n            if isinstall and args.revision:\\n                with Spinner(\\n                    f\\\"Collecting package metadata ({repodata_fn})\\\",\\n                    not context.verbose and not context.quiet,\\n                    context.json,\\n                ):\\n                    index = get_index(\\n                        channel_urls=index_args[\\\"channel_urls\\\"],\\n                        prepend=index_args[\\\"prepend\\\"],\\n                        platform=None,\\n                        use_local=index_args[\\\"use_local\\\"],\\n                        use_cache=index_args[\\\"use_cache\\\"],\\n                        unknown=index_args[\\\"unknown\\\"],\\n                        prefix=prefix,\\n                        repodata_fn=repodata_fn,\\n                    )\\n                revision_idx = get_revision(args.revision)\\n                with Spinner(\\n                    f\\\"Reverting to revision {revision_idx}\\\",\\n                    not context.verbose and not context.quiet,\\n                    context.json,\\n                ):\\n                    unlink_link_transaction = revert_actions(\\n                        prefix, revision_idx, index\\n                    )\\n            else:\\n                solver_backend = context.plugin_manager.get_cached_solver_backend()\\n                solver = solver_backend(\\n                    prefix,\\n                    context.channels,\\n                    context.subdirs,\\n                    specs_to_add=specs,\\n                    repodata_fn=repodata_fn,\\n                    command=args.cmd,\\n                )\\n                update_modifier = context.update_modifier\\n                if (isinstall or isremove) and args.update_modifier == NULL:\\n                    update_modifier = UpdateModifier.FREEZE_INSTALLED\\n                deps_modifier = context.deps_modifier\\n                if isupdate:\\n                    deps_modifier = context.deps_modifier or DepsModifier.UPDATE_SPECS\\n\\n                unlink_link_transaction = solver.solve_for_transaction(\\n                    deps_modifier=deps_modifier,\\n                    update_modifier=update_modifier,\\n                    force_reinstall=context.force_reinstall or context.force,\\n                    should_retry_solve=(\\n                        _should_retry_unfrozen or repodata_fn != repodata_fns[-1]\\n                    ),\\n                )\\n            # we only need one of these to work.  If we haven't raised an exception,\\n            #   we're good.\\n            break\\n\\n        except (ResolvePackageNotFound, PackagesNotFoundError) as e:\\n            if not getattr(e, \\\"allow_retry\\\", True):\\n                raise e  # see note in next except block\\n            # end of the line.  Raise the exception\\n            if repodata_fn == repodata_fns[-1]:\\n                # PackagesNotFoundError is the only exception type we want to raise.\\n                #    Over time, we should try to get rid of ResolvePackageNotFound\\n                if isinstance(e, PackagesNotFoundError):\\n                    raise e\\n                else:\\n                    channels_urls = tuple(\\n                        calculate_channel_urls(\\n                            channel_urls=index_args[\\\"channel_urls\\\"],\\n                            prepend=index_args[\\\"prepend\\\"],\\n                            platform=None,\\n                            use_local=index_args[\\\"use_local\\\"],\\n                        )\\n                    )\\n                    # convert the ResolvePackageNotFound into PackagesNotFoundError\\n                    raise PackagesNotFoundError(e._formatted_chains, channels_urls)\\n\\n        except (UnsatisfiableError, SystemExit, SpecsConfigurationConflictError) as e:\\n            if not getattr(e, \\\"allow_retry\\\", True):\\n                # TODO: This is a temporary workaround to allow downstream libraries\\n                # to inject this attribute set to False and skip the retry logic\\n                # Other solvers might implement their own internal retry logic without\\n                # depending --freeze-install implicitly like conda classic does. Example\\n                # retry loop in conda-libmamba-solver:\\n                # https://github.com/conda-incubator/conda-libmamba-solver/blob/da5b1ba/conda_libmamba_solver/solver.py#L254-L299\\n                # If we end up raising UnsatisfiableError, we annotate it with `allow_retry`\\n                # so we don't have go through all the repodatas and freeze-installed logic\\n                # unnecessarily (see https://github.com/conda/conda/issues/11294). see also:\\n                # https://github.com/conda-incubator/conda-libmamba-solver/blob/7c698209/conda_libmamba_solver/solver.py#L617\\n                raise e\\n            # Quick solve with frozen env or trimmed repodata failed.  Try again without that.\\n            if not hasattr(args, \\\"update_modifier\\\"):\\n                if repodata_fn == repodata_fns[-1]:\\n                    raise e\\n            elif _should_retry_unfrozen:\\n                try:\\n                    unlink_link_transaction = solver.solve_for_transaction(\\n                        deps_modifier=deps_modifier,\\n                        update_modifier=UpdateModifier.UPDATE_SPECS,\\n                        force_reinstall=context.force_reinstall or context.force,\\n                        should_retry_solve=(repodata_fn != repodata_fns[-1]),\\n                    )\\n                except (\\n                    UnsatisfiableError,\\n                    SystemExit,\\n                    SpecsConfigurationConflictError,\\n                ) as e:\\n                    # Unsatisfiable package specifications/no such revision/import error\\n                    if e.args and \\\"could not import\\\" in e.args[0]:\\n                        raise CondaImportError(str(e))\\n                    # we want to fall through without raising if we're not at the end of the list\\n                    #    of fns.  That way, we fall to the next fn.\\n                    if repodata_fn == repodata_fns[-1]:\\n                        raise e\\n            elif repodata_fn != repodata_fns[-1]:\\n                continue  # if we hit this, we should retry with next repodata source\\n            else:\\n                # end of the line.  Raise the exception\\n                # Unsatisfiable package specifications/no such revision/import error\\n                if e.args and \\\"could not import\\\" in e.args[0]:\\n                    raise CondaImportError(str(e))\\n                raise e\\n    handle_txn(unlink_link_transaction, prefix, args, newenv)\\n\\n\\ndef revert_actions(prefix, revision=-1, index=None):\\n    # TODO: If revision raise a revision error, should always go back to a safe revision\\n    h = History(prefix)\\n    # TODO: need a History method to get user-requested specs for revision number\\n    #       Doing a revert right now messes up user-requested spec history.\\n    #       Either need to wipe out history after ``revision``, or add the correct\\n    #       history information to the new entry about to be created.\\n    # TODO: This is wrong!!!!!!!!!!\\n    user_requested_specs = h.get_requested_specs_map().values()\\n    try:\\n        target_state = {\\n            MatchSpec.from_dist_str(dist_str) for dist_str in h.get_state(revision)\\n        }\\n    except IndexError:\\n        raise CondaIndexError(\\\"no such revision: %d\\\" % revision)\\n\\n    _supplement_index_with_prefix(index, prefix)\\n\\n    not_found_in_index_specs = set()\\n    link_precs = set()\\n    for spec in target_state:\\n        precs = tuple(prec for prec in index.values() if spec.match(prec))\\n        if not precs:\\n            not_found_in_index_specs.add(spec)\\n        elif len(precs) > 1:\\n            link_precs.add(_get_best_prec_match(precs))\\n        else:\\n            link_precs.add(precs[0])\\n\\n    if not_found_in_index_specs:\\n        raise PackagesNotFoundError(not_found_in_index_specs)\\n\\n    final_precs = IndexedSet(PrefixGraph(link_precs).graph)  # toposort\\n    unlink_precs, link_precs = diff_for_unlink_link_precs(prefix, final_precs)\\n    setup = PrefixSetup(prefix, unlink_precs, link_precs, (), user_requested_specs, ())\\n    return UnlinkLinkTransaction(setup)\\n\\n\\ndef handle_txn(unlink_link_transaction, prefix, args, newenv, remove_op=False):\\n    if unlink_link_transaction.nothing_to_do:\\n        if remove_op:\\n            # No packages found to remove from environment\\n            raise PackagesNotFoundError(args.package_names)\\n        elif not newenv:\\n            if context.json:\\n                common.stdout_json_success(\\n                    message=\\\"All requested packages already installed.\\\"\\n                )\\n            else:\\n                print(\\\"\\\\n# All requested packages already installed.\\\\n\\\")\\n            return\\n\\n    if not context.json:\\n        unlink_link_transaction.print_transaction_summary()\\n        common.confirm_yn()\\n\\n    elif context.dry_run:\\n        actions = unlink_link_transaction._make_legacy_action_groups()[0]\\n        common.stdout_json_success(prefix=prefix, actions=actions, dry_run=True)\\n        raise DryRunExit()\\n\\n    try:\\n        unlink_link_transaction.download_and_extract()\\n        if context.download_only:\\n            raise CondaExitZero(\\n                \\\"Package caches prepared. UnlinkLinkTransaction cancelled with \\\"\\n                \\\"--download-only option.\\\"\\n            )\\n        unlink_link_transaction.execute()\\n\\n    except SystemExit as e:\\n        raise CondaSystemExit(\\\"Exiting\\\", e)\\n\\n    if newenv:\\n        touch_nonadmin(prefix)\\n        if context.subdir != context._native_subdir():\\n            set_keys(\\n                (\\\"subdir\\\", context.subdir),\\n                path=Path(prefix, \\\".condarc\\\"),\\n            )\\n        print_activate(args.name or prefix)\\n\\n    if context.json:\\n        actions = unlink_link_transaction._make_legacy_action_groups()[0]\\n        common.stdout_json_success(prefix=prefix, actions=actions)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"\\nCollection of custom argparse actions.\\n\\\"\\\"\\\"\\n\\nfrom argparse import Action, _CountAction\\n\\nfrom ..common.constants import NULL\\n\\n\\nclass NullCountAction(_CountAction):\\n    @staticmethod\\n    def _ensure_value(namespace, name, value):\\n        if getattr(namespace, name, NULL) in (NULL, None):\\n            setattr(namespace, name, value)\\n        return getattr(namespace, name)\\n\\n    def __call__(self, parser, namespace, values, option_string=None):\\n        new_count = self._ensure_value(namespace, self.dest, 0) + 1\\n        setattr(namespace, self.dest, new_count)\\n\\n\\nclass ExtendConstAction(Action):\\n    \\\"\\\"\\\"\\n    A derivative of _AppendConstAction and Python 3.8's _ExtendAction\\n    \\\"\\\"\\\"\\n\\n    def __init__(\\n        self,\\n        option_strings,\\n        dest,\\n        const,\\n        default=None,\\n        type=None,\\n        choices=None,\\n        required=False,\\n        help=None,\\n        metavar=None,\\n    ):\\n        super().__init__(\\n            option_strings=option_strings,\\n            dest=dest,\\n            nargs=\\\"*\\\",\\n            const=const,\\n            default=default,\\n            type=type,\\n            choices=choices,\\n            required=required,\\n            help=help,\\n            metavar=metavar,\\n        )\\n\\n    def __call__(self, parser, namespace, values, option_string=None):\\n        items = getattr(namespace, self.dest, None)\\n        items = [] if items is None else items[:]\\n        items.extend(values or [self.const])\\n        setattr(namespace, self.dest, items)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"CLI implementation for `conda rename`.\\n\\nRenames an existing environment by cloning it and then removing the original environment.\\n\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport os\\nfrom functools import partial\\nfrom pathlib import Path\\nfrom typing import TYPE_CHECKING\\n\\nfrom ..deprecations import deprecated\\n\\nif TYPE_CHECKING:\\n    from argparse import ArgumentParser, Namespace, _SubParsersAction\\n\\n\\ndef configure_parser(sub_parsers: _SubParsersAction, **kwargs) -> ArgumentParser:\\n    from ..auxlib.ish import dals\\n    from .helpers import add_parser_prefix\\n\\n    summary = \\\"Rename an existing environment.\\\"\\n    description = dals(\\n        f\\\"\\\"\\\"\\n        {summary}\\n\\n        This command renames a conda environment via its name (-n/--name) or\\n        its prefix (-p/--prefix).\\n\\n        The base environment and the currently-active environment cannot be renamed.\\n        \\\"\\\"\\\"\\n    )\\n    epilog = dals(\\n        \\\"\\\"\\\"\\n        Examples::\\n\\n            conda rename -n test123 test321\\n\\n            conda rename --name test123 test321\\n\\n            conda rename -p path/to/test123 test321\\n\\n            conda rename --prefix path/to/test123 test321\\n\\n        \\\"\\\"\\\"\\n    )\\n\\n    p = sub_parsers.add_parser(\\n        \\\"rename\\\",\\n        help=summary,\\n        description=description,\\n        epilog=epilog,\\n        **kwargs,\\n    )\\n    # Add name and prefix args\\n    add_parser_prefix(p)\\n\\n    p.add_argument(\\\"destination\\\", help=\\\"New name for the conda environment.\\\")\\n    # TODO: deprecate --force in favor of --yes\\n    p.add_argument(\\n        \\\"--force\\\",\\n        help=\\\"Force rename of an environment.\\\",\\n        action=\\\"store_true\\\",\\n        default=False,\\n    )\\n    p.add_argument(\\n        \\\"-d\\\",\\n        \\\"--dry-run\\\",\\n        help=\\\"Only display what would have been done by the current command, arguments, \\\"\\n        \\\"and other flags.\\\",\\n        action=\\\"store_true\\\",\\n        default=False,\\n    )\\n    p.set_defaults(func=\\\"conda.cli.main_rename.execute\\\")\\n\\n    return p\\n\\n\\n@deprecated.argument(\\\"24.3\\\", \\\"24.9\\\", \\\"name\\\")\\n@deprecated.argument(\\\"24.3\\\", \\\"24.9\\\", \\\"prefix\\\")\\ndef validate_src() -> str:\\n    \\\"\\\"\\\"\\n    Validate that we are receiving at least one valid value for --name or\\n    --prefix and ensure that the \\\"base\\\" environment is not being renamed\\n    \\\"\\\"\\\"\\n    from ..base.context import context\\n    from ..exceptions import CondaEnvException\\n\\n    prefix = Path(context.target_prefix)\\n    if not prefix.exists():\\n        raise CondaEnvException(\\n            \\\"The environment you are trying to rename does not exist.\\\"\\n        )\\n    if prefix.samefile(context.root_prefix):\\n        raise CondaEnvException(\\\"The 'base' environment cannot be renamed\\\")\\n    if context.active_prefix and prefix.samefile(context.active_prefix):\\n        raise CondaEnvException(\\\"Cannot rename the active environment\\\")\\n\\n    return context.target_prefix\\n\\n\\ndef validate_destination(dest: str, force: bool = False) -> str:\\n    \\\"\\\"\\\"Ensure that our destination does not exist\\\"\\\"\\\"\\n    from ..base.context import context, validate_prefix_name\\n    from ..common.path import expand\\n    from ..exceptions import CondaEnvException\\n\\n    if os.sep in dest:\\n        dest = expand(dest)\\n    else:\\n        dest = validate_prefix_name(dest, ctx=context, allow_base=False)\\n\\n    if not force and os.path.exists(dest):\\n        env_name = os.path.basename(os.path.normpath(dest))\\n        raise CondaEnvException(\\n            f\\\"The environment '{env_name}' already exists. Override with --force.\\\"\\n        )\\n    return dest\\n\\n\\ndef execute(args: Namespace, parser: ArgumentParser) -> int:\\n    \\\"\\\"\\\"Executes the command for renaming an existing environment.\\\"\\\"\\\"\\n    from ..base.constants import DRY_RUN_PREFIX\\n    from ..base.context import context\\n    from ..cli import install\\n    from ..gateways.disk.delete import rm_rf\\n    from ..gateways.disk.update import rename_context\\n\\n    source = validate_src()\\n    destination = validate_destination(args.destination, force=args.force)\\n\\n    def clone_and_remove() -> None:\\n        actions: tuple[partial, ...] = (\\n            partial(\\n                install.clone,\\n                source,\\n                destination,\\n                quiet=context.quiet,\\n                json=context.json,\\n            ),\\n            partial(rm_rf, source),\\n        )\\n\\n        # We now either run collected actions or print dry run statement\\n        for func in actions:\\n            if args.dry_run:\\n                print(f\\\"{DRY_RUN_PREFIX} {func.func.__name__} {','.join(func.args)}\\\")\\n            else:\\n                func()\\n\\n    if args.force:\\n        with rename_context(destination, dry_run=args.dry_run):\\n            clone_and_remove()\\n    else:\\n        clone_and_remove()\\n    return 0\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"CLI implementation for `conda-env update`.\\n\\nUpdates the conda environments with the specified packages.\\n\\\"\\\"\\\"\\n\\nimport os\\nfrom argparse import (\\n    ArgumentParser,\\n    Namespace,\\n    _SubParsersAction,\\n)\\n\\nfrom .. import CondaError\\nfrom ..notices import notices\\n\\n\\ndef configure_parser(sub_parsers: _SubParsersAction, **kwargs) -> ArgumentParser:\\n    from ..auxlib.ish import dals\\n    from .helpers import (\\n        add_parser_json,\\n        add_parser_prefix,\\n        add_parser_solver,\\n    )\\n\\n    summary = \\\"Update the current environment based on environment file.\\\"\\n    description = summary\\n    epilog = dals(\\n        \\\"\\\"\\\"\\n        Examples::\\n\\n            conda env update\\n            conda env update -n=foo\\n            conda env update -f=/path/to/environment.yml\\n            conda env update --name=foo --file=environment.yml\\n            conda env update vader/deathstar\\n\\n        \\\"\\\"\\\"\\n    )\\n\\n    p = sub_parsers.add_parser(\\n        \\\"update\\\",\\n        help=summary,\\n        description=description,\\n        epilog=epilog,\\n        **kwargs,\\n    )\\n    add_parser_prefix(p)\\n    p.add_argument(\\n        \\\"-f\\\",\\n        \\\"--file\\\",\\n        action=\\\"store\\\",\\n        help=\\\"environment definition (default: environment.yml)\\\",\\n        default=\\\"environment.yml\\\",\\n    )\\n    p.add_argument(\\n        \\\"--prune\\\",\\n        action=\\\"store_true\\\",\\n        default=False,\\n        help=\\\"remove installed packages not defined in environment.yml\\\",\\n    )\\n    p.add_argument(\\n        \\\"remote_definition\\\",\\n        help=\\\"remote environment definition / IPython notebook\\\",\\n        action=\\\"store\\\",\\n        default=None,\\n        nargs=\\\"?\\\",\\n    )\\n    add_parser_json(p)\\n    add_parser_solver(p)\\n    p.set_defaults(func=\\\"conda.cli.main_env_update.execute\\\")\\n\\n    return p\\n\\n\\n@notices\\ndef execute(args: Namespace, parser: ArgumentParser) -> int:\\n    from ..auxlib.ish import dals\\n    from ..base.context import context, determine_target_prefix\\n    from ..core.prefix_data import PrefixData\\n    from ..env import specs as install_specs\\n    from ..env.env import get_filename, print_result\\n    from ..env.installers.base import get_installer\\n    from ..exceptions import CondaEnvException, InvalidInstaller\\n    from ..misc import touch_nonadmin\\n\\n    spec = install_specs.detect(\\n        name=args.name,\\n        filename=get_filename(args.file),\\n        directory=os.getcwd(),\\n        remote_definition=args.remote_definition,\\n    )\\n    env = spec.environment\\n\\n    if not (args.name or args.prefix):\\n        if not env.name:\\n            # Note, this is a hack fofr get_prefix that assumes argparse results\\n            # TODO Refactor common.get_prefix\\n            name = os.environ.get(\\\"CONDA_DEFAULT_ENV\\\", False)\\n            if not name:\\n                msg = \\\"Unable to determine environment\\\\n\\\\n\\\"\\n                instuctions = dals(\\n                    \\\"\\\"\\\"\\n                    Please re-run this command with one of the following options:\\n\\n                    * Provide an environment name via --name or -n\\n                    * Re-run this command inside an activated conda environment.\\n                    \\\"\\\"\\\"\\n                )\\n                msg += instuctions\\n                # TODO Add json support\\n                raise CondaEnvException(msg)\\n\\n        # Note: stubbing out the args object as all of the\\n        # conda.cli.common code thinks that name will always\\n        # be specified.\\n        args.name = env.name\\n\\n    prefix = determine_target_prefix(context, args)\\n    # CAN'T Check with this function since it assumes we will create prefix.\\n    # cli_install.check_prefix(prefix, json=args.json)\\n\\n    # TODO, add capability\\n    # common.ensure_override_channels_requires_channel(args)\\n    # channel_urls = args.channel or ()\\n\\n    # create installers before running any of them\\n    # to avoid failure to import after the file being deleted\\n    # e.g. due to conda_env being upgraded or Python version switched.\\n    installers = {}\\n\\n    for installer_type in env.dependencies:\\n        try:\\n            installers[installer_type] = get_installer(installer_type)\\n        except InvalidInstaller:\\n            raise CondaError(\\n                dals(\\n                    f\\\"\\\"\\\"\\n                    Unable to install package for {0}.\\n\\n                    Please double check and ensure you dependencies file has\\n                    the correct spelling.  You might also try installing the\\n                    conda-env-{0} package to see if provides the required\\n                    installer.\\n                    \\\"\\\"\\\"\\n                )\\n            )\\n\\n            return -1\\n\\n    result = {\\\"conda\\\": None, \\\"pip\\\": None}\\n    for installer_type, specs in env.dependencies.items():\\n        installer = installers[installer_type]\\n        result[installer_type] = installer.install(prefix, specs, args, env)\\n\\n    if env.variables:\\n        pd = PrefixData(prefix)\\n        pd.set_environment_env_vars(env.variables)\\n\\n    touch_nonadmin(prefix)\\n    print_result(args, prefix, result)\\n\\n    return 0\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"CLI implementation for `conda-env list`, now aliased to `conda info --envs`.\\n\\nLists available conda environments.\\n\\\"\\\"\\\"\\n\\nfrom argparse import ArgumentParser, Namespace, _SubParsersAction\\n\\nfrom conda.deprecations import deprecated\\n\\n\\ndef configure_parser(sub_parsers: _SubParsersAction, **kwargs) -> ArgumentParser:\\n    from ..auxlib.ish import dals\\n    from .helpers import add_parser_json\\n\\n    summary = \\\"An alias for `conda info --envs`. Lists all conda environments.\\\"\\n    description = summary\\n    epilog = dals(\\n        \\\"\\\"\\\"\\n        Examples::\\n\\n            conda env list\\n            conda env list --json\\n\\n        \\\"\\\"\\\"\\n    )\\n    p = sub_parsers.add_parser(\\n        \\\"list\\\",\\n        help=summary,\\n        description=description,\\n        epilog=epilog,\\n        **kwargs,\\n    )\\n\\n    add_parser_json(p)\\n\\n    p.set_defaults(\\n        func=\\\"conda.cli.main_info.execute\\\",\\n        # The following are the necessary default args for the `conda info` command\\n        envs=True,\\n        base=False,\\n        unsafe_channels=False,\\n        system=False,\\n        all=False,\\n    )\\n\\n    return p\\n\\n\\n@deprecated(\\\"24.9\\\", \\\"25.3\\\", addendum=\\\"Use `conda.cli.main_info.execute` instead.\\\")\\ndef execute(args: Namespace, parser: ArgumentParser):\\n    from conda.cli.main_info import execute as execute_info\\n\\n    execute_info(args, parser)\\n\\n    return 0\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Common utilities for conda command line tools.\\\"\\\"\\\"\\n\\nimport re\\nimport sys\\nfrom logging import getLogger\\nfrom os.path import basename, dirname, isdir, isfile, join, normcase\\n\\nfrom ..auxlib.ish import dals\\nfrom ..base.constants import ROOT_ENV_NAME\\nfrom ..base.context import context, env_name\\nfrom ..common.constants import NULL\\nfrom ..common.io import swallow_broken_pipe\\nfrom ..common.path import paths_equal\\nfrom ..common.serialize import json_dump\\nfrom ..exceptions import (\\n    CondaError,\\n    DirectoryNotACondaEnvironmentError,\\n    EnvironmentLocationNotFound,\\n)\\nfrom ..models.match_spec import MatchSpec\\n\\n\\ndef confirm(message=\\\"Proceed\\\", choices=(\\\"yes\\\", \\\"no\\\"), default=\\\"yes\\\", dry_run=NULL):\\n    assert default in choices, default\\n    if (dry_run is NULL and context.dry_run) or dry_run:\\n        from ..exceptions import DryRunExit\\n\\n        raise DryRunExit()\\n\\n    options = []\\n    for option in choices:\\n        if option == default:\\n            options.append(f\\\"[{option[0]}]\\\")\\n        else:\\n            options.append(option[0])\\n    message = \\\"{} ({})? \\\".format(message, \\\"/\\\".join(options))\\n    choices = {alt: choice for choice in choices for alt in [choice, choice[0]]}\\n    choices[\\\"\\\"] = default\\n    while True:\\n        # raw_input has a bug and prints to stderr, not desirable\\n        sys.stdout.write(message)\\n        sys.stdout.flush()\\n        try:\\n            user_choice = sys.stdin.readline().strip().lower()\\n        except OSError as e:\\n            raise CondaError(f\\\"cannot read from stdin: {e}\\\")\\n        if user_choice not in choices:\\n            print(f\\\"Invalid choice: {user_choice}\\\")\\n        else:\\n            sys.stdout.write(\\\"\\\\n\\\")\\n            sys.stdout.flush()\\n            return choices[user_choice]\\n\\n\\ndef confirm_yn(message=\\\"Proceed\\\", default=\\\"yes\\\", dry_run=NULL):\\n    if (dry_run is NULL and context.dry_run) or dry_run:\\n        from ..exceptions import DryRunExit\\n\\n        raise DryRunExit()\\n    if context.always_yes:\\n        return True\\n    try:\\n        choice = confirm(\\n            message=message, choices=(\\\"yes\\\", \\\"no\\\"), default=default, dry_run=dry_run\\n        )\\n    except KeyboardInterrupt:  # pragma: no cover\\n        from ..exceptions import CondaSystemExit\\n\\n        raise CondaSystemExit(\\\"\\\\nOperation aborted.  Exiting.\\\")\\n    if choice == \\\"no\\\":\\n        from ..exceptions import CondaSystemExit\\n\\n        raise CondaSystemExit(\\\"Exiting.\\\")\\n    return True\\n\\n\\ndef is_active_prefix(prefix: str) -> bool:\\n    \\\"\\\"\\\"\\n    Determines whether the args we pass in are pointing to the active prefix.\\n    Can be used a validation step to make sure operations are not being\\n    performed on the active prefix.\\n    \\\"\\\"\\\"\\n    if context.active_prefix is None:\\n        return False\\n    return (\\n        paths_equal(prefix, context.active_prefix)\\n        # normcasing our prefix check for Windows, for case insensitivity\\n        or normcase(prefix) == normcase(env_name(context.active_prefix))\\n    )\\n\\n\\ndef arg2spec(arg, json=False, update=False):\\n    try:\\n        spec = MatchSpec(arg)\\n    except:\\n        from ..exceptions import CondaValueError\\n\\n        raise CondaValueError(f\\\"invalid package specification: {arg}\\\")\\n\\n    name = spec.name\\n    if not spec._is_simple() and update:\\n        from ..exceptions import CondaValueError\\n\\n        raise CondaValueError(\\n            \\\"version specifications not allowed with 'update'; use\\\\n\\\"\\n            f\\\"    conda update  {name:<{len(arg)}}  or\\\\n\\\"\\n            f\\\"    conda install {arg:<{len(name)}}\\\"\\n        )\\n\\n    return str(spec)\\n\\n\\ndef specs_from_args(args, json=False):\\n    return [arg2spec(arg, json=json) for arg in args]\\n\\n\\nspec_pat = re.compile(\\n    r\\\"\\\"\\\"\\n    (?P<name>[^=<>!\\\\s]+)                # package name\\n    \\\\s*                                 # ignore spaces\\n    (\\n        (?P<cc>=[^=]+(=[^=]+)?)         # conda constraint\\n        |\\n        (?P<pc>(?:[=!]=|[><]=?|~=).+)   # new pip-style constraints\\n    )?$\\n    \\\"\\\"\\\",\\n    re.VERBOSE,\\n)\\n\\n\\ndef strip_comment(line):\\n    return line.split(\\\"#\\\")[0].rstrip()\\n\\n\\ndef spec_from_line(line):\\n    m = spec_pat.match(strip_comment(line))\\n    if m is None:\\n        return None\\n    name, cc, pc = (m.group(\\\"name\\\").lower(), m.group(\\\"cc\\\"), m.group(\\\"pc\\\"))\\n    if cc:\\n        return name + cc.replace(\\\"=\\\", \\\" \\\")\\n    elif pc:\\n        if pc.startswith(\\\"~= \\\"):\\n            assert (\\n                pc.count(\\\"~=\\\") == 1\\n            ), f\\\"Overly complex 'Compatible release' spec not handled {line}\\\"\\n            assert pc.count(\\\".\\\"), f\\\"No '.' in 'Compatible release' version {line}\\\"\\n            ver = pc.replace(\\\"~= \\\", \\\"\\\")\\n            ver2 = \\\".\\\".join(ver.split(\\\".\\\")[:-1]) + \\\".*\\\"\\n            return name + \\\" >=\\\" + ver + \\\",==\\\" + ver2\\n        else:\\n            return name + \\\" \\\" + pc.replace(\\\" \\\", \\\"\\\")\\n    else:\\n        return name\\n\\n\\ndef specs_from_url(url, json=False):\\n    from ..gateways.connection.download import TmpDownload\\n\\n    explicit = False\\n    with TmpDownload(url, verbose=False) as path:\\n        specs = []\\n        try:\\n            for line in open(path):\\n                line = line.strip()\\n                if not line or line.startswith(\\\"#\\\"):\\n                    continue\\n                if line == \\\"@EXPLICIT\\\":\\n                    explicit = True\\n                if explicit:\\n                    specs.append(line)\\n                    continue\\n                spec = spec_from_line(line)\\n                if spec is None:\\n                    from ..exceptions import CondaValueError\\n\\n                    raise CondaValueError(f\\\"could not parse '{line}' in: {url}\\\")\\n                specs.append(spec)\\n        except OSError as e:\\n            from ..exceptions import CondaFileIOError\\n\\n            raise CondaFileIOError(path, e)\\n    return specs\\n\\n\\ndef names_in_specs(names, specs):\\n    return any(spec.split()[0] in names for spec in specs)\\n\\n\\ndef disp_features(features):\\n    if features:\\n        return \\\"[{}]\\\".format(\\\" \\\".join(features))\\n    else:\\n        return \\\"\\\"\\n\\n\\n@swallow_broken_pipe\\ndef stdout_json(d):\\n    getLogger(\\\"conda.stdout\\\").info(json_dump(d))\\n\\n\\ndef stdout_json_success(success=True, **kwargs):\\n    result = {\\\"success\\\": success}\\n    actions = kwargs.pop(\\\"actions\\\", None)\\n    if actions:\\n        if \\\"LINK\\\" in actions:\\n            actions[\\\"LINK\\\"] = [prec.dist_fields_dump() for prec in actions[\\\"LINK\\\"]]\\n        if \\\"UNLINK\\\" in actions:\\n            actions[\\\"UNLINK\\\"] = [prec.dist_fields_dump() for prec in actions[\\\"UNLINK\\\"]]\\n        result[\\\"actions\\\"] = actions\\n    result.update(kwargs)\\n    stdout_json(result)\\n\\n\\ndef print_envs_list(known_conda_prefixes, output=True):\\n    if output:\\n        print(\\\"# conda environments:\\\")\\n        print(\\\"#\\\")\\n\\n    def disp_env(prefix):\\n        fmt = \\\"%-20s  %s  %s\\\"\\n        active = \\\"*\\\" if prefix == context.active_prefix else \\\" \\\"\\n        if prefix == context.root_prefix:\\n            name = ROOT_ENV_NAME\\n        elif any(\\n            paths_equal(envs_dir, dirname(prefix)) for envs_dir in context.envs_dirs\\n        ):\\n            name = basename(prefix)\\n        else:\\n            name = \\\"\\\"\\n        if output:\\n            print(fmt % (name, active, prefix))\\n\\n    for prefix in known_conda_prefixes:\\n        disp_env(prefix)\\n\\n    if output:\\n        print()\\n\\n\\ndef check_non_admin():\\n    from ..common._os import is_admin\\n\\n    if not context.non_admin_enabled and not is_admin():\\n        from ..exceptions import OperationNotAllowed\\n\\n        raise OperationNotAllowed(\\n            dals(\\n                \\\"\\\"\\\"\\n            The create, install, update, and remove operations have been disabled\\n            on your system for non-privileged users.\\n        \\\"\\\"\\\"\\n            )\\n        )\\n\\n\\ndef validate_prefix(prefix):\\n    \\\"\\\"\\\"Verifies the prefix is a valid conda environment.\\n\\n    :raises EnvironmentLocationNotFound: Non-existent path or not a directory.\\n    :raises DirectoryNotACondaEnvironmentError: Directory is not a conda environment.\\n    :returns: Valid prefix.\\n    :rtype: str\\n    \\\"\\\"\\\"\\n    if isdir(prefix):\\n        if not isfile(join(prefix, \\\"conda-meta\\\", \\\"history\\\")):\\n            raise DirectoryNotACondaEnvironmentError(prefix)\\n    else:\\n        raise EnvironmentLocationNotFound(prefix)\\n\\n    return prefix\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\nfrom .main import main  # NOQA\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"CLI implementation for `conda notices`.\\n\\nManually retrieves channel notifications, caches them and displays them.\\n\\\"\\\"\\\"\\n\\nfrom argparse import ArgumentParser, Namespace, _SubParsersAction\\n\\n\\ndef configure_parser(sub_parsers: _SubParsersAction, **kwargs) -> ArgumentParser:\\n    from ..auxlib.ish import dals\\n    from .helpers import add_parser_channels, add_parser_json\\n\\n    summary = \\\"Retrieve latest channel notifications.\\\"\\n    description = dals(\\n        f\\\"\\\"\\\"\\n        {summary}\\n\\n        Conda channel maintainers have the option of setting messages that\\n        users will see intermittently. Some of these notices are informational\\n        while others are messages concerning the stability of the channel.\\n\\n        \\\"\\\"\\\"\\n    )\\n    epilog = dals(\\n        \\\"\\\"\\\"\\n        Examples::\\n\\n            conda notices\\n\\n            conda notices -c defaults\\n\\n        \\\"\\\"\\\"\\n    )\\n\\n    p = sub_parsers.add_parser(\\n        \\\"notices\\\",\\n        help=summary,\\n        description=description,\\n        epilog=epilog,\\n        **kwargs,\\n    )\\n    add_parser_channels(p)\\n    add_parser_json(p)\\n\\n    p.set_defaults(func=\\\"conda.cli.main_notices.execute\\\")\\n\\n    return p\\n\\n\\ndef execute(args: Namespace, parser: ArgumentParser) -> int:\\n    \\\"\\\"\\\"Command that retrieves channel notifications, caches them and displays them.\\\"\\\"\\\"\\n    from ..exceptions import CondaError\\n    from ..notices import core as notices\\n\\n    try:\\n        channel_notice_set = notices.retrieve_notices()\\n    except OSError as exc:\\n        raise CondaError(f\\\"Unable to retrieve notices: {exc}\\\")\\n\\n    notices.display_notices(channel_notice_set)\\n\\n    return 0\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"DEPRECATED: Use `conda.cli.main_export` instead.\\n\\nCLI implementation for `conda-env export`.\\n\\nDumps specified environment package specifications to the screen.\\n\\\"\\\"\\\"\\n\\n# Import from conda.cli.main_export since this module is deprecated.\\nfrom conda.cli.main_export import configure_parser, execute  # noqa\\nfrom conda.deprecations import deprecated\\n\\ndeprecated.module(\\\"24.9\\\", \\\"25.3\\\", addendum=\\\"Use `conda.cli.main_export` instead.\\\")\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Anaconda-client (binstar) token management for CondaSession.\\\"\\\"\\\"\\n\\nimport os\\nimport re\\nfrom logging import getLogger\\nfrom os.path import isdir, isfile, join\\nfrom stat import S_IREAD, S_IWRITE\\n\\ntry:\\n    from platformdirs import user_config_dir\\nexcept ImportError:  # pragma: no cover\\n    from .._vendor.appdirs import user_data_dir as user_config_dir\\n\\nfrom ..common.url import quote_plus, unquote_plus\\nfrom ..deprecations import deprecated\\nfrom .disk.delete import rm_rf\\n\\nlog = getLogger(__name__)\\n\\n\\ndef replace_first_api_with_conda(url):\\n    # replace first occurrence of 'api' with 'conda' in url\\n    return re.sub(r\\\"([./])api([./]|$)\\\", r\\\"\\\\1conda\\\\2\\\", url, count=1)\\n\\n\\n@deprecated(\\\"24.3\\\", \\\"24.9\\\", addendum=\\\"Use `platformdirs` instead.\\\")\\nclass EnvAppDirs:\\n    def __init__(self, appname, appauthor, root_path):\\n        self.appname = appname\\n        self.appauthor = appauthor\\n        self.root_path = root_path\\n\\n    @property\\n    def user_data_dir(self):\\n        return join(self.root_path, \\\"data\\\")\\n\\n    @property\\n    def site_data_dir(self):\\n        return join(self.root_path, \\\"data\\\")\\n\\n    @property\\n    def user_cache_dir(self):\\n        return join(self.root_path, \\\"cache\\\")\\n\\n    @property\\n    def user_log_dir(self):\\n        return join(self.root_path, \\\"log\\\")\\n\\n\\ndef _get_binstar_token_directory():\\n    if \\\"BINSTAR_CONFIG_DIR\\\" in os.environ:\\n        return os.path.join(os.environ[\\\"BINSTAR_CONFIG_DIR\\\"], \\\"data\\\")\\n    else:\\n        return user_config_dir(appname=\\\"binstar\\\", appauthor=\\\"ContinuumIO\\\")\\n\\n\\ndef read_binstar_tokens():\\n    tokens = {}\\n    token_dir = _get_binstar_token_directory()\\n    if not isdir(token_dir):\\n        return tokens\\n\\n    for tkn_entry in os.scandir(token_dir):\\n        if tkn_entry.name[-6:] != \\\".token\\\":\\n            continue\\n        url = re.sub(r\\\"\\\\.token$\\\", \\\"\\\", unquote_plus(tkn_entry.name))\\n        with open(tkn_entry.path) as f:\\n            token = f.read()\\n        tokens[url] = tokens[replace_first_api_with_conda(url)] = token\\n    return tokens\\n\\n\\ndef set_binstar_token(url, token):\\n    token_dir = _get_binstar_token_directory()\\n    if not isdir(token_dir):\\n        os.makedirs(token_dir)\\n\\n    tokenfile = join(token_dir, f\\\"{quote_plus(url)}.token\\\")\\n\\n    if isfile(tokenfile):\\n        os.unlink(tokenfile)\\n    with open(tokenfile, \\\"w\\\") as fd:\\n        fd.write(token)\\n    os.chmod(tokenfile, S_IWRITE | S_IREAD)\\n\\n\\ndef remove_binstar_token(url):\\n    token_dir = _get_binstar_token_directory()\\n    tokenfile = join(token_dir, f\\\"{quote_plus(url)}.token\\\")\\n    rm_rf(tokenfile)\\n\\n\\nif __name__ == \\\"__main__\\\":\\n    print(read_binstar_tokens())\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Configure logging for conda.\\\"\\\"\\\"\\n\\nimport logging\\nimport re\\nimport sys\\nfrom datetime import datetime\\nfrom functools import lru_cache, partial\\nfrom logging import (\\n    DEBUG,\\n    ERROR,\\n    INFO,\\n    WARN,\\n    Filter,\\n    Formatter,\\n    StreamHandler,\\n    getLogger,\\n)\\n\\nfrom .. import CondaError\\nfrom ..common.constants import TRACE\\nfrom ..common.io import _FORMATTER, attach_stderr_handler\\nfrom ..deprecations import deprecated\\n\\nlog = getLogger(__name__)\\n\\n_VERBOSITY_LEVELS = {\\n    0: WARN,  # standard output\\n    1: WARN,  # -v, detailed output\\n    2: INFO,  # -vv, info logging\\n    3: DEBUG,  # -vvv, debug logging\\n    4: TRACE,  # -vvvv, trace logging\\n}\\ndeprecated.constant(\\\"24.3\\\", \\\"24.9\\\", \\\"VERBOSITY_LEVELS\\\", _VERBOSITY_LEVELS)\\n\\n\\nclass TokenURLFilter(Filter):\\n    TOKEN_URL_PATTERN = re.compile(\\n        r\\\"(|https?://)\\\"  # \\\\1  scheme\\n        r\\\"(|\\\\s\\\"  # \\\\2  space, or\\n        r\\\"|(?:(?:\\\\d{1,3}\\\\.){3}\\\\d{1,3})\\\"  # ipv4, or\\n        r\\\"|(?:\\\"  # domain name\\n        r\\\"(?:[a-zA-Z0-9-]{1,20}\\\\.){0,10}\\\"  # non-tld\\n        r\\\"(?:[a-zA-Z]{2}[a-zA-Z0-9-]{0,18})\\\"  # tld\\n        r\\\"))\\\"  # end domain name\\n        r\\\"(|:\\\\d{1,5})?\\\"  # \\\\3  port\\n        r\\\"/t/[a-z0-9A-Z-]+/\\\"  # token\\n    )\\n    TOKEN_REPLACE = partial(TOKEN_URL_PATTERN.sub, r\\\"\\\\1\\\\2\\\\3/t/<TOKEN>/\\\")\\n\\n    def filter(self, record):\\n        \\\"\\\"\\\"\\n        Since Python 2's getMessage() is incapable of handling any\\n        strings that are not unicode when it interpolates the message\\n        with the arguments, we fix that here by doing it ourselves.\\n\\n        At the same time we replace tokens in the arguments which was\\n        not happening until now.\\n        \\\"\\\"\\\"\\n        if not isinstance(record.msg, str):\\n            # This should always be the case but it's not checked so\\n            # we avoid any potential logging errors.\\n            return True\\n        if record.args:\\n            record.msg = record.msg % record.args\\n            record.args = None\\n        record.msg = self.TOKEN_REPLACE(record.msg)\\n        return True\\n\\n\\nclass StdStreamHandler(StreamHandler):\\n    \\\"\\\"\\\"Log StreamHandler that always writes to the current sys stream.\\\"\\\"\\\"\\n\\n    terminator = \\\"\\\\n\\\"\\n\\n    def __init__(self, sys_stream):\\n        \\\"\\\"\\\"\\n        Args:\\n            sys_stream: stream name, either \\\"stdout\\\" or \\\"stderr\\\" (attribute of module sys)\\n        \\\"\\\"\\\"\\n        super().__init__(getattr(sys, sys_stream))\\n        self.sys_stream = sys_stream\\n        del self.stream\\n\\n    def __getattr__(self, attr):\\n        # always get current sys.stdout/sys.stderr, unless self.stream has been set explicitly\\n        if attr == \\\"stream\\\":\\n            return getattr(sys, self.sys_stream)\\n        return super().__getattribute__(attr)\\n\\n    \\\"\\\"\\\"\\n    def emit(self, record):\\n        # in contrast to the Python 2.7 StreamHandler, this has no special Unicode handling;\\n        # however, this backports the Python >=3.2 terminator attribute and additionally makes it\\n        # further customizable by giving record an identically named attribute, e.g., via\\n        # logger.log(..., extra={\\\"terminator\\\": \\\"\\\"}) or LoggerAdapter(logger, {\\\"terminator\\\": \\\"\\\"}).\\n        try:\\n            msg = self.format(record)\\n            terminator = getattr(record, \\\"terminator\\\", self.terminator)\\n            stream = self.stream\\n            stream.write(msg)\\n            stream.write(terminator)\\n            self.flush()\\n        except Exception:\\n            self.handleError(record)\\n\\n    \\\"\\\"\\\"\\n\\n    # Updated Python 2.7.15's stdlib, with terminator and unicode support.\\n    def emit(self, record):\\n        \\\"\\\"\\\"\\n        Emit a record.\\n\\n        If a formatter is specified, it is used to format the record.\\n        The record is then written to the stream with a trailing newline.  If\\n        exception information is present, it is formatted using\\n        traceback.print_exception and appended to the stream.  If the stream\\n        has an 'encoding' attribute, it is used to determine how to do the\\n        output to the stream.\\n        \\\"\\\"\\\"\\n        try:\\n            msg = self.format(record)\\n            stream = self.stream\\n            fs = \\\"%s\\\"\\n            stream.write(fs % msg)\\n            terminator = getattr(record, \\\"terminator\\\", self.terminator)\\n            stream.write(terminator)\\n            self.flush()\\n        # How does conda handle Ctrl-C? Find out..\\n        # except (KeyboardInterrupt, SystemExit):\\n        #     raise\\n        except Exception:\\n            self.handleError(record)\\n\\n\\n# Don't use initialize_logging/initialize_root_logger/set_conda_log_level in\\n# cli.python_api! There we want the user to have control over their logging,\\n# e.g., using their own levels, handlers, formatters and propagation settings.\\n\\n\\n@lru_cache(maxsize=None)\\ndef initialize_logging():\\n    # 'conda' gets level WARN and does not propagate to root.\\n    getLogger(\\\"conda\\\").setLevel(WARN)\\n    set_conda_log_level()\\n    initialize_std_loggers()\\n\\n\\ndef initialize_std_loggers():\\n    # Set up special loggers 'conda.stdout'/'conda.stderr' which output directly to the\\n    # corresponding sys streams, filter token urls and don't propagate.\\n    formatter = Formatter(\\\"%(message)s\\\")\\n\\n    for stream in (\\\"stdout\\\", \\\"stderr\\\"):\\n        logger = getLogger(f\\\"conda.{stream}\\\")\\n        logger.handlers = []\\n        logger.setLevel(INFO)\\n        handler = StdStreamHandler(stream)\\n        handler.setLevel(INFO)\\n        handler.setFormatter(formatter)\\n        logger.addHandler(handler)\\n        logger.addFilter(TokenURLFilter())\\n        logger.propagate = False\\n\\n        stdlog_logger = getLogger(f\\\"conda.{stream}log\\\")\\n        stdlog_logger.handlers = []\\n        stdlog_logger.setLevel(DEBUG)\\n        stdlog_handler = StdStreamHandler(stream)\\n        stdlog_handler.terminator = \\\"\\\"\\n        stdlog_handler.setLevel(DEBUG)\\n        stdlog_handler.setFormatter(formatter)\\n        stdlog_logger.addHandler(stdlog_handler)\\n        stdlog_logger.propagate = False\\n\\n    verbose_logger = getLogger(\\\"conda.stdout.verbose\\\")\\n    verbose_logger.handlers = []\\n    verbose_logger.setLevel(INFO)\\n    verbose_handler = StdStreamHandler(\\\"stdout\\\")\\n    verbose_handler.setLevel(INFO)\\n    verbose_handler.setFormatter(formatter)\\n    verbose_handler.addFilter(TokenURLFilter())\\n    verbose_logger.addHandler(verbose_handler)\\n    verbose_logger.propagate = False\\n\\n\\n@deprecated(\\\"25.3\\\", \\\"25.9\\\", addendum=\\\"Unused.\\\")\\ndef initialize_root_logger(level=ERROR):\\n    attach_stderr_handler(level=level, filters=[TokenURLFilter()])\\n\\n\\ndef set_conda_log_level(level=WARN):\\n    attach_stderr_handler(level=level, logger_name=\\\"conda\\\", filters=[TokenURLFilter()])\\n\\n\\ndef set_all_logger_level(level=DEBUG):\\n    formatter = Formatter(\\\"%(message)s\\\\n\\\") if level >= INFO else None\\n    attach_stderr_handler(level, formatter=formatter, filters=[TokenURLFilter()])\\n    set_conda_log_level(level)\\n    # 'requests' loggers get their own handlers so that they always output messages in long format\\n    # regardless of the level.\\n    attach_stderr_handler(level, \\\"requests\\\", filters=[TokenURLFilter()])\\n    attach_stderr_handler(\\n        level, \\\"requests.packages.urllib3\\\", filters=[TokenURLFilter()]\\n    )\\n\\n\\n@lru_cache(maxsize=None)\\ndef set_file_logging(logger_name=None, level=DEBUG, path=None):\\n    if path is None:\\n        timestamp = datetime.utcnow().strftime(\\\"%Y%m%d-%H%M%S\\\")\\n        path = f\\\".conda.{timestamp}.log\\\"\\n\\n    conda_logger = getLogger(logger_name)\\n    handler = logging.FileHandler(path)\\n    handler.setFormatter(_FORMATTER)\\n    handler.setLevel(level)\\n    conda_logger.addHandler(handler)\\n\\n\\n@deprecated(\\n    \\\"24.3\\\",\\n    \\\"24.9\\\",\\n    addendum=\\\"Use `conda.gateways.logging.set_log_level` instead.\\\",\\n)\\ndef set_verbosity(verbosity: int):\\n    try:\\n        set_log_level(_VERBOSITY_LEVELS[verbosity])\\n    except KeyError:\\n        raise CondaError(f\\\"Invalid verbosity level: {verbosity}\\\") from None\\n\\n\\ndef set_log_level(log_level: int):\\n    set_all_logger_level(log_level)\\n    log.debug(\\\"log_level set to %d\\\", log_level)\\n\\n\\n@deprecated(\\n    \\\"24.9\\\",\\n    \\\"25.3\\\",\\n    addendum=\\\"Use `logging.getLogger(__name__)(conda.common.constants.TRACE, ...)` instead.\\\",\\n)\\ndef trace(self, message, *args, **kwargs):\\n    if self.isEnabledFor(TRACE):\\n        self._log(TRACE, message, args, **kwargs)\\n\\n\\nlogging.addLevelName(TRACE, \\\"TRACE\\\")\\nlogging.Logger.trace = trace  # type: ignore[attr-defined]\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"\\nGateways isolate interaction of conda code with the outside world.  Disk manipulation,\\ndatabase interaction, and remote requests should all be through various gateways.  Functions\\nand methods in ``conda.gateways`` must use ``conda.models`` for arguments and return values.\\n\\nConda modules importable from ``conda.gateways`` are\\n\\n- ``conda._vendor``\\n- ``conda.common``\\n- ``conda.models``\\n- ``conda.gateways``\\n\\nConda modules off limits for import within ``conda.gateways`` are\\n\\n- ``conda.api``\\n- ``conda.cli``\\n- ``conda.client``\\n- ``conda.core``\\n\\nConda modules strictly prohibited from importing ``conda.gateways`` are\\n\\n- ``conda.api``\\n- ``conda.cli``\\n- ``conda.client``\\n\\n\\\"\\\"\\\"\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Helpler functions for subprocess.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport os\\nimport sys\\nfrom collections import namedtuple\\nfrom logging import getLogger\\nfrom os.path import abspath\\nfrom subprocess import PIPE, CalledProcessError, Popen\\nfrom typing import TYPE_CHECKING\\n\\nfrom .. import ACTIVE_SUBPROCESSES\\nfrom ..auxlib.compat import shlex_split_unicode\\nfrom ..auxlib.ish import dals\\nfrom ..base.context import context\\nfrom ..common.compat import encode_environment, isiterable\\nfrom ..common.constants import TRACE\\nfrom ..gateways.disk.delete import rm_rf\\nfrom ..utils import wrap_subprocess_call\\n\\nif TYPE_CHECKING:\\n    from pathlib import Path\\n    from typing import Sequence\\n\\nlog = getLogger(__name__)\\nResponse = namedtuple(\\\"Response\\\", (\\\"stdout\\\", \\\"stderr\\\", \\\"rc\\\"))\\n\\n\\ndef _format_output(command_str, cwd, rc, stdout, stderr):\\n    return dals(\\n        \\\"\\\"\\\"\\n    $ %s\\n    ==> cwd: %s <==\\n    ==> exit code: %d <==\\n    ==> stdout <==\\n    %s\\n    ==> stderr <==\\n    %s\\n    \\\"\\\"\\\"\\n    ) % (command_str, cwd, rc, stdout, stderr)\\n\\n\\ndef any_subprocess(args, prefix, env=None, cwd=None):\\n    script_caller, command_args = wrap_subprocess_call(\\n        context.root_prefix,\\n        prefix,\\n        context.dev,\\n        context.debug,\\n        args,\\n    )\\n    process = Popen(\\n        command_args,\\n        cwd=cwd or prefix,\\n        universal_newlines=False,\\n        stdout=PIPE,\\n        stderr=PIPE,\\n        env=env,\\n    )\\n    stdout, stderr = process.communicate()\\n    if script_caller is not None:\\n        if \\\"CONDA_TEST_SAVE_TEMPS\\\" not in os.environ:\\n            rm_rf(script_caller)\\n        else:\\n            log.warning(\\n                f\\\"CONDA_TEST_SAVE_TEMPS :: retaining pip run_script {script_caller}\\\"\\n            )\\n    if hasattr(stdout, \\\"decode\\\"):\\n        stdout = stdout.decode(\\\"utf-8\\\", errors=\\\"replace\\\")\\n    if hasattr(stderr, \\\"decode\\\"):\\n        stderr = stderr.decode(\\\"utf-8\\\", errors=\\\"replace\\\")\\n    return stdout, stderr, process.returncode\\n\\n\\ndef subprocess_call(\\n    command: str | os.PathLike | Path | Sequence[str | os.PathLike | Path],\\n    env: dict[str, str] | None = None,\\n    path: str | os.PathLike | Path | None = None,\\n    stdin: str | None = None,\\n    raise_on_error: bool = True,\\n    capture_output: bool = True,\\n):\\n    \\\"\\\"\\\"This utility function should be preferred for all conda subprocessing.\\n    It handles multiple tricky details.\\n    \\\"\\\"\\\"\\n    env = encode_environment(env or os.environ)\\n    cwd = sys.prefix if path is None else abspath(path)\\n    if not isiterable(command):\\n        command = shlex_split_unicode(command)\\n    try:\\n        command_str = os.fspath(command)\\n    except TypeError:\\n        # TypeError: command is not a str or PathLike\\n        command_str = \\\" \\\".join(map(os.fspath, command))\\n    log.debug(\\\"executing>> %s\\\", command_str)\\n\\n    pipe = None\\n    if capture_output:\\n        pipe = PIPE\\n    elif stdin:\\n        raise ValueError(\\\"When passing stdin, output needs to be captured\\\")\\n    else:\\n        stdin = None\\n\\n    # spawn subprocess\\n    process = Popen(\\n        command,\\n        cwd=cwd,\\n        stdin=pipe,\\n        stdout=pipe,\\n        stderr=pipe,\\n        env=env,\\n        text=True,  # open streams in text mode so that we don't have to decode\\n        errors=\\\"replace\\\",\\n    )\\n    ACTIVE_SUBPROCESSES.add(process)\\n\\n    # decode output, if not PIPE, stdout/stderr will be None\\n    stdout, stderr = process.communicate(input=stdin)\\n    rc = process.returncode\\n    ACTIVE_SUBPROCESSES.remove(process)\\n\\n    if (raise_on_error and rc != 0) or log.isEnabledFor(TRACE):\\n        formatted_output = _format_output(command_str, cwd, rc, stdout, stderr)\\n    if raise_on_error and rc != 0:\\n        log.info(formatted_output)\\n        raise CalledProcessError(rc, command, output=formatted_output)\\n    if log.isEnabledFor(TRACE):\\n        log.log(TRACE, formatted_output)\\n\\n    return Response(stdout, stderr, int(rc))\\n\\n\\ndef _subprocess_clean_env(env, clean_python=True, clean_conda=True):\\n    dels = []\\n    if clean_python:\\n        dels.extend((\\\"PYTHONPATH\\\", \\\"PYTHONHOME\\\"))\\n    if clean_conda:\\n        dels.extend(\\n            (\\\"CONDA_ROOT\\\", \\\"CONDA_PROMPT_MODIFIER\\\", \\\"CONDA_EXE\\\", \\\"CONDA_DEFAULT_ENV\\\")\\n        )\\n    for key in dels:\\n        if key in env:\\n            del env[key]\\n\\n\\ndef subprocess_call_with_clean_env(\\n    command,\\n    path=None,\\n    stdin=None,\\n    raise_on_error=True,\\n    clean_python=True,\\n    clean_conda=True,\\n):\\n    # Any of these env vars are likely to mess the whole thing up.\\n    # This has been seen to be the case with PYTHONPATH.\\n    env = os.environ.copy()\\n    _subprocess_clean_env(env, clean_python, clean_conda)\\n    # env['CONDA_DLL_SEARCH_MODIFICATION_ENABLE'] = '1'\\n    return subprocess_call(\\n        command, env=env, path=path, stdin=stdin, raise_on_error=raise_on_error\\n    )\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Requests session configured with all accepted scheme adapters.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nfrom fnmatch import fnmatch\\nfrom functools import lru_cache\\nfrom logging import getLogger\\nfrom threading import local\\n\\nfrom ... import CondaError\\nfrom ...auxlib.ish import dals\\nfrom ...base.constants import CONDA_HOMEPAGE_URL\\nfrom ...base.context import context\\nfrom ...common.url import (\\n    add_username_and_password,\\n    get_proxy_username_and_pass,\\n    split_anaconda_token,\\n    urlparse,\\n)\\nfrom ...exceptions import ProxyError\\nfrom ...models.channel import Channel\\nfrom ..anaconda_client import read_binstar_tokens\\nfrom . import (\\n    AuthBase,\\n    BaseAdapter,\\n    Retry,\\n    Session,\\n    _basic_auth_str,\\n    extract_cookies_to_jar,\\n    get_auth_from_url,\\n    get_netrc_auth,\\n)\\nfrom .adapters.ftp import FTPAdapter\\nfrom .adapters.http import HTTPAdapter\\nfrom .adapters.localfs import LocalFSAdapter\\nfrom .adapters.s3 import S3Adapter\\n\\nlog = getLogger(__name__)\\nRETRIES = 3\\n\\n\\nCONDA_SESSION_SCHEMES = frozenset(\\n    (\\n        \\\"http\\\",\\n        \\\"https\\\",\\n        \\\"ftp\\\",\\n        \\\"s3\\\",\\n        \\\"file\\\",\\n    )\\n)\\n\\n\\nclass EnforceUnusedAdapter(BaseAdapter):\\n    def send(self, request, *args, **kwargs):\\n        message = dals(\\n            f\\\"\\\"\\\"\\n        EnforceUnusedAdapter called with url {request.url}\\n        This command is using a remote connection in offline mode.\\n        \\\"\\\"\\\"\\n        )\\n        raise RuntimeError(message)\\n\\n    def close(self):\\n        raise NotImplementedError()\\n\\n\\ndef get_channel_name_from_url(url: str) -> str | None:\\n    \\\"\\\"\\\"\\n    Given a URL, determine the channel it belongs to and return its name.\\n    \\\"\\\"\\\"\\n    return Channel.from_url(url).canonical_name\\n\\n\\n@lru_cache(maxsize=None)\\ndef get_session(url: str):\\n    \\\"\\\"\\\"\\n    Function that determines the correct Session object to be returned\\n    based on the URL that is passed in.\\n    \\\"\\\"\\\"\\n    channel_name = get_channel_name_from_url(url)\\n\\n    # If for whatever reason a channel name can't be determined, (should be unlikely)\\n    # we just return the default session object.\\n    if channel_name is None:\\n        return CondaSession()\\n\\n    # We ensure here if there are duplicates defined, we choose the last one\\n    channel_settings = {}\\n    for settings in context.channel_settings:\\n        channel = settings.get(\\\"channel\\\", \\\"\\\")\\n        if channel == channel_name:\\n            # First we check for exact match\\n            channel_settings = settings\\n            continue\\n\\n        # If we don't have an exact match, we attempt to match a URL pattern\\n        parsed_url = urlparse(url)\\n        parsed_setting = urlparse(channel)\\n\\n        # We require that the schemes must be identical to prevent downgrade attacks.\\n        # This includes the case of a scheme-less pattern like \\\"*\\\", which is not allowed.\\n        if parsed_setting.scheme != parsed_url.scheme:\\n            continue\\n\\n        url_without_schema = parsed_url.netloc + parsed_url.path\\n        pattern = parsed_setting.netloc + parsed_setting.path\\n        if fnmatch(url_without_schema, pattern):\\n            channel_settings = settings\\n\\n    auth_handler = channel_settings.get(\\\"auth\\\", \\\"\\\").strip() or None\\n\\n    # Return default session object\\n    if auth_handler is None:\\n        return CondaSession()\\n\\n    auth_handler_cls = context.plugin_manager.get_auth_handler(auth_handler)\\n\\n    if not auth_handler_cls:\\n        return CondaSession()\\n\\n    return CondaSession(auth=auth_handler_cls(channel_name))\\n\\n\\ndef get_session_storage_key(auth) -> str:\\n    \\\"\\\"\\\"\\n    Function that determines which storage key to use for our CondaSession object caching\\n    \\\"\\\"\\\"\\n    if auth is None:\\n        return \\\"default\\\"\\n\\n    if isinstance(auth, tuple):\\n        return hash(auth)\\n\\n    auth_type = type(auth)\\n\\n    return f\\\"{auth_type.__module__}.{auth_type.__qualname__}::{auth.channel_name}\\\"\\n\\n\\nclass CondaSessionType(type):\\n    \\\"\\\"\\\"\\n    Takes advice from https://github.com/requests/requests/issues/1871#issuecomment-33327847\\n    and creates one Session instance per thread.\\n    \\\"\\\"\\\"\\n\\n    def __new__(mcs, name, bases, dct):\\n        dct[\\\"_thread_local\\\"] = local()\\n        return super().__new__(mcs, name, bases, dct)\\n\\n    def __call__(cls, **kwargs):\\n        storage_key = get_session_storage_key(kwargs.get(\\\"auth\\\"))\\n\\n        try:\\n            return cls._thread_local.sessions[storage_key]\\n        except AttributeError:\\n            session = super().__call__(**kwargs)\\n            cls._thread_local.sessions = {storage_key: session}\\n        except KeyError:\\n            session = cls._thread_local.sessions[storage_key] = super().__call__(\\n                **kwargs\\n            )\\n\\n        return session\\n\\n\\nclass CondaSession(Session, metaclass=CondaSessionType):\\n    def __init__(self, auth: AuthBase | tuple[str, str] | None = None):\\n        \\\"\\\"\\\"\\n        :param auth: Optionally provide ``requests.AuthBase`` compliant objects\\n        \\\"\\\"\\\"\\n        super().__init__()\\n\\n        self.auth = auth or CondaHttpAuth()\\n\\n        self.proxies.update(context.proxy_servers)\\n\\n        ssl_context = None\\n        if context.ssl_verify == \\\"truststore\\\":\\n            try:\\n                import ssl\\n\\n                import truststore\\n\\n                ssl_context = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT)\\n            except ImportError:\\n                raise CondaError(\\n                    \\\"The `ssl_verify: truststore` setting is only supported on\\\"\\n                    \\\"Python 3.10 or later.\\\"\\n                )\\n            self.verify = True\\n        else:\\n            self.verify = context.ssl_verify\\n\\n        if context.offline:\\n            unused_adapter = EnforceUnusedAdapter()\\n            self.mount(\\\"http://\\\", unused_adapter)\\n            self.mount(\\\"https://\\\", unused_adapter)\\n            self.mount(\\\"ftp://\\\", unused_adapter)\\n            self.mount(\\\"s3://\\\", unused_adapter)\\n\\n        else:\\n            # Configure retries\\n            retry = Retry(\\n                total=context.remote_max_retries,\\n                backoff_factor=context.remote_backoff_factor,\\n                status_forcelist=[413, 429, 500, 503],\\n                raise_on_status=False,\\n                respect_retry_after_header=False,\\n            )\\n            http_adapter = HTTPAdapter(max_retries=retry, ssl_context=ssl_context)\\n            self.mount(\\\"http://\\\", http_adapter)\\n            self.mount(\\\"https://\\\", http_adapter)\\n            self.mount(\\\"ftp://\\\", FTPAdapter())\\n            self.mount(\\\"s3://\\\", S3Adapter())\\n\\n        self.mount(\\\"file://\\\", LocalFSAdapter())\\n\\n        self.headers[\\\"User-Agent\\\"] = context.user_agent\\n\\n        if context.client_ssl_cert_key:\\n            self.cert = (context.client_ssl_cert, context.client_ssl_cert_key)\\n        elif context.client_ssl_cert:\\n            self.cert = context.client_ssl_cert\\n\\n    @classmethod\\n    def cache_clear(cls):\\n        try:\\n            cls._thread_local.sessions.clear()\\n        except AttributeError:\\n            # AttributeError: thread's session cache has not been initialized\\n            pass\\n\\n\\nclass CondaHttpAuth(AuthBase):\\n    # TODO: make this class thread-safe by adding some of the requests.auth.HTTPDigestAuth() code\\n\\n    def __call__(self, request):\\n        request.url = CondaHttpAuth.add_binstar_token(request.url)\\n        self._apply_basic_auth(request)\\n        request.register_hook(\\\"response\\\", self.handle_407)\\n        return request\\n\\n    @staticmethod\\n    def _apply_basic_auth(request):\\n        # this logic duplicated from Session.prepare_request and PreparedRequest.prepare_auth\\n        url_auth = get_auth_from_url(request.url)\\n        auth = url_auth if any(url_auth) else None\\n\\n        if auth is None:\\n            # look for auth information in a .netrc file\\n            auth = get_netrc_auth(request.url)\\n\\n        if isinstance(auth, tuple) and len(auth) == 2:\\n            request.headers[\\\"Authorization\\\"] = _basic_auth_str(*auth)\\n\\n        return request\\n\\n    @staticmethod\\n    def add_binstar_token(url):\\n        clean_url, token = split_anaconda_token(url)\\n        if not token and context.add_anaconda_token:\\n            for binstar_url, token in read_binstar_tokens().items():\\n                if clean_url.startswith(binstar_url):\\n                    log.debug(\\\"Adding anaconda token for url <%s>\\\", clean_url)\\n                    from ...models.channel import Channel\\n\\n                    channel = Channel(clean_url)\\n                    channel.token = token\\n                    return channel.url(with_credentials=True)\\n        return url\\n\\n    @staticmethod\\n    def handle_407(response, **kwargs):  # pragma: no cover\\n        \\\"\\\"\\\"\\n        Prompts the user for the proxy username and password and modifies the\\n        proxy in the session object to include it.\\n\\n        This method is modeled after\\n          * requests.auth.HTTPDigestAuth.handle_401()\\n          * requests.auth.HTTPProxyAuth\\n          * the previous conda.fetch.handle_proxy_407()\\n\\n        It both adds 'username:password' to the proxy URL, as well as adding a\\n        'Proxy-Authorization' header.  If any of this is incorrect, please file an issue.\\n\\n        \\\"\\\"\\\"\\n        # kwargs = {'verify': True, 'cert': None, 'proxies': {}, 'stream': False,\\n        #           'timeout': (3.05, 60)}\\n\\n        if response.status_code != 407:\\n            return response\\n\\n        # Consume content and release the original connection\\n        # to allow our new request to reuse the same one.\\n        response.content\\n        response.close()\\n\\n        proxies = kwargs.pop(\\\"proxies\\\")\\n\\n        proxy_scheme = urlparse(response.url).scheme\\n        if proxy_scheme not in proxies:\\n            raise ProxyError(\\n                dals(\\n                    f\\\"\\\"\\\"\\n            Could not find a proxy for {proxy_scheme!r}. See\\n            {CONDA_HOMEPAGE_URL}/docs/html#configure-conda-for-use-behind-a-proxy-server\\n            for more information on how to configure proxies.\\n            \\\"\\\"\\\"\\n                )\\n            )\\n\\n        # fix-up proxy_url with username & password\\n        proxy_url = proxies[proxy_scheme]\\n        username, password = get_proxy_username_and_pass(proxy_scheme)\\n        proxy_url = add_username_and_password(proxy_url, username, password)\\n        proxy_authorization_header = _basic_auth_str(username, password)\\n        proxies[proxy_scheme] = proxy_url\\n        kwargs[\\\"proxies\\\"] = proxies\\n\\n        prep = response.request.copy()\\n        extract_cookies_to_jar(prep._cookies, response.request, response.raw)\\n        prep.prepare_cookies(prep._cookies)\\n        prep.headers[\\\"Proxy-Authorization\\\"] = proxy_authorization_header\\n\\n        _response = response.connection.send(prep, **kwargs)\\n        _response.history.append(response)\\n        _response.request = prep\\n\\n        return _response\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Download logic for conda indices and packages.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport hashlib\\nimport os\\nimport tempfile\\nimport warnings\\nfrom contextlib import contextmanager\\nfrom logging import DEBUG, getLogger\\nfrom os.path import basename, exists, join\\nfrom pathlib import Path\\n\\nfrom ... import CondaError\\nfrom ...auxlib.ish import dals\\nfrom ...auxlib.logz import stringify\\nfrom ...base.context import context\\nfrom ...common.io import time_recorder\\nfrom ...exceptions import (\\n    BasicClobberError,\\n    ChecksumMismatchError,\\n    CondaDependencyError,\\n    CondaHTTPError,\\n    CondaSSLError,\\n    CondaValueError,\\n    ProxyError,\\n    maybe_raise,\\n)\\nfrom ..disk.delete import rm_rf\\nfrom ..disk.lock import lock\\nfrom . import (\\n    ConnectionError,\\n    HTTPError,\\n    InsecureRequestWarning,\\n    InvalidSchema,\\n    RequestsProxyError,\\n    SSLError,\\n)\\nfrom .session import get_session\\n\\nlog = getLogger(__name__)\\n\\n\\nCHUNK_SIZE = 1 << 14\\n\\n\\ndef disable_ssl_verify_warning():\\n    warnings.simplefilter(\\\"ignore\\\", InsecureRequestWarning)\\n\\n\\n@time_recorder(\\\"download\\\")\\ndef download(\\n    url,\\n    target_full_path,\\n    md5=None,\\n    sha256=None,\\n    size=None,\\n    progress_update_callback=None,\\n):\\n    if exists(target_full_path):\\n        maybe_raise(BasicClobberError(target_full_path, url, context), context)\\n    if not context.ssl_verify:\\n        disable_ssl_verify_warning()\\n\\n    with download_http_errors(url):\\n        download_inner(\\n            url, target_full_path, md5, sha256, size, progress_update_callback\\n        )\\n\\n\\ndef download_inner(url, target_full_path, md5, sha256, size, progress_update_callback):\\n    timeout = context.remote_connect_timeout_secs, context.remote_read_timeout_secs\\n    session = get_session(url)\\n\\n    partial = False\\n    if size and (md5 or sha256):\\n        partial = True\\n\\n    streamed_bytes = 0\\n    size_builder = 0\\n\\n    # Use `.partial` even for full downloads. Avoid creating incomplete files\\n    # with the final filename.\\n    with download_partial_file(\\n        target_full_path, url=url, md5=md5, sha256=sha256, size=size\\n    ) as target:\\n        stat_result = os.fstat(target.fileno())\\n        if size is not None and stat_result.st_size >= size:\\n            return  # moves partial onto target_path, checksum will be checked\\n\\n        headers = {}\\n        if partial and stat_result.st_size > 0:\\n            headers = {\\\"Range\\\": f\\\"bytes={stat_result.st_size}-\\\"}\\n\\n        resp = session.get(\\n            url, stream=True, headers=headers, proxies=session.proxies, timeout=timeout\\n        )\\n        if log.isEnabledFor(DEBUG):\\n            log.debug(stringify(resp, content_max_len=256))\\n        resp.raise_for_status()\\n\\n        # Reset file if we think we're downloading partial content but the\\n        # server doesn't respond with 206 Partial Content\\n        if partial and resp.status_code != 206:\\n            target.seek(0)\\n            target.truncate()\\n\\n        content_length = total_content_length = int(\\n            resp.headers.get(\\\"Content-Length\\\", 0)\\n        )\\n        if partial and headers:\\n            # Get total content length, not the range we are currently fetching.\\n            # ex. Content-Range: bytes 200-1000/67589\\n            content_range = resp.headers.get(\\\"Content-Range\\\", \\\"bytes 0-0/0\\\")\\n            try:\\n                total_content_length = int(\\n                    content_range.split(\\\" \\\", 1)[1].rsplit(\\\"/\\\")[-1]\\n                )\\n            except (LookupError, ValueError):\\n                pass\\n\\n        for chunk in resp.iter_content(chunk_size=CHUNK_SIZE):\\n            # chunk could be the decompressed form of the real data\\n            # but we want the exact number of bytes read till now\\n            streamed_bytes = resp.raw.tell()\\n            try:\\n                target.write(chunk)\\n            except OSError as e:\\n                message = \\\"Failed to write to %(target_path)s\\\\n  errno: %(errno)d\\\"\\n                raise CondaError(message, target_path=target.name, errno=e.errno)\\n            size_builder += len(chunk)\\n\\n            if total_content_length and 0 <= streamed_bytes <= content_length:\\n                if progress_update_callback:\\n                    progress_update_callback(\\n                        (stat_result.st_size + streamed_bytes) / total_content_length\\n                    )\\n\\n        if content_length and streamed_bytes != content_length:\\n            # TODO: needs to be a more-specific error type\\n            message = dals(\\n                \\\"\\\"\\\"\\n            Downloaded bytes did not match Content-Length\\n                url: %(url)s\\n                target_path: %(target_path)s\\n                Content-Length: %(content_length)d\\n                downloaded bytes: %(downloaded_bytes)d\\n            \\\"\\\"\\\"\\n            )\\n            raise CondaError(\\n                message,\\n                url=url,\\n                target_path=target_full_path,\\n                content_length=content_length,\\n                downloaded_bytes=streamed_bytes,\\n            )\\n    # exit context manager, renaming target to target_full_path\\n\\n\\n@contextmanager\\ndef download_partial_file(\\n    target_full_path: str | Path, *, url: str, sha256: str, md5: str, size: int\\n):\\n    \\\"\\\"\\\"\\n    Create or open locked partial download file, moving onto target_full_path\\n    when finished. Preserve partial file on exception.\\n    \\\"\\\"\\\"\\n    target_full_path = Path(target_full_path)\\n    parent = target_full_path.parent\\n    name = Path(target_full_path).name\\n    partial_name = f\\\"{name}.partial\\\"\\n    partial_path = parent / partial_name\\n\\n    def check(target):\\n        target.seek(0)\\n        if md5 or sha256:\\n            checksum_type = \\\"sha256\\\" if sha256 else \\\"md5\\\"\\n            checksum = sha256 if sha256 else md5\\n            try:\\n                checksum_bytes = bytes.fromhex(checksum)\\n            except (ValueError, TypeError) as exc:\\n                raise CondaValueError(exc) from exc\\n            hasher = hashlib.new(checksum_type)\\n            target.seek(0)\\n            while read := target.read(CHUNK_SIZE):\\n                hasher.update(read)\\n\\n            if hasher.digest() != checksum_bytes:\\n                actual_checksum = hasher.hexdigest()\\n                log.debug(\\n                    \\\"%s mismatch for download: %s (%s != %s)\\\",\\n                    checksum_type,\\n                    url,\\n                    actual_checksum,\\n                    checksum,\\n                )\\n                raise ChecksumMismatchError(\\n                    url, target_full_path, checksum_type, checksum, actual_checksum\\n                )\\n        if size is not None:\\n            actual_size = os.fstat(target.fileno()).st_size\\n            if actual_size != size:\\n                log.debug(\\n                    \\\"size mismatch for download: %s (%s != %s)\\\",\\n                    url,\\n                    actual_size,\\n                    size,\\n                )\\n                raise ChecksumMismatchError(\\n                    url, target_full_path, \\\"size\\\", size, actual_size\\n                )\\n\\n    try:\\n        with partial_path.open(mode=\\\"a+b\\\") as partial, lock(partial):\\n            yield partial\\n            check(partial)\\n    except HTTPError as e:  # before conda error handler wrapper\\n        # Don't keep `.partial` for errors like 404 not found, or 'Range not\\n        # Satisfiable' that will never succeed\\n        try:\\n            status_code = e.response.status_code\\n        except AttributeError:\\n            status_code = None\\n        if isinstance(status_code, int) and 400 <= status_code < 500:\\n            partial_path.unlink()\\n        raise\\n    except ChecksumMismatchError:\\n        partial_path.unlink()\\n        raise\\n\\n    try:\\n        partial_path.rename(target_full_path)\\n    except OSError:  # Windows doesn't rename onto existing paths\\n        target_full_path.unlink()\\n        partial_path.rename(target_full_path)\\n\\n\\n@contextmanager\\ndef download_http_errors(url: str):\\n    \\\"\\\"\\\"Exception translator used inside download()\\\"\\\"\\\"\\n    # This complex exception translation strategy is reminiscent of def\\n    # conda_http_errors(url, repodata_fn): in gateways/repodata\\n\\n    try:\\n        yield\\n\\n    except ConnectionResetError as e:\\n        log.debug(f\\\"{e}, trying again\\\")\\n        # where does retry happen?\\n        raise\\n\\n    except RequestsProxyError:\\n        raise ProxyError()  # see #3962\\n\\n    except InvalidSchema as e:\\n        if \\\"SOCKS\\\" in str(e):\\n            message = dals(\\n                \\\"\\\"\\\"\\n                Requests has identified that your current working environment is configured\\n                to use a SOCKS proxy, but pysocks is not installed.  To proceed, remove your\\n                proxy configuration, run `conda install pysocks`, and then you can re-enable\\n                your proxy configuration.\\n                \\\"\\\"\\\"\\n            )\\n            raise CondaDependencyError(message)\\n        else:\\n            raise\\n\\n    except SSLError as e:\\n        # SSLError: either an invalid certificate or OpenSSL is unavailable\\n        try:\\n            import ssl  # noqa: F401\\n        except ImportError:\\n            raise CondaSSLError(\\n                dals(\\n                    f\\\"\\\"\\\"\\n                    OpenSSL appears to be unavailable on this machine. OpenSSL is required to\\n                    download and install packages.\\n\\n                    Exception: {e}\\n                    \\\"\\\"\\\"\\n                )\\n            )\\n        else:\\n            raise CondaSSLError(\\n                dals(\\n                    f\\\"\\\"\\\"\\n                    Encountered an SSL error. Most likely a certificate verification issue.\\n\\n                    Exception: {e}\\n                    \\\"\\\"\\\"\\n                )\\n            )\\n\\n    except (ConnectionError, HTTPError) as e:\\n        help_message = dals(\\n            \\\"\\\"\\\"\\n        An HTTP error occurred when trying to retrieve this URL.\\n        HTTP errors are often intermittent, and a simple retry will get you on your way.\\n        \\\"\\\"\\\"\\n        )\\n        raise CondaHTTPError(\\n            help_message,\\n            url,\\n            getattr(e.response, \\\"status_code\\\", None),\\n            getattr(e.response, \\\"reason\\\", None),\\n            getattr(e.response, \\\"elapsed\\\", None),\\n            e.response,\\n            caused_by=e,\\n        )\\n\\n\\ndef download_text(url):\\n    if not context.ssl_verify:\\n        disable_ssl_verify_warning()\\n    with download_http_errors(url):\\n        timeout = context.remote_connect_timeout_secs, context.remote_read_timeout_secs\\n        session = get_session(url)\\n        response = session.get(\\n            url, stream=True, proxies=session.proxies, timeout=timeout\\n        )\\n        if log.isEnabledFor(DEBUG):\\n            log.debug(stringify(response, content_max_len=256))\\n        response.raise_for_status()\\n    return response.text\\n\\n\\nclass TmpDownload:\\n    \\\"\\\"\\\"Context manager to handle downloads to a tempfile.\\\"\\\"\\\"\\n\\n    def __init__(self, url, verbose=True):\\n        self.url = url\\n        self.verbose = verbose\\n\\n    def __enter__(self):\\n        if \\\"://\\\" not in self.url:\\n            # if we provide the file itself, no tmp dir is created\\n            self.tmp_dir = None\\n            return self.url\\n        else:\\n            self.tmp_dir = tempfile.mkdtemp()\\n            dst = join(self.tmp_dir, basename(self.url))\\n            download(self.url, dst)\\n            return dst\\n\\n    def __exit__(self, exc_type, exc_value, traceback):\\n        if self.tmp_dir:\\n            rm_rf(self.tmp_dir)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\nfrom requests import ConnectionError, HTTPError, Session  # noqa: F401\\nfrom requests.adapters import DEFAULT_POOLBLOCK, BaseAdapter, HTTPAdapter  # noqa: F401\\nfrom requests.auth import AuthBase, _basic_auth_str  # noqa: F401\\nfrom requests.cookies import extract_cookies_to_jar  # noqa: F401\\nfrom requests.exceptions import (  # noqa: F401\\n    ChunkedEncodingError,\\n    InvalidSchema,\\n    SSLError,\\n)\\nfrom requests.exceptions import ProxyError as RequestsProxyError  # noqa: F401\\nfrom requests.hooks import dispatch_hook  # noqa: F401\\nfrom requests.models import PreparedRequest, Response  # noqa: F401\\nfrom requests.packages.urllib3.exceptions import InsecureRequestWarning  # noqa: F401\\nfrom requests.packages.urllib3.util.retry import Retry  # noqa: F401\\nfrom requests.structures import CaseInsensitiveDict  # noqa: F401\\nfrom requests.utils import get_auth_from_url, get_netrc_auth  # noqa: F401\\n\\n\\n# Copyright (C) 2012 Cory Benfield\\n# SPDX-License-Identifier: Apache-2.0\\n\\\"\\\"\\\"Defines FTP transport adapter for CondaSession (requests.Session).\\n\\nTaken from requests-ftp (https://github.com/Lukasa/requests-ftp/blob/master/requests_ftp/ftp.py).\\n\\nLicensed under the Apache License, Version 2.0 (the \\\"License\\\");\\nyou may not use this file except in compliance with the License.\\nYou may obtain a copy of the License at\\n\\n    http://www.apache.org/licenses/LICENSE-2.0\\n\\nUnless required by applicable law or agreed to in writing, software\\ndistributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\nSee the License for the specific language governing permissions and\\nlimitations under the License.\\n\\\"\\\"\\\"\\n\\nimport ftplib\\nimport os\\nfrom base64 import b64decode\\nfrom io import BytesIO, StringIO\\nfrom logging import getLogger\\n\\nfrom ....common.url import urlparse\\nfrom ....deprecations import deprecated\\nfrom ....exceptions import AuthenticationError\\nfrom .. import BaseAdapter, Response, dispatch_hook\\n\\nlog = getLogger(__name__)\\n\\n\\n# After: https://stackoverflow.com/a/44073062/3257826\\n#   And: https://stackoverflow.com/a/35368154/3257826\\n_old_makepasv = ftplib.FTP.makepasv\\n\\n\\ndef _new_makepasv(self):\\n    host, port = _old_makepasv(self)\\n    host = self.sock.getpeername()[0]\\n    return host, port\\n\\n\\nftplib.FTP.makepasv = _new_makepasv\\n\\n\\nclass FTPAdapter(BaseAdapter):\\n    \\\"\\\"\\\"A Requests Transport Adapter that handles FTP urls.\\\"\\\"\\\"\\n\\n    def __init__(self):\\n        super().__init__()\\n\\n        # Build a dictionary keyed off the methods we support in upper case.\\n        # The values of this dictionary should be the functions we use to\\n        # send the specific queries.\\n        self.func_table = {\\n            \\\"LIST\\\": self.list,\\n            \\\"RETR\\\": self.retr,\\n            \\\"STOR\\\": self.stor,\\n            \\\"NLST\\\": self.nlst,\\n            \\\"GET\\\": self.retr,\\n        }\\n\\n    def send(self, request, **kwargs):\\n        \\\"\\\"\\\"Sends a PreparedRequest object over FTP. Returns a response object.\\\"\\\"\\\"\\n        # Get the authentication from the prepared request, if any.\\n        auth = self.get_username_password_from_header(request)\\n\\n        # Next, get the host and the path.\\n        host, port, path = self.get_host_and_path_from_url(request)\\n\\n        # Sort out the timeout.\\n        timeout = kwargs.get(\\\"timeout\\\", None)\\n        if not isinstance(timeout, int):\\n            # https://github.com/conda/conda/pull/3392\\n            timeout = 10\\n\\n        # Establish the connection and login if needed.\\n        self.conn = ftplib.FTP()\\n        self.conn.connect(host, port, timeout)\\n\\n        if auth is not None:\\n            self.conn.login(auth[0], auth[1])\\n        else:\\n            self.conn.login()\\n\\n        # Get the method and attempt to find the function to call.\\n        resp = self.func_table[request.method](path, request)\\n\\n        # Return the response.\\n        return resp\\n\\n    def close(self):\\n        \\\"\\\"\\\"Dispose of any internal state.\\\"\\\"\\\"\\n        # Currently this is a no-op.\\n        pass\\n\\n    def list(self, path, request):\\n        \\\"\\\"\\\"Executes the FTP LIST command on the given path.\\\"\\\"\\\"\\n        data = StringIO()\\n\\n        # To ensure the StringIO gets cleaned up, we need to alias its close\\n        # method to the release_conn() method. This is a dirty hack, but there\\n        # you go.\\n        data.release_conn = data.close\\n\\n        self.conn.cwd(path)\\n        code = self.conn.retrbinary(\\\"LIST\\\", data_callback_factory(data))\\n\\n        # When that call has finished executing, we'll have all our data.\\n        response = build_text_response(request, data, code)\\n\\n        # Close the connection.\\n        self.conn.close()\\n\\n        return response\\n\\n    def retr(self, path, request):\\n        \\\"\\\"\\\"Executes the FTP RETR command on the given path.\\\"\\\"\\\"\\n        data = BytesIO()\\n\\n        # To ensure the BytesIO gets cleaned up, we need to alias its close\\n        # method. See self.list().\\n        data.release_conn = data.close\\n\\n        code = self.conn.retrbinary(\\\"RETR \\\" + path, data_callback_factory(data))\\n\\n        response = build_binary_response(request, data, code)\\n\\n        # Close the connection.\\n        self.conn.close()\\n\\n        return response\\n\\n    @deprecated(\\\"24.3\\\", \\\"24.9\\\")\\n    def stor(self, path, request):\\n        \\\"\\\"\\\"Executes the FTP STOR command on the given path.\\\"\\\"\\\"\\n        # First, get the file handle. We assume (bravely)\\n        # that there is only one file to be sent to a given URL. We also\\n        # assume that the filename is sent as part of the URL, not as part of\\n        # the files argument. Both of these assumptions are rarely correct,\\n        # but they are easy.\\n        data = parse_multipart_files(request)\\n\\n        # Split into the path and the filename.\\n        path, filename = os.path.split(path)\\n\\n        # Switch directories and upload the data.\\n        self.conn.cwd(path)\\n        code = self.conn.storbinary(\\\"STOR \\\" + filename, data)\\n\\n        # Close the connection and build the response.\\n        self.conn.close()\\n\\n        response = build_binary_response(request, BytesIO(), code)\\n\\n        return response\\n\\n    def nlst(self, path, request):\\n        \\\"\\\"\\\"Executes the FTP NLST command on the given path.\\\"\\\"\\\"\\n        data = StringIO()\\n\\n        # Alias the close method.\\n        data.release_conn = data.close\\n\\n        self.conn.cwd(path)\\n        code = self.conn.retrbinary(\\\"NLST\\\", data_callback_factory(data))\\n\\n        # When that call has finished executing, we'll have all our data.\\n        response = build_text_response(request, data, code)\\n\\n        # Close the connection.\\n        self.conn.close()\\n\\n        return response\\n\\n    def get_username_password_from_header(self, request):\\n        \\\"\\\"\\\"Given a PreparedRequest object, reverse the process of adding HTTP\\n        Basic auth to obtain the username and password. Allows the FTP adapter\\n        to piggyback on the basic auth notation without changing the control\\n        flow.\\n        \\\"\\\"\\\"\\n        auth_header = request.headers.get(\\\"Authorization\\\")\\n\\n        if auth_header:\\n            # The basic auth header is of the form 'Basic xyz'. We want the\\n            # second part. Check that we have the right kind of auth though.\\n            encoded_components = auth_header.split()[:2]\\n            if encoded_components[0] != \\\"Basic\\\":\\n                raise AuthenticationError(\\\"Invalid form of Authentication used.\\\")\\n            else:\\n                encoded = encoded_components[1]\\n\\n            # Decode the base64 encoded string.\\n            decoded = b64decode(encoded)\\n\\n            # The string is of the form 'username:password'. Split on the\\n            # colon.\\n            components = decoded.split(\\\":\\\")\\n            username = components[0]\\n            password = components[1]\\n            return (username, password)\\n        else:\\n            # No auth header. Return None.\\n            return None\\n\\n    def get_host_and_path_from_url(self, request):\\n        \\\"\\\"\\\"Given a PreparedRequest object, split the URL in such a manner as to\\n        determine the host and the path. This is a separate method to wrap some\\n        of urlparse's craziness.\\n        \\\"\\\"\\\"\\n        url = request.url\\n        parsed = urlparse(url)\\n        path = parsed.path\\n\\n        # If there is a slash on the front of the path, chuck it.\\n        if path[0] == \\\"/\\\":\\n            path = path[1:]\\n\\n        host = parsed.hostname\\n        port = parsed.port or 0\\n\\n        return (host, port, path)\\n\\n\\ndef data_callback_factory(variable):\\n    \\\"\\\"\\\"Returns a callback suitable for use by the FTP library. This callback\\n    will repeatedly save data into the variable provided to this function. This\\n    variable should be a file-like structure.\\n    \\\"\\\"\\\"\\n\\n    def callback(data):\\n        variable.write(data)\\n\\n    return callback\\n\\n\\ndef build_text_response(request, data, code):\\n    \\\"\\\"\\\"Build a response for textual data.\\\"\\\"\\\"\\n    return build_response(request, data, code, \\\"ascii\\\")\\n\\n\\ndef build_binary_response(request, data, code):\\n    \\\"\\\"\\\"Build a response for data whose encoding is unknown.\\\"\\\"\\\"\\n    return build_response(request, data, code, None)\\n\\n\\ndef build_response(request, data, code, encoding):\\n    \\\"\\\"\\\"Builds a response object from the data returned by ftplib, using the\\n    specified encoding.\\n    \\\"\\\"\\\"\\n    response = Response()\\n\\n    response.encoding = encoding\\n\\n    # Fill in some useful fields.\\n    response.raw = data\\n    response.url = request.url\\n    response.request = request\\n    response.status_code = get_status_code_from_code_response(code)\\n\\n    # Make sure to seek the file-like raw object back to the start.\\n    response.raw.seek(0)\\n\\n    # Run the response hook.\\n    response = dispatch_hook(\\\"response\\\", request.hooks, response)\\n    return response\\n\\n\\n@deprecated(\\\"24.3\\\", \\\"24.9\\\")\\ndef parse_multipart_files(request):\\n    \\\"\\\"\\\"Given a prepared request, return a file-like object containing the\\n    original data. This is pretty hacky.\\n    \\\"\\\"\\\"\\n    import cgi\\n\\n    # Start by grabbing the pdict.\\n    _, pdict = cgi.parse_header(request.headers[\\\"Content-Type\\\"])\\n\\n    # Now, wrap the multipart data in a BytesIO buffer. This is annoying.\\n    buf = BytesIO()\\n    buf.write(request.body)\\n    buf.seek(0)\\n\\n    # Parse the data. Simply take the first file.\\n    data = cgi.parse_multipart(buf, pdict)\\n    _, filedata = data.popitem()\\n    buf.close()\\n\\n    # Get a BytesIO now, and write the file into it.\\n    buf = BytesIO()\\n    buf.write(\\\"\\\".join(filedata))\\n    buf.seek(0)\\n\\n    return buf\\n\\n\\ndef get_status_code_from_code_response(code):\\n    r\\\"\\\"\\\"Handle complicated code response, even multi-lines.\\n\\n    We get the status code in two ways:\\n    - extracting the code from the last valid line in the response\\n    - getting it from the 3 first digits in the code\\n    After a comparison between the two values,\\n    we can safely set the code or raise a warning.\\n    Examples:\\n        - get_status_code_from_code_response('200 Welcome') == 200\\n        - multi_line_code = '226-File successfully transferred\\\\n226 0.000 seconds'\\n          get_status_code_from_code_response(multi_line_code) == 226\\n        - multi_line_with_code_conflicts = '200-File successfully transferred\\\\n226 0.000 seconds'\\n          get_status_code_from_code_response(multi_line_with_code_conflicts) == 226\\n    For more detail see RFC 959, page 36, on multi-line responses:\\n        https://www.ietf.org/rfc/rfc959.txt\\n        \\\"Thus the format for multi-line replies is that the first line\\n         will begin with the exact required reply code, followed\\n         immediately by a Hyphen, \\\"-\\\" (also known as Minus), followed by\\n         text.  The last line will begin with the same code, followed\\n         immediately by Space <SP>, optionally some text, and the Telnet\\n         end-of-line code.\\\"\\n    \\\"\\\"\\\"\\n    last_valid_line_from_code = [line for line in code.split(\\\"\\\\n\\\") if line][-1]\\n    status_code_from_last_line = int(last_valid_line_from_code.split()[0])\\n    status_code_from_first_digits = int(code[:3])\\n    if status_code_from_last_line != status_code_from_first_digits:\\n        log.warning(\\n            \\\"FTP response status code seems to be inconsistent.\\\\n\\\"\\n            \\\"Code received: %s, extracted: %s and %s\\\",\\n            code,\\n            status_code_from_last_line,\\n            status_code_from_first_digits,\\n        )\\n    return status_code_from_last_line\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n# Copyright (c) 2008-2023 The pip developers\\n# SPDX-License-Identifier: MIT\\n#\\n\\\"\\\"\\\"Defines HTTP transport adapter for CondaSession (requests.Session).\\n\\nClosely derived from pip:\\n\\nhttps://github.com/pypa/pip/blob/8c24fd2a80bad21aa29aec02fb48bd89a1e8f5e1/src/pip/_internal/network/session.py#L254\\n\\nUnder the MIT license:\\n\\nCopyright (c) 2008-2023 The pip developers (see AUTHORS.txt file on the pip repository)\\n\\nPermission is hereby granted, free of charge, to any person obtaining\\na copy of this software and associated documentation files (the\\n\\\"Software\\\"), to deal in the Software without restriction, including\\nwithout limitation the rights to use, copy, modify, merge, publish,\\ndistribute, sublicense, and/or sell copies of the Software, and to\\npermit persons to whom the Software is furnished to do so, subject to\\nthe following conditions:\\n\\nThe above copyright notice and this permission notice shall be\\nincluded in all copies or substantial portions of the Software.\\n\\nTHE SOFTWARE IS PROVIDED \\\"AS IS\\\", WITHOUT WARRANTY OF ANY KIND,\\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\\n\\\"\\\"\\\"\\n\\nfrom typing import TYPE_CHECKING, Any, Optional\\n\\nfrom .. import DEFAULT_POOLBLOCK\\nfrom .. import HTTPAdapter as BaseHTTPAdapter\\n\\nif TYPE_CHECKING:\\n    from ssl import SSLContext\\n\\n    from urllib3 import PoolManager\\n\\n\\nclass _SSLContextAdapterMixin:\\n    \\\"\\\"\\\"Mixin to add the ``ssl_context`` constructor argument to HTTP adapters.\\n\\n    The additional argument is forwarded directly to the pool manager. This allows us\\n    to dynamically decide what SSL store to use at runtime, which is used to implement\\n    the optional ``truststore`` backend.\\n    \\\"\\\"\\\"\\n\\n    def __init__(\\n        self,\\n        *,\\n        ssl_context: Optional[\\\"SSLContext\\\"] = None,\\n        **kwargs: Any,\\n    ) -> None:\\n        self._ssl_context = ssl_context\\n        super().__init__(**kwargs)\\n\\n    def init_poolmanager(\\n        self,\\n        connections: int,\\n        maxsize: int,\\n        block: bool = DEFAULT_POOLBLOCK,\\n        **pool_kwargs: Any,\\n    ) -> \\\"PoolManager\\\":\\n        if self._ssl_context is not None:\\n            pool_kwargs.setdefault(\\\"ssl_context\\\", self._ssl_context)\\n        return super().init_poolmanager(  # type: ignore[misc]\\n            connections=connections,\\n            maxsize=maxsize,\\n            block=block,\\n            **pool_kwargs,\\n        )\\n\\n\\nclass HTTPAdapter(_SSLContextAdapterMixin, BaseHTTPAdapter):\\n    pass\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Defines local filesystem transport adapter for CondaSession (requests.Session).\\\"\\\"\\\"\\n\\nimport json\\nfrom email.utils import formatdate\\nfrom logging import getLogger\\nfrom mimetypes import guess_type\\nfrom os import stat\\nfrom tempfile import SpooledTemporaryFile\\n\\nfrom ....common.compat import ensure_binary\\nfrom ....common.path import url_to_path\\nfrom .. import BaseAdapter, CaseInsensitiveDict, Response\\n\\nlog = getLogger(__name__)\\n\\n\\nclass LocalFSAdapter(BaseAdapter):\\n    def send(\\n        self, request, stream=None, timeout=None, verify=None, cert=None, proxies=None\\n    ):\\n        pathname = url_to_path(request.url)\\n\\n        resp = Response()\\n        resp.status_code = 200\\n        resp.url = request.url\\n\\n        try:\\n            stats = stat(pathname)\\n        except OSError as exc:\\n            resp.status_code = 404\\n            message = {\\n                \\\"error\\\": \\\"file does not exist\\\",\\n                \\\"path\\\": pathname,\\n                \\\"exception\\\": repr(exc),\\n            }\\n            fh = SpooledTemporaryFile()\\n            fh.write(ensure_binary(json.dumps(message)))\\n            fh.seek(0)\\n            resp.raw = fh\\n            resp.close = resp.raw.close\\n        else:\\n            modified = formatdate(stats.st_mtime, usegmt=True)\\n            content_type = guess_type(pathname)[0] or \\\"text/plain\\\"\\n            resp.headers = CaseInsensitiveDict(\\n                {\\n                    \\\"Content-Type\\\": content_type,\\n                    \\\"Content-Length\\\": stats.st_size,\\n                    \\\"Last-Modified\\\": modified,\\n                }\\n            )\\n\\n            resp.raw = open(pathname, \\\"rb\\\")\\n            resp.close = resp.raw.close\\n        return resp\\n\\n    def close(self):\\n        pass  # pragma: no cover\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Defines S3 transport adapter for CondaSession (requests.Session).\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport json\\nfrom logging import LoggerAdapter, getLogger\\nfrom tempfile import SpooledTemporaryFile\\nfrom typing import TYPE_CHECKING\\n\\nfrom ....common.compat import ensure_binary\\nfrom ....common.url import url_to_s3_info\\nfrom .. import BaseAdapter, CaseInsensitiveDict, Response\\n\\nif TYPE_CHECKING:\\n    from .. import PreparedRequest\\n\\nlog = getLogger(__name__)\\nstderrlog = LoggerAdapter(getLogger(\\\"conda.stderrlog\\\"), extra=dict(terminator=\\\"\\\\n\\\"))\\n\\n\\nclass S3Adapter(BaseAdapter):\\n    def send(\\n        self,\\n        request: PreparedRequest,\\n        stream: bool = False,\\n        timeout: None | float | tuple[float, float] | tuple[float, None] = None,\\n        verify: bool | str = True,\\n        cert: None | bytes | str | tuple[bytes | str, bytes | str] = None,\\n        proxies: dict[str, str] | None = None,\\n    ) -> Response:\\n        resp = Response()\\n        resp.status_code = 200\\n        resp.url = request.url\\n\\n        try:\\n            return self._send_boto3(resp, request)\\n        except ImportError:\\n            stderrlog.info(\\n                \\\"\\\\nError: boto3 is required for S3 channels. \\\"\\n                \\\"Please install with `conda install boto3`\\\\n\\\"\\n                \\\"Make sure to run `conda deactivate` if you \\\"\\n                \\\"are in a conda environment.\\\\n\\\"\\n            )\\n            resp.status_code = 404\\n            return resp\\n\\n    def close(self):\\n        pass\\n\\n    def _send_boto3(self, resp: Response, request: PreparedRequest) -> Response:\\n        from boto3.session import Session\\n        from botocore.exceptions import BotoCoreError, ClientError\\n\\n        bucket_name, key_string = url_to_s3_info(request.url)\\n        # https://github.com/conda/conda/issues/8993\\n        # creating a separate boto3 session to make this thread safe\\n        session = Session()\\n        # create a resource client using this thread's session object\\n        s3 = session.resource(\\\"s3\\\")\\n        # finally get the S3 object\\n        key = s3.Object(bucket_name, key_string[1:])\\n\\n        try:\\n            response = key.get()\\n        except (BotoCoreError, ClientError) as e:\\n            resp.status_code = 404\\n            message = {\\n                \\\"error\\\": \\\"error downloading file from s3\\\",\\n                \\\"path\\\": request.url,\\n                \\\"exception\\\": repr(e),\\n            }\\n            resp.raw = self._write_tempfile(\\n                lambda x: x.write(ensure_binary(json.dumps(message)))\\n            )\\n            resp.close = resp.raw.close\\n            return resp\\n\\n        key_headers = response[\\\"ResponseMetadata\\\"][\\\"HTTPHeaders\\\"]\\n        resp.headers = CaseInsensitiveDict(\\n            {\\n                \\\"Content-Type\\\": key_headers.get(\\\"content-type\\\", \\\"text/plain\\\"),\\n                \\\"Content-Length\\\": key_headers[\\\"content-length\\\"],\\n                \\\"Last-Modified\\\": key_headers[\\\"last-modified\\\"],\\n            }\\n        )\\n\\n        resp.raw = self._write_tempfile(key.download_fileobj)\\n        resp.close = resp.raw.close\\n\\n        return resp\\n\\n    def _write_tempfile(self, writer_callable):\\n        fh = SpooledTemporaryFile()\\n        writer_callable(fh)\\n        fh.seek(0)\\n        return fh\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc & Jason R. Coombs\\n# SPDX-License-Identifier: BSD-3-Clause, MIT\\n\\\"\\\"\\\"Disk utility functions for symlinking files and folders.\\n\\nPortions of the code within this module are taken from https://github.com/jaraco/jaraco.windows\\nwhich is MIT licensed by Jason R. Coombs.\\n\\nhttps://github.com/jaraco/skeleton/issues/1#issuecomment-285448440\\n\\\"\\\"\\\"\\n\\nimport sys\\nfrom logging import getLogger\\nfrom os import chmod as os_chmod\\nfrom os.path import abspath, isdir\\nfrom os.path import islink as os_islink\\nfrom os.path import lexists as os_lexists\\n\\nfrom ...common.compat import on_win\\nfrom ...exceptions import CondaOSError, ParseError\\n\\n__all__ = (\\\"islink\\\", \\\"lchmod\\\", \\\"lexists\\\", \\\"link\\\", \\\"readlink\\\", \\\"symlink\\\")\\n\\nlog = getLogger(__name__)\\nPYPY = sys.implementation.name == \\\"pypy\\\"\\n\\n\\ntry:\\n    from os import lchmod as os_lchmod\\n\\n    lchmod = os_lchmod\\nexcept ImportError:  # pragma: no cover\\n\\n    def lchmod(path, mode):\\n        # On systems that don't allow permissions on symbolic links, skip\\n        # links entirely.\\n        if not islink(path):\\n            os_chmod(path, mode)\\n\\n\\nif not on_win:  # pragma: win no cover\\n    from os import link, symlink\\n\\n    link = link\\n    symlink = symlink\\n\\nelse:  # pragma: unix no cover\\n    from ctypes import windll, wintypes\\n\\n    CreateHardLink = windll.kernel32.CreateHardLinkW\\n    CreateHardLink.restype = wintypes.BOOL\\n    CreateHardLink.argtypes = [wintypes.LPCWSTR, wintypes.LPCWSTR, wintypes.LPVOID]\\n    try:\\n        CreateSymbolicLink = windll.kernel32.CreateSymbolicLinkW\\n        CreateSymbolicLink.restype = wintypes.BOOL\\n        CreateSymbolicLink.argtypes = [\\n            wintypes.LPCWSTR,\\n            wintypes.LPCWSTR,\\n            wintypes.DWORD,\\n        ]\\n    except AttributeError:\\n        CreateSymbolicLink = None\\n\\n    def win_hard_link(src, dst):\\n        \\\"\\\"\\\"Equivalent to os.link, using the win32 CreateHardLink call.\\\"\\\"\\\"\\n        if not CreateHardLink(dst, src, None):\\n            raise CondaOSError(f\\\"win32 hard link failed\\\\n  src: {src}\\\\n  dst: {dst}\\\")\\n\\n    def win_soft_link(src, dst):\\n        \\\"\\\"\\\"Equivalent to os.symlink, using the win32 CreateSymbolicLink call.\\\"\\\"\\\"\\n        if CreateSymbolicLink is None:\\n            raise CondaOSError(\\\"win32 soft link not supported\\\")\\n        if not CreateSymbolicLink(dst, src, isdir(src)):\\n            raise CondaOSError(f\\\"win32 soft link failed\\\\n  src: {src}\\\\n  dst: {dst}\\\")\\n\\n    link = win_hard_link\\n    symlink = win_soft_link\\n\\n\\nif not (on_win and PYPY):\\n    from os import readlink\\n\\n    islink = os_islink\\n    lexists = os_lexists\\n    readlink = readlink\\n\\nelse:  # pragma: no cover\\n    import builtins\\n    import inspect\\n    import sys\\n    from ctypes import POINTER, Structure, byref, c_uint64, cast, windll, wintypes\\n    from os import getcwd\\n    from os.path import isfile\\n\\n    def islink(path):\\n        \\\"\\\"\\\"Determine if the given path is a symlink\\\"\\\"\\\"\\n        return is_reparse_point(path) and is_symlink(path)\\n\\n    def lexists(path):\\n        if islink(path):\\n            return True\\n        if isdir(path):\\n            return True\\n        if isfile(path):\\n            return True\\n        return False\\n\\n    MAX_PATH = 260\\n    IO_REPARSE_TAG_SYMLINK = 0xA000000C\\n    INVALID_FILE_ATTRIBUTES = 0xFFFFFFFF\\n    FILE_ATTRIBUTE_REPARSE_POINT = 0x400\\n    NULL = 0\\n    ERROR_NO_MORE_FILES = 0x12\\n\\n    class WIN32_FIND_DATA(Structure):\\n        _fields_ = [\\n            (\\\"file_attributes\\\", wintypes.DWORD),\\n            (\\\"creation_time\\\", wintypes.FILETIME),\\n            (\\\"last_access_time\\\", wintypes.FILETIME),\\n            (\\\"last_write_time\\\", wintypes.FILETIME),\\n            (\\\"file_size_words\\\", wintypes.DWORD * 2),\\n            (\\\"reserved\\\", wintypes.DWORD * 2),\\n            (\\\"filename\\\", wintypes.WCHAR * MAX_PATH),\\n            (\\\"alternate_filename\\\", wintypes.WCHAR * 14),\\n        ]\\n\\n        @property\\n        def file_size(self):\\n            return cast(self.file_size_words, POINTER(c_uint64)).contents\\n\\n    LPWIN32_FIND_DATA = POINTER(WIN32_FIND_DATA)\\n    FindFirstFile = windll.kernel32.FindFirstFileW\\n    FindFirstFile.argtypes = (wintypes.LPWSTR, LPWIN32_FIND_DATA)\\n    FindFirstFile.restype = wintypes.HANDLE\\n    FindNextFile = windll.kernel32.FindNextFileW\\n    FindNextFile.argtypes = (wintypes.HANDLE, LPWIN32_FIND_DATA)\\n    FindNextFile.restype = wintypes.BOOLEAN\\n    INVALID_HANDLE_VALUE = wintypes.HANDLE(-1).value\\n    GetFileAttributes = windll.kernel32.GetFileAttributesW\\n    GetFileAttributes.restype = wintypes.DWORD\\n    GetFileAttributes.argtypes = (wintypes.LPWSTR,)\\n\\n    def handle_nonzero_success(result):\\n        if result == 0:\\n            raise OSError()\\n\\n    def format_system_message(errno):\\n        \\\"\\\"\\\"\\n        Call FormatMessage with a system error number to retrieve\\n        the descriptive error message.\\n        \\\"\\\"\\\"\\n        # first some flags used by FormatMessageW\\n        ALLOCATE_BUFFER = 0x100\\n        FROM_SYSTEM = 0x1000\\n\\n        # Let FormatMessageW allocate the buffer (we'll free it below)\\n        # Also, let it know we want a system error message.\\n        flags = ALLOCATE_BUFFER | FROM_SYSTEM\\n        source = None\\n        message_id = errno\\n        language_id = 0\\n        result_buffer = wintypes.LPWSTR()\\n        buffer_size = 0\\n        arguments = None\\n        bytes = windll.kernel32.FormatMessageW(\\n            flags,\\n            source,\\n            message_id,\\n            language_id,\\n            byref(result_buffer),\\n            buffer_size,\\n            arguments,\\n        )\\n        # note the following will cause an infinite loop if GetLastError\\n        #  repeatedly returns an error that cannot be formatted, although\\n        #  this should not happen.\\n        handle_nonzero_success(bytes)\\n        message = result_buffer.value\\n        windll.kernel32.LocalFree(result_buffer)\\n        return message\\n\\n    class WindowsError(builtins.WindowsError):\\n        # more info about errors at http://msdn.microsoft.com/en-us/library/ms681381(VS.85).aspx\\n\\n        def __init__(self, value=None):\\n            if value is None:\\n                value = windll.kernel32.GetLastError()\\n            strerror = format_system_message(value)\\n            args = 0, strerror, None, value\\n            super().__init__(*args)\\n\\n        @property\\n        def message(self):\\n            return self.strerror\\n\\n        @property\\n        def code(self):\\n            return self.winerror\\n\\n        def __str__(self):\\n            return self.message\\n\\n        def __repr__(self):\\n            return \\\"{self.__class__.__name__}({self.winerror})\\\".format(**vars())\\n\\n    def _is_symlink(find_data):\\n        return find_data.reserved[0] == IO_REPARSE_TAG_SYMLINK\\n\\n    def _patch_path(path):\\n        r\\\"\\\"\\\"\\n        Paths have a max length of api.MAX_PATH characters (260). If a target path\\n        is longer than that, it needs to be made absolute and prepended with\\n        \\\\\\\\?\\\\ in order to work with API calls.\\n        See http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx for\\n        details.\\n        \\\"\\\"\\\"  # NOQA\\n        if path.startswith(\\\"\\\\\\\\\\\\\\\\?\\\\\\\\\\\"):\\n            return path\\n        path = abspath(path)\\n        if not path[1] == \\\":\\\":\\n            # python doesn't include the drive letter, but \\\\\\\\?\\\\ requires it\\n            path = getcwd()[:2] + path\\n        return \\\"\\\\\\\\\\\\\\\\?\\\\\\\\\\\" + path\\n\\n    def local_format(string):\\n        \\\"\\\"\\\"Format the string using variables in the caller's local namespace.\\n\\n        .. code-block:: pycon\\n            >>> a = 3\\n            >>> local_format(\\\"{a:5}\\\")\\n            '    3'\\n        \\\"\\\"\\\"\\n        context = inspect.currentframe().f_back.f_locals\\n        return string.format_map(context)\\n\\n    def is_symlink(path):\\n        \\\"\\\"\\\"Assuming path is a reparse point, determine if it's a symlink.\\\"\\\"\\\"\\n        path = _patch_path(path)\\n        try:\\n            return _is_symlink(next(find_files(path)))\\n        except OSError as orig_error:  # NOQA\\n            tmpl = \\\"Error accessing {path}: {orig_error.message}\\\"\\n            raise OSError(local_format(tmpl))\\n\\n    def find_files(spec):\\n        r\\\"\\\"\\\"\\n        A pythonic wrapper around the FindFirstFile/FindNextFile win32 api.\\n        >>> root_files = tuple(find_files(r'c:\\\\*'))\\n        >>> len(root_files) > 1\\n        True\\n        >>> root_files[0].filename == root_files[1].filename\\n        False\\n        >>> # This test might fail on a non-standard installation\\n        >>> 'Windows' in (fd.filename for fd in root_files)\\n        True\\n        \\\"\\\"\\\"  # NOQA\\n        fd = WIN32_FIND_DATA()\\n        handle = FindFirstFile(spec, byref(fd))\\n        while True:\\n            if handle == INVALID_HANDLE_VALUE:\\n                raise OSError()\\n            yield fd\\n            fd = WIN32_FIND_DATA()\\n            res = FindNextFile(handle, byref(fd))\\n            if res == 0:  # error\\n                error = WindowsError()\\n                if error.code == ERROR_NO_MORE_FILES:\\n                    break\\n                else:\\n                    raise error\\n        # todo: how to close handle when generator is destroyed?\\n        # hint: catch GeneratorExit\\n        windll.kernel32.FindClose(handle)\\n\\n    def is_reparse_point(path):\\n        \\\"\\\"\\\"\\n        Determine if the given path is a reparse point.\\n        Return False if the file does not exist or the file attributes cannot\\n        be determined.\\n        \\\"\\\"\\\"\\n        res = GetFileAttributes(path)\\n        return res != INVALID_FILE_ATTRIBUTES and bool(\\n            res & FILE_ATTRIBUTE_REPARSE_POINT\\n        )\\n\\n    OPEN_EXISTING = 3\\n    FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000\\n    FILE_FLAG_BACKUP_SEMANTICS = 0x2000000\\n    FSCTL_GET_REPARSE_POINT = 0x900A8\\n    LPDWORD = POINTER(wintypes.DWORD)\\n    LPOVERLAPPED = wintypes.LPVOID\\n    # VOLUME_NAME_DOS = 0\\n\\n    class SECURITY_ATTRIBUTES(Structure):\\n        _fields_ = (\\n            (\\\"length\\\", wintypes.DWORD),\\n            (\\\"p_security_descriptor\\\", wintypes.LPVOID),\\n            (\\\"inherit_handle\\\", wintypes.BOOLEAN),\\n        )\\n\\n    LPSECURITY_ATTRIBUTES = POINTER(SECURITY_ATTRIBUTES)\\n\\n    CreateFile = windll.kernel32.CreateFileW\\n    CreateFile.argtypes = (\\n        wintypes.LPWSTR,\\n        wintypes.DWORD,\\n        wintypes.DWORD,\\n        LPSECURITY_ATTRIBUTES,\\n        wintypes.DWORD,\\n        wintypes.DWORD,\\n        wintypes.HANDLE,\\n    )\\n    CreateFile.restype = wintypes.HANDLE\\n\\n    CloseHandle = windll.kernel32.CloseHandle\\n    CloseHandle.argtypes = (wintypes.HANDLE,)\\n    CloseHandle.restype = wintypes.BOOLEAN\\n\\n    from ctypes import Array, c_byte, c_ulong, c_ushort, create_string_buffer, sizeof\\n\\n    class REPARSE_DATA_BUFFER(Structure):\\n        _fields_ = [\\n            (\\\"tag\\\", c_ulong),\\n            (\\\"data_length\\\", c_ushort),\\n            (\\\"reserved\\\", c_ushort),\\n            (\\\"substitute_name_offset\\\", c_ushort),\\n            (\\\"substitute_name_length\\\", c_ushort),\\n            (\\\"print_name_offset\\\", c_ushort),\\n            (\\\"print_name_length\\\", c_ushort),\\n            (\\\"flags\\\", c_ulong),\\n            (\\\"path_buffer\\\", c_byte * 1),\\n        ]\\n\\n        def get_print_name(self):\\n            wchar_size = sizeof(wintypes.WCHAR)\\n            arr_typ = wintypes.WCHAR * (self.print_name_length // wchar_size)\\n            data = byref(self.path_buffer, self.print_name_offset)\\n            return cast(data, POINTER(arr_typ)).contents.value\\n\\n        def get_substitute_name(self):\\n            wchar_size = sizeof(wintypes.WCHAR)\\n            arr_typ = wintypes.WCHAR * (self.substitute_name_length // wchar_size)\\n            data = byref(self.path_buffer, self.substitute_name_offset)\\n            return cast(data, POINTER(arr_typ)).contents.value\\n\\n    def readlink(link):\\n        \\\"\\\"\\\"Return a string representing the path to which the symbolic link points.\\n\\n        readlink(link) -> target\\n        \\\"\\\"\\\"\\n        handle = CreateFile(\\n            link,\\n            0,\\n            0,\\n            None,\\n            OPEN_EXISTING,\\n            FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,\\n            None,\\n        )\\n\\n        if handle == INVALID_HANDLE_VALUE:\\n            raise OSError()\\n\\n        res = reparse_DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, None, 10240)\\n\\n        bytes = create_string_buffer(res)\\n        p_rdb = cast(bytes, POINTER(REPARSE_DATA_BUFFER))\\n        rdb = p_rdb.contents\\n        if not rdb.tag == IO_REPARSE_TAG_SYMLINK:\\n            raise ParseError(\\\"Expected IO_REPARSE_TAG_SYMLINK, but got %d\\\" % rdb.tag)\\n\\n        handle_nonzero_success(CloseHandle(handle))\\n        return rdb.get_substitute_name()\\n\\n    DeviceIoControl = windll.kernel32.DeviceIoControl\\n    DeviceIoControl.argtypes = [\\n        wintypes.HANDLE,\\n        wintypes.DWORD,\\n        wintypes.LPVOID,\\n        wintypes.DWORD,\\n        wintypes.LPVOID,\\n        wintypes.DWORD,\\n        LPDWORD,\\n        LPOVERLAPPED,\\n    ]\\n    DeviceIoControl.restype = wintypes.BOOL\\n\\n    def reparse_DeviceIoControl(\\n        device, io_control_code, in_buffer, out_buffer, overlapped=None\\n    ):\\n        if overlapped is not None:\\n            raise NotImplementedError(\\\"overlapped handles not yet supported\\\")\\n\\n        if isinstance(out_buffer, int):\\n            out_buffer = create_string_buffer(out_buffer)\\n\\n        in_buffer_size = len(in_buffer) if in_buffer is not None else 0\\n        out_buffer_size = len(out_buffer)\\n        assert isinstance(out_buffer, Array)\\n\\n        returned_bytes = wintypes.DWORD()\\n\\n        res = DeviceIoControl(\\n            device,\\n            io_control_code,\\n            in_buffer,\\n            in_buffer_size,\\n            out_buffer,\\n            out_buffer_size,\\n            returned_bytes,\\n            overlapped,\\n        )\\n\\n        handle_nonzero_success(res)\\n        handle_nonzero_success(returned_bytes)\\n        return out_buffer[: returned_bytes.value]\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"\\nRecord locking to manage potential repodata / repodata metadata file contention\\nbetween conda processes. Try to acquire a lock on a single byte in the metadat\\nfile; modify both files; then release the lock.\\n\\\"\\\"\\\"\\n\\nimport time\\nimport warnings\\nfrom contextlib import contextmanager\\n\\nfrom ...base.context import context\\n\\nLOCK_BYTE = 21  # mamba interop\\nLOCK_ATTEMPTS = 10\\nLOCK_SLEEP = 1\\n\\n\\n@contextmanager\\ndef _lock_noop(fd):\\n    \\\"\\\"\\\"When locking is not available.\\\"\\\"\\\"\\n    yield\\n\\n\\ntry:  # pragma: no cover\\n    import msvcrt\\n\\n    @contextmanager\\n    def _lock_impl(fd):  # type: ignore\\n        tell = fd.tell()\\n        fd.seek(LOCK_BYTE)\\n        msvcrt.locking(fd.fileno(), msvcrt.LK_LOCK, 1)  # type: ignore\\n        try:\\n            fd.seek(tell)\\n            yield\\n        finally:\\n            fd.seek(LOCK_BYTE)\\n            msvcrt.locking(fd.fileno(), msvcrt.LK_UNLCK, 1)  # type: ignore\\n\\nexcept ImportError:\\n    try:\\n        import fcntl\\n    except ImportError:  # pragma: no cover\\n        # \\\"fcntl Availibility: not Emscripten, not WASI.\\\"\\n        warnings.warn(\\\"file locking not available\\\")\\n\\n        _lock_impl = _lock_noop  # type: ignore\\n\\n    else:\\n\\n        class _lock_impl:\\n            def __init__(self, fd):\\n                self.fd = fd\\n\\n            def __enter__(self):\\n                for attempt in range(LOCK_ATTEMPTS):\\n                    try:\\n                        # msvcrt locking does something similar\\n                        fcntl.lockf(\\n                            self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB, 1, LOCK_BYTE\\n                        )\\n                        break\\n                    except OSError:\\n                        if attempt > LOCK_ATTEMPTS - 2:\\n                            raise\\n                        time.sleep(LOCK_SLEEP)\\n\\n            def __exit__(self, *exc):\\n                fcntl.lockf(self.fd, fcntl.LOCK_UN, 1, LOCK_BYTE)\\n\\n\\ndef lock(fd):\\n    if not context.no_lock:\\n        # locking required for jlap, now default for all\\n        return _lock_impl(fd)\\n    return _lock_noop(fd)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Disk utility functions testing path properties (e.g., writable, hardlinks, softlinks, etc.).\\\"\\\"\\\"\\n\\nfrom functools import lru_cache\\nfrom logging import getLogger\\nfrom os import W_OK, access\\nfrom os.path import basename, dirname, isdir, isfile, join\\nfrom uuid import uuid4\\n\\nfrom ...base.constants import PREFIX_MAGIC_FILE\\nfrom ...common.constants import TRACE\\nfrom ...common.path import expand\\nfrom ...models.enums import LinkType\\nfrom .create import create_link\\nfrom .delete import rm_rf\\nfrom .link import islink, lexists\\n\\nlog = getLogger(__name__)\\n\\n\\ndef file_path_is_writable(path):\\n    path = expand(path)\\n    log.log(TRACE, \\\"checking path is writable %s\\\", path)\\n    if isdir(dirname(path)):\\n        path_existed = lexists(path)\\n        try:\\n            fh = open(path, \\\"a+\\\")\\n        except OSError as e:\\n            log.debug(e)\\n            return False\\n        else:\\n            fh.close()\\n            if not path_existed:\\n                rm_rf(path)\\n            return True\\n    else:\\n        # TODO: probably won't work well on Windows\\n        return access(path, W_OK)\\n\\n\\n@lru_cache(maxsize=None)\\ndef hardlink_supported(source_file, dest_dir):\\n    test_file = join(dest_dir, f\\\".tmp.{basename(source_file)}.{str(uuid4())[:8]}\\\")\\n    assert isfile(source_file), source_file\\n    assert isdir(dest_dir), dest_dir\\n    if lexists(test_file):\\n        rm_rf(test_file)\\n    assert not lexists(test_file), test_file\\n    try:\\n        # BeeGFS is a file system that does not support hard links between files in different\\n        # directories. Sometimes a soft link will be created with the hard link system call.\\n        create_link(source_file, test_file, LinkType.hardlink, force=True)\\n        is_supported = not islink(test_file)\\n        if is_supported:\\n            log.log(TRACE, \\\"hard link supported for %s => %s\\\", source_file, dest_dir)\\n        else:\\n            log.log(\\n                TRACE, \\\"hard link IS NOT supported for %s => %s\\\", source_file, dest_dir\\n            )\\n        return is_supported\\n    except OSError:\\n        log.log(TRACE, \\\"hard link IS NOT supported for %s => %s\\\", source_file, dest_dir)\\n        return False\\n    finally:\\n        rm_rf(test_file)\\n\\n\\n@lru_cache(maxsize=None)\\ndef softlink_supported(source_file, dest_dir):\\n    # On Windows, softlink creation is restricted to Administrative users by default. It can\\n    # optionally be enabled for non-admin users through explicit registry modification.\\n    log.log(TRACE, \\\"checking soft link capability for %s => %s\\\", source_file, dest_dir)\\n    test_path = join(dest_dir, \\\".tmp.\\\" + basename(source_file))\\n    assert isfile(source_file), source_file\\n    assert isdir(dest_dir), dest_dir\\n    assert not lexists(test_path), test_path\\n    try:\\n        create_link(source_file, test_path, LinkType.softlink, force=True)\\n        return islink(test_path)\\n    except OSError:\\n        return False\\n    finally:\\n        rm_rf(test_path)\\n\\n\\ndef is_conda_environment(prefix):\\n    return isfile(join(prefix, PREFIX_MAGIC_FILE))\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Disk utility functions for modifying file and directory permissions.\\\"\\\"\\\"\\n\\nfrom errno import EACCES, ENOENT, EPERM, EROFS\\nfrom itertools import chain\\nfrom logging import getLogger\\nfrom os import X_OK, access, chmod, lstat, walk\\nfrom os.path import isdir, isfile, join\\nfrom stat import S_IEXEC, S_IMODE, S_ISDIR, S_ISREG, S_IWRITE, S_IXGRP, S_IXOTH, S_IXUSR\\n\\nfrom ...common.compat import on_win\\nfrom ...common.constants import TRACE\\nfrom . import MAX_TRIES, exp_backoff_fn\\nfrom .link import islink, lchmod\\n\\nlog = getLogger(__name__)\\n\\n\\ndef make_writable(path):\\n    try:\\n        mode = lstat(path).st_mode\\n        if S_ISDIR(mode):\\n            chmod(path, S_IMODE(mode) | S_IWRITE | S_IEXEC)\\n        elif islink(path):\\n            lchmod(path, S_IMODE(mode) | S_IWRITE)\\n        elif S_ISREG(mode):\\n            chmod(path, S_IMODE(mode) | S_IWRITE)\\n        else:\\n            log.debug(\\\"path cannot be made writable: %s\\\", path)\\n        return True\\n    except Exception as e:\\n        eno = getattr(e, \\\"errno\\\", None)\\n        if eno in (ENOENT,):\\n            log.debug(\\\"tried to make writable, but didn't exist: %s\\\", path)\\n            raise\\n        elif eno in (EACCES, EPERM, EROFS):\\n            log.debug(\\\"tried make writable but failed: %s\\\\n%r\\\", path, e)\\n            return False\\n        else:\\n            log.warning(\\\"Error making path writable: %s\\\\n%r\\\", path, e)\\n            raise\\n\\n\\ndef make_read_only(path):\\n    mode = lstat(path).st_mode\\n    if S_ISDIR(mode):\\n        chmod(path, S_IMODE(mode) & ~S_IWRITE)\\n    elif islink(path):\\n        lchmod(path, S_IMODE(mode) & ~S_IWRITE)\\n    elif S_ISREG(mode):\\n        chmod(path, S_IMODE(mode) & ~S_IWRITE)\\n    else:\\n        log.debug(\\\"path cannot be made read only: %s\\\", path)\\n    return True\\n\\n\\ndef recursive_make_writable(path, max_tries=MAX_TRIES):\\n    # The need for this function was pointed out at\\n    #   https://github.com/conda/conda/issues/3266#issuecomment-239241915\\n    # Especially on windows, file removal will often fail because it is marked read-only\\n    if isdir(path):\\n        for root, dirs, files in walk(path):\\n            for path in chain.from_iterable((files, dirs)):\\n                try:\\n                    exp_backoff_fn(make_writable, join(root, path), max_tries=max_tries)\\n                except OSError as e:\\n                    if e.errno == ENOENT:\\n                        log.debug(\\\"no such file or directory: %s\\\", path)\\n                    else:\\n                        raise\\n    else:\\n        exp_backoff_fn(make_writable, path, max_tries=max_tries)\\n\\n\\ndef make_executable(path):\\n    if isfile(path):\\n        mode = lstat(path).st_mode\\n        log.log(TRACE, \\\"chmod +x %s\\\", path)\\n        chmod(path, S_IMODE(mode) | S_IXUSR | S_IXGRP | S_IXOTH)\\n    else:\\n        log.error(\\\"Cannot make path '%s' executable\\\", path)\\n\\n\\ndef is_executable(path):\\n    if isfile(path):  # for now, leave out `and not islink(path)`\\n        return path.endswith((\\\".exe\\\", \\\".bat\\\")) if on_win else access(path, X_OK)\\n    return False\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Disk utility functions for creating new files or directories.\\\"\\\"\\\"\\n\\nimport codecs\\nimport os\\nimport sys\\nimport tempfile\\nimport warnings as _warnings\\nfrom errno import EACCES, EPERM, EROFS\\nfrom logging import getLogger\\nfrom os.path import dirname, isdir, isfile, join, splitext\\nfrom shutil import copyfileobj, copystat\\n\\nfrom ... import CondaError\\nfrom ...auxlib.ish import dals\\nfrom ...base.constants import CONDA_PACKAGE_EXTENSION_V1, PACKAGE_CACHE_MAGIC_FILE\\nfrom ...base.context import context\\nfrom ...common.compat import on_linux, on_win\\nfrom ...common.constants import TRACE\\nfrom ...common.path import ensure_pad, expand, win_path_double_escape, win_path_ok\\nfrom ...common.serialize import json_dump\\nfrom ...exceptions import BasicClobberError, CondaOSError, maybe_raise\\nfrom ...models.enums import LinkType\\nfrom . import mkdir_p\\nfrom .delete import path_is_clean, rm_rf\\nfrom .link import islink, lexists, link, readlink, symlink\\nfrom .permissions import make_executable\\nfrom .update import touch\\n\\n\\n# we have our own TemporaryDirectory implementation both for historical reasons and because\\n#     using our rm_rf function is more robust than the shutil equivalent\\nclass TemporaryDirectory:\\n    \\\"\\\"\\\"Create and return a temporary directory.  This has the same\\n    behavior as mkdtemp but can be used as a context manager.  For\\n    example:\\n\\n        with TemporaryDirectory() as tmpdir:\\n            ...\\n\\n    Upon exiting the context, the directory and everything contained\\n    in it are removed.\\n    \\\"\\\"\\\"\\n\\n    # Handle mkdtemp raising an exception\\n    name = None\\n    _closed = False\\n\\n    def __init__(self, suffix=\\\"\\\", prefix=\\\"tmp\\\", dir=None):\\n        self.name = tempfile.mkdtemp(suffix, prefix, dir)\\n\\n    def __repr__(self):\\n        return f\\\"<{self.__class__.__name__} {self.name!r}>\\\"\\n\\n    def __enter__(self):\\n        return self.name\\n\\n    def cleanup(self, _warn=False, _warnings=_warnings):\\n        from .delete import rm_rf as _rm_rf\\n\\n        if self.name and not self._closed:\\n            try:\\n                _rm_rf(self.name)\\n            except (TypeError, AttributeError) as ex:\\n                if \\\"None\\\" not in f\\\"{ex}\\\":\\n                    raise\\n                _rm_rf(self.name)\\n            self._closed = True\\n\\n    def __exit__(self, exc, value, tb):\\n        self.cleanup()\\n\\n    def __del__(self):\\n        # Issue a ResourceWarning if implicit cleanup needed\\n        self.cleanup(_warn=True)\\n\\n\\nlog = getLogger(__name__)\\nstdoutlog = getLogger(\\\"conda.stdoutlog\\\")\\n\\n# in __init__.py to help with circular imports\\nmkdir_p = mkdir_p\\n\\npython_entry_point_template = dals(\\n    r\\\"\\\"\\\"\\n# -*- coding: utf-8 -*-\\nimport re\\nimport sys\\n\\nfrom %(module)s import %(import_name)s\\n\\nif __name__ == '__main__':\\n    sys.argv[0] = re.sub(r'(-script\\\\.pyw?|\\\\.exe)?$', '', sys.argv[0])\\n    sys.exit(%(func)s())\\n\\\"\\\"\\\"\\n)  # NOQA\\n\\napplication_entry_point_template = dals(\\n    \\\"\\\"\\\"\\n# -*- coding: utf-8 -*-\\nif __name__ == '__main__':\\n    import os\\n    import sys\\n    args = [\\\"%(source_full_path)s\\\"]\\n    if len(sys.argv) > 1:\\n        args += sys.argv[1:]\\n    os.execv(args[0], args)\\n\\\"\\\"\\\"\\n)\\n\\n\\ndef write_as_json_to_file(file_path, obj):\\n    log.log(TRACE, \\\"writing json to file %s\\\", file_path)\\n    with codecs.open(file_path, mode=\\\"wb\\\", encoding=\\\"utf-8\\\") as fo:\\n        json_str = json_dump(obj)\\n        fo.write(json_str)\\n\\n\\ndef create_python_entry_point(target_full_path, python_full_path, module, func):\\n    if lexists(target_full_path):\\n        maybe_raise(\\n            BasicClobberError(\\n                source_path=None,\\n                target_path=target_full_path,\\n                context=context,\\n            ),\\n            context,\\n        )\\n\\n    import_name = func.split(\\\".\\\")[0]\\n    pyscript = python_entry_point_template % {\\n        \\\"module\\\": module,\\n        \\\"func\\\": func,\\n        \\\"import_name\\\": import_name,\\n    }\\n    if python_full_path is not None:\\n        from ...core.portability import generate_shebang_for_entry_point\\n\\n        shebang = generate_shebang_for_entry_point(python_full_path)\\n    else:\\n        shebang = None\\n\\n    with codecs.open(target_full_path, mode=\\\"wb\\\", encoding=\\\"utf-8\\\") as fo:\\n        if shebang is not None:\\n            fo.write(shebang)\\n        fo.write(pyscript)\\n\\n    if shebang is not None:\\n        make_executable(target_full_path)\\n\\n    return target_full_path\\n\\n\\ndef create_application_entry_point(\\n    source_full_path, target_full_path, python_full_path\\n):\\n    # source_full_path: where the entry point file points to\\n    # target_full_path: the location of the new entry point file being created\\n    if lexists(target_full_path):\\n        maybe_raise(\\n            BasicClobberError(\\n                source_path=None,\\n                target_path=target_full_path,\\n                context=context,\\n            ),\\n            context,\\n        )\\n\\n    entry_point = application_entry_point_template % {\\n        \\\"source_full_path\\\": win_path_double_escape(source_full_path),\\n    }\\n    if not isdir(dirname(target_full_path)):\\n        mkdir_p(dirname(target_full_path))\\n    with open(target_full_path, \\\"w\\\") as fo:\\n        if \\\" \\\" in python_full_path:\\n            python_full_path = ensure_pad(python_full_path, '\\\"')\\n        fo.write(f\\\"#!{python_full_path}\\\\n\\\")\\n        fo.write(entry_point)\\n    make_executable(target_full_path)\\n\\n\\nclass ProgressFileWrapper:\\n    def __init__(self, fileobj, progress_update_callback):\\n        self.progress_file = fileobj\\n        self.progress_update_callback = progress_update_callback\\n        self.progress_file_size = max(1, os.fstat(fileobj.fileno()).st_size)\\n        self.progress_max_pos = 0\\n\\n    def __getattr__(self, name):\\n        return getattr(self.progress_file, name)\\n\\n    def __setattr__(self, name, value):\\n        if name.startswith(\\\"progress_\\\"):\\n            super().__setattr__(name, value)\\n        else:\\n            setattr(self.progress_file, name, value)\\n\\n    def read(self, size=-1):\\n        data = self.progress_file.read(size)\\n        self.progress_update()\\n        return data\\n\\n    def progress_update(self):\\n        pos = max(self.progress_max_pos, self.progress_file.tell())\\n        pos = min(pos, self.progress_file_size)\\n        self.progress_max_pos = pos\\n        rel_pos = pos / self.progress_file_size\\n        self.progress_update_callback(rel_pos)\\n\\n\\ndef extract_tarball(\\n    tarball_full_path, destination_directory=None, progress_update_callback=None\\n):\\n    import conda_package_handling.api\\n\\n    if destination_directory is None:\\n        if tarball_full_path[-8:] == CONDA_PACKAGE_EXTENSION_V1:\\n            destination_directory = tarball_full_path[:-8]\\n        else:\\n            destination_directory = tarball_full_path.splitext()[0]\\n    log.debug(\\\"extracting %s\\\\n  to %s\\\", tarball_full_path, destination_directory)\\n\\n    # the most common reason this happens is due to hard-links, windows thinks\\n    #    files in the package cache are in-use. rm_rf should have moved them to\\n    #    have a .conda_trash extension though, so it's ok to just write into\\n    #    the same existing folder.\\n    if not path_is_clean(destination_directory):\\n        log.debug(\\n            \\\"package folder %s was not empty, but we're writing there.\\\",\\n            destination_directory,\\n        )\\n\\n    conda_package_handling.api.extract(\\n        tarball_full_path, dest_dir=destination_directory\\n    )\\n\\n    if hasattr(conda_package_handling.api, \\\"THREADSAFE_EXTRACT\\\"):\\n        return  # indicates conda-package-handling 2.x, which implements --no-same-owner\\n\\n    if on_linux and os.getuid() == 0:  # pragma: no cover\\n        # When extracting as root, tarfile will by restore ownership\\n        # of extracted files.  However, we want root to be the owner\\n        # (our implementation of --no-same-owner).\\n        for root, dirs, files in os.walk(destination_directory):\\n            for fn in files:\\n                p = join(root, fn)\\n                os.lchown(p, 0, 0)\\n\\n\\ndef make_menu(prefix, file_path, remove=False):\\n    \\\"\\\"\\\"\\n    Create cross-platform menu items (e.g. Windows Start Menu)\\n\\n    Passes all menu config files %PREFIX%/Menu/*.json to ``menuinst.install``.\\n    ``remove=True`` will remove the menu items.\\n    \\\"\\\"\\\"\\n    try:\\n        import menuinst\\n\\n        menuinst.install(\\n            join(prefix, win_path_ok(file_path)),\\n            remove=remove,\\n            prefix=prefix,\\n            root_prefix=context.root_prefix,\\n        )\\n    except Exception:\\n        stdoutlog.error(\\\"menuinst Exception\\\", exc_info=True)\\n\\n\\ndef create_hard_link_or_copy(src, dst):\\n    if islink(src):\\n        message = dals(\\n            f\\\"\\\"\\\"\\n        Cannot hard link a soft link\\n          source: {src}\\n          destination: {dst}\\n        \\\"\\\"\\\"\\n        )\\n        raise CondaOSError(message)\\n\\n    try:\\n        log.log(TRACE, \\\"creating hard link %s => %s\\\", src, dst)\\n        link(src, dst)\\n    except OSError:\\n        log.info(\\\"hard link failed, so copying %s => %s\\\", src, dst)\\n        _do_copy(src, dst)\\n\\n\\ndef _is_unix_executable_using_ORIGIN(path):\\n    if on_win:\\n        return False\\n    else:\\n        return isfile(path) and not islink(path) and os.access(path, os.X_OK)\\n\\n\\ndef _do_softlink(src, dst):\\n    if _is_unix_executable_using_ORIGIN(src):\\n        # for extra details, see https://github.com/conda/conda/pull/4625#issuecomment-280696371\\n        # We only need to do this copy for executables which have an RPATH containing $ORIGIN\\n        #   on Linux, so `is_executable()` is currently overly aggressive.\\n        # A future optimization will be to copy code from @mingwandroid's virtualenv patch.\\n        copy(src, dst)\\n    else:\\n        log.log(TRACE, \\\"soft linking %s => %s\\\", src, dst)\\n        symlink(src, dst)\\n\\n\\ndef create_fake_executable_softlink(src, dst):\\n    assert on_win\\n    src_root, _ = splitext(src)\\n    # TODO: this open will clobber, consider raising\\n    with open(dst, \\\"w\\\") as f:\\n        f.write(f'@echo off\\\\ncall \\\"{src_root}\\\" %*\\\\n')\\n    return dst\\n\\n\\ndef copy(src, dst):\\n    # on unix, make sure relative symlinks stay symlinks\\n    if not on_win and islink(src):\\n        src_points_to = readlink(src)\\n        if not src_points_to.startswith(\\\"/\\\"):\\n            # copy relative symlinks as symlinks\\n            log.log(TRACE, \\\"soft linking %s => %s\\\", src, dst)\\n            symlink(src_points_to, dst)\\n            return\\n    _do_copy(src, dst)\\n\\n\\ndef _do_copy(src, dst):\\n    log.log(TRACE, \\\"copying %s => %s\\\", src, dst)\\n    # src and dst are always files. So we can bypass some checks that shutil.copy does.\\n    # Also shutil.copy calls shutil.copymode, which we can skip because we are explicitly\\n    # calling copystat.\\n\\n    # Same size as used by Linux cp command (has performance advantage).\\n    # Python's default is 16k.\\n    buffer_size = 4194304  # 4 * 1024 * 1024  == 4 MB\\n    with open(src, \\\"rb\\\") as fsrc:\\n        with open(dst, \\\"wb\\\") as fdst:\\n            copyfileobj(fsrc, fdst, buffer_size)\\n\\n    try:\\n        copystat(src, dst)\\n    except OSError as e:  # pragma: no cover\\n        # shutil.copystat gives a permission denied when using the os.setxattr function\\n        # on the security.selinux property.\\n        log.debug(\\\"%r\\\", e)\\n\\n\\ndef create_link(src, dst, link_type=LinkType.hardlink, force=False):\\n    if link_type == LinkType.directory:\\n        # A directory is technically not a link.  So link_type is a misnomer.\\n        #   Naming is hard.\\n        if lexists(dst) and not isdir(dst):\\n            if not force:\\n                maybe_raise(BasicClobberError(src, dst, context), context)\\n            log.info(f\\\"file exists, but clobbering for directory: {dst!r}\\\")\\n            rm_rf(dst)\\n        mkdir_p(dst)\\n        return\\n\\n    if not lexists(src):\\n        raise CondaError(\\n            f\\\"Cannot link a source that does not exist. {src}\\\\n\\\"\\n            \\\"Running `conda clean --packages` may resolve your problem.\\\"\\n        )\\n\\n    if lexists(dst):\\n        if not force:\\n            maybe_raise(BasicClobberError(src, dst, context), context)\\n        log.info(f\\\"file exists, but clobbering: {dst!r}\\\")\\n        rm_rf(dst)\\n\\n    if link_type == LinkType.hardlink:\\n        if isdir(src):\\n            raise CondaError(f\\\"Cannot hard link a directory. {src}\\\")\\n        try:\\n            log.log(TRACE, \\\"hard linking %s => %s\\\", src, dst)\\n            link(src, dst)\\n        except OSError as e:\\n            log.debug(\\\"%r\\\", e)\\n            log.debug(\\n                \\\"hard-link failed. falling back to copy\\\\n\\\"\\n                \\\"  error: %r\\\\n\\\"\\n                \\\"  src: %s\\\\n\\\"\\n                \\\"  dst: %s\\\",\\n                e,\\n                src,\\n                dst,\\n            )\\n\\n            copy(src, dst)\\n    elif link_type == LinkType.softlink:\\n        _do_softlink(src, dst)\\n    elif link_type == LinkType.copy:\\n        copy(src, dst)\\n    else:\\n        raise CondaError(f\\\"Did not expect linktype={link_type!r}\\\")\\n\\n\\ndef compile_multiple_pyc(\\n    python_exe_full_path, py_full_paths, pyc_full_paths, prefix, py_ver\\n):\\n    py_full_paths = tuple(py_full_paths)\\n    pyc_full_paths = tuple(pyc_full_paths)\\n    if len(py_full_paths) == 0:\\n        return []\\n\\n    fd, filename = tempfile.mkstemp()\\n    try:\\n        for f in py_full_paths:\\n            f = os.path.relpath(f, prefix)\\n            if hasattr(f, \\\"encode\\\"):\\n                f = f.encode(sys.getfilesystemencoding(), errors=\\\"replace\\\")\\n            os.write(fd, f + b\\\"\\\\n\\\")\\n        os.close(fd)\\n        command = [\\\"-Wi\\\", \\\"-m\\\", \\\"compileall\\\", \\\"-q\\\", \\\"-l\\\", \\\"-i\\\", filename]\\n        # if the python version in the prefix is 3.5+, we have some extra args.\\n        #    -j 0 will do the compilation in parallel, with os.cpu_count() cores\\n        if int(py_ver[0]) >= 3 and int(py_ver.split(\\\".\\\")[1]) > 5:\\n            command.extend([\\\"-j\\\", \\\"0\\\"])\\n        command[0:0] = [python_exe_full_path]\\n        # command[0:0] = ['--cwd', prefix, '--dev', '-p', prefix, python_exe_full_path]\\n        log.log(TRACE, command)\\n        from ..subprocess import any_subprocess\\n\\n        # from ...common.io import env_vars\\n        # This stack does not maintain its _argparse_args correctly?\\n        # from ...base.context import stack_context_default\\n        # with env_vars({}, stack_context_default):\\n        #     stdout, stderr, rc = run_command(Commands.RUN, *command)\\n        stdout, stderr, rc = any_subprocess(command, prefix)\\n    finally:\\n        os.remove(filename)\\n\\n    created_pyc_paths = []\\n    for py_full_path, pyc_full_path in zip(py_full_paths, pyc_full_paths):\\n        if not isfile(pyc_full_path):\\n            message = dals(\\n                \\\"\\\"\\\"\\n            pyc file failed to compile successfully (run_command failed)\\n            python_exe_full_path: %s\\n            py_full_path: %s\\n            pyc_full_path: %s\\n            compile rc: %s\\n            compile stdout: %s\\n            compile stderr: %s\\n            \\\"\\\"\\\"\\n            )\\n            log.info(\\n                message,\\n                python_exe_full_path,\\n                py_full_path,\\n                pyc_full_path,\\n                rc,\\n                stdout,\\n                stderr,\\n            )\\n        else:\\n            created_pyc_paths.append(pyc_full_path)\\n\\n    return created_pyc_paths\\n\\n\\ndef create_package_cache_directory(pkgs_dir):\\n    # returns False if package cache directory cannot be created\\n    try:\\n        log.log(TRACE, \\\"creating package cache directory '%s'\\\", pkgs_dir)\\n        sudo_safe = expand(pkgs_dir).startswith(expand(\\\"~\\\"))\\n        touch(join(pkgs_dir, PACKAGE_CACHE_MAGIC_FILE), mkdir=True, sudo_safe=sudo_safe)\\n        touch(join(pkgs_dir, \\\"urls\\\"), sudo_safe=sudo_safe)\\n    except OSError as e:\\n        if e.errno in (EACCES, EPERM, EROFS):\\n            log.log(TRACE, \\\"cannot create package cache directory '%s'\\\", pkgs_dir)\\n            return False\\n        else:\\n            raise\\n    return True\\n\\n\\ndef create_envs_directory(envs_dir):\\n    # returns False if envs directory cannot be created\\n\\n    # The magic file being used here could change in the future.  Don't write programs\\n    # outside this code base that rely on the presence of this file.\\n    # This value is duplicated in conda.base.context._first_writable_envs_dir().\\n    envs_dir_magic_file = join(envs_dir, \\\".conda_envs_dir_test\\\")\\n    try:\\n        log.log(TRACE, \\\"creating envs directory '%s'\\\", envs_dir)\\n        sudo_safe = expand(envs_dir).startswith(expand(\\\"~\\\"))\\n        touch(join(envs_dir, envs_dir_magic_file), mkdir=True, sudo_safe=sudo_safe)\\n    except OSError as e:\\n        if e.errno in (EACCES, EPERM, EROFS):\\n            log.log(TRACE, \\\"cannot create envs directory '%s'\\\", envs_dir)\\n            return False\\n        else:\\n            raise\\n    return True\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Disk utility functions for deleting files and folders.\\\"\\\"\\\"\\n\\nimport fnmatch\\nimport shutil\\nimport sys\\nfrom errno import ENOENT\\nfrom logging import getLogger\\nfrom os import environ, getcwd, makedirs, rename, rmdir, scandir, unlink, walk\\nfrom os.path import (\\n    abspath,\\n    basename,\\n    dirname,\\n    exists,\\n    isdir,\\n    isfile,\\n    join,\\n    normpath,\\n    split,\\n)\\nfrom subprocess import STDOUT, CalledProcessError, check_output\\n\\nfrom ...base.constants import CONDA_TEMP_EXTENSION\\nfrom ...base.context import context\\nfrom ...common.compat import on_win\\nfrom ...common.constants import TRACE\\nfrom . import MAX_TRIES, exp_backoff_fn\\nfrom .link import islink, lexists\\nfrom .permissions import make_writable, recursive_make_writable\\n\\nif not on_win:\\n    from shutil import which\\n\\n\\nlog = getLogger(__name__)\\n\\n\\ndef rmtree(path, *args, **kwargs):\\n    # subprocessing to delete large folders can be quite a bit faster\\n    path = normpath(path)\\n    if on_win:\\n        try:\\n            # the fastest way seems to be using DEL to recursively delete files\\n            # https://www.ghacks.net/2017/07/18/how-to-delete-large-folders-in-windows-super-fast/\\n            # However, this is not entirely safe, as it can end up following symlinks to folders\\n            # https://superuser.com/a/306618/184799\\n            # so, we stick with the slower, but hopefully safer way.  Maybe if we figured out how\\n            #    to scan for any possible symlinks, we could do the faster way.\\n            # out = check_output('DEL /F/Q/S *.* > NUL 2> NUL'.format(path), shell=True,\\n            #                    stderr=STDOUT, cwd=path)\\n\\n            out = check_output(\\n                f'RD /S /Q \\\"{path}\\\" > NUL 2> NUL', shell=True, stderr=STDOUT\\n            )\\n        except:\\n            try:\\n                # Try to delete in Unicode\\n                name = None\\n                from ...auxlib.compat import Utf8NamedTemporaryFile\\n                from ...utils import quote_for_shell\\n\\n                with Utf8NamedTemporaryFile(\\n                    mode=\\\"w\\\", suffix=\\\".bat\\\", delete=False\\n                ) as batch_file:\\n                    batch_file.write(f\\\"RD /S {quote_for_shell(path)}\\\\n\\\")\\n                    batch_file.write(\\\"chcp 65001\\\\n\\\")\\n                    batch_file.write(f\\\"RD /S {quote_for_shell(path)}\\\\n\\\")\\n                    batch_file.write(\\\"EXIT 0\\\\n\\\")\\n                    name = batch_file.name\\n                # If the above is bugged we can end up deleting hard-drives, so we check\\n                # that 'path' appears in it. This is not bulletproof but it could save you (me).\\n                with open(name) as contents:\\n                    content = contents.read()\\n                    assert path in content\\n                comspec = environ[\\\"COMSPEC\\\"]\\n                CREATE_NO_WINDOW = 0x08000000\\n                # It is essential that we `pass stdout=None, stderr=None, stdin=None` here because\\n                # if we do not, then the standard console handles get attached and chcp affects the\\n                # parent process (and any which share those console handles!)\\n                out = check_output(\\n                    [comspec, \\\"/d\\\", \\\"/c\\\", name],\\n                    shell=False,\\n                    stdout=None,\\n                    stderr=None,\\n                    stdin=None,\\n                    creationflags=CREATE_NO_WINDOW,\\n                )\\n\\n            except CalledProcessError as e:\\n                if e.returncode != 5:\\n                    log.error(\\n                        f\\\"Removing folder {name} the fast way failed.  Output was: {out}\\\"\\n                    )\\n                    raise\\n                else:\\n                    log.debug(\\n                        f\\\"removing dir contents the fast way failed.  Output was: {out}\\\"\\n                    )\\n    else:\\n        try:\\n            makedirs(\\\".empty\\\")\\n        except:\\n            pass\\n        # yes, this looks strange.  See\\n        #    https://unix.stackexchange.com/a/79656/34459\\n        #    https://web.archive.org/web/20130929001850/http://linuxnote.net/jianingy/en/linux/a-fast-way-to-remove-huge-number-of-files.html  # NOQA\\n\\n        if isdir(\\\".empty\\\"):\\n            rsync = which(\\\"rsync\\\")\\n\\n            if rsync:\\n                try:\\n                    out = check_output(\\n                        [\\n                            rsync,\\n                            \\\"-a\\\",\\n                            \\\"--force\\\",\\n                            \\\"--delete\\\",\\n                            join(getcwd(), \\\".empty\\\") + \\\"/\\\",\\n                            path + \\\"/\\\",\\n                        ],\\n                        stderr=STDOUT,\\n                    )\\n                except CalledProcessError:\\n                    log.debug(\\n                        f\\\"removing dir contents the fast way failed.  Output was: {out}\\\"\\n                    )\\n\\n            shutil.rmtree(\\\".empty\\\")\\n    shutil.rmtree(path)\\n\\n\\ndef unlink_or_rename_to_trash(path):\\n    \\\"\\\"\\\"If files are in use, especially on windows, we can't remove them.\\n    The fallback path is to rename them (but keep their folder the same),\\n    which maintains the file handle validity.  See comments at:\\n    https://serverfault.com/a/503769\\n    \\\"\\\"\\\"\\n    try:\\n        make_writable(path)\\n        unlink(path)\\n    except OSError:\\n        try:\\n            rename(path, path + \\\".conda_trash\\\")\\n        except OSError:\\n            if on_win:\\n                # on windows, it is important to use the rename program, as just using python's\\n                #    rename leads to permission errors when files are in use.\\n                condabin_dir = join(context.conda_prefix, \\\"condabin\\\")\\n                trash_script = join(condabin_dir, \\\"rename_tmp.bat\\\")\\n                if exists(trash_script):\\n                    _dirname, _fn = split(path)\\n                    dest_fn = path + \\\".conda_trash\\\"\\n                    counter = 1\\n                    while isfile(dest_fn):\\n                        dest_fn = dest_fn.splitext[0] + f\\\".conda_trash_{counter}\\\"\\n                        counter += 1\\n                    out = \\\"< empty >\\\"\\n                    try:\\n                        out = check_output(\\n                            [\\n                                \\\"cmd.exe\\\",\\n                                \\\"/C\\\",\\n                                trash_script,\\n                                _dirname,\\n                                _fn,\\n                                basename(dest_fn),\\n                            ],\\n                            stderr=STDOUT,\\n                        )\\n                    except CalledProcessError:\\n                        log.debug(\\n                            f\\\"renaming file path {path} to trash failed.  Output was: {out}\\\"\\n                        )\\n\\n                else:\\n                    log.debug(\\n                        f\\\"{trash_script} is missing.  Conda was not installed correctly or has been \\\"\\n                        \\\"corrupted.  Please file an issue on the conda github repo.\\\"\\n                    )\\n            log.warning(\\n                f\\\"Could not remove or rename {path}.  Please remove this file manually (you \\\"\\n                \\\"may need to reboot to free file handles)\\\"\\n            )\\n\\n\\ndef remove_empty_parent_paths(path):\\n    # recurse to clean up empty folders that were created to have a nested hierarchy\\n    parent_path = dirname(path)\\n\\n    while isdir(parent_path) and not next(scandir(parent_path), None):\\n        rmdir(parent_path)\\n        parent_path = dirname(parent_path)\\n\\n\\ndef rm_rf(path, max_retries=5, trash=True, clean_empty_parents=False, *args, **kw):\\n    \\\"\\\"\\\"\\n    Completely delete path\\n    max_retries is the number of times to retry on failure. The default is 5. This only applies\\n    to deleting a directory.\\n    If removing path fails and trash is True, files will be moved to the trash directory.\\n    \\\"\\\"\\\"\\n    try:\\n        path = abspath(path)\\n        log.log(TRACE, \\\"rm_rf %s\\\", path)\\n        if isdir(path) and not islink(path):\\n            backoff_rmdir(path)\\n        elif lexists(path):\\n            unlink_or_rename_to_trash(path)\\n        else:\\n            log.log(TRACE, \\\"rm_rf failed. Not a link, file, or directory: %s\\\", path)\\n    finally:\\n        if lexists(path):\\n            log.info(\\\"rm_rf failed for %s\\\", path)\\n            return False\\n    if isdir(path):\\n        delete_trash(path)\\n    if clean_empty_parents:\\n        remove_empty_parent_paths(path)\\n    return True\\n\\n\\n# aliases that all do the same thing (legacy compat)\\ntry_rmdir_all_empty = move_to_trash = move_path_to_trash = rm_rf\\n\\n\\ndef delete_trash(prefix):\\n    if not prefix:\\n        prefix = sys.prefix\\n    exclude = {\\\"envs\\\", \\\"pkgs\\\"}\\n    for root, dirs, files in walk(prefix, topdown=True):\\n        dirs[:] = [d for d in dirs if d not in exclude]\\n        for fn in files:\\n            if fnmatch.fnmatch(fn, \\\"*.conda_trash*\\\") or fnmatch.fnmatch(\\n                fn, \\\"*\\\" + CONDA_TEMP_EXTENSION\\n            ):\\n                filename = join(root, fn)\\n                try:\\n                    unlink(filename)\\n                    remove_empty_parent_paths(filename)\\n                except OSError as e:\\n                    log.debug(\\\"%r errno %d\\\\nCannot unlink %s.\\\", e, e.errno, filename)\\n\\n\\ndef backoff_rmdir(dirpath, max_tries=MAX_TRIES):\\n    if not isdir(dirpath):\\n        return\\n\\n    def retry(func, path, exc_info):\\n        if getattr(exc_info[1], \\\"errno\\\", None) == ENOENT:\\n            return\\n        recursive_make_writable(dirname(path), max_tries=max_tries)\\n        func(path)\\n\\n    def _rmdir(path):\\n        try:\\n            recursive_make_writable(path)\\n            exp_backoff_fn(rmtree, path, onerror=retry, max_tries=max_tries)\\n        except OSError as e:\\n            if e.errno == ENOENT:\\n                log.log(TRACE, \\\"no such file or directory: %s\\\", path)\\n            else:\\n                raise\\n\\n    try:\\n        rmtree(dirpath)\\n    # we don't really care about errors that much.  We'll catch remaining files\\n    #    with slower python logic.\\n    except:\\n        pass\\n\\n    for root, dirs, files in walk(dirpath, topdown=False):\\n        for file in files:\\n            unlink_or_rename_to_trash(join(root, file))\\n\\n\\ndef path_is_clean(path):\\n    \\\"\\\"\\\"Sometimes we can't completely remove a path because files are considered in use\\n    by python (hardlinking confusion).  For our tests, it is sufficient that either the\\n    folder doesn't exist, or nothing but temporary file copies are left.\\n    \\\"\\\"\\\"\\n    clean = not exists(path)\\n    if not clean:\\n        for root, dirs, fns in walk(path):\\n            for fn in fns:\\n                if not (\\n                    fnmatch.fnmatch(fn, \\\"*.conda_trash*\\\")\\n                    or fnmatch.fnmatch(fn, \\\"*\\\" + CONDA_TEMP_EXTENSION)\\n                ):\\n                    return False\\n    return True\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Disk utility functions for reading and processing file contents.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport hashlib\\nimport json\\nimport os\\nfrom base64 import b64encode\\nfrom collections import namedtuple\\nfrom errno import ENOENT\\nfrom functools import partial\\nfrom itertools import chain\\nfrom logging import getLogger\\nfrom os.path import isdir, isfile, join  # noqa\\nfrom pathlib import Path\\nfrom typing import TYPE_CHECKING\\n\\nfrom ...auxlib.collection import first\\nfrom ...auxlib.compat import shlex_split_unicode\\nfrom ...auxlib.ish import dals\\nfrom ...base.constants import PREFIX_PLACEHOLDER\\nfrom ...common.compat import open\\nfrom ...common.pkg_formats.python import (\\n    PythonDistribution,\\n    PythonEggInfoDistribution,\\n    PythonEggLinkDistribution,\\n    PythonInstalledDistribution,\\n)\\nfrom ...exceptions import CondaUpgradeError, CondaVerificationError, PathNotFoundError\\nfrom ...models.channel import Channel\\nfrom ...models.enums import FileMode, PackageType, PathType\\nfrom ...models.package_info import PackageInfo, PackageMetadata\\nfrom ...models.records import PathData, PathDataV1, PathsData, PrefixRecord\\nfrom .create import TemporaryDirectory\\nfrom .link import islink, lexists  # noqa\\n\\nif TYPE_CHECKING:\\n    from typing import Literal\\n\\nlog = getLogger(__name__)\\n\\nlistdir = lambda d: list(entry.name for entry in os.scandir(d))  # noqa\\n\\n\\ndef yield_lines(path):\\n    \\\"\\\"\\\"Generator function for lines in file.  Empty generator if path does not exist.\\n\\n    Args:\\n        path (str): path to file\\n\\n    Returns:\\n        iterator: each line in file, not starting with '#'\\n\\n    \\\"\\\"\\\"\\n    try:\\n        with open(path) as fh:\\n            for line in fh:\\n                line = line.strip()\\n                if not line or line.startswith(\\\"#\\\"):\\n                    continue\\n                yield line\\n    except OSError as e:\\n        if e.errno == ENOENT:\\n            pass\\n        else:\\n            raise\\n\\n\\ndef compute_sum(path: str | os.PathLike, algo: Literal[\\\"md5\\\", \\\"sha256\\\"]) -> str:\\n    path = Path(path)\\n    if not path.is_file():\\n        raise PathNotFoundError(path)\\n\\n    # FUTURE: Python 3.11+, replace with hashlib.file_digest\\n    hasher = hashlib.new(algo)\\n    with path.open(\\\"rb\\\") as fh:\\n        for chunk in iter(partial(fh.read, 8192), b\\\"\\\"):\\n            hasher.update(chunk)\\n    return hasher.hexdigest()\\n\\n\\n# ####################################################\\n# functions supporting read_package_info()\\n# ####################################################\\n\\n\\ndef read_package_info(record, package_cache_record):\\n    epd = package_cache_record.extracted_package_dir\\n    icondata = read_icondata(epd)\\n    package_metadata = read_package_metadata(epd)\\n    paths_data = read_paths_json(epd)\\n\\n    return PackageInfo(\\n        extracted_package_dir=epd,\\n        package_tarball_full_path=package_cache_record.package_tarball_full_path,\\n        channel=Channel(record.schannel or record.channel),\\n        repodata_record=record,\\n        url=package_cache_record.url,\\n        icondata=icondata,\\n        package_metadata=package_metadata,\\n        paths_data=paths_data,\\n    )\\n\\n\\ndef read_index_json(extracted_package_directory):\\n    with open(join(extracted_package_directory, \\\"info\\\", \\\"index.json\\\")) as fi:\\n        return json.load(fi)\\n\\n\\ndef read_index_json_from_tarball(package_tarball_full_path):\\n    import conda_package_handling.api\\n\\n    with TemporaryDirectory() as tmpdir:\\n        conda_package_handling.api.extract(package_tarball_full_path, tmpdir, \\\"info\\\")\\n        with open(join(tmpdir, \\\"info\\\", \\\"index.json\\\")) as f:\\n            json_data = json.load(f)\\n    return json_data\\n\\n\\ndef read_repodata_json(extracted_package_directory):\\n    with open(join(extracted_package_directory, \\\"info\\\", \\\"repodata_record.json\\\")) as fi:\\n        return json.load(fi)\\n\\n\\ndef read_icondata(extracted_package_directory):\\n    icon_file_path = join(extracted_package_directory, \\\"info\\\", \\\"icon.png\\\")\\n    if isfile(icon_file_path):\\n        with open(icon_file_path, \\\"rb\\\") as f:\\n            data = f.read()\\n        return b64encode(data).decode(\\\"utf-8\\\")\\n    else:\\n        return None\\n\\n\\ndef read_package_metadata(extracted_package_directory):\\n    def _paths():\\n        yield join(extracted_package_directory, \\\"info\\\", \\\"link.json\\\")\\n        yield join(extracted_package_directory, \\\"info\\\", \\\"package_metadata.json\\\")\\n\\n    path = first(_paths(), key=isfile)\\n    if not path:\\n        return None\\n    else:\\n        with open(path) as f:\\n            data = json.loads(f.read())\\n            if data.get(\\\"package_metadata_version\\\") != 1:\\n                raise CondaUpgradeError(\\n                    dals(\\n                        \\\"\\\"\\\"\\n                The current version of conda is too old to install this package. (This version\\n                only supports link.json schema version 1.)  Please update conda to install\\n                this package.\\n                \\\"\\\"\\\"\\n                    )\\n                )\\n        package_metadata = PackageMetadata(**data)\\n        return package_metadata\\n\\n\\ndef read_paths_json(extracted_package_directory):\\n    info_dir = join(extracted_package_directory, \\\"info\\\")\\n    paths_json_path = join(info_dir, \\\"paths.json\\\")\\n    if isfile(paths_json_path):\\n        with open(paths_json_path) as paths_json:\\n            data = json.load(paths_json)\\n        if data.get(\\\"paths_version\\\") != 1:\\n            raise CondaUpgradeError(\\n                dals(\\n                    \\\"\\\"\\\"\\n            The current version of conda is too old to install this package. (This version\\n            only supports paths.json schema version 1.)  Please update conda to install\\n            this package.\\\"\\\"\\\"\\n                )\\n            )\\n        paths_data = PathsData(\\n            paths_version=1,\\n            paths=(PathDataV1(**f) for f in data[\\\"paths\\\"]),\\n        )\\n    else:\\n        has_prefix_files = read_has_prefix(join(info_dir, \\\"has_prefix\\\"))\\n        no_link = read_no_link(info_dir)\\n\\n        def read_files_file():\\n            files_path = join(info_dir, \\\"files\\\")\\n            for f in (\\n                ln for ln in (line.strip() for line in yield_lines(files_path)) if ln\\n            ):\\n                path_info = {\\\"_path\\\": f}\\n                if f in has_prefix_files.keys():\\n                    path_info[\\\"prefix_placeholder\\\"] = has_prefix_files[f][0]\\n                    path_info[\\\"file_mode\\\"] = has_prefix_files[f][1]\\n                if f in no_link:\\n                    path_info[\\\"no_link\\\"] = True\\n                if islink(join(extracted_package_directory, f)):\\n                    path_info[\\\"path_type\\\"] = PathType.softlink\\n                else:\\n                    path_info[\\\"path_type\\\"] = PathType.hardlink\\n                yield PathData(**path_info)\\n\\n        paths = tuple(read_files_file())\\n        paths_data = PathsData(\\n            paths_version=0,\\n            paths=paths,\\n        )\\n    return paths_data\\n\\n\\ndef read_has_prefix(path):\\n    \\\"\\\"\\\"Reads `has_prefix` file and return dict mapping filepaths to tuples(placeholder, FileMode).\\n\\n    A line in `has_prefix` contains one of:\\n      * filepath\\n      * placeholder mode filepath\\n\\n    Mode values are one of:\\n      * text\\n      * binary\\n    \\\"\\\"\\\"\\n    ParseResult = namedtuple(\\\"ParseResult\\\", (\\\"placeholder\\\", \\\"filemode\\\", \\\"filepath\\\"))\\n\\n    def parse_line(line):\\n        # placeholder, filemode, filepath\\n        parts = tuple(x.strip(\\\"\\\\\\\"'\\\") for x in shlex_split_unicode(line, posix=False))\\n        if len(parts) == 1:\\n            return ParseResult(PREFIX_PLACEHOLDER, FileMode.text, parts[0])\\n        elif len(parts) == 3:\\n            return ParseResult(parts[0], FileMode(parts[1]), parts[2])\\n        else:\\n            raise CondaVerificationError(f\\\"Invalid has_prefix file at path: {path}\\\")\\n\\n    parsed_lines = (parse_line(line) for line in yield_lines(path))\\n    return {pr.filepath: (pr.placeholder, pr.filemode) for pr in parsed_lines}\\n\\n\\ndef read_no_link(info_dir):\\n    return set(\\n        chain(\\n            yield_lines(join(info_dir, \\\"no_link\\\")),\\n            yield_lines(join(info_dir, \\\"no_softlink\\\")),\\n        )\\n    )\\n\\n\\ndef read_soft_links(extracted_package_directory, files):\\n    return tuple(f for f in files if islink(join(extracted_package_directory, f)))\\n\\n\\ndef read_python_record(prefix_path, anchor_file, python_version):\\n    \\\"\\\"\\\"\\n    Convert a python package defined by an anchor file (Metadata information)\\n    into a conda prefix record object.\\n    \\\"\\\"\\\"\\n    pydist = PythonDistribution.init(prefix_path, anchor_file, python_version)\\n    depends, constrains = pydist.get_conda_dependencies()\\n\\n    if isinstance(pydist, PythonInstalledDistribution):\\n        channel = Channel(\\\"pypi\\\")\\n        build = \\\"pypi_0\\\"\\n        package_type = PackageType.VIRTUAL_PYTHON_WHEEL\\n\\n        paths_tups = pydist.get_paths()\\n        paths_data = PathsData(\\n            paths_version=1,\\n            paths=(\\n                PathDataV1(\\n                    _path=path,\\n                    path_type=PathType.hardlink,\\n                    sha256=checksum,\\n                    size_in_bytes=size,\\n                )\\n                for (path, checksum, size) in paths_tups\\n            ),\\n        )\\n        files = tuple(p[0] for p in paths_tups)\\n\\n    elif isinstance(pydist, PythonEggLinkDistribution):\\n        channel = Channel(\\\"<develop>\\\")\\n        build = \\\"dev_0\\\"\\n        package_type = PackageType.VIRTUAL_PYTHON_EGG_LINK\\n\\n        paths_data, files = PathsData(paths_version=1, paths=()), ()\\n\\n    elif isinstance(pydist, PythonEggInfoDistribution):\\n        channel = Channel(\\\"pypi\\\")\\n        build = \\\"pypi_0\\\"\\n        if pydist.is_manageable:\\n            package_type = PackageType.VIRTUAL_PYTHON_EGG_MANAGEABLE\\n\\n            paths_tups = pydist.get_paths()\\n            files = tuple(p[0] for p in paths_tups)\\n            paths_data = PathsData(\\n                paths_version=1,\\n                paths=(\\n                    PathData(_path=path, path_type=PathType.hardlink) for path in files\\n                ),\\n            )\\n        else:\\n            package_type = PackageType.VIRTUAL_PYTHON_EGG_UNMANAGEABLE\\n            paths_data, files = PathsData(paths_version=1, paths=()), ()\\n\\n    else:\\n        raise NotImplementedError()\\n\\n    return PrefixRecord(\\n        package_type=package_type,\\n        name=pydist.conda_name,\\n        version=pydist.version,\\n        channel=channel,\\n        subdir=\\\"pypi\\\",\\n        fn=pydist.sp_reference,\\n        build=build,\\n        build_number=0,\\n        paths_data=paths_data,\\n        files=files,\\n        depends=depends,\\n        constrains=constrains,\\n    )\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\nimport os\\nimport sys\\nfrom errno import EACCES, EEXIST, ENOENT, ENOTEMPTY, EPERM, errorcode\\nfrom logging import getLogger\\nfrom os.path import basename, dirname, isdir\\nfrom subprocess import CalledProcessError\\nfrom time import sleep\\n\\nfrom ...common.compat import on_win\\nfrom ...common.constants import TRACE\\n\\nlog = getLogger(__name__)\\n\\nMAX_TRIES = 7\\n\\n\\ndef exp_backoff_fn(fn, *args, **kwargs):\\n    \\\"\\\"\\\"Mostly for retrying file operations that fail on Windows due to virus scanners\\\"\\\"\\\"\\n    max_tries = kwargs.pop(\\\"max_tries\\\", MAX_TRIES)\\n    if not on_win:\\n        return fn(*args, **kwargs)\\n\\n    import random\\n\\n    # with max_tries = 6, max total time ~= 3.2 sec\\n    # with max_tries = 7, max total time ~= 6.5 sec\\n\\n    def sleep_some(n, exc):\\n        if n == max_tries - 1:\\n            raise\\n        sleep_time = ((2**n) + random.random()) * 0.1\\n        caller_frame = sys._getframe(1)\\n        log.log(\\n            TRACE,\\n            \\\"retrying %s/%s %s() in %g sec\\\",\\n            basename(caller_frame.f_code.co_filename),\\n            caller_frame.f_lineno,\\n            fn.__name__,\\n            sleep_time,\\n        )\\n        sleep(sleep_time)\\n\\n    for n in range(max_tries):\\n        try:\\n            result = fn(*args, **kwargs)\\n        except OSError as e:\\n            log.log(TRACE, repr(e))\\n            if e.errno in (EPERM, EACCES):\\n                sleep_some(n, e)\\n            elif e.errno in (ENOENT, ENOTEMPTY):\\n                # errno.ENOENT File not found error / No such file or directory\\n                # errno.ENOTEMPTY OSError(41, 'The directory is not empty')\\n                raise\\n            else:\\n                log.warning(\\n                    \\\"Uncaught backoff with errno %s %d\\\", errorcode[e.errno], e.errno\\n                )\\n                raise\\n        except CalledProcessError as e:\\n            sleep_some(n, e)\\n        else:\\n            return result\\n\\n\\ndef mkdir_p(path):\\n    # putting this here to help with circular imports\\n    try:\\n        log.log(TRACE, \\\"making directory %s\\\", path)\\n        if path:\\n            os.makedirs(path)\\n            return isdir(path) and path\\n    except OSError as e:\\n        if e.errno == EEXIST and isdir(path):\\n            return path\\n        else:\\n            raise\\n\\n\\ndef mkdir_p_sudo_safe(path):\\n    if isdir(path):\\n        return\\n    base_dir = dirname(path)\\n    if not isdir(base_dir):\\n        mkdir_p_sudo_safe(base_dir)\\n    log.log(TRACE, \\\"making directory %s\\\", path)\\n    try:\\n        os.mkdir(path)\\n    except OSError as e:\\n        if not (e.errno == EEXIST and isdir(path)):\\n            raise\\n    # # per the following issues, removing this code as of 4.6.0:\\n    # #   - https://github.com/conda/conda/issues/6569\\n    # #   - https://github.com/conda/conda/issues/6576\\n    # #   - https://github.com/conda/conda/issues/7109\\n    # if not on_win and os.environ.get('SUDO_UID') is not None:\\n    #     uid = int(os.environ['SUDO_UID'])\\n    #     gid = int(os.environ.get('SUDO_GID', -1))\\n    #     log.log(TRACE, \\\"chowning %s:%s %s\\\", uid, gid, path)\\n    #     os.chown(path, uid, gid)\\n    if not on_win:\\n        # set newly-created directory permissions to 02775\\n        # https://github.com/conda/conda/issues/6610#issuecomment-354478489\\n        try:\\n            os.chmod(path, 0o2775)\\n        except OSError as e:\\n            log.log(\\n                TRACE,\\n                \\\"Failed to set permissions to 2775 on %s (%d %d)\\\",\\n                path,\\n                e.errno,\\n                errorcode[e.errno],\\n            )\\n            pass\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Disk utility functions for modifying existing files or directories.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport os\\nimport re\\nimport tempfile\\nfrom contextlib import contextmanager\\nfrom errno import EINVAL, EPERM, EXDEV\\nfrom logging import getLogger\\nfrom os.path import basename, dirname, exists, isdir, join, split\\nfrom shutil import move\\nfrom subprocess import PIPE, Popen\\n\\nfrom ...base.constants import DRY_RUN_PREFIX\\nfrom ...base.context import context\\nfrom ...common.compat import on_win\\nfrom ...common.constants import TRACE\\nfrom ...common.path import expand\\nfrom ...exceptions import NotWritableError\\nfrom . import exp_backoff_fn, mkdir_p, mkdir_p_sudo_safe\\nfrom .delete import rm_rf\\nfrom .link import lexists\\n\\nlog = getLogger(__name__)\\n\\nSHEBANG_REGEX = re.compile(rb\\\"^(#!((?:\\\\\\\\ |[^ \\\\n\\\\r])+)(.*))\\\")\\n\\n\\nclass CancelOperation(Exception):\\n    pass\\n\\n\\ndef update_file_in_place_as_binary(file_full_path, callback):\\n    # callback should be a callable that takes one positional argument, which is the\\n    #   content of the file before updating\\n    # this method updates the file in-place, without releasing the file lock\\n    fh = None\\n    try:\\n        fh = exp_backoff_fn(open, file_full_path, \\\"rb+\\\")\\n        log.log(TRACE, \\\"in-place update path locked for %s\\\", file_full_path)\\n        data = fh.read()\\n        fh.seek(0)\\n        try:\\n            fh.write(callback(data))\\n            fh.truncate()\\n            return True\\n        except CancelOperation:\\n            pass  # NOQA\\n    finally:\\n        if fh:\\n            fh.close()\\n    return False\\n\\n\\ndef rename(source_path, destination_path, force=False):\\n    if lexists(destination_path) and force:\\n        rm_rf(destination_path)\\n    if lexists(source_path):\\n        log.log(TRACE, \\\"renaming %s => %s\\\", source_path, destination_path)\\n        try:\\n            os.rename(source_path, destination_path)\\n        except OSError as e:\\n            if (\\n                on_win\\n                and dirname(source_path) == dirname(destination_path)\\n                and os.path.isfile(source_path)\\n            ):\\n                condabin_dir = join(context.conda_prefix, \\\"condabin\\\")\\n                rename_script = join(condabin_dir, \\\"rename_tmp.bat\\\")\\n                if exists(rename_script):\\n                    _dirname, _src_fn = split(source_path)\\n                    _dest_fn = basename(destination_path)\\n                    p = Popen(\\n                        [\\\"cmd.exe\\\", \\\"/C\\\", rename_script, _dirname, _src_fn, _dest_fn],\\n                        stdout=PIPE,\\n                        stderr=PIPE,\\n                    )\\n                    stdout, stderr = p.communicate()\\n                else:\\n                    log.debug(\\n                        f\\\"{rename_script} is missing.  Conda was not installed correctly or has been \\\"\\n                        \\\"corrupted.  Please file an issue on the conda github repo.\\\"\\n                    )\\n            elif e.errno in (EINVAL, EXDEV, EPERM):\\n                # https://github.com/conda/conda/issues/6811\\n                # https://github.com/conda/conda/issues/6711\\n                log.log(\\n                    TRACE,\\n                    \\\"Could not rename %s => %s due to errno [%s]. Falling back\\\"\\n                    \\\" to copy/unlink\\\",\\n                    source_path,\\n                    destination_path,\\n                    e.errno,\\n                )\\n                # https://github.com/moby/moby/issues/25409#issuecomment-238537855\\n                # shutil.move() falls back to copy+unlink\\n                move(source_path, destination_path)\\n            else:\\n                raise\\n    else:\\n        log.log(TRACE, \\\"cannot rename; source path does not exist '%s'\\\", source_path)\\n\\n\\n@contextmanager\\ndef rename_context(source: str, destination: str | None = None, dry_run: bool = False):\\n    \\\"\\\"\\\"\\n    Used for removing a directory when there are dependent actions (i.e. you need to ensure\\n    other actions succeed before removing it).\\n\\n    Example:\\n        with rename_context(directory):\\n            # Do dependent actions here\\n    \\\"\\\"\\\"\\n    if destination is None:\\n        destination = tempfile.mkdtemp()\\n\\n    if dry_run:\\n        print(f\\\"{DRY_RUN_PREFIX} rename_context {source} > {destination}\\\")\\n        yield\\n        return\\n\\n    try:\\n        rename(source, destination, force=True)\\n        yield\\n    except Exception as exc:\\n        # Error occurred, roll back change\\n        rename(destination, source, force=True)\\n        raise exc\\n\\n\\ndef backoff_rename(source_path, destination_path, force=False):\\n    exp_backoff_fn(rename, source_path, destination_path, force)\\n\\n\\ndef touch(path, mkdir=False, sudo_safe=False):\\n    # sudo_safe: use any time `path` is within the user's home directory\\n    # returns:\\n    #   True if the file did not exist but was created\\n    #   False if the file already existed\\n    # raises: NotWritableError, which is also an OSError having attached errno\\n    try:\\n        path = expand(path)\\n        log.log(TRACE, \\\"touching path %s\\\", path)\\n        if lexists(path):\\n            os.utime(path, None)\\n            return True\\n        else:\\n            dirpath = dirname(path)\\n            if not isdir(dirpath) and mkdir:\\n                if sudo_safe:\\n                    mkdir_p_sudo_safe(dirpath)\\n                else:\\n                    mkdir_p(dirpath)\\n            else:\\n                assert isdir(dirname(path))\\n            with open(path, \\\"a\\\"):\\n                pass\\n            # This chown call causes a false positive PermissionError to be\\n            # raised (similar to #7109) when called in an environment which\\n            # comes from sudo -u.\\n            #\\n            # if sudo_safe and not on_win and os.environ.get('SUDO_UID') is not None:\\n            #     uid = int(os.environ['SUDO_UID'])\\n            #     gid = int(os.environ.get('SUDO_GID', -1))\\n            #     log.log(TRACE, \\\"chowning %s:%s %s\\\", uid, gid, path)\\n            #     os.chown(path, uid, gid)\\n            return False\\n    except OSError as e:\\n        raise NotWritableError(path, e.errno, caused_by=e)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"\\nBackwards compatibility import.\\n\\nMoved to prevent circular imports.\\n\\\"\\\"\\\"\\n\\nfrom ..disk.lock import lock  # noqa: F401\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Repodata interface.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport abc\\nimport datetime\\nimport errno\\nimport hashlib\\nimport json\\nimport logging\\nimport os\\nimport pathlib\\nimport re\\nimport time\\nimport warnings\\nfrom collections import UserDict\\nfrom contextlib import contextmanager\\nfrom os.path import dirname\\nfrom typing import TYPE_CHECKING\\n\\nfrom ... import CondaError\\nfrom ...auxlib.logz import stringify\\nfrom ...base.constants import CONDA_HOMEPAGE_URL, REPODATA_FN\\nfrom ...base.context import context\\nfrom ...common.url import join_url, maybe_unquote\\nfrom ...core.package_cache_data import PackageCacheData\\nfrom ...exceptions import (\\n    CondaDependencyError,\\n    CondaHTTPError,\\n    CondaSSLError,\\n    NotWritableError,\\n    ProxyError,\\n    UnavailableInvalidChannel,\\n)\\nfrom ...models.channel import Channel\\nfrom ..connection import (\\n    ChunkedEncodingError,\\n    ConnectionError,\\n    HTTPError,\\n    InsecureRequestWarning,\\n    InvalidSchema,\\n    RequestsProxyError,\\n    SSLError,\\n)\\nfrom ..connection.session import get_session\\nfrom ..disk import mkdir_p_sudo_safe\\nfrom ..disk.lock import lock\\n\\nif TYPE_CHECKING:\\n    from pathlib import Path\\n    from typing import Any\\n\\n    from ..connection import Response\\n\\nlog = logging.getLogger(__name__)\\nstderrlog = logging.getLogger(\\\"conda.stderrlog\\\")\\n\\n\\n# if repodata.json.zst or repodata.jlap were unavailable, check again later.\\nCHECK_ALTERNATE_FORMAT_INTERVAL = datetime.timedelta(days=7)\\n\\n# repodata.info/state.json keys to keep up with the CEP\\nLAST_MODIFIED_KEY = \\\"mod\\\"\\nETAG_KEY = \\\"etag\\\"\\nCACHE_CONTROL_KEY = \\\"cache_control\\\"\\nURL_KEY = \\\"url\\\"\\nCACHE_STATE_SUFFIX = \\\".info.json\\\"\\n\\n# show some unparseable json in error\\nERROR_SNIPPET_LENGTH = 32\\n\\n\\nclass RepodataIsEmpty(UnavailableInvalidChannel):\\n    \\\"\\\"\\\"\\n    Subclass used to determine when empty repodata should be cached, e.g. for a\\n    channel that doesn't provide current_repodata.json\\n    \\\"\\\"\\\"\\n\\n\\nclass RepodataOnDisk(Exception):\\n    \\\"\\\"\\\"\\n    Indicate that RepoInterface.repodata() successfully wrote repodata to disk,\\n    instead of returning a string.\\n    \\\"\\\"\\\"\\n\\n\\nclass RepoInterface(abc.ABC):\\n    # TODO: Support async operations\\n    # TODO: Support progress bars\\n    def repodata(self, state: dict) -> str:\\n        \\\"\\\"\\\"\\n        Given a mutable state dictionary with information about the cache,\\n        return repodata.json (or current_repodata.json) as a str. This function\\n        also updates state, which is expected to be saved by the caller.\\n        \\\"\\\"\\\"\\n        ...\\n\\n\\nclass Response304ContentUnchanged(Exception):\\n    pass\\n\\n\\ndef get_repo_interface() -> type[RepoInterface]:\\n    if \\\"jlap\\\" in context.experimental:\\n        try:\\n            from .jlap.interface import JlapRepoInterface\\n\\n            return JlapRepoInterface\\n        except ImportError as e:  # pragma: no cover\\n            warnings.warn(\\n                \\\"Could not load the configured jlap repo interface. \\\"\\n                f\\\"Is the required jsonpatch package installed?  {e}\\\"\\n            )\\n\\n    if context.repodata_use_zst:\\n        try:\\n            from .jlap.interface import ZstdRepoInterface\\n\\n            return ZstdRepoInterface\\n        except ImportError:  # pragma: no cover\\n            pass\\n\\n    return CondaRepoInterface\\n\\n\\nclass CondaRepoInterface(RepoInterface):\\n    \\\"\\\"\\\"Provides an interface for retrieving repodata data from channels.\\\"\\\"\\\"\\n\\n    #: Channel URL\\n    _url: str\\n\\n    #: Filename of the repodata file; defaults to value of conda.base.constants.REPODATA_FN\\n    _repodata_fn: str\\n\\n    def __init__(self, url: str, repodata_fn: str | None, **kwargs) -> None:\\n        log.debug(\\\"Using CondaRepoInterface\\\")\\n        self._url = url\\n        self._repodata_fn = repodata_fn or REPODATA_FN\\n\\n    def repodata(self, state: RepodataState) -> str | None:\\n        if not context.ssl_verify:\\n            warnings.simplefilter(\\\"ignore\\\", InsecureRequestWarning)\\n\\n        session = get_session(self._url)\\n\\n        headers = {}\\n        etag = state.etag\\n        last_modified = state.mod\\n        if etag:\\n            headers[\\\"If-None-Match\\\"] = str(etag)\\n        if last_modified:\\n            headers[\\\"If-Modified-Since\\\"] = str(last_modified)\\n        filename = self._repodata_fn\\n\\n        url = join_url(self._url, filename)\\n\\n        with conda_http_errors(self._url, filename):\\n            timeout = (\\n                context.remote_connect_timeout_secs,\\n                context.remote_read_timeout_secs,\\n            )\\n            response: Response = session.get(\\n                url, headers=headers, proxies=session.proxies, timeout=timeout\\n            )\\n            if log.isEnabledFor(logging.DEBUG):\\n                log.debug(stringify(response, content_max_len=256))\\n            response.raise_for_status()\\n\\n        if response.status_code == 304:\\n            # should we save cache-control to state here to put another n\\n            # seconds on the \\\"make a remote request\\\" clock and/or touch cache\\n            # mtime\\n            raise Response304ContentUnchanged()\\n\\n        json_str = response.text\\n\\n        # We no longer add these tags to the large `resp.content` json\\n        saved_fields = {\\\"_url\\\": self._url}\\n        _add_http_value_to_dict(response, \\\"Etag\\\", saved_fields, \\\"_etag\\\")\\n        _add_http_value_to_dict(response, \\\"Last-Modified\\\", saved_fields, \\\"_mod\\\")\\n        _add_http_value_to_dict(\\n            response, \\\"Cache-Control\\\", saved_fields, \\\"_cache_control\\\"\\n        )\\n\\n        state.clear()\\n        state.update(saved_fields)\\n\\n        return json_str\\n\\n\\ndef _add_http_value_to_dict(resp, http_key, d, dict_key):\\n    value = resp.headers.get(http_key)\\n    if value:\\n        d[dict_key] = value\\n\\n\\n@contextmanager\\ndef conda_http_errors(url, repodata_fn):\\n    \\\"\\\"\\\"Use in a with: statement to translate requests exceptions to conda ones.\\\"\\\"\\\"\\n    try:\\n        yield\\n    except RequestsProxyError:\\n        raise ProxyError()  # see #3962\\n\\n    except InvalidSchema as e:\\n        if \\\"SOCKS\\\" in str(e):\\n            message = \\\"\\\"\\\"\\\\\\nRequests has identified that your current working environment is configured\\nto use a SOCKS proxy, but pysocks is not installed.  To proceed, remove your\\nproxy configuration, run `conda install pysocks`, and then you can re-enable\\nyour proxy configuration.\\n\\\"\\\"\\\"\\n            raise CondaDependencyError(message)\\n        else:\\n            raise\\n\\n    except SSLError as e:\\n        # SSLError: either an invalid certificate or OpenSSL is unavailable\\n        try:\\n            import ssl  # noqa: F401\\n        except ImportError:\\n            raise CondaSSLError(\\n                f\\\"\\\"\\\"\\\\\\nOpenSSL appears to be unavailable on this machine. OpenSSL is required to\\ndownload and install packages.\\n\\nException: {e}\\n\\\"\\\"\\\"\\n            )\\n        else:\\n            raise CondaSSLError(\\n                f\\\"\\\"\\\"\\\\\\nEncountered an SSL error. Most likely a certificate verification issue.\\n\\nException: {e}\\n\\\"\\\"\\\"\\n            )\\n\\n    except (ConnectionError, HTTPError, ChunkedEncodingError) as e:\\n        status_code = getattr(e.response, \\\"status_code\\\", None)\\n        if status_code in (403, 404):\\n            if not url.endswith(\\\"/noarch\\\"):\\n                log.info(\\n                    \\\"Unable to retrieve repodata (response: %d) for %s\\\",\\n                    status_code,\\n                    url + \\\"/\\\" + repodata_fn,\\n                )\\n                raise RepodataIsEmpty(\\n                    Channel(dirname(url)),\\n                    status_code,\\n                    response=e.response,\\n                )\\n            else:\\n                if context.allow_non_channel_urls:\\n                    stderrlog.warning(\\n                        \\\"Unable to retrieve repodata (response: %d) for %s\\\",\\n                        status_code,\\n                        url + \\\"/\\\" + repodata_fn,\\n                    )\\n                    raise RepodataIsEmpty(\\n                        Channel(dirname(url)),\\n                        status_code,\\n                        response=e.response,\\n                    )\\n                else:\\n                    raise UnavailableInvalidChannel(\\n                        Channel(dirname(url)),\\n                        status_code,\\n                        response=e.response,\\n                    )\\n\\n        elif status_code == 401:\\n            channel = Channel(url)\\n            if channel.token:\\n                help_message = \\\"\\\"\\\"\\\\\\nThe token '{}' given for the URL is invalid.\\n\\nIf this token was pulled from anaconda-client, you will need to use\\nanaconda-client to reauthenticate.\\n\\nIf you supplied this token to conda directly, you will need to adjust your\\nconda configuration to proceed.\\n\\nUse `conda config --show` to view your configuration's current state.\\nFurther configuration help can be found at <{}>.\\n\\\"\\\"\\\".format(\\n                    channel.token,\\n                    join_url(CONDA_HOMEPAGE_URL, \\\"docs/config.html\\\"),\\n                )\\n\\n            elif context.channel_alias.location in url:\\n                # Note, this will not trigger if the binstar configured url does\\n                # not match the conda configured one.\\n                help_message = \\\"\\\"\\\"\\\\\\nThe remote server has indicated you are using invalid credentials for this channel.\\n\\nIf the remote site is anaconda.org or follows the Anaconda Server API, you\\nwill need to\\n    (a) remove the invalid token from your system with `anaconda logout`, optionally\\n        followed by collecting a new token with `anaconda login`, or\\n    (b) provide conda with a valid token directly.\\n\\nFurther configuration help can be found at <{}>.\\n\\\"\\\"\\\".format(join_url(CONDA_HOMEPAGE_URL, \\\"docs/config.html\\\"))\\n\\n            else:\\n                help_message = \\\"\\\"\\\"\\\\\\nThe credentials you have provided for this URL are invalid.\\n\\nYou will need to modify your conda configuration to proceed.\\nUse `conda config --show` to view your configuration's current state.\\nFurther configuration help can be found at <{}>.\\n\\\"\\\"\\\".format(join_url(CONDA_HOMEPAGE_URL, \\\"docs/config.html\\\"))\\n\\n        elif status_code is not None and 500 <= status_code < 600:\\n            help_message = \\\"\\\"\\\"\\\\\\nA remote server error occurred when trying to retrieve this URL.\\n\\nA 500-type error (e.g. 500, 501, 502, 503, etc.) indicates the server failed to\\nfulfill a valid request.  The problem may be spurious, and will resolve itself if you\\ntry your request again.  If the problem persists, consider notifying the maintainer\\nof the remote server.\\n\\\"\\\"\\\"\\n\\n        else:\\n            if url.startswith(\\\"https://repo.anaconda.com/\\\"):\\n                help_message = f\\\"\\\"\\\"\\\\\\nAn HTTP error occurred when trying to retrieve this URL.\\nHTTP errors are often intermittent, and a simple retry will get you on your way.\\n\\nIf your current network has https://repo.anaconda.com blocked, please file\\na support request with your network engineering team.\\n\\n{maybe_unquote(repr(url))}\\n\\\"\\\"\\\"\\n\\n            else:\\n                help_message = f\\\"\\\"\\\"\\\\\\nAn HTTP error occurred when trying to retrieve this URL.\\nHTTP errors are often intermittent, and a simple retry will get you on your way.\\n{maybe_unquote(repr(url))}\\n\\\"\\\"\\\"\\n\\n        raise CondaHTTPError(\\n            help_message,\\n            join_url(url, repodata_fn),\\n            status_code,\\n            getattr(e.response, \\\"reason\\\", None),\\n            getattr(e.response, \\\"elapsed\\\", None),\\n            e.response,\\n            caused_by=e,\\n        )\\n\\n\\nclass RepodataState(UserDict):\\n    \\\"\\\"\\\"Load/save info file that accompanies cached `repodata.json`.\\\"\\\"\\\"\\n\\n    # Accept old keys for new serialization\\n    _aliased = {\\n        \\\"_mod\\\": LAST_MODIFIED_KEY,\\n        \\\"_etag\\\": ETAG_KEY,\\n        \\\"_cache_control\\\": CACHE_CONTROL_KEY,\\n        \\\"_url\\\": URL_KEY,\\n    }\\n\\n    # Enforce string type on these keys\\n    _strings = {\\\"mod\\\", \\\"etag\\\", \\\"cache_control\\\", \\\"url\\\"}\\n\\n    def __init__(\\n        self,\\n        cache_path_json: Path | str = \\\"\\\",\\n        cache_path_state: Path | str = \\\"\\\",\\n        repodata_fn=\\\"\\\",\\n        dict=None,\\n    ):\\n        # dict is a positional-only argument in UserDict.\\n        super().__init__(dict)\\n        self.cache_path_json = pathlib.Path(cache_path_json)\\n        self.cache_path_state = pathlib.Path(cache_path_state)\\n        # XXX may not be that useful/used compared to the full URL\\n        self.repodata_fn = repodata_fn\\n\\n    @property\\n    def mod(self) -> str:\\n        \\\"\\\"\\\"\\n        Last-Modified header or \\\"\\\"\\n        \\\"\\\"\\\"\\n        return self.get(LAST_MODIFIED_KEY) or \\\"\\\"\\n\\n    @mod.setter\\n    def mod(self, value):\\n        self[LAST_MODIFIED_KEY] = value or \\\"\\\"\\n\\n    @property\\n    def etag(self) -> str:\\n        \\\"\\\"\\\"\\n        Etag header or \\\"\\\"\\n        \\\"\\\"\\\"\\n        return self.get(ETAG_KEY) or \\\"\\\"\\n\\n    @etag.setter\\n    def etag(self, value):\\n        self[ETAG_KEY] = value or \\\"\\\"\\n\\n    @property\\n    def cache_control(self) -> str:\\n        \\\"\\\"\\\"\\n        Cache-Control header or \\\"\\\"\\n        \\\"\\\"\\\"\\n        return self.get(CACHE_CONTROL_KEY) or \\\"\\\"\\n\\n    @cache_control.setter\\n    def cache_control(self, value):\\n        self[CACHE_CONTROL_KEY] = value or \\\"\\\"\\n\\n    def has_format(self, format: str) -> tuple[bool, datetime.datetime | None]:\\n        # \\\"has_zst\\\": {\\n        #     // UTC RFC3999 timestamp of when we last checked whether the file is available or not\\n        #     // in this case the `repodata.json.zst` file\\n        #     // Note: same format as conda TUF spec\\n        #     \\\"last_checked\\\": \\\"2023-01-08T11:45:44Z\\\",\\n        #     // false = unavailable, true = available\\n        #     \\\"value\\\": BOOLEAN\\n        # },\\n\\n        key = f\\\"has_{format}\\\"\\n        if key not in self:\\n            return (True, None)  # we want to check by default\\n\\n        try:\\n            obj = self[key]\\n            last_checked_str = obj[\\\"last_checked\\\"]\\n            if last_checked_str.endswith(\\\"Z\\\"):\\n                last_checked_str = f\\\"{last_checked_str[:-1]}+00:00\\\"\\n            last_checked = datetime.datetime.fromisoformat(last_checked_str)\\n            value = bool(obj[\\\"value\\\"])\\n            return (value, last_checked)\\n        except (KeyError, ValueError, TypeError) as e:\\n            log.warning(\\n                f\\\"error parsing `has_` object from `<cache key>{CACHE_STATE_SUFFIX}`\\\",\\n                exc_info=e,\\n            )\\n            self.pop(key)\\n\\n        return False, datetime.datetime.now(tz=datetime.timezone.utc)\\n\\n    def set_has_format(self, format: str, value: bool):\\n        key = f\\\"has_{format}\\\"\\n        self[key] = {\\n            \\\"last_checked\\\": datetime.datetime.now(tz=datetime.timezone.utc).isoformat()[\\n                : -len(\\\"+00:00\\\")\\n            ]\\n            + \\\"Z\\\",\\n            \\\"value\\\": value,\\n        }\\n\\n    def clear_has_format(self, format: str):\\n        \\\"\\\"\\\"Remove 'has_{format}' instead of setting to False.\\\"\\\"\\\"\\n        key = f\\\"has_{format}\\\"\\n        self.pop(key, None)\\n\\n    def should_check_format(self, format: str) -> bool:\\n        \\\"\\\"\\\"Return True if named format should be attempted.\\\"\\\"\\\"\\n        has, when = self.has_format(format)\\n        return (\\n            has is True\\n            or isinstance(when, datetime.datetime)\\n            and datetime.datetime.now(tz=datetime.timezone.utc) - when\\n            > CHECK_ALTERNATE_FORMAT_INTERVAL\\n        )\\n\\n    def __contains__(self, key: str) -> bool:\\n        key = self._aliased.get(key, key)\\n        return super().__contains__(key)\\n\\n    def __setitem__(self, key: str, item: Any) -> None:\\n        key = self._aliased.get(key, key)\\n        if key in self._strings and not isinstance(item, str):\\n            log.debug('Replaced non-str RepodataState[%s] with \\\"\\\"', key)\\n            item = \\\"\\\"\\n        return super().__setitem__(key, item)\\n\\n    def __getitem__(self, key: str) -> Any:\\n        key = self._aliased.get(key, key)\\n        return super().__getitem__(key)\\n\\n\\nclass RepodataCache:\\n    \\\"\\\"\\\"\\n    Handle caching for a single repodata.json + repodata.info.json\\n    (<hex-string>*.json inside `dir`)\\n\\n    Avoid race conditions while loading, saving repodata.json and cache state.\\n    \\\"\\\"\\\"\\n\\n    def __init__(self, base, repodata_fn):\\n        \\\"\\\"\\\"\\n        base: directory and filename prefix for cache, e.g. /cache/dir/abc123;\\n        writes /cache/dir/abc123.json\\n        \\\"\\\"\\\"\\n        cache_path_base = pathlib.Path(base)\\n        self.cache_dir = cache_path_base.parent\\n        self.name = cache_path_base.name\\n        # XXX can we skip repodata_fn or include the full url for debugging\\n        self.repodata_fn = repodata_fn\\n        self.state = RepodataState(\\n            self.cache_path_json, self.cache_path_state, repodata_fn\\n        )\\n\\n    @property\\n    def cache_path_json(self):\\n        return pathlib.Path(\\n            self.cache_dir,\\n            self.name + (\\\"1\\\" if context.use_only_tar_bz2 else \\\"\\\") + \\\".json\\\",\\n        )\\n\\n    @property\\n    def cache_path_state(self):\\n        \\\"\\\"\\\"Out-of-band etag and other state needed by the RepoInterface.\\\"\\\"\\\"\\n        return self.cache_path_json.with_suffix(CACHE_STATE_SUFFIX)\\n\\n    def load(self, *, state_only=False) -> str:\\n        # read state and repodata.json with locking\\n\\n        # lock {CACHE_STATE_SUFFIX} file\\n        # read {CACHE_STATES_SUFFIX} file\\n        # read repodata.json\\n        # check stat, if wrong clear cache information\\n\\n        with self.lock(\\\"r+\\\") as state_file:\\n            # cannot use pathlib.read_text / write_text on any locked file, as\\n            # it will release the lock early\\n            state = json.loads(state_file.read())\\n\\n            # json and state files should match. must read json before checking\\n            # stat (if json_data is to be trusted)\\n            if state_only:\\n                json_data = \\\"\\\"\\n            else:\\n                json_data = self.cache_path_json.read_text()\\n\\n            json_stat = self.cache_path_json.stat()\\n            if not (\\n                state.get(\\\"mtime_ns\\\") == json_stat.st_mtime_ns\\n                and state.get(\\\"size\\\") == json_stat.st_size\\n            ):\\n                # clear mod, etag, cache_control to encourage re-download\\n                state.update(\\n                    {\\n                        ETAG_KEY: \\\"\\\",\\n                        LAST_MODIFIED_KEY: \\\"\\\",\\n                        CACHE_CONTROL_KEY: \\\"\\\",\\n                        \\\"size\\\": 0,\\n                    }\\n                )\\n            self.state.clear()\\n            self.state.update(\\n                state\\n            )  # will aliased _mod, _etag (not cleared above) pass through as mod, etag?\\n\\n        return json_data\\n\\n        # check repodata.json stat(); mtime_ns must equal value in\\n        # {CACHE_STATE_SUFFIX} file, or it is stale.\\n        # read repodata.json\\n        # check repodata.json stat() again: st_size, st_mtime_ns must be equal\\n\\n        # repodata.json is okay - use it somewhere\\n\\n        # repodata.json is not okay - maybe use it, but don't allow cache updates\\n\\n        # unlock {CACHE_STATE_SUFFIX} file\\n\\n        # also, add refresh_ns instead of touching repodata.json file\\n\\n    def load_state(self):\\n        \\\"\\\"\\\"\\n        Update self.state without reading repodata.json.\\n\\n        Return self.state.\\n        \\\"\\\"\\\"\\n        try:\\n            self.load(state_only=True)\\n        except (FileNotFoundError, json.JSONDecodeError) as e:\\n            if isinstance(e, json.JSONDecodeError):\\n                log.warning(f\\\"{e.__class__.__name__} loading {self.cache_path_state}\\\")\\n            self.state.clear()\\n        return self.state\\n\\n    def save(self, data: str):\\n        \\\"\\\"\\\"Write data to <repodata>.json cache path, synchronize state.\\\"\\\"\\\"\\n        temp_path = self.cache_dir / f\\\"{self.name}.{os.urandom(2).hex()}.tmp\\\"\\n\\n        try:\\n            with temp_path.open(\\\"x\\\") as temp:  # exclusive mode, error if exists\\n                temp.write(data)\\n\\n            return self.replace(temp_path)\\n\\n        finally:\\n            try:\\n                temp_path.unlink()\\n            except OSError:\\n                pass\\n\\n    def replace(self, temp_path: Path):\\n        \\\"\\\"\\\"\\n        Rename path onto <repodata>.json path, synchronize state.\\n\\n        Relies on path's mtime not changing on move. `temp_path` should be\\n        adjacent to `self.cache_path_json` to be on the same filesystem.\\n        \\\"\\\"\\\"\\n        with self.lock() as state_file:\\n            # \\\"a+\\\" creates the file if necessary, does not trunctate file.\\n            state_file.seek(0)\\n            state_file.truncate()\\n            stat = temp_path.stat()\\n            # XXX make sure self.state has the correct etag, etc. for temp_path.\\n            # UserDict has inscrutable typing, which we ignore\\n            self.state[\\\"mtime_ns\\\"] = stat.st_mtime_ns  # type: ignore\\n            self.state[\\\"size\\\"] = stat.st_size  # type: ignore\\n            self.state[\\\"refresh_ns\\\"] = time.time_ns()  # type: ignore\\n            try:\\n                temp_path.rename(self.cache_path_json)\\n            except FileExistsError:  # Windows\\n                self.cache_path_json.unlink()\\n                temp_path.rename(self.cache_path_json)\\n            state_file.write(json.dumps(dict(self.state), indent=2))\\n\\n    def refresh(self, refresh_ns=0):\\n        \\\"\\\"\\\"\\n        Update access time in cache info file to indicate a HTTP 304 Not Modified response.\\n        \\\"\\\"\\\"\\n        # Note this is not thread-safe.\\n        with self.lock() as state_file:\\n            # \\\"a+\\\" creates the file if necessary, does not trunctate file.\\n            state_file.seek(0)\\n            state_file.truncate()\\n            self.state[\\\"refresh_ns\\\"] = refresh_ns or time.time_ns()\\n            state_file.write(json.dumps(dict(self.state), indent=2))\\n\\n    @contextmanager\\n    def lock(self, mode=\\\"a+\\\"):\\n        \\\"\\\"\\\"\\n        Lock .info.json file. Hold lock while modifying related files.\\n\\n        mode: \\\"a+\\\" then seek(0) to write/create; \\\"r+\\\" to read.\\n        \\\"\\\"\\\"\\n        with self.cache_path_state.open(mode) as state_file, lock(state_file):\\n            yield state_file\\n\\n    def stale(self):\\n        \\\"\\\"\\\"\\n        Compare state refresh_ns against cache control header and\\n        context.local_repodata_ttl.\\n        \\\"\\\"\\\"\\n        if context.local_repodata_ttl > 1:\\n            max_age = context.local_repodata_ttl\\n        elif context.local_repodata_ttl == 1:\\n            max_age = get_cache_control_max_age(self.state.cache_control)\\n        else:\\n            max_age = 0\\n\\n        max_age *= 10**9  # nanoseconds\\n        now = time.time_ns()\\n        refresh = self.state.get(\\\"refresh_ns\\\", 0)\\n        return (now - refresh) > max_age\\n\\n    def timeout(self):\\n        \\\"\\\"\\\"\\n        Return number of seconds until cache times out (<= 0 if already timed\\n        out).\\n        \\\"\\\"\\\"\\n        if context.local_repodata_ttl > 1:\\n            max_age = context.local_repodata_ttl\\n        elif context.local_repodata_ttl == 1:\\n            max_age = get_cache_control_max_age(self.state.cache_control)\\n        else:\\n            max_age = 0\\n\\n        max_age *= 10**9  # nanoseconds\\n        now = time.time_ns()\\n        refresh = self.state.get(\\\"refresh_ns\\\", 0)\\n        return ((now - refresh) + max_age) / 1e9\\n\\n\\nclass RepodataFetch:\\n    \\\"\\\"\\\"\\n    Combine RepodataCache and RepoInterface to provide subdir_data.SubdirData()\\n    with what it needs.\\n\\n    Provide a variety of formats since some ``RepoInterface`` have to\\n    ``json.loads(...)`` anyway, and some clients don't need the Python data\\n    structure at all.\\n    \\\"\\\"\\\"\\n\\n    cache_path_base: Path\\n    channel: Channel\\n    repodata_fn: str\\n    url_w_subdir: str\\n    url_w_credentials: str\\n    repo_interface_cls: Any\\n\\n    def __init__(\\n        self,\\n        cache_path_base: Path,\\n        channel: Channel,\\n        repodata_fn: str,\\n        *,\\n        repo_interface_cls,\\n    ):\\n        self.cache_path_base = cache_path_base\\n        self.channel = channel\\n        self.repodata_fn = repodata_fn\\n\\n        self.url_w_subdir = self.channel.url(with_credentials=False) or \\\"\\\"\\n        self.url_w_credentials = self.channel.url(with_credentials=True) or \\\"\\\"\\n\\n        self.repo_interface_cls = repo_interface_cls\\n\\n    def fetch_latest_parsed(self) -> tuple[dict, RepodataState]:\\n        \\\"\\\"\\\"\\n        Retrieve parsed latest or latest-cached repodata as a dict; update\\n        cache.\\n\\n        :return: (repodata contents, state including cache headers)\\n        \\\"\\\"\\\"\\n        parsed, state = self.fetch_latest()\\n        if isinstance(parsed, str):\\n            try:\\n                return json.loads(parsed), state\\n            except json.JSONDecodeError as e:\\n                e.args = (\\n                    f'{e.args[0]}; got \\\"{parsed[:ERROR_SNIPPET_LENGTH]}\\\"',\\n                    *e.args[1:],\\n                )\\n                raise\\n        else:\\n            return parsed, state\\n\\n    def fetch_latest_path(self) -> tuple[Path, RepodataState]:\\n        \\\"\\\"\\\"\\n        Retrieve latest or latest-cached repodata; update cache.\\n\\n        :return: (pathlib.Path to uncompressed repodata contents, RepodataState)\\n        \\\"\\\"\\\"\\n        _, state = self.fetch_latest()\\n        return self.cache_path_json, state\\n\\n    @property\\n    def url_w_repodata_fn(self):\\n        return self.url_w_subdir + \\\"/\\\" + self.repodata_fn\\n\\n    @property\\n    def cache_path_json(self):\\n        return self.repo_cache.cache_path_json\\n\\n    @property\\n    def cache_path_state(self):\\n        \\\"\\\"\\\"\\n        Out-of-band etag and other state needed by the RepoInterface.\\n        \\\"\\\"\\\"\\n        return self.repo_cache.cache_path_state\\n\\n    @property\\n    def repo_cache(self) -> RepodataCache:\\n        return RepodataCache(self.cache_path_base, self.repodata_fn)\\n\\n    @property\\n    def _repo(self) -> RepoInterface:\\n        \\\"\\\"\\\"\\n        Changes as we mutate self.repodata_fn.\\n        \\\"\\\"\\\"\\n        return self.repo_interface_cls(\\n            self.url_w_credentials,\\n            repodata_fn=self.repodata_fn,\\n            cache=self.repo_cache,\\n        )\\n\\n    def fetch_latest(self) -> tuple[dict | str, RepodataState]:\\n        \\\"\\\"\\\"\\n        Return up-to-date repodata and cache information. Fetch repodata from\\n        remote if cache has expired; return cached data if cache has not\\n        expired; return stale cached data or dummy data if in offline mode.\\n        \\\"\\\"\\\"\\n        cache = self.repo_cache\\n        cache.load_state()\\n\\n        # XXX cache_path_json and cache_path_state must exist; just try loading\\n        # it and fall back to this on error?\\n        if not cache.cache_path_json.exists():\\n            log.debug(\\n                \\\"No local cache found for %s at %s\\\",\\n                self.url_w_repodata_fn,\\n                self.cache_path_json,\\n            )\\n            if context.use_index_cache or (\\n                context.offline and not self.url_w_subdir.startswith(\\\"file://\\\")\\n            ):\\n                log.debug(\\n                    \\\"Using cached data for %s at %s forced. Returning empty repodata.\\\",\\n                    self.url_w_repodata_fn,\\n                    self.cache_path_json,\\n                )\\n                return (\\n                    {},\\n                    cache.state,\\n                )  # XXX basic properties like info, packages, packages.conda? instead of {}?\\n\\n        else:\\n            if context.use_index_cache:\\n                log.debug(\\n                    \\\"Using cached repodata for %s at %s because use_cache=True\\\",\\n                    self.url_w_repodata_fn,\\n                    self.cache_path_json,\\n                )\\n\\n                _internal_state = self.read_cache()\\n                return _internal_state\\n\\n            stale = cache.stale()\\n            if (not stale or context.offline) and not self.url_w_subdir.startswith(\\n                \\\"file://\\\"\\n            ):\\n                timeout = cache.timeout()\\n                log.debug(\\n                    \\\"Using cached repodata for %s at %s. Timeout in %d sec\\\",\\n                    self.url_w_repodata_fn,\\n                    self.cache_path_json,\\n                    timeout,\\n                )\\n                _internal_state = self.read_cache()\\n                return _internal_state\\n\\n            log.debug(\\n                \\\"Local cache timed out for %s at %s\\\",\\n                self.url_w_repodata_fn,\\n                self.cache_path_json,\\n            )\\n\\n        try:\\n            try:\\n                repo = self._repo\\n                if hasattr(repo, \\\"repodata_parsed\\\"):\\n                    raw_repodata = repo.repodata_parsed(cache.state)  # type: ignore\\n                else:\\n                    raw_repodata = repo.repodata(cache.state)  # type: ignore\\n            except RepodataIsEmpty:\\n                if self.repodata_fn != REPODATA_FN:\\n                    raise  # is UnavailableInvalidChannel subclass\\n                # the surrounding try/except/else will cache \\\"{}\\\"\\n                raw_repodata = None\\n            except RepodataOnDisk:\\n                # used as a sentinel, not the raised exception object\\n                raw_repodata = RepodataOnDisk\\n\\n        except Response304ContentUnchanged:\\n            log.debug(\\n                \\\"304 NOT MODIFIED for '%s'. Updating mtime and loading from disk\\\",\\n                self.url_w_repodata_fn,\\n            )\\n            cache.refresh()\\n            _internal_state = self.read_cache()\\n            return _internal_state\\n        else:\\n            try:\\n                if raw_repodata is RepodataOnDisk:\\n                    # this is handled very similar to a 304. Can the cases be merged?\\n                    # we may need to read_bytes() and compare a hash to the state, instead.\\n                    # XXX use self._repo_cache.load() or replace after passing temp path to jlap\\n                    raw_repodata = self.cache_path_json.read_text()\\n                    stat = self.cache_path_json.stat()\\n                    cache.state[\\\"size\\\"] = stat.st_size  # type: ignore\\n                    mtime_ns = stat.st_mtime_ns\\n                    cache.state[\\\"mtime_ns\\\"] = mtime_ns  # type: ignore\\n                    cache.refresh()\\n                elif isinstance(raw_repodata, dict):\\n                    # repo implementation cached it, and parsed it\\n                    # XXX check size upstream for locking reasons\\n                    stat = self.cache_path_json.stat()\\n                    cache.state[\\\"size\\\"] = stat.st_size\\n                    mtime_ns = stat.st_mtime_ns\\n                    cache.state[\\\"mtime_ns\\\"] = mtime_ns  # type: ignore\\n                    cache.refresh()\\n                elif isinstance(raw_repodata, (str, type(None))):\\n                    # Can we pass this information in state or with a sentinel/special exception?\\n                    if raw_repodata is None:\\n                        raw_repodata = \\\"{}\\\"\\n                    cache.save(raw_repodata)\\n                else:  # pragma: no cover\\n                    # it can be a dict?\\n                    assert False, f\\\"Unreachable {raw_repodata}\\\"\\n            except OSError as e:\\n                if e.errno in (errno.EACCES, errno.EPERM, errno.EROFS):\\n                    raise NotWritableError(self.cache_path_json, e.errno, caused_by=e)\\n                else:\\n                    raise\\n\\n            return raw_repodata, cache.state\\n\\n    def read_cache(self) -> tuple[str, RepodataState]:\\n        \\\"\\\"\\\"\\n        Read repodata from disk, without trying to fetch a fresh version.\\n        \\\"\\\"\\\"\\n        # pickled data is bad or doesn't exist; load cached json\\n        log.debug(\\n            \\\"Loading raw json for %s at %s\\\",\\n            self.url_w_repodata_fn,\\n            self.cache_path_json,\\n        )\\n\\n        cache = self.repo_cache\\n\\n        try:\\n            raw_repodata_str = cache.load()\\n            return raw_repodata_str, cache.state\\n        except ValueError as e:\\n            # OSError (locked) may happen here\\n            # ValueError: Expecting object: line 11750 column 6 (char 303397)\\n            log.debug(\\\"Error for cache path: '%s'\\\\n%r\\\", self.cache_path_json, e)\\n            message = \\\"\\\"\\\"An error occurred when loading cached repodata.  Executing\\n`conda clean --index-cache` will remove cached repodata files\\nso they can be downloaded again.\\\"\\\"\\\"\\n            raise CondaError(message)\\n\\n\\ntry:\\n    hashlib.md5(b\\\"\\\", usedforsecurity=False)\\n\\n    def _md5_not_for_security(data):\\n        return hashlib.md5(data, usedforsecurity=False)\\n\\nexcept TypeError:  # pragma: no cover\\n    # Python < 3.9\\n    def _md5_not_for_security(data):\\n        return hashlib.md5(data)\\n\\n\\ndef cache_fn_url(url, repodata_fn=REPODATA_FN):\\n    # url must be right-padded with '/' to not invalidate any existing caches\\n    if not url.endswith(\\\"/\\\"):\\n        url += \\\"/\\\"\\n    # add the repodata_fn in for uniqueness, but keep it off for standard stuff.\\n    #    It would be more sane to add it for everything, but old programs (Navigator)\\n    #    are looking for the cache under keys without this.\\n    if repodata_fn != REPODATA_FN:\\n        url += repodata_fn\\n\\n    md5 = _md5_not_for_security(url.encode(\\\"utf-8\\\"))\\n    return f\\\"{md5.hexdigest()[:8]}.json\\\"\\n\\n\\ndef get_cache_control_max_age(cache_control_value: str | None):\\n    cache_control_value = cache_control_value or \\\"\\\"\\n    max_age = re.search(r\\\"max-age=(\\\\d+)\\\", cache_control_value)\\n    return int(max_age.groups()[0]) if max_age else 0\\n\\n\\ndef create_cache_dir():\\n    cache_dir = os.path.join(PackageCacheData.first_writable().pkgs_dir, \\\"cache\\\")\\n    mkdir_p_sudo_safe(cache_dir)\\n    return cache_dir\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"JLAP reader.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport logging\\nfrom collections import UserList\\nfrom hashlib import blake2b\\nfrom pathlib import Path\\nfrom typing import TYPE_CHECKING\\n\\nif TYPE_CHECKING:\\n    from typing import Iterable, Iterator\\n\\nlog = logging.getLogger(__name__)\\n\\n\\nDIGEST_SIZE = 32  # 160 bits a minimum 'for security' length?\\nDEFAULT_IV = b\\\"\\\\0\\\" * DIGEST_SIZE\\n\\n\\ndef keyed_hash(data: bytes, key: bytes):\\n    \\\"\\\"\\\"Keyed hash.\\\"\\\"\\\"\\n    return blake2b(data, key=key, digest_size=DIGEST_SIZE)\\n\\n\\ndef line_and_pos(lines: Iterable[bytes], pos=0) -> Iterator[tuple[int, bytes]]:\\n    r\\\"\\\"\\\"\\n    :param lines: iterator over input split by '\\\\n', with '\\\\n' removed.\\n    :param pos: initial position\\n    \\\"\\\"\\\"\\n    for line in lines:\\n        yield pos, line\\n        pos += len(line) + 1\\n\\n\\nclass JLAP(UserList):\\n    @classmethod\\n    def from_lines(cls, lines: Iterable[bytes], iv: bytes, pos=0, verify=True):\\n        r\\\"\\\"\\\"\\n        :param lines: iterator over input split by b'\\\\n', with b'\\\\n' removed\\n        :param pos: initial position\\n        :param iv: initialization vector (first line of .jlap stream, hex\\n            decoded). Ignored if pos==0.\\n        :param verify: assert last line equals computed checksum of previous\\n            line. Useful for writing new .jlap files if False.\\n\\n        :raises ValueError: if trailing and computed checksums do not match\\n\\n        :return: list of (offset, line, checksum)\\n        \\\"\\\"\\\"\\n        # save initial iv in case there were no new lines\\n        buffer: list[tuple[int, str, str]] = [(-1, iv.hex(), iv.hex())]\\n        initial_pos = pos\\n\\n        for pos, line in line_and_pos(lines, pos=pos):\\n            if pos == 0:\\n                iv = bytes.fromhex(line.decode(\\\"utf-8\\\"))\\n                buffer = [(0, iv.hex(), iv.hex())]\\n            else:\\n                iv = keyed_hash(line, iv).digest()\\n                buffer.append((pos, line.decode(\\\"utf-8\\\"), iv.hex()))\\n\\n        log.debug(\\\"%d bytes read\\\", pos - initial_pos)  # maybe + length of last line\\n\\n        if verify:\\n            if buffer[-1][1] != buffer[-2][-1]:\\n                raise ValueError(\\\"checksum mismatch\\\")\\n            else:\\n                log.info(\\\"Checksum OK\\\")\\n\\n        return cls(buffer)\\n\\n    @classmethod\\n    def from_path(cls, path: Path | str, verify=True):\\n        # in binary mode, line separator is hardcoded as \\\\n\\n        with Path(path).open(\\\"rb\\\") as p:\\n            return cls.from_lines(\\n                (line.rstrip(b\\\"\\\\n\\\") for line in p), b\\\"\\\", verify=verify\\n            )\\n\\n    def add(self, line: str):\\n        \\\"\\\"\\\"\\n        Add line to buffer, following checksum rules.\\n\\n        Buffer must not be empty.\\n\\n        (Remember to pop trailing checksum and possibly trailing metadata line, if\\n        appending to a complete jlap file)\\n\\n        Less efficient than creating a new buffer from many lines and our last iv,\\n        and extending.\\n\\n        :return: self\\n        \\\"\\\"\\\"\\n        if \\\"\\\\n\\\" in line:\\n            raise ValueError(\\\"\\\\\\\\n not allowed in line\\\")\\n        pos, last_line, iv = self[-1]\\n        # include last line's utf-8 encoded length, plus 1 in pos?\\n        pos += len(last_line.encode(\\\"utf-8\\\")) + 1\\n        self.extend(\\n            JLAP.from_lines(\\n                (line.encode(\\\"utf-8\\\"),), bytes.fromhex(iv), pos, verify=False\\n            )[1:]\\n        )\\n        return self\\n\\n    def terminate(self):\\n        \\\"\\\"\\\"\\n        Add trailing checksum to buffer.\\n\\n        :return: self\\n        \\\"\\\"\\\"\\n        _, _, iv = self[-1]\\n        self.add(iv)\\n        return self\\n\\n    def write(self, path: Path):\\n        \\\"\\\"\\\"Write buffer to path.\\\"\\\"\\\"\\n        with Path(path).open(\\\"w\\\", encoding=\\\"utf-8\\\", newline=\\\"\\\\n\\\") as p:\\n            return p.write(\\\"\\\\n\\\".join(b[1] for b in self))\\n\\n    @property\\n    def body(self):\\n        \\\"\\\"\\\"All lines except the first, and last two.\\\"\\\"\\\"\\n        return self[1:-2]\\n\\n    @property\\n    def penultimate(self):\\n        \\\"\\\"\\\"Next-to-last line. Should contain the footer.\\\"\\\"\\\"\\n        return self[-2]\\n\\n    @property\\n    def last(self):\\n        \\\"\\\"\\\"Last line. Should contain the trailing checksum.\\\"\\\"\\\"\\n        return self[-1]\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"JLAP consumer.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport io\\nimport json\\nimport logging\\nimport pprint\\nimport re\\nimport time\\nfrom contextlib import contextmanager\\nfrom hashlib import blake2b\\nfrom typing import TYPE_CHECKING\\n\\nimport jsonpatch\\nimport zstandard\\nfrom requests import HTTPError\\n\\nfrom conda.common.url import mask_anaconda_token\\n\\nfrom ....base.context import context\\nfrom .. import ETAG_KEY, LAST_MODIFIED_KEY, RepodataState\\nfrom .core import JLAP\\n\\nif TYPE_CHECKING:\\n    import pathlib\\n    from typing import Iterator\\n\\n    from ...connection import Response, Session\\n    from .. import RepodataCache\\n\\nlog = logging.getLogger(__name__)\\n\\n\\nDIGEST_SIZE = 32  # 256 bits\\n\\nJLAP_KEY = \\\"jlap\\\"\\nHEADERS = \\\"headers\\\"\\nNOMINAL_HASH = \\\"blake2_256_nominal\\\"\\nON_DISK_HASH = \\\"blake2_256\\\"\\nLATEST = \\\"latest\\\"\\n\\n# save these headers. at least etag, last-modified, cache-control plus a few\\n# useful extras.\\nSTORE_HEADERS = {\\n    \\\"etag\\\",\\n    \\\"last-modified\\\",\\n    \\\"cache-control\\\",\\n    \\\"content-range\\\",\\n    \\\"content-length\\\",\\n    \\\"date\\\",\\n    \\\"content-type\\\",\\n    \\\"content-encoding\\\",\\n}\\n\\n\\ndef hash():\\n    \\\"\\\"\\\"Ordinary hash.\\\"\\\"\\\"\\n    return blake2b(digest_size=DIGEST_SIZE)\\n\\n\\nclass Jlap304NotModified(Exception):\\n    pass\\n\\n\\nclass JlapSkipZst(Exception):\\n    pass\\n\\n\\nclass JlapPatchNotFound(LookupError):\\n    pass\\n\\n\\ndef process_jlap_response(response: Response, pos=0, iv=b\\\"\\\"):\\n    # if response is 304 Not Modified, could return a buffer with only the\\n    # cached footer...\\n    if response.status_code == 304:\\n        raise Jlap304NotModified()\\n\\n    def lines() -> Iterator[bytes]:\\n        yield from response.iter_lines(delimiter=b\\\"\\\\n\\\")  # type: ignore\\n\\n    buffer = JLAP.from_lines(lines(), iv, pos)\\n\\n    # new iv == initial iv if nothing changed\\n    pos, footer, _ = buffer[-2]\\n    footer = json.loads(footer)\\n\\n    new_state = {\\n        # we need to save etag, last-modified, cache-control\\n        \\\"headers\\\": {\\n            k.lower(): v\\n            for k, v in response.headers.items()\\n            if k.lower() in STORE_HEADERS\\n        },\\n        \\\"iv\\\": buffer[-3][-1],\\n        \\\"pos\\\": pos,\\n        \\\"footer\\\": footer,\\n    }\\n\\n    return buffer, new_state\\n\\n\\ndef fetch_jlap(url, pos=0, etag=None, iv=b\\\"\\\", ignore_etag=True, session=None):\\n    response = request_jlap(\\n        url, pos=pos, etag=etag, ignore_etag=ignore_etag, session=session\\n    )\\n    return process_jlap_response(response, pos=pos, iv=iv)\\n\\n\\ndef request_jlap(\\n    url, pos=0, etag=None, ignore_etag=True, session: Session | None = None\\n):\\n    \\\"\\\"\\\"Return the part of the remote .jlap file we are interested in.\\\"\\\"\\\"\\n    headers = {}\\n    if pos:\\n        headers[\\\"range\\\"] = f\\\"bytes={pos}-\\\"\\n    if etag and not ignore_etag:\\n        headers[\\\"if-none-match\\\"] = etag\\n\\n    log.debug(\\\"%s %s\\\", mask_anaconda_token(url), headers)\\n\\n    assert session is not None\\n\\n    timeout = context.remote_connect_timeout_secs, context.remote_read_timeout_secs\\n    response = session.get(url, stream=True, headers=headers, timeout=timeout)\\n    response.raise_for_status()\\n\\n    if response.request:\\n        log.debug(\\\"request headers: %s\\\", pprint.pformat(response.request.headers))\\n    else:\\n        log.debug(\\\"response without request.\\\")\\n    log.debug(\\n        \\\"response headers: %s\\\",\\n        pprint.pformat(\\n            {k: v for k, v in response.headers.items() if k.lower() in STORE_HEADERS}\\n        ),\\n    )\\n    log.debug(\\\"status: %d\\\", response.status_code)\\n    if \\\"range\\\" in headers:\\n        # 200 is also a possibility that we'd rather not deal with; if the\\n        # server can't do range requests, also mark jlap as unavailable. Which\\n        # status codes mean 'try again' instead of 'it will never work'?\\n        if response.status_code not in (206, 304, 404, 416):\\n            raise HTTPError(\\n                f\\\"Unexpected response code for range request {response.status_code}\\\",\\n                response=response,\\n            )\\n\\n    log.info(\\\"%s\\\", response)\\n\\n    return response\\n\\n\\ndef format_hash(hash):\\n    \\\"\\\"\\\"Abbreviate hash for formatting.\\\"\\\"\\\"\\n    return hash[:16] + \\\"\\\\N{HORIZONTAL ELLIPSIS}\\\"\\n\\n\\ndef find_patches(patches, have, want):\\n    apply = []\\n    for patch in reversed(patches):\\n        if have == want:\\n            break\\n        if patch[\\\"to\\\"] == want:\\n            apply.append(patch)\\n            want = patch[\\\"from\\\"]\\n\\n    if have != want:\\n        log.debug(f\\\"No patch from local revision {format_hash(have)}\\\")\\n        raise JlapPatchNotFound(f\\\"No patch from local revision {format_hash(have)}\\\")\\n\\n    return apply\\n\\n\\ndef apply_patches(data, apply):\\n    while apply:\\n        patch = apply.pop()\\n        log.debug(\\n            f\\\"{format_hash(patch['from'])} \\\\N{RIGHTWARDS ARROW} {format_hash(patch['to'])}, \\\"\\n            f\\\"{len(patch['patch'])} steps\\\"\\n        )\\n        data = jsonpatch.JsonPatch(patch[\\\"patch\\\"]).apply(data, in_place=True)\\n\\n\\ndef withext(url, ext):\\n    return re.sub(r\\\"(\\\\.\\\\w+)$\\\", ext, url)\\n\\n\\n@contextmanager\\ndef timeme(message):\\n    begin = time.monotonic()\\n    yield\\n    end = time.monotonic()\\n    log.debug(\\\"%sTook %0.02fs\\\", message, end - begin)\\n\\n\\ndef build_headers(json_path: pathlib.Path, state: RepodataState):\\n    \\\"\\\"\\\"Caching headers for a path and state.\\\"\\\"\\\"\\n    headers = {}\\n    # simplify if we require state to be empty when json_path is missing.\\n    if json_path.exists():\\n        etag = state.get(\\\"_etag\\\")\\n        if etag:\\n            headers[\\\"if-none-match\\\"] = etag\\n    return headers\\n\\n\\nclass HashWriter(io.RawIOBase):\\n    def __init__(self, backing, hasher):\\n        self.backing = backing\\n        self.hasher = hasher\\n\\n    def write(self, b: bytes):\\n        self.hasher.update(b)\\n        return self.backing.write(b)\\n\\n    def close(self):\\n        self.backing.close()\\n\\n\\ndef download_and_hash(\\n    hasher,\\n    url,\\n    json_path: pathlib.Path,\\n    session: Session,\\n    state: RepodataState | None,\\n    is_zst=False,\\n    dest_path: pathlib.Path | None = None,\\n):\\n    \\\"\\\"\\\"Download url if it doesn't exist, passing bytes through hasher.update().\\n\\n    json_path: Path of old cached data (ignore etag if not exists).\\n    dest_path: Path to write new data.\\n    \\\"\\\"\\\"\\n    if dest_path is None:\\n        dest_path = json_path\\n    state = state or RepodataState()\\n    headers = build_headers(json_path, state)\\n    timeout = context.remote_connect_timeout_secs, context.remote_read_timeout_secs\\n    response = session.get(url, stream=True, timeout=timeout, headers=headers)\\n    log.debug(\\\"%s %s\\\", url, response.headers)\\n    response.raise_for_status()\\n    length = 0\\n    # is there a status code for which we must clear the file?\\n    if response.status_code == 200:\\n        if is_zst:\\n            decompressor = zstandard.ZstdDecompressor()\\n            writer = decompressor.stream_writer(\\n                HashWriter(dest_path.open(\\\"wb\\\"), hasher),  # type: ignore\\n                closefd=True,\\n            )\\n        else:\\n            writer = HashWriter(dest_path.open(\\\"wb\\\"), hasher)\\n        with writer as repodata:\\n            for block in response.iter_content(chunk_size=1 << 14):\\n                repodata.write(block)\\n    if response.request:\\n        try:\\n            length = int(response.headers[\\\"Content-Length\\\"])\\n        except (KeyError, ValueError, AttributeError):\\n            pass\\n        log.info(\\\"Download %d bytes %r\\\", length, response.request.headers)\\n    return response  # can be 304 not modified\\n\\n\\ndef _is_http_error_most_400_codes(e: HTTPError) -> bool:\\n    \\\"\\\"\\\"\\n    Determine whether the `HTTPError` is an HTTP 400 error code (except for 416).\\n    \\\"\\\"\\\"\\n    if e.response is None:  # 404 e.response is falsey\\n        return False\\n    status_code = e.response.status_code\\n    return 400 <= status_code < 500 and status_code != 416\\n\\n\\ndef request_url_jlap_state(\\n    url,\\n    state: RepodataState,\\n    full_download=False,\\n    *,\\n    session: Session,\\n    cache: RepodataCache,\\n    temp_path: pathlib.Path,\\n) -> dict | None:\\n    jlap_state = state.get(JLAP_KEY, {})\\n    headers = jlap_state.get(HEADERS, {})\\n    json_path = cache.cache_path_json\\n\\n    buffer = JLAP()  # type checks\\n\\n    if (\\n        full_download\\n        or not (NOMINAL_HASH in state and json_path.exists())\\n        or not state.should_check_format(\\\"jlap\\\")\\n    ):\\n        hasher = hash()\\n        with timeme(f\\\"Download complete {url} \\\"):\\n            # Don't deal with 304 Not Modified if hash unavailable e.g. if\\n            # cached without jlap\\n            if NOMINAL_HASH not in state:\\n                state.pop(ETAG_KEY, None)\\n                state.pop(LAST_MODIFIED_KEY, None)\\n\\n            try:\\n                if state.should_check_format(\\\"zst\\\"):\\n                    response = download_and_hash(\\n                        hasher,\\n                        withext(url, \\\".json.zst\\\"),\\n                        json_path,  # makes conditional request if exists\\n                        dest_path=temp_path,  # writes to\\n                        session=session,\\n                        state=state,\\n                        is_zst=True,\\n                    )\\n                else:\\n                    raise JlapSkipZst()\\n            except (JlapSkipZst, HTTPError, zstandard.ZstdError) as e:\\n                if isinstance(e, zstandard.ZstdError):\\n                    log.warning(\\n                        \\\"Could not decompress %s as zstd. Fall back to .json. (%s)\\\",\\n                        mask_anaconda_token(withext(url, \\\".json.zst\\\")),\\n                        e,\\n                    )\\n                if isinstance(e, HTTPError) and not _is_http_error_most_400_codes(e):\\n                    raise\\n                if not isinstance(e, JlapSkipZst):\\n                    # don't update last-checked timestamp on skip\\n                    state.set_has_format(\\\"zst\\\", False)\\n                response = download_and_hash(\\n                    hasher,\\n                    withext(url, \\\".json\\\"),\\n                    json_path,\\n                    dest_path=temp_path,\\n                    session=session,\\n                    state=state,\\n                )\\n\\n            # will we use state['headers'] for caching against\\n            state[\\\"_mod\\\"] = response.headers.get(\\\"last-modified\\\")\\n            state[\\\"_etag\\\"] = response.headers.get(\\\"etag\\\")\\n            state[\\\"_cache_control\\\"] = response.headers.get(\\\"cache-control\\\")\\n\\n        # was not re-hashed if 304 not modified\\n        if response.status_code == 200:\\n            state[NOMINAL_HASH] = state[ON_DISK_HASH] = hasher.hexdigest()\\n\\n        have = state[NOMINAL_HASH]\\n\\n        # a jlap buffer with zero patches. the buffer format is (position,\\n        # payload, checksum) where position is the offset from the beginning of\\n        # the file; payload is the leading or trailing checksum or other data;\\n        # and checksum is the running checksum for the file up to that point.\\n        buffer = JLAP([[-1, \\\"\\\", \\\"\\\"], [0, json.dumps({LATEST: have}), \\\"\\\"], [1, \\\"\\\", \\\"\\\"]])\\n\\n    else:\\n        have = state[NOMINAL_HASH]\\n        # have_hash = state.get(ON_DISK_HASH)\\n\\n        need_jlap = True\\n        try:\\n            iv_hex = jlap_state.get(\\\"iv\\\", \\\"\\\")\\n            pos = jlap_state.get(\\\"pos\\\", 0)\\n            etag = headers.get(ETAG_KEY, None)\\n            jlap_url = withext(url, \\\".jlap\\\")\\n            log.debug(\\n                \\\"Fetch %s from iv=%s, pos=%s\\\",\\n                mask_anaconda_token(jlap_url),\\n                iv_hex,\\n                pos,\\n            )\\n            # wrong to read state outside of function, and totally rebuild inside\\n            buffer, jlap_state = fetch_jlap(\\n                jlap_url,\\n                pos=pos,\\n                etag=etag,\\n                iv=bytes.fromhex(iv_hex),\\n                session=session,\\n                ignore_etag=False,\\n            )\\n            state.set_has_format(\\\"jlap\\\", True)\\n            need_jlap = False\\n        except ValueError:\\n            log.info(\\\"Checksum not OK on JLAP range request. Retry with complete JLAP.\\\")\\n        except IndexError:\\n            log.exception(\\\"IndexError reading JLAP. Invalid file?\\\")\\n        except HTTPError as e:\\n            # If we get a 416 Requested range not satisfiable, the server-side\\n            # file may have been truncated and we need to fetch from 0\\n            if _is_http_error_most_400_codes(e):\\n                state.set_has_format(\\\"jlap\\\", False)\\n                return request_url_jlap_state(\\n                    url,\\n                    state,\\n                    full_download=True,\\n                    session=session,\\n                    cache=cache,\\n                    temp_path=temp_path,\\n                )\\n            log.info(\\n                \\\"Response code %d on JLAP range request. Retry with complete JLAP.\\\",\\n                e.response.status_code,\\n            )\\n\\n        if need_jlap:  # retry whole file, if range failed\\n            try:\\n                buffer, jlap_state = fetch_jlap(withext(url, \\\".jlap\\\"), session=session)\\n            except (ValueError, IndexError) as e:\\n                log.exception(\\\"Error parsing jlap\\\", exc_info=e)\\n                # a 'latest' hash that we can't achieve, triggering later error handling\\n                buffer = JLAP(\\n                    [[-1, \\\"\\\", \\\"\\\"], [0, json.dumps({LATEST: \\\"0\\\" * 32}), \\\"\\\"], [1, \\\"\\\", \\\"\\\"]]\\n                )\\n                state.set_has_format(\\\"jlap\\\", False)\\n\\n        state[JLAP_KEY] = jlap_state\\n\\n    with timeme(\\\"Apply Patches \\\"):\\n        # buffer[0] == previous iv\\n        # buffer[1:-2] == patches\\n        # buffer[-2] == footer = new_state[\\\"footer\\\"]\\n        # buffer[-1] == trailing checksum\\n\\n        patches = list(json.loads(patch) for _, patch, _ in buffer.body)\\n        _, footer, _ = buffer.penultimate\\n        want = json.loads(footer)[\\\"latest\\\"]\\n\\n        try:\\n            apply = find_patches(patches, have, want)\\n            log.info(\\n                f\\\"Apply {len(apply)} patches \\\"\\n                f\\\"{format_hash(have)} \\\\N{RIGHTWARDS ARROW} {format_hash(want)}\\\"\\n            )\\n\\n            if apply:\\n                with timeme(\\\"Load \\\"):\\n                    # we haven't loaded repodata yet; it could fail to parse, or\\n                    # have the wrong hash.\\n                    # if this fails, then we also need to fetch again from 0\\n                    repodata_json = json.loads(cache.load())\\n                    # XXX cache.state must equal what we started with, otherwise\\n                    # bail with 'repodata on disk' (indicating another process\\n                    # downloaded repodata.json in parallel with us)\\n                    if have != cache.state.get(NOMINAL_HASH):  # or check mtime_ns?\\n                        log.warning(\\\"repodata cache changed during jlap fetch.\\\")\\n                        return None\\n\\n                apply_patches(repodata_json, apply)\\n\\n                with timeme(\\\"Write changed \\\"), temp_path.open(\\\"wb\\\") as repodata:\\n                    hasher = hash()\\n                    HashWriter(repodata, hasher).write(\\n                        json.dumps(repodata_json, separators=(\\\",\\\", \\\":\\\")).encode(\\\"utf-8\\\")\\n                    )\\n\\n                    # actual hash of serialized json\\n                    state[ON_DISK_HASH] = hasher.hexdigest()\\n\\n                    # hash of equivalent upstream json\\n                    state[NOMINAL_HASH] = want\\n\\n                    # avoid duplicate parsing\\n                    return repodata_json\\n            else:\\n                assert state[NOMINAL_HASH] == want\\n\\n        except (JlapPatchNotFound, json.JSONDecodeError) as e:\\n            if isinstance(e, JlapPatchNotFound):\\n                # 'have' hash not mentioned in patchset\\n                #\\n                # XXX or skip jlap at top of fn; make sure it is not\\n                # possible to download the complete json twice\\n                log.info(\\n                    \\\"Current repodata.json %s not found in patchset. Re-download repodata.json\\\"\\n                )\\n\\n            assert not full_download, \\\"Recursion error\\\"  # pragma: no cover\\n\\n            return request_url_jlap_state(\\n                url,\\n                state,\\n                full_download=True,\\n                session=session,\\n                cache=cache,\\n                temp_path=temp_path,\\n            )\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Incremental repodata feature based on .jlap patch files.\\\"\\\"\\\"\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"JLAP interface for repodata.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport logging\\nimport os\\nfrom typing import TYPE_CHECKING\\n\\nfrom ....base.context import context\\nfrom ...connection.download import disable_ssl_verify_warning\\nfrom ...connection.session import get_session\\nfrom .. import (\\n    CACHE_CONTROL_KEY,\\n    ETAG_KEY,\\n    LAST_MODIFIED_KEY,\\n    URL_KEY,\\n    RepodataOnDisk,\\n    RepodataState,\\n    RepoInterface,\\n    Response304ContentUnchanged,\\n    conda_http_errors,\\n)\\nfrom . import fetch\\n\\nif TYPE_CHECKING:\\n    from .. import RepodataCache\\n\\nlog = logging.getLogger(__name__)\\n\\n\\nclass JlapRepoInterface(RepoInterface):\\n    def __init__(\\n        self,\\n        url: str,\\n        repodata_fn: str | None,\\n        *,\\n        cache: RepodataCache,\\n        **kwargs,\\n    ) -> None:\\n        log.debug(\\\"Using %s\\\", self.__class__.__name__)\\n\\n        self._cache = cache\\n\\n        self._url = url\\n        self._repodata_fn = repodata_fn\\n\\n        self._log = logging.getLogger(__name__)\\n        self._stderrlog = logging.getLogger(\\\"conda.stderrlog\\\")\\n\\n    def repodata(self, state: dict | RepodataState) -> str | None:\\n        \\\"\\\"\\\"\\n        Fetch newest repodata if necessary.\\n\\n        Always writes to ``cache_path_json``.\\n        \\\"\\\"\\\"\\n        self.repodata_parsed(state)\\n        raise RepodataOnDisk()\\n\\n    def repodata_parsed(self, state: dict | RepodataState) -> dict | None:\\n        \\\"\\\"\\\"\\n        JLAP has to parse the JSON anyway.\\n\\n        Use this to avoid a redundant parse when repodata is updated.\\n\\n        When repodata is not updated, it doesn't matter whether this function or\\n        the caller reads from a file.\\n        \\\"\\\"\\\"\\n        session = get_session(self._url)\\n\\n        if not context.ssl_verify:\\n            disable_ssl_verify_warning()\\n\\n        repodata_url = f\\\"{self._url}/{self._repodata_fn}\\\"\\n\\n        # XXX won't modify caller's state dict\\n        state_ = self._repodata_state_copy(state)\\n\\n        # at this point, self._cache.state == state == state_\\n\\n        temp_path = (\\n            self._cache.cache_dir / f\\\"{self._cache.name}.{os.urandom(2).hex()}.tmp\\\"\\n        )\\n        try:\\n            with conda_http_errors(self._url, self._repodata_fn):\\n                repodata_json_or_none = fetch.request_url_jlap_state(\\n                    repodata_url,\\n                    state_,\\n                    session=session,\\n                    cache=self._cache,\\n                    temp_path=temp_path,\\n                )\\n\\n                # update caller's state dict-or-RepodataState. Do this before\\n                # the self._cache.replace() call which also writes state, then\\n                # signal not to write state to caller.\\n                state.update(state_)\\n\\n                state[URL_KEY] = self._url\\n                headers = state.get(\\\"jlap\\\", {}).get(\\n                    \\\"headers\\\"\\n                )  # XXX overwrite headers in jlapper.request_url_jlap_state\\n                if headers:\\n                    state[ETAG_KEY] = headers.get(\\\"etag\\\")\\n                    state[LAST_MODIFIED_KEY] = headers.get(\\\"last-modified\\\")\\n                    state[CACHE_CONTROL_KEY] = headers.get(\\\"cache-control\\\")\\n\\n                self._cache.state.update(state)\\n\\n            if temp_path.exists():\\n                self._cache.replace(temp_path)\\n        except fetch.Jlap304NotModified:\\n            raise Response304ContentUnchanged()\\n        finally:\\n            # Clean up the temporary file. In the successful case it raises\\n            # OSError as self._cache_replace() removed temp_file.\\n            try:\\n                temp_path.unlink()\\n            except OSError:\\n                pass\\n\\n        if repodata_json_or_none is None:  # common\\n            # Indicate that subdir_data mustn't rewrite cache_path_json\\n            raise RepodataOnDisk()\\n        else:\\n            return repodata_json_or_none\\n\\n    def _repodata_state_copy(self, state: dict | RepodataState):\\n        return RepodataState(dict=state)\\n\\n\\nclass RepodataStateSkipFormat(RepodataState):\\n    skip_formats: set[str]\\n\\n    def __init__(self, *args, skip_formats=set(), **kwargs):\\n        super().__init__(*args, **kwargs)\\n        self.skip_formats = set(skip_formats)\\n\\n    def should_check_format(self, format):\\n        if format in self.skip_formats:\\n            return False\\n        return super().should_check_format(format)\\n\\n\\nclass ZstdRepoInterface(JlapRepoInterface):\\n    \\\"\\\"\\\"\\n    Support repodata.json.zst (if available) without checking .jlap\\n    \\\"\\\"\\\"\\n\\n    def _repodata_state_copy(self, state: dict | RepodataState):\\n        return RepodataStateSkipFormat(dict=state, skip_formats=[\\\"jlap\\\"])\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Interface between conda-content-trust and conda.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport json\\nimport os\\nimport re\\nimport warnings\\nfrom functools import lru_cache\\nfrom logging import getLogger\\nfrom pathlib import Path\\n\\ntry:\\n    from conda_content_trust.authentication import verify_delegation, verify_root\\n    from conda_content_trust.common import (\\n        SignatureError,\\n        load_metadata_from_file,\\n        write_metadata_to_file,\\n    )\\n    from conda_content_trust.signing import wrap_as_signable\\nexcept ImportError:\\n    # _SignatureVerification.enabled handles the rest of this state\\n    class SignatureError(Exception):\\n        pass\\n\\n\\nfrom typing import TYPE_CHECKING\\n\\nfrom ..base.constants import CONDA_PACKAGE_EXTENSION_V1, CONDA_PACKAGE_EXTENSION_V2\\nfrom ..base.context import context\\nfrom ..common.url import join_url\\nfrom ..core.subdir_data import SubdirData\\nfrom ..gateways.connection import HTTPError, InsecureRequestWarning\\nfrom ..gateways.connection.session import get_session\\nfrom .constants import INITIAL_TRUST_ROOT, KEY_MGR_FILE\\n\\nif TYPE_CHECKING:\\n    from ..models.records import PackageRecord\\n\\nlog = getLogger(__name__)\\n\\n\\nRE_ROOT_METADATA = re.compile(r\\\"(?P<number>\\\\d+)\\\\.root\\\\.json\\\")\\n\\n\\nclass _SignatureVerification:\\n    # FUTURE: Python 3.8+, replace with functools.cached_property\\n    @property\\n    @lru_cache(maxsize=None)\\n    def enabled(self) -> bool:\\n        # safety checks must be enabled\\n        if not context.extra_safety_checks:\\n            return False\\n\\n        # signing url must be defined\\n        if not context.signing_metadata_url_base:\\n            log.warning(\\n                \\\"metadata signature verification requested, \\\"\\n                \\\"but no metadata URL base has not been specified.\\\"\\n            )\\n            return False\\n\\n        # conda_content_trust must be installed\\n        try:\\n            import conda_content_trust  # noqa: F401\\n        except ImportError:\\n            log.warning(\\n                \\\"metadata signature verification requested, \\\"\\n                \\\"but `conda-content-trust` is not installed.\\\"\\n            )\\n            return False\\n\\n        # ensure artifact verification directory exists\\n        Path(context.av_data_dir).mkdir(parents=True, exist_ok=True)\\n\\n        # ensure the trusted_root exists\\n        if self.trusted_root is None:\\n            log.warning(\\n                \\\"could not find trusted_root data for metadata signature verification\\\"\\n            )\\n            return False\\n\\n        # ensure the key_mgr exists\\n        if self.key_mgr is None:\\n            log.warning(\\n                \\\"could not find key_mgr data for metadata signature verification\\\"\\n            )\\n            return False\\n\\n        # signature verification is enabled\\n        return True\\n\\n    # FUTURE: Python 3.8+, replace with functools.cached_property\\n    @property\\n    @lru_cache(maxsize=None)\\n    def trusted_root(self) -> dict:\\n        # TODO: formalize paths for `*.root.json` and `key_mgr.json` on server-side\\n        trusted: dict | None = None\\n\\n        # Load latest trust root metadata from filesystem\\n        try:\\n            paths = {\\n                int(m.group(\\\"number\\\")): entry\\n                for entry in os.scandir(context.av_data_dir)\\n                if (m := RE_ROOT_METADATA.match(entry.name))\\n            }\\n        except (FileNotFoundError, NotADirectoryError, PermissionError):\\n            # FileNotFoundError: context.av_data_dir does not exist\\n            # NotADirectoryError: context.av_data_dir is not a directory\\n            # PermsissionError: context.av_data_dir is not readable\\n            pass\\n        else:\\n            for _, entry in sorted(paths.items(), reverse=True):\\n                log.info(f\\\"Loading root metadata from {entry}.\\\")\\n                try:\\n                    trusted = load_metadata_from_file(entry)\\n                except (IsADirectoryError, FileNotFoundError, PermissionError):\\n                    # IsADirectoryError: entry is not a file\\n                    # FileNotFoundError: entry does not exist\\n                    # PermsissionError: entry is not readable\\n                    continue\\n                else:\\n                    break\\n\\n        # Fallback to default root metadata if unable to fetch any\\n        if not trusted:\\n            log.debug(\\n                f\\\"No root metadata in {context.av_data_dir}. \\\"\\n                \\\"Using built-in root metadata.\\\"\\n            )\\n            trusted = INITIAL_TRUST_ROOT\\n\\n        # Refresh trust root metadata\\n        while True:\\n            # TODO: caching mechanism to reduce number of refresh requests\\n            fname = f\\\"{trusted['signed']['version'] + 1}.root.json\\\"\\n            path = Path(context.av_data_dir, fname)\\n\\n            try:\\n                # TODO: support fetching root data with credentials\\n                untrusted = self._fetch_channel_signing_data(\\n                    context.signing_metadata_url_base,\\n                    fname,\\n                )\\n\\n                verify_root(trusted, untrusted)\\n            except HTTPError as err:\\n                # HTTP 404 implies no updated root.json is available, which is\\n                # not really an \\\"error\\\" and does not need to be logged.\\n                if err.response.status_code != 404:\\n                    log.error(err)\\n                break\\n            except Exception as err:\\n                # TODO: more error handling\\n                log.error(err)\\n                break\\n            else:\\n                # New trust root metadata checks out\\n                write_metadata_to_file(trusted := untrusted, path)\\n\\n        return trusted\\n\\n    # FUTURE: Python 3.8+, replace with functools.cached_property\\n    @property\\n    @lru_cache(maxsize=None)\\n    def key_mgr(self) -> dict | None:\\n        trusted: dict | None = None\\n\\n        # Refresh key manager metadata\\n        fname = KEY_MGR_FILE\\n        path = Path(context.av_data_dir, fname)\\n\\n        try:\\n            untrusted = self._fetch_channel_signing_data(\\n                context.signing_metadata_url_base,\\n                fname,\\n            )\\n\\n            verify_delegation(\\\"key_mgr\\\", untrusted, self.trusted_root)\\n        except ConnectionError as err:\\n            log.warning(err)\\n        except HTTPError as err:\\n            # sometimes the HTTPError message is blank, when that occurs include the\\n            # HTTP status code\\n            log.warning(\\n                str(err) or f\\\"{err.__class__.__name__} ({err.response.status_code})\\\"\\n            )\\n        else:\\n            # New key manager metadata checks out\\n            write_metadata_to_file(trusted := untrusted, path)\\n\\n        # If key_mgr is unavailable from server, fall back to copy on disk\\n        if not trusted and path.exists():\\n            trusted = load_metadata_from_file(path)\\n\\n        return trusted\\n\\n    def _fetch_channel_signing_data(\\n        self, signing_data_url: str, filename: str, etag=None, mod_stamp=None\\n    ) -> dict:\\n        session = get_session(signing_data_url)\\n\\n        if not context.ssl_verify:\\n            warnings.simplefilter(\\\"ignore\\\", InsecureRequestWarning)\\n\\n        headers = {\\n            \\\"Accept-Encoding\\\": \\\"gzip, deflate, compress, identity\\\",\\n            \\\"Content-Type\\\": \\\"application/json\\\",\\n        }\\n        if etag:\\n            headers[\\\"If-None-Match\\\"] = etag\\n        if mod_stamp:\\n            headers[\\\"If-Modified-Since\\\"] = mod_stamp\\n\\n        saved_token_setting = context.add_anaconda_token\\n        try:\\n            # Assume trust metadata is intended to be \\\"generally available\\\",\\n            # and specifically, _not_ protected by a conda/binstar token.\\n            # Seems reasonable, since we (probably) don't want the headaches of\\n            # dealing with protected, per-channel trust metadata.\\n            #\\n            # Note: Setting `auth=None` here does allow trust metadata to be\\n            # protected using standard HTTP basic auth mechanisms, with the\\n            # login information being provided in the user's netrc file.\\n            context.add_anaconda_token = False\\n            resp = session.get(\\n                join_url(signing_data_url, filename),\\n                headers=headers,\\n                proxies=session.proxies,\\n                auth=None,\\n                timeout=(\\n                    context.remote_connect_timeout_secs,\\n                    context.remote_read_timeout_secs,\\n                ),\\n            )\\n            # TODO: maybe add more sensible error handling\\n            resp.raise_for_status()\\n        finally:\\n            context.add_anaconda_token = saved_token_setting\\n\\n        # In certain cases (e.g., using `-c` access anaconda.org channels), the\\n        # `CondaSession.get()` retry logic combined with the remote server's\\n        # behavior can result in non-JSON content being returned.  Parse returned\\n        # content here (rather than directly in the return statement) so callers of\\n        # this function only have to worry about a ValueError being raised.\\n        try:\\n            return resp.json()\\n        except json.decoder.JSONDecodeError as err:  # noqa\\n            # TODO: additional loading and error handling improvements?\\n            raise ValueError(\\n                f\\\"Invalid JSON returned from {signing_data_url}/{filename}\\\"\\n            )\\n\\n    def verify(self, repodata_fn: str, record: PackageRecord):\\n        repodata, _ = SubdirData(\\n            record.channel,\\n            repodata_fn=repodata_fn,\\n        ).repo_fetch.fetch_latest_parsed()\\n\\n        # short-circuit if no signatures are defined\\n        if \\\"signatures\\\" not in repodata:\\n            record.metadata.add(\\n                f\\\"(no signatures found for {record.channel.canonical_name})\\\"\\n            )\\n            return\\n        signatures = repodata[\\\"signatures\\\"]\\n\\n        # short-circuit if no signature is defined for this package\\n        if record.fn not in signatures:\\n            record.metadata.add(f\\\"(no signatures found for {record.fn})\\\")\\n            return\\n        signature = signatures[record.fn]\\n\\n        # extract metadata to be verified\\n        if record.fn.endswith(CONDA_PACKAGE_EXTENSION_V1):\\n            info = repodata[\\\"packages\\\"][record.fn]\\n        elif record.fn.endswith(CONDA_PACKAGE_EXTENSION_V2):\\n            info = repodata[\\\"packages.conda\\\"][record.fn]\\n        else:\\n            raise ValueError(\\\"unknown package extension\\\")\\n\\n        # create a signable envelope (a dict with the info and signatures)\\n        envelope = wrap_as_signable(info)\\n        envelope[\\\"signatures\\\"] = signature\\n\\n        try:\\n            verify_delegation(\\\"pkg_mgr\\\", envelope, self.key_mgr)\\n        except SignatureError:\\n            log.warning(f\\\"invalid signature for {record.fn}\\\")\\n            record.metadata.add(\\\"(package metadata is UNTRUSTED)\\\")\\n        else:\\n            log.info(f\\\"valid signature for {record.fn}\\\")\\n            record.metadata.add(\\\"(package metadata is TRUSTED)\\\")\\n\\n    def __call__(\\n        self,\\n        repodata_fn: str,\\n        unlink_precs: tuple[PackageRecord, ...],\\n        link_precs: tuple[PackageRecord, ...],\\n    ) -> None:\\n        if not self.enabled:\\n            return\\n\\n        for prec in link_precs:\\n            self.verify(repodata_fn, prec)\\n\\n    @classmethod\\n    def cache_clear(cls) -> None:\\n        cls.enabled.fget.cache_clear()\\n        cls.trusted_root.fget.cache_clear()\\n        cls.key_mgr.fget.cache_clear()\\n\\n\\n# singleton for caching\\nsignature_verification = _SignatureVerification()\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Context trust constants.\\n\\nYou could argue that the signatures being here is not necessary; indeed, we\\nare not necessarily going to be able to check them *properly* (based on some\\nprior expectations) as the user, since this is the beginning of trust\\nbootstrapping, the first/backup version of the root of trust metadata.\\nStill, the signatures here are useful for diagnostic purposes, and, more\\nimportant, to allow self-consistency checks: that helps us avoid breaking the\\nchain of trust if someone accidentally lists the wrong keys down the line. (:\\nThe discrepancy can be detected when loading the root data, and we can\\ndecline to cache incorrect trust metadata that would make further root\\nupdates impossible.\\n\\\"\\\"\\\"\\n\\nINITIAL_TRUST_ROOT = {\\n    \\\"signatures\\\": {\\n        \\\"6d4d5888398ad77465e9fd53996309187723e16509144aa6733015c960378e7a\\\": {\\n            \\\"other_headers\\\": \\\"04001608001d162104d2ca1d4bf5d77e7c312534284dd9c45328b685ec0502605dbb03\\\",  # noqa: E501\\n            \\\"signature\\\": \\\"b71c9b3aa60e77258c402e574397127bcb4bc15ef3055ada8539b0d1e355bf1415a135fb7cecc9244f839a929f6b1f82844a5b3df8d6225ec9a50b181692490f\\\",  # noqa: E501\\n        },\\n        \\\"508debb915ede0b16dc0cff63f250bde73c5923317b44719fcfc25cc95560c44\\\": {\\n            \\\"other_headers\\\": \\\"04001608001d162104e6dffee4638f24cfa60a08ba03afe1314a3a38fc050260621281\\\",  # noqa: E501\\n            \\\"signature\\\": \\\"29d53d4e7dbea0a3efb07266d22e57cf4df7abe004453981c631245716e1b737c7a6b4ab95f42592af70be67abf56e97020e1aa1f52b49ef39b37481f05d5701\\\",  # noqa: E501\\n        },\\n    },\\n    \\\"signed\\\": {\\n        \\\"delegations\\\": {\\n            \\\"key_mgr\\\": {\\n                \\\"pubkeys\\\": [\\n                    \\\"f24c813d23a9b26be665eee5c54680c35321061b337f862385ed6d783b0bedb0\\\"\\n                ],\\n                \\\"threshold\\\": 1,\\n            },\\n            \\\"root\\\": {\\n                \\\"pubkeys\\\": [\\n                    \\\"668a3217d72d4064edb16648435dc4a3e09a172ecee45dcab1464dcd2f402ec6\\\",\\n                    \\\"508debb915ede0b16dc0cff63f250bde73c5923317b44719fcfc25cc95560c44\\\",\\n                    \\\"6d4d5888398ad77465e9fd53996309187723e16509144aa6733015c960378e7a\\\",\\n                    \\\"e0c88b4c0721bd451b7e720dfb0d0bb6b3853f0cbcf5570edd73367d0841be51\\\",\\n                ],\\n                \\\"threshold\\\": 2,\\n            },\\n        },\\n        \\\"expiration\\\": \\\"2022-10-31T18:00:00Z\\\",\\n        \\\"metadata_spec_version\\\": \\\"0.6.0\\\",\\n        \\\"timestamp\\\": \\\"2021-03-26T00:00:00Z\\\",\\n        \\\"type\\\": \\\"root\\\",\\n        \\\"version\\\": 1,\\n    },\\n}\\n\\nKEY_MGR_FILE = \\\"key_mgr.json\\\"\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Tools for managing the packages installed within an environment.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport json\\nimport os\\nimport re\\nfrom logging import getLogger\\nfrom os.path import basename, lexists\\nfrom pathlib import Path\\n\\nfrom ..auxlib.exceptions import ValidationError\\nfrom ..base.constants import (\\n    CONDA_ENV_VARS_UNSET_VAR,\\n    CONDA_PACKAGE_EXTENSIONS,\\n    PREFIX_MAGIC_FILE,\\n    PREFIX_STATE_FILE,\\n)\\nfrom ..base.context import context\\nfrom ..common.constants import NULL\\nfrom ..common.io import time_recorder\\nfrom ..common.path import get_python_site_packages_short_path, win_path_ok\\nfrom ..common.pkg_formats.python import get_site_packages_anchor_files\\nfrom ..common.serialize import json_load\\nfrom ..common.url import mask_anaconda_token\\nfrom ..common.url import remove_auth as url_remove_auth\\nfrom ..deprecations import deprecated\\nfrom ..exceptions import (\\n    BasicClobberError,\\n    CondaDependencyError,\\n    CorruptedEnvironmentError,\\n    maybe_raise,\\n)\\nfrom ..gateways.disk.create import write_as_json_to_file\\nfrom ..gateways.disk.delete import rm_rf\\nfrom ..gateways.disk.read import read_python_record\\nfrom ..gateways.disk.test import file_path_is_writable\\nfrom ..models.match_spec import MatchSpec\\nfrom ..models.prefix_graph import PrefixGraph\\nfrom ..models.records import PackageRecord, PrefixRecord\\n\\nlog = getLogger(__name__)\\n\\n\\nclass PrefixDataType(type):\\n    \\\"\\\"\\\"Basic caching of PrefixData instance objects.\\\"\\\"\\\"\\n\\n    def __call__(\\n        cls,\\n        prefix_path: str | os.PathLike | Path,\\n        pip_interop_enabled: bool | None = None,\\n    ):\\n        if isinstance(prefix_path, PrefixData):\\n            return prefix_path\\n        elif (prefix_path := Path(prefix_path)) in PrefixData._cache_:\\n            return PrefixData._cache_[prefix_path]\\n        else:\\n            prefix_data_instance = super().__call__(prefix_path, pip_interop_enabled)\\n            PrefixData._cache_[prefix_path] = prefix_data_instance\\n            return prefix_data_instance\\n\\n\\nclass PrefixData(metaclass=PrefixDataType):\\n    _cache_: dict[Path, PrefixData] = {}\\n\\n    def __init__(\\n        self,\\n        prefix_path: Path,\\n        pip_interop_enabled: bool | None = None,\\n    ):\\n        # pip_interop_enabled is a temporary parameter; DO NOT USE\\n        # TODO: when removing pip_interop_enabled, also remove from meta class\\n        self.prefix_path = prefix_path\\n        self.__prefix_records = None\\n        self.__is_writable = NULL\\n        self._pip_interop_enabled = (\\n            pip_interop_enabled\\n            if pip_interop_enabled is not None\\n            else context.pip_interop_enabled\\n        )\\n\\n    @time_recorder(module_name=__name__)\\n    def load(self):\\n        self.__prefix_records = {}\\n        _conda_meta_dir = self.prefix_path / \\\"conda-meta\\\"\\n        if lexists(_conda_meta_dir):\\n            conda_meta_json_paths = (\\n                p\\n                for p in (entry.path for entry in os.scandir(_conda_meta_dir))\\n                if p[-5:] == \\\".json\\\"\\n            )\\n            for meta_file in conda_meta_json_paths:\\n                self._load_single_record(meta_file)\\n        if self._pip_interop_enabled:\\n            self._load_site_packages()\\n\\n    def reload(self):\\n        self.load()\\n        return self\\n\\n    def _get_json_fn(self, prefix_record):\\n        fn = prefix_record.fn\\n        known_ext = False\\n        # .dist-info is for things installed by pip\\n        for ext in CONDA_PACKAGE_EXTENSIONS + (\\\".dist-info\\\",):\\n            if fn.endswith(ext):\\n                fn = fn.replace(ext, \\\"\\\")\\n                known_ext = True\\n        if not known_ext:\\n            raise ValueError(\\n                f\\\"Attempted to make prefix record for unknown package type: {fn}\\\"\\n            )\\n        return fn + \\\".json\\\"\\n\\n    def insert(self, prefix_record, remove_auth=True):\\n        assert prefix_record.name not in self._prefix_records, (\\n            f\\\"Prefix record insertion error: a record with name {prefix_record.name} already exists \\\"\\n            \\\"in the prefix. This is a bug in conda. Please report it at \\\"\\n            \\\"https://github.com/conda/conda/issues\\\"\\n        )\\n\\n        prefix_record_json_path = (\\n            self.prefix_path / \\\"conda-meta\\\" / self._get_json_fn(prefix_record)\\n        )\\n        if lexists(prefix_record_json_path):\\n            maybe_raise(\\n                BasicClobberError(\\n                    source_path=None,\\n                    target_path=prefix_record_json_path,\\n                    context=context,\\n                ),\\n                context,\\n            )\\n            rm_rf(prefix_record_json_path)\\n        if remove_auth:\\n            prefix_record_json = prefix_record.dump()\\n            prefix_record_json[\\\"url\\\"] = url_remove_auth(\\n                mask_anaconda_token(prefix_record.url)\\n            )\\n        else:\\n            prefix_record_json = prefix_record\\n        write_as_json_to_file(prefix_record_json_path, prefix_record_json)\\n\\n        self._prefix_records[prefix_record.name] = prefix_record\\n\\n    def remove(self, package_name):\\n        assert package_name in self._prefix_records\\n\\n        prefix_record = self._prefix_records[package_name]\\n\\n        prefix_record_json_path = (\\n            self.prefix_path / \\\"conda-meta\\\" / self._get_json_fn(prefix_record)\\n        )\\n        if self.is_writable:\\n            rm_rf(prefix_record_json_path)\\n\\n        del self._prefix_records[package_name]\\n\\n    def get(self, package_name, default=NULL):\\n        try:\\n            return self._prefix_records[package_name]\\n        except KeyError:\\n            if default is not NULL:\\n                return default\\n            else:\\n                raise\\n\\n    def iter_records(self):\\n        return iter(self._prefix_records.values())\\n\\n    def iter_records_sorted(self):\\n        prefix_graph = PrefixGraph(self.iter_records())\\n        return iter(prefix_graph.graph)\\n\\n    def all_subdir_urls(self):\\n        subdir_urls = set()\\n        for prefix_record in self.iter_records():\\n            subdir_url = prefix_record.channel.subdir_url\\n            if subdir_url and subdir_url not in subdir_urls:\\n                log.debug(\\\"adding subdir url %s for %s\\\", subdir_url, prefix_record)\\n                subdir_urls.add(subdir_url)\\n        return subdir_urls\\n\\n    def query(self, package_ref_or_match_spec):\\n        # returns a generator\\n        param = package_ref_or_match_spec\\n        if isinstance(param, str):\\n            param = MatchSpec(param)\\n        if isinstance(param, MatchSpec):\\n            return (\\n                prefix_rec\\n                for prefix_rec in self.iter_records()\\n                if param.match(prefix_rec)\\n            )\\n        else:\\n            assert isinstance(param, PackageRecord)\\n            return (\\n                prefix_rec for prefix_rec in self.iter_records() if prefix_rec == param\\n            )\\n\\n    @property\\n    def _prefix_records(self):\\n        return self.__prefix_records or self.load() or self.__prefix_records\\n\\n    def _load_single_record(self, prefix_record_json_path):\\n        log.debug(\\\"loading prefix record %s\\\", prefix_record_json_path)\\n        with open(prefix_record_json_path) as fh:\\n            try:\\n                json_data = json_load(fh.read())\\n            except (UnicodeDecodeError, json.JSONDecodeError):\\n                # UnicodeDecodeError: catch horribly corrupt files\\n                # JSONDecodeError: catch bad json format files\\n                raise CorruptedEnvironmentError(\\n                    self.prefix_path, prefix_record_json_path\\n                )\\n\\n            # TODO: consider, at least in memory, storing prefix_record_json_path as part\\n            #       of PrefixRecord\\n            prefix_record = PrefixRecord(**json_data)\\n\\n            # check that prefix record json filename conforms to name-version-build\\n            # apparently implemented as part of #2638 to resolve #2599\\n            try:\\n                n, v, b = basename(prefix_record_json_path)[:-5].rsplit(\\\"-\\\", 2)\\n                if (n, v, b) != (\\n                    prefix_record.name,\\n                    prefix_record.version,\\n                    prefix_record.build,\\n                ):\\n                    raise ValueError()\\n            except ValueError:\\n                log.warning(\\n                    \\\"Ignoring malformed prefix record at: %s\\\", prefix_record_json_path\\n                )\\n                # TODO: consider just deleting here this record file in the future\\n                return\\n\\n            self.__prefix_records[prefix_record.name] = prefix_record\\n\\n    @property\\n    def is_writable(self):\\n        if self.__is_writable == NULL:\\n            test_path = self.prefix_path / PREFIX_MAGIC_FILE\\n            if not test_path.is_file():\\n                is_writable = None\\n            else:\\n                is_writable = file_path_is_writable(test_path)\\n            self.__is_writable = is_writable\\n        return self.__is_writable\\n\\n    @deprecated(\\\"24.3\\\", \\\"24.9\\\")\\n    def _has_python(self):\\n        return \\\"python\\\" in self._prefix_records\\n\\n    @property\\n    def _python_pkg_record(self):\\n        \\\"\\\"\\\"Return the prefix record for the package python.\\\"\\\"\\\"\\n        return next(\\n            (\\n                prefix_record\\n                for prefix_record in self.__prefix_records.values()\\n                if prefix_record.name == \\\"python\\\"\\n            ),\\n            None,\\n        )\\n\\n    def _load_site_packages(self):\\n        \\\"\\\"\\\"\\n        Load non-conda-installed python packages in the site-packages of the prefix.\\n\\n        Python packages not handled by conda are installed via other means,\\n        like using pip or using python setup.py develop for local development.\\n\\n        Packages found that are not handled by conda are converted into a\\n        prefix record and handled in memory.\\n\\n        Packages clobbering conda packages (i.e. the conda-meta record) are\\n        removed from the in memory representation.\\n        \\\"\\\"\\\"\\n        python_pkg_record = self._python_pkg_record\\n\\n        if not python_pkg_record:\\n            return {}\\n\\n        site_packages_dir = get_python_site_packages_short_path(\\n            python_pkg_record.version\\n        )\\n        site_packages_path = self.prefix_path / win_path_ok(site_packages_dir)\\n\\n        if not site_packages_path.is_dir():\\n            return {}\\n\\n        # Get anchor files for corresponding conda (handled) python packages\\n        prefix_graph = PrefixGraph(self.iter_records())\\n        python_records = prefix_graph.all_descendants(python_pkg_record)\\n        conda_python_packages = get_conda_anchor_files_and_records(\\n            site_packages_dir, python_records\\n        )\\n\\n        # Get all anchor files and compare against conda anchor files to find clobbered conda\\n        # packages and python packages installed via other means (not handled by conda)\\n        sp_anchor_files = get_site_packages_anchor_files(\\n            site_packages_path, site_packages_dir\\n        )\\n        conda_anchor_files = set(conda_python_packages)\\n        clobbered_conda_anchor_files = conda_anchor_files - sp_anchor_files\\n        non_conda_anchor_files = sp_anchor_files - conda_anchor_files\\n\\n        # If there's a mismatch for anchor files between what conda expects for a package\\n        # based on conda-meta, and for what is actually in site-packages, then we'll delete\\n        # the in-memory record for the conda package.  In the future, we should consider\\n        # also deleting the record on disk in the conda-meta/ directory.\\n        for conda_anchor_file in clobbered_conda_anchor_files:\\n            prefix_rec = self._prefix_records.pop(\\n                conda_python_packages[conda_anchor_file].name\\n            )\\n            try:\\n                extracted_package_dir = basename(prefix_rec.extracted_package_dir)\\n            except AttributeError:\\n                extracted_package_dir = \\\"-\\\".join(\\n                    (prefix_rec.name, prefix_rec.version, prefix_rec.build)\\n                )\\n            prefix_rec_json_path = (\\n                self.prefix_path / \\\"conda-meta\\\" / f\\\"{extracted_package_dir}.json\\\"\\n            )\\n            try:\\n                rm_rf(prefix_rec_json_path)\\n            except OSError:\\n                log.debug(\\n                    \\\"stale information, but couldn't remove: %s\\\", prefix_rec_json_path\\n                )\\n            else:\\n                log.debug(\\\"removed due to stale information: %s\\\", prefix_rec_json_path)\\n\\n        # Create prefix records for python packages not handled by conda\\n        new_packages = {}\\n        for af in non_conda_anchor_files:\\n            try:\\n                python_record = read_python_record(\\n                    self.prefix_path, af, python_pkg_record.version\\n                )\\n            except OSError as e:\\n                log.info(\\n                    \\\"Python record ignored for anchor path '%s'\\\\n  due to %s\\\", af, e\\n                )\\n                continue\\n            except ValidationError:\\n                import sys\\n\\n                exc_type, exc_value, exc_traceback = sys.exc_info()\\n                import traceback\\n\\n                tb = traceback.format_exception(exc_type, exc_value, exc_traceback)\\n                log.warning(\\n                    \\\"Problem reading non-conda package record at %s. Please verify that you \\\"\\n                    \\\"still need this, and if so, that this is still installed correctly. \\\"\\n                    \\\"Reinstalling this package may help.\\\",\\n                    af,\\n                )\\n                log.debug(\\\"ValidationError: \\\\n%s\\\\n\\\", \\\"\\\\n\\\".join(tb))\\n                continue\\n            if not python_record:\\n                continue\\n            self.__prefix_records[python_record.name] = python_record\\n            new_packages[python_record.name] = python_record\\n\\n        return new_packages\\n\\n    def _get_environment_state_file(self):\\n        env_vars_file = self.prefix_path / PREFIX_STATE_FILE\\n        if lexists(env_vars_file):\\n            with open(env_vars_file) as f:\\n                prefix_state = json.loads(f.read())\\n        else:\\n            prefix_state = {}\\n        return prefix_state\\n\\n    def _write_environment_state_file(self, state):\\n        env_vars_file = self.prefix_path / PREFIX_STATE_FILE\\n        env_vars_file.write_text(\\n            json.dumps(state, ensure_ascii=False, default=lambda x: x.__dict__)\\n        )\\n\\n    def get_environment_env_vars(self):\\n        prefix_state = self._get_environment_state_file()\\n        env_vars_all = dict(prefix_state.get(\\\"env_vars\\\", {}))\\n        env_vars = {\\n            k: v for k, v in env_vars_all.items() if v != CONDA_ENV_VARS_UNSET_VAR\\n        }\\n        return env_vars\\n\\n    def set_environment_env_vars(self, env_vars):\\n        env_state_file = self._get_environment_state_file()\\n        current_env_vars = env_state_file.get(\\\"env_vars\\\")\\n        if current_env_vars:\\n            current_env_vars.update(env_vars)\\n        else:\\n            env_state_file[\\\"env_vars\\\"] = env_vars\\n        self._write_environment_state_file(env_state_file)\\n        return env_state_file.get(\\\"env_vars\\\")\\n\\n    def unset_environment_env_vars(self, env_vars):\\n        env_state_file = self._get_environment_state_file()\\n        current_env_vars = env_state_file.get(\\\"env_vars\\\")\\n        if current_env_vars:\\n            for env_var in env_vars:\\n                if env_var in current_env_vars.keys():\\n                    current_env_vars[env_var] = CONDA_ENV_VARS_UNSET_VAR\\n            self._write_environment_state_file(env_state_file)\\n        return env_state_file.get(\\\"env_vars\\\")\\n\\n\\ndef get_conda_anchor_files_and_records(site_packages_short_path, python_records):\\n    \\\"\\\"\\\"Return the anchor files for the conda records of python packages.\\\"\\\"\\\"\\n    anchor_file_endings = (\\\".egg-info/PKG-INFO\\\", \\\".dist-info/RECORD\\\", \\\".egg-info\\\")\\n    conda_python_packages = {}\\n\\n    matcher = re.compile(\\n        r\\\"^{}/[^/]+(?:{})$\\\".format(\\n            re.escape(site_packages_short_path),\\n            r\\\"|\\\".join(re.escape(fn) for fn in anchor_file_endings),\\n        )\\n    ).match\\n\\n    for prefix_record in python_records:\\n        anchor_paths = tuple(fpath for fpath in prefix_record.files if matcher(fpath))\\n        if len(anchor_paths) > 1:\\n            anchor_path = sorted(anchor_paths, key=len)[0]\\n            log.info(\\n                \\\"Package %s has multiple python anchor files.\\\\n  Using %s\\\",\\n                prefix_record.record_id(),\\n                anchor_path,\\n            )\\n            conda_python_packages[anchor_path] = prefix_record\\n        elif anchor_paths:\\n            conda_python_packages[anchor_paths[0]] = prefix_record\\n\\n    return conda_python_packages\\n\\n\\ndef get_python_version_for_prefix(prefix):\\n    # returns a string e.g. \\\"2.7\\\", \\\"3.4\\\", \\\"3.5\\\" or None\\n    py_record_iter = (\\n        rcrd for rcrd in PrefixData(prefix).iter_records() if rcrd.name == \\\"python\\\"\\n    )\\n    record = next(py_record_iter, None)\\n    if record is None:\\n        return None\\n    next_record = next(py_record_iter, None)\\n    if next_record is not None:\\n        raise CondaDependencyError(f\\\"multiple python records found in prefix {prefix}\\\")\\n    elif record.version[3].isdigit():\\n        return record.version[:4]\\n    else:\\n        return record.version[:3]\\n\\n\\ndef delete_prefix_from_linked_data(path: str | os.PathLike | Path) -> bool:\\n    \\\"\\\"\\\"Here, path may be a complete prefix or a dist inside a prefix\\\"\\\"\\\"\\n    path = Path(path)\\n    for prefix in sorted(PrefixData._cache_, reverse=True):\\n        try:\\n            path.relative_to(prefix)\\n            del PrefixData._cache_[prefix]\\n            return True\\n        except ValueError:\\n            # ValueError: path is not relative to prefix\\n            continue\\n    return False\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"The classic solver implementation.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport copy\\nimport sys\\nfrom itertools import chain\\nfrom logging import DEBUG, getLogger\\nfrom os.path import exists, join\\nfrom textwrap import dedent\\nfrom typing import TYPE_CHECKING\\n\\nfrom boltons.setutils import IndexedSet\\n\\nfrom .. import CondaError\\nfrom .. import __version__ as CONDA_VERSION\\nfrom ..auxlib.decorators import memoizedproperty\\nfrom ..auxlib.ish import dals\\nfrom ..base.constants import REPODATA_FN, UNKNOWN_CHANNEL, DepsModifier, UpdateModifier\\nfrom ..base.context import context\\nfrom ..common.constants import NULL, TRACE\\nfrom ..common.io import Spinner, dashlist, time_recorder\\nfrom ..common.iterators import groupby_to_dict as groupby\\nfrom ..common.path import get_major_minor_version, paths_equal\\nfrom ..exceptions import (\\n    PackagesNotFoundError,\\n    SpecsConfigurationConflictError,\\n    UnsatisfiableError,\\n)\\nfrom ..history import History\\nfrom ..models.channel import Channel\\nfrom ..models.enums import NoarchType\\nfrom ..models.match_spec import MatchSpec\\nfrom ..models.prefix_graph import PrefixGraph\\nfrom ..models.version import VersionOrder\\nfrom ..resolve import Resolve\\nfrom .index import _supplement_index_with_system, get_reduced_index\\nfrom .link import PrefixSetup, UnlinkLinkTransaction\\nfrom .prefix_data import PrefixData\\nfrom .subdir_data import SubdirData\\n\\ntry:\\n    from frozendict import frozendict\\nexcept ImportError:\\n    from ..auxlib.collection import frozendict\\n\\nif TYPE_CHECKING:\\n    from typing import Iterable\\n\\n    from ..models.records import PackageRecord\\n\\nlog = getLogger(__name__)\\n\\n\\nclass Solver:\\n    \\\"\\\"\\\"\\n    A high-level API to conda's solving logic. Three public methods are provided to access a\\n    solution in various forms.\\n\\n      * :meth:`solve_final_state`\\n      * :meth:`solve_for_diff`\\n      * :meth:`solve_for_transaction`\\n    \\\"\\\"\\\"\\n\\n    def __init__(\\n        self,\\n        prefix: str,\\n        channels: Iterable[Channel],\\n        subdirs: Iterable[str] = (),\\n        specs_to_add: Iterable[MatchSpec] = (),\\n        specs_to_remove: Iterable[MatchSpec] = (),\\n        repodata_fn: str = REPODATA_FN,\\n        command=NULL,\\n    ):\\n        \\\"\\\"\\\"\\n        Args:\\n            prefix (str):\\n                The conda prefix / environment location for which the :class:`Solver`\\n                is being instantiated.\\n            channels (Sequence[:class:`Channel`]):\\n                A prioritized list of channels to use for the solution.\\n            subdirs (Sequence[str]):\\n                A prioritized list of subdirs to use for the solution.\\n            specs_to_add (set[:class:`MatchSpec`]):\\n                The set of package specs to add to the prefix.\\n            specs_to_remove (set[:class:`MatchSpec`]):\\n                The set of package specs to remove from the prefix.\\n\\n        \\\"\\\"\\\"\\n        self.prefix = prefix\\n        self._channels = channels or context.channels\\n        self.channels = IndexedSet(Channel(c) for c in self._channels)\\n        self.subdirs = tuple(s for s in subdirs or context.subdirs)\\n        self.specs_to_add = frozenset(MatchSpec.merge(s for s in specs_to_add))\\n        self.specs_to_add_names = frozenset(_.name for _ in self.specs_to_add)\\n        self.specs_to_remove = frozenset(MatchSpec.merge(s for s in specs_to_remove))\\n        self.neutered_specs = ()\\n        self._command = command\\n\\n        assert all(s in context.known_subdirs for s in self.subdirs)\\n        self._repodata_fn = repodata_fn\\n        self._index = None\\n        self._r = None\\n        self._prepared = False\\n        self._pool_cache = {}\\n\\n    def solve_for_transaction(\\n        self,\\n        update_modifier=NULL,\\n        deps_modifier=NULL,\\n        prune=NULL,\\n        ignore_pinned=NULL,\\n        force_remove=NULL,\\n        force_reinstall=NULL,\\n        should_retry_solve=False,\\n    ):\\n        \\\"\\\"\\\"Gives an UnlinkLinkTransaction instance that can be used to execute the solution\\n        on an environment.\\n\\n        Args:\\n            deps_modifier (DepsModifier):\\n                See :meth:`solve_final_state`.\\n            prune (bool):\\n                See :meth:`solve_final_state`.\\n            ignore_pinned (bool):\\n                See :meth:`solve_final_state`.\\n            force_remove (bool):\\n                See :meth:`solve_final_state`.\\n            force_reinstall (bool):\\n                See :meth:`solve_for_diff`.\\n            should_retry_solve (bool):\\n                See :meth:`solve_final_state`.\\n\\n        Returns:\\n            UnlinkLinkTransaction:\\n\\n        \\\"\\\"\\\"\\n        if self.prefix == context.root_prefix and context.enable_private_envs:\\n            # This path has the ability to generate a multi-prefix transaction. The basic logic\\n            # is in the commented out get_install_transaction() function below. Exercised at\\n            # the integration level in the PrivateEnvIntegrationTests in test_create.py.\\n            raise NotImplementedError()\\n\\n        # run pre-solve processes here before solving for a solution\\n        context.plugin_manager.invoke_pre_solves(\\n            self.specs_to_add,\\n            self.specs_to_remove,\\n        )\\n\\n        unlink_precs, link_precs = self.solve_for_diff(\\n            update_modifier,\\n            deps_modifier,\\n            prune,\\n            ignore_pinned,\\n            force_remove,\\n            force_reinstall,\\n            should_retry_solve,\\n        )\\n        # TODO: Only explicitly requested remove and update specs are being included in\\n        #   History right now. Do we need to include other categories from the solve?\\n\\n        # run post-solve processes here before performing the transaction\\n        context.plugin_manager.invoke_post_solves(\\n            self._repodata_fn,\\n            unlink_precs,\\n            link_precs,\\n        )\\n\\n        self._notify_conda_outdated(link_precs)\\n        return UnlinkLinkTransaction(\\n            PrefixSetup(\\n                self.prefix,\\n                unlink_precs,\\n                link_precs,\\n                self.specs_to_remove,\\n                self.specs_to_add,\\n                self.neutered_specs,\\n            )\\n        )\\n\\n    def solve_for_diff(\\n        self,\\n        update_modifier=NULL,\\n        deps_modifier=NULL,\\n        prune=NULL,\\n        ignore_pinned=NULL,\\n        force_remove=NULL,\\n        force_reinstall=NULL,\\n        should_retry_solve=False,\\n    ) -> tuple[tuple[PackageRecord, ...], tuple[PackageRecord, ...]]:\\n        \\\"\\\"\\\"Gives the package references to remove from an environment, followed by\\n        the package references to add to an environment.\\n\\n        Args:\\n            deps_modifier (DepsModifier):\\n                See :meth:`solve_final_state`.\\n            prune (bool):\\n                See :meth:`solve_final_state`.\\n            ignore_pinned (bool):\\n                See :meth:`solve_final_state`.\\n            force_remove (bool):\\n                See :meth:`solve_final_state`.\\n            force_reinstall (bool):\\n                For requested specs_to_add that are already satisfied in the environment,\\n                    instructs the solver to remove the package and spec from the environment,\\n                    and then add it back--possibly with the exact package instance modified,\\n                    depending on the spec exactness.\\n            should_retry_solve (bool):\\n                See :meth:`solve_final_state`.\\n\\n        Returns:\\n            tuple[PackageRef], tuple[PackageRef]:\\n                A two-tuple of PackageRef sequences.  The first is the group of packages to\\n                remove from the environment, in sorted dependency order from leaves to roots.\\n                The second is the group of packages to add to the environment, in sorted\\n                dependency order from roots to leaves.\\n\\n        \\\"\\\"\\\"\\n        final_precs = self.solve_final_state(\\n            update_modifier,\\n            deps_modifier,\\n            prune,\\n            ignore_pinned,\\n            force_remove,\\n            should_retry_solve,\\n        )\\n        unlink_precs, link_precs = diff_for_unlink_link_precs(\\n            self.prefix, final_precs, self.specs_to_add, force_reinstall\\n        )\\n\\n        # assert that all unlink_precs are manageable\\n        unmanageable = groupby(lambda prec: prec.is_unmanageable, unlink_precs).get(\\n            True\\n        )\\n        if unmanageable:\\n            raise RuntimeError(\\n                f\\\"Cannot unlink unmanageable packages:{dashlist(prec.record_id() for prec in unmanageable)}\\\"\\n            )\\n\\n        return unlink_precs, link_precs\\n\\n    def solve_final_state(\\n        self,\\n        update_modifier=NULL,\\n        deps_modifier=NULL,\\n        prune=NULL,\\n        ignore_pinned=NULL,\\n        force_remove=NULL,\\n        should_retry_solve=False,\\n    ):\\n        \\\"\\\"\\\"Gives the final, solved state of the environment.\\n\\n        Args:\\n            update_modifier (UpdateModifier):\\n                An optional flag directing how updates are handled regarding packages already\\n                existing in the environment.\\n\\n            deps_modifier (DepsModifier):\\n                An optional flag indicating special solver handling for dependencies. The\\n                default solver behavior is to be as conservative as possible with dependency\\n                updates (in the case the dependency already exists in the environment), while\\n                still ensuring all dependencies are satisfied.  Options include\\n                * NO_DEPS\\n                * ONLY_DEPS\\n                * UPDATE_DEPS\\n                * UPDATE_DEPS_ONLY_DEPS\\n                * FREEZE_INSTALLED\\n            prune (bool):\\n                If ``True``, the solution will not contain packages that were\\n                previously brought into the environment as dependencies but are no longer\\n                required as dependencies and are not user-requested.\\n            ignore_pinned (bool):\\n                If ``True``, the solution will ignore pinned package configuration\\n                for the prefix.\\n            force_remove (bool):\\n                Forces removal of a package without removing packages that depend on it.\\n            should_retry_solve (bool):\\n                Indicates whether this solve will be retried. This allows us to control\\n                whether to call find_conflicts (slow) in ssc.r.solve\\n\\n        Returns:\\n            tuple[PackageRef]:\\n                In sorted dependency order from roots to leaves, the package references for\\n                the solved state of the environment.\\n\\n        \\\"\\\"\\\"\\n        if prune and update_modifier == UpdateModifier.FREEZE_INSTALLED:\\n            update_modifier = NULL\\n        if update_modifier is NULL:\\n            update_modifier = context.update_modifier\\n        else:\\n            update_modifier = UpdateModifier(str(update_modifier).lower())\\n        if deps_modifier is NULL:\\n            deps_modifier = context.deps_modifier\\n        else:\\n            deps_modifier = DepsModifier(str(deps_modifier).lower())\\n        ignore_pinned = (\\n            context.ignore_pinned if ignore_pinned is NULL else ignore_pinned\\n        )\\n        force_remove = context.force_remove if force_remove is NULL else force_remove\\n\\n        log.debug(\\n            \\\"solving prefix %s\\\\n\\\"\\n            \\\"  specs_to_remove: %s\\\\n\\\"\\n            \\\"  specs_to_add: %s\\\\n\\\"\\n            \\\"  prune: %s\\\",\\n            self.prefix,\\n            self.specs_to_remove,\\n            self.specs_to_add,\\n            prune,\\n        )\\n\\n        retrying = hasattr(self, \\\"ssc\\\")\\n\\n        if not retrying:\\n            ssc = SolverStateContainer(\\n                self.prefix,\\n                update_modifier,\\n                deps_modifier,\\n                prune,\\n                ignore_pinned,\\n                force_remove,\\n                should_retry_solve,\\n            )\\n            self.ssc = ssc\\n        else:\\n            ssc = self.ssc\\n            ssc.update_modifier = update_modifier\\n            ssc.deps_modifier = deps_modifier\\n            ssc.should_retry_solve = should_retry_solve\\n\\n        # force_remove is a special case where we return early\\n        if self.specs_to_remove and force_remove:\\n            if self.specs_to_add:\\n                raise NotImplementedError()\\n            solution = tuple(\\n                prec\\n                for prec in ssc.solution_precs\\n                if not any(spec.match(prec) for spec in self.specs_to_remove)\\n            )\\n            return IndexedSet(PrefixGraph(solution).graph)\\n\\n        # Check if specs are satisfied by current environment. If they are, exit early.\\n        if (\\n            update_modifier == UpdateModifier.SPECS_SATISFIED_SKIP_SOLVE\\n            and not self.specs_to_remove\\n            and not prune\\n        ):\\n            for spec in self.specs_to_add:\\n                if not next(ssc.prefix_data.query(spec), None):\\n                    break\\n            else:\\n                # All specs match a package in the current environment.\\n                # Return early, with a solution that should just be PrefixData().iter_records()\\n                return IndexedSet(PrefixGraph(ssc.solution_precs).graph)\\n\\n        if not ssc.r:\\n            with Spinner(\\n                f\\\"Collecting package metadata ({self._repodata_fn})\\\",\\n                not context.verbose and not context.quiet and not retrying,\\n                context.json,\\n            ):\\n                ssc = self._collect_all_metadata(ssc)\\n\\n        if should_retry_solve and update_modifier == UpdateModifier.FREEZE_INSTALLED:\\n            fail_message = (\\n                \\\"unsuccessful initial attempt using frozen solve. Retrying\\\"\\n                \\\" with flexible solve.\\\\n\\\"\\n            )\\n        elif self._repodata_fn != REPODATA_FN:\\n            fail_message = (\\n                f\\\"unsuccessful attempt using repodata from {self._repodata_fn}, retrying\\\"\\n                \\\" with next repodata source.\\\\n\\\"\\n            )\\n        else:\\n            fail_message = \\\"failed\\\\n\\\"\\n\\n        with Spinner(\\n            \\\"Solving environment\\\",\\n            not context.verbose and not context.quiet,\\n            context.json,\\n            fail_message=fail_message,\\n        ):\\n            ssc = self._remove_specs(ssc)\\n            ssc = self._add_specs(ssc)\\n            solution_precs = copy.copy(ssc.solution_precs)\\n\\n            pre_packages = self.get_request_package_in_solution(\\n                ssc.solution_precs, ssc.specs_map\\n            )\\n            ssc = self._find_inconsistent_packages(ssc)\\n            # this will prune precs that are deps of precs that get removed due to conflicts\\n            ssc = self._run_sat(ssc)\\n            post_packages = self.get_request_package_in_solution(\\n                ssc.solution_precs, ssc.specs_map\\n            )\\n\\n            if ssc.update_modifier == UpdateModifier.UPDATE_SPECS:\\n                constrained = self.get_constrained_packages(\\n                    pre_packages, post_packages, ssc.index.keys()\\n                )\\n                if len(constrained) > 0:\\n                    for spec in constrained:\\n                        self.determine_constricting_specs(spec, ssc.solution_precs)\\n\\n            # if there were any conflicts, we need to add their orphaned deps back in\\n            if ssc.add_back_map:\\n                orphan_precs = (\\n                    set(solution_precs)\\n                    - set(ssc.solution_precs)\\n                    - set(ssc.add_back_map)\\n                )\\n                solution_prec_names = [_.name for _ in ssc.solution_precs]\\n                ssc.solution_precs.extend(\\n                    [\\n                        _\\n                        for _ in orphan_precs\\n                        if _.name not in ssc.specs_map\\n                        and _.name not in solution_prec_names\\n                    ]\\n                )\\n\\n            ssc = self._post_sat_handling(ssc)\\n\\n        time_recorder.log_totals()\\n\\n        ssc.solution_precs = IndexedSet(PrefixGraph(ssc.solution_precs).graph)\\n        log.debug(\\n            \\\"solved prefix %s\\\\n  solved_linked_dists:\\\\n    %s\\\\n\\\",\\n            self.prefix,\\n            \\\"\\\\n    \\\".join(prec.dist_str() for prec in ssc.solution_precs),\\n        )\\n\\n        return ssc.solution_precs\\n\\n    def determine_constricting_specs(self, spec, solution_precs):\\n        highest_version = [\\n            VersionOrder(sp.version) for sp in solution_precs if sp.name == spec.name\\n        ][0]\\n        constricting = []\\n        for prec in solution_precs:\\n            if any(j for j in prec.depends if spec.name in j):\\n                for dep in prec.depends:\\n                    m_dep = MatchSpec(dep)\\n                    if (\\n                        m_dep.name == spec.name\\n                        and m_dep.version is not None\\n                        and (m_dep.version.exact_value or \\\"<\\\" in m_dep.version.spec)\\n                    ):\\n                        if \\\",\\\" in m_dep.version.spec:\\n                            constricting.extend(\\n                                [\\n                                    (prec.name, MatchSpec(f\\\"{m_dep.name} {v}\\\"))\\n                                    for v in m_dep.version.tup\\n                                    if \\\"<\\\" in v.spec\\n                                ]\\n                            )\\n                        else:\\n                            constricting.append((prec.name, m_dep))\\n\\n        hard_constricting = [\\n            i for i in constricting if i[1].version.matcher_vo <= highest_version\\n        ]\\n        if len(hard_constricting) == 0:\\n            return None\\n\\n        print(f\\\"\\\\n\\\\nUpdating {spec.name} is constricted by \\\\n\\\")\\n        for const in hard_constricting:\\n            print(f\\\"{const[0]} -> requires {const[1]}\\\")\\n        print(\\n            \\\"\\\\nIf you are sure you want an update of your package either try \\\"\\n            \\\"`conda update --all` or install a specific version of the \\\"\\n            \\\"package you want using `conda install <pkg>=<version>`\\\\n\\\"\\n        )\\n        return hard_constricting\\n\\n    def get_request_package_in_solution(self, solution_precs, specs_map):\\n        requested_packages = {}\\n        for pkg in self.specs_to_add:\\n            update_pkg_request = pkg.name\\n\\n            requested_packages[update_pkg_request] = [\\n                (i.name, str(i.version))\\n                for i in solution_precs\\n                if i.name == update_pkg_request and i.version is not None\\n            ]\\n            requested_packages[update_pkg_request].extend(\\n                [\\n                    (v.name, str(v.version))\\n                    for k, v in specs_map.items()\\n                    if k == update_pkg_request and v.version is not None\\n                ]\\n            )\\n\\n        return requested_packages\\n\\n    def get_constrained_packages(self, pre_packages, post_packages, index_keys):\\n        update_constrained = set()\\n\\n        def empty_package_list(pkg):\\n            for k, v in pkg.items():\\n                if len(v) == 0:\\n                    return True\\n            return False\\n\\n        if empty_package_list(pre_packages) or empty_package_list(post_packages):\\n            return update_constrained\\n\\n        for pkg in self.specs_to_add:\\n            if pkg.name.startswith(\\\"__\\\"):  # ignore virtual packages\\n                continue\\n            current_version = max(i[1] for i in pre_packages[pkg.name])\\n            if current_version == max(\\n                i.version for i in index_keys if i.name == pkg.name\\n            ):\\n                continue\\n            else:\\n                if post_packages == pre_packages:\\n                    update_constrained = update_constrained | {pkg}\\n        return update_constrained\\n\\n    @time_recorder(module_name=__name__)\\n    def _collect_all_metadata(self, ssc):\\n        if ssc.prune:\\n            # When pruning DO NOT consider history of already installed packages when solving.\\n            prepared_specs = {*self.specs_to_remove, *self.specs_to_add}\\n        else:\\n            # add in historically-requested specs\\n            ssc.specs_map.update(ssc.specs_from_history_map)\\n\\n            # these are things that we want to keep even if they're not explicitly specified.  This\\n            #     is to compensate for older installers not recording these appropriately for them\\n            #     to be preserved.\\n            for pkg_name in (\\n                \\\"anaconda\\\",\\n                \\\"conda\\\",\\n                \\\"conda-build\\\",\\n                \\\"python.app\\\",\\n                \\\"console_shortcut\\\",\\n                \\\"powershell_shortcut\\\",\\n            ):\\n                if pkg_name not in ssc.specs_map and ssc.prefix_data.get(\\n                    pkg_name, None\\n                ):\\n                    ssc.specs_map[pkg_name] = MatchSpec(pkg_name)\\n\\n            # Add virtual packages so they are taken into account by the solver\\n            virtual_pkg_index = {}\\n            _supplement_index_with_system(virtual_pkg_index)\\n            virtual_pkgs = [p.name for p in virtual_pkg_index.keys()]\\n            for virtual_pkgs_name in virtual_pkgs:\\n                if virtual_pkgs_name not in ssc.specs_map:\\n                    ssc.specs_map[virtual_pkgs_name] = MatchSpec(virtual_pkgs_name)\\n\\n            for prec in ssc.prefix_data.iter_records():\\n                # first check: add everything if we have no history to work with.\\n                #    This happens with \\\"update --all\\\", for example.\\n                #\\n                # second check: add in aggressively updated packages\\n                #\\n                # third check: add in foreign stuff (e.g. from pip) into the specs\\n                #    map. We add it so that it can be left alone more. This is a\\n                #    declaration that it is manually installed, much like the\\n                #    history map. It may still be replaced if it is in conflict,\\n                #    but it is not just an indirect dep that can be pruned.\\n                if (\\n                    not ssc.specs_from_history_map\\n                    or MatchSpec(prec.name) in context.aggressive_update_packages\\n                    or prec.subdir == \\\"pypi\\\"\\n                ):\\n                    ssc.specs_map.update({prec.name: MatchSpec(prec.name)})\\n\\n            prepared_specs = {\\n                *self.specs_to_remove,\\n                *self.specs_to_add,\\n                *ssc.specs_from_history_map.values(),\\n            }\\n\\n        index, r = self._prepare(prepared_specs)\\n        ssc.set_repository_metadata(index, r)\\n        return ssc\\n\\n    def _remove_specs(self, ssc):\\n        if self.specs_to_remove:\\n            # In a previous implementation, we invoked SAT here via `r.remove()` to help with\\n            # spec removal, and then later invoking SAT again via `r.solve()`. Rather than invoking\\n            # SAT for spec removal determination, we can use the PrefixGraph and simple tree\\n            # traversal if we're careful about how we handle features. We still invoke sat via\\n            # `r.solve()` later.\\n            _track_fts_specs = (\\n                spec for spec in self.specs_to_remove if \\\"track_features\\\" in spec\\n            )\\n            feature_names = set(\\n                chain.from_iterable(\\n                    spec.get_raw_value(\\\"track_features\\\") for spec in _track_fts_specs\\n                )\\n            )\\n            graph = PrefixGraph(ssc.solution_precs, ssc.specs_map.values())\\n\\n            all_removed_records = []\\n            no_removed_records_specs = []\\n            for spec in self.specs_to_remove:\\n                # If the spec was a track_features spec, then we need to also remove every\\n                # package with a feature that matches the track_feature. The\\n                # `graph.remove_spec()` method handles that for us.\\n                log.log(TRACE, \\\"using PrefixGraph to remove records for %s\\\", spec)\\n                removed_records = graph.remove_spec(spec)\\n                if removed_records:\\n                    all_removed_records.extend(removed_records)\\n                else:\\n                    no_removed_records_specs.append(spec)\\n\\n            # ensure that each spec in specs_to_remove is actually associated with removed records\\n            unmatched_specs_to_remove = tuple(\\n                spec\\n                for spec in no_removed_records_specs\\n                if not any(spec.match(rec) for rec in all_removed_records)\\n            )\\n            if unmatched_specs_to_remove:\\n                raise PackagesNotFoundError(\\n                    tuple(sorted(str(s) for s in unmatched_specs_to_remove))\\n                )\\n\\n            for rec in all_removed_records:\\n                # We keep specs (minus the feature part) for the non provides_features packages\\n                # if they're in the history specs.  Otherwise, we pop them from the specs_map.\\n                rec_has_a_feature = set(rec.features or ()) & feature_names\\n                if rec_has_a_feature and rec.name in ssc.specs_from_history_map:\\n                    spec = ssc.specs_map.get(rec.name, MatchSpec(rec.name))\\n                    spec._match_components = frozendict(\\n                        {\\n                            key: value\\n                            for key, value in spec._match_components.items()\\n                            if key != \\\"features\\\"\\n                        }\\n                    )\\n                    ssc.specs_map[spec.name] = spec\\n                else:\\n                    ssc.specs_map.pop(rec.name, None)\\n\\n            ssc.solution_precs = tuple(graph.graph)\\n        return ssc\\n\\n    @time_recorder(module_name=__name__)\\n    def _find_inconsistent_packages(self, ssc):\\n        # We handle as best as possible environments in inconsistent states. To do this,\\n        # we remove now from consideration the set of packages causing inconsistencies,\\n        # and then we add them back in following the main SAT call.\\n        _, inconsistent_precs = ssc.r.bad_installed(ssc.solution_precs, ())\\n        if inconsistent_precs:\\n            # It is possible that the package metadata is incorrect, for example when\\n            # un-patched metadata from the Miniconda or Anaconda installer is present, see:\\n            # https://github.com/conda/conda/issues/8076\\n            # Update the metadata with information from the index and see if that makes the\\n            # environment consistent.\\n            ssc.solution_precs = tuple(ssc.index.get(k, k) for k in ssc.solution_precs)\\n            _, inconsistent_precs = ssc.r.bad_installed(ssc.solution_precs, ())\\n        if log.isEnabledFor(DEBUG):\\n            log.debug(\\n                \\\"inconsistent precs: %s\\\",\\n                dashlist(inconsistent_precs) if inconsistent_precs else \\\"None\\\",\\n            )\\n        if inconsistent_precs:\\n            print(\\n                dedent(\\n                    \\\"\\\"\\\"\\n            The environment is inconsistent, please check the package plan carefully\\n            The following packages are causing the inconsistency:\\\"\\\"\\\"\\n                ),\\n                file=sys.stderr,\\n            )\\n            print(dashlist(inconsistent_precs), file=sys.stderr)\\n            for prec in inconsistent_precs:\\n                # pop and save matching spec in specs_map\\n                spec = ssc.specs_map.pop(prec.name, None)\\n                ssc.add_back_map[prec.name] = (prec, spec)\\n                # let the package float.  This is essential to keep the package's dependencies\\n                #    in the solution\\n                ssc.specs_map[prec.name] = MatchSpec(prec.name, target=prec.dist_str())\\n                # inconsistent environments should maintain the python version\\n                # unless explicitly requested by the user. This along with the logic in\\n                # _add_specs maintains the major.minor version\\n                if prec.name == \\\"python\\\" and spec:\\n                    ssc.specs_map[\\\"python\\\"] = spec\\n            ssc.solution_precs = tuple(\\n                prec for prec in ssc.solution_precs if prec not in inconsistent_precs\\n            )\\n        return ssc\\n\\n    def _package_has_updates(self, ssc, spec, installed_pool):\\n        installed_prec = installed_pool.get(spec.name)\\n        has_update = False\\n\\n        if installed_prec:\\n            installed_prec = installed_prec[0]\\n            for prec in ssc.r.groups.get(spec.name, []):\\n                if prec.version > installed_prec.version:\\n                    has_update = True\\n                    break\\n                elif (\\n                    prec.version == installed_prec.version\\n                    and prec.build_number > installed_prec.build_number\\n                ):\\n                    has_update = True\\n                    break\\n        # let conda determine the latest version by just adding a name spec\\n        return (\\n            MatchSpec(spec.name, version=prec.version, build_number=prec.build_number)\\n            if has_update\\n            else spec\\n        )\\n\\n    def _should_freeze(\\n        self, ssc, target_prec, conflict_specs, explicit_pool, installed_pool\\n    ):\\n        # never, ever freeze anything if we have no history.\\n        if not ssc.specs_from_history_map:\\n            return False\\n        # never freeze if not in FREEZE_INSTALLED mode\\n        if ssc.update_modifier != UpdateModifier.FREEZE_INSTALLED:\\n            return False\\n\\n        # if all package specs have overlapping package choices (satisfiable in at least one way)\\n        pkg_name = target_prec.name\\n        no_conflict = pkg_name not in conflict_specs and (\\n            pkg_name not in explicit_pool or target_prec in explicit_pool[pkg_name]\\n        )\\n\\n        return no_conflict\\n\\n    def _add_specs(self, ssc):\\n        # For the remaining specs in specs_map, add target to each spec. `target` is a reference\\n        # to the package currently existing in the environment. Setting target instructs the\\n        # solver to not disturb that package if it's not necessary.\\n        # If the spec.name is being modified by inclusion in specs_to_add, we don't set `target`,\\n        # since we *want* the solver to modify/update that package.\\n        #\\n        # TLDR: when working with MatchSpec objects,\\n        #  - to minimize the version change, set MatchSpec(name=name, target=prec.dist_str())\\n        #  - to freeze the package, set all the components of MatchSpec individually\\n\\n        installed_pool = groupby(lambda x: x.name, ssc.prefix_data.iter_records())\\n\\n        # the only things we should consider freezing are things that don't conflict with the new\\n        #    specs being added.\\n        explicit_pool = ssc.r._get_package_pool(self.specs_to_add)\\n        if ssc.prune:\\n            # Ignore installed specs on prune.\\n            installed_specs = ()\\n        else:\\n            installed_specs = [\\n                record.to_match_spec() for record in ssc.prefix_data.iter_records()\\n            ]\\n\\n        conflict_specs = (\\n            ssc.r.get_conflicting_specs(installed_specs, self.specs_to_add) or tuple()\\n        )\\n        conflict_specs = {spec.name for spec in conflict_specs}\\n\\n        for pkg_name, spec in ssc.specs_map.items():\\n            matches_for_spec = tuple(\\n                prec for prec in ssc.solution_precs if spec.match(prec)\\n            )\\n            if matches_for_spec:\\n                if len(matches_for_spec) != 1:\\n                    raise CondaError(\\n                        dals(\\n                            \\\"\\\"\\\"\\n                    Conda encountered an error with your environment.  Please report an issue\\n                    at https://github.com/conda/conda/issues.  In your report, please include\\n                    the output of 'conda info' and 'conda list' for the active environment, along\\n                    with the command you invoked that resulted in this error.\\n                      pkg_name: %s\\n                      spec: %s\\n                      matches_for_spec: %s\\n                    \\\"\\\"\\\"\\n                        )\\n                        % (\\n                            pkg_name,\\n                            spec,\\n                            dashlist((str(s) for s in matches_for_spec), indent=4),\\n                        )\\n                    )\\n                target_prec = matches_for_spec[0]\\n                if target_prec.is_unmanageable:\\n                    ssc.specs_map[pkg_name] = target_prec.to_match_spec()\\n                elif MatchSpec(pkg_name) in context.aggressive_update_packages:\\n                    ssc.specs_map[pkg_name] = MatchSpec(pkg_name)\\n                elif self._should_freeze(\\n                    ssc, target_prec, conflict_specs, explicit_pool, installed_pool\\n                ):\\n                    ssc.specs_map[pkg_name] = target_prec.to_match_spec()\\n                elif pkg_name in ssc.specs_from_history_map:\\n                    ssc.specs_map[pkg_name] = MatchSpec(\\n                        ssc.specs_from_history_map[pkg_name],\\n                        target=target_prec.dist_str(),\\n                    )\\n                else:\\n                    ssc.specs_map[pkg_name] = MatchSpec(\\n                        pkg_name, target=target_prec.dist_str()\\n                    )\\n\\n        pin_overrides = set()\\n        for s in ssc.pinned_specs:\\n            if s.name in explicit_pool:\\n                if s.name not in self.specs_to_add_names and not ssc.ignore_pinned:\\n                    ssc.specs_map[s.name] = MatchSpec(s, optional=False)\\n                elif explicit_pool[s.name] & ssc.r._get_package_pool([s]).get(\\n                    s.name, set()\\n                ):\\n                    ssc.specs_map[s.name] = MatchSpec(s, optional=False)\\n                    pin_overrides.add(s.name)\\n                else:\\n                    log.warning(\\n                        \\\"pinned spec %s conflicts with explicit specs.  \\\"\\n                        \\\"Overriding pinned spec.\\\",\\n                        s,\\n                    )\\n\\n        # we want to freeze any packages in the env that are not conflicts, so that the\\n        #     solve goes faster.  This is kind of like an iterative solve, except rather\\n        #     than just providing a starting place, we are preventing some solutions.\\n        #     A true iterative solve would probably be better in terms of reaching the\\n        #     optimal output all the time.  It would probably also get rid of the need\\n        #     to retry with an unfrozen (UPDATE_SPECS) solve.\\n        if ssc.update_modifier == UpdateModifier.FREEZE_INSTALLED:\\n            precs = [\\n                _ for _ in ssc.prefix_data.iter_records() if _.name not in ssc.specs_map\\n            ]\\n            for prec in precs:\\n                if prec.name not in conflict_specs:\\n                    ssc.specs_map[prec.name] = prec.to_match_spec()\\n                else:\\n                    ssc.specs_map[prec.name] = MatchSpec(\\n                        prec.name, target=prec.to_match_spec(), optional=True\\n                    )\\n        log.debug(\\\"specs_map with targets: %s\\\", ssc.specs_map)\\n\\n        # If we're in UPDATE_ALL mode, we need to drop all the constraints attached to specs,\\n        # so they can all float and the solver can find the most up-to-date solution. In the case\\n        # of UPDATE_ALL, `specs_map` wasn't initialized with packages from the current environment,\\n        # but *only* historically-requested specs.  This lets UPDATE_ALL drop dependencies if\\n        # they're no longer needed, and their presence would otherwise prevent the updated solution\\n        # the user most likely wants.\\n        if ssc.update_modifier == UpdateModifier.UPDATE_ALL:\\n            # history is preferable because it has explicitly installed stuff in it.\\n            #   that simplifies our solution.\\n            if ssc.specs_from_history_map:\\n                ssc.specs_map = dict(\\n                    (spec, MatchSpec(spec))\\n                    if MatchSpec(spec).name not in (_.name for _ in ssc.pinned_specs)\\n                    else (MatchSpec(spec).name, ssc.specs_map[MatchSpec(spec).name])\\n                    for spec in ssc.specs_from_history_map\\n                )\\n                for prec in ssc.prefix_data.iter_records():\\n                    # treat pip-installed stuff as explicitly installed, too.\\n                    if prec.subdir == \\\"pypi\\\":\\n                        ssc.specs_map.update({prec.name: MatchSpec(prec.name)})\\n            else:\\n                ssc.specs_map = {\\n                    prec.name: (\\n                        MatchSpec(prec.name)\\n                        if prec.name not in (_.name for _ in ssc.pinned_specs)\\n                        else ssc.specs_map[prec.name]\\n                    )\\n                    for prec in ssc.prefix_data.iter_records()\\n                }\\n\\n        # ensure that our self.specs_to_add are not being held back by packages in the env.\\n        #    This factors in pins and also ignores specs from the history.  It is unfreezing only\\n        #    for the indirect specs that otherwise conflict with update of the immediate request\\n        elif ssc.update_modifier == UpdateModifier.UPDATE_SPECS:\\n            skip = lambda x: (\\n                (\\n                    x.name not in pin_overrides\\n                    and any(x.name == _.name for _ in ssc.pinned_specs)\\n                    and not ssc.ignore_pinned\\n                )\\n                or x.name in ssc.specs_from_history_map\\n            )\\n\\n            specs_to_add = tuple(\\n                self._package_has_updates(ssc, _, installed_pool)\\n                for _ in self.specs_to_add\\n                if not skip(_)\\n            )\\n            # the index is sorted, so the first record here gives us what we want.\\n            conflicts = ssc.r.get_conflicting_specs(\\n                tuple(MatchSpec(_) for _ in ssc.specs_map.values()), specs_to_add\\n            )\\n            for conflict in conflicts or ():\\n                # neuter the spec due to a conflict\\n                if (\\n                    conflict.name in ssc.specs_map\\n                    and (\\n                        # add optional because any pinned specs will include it\\n                        MatchSpec(conflict, optional=True) not in ssc.pinned_specs\\n                        or ssc.ignore_pinned\\n                    )\\n                    and conflict.name not in ssc.specs_from_history_map\\n                ):\\n                    ssc.specs_map[conflict.name] = MatchSpec(conflict.name)\\n\\n        # As a business rule, we never want to update python beyond the current minor version,\\n        # unless that's requested explicitly by the user (which we actively discourage).\\n        py_in_prefix = any(_.name == \\\"python\\\" for _ in ssc.solution_precs)\\n        py_requested_explicitly = any(s.name == \\\"python\\\" for s in self.specs_to_add)\\n        if py_in_prefix and not py_requested_explicitly:\\n            python_prefix_rec = ssc.prefix_data.get(\\\"python\\\")\\n            freeze_installed = ssc.update_modifier == UpdateModifier.FREEZE_INSTALLED\\n            if \\\"python\\\" not in conflict_specs and freeze_installed:\\n                ssc.specs_map[\\\"python\\\"] = python_prefix_rec.to_match_spec()\\n            else:\\n                # will our prefix record conflict with any explicit spec?  If so, don't add\\n                #     anything here - let python float when it hasn't been explicitly specified\\n                python_spec = ssc.specs_map.get(\\\"python\\\", MatchSpec(\\\"python\\\"))\\n                if not python_spec.get(\\\"version\\\"):\\n                    pinned_version = (\\n                        get_major_minor_version(python_prefix_rec.version) + \\\".*\\\"\\n                    )\\n                    python_spec = MatchSpec(python_spec, version=pinned_version)\\n\\n                spec_set = (python_spec,) + tuple(self.specs_to_add)\\n                if ssc.r.get_conflicting_specs(spec_set, self.specs_to_add):\\n                    if self._command != \\\"install\\\" or (\\n                        self._repodata_fn == REPODATA_FN\\n                        and (not ssc.should_retry_solve or not freeze_installed)\\n                    ):\\n                        # raises a hopefully helpful error message\\n                        ssc.r.find_conflicts(spec_set)\\n                    else:\\n                        raise UnsatisfiableError({})\\n                ssc.specs_map[\\\"python\\\"] = python_spec\\n\\n        # For the aggressive_update_packages configuration parameter, we strip any target\\n        # that's been set.\\n        if not context.offline:\\n            for spec in context.aggressive_update_packages:\\n                if spec.name in ssc.specs_map:\\n                    ssc.specs_map[spec.name] = spec\\n\\n        # add in explicitly requested specs from specs_to_add\\n        # this overrides any name-matching spec already in the spec map\\n        ssc.specs_map.update(\\n            (s.name, s) for s in self.specs_to_add if s.name not in pin_overrides\\n        )\\n\\n        # As a business rule, we never want to downgrade conda below the current version,\\n        # unless that's requested explicitly by the user (which we actively discourage).\\n        if \\\"conda\\\" in ssc.specs_map and paths_equal(self.prefix, context.conda_prefix):\\n            conda_prefix_rec = ssc.prefix_data.get(\\\"conda\\\")\\n            if conda_prefix_rec:\\n                version_req = f\\\">={conda_prefix_rec.version}\\\"\\n                conda_requested_explicitly = any(\\n                    s.name == \\\"conda\\\" for s in self.specs_to_add\\n                )\\n                conda_spec = ssc.specs_map[\\\"conda\\\"]\\n                conda_in_specs_to_add_version = ssc.specs_map.get(\\\"conda\\\", {}).get(\\n                    \\\"version\\\"\\n                )\\n                if not conda_in_specs_to_add_version:\\n                    conda_spec = MatchSpec(conda_spec, version=version_req)\\n                if context.auto_update_conda and not conda_requested_explicitly:\\n                    conda_spec = MatchSpec(\\\"conda\\\", version=version_req, target=None)\\n                ssc.specs_map[\\\"conda\\\"] = conda_spec\\n\\n        return ssc\\n\\n    @time_recorder(module_name=__name__)\\n    def _run_sat(self, ssc):\\n        final_environment_specs = IndexedSet(\\n            (\\n                *ssc.specs_map.values(),\\n                *ssc.track_features_specs,\\n                # pinned specs removed here - added to specs_map in _add_specs instead\\n            )\\n        )\\n\\n        absent_specs = [s for s in ssc.specs_map.values() if not ssc.r.find_matches(s)]\\n        if absent_specs:\\n            raise PackagesNotFoundError(absent_specs)\\n\\n        # We've previously checked `solution` for consistency (which at that point was the\\n        # pre-solve state of the environment). Now we check our compiled set of\\n        # `final_environment_specs` for the possibility of a solution.  If there are conflicts,\\n        # we can often avoid them by neutering specs that have a target (e.g. removing version\\n        # constraint) and also making them optional. The result here will be less cases of\\n        # `UnsatisfiableError` handed to users, at the cost of more packages being modified\\n        # or removed from the environment.\\n        #\\n        # get_conflicting_specs() returns a \\\"minimal unsatisfiable subset\\\" which\\n        # may not be the only unsatisfiable subset. We may have to call get_conflicting_specs()\\n        # several times, each time making modifications to loosen constraints.\\n\\n        conflicting_specs = set(\\n            ssc.r.get_conflicting_specs(\\n                tuple(final_environment_specs), self.specs_to_add\\n            )\\n            or []\\n        )\\n        while conflicting_specs:\\n            specs_modified = False\\n            if log.isEnabledFor(DEBUG):\\n                log.debug(\\n                    \\\"conflicting specs: %s\\\",\\n                    dashlist(s.target or s for s in conflicting_specs),\\n                )\\n\\n            # Are all conflicting specs in specs_map? If not, that means they're in\\n            # track_features_specs or pinned_specs, which we should raise an error on.\\n            specs_map_set = set(ssc.specs_map.values())\\n            grouped_specs = groupby(lambda s: s in specs_map_set, conflicting_specs)\\n            # force optional to true. This is what it is originally in\\n            # pinned_specs, but we override that in _add_specs to make it\\n            # non-optional when there's a name match in the explicit package\\n            # pool\\n            conflicting_pinned_specs = groupby(\\n                lambda s: MatchSpec(s, optional=True) in ssc.pinned_specs,\\n                conflicting_specs,\\n            )\\n\\n            if conflicting_pinned_specs.get(True):\\n                in_specs_map = grouped_specs.get(True, ())\\n                pinned_conflicts = conflicting_pinned_specs.get(True, ())\\n                in_specs_map_or_specs_to_add = (\\n                    set(in_specs_map) | set(self.specs_to_add)\\n                ) - set(pinned_conflicts)\\n\\n                raise SpecsConfigurationConflictError(\\n                    sorted(s.__str__() for s in in_specs_map_or_specs_to_add),\\n                    sorted(s.__str__() for s in {s for s in pinned_conflicts}),\\n                    self.prefix,\\n                )\\n            for spec in conflicting_specs:\\n                if spec.target and not spec.optional:\\n                    specs_modified = True\\n                    final_environment_specs.remove(spec)\\n                    if spec.get(\\\"version\\\"):\\n                        neutered_spec = MatchSpec(spec.name, version=spec.version)\\n                    else:\\n                        neutered_spec = MatchSpec(spec.name)\\n                    final_environment_specs.add(neutered_spec)\\n                    ssc.specs_map[spec.name] = neutered_spec\\n            if specs_modified:\\n                conflicting_specs = set(\\n                    ssc.r.get_conflicting_specs(\\n                        tuple(final_environment_specs), self.specs_to_add\\n                    )\\n                )\\n            else:\\n                # Let r.solve() use r.find_conflicts() to report conflict chains.\\n                break\\n\\n        # Finally! We get to call SAT.\\n        if log.isEnabledFor(DEBUG):\\n            log.debug(\\n                \\\"final specs to add: %s\\\",\\n                dashlist(sorted(str(s) for s in final_environment_specs)),\\n            )\\n\\n        # this will raise for unsatisfiable stuff.  We can\\n        if not conflicting_specs or context.unsatisfiable_hints:\\n            ssc.solution_precs = ssc.r.solve(\\n                tuple(final_environment_specs),\\n                specs_to_add=self.specs_to_add,\\n                history_specs=ssc.specs_from_history_map,\\n                should_retry_solve=ssc.should_retry_solve,\\n            )\\n        else:\\n            # shortcut to raise an unsat error without needing another solve step when\\n            # unsatisfiable_hints is off\\n            raise UnsatisfiableError({})\\n\\n        self.neutered_specs = tuple(\\n            v\\n            for k, v in ssc.specs_map.items()\\n            if k in ssc.specs_from_history_map\\n            and v.strictness < ssc.specs_from_history_map[k].strictness\\n        )\\n\\n        # add back inconsistent packages to solution\\n        if ssc.add_back_map:\\n            for name, (prec, spec) in ssc.add_back_map.items():\\n                # spec here will only be set if the conflicting prec was in the original specs_map\\n                #    if it isn't there, then we restore the conflict.  If it is there, though,\\n                #    we keep the new, consistent solution\\n                if not spec:\\n                    # filter out solution precs and reinsert the conflict.  Any resolution\\n                    #    of the conflict should be explicit (i.e. it must be in ssc.specs_map)\\n                    ssc.solution_precs = [\\n                        _ for _ in ssc.solution_precs if _.name != name\\n                    ]\\n                    ssc.solution_precs.append(prec)\\n                    final_environment_specs.add(spec)\\n\\n        ssc.final_environment_specs = final_environment_specs\\n        return ssc\\n\\n    def _post_sat_handling(self, ssc):\\n        # Special case handling for various DepsModifier flags.\\n        final_environment_specs = ssc.final_environment_specs\\n        if ssc.deps_modifier == DepsModifier.NO_DEPS:\\n            # In the NO_DEPS case, we need to start with the original list of packages in the\\n            # environment, and then only modify packages that match specs_to_add or\\n            # specs_to_remove.\\n            #\\n            # Help information notes that use of NO_DEPS is expected to lead to broken\\n            # environments.\\n            _no_deps_solution = IndexedSet(ssc.prefix_data.iter_records())\\n            only_remove_these = {\\n                prec\\n                for spec in self.specs_to_remove\\n                for prec in _no_deps_solution\\n                if spec.match(prec)\\n            }\\n            _no_deps_solution -= only_remove_these\\n\\n            only_add_these = {\\n                prec\\n                for spec in self.specs_to_add\\n                for prec in ssc.solution_precs\\n                if spec.match(prec)\\n            }\\n            remove_before_adding_back = {prec.name for prec in only_add_these}\\n            _no_deps_solution = IndexedSet(\\n                prec\\n                for prec in _no_deps_solution\\n                if prec.name not in remove_before_adding_back\\n            )\\n            _no_deps_solution |= only_add_these\\n            ssc.solution_precs = _no_deps_solution\\n\\n            # TODO: check if solution is satisfiable, and emit warning if it's not\\n\\n        elif (\\n            ssc.deps_modifier == DepsModifier.ONLY_DEPS\\n            and ssc.update_modifier != UpdateModifier.UPDATE_DEPS\\n        ):\\n            # Using a special instance of PrefixGraph to remove youngest child nodes that match\\n            # the original specs_to_add.  It's important to remove only the *youngest* child nodes,\\n            # because a typical use might be `conda install --only-deps python=2 flask`, and in\\n            # that case we'd want to keep python.\\n            #\\n            # What are we supposed to do if flask was already in the environment?\\n            # We can't be removing stuff here that's already in the environment.\\n            #\\n            # What should be recorded for the user-requested specs in this case? Probably all\\n            # direct dependencies of flask.\\n            graph = PrefixGraph(ssc.solution_precs, self.specs_to_add)\\n            removed_nodes = graph.remove_youngest_descendant_nodes_with_specs()\\n            self.specs_to_add = set(self.specs_to_add)\\n            for prec in removed_nodes:\\n                for dep in prec.depends:\\n                    dep = MatchSpec(dep)\\n                    if dep.name not in ssc.specs_map:\\n                        self.specs_to_add.add(dep)\\n            # unfreeze\\n            self.specs_to_add = frozenset(self.specs_to_add)\\n\\n            # Add back packages that are already in the prefix.\\n            specs_to_remove_names = {spec.name for spec in self.specs_to_remove}\\n            add_back = tuple(\\n                ssc.prefix_data.get(node.name, None)\\n                for node in removed_nodes\\n                if node.name not in specs_to_remove_names\\n            )\\n            ssc.solution_precs = tuple(\\n                PrefixGraph((*graph.graph, *filter(None, add_back))).graph\\n            )\\n\\n            # TODO: check if solution is satisfiable, and emit warning if it's not\\n\\n        elif ssc.update_modifier == UpdateModifier.UPDATE_DEPS:\\n            # Here we have to SAT solve again :(  It's only now that we know the dependency\\n            # chain of specs_to_add.\\n            #\\n            # UPDATE_DEPS is effectively making each spec in the dependency chain a user-requested\\n            # spec.  We don't modify pinned_specs, track_features_specs, or specs_to_add.  For\\n            # all other specs, we drop all information but name, drop target, and add them to\\n            # the specs_to_add that gets recorded in the history file.\\n            #\\n            # It's like UPDATE_ALL, but only for certain dependency chains.\\n            graph = PrefixGraph(ssc.solution_precs)\\n            update_names = set()\\n            for spec in self.specs_to_add:\\n                node = graph.get_node_by_name(spec.name)\\n                update_names.update(\\n                    ancest_rec.name for ancest_rec in graph.all_ancestors(node)\\n                )\\n            specs_map = {name: MatchSpec(name) for name in update_names}\\n\\n            # Remove pinned_specs and any python spec (due to major-minor pinning business rule).\\n            # Add in the original specs_to_add on top.\\n            for spec in ssc.pinned_specs:\\n                specs_map.pop(spec.name, None)\\n            if \\\"python\\\" in specs_map:\\n                python_rec = ssc.prefix_data.get(\\\"python\\\")\\n                py_ver = \\\".\\\".join(python_rec.version.split(\\\".\\\")[:2]) + \\\".*\\\"\\n                specs_map[\\\"python\\\"] = MatchSpec(name=\\\"python\\\", version=py_ver)\\n            specs_map.update({spec.name: spec for spec in self.specs_to_add})\\n            new_specs_to_add = tuple(specs_map.values())\\n\\n            # It feels wrong/unsafe to modify this instance, but I guess let's go with it for now.\\n            self.specs_to_add = new_specs_to_add\\n            ssc.solution_precs = self.solve_final_state(\\n                update_modifier=UpdateModifier.UPDATE_SPECS,\\n                deps_modifier=ssc.deps_modifier,\\n                prune=ssc.prune,\\n                ignore_pinned=ssc.ignore_pinned,\\n                force_remove=ssc.force_remove,\\n            )\\n            ssc.prune = False\\n\\n        if ssc.prune:\\n            graph = PrefixGraph(ssc.solution_precs, final_environment_specs)\\n            graph.prune()\\n            ssc.solution_precs = tuple(graph.graph)\\n\\n        return ssc\\n\\n    def _notify_conda_outdated(self, link_precs):\\n        if not context.notify_outdated_conda or context.quiet:\\n            return\\n        current_conda_prefix_rec = PrefixData(context.conda_prefix).get(\\\"conda\\\", None)\\n        if current_conda_prefix_rec:\\n            channel_name = current_conda_prefix_rec.channel.canonical_name\\n            if channel_name == UNKNOWN_CHANNEL:\\n                channel_name = \\\"defaults\\\"\\n\\n            # only look for a newer conda in the channel conda is currently installed from\\n            conda_newer_spec = MatchSpec(f\\\"{channel_name}::conda>{CONDA_VERSION}\\\")\\n\\n            if paths_equal(self.prefix, context.conda_prefix):\\n                if any(conda_newer_spec.match(prec) for prec in link_precs):\\n                    return\\n\\n            conda_newer_precs = sorted(\\n                SubdirData.query_all(\\n                    conda_newer_spec,\\n                    self.channels,\\n                    self.subdirs,\\n                    repodata_fn=self._repodata_fn,\\n                ),\\n                key=lambda x: VersionOrder(x.version),\\n                # VersionOrder is fine here rather than r.version_key because all precs\\n                # should come from the same channel\\n            )\\n            if conda_newer_precs:\\n                latest_version = conda_newer_precs[-1].version\\n                # If conda comes from defaults, ensure we're giving instructions to users\\n                # that should resolve release timing issues between defaults and conda-forge.\\n                print(\\n                    dedent(\\n                        f\\\"\\\"\\\"\\n\\n                ==> WARNING: A newer version of conda exists. <==\\n                  current version: {CONDA_VERSION}\\n                  latest version: {latest_version}\\n\\n                Please update conda by running\\n\\n                    $ conda update -n base -c {channel_name} conda\\n\\n                Or to minimize the number of packages updated during conda update use\\n\\n                     conda install conda={latest_version}\\n\\n                \\\"\\\"\\\"\\n                    ),\\n                    file=sys.stderr,\\n                )\\n\\n    def _prepare(self, prepared_specs):\\n        # All of this _prepare() method is hidden away down here. Someday we may want to further\\n        # abstract away the use of `index` or the Resolve object.\\n\\n        if self._prepared and prepared_specs == self._prepared_specs:\\n            return self._index, self._r\\n\\n        if hasattr(self, \\\"_index\\\") and self._index:\\n            # added in install_actions for conda-build back-compat\\n            self._prepared_specs = prepared_specs\\n            _supplement_index_with_system(self._index)\\n            self._r = Resolve(self._index, channels=self.channels)\\n        else:\\n            # add in required channels that aren't explicitly given in the channels list\\n            # For correctness, we should probably add to additional_channels any channel that\\n            #  is given by PrefixData(self.prefix).all_subdir_urls().  However that causes\\n            #  usability problems with bad / expired tokens.\\n\\n            additional_channels = set()\\n            for spec in self.specs_to_add:\\n                # TODO: correct handling for subdir isn't yet done\\n                channel = spec.get_exact_value(\\\"channel\\\")\\n                if channel:\\n                    additional_channels.add(Channel(channel))\\n\\n            self.channels.update(additional_channels)\\n\\n            reduced_index = get_reduced_index(\\n                self.prefix,\\n                self.channels,\\n                self.subdirs,\\n                prepared_specs,\\n                self._repodata_fn,\\n            )\\n            _supplement_index_with_system(reduced_index)\\n\\n            self._prepared_specs = prepared_specs\\n            self._index = reduced_index\\n            self._r = Resolve(reduced_index, channels=self.channels)\\n\\n        self._prepared = True\\n        return self._index, self._r\\n\\n\\nclass SolverStateContainer:\\n    # A mutable container with defined attributes to help keep method signatures clean\\n    # and also keep track of important state variables.\\n\\n    def __init__(\\n        self,\\n        prefix,\\n        update_modifier,\\n        deps_modifier,\\n        prune,\\n        ignore_pinned,\\n        force_remove,\\n        should_retry_solve,\\n    ):\\n        # prefix, channels, subdirs, specs_to_add, specs_to_remove\\n        # self.prefix = prefix\\n        # self.channels = channels\\n        # self.subdirs = subdirs\\n        # self.specs_to_add = specs_to_add\\n        # self.specs_to_remove = specs_to_remove\\n\\n        # Group 1. Behavior flags\\n        self.update_modifier = update_modifier\\n        self.deps_modifier = deps_modifier\\n        self.prune = prune\\n        self.ignore_pinned = ignore_pinned\\n        self.force_remove = force_remove\\n        self.should_retry_solve = should_retry_solve\\n\\n        # Group 2. System state\\n        self.prefix = prefix\\n        # self.prefix_data = None\\n        # self.specs_from_history_map = None\\n        # self.track_features_specs = None\\n        # self.pinned_specs = None\\n\\n        # Group 3. Repository metadata\\n        self.index = None\\n        self.r = None\\n\\n        # Group 4. Mutable working containers\\n        self.specs_map = {}\\n        self.solution_precs = None\\n        self._init_solution_precs()\\n        self.add_back_map = {}  # name: (prec, spec)\\n        self.final_environment_specs = None\\n\\n    @memoizedproperty\\n    def prefix_data(self):\\n        return PrefixData(self.prefix)\\n\\n    @memoizedproperty\\n    def specs_from_history_map(self):\\n        return History(self.prefix).get_requested_specs_map()\\n\\n    @memoizedproperty\\n    def track_features_specs(self):\\n        return tuple(MatchSpec(x + \\\"@\\\") for x in context.track_features)\\n\\n    @memoizedproperty\\n    def pinned_specs(self):\\n        return () if self.ignore_pinned else get_pinned_specs(self.prefix)\\n\\n    def set_repository_metadata(self, index, r):\\n        self.index, self.r = index, r\\n\\n    def _init_solution_precs(self):\\n        if self.prune:\\n            # DO NOT add existing prefix data to solution on prune\\n            self.solution_precs = tuple()\\n        else:\\n            self.solution_precs = tuple(self.prefix_data.iter_records())\\n\\n    def working_state_reset(self):\\n        self.specs_map = {}\\n        self._init_solution_precs()\\n        self.add_back_map = {}  # name: (prec, spec)\\n        self.final_environment_specs = None\\n\\n\\ndef get_pinned_specs(prefix):\\n    \\\"\\\"\\\"Find pinned specs from file and return a tuple of MatchSpec.\\\"\\\"\\\"\\n    pinfile = join(prefix, \\\"conda-meta\\\", \\\"pinned\\\")\\n    if exists(pinfile):\\n        with open(pinfile) as f:\\n            from_file = (\\n                i\\n                for i in f.read().strip().splitlines()\\n                if i and not i.strip().startswith(\\\"#\\\")\\n            )\\n    else:\\n        from_file = ()\\n\\n    return tuple(\\n        MatchSpec(spec, optional=True)\\n        for spec in (*context.pinned_packages, *from_file)\\n    )\\n\\n\\ndef diff_for_unlink_link_precs(\\n    prefix,\\n    final_precs,\\n    specs_to_add=(),\\n    force_reinstall=NULL,\\n) -> tuple[tuple[PackageRecord, ...], tuple[PackageRecord, ...]]:\\n    # Ensure final_precs supports the IndexedSet interface\\n    if not isinstance(final_precs, IndexedSet):\\n        assert hasattr(\\n            final_precs, \\\"__getitem__\\\"\\n        ), \\\"final_precs must support list indexing\\\"\\n        assert hasattr(\\n            final_precs, \\\"__sub__\\\"\\n        ), \\\"final_precs must support set difference\\\"\\n\\n    previous_records = IndexedSet(PrefixGraph(PrefixData(prefix).iter_records()).graph)\\n    force_reinstall = (\\n        context.force_reinstall if force_reinstall is NULL else force_reinstall\\n    )\\n\\n    unlink_precs = previous_records - final_precs\\n    link_precs = final_precs - previous_records\\n\\n    def _add_to_unlink_and_link(rec):\\n        link_precs.add(rec)\\n        if prec in previous_records:\\n            unlink_precs.add(rec)\\n\\n    # If force_reinstall is enabled, make sure any package in specs_to_add is unlinked then\\n    # re-linked\\n    if force_reinstall:\\n        for spec in specs_to_add:\\n            prec = next((rec for rec in final_precs if spec.match(rec)), None)\\n            assert prec\\n            _add_to_unlink_and_link(prec)\\n\\n    # add back 'noarch: python' packages to unlink and link if python version changes\\n    python_spec = MatchSpec(\\\"python\\\")\\n    prev_python = next(\\n        (rec for rec in previous_records if python_spec.match(rec)), None\\n    )\\n    curr_python = next((rec for rec in final_precs if python_spec.match(rec)), None)\\n    gmm = get_major_minor_version\\n    if (\\n        prev_python\\n        and curr_python\\n        and gmm(prev_python.version) != gmm(curr_python.version)\\n    ):\\n        noarch_python_precs = (p for p in final_precs if p.noarch == NoarchType.python)\\n        for prec in noarch_python_precs:\\n            _add_to_unlink_and_link(prec)\\n\\n    unlink_precs = IndexedSet(\\n        reversed(sorted(unlink_precs, key=lambda x: previous_records.index(x)))\\n    )\\n    link_precs = IndexedSet(sorted(link_precs, key=lambda x: final_precs.index(x)))\\n    return tuple(unlink_precs), tuple(link_precs)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Package installation implemented as a series of link/unlink transactions.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport itertools\\nimport os\\nimport sys\\nimport warnings\\nfrom collections import defaultdict\\nfrom itertools import chain\\nfrom logging import getLogger\\nfrom os.path import basename, dirname, isdir, join\\nfrom pathlib import Path\\nfrom textwrap import indent\\nfrom traceback import format_exception_only\\nfrom typing import TYPE_CHECKING, NamedTuple\\n\\nfrom .. import CondaError, CondaMultiError, conda_signal_handler\\nfrom ..auxlib.collection import first\\nfrom ..auxlib.ish import dals\\nfrom ..base.constants import DEFAULTS_CHANNEL_NAME, PREFIX_MAGIC_FILE, SafetyChecks\\nfrom ..base.context import context\\nfrom ..cli.common import confirm_yn\\nfrom ..common.compat import ensure_text_type, on_win\\nfrom ..common.io import (\\n    DummyExecutor,\\n    Spinner,\\n    ThreadLimitedThreadPoolExecutor,\\n    dashlist,\\n    time_recorder,\\n)\\nfrom ..common.path import (\\n    explode_directories,\\n    get_all_directories,\\n    get_major_minor_version,\\n    get_python_site_packages_short_path,\\n)\\nfrom ..common.signals import signal_handler\\nfrom ..exceptions import (\\n    CondaSystemExit,\\n    DisallowedPackageError,\\n    EnvironmentNotWritableError,\\n    KnownPackageClobberError,\\n    LinkError,\\n    RemoveError,\\n    SharedLinkPathClobberError,\\n    UnknownPackageClobberError,\\n    maybe_raise,\\n)\\nfrom ..gateways.disk import mkdir_p\\nfrom ..gateways.disk.delete import rm_rf\\nfrom ..gateways.disk.read import isfile, lexists, read_package_info\\nfrom ..gateways.disk.test import (\\n    hardlink_supported,\\n    is_conda_environment,\\n    softlink_supported,\\n)\\nfrom ..gateways.subprocess import subprocess_call\\nfrom ..models.enums import LinkType\\nfrom ..models.version import VersionOrder\\nfrom ..resolve import MatchSpec\\nfrom ..utils import get_comspec, human_bytes, wrap_subprocess_call\\nfrom .package_cache_data import PackageCacheData\\nfrom .path_actions import (\\n    AggregateCompileMultiPycAction,\\n    CompileMultiPycAction,\\n    CreateNonadminAction,\\n    CreatePrefixRecordAction,\\n    CreatePythonEntryPointAction,\\n    LinkPathAction,\\n    MakeMenuAction,\\n    RegisterEnvironmentLocationAction,\\n    RemoveLinkedPackageRecordAction,\\n    RemoveMenuAction,\\n    UnlinkPathAction,\\n    UnregisterEnvironmentLocationAction,\\n    UpdateHistoryAction,\\n)\\nfrom .prefix_data import PrefixData, get_python_version_for_prefix\\n\\nif TYPE_CHECKING:\\n    from typing import Iterable\\n\\n    from ..models.package_info import PackageInfo\\n    from ..models.records import PackageRecord\\n    from .path_actions import _Action\\n\\nlog = getLogger(__name__)\\n\\n\\ndef determine_link_type(extracted_package_dir, target_prefix):\\n    source_test_file = join(extracted_package_dir, \\\"info\\\", \\\"index.json\\\")\\n    if context.always_copy:\\n        return LinkType.copy\\n    if context.always_softlink:\\n        return LinkType.softlink\\n    if hardlink_supported(source_test_file, target_prefix):\\n        return LinkType.hardlink\\n    if context.allow_softlinks and softlink_supported(source_test_file, target_prefix):\\n        return LinkType.softlink\\n    return LinkType.copy\\n\\n\\ndef make_unlink_actions(transaction_context, target_prefix, prefix_record):\\n    # no side effects in this function!\\n    unlink_path_actions = tuple(\\n        UnlinkPathAction(transaction_context, prefix_record, target_prefix, trgt)\\n        for trgt in prefix_record.files\\n    )\\n\\n    try:\\n        extracted_package_dir = basename(prefix_record.extracted_package_dir)\\n    except AttributeError:\\n        try:\\n            extracted_package_dir = basename(prefix_record.link.source)\\n        except AttributeError:\\n            # for backward compatibility only\\n            extracted_package_dir = (\\n                f\\\"{prefix_record.name}-{prefix_record.version}-{prefix_record.build}\\\"\\n            )\\n\\n    meta_short_path = \\\"{}/{}\\\".format(\\\"conda-meta\\\", extracted_package_dir + \\\".json\\\")\\n    remove_conda_meta_actions = (\\n        RemoveLinkedPackageRecordAction(\\n            transaction_context, prefix_record, target_prefix, meta_short_path\\n        ),\\n    )\\n\\n    _all_d = get_all_directories(axn.target_short_path for axn in unlink_path_actions)\\n    all_directories = sorted(explode_directories(_all_d), reverse=True)\\n    directory_remove_actions = tuple(\\n        UnlinkPathAction(\\n            transaction_context, prefix_record, target_prefix, d, LinkType.directory\\n        )\\n        for d in all_directories\\n    )\\n\\n    # unregister_private_package_actions = UnregisterPrivateEnvAction.create_actions(\\n    #     transaction_context, package_cache_record, target_prefix\\n    # )\\n\\n    return (\\n        *unlink_path_actions,\\n        *directory_remove_actions,\\n        # *unregister_private_package_actions,\\n        *remove_conda_meta_actions,\\n    )\\n\\n\\ndef match_specs_to_dists(packages_info_to_link, specs):\\n    matched_specs = [None for _ in range(len(packages_info_to_link))]\\n    for spec in specs or ():\\n        spec = MatchSpec(spec)\\n        idx = next(\\n            (\\n                q\\n                for q, pkg_info in enumerate(packages_info_to_link)\\n                if pkg_info.repodata_record.name == spec.name\\n            ),\\n            None,\\n        )\\n        if idx is not None:\\n            matched_specs[idx] = spec\\n    return tuple(matched_specs)\\n\\n\\nclass PrefixSetup(NamedTuple):\\n    target_prefix: str\\n    unlink_precs: tuple[PackageRecord, ...]\\n    link_precs: tuple[PackageRecord, ...]\\n    remove_specs: tuple[MatchSpec, ...]\\n    update_specs: tuple[MatchSpec, ...]\\n    neutered_specs: tuple[MatchSpec, ...]\\n\\n\\nclass ActionGroup(NamedTuple):\\n    type: str\\n    pkg_data: PackageInfo | None\\n    actions: Iterable[_Action]\\n    target_prefix: str\\n\\n\\nclass PrefixActionGroup(NamedTuple):\\n    remove_menu_action_groups: Iterable[ActionGroup]\\n    unlink_action_groups: Iterable[ActionGroup]\\n    unregister_action_groups: Iterable[ActionGroup]\\n    link_action_groups: Iterable[ActionGroup]\\n    register_action_groups: Iterable[ActionGroup]\\n    compile_action_groups: Iterable[ActionGroup]\\n    make_menu_action_groups: Iterable[ActionGroup]\\n    entry_point_action_groups: Iterable[ActionGroup]\\n    prefix_record_groups: Iterable[ActionGroup]\\n\\n\\nclass ChangeReport(NamedTuple):\\n    prefix: str\\n    specs_to_remove: Iterable[MatchSpec]\\n    specs_to_add: Iterable[MatchSpec]\\n    removed_precs: Iterable[PackageRecord]\\n    new_precs: Iterable[PackageRecord]\\n    updated_precs: Iterable[PackageRecord]\\n    downgraded_precs: Iterable[PackageRecord]\\n    superseded_precs: Iterable[PackageRecord]\\n    fetch_precs: Iterable[PackageRecord]\\n\\n\\nclass UnlinkLinkTransaction:\\n    def __init__(self, *setups):\\n        self.prefix_setups = {stp.target_prefix: stp for stp in setups}\\n        self.prefix_action_groups = {}\\n\\n        for stp in self.prefix_setups.values():\\n            log.info(\\n                \\\"initializing UnlinkLinkTransaction with\\\\n\\\"\\n                \\\"  target_prefix: %s\\\\n\\\"\\n                \\\"  unlink_precs:\\\\n\\\"\\n                \\\"    %s\\\\n\\\"\\n                \\\"  link_precs:\\\\n\\\"\\n                \\\"    %s\\\\n\\\",\\n                stp.target_prefix,\\n                \\\"\\\\n    \\\".join(prec.dist_str() for prec in stp.unlink_precs),\\n                \\\"\\\\n    \\\".join(prec.dist_str() for prec in stp.link_precs),\\n            )\\n\\n        self._pfe = None\\n        self._prepared = False\\n        self._verified = False\\n        # this can be CPU-bound.  Use ProcessPoolExecutor.\\n        self.verify_executor = (\\n            DummyExecutor()\\n            if context.debug or context.verify_threads == 1\\n            else ThreadLimitedThreadPoolExecutor(context.verify_threads)\\n        )\\n        # this is more I/O bound.  Use ThreadPoolExecutor.\\n        self.execute_executor = (\\n            DummyExecutor()\\n            if context.debug or context.execute_threads == 1\\n            else ThreadLimitedThreadPoolExecutor(context.execute_threads)\\n        )\\n\\n    @property\\n    def nothing_to_do(self):\\n        return not any(\\n            (stp.unlink_precs or stp.link_precs) for stp in self.prefix_setups.values()\\n        ) and all(\\n            is_conda_environment(stp.target_prefix)\\n            for stp in self.prefix_setups.values()\\n        )\\n\\n    def download_and_extract(self):\\n        if self._pfe is None:\\n            self._get_pfe()\\n        if not self._pfe._executed:\\n            self._pfe.execute()\\n\\n    def prepare(self):\\n        if self._pfe is None:\\n            self._get_pfe()\\n        if not self._pfe._executed:\\n            self._pfe.execute()\\n\\n        if self._prepared:\\n            return\\n\\n        self.transaction_context = {}\\n\\n        with Spinner(\\n            \\\"Preparing transaction\\\",\\n            not context.verbose and not context.quiet,\\n            context.json,\\n        ):\\n            for stp in self.prefix_setups.values():\\n                grps = self._prepare(\\n                    self.transaction_context,\\n                    stp.target_prefix,\\n                    stp.unlink_precs,\\n                    stp.link_precs,\\n                    stp.remove_specs,\\n                    stp.update_specs,\\n                    stp.neutered_specs,\\n                )\\n                self.prefix_action_groups[stp.target_prefix] = PrefixActionGroup(*grps)\\n\\n        self._prepared = True\\n\\n    @time_recorder(\\\"unlink_link_prepare_and_verify\\\")\\n    def verify(self):\\n        if not self._prepared:\\n            self.prepare()\\n\\n        assert not context.dry_run\\n\\n        if context.safety_checks == SafetyChecks.disabled:\\n            self._verified = True\\n            return\\n\\n        with Spinner(\\n            \\\"Verifying transaction\\\",\\n            not context.verbose and not context.quiet,\\n            context.json,\\n        ):\\n            exceptions = self._verify(self.prefix_setups, self.prefix_action_groups)\\n            if exceptions:\\n                try:\\n                    maybe_raise(CondaMultiError(exceptions), context)\\n                except:\\n                    rm_rf(self.transaction_context[\\\"temp_dir\\\"])\\n                    raise\\n                log.info(exceptions)\\n        try:\\n            self._verify_pre_link_message(\\n                itertools.chain(\\n                    *(\\n                        act.link_action_groups\\n                        for act in self.prefix_action_groups.values()\\n                    )\\n                )\\n            )\\n        except CondaSystemExit:\\n            rm_rf(self.transaction_context[\\\"temp_dir\\\"])\\n            raise\\n        self._verified = True\\n\\n    def _verify_pre_link_message(self, all_link_groups):\\n        flag_pre_link = False\\n        for act in all_link_groups:\\n            prelink_msg_dir = (\\n                Path(act.pkg_data.extracted_package_dir) / \\\"info\\\" / \\\"prelink_messages\\\"\\n            )\\n            all_msg_subdir = list(\\n                item for item in prelink_msg_dir.glob(\\\"**/*\\\") if item.is_file()\\n            )\\n            if prelink_msg_dir.is_dir() and all_msg_subdir:\\n                print(\\\"\\\\n\\\\nThe following PRELINK MESSAGES are INCLUDED:\\\\n\\\\n\\\")\\n                flag_pre_link = True\\n\\n                for msg_file in all_msg_subdir:\\n                    print(f\\\"  File {msg_file.name}:\\\\n\\\")\\n                    print(indent(msg_file.read_text(), \\\"  \\\"))\\n                    print()\\n        if flag_pre_link:\\n            confirm_yn()\\n\\n    def execute(self):\\n        if not self._verified:\\n            self.verify()\\n\\n        assert not context.dry_run\\n        try:\\n            # innermost dict.values() is an iterable of PrefixActionGroup namedtuple\\n            # zip() is an iterable of each PrefixActionGroup namedtuple key\\n            self._execute(\\n                tuple(chain(*chain(*zip(*self.prefix_action_groups.values()))))\\n            )\\n        finally:\\n            rm_rf(self.transaction_context[\\\"temp_dir\\\"])\\n\\n    def _get_pfe(self):\\n        from .package_cache_data import ProgressiveFetchExtract\\n\\n        if self._pfe is not None:\\n            pfe = self._pfe\\n        elif not self.prefix_setups:\\n            self._pfe = pfe = ProgressiveFetchExtract(())\\n        else:\\n            link_precs = set(\\n                chain.from_iterable(\\n                    stp.link_precs for stp in self.prefix_setups.values()\\n                )\\n            )\\n            self._pfe = pfe = ProgressiveFetchExtract(link_precs)\\n        return pfe\\n\\n    @classmethod\\n    def _prepare(\\n        cls,\\n        transaction_context,\\n        target_prefix,\\n        unlink_precs,\\n        link_precs,\\n        remove_specs,\\n        update_specs,\\n        neutered_specs,\\n    ):\\n        # make sure prefix directory exists\\n        if not isdir(target_prefix):\\n            try:\\n                mkdir_p(target_prefix)\\n            except OSError as e:\\n                log.debug(repr(e))\\n                raise CondaError(\\n                    f\\\"Unable to create prefix directory '{target_prefix}'.\\\\n\\\"\\n                    \\\"Check that you have sufficient permissions.\\\"\\n                    \\\"\\\"\\n                )\\n\\n        # gather information from disk and caches\\n        prefix_data = PrefixData(target_prefix)\\n        prefix_recs_to_unlink = (prefix_data.get(prec.name) for prec in unlink_precs)\\n        # NOTE: load_meta can return None\\n        # TODO: figure out if this filter shouldn't be an assert not None\\n        prefix_recs_to_unlink = tuple(lpd for lpd in prefix_recs_to_unlink if lpd)\\n        pkg_cache_recs_to_link = tuple(\\n            PackageCacheData.get_entry_to_link(prec) for prec in link_precs\\n        )\\n        assert all(pkg_cache_recs_to_link)\\n        packages_info_to_link = tuple(\\n            read_package_info(prec, pcrec)\\n            for prec, pcrec in zip(link_precs, pkg_cache_recs_to_link)\\n        )\\n\\n        link_types = tuple(\\n            determine_link_type(pkg_info.extracted_package_dir, target_prefix)\\n            for pkg_info in packages_info_to_link\\n        )\\n\\n        # make all the path actions\\n        # no side effects allowed when instantiating these action objects\\n        python_version = cls._get_python_version(\\n            target_prefix, prefix_recs_to_unlink, packages_info_to_link\\n        )\\n        transaction_context[\\\"target_python_version\\\"] = python_version\\n        sp = get_python_site_packages_short_path(python_version)\\n        transaction_context[\\\"target_site_packages_short_path\\\"] = sp\\n\\n        transaction_context[\\\"temp_dir\\\"] = join(target_prefix, \\\".condatmp\\\")\\n\\n        remove_menu_action_groups = []\\n        unlink_action_groups = []\\n        for prefix_rec in prefix_recs_to_unlink:\\n            unlink_action_groups.append(\\n                ActionGroup(\\n                    \\\"unlink\\\",\\n                    prefix_rec,\\n                    make_unlink_actions(transaction_context, target_prefix, prefix_rec),\\n                    target_prefix,\\n                )\\n            )\\n\\n            remove_menu_action_groups.append(\\n                ActionGroup(\\n                    \\\"remove_menus\\\",\\n                    prefix_rec,\\n                    RemoveMenuAction.create_actions(\\n                        transaction_context, prefix_rec, target_prefix\\n                    ),\\n                    target_prefix,\\n                )\\n            )\\n\\n        if unlink_action_groups:\\n            axns = (\\n                UnregisterEnvironmentLocationAction(transaction_context, target_prefix),\\n            )\\n            unregister_action_groups = [\\n                ActionGroup(\\\"unregister\\\", None, axns, target_prefix)\\n            ]\\n        else:\\n            unregister_action_groups = ()\\n\\n        matchspecs_for_link_dists = match_specs_to_dists(\\n            packages_info_to_link, update_specs\\n        )\\n        link_action_groups = []\\n        entry_point_action_groups = []\\n        compile_action_groups = []\\n        make_menu_action_groups = []\\n        record_axns = []\\n        for pkg_info, lt, spec in zip(\\n            packages_info_to_link, link_types, matchspecs_for_link_dists\\n        ):\\n            link_ag = ActionGroup(\\n                \\\"link\\\",\\n                pkg_info,\\n                cls._make_link_actions(\\n                    transaction_context, pkg_info, target_prefix, lt, spec\\n                ),\\n                target_prefix,\\n            )\\n            link_action_groups.append(link_ag)\\n\\n            entry_point_ag = ActionGroup(\\n                \\\"entry_point\\\",\\n                pkg_info,\\n                cls._make_entry_point_actions(\\n                    transaction_context,\\n                    pkg_info,\\n                    target_prefix,\\n                    lt,\\n                    spec,\\n                    link_action_groups,\\n                ),\\n                target_prefix,\\n            )\\n            entry_point_action_groups.append(entry_point_ag)\\n\\n            compile_ag = ActionGroup(\\n                \\\"compile\\\",\\n                pkg_info,\\n                cls._make_compile_actions(\\n                    transaction_context,\\n                    pkg_info,\\n                    target_prefix,\\n                    lt,\\n                    spec,\\n                    link_action_groups,\\n                ),\\n                target_prefix,\\n            )\\n            compile_action_groups.append(compile_ag)\\n\\n            make_menu_ag = ActionGroup(\\n                \\\"make_menus\\\",\\n                pkg_info,\\n                MakeMenuAction.create_actions(\\n                    transaction_context, pkg_info, target_prefix, lt\\n                ),\\n                target_prefix,\\n            )\\n            make_menu_action_groups.append(make_menu_ag)\\n\\n            all_link_path_actions = (\\n                *link_ag.actions,\\n                *compile_ag.actions,\\n                *entry_point_ag.actions,\\n                *make_menu_ag.actions,\\n            )\\n            record_axns.extend(\\n                CreatePrefixRecordAction.create_actions(\\n                    transaction_context,\\n                    pkg_info,\\n                    target_prefix,\\n                    lt,\\n                    spec,\\n                    all_link_path_actions,\\n                )\\n            )\\n\\n        prefix_record_groups = [ActionGroup(\\\"record\\\", None, record_axns, target_prefix)]\\n\\n        # We're post solve here.  The update_specs are explicit requests.  We need to neuter\\n        #    any historic spec that was neutered prior to the solve.\\n        history_actions = UpdateHistoryAction.create_actions(\\n            transaction_context,\\n            target_prefix,\\n            remove_specs,\\n            update_specs,\\n            neutered_specs,\\n        )\\n        register_actions = (\\n            RegisterEnvironmentLocationAction(transaction_context, target_prefix),\\n        )\\n        register_action_groups = [\\n            ActionGroup(\\n                \\\"register\\\", None, register_actions + history_actions, target_prefix\\n            )\\n        ]\\n        return PrefixActionGroup(\\n            remove_menu_action_groups,\\n            unlink_action_groups,\\n            unregister_action_groups,\\n            link_action_groups,\\n            register_action_groups,\\n            compile_action_groups,\\n            make_menu_action_groups,\\n            entry_point_action_groups,\\n            prefix_record_groups,\\n        )\\n\\n    @staticmethod\\n    def _verify_individual_level(prefix_action_group):\\n        all_actions = chain.from_iterable(\\n            axngroup.actions\\n            for action_groups in prefix_action_group\\n            for axngroup in action_groups\\n        )\\n\\n        # run all per-action (per-package) verify methods\\n        #   one of the more important of these checks is to verify that a file listed in\\n        #   the packages manifest (i.e. info/files) is actually contained within the package\\n        error_results = []\\n        for axn in all_actions:\\n            if axn.verified:\\n                continue\\n            error_result = axn.verify()\\n            if error_result:\\n                formatted_error = \\\"\\\".join(\\n                    format_exception_only(type(error_result), error_result)\\n                )\\n                log.debug(\\\"Verification error in action %s\\\\n%s\\\", axn, formatted_error)\\n                error_results.append(error_result)\\n        return error_results\\n\\n    @staticmethod\\n    def _verify_prefix_level(target_prefix_AND_prefix_action_group_tuple):\\n        # further verification of the whole transaction\\n        # for each path we are creating in link_actions, we need to make sure\\n        #   1. each path either doesn't already exist in the prefix, or will be unlinked\\n        #   2. there's only a single instance of each path\\n        #   3. if the target is a private env, leased paths need to be verified\\n        #   4. make sure conda-meta/history file is writable\\n        #   5. make sure envs/catalog.json is writable; done with RegisterEnvironmentLocationAction\\n        # TODO: 3, 4\\n\\n        # this strange unpacking is to help the parallel execution work.  Unpacking\\n        #    tuples in the map call could be done with a lambda, but that is then not picklable,\\n        #    which precludes the use of ProcessPoolExecutor (but not ThreadPoolExecutor)\\n        target_prefix, prefix_action_group = target_prefix_AND_prefix_action_group_tuple\\n\\n        unlink_action_groups = prefix_action_group.unlink_action_groups\\n        prefix_record_groups = prefix_action_group.prefix_record_groups\\n\\n        lower_on_win = lambda p: p.lower() if on_win else p\\n        unlink_paths = {\\n            lower_on_win(axn.target_short_path)\\n            for grp in unlink_action_groups\\n            for axn in grp.actions\\n            if isinstance(axn, UnlinkPathAction)\\n        }\\n        # we can get all of the paths being linked by looking only at the\\n        #   CreateLinkedPackageRecordAction actions\\n        create_lpr_actions = (\\n            axn\\n            for grp in prefix_record_groups\\n            for axn in grp.actions\\n            if isinstance(axn, CreatePrefixRecordAction)\\n        )\\n\\n        error_results = []\\n        # Verification 1. each path either doesn't already exist in the prefix, or will be unlinked\\n        link_paths_dict = defaultdict(list)\\n        for axn in create_lpr_actions:\\n            for link_path_action in axn.all_link_path_actions:\\n                if isinstance(link_path_action, CompileMultiPycAction):\\n                    target_short_paths = link_path_action.target_short_paths\\n                elif isinstance(link_path_action, CreateNonadminAction):\\n                    continue\\n                else:\\n                    target_short_paths = (\\n                        (link_path_action.target_short_path,)\\n                        if not hasattr(link_path_action, \\\"link_type\\\")\\n                        or link_path_action.link_type != LinkType.directory\\n                        else ()\\n                    )\\n                for path in target_short_paths:\\n                    path = lower_on_win(path)\\n                    link_paths_dict[path].append(axn)\\n                    if path not in unlink_paths and lexists(join(target_prefix, path)):\\n                        # we have a collision; at least try to figure out where it came from\\n                        colliding_prefix_rec = first(\\n                            (\\n                                prefix_rec\\n                                for prefix_rec in PrefixData(\\n                                    target_prefix\\n                                ).iter_records()\\n                            ),\\n                            key=lambda prefix_rec: path in prefix_rec.files,\\n                        )\\n                        if colliding_prefix_rec:\\n                            error_results.append(\\n                                KnownPackageClobberError(\\n                                    path,\\n                                    axn.package_info.repodata_record.dist_str(),\\n                                    colliding_prefix_rec.dist_str(),\\n                                    context,\\n                                )\\n                            )\\n                        else:\\n                            error_results.append(\\n                                UnknownPackageClobberError(\\n                                    path,\\n                                    axn.package_info.repodata_record.dist_str(),\\n                                    context,\\n                                )\\n                            )\\n\\n        # Verification 2. there's only a single instance of each path\\n        for path, axns in link_paths_dict.items():\\n            if len(axns) > 1:\\n                error_results.append(\\n                    SharedLinkPathClobberError(\\n                        path,\\n                        tuple(\\n                            axn.package_info.repodata_record.dist_str() for axn in axns\\n                        ),\\n                        context,\\n                    )\\n                )\\n        return error_results\\n\\n    @staticmethod\\n    def _verify_transaction_level(prefix_setups):\\n        # 1. make sure we're not removing conda from conda's env\\n        # 2. make sure we're not removing a conda dependency from conda's env\\n        # 3. enforce context.disallowed_packages\\n        # 4. make sure we're not removing pinned packages without no-pin flag\\n        # 5. make sure conda-meta/history for each prefix is writable\\n        # TODO: Verification 4\\n\\n        conda_prefixes = (\\n            join(context.root_prefix, \\\"envs\\\", \\\"_conda_\\\"),\\n            context.root_prefix,\\n        )\\n        conda_setups = tuple(\\n            setup\\n            for setup in prefix_setups.values()\\n            if setup.target_prefix in conda_prefixes\\n        )\\n\\n        conda_unlinked = any(\\n            prec.name == \\\"conda\\\"\\n            for setup in conda_setups\\n            for prec in setup.unlink_precs\\n        )\\n\\n        conda_prec, conda_final_setup = next(\\n            (\\n                (prec, setup)\\n                for setup in conda_setups\\n                for prec in setup.link_precs\\n                if prec.name == \\\"conda\\\"\\n            ),\\n            (None, None),\\n        )\\n\\n        if conda_unlinked and conda_final_setup is None:\\n            # means conda is being unlinked and not re-linked anywhere\\n            # this should never be able to be skipped, even with --force\\n            yield RemoveError(\\n                \\\"This operation will remove conda without replacing it with\\\\n\\\"\\n                \\\"another version of conda.\\\"\\n            )\\n\\n        if conda_final_setup is None:\\n            # means we're not unlinking then linking a new package, so look up current conda record\\n            conda_final_prefix = context.conda_prefix\\n            pd = PrefixData(conda_final_prefix)\\n            pkg_names_already_lnkd = tuple(rec.name for rec in pd.iter_records())\\n            pkg_names_being_lnkd = ()\\n            pkg_names_being_unlnkd = ()\\n            conda_linked_depends = next(\\n                (\\n                    record.depends\\n                    for record in pd.iter_records()\\n                    if record.name == \\\"conda\\\"\\n                ),\\n                (),\\n            )\\n        else:\\n            conda_final_prefix = conda_final_setup.target_prefix\\n            pd = PrefixData(conda_final_prefix)\\n            pkg_names_already_lnkd = tuple(rec.name for rec in pd.iter_records())\\n            pkg_names_being_lnkd = tuple(\\n                prec.name for prec in conda_final_setup.link_precs or ()\\n            )\\n            pkg_names_being_unlnkd = tuple(\\n                prec.name for prec in conda_final_setup.unlink_precs or ()\\n            )\\n            conda_linked_depends = conda_prec.depends\\n\\n        if conda_final_prefix in prefix_setups:\\n            for conda_dependency in conda_linked_depends:\\n                dep_name = MatchSpec(conda_dependency).name\\n                if dep_name not in pkg_names_being_lnkd and (\\n                    dep_name not in pkg_names_already_lnkd\\n                    or dep_name in pkg_names_being_unlnkd\\n                ):\\n                    yield RemoveError(\\n                        f\\\"'{dep_name}' is a dependency of conda and cannot be removed from\\\\n\\\"\\n                        \\\"conda's operating environment.\\\"\\n                    )\\n\\n        # Verification 3. enforce disallowed_packages\\n        disallowed = tuple(MatchSpec(s) for s in context.disallowed_packages)\\n        for prefix_setup in prefix_setups.values():\\n            for prec in prefix_setup.link_precs:\\n                if any(d.match(prec) for d in disallowed):\\n                    yield DisallowedPackageError(prec)\\n\\n        # Verification 5. make sure conda-meta/history for each prefix is writable\\n        for prefix_setup in prefix_setups.values():\\n            test_path = join(prefix_setup.target_prefix, PREFIX_MAGIC_FILE)\\n            test_path_existed = lexists(test_path)\\n            dir_existed = None\\n            try:\\n                dir_existed = mkdir_p(dirname(test_path))\\n                open(test_path, \\\"a\\\").close()\\n            except OSError:\\n                if dir_existed is False:\\n                    rm_rf(dirname(test_path))\\n                yield EnvironmentNotWritableError(prefix_setup.target_prefix)\\n            else:\\n                if not dir_existed:\\n                    rm_rf(dirname(test_path))\\n                elif not test_path_existed:\\n                    rm_rf(test_path)\\n\\n    def _verify(self, prefix_setups, prefix_action_groups):\\n        transaction_exceptions = tuple(\\n            exc\\n            for exc in UnlinkLinkTransaction._verify_transaction_level(prefix_setups)\\n            if exc\\n        )\\n        if transaction_exceptions:\\n            return transaction_exceptions\\n\\n        exceptions = []\\n        for exc in self.verify_executor.map(\\n            UnlinkLinkTransaction._verify_individual_level,\\n            prefix_action_groups.values(),\\n        ):\\n            if exc:\\n                exceptions.extend(exc)\\n        for exc in self.verify_executor.map(\\n            UnlinkLinkTransaction._verify_prefix_level, prefix_action_groups.items()\\n        ):\\n            if exc:\\n                exceptions.extend(exc)\\n        return exceptions\\n\\n    def _execute(self, all_action_groups):\\n        # unlink unlink_action_groups and unregister_action_groups\\n        unlink_actions = tuple(\\n            group for group in all_action_groups if group.type == \\\"unlink\\\"\\n        )\\n        # link unlink_action_groups and register_action_groups\\n        link_actions = list(\\n            group for group in all_action_groups if group.type == \\\"link\\\"\\n        )\\n        compile_actions = list(\\n            group for group in all_action_groups if group.type == \\\"compile\\\"\\n        )\\n        entry_point_actions = list(\\n            group for group in all_action_groups if group.type == \\\"entry_point\\\"\\n        )\\n        record_actions = list(\\n            group for group in all_action_groups if group.type == \\\"record\\\"\\n        )\\n        make_menu_actions = list(\\n            group for group in all_action_groups if group.type == \\\"make_menus\\\"\\n        )\\n        remove_menu_actions = list(\\n            group for group in all_action_groups if group.type == \\\"remove_menus\\\"\\n        )\\n\\n        with signal_handler(conda_signal_handler), time_recorder(\\\"unlink_link_execute\\\"):\\n            exceptions = []\\n            with Spinner(\\n                \\\"Executing transaction\\\",\\n                not context.verbose and not context.quiet,\\n                context.json,\\n            ):\\n                # Execute unlink actions\\n                for group, register_group, install_side in (\\n                    (unlink_actions, \\\"unregister\\\", False),\\n                    (link_actions, \\\"register\\\", True),\\n                ):\\n                    if not install_side:\\n                        # uninstalling menus must happen prior to unlinking, or else they might\\n                        #   call something that isn't there anymore\\n                        for axngroup in remove_menu_actions:\\n                            UnlinkLinkTransaction._execute_actions(axngroup)\\n\\n                    for axngroup in group:\\n                        is_unlink = axngroup.type == \\\"unlink\\\"\\n                        target_prefix = axngroup.target_prefix\\n                        prec = axngroup.pkg_data\\n                        run_script(\\n                            target_prefix if is_unlink else prec.extracted_package_dir,\\n                            prec,\\n                            \\\"pre-unlink\\\" if is_unlink else \\\"pre-link\\\",\\n                            target_prefix,\\n                        )\\n\\n                    # parallel block 1:\\n                    for exc in self.execute_executor.map(\\n                        UnlinkLinkTransaction._execute_actions, group\\n                    ):\\n                        if exc:\\n                            exceptions.append(exc)\\n\\n                    # post link scripts may employ entry points.  Do them before post-link.\\n                    if install_side:\\n                        for axngroup in entry_point_actions:\\n                            UnlinkLinkTransaction._execute_actions(axngroup)\\n\\n                    # Run post-link or post-unlink scripts and registering AFTER link/unlink,\\n                    #    because they may depend on files in the prefix.  Additionally, run\\n                    #    them serially, just in case order matters (hopefully not)\\n                    for axngroup in group:\\n                        exc = UnlinkLinkTransaction._execute_post_link_actions(axngroup)\\n                        if exc:\\n                            exceptions.append(exc)\\n\\n                    # parallel block 2:\\n                    composite_ag = []\\n                    if install_side:\\n                        composite_ag.extend(record_actions)\\n                        # consolidate compile actions into one big'un for better efficiency\\n                        individual_actions = [\\n                            axn for ag in compile_actions for axn in ag.actions\\n                        ]\\n                        if individual_actions:\\n                            composite = AggregateCompileMultiPycAction(\\n                                *individual_actions\\n                            )\\n                            composite_ag.append(\\n                                ActionGroup(\\n                                    \\\"compile\\\",\\n                                    None,\\n                                    [composite],\\n                                    composite.target_prefix,\\n                                )\\n                            )\\n                    # functions return None unless there was an exception\\n                    for exc in self.execute_executor.map(\\n                        UnlinkLinkTransaction._execute_actions, composite_ag\\n                    ):\\n                        if exc:\\n                            exceptions.append(exc)\\n\\n                    # must do the register actions AFTER all link/unlink is done\\n                    register_actions = tuple(\\n                        group\\n                        for group in all_action_groups\\n                        if group.type == register_group\\n                    )\\n                    for axngroup in register_actions:\\n                        exc = UnlinkLinkTransaction._execute_actions(axngroup)\\n                        if exc:\\n                            exceptions.append(exc)\\n                    if exceptions:\\n                        break\\n                    if install_side:\\n                        # uninstalling menus must happen prior to unlinking, or else they might\\n                        #   call something that isn't there anymore\\n                        for axngroup in make_menu_actions:\\n                            UnlinkLinkTransaction._execute_actions(axngroup)\\n            if exceptions:\\n                # might be good to show all errors, but right now we only show the first\\n                e = exceptions[0]\\n                axngroup = e.errors[1]\\n\\n                action, is_unlink = (None, axngroup.type == \\\"unlink\\\")\\n                prec = axngroup.pkg_data\\n\\n                if prec:\\n                    log.error(\\n                        \\\"An error occurred while {} package '{}'.\\\".format(\\n                            \\\"uninstalling\\\" if is_unlink else \\\"installing\\\",\\n                            prec.dist_str(),\\n                        )\\n                    )\\n\\n                # reverse all executed packages except the one that failed\\n                rollback_excs = []\\n                if context.rollback_enabled:\\n                    with Spinner(\\n                        \\\"Rolling back transaction\\\",\\n                        not context.verbose and not context.quiet,\\n                        context.json,\\n                    ):\\n                        reverse_actions = reversed(tuple(all_action_groups))\\n                        for axngroup in reverse_actions:\\n                            excs = UnlinkLinkTransaction._reverse_actions(axngroup)\\n                            rollback_excs.extend(excs)\\n\\n                raise CondaMultiError(\\n                    (\\n                        *(\\n                            (e.errors[0], e.errors[2:])\\n                            if isinstance(e, CondaMultiError)\\n                            else (e,)\\n                        ),\\n                        *rollback_excs,\\n                    )\\n                )\\n            else:\\n                for axngroup in all_action_groups:\\n                    for action in axngroup.actions:\\n                        action.cleanup()\\n\\n    @staticmethod\\n    def _execute_actions(axngroup):\\n        target_prefix = axngroup.target_prefix\\n        prec = axngroup.pkg_data\\n\\n        conda_meta_dir = join(target_prefix, \\\"conda-meta\\\")\\n        if not isdir(conda_meta_dir):\\n            mkdir_p(conda_meta_dir)\\n\\n        try:\\n            if axngroup.type == \\\"unlink\\\":\\n                log.info(\\n                    \\\"===> UNLINKING PACKAGE: %s <===\\\\n  prefix=%s\\\\n\\\",\\n                    prec.dist_str(),\\n                    target_prefix,\\n                )\\n\\n            elif axngroup.type == \\\"link\\\":\\n                log.info(\\n                    \\\"===> LINKING PACKAGE: %s <===\\\\n  prefix=%s\\\\n  source=%s\\\\n\\\",\\n                    prec.dist_str(),\\n                    target_prefix,\\n                    prec.extracted_package_dir,\\n                )\\n\\n            for action in axngroup.actions:\\n                action.execute()\\n        except Exception as e:  # this won't be a multi error\\n            # reverse this package\\n            reverse_excs = ()\\n            if context.rollback_enabled:\\n                reverse_excs = UnlinkLinkTransaction._reverse_actions(axngroup)\\n            return CondaMultiError(\\n                (\\n                    e,\\n                    axngroup,\\n                    *reverse_excs,\\n                )\\n            )\\n\\n    @staticmethod\\n    def _execute_post_link_actions(axngroup):\\n        target_prefix = axngroup.target_prefix\\n        is_unlink = axngroup.type == \\\"unlink\\\"\\n        prec = axngroup.pkg_data\\n        if prec:\\n            try:\\n                run_script(\\n                    target_prefix,\\n                    prec,\\n                    \\\"post-unlink\\\" if is_unlink else \\\"post-link\\\",\\n                    activate=True,\\n                )\\n            except Exception as e:  # this won't be a multi error\\n                # reverse this package\\n                reverse_excs = ()\\n                if context.rollback_enabled:\\n                    reverse_excs = UnlinkLinkTransaction._reverse_actions(axngroup)\\n                return CondaMultiError(\\n                    (\\n                        e,\\n                        axngroup,\\n                        *reverse_excs,\\n                    )\\n                )\\n\\n    @staticmethod\\n    def _reverse_actions(axngroup, reverse_from_idx=-1):\\n        target_prefix = axngroup.target_prefix\\n\\n        # reverse_from_idx = -1 means reverse all actions\\n        prec = axngroup.pkg_data\\n\\n        if axngroup.type == \\\"unlink\\\":\\n            log.info(\\n                \\\"===> REVERSING PACKAGE UNLINK: %s <===\\\\n  prefix=%s\\\\n\\\",\\n                prec.dist_str(),\\n                target_prefix,\\n            )\\n\\n        elif axngroup.type == \\\"link\\\":\\n            log.info(\\n                \\\"===> REVERSING PACKAGE LINK: %s <===\\\\n  prefix=%s\\\\n\\\",\\n                prec.dist_str(),\\n                target_prefix,\\n            )\\n\\n        exceptions = []\\n        if reverse_from_idx < 0:\\n            reverse_actions = axngroup.actions\\n        else:\\n            reverse_actions = axngroup.actions[: reverse_from_idx + 1]\\n        for axn_idx, action in reversed(tuple(enumerate(reverse_actions))):\\n            try:\\n                action.reverse()\\n            except Exception as e:\\n                log.debug(\\\"action.reverse() error in action %r\\\", action, exc_info=True)\\n                exceptions.append(e)\\n        return exceptions\\n\\n    @staticmethod\\n    def _get_python_version(target_prefix, pcrecs_to_unlink, packages_info_to_link):\\n        # this method determines the python version that will be present at the\\n        # end of the transaction\\n        linking_new_python = next(\\n            (\\n                package_info\\n                for package_info in packages_info_to_link\\n                if package_info.repodata_record.name == \\\"python\\\"\\n            ),\\n            None,\\n        )\\n        if linking_new_python:\\n            # is python being linked? we're done\\n            full_version = linking_new_python.repodata_record.version\\n            assert full_version\\n            log.debug(\\\"found in current transaction python version %s\\\", full_version)\\n            return get_major_minor_version(full_version)\\n\\n        # is python already linked and not being unlinked? that's ok too\\n        linked_python_version = get_python_version_for_prefix(target_prefix)\\n        if linked_python_version:\\n            find_python = (\\n                lnkd_pkg_data\\n                for lnkd_pkg_data in pcrecs_to_unlink\\n                if lnkd_pkg_data.name == \\\"python\\\"\\n            )\\n            unlinking_this_python = next(find_python, None)\\n            if unlinking_this_python is None:\\n                # python is not being unlinked\\n                log.debug(\\n                    \\\"found in current prefix python version %s\\\", linked_python_version\\n                )\\n                return linked_python_version\\n\\n        # there won't be any python in the finished environment\\n        log.debug(\\\"no python version found in prefix\\\")\\n        return None\\n\\n    @staticmethod\\n    def _make_link_actions(\\n        transaction_context,\\n        package_info,\\n        target_prefix,\\n        requested_link_type,\\n        requested_spec,\\n    ):\\n        required_quad = (\\n            transaction_context,\\n            package_info,\\n            target_prefix,\\n            requested_link_type,\\n        )\\n\\n        file_link_actions = LinkPathAction.create_file_link_actions(*required_quad)\\n        create_directory_actions = LinkPathAction.create_directory_actions(\\n            *required_quad, file_link_actions=file_link_actions\\n        )\\n        create_nonadmin_actions = CreateNonadminAction.create_actions(*required_quad)\\n\\n        # the ordering here is significant\\n        return (\\n            *create_directory_actions,\\n            *file_link_actions,\\n            *create_nonadmin_actions,\\n        )\\n\\n    @staticmethod\\n    def _make_entry_point_actions(\\n        transaction_context,\\n        package_info,\\n        target_prefix,\\n        requested_link_type,\\n        requested_spec,\\n        link_action_groups,\\n    ):\\n        required_quad = (\\n            transaction_context,\\n            package_info,\\n            target_prefix,\\n            requested_link_type,\\n        )\\n        return CreatePythonEntryPointAction.create_actions(*required_quad)\\n\\n    @staticmethod\\n    def _make_compile_actions(\\n        transaction_context,\\n        package_info,\\n        target_prefix,\\n        requested_link_type,\\n        requested_spec,\\n        link_action_groups,\\n    ):\\n        required_quad = (\\n            transaction_context,\\n            package_info,\\n            target_prefix,\\n            requested_link_type,\\n        )\\n        link_action_group = next(\\n            ag for ag in link_action_groups if ag.pkg_data == package_info\\n        )\\n        return CompileMultiPycAction.create_actions(\\n            *required_quad, file_link_actions=link_action_group.actions\\n        )\\n\\n    def _make_legacy_action_groups(self):\\n        # this code reverts json output for plan back to previous behavior\\n        #   relied on by Anaconda Navigator and nb_conda\\n        legacy_action_groups = []\\n\\n        if self._pfe is None:\\n            self._get_pfe()\\n\\n        for q, (prefix, setup) in enumerate(self.prefix_setups.items()):\\n            actions = defaultdict(list)\\n            if q == 0:\\n                self._pfe.prepare()\\n                download_urls = {axn.url for axn in self._pfe.cache_actions}\\n                actions[\\\"FETCH\\\"].extend(\\n                    prec for prec in self._pfe.link_precs if prec.url in download_urls\\n                )\\n\\n            actions[\\\"PREFIX\\\"] = setup.target_prefix\\n            for prec in setup.unlink_precs:\\n                actions[\\\"UNLINK\\\"].append(prec)\\n            for prec in setup.link_precs:\\n                # TODO (AV): maybe add warnings about unverified packages here;\\n                # be warned that doing so may break compatibility with other\\n                # applications.\\n                actions[\\\"LINK\\\"].append(prec)\\n\\n            legacy_action_groups.append(actions)\\n\\n        return legacy_action_groups\\n\\n    def print_transaction_summary(self):\\n        legacy_action_groups = self._make_legacy_action_groups()\\n\\n        download_urls = {axn.url for axn in self._pfe.cache_actions}\\n\\n        for actions, (prefix, stp) in zip(\\n            legacy_action_groups, self.prefix_setups.items()\\n        ):\\n            change_report = self._calculate_change_report(\\n                prefix,\\n                stp.unlink_precs,\\n                stp.link_precs,\\n                download_urls,\\n                stp.remove_specs,\\n                stp.update_specs,\\n            )\\n            change_report_str = self._change_report_str(change_report)\\n            print(ensure_text_type(change_report_str))\\n\\n        return legacy_action_groups\\n\\n    def _change_report_str(self, change_report):\\n        # TODO (AV): add warnings about unverified packages in this function\\n        builder = [\\\"\\\", \\\"## Package Plan ##\\\\n\\\"]\\n        builder.append(f\\\"  environment location: {change_report.prefix}\\\")\\n        builder.append(\\\"\\\")\\n        if change_report.specs_to_remove:\\n            builder.append(\\n                \\\"  removed specs:{}\\\".format(\\n                    dashlist(\\n                        sorted(str(s) for s in change_report.specs_to_remove), indent=4\\n                    )\\n                )\\n            )\\n            builder.append(\\\"\\\")\\n        if change_report.specs_to_add:\\n            builder.append(\\n                f\\\"  added / updated specs:{dashlist(sorted(str(s) for s in change_report.specs_to_add), indent=4)}\\\"\\n            )\\n            builder.append(\\\"\\\")\\n\\n        def channel_filt(s):\\n            if context.show_channel_urls is False:\\n                return \\\"\\\"\\n            if context.show_channel_urls is None and s == DEFAULTS_CHANNEL_NAME:\\n                return \\\"\\\"\\n            return s\\n\\n        def print_dists(dists_extras):\\n            lines = []\\n            fmt = \\\"    %-27s|%17s\\\"\\n            lines.append(fmt % (\\\"package\\\", \\\"build\\\"))\\n            lines.append(fmt % (\\\"-\\\" * 27, \\\"-\\\" * 17))\\n            for prec, extra in dists_extras:\\n                line = fmt % (\\n                    strip_global(prec.namekey) + \\\"-\\\" + prec.version,\\n                    prec.build,\\n                )\\n                if extra:\\n                    line += extra\\n                lines.append(line)\\n            return lines\\n\\n        convert_namekey = lambda x: (\\\"0:\\\" + x[7:]) if x.startswith(\\\"global:\\\") else x\\n        strip_global = lambda x: x[7:] if x.startswith(\\\"global:\\\") else x\\n\\n        if change_report.fetch_precs:\\n            builder.append(\\\"\\\\nThe following packages will be downloaded:\\\\n\\\")\\n\\n            disp_lst = []\\n            total_download_bytes = 0\\n            for prec in sorted(\\n                change_report.fetch_precs, key=lambda x: convert_namekey(x.namekey)\\n            ):\\n                size = prec.size\\n                extra = \\\"%15s\\\" % human_bytes(size)\\n                total_download_bytes += size\\n                schannel = channel_filt(str(prec.channel.canonical_name))\\n                if schannel:\\n                    extra += \\\"  \\\" + schannel\\n                disp_lst.append((prec, extra))\\n            builder.extend(print_dists(disp_lst))\\n\\n            builder.append(\\\" \\\" * 4 + \\\"-\\\" * 60)\\n            builder.append(\\\" \\\" * 43 + \\\"Total: %14s\\\" % human_bytes(total_download_bytes))\\n\\n        def diff_strs(unlink_prec, link_prec):\\n            channel_change = unlink_prec.channel.name != link_prec.channel.name\\n            subdir_change = unlink_prec.subdir != link_prec.subdir\\n            version_change = unlink_prec.version != link_prec.version\\n            build_change = unlink_prec.build != link_prec.build\\n\\n            builder_left = []\\n            builder_right = []\\n\\n            if channel_change or subdir_change:\\n                if unlink_prec.channel.name is not None:\\n                    builder_left.append(unlink_prec.channel.name)\\n                if link_prec.channel.name is not None:\\n                    builder_right.append(link_prec.channel.name)\\n            if subdir_change:\\n                builder_left.append(\\\"/\\\" + unlink_prec.subdir)\\n                builder_right.append(\\\"/\\\" + link_prec.subdir)\\n            if (channel_change or subdir_change) and (version_change or build_change):\\n                builder_left.append(\\\"::\\\" + unlink_prec.name + \\\"-\\\")\\n                builder_right.append(\\\"::\\\" + link_prec.name + \\\"-\\\")\\n            if version_change or build_change:\\n                builder_left.append(unlink_prec.version + \\\"-\\\" + unlink_prec.build)\\n                builder_right.append(link_prec.version + \\\"-\\\" + link_prec.build)\\n\\n            return \\\"\\\".join(builder_left), \\\"\\\".join(builder_right)\\n\\n        def add_single(display_key, disp_str):\\n            if len(display_key) > 18:\\n                display_key = display_key[:17] + \\\"~\\\"\\n            builder.append(\\\"  %-18s %s\\\" % (display_key, disp_str))\\n\\n        def add_double(display_key, left_str, right_str):\\n            if len(display_key) > 18:\\n                display_key = display_key[:17] + \\\"~\\\"\\n            if len(left_str) > 38:\\n                left_str = left_str[:37] + \\\"~\\\"\\n            builder.append(\\\"  %-18s %38s --> %s\\\" % (display_key, left_str, right_str))\\n\\n        if change_report.new_precs:\\n            builder.append(\\\"\\\\nThe following NEW packages will be INSTALLED:\\\\n\\\")\\n            for namekey in sorted(change_report.new_precs, key=convert_namekey):\\n                link_prec = change_report.new_precs[namekey]\\n                add_single(\\n                    strip_global(namekey),\\n                    f\\\"{link_prec.record_id()} {' '.join(link_prec.metadata)}\\\",\\n                )\\n\\n        if change_report.removed_precs:\\n            builder.append(\\\"\\\\nThe following packages will be REMOVED:\\\\n\\\")\\n            for namekey in sorted(change_report.removed_precs, key=convert_namekey):\\n                unlink_prec = change_report.removed_precs[namekey]\\n                builder.append(\\n                    f\\\"  {unlink_prec.name}-{unlink_prec.version}-{unlink_prec.build}\\\"\\n                )\\n\\n        if change_report.updated_precs:\\n            builder.append(\\\"\\\\nThe following packages will be UPDATED:\\\\n\\\")\\n            for namekey in sorted(change_report.updated_precs, key=convert_namekey):\\n                unlink_prec, link_prec = change_report.updated_precs[namekey]\\n                left_str, right_str = diff_strs(unlink_prec, link_prec)\\n                add_double(\\n                    strip_global(namekey),\\n                    left_str,\\n                    f\\\"{right_str} {' '.join(link_prec.metadata)}\\\",\\n                )\\n\\n        if change_report.superseded_precs:\\n            builder.append(\\n                \\\"\\\\nThe following packages will be SUPERSEDED \\\"\\n                \\\"by a higher-priority channel:\\\\n\\\"\\n            )\\n            for namekey in sorted(change_report.superseded_precs, key=convert_namekey):\\n                unlink_prec, link_prec = change_report.superseded_precs[namekey]\\n                left_str, right_str = diff_strs(unlink_prec, link_prec)\\n                add_double(\\n                    strip_global(namekey),\\n                    left_str,\\n                    f\\\"{right_str} {' '.join(link_prec.metadata)}\\\",\\n                )\\n\\n        if change_report.downgraded_precs:\\n            builder.append(\\\"\\\\nThe following packages will be DOWNGRADED:\\\\n\\\")\\n            for namekey in sorted(change_report.downgraded_precs, key=convert_namekey):\\n                unlink_prec, link_prec = change_report.downgraded_precs[namekey]\\n                left_str, right_str = diff_strs(unlink_prec, link_prec)\\n                add_double(\\n                    strip_global(namekey),\\n                    left_str,\\n                    f\\\"{right_str} {' '.join(link_prec.metadata)}\\\",\\n                )\\n        builder.append(\\\"\\\")\\n        builder.append(\\\"\\\")\\n        return \\\"\\\\n\\\".join(builder)\\n\\n    @staticmethod\\n    def _calculate_change_report(\\n        prefix, unlink_precs, link_precs, download_urls, specs_to_remove, specs_to_add\\n    ):\\n        unlink_map = {prec.namekey: prec for prec in unlink_precs}\\n        link_map = {prec.namekey: prec for prec in link_precs}\\n        unlink_namekeys, link_namekeys = set(unlink_map), set(link_map)\\n\\n        removed_precs = {\\n            namekey: unlink_map[namekey]\\n            for namekey in (unlink_namekeys - link_namekeys)\\n        }\\n        new_precs = {\\n            namekey: link_map[namekey] for namekey in (link_namekeys - unlink_namekeys)\\n        }\\n\\n        # updated means a version increase, or a build number increase\\n        # downgraded means a version decrease, or build number decrease, but channel canonical_name\\n        #   has to be the same\\n        # superseded then should be everything else left over\\n        updated_precs = {}\\n        downgraded_precs = {}\\n        superseded_precs = {}\\n\\n        common_namekeys = link_namekeys & unlink_namekeys\\n        for namekey in common_namekeys:\\n            unlink_prec, link_prec = unlink_map[namekey], link_map[namekey]\\n            unlink_vo = VersionOrder(unlink_prec.version)\\n            link_vo = VersionOrder(link_prec.version)\\n            build_number_increases = link_prec.build_number > unlink_prec.build_number\\n            if link_vo == unlink_vo and build_number_increases or link_vo > unlink_vo:\\n                updated_precs[namekey] = (unlink_prec, link_prec)\\n            elif (\\n                link_prec.channel.name == unlink_prec.channel.name\\n                and link_prec.subdir == unlink_prec.subdir\\n            ):\\n                if link_prec == unlink_prec:\\n                    # noarch: python packages are re-linked on a python version change\\n                    # just leave them out of the package report\\n                    continue\\n                downgraded_precs[namekey] = (unlink_prec, link_prec)\\n            else:\\n                superseded_precs[namekey] = (unlink_prec, link_prec)\\n\\n        fetch_precs = {prec for prec in link_precs if prec.url in download_urls}\\n        change_report = ChangeReport(\\n            prefix,\\n            specs_to_remove,\\n            specs_to_add,\\n            removed_precs,\\n            new_precs,\\n            updated_precs,\\n            downgraded_precs,\\n            superseded_precs,\\n            fetch_precs,\\n        )\\n        return change_report\\n\\n\\ndef run_script(\\n    prefix: str,\\n    prec,\\n    action: str = \\\"post-link\\\",\\n    env_prefix: str = None,\\n    activate: bool = False,\\n) -> bool:\\n    \\\"\\\"\\\"\\n    Call the post-link (or pre-unlink) script, returning True on success,\\n    False on failure.\\n    \\\"\\\"\\\"\\n    path = join(\\n        prefix,\\n        \\\"Scripts\\\" if on_win else \\\"bin\\\",\\n        \\\".{}-{}.{}\\\".format(prec.name, action, \\\"bat\\\" if on_win else \\\"sh\\\"),\\n    )\\n    if not isfile(path):\\n        return True\\n\\n    env = os.environ.copy()\\n\\n    if action == \\\"pre-link\\\":  # pragma: no cover\\n        # old no-arch support; deprecated\\n        is_old_noarch = False\\n        try:\\n            with open(path) as f:\\n                script_text = ensure_text_type(f.read())\\n            if (\\n                on_win and \\\"%PREFIX%\\\\\\\\python.exe %SOURCE_DIR%\\\\\\\\link.py\\\" in script_text\\n            ) or \\\"$PREFIX/bin/python $SOURCE_DIR/link.py\\\" in script_text:\\n                is_old_noarch = True\\n        except Exception as e:\\n            log.debug(e, exc_info=True)\\n\\n        env[\\\"SOURCE_DIR\\\"] = prefix\\n        if not is_old_noarch:\\n            warnings.warn(\\n                dals(\\n                    \\\"\\\"\\\"\\n            Package %s uses a pre-link script. Pre-link scripts are potentially dangerous.\\n            This is because pre-link scripts have the ability to change the package contents in the\\n            package cache, and therefore modify the underlying files for already-created conda\\n            environments.  Future versions of conda may deprecate and ignore pre-link scripts.\\n            \\\"\\\"\\\"\\n                )\\n                % prec.dist_str()\\n            )\\n\\n    script_caller = None\\n    if on_win:\\n        try:\\n            comspec = get_comspec()  # fail early with KeyError if undefined\\n        except KeyError:\\n            log.info(\\n                \\\"failed to run %s for %s due to COMSPEC KeyError\\\",\\n                action,\\n                prec.dist_str(),\\n            )\\n            return False\\n        if activate:\\n            script_caller, command_args = wrap_subprocess_call(\\n                context.root_prefix,\\n                prefix,\\n                context.dev,\\n                False,\\n                (\\\"@CALL\\\", path),\\n            )\\n        else:\\n            command_args = [comspec, \\\"/d\\\", \\\"/c\\\", path]\\n    else:\\n        shell_path = \\\"sh\\\" if \\\"bsd\\\" in sys.platform else \\\"bash\\\"\\n        if activate:\\n            script_caller, command_args = wrap_subprocess_call(\\n                context.root_prefix,\\n                prefix,\\n                context.dev,\\n                False,\\n                (\\\".\\\", path),\\n            )\\n        else:\\n            shell_path = \\\"sh\\\" if \\\"bsd\\\" in sys.platform else \\\"bash\\\"\\n            command_args = [shell_path, \\\"-x\\\", path]\\n\\n    env[\\\"ROOT_PREFIX\\\"] = context.root_prefix\\n    env[\\\"PREFIX\\\"] = env_prefix or prefix\\n    env[\\\"PKG_NAME\\\"] = prec.name\\n    env[\\\"PKG_VERSION\\\"] = prec.version\\n    env[\\\"PKG_BUILDNUM\\\"] = prec.build_number\\n    env[\\\"PATH\\\"] = os.pathsep.join((dirname(path), env.get(\\\"PATH\\\", \\\"\\\")))\\n\\n    log.debug(\\n        \\\"for %s at %s, executing script: $ %s\\\",\\n        prec.dist_str(),\\n        env[\\\"PREFIX\\\"],\\n        \\\" \\\".join(command_args),\\n    )\\n    try:\\n        response = subprocess_call(\\n            command_args, env=env, path=dirname(path), raise_on_error=False\\n        )\\n        if response.rc != 0:\\n            m = messages(prefix)\\n            if action in (\\\"pre-link\\\", \\\"post-link\\\"):\\n                if \\\"openssl\\\" in prec.dist_str():\\n                    # this is a hack for conda-build string parsing in the conda_build/build.py\\n                    #   create_env function\\n                    message = f\\\"{action} failed for: {prec}\\\"\\n                else:\\n                    message = dals(\\n                        \\\"\\\"\\\"\\n                    %s script failed for package %s\\n                    location of failed script: %s\\n                    ==> script messages <==\\n                    %s\\n                    ==> script output <==\\n                    stdout: %s\\n                    stderr: %s\\n                    return code: %s\\n                    \\\"\\\"\\\"\\n                    ) % (\\n                        action,\\n                        prec.dist_str(),\\n                        path,\\n                        m or \\\"<None>\\\",\\n                        response.stdout,\\n                        response.stderr,\\n                        response.rc,\\n                    )\\n                raise LinkError(message)\\n            else:\\n                log.warning(\\n                    \\\"%s script failed for package %s\\\\n\\\"\\n                    \\\"consider notifying the package maintainer\\\",\\n                    action,\\n                    prec.dist_str(),\\n                )\\n                return False\\n        else:\\n            messages(prefix)\\n            return True\\n    finally:\\n        if script_caller is not None:\\n            if \\\"CONDA_TEST_SAVE_TEMPS\\\" not in os.environ:\\n                rm_rf(script_caller)\\n            else:\\n                log.warning(\\n                    f\\\"CONDA_TEST_SAVE_TEMPS :: retaining run_script {script_caller}\\\"\\n                )\\n\\n\\ndef messages(prefix):\\n    path = join(prefix, \\\".messages.txt\\\")\\n    try:\\n        if isfile(path):\\n            with open(path) as fi:\\n                m = fi.read()\\n                if hasattr(m, \\\"decode\\\"):\\n                    m = m.decode(\\\"utf-8\\\")\\n                print(m, file=sys.stderr if context.json else sys.stdout)\\n                return m\\n    finally:\\n        rm_rf(path)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Tools for managing the package cache (previously downloaded packages).\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport codecs\\nimport os\\nfrom collections import defaultdict\\nfrom concurrent.futures import CancelledError, ThreadPoolExecutor, as_completed\\nfrom errno import EACCES, ENOENT, EPERM, EROFS\\nfrom functools import partial\\nfrom itertools import chain\\nfrom json import JSONDecodeError\\nfrom logging import getLogger\\nfrom os import scandir\\nfrom os.path import basename, dirname, getsize, join\\nfrom sys import platform\\nfrom tarfile import ReadError\\nfrom typing import TYPE_CHECKING\\n\\nfrom .. import CondaError, CondaMultiError, conda_signal_handler\\nfrom ..auxlib.collection import first\\nfrom ..auxlib.decorators import memoizemethod\\nfrom ..auxlib.entity import ValidationError\\nfrom ..base.constants import (\\n    CONDA_PACKAGE_EXTENSION_V1,\\n    CONDA_PACKAGE_EXTENSION_V2,\\n    CONDA_PACKAGE_EXTENSIONS,\\n    PACKAGE_CACHE_MAGIC_FILE,\\n)\\nfrom ..base.context import context\\nfrom ..common.constants import NULL, TRACE\\nfrom ..common.io import IS_INTERACTIVE, ProgressBar, time_recorder\\nfrom ..common.iterators import groupby_to_dict as groupby\\nfrom ..common.path import expand, strip_pkg_extension, url_to_path\\nfrom ..common.signals import signal_handler\\nfrom ..common.url import path_to_url\\nfrom ..deprecations import deprecated\\nfrom ..exceptions import NotWritableError, NoWritablePkgsDirError\\nfrom ..gateways.disk.create import (\\n    create_package_cache_directory,\\n    extract_tarball,\\n    write_as_json_to_file,\\n)\\nfrom ..gateways.disk.delete import rm_rf\\nfrom ..gateways.disk.read import (\\n    compute_sum,\\n    isdir,\\n    isfile,\\n    islink,\\n    read_index_json,\\n    read_index_json_from_tarball,\\n    read_repodata_json,\\n)\\nfrom ..gateways.disk.test import file_path_is_writable\\nfrom ..models.match_spec import MatchSpec\\nfrom ..models.records import PackageCacheRecord, PackageRecord\\nfrom ..utils import human_bytes\\nfrom .path_actions import CacheUrlAction, ExtractPackageAction\\n\\nif TYPE_CHECKING:\\n    from concurrent.futures import Future\\n    from pathlib import Path\\n\\nlog = getLogger(__name__)\\n\\nFileNotFoundError = IOError\\n\\ntry:\\n    from conda_package_handling.api import THREADSAFE_EXTRACT\\nexcept ImportError:\\n    THREADSAFE_EXTRACT = False\\n# On the machines we tested, extraction doesn't get any faster after 3 threads\\nEXTRACT_THREADS = min(os.cpu_count() or 1, 3) if THREADSAFE_EXTRACT else 1\\n\\n\\nclass PackageCacheType(type):\\n    \\\"\\\"\\\"This metaclass does basic caching of PackageCache instance objects.\\\"\\\"\\\"\\n\\n    def __call__(cls, pkgs_dir: str | os.PathLike | Path):\\n        if isinstance(pkgs_dir, PackageCacheData):\\n            return pkgs_dir\\n        elif (pkgs_dir := str(pkgs_dir)) in PackageCacheData._cache_:\\n            return PackageCacheData._cache_[pkgs_dir]\\n        else:\\n            package_cache_instance = super().__call__(pkgs_dir)\\n            PackageCacheData._cache_[pkgs_dir] = package_cache_instance\\n            return package_cache_instance\\n\\n\\nclass PackageCacheData(metaclass=PackageCacheType):\\n    _cache_: dict[str, PackageCacheData] = {}\\n\\n    def __init__(self, pkgs_dir):\\n        self.pkgs_dir = pkgs_dir\\n        self.__package_cache_records = None\\n        self.__is_writable = NULL\\n\\n        self._urls_data = UrlsData(pkgs_dir)\\n\\n    def insert(self, package_cache_record):\\n        meta = join(\\n            package_cache_record.extracted_package_dir, \\\"info\\\", \\\"repodata_record.json\\\"\\n        )\\n        write_as_json_to_file(meta, PackageRecord.from_objects(package_cache_record))\\n\\n        self._package_cache_records[package_cache_record] = package_cache_record\\n\\n    def load(self):\\n        self.__package_cache_records = _package_cache_records = {}\\n        self._check_writable()  # called here to create the cache if it doesn't exist\\n        if not isdir(self.pkgs_dir):\\n            # no directory exists, and we didn't have permissions to create it\\n            return\\n\\n        _CONDA_TARBALL_EXTENSIONS = CONDA_PACKAGE_EXTENSIONS\\n        pkgs_dir_contents = tuple(entry.name for entry in scandir(self.pkgs_dir))\\n        for base_name in self._dedupe_pkgs_dir_contents(pkgs_dir_contents):\\n            full_path = join(self.pkgs_dir, base_name)\\n            if islink(full_path):\\n                continue\\n            elif (\\n                isdir(full_path)\\n                and isfile(join(full_path, \\\"info\\\", \\\"index.json\\\"))\\n                or isfile(full_path)\\n                and full_path.endswith(_CONDA_TARBALL_EXTENSIONS)\\n            ):\\n                try:\\n                    package_cache_record = self._make_single_record(base_name)\\n                except ValidationError as err:\\n                    # ValidationError: package fields are invalid\\n                    log.warning(\\n                        f\\\"Failed to create package cache record for '{base_name}'. {err}\\\"\\n                    )\\n                    package_cache_record = None\\n\\n                # if package_cache_record is None, it means we couldn't create a record, ignore\\n                if package_cache_record:\\n                    _package_cache_records[package_cache_record] = package_cache_record\\n\\n    def reload(self):\\n        self.load()\\n        return self\\n\\n    def get(self, package_ref, default=NULL):\\n        assert isinstance(package_ref, PackageRecord)\\n        try:\\n            return self._package_cache_records[package_ref]\\n        except KeyError:\\n            if default is not NULL:\\n                return default\\n            else:\\n                raise\\n\\n    def remove(self, package_ref, default=NULL):\\n        if default is NULL:\\n            return self._package_cache_records.pop(package_ref)\\n        else:\\n            return self._package_cache_records.pop(package_ref, default)\\n\\n    def query(self, package_ref_or_match_spec):\\n        # returns a generator\\n        param = package_ref_or_match_spec\\n        if isinstance(param, str):\\n            param = MatchSpec(param)\\n        if isinstance(param, MatchSpec):\\n            return (\\n                pcrec\\n                for pcrec in self._package_cache_records.values()\\n                if param.match(pcrec)\\n            )\\n        else:\\n            assert isinstance(param, PackageRecord)\\n            return (\\n                pcrec\\n                for pcrec in self._package_cache_records.values()\\n                if pcrec == param\\n            )\\n\\n    def iter_records(self):\\n        return iter(self._package_cache_records)\\n\\n    @classmethod\\n    def query_all(cls, package_ref_or_match_spec, pkgs_dirs=None):\\n        if pkgs_dirs is None:\\n            pkgs_dirs = context.pkgs_dirs\\n\\n        return chain.from_iterable(\\n            pcache.query(package_ref_or_match_spec)\\n            for pcache in cls.all_caches_writable_first(pkgs_dirs)\\n        )\\n\\n    # ##########################################################################################\\n    # these class methods reach across all package cache directories (usually context.pkgs_dirs)\\n    # ##########################################################################################\\n\\n    @classmethod\\n    def first_writable(cls, pkgs_dirs=None):\\n        # Calling this method will *create* a package cache directory if one does not already\\n        # exist. Any caller should intend to *use* that directory for *writing*, not just reading.\\n        if pkgs_dirs is None:\\n            pkgs_dirs = context.pkgs_dirs\\n        for pkgs_dir in pkgs_dirs:\\n            package_cache = cls(pkgs_dir)\\n            i_wri = package_cache.is_writable\\n            if i_wri is True:\\n                return package_cache\\n            elif i_wri is None:\\n                # means package cache directory doesn't exist, need to try to create it\\n                try:\\n                    created = create_package_cache_directory(package_cache.pkgs_dir)\\n                except NotWritableError:\\n                    continue\\n                if created:\\n                    package_cache.__is_writable = True\\n                    return package_cache\\n\\n        raise NoWritablePkgsDirError(pkgs_dirs)\\n\\n    @classmethod\\n    def writable_caches(cls, pkgs_dirs=None):\\n        if pkgs_dirs is None:\\n            pkgs_dirs = context.pkgs_dirs\\n        writable_caches = tuple(\\n            filter(lambda c: c.is_writable, (cls(pd) for pd in pkgs_dirs))\\n        )\\n        return writable_caches\\n\\n    @classmethod\\n    def read_only_caches(cls, pkgs_dirs=None):\\n        if pkgs_dirs is None:\\n            pkgs_dirs = context.pkgs_dirs\\n        read_only_caches = tuple(\\n            filter(lambda c: not c.is_writable, (cls(pd) for pd in pkgs_dirs))\\n        )\\n        return read_only_caches\\n\\n    @classmethod\\n    def all_caches_writable_first(cls, pkgs_dirs=None):\\n        if pkgs_dirs is None:\\n            pkgs_dirs = context.pkgs_dirs\\n        pc_groups = groupby(lambda pc: pc.is_writable, (cls(pd) for pd in pkgs_dirs))\\n        return (*pc_groups.get(True, ()), *pc_groups.get(False, ()))\\n\\n    @classmethod\\n    def get_all_extracted_entries(cls):\\n        package_caches = (cls(pd) for pd in context.pkgs_dirs)\\n        return tuple(\\n            pc_entry\\n            for pc_entry in chain.from_iterable(\\n                package_cache.values() for package_cache in package_caches\\n            )\\n            if pc_entry.is_extracted\\n        )\\n\\n    @classmethod\\n    def get_entry_to_link(cls, package_ref):\\n        pc_entry = next(\\n            (pcrec for pcrec in cls.query_all(package_ref) if pcrec.is_extracted), None\\n        )\\n        if pc_entry is not None:\\n            return pc_entry\\n\\n        # this can happen with `conda install path/to/package.tar.bz2`\\n        #   because dist has channel '<unknown>'\\n        # if ProgressiveFetchExtract did its job correctly, what we're looking for\\n        #   should be the matching dist_name in the first writable package cache\\n        # we'll search all caches for a match, but search writable caches first\\n        dist_str = package_ref.dist_str().rsplit(\\\":\\\", 1)[-1]\\n        pc_entry = next(\\n            (\\n                cache._scan_for_dist_no_channel(dist_str)\\n                for cache in cls.all_caches_writable_first()\\n                if cache\\n            ),\\n            None,\\n        )\\n        if pc_entry is not None:\\n            return pc_entry\\n        raise CondaError(\\n            f\\\"No package '{package_ref.dist_str()}' found in cache directories.\\\"\\n        )\\n\\n    @classmethod\\n    def tarball_file_in_cache(cls, tarball_path, md5sum=None, exclude_caches=()):\\n        tarball_full_path, md5sum = cls._clean_tarball_path_and_get_md5sum(\\n            tarball_path, md5sum\\n        )\\n        pc_entry = first(\\n            cls(pkgs_dir).tarball_file_in_this_cache(tarball_full_path, md5sum)\\n            for pkgs_dir in context.pkgs_dirs\\n            if pkgs_dir not in exclude_caches\\n        )\\n        return pc_entry\\n\\n    @classmethod\\n    def clear(cls):\\n        cls._cache_.clear()\\n\\n    def tarball_file_in_this_cache(self, tarball_path, md5sum=None):\\n        tarball_full_path, md5sum = self._clean_tarball_path_and_get_md5sum(\\n            tarball_path, md5sum\\n        )\\n        tarball_basename = basename(tarball_full_path)\\n        pc_entry = first(\\n            (pc_entry for pc_entry in self.values()),\\n            key=lambda pce: pce.tarball_basename == tarball_basename\\n            and pce.md5 == md5sum,\\n        )\\n        return pc_entry\\n\\n    @property\\n    def _package_cache_records(self):\\n        # don't actually populate _package_cache_records until we need it\\n        if self.__package_cache_records is None:\\n            self.load()\\n        return self.__package_cache_records\\n\\n    @property\\n    def is_writable(self):\\n        # returns None if package cache directory does not exist / has not been created\\n        if self.__is_writable is NULL:\\n            return self._check_writable()\\n        return self.__is_writable\\n\\n    def _check_writable(self):\\n        magic_file = join(self.pkgs_dir, PACKAGE_CACHE_MAGIC_FILE)\\n        if isfile(magic_file):\\n            i_wri = file_path_is_writable(join(self.pkgs_dir, PACKAGE_CACHE_MAGIC_FILE))\\n            self.__is_writable = i_wri\\n            log.debug(\\\"package cache directory '%s' writable: %s\\\", self.pkgs_dir, i_wri)\\n        else:\\n            log.log(TRACE, \\\"package cache directory '%s' does not exist\\\", self.pkgs_dir)\\n            self.__is_writable = i_wri = None\\n        return i_wri\\n\\n    @staticmethod\\n    def _clean_tarball_path_and_get_md5sum(tarball_path, md5sum=None):\\n        if tarball_path.startswith(\\\"file:/\\\"):\\n            tarball_path = url_to_path(tarball_path)\\n        tarball_full_path = expand(tarball_path)\\n\\n        if isfile(tarball_full_path) and md5sum is None:\\n            md5sum = compute_sum(tarball_full_path, \\\"md5\\\")\\n\\n        return tarball_full_path, md5sum\\n\\n    def _scan_for_dist_no_channel(self, dist_str):\\n        return next(\\n            (\\n                pcrec\\n                for pcrec in self._package_cache_records\\n                if pcrec.dist_str().rsplit(\\\":\\\", 1)[-1] == dist_str\\n            ),\\n            None,\\n        )\\n\\n    def itervalues(self):\\n        return iter(self.values())\\n\\n    def values(self):\\n        return self._package_cache_records.values()\\n\\n    def __repr__(self):\\n        args = (f\\\"{key}={getattr(self, key)!r}\\\" for key in (\\\"pkgs_dir\\\",))\\n        return \\\"{}({})\\\".format(self.__class__.__name__, \\\", \\\".join(args))\\n\\n    def _make_single_record(self, package_filename):\\n        # delay-load this to help make sure libarchive can be found\\n        from conda_package_handling.api import InvalidArchiveError\\n\\n        package_tarball_full_path = join(self.pkgs_dir, package_filename)\\n        log.log(TRACE, \\\"adding to package cache %s\\\", package_tarball_full_path)\\n        extracted_package_dir, pkg_ext = strip_pkg_extension(package_tarball_full_path)\\n\\n        # try reading info/repodata_record.json\\n        try:\\n            repodata_record = read_repodata_json(extracted_package_dir)\\n            package_cache_record = PackageCacheRecord.from_objects(\\n                repodata_record,\\n                package_tarball_full_path=package_tarball_full_path,\\n                extracted_package_dir=extracted_package_dir,\\n            )\\n            return package_cache_record\\n        except (OSError, JSONDecodeError, ValueError, FileNotFoundError) as e:\\n            # EnvironmentError if info/repodata_record.json doesn't exists\\n            # JsonDecodeError if info/repodata_record.json is partially extracted or corrupted\\n            #   python 2.7 raises ValueError instead of JsonDecodeError\\n            #   ValueError(\\\"No JSON object could be decoded\\\")\\n            log.debug(\\n                \\\"unable to read %s\\\\n  because %r\\\",\\n                join(extracted_package_dir, \\\"info\\\", \\\"repodata_record.json\\\"),\\n                e,\\n            )\\n\\n            # try reading info/index.json\\n            try:\\n                raw_json_record = read_index_json(extracted_package_dir)\\n            except (OSError, JSONDecodeError, ValueError, FileNotFoundError) as e:\\n                # EnvironmentError if info/index.json doesn't exist\\n                # JsonDecodeError if info/index.json is partially extracted or corrupted\\n                #   python 2.7 raises ValueError instead of JsonDecodeError\\n                #   ValueError(\\\"No JSON object could be decoded\\\")\\n                log.debug(\\n                    \\\"unable to read %s\\\\n  because %r\\\",\\n                    join(extracted_package_dir, \\\"info\\\", \\\"index.json\\\"),\\n                    e,\\n                )\\n\\n                if isdir(extracted_package_dir) and not isfile(\\n                    package_tarball_full_path\\n                ):\\n                    # We have a directory that looks like a conda package, but without\\n                    # (1) info/repodata_record.json or info/index.json, and (2) a conda package\\n                    # tarball, there's not much we can do.  We'll just ignore it.\\n                    return None\\n\\n                try:\\n                    if self.is_writable:\\n                        if isdir(extracted_package_dir):\\n                            # We have a partially unpacked conda package directory. Best thing\\n                            # to do is remove it and try extracting.\\n                            rm_rf(extracted_package_dir)\\n                        try:\\n                            extract_tarball(\\n                                package_tarball_full_path, extracted_package_dir\\n                            )\\n                        except (OSError, InvalidArchiveError) as e:\\n                            if e.errno == ENOENT:\\n                                # FileNotFoundError(2, 'No such file or directory')\\n                                # At this point, we can assume the package tarball is bad.\\n                                # Remove everything and move on.\\n                                # see https://github.com/conda/conda/issues/6707\\n                                rm_rf(package_tarball_full_path)\\n                                rm_rf(extracted_package_dir)\\n                                return None\\n                        try:\\n                            raw_json_record = read_index_json(extracted_package_dir)\\n                        except (OSError, JSONDecodeError, FileNotFoundError):\\n                            # At this point, we can assume the package tarball is bad.\\n                            # Remove everything and move on.\\n                            rm_rf(package_tarball_full_path)\\n                            rm_rf(extracted_package_dir)\\n                            return None\\n                    else:\\n                        raw_json_record = read_index_json_from_tarball(\\n                            package_tarball_full_path\\n                        )\\n                except (\\n                    EOFError,\\n                    ReadError,\\n                    FileNotFoundError,\\n                    InvalidArchiveError,\\n                ) as e:\\n                    # EOFError: Compressed file ended before the end-of-stream marker was reached\\n                    # tarfile.ReadError: file could not be opened successfully\\n                    # We have a corrupted tarball. Remove the tarball so it doesn't affect\\n                    # anything, and move on.\\n                    log.debug(\\n                        \\\"unable to extract info/index.json from %s\\\\n  because %r\\\",\\n                        package_tarball_full_path,\\n                        e,\\n                    )\\n                    rm_rf(package_tarball_full_path)\\n                    return None\\n\\n            # we were able to read info/index.json, so let's continue\\n            if isfile(package_tarball_full_path):\\n                md5 = compute_sum(package_tarball_full_path, \\\"md5\\\")\\n            else:\\n                md5 = None\\n\\n            url = self._urls_data.get_url(package_filename)\\n            package_cache_record = PackageCacheRecord.from_objects(\\n                raw_json_record,\\n                url=url,\\n                fn=basename(package_tarball_full_path),\\n                md5=md5,\\n                size=getsize(package_tarball_full_path),\\n                package_tarball_full_path=package_tarball_full_path,\\n                extracted_package_dir=extracted_package_dir,\\n            )\\n\\n            # write the info/repodata_record.json file so we can short-circuit this next time\\n            if self.is_writable:\\n                repodata_record = PackageRecord.from_objects(package_cache_record)\\n                repodata_record_path = join(\\n                    extracted_package_dir, \\\"info\\\", \\\"repodata_record.json\\\"\\n                )\\n                try:\\n                    write_as_json_to_file(repodata_record_path, repodata_record)\\n                except OSError as e:\\n                    if e.errno in (EACCES, EPERM, EROFS) and isdir(\\n                        dirname(repodata_record_path)\\n                    ):\\n                        raise NotWritableError(\\n                            repodata_record_path, e.errno, caused_by=e\\n                        )\\n                    else:\\n                        raise\\n\\n            return package_cache_record\\n\\n    @staticmethod\\n    def _dedupe_pkgs_dir_contents(pkgs_dir_contents):\\n        # if both 'six-1.10.0-py35_0/' and 'six-1.10.0-py35_0.tar.bz2' are in pkgs_dir,\\n        #   only 'six-1.10.0-py35_0.tar.bz2' will be in the return contents\\n        if not pkgs_dir_contents:\\n            return []\\n        _CONDA_TARBALL_EXTENSION_V1 = CONDA_PACKAGE_EXTENSION_V1\\n        _CONDA_TARBALL_EXTENSION_V2 = CONDA_PACKAGE_EXTENSION_V2\\n        _strip_pkg_extension = strip_pkg_extension\\n        groups = defaultdict(set)\\n        any(\\n            groups[ext].add(fn_root)\\n            for fn_root, ext in (_strip_pkg_extension(fn) for fn in pkgs_dir_contents)\\n        )\\n        conda_extensions = groups[_CONDA_TARBALL_EXTENSION_V2]\\n        tar_bz2_extensions = groups[_CONDA_TARBALL_EXTENSION_V1] - conda_extensions\\n        others = groups[None] - conda_extensions - tar_bz2_extensions\\n        return sorted(\\n            (\\n                *(path + _CONDA_TARBALL_EXTENSION_V2 for path in conda_extensions),\\n                *(path + _CONDA_TARBALL_EXTENSION_V1 for path in tar_bz2_extensions),\\n                *others,\\n            )\\n        )\\n\\n\\nclass UrlsData:\\n    # this is a class to manage urls.txt\\n    # it should basically be thought of as a sequence\\n    # in this class I'm breaking the rule that all disk access goes through conda.gateways\\n\\n    def __init__(self, pkgs_dir):\\n        self.pkgs_dir = pkgs_dir\\n        self.urls_txt_path = urls_txt_path = join(pkgs_dir, \\\"urls.txt\\\")\\n        if isfile(urls_txt_path):\\n            with open(urls_txt_path, \\\"rb\\\") as fh:\\n                self._urls_data = [line.strip().decode(\\\"utf-8\\\") for line in fh]\\n                self._urls_data.reverse()\\n        else:\\n            self._urls_data = []\\n\\n    def __contains__(self, url):\\n        return url in self._urls_data\\n\\n    def __iter__(self):\\n        return iter(self._urls_data)\\n\\n    def add_url(self, url):\\n        with codecs.open(self.urls_txt_path, mode=\\\"ab\\\", encoding=\\\"utf-8\\\") as fh:\\n            linefeed = \\\"\\\\r\\\\n\\\" if platform == \\\"win32\\\" else \\\"\\\\n\\\"\\n            fh.write(url + linefeed)\\n        self._urls_data.insert(0, url)\\n\\n    @memoizemethod\\n    def get_url(self, package_path):\\n        # package path can be a full path or just a basename\\n        #   can be either an extracted directory or tarball\\n        package_path = basename(package_path)\\n        # NOTE: This makes an assumption that all extensionless packages came from a .tar.bz2.\\n        #       That's probably a good assumption going forward, because we should now always\\n        #       be recording the extension in urls.txt.  The extensionless situation should be\\n        #       legacy behavior only.\\n        if not package_path.endswith(CONDA_PACKAGE_EXTENSIONS):\\n            package_path += CONDA_PACKAGE_EXTENSION_V1\\n        return first(self, lambda url: basename(url) == package_path)\\n\\n\\n# ##############################\\n# downloading\\n# ##############################\\n\\n\\nclass ProgressiveFetchExtract:\\n    @staticmethod\\n    def make_actions_for_record(pref_or_spec):\\n        assert pref_or_spec is not None\\n        # returns a cache_action and extract_action\\n\\n        # if the pref or spec has an md5 value\\n        # look in all caches for package cache record that is\\n        #   (1) already extracted, and\\n        #   (2) matches the md5\\n        # If one exists, no actions are needed.\\n        sha256 = pref_or_spec.get(\\\"sha256\\\")\\n        size = pref_or_spec.get(\\\"size\\\")\\n        md5 = pref_or_spec.get(\\\"md5\\\")\\n        legacy_bz2_size = pref_or_spec.get(\\\"legacy_bz2_size\\\")\\n        legacy_bz2_md5 = pref_or_spec.get(\\\"legacy_bz2_md5\\\")\\n\\n        def pcrec_matches(pcrec):\\n            matches = True\\n            # sha256 is overkill for things that are already in the package cache.\\n            #     It's just a quick match.\\n            # if sha256 is not None and pcrec.sha256 is not None:\\n            #     matches = sha256 == pcrec.sha256\\n            if size is not None and pcrec.get(\\\"size\\\") is not None:\\n                matches = pcrec.size in (size, legacy_bz2_size)\\n            if matches and md5 is not None and pcrec.get(\\\"md5\\\") is not None:\\n                matches = pcrec.md5 in (md5, legacy_bz2_md5)\\n            return matches\\n\\n        extracted_pcrec = next(\\n            (\\n                pcrec\\n                for pcrec in chain.from_iterable(\\n                    PackageCacheData(pkgs_dir).query(pref_or_spec)\\n                    for pkgs_dir in context.pkgs_dirs\\n                )\\n                if pcrec.is_extracted\\n            ),\\n            None,\\n        )\\n        if (\\n            extracted_pcrec\\n            and pcrec_matches(extracted_pcrec)\\n            and extracted_pcrec.get(\\\"url\\\")\\n        ):\\n            return None, None\\n\\n        # there is no extracted dist that can work, so now we look for tarballs that\\n        #   aren't extracted\\n        # first we look in all writable caches, and if we find a match, we extract in place\\n        # otherwise, if we find a match in a non-writable cache, we link it to the first writable\\n        #   cache, and then extract\\n        pcrec_from_writable_cache = next(\\n            (\\n                pcrec\\n                for pcrec in chain.from_iterable(\\n                    pcache.query(pref_or_spec)\\n                    for pcache in PackageCacheData.writable_caches()\\n                )\\n                if pcrec.is_fetched\\n            ),\\n            None,\\n        )\\n        if (\\n            pcrec_from_writable_cache\\n            and pcrec_matches(pcrec_from_writable_cache)\\n            and pcrec_from_writable_cache.get(\\\"url\\\")\\n        ):\\n            # extract in place\\n            extract_action = ExtractPackageAction(\\n                source_full_path=pcrec_from_writable_cache.package_tarball_full_path,\\n                target_pkgs_dir=dirname(\\n                    pcrec_from_writable_cache.package_tarball_full_path\\n                ),\\n                target_extracted_dirname=basename(\\n                    pcrec_from_writable_cache.extracted_package_dir\\n                ),\\n                record_or_spec=pcrec_from_writable_cache,\\n                sha256=pcrec_from_writable_cache.sha256 or sha256,\\n                size=pcrec_from_writable_cache.size or size,\\n                md5=pcrec_from_writable_cache.md5 or md5,\\n            )\\n            return None, extract_action\\n\\n        pcrec_from_read_only_cache = next(\\n            (\\n                pcrec\\n                for pcrec in chain.from_iterable(\\n                    pcache.query(pref_or_spec)\\n                    for pcache in PackageCacheData.read_only_caches()\\n                )\\n                if pcrec.is_fetched\\n            ),\\n            None,\\n        )\\n\\n        first_writable_cache = PackageCacheData.first_writable()\\n        if pcrec_from_read_only_cache and pcrec_matches(pcrec_from_read_only_cache):\\n            # we found a tarball, but it's in a read-only package cache\\n            # we need to link the tarball into the first writable package cache,\\n            #   and then extract\\n            cache_action = CacheUrlAction(\\n                url=path_to_url(pcrec_from_read_only_cache.package_tarball_full_path),\\n                target_pkgs_dir=first_writable_cache.pkgs_dir,\\n                target_package_basename=pcrec_from_read_only_cache.fn,\\n                sha256=pcrec_from_read_only_cache.get(\\\"sha256\\\") or sha256,\\n                size=pcrec_from_read_only_cache.get(\\\"size\\\") or size,\\n                md5=pcrec_from_read_only_cache.get(\\\"md5\\\") or md5,\\n            )\\n            trgt_extracted_dirname = strip_pkg_extension(pcrec_from_read_only_cache.fn)[\\n                0\\n            ]\\n            extract_action = ExtractPackageAction(\\n                source_full_path=cache_action.target_full_path,\\n                target_pkgs_dir=first_writable_cache.pkgs_dir,\\n                target_extracted_dirname=trgt_extracted_dirname,\\n                record_or_spec=pcrec_from_read_only_cache,\\n                sha256=pcrec_from_read_only_cache.get(\\\"sha256\\\") or sha256,\\n                size=pcrec_from_read_only_cache.get(\\\"size\\\") or size,\\n                md5=pcrec_from_read_only_cache.get(\\\"md5\\\") or md5,\\n            )\\n            return cache_action, extract_action\\n\\n        # if we got here, we couldn't find a matching package in the caches\\n        #   we'll have to download one; fetch and extract\\n        url = pref_or_spec.get(\\\"url\\\")\\n        assert url\\n\\n        cache_action = CacheUrlAction(\\n            url=url,\\n            target_pkgs_dir=first_writable_cache.pkgs_dir,\\n            target_package_basename=pref_or_spec.fn,\\n            sha256=sha256,\\n            size=size,\\n            md5=md5,\\n        )\\n        extract_action = ExtractPackageAction(\\n            source_full_path=cache_action.target_full_path,\\n            target_pkgs_dir=first_writable_cache.pkgs_dir,\\n            target_extracted_dirname=strip_pkg_extension(pref_or_spec.fn)[0],\\n            record_or_spec=pref_or_spec,\\n            sha256=sha256,\\n            size=size,\\n            md5=md5,\\n        )\\n        return cache_action, extract_action\\n\\n    def __init__(self, link_prefs):\\n        \\\"\\\"\\\"\\n        Args:\\n            link_prefs (tuple[PackageRecord]):\\n                A sequence of :class:`PackageRecord`s to ensure available in a known\\n                package cache, typically for a follow-on :class:`UnlinkLinkTransaction`.\\n                Here, \\\"available\\\" means the package tarball is both downloaded and extracted\\n                to a package directory.\\n        \\\"\\\"\\\"\\n        self.link_precs = link_prefs\\n\\n        log.debug(\\n            \\\"instantiating ProgressiveFetchExtract with\\\\n  %s\\\\n\\\",\\n            \\\"\\\\n  \\\".join(pkg_rec.dist_str() for pkg_rec in link_prefs),\\n        )\\n\\n        self.paired_actions = {}  # Map[pref, Tuple(CacheUrlAction, ExtractPackageAction)]\\n\\n        self._prepared = False\\n        self._executed = False\\n\\n    @time_recorder(\\\"fetch_extract_prepare\\\")\\n    def prepare(self):\\n        if self._prepared:\\n            return\\n\\n        # Download largest first\\n        def by_size(prec: PackageRecord | MatchSpec):\\n            # the test suite passes MatchSpec in here, is that an intentional\\n            # feature?\\n            try:\\n                return int(prec.size)  # type: ignore\\n            except (LookupError, ValueError, AttributeError):\\n                return 0\\n\\n        largest_first = sorted(self.link_precs, key=by_size, reverse=True)\\n\\n        self.paired_actions.update(\\n            (prec, self.make_actions_for_record(prec)) for prec in largest_first\\n        )\\n        self._prepared = True\\n\\n    @property\\n    def cache_actions(self):\\n        return tuple(axns[0] for axns in self.paired_actions.values() if axns[0])\\n\\n    @property\\n    def extract_actions(self):\\n        return tuple(axns[1] for axns in self.paired_actions.values() if axns[1])\\n\\n    def execute(self):\\n        \\\"\\\"\\\"\\n        Run each action in self.paired_actions. Each action in cache_actions\\n        runs before its corresponding extract_actions.\\n        \\\"\\\"\\\"\\n        if self._executed:\\n            return\\n        if not self._prepared:\\n            self.prepare()\\n\\n        assert not context.dry_run\\n\\n        if not self.paired_actions:\\n            return\\n\\n        if not context.verbose and not context.quiet and not context.json:\\n            print(\\n                \\\"\\\\nDownloading and Extracting Packages:\\\",\\n                end=\\\"\\\\n\\\" if IS_INTERACTIVE else \\\" ...working...\\\",\\n            )\\n        else:\\n            log.debug(\\n                \\\"prepared package cache actions:\\\\n\\\"\\n                \\\"  cache_actions:\\\\n\\\"\\n                \\\"    %s\\\\n\\\"\\n                \\\"  extract_actions:\\\\n\\\"\\n                \\\"    %s\\\\n\\\",\\n                \\\"\\\\n    \\\".join(str(ca) for ca in self.cache_actions),\\n                \\\"\\\\n    \\\".join(str(ea) for ea in self.extract_actions),\\n            )\\n\\n        exceptions = []\\n        progress_bars = {}\\n        futures: list[Future] = []\\n\\n        cancelled_flag = False\\n\\n        def cancelled():\\n            \\\"\\\"\\\"\\n            Used to cancel download threads.\\n            \\\"\\\"\\\"\\n            nonlocal cancelled_flag\\n            return cancelled_flag\\n\\n        with signal_handler(conda_signal_handler), time_recorder(\\n            \\\"fetch_extract_execute\\\"\\n        ), ThreadPoolExecutor(\\n            context.fetch_threads\\n        ) as fetch_executor, ThreadPoolExecutor(EXTRACT_THREADS) as extract_executor:\\n            for prec_or_spec, (\\n                cache_action,\\n                extract_action,\\n            ) in self.paired_actions.items():\\n                if cache_action is None and extract_action is None:\\n                    # Not sure when this is reached.\\n                    continue\\n\\n                progress_bar = self._progress_bar(prec_or_spec, leave=False)\\n\\n                progress_bars[prec_or_spec] = progress_bar\\n\\n                future = fetch_executor.submit(\\n                    do_cache_action,\\n                    prec_or_spec,\\n                    cache_action,\\n                    progress_bar,\\n                    cancelled=cancelled,\\n                )\\n\\n                future.add_done_callback(\\n                    partial(\\n                        done_callback,\\n                        actions=(cache_action,),\\n                        exceptions=exceptions,\\n                        progress_bar=progress_bar,\\n                        finish=False,\\n                    )\\n                )\\n                futures.append(future)\\n\\n            try:\\n                for completed_future in as_completed(futures):\\n                    futures.remove(completed_future)\\n                    prec_or_spec = completed_future.result()\\n\\n                    cache_action, extract_action = self.paired_actions[prec_or_spec]\\n                    extract_future = extract_executor.submit(\\n                        do_extract_action,\\n                        prec_or_spec,\\n                        extract_action,\\n                        progress_bars[prec_or_spec],\\n                    )\\n                    extract_future.add_done_callback(\\n                        partial(\\n                            done_callback,\\n                            actions=(cache_action, extract_action),\\n                            exceptions=exceptions,\\n                            progress_bar=progress_bars[prec_or_spec],\\n                            finish=True,\\n                        )\\n                    )\\n            except BaseException as e:\\n                # We are interested in KeyboardInterrupt delivered to\\n                # as_completed() while waiting, or any exception raised from\\n                # completed_future.result(). cancelled_flag is checked in the\\n                # progress callback to stop running transfers, shutdown() should\\n                # prevent new downloads from starting.\\n                cancelled_flag = True\\n                for future in futures:  # needed on top of .shutdown()\\n                    future.cancel()\\n                # Has a Python >=3.9 cancel_futures= parameter that does not\\n                # replace the above loop:\\n                fetch_executor.shutdown(wait=False)\\n                exceptions.append(e)\\n\\n        for bar in progress_bars.values():\\n            bar.close()\\n\\n        if not context.verbose and not context.quiet and not context.json:\\n            if IS_INTERACTIVE:\\n                print(\\\"\\\\r\\\")  # move to column 0\\n            else:\\n                print(\\\" done\\\")\\n\\n        if exceptions:\\n            # avoid printing one CancelledError() per pending download\\n            not_cancelled = [e for e in exceptions if not isinstance(e, CancelledError)]\\n            raise CondaMultiError(not_cancelled)\\n\\n        self._executed = True\\n\\n    @staticmethod\\n    def _progress_bar(prec_or_spec, position=None, leave=False) -> ProgressBar:\\n        desc = \\\"\\\"\\n        if prec_or_spec.name and prec_or_spec.version:\\n            desc = \\\"{}-{}\\\".format(prec_or_spec.name or \\\"\\\", prec_or_spec.version or \\\"\\\")\\n        size = getattr(prec_or_spec, \\\"size\\\", None)\\n        size_str = size and human_bytes(size) or \\\"\\\"\\n        if len(desc) > 0:\\n            desc = \\\"%-20.20s | \\\" % desc\\n        if len(size_str) > 0:\\n            desc += \\\"%-9s | \\\" % size_str\\n\\n        progress_bar = ProgressBar(\\n            desc,\\n            not context.verbose and not context.quiet and IS_INTERACTIVE,\\n            context.json,\\n            position=position,\\n            leave=leave,\\n        )\\n\\n        return progress_bar\\n\\n    def __hash__(self):\\n        return hash(self.link_precs)\\n\\n    def __eq__(self, other):\\n        return hash(self) == hash(other)\\n\\n\\ndef do_cache_action(prec, cache_action, progress_bar, download_total=1.0, *, cancelled):\\n    \\\"\\\"\\\"This function gets called from `ProgressiveFetchExtract.execute`.\\\"\\\"\\\"\\n    # pass None if already cached (simplifies code)\\n    if not cache_action:\\n        return prec\\n    cache_action.verify()\\n\\n    if not cache_action.url.startswith(\\\"file:/\\\"):\\n\\n        def progress_update_cache_action(pct_completed):\\n            if cancelled():\\n                \\\"\\\"\\\"\\n                Used to cancel dowload threads when parent thread is interrupted.\\n                \\\"\\\"\\\"\\n                raise CancelledError()\\n            progress_bar.update_to(pct_completed * download_total)\\n\\n    else:\\n        download_total = 0\\n        progress_update_cache_action = None\\n\\n    cache_action.execute(progress_update_cache_action)\\n    return prec\\n\\n\\ndef do_extract_action(prec, extract_action, progress_bar):\\n    \\\"\\\"\\\"This function gets called after do_cache_action completes.\\\"\\\"\\\"\\n    # pass None if already extracted (simplifies code)\\n    if not extract_action:\\n        return prec\\n    extract_action.verify()\\n    # currently unable to do updates on extract;\\n    # likely too fast to bother\\n    extract_action.execute(None)\\n    progress_bar.update_to(1.0)\\n    return prec\\n\\n\\ndef do_cleanup(actions):\\n    for action in actions:\\n        if action:\\n            action.cleanup()\\n\\n\\ndef do_reverse(actions):\\n    for action in actions:\\n        if action:\\n            action.reverse()\\n\\n\\ndef done_callback(\\n    future: Future,\\n    actions: tuple[CacheUrlAction | ExtractPackageAction, ...],\\n    progress_bar: ProgressBar,\\n    exceptions: list[Exception],\\n    finish: bool = False,\\n):\\n    try:\\n        future.result()\\n    except Exception as e:\\n        # if it was interrupted with CTRL-C this might be BaseException and not\\n        # get caught here, but conda's signal handler also converts that to\\n        # CondaError which is just Exception.\\n        do_reverse(reversed(actions))\\n        exceptions.append(e)\\n    else:\\n        do_cleanup(actions)\\n        if finish:\\n            progress_bar.finish()\\n            progress_bar.refresh()\\n\\n\\n@deprecated(\\\"24.3\\\", \\\"24.9\\\")\\ndef rm_fetched(dist):\\n    \\\"\\\"\\\"\\n    Checks to see if the requested package is in the cache; and if so, it removes both\\n    the package itself and its extracted contents.\\n    \\\"\\\"\\\"\\n    # in conda/exports.py and conda_build/conda_interface.py, but not actually\\n    #   used in conda-build\\n    raise NotImplementedError()\\n\\n\\n@deprecated(\\n    \\\"24.3\\\",\\n    \\\"24.9\\\",\\n    addendum=\\\"Use `conda.gateways.connection.download.download` instead.\\\",\\n)\\ndef download(url, dst_path, session=None, md5sum=None, urlstxt=False, retries=3):\\n    from ..gateways.connection.download import download as gateway_download\\n\\n    gateway_download(url, dst_path, md5sum)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Tools for managing conda environments.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport os\\nfrom errno import EACCES, ENOENT, EROFS\\nfrom logging import getLogger\\nfrom os.path import dirname, isdir, isfile, join, normpath\\nfrom typing import TYPE_CHECKING\\n\\nfrom ..base.context import context\\nfrom ..common._os import is_admin\\nfrom ..common.compat import ensure_text_type, on_win, open\\nfrom ..common.path import expand\\nfrom ..gateways.disk.read import yield_lines\\nfrom ..gateways.disk.test import is_conda_environment\\nfrom .prefix_data import PrefixData\\n\\nif TYPE_CHECKING:\\n    from typing import Iterator\\n\\nlog = getLogger(__name__)\\n\\n\\ndef get_user_environments_txt_file(userhome: str = \\\"~\\\") -> str:\\n    \\\"\\\"\\\"\\n    Gets the path to the user's environments.txt file.\\n\\n    :param userhome: The home directory of the user.\\n    :type userhome: str\\n    :return: Path to the environments.txt file.\\n    :rtype: str\\n    \\\"\\\"\\\"\\n    return expand(join(userhome, \\\".conda\\\", \\\"environments.txt\\\"))\\n\\n\\ndef register_env(location: str) -> None:\\n    \\\"\\\"\\\"\\n    Registers an environment by adding it to environments.txt file.\\n\\n    :param location: The file path of the environment to register.\\n    :type location: str\\n    :return: None\\n    \\\"\\\"\\\"\\n    if not context.register_envs:\\n        return\\n\\n    user_environments_txt_file = get_user_environments_txt_file()\\n    location = normpath(location)\\n    folder = dirname(location)\\n    try:\\n        os.makedirs(folder)\\n    except:\\n        pass\\n\\n    if (\\n        \\\"placehold_pl\\\" in location\\n        or \\\"skeleton_\\\" in location\\n        or user_environments_txt_file == os.devnull\\n    ):\\n        # Don't record envs created by conda-build.\\n        return\\n\\n    if location in yield_lines(user_environments_txt_file):\\n        # Nothing to do. Location is already recorded in a known environments.txt file.\\n        return\\n\\n    user_environments_txt_directory = os.path.dirname(user_environments_txt_file)\\n    try:\\n        os.makedirs(user_environments_txt_directory, exist_ok=True)\\n    except OSError as exc:\\n        log.warning(\\n            \\\"Unable to register environment. \\\"\\n            f\\\"Could not create {user_environments_txt_directory}. \\\"\\n            f\\\"Reason: {exc}\\\"\\n        )\\n        return\\n\\n    try:\\n        with open(user_environments_txt_file, \\\"a\\\") as fh:\\n            fh.write(ensure_text_type(location))\\n            fh.write(\\\"\\\\n\\\")\\n    except OSError as e:\\n        if e.errno in (EACCES, EROFS, ENOENT):\\n            log.warning(\\n                \\\"Unable to register environment. Path not writable or missing.\\\\n\\\"\\n                \\\"  environment location: %s\\\\n\\\"\\n                \\\"  registry file: %s\\\",\\n                location,\\n                user_environments_txt_file,\\n            )\\n        else:\\n            raise\\n\\n\\ndef unregister_env(location: str) -> None:\\n    \\\"\\\"\\\"\\n    Unregisters an environment by removing its entry from the environments.txt file if certain conditions are met.\\n\\n    The environment is only unregistered if its associated 'conda-meta' directory exists and contains no significant files other than 'history'. If these conditions are met, the environment's path is removed from environments.txt.\\n\\n    :param location: The file path of the environment to unregister.\\n    :type location: str\\n    :return: None\\n    \\\"\\\"\\\"\\n    if isdir(location):\\n        meta_dir = join(location, \\\"conda-meta\\\")\\n        if isdir(meta_dir):\\n            meta_dir_contents = tuple(entry.name for entry in os.scandir(meta_dir))\\n            if len(meta_dir_contents) > 1:\\n                # if there are any files left other than 'conda-meta/history'\\n                #   then don't unregister\\n                return\\n\\n    _clean_environments_txt(get_user_environments_txt_file(), location)\\n\\n\\ndef list_all_known_prefixes() -> list[str]:\\n    \\\"\\\"\\\"\\n    Lists all known conda environment prefixes.\\n\\n    :return: A list of all known conda environment prefixes.\\n    :rtype: List[str]\\n    \\\"\\\"\\\"\\n    all_env_paths = set()\\n    # If the user is an admin, load environments from all user home directories\\n    if is_admin():\\n        if on_win:\\n            home_dir_dir = dirname(expand(\\\"~\\\"))\\n            search_dirs = tuple(entry.path for entry in os.scandir(home_dir_dir))\\n        else:\\n            from pwd import getpwall\\n\\n            search_dirs = tuple(pwentry.pw_dir for pwentry in getpwall()) or (\\n                expand(\\\"~\\\"),\\n            )\\n    else:\\n        search_dirs = (expand(\\\"~\\\"),)\\n    for home_dir in filter(None, search_dirs):\\n        environments_txt_file = get_user_environments_txt_file(home_dir)\\n        if isfile(environments_txt_file):\\n            try:\\n                # When the user is an admin, some environments.txt files might\\n                # not be readable (if on network file system for example)\\n                all_env_paths.update(_clean_environments_txt(environments_txt_file))\\n            except PermissionError:\\n                log.warning(f\\\"Unable to access {environments_txt_file}\\\")\\n\\n    # in case environments.txt files aren't complete, also add all known conda environments in\\n    # all envs_dirs\\n    envs_dirs = (envs_dir for envs_dir in context.envs_dirs if isdir(envs_dir))\\n    all_env_paths.update(\\n        path\\n        for path in (\\n            entry.path for envs_dir in envs_dirs for entry in os.scandir(envs_dir)\\n        )\\n        if path not in all_env_paths and is_conda_environment(path)\\n    )\\n\\n    all_env_paths.add(context.root_prefix)\\n    return sorted(all_env_paths)\\n\\n\\ndef query_all_prefixes(spec: str) -> Iterator[tuple[str, tuple]]:\\n    \\\"\\\"\\\"\\n    Queries all known prefixes for a given specification.\\n\\n    :param spec: The specification to query for.\\n    :type spec: str\\n    :return: An iterator of tuples containing the prefix and the query results.\\n    :rtype: Iterator[Tuple[str, Tuple]]\\n    \\\"\\\"\\\"\\n    for prefix in list_all_known_prefixes():\\n        prefix_recs = tuple(PrefixData(prefix).query(spec))\\n        if prefix_recs:\\n            yield prefix, prefix_recs\\n\\n\\ndef _clean_environments_txt(\\n    environments_txt_file: str,\\n    remove_location: str | None = None,\\n) -> tuple[str, ...]:\\n    \\\"\\\"\\\"\\n    Cleans the environments.txt file by removing specified locations.\\n\\n    :param environments_txt_file: The file path of environments.txt.\\n    :param remove_location: Optional location to remove from the file.\\n    :type environments_txt_file: str\\n    :type remove_location: Optional[str]\\n    :return: A tuple of the cleaned lines.\\n    :rtype: Tuple[str, ...]\\n    \\\"\\\"\\\"\\n    if not isfile(environments_txt_file):\\n        return ()\\n\\n    if remove_location:\\n        remove_location = normpath(remove_location)\\n    environments_txt_lines = tuple(yield_lines(environments_txt_file))\\n    environments_txt_lines_cleaned = tuple(\\n        prefix\\n        for prefix in environments_txt_lines\\n        if prefix != remove_location and is_conda_environment(prefix)\\n    )\\n    if environments_txt_lines_cleaned != environments_txt_lines:\\n        _rewrite_environments_txt(environments_txt_file, environments_txt_lines_cleaned)\\n    return environments_txt_lines_cleaned\\n\\n\\ndef _rewrite_environments_txt(environments_txt_file: str, prefixes: list[str]) -> None:\\n    \\\"\\\"\\\"\\n    Rewrites the environments.txt file with the specified prefixes.\\n\\n    :param environments_txt_file: The file path of environments.txt.\\n    :param prefixes: List of prefixes to write into the file.\\n    :type environments_txt_file: str\\n    :type prefixes: List[str]\\n    :return: None\\n    \\\"\\\"\\\"\\n    try:\\n        with open(environments_txt_file, \\\"w\\\") as fh:\\n            fh.write(\\\"\\\\n\\\".join(prefixes))\\n            fh.write(\\\"\\\\n\\\")\\n    except OSError as e:\\n        log.info(\\\"File not cleaned: %s\\\", environments_txt_file)\\n        log.debug(\\\"%r\\\", e, exc_info=True)\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Backend logic for `conda init`.\\n\\nSections in this module are\\n\\n  1. top-level functions\\n  2. plan creators\\n  3. plan runners\\n  4. individual operations\\n  5. helper functions\\n\\nThe top-level functions compose and execute full plans.\\n\\nA plan is created by composing various individual operations.  The plan data structure is a\\nlist of dicts, where each dict represents an individual operation.  The dict contains two\\nkeys--`function` and `kwargs`--where function is the name of the individual operation function\\nwithin this module.\\n\\nEach individual operation must\\n\\n  a) return a `Result` (i.e. NEEDS_SUDO, MODIFIED, or NO_CHANGE)\\n  b) have no side effects if context.dry_run is True\\n  c) be verbose and descriptive about the changes being made or proposed is context.verbose\\n\\nThe plan runner functions take the plan (list of dicts) as an argument, and then coordinate the\\nexecution of each individual operation.  The docstring for `run_plan_elevated()` has details on\\nhow that strategy is implemented.\\n\\n\\\"\\\"\\\"\\n\\nimport json\\nimport os\\nimport re\\nimport struct\\nimport sys\\nfrom difflib import unified_diff\\nfrom errno import ENOENT\\nfrom glob import glob\\nfrom itertools import chain\\nfrom logging import getLogger\\nfrom os.path import abspath, basename, dirname, exists, expanduser, isdir, isfile, join\\nfrom pathlib import Path\\nfrom random import randint\\n\\nfrom .. import CONDA_PACKAGE_ROOT, CondaError\\nfrom .. import __version__ as CONDA_VERSION\\nfrom ..activate import (\\n    CshActivator,\\n    FishActivator,\\n    PosixActivator,\\n    PowerShellActivator,\\n    XonshActivator,\\n)\\nfrom ..auxlib.compat import Utf8NamedTemporaryFile\\nfrom ..auxlib.ish import dals\\nfrom ..base.context import context\\nfrom ..common.compat import (\\n    ensure_binary,\\n    ensure_text_type,\\n    ensure_utf8_encoding,\\n    on_mac,\\n    on_win,\\n    open,\\n)\\nfrom ..common.path import (\\n    expand,\\n    get_bin_directory_short_path,\\n    get_python_short_path,\\n    get_python_site_packages_short_path,\\n    win_path_ok,\\n)\\nfrom ..exceptions import CondaValueError\\nfrom ..gateways.disk.create import copy, mkdir_p\\nfrom ..gateways.disk.delete import rm_rf\\nfrom ..gateways.disk.link import lexists\\nfrom ..gateways.disk.permissions import make_executable\\nfrom ..gateways.disk.read import compute_sum\\nfrom ..gateways.subprocess import subprocess_call\\nfrom .portability import generate_shebang_for_entry_point\\n\\nif on_win:  # pragma: no cover\\n    import winreg\\n\\n    # Use v1 import paths to avoid bootstrapping issues\\n    # TODO: Remove once fully deployed (one release after merge)\\n    from menuinst.knownfolders import FOLDERID, get_folder_path\\n    from menuinst.winshortcut import create_shortcut\\n\\n\\nlog = getLogger(__name__)\\n\\nCONDA_INITIALIZE_RE_BLOCK = (\\n    r\\\"^# >>> conda initialize >>>(?:\\\\n|\\\\r\\\\n)\\\"\\n    r\\\"([\\\\s\\\\S]*?)\\\"\\n    r\\\"# <<< conda initialize <<<(?:\\\\n|\\\\r\\\\n)?\\\"\\n)\\n\\nCONDA_INITIALIZE_PS_RE_BLOCK = (\\n    r\\\"^#region conda initialize(?:\\\\n|\\\\r\\\\n)([\\\\s\\\\S]*?)#endregion(?:\\\\n|\\\\r\\\\n)?\\\"\\n)\\n\\n\\nclass Result:\\n    NEEDS_SUDO = \\\"needs sudo\\\"\\n    MODIFIED = \\\"modified\\\"\\n    NO_CHANGE = \\\"no change\\\"\\n\\n\\n# #####################################################\\n# top-level functions\\n# #####################################################\\n\\n\\ndef install(conda_prefix):\\n    plan = make_install_plan(conda_prefix)\\n    run_plan(plan)\\n    if not context.dry_run:\\n        assert not any(step[\\\"result\\\"] == Result.NEEDS_SUDO for step in plan)\\n    print_plan_results(plan)\\n    return 0\\n\\n\\ndef initialize(\\n    conda_prefix, shells, for_user, for_system, anaconda_prompt, reverse=False\\n):\\n    plan1 = []\\n    if os.getenv(\\\"CONDA_PIP_UNINITIALIZED\\\") == \\\"true\\\":\\n        plan1 = make_install_plan(conda_prefix)\\n        run_plan(plan1)\\n        if not context.dry_run:\\n            run_plan_elevated(plan1)\\n\\n    plan2 = make_initialize_plan(\\n        conda_prefix, shells, for_user, for_system, anaconda_prompt, reverse=reverse\\n    )\\n    run_plan(plan2)\\n    if not context.dry_run:\\n        run_plan_elevated(plan2)\\n\\n    plan = plan1 + plan2\\n    print_plan_results(plan)\\n\\n    if any(step[\\\"result\\\"] == Result.NEEDS_SUDO for step in plan):\\n        print(\\\"Operation failed.\\\", file=sys.stderr)\\n        return 1\\n\\n\\ndef initialize_dev(shell, dev_env_prefix=None, conda_source_root=None):\\n    # > alias conda-dev='eval \\\"$(python -m conda init --dev)\\\"'\\n    # > eval \\\"$(python -m conda init --dev)\\\"\\n\\n    prefix = expand(dev_env_prefix or sys.prefix)\\n    conda_source_root = expand(conda_source_root or os.getcwd())\\n\\n    python_exe, python_version, site_packages_dir = _get_python_info(prefix)\\n\\n    if not isfile(join(conda_source_root, \\\"conda\\\", \\\"__main__.py\\\")):\\n        raise CondaValueError(\\n            f\\\"Directory is not a conda source root: {conda_source_root}\\\"\\n        )\\n\\n    plan = make_install_plan(prefix)\\n    plan.append(\\n        {\\n            \\\"function\\\": remove_conda_in_sp_dir.__name__,\\n            \\\"kwargs\\\": {\\n                \\\"target_path\\\": site_packages_dir,\\n            },\\n        }\\n    )\\n    plan.append(\\n        {\\n            \\\"function\\\": make_conda_egg_link.__name__,\\n            \\\"kwargs\\\": {\\n                \\\"target_path\\\": join(site_packages_dir, \\\"conda.egg-link\\\"),\\n                \\\"conda_source_root\\\": conda_source_root,\\n            },\\n        }\\n    )\\n    plan.append(\\n        {\\n            \\\"function\\\": modify_easy_install_pth.__name__,\\n            \\\"kwargs\\\": {\\n                \\\"target_path\\\": join(site_packages_dir, \\\"easy-install.pth\\\"),\\n                \\\"conda_source_root\\\": conda_source_root,\\n            },\\n        }\\n    )\\n    plan.append(\\n        {\\n            \\\"function\\\": make_dev_egg_info_file.__name__,\\n            \\\"kwargs\\\": {\\n                \\\"target_path\\\": join(conda_source_root, \\\"conda.egg-info\\\"),\\n            },\\n        }\\n    )\\n\\n    run_plan(plan)\\n\\n    if context.dry_run or context.verbose:\\n        print_plan_results(plan, sys.stderr)\\n\\n    if any(step[\\\"result\\\"] == Result.NEEDS_SUDO for step in plan):  # pragma: no cover\\n        raise CondaError(\\n            \\\"Operation failed. Privileged install disallowed for 'conda init --dev'.\\\"\\n        )\\n\\n    env_vars = {\\n        \\\"PYTHONHASHSEED\\\": randint(0, 4294967296),\\n        \\\"PYTHON_MAJOR_VERSION\\\": python_version[0],\\n        \\\"TEST_PLATFORM\\\": \\\"win\\\" if on_win else \\\"unix\\\",\\n    }\\n    unset_env_vars = (\\n        \\\"CONDA_DEFAULT_ENV\\\",\\n        \\\"CONDA_EXE\\\",\\n        \\\"_CE_M\\\",\\n        \\\"_CE_CONDA\\\",\\n        \\\"CONDA_PREFIX\\\",\\n        \\\"CONDA_PREFIX_1\\\",\\n        \\\"CONDA_PREFIX_2\\\",\\n        \\\"CONDA_PYTHON_EXE\\\",\\n        \\\"CONDA_PROMPT_MODIFIER\\\",\\n        \\\"CONDA_SHLVL\\\",\\n    )\\n\\n    if shell == \\\"bash\\\":\\n        print(\\\"\\\\n\\\".join(_initialize_dev_bash(prefix, env_vars, unset_env_vars)))\\n    elif shell == \\\"cmd.exe\\\":\\n        script = _initialize_dev_cmdexe(prefix, env_vars, unset_env_vars)\\n        if not context.dry_run:\\n            with open(\\\"dev-init.bat\\\", \\\"w\\\") as fh:\\n                fh.write(\\\"\\\\n\\\".join(script))\\n        if context.verbose:\\n            print(\\\"\\\\n\\\".join(script))\\n        print(\\\"now run  > .\\\\\\\\dev-init.bat\\\")\\n    else:\\n        raise NotImplementedError()\\n    return 0\\n\\n\\ndef _initialize_dev_bash(prefix, env_vars, unset_env_vars):\\n    sys_executable = abspath(sys.executable)\\n    if on_win:\\n        sys_executable = f\\\"$(cygpath '{sys_executable}')\\\"\\n\\n    # unset/set environment variables\\n    yield from (f\\\"unset {envvar}\\\" for envvar in unset_env_vars)\\n    yield from (\\n        f\\\"export {envvar}='{value}'\\\" for envvar, value in sorted(env_vars.items())\\n    )\\n\\n    # initialize shell interface\\n    yield f'eval \\\"$(\\\"{sys_executable}\\\" -m conda shell.bash hook)\\\"'\\n\\n    # optionally activate environment\\n    if context.auto_activate_base:\\n        yield f\\\"conda activate '{prefix}'\\\"\\n\\n\\ndef _initialize_dev_cmdexe(prefix, env_vars, unset_env_vars):\\n    dev_arg = \\\"\\\"\\n    if context.dev:\\n        dev_arg = \\\"--dev\\\"\\n    condabin = Path(prefix, \\\"condabin\\\")\\n\\n    yield (\\n        '@IF NOT \\\"%CONDA_PROMPT_MODIFIER%\\\" == \\\"\\\" '\\n        '@CALL SET \\\"PROMPT=%%PROMPT:%CONDA_PROMPT_MODIFIER%=%_empty_not_set_%%%\\\"'\\n    )\\n\\n    # unset/set environment variables\\n    yield from (f\\\"@SET {envvar}=\\\" for envvar in unset_env_vars)\\n    yield from (\\n        f'@SET \\\"{envvar}={value}\\\"' for envvar, value in sorted(env_vars.items())\\n    )\\n\\n    # initialize shell interface\\n    yield f'@CALL \\\"{condabin / \\\"conda_hook.bat\\\"}\\\" {dev_arg}'\\n    yield \\\"@IF %ERRORLEVEL% NEQ 0 @EXIT /B %ERRORLEVEL%\\\"\\n\\n    # optionally activate environment\\n    if context.auto_activate_base:\\n        yield f'@CALL \\\"{condabin / \\\"conda.bat\\\"}\\\" activate {dev_arg} \\\"{prefix}\\\"'\\n        yield \\\"@IF %ERRORLEVEL% NEQ 0 @EXIT /B %ERRORLEVEL%\\\"\\n\\n\\n# #####################################################\\n# plan creators\\n# #####################################################\\n\\n\\ndef make_install_plan(conda_prefix):\\n    try:\\n        python_exe, python_version, site_packages_dir = _get_python_info(conda_prefix)\\n    except OSError:\\n        python_exe, python_version, site_packages_dir = None, None, None  # NOQA\\n\\n    plan = []\\n\\n    # ######################################\\n    # executables\\n    # ######################################\\n    if on_win:\\n        conda_exe_path = join(conda_prefix, \\\"Scripts\\\", \\\"conda-script.py\\\")\\n        conda_env_exe_path = join(conda_prefix, \\\"Scripts\\\", \\\"conda-env-script.py\\\")\\n        plan.append(\\n            {\\n                \\\"function\\\": make_entry_point_exe.__name__,\\n                \\\"kwargs\\\": {\\n                    \\\"target_path\\\": join(conda_prefix, \\\"Scripts\\\", \\\"conda.exe\\\"),\\n                    \\\"conda_prefix\\\": conda_prefix,\\n                },\\n            }\\n        )\\n        plan.append(\\n            {\\n                \\\"function\\\": make_entry_point_exe.__name__,\\n                \\\"kwargs\\\": {\\n                    \\\"target_path\\\": join(conda_prefix, \\\"Scripts\\\", \\\"conda-env.exe\\\"),\\n                    \\\"conda_prefix\\\": conda_prefix,\\n                },\\n            }\\n        )\\n    else:\\n        # We can't put a conda.exe in condabin on Windows. It'll conflict with conda.bat.\\n        plan.append(\\n            {\\n                \\\"function\\\": make_entry_point.__name__,\\n                \\\"kwargs\\\": {\\n                    \\\"target_path\\\": join(conda_prefix, \\\"condabin\\\", \\\"conda\\\"),\\n                    \\\"conda_prefix\\\": conda_prefix,\\n                    \\\"module\\\": \\\"conda.cli\\\",\\n                    \\\"func\\\": \\\"main\\\",\\n                },\\n            }\\n        )\\n        conda_exe_path = join(conda_prefix, \\\"bin\\\", \\\"conda\\\")\\n        conda_env_exe_path = join(conda_prefix, \\\"bin\\\", \\\"conda-env\\\")\\n\\n    plan.append(\\n        {\\n            \\\"function\\\": make_entry_point.__name__,\\n            \\\"kwargs\\\": {\\n                \\\"target_path\\\": conda_exe_path,\\n                \\\"conda_prefix\\\": conda_prefix,\\n                \\\"module\\\": \\\"conda.cli\\\",\\n                \\\"func\\\": \\\"main\\\",\\n            },\\n        }\\n    )\\n    plan.append(\\n        {\\n            \\\"function\\\": make_entry_point.__name__,\\n            \\\"kwargs\\\": {\\n                \\\"target_path\\\": conda_env_exe_path,\\n                \\\"conda_prefix\\\": conda_prefix,\\n                # TODO: Remove upon full deprecation in 25.3\\n                \\\"module\\\": \\\"conda_env.cli.main\\\",\\n                \\\"func\\\": \\\"main\\\",\\n            },\\n        }\\n    )\\n\\n    # ######################################\\n    # shell wrappers\\n    # ######################################\\n    if on_win:\\n        plan.append(\\n            {\\n                \\\"function\\\": install_condabin_conda_bat.__name__,\\n                \\\"kwargs\\\": {\\n                    \\\"target_path\\\": join(conda_prefix, \\\"condabin\\\", \\\"conda.bat\\\"),\\n                    \\\"conda_prefix\\\": conda_prefix,\\n                },\\n            }\\n        )\\n        plan.append(\\n            {\\n                \\\"function\\\": install_library_bin_conda_bat.__name__,\\n                \\\"kwargs\\\": {\\n                    \\\"target_path\\\": join(conda_prefix, \\\"Library\\\", \\\"bin\\\", \\\"conda.bat\\\"),\\n                    \\\"conda_prefix\\\": conda_prefix,\\n                },\\n            }\\n        )\\n        plan.append(\\n            {\\n                \\\"function\\\": install_condabin_conda_activate_bat.__name__,\\n                \\\"kwargs\\\": {\\n                    \\\"target_path\\\": join(\\n                        conda_prefix, \\\"condabin\\\", \\\"_conda_activate.bat\\\"\\n                    ),\\n                    \\\"conda_prefix\\\": conda_prefix,\\n                },\\n            }\\n        )\\n        plan.append(\\n            {\\n                \\\"function\\\": install_condabin_rename_tmp_bat.__name__,\\n                \\\"kwargs\\\": {\\n                    \\\"target_path\\\": join(conda_prefix, \\\"condabin\\\", \\\"rename_tmp.bat\\\"),\\n                    \\\"conda_prefix\\\": conda_prefix,\\n                },\\n            }\\n        )\\n        plan.append(\\n            {\\n                \\\"function\\\": install_condabin_conda_auto_activate_bat.__name__,\\n                \\\"kwargs\\\": {\\n                    \\\"target_path\\\": join(\\n                        conda_prefix, \\\"condabin\\\", \\\"conda_auto_activate.bat\\\"\\n                    ),\\n                    \\\"conda_prefix\\\": conda_prefix,\\n                },\\n            }\\n        )\\n        plan.append(\\n            {\\n                \\\"function\\\": install_condabin_hook_bat.__name__,\\n                \\\"kwargs\\\": {\\n                    \\\"target_path\\\": join(conda_prefix, \\\"condabin\\\", \\\"conda_hook.bat\\\"),\\n                    \\\"conda_prefix\\\": conda_prefix,\\n                },\\n            }\\n        )\\n        plan.append(\\n            {\\n                \\\"function\\\": install_Scripts_activate_bat.__name__,\\n                \\\"kwargs\\\": {\\n                    \\\"target_path\\\": join(conda_prefix, \\\"Scripts\\\", \\\"activate.bat\\\"),\\n                    \\\"conda_prefix\\\": conda_prefix,\\n                },\\n            }\\n        )\\n        plan.append(\\n            {\\n                \\\"function\\\": install_activate_bat.__name__,\\n                \\\"kwargs\\\": {\\n                    \\\"target_path\\\": join(conda_prefix, \\\"condabin\\\", \\\"activate.bat\\\"),\\n                    \\\"conda_prefix\\\": conda_prefix,\\n                },\\n            }\\n        )\\n        plan.append(\\n            {\\n                \\\"function\\\": install_deactivate_bat.__name__,\\n                \\\"kwargs\\\": {\\n                    \\\"target_path\\\": join(conda_prefix, \\\"condabin\\\", \\\"deactivate.bat\\\"),\\n                    \\\"conda_prefix\\\": conda_prefix,\\n                },\\n            }\\n        )\\n\\n    plan.append(\\n        {\\n            \\\"function\\\": install_activate.__name__,\\n            \\\"kwargs\\\": {\\n                \\\"target_path\\\": join(\\n                    conda_prefix, get_bin_directory_short_path(), \\\"activate\\\"\\n                ),\\n                \\\"conda_prefix\\\": conda_prefix,\\n            },\\n        }\\n    )\\n    plan.append(\\n        {\\n            \\\"function\\\": install_deactivate.__name__,\\n            \\\"kwargs\\\": {\\n                \\\"target_path\\\": join(\\n                    conda_prefix, get_bin_directory_short_path(), \\\"deactivate\\\"\\n                ),\\n                \\\"conda_prefix\\\": conda_prefix,\\n            },\\n        }\\n    )\\n\\n    plan.append(\\n        {\\n            \\\"function\\\": install_conda_sh.__name__,\\n            \\\"kwargs\\\": {\\n                \\\"target_path\\\": join(conda_prefix, \\\"etc\\\", \\\"profile.d\\\", \\\"conda.sh\\\"),\\n                \\\"conda_prefix\\\": conda_prefix,\\n            },\\n        }\\n    )\\n    plan.append(\\n        {\\n            \\\"function\\\": install_conda_fish.__name__,\\n            \\\"kwargs\\\": {\\n                \\\"target_path\\\": join(\\n                    conda_prefix, \\\"etc\\\", \\\"fish\\\", \\\"conf.d\\\", \\\"conda.fish\\\"\\n                ),\\n                \\\"conda_prefix\\\": conda_prefix,\\n            },\\n        }\\n    )\\n    plan.append(\\n        {\\n            \\\"function\\\": install_conda_psm1.__name__,\\n            \\\"kwargs\\\": {\\n                \\\"target_path\\\": join(conda_prefix, \\\"shell\\\", \\\"condabin\\\", \\\"Conda.psm1\\\"),\\n                \\\"conda_prefix\\\": conda_prefix,\\n            },\\n        }\\n    )\\n    plan.append(\\n        {\\n            \\\"function\\\": install_conda_hook_ps1.__name__,\\n            \\\"kwargs\\\": {\\n                \\\"target_path\\\": join(\\n                    conda_prefix, \\\"shell\\\", \\\"condabin\\\", \\\"conda-hook.ps1\\\"\\n                ),\\n                \\\"conda_prefix\\\": conda_prefix,\\n            },\\n        }\\n    )\\n    if site_packages_dir:\\n        plan.append(\\n            {\\n                \\\"function\\\": install_conda_xsh.__name__,\\n                \\\"kwargs\\\": {\\n                    \\\"target_path\\\": join(site_packages_dir, \\\"xontrib\\\", \\\"conda.xsh\\\"),\\n                    \\\"conda_prefix\\\": conda_prefix,\\n                },\\n            }\\n        )\\n    else:\\n        print(\\n            \\\"WARNING: Cannot install xonsh wrapper without a python interpreter in prefix: \\\"\\n            f\\\"{conda_prefix}\\\",\\n            file=sys.stderr,\\n        )\\n    plan.append(\\n        {\\n            \\\"function\\\": install_conda_csh.__name__,\\n            \\\"kwargs\\\": {\\n                \\\"target_path\\\": join(conda_prefix, \\\"etc\\\", \\\"profile.d\\\", \\\"conda.csh\\\"),\\n                \\\"conda_prefix\\\": conda_prefix,\\n            },\\n        }\\n    )\\n    return plan\\n\\n\\ndef make_initialize_plan(\\n    conda_prefix, shells, for_user, for_system, anaconda_prompt, reverse=False\\n):\\n    \\\"\\\"\\\"\\n    Creates a plan for initializing conda in shells.\\n\\n    Bash:\\n    On Linux, when opening the terminal, .bashrc is sourced (because it is an interactive shell).\\n    On macOS on the other hand, the .bash_profile gets sourced by default when executing it in\\n    Terminal.app. Some other programs do the same on macOS so that's why we're initializing conda\\n    in .bash_profile.\\n    On Windows, there are multiple ways to open bash depending on how it was installed. Git Bash,\\n    Cygwin, and MSYS2 all use .bash_profile by default.\\n\\n    PowerShell:\\n    There's several places PowerShell can store its path, depending on if it's Windows PowerShell,\\n    PowerShell Core on Windows, or PowerShell Core on macOS/Linux. The easiest way to resolve it\\n    is to just ask different possible installations of PowerShell where their profiles are.\\n    \\\"\\\"\\\"\\n    plan = make_install_plan(conda_prefix)\\n    shells = set(shells)\\n    if shells & {\\\"bash\\\", \\\"zsh\\\"}:\\n        if \\\"bash\\\" in shells and for_user:\\n            bashrc_path = expand(\\n                join(\\\"~\\\", \\\".bash_profile\\\" if (on_mac or on_win) else \\\".bashrc\\\")\\n            )\\n            plan.append(\\n                {\\n                    \\\"function\\\": init_sh_user.__name__,\\n                    \\\"kwargs\\\": {\\n                        \\\"target_path\\\": bashrc_path,\\n                        \\\"conda_prefix\\\": conda_prefix,\\n                        \\\"shell\\\": \\\"bash\\\",\\n                        \\\"reverse\\\": reverse,\\n                    },\\n                }\\n            )\\n\\n        if \\\"zsh\\\" in shells and for_user:\\n            if \\\"ZDOTDIR\\\" in os.environ:\\n                zshrc_path = expand(join(\\\"$ZDOTDIR\\\", \\\".zshrc\\\"))\\n            else:\\n                zshrc_path = expand(join(\\\"~\\\", \\\".zshrc\\\"))\\n            plan.append(\\n                {\\n                    \\\"function\\\": init_sh_user.__name__,\\n                    \\\"kwargs\\\": {\\n                        \\\"target_path\\\": zshrc_path,\\n                        \\\"conda_prefix\\\": conda_prefix,\\n                        \\\"shell\\\": \\\"zsh\\\",\\n                        \\\"reverse\\\": reverse,\\n                    },\\n                }\\n            )\\n\\n        if for_system:\\n            plan.append(\\n                {\\n                    \\\"function\\\": init_sh_system.__name__,\\n                    \\\"kwargs\\\": {\\n                        \\\"target_path\\\": \\\"/etc/profile.d/conda.sh\\\",\\n                        \\\"conda_prefix\\\": conda_prefix,\\n                        \\\"reverse\\\": reverse,\\n                    },\\n                }\\n            )\\n\\n    if \\\"fish\\\" in shells:\\n        if for_user:\\n            config_fish_path = expand(join(\\\"~\\\", \\\".config\\\", \\\"fish\\\", \\\"config.fish\\\"))\\n            plan.append(\\n                {\\n                    \\\"function\\\": init_fish_user.__name__,\\n                    \\\"kwargs\\\": {\\n                        \\\"target_path\\\": config_fish_path,\\n                        \\\"conda_prefix\\\": conda_prefix,\\n                        \\\"reverse\\\": reverse,\\n                    },\\n                }\\n            )\\n\\n        if for_system:\\n            config_fish_path = expand(join(\\\"~\\\", \\\".config\\\", \\\"fish\\\", \\\"config.fish\\\"))\\n            plan.append(\\n                {\\n                    \\\"function\\\": init_fish_user.__name__,\\n                    \\\"kwargs\\\": {\\n                        \\\"target_path\\\": config_fish_path,\\n                        \\\"conda_prefix\\\": conda_prefix,\\n                        \\\"reverse\\\": reverse,\\n                    },\\n                }\\n            )\\n\\n    if \\\"xonsh\\\" in shells:\\n        if for_user:\\n            config_xonsh_path = expand(join(\\\"~\\\", \\\".xonshrc\\\"))\\n            plan.append(\\n                {\\n                    \\\"function\\\": init_xonsh_user.__name__,\\n                    \\\"kwargs\\\": {\\n                        \\\"target_path\\\": config_xonsh_path,\\n                        \\\"conda_prefix\\\": conda_prefix,\\n                        \\\"reverse\\\": reverse,\\n                    },\\n                }\\n            )\\n\\n        if for_system:\\n            if on_win:\\n                config_xonsh_path = expand(\\n                    join(\\\"%ALLUSERSPROFILE%\\\", \\\"xonsh\\\", \\\"xonshrc\\\")\\n                )\\n            else:\\n                config_xonsh_path = \\\"/etc/xonshrc\\\"\\n            plan.append(\\n                {\\n                    \\\"function\\\": init_xonsh_user.__name__,\\n                    \\\"kwargs\\\": {\\n                        \\\"target_path\\\": config_xonsh_path,\\n                        \\\"conda_prefix\\\": conda_prefix,\\n                        \\\"reverse\\\": reverse,\\n                    },\\n                }\\n            )\\n\\n    if \\\"tcsh\\\" in shells and for_user:\\n        tcshrc_path = expand(join(\\\"~\\\", \\\".tcshrc\\\"))\\n        plan.append(\\n            {\\n                \\\"function\\\": init_sh_user.__name__,\\n                \\\"kwargs\\\": {\\n                    \\\"target_path\\\": tcshrc_path,\\n                    \\\"conda_prefix\\\": conda_prefix,\\n                    \\\"shell\\\": \\\"tcsh\\\",\\n                    \\\"reverse\\\": reverse,\\n                },\\n            }\\n        )\\n\\n    if \\\"powershell\\\" in shells:\\n        if for_user:\\n            profile = \\\"$PROFILE.CurrentUserAllHosts\\\"\\n\\n        if for_system:\\n            profile = \\\"$PROFILE.AllUsersAllHosts\\\"\\n\\n        def find_powershell_paths(*exe_names):\\n            for exe_name in exe_names:\\n                try:\\n                    yield subprocess_call(\\n                        (exe_name, \\\"-NoProfile\\\", \\\"-Command\\\", profile)\\n                    ).stdout.strip()\\n                except Exception:\\n                    pass\\n\\n        config_powershell_paths = set(\\n            find_powershell_paths(\\\"powershell\\\", \\\"pwsh\\\", \\\"pwsh-preview\\\")\\n        )\\n\\n        for config_path in config_powershell_paths:\\n            if config_path is not None:\\n                plan.append(\\n                    {\\n                        \\\"function\\\": init_powershell_user.__name__,\\n                        \\\"kwargs\\\": {\\n                            \\\"target_path\\\": config_path,\\n                            \\\"conda_prefix\\\": conda_prefix,\\n                            \\\"reverse\\\": reverse,\\n                        },\\n                    }\\n                )\\n\\n    if \\\"cmd.exe\\\" in shells:\\n        if for_user:\\n            plan.append(\\n                {\\n                    \\\"function\\\": init_cmd_exe_registry.__name__,\\n                    \\\"kwargs\\\": {\\n                        \\\"target_path\\\": \\\"HKEY_CURRENT_USER\\\\\\\\Software\\\\\\\\Microsoft\\\\\\\\\\\"\\n                        \\\"Command Processor\\\\\\\\AutoRun\\\",\\n                        \\\"conda_prefix\\\": conda_prefix,\\n                        \\\"reverse\\\": reverse,\\n                    },\\n                }\\n            )\\n        if for_system:\\n            plan.append(\\n                {\\n                    \\\"function\\\": init_cmd_exe_registry.__name__,\\n                    \\\"kwargs\\\": {\\n                        \\\"target_path\\\": \\\"HKEY_LOCAL_MACHINE\\\\\\\\Software\\\\\\\\Microsoft\\\\\\\\\\\"\\n                        \\\"Command Processor\\\\\\\\AutoRun\\\",\\n                        \\\"conda_prefix\\\": conda_prefix,\\n                        \\\"reverse\\\": reverse,\\n                    },\\n                }\\n            )\\n            # it would be nice to enable this on a user-level basis, but unfortunately, it is\\n            #    a system-level key only.\\n            plan.append(\\n                {\\n                    \\\"function\\\": init_long_path.__name__,\\n                    \\\"kwargs\\\": {\\n                        \\\"target_path\\\": \\\"HKEY_LOCAL_MACHINE\\\\\\\\SYSTEM\\\\\\\\CurrentControlSet\\\\\\\\Control\\\\\\\\\\\"\\n                        \\\"FileSystem\\\\\\\\LongPathsEnabled\\\"\\n                    },\\n                }\\n            )\\n        if anaconda_prompt:\\n            plan.append(\\n                {\\n                    \\\"function\\\": install_anaconda_prompt.__name__,\\n                    \\\"kwargs\\\": {\\n                        \\\"target_path\\\": join(\\n                            conda_prefix, \\\"condabin\\\", \\\"Anaconda Prompt.lnk\\\"\\n                        ),\\n                        \\\"conda_prefix\\\": conda_prefix,\\n                        \\\"reverse\\\": reverse,\\n                    },\\n                }\\n            )\\n            if on_win:\\n                desktop_dir, exception = get_folder_path(FOLDERID.Desktop)\\n                assert not exception\\n            else:\\n                desktop_dir = join(expanduser(\\\"~\\\"), \\\"Desktop\\\")\\n            plan.append(\\n                {\\n                    \\\"function\\\": install_anaconda_prompt.__name__,\\n                    \\\"kwargs\\\": {\\n                        \\\"target_path\\\": join(desktop_dir, \\\"Anaconda Prompt.lnk\\\"),\\n                        \\\"conda_prefix\\\": conda_prefix,\\n                        \\\"reverse\\\": reverse,\\n                    },\\n                }\\n            )\\n\\n    return plan\\n\\n\\n# #####################################################\\n# plan runners\\n# #####################################################\\n\\n\\ndef run_plan(plan):\\n    for step in plan:\\n        previous_result = step.get(\\\"result\\\", None)\\n        if previous_result in (Result.MODIFIED, Result.NO_CHANGE):\\n            continue\\n        try:\\n            result = globals()[step[\\\"function\\\"]](\\n                *step.get(\\\"args\\\", ()), **step.get(\\\"kwargs\\\", {})\\n            )\\n        except OSError as e:\\n            log.info(\\\"%s: %r\\\", step[\\\"function\\\"], e, exc_info=True)\\n            result = Result.NEEDS_SUDO\\n        step[\\\"result\\\"] = result\\n\\n\\ndef run_plan_elevated(plan):\\n    \\\"\\\"\\\"\\n    The strategy of this function differs between unix and Windows.  Both strategies use a\\n    subprocess call, where the subprocess is run with elevated privileges.  The executable\\n    invoked with the subprocess is `python -m conda.core.initialize`, so see the\\n    `if __name__ == \\\"__main__\\\"` at the bottom of this module.\\n\\n    For unix platforms, we convert the plan list to json, and then call this module with\\n    `sudo python -m conda.core.initialize` while piping the plan json to stdin.  We collect json\\n    from stdout for the results of the plan execution with elevated privileges.\\n\\n    For Windows, we create a temporary file that holds the json content of the plan.  The\\n    subprocess reads the content of the file, modifies the content of the file with updated\\n    execution status, and then closes the file.  This process then reads the content of that file\\n    for the individual operation execution results, and then deletes the file.\\n    \\\"\\\"\\\"\\n    if any(step[\\\"result\\\"] == Result.NEEDS_SUDO for step in plan):\\n        if on_win:\\n            from ..common._os.windows import run_as_admin\\n\\n            temp_path = None\\n            try:\\n                with Utf8NamedTemporaryFile(\\\"w+\\\", suffix=\\\".json\\\", delete=False) as tf:\\n                    # the default mode is 'w+b', and universal new lines don't work in that mode\\n                    tf.write(\\n                        json.dumps(\\n                            plan, ensure_ascii=False, default=lambda x: x.__dict__\\n                        )\\n                    )\\n                    temp_path = tf.name\\n                python_exe = f'\\\"{abspath(sys.executable)}\\\"'\\n                hinstance, error_code = run_as_admin(\\n                    (python_exe, \\\"-m\\\", \\\"conda.core.initialize\\\", f'\\\"{temp_path}\\\"')\\n                )\\n                if error_code is not None:\\n                    print(\\n                        f\\\"ERROR during elevated execution.\\\\n  rc: {error_code}\\\",\\n                        file=sys.stderr,\\n                    )\\n\\n                with open(temp_path) as fh:\\n                    _plan = json.loads(ensure_text_type(fh.read()))\\n\\n            finally:\\n                if temp_path and lexists(temp_path):\\n                    rm_rf(temp_path)\\n\\n        else:\\n            stdin = json.dumps(plan, ensure_ascii=False, default=lambda x: x.__dict__)\\n            result = subprocess_call(\\n                f\\\"sudo {sys.executable} -m conda.core.initialize\\\",\\n                env={},\\n                path=os.getcwd(),\\n                stdin=stdin,\\n            )\\n            stderr = result.stderr.strip()\\n            if stderr:\\n                print(stderr, file=sys.stderr)\\n            _plan = json.loads(result.stdout.strip())\\n\\n        del plan[:]\\n        plan.extend(_plan)\\n\\n\\ndef run_plan_from_stdin():\\n    stdin = sys.stdin.read().strip()\\n    plan = json.loads(stdin)\\n    run_plan(plan)\\n    sys.stdout.write(json.dumps(plan))\\n\\n\\ndef run_plan_from_temp_file(temp_path):\\n    with open(temp_path) as fh:\\n        plan = json.loads(ensure_text_type(fh.read()))\\n    run_plan(plan)\\n    with open(temp_path, \\\"w+b\\\") as fh:\\n        fh.write(ensure_binary(json.dumps(plan, ensure_ascii=False)))\\n\\n\\ndef print_plan_results(plan, stream=None):\\n    if not stream:\\n        stream = sys.stdout\\n    for step in plan:\\n        print(\\n            \\\"%-14s%s\\\" % (step.get(\\\"result\\\"), step[\\\"kwargs\\\"][\\\"target_path\\\"]), file=stream\\n        )\\n\\n    changed = any(step.get(\\\"result\\\") == Result.MODIFIED for step in plan)\\n    if changed:\\n        print(\\n            \\\"\\\\n==> For changes to take effect, close and re-open your current shell. <==\\\\n\\\",\\n            file=stream,\\n        )\\n    else:\\n        print(\\\"No action taken.\\\", file=stream)\\n\\n\\n# #####################################################\\n# individual operations\\n# #####################################################\\n\\n\\ndef make_entry_point(target_path, conda_prefix, module, func):\\n    # 'ep' in this function refers to 'entry point'\\n    # target_path: join(conda_prefix, 'bin', 'conda')\\n    conda_ep_path = target_path\\n\\n    if isfile(conda_ep_path):\\n        with open(conda_ep_path) as fh:\\n            original_ep_content = fh.read()\\n    else:\\n        original_ep_content = \\\"\\\"\\n\\n    if on_win:\\n        # no shebang needed on windows\\n        new_ep_content = \\\"\\\"\\n    else:\\n        python_path = join(conda_prefix, get_python_short_path())\\n        new_ep_content = generate_shebang_for_entry_point(\\n            python_path, with_usr_bin_env=True\\n        )\\n\\n    conda_extra = dals(\\n        \\\"\\\"\\\"\\n    # Before any more imports, leave cwd out of sys.path for internal 'conda shell.*' commands.\\n    # see https://github.com/conda/conda/issues/6549\\n    if len(sys.argv) > 1 and sys.argv[1].startswith('shell.') and sys.path and sys.path[0] == '':\\n        # The standard first entry in sys.path is an empty string,\\n        # and os.path.abspath('') expands to os.getcwd().\\n        del sys.path[0]\\n    \\\"\\\"\\\"\\n    )\\n\\n    new_ep_content += dals(\\n        \\\"\\\"\\\"\\n    # -*- coding: utf-8 -*-\\n    import sys\\n    %(extra)s\\n    if __name__ == '__main__':\\n        from %(module)s import %(func)s\\n        sys.exit(%(func)s())\\n    \\\"\\\"\\\"\\n    ) % {\\n        \\\"extra\\\": conda_extra if module == \\\"conda.cli\\\" else \\\"\\\",\\n        \\\"module\\\": module,\\n        \\\"func\\\": func,\\n    }\\n\\n    if new_ep_content != original_ep_content:\\n        if context.verbose:\\n            print(\\\"\\\\n\\\")\\n            print(target_path)\\n            print(make_diff(original_ep_content, new_ep_content))\\n        if not context.dry_run:\\n            mkdir_p(dirname(conda_ep_path))\\n            with open(conda_ep_path, \\\"w\\\") as fdst:\\n                fdst.write(new_ep_content)\\n            if not on_win:\\n                make_executable(conda_ep_path)\\n        return Result.MODIFIED\\n    else:\\n        return Result.NO_CHANGE\\n\\n\\ndef make_entry_point_exe(target_path, conda_prefix):\\n    # target_path: join(conda_prefix, 'Scripts', 'conda.exe')\\n    exe_path = target_path\\n    bits = 8 * struct.calcsize(\\\"P\\\")\\n    source_exe_path = join(CONDA_PACKAGE_ROOT, \\\"shell\\\", \\\"cli-%d.exe\\\" % bits)\\n    if isfile(exe_path):\\n        if compute_sum(exe_path, \\\"md5\\\") == compute_sum(source_exe_path, \\\"md5\\\"):\\n            return Result.NO_CHANGE\\n\\n    if not context.dry_run:\\n        if not isdir(dirname(exe_path)):\\n            mkdir_p(dirname(exe_path))\\n        # prefer copy() over create_hard_link_or_copy() because of windows file deletion issues\\n        # with open processes\\n        copy(source_exe_path, exe_path)\\n    return Result.MODIFIED\\n\\n\\ndef install_anaconda_prompt(target_path, conda_prefix, reverse):\\n    # target_path: join(conda_prefix, 'condabin', 'Anaconda Prompt.lnk')\\n    # target: join(os.environ[\\\"HOMEPATH\\\"], \\\"Desktop\\\", \\\"Anaconda Prompt.lnk\\\")\\n    icon_path = join(CONDA_PACKAGE_ROOT, \\\"shell\\\", \\\"conda_icon.ico\\\")\\n    target = join(os.environ[\\\"HOMEPATH\\\"], \\\"Desktop\\\", \\\"Anaconda Prompt.lnk\\\")\\n\\n    args = (\\n        \\\"/K\\\",\\n        '\\\"\\\"{}\\\" && \\\"{}\\\"\\\"'.format(\\n            join(conda_prefix, \\\"condabin\\\", \\\"conda_hook.bat\\\"),\\n            join(conda_prefix, \\\"condabin\\\", \\\"conda_auto_activate.bat\\\"),\\n        ),\\n    )\\n    # The API for the call to 'create_shortcut' has 3\\n    # required arguments (path, description, filename)\\n    # and 4 optional ones (args, working_dir, icon_path, icon_index).\\n    result = Result.NO_CHANGE\\n    if not context.dry_run:\\n        create_shortcut(\\n            \\\"%windir%\\\\\\\\System32\\\\\\\\cmd.exe\\\",\\n            \\\"Anconda Prompt\\\",\\n            \\\"\\\" + target_path,\\n            \\\" \\\".join(args),\\n            \\\"\\\" + expanduser(\\\"~\\\"),\\n            \\\"\\\" + icon_path,\\n        )\\n        result = Result.MODIFIED\\n    if reverse:\\n        if os.path.isfile(target):\\n            os.remove(target)\\n            result = Result.MODIFIED\\n    return result\\n\\n\\ndef _install_file(target_path, file_content):\\n    if isfile(target_path):\\n        with open(target_path) as fh:\\n            original_content = fh.read()\\n    else:\\n        original_content = \\\"\\\"\\n\\n    new_content = file_content\\n\\n    if new_content != original_content:\\n        if context.verbose:\\n            print(\\\"\\\\n\\\")\\n            print(target_path)\\n            print(make_diff(original_content, new_content))\\n        if not context.dry_run:\\n            mkdir_p(dirname(target_path))\\n            with open(target_path, \\\"w\\\") as fdst:\\n                fdst.write(new_content)\\n        return Result.MODIFIED\\n    else:\\n        return Result.NO_CHANGE\\n\\n\\ndef install_conda_sh(target_path, conda_prefix):\\n    # target_path: join(conda_prefix, 'etc', 'profile.d', 'conda.sh')\\n    file_content = PosixActivator().hook(auto_activate_base=False)\\n    return _install_file(target_path, file_content)\\n\\n\\ndef install_Scripts_activate_bat(target_path, conda_prefix):\\n    # target_path: join(conda_prefix, 'Scripts', 'activate.bat')\\n    src_path = join(CONDA_PACKAGE_ROOT, \\\"shell\\\", \\\"Scripts\\\", \\\"activate.bat\\\")\\n    with open(src_path) as fsrc:\\n        file_content = fsrc.read()\\n    return _install_file(target_path, file_content)\\n\\n\\ndef install_activate_bat(target_path, conda_prefix):\\n    # target_path: join(conda_prefix, 'condabin', 'activate.bat')\\n    src_path = join(CONDA_PACKAGE_ROOT, \\\"shell\\\", \\\"condabin\\\", \\\"activate.bat\\\")\\n    with open(src_path) as fsrc:\\n        file_content = fsrc.read()\\n    return _install_file(target_path, file_content)\\n\\n\\ndef install_deactivate_bat(target_path, conda_prefix):\\n    # target_path: join(conda_prefix, 'condabin', 'deactivate.bat')\\n    src_path = join(CONDA_PACKAGE_ROOT, \\\"shell\\\", \\\"condabin\\\", \\\"deactivate.bat\\\")\\n    with open(src_path) as fsrc:\\n        file_content = fsrc.read()\\n    return _install_file(target_path, file_content)\\n\\n\\ndef install_activate(target_path, conda_prefix):\\n    # target_path: join(conda_prefix, get_bin_directory_short_path(), 'activate')\\n    src_path = join(CONDA_PACKAGE_ROOT, \\\"shell\\\", \\\"bin\\\", \\\"activate\\\")\\n    file_content = f'#!/bin/sh\\\\n_CONDA_ROOT=\\\"{conda_prefix}\\\"\\\\n'\\n    with open(src_path) as fsrc:\\n        file_content += fsrc.read()\\n    return _install_file(target_path, file_content)\\n\\n\\ndef install_deactivate(target_path, conda_prefix):\\n    # target_path: join(conda_prefix, get_bin_directory_short_path(), 'deactivate')\\n    src_path = join(CONDA_PACKAGE_ROOT, \\\"shell\\\", \\\"bin\\\", \\\"deactivate\\\")\\n    file_content = f'#!/bin/sh\\\\n_CONDA_ROOT=\\\"{conda_prefix}\\\"\\\\n'\\n    with open(src_path) as fsrc:\\n        file_content += fsrc.read()\\n    return _install_file(target_path, file_content)\\n\\n\\ndef install_condabin_conda_bat(target_path, conda_prefix):\\n    # target_path: join(conda_prefix, 'condabin', 'conda.bat')\\n    conda_bat_src_path = join(CONDA_PACKAGE_ROOT, \\\"shell\\\", \\\"condabin\\\", \\\"conda.bat\\\")\\n    with open(conda_bat_src_path) as fsrc:\\n        file_content = fsrc.read()\\n    return _install_file(target_path, file_content)\\n\\n\\ndef install_library_bin_conda_bat(target_path, conda_prefix):\\n    # target_path: join(conda_prefix, 'Library', 'bin', 'conda.bat')\\n    conda_bat_src_path = join(\\n        CONDA_PACKAGE_ROOT, \\\"shell\\\", \\\"Library\\\", \\\"bin\\\", \\\"conda.bat\\\"\\n    )\\n    with open(conda_bat_src_path) as fsrc:\\n        file_content = fsrc.read()\\n    return _install_file(target_path, file_content)\\n\\n\\ndef install_condabin_conda_activate_bat(target_path, conda_prefix):\\n    # target_path: join(conda_prefix, 'condabin', '_conda_activate.bat')\\n    conda_bat_src_path = join(\\n        CONDA_PACKAGE_ROOT, \\\"shell\\\", \\\"condabin\\\", \\\"_conda_activate.bat\\\"\\n    )\\n    with open(conda_bat_src_path) as fsrc:\\n        file_content = fsrc.read()\\n    return _install_file(target_path, file_content)\\n\\n\\ndef install_condabin_rename_tmp_bat(target_path, conda_prefix):\\n    # target_path: join(conda_prefix, 'condabin', 'rename_tmp.bat')\\n    conda_bat_src_path = join(CONDA_PACKAGE_ROOT, \\\"shell\\\", \\\"condabin\\\", \\\"rename_tmp.bat\\\")\\n    with open(conda_bat_src_path) as fsrc:\\n        file_content = fsrc.read()\\n    return _install_file(target_path, file_content)\\n\\n\\ndef install_condabin_conda_auto_activate_bat(target_path, conda_prefix):\\n    # target_path: join(conda_prefix, 'condabin', 'conda_auto_activate.bat')\\n    conda_bat_src_path = join(\\n        CONDA_PACKAGE_ROOT, \\\"shell\\\", \\\"condabin\\\", \\\"conda_auto_activate.bat\\\"\\n    )\\n    with open(conda_bat_src_path) as fsrc:\\n        file_content = fsrc.read()\\n    return _install_file(target_path, file_content)\\n\\n\\ndef install_condabin_hook_bat(target_path, conda_prefix):\\n    # target_path: join(conda_prefix, 'condabin', 'conda_hook.bat')\\n    conda_bat_src_path = join(CONDA_PACKAGE_ROOT, \\\"shell\\\", \\\"condabin\\\", \\\"conda_hook.bat\\\")\\n    with open(conda_bat_src_path) as fsrc:\\n        file_content = fsrc.read()\\n    return _install_file(target_path, file_content)\\n\\n\\ndef install_conda_fish(target_path, conda_prefix):\\n    # target_path: join(conda_prefix, 'etc', 'fish', 'conf.d', 'conda.fish')\\n    file_content = FishActivator().hook(auto_activate_base=False)\\n    return _install_file(target_path, file_content)\\n\\n\\ndef install_conda_psm1(target_path, conda_prefix):\\n    # target_path: join(conda_prefix, 'shell', 'condabin', 'Conda.psm1')\\n    conda_psm1_path = join(CONDA_PACKAGE_ROOT, \\\"shell\\\", \\\"condabin\\\", \\\"Conda.psm1\\\")\\n    with open(conda_psm1_path) as fsrc:\\n        file_content = fsrc.read()\\n    return _install_file(target_path, file_content)\\n\\n\\ndef install_conda_hook_ps1(target_path, conda_prefix):\\n    # target_path: join(conda_prefix, 'shell', 'condabin', 'conda-hook.ps1')\\n    file_content = PowerShellActivator().hook(auto_activate_base=False)\\n    return _install_file(target_path, file_content)\\n\\n\\ndef install_conda_xsh(target_path, conda_prefix):\\n    # target_path: join(site_packages_dir, 'xonsh', 'conda.xsh')\\n    file_content = XonshActivator().hook(auto_activate_base=False)\\n    return _install_file(target_path, file_content)\\n\\n\\ndef install_conda_csh(target_path, conda_prefix):\\n    # target_path: join(conda_prefix, 'etc', 'profile.d', 'conda.csh')\\n    file_content = CshActivator().hook(auto_activate_base=False)\\n    return _install_file(target_path, file_content)\\n\\n\\ndef _config_fish_content(conda_prefix):\\n    if on_win:\\n        from ..activate import native_path_to_unix\\n\\n        conda_exe = native_path_to_unix(join(conda_prefix, \\\"Scripts\\\", \\\"conda.exe\\\"))\\n    else:\\n        conda_exe = join(conda_prefix, \\\"bin\\\", \\\"conda\\\")\\n    conda_initialize_content = dals(\\n        \\\"\\\"\\\"\\n        # >>> conda initialize >>>\\n        # !! Contents within this block are managed by 'conda init' !!\\n        if test -f %(conda_exe)s\\n            eval %(conda_exe)s \\\"shell.fish\\\" \\\"hook\\\" $argv | source\\n        else\\n            if test -f \\\"%(conda_prefix)s/etc/fish/conf.d/conda.fish\\\"\\n                . \\\"%(conda_prefix)s/etc/fish/conf.d/conda.fish\\\"\\n            else\\n                set -x PATH \\\"%(conda_prefix)s/bin\\\" $PATH\\n            end\\n        end\\n        # <<< conda initialize <<<\\n        \\\"\\\"\\\"\\n    ) % {\\n        \\\"conda_exe\\\": conda_exe,\\n        \\\"conda_prefix\\\": conda_prefix,\\n    }\\n    return conda_initialize_content\\n\\n\\ndef init_fish_user(target_path, conda_prefix, reverse):\\n    # target_path: ~/.config/config.fish\\n    user_rc_path = target_path\\n\\n    try:\\n        with open(user_rc_path) as fh:\\n            rc_content = fh.read()\\n    except FileNotFoundError:\\n        rc_content = \\\"\\\"\\n    except:\\n        raise\\n\\n    rc_original_content = rc_content\\n\\n    conda_init_comment = \\\"# commented out by conda initialize\\\"\\n    conda_initialize_content = _config_fish_content(conda_prefix)\\n    if reverse:\\n        # uncomment any lines that were commented by prior conda init run\\n        rc_content = re.sub(\\n            rf\\\"#\\\\s(.*?)\\\\s*{conda_init_comment}\\\",\\n            r\\\"\\\\1\\\",\\n            rc_content,\\n            flags=re.MULTILINE,\\n        )\\n\\n        # remove any conda init sections added\\n        rc_content = re.sub(\\n            r\\\"^\\\\s*\\\" + CONDA_INITIALIZE_RE_BLOCK,\\n            \\\"\\\",\\n            rc_content,\\n            flags=re.DOTALL | re.MULTILINE,\\n        )\\n    else:\\n        if not on_win:\\n            rc_content = re.sub(\\n                rf\\\"^[ \\\\t]*?(set -gx PATH ([\\\\'\\\\\\\"]?).*?{basename(conda_prefix)}\\\\/bin\\\\2 [^\\\\n]*?\\\\$PATH)\\\"\\n                r\\\"\\\",\\n                rf\\\"# \\\\1  {conda_init_comment}\\\",\\n                rc_content,\\n                flags=re.MULTILINE,\\n            )\\n\\n        rc_content = re.sub(\\n            r\\\"^[ \\\\t]*[^#\\\\n]?[ \\\\t]*((?:source|\\\\.) .*etc\\\\/fish\\\\/conf\\\\.d\\\\/conda\\\\.fish.*?)\\\\n\\\"\\n            r\\\"(conda activate.*?)$\\\",\\n            rf\\\"# \\\\1  {conda_init_comment}\\\\n# \\\\2  {conda_init_comment}\\\",\\n            rc_content,\\n            flags=re.MULTILINE,\\n        )\\n        rc_content = re.sub(\\n            r\\\"^[ \\\\t]*[^#\\\\n]?[ \\\\t]*((?:source|\\\\.) .*etc\\\\/fish\\\\/conda\\\\.d\\\\/conda\\\\.fish.*?)$\\\",\\n            rf\\\"# \\\\1  {conda_init_comment}\\\",\\n            rc_content,\\n            flags=re.MULTILINE,\\n        )\\n\\n        replace_str = \\\"__CONDA_REPLACE_ME_123__\\\"\\n        rc_content = re.sub(\\n            CONDA_INITIALIZE_RE_BLOCK,\\n            replace_str,\\n            rc_content,\\n            flags=re.MULTILINE,\\n        )\\n        # TODO: maybe remove all but last of replace_str, if there's more than one occurrence\\n        rc_content = rc_content.replace(replace_str, conda_initialize_content)\\n\\n        if \\\"# >>> conda initialize >>>\\\" not in rc_content:\\n            rc_content += f\\\"\\\\n{conda_initialize_content}\\\\n\\\"\\n\\n    if rc_content != rc_original_content:\\n        if context.verbose:\\n            print(\\\"\\\\n\\\")\\n            print(target_path)\\n            print(make_diff(rc_original_content, rc_content))\\n        if not context.dry_run:\\n            # Make the directory if needed.\\n            if not exists(dirname(user_rc_path)):\\n                mkdir_p(dirname(user_rc_path))\\n            with open(user_rc_path, \\\"w\\\") as fh:\\n                fh.write(rc_content)\\n        return Result.MODIFIED\\n    else:\\n        return Result.NO_CHANGE\\n\\n\\ndef _config_xonsh_content(conda_prefix):\\n    if on_win:\\n        from ..activate import native_path_to_unix\\n\\n        conda_exe = native_path_to_unix(join(conda_prefix, \\\"Scripts\\\", \\\"conda.exe\\\"))\\n    else:\\n        conda_exe = join(conda_prefix, \\\"bin\\\", \\\"conda\\\")\\n    conda_initialize_content = dals(\\n        \\\"\\\"\\\"\\n    # >>> conda initialize >>>\\n    # !! Contents within this block are managed by 'conda init' !!\\n    if !(test -f \\\"{conda_exe}\\\"):\\n        import sys as _sys\\n        from types import ModuleType as _ModuleType\\n        _mod = _ModuleType(\\\"xontrib.conda\\\",\\n                        \\\"Autogenerated from $({conda_exe} shell.xonsh hook)\\\")\\n        __xonsh__.execer.exec($(\\\"{conda_exe}\\\" \\\"shell.xonsh\\\" \\\"hook\\\"),\\n                            glbs=_mod.__dict__,\\n                            filename=\\\"$({conda_exe} shell.xonsh hook)\\\")\\n        _sys.modules[\\\"xontrib.conda\\\"] = _mod\\n        del _sys, _mod, _ModuleType\\n    # <<< conda initialize <<<\\n    \\\"\\\"\\\"\\n    ).format(conda_exe=conda_exe)\\n    return conda_initialize_content\\n\\n\\ndef init_xonsh_user(target_path, conda_prefix, reverse):\\n    # target_path: ~/.xonshrc\\n    user_rc_path = target_path\\n\\n    try:\\n        with open(user_rc_path) as fh:\\n            rc_content = fh.read()\\n    except FileNotFoundError:\\n        rc_content = \\\"\\\"\\n    except:\\n        raise\\n\\n    rc_original_content = rc_content\\n\\n    conda_init_comment = \\\"# commented out by conda initialize\\\"\\n    conda_initialize_content = _config_xonsh_content(conda_prefix)\\n    if reverse:\\n        # uncomment any lines that were commented by prior conda init run\\n        rc_content = re.sub(\\n            rf\\\"#\\\\s(.*?)\\\\s*{conda_init_comment}\\\",\\n            r\\\"\\\\1\\\",\\n            rc_content,\\n            flags=re.MULTILINE,\\n        )\\n\\n        # remove any conda init sections added\\n        rc_content = re.sub(\\n            r\\\"^\\\\s*\\\" + CONDA_INITIALIZE_RE_BLOCK,\\n            \\\"\\\",\\n            rc_content,\\n            flags=re.DOTALL | re.MULTILINE,\\n        )\\n    else:\\n        replace_str = \\\"__CONDA_REPLACE_ME_123__\\\"\\n        rc_content = re.sub(\\n            CONDA_INITIALIZE_RE_BLOCK,\\n            replace_str,\\n            rc_content,\\n            flags=re.MULTILINE,\\n        )\\n        # TODO: maybe remove all but last of replace_str, if there's more than one occurrence\\n        rc_content = rc_content.replace(replace_str, conda_initialize_content)\\n\\n        if \\\"# >>> conda initialize >>>\\\" not in rc_content:\\n            rc_content += f\\\"\\\\n{conda_initialize_content}\\\\n\\\"\\n\\n    if rc_content != rc_original_content:\\n        if context.verbose:\\n            print(\\\"\\\\n\\\")\\n            print(target_path)\\n            print(make_diff(rc_original_content, rc_content))\\n        if not context.dry_run:\\n            # Make the directory if needed.\\n            if not exists(dirname(user_rc_path)):\\n                mkdir_p(dirname(user_rc_path))\\n            with open(user_rc_path, \\\"w\\\") as fh:\\n                fh.write(rc_content)\\n        return Result.MODIFIED\\n    else:\\n        return Result.NO_CHANGE\\n\\n\\ndef _bashrc_content(conda_prefix, shell):\\n    if on_win:\\n        from ..activate import native_path_to_unix\\n\\n        conda_exe = native_path_to_unix(join(conda_prefix, \\\"Scripts\\\", \\\"conda.exe\\\"))\\n        conda_initialize_content = dals(\\n            \\\"\\\"\\\"\\n        # >>> conda initialize >>>\\n        # !! Contents within this block are managed by 'conda init' !!\\n        if [ -f '%(conda_exe)s' ]; then\\n            eval \\\"$('%(conda_exe)s' 'shell.%(shell)s' 'hook')\\\"\\n        fi\\n        # <<< conda initialize <<<\\n        \\\"\\\"\\\"\\n        ) % {\\n            \\\"conda_exe\\\": conda_exe,\\n            \\\"shell\\\": shell,\\n        }\\n    else:\\n        conda_exe = join(conda_prefix, \\\"bin\\\", \\\"conda\\\")\\n        if shell in (\\\"csh\\\", \\\"tcsh\\\"):\\n            conda_initialize_content = dals(\\n                \\\"\\\"\\\"\\n            # >>> conda initialize >>>\\n            # !! Contents within this block are managed by 'conda init' !!\\n            if ( -f \\\"%(conda_prefix)s/etc/profile.d/conda.csh\\\" ) then\\n                source \\\"%(conda_prefix)s/etc/profile.d/conda.csh\\\"\\n            else\\n                setenv PATH \\\"%(conda_bin)s:$PATH\\\"\\n            endif\\n            # <<< conda initialize <<<\\n            \\\"\\\"\\\"\\n            ) % {\\n                \\\"conda_exe\\\": conda_exe,\\n                \\\"shell\\\": shell,\\n                \\\"conda_bin\\\": dirname(conda_exe),\\n                \\\"conda_prefix\\\": conda_prefix,\\n            }\\n        else:\\n            conda_initialize_content = dals(\\n                \\\"\\\"\\\"\\n            # >>> conda initialize >>>\\n            # !! Contents within this block are managed by 'conda init' !!\\n            __conda_setup=\\\"$('%(conda_exe)s' 'shell.%(shell)s' 'hook' 2> /dev/null)\\\"\\n            if [ $? -eq 0 ]; then\\n                eval \\\"$__conda_setup\\\"\\n            else\\n                if [ -f \\\"%(conda_prefix)s/etc/profile.d/conda.sh\\\" ]; then\\n                    . \\\"%(conda_prefix)s/etc/profile.d/conda.sh\\\"\\n                else\\n                    export PATH=\\\"%(conda_bin)s:$PATH\\\"\\n                fi\\n            fi\\n            unset __conda_setup\\n            # <<< conda initialize <<<\\n            \\\"\\\"\\\"\\n            ) % {\\n                \\\"conda_exe\\\": conda_exe,\\n                \\\"shell\\\": shell,\\n                \\\"conda_bin\\\": dirname(conda_exe),\\n                \\\"conda_prefix\\\": conda_prefix,\\n            }\\n    return conda_initialize_content\\n\\n\\ndef init_sh_user(target_path, conda_prefix, shell, reverse=False):\\n    # target_path: ~/.bash_profile\\n    user_rc_path = target_path\\n\\n    try:\\n        with open(user_rc_path) as fh:\\n            rc_content = fh.read()\\n    except FileNotFoundError:\\n        rc_content = \\\"\\\"\\n    except:\\n        raise\\n\\n    rc_original_content = rc_content\\n\\n    conda_initialize_content = _bashrc_content(conda_prefix, shell)\\n    conda_init_comment = \\\"# commented out by conda initialize\\\"\\n\\n    if reverse:\\n        # uncomment any lines that were commented by prior conda init run\\n        rc_content = re.sub(\\n            rf\\\"#\\\\s(.*?)\\\\s*{conda_init_comment}\\\",\\n            r\\\"\\\\1\\\",\\n            rc_content,\\n            flags=re.MULTILINE,\\n        )\\n\\n        # remove any conda init sections added\\n        rc_content = re.sub(\\n            r\\\"^\\\\s*\\\" + CONDA_INITIALIZE_RE_BLOCK,\\n            \\\"\\\",\\n            rc_content,\\n            flags=re.DOTALL | re.MULTILINE,\\n        )\\n    else:\\n        if not on_win:\\n            rc_content = re.sub(\\n                rf\\\"^[ \\\\t]*?(export PATH=[\\\\'\\\\\\\"].*?{basename(conda_prefix)}\\\\/bin:\\\\$PATH[\\\\'\\\\\\\"])\\\"\\n                r\\\"\\\",\\n                rf\\\"# \\\\1  {conda_init_comment}\\\",\\n                rc_content,\\n                flags=re.MULTILINE,\\n            )\\n\\n        rc_content = re.sub(\\n            r\\\"^[ \\\\t]*[^#\\\\n]?[ \\\\t]*((?:source|\\\\.) .*etc\\\\/profile\\\\.d\\\\/conda\\\\.sh.*?)\\\\n\\\"\\n            r\\\"(conda activate.*?)$\\\",\\n            rf\\\"# \\\\1  {conda_init_comment}\\\\n# \\\\2  {conda_init_comment}\\\",\\n            rc_content,\\n            flags=re.MULTILINE,\\n        )\\n        rc_content = re.sub(\\n            r\\\"^[ \\\\t]*[^#\\\\n]?[ \\\\t]*((?:source|\\\\.) .*etc\\\\/profile\\\\.d\\\\/conda\\\\.sh.*?)$\\\",\\n            rf\\\"# \\\\1  {conda_init_comment}\\\",\\n            rc_content,\\n            flags=re.MULTILINE,\\n        )\\n\\n        if on_win:\\n            rc_content = re.sub(\\n                r\\\"^[ \\\\t]*^[ \\\\t]*[^#\\\\n]?[ \\\\t]*((?:source|\\\\.) .*Scripts[/\\\\\\\\]activate.*?)$\\\",\\n                r\\\"# \\\\1  # commented out by conda initialize\\\",\\n                rc_content,\\n                flags=re.MULTILINE,\\n            )\\n        else:\\n            rc_content = re.sub(\\n                r\\\"^[ \\\\t]*^[ \\\\t]*[^#\\\\n]?[ \\\\t]*((?:source|\\\\.) .*bin/activate.*?)$\\\",\\n                r\\\"# \\\\1  # commented out by conda initialize\\\",\\n                rc_content,\\n                flags=re.MULTILINE,\\n            )\\n\\n        replace_str = \\\"__CONDA_REPLACE_ME_123__\\\"\\n        rc_content = re.sub(\\n            CONDA_INITIALIZE_RE_BLOCK,\\n            replace_str,\\n            rc_content,\\n            flags=re.MULTILINE,\\n        )\\n        # TODO: maybe remove all but last of replace_str, if there's more than one occurrence\\n        rc_content = rc_content.replace(replace_str, conda_initialize_content)\\n\\n        if \\\"# >>> conda initialize >>>\\\" not in rc_content:\\n            rc_content += f\\\"\\\\n{conda_initialize_content}\\\\n\\\"\\n\\n    if rc_content != rc_original_content:\\n        if context.verbose:\\n            print(\\\"\\\\n\\\")\\n            print(target_path)\\n            print(make_diff(rc_original_content, rc_content))\\n        if not context.dry_run:\\n            with open(user_rc_path, \\\"w\\\") as fh:\\n                fh.write(rc_content)\\n        return Result.MODIFIED\\n    else:\\n        return Result.NO_CHANGE\\n\\n\\ndef init_sh_system(target_path, conda_prefix, reverse=False):\\n    # target_path: '/etc/profile.d/conda.sh'\\n    conda_sh_system_path = target_path\\n\\n    if exists(conda_sh_system_path):\\n        with open(conda_sh_system_path) as fh:\\n            conda_sh_system_contents = fh.read()\\n    else:\\n        conda_sh_system_contents = \\\"\\\"\\n    if reverse:\\n        if exists(conda_sh_system_path):\\n            os.remove(conda_sh_system_path)\\n            return Result.MODIFIED\\n    else:\\n        conda_sh_contents = _bashrc_content(conda_prefix, \\\"posix\\\")\\n        if conda_sh_system_contents != conda_sh_contents:\\n            if context.verbose:\\n                print(\\\"\\\\n\\\")\\n                print(target_path)\\n                print(make_diff(conda_sh_contents, conda_sh_system_contents))\\n            if not context.dry_run:\\n                if lexists(conda_sh_system_path):\\n                    rm_rf(conda_sh_system_path)\\n                mkdir_p(dirname(conda_sh_system_path))\\n                with open(conda_sh_system_path, \\\"w\\\") as fh:\\n                    fh.write(conda_sh_contents)\\n            return Result.MODIFIED\\n    return Result.NO_CHANGE\\n\\n\\ndef _read_windows_registry(target_path):  # pragma: no cover\\n    # HKEY_LOCAL_MACHINE\\\\Software\\\\Microsoft\\\\Command Processor\\\\AutoRun\\n    # HKEY_CURRENT_USER\\\\Software\\\\Microsoft\\\\Command Processor\\\\AutoRun\\n    # returns value_value, value_type  -or-  None, None if target does not exist\\n    main_key, the_rest = target_path.split(\\\"\\\\\\\\\\\", 1)\\n    subkey_str, value_name = the_rest.rsplit(\\\"\\\\\\\\\\\", 1)\\n    main_key = getattr(winreg, main_key)\\n\\n    try:\\n        key = winreg.OpenKey(main_key, subkey_str, 0, winreg.KEY_READ)\\n    except OSError as e:\\n        if e.errno != ENOENT:\\n            raise\\n        return None, None\\n\\n    try:\\n        value_tuple = winreg.QueryValueEx(key, value_name)\\n        value_value = value_tuple[0]\\n        if isinstance(value_value, str):\\n            value_value = value_value.strip()\\n        value_type = value_tuple[1]\\n        return value_value, value_type\\n    except Exception:\\n        # [WinError 2] The system cannot find the file specified\\n        winreg.CloseKey(key)\\n        return None, None\\n    finally:\\n        winreg.CloseKey(key)\\n\\n\\ndef _write_windows_registry(target_path, value_value, value_type):  # pragma: no cover\\n    main_key, the_rest = target_path.split(\\\"\\\\\\\\\\\", 1)\\n    subkey_str, value_name = the_rest.rsplit(\\\"\\\\\\\\\\\", 1)\\n    main_key = getattr(winreg, main_key)\\n    try:\\n        key = winreg.OpenKey(main_key, subkey_str, 0, winreg.KEY_WRITE)\\n    except OSError as e:\\n        if e.errno != ENOENT:\\n            raise\\n        key = winreg.CreateKey(main_key, subkey_str)\\n    try:\\n        winreg.SetValueEx(key, value_name, 0, value_type, value_value)\\n    finally:\\n        winreg.CloseKey(key)\\n\\n\\ndef init_cmd_exe_registry(target_path, conda_prefix, reverse=False):\\n    # HKEY_LOCAL_MACHINE\\\\Software\\\\Microsoft\\\\Command Processor\\\\AutoRun\\n    # HKEY_CURRENT_USER\\\\Software\\\\Microsoft\\\\Command Processor\\\\AutoRun\\n\\n    prev_value, value_type = _read_windows_registry(target_path)\\n    if prev_value is None:\\n        prev_value = \\\"\\\"\\n        value_type = winreg.REG_EXPAND_SZ\\n\\n    old_hook_path = '\\\"{}\\\"'.format(join(conda_prefix, \\\"condabin\\\", \\\"conda_hook.bat\\\"))\\n    new_hook = f\\\"if exist {old_hook_path} {old_hook_path}\\\"\\n    if reverse:\\n        # we can't just reset it to None and remove it, because there may be other contents here.\\n        #   We need to strip out our part, and if there's nothing left, remove the key.\\n        # Break up string by parts joined with \\\"&\\\"\\n        autorun_parts = prev_value.split(\\\"&\\\")\\n        autorun_parts = [part.strip() for part in autorun_parts if new_hook not in part]\\n        # We must remove the old hook path too if it is there\\n        autorun_parts = [\\n            part.strip() for part in autorun_parts if old_hook_path not in part\\n        ]\\n        new_value = \\\" & \\\".join(autorun_parts)\\n    else:\\n        replace_str = \\\"__CONDA_REPLACE_ME_123__\\\"\\n        # Replace new (if exist checked) hook\\n        new_value = re.sub(\\n            r\\\"(if exist \\\\\\\"[^\\\\\\\"]*?conda[-_]hook\\\\.bat\\\\\\\" \\\\\\\"[^\\\\\\\"]*?conda[-_]hook\\\\.bat\\\\\\\")\\\",\\n            replace_str,\\n            prev_value,\\n            count=1,\\n            flags=re.IGNORECASE | re.UNICODE,\\n        )\\n        # Replace old hook\\n        new_value = re.sub(\\n            r\\\"(\\\\\\\"[^\\\\\\\"]*?conda[-_]hook\\\\.bat\\\\\\\")\\\",\\n            replace_str,\\n            new_value,\\n            flags=re.IGNORECASE | re.UNICODE,\\n        )\\n\\n        # Fold repeats of 'HOOK & HOOK'\\n        new_value_2 = new_value.replace(replace_str + \\\" & \\\" + replace_str, replace_str)\\n        while new_value_2 != new_value:\\n            new_value = new_value_2\\n            new_value_2 = new_value.replace(\\n                replace_str + \\\" & \\\" + replace_str, replace_str\\n            )\\n        new_value = new_value_2.replace(replace_str, new_hook)\\n        if new_hook not in new_value:\\n            if new_value:\\n                new_value += \\\" & \\\" + new_hook\\n            else:\\n                new_value = new_hook\\n\\n    if prev_value != new_value:\\n        if context.verbose:\\n            print(\\\"\\\\n\\\")\\n            print(target_path)\\n            print(make_diff(prev_value, new_value))\\n        if not context.dry_run:\\n            _write_windows_registry(target_path, new_value, value_type)\\n        return Result.MODIFIED\\n    else:\\n        return Result.NO_CHANGE\\n\\n\\ndef init_long_path(target_path):\\n    win_ver, _, win_rev = context.os_distribution_name_version[1].split(\\\".\\\")\\n    # win10, build 14352 was the first preview release that supported this\\n    if int(win_ver) >= 10 and int(win_rev) >= 14352:\\n        prev_value, value_type = _read_windows_registry(target_path)\\n        if str(prev_value) != \\\"1\\\":\\n            if context.verbose:\\n                print(\\\"\\\\n\\\")\\n                print(target_path)\\n                print(make_diff(str(prev_value), \\\"1\\\"))\\n            if not context.dry_run:\\n                _write_windows_registry(target_path, 1, winreg.REG_DWORD)\\n            return Result.MODIFIED\\n        else:\\n            return Result.NO_CHANGE\\n    else:\\n        if context.verbose:\\n            print(\\\"\\\\n\\\")\\n            print(\\n                \\\"Not setting long path registry key; Windows version must be at least 10 with \\\"\\n                'the fall 2016 \\\"Anniversary update\\\" or newer.'\\n            )\\n            return Result.NO_CHANGE\\n\\n\\ndef _powershell_profile_content(conda_prefix):\\n    if on_win:\\n        conda_exe = join(conda_prefix, \\\"Scripts\\\", \\\"conda.exe\\\")\\n    else:\\n        conda_exe = join(conda_prefix, \\\"bin\\\", \\\"conda\\\")\\n\\n    conda_powershell_module = dals(\\n        f\\\"\\\"\\\"\\n    #region conda initialize\\n    # !! Contents within this block are managed by 'conda init' !!\\n    If (Test-Path \\\"{conda_exe}\\\") {{\\n        (& \\\"{conda_exe}\\\" \\\"shell.powershell\\\" \\\"hook\\\") | Out-String | ?{{$_}} | Invoke-Expression\\n    }}\\n    #endregion\\n    \\\"\\\"\\\"\\n    )\\n\\n    return conda_powershell_module\\n\\n\\ndef init_powershell_user(target_path, conda_prefix, reverse):\\n    # target_path: $PROFILE\\n    profile_path = target_path\\n\\n    # NB: the user may not have created a profile. We need to check\\n    #     if the file exists first.\\n    if os.path.exists(profile_path):\\n        with open(profile_path) as fp:\\n            profile_content = fp.read()\\n    else:\\n        profile_content = \\\"\\\"\\n\\n    profile_original_content = profile_content\\n\\n    # TODO: comment out old ipmos and Import-Modules.\\n\\n    if reverse:\\n        profile_content = re.sub(\\n            CONDA_INITIALIZE_PS_RE_BLOCK,\\n            \\\"\\\",\\n            profile_content,\\n            count=1,\\n            flags=re.DOTALL | re.MULTILINE,\\n        )\\n    else:\\n        # Find what content we need to add.\\n        conda_initialize_content = _powershell_profile_content(conda_prefix)\\n\\n        if \\\"#region conda initialize\\\" not in profile_content:\\n            profile_content += f\\\"\\\\n{conda_initialize_content}\\\\n\\\"\\n        else:\\n            profile_content = re.sub(\\n                CONDA_INITIALIZE_PS_RE_BLOCK,\\n                \\\"__CONDA_REPLACE_ME_123__\\\",\\n                profile_content,\\n                count=1,\\n                flags=re.DOTALL | re.MULTILINE,\\n            ).replace(\\\"__CONDA_REPLACE_ME_123__\\\", conda_initialize_content)\\n\\n    if profile_content != profile_original_content:\\n        if context.verbose:\\n            print(\\\"\\\\n\\\")\\n            print(target_path)\\n            print(make_diff(profile_original_content, profile_content))\\n        if not context.dry_run:\\n            # Make the directory if needed.\\n            if not exists(dirname(profile_path)):\\n                mkdir_p(dirname(profile_path))\\n            with open(profile_path, \\\"w\\\") as fp:\\n                fp.write(profile_content)\\n        return Result.MODIFIED\\n    else:\\n        return Result.NO_CHANGE\\n\\n\\ndef remove_conda_in_sp_dir(target_path):\\n    # target_path: site_packages_dir\\n    modified = False\\n    site_packages_dir = target_path\\n    rm_rf_these = chain.from_iterable(\\n        (\\n            glob(join(site_packages_dir, \\\"conda-*info\\\")),\\n            glob(join(site_packages_dir, \\\"conda.*\\\")),\\n            glob(join(site_packages_dir, \\\"conda-*.egg\\\")),\\n        )\\n    )\\n    rm_rf_these = (p for p in rm_rf_these if not p.endswith(\\\"conda.egg-link\\\"))\\n    for fn in rm_rf_these:\\n        print(f\\\"rm -rf {join(site_packages_dir, fn)}\\\", file=sys.stderr)\\n        if not context.dry_run:\\n            rm_rf(join(site_packages_dir, fn))\\n        modified = True\\n    others = (\\n        \\\"conda\\\",\\n        \\\"conda_env\\\",\\n    )\\n    for other in others:\\n        path = join(site_packages_dir, other)\\n        if lexists(path):\\n            print(f\\\"rm -rf {path}\\\", file=sys.stderr)\\n            if not context.dry_run:\\n                rm_rf(path)\\n            modified = True\\n    if modified:\\n        return Result.MODIFIED\\n    else:\\n        return Result.NO_CHANGE\\n\\n\\ndef make_conda_egg_link(target_path, conda_source_root):\\n    # target_path: join(site_packages_dir, 'conda.egg-link')\\n    conda_egg_link_contents = conda_source_root + os.linesep\\n\\n    if isfile(target_path):\\n        with open(target_path, \\\"rb\\\") as fh:\\n            conda_egg_link_contents_old = fh.read()\\n    else:\\n        conda_egg_link_contents_old = \\\"\\\"\\n\\n    if conda_egg_link_contents_old != conda_egg_link_contents:\\n        if context.verbose:\\n            print(\\\"\\\\n\\\", file=sys.stderr)\\n            print(target_path, file=sys.stderr)\\n            print(\\n                make_diff(conda_egg_link_contents_old, conda_egg_link_contents),\\n                file=sys.stderr,\\n            )\\n        if not context.dry_run:\\n            with open(target_path, \\\"wb\\\") as fh:\\n                fh.write(ensure_utf8_encoding(conda_egg_link_contents))\\n        return Result.MODIFIED\\n    else:\\n        return Result.NO_CHANGE\\n\\n\\ndef modify_easy_install_pth(target_path, conda_source_root):\\n    # target_path: join(site_packages_dir, 'easy-install.pth')\\n    easy_install_new_line = conda_source_root\\n\\n    if isfile(target_path):\\n        with open(target_path) as fh:\\n            old_contents = fh.read()\\n    else:\\n        old_contents = \\\"\\\"\\n\\n    old_contents_lines = old_contents.splitlines()\\n    if easy_install_new_line in old_contents_lines:\\n        return Result.NO_CHANGE\\n\\n    ln_end = os.sep + \\\"conda\\\"\\n    old_contents_lines = tuple(\\n        ln for ln in old_contents_lines if not ln.endswith(ln_end)\\n    )\\n    new_contents = (\\n        easy_install_new_line\\n        + os.linesep\\n        + os.linesep.join(old_contents_lines)\\n        + os.linesep\\n    )\\n\\n    if context.verbose:\\n        print(\\\"\\\\n\\\", file=sys.stderr)\\n        print(target_path, file=sys.stderr)\\n        print(make_diff(old_contents, new_contents), file=sys.stderr)\\n    if not context.dry_run:\\n        with open(target_path, \\\"wb\\\") as fh:\\n            fh.write(ensure_utf8_encoding(new_contents))\\n    return Result.MODIFIED\\n\\n\\ndef make_dev_egg_info_file(target_path):\\n    # target_path: join(conda_source_root, 'conda.egg-info')\\n\\n    if isfile(target_path):\\n        with open(target_path) as fh:\\n            old_contents = fh.read()\\n    else:\\n        old_contents = \\\"\\\"\\n\\n    new_contents = (\\n        dals(\\n            \\\"\\\"\\\"\\n    Metadata-Version: 1.1\\n    Name: conda\\n    Version: %s\\n    Platform: UNKNOWN\\n    Summary: OS-agnostic, system-level binary package manager.\\n    \\\"\\\"\\\"\\n        )\\n        % CONDA_VERSION\\n    )\\n\\n    if old_contents == new_contents:\\n        return Result.NO_CHANGE\\n\\n    if context.verbose:\\n        print(\\\"\\\\n\\\", file=sys.stderr)\\n        print(target_path, file=sys.stderr)\\n        print(make_diff(old_contents, new_contents), file=sys.stderr)\\n    if not context.dry_run:\\n        if lexists(target_path):\\n            rm_rf(target_path)\\n        with open(target_path, \\\"w\\\") as fh:\\n            fh.write(new_contents)\\n    return Result.MODIFIED\\n\\n\\n# #####################################################\\n# helper functions\\n# #####################################################\\n\\n\\ndef make_diff(old, new):\\n    return \\\"\\\\n\\\".join(unified_diff(old.splitlines(), new.splitlines()))\\n\\n\\ndef _get_python_info(prefix):\\n    python_exe = join(prefix, get_python_short_path())\\n    result = subprocess_call(f\\\"{python_exe} --version\\\")\\n    stdout, stderr = result.stdout.strip(), result.stderr.strip()\\n    if stderr:\\n        python_version = stderr.split()[1]\\n    elif stdout:  # pragma: no cover\\n        python_version = stdout.split()[1]\\n    else:  # pragma: no cover\\n        raise ValueError(\\\"No python version information available.\\\")\\n\\n    site_packages_dir = join(\\n        prefix, win_path_ok(get_python_site_packages_short_path(python_version))\\n    )\\n    return python_exe, python_version, site_packages_dir\\n\\n\\nif __name__ == \\\"__main__\\\":\\n    if on_win:\\n        temp_path = sys.argv[1]\\n        run_plan_from_temp_file(temp_path)\\n    else:\\n        run_plan_from_stdin()\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Tools for managing a subdir's repodata.json.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport json\\nimport pickle\\nfrom collections import UserList, defaultdict\\nfrom functools import partial\\nfrom itertools import chain\\nfrom logging import getLogger\\nfrom os.path import exists, getmtime, isfile, join, splitext\\nfrom pathlib import Path\\nfrom time import time\\nfrom typing import TYPE_CHECKING\\n\\nfrom boltons.setutils import IndexedSet\\n\\nfrom ..auxlib.ish import dals\\nfrom ..base.constants import CONDA_PACKAGE_EXTENSION_V1, REPODATA_FN\\nfrom ..base.context import context\\nfrom ..common.io import DummyExecutor, ThreadLimitedThreadPoolExecutor, dashlist\\nfrom ..common.iterators import groupby_to_dict as groupby\\nfrom ..common.path import url_to_path\\nfrom ..common.url import join_url\\nfrom ..deprecations import deprecated\\nfrom ..exceptions import ChannelError, CondaUpgradeError, UnavailableInvalidChannel\\nfrom ..gateways.disk.delete import rm_rf\\nfrom ..gateways.repodata import (\\n    CACHE_STATE_SUFFIX,\\n    CondaRepoInterface,\\n    RepodataFetch,\\n    RepodataState,\\n    cache_fn_url,\\n    create_cache_dir,\\n    get_repo_interface,\\n)\\nfrom ..gateways.repodata import (\\n    get_cache_control_max_age as _get_cache_control_max_age,\\n)\\nfrom ..models.channel import Channel, all_channel_urls\\nfrom ..models.match_spec import MatchSpec\\nfrom ..models.records import PackageRecord\\n\\nif TYPE_CHECKING:\\n    from ..gateways.repodata import RepodataCache, RepoInterface\\n\\nlog = getLogger(__name__)\\n\\nREPODATA_PICKLE_VERSION = 30\\nMAX_REPODATA_VERSION = 2\\nREPODATA_HEADER_RE = b'\\\"(_etag|_mod|_cache_control)\\\":[ ]?\\\"(.*?[^\\\\\\\\\\\\\\\\])\\\"[,}\\\\\\\\s]'  # NOQA\\n\\n\\n@deprecated(\\n    \\\"24.3\\\",\\n    \\\"24.9\\\",\\n    addendum=\\\"Use `conda.gateways.repodata.get_cache_control_max_age` instead.\\\",\\n)\\ndef get_cache_control_max_age(cache_control_value: str) -> int:\\n    return _get_cache_control_max_age(cache_control_value)\\n\\n\\nclass SubdirDataType(type):\\n    def __call__(cls, channel, repodata_fn=REPODATA_FN):\\n        assert channel.subdir\\n        assert not channel.package_filename\\n        assert type(channel) is Channel\\n        now = time()\\n        repodata_fn = repodata_fn or REPODATA_FN\\n        cache_key = channel.url(with_credentials=True), repodata_fn\\n        if cache_key in SubdirData._cache_:\\n            cache_entry = SubdirData._cache_[cache_key]\\n            if cache_key[0] and cache_key[0].startswith(\\\"file://\\\"):\\n                channel_url = channel.url()\\n                if channel_url:\\n                    file_path = url_to_path(channel_url + \\\"/\\\" + repodata_fn)\\n                    if exists(file_path) and cache_entry._mtime >= getmtime(file_path):\\n                        return cache_entry\\n            else:\\n                return cache_entry\\n        subdir_data_instance = super().__call__(\\n            channel, repodata_fn, RepoInterface=get_repo_interface()\\n        )\\n        subdir_data_instance._mtime = now\\n        SubdirData._cache_[cache_key] = subdir_data_instance\\n        return subdir_data_instance\\n\\n\\nclass PackageRecordList(UserList):\\n    \\\"\\\"\\\"Lazily convert dicts to PackageRecord.\\\"\\\"\\\"\\n\\n    def __getitem__(self, i):\\n        if isinstance(i, slice):\\n            return self.__class__(self.data[i])\\n        else:\\n            record = self.data[i]\\n            if not isinstance(record, PackageRecord):\\n                record = PackageRecord(**record)\\n                self.data[i] = record\\n            return record\\n\\n\\nclass SubdirData(metaclass=SubdirDataType):\\n    _cache_ = {}\\n\\n    @classmethod\\n    def clear_cached_local_channel_data(cls, exclude_file=True):\\n        # This should only ever be needed during unit tests, when\\n        # CONDA_USE_ONLY_TAR_BZ2 may change during process lifetime.\\n        if exclude_file:\\n            cls._cache_ = {\\n                k: v for k, v in cls._cache_.items() if not k[0].startswith(\\\"file://\\\")\\n            }\\n        else:\\n            cls._cache_.clear()\\n\\n    @staticmethod\\n    def query_all(\\n        package_ref_or_match_spec, channels=None, subdirs=None, repodata_fn=REPODATA_FN\\n    ):\\n        from .index import check_allowlist  # TODO: fix in-line import\\n\\n        # ensure that this is not called by threaded code\\n        create_cache_dir()\\n        if channels is None:\\n            channels = context.channels\\n        if subdirs is None:\\n            subdirs = context.subdirs\\n        channel_urls = all_channel_urls(channels, subdirs=subdirs)\\n        if context.offline:\\n            grouped_urls = groupby(lambda url: url.startswith(\\\"file://\\\"), channel_urls)\\n            ignored_urls = grouped_urls.get(False, ())\\n            if ignored_urls:\\n                log.info(\\n                    \\\"Ignoring the following channel urls because mode is offline.%s\\\",\\n                    dashlist(ignored_urls),\\n                )\\n            channel_urls = IndexedSet(grouped_urls.get(True, ()))\\n\\n        check_allowlist(channel_urls)\\n\\n        def subdir_query(url):\\n            return tuple(\\n                SubdirData(Channel(url), repodata_fn=repodata_fn).query(\\n                    package_ref_or_match_spec\\n                )\\n            )\\n\\n        # TODO test timing with ProcessPoolExecutor\\n        Executor = (\\n            DummyExecutor\\n            if context.debug or context.repodata_threads == 1\\n            else partial(\\n                ThreadLimitedThreadPoolExecutor, max_workers=context.repodata_threads\\n            )\\n        )\\n        with Executor() as executor:\\n            result = tuple(\\n                chain.from_iterable(executor.map(subdir_query, channel_urls))\\n            )\\n        return result\\n\\n    def query(self, package_ref_or_match_spec):\\n        if not self._loaded:\\n            self.load()\\n        param = package_ref_or_match_spec\\n        if isinstance(param, str):\\n            param = MatchSpec(param)  # type: ignore\\n        if isinstance(param, MatchSpec):\\n            if param.get_exact_value(\\\"name\\\"):\\n                package_name = param.get_exact_value(\\\"name\\\")\\n                for prec in self._iter_records_by_name(package_name):\\n                    if param.match(prec):\\n                        yield prec\\n            else:\\n                for prec in self.iter_records():\\n                    if param.match(prec):\\n                        yield prec\\n        else:\\n            assert isinstance(param, PackageRecord)\\n            for prec in self._iter_records_by_name(param.name):\\n                if prec == param:\\n                    yield prec\\n\\n    def __init__(\\n        self, channel, repodata_fn=REPODATA_FN, RepoInterface=CondaRepoInterface\\n    ):\\n        assert channel.subdir\\n        # metaclass __init__ asserts no package_filename\\n        if channel.package_filename:  # pragma: no cover\\n            parts = channel.dump()\\n            del parts[\\\"package_filename\\\"]\\n            channel = Channel(**parts)\\n        self.channel = channel\\n        # disallow None (typing)\\n        self.url_w_subdir = self.channel.url(with_credentials=False) or \\\"\\\"\\n        self.url_w_credentials = self.channel.url(with_credentials=True) or \\\"\\\"\\n        # these can be overriden by repodata.json v2\\n        self._base_url = self.url_w_subdir\\n        self._base_url_w_credentials = self.url_w_credentials\\n        # whether or not to try using the new, trimmed-down repodata\\n        self.repodata_fn = repodata_fn\\n        self.RepoInterface = RepoInterface\\n        self._loaded = False\\n        self._key_mgr = None\\n\\n    @property\\n    def _repo(self) -> RepoInterface:\\n        \\\"\\\"\\\"\\n        Changes as we mutate self.repodata_fn.\\n        \\\"\\\"\\\"\\n        return self.repo_fetch._repo\\n\\n    @property\\n    def repo_cache(self) -> RepodataCache:\\n        return self.repo_fetch.repo_cache\\n\\n    @property\\n    def repo_fetch(self) -> RepodataFetch:\\n        \\\"\\\"\\\"\\n        Object to get repodata. Not cached since self.repodata_fn is mutable.\\n\\n        Replaces self._repo & self.repo_cache.\\n        \\\"\\\"\\\"\\n        return RepodataFetch(\\n            Path(self.cache_path_base),\\n            self.channel,\\n            self.repodata_fn,\\n            repo_interface_cls=self.RepoInterface,\\n        )\\n\\n    def reload(self):\\n        self._loaded = False\\n        self.load()\\n        return self\\n\\n    @property\\n    def cache_path_base(self):\\n        return join(\\n            create_cache_dir(),\\n            splitext(cache_fn_url(self.url_w_credentials, self.repodata_fn))[0],\\n        )\\n\\n    @property\\n    def url_w_repodata_fn(self):\\n        return self.url_w_subdir + \\\"/\\\" + self.repodata_fn\\n\\n    @property\\n    def cache_path_json(self):\\n        return Path(\\n            self.cache_path_base + (\\\"1\\\" if context.use_only_tar_bz2 else \\\"\\\") + \\\".json\\\"\\n        )\\n\\n    @property\\n    def cache_path_state(self):\\n        \\\"\\\"\\\"Out-of-band etag and other state needed by the RepoInterface.\\\"\\\"\\\"\\n        return Path(\\n            self.cache_path_base\\n            + (\\\"1\\\" if context.use_only_tar_bz2 else \\\"\\\")\\n            + CACHE_STATE_SUFFIX\\n        )\\n\\n    @property\\n    def cache_path_pickle(self):\\n        return self.cache_path_base + (\\\"1\\\" if context.use_only_tar_bz2 else \\\"\\\") + \\\".q\\\"\\n\\n    def load(self):\\n        _internal_state = self._load()\\n        if _internal_state.get(\\\"repodata_version\\\", 0) > MAX_REPODATA_VERSION:\\n            raise CondaUpgradeError(\\n                dals(\\n                    \\\"\\\"\\\"\\n                The current version of conda is too old to read repodata from\\n\\n                    %s\\n\\n                (This version only supports repodata_version 1 and 2.)\\n                Please update conda to use this channel.\\n                \\\"\\\"\\\"\\n                )\\n                % self.url_w_repodata_fn\\n            )\\n        self._base_url = _internal_state.get(\\\"base_url\\\", self.url_w_subdir)\\n        self._base_url_w_credentials = _internal_state.get(\\n            \\\"base_url_w_credentials\\\", self.url_w_credentials\\n        )\\n        self._internal_state = _internal_state\\n        self._package_records = _internal_state[\\\"_package_records\\\"]\\n        self._names_index = _internal_state[\\\"_names_index\\\"]\\n        # Unused since early 2023:\\n        self._track_features_index = _internal_state[\\\"_track_features_index\\\"]\\n        self._loaded = True\\n        return self\\n\\n    def iter_records(self):\\n        if not self._loaded:\\n            self.load()\\n        return iter(self._package_records)\\n        # could replace self._package_records with fully-converted UserList.data\\n        # after going through entire list\\n\\n    def _iter_records_by_name(self, name):\\n        for i in self._names_index[name]:\\n            yield self._package_records[i]\\n\\n    def _load(self):\\n        \\\"\\\"\\\"\\n        Try to load repodata. If e.g. we are downloading\\n        `current_repodata.json`, fall back to `repodata.json` when the former is\\n        unavailable.\\n        \\\"\\\"\\\"\\n        try:\\n            fetcher = self.repo_fetch\\n            repodata, state = fetcher.fetch_latest_parsed()\\n            return self._process_raw_repodata(repodata, state)\\n        except UnavailableInvalidChannel:\\n            if self.repodata_fn != REPODATA_FN:\\n                self.repodata_fn = REPODATA_FN\\n                return self._load()\\n            else:\\n                raise\\n\\n    def _pickle_me(self):\\n        try:\\n            log.debug(\\n                \\\"Saving pickled state for %s at %s\\\",\\n                self.url_w_repodata_fn,\\n                self.cache_path_pickle,\\n            )\\n            with open(self.cache_path_pickle, \\\"wb\\\") as fh:\\n                pickle.dump(self._internal_state, fh, pickle.HIGHEST_PROTOCOL)\\n        except Exception:\\n            log.debug(\\\"Failed to dump pickled repodata.\\\", exc_info=True)\\n\\n    def _read_local_repodata(self, state: RepodataState):\\n        # first try reading pickled data\\n        _pickled_state = self._read_pickled(state)\\n        if _pickled_state:\\n            return _pickled_state\\n\\n        raw_repodata_str, state = self.repo_fetch.read_cache()\\n        _internal_state = self._process_raw_repodata_str(raw_repodata_str, state)\\n        # taken care of by _process_raw_repodata():\\n        assert self._internal_state is _internal_state\\n        self._pickle_me()\\n        return _internal_state\\n\\n    def _pickle_valid_checks(self, pickled_state, mod, etag):\\n        \\\"\\\"\\\"Throw away the pickle if these don't all match.\\\"\\\"\\\"\\n        yield \\\"_url\\\", pickled_state.get(\\\"_url\\\"), self.url_w_credentials\\n        yield \\\"_schannel\\\", pickled_state.get(\\\"_schannel\\\"), self.channel.canonical_name\\n        yield (\\n            \\\"_add_pip\\\",\\n            pickled_state.get(\\\"_add_pip\\\"),\\n            context.add_pip_as_python_dependency,\\n        )\\n        yield \\\"_mod\\\", pickled_state.get(\\\"_mod\\\"), mod\\n        yield \\\"_etag\\\", pickled_state.get(\\\"_etag\\\"), etag\\n        yield (\\n            \\\"_pickle_version\\\",\\n            pickled_state.get(\\\"_pickle_version\\\"),\\n            REPODATA_PICKLE_VERSION,\\n        )\\n        yield \\\"fn\\\", pickled_state.get(\\\"fn\\\"), self.repodata_fn\\n\\n    def _read_pickled(self, state: RepodataState):\\n        if not isinstance(state, RepodataState):\\n            state = RepodataState(\\n                self.cache_path_json,\\n                self.cache_path_state,\\n                self.repodata_fn,\\n                dict=state,\\n            )\\n\\n        if not isfile(self.cache_path_pickle) or not isfile(self.cache_path_json):\\n            # Don't trust pickled data if there is no accompanying json data\\n            return None\\n\\n        try:\\n            if isfile(self.cache_path_pickle):\\n                log.debug(\\\"found pickle file %s\\\", self.cache_path_pickle)\\n            with open(self.cache_path_pickle, \\\"rb\\\") as fh:\\n                _pickled_state = pickle.load(fh)\\n        except Exception:\\n            log.debug(\\\"Failed to load pickled repodata.\\\", exc_info=True)\\n            rm_rf(self.cache_path_pickle)\\n            return None\\n\\n        def checks():\\n            return self._pickle_valid_checks(_pickled_state, state.mod, state.etag)\\n\\n        def _check_pickled_valid():\\n            for _, left, right in checks():\\n                yield left == right\\n\\n        if not all(_check_pickled_valid()):\\n            log.debug(\\n                \\\"Pickle load validation failed for %s at %s. %r\\\",\\n                self.url_w_repodata_fn,\\n                self.cache_path_json,\\n                tuple(checks()),\\n            )\\n            return None\\n\\n        return _pickled_state\\n\\n    def _process_raw_repodata_str(\\n        self,\\n        raw_repodata_str,\\n        state: RepodataState | None = None,\\n    ):\\n        \\\"\\\"\\\"State contains information that was previously in-band in raw_repodata_str.\\\"\\\"\\\"\\n        json_obj = json.loads(raw_repodata_str or \\\"{}\\\")\\n        return self._process_raw_repodata(json_obj, state=state)\\n\\n    def _process_raw_repodata(self, repodata: dict, state: RepodataState | None = None):\\n        if not isinstance(state, RepodataState):\\n            state = RepodataState(\\n                self.cache_path_json,\\n                self.cache_path_state,\\n                self.repodata_fn,\\n                dict=state,\\n            )\\n\\n        subdir = repodata.get(\\\"info\\\", {}).get(\\\"subdir\\\") or self.channel.subdir\\n        assert subdir == self.channel.subdir\\n        add_pip = context.add_pip_as_python_dependency\\n        schannel = self.channel.canonical_name\\n\\n        self._package_records = _package_records = PackageRecordList()\\n        self._names_index = _names_index = defaultdict(list)\\n        self._track_features_index = _track_features_index = defaultdict(list)\\n        base_url = self._get_base_url(repodata, with_credentials=False)\\n        base_url_w_credentials = self._get_base_url(repodata, with_credentials=True)\\n\\n        _internal_state = {\\n            \\\"channel\\\": self.channel,\\n            \\\"url_w_subdir\\\": self.url_w_subdir,\\n            \\\"url_w_credentials\\\": self.url_w_credentials,\\n            \\\"base_url\\\": base_url,\\n            \\\"base_url_w_credentials\\\": base_url_w_credentials,\\n            \\\"cache_path_base\\\": self.cache_path_base,\\n            \\\"fn\\\": self.repodata_fn,\\n            \\\"_package_records\\\": _package_records,\\n            \\\"_names_index\\\": _names_index,\\n            \\\"_track_features_index\\\": _track_features_index,\\n            \\\"_etag\\\": state.get(\\\"_etag\\\"),\\n            \\\"_mod\\\": state.get(\\\"_mod\\\"),\\n            \\\"_cache_control\\\": state.get(\\\"_cache_control\\\"),\\n            \\\"_url\\\": state.get(\\\"_url\\\"),\\n            \\\"_add_pip\\\": add_pip,\\n            \\\"_pickle_version\\\": REPODATA_PICKLE_VERSION,\\n            \\\"_schannel\\\": schannel,\\n            \\\"repodata_version\\\": state.get(\\\"repodata_version\\\", 0),\\n        }\\n        if _internal_state[\\\"repodata_version\\\"] > MAX_REPODATA_VERSION:\\n            raise CondaUpgradeError(\\n                dals(\\n                    \\\"\\\"\\\"\\n                The current version of conda is too old to read repodata from\\n\\n                    %s\\n\\n                (This version only supports repodata_version 1 and 2.)\\n                Please update conda to use this channel.\\n                \\\"\\\"\\\"\\n                )\\n                % self.url_w_subdir\\n            )\\n\\n        meta_in_common = {  # just need to make this once, then apply with .update()\\n            \\\"arch\\\": repodata.get(\\\"info\\\", {}).get(\\\"arch\\\"),\\n            \\\"channel\\\": self.channel,\\n            \\\"platform\\\": repodata.get(\\\"info\\\", {}).get(\\\"platform\\\"),\\n            \\\"schannel\\\": schannel,\\n            \\\"subdir\\\": subdir,\\n        }\\n\\n        legacy_packages = repodata.get(\\\"packages\\\", {})\\n        conda_packages = (\\n            {} if context.use_only_tar_bz2 else repodata.get(\\\"packages.conda\\\", {})\\n        )\\n\\n        _tar_bz2 = CONDA_PACKAGE_EXTENSION_V1\\n        use_these_legacy_keys = set(legacy_packages.keys()) - {\\n            k[:-6] + _tar_bz2 for k in conda_packages.keys()\\n        }\\n\\n        for group, copy_legacy_md5 in (\\n            (conda_packages.items(), True),\\n            (((k, legacy_packages[k]) for k in use_these_legacy_keys), False),\\n        ):\\n            for fn, info in group:\\n                if copy_legacy_md5:\\n                    counterpart = fn.replace(\\\".conda\\\", \\\".tar.bz2\\\")\\n                    if counterpart in legacy_packages:\\n                        info[\\\"legacy_bz2_md5\\\"] = legacy_packages[counterpart].get(\\\"md5\\\")\\n                        info[\\\"legacy_bz2_size\\\"] = legacy_packages[counterpart].get(\\n                            \\\"size\\\"\\n                        )\\n                if (\\n                    add_pip\\n                    and info[\\\"name\\\"] == \\\"python\\\"\\n                    and info[\\\"version\\\"].startswith((\\\"2.\\\", \\\"3.\\\"))\\n                ):\\n                    info[\\\"depends\\\"].append(\\\"pip\\\")\\n                info.update(meta_in_common)\\n                if info.get(\\\"record_version\\\", 0) > 1:\\n                    log.debug(\\n                        \\\"Ignoring record_version %d from %s\\\",\\n                        info[\\\"record_version\\\"],\\n                        info[\\\"url\\\"],\\n                    )\\n                    continue\\n\\n                # lazy\\n                # package_record = PackageRecord(**info)\\n                info[\\\"fn\\\"] = fn\\n                info[\\\"url\\\"] = join_url(base_url_w_credentials, fn)\\n                _package_records.append(info)\\n                record_index = len(_package_records) - 1\\n                _names_index[info[\\\"name\\\"]].append(record_index)\\n\\n        self._internal_state = _internal_state\\n        return _internal_state\\n\\n    def _get_base_url(self, repodata: dict, with_credentials: bool = True) -> str:\\n        \\\"\\\"\\\"\\n        In repodata_version=1, .tar.bz2 and .conda artifacts are assumed to\\n        be colocated next to repodata.json, in the same server and directory.\\n\\n        In repodata_version=2, repodata.json files can define a 'base_url' field\\n        to override that default assumption. See CEP-15 for more details.\\n\\n        This method deals with both cases and returns the appropriate value.\\n        \\\"\\\"\\\"\\n        maybe_base_url = repodata.get(\\\"info\\\", {}).get(\\\"base_url\\\")\\n        if maybe_base_url:  # repodata defines base_url field\\n            try:\\n                base_url_parts = Channel(maybe_base_url).dump()\\n            except ValueError as exc:\\n                raise ChannelError(\\n                    f\\\"Subdir for {self.channel.canonical_name} at url '{self.url_w_subdir}' \\\"\\n                    \\\"has invalid 'base_url'\\\"\\n                ) from exc\\n            if with_credentials and self.url_w_credentials != self.url_w_subdir:\\n                # We don't check for .token or .auth because those are not well defined\\n                # in multichannel objects. It's safer to compare the resulting URLs.\\n                # Note that base_url is assumed to have the same authentication as the repodata\\n                channel_parts = self.channel.dump()\\n                for key in (\\\"auth\\\", \\\"token\\\"):\\n                    if base_url_parts.get(key):\\n                        raise ChannelError(\\n                            f\\\"'{self.url_w_subdir}' has 'base_url' with credentials. \\\"\\n                            \\\"This is not supported.\\\"\\n                        )\\n                    channel_creds = channel_parts.get(key)\\n                    if channel_creds:\\n                        base_url_parts[key] = channel_creds\\n                return Channel(**base_url_parts).url(with_credentials=True)\\n            return maybe_base_url\\n        if with_credentials:\\n            return self.url_w_credentials\\n        return self.url_w_subdir\\n\\n\\ndef make_feature_record(feature_name):\\n    # necessary for the SAT solver to do the right thing with features\\n    pkg_name = f\\\"{feature_name}@\\\"\\n    return PackageRecord(\\n        name=pkg_name,\\n        version=\\\"0\\\",\\n        build=\\\"0\\\",\\n        channel=\\\"@\\\",\\n        subdir=context.subdir,\\n        md5=\\\"12345678901234567890123456789012\\\",\\n        track_features=(feature_name,),\\n        build_number=0,\\n        fn=pkg_name,\\n    )\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Tools for cross-OS portability.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nimport os\\nimport re\\nimport struct\\nimport subprocess\\nfrom logging import getLogger\\nfrom os.path import basename, realpath\\n\\nfrom ..auxlib.ish import dals\\nfrom ..base.constants import PREFIX_PLACEHOLDER\\nfrom ..base.context import context\\nfrom ..common.compat import on_linux, on_mac, on_win\\nfrom ..exceptions import BinaryPrefixReplacementError, CondaIOError\\nfrom ..gateways.disk.update import CancelOperation, update_file_in_place_as_binary\\nfrom ..models.enums import FileMode\\n\\nlog = getLogger(__name__)\\n\\n\\n# three capture groups: whole_shebang, executable, options\\nSHEBANG_REGEX = (\\n    rb\\\"^(#!\\\"  # pretty much the whole match string\\n    rb\\\"(?:[ ]*)\\\"  # allow spaces between #! and beginning of the executable path\\n    rb\\\"(/(?:\\\\\\\\ |[^ \\\\n\\\\r\\\\t])*)\\\"  # the executable is the next text block without an escaped space or non-space whitespace character  # NOQA\\n    rb\\\"(.*)\\\"  # the rest of the line can contain option flags\\n    rb\\\")$\\\"\\n)  # end whole_shebang group\\n\\nMAX_SHEBANG_LENGTH = 127 if on_linux else 512  # Not used on Windows\\n\\n# These are the most common file encodings that we run across when having to replace our\\n# PREFIX_PLACEHOLDER string. They apply to binary and text formats.\\n# More information/discussion: https://github.com/conda/conda/pull/9946\\nPOPULAR_ENCODINGS = (\\n    \\\"utf-8\\\",\\n    \\\"utf-16-le\\\",\\n    \\\"utf-16-be\\\",\\n    \\\"utf-32-le\\\",\\n    \\\"utf-32-be\\\",\\n)\\n\\n\\nclass _PaddingError(Exception):\\n    pass\\n\\n\\ndef _subdir_is_win(subdir: str) -> bool:\\n    if \\\"-\\\" in subdir:\\n        os, _ = subdir.lower().split(\\\"-\\\", 1)\\n        return os == \\\"win\\\"\\n    else:\\n        # For noarch, check that we are running on windows\\n        return on_win\\n\\n\\ndef update_prefix(\\n    path,\\n    new_prefix,\\n    placeholder=PREFIX_PLACEHOLDER,\\n    mode=FileMode.text,\\n    subdir=context.subdir,\\n):\\n    if _subdir_is_win(subdir) and mode == FileMode.text:\\n        # force all prefix replacements to forward slashes to simplify need to escape backslashes\\n        # replace with unix-style path separators\\n        new_prefix = new_prefix.replace(\\\"\\\\\\\\\\\", \\\"/\\\")\\n\\n    def _update_prefix(original_data):\\n        # Step 1. do all prefix replacement\\n        data = replace_prefix(mode, original_data, placeholder, new_prefix, subdir)\\n\\n        # Step 2. if the shebang is too long or the new prefix contains spaces, shorten it using\\n        # /usr/bin/env trick -- NOTE: this trick assumes the environment WILL BE activated\\n        if not _subdir_is_win(subdir):\\n            data = replace_long_shebang(mode, data)\\n\\n        # Step 3. if the before and after content is the same, skip writing\\n        if data == original_data:\\n            raise CancelOperation()\\n\\n        # Step 4. if we have a binary file, make sure the byte size is the same before\\n        #         and after the update\\n        if mode == FileMode.binary and len(data) != len(original_data):\\n            raise BinaryPrefixReplacementError(\\n                path, placeholder, new_prefix, len(original_data), len(data)\\n            )\\n\\n        return data\\n\\n    updated = update_file_in_place_as_binary(realpath(path), _update_prefix)\\n\\n    if updated and mode == FileMode.binary and subdir == \\\"osx-arm64\\\" and on_mac:\\n        # Apple arm64 needs signed executables\\n        subprocess.run(\\n            [\\\"/usr/bin/codesign\\\", \\\"-s\\\", \\\"-\\\", \\\"-f\\\", realpath(path)], capture_output=True\\n        )\\n\\n\\ndef replace_prefix(\\n    mode: FileMode,\\n    data: bytes,\\n    placeholder: str,\\n    new_prefix: str,\\n    subdir: str = \\\"noarch\\\",\\n) -> bytes:\\n    \\\"\\\"\\\"\\n    Replaces `placeholder` text with the `new_prefix` provided. The `mode` provided can\\n    either be text or binary.\\n\\n    We use the `POPULAR_ENCODINGS` module level constant defined above to make several\\n    passes at replacing the placeholder. We do this to account for as many encodings as\\n    possible. If this causes any performance problems in the future, it could potentially\\n    be removed (i.e. just using the most popular \\\"utf-8\\\" encoding\\\").\\n\\n    More information/discussion available here: https://github.com/conda/conda/pull/9946\\n    \\\"\\\"\\\"\\n    for encoding in POPULAR_ENCODINGS:\\n        if mode == FileMode.text:\\n            if not _subdir_is_win(subdir):\\n                # if new_prefix contains spaces, it might break the shebang!\\n                # handle this by escaping the spaces early, which will trigger a\\n                # /usr/bin/env replacement later on\\n                newline_pos = data.find(b\\\"\\\\n\\\")\\n                if newline_pos > -1:\\n                    shebang_line, rest_of_data = data[:newline_pos], data[newline_pos:]\\n                    shebang_placeholder = f\\\"#!{placeholder}\\\".encode(encoding)\\n                    if shebang_placeholder in shebang_line:\\n                        escaped_shebang = f\\\"#!{new_prefix}\\\".replace(\\\" \\\", \\\"\\\\\\\\ \\\").encode(\\n                            encoding\\n                        )\\n                        shebang_line = shebang_line.replace(\\n                            shebang_placeholder, escaped_shebang\\n                        )\\n                        data = shebang_line + rest_of_data\\n            # the rest of the file can be replaced normally\\n            data = data.replace(\\n                placeholder.encode(encoding), new_prefix.encode(encoding)\\n            )\\n        elif mode == FileMode.binary:\\n            data = binary_replace(\\n                data,\\n                placeholder.encode(encoding),\\n                new_prefix.encode(encoding),\\n                encoding=encoding,\\n                subdir=subdir,\\n            )\\n        else:\\n            raise CondaIOError(f\\\"Invalid mode: {mode!r}\\\")\\n    return data\\n\\n\\ndef binary_replace(\\n    data: bytes,\\n    search: bytes,\\n    replacement: bytes,\\n    encoding: str = \\\"utf-8\\\",\\n    subdir: str = \\\"noarch\\\",\\n) -> bytes:\\n    \\\"\\\"\\\"\\n    Perform a binary replacement of `data`, where the placeholder `search` is\\n    replaced with `replacement` and the remaining string is padded with null characters.\\n    All input arguments are expected to be bytes objects.\\n\\n    Parameters\\n    ----------\\n    data:\\n        The bytes object that will be searched and replaced\\n    search:\\n        The bytes object to find\\n    replacement:\\n        The bytes object that will replace `search`\\n    encoding: str\\n        The encoding of the expected string in the binary.\\n    \\\"\\\"\\\"\\n    zeros = \\\"\\\\0\\\".encode(encoding)\\n    if _subdir_is_win(subdir):\\n        # on Windows for binary files, we currently only replace a pyzzer-type entry point\\n        #   we skip all other prefix replacement\\n        if has_pyzzer_entry_point(data):\\n            return replace_pyzzer_entry_point_shebang(data, search, replacement)\\n        else:\\n            return data\\n\\n    def replace(match):\\n        occurrences = match.group().count(search)\\n        padding = (len(search) - len(replacement)) * occurrences\\n        if padding < 0:\\n            raise _PaddingError\\n        return match.group().replace(search, replacement) + b\\\"\\\\0\\\" * padding\\n\\n    original_data_len = len(data)\\n    pat = re.compile(\\n        re.escape(search) + b\\\"(?:(?!(?:\\\" + zeros + b\\\")).)*\\\" + zeros, flags=re.DOTALL\\n    )\\n    data = pat.sub(replace, data)\\n    assert len(data) == original_data_len\\n\\n    return data\\n\\n\\ndef has_pyzzer_entry_point(data):\\n    pos = data.rfind(b\\\"PK\\\\x05\\\\x06\\\")\\n    return pos >= 0\\n\\n\\ndef replace_pyzzer_entry_point_shebang(all_data, placeholder, new_prefix):\\n    \\\"\\\"\\\"Code adapted from pyzzer.  This is meant to deal with entry point exe's created by distlib,\\n    which consist of a launcher, then a shebang, then a zip archive of the entry point code to run.\\n    We need to change the shebang.\\n    https://bitbucket.org/vinay.sajip/pyzzer/src/5d5740cb04308f067d5844a56fbe91e7a27efccc/pyzzer/__init__.py?at=default&fileviewer=file-view-default#__init__.py-112  # NOQA\\n    \\\"\\\"\\\"\\n    # Copyright (c) 2013 Vinay Sajip.\\n    #\\n    # Permission is hereby granted, free of charge, to any person obtaining a copy\\n    # of this software and associated documentation files (the \\\"Software\\\"), to deal\\n    # in the Software without restriction, including without limitation the rights\\n    # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\\n    # copies of the Software, and to permit persons to whom the Software is\\n    # furnished to do so, subject to the following conditions:\\n    #\\n    # The above copyright notice and this permission notice shall be included in\\n    # all copies or substantial portions of the Software.\\n    #\\n    # THE SOFTWARE IS PROVIDED \\\"AS IS\\\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\\n    # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\\n    # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\\n    # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\\n    # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\\n    # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\\n    # THE SOFTWARE.\\n    launcher = shebang = None\\n    pos = all_data.rfind(b\\\"PK\\\\x05\\\\x06\\\")\\n    if pos >= 0:\\n        end_cdr = all_data[pos + 12 : pos + 20]\\n        cdr_size, cdr_offset = struct.unpack(\\\"<LL\\\", end_cdr)\\n        arc_pos = pos - cdr_size - cdr_offset\\n        data = all_data[arc_pos:]\\n        if arc_pos > 0:\\n            pos = all_data.rfind(b\\\"#!\\\", 0, arc_pos)\\n            if pos >= 0:\\n                shebang = all_data[pos:arc_pos]\\n                if pos > 0:\\n                    launcher = all_data[:pos]\\n\\n        if data and shebang and launcher:\\n            if hasattr(placeholder, \\\"encode\\\"):\\n                placeholder = placeholder.encode(\\\"utf-8\\\")\\n            if hasattr(new_prefix, \\\"encode\\\"):\\n                new_prefix = new_prefix.encode(\\\"utf-8\\\")\\n            shebang = shebang.replace(placeholder, new_prefix)\\n            all_data = b\\\"\\\".join([launcher, shebang, data])\\n    return all_data\\n\\n\\ndef replace_long_shebang(mode, data):\\n    # this function only changes a shebang line if it exists and is greater than 127 characters\\n    if mode == FileMode.text:\\n        if not isinstance(data, bytes):\\n            try:\\n                data = bytes(data, encoding=\\\"utf-8\\\")\\n            except:\\n                data = data.encode(\\\"utf-8\\\")\\n\\n        shebang_match = re.match(SHEBANG_REGEX, data, re.MULTILINE)\\n        if shebang_match:\\n            whole_shebang, executable, options = shebang_match.groups()\\n            prefix, executable_name = executable.decode(\\\"utf-8\\\").rsplit(\\\"/\\\", 1)\\n            if len(whole_shebang) > MAX_SHEBANG_LENGTH or \\\"\\\\\\\\ \\\" in prefix:\\n                new_shebang = (\\n                    f\\\"#!/usr/bin/env {executable_name}{options.decode('utf-8')}\\\"\\n                )\\n                data = data.replace(whole_shebang, new_shebang.encode(\\\"utf-8\\\"))\\n\\n    else:\\n        # TODO: binary shebangs exist; figure this out in the future if text works well\\n        pass\\n    return data\\n\\n\\ndef generate_shebang_for_entry_point(executable, with_usr_bin_env=False):\\n    \\\"\\\"\\\"\\n    This function can be used to generate a shebang line for Python entry points.\\n\\n    Use cases:\\n    - At install/link time, to generate the `noarch: python` entry points.\\n    - conda init uses it to create its own entry point during conda-build\\n    \\\"\\\"\\\"\\n    shebang = f\\\"#!{executable}\\\\n\\\"\\n    if os.environ.get(\\\"CONDA_BUILD\\\") == \\\"1\\\" and \\\"/_h_env_placehold\\\" in executable:\\n        # This is being used during a conda-build process,\\n        # which uses long prefixes on purpose. This will be replaced\\n        # with the real environment prefix at install time. Do not\\n        # do nothing for now.\\n        return shebang\\n\\n    # In principle, the naive shebang will work as long as the path\\n    # to the python executable does not contain spaces AND it's not\\n    # longer than 127 characters. Otherwise, we must fix it\\n    if len(shebang) > MAX_SHEBANG_LENGTH or \\\" \\\" in executable:\\n        if with_usr_bin_env:\\n            # This approach works well for all cases BUT it requires\\n            # the executable to be in PATH. In other words, the environment\\n            # needs to be activated!\\n            shebang = f\\\"#!/usr/bin/env {basename(executable)}\\\\n\\\"\\n        else:\\n            # This approach follows a method inspired by `pypa/distlib`\\n            # https://github.com/pypa/distlib/blob/91aa92e64/distlib/scripts.py#L129\\n            # Explanation: these lines are both valid Python and shell :)\\n            # 1. Python will read it as a triple-quoted string; end of story\\n            # 2. The shell will see:\\n            #    * '' (empty string)\\n            #    * 'exec' \\\"path/with spaces/to/python\\\" \\\"this file\\\" \\\"arguments\\\"\\n            #    * # ''' (inline comment with three quotes, ignored by shell)\\n            # This method works well BUT in some shells, $PS1 is dropped, which\\n            # makes the prompt disappear. This is very problematic for the conda\\n            # entry point! Details: https://github.com/conda/conda/issues/11885\\n            shebang = dals(\\n                f\\\"\\\"\\\"\\n                #!/bin/sh\\n                '''exec' \\\"{executable}\\\" \\\"$0\\\" \\\"$@\\\" #'''\\n                \\\"\\\"\\\"\\n            )\\n\\n    return shebang\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Atomic actions that make up a package installation or removal transaction.\\\"\\\"\\\"\\n\\nimport re\\nimport sys\\nfrom abc import ABCMeta, abstractmethod, abstractproperty\\nfrom itertools import chain\\nfrom json import JSONDecodeError\\nfrom logging import getLogger\\nfrom os.path import basename, dirname, getsize, isdir, join\\nfrom uuid import uuid4\\n\\nfrom .. import CondaError\\nfrom ..auxlib.ish import dals\\nfrom ..base.constants import CONDA_TEMP_EXTENSION\\nfrom ..base.context import context\\nfrom ..common.compat import on_win\\nfrom ..common.constants import TRACE\\nfrom ..common.path import (\\n    get_bin_directory_short_path,\\n    get_leaf_directories,\\n    get_python_noarch_target_path,\\n    get_python_short_path,\\n    parse_entry_point_def,\\n    pyc_path,\\n    url_to_path,\\n    win_path_ok,\\n)\\nfrom ..common.url import has_platform, path_to_url\\nfrom ..exceptions import (\\n    CondaUpgradeError,\\n    CondaVerificationError,\\n    NotWritableError,\\n    PaddingError,\\n    SafetyError,\\n)\\nfrom ..gateways.connection.download import download\\nfrom ..gateways.disk.create import (\\n    compile_multiple_pyc,\\n    copy,\\n    create_hard_link_or_copy,\\n    create_link,\\n    create_python_entry_point,\\n    extract_tarball,\\n    make_menu,\\n    mkdir_p,\\n    write_as_json_to_file,\\n)\\nfrom ..gateways.disk.delete import rm_rf\\nfrom ..gateways.disk.permissions import make_writable\\nfrom ..gateways.disk.read import compute_sum, islink, lexists, read_index_json\\nfrom ..gateways.disk.update import backoff_rename, touch\\nfrom ..history import History\\nfrom ..models.channel import Channel\\nfrom ..models.enums import LinkType, NoarchType, PathType\\nfrom ..models.match_spec import MatchSpec\\nfrom ..models.records import (\\n    Link,\\n    PackageCacheRecord,\\n    PackageRecord,\\n    PathDataV1,\\n    PathsData,\\n    PrefixRecord,\\n)\\nfrom .envs_manager import get_user_environments_txt_file, register_env, unregister_env\\nfrom .portability import _PaddingError, update_prefix\\nfrom .prefix_data import PrefixData\\n\\ntry:\\n    FileNotFoundError\\nexcept NameError:\\n    FileNotFoundError = IOError\\n\\nlog = getLogger(__name__)\\n\\n_MENU_RE = re.compile(r\\\"^menu/.*\\\\.json$\\\", re.IGNORECASE)\\nREPR_IGNORE_KWARGS = (\\n    \\\"transaction_context\\\",\\n    \\\"package_info\\\",\\n    \\\"hold_path\\\",\\n)\\n\\n\\nclass _Action(metaclass=ABCMeta):\\n    _verified = False\\n\\n    @abstractmethod\\n    def verify(self):\\n        # if verify fails, it should return an exception object rather than raise\\n        #  at the end of a verification run, all errors will be raised as a CondaMultiError\\n        # after successful verification, the verify method should set self._verified = True\\n        raise NotImplementedError()\\n\\n    @abstractmethod\\n    def execute(self):\\n        raise NotImplementedError()\\n\\n    @abstractmethod\\n    def reverse(self):\\n        raise NotImplementedError()\\n\\n    @abstractmethod\\n    def cleanup(self):\\n        raise NotImplementedError()\\n\\n    @property\\n    def verified(self):\\n        return self._verified\\n\\n    def __repr__(self):\\n        args = (\\n            f\\\"{key}={value!r}\\\"\\n            for key, value in vars(self).items()\\n            if key not in REPR_IGNORE_KWARGS\\n        )\\n        return \\\"{}({})\\\".format(self.__class__.__name__, \\\", \\\".join(args))\\n\\n\\nclass PathAction(_Action, metaclass=ABCMeta):\\n    @abstractproperty\\n    def target_full_path(self):\\n        raise NotImplementedError()\\n\\n\\nclass MultiPathAction(_Action, metaclass=ABCMeta):\\n    @abstractproperty\\n    def target_full_paths(self):\\n        raise NotImplementedError()\\n\\n\\nclass PrefixPathAction(PathAction, metaclass=ABCMeta):\\n    def __init__(self, transaction_context, target_prefix, target_short_path):\\n        self.transaction_context = transaction_context\\n        self.target_prefix = target_prefix\\n        self.target_short_path = target_short_path\\n\\n    @property\\n    def target_short_paths(self):\\n        return (self.target_short_path,)\\n\\n    @property\\n    def target_full_path(self):\\n        trgt, shrt_pth = self.target_prefix, self.target_short_path\\n        if trgt is not None and shrt_pth is not None:\\n            return join(trgt, win_path_ok(shrt_pth))\\n        else:\\n            return None\\n\\n\\n# ######################################################\\n#  Creation of Paths within a Prefix\\n# ######################################################\\n\\n\\nclass CreateInPrefixPathAction(PrefixPathAction, metaclass=ABCMeta):\\n    # All CreatePathAction subclasses must create a SINGLE new path\\n    #   the short/in-prefix version of that path must be returned by execute()\\n\\n    def __init__(\\n        self,\\n        transaction_context,\\n        package_info,\\n        source_prefix,\\n        source_short_path,\\n        target_prefix,\\n        target_short_path,\\n    ):\\n        super().__init__(transaction_context, target_prefix, target_short_path)\\n        self.package_info = package_info\\n        self.source_prefix = source_prefix\\n        self.source_short_path = source_short_path\\n\\n    def verify(self):\\n        self._verified = True\\n\\n    def cleanup(self):\\n        # create actions typically won't need cleanup\\n        pass\\n\\n    @property\\n    def source_full_path(self):\\n        prfx, shrt_pth = self.source_prefix, self.source_short_path\\n        return join(prfx, win_path_ok(shrt_pth)) if prfx and shrt_pth else None\\n\\n\\nclass LinkPathAction(CreateInPrefixPathAction):\\n    @classmethod\\n    def create_file_link_actions(\\n        cls, transaction_context, package_info, target_prefix, requested_link_type\\n    ):\\n        def get_prefix_replace(source_path_data):\\n            if source_path_data.path_type == PathType.softlink:\\n                link_type = LinkType.copy\\n                prefix_placehoder, file_mode = \\\"\\\", None\\n            elif source_path_data.prefix_placeholder:\\n                link_type = LinkType.copy\\n                prefix_placehoder = source_path_data.prefix_placeholder\\n                file_mode = source_path_data.file_mode\\n            elif source_path_data.no_link:\\n                link_type = LinkType.copy\\n                prefix_placehoder, file_mode = \\\"\\\", None\\n            else:\\n                link_type = requested_link_type\\n                prefix_placehoder, file_mode = \\\"\\\", None\\n\\n            return link_type, prefix_placehoder, file_mode\\n\\n        def make_file_link_action(source_path_data):\\n            # TODO: this inner function is still kind of a mess\\n            noarch = package_info.repodata_record.noarch\\n            if noarch is None and package_info.package_metadata is not None:\\n                # Look in package metadata in case it was omitted from repodata (see issue #8311)\\n                noarch = package_info.package_metadata.noarch\\n                if noarch is not None:\\n                    noarch = noarch.type\\n            if noarch == NoarchType.python:\\n                sp_dir = transaction_context[\\\"target_site_packages_short_path\\\"]\\n                if sp_dir is None:\\n                    raise CondaError(\\n                        \\\"Unable to determine python site-packages \\\"\\n                        \\\"dir in target_prefix!\\\\nPlease make sure \\\"\\n                        f\\\"python is installed in {target_prefix}\\\"\\n                    )\\n                target_short_path = get_python_noarch_target_path(\\n                    source_path_data.path, sp_dir\\n                )\\n            elif noarch is None or noarch == NoarchType.generic:\\n                target_short_path = source_path_data.path\\n            else:\\n                raise CondaUpgradeError(\\n                    dals(\\n                        \\\"\\\"\\\"\\n                The current version of conda is too old to install this package.\\n                Please update conda.\\\"\\\"\\\"\\n                    )\\n                )\\n\\n            link_type, placeholder, fmode = get_prefix_replace(source_path_data)\\n\\n            if placeholder:\\n                return PrefixReplaceLinkAction(\\n                    transaction_context,\\n                    package_info,\\n                    package_info.extracted_package_dir,\\n                    source_path_data.path,\\n                    target_prefix,\\n                    target_short_path,\\n                    requested_link_type,\\n                    placeholder,\\n                    fmode,\\n                    source_path_data,\\n                )\\n            else:\\n                return LinkPathAction(\\n                    transaction_context,\\n                    package_info,\\n                    package_info.extracted_package_dir,\\n                    source_path_data.path,\\n                    target_prefix,\\n                    target_short_path,\\n                    link_type,\\n                    source_path_data,\\n                )\\n\\n        return tuple(\\n            make_file_link_action(spi) for spi in package_info.paths_data.paths\\n        )\\n\\n    @classmethod\\n    def create_directory_actions(\\n        cls,\\n        transaction_context,\\n        package_info,\\n        target_prefix,\\n        requested_link_type,\\n        file_link_actions,\\n    ):\\n        leaf_directories = get_leaf_directories(\\n            axn.target_short_path for axn in file_link_actions\\n        )\\n        return tuple(\\n            cls(\\n                transaction_context,\\n                package_info,\\n                None,\\n                None,\\n                target_prefix,\\n                directory_short_path,\\n                LinkType.directory,\\n                None,\\n            )\\n            for directory_short_path in leaf_directories\\n        )\\n\\n    @classmethod\\n    def create_python_entry_point_windows_exe_action(\\n        cls,\\n        transaction_context,\\n        package_info,\\n        target_prefix,\\n        requested_link_type,\\n        entry_point_def,\\n    ):\\n        source_directory = context.conda_prefix\\n        source_short_path = \\\"Scripts/conda.exe\\\"\\n        command, _, _ = parse_entry_point_def(entry_point_def)\\n        target_short_path = f\\\"Scripts/{command}.exe\\\"\\n        source_path_data = PathDataV1(\\n            _path=target_short_path,\\n            path_type=PathType.windows_python_entry_point_exe,\\n        )\\n        return cls(\\n            transaction_context,\\n            package_info,\\n            source_directory,\\n            source_short_path,\\n            target_prefix,\\n            target_short_path,\\n            requested_link_type,\\n            source_path_data,\\n        )\\n\\n    def __init__(\\n        self,\\n        transaction_context,\\n        package_info,\\n        extracted_package_dir,\\n        source_short_path,\\n        target_prefix,\\n        target_short_path,\\n        link_type,\\n        source_path_data,\\n    ):\\n        super().__init__(\\n            transaction_context,\\n            package_info,\\n            extracted_package_dir,\\n            source_short_path,\\n            target_prefix,\\n            target_short_path,\\n        )\\n        self.link_type = link_type\\n        self._execute_successful = False\\n        self.source_path_data = source_path_data\\n        self.prefix_path_data = None\\n\\n    def verify(self):\\n        if self.link_type != LinkType.directory and not lexists(\\n            self.source_full_path\\n        ):  # pragma: no cover  # NOQA\\n            return CondaVerificationError(\\n                dals(\\n                    f\\\"\\\"\\\"\\n            The package for {self.package_info.repodata_record.name} located at {self.package_info.extracted_package_dir}\\n            appears to be corrupted. The path '{self.source_short_path}'\\n            specified in the package manifest cannot be found.\\n            \\\"\\\"\\\"\\n                )\\n            )\\n\\n        source_path_data = self.source_path_data\\n        try:\\n            source_path_type = source_path_data.path_type\\n        except AttributeError:\\n            source_path_type = None\\n        if source_path_type in PathType.basic_types:\\n            # this let's us keep the non-generic path types like windows_python_entry_point_exe\\n            source_path_type = None\\n\\n        if self.link_type == LinkType.directory:\\n            self.prefix_path_data = None\\n        elif self.link_type == LinkType.softlink:\\n            self.prefix_path_data = PathDataV1.from_objects(\\n                self.source_path_data,\\n                path_type=source_path_type or PathType.softlink,\\n            )\\n        elif (\\n            self.link_type == LinkType.copy\\n            and source_path_data.path_type == PathType.softlink\\n        ):\\n            self.prefix_path_data = PathDataV1.from_objects(\\n                self.source_path_data,\\n                path_type=source_path_type or PathType.softlink,\\n            )\\n\\n        elif source_path_data.path_type == PathType.hardlink:\\n            try:\\n                reported_size_in_bytes = source_path_data.size_in_bytes\\n            except AttributeError:\\n                reported_size_in_bytes = None\\n            source_size_in_bytes = 0\\n            if reported_size_in_bytes:\\n                source_size_in_bytes = getsize(self.source_full_path)\\n                if reported_size_in_bytes != source_size_in_bytes:\\n                    return SafetyError(\\n                        dals(\\n                            f\\\"\\\"\\\"\\n                    The package for {self.package_info.repodata_record.name} located at {self.package_info.extracted_package_dir}\\n                    appears to be corrupted. The path '{self.source_short_path}'\\n                    has an incorrect size.\\n                      reported size: {reported_size_in_bytes} bytes\\n                      actual size: {source_size_in_bytes} bytes\\n                    \\\"\\\"\\\"\\n                        )\\n                    )\\n\\n            try:\\n                reported_sha256 = source_path_data.sha256\\n            except AttributeError:\\n                reported_sha256 = None\\n            # sha256 is expensive.  Only run if file sizes agree, and then only if enabled\\n            if (\\n                source_size_in_bytes\\n                and reported_size_in_bytes == source_size_in_bytes\\n                and context.extra_safety_checks\\n            ):\\n                source_sha256 = compute_sum(self.source_full_path, \\\"sha256\\\")\\n\\n                if reported_sha256 and reported_sha256 != source_sha256:\\n                    return SafetyError(\\n                        dals(\\n                            f\\\"\\\"\\\"\\n                    The package for {self.package_info.repodata_record.name} located at {self.package_info.extracted_package_dir}\\n                    appears to be corrupted. The path '{self.source_short_path}'\\n                    has a sha256 mismatch.\\n                    reported sha256: {reported_sha256}\\n                    actual sha256: {source_sha256}\\n                    \\\"\\\"\\\"\\n                        )\\n                    )\\n            self.prefix_path_data = PathDataV1.from_objects(\\n                source_path_data,\\n                sha256=reported_sha256,\\n                sha256_in_prefix=reported_sha256,\\n                path_type=source_path_type or PathType.hardlink,\\n            )\\n        elif source_path_data.path_type == PathType.windows_python_entry_point_exe:\\n            self.prefix_path_data = source_path_data\\n        else:\\n            raise NotImplementedError()\\n\\n        self._verified = True\\n\\n    def execute(self):\\n        log.log(TRACE, \\\"linking %s => %s\\\", self.source_full_path, self.target_full_path)\\n        create_link(\\n            self.source_full_path,\\n            self.target_full_path,\\n            self.link_type,\\n            force=context.force,\\n        )\\n        self._execute_successful = True\\n\\n    def reverse(self):\\n        if self._execute_successful:\\n            log.log(TRACE, \\\"reversing link creation %s\\\", self.target_prefix)\\n            if not isdir(self.target_full_path):\\n                rm_rf(self.target_full_path, clean_empty_parents=True)\\n\\n\\nclass PrefixReplaceLinkAction(LinkPathAction):\\n    def __init__(\\n        self,\\n        transaction_context,\\n        package_info,\\n        extracted_package_dir,\\n        source_short_path,\\n        target_prefix,\\n        target_short_path,\\n        link_type,\\n        prefix_placeholder,\\n        file_mode,\\n        source_path_data,\\n    ):\\n        # This link_type used in execute(). Make sure we always respect LinkType.copy request.\\n        link_type = LinkType.copy if link_type == LinkType.copy else LinkType.hardlink\\n        super().__init__(\\n            transaction_context,\\n            package_info,\\n            extracted_package_dir,\\n            source_short_path,\\n            target_prefix,\\n            target_short_path,\\n            link_type,\\n            source_path_data,\\n        )\\n        self.prefix_placeholder = prefix_placeholder\\n        self.file_mode = file_mode\\n        self.intermediate_path = None\\n\\n    def verify(self):\\n        validation_error = super().verify()\\n        if validation_error:\\n            return validation_error\\n\\n        if islink(self.source_full_path):\\n            log.log(\\n                TRACE,\\n                \\\"ignoring prefix update for symlink with source path %s\\\",\\n                self.source_full_path,\\n            )\\n            # return\\n            assert False, \\\"I don't think this is the right place to ignore this\\\"\\n\\n        mkdir_p(self.transaction_context[\\\"temp_dir\\\"])\\n        self.intermediate_path = join(\\n            self.transaction_context[\\\"temp_dir\\\"], str(uuid4())\\n        )\\n\\n        log.log(\\n            TRACE, \\\"copying %s => %s\\\", self.source_full_path, self.intermediate_path\\n        )\\n        create_link(self.source_full_path, self.intermediate_path, LinkType.copy)\\n        make_writable(self.intermediate_path)\\n\\n        try:\\n            log.log(TRACE, \\\"rewriting prefixes in %s\\\", self.target_full_path)\\n            update_prefix(\\n                self.intermediate_path,\\n                context.target_prefix_override or self.target_prefix,\\n                self.prefix_placeholder,\\n                self.file_mode,\\n                subdir=self.package_info.repodata_record.subdir,\\n            )\\n        except _PaddingError:\\n            raise PaddingError(\\n                self.target_full_path,\\n                self.prefix_placeholder,\\n                len(self.prefix_placeholder),\\n            )\\n\\n        sha256_in_prefix = compute_sum(self.intermediate_path, \\\"sha256\\\")\\n\\n        self.prefix_path_data = PathDataV1.from_objects(\\n            self.prefix_path_data,\\n            file_mode=self.file_mode,\\n            path_type=PathType.hardlink,\\n            prefix_placeholder=self.prefix_placeholder,\\n            sha256_in_prefix=sha256_in_prefix,\\n        )\\n\\n        self._verified = True\\n\\n    def execute(self):\\n        if not self._verified:\\n            self.verify()\\n        source_path = self.intermediate_path or self.source_full_path\\n        log.log(TRACE, \\\"linking %s => %s\\\", source_path, self.target_full_path)\\n        create_link(source_path, self.target_full_path, self.link_type)\\n        self._execute_successful = True\\n\\n\\nclass MakeMenuAction(CreateInPrefixPathAction):\\n    @classmethod\\n    def create_actions(\\n        cls, transaction_context, package_info, target_prefix, requested_link_type\\n    ):\\n        shorcuts_lower = [name.lower() for name in (context.shortcuts_only or ())]\\n        if context.shortcuts and (\\n            not context.shortcuts_only\\n            or (shorcuts_lower and package_info.name.lower() in shorcuts_lower)\\n        ):\\n            return tuple(\\n                cls(transaction_context, package_info, target_prefix, spi.path)\\n                for spi in package_info.paths_data.paths\\n                if bool(_MENU_RE.match(spi.path))\\n            )\\n        else:\\n            return ()\\n\\n    def __init__(\\n        self, transaction_context, package_info, target_prefix, target_short_path\\n    ):\\n        super().__init__(\\n            transaction_context,\\n            package_info,\\n            None,\\n            None,\\n            target_prefix,\\n            target_short_path,\\n        )\\n        self._execute_successful = False\\n\\n    def execute(self):\\n        log.log(TRACE, \\\"making menu for %s\\\", self.target_full_path)\\n        make_menu(self.target_prefix, self.target_short_path, remove=False)\\n        self._execute_successful = True\\n\\n    def reverse(self):\\n        if self._execute_successful:\\n            log.log(TRACE, \\\"removing menu for %s\\\", self.target_full_path)\\n            make_menu(self.target_prefix, self.target_short_path, remove=True)\\n\\n\\nclass CreateNonadminAction(CreateInPrefixPathAction):\\n    @classmethod\\n    def create_actions(\\n        cls, transaction_context, package_info, target_prefix, requested_link_type\\n    ):\\n        if on_win and lexists(join(context.root_prefix, \\\".nonadmin\\\")):\\n            return (cls(transaction_context, package_info, target_prefix),)\\n        else:\\n            return ()\\n\\n    def __init__(self, transaction_context, package_info, target_prefix):\\n        super().__init__(\\n            transaction_context, package_info, None, None, target_prefix, \\\".nonadmin\\\"\\n        )\\n        self._file_created = False\\n\\n    def execute(self):\\n        log.log(TRACE, \\\"touching nonadmin %s\\\", self.target_full_path)\\n        self._file_created = touch(self.target_full_path)\\n\\n    def reverse(self):\\n        if self._file_created:\\n            log.log(TRACE, \\\"removing nonadmin file %s\\\", self.target_full_path)\\n            rm_rf(self.target_full_path)\\n\\n\\nclass CompileMultiPycAction(MultiPathAction):\\n    @classmethod\\n    def create_actions(\\n        cls,\\n        transaction_context,\\n        package_info,\\n        target_prefix,\\n        requested_link_type,\\n        file_link_actions,\\n    ):\\n        noarch = package_info.package_metadata and package_info.package_metadata.noarch\\n        if noarch is not None and noarch.type == NoarchType.python:\\n            noarch_py_file_re = re.compile(r\\\"^site-packages[/\\\\\\\\][^\\\\t\\\\n\\\\r\\\\f\\\\v]+\\\\.py$\\\")\\n            py_ver = transaction_context[\\\"target_python_version\\\"]\\n            py_files = tuple(\\n                axn.target_short_path\\n                for axn in file_link_actions\\n                if getattr(axn, \\\"source_short_path\\\")\\n                and noarch_py_file_re.match(axn.source_short_path)\\n            )\\n            pyc_files = tuple(pyc_path(pf, py_ver) for pf in py_files)\\n            return (\\n                cls(\\n                    transaction_context,\\n                    package_info,\\n                    target_prefix,\\n                    py_files,\\n                    pyc_files,\\n                ),\\n            )\\n        else:\\n            return ()\\n\\n    def __init__(\\n        self,\\n        transaction_context,\\n        package_info,\\n        target_prefix,\\n        source_short_paths,\\n        target_short_paths,\\n    ):\\n        self.transaction_context = transaction_context\\n        self.package_info = package_info\\n        self.target_prefix = target_prefix\\n        self.source_short_paths = source_short_paths\\n        self.target_short_paths = target_short_paths\\n        self.prefix_path_data = None\\n        self.prefix_paths_data = [\\n            PathDataV1(\\n                _path=p,\\n                path_type=PathType.pyc_file,\\n            )\\n            for p in self.target_short_paths\\n        ]\\n        self._execute_successful = False\\n\\n    @property\\n    def target_full_paths(self):\\n        def join_or_none(prefix, short_path):\\n            if prefix is None or short_path is None:\\n                return None\\n            else:\\n                return join(prefix, win_path_ok(short_path))\\n\\n        return (join_or_none(self.target_prefix, p) for p in self.target_short_paths)\\n\\n    @property\\n    def source_full_paths(self):\\n        def join_or_none(prefix, short_path):\\n            if prefix is None or short_path is None:\\n                return None\\n            else:\\n                return join(prefix, win_path_ok(short_path))\\n\\n        return (join_or_none(self.target_prefix, p) for p in self.source_short_paths)\\n\\n    def verify(self):\\n        self._verified = True\\n\\n    def cleanup(self):\\n        # create actions typically won't need cleanup\\n        pass\\n\\n    def execute(self):\\n        # compile_pyc is sometimes expected to fail, for example a python 3.6 file\\n        #   installed into a python 2 environment, but no code paths actually importing it\\n        # technically then, this file should be removed from the manifest in conda-meta, but\\n        #   at the time of this writing that's not currently happening\\n        log.log(TRACE, \\\"compiling %s\\\", \\\" \\\".join(self.target_full_paths))\\n        target_python_version = self.transaction_context[\\\"target_python_version\\\"]\\n        python_short_path = get_python_short_path(target_python_version)\\n        python_full_path = join(self.target_prefix, win_path_ok(python_short_path))\\n        compile_multiple_pyc(\\n            python_full_path,\\n            self.source_full_paths,\\n            self.target_full_paths,\\n            self.target_prefix,\\n            self.transaction_context[\\\"target_python_version\\\"],\\n        )\\n        self._execute_successful = True\\n\\n    def reverse(self):\\n        # this removes all pyc files even if they were not created\\n        if self._execute_successful:\\n            log.log(\\n                TRACE, \\\"reversing pyc creation %s\\\", \\\" \\\".join(self.target_full_paths)\\n            )\\n            for target_full_path in self.target_full_paths:\\n                rm_rf(target_full_path)\\n\\n\\nclass AggregateCompileMultiPycAction(CompileMultiPycAction):\\n    \\\"\\\"\\\"Bunch up all of our compile actions, so that they all get carried out at once.\\n    This avoids clobbering and is faster when we have several individual packages requiring\\n    compilation.\\n    \\\"\\\"\\\"\\n\\n    def __init__(self, *individuals, **kw):\\n        transaction_context = individuals[0].transaction_context\\n        # not used; doesn't matter\\n        package_info = individuals[0].package_info\\n        target_prefix = individuals[0].target_prefix\\n        source_short_paths = set()\\n        target_short_paths = set()\\n        for individual in individuals:\\n            source_short_paths.update(individual.source_short_paths)\\n            target_short_paths.update(individual.target_short_paths)\\n        super().__init__(\\n            transaction_context,\\n            package_info,\\n            target_prefix,\\n            source_short_paths,\\n            target_short_paths,\\n        )\\n\\n\\nclass CreatePythonEntryPointAction(CreateInPrefixPathAction):\\n    @classmethod\\n    def create_actions(\\n        cls, transaction_context, package_info, target_prefix, requested_link_type\\n    ):\\n        noarch = package_info.package_metadata and package_info.package_metadata.noarch\\n        if noarch is not None and noarch.type == NoarchType.python:\\n\\n            def this_triplet(entry_point_def):\\n                command, module, func = parse_entry_point_def(entry_point_def)\\n                target_short_path = f\\\"{get_bin_directory_short_path()}/{command}\\\"\\n                if on_win:\\n                    target_short_path += \\\"-script.py\\\"\\n                return target_short_path, module, func\\n\\n            actions = tuple(\\n                cls(\\n                    transaction_context,\\n                    package_info,\\n                    target_prefix,\\n                    *this_triplet(ep_def),\\n                )\\n                for ep_def in noarch.entry_points or ()\\n            )\\n\\n            if on_win:  # pragma: unix no cover\\n                actions += tuple(\\n                    LinkPathAction.create_python_entry_point_windows_exe_action(\\n                        transaction_context,\\n                        package_info,\\n                        target_prefix,\\n                        requested_link_type,\\n                        ep_def,\\n                    )\\n                    for ep_def in noarch.entry_points or ()\\n                )\\n\\n            return actions\\n        else:\\n            return ()\\n\\n    def __init__(\\n        self,\\n        transaction_context,\\n        package_info,\\n        target_prefix,\\n        target_short_path,\\n        module,\\n        func,\\n    ):\\n        super().__init__(\\n            transaction_context,\\n            package_info,\\n            None,\\n            None,\\n            target_prefix,\\n            target_short_path,\\n        )\\n        self.module = module\\n        self.func = func\\n\\n        if on_win:\\n            path_type = PathType.windows_python_entry_point_script\\n        else:\\n            path_type = PathType.unix_python_entry_point\\n        self.prefix_path_data = PathDataV1(\\n            _path=self.target_short_path,\\n            path_type=path_type,\\n        )\\n\\n        self._execute_successful = False\\n\\n    def execute(self):\\n        log.log(TRACE, \\\"creating python entry point %s\\\", self.target_full_path)\\n        if on_win:\\n            python_full_path = None\\n        else:\\n            target_python_version = self.transaction_context[\\\"target_python_version\\\"]\\n            python_short_path = get_python_short_path(target_python_version)\\n            python_full_path = join(\\n                context.target_prefix_override or self.target_prefix,\\n                win_path_ok(python_short_path),\\n            )\\n\\n        create_python_entry_point(\\n            self.target_full_path, python_full_path, self.module, self.func\\n        )\\n        self._execute_successful = True\\n\\n    def reverse(self):\\n        if self._execute_successful:\\n            log.log(\\n                TRACE, \\\"reversing python entry point creation %s\\\", self.target_full_path\\n            )\\n            rm_rf(self.target_full_path)\\n\\n\\nclass CreatePrefixRecordAction(CreateInPrefixPathAction):\\n    # this is the action that creates a packages json file in the conda-meta/ directory\\n\\n    @classmethod\\n    def create_actions(\\n        cls,\\n        transaction_context,\\n        package_info,\\n        target_prefix,\\n        requested_link_type,\\n        requested_spec,\\n        all_link_path_actions,\\n    ):\\n        extracted_package_dir = package_info.extracted_package_dir\\n        target_short_path = f\\\"conda-meta/{basename(extracted_package_dir)}.json\\\"\\n        return (\\n            cls(\\n                transaction_context,\\n                package_info,\\n                target_prefix,\\n                target_short_path,\\n                requested_link_type,\\n                requested_spec,\\n                all_link_path_actions,\\n            ),\\n        )\\n\\n    def __init__(\\n        self,\\n        transaction_context,\\n        package_info,\\n        target_prefix,\\n        target_short_path,\\n        requested_link_type,\\n        requested_spec,\\n        all_link_path_actions,\\n    ):\\n        super().__init__(\\n            transaction_context,\\n            package_info,\\n            None,\\n            None,\\n            target_prefix,\\n            target_short_path,\\n        )\\n        self.requested_link_type = requested_link_type\\n        self.requested_spec = requested_spec\\n        self.all_link_path_actions = list(all_link_path_actions)\\n        self._execute_successful = False\\n\\n    def execute(self):\\n        link = Link(\\n            source=self.package_info.extracted_package_dir,\\n            type=self.requested_link_type,\\n        )\\n        extracted_package_dir = self.package_info.extracted_package_dir\\n        package_tarball_full_path = self.package_info.package_tarball_full_path\\n\\n        def files_from_action(link_path_action):\\n            if isinstance(link_path_action, CompileMultiPycAction):\\n                return link_path_action.target_short_paths\\n            else:\\n                return (\\n                    (link_path_action.target_short_path,)\\n                    if isinstance(link_path_action, CreateInPrefixPathAction)\\n                    and (\\n                        not hasattr(link_path_action, \\\"link_type\\\")\\n                        or link_path_action.link_type != LinkType.directory\\n                    )\\n                    else ()\\n                )\\n\\n        def paths_from_action(link_path_action):\\n            if isinstance(link_path_action, CompileMultiPycAction):\\n                return link_path_action.prefix_paths_data\\n            else:\\n                if (\\n                    not hasattr(link_path_action, \\\"prefix_path_data\\\")\\n                    or link_path_action.prefix_path_data is None\\n                ):\\n                    return ()\\n                else:\\n                    return (link_path_action.prefix_path_data,)\\n\\n        files = list(\\n            chain.from_iterable(\\n                files_from_action(x) for x in self.all_link_path_actions if x\\n            )\\n        )\\n        paths_data = PathsData(\\n            paths_version=1,\\n            paths=chain.from_iterable(\\n                paths_from_action(x) for x in self.all_link_path_actions if x\\n            ),\\n        )\\n\\n        self.prefix_record = PrefixRecord.from_objects(\\n            self.package_info.repodata_record,\\n            # self.package_info.index_json_record,\\n            self.package_info.package_metadata,\\n            requested_spec=str(self.requested_spec),\\n            paths_data=paths_data,\\n            files=files,\\n            link=link,\\n            url=self.package_info.url,\\n            extracted_package_dir=extracted_package_dir,\\n            package_tarball_full_path=package_tarball_full_path,\\n        )\\n\\n        log.log(TRACE, \\\"creating linked package record %s\\\", self.target_full_path)\\n        PrefixData(self.target_prefix).insert(self.prefix_record)\\n        self._execute_successful = True\\n\\n    def reverse(self):\\n        log.log(\\n            TRACE, \\\"reversing linked package record creation %s\\\", self.target_full_path\\n        )\\n        if self._execute_successful:\\n            PrefixData(self.target_prefix).remove(\\n                self.package_info.repodata_record.name\\n            )\\n\\n\\nclass UpdateHistoryAction(CreateInPrefixPathAction):\\n    @classmethod\\n    def create_actions(\\n        cls,\\n        transaction_context,\\n        target_prefix,\\n        remove_specs,\\n        update_specs,\\n        neutered_specs,\\n    ):\\n        target_short_path = join(\\\"conda-meta\\\", \\\"history\\\")\\n        return (\\n            cls(\\n                transaction_context,\\n                target_prefix,\\n                target_short_path,\\n                remove_specs,\\n                update_specs,\\n                neutered_specs,\\n            ),\\n        )\\n\\n    def __init__(\\n        self,\\n        transaction_context,\\n        target_prefix,\\n        target_short_path,\\n        remove_specs,\\n        update_specs,\\n        neutered_specs,\\n    ):\\n        super().__init__(\\n            transaction_context, None, None, None, target_prefix, target_short_path\\n        )\\n        self.remove_specs = remove_specs\\n        self.update_specs = update_specs\\n        self.neutered_specs = neutered_specs\\n\\n        self.hold_path = self.target_full_path + CONDA_TEMP_EXTENSION\\n\\n    def execute(self):\\n        log.log(TRACE, \\\"updating environment history %s\\\", self.target_full_path)\\n\\n        if lexists(self.target_full_path):\\n            copy(self.target_full_path, self.hold_path)\\n\\n        h = History(self.target_prefix)\\n        h.update()\\n        h.write_specs(self.remove_specs, self.update_specs, self.neutered_specs)\\n\\n    def reverse(self):\\n        if lexists(self.hold_path):\\n            log.log(TRACE, \\\"moving %s => %s\\\", self.hold_path, self.target_full_path)\\n            backoff_rename(self.hold_path, self.target_full_path, force=True)\\n\\n    def cleanup(self):\\n        rm_rf(self.hold_path)\\n\\n\\nclass RegisterEnvironmentLocationAction(PathAction):\\n    def __init__(self, transaction_context, target_prefix):\\n        self.transaction_context = transaction_context\\n        self.target_prefix = target_prefix\\n\\n        self._execute_successful = False\\n\\n    def verify(self):\\n        user_environments_txt_file = get_user_environments_txt_file()\\n        try:\\n            touch(user_environments_txt_file, mkdir=True, sudo_safe=True)\\n            self._verified = True\\n        except NotWritableError:\\n            log.warning(\\n                \\\"Unable to create environments file. Path not writable.\\\\n\\\"\\n                \\\"  environment location: %s\\\\n\\\",\\n                user_environments_txt_file,\\n            )\\n\\n    def execute(self):\\n        log.log(TRACE, \\\"registering environment in catalog %s\\\", self.target_prefix)\\n\\n        register_env(self.target_prefix)\\n        self._execute_successful = True\\n\\n    def reverse(self):\\n        pass\\n\\n    def cleanup(self):\\n        pass\\n\\n    @property\\n    def target_full_path(self):\\n        raise NotImplementedError()\\n\\n\\n# ######################################################\\n#  Removal of Paths within a Prefix\\n# ######################################################\\n\\n\\nclass RemoveFromPrefixPathAction(PrefixPathAction, metaclass=ABCMeta):\\n    def __init__(\\n        self, transaction_context, linked_package_data, target_prefix, target_short_path\\n    ):\\n        super().__init__(transaction_context, target_prefix, target_short_path)\\n        self.linked_package_data = linked_package_data\\n\\n    def verify(self):\\n        # inability to remove will trigger a rollback\\n        # can't definitely know if path can be removed until it's attempted and failed\\n        self._verified = True\\n\\n\\nclass UnlinkPathAction(RemoveFromPrefixPathAction):\\n    def __init__(\\n        self,\\n        transaction_context,\\n        linked_package_data,\\n        target_prefix,\\n        target_short_path,\\n        link_type=LinkType.hardlink,\\n    ):\\n        super().__init__(\\n            transaction_context, linked_package_data, target_prefix, target_short_path\\n        )\\n        self.holding_short_path = self.target_short_path + CONDA_TEMP_EXTENSION\\n        self.holding_full_path = self.target_full_path + CONDA_TEMP_EXTENSION\\n        self.link_type = link_type\\n\\n    def execute(self):\\n        if self.link_type != LinkType.directory:\\n            log.log(\\n                TRACE,\\n                \\\"renaming %s => %s\\\",\\n                self.target_short_path,\\n                self.holding_short_path,\\n            )\\n            backoff_rename(self.target_full_path, self.holding_full_path, force=True)\\n\\n    def reverse(self):\\n        if self.link_type != LinkType.directory and lexists(self.holding_full_path):\\n            log.log(\\n                TRACE,\\n                \\\"reversing rename %s => %s\\\",\\n                self.holding_short_path,\\n                self.target_short_path,\\n            )\\n            backoff_rename(self.holding_full_path, self.target_full_path, force=True)\\n\\n    def cleanup(self):\\n        if not isdir(self.holding_full_path):\\n            rm_rf(self.holding_full_path, clean_empty_parents=True)\\n\\n\\nclass RemoveMenuAction(RemoveFromPrefixPathAction):\\n    @classmethod\\n    def create_actions(cls, transaction_context, linked_package_data, target_prefix):\\n        return tuple(\\n            cls(transaction_context, linked_package_data, target_prefix, trgt)\\n            for trgt in linked_package_data.files\\n            if bool(_MENU_RE.match(trgt))\\n        )\\n\\n    def __init__(\\n        self, transaction_context, linked_package_data, target_prefix, target_short_path\\n    ):\\n        super().__init__(\\n            transaction_context, linked_package_data, target_prefix, target_short_path\\n        )\\n\\n    def execute(self):\\n        log.log(TRACE, \\\"removing menu for %s \\\", self.target_prefix)\\n        make_menu(self.target_prefix, self.target_short_path, remove=True)\\n\\n    def reverse(self):\\n        log.log(TRACE, \\\"re-creating menu for %s \\\", self.target_prefix)\\n        make_menu(self.target_prefix, self.target_short_path, remove=False)\\n\\n    def cleanup(self):\\n        pass\\n\\n\\nclass RemoveLinkedPackageRecordAction(UnlinkPathAction):\\n    def __init__(\\n        self, transaction_context, linked_package_data, target_prefix, target_short_path\\n    ):\\n        super().__init__(\\n            transaction_context, linked_package_data, target_prefix, target_short_path\\n        )\\n\\n    def execute(self):\\n        super().execute()\\n        PrefixData(self.target_prefix).remove(self.linked_package_data.name)\\n\\n    def reverse(self):\\n        super().reverse()\\n        PrefixData(self.target_prefix)._load_single_record(self.target_full_path)\\n\\n\\nclass UnregisterEnvironmentLocationAction(PathAction):\\n    def __init__(self, transaction_context, target_prefix):\\n        self.transaction_context = transaction_context\\n        self.target_prefix = target_prefix\\n\\n        self._execute_successful = False\\n\\n    def verify(self):\\n        self._verified = True\\n\\n    def execute(self):\\n        log.log(TRACE, \\\"unregistering environment in catalog %s\\\", self.target_prefix)\\n\\n        unregister_env(self.target_prefix)\\n        self._execute_successful = True\\n\\n    def reverse(self):\\n        pass\\n\\n    def cleanup(self):\\n        pass\\n\\n    @property\\n    def target_full_path(self):\\n        raise NotImplementedError()\\n\\n\\n# ######################################################\\n#  Fetch / Extract Actions\\n# ######################################################\\n\\n\\nclass CacheUrlAction(PathAction):\\n    def __init__(\\n        self,\\n        url,\\n        target_pkgs_dir,\\n        target_package_basename,\\n        sha256=None,\\n        size=None,\\n        md5=None,\\n    ):\\n        self.url = url\\n        self.target_pkgs_dir = target_pkgs_dir\\n        self.target_package_basename = target_package_basename\\n        self.sha256 = sha256\\n        self.size = size\\n        self.md5 = md5\\n        self.hold_path = self.target_full_path + CONDA_TEMP_EXTENSION\\n\\n    def verify(self):\\n        assert \\\"::\\\" not in self.url\\n        self._verified = True\\n\\n    def execute(self, progress_update_callback=None):\\n        # I hate inline imports, but I guess it's ok since we're importing from the conda.core\\n        # The alternative is passing the PackageCache class to CacheUrlAction __init__\\n        from .package_cache_data import PackageCacheData\\n\\n        target_package_cache = PackageCacheData(self.target_pkgs_dir)\\n\\n        log.log(TRACE, \\\"caching url %s => %s\\\", self.url, self.target_full_path)\\n\\n        if lexists(self.hold_path):\\n            rm_rf(self.hold_path)\\n\\n        if lexists(self.target_full_path):\\n            if self.url.startswith(\\\"file:/\\\") and self.url == path_to_url(\\n                self.target_full_path\\n            ):\\n                # the source and destination are the same file, so we're done\\n                return\\n            else:\\n                backoff_rename(self.target_full_path, self.hold_path, force=True)\\n\\n        if self.url.startswith(\\\"file:/\\\"):\\n            source_path = url_to_path(self.url)\\n            self._execute_local(\\n                source_path, target_package_cache, progress_update_callback\\n            )\\n        else:\\n            self._execute_channel(target_package_cache, progress_update_callback)\\n\\n    def _execute_local(\\n        self, source_path, target_package_cache, progress_update_callback=None\\n    ):\\n        from .package_cache_data import PackageCacheData\\n\\n        if dirname(source_path) in context.pkgs_dirs:\\n            # if url points to another package cache, link to the writable cache\\n            create_hard_link_or_copy(source_path, self.target_full_path)\\n            source_package_cache = PackageCacheData(dirname(source_path))\\n\\n            # the package is already in a cache, so it came from a remote url somewhere;\\n            #   make sure that remote url is the most recent url in the\\n            #   writable cache urls.txt\\n            origin_url = source_package_cache._urls_data.get_url(\\n                self.target_package_basename\\n            )\\n            if origin_url and has_platform(origin_url, context.known_subdirs):\\n                target_package_cache._urls_data.add_url(origin_url)\\n        else:\\n            # so our tarball source isn't a package cache, but that doesn't mean it's not\\n            #   in another package cache somewhere\\n            # let's try to find the actual, remote source url by matching md5sums, and then\\n            #   record that url as the remote source url in urls.txt\\n            # we do the search part of this operation before the create_link so that we\\n            #   don't md5sum-match the file created by 'create_link'\\n            # there is no point in looking for the tarball in the cache that we are writing\\n            #   this file into because we have already removed the previous file if there was\\n            #   any. This also makes sure that we ignore the md5sum of a possible extracted\\n            #   directory that might exist in this cache because we are going to overwrite it\\n            #   anyway when we extract the tarball.\\n            source_md5sum = compute_sum(source_path, \\\"md5\\\")\\n            exclude_caches = (self.target_pkgs_dir,)\\n            pc_entry = PackageCacheData.tarball_file_in_cache(\\n                source_path, source_md5sum, exclude_caches=exclude_caches\\n            )\\n\\n            if pc_entry:\\n                origin_url = target_package_cache._urls_data.get_url(\\n                    pc_entry.extracted_package_dir\\n                )\\n            else:\\n                origin_url = None\\n\\n            # copy the tarball to the writable cache\\n            create_link(\\n                source_path,\\n                self.target_full_path,\\n                link_type=LinkType.copy,\\n                force=context.force,\\n            )\\n\\n            if origin_url and has_platform(origin_url, context.known_subdirs):\\n                target_package_cache._urls_data.add_url(origin_url)\\n            else:\\n                target_package_cache._urls_data.add_url(self.url)\\n\\n    def _execute_channel(self, target_package_cache, progress_update_callback=None):\\n        kwargs = {}\\n        if self.size is not None:\\n            kwargs[\\\"size\\\"] = self.size\\n        if self.sha256:\\n            kwargs[\\\"sha256\\\"] = self.sha256\\n        elif self.md5:\\n            kwargs[\\\"md5\\\"] = self.md5\\n        download(\\n            self.url,\\n            self.target_full_path,\\n            progress_update_callback=progress_update_callback,\\n            **kwargs,\\n        )\\n        target_package_cache._urls_data.add_url(self.url)\\n\\n    def reverse(self):\\n        if lexists(self.hold_path):\\n            log.log(TRACE, \\\"moving %s => %s\\\", self.hold_path, self.target_full_path)\\n            backoff_rename(self.hold_path, self.target_full_path, force=True)\\n\\n    def cleanup(self):\\n        rm_rf(self.hold_path)\\n\\n    @property\\n    def target_full_path(self):\\n        return join(self.target_pkgs_dir, self.target_package_basename)\\n\\n    def __str__(self):\\n        return f\\\"CacheUrlAction<url={self.url!r}, target_full_path={self.target_full_path!r}>\\\"\\n\\n\\nclass ExtractPackageAction(PathAction):\\n    def __init__(\\n        self,\\n        source_full_path,\\n        target_pkgs_dir,\\n        target_extracted_dirname,\\n        record_or_spec,\\n        sha256,\\n        size,\\n        md5,\\n    ):\\n        self.source_full_path = source_full_path\\n        self.target_pkgs_dir = target_pkgs_dir\\n        self.target_extracted_dirname = target_extracted_dirname\\n        self.hold_path = self.target_full_path + CONDA_TEMP_EXTENSION\\n        self.record_or_spec = record_or_spec\\n        self.sha256 = sha256\\n        self.size = size\\n        self.md5 = md5\\n\\n    def verify(self):\\n        self._verified = True\\n\\n    def execute(self, progress_update_callback=None):\\n        # I hate inline imports, but I guess it's ok since we're importing from the conda.core\\n        # The alternative is passing the the classes to ExtractPackageAction __init__\\n        from .package_cache_data import PackageCacheData\\n\\n        log.log(\\n            TRACE, \\\"extracting %s => %s\\\", self.source_full_path, self.target_full_path\\n        )\\n\\n        if lexists(self.target_full_path):\\n            rm_rf(self.target_full_path)\\n\\n        extract_tarball(\\n            self.source_full_path,\\n            self.target_full_path,\\n            progress_update_callback=progress_update_callback,\\n        )\\n\\n        try:\\n            raw_index_json = read_index_json(self.target_full_path)\\n        except (OSError, JSONDecodeError, FileNotFoundError):\\n            # At this point, we can assume the package tarball is bad.\\n            # Remove everything and move on.\\n            print(\\n                f\\\"ERROR: Encountered corrupt package tarball at {self.source_full_path}. Conda has \\\"\\n                \\\"left it in place. Please report this to the maintainers \\\"\\n                \\\"of the package.\\\"\\n            )\\n            sys.exit(1)\\n\\n        if isinstance(self.record_or_spec, MatchSpec):\\n            url = self.record_or_spec.get_raw_value(\\\"url\\\")\\n            assert url\\n            channel = (\\n                Channel(url)\\n                if has_platform(url, context.known_subdirs)\\n                else Channel(None)\\n            )\\n            fn = basename(url)\\n            sha256 = self.sha256 or compute_sum(self.source_full_path, \\\"sha256\\\")\\n            size = getsize(self.source_full_path)\\n            if self.size is not None:\\n                assert size == self.size, (size, self.size)\\n            md5 = self.md5 or compute_sum(self.source_full_path, \\\"md5\\\")\\n            repodata_record = PackageRecord.from_objects(\\n                raw_index_json,\\n                url=url,\\n                channel=channel,\\n                fn=fn,\\n                sha256=sha256,\\n                size=size,\\n                md5=md5,\\n            )\\n        else:\\n            repodata_record = PackageRecord.from_objects(\\n                self.record_or_spec, raw_index_json\\n            )\\n\\n        repodata_record_path = join(\\n            self.target_full_path, \\\"info\\\", \\\"repodata_record.json\\\"\\n        )\\n        write_as_json_to_file(repodata_record_path, repodata_record)\\n\\n        target_package_cache = PackageCacheData(self.target_pkgs_dir)\\n        package_cache_record = PackageCacheRecord.from_objects(\\n            repodata_record,\\n            package_tarball_full_path=self.source_full_path,\\n            extracted_package_dir=self.target_full_path,\\n        )\\n        target_package_cache.insert(package_cache_record)\\n\\n    def reverse(self):\\n        rm_rf(self.target_full_path)\\n        if lexists(self.hold_path):\\n            log.log(TRACE, \\\"moving %s => %s\\\", self.hold_path, self.target_full_path)\\n            rm_rf(self.target_full_path)\\n            backoff_rename(self.hold_path, self.target_full_path)\\n\\n    def cleanup(self):\\n        rm_rf(self.hold_path)\\n\\n    @property\\n    def target_full_path(self):\\n        return join(self.target_pkgs_dir, self.target_extracted_dirname)\\n\\n    def __str__(self):\\n        return f\\\"ExtractPackageAction<source_full_path={self.source_full_path!r}, target_full_path={self.target_full_path!r}>\\\"\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Backport of conda.core.package_cache_data for conda-build.\\\"\\\"\\\"\\n\\nfrom ..deprecations import deprecated\\nfrom .package_cache_data import ProgressiveFetchExtract\\n\\ndeprecated.module(\\n    \\\"24.3\\\", \\\"24.9\\\", addendum=\\\"Use `conda.core.package_cache_data` instead.\\\"\\n)\\n\\nProgressiveFetchExtract = ProgressiveFetchExtract\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"\\nCode in ``conda.core`` is the core logic.  It is strictly forbidden from having side effects.\\nNo printing to stdout or stderr, no disk manipulation, no http requests.\\nAll side effects should be implemented through ``conda.gateways``.  Objects defined in\\n``conda.models`` should be heavily preferred for ``conda.core`` function/method arguments\\nand return values.\\n\\nConda modules importable from ``conda.core`` are\\n\\n- ``conda._vendor``\\n- ``conda.common``\\n- ``conda.core``\\n- ``conda.models``\\n- ``conda.gateways``\\n\\nConda modules strictly off limits for import within ``conda.core`` are\\n\\n- ``conda.api``\\n- ``conda.cli``\\n- ``conda.client``\\n\\n\\\"\\\"\\\"\\n\\n\\n# Copyright (C) 2012 Anaconda, Inc\\n# SPDX-License-Identifier: BSD-3-Clause\\n\\\"\\\"\\\"Tools for fetching the current index.\\\"\\\"\\\"\\n\\nfrom __future__ import annotations\\n\\nfrom itertools import chain\\nfrom logging import getLogger\\nfrom typing import TYPE_CHECKING\\n\\nfrom boltons.setutils import IndexedSet\\n\\nfrom ..base.context import context\\nfrom ..common.io import ThreadLimitedThreadPoolExecutor, time_recorder\\nfrom ..exceptions import ChannelNotAllowed, InvalidSpec\\nfrom ..gateways.logging import initialize_logging\\nfrom ..models.channel import Channel, all_channel_urls\\nfrom ..models.enums import PackageType\\nfrom ..models.match_spec import MatchSpec\\nfrom ..models.records import EMPTY_LINK, PackageCacheRecord, PackageRecord, PrefixRecord\\nfrom .package_cache_data import PackageCacheData\\nfrom .prefix_data import PrefixData\\nfrom .subdir_data import SubdirData, make_feature_record\\n\\nif TYPE_CHECKING:\\n    from typing import Any\\n\\n\\nlog = getLogger(__name__)\\n\\n\\ndef check_allowlist(channel_urls: list[str]) -> None:\\n    \\\"\\\"\\\"\\n    Check if the given channel URLs are allowed by the context's allowlist.\\n\\n    :param channel_urls: A list of channel URLs to check against the allowlist.\\n    :raises ChannelNotAllowed: If any URL is not in the allowlist.\\n    \\\"\\\"\\\"\\n    if context.allowlist_channels:\\n        allowlist_channel_urls = tuple(\\n            chain.from_iterable(\\n                Channel(c).base_urls for c in context.allowlist_channels\\n            )\\n        )\\n        for url in channel_urls:\\n            these_urls = Channel(url).base_urls\\n            if not all(this_url in allowlist_channel_urls for this_url in these_urls):\\n                raise ChannelNotAllowed(Channel(url))\\n\\n\\nLAST_CHANNEL_URLS = []\\n\\n\\n@time_recorder(\\\"get_index\\\")\\ndef get_index(\\n    channel_urls: tuple[str] = (),\\n    prepend: bool = True,\\n    platform: str | None = None,\\n    use_local: bool = False,\\n    use_cache: bool = False,\\n    unknown: bool | None = None,\\n    prefix: str | None = None,\\n    repodata_fn: str = context.repodata_fns[-1],\\n) -> dict:\\n    \\\"\\\"\\\"\\n    Return the index of packages available on the channels\\n\\n    If prepend=False, only the channels passed in as arguments are used.\\n    If platform=None, then the current platform is used.\\n    If prefix is supplied, then the packages installed in that prefix are added.\\n\\n    :param channel_urls: Channels to include in the index.\\n    :param prepend: If False, only the channels passed in are used.\\n    :param platform: Target platform for the index.\\n    :param use_local: Whether to use local channels.\\n    :param use_cache: Whether to use cached index information.\\n    :param unknown: Include unknown packages.\\n    :param prefix: Path to environment prefix to include in the index.\\n    :param repodata_fn: Filename of the repodata file.\\n    :return: A dictionary representing the package index.\\n    \\\"\\\"\\\"\\n    initialize_logging()  # needed in case this function is called directly as a public API\\n\\n    if context.offline and unknown is None:\\n        unknown = True\\n\\n    channel_urls = calculate_channel_urls(channel_urls, prepend, platform, use_local)\\n    LAST_CHANNEL_URLS.clear()\\n    LAST_CHANNEL_URLS.extend(channel_urls)\\n\\n    check_allowlist(channel_urls)\\n\\n    index = fetch_index(channel_urls, use_cache=use_cache, repodata_fn=repodata_fn)\\n\\n    if prefix:\\n        _supplement_index_with_prefix(index, prefix)\\n    if unknown:\\n        _supplement_index_with_cache(index)\\n    if context.track_features:\\n        _supplement_index_with_features(index)\\n    return index\\n\\n\\ndef fetch_index(\\n    channel_urls: list[str],\\n    use_cache: bool = False,\\n    index: dict | None = None,\\n    repodata_fn: str = context.repodata_fns[-1],\\n) -> dict:\\n    \\\"\\\"\\\"\\n    Fetch the package index from the specified channels.\\n\\n    :param channel_urls: A list of channel URLs to fetch the index from.\\n    :param use_cache: Whether to use the cached index data.\\n    :param index: An optional pre-existing index to update.\\n    :param repodata_fn: The name of the repodata file.\\n    :return: A dictionary representing the fetched or updated package index.\\n    \\\"\\\"\\\"\\n    log.debug(\\\"channel_urls=\\\" + repr(channel_urls))\\n    index = {}\\n    with ThreadLimitedThreadPoolExecutor() as executor:\\n        subdir_instantiator = lambda url: SubdirData(\\n            Channel(url), repodata_fn=repodata_fn\\n        )\\n        for f in executor.map(subdir_instantiator, channel_urls):\\n            index.update((rec, rec) for rec in f.iter_records())\\n    return index\\n\\n\\ndef dist_str_in_index(index: dict[Any, Any], dist_str: str) -> bool:\\n    \\\"\\\"\\\"\\n    Check if a distribution string matches any package in the index.\\n\\n    :param index: The package index.\\n    :param dist_str: The distribution string to match against the index.\\n    :return: True if there is a match; False otherwise.\\n    \\\"\\\"\\\"\\n    match_spec = MatchSpec.from_dist_str(dist_str)\\n    return any(match_spec.match(prec) for prec in index.values())\\n\\n\\ndef _supplement_index_with_prefix(index: dict[Any, Any], prefix: str) -> None:\\n    \\\"\\\"\\\"\\n    Supplement the given index with information from the specified environment prefix.\\n\\n    :param index: The package index to supplement.\\n    :param prefix: The path to the environment prefix.\\n    \\\"\\\"\\\"\\n    # supplement index with information from prefix/conda-meta\\n    assert prefix\\n    for prefix_record in PrefixData(prefix).iter_records():\\n        if prefix_record in index:\\n            current_record = index[prefix_record]\\n            if current_record.channel == prefix_record.channel:\\n                # The downloaded repodata takes priority, so we do not overwrite.\\n                # We do, however, copy the link information so that the solver (i.e. resolve)\\n                # knows this package is installed.\\n                link = prefix_record.get(\\\"link\\\") or EMPTY_LINK\\n                index[prefix_record] = PrefixRecord.from_objects(\\n                    current_record, prefix_record, link=link\\n                )\\n            else:\\n                # If the local packages channel information does not agree with\\n                # the channel information in the index then they are most\\n                # likely referring to different packages.  This can occur if a\\n                # multi-channel changes configuration, e.g. defaults with and\\n                # without the free channel. In this case we need to fake the\\n                # channel data for the existing package.\\n                prefix_channel = prefix_record.channel\\n                prefix_channel._Channel__canonical_name = prefix_channel.url()\\n                del prefix_record._PackageRecord__pkey\\n                index[prefix_record] = prefix_record\\n        else:\\n            # If the package is not in the repodata, use the local data.\\n            # If the channel is known but the package is not in the index, it\\n            # is because 1) the channel is unavailable offline, or 2) it no\\n            # longer contains this package. Either way, we should prefer any\\n            # other version of the package to this one. On the other hand, if\\n            # it is in a channel we don't know about, assign it a value just\\n            # above the priority of all known channels.\\n            index[prefix_record] = prefix_record\\n\\n\\ndef _supplement_index_with_cache(index: dict[Any, Any]) -> None:\\n    \\\"\\\"\\\"\\n    Supplement the given index with packages from the cache.\\n\\n    :param index: The package index to supplement.\\n    \\\"\\\"\\\"\\n    # supplement index with packages from the cache\\n    for pcrec in PackageCacheData.get_all_extracted_entries():\\n        if pcrec in index:\\n            # The downloaded repodata takes priority\\n            current_record = index[pcrec]\\n            index[pcrec] = PackageCacheRecord.from_objects(current_record, pcrec)\\n        else:\\n            index[pcrec] = pcrec\\n\\n\\ndef _make_virtual_package(\\n    name: str, version: str | None = None, build_string: str | None = None\\n) -> PackageRecord:\\n    \\\"\\\"\\\"\\n    Create a virtual package record.\\n\\n    :param name: The name of the virtual package.\\n    :param version: The version of the virtual package, defaults to \\\"0\\\".\\n    :param build_string: The build string of the virtual package, defaults to \\\"0\\\".\\n    :return: A PackageRecord representing the virtual package.\\n    \\\"\\\"\\\"\\n    return PackageRecord(\\n        package_type=PackageType.VIRTUAL_SYSTEM,\\n        name=name,\\n        version=version or \\\"0\\\",\\n        build_string=build_string or \\\"0\\\",\\n        channel=\\\"@\\\",\\n        subdir=context.subdir,\\n        md5=\\\"12345678901234567890123456789012\\\",\\n        build_number=0,\\n        fn=name,\\n    )\\n\\n\\ndef _supplement_index_with_features(\\n    index: dict[PackageRecord, PackageRecord], features: list[str] = []\\n) -> None:\\n    \\\"\\\"\\\"\\n    Supplement the given index with virtual feature records.\\n\\n    :param index: The package index to supplement.\\n    :param features: A list of feature names to add to the index.\\n    \\\"\\\"\\\"\\n    for feature in chain(context.track_features, features):\\n        rec = make_feature_record(feature)\\n        index[rec] = rec\\n\\n\\ndef _supplement_index_with_system(index: dict[PackageRecord, PackageRecord]) -> None:\\n    \\\"\\\"\\\"\\n    Loads and populates virtual package records from conda plugins\\n    and adds them to the provided index, unless there is a naming\\n    conflict.\\n\\n    :param index: The package index to supplement.\\n    \\\"\\\"\\\"\\n    for package in context.plugin_manager.get_virtual_packages():\\n        rec = _make_virtual_package(f\\\"__{package.name}\\\", package.version, package.build)\\n        index[rec] = rec\\n\\n\\ndef get_archspec_name() -> str | None:\\n    \\\"\\\"\\\"\\n    Determine the architecture specification name for the current environment.\\n\\n    :return: The architecture name if available, otherwise None.\\n    \\\"\\\"\\\"\\n    from ..base.context import _arch_names, non_x86_machines\\n\\n    target_plat, target_arch = context.subdir.split(\\\"-\\\")\\n    # This has to reverse what Context.subdir is doing\\n    if target_arch in non_x86_machines:\\n        machine = target_arch\\n    elif target_arch == \\\"zos\\\":\\n        return None\\n    elif target_arch.isdigit():\\n        machine = _arch_names[int(target_arch)]\\n    else:\\n        return None\\n\\n    native_subdir = context._native_subdir()\\n\\n    if native_subdir != context.subdir:\\n        return machine\\n    else:\\n        import archspec.cpu\\n\\n        return str(archspec.cpu.host())\\n\\n\\ndef calculate_channel_urls(\\n    channel_urls: tuple[str] = (),\\n    prepend: bool = True,\\n    platform: str | None = None,\\n    use_local: bool = False,\\n) -> list[str]:\\n    \\\"\\\"\\\"\\n    Calculate the full list of channel URLs to use based on the given parameters.\\n\\n    :param channel_urls: Initial list of channel URLs.\\n    :param prepend: Whether to prepend default channels to the list.\\n    :param platform: The target platform for the channels.\\n    :param use_local: Whether to include the local channel.\\n    :return: The calculated list of channel URLs.\\n    \\\"\\\"\\\"\\n    if use_local:\\n        channel_urls = [\\\"local\\\"] + list(channel_urls)\\n    if prepend:\\n        channel_urls += context.channels\\n\\n    subdirs = (platform, \\\"noarch\\\") if platform is not None else context.subdirs\\n    return all_channel_urls(channel_urls, subdirs=subdirs)\\n\\n\\ndef get_reduced_index(\\n    prefix: str | None,\\n    channels: list[str],\\n    subdirs: list[str],\\n    specs: list[MatchSpec],\\n    repodata_fn: str,\\n) -> dict:\\n    \\\"\\\"\\\"\\n    Generate a reduced package index based on the given specifications.\\n\\n    This function is useful for optimizing the solver by reducing the amount\\n    of data it needs to consider.\\n\\n    :param prefix: Path to an environment prefix to include installed packages.\\n    :param channels: A list of channel names to include in the index.\\n    :param subdirs: A list of subdirectories to consider for each channel.\\n    :param specs: A list of MatchSpec objects to filter the packages.\\n    :param repodata_fn: Filename of the repodata file to use.\\n    :return: A dictionary representing the reduced package index.\\n    \\\"\\\"\\\"\\n    records = IndexedSet()\\n    collected_names = set()\\n    collected_track_features = set()\\n    pending_names = set()\\n    pending_track_features = set()\\n\\n    def push_spec(spec: MatchSpec) -> None:\\n        \\\"\\\"\\\"\\n        Add a package name or track feature from a MatchSpec to the pending set.\\n\\n        :param spec: The MatchSpec to process.\\n        \\\"\\\"\\\"\\n        name = spec.get_raw_value(\\\"name\\\")\\n        if name and name not in collected_names:\\n            pending_names.add(name)\\n        track_features = spec.get_raw_value(\\\"track_features\\\")\\n        if track_features:\\n            for ftr_name in track_features:\\n                if ftr_name not in collected_track_features:\\n                    pending_track_features.add(ftr_name)\\n\\n    def push_record(record: PackageRecord) -> None:\\n        \\\"\\\"\\\"\\n        Process a package record to collect its dependencies and features.\\n\\n        :param record: The package record to process.\\n        \\\"\\\"\\\"\\n        try:\\n            combined_depends = record.combined_depends\\n        except InvalidSpec as e:\\n            log.warning(\\n                \\\"Skipping %s due to InvalidSpec: %s\\\",\\n                record.record_id(),\\n                e._kwargs[\\\"invalid_spec\\\"],\\n            )\\n            return\\n        push_spec(MatchSpec(record.name))\\n        for _spec in combined_depends:\\n            push_spec(_spec)\\n        if record.track_features:\\n            for ftr_name in record.track_features:\\n                push_spec(MatchSpec(track_features=ftr_name))\\n\\n    if prefix:\\n        for prefix_rec in PrefixData(prefix).iter_records():\\n            push_record(prefix_rec)\\n    for spec in specs:\\n        push_spec(spec)\\n\\n    while pending_names or pending_track_features:\\n        while pending_names:\\n            name = pending_names.pop()\\n            collected_names.add(name)\\n            spec = MatchSpec(name)\\n            new_records = SubdirData.query_all(\\n                spec, channels=channels, subdirs=subdirs, repodata_fn=repodata_fn\\n            )\\n            for record in new_records:\\n                push_record(record)\\n            records.update(new_records)\\n\\n        while pending_track_features:\\n            feature_name = pending_track_features.pop()\\n            collected_track_features.add(feature_name)\\n            spec = MatchSpec(track_features=feature_name)\\n            new_records = SubdirData.query_all(\\n                spec, channels=channels, subdirs=subdirs, repodata_fn=repodata_fn\\n            )\\n            for record in new_records:\\n                push_record(record)\\n            records.update(new_records)\\n\\n    reduced_index = {rec: rec for rec in records}\\n\\n    if prefix is not None:\\n        _supplement_index_with_prefix(reduced_index, prefix)\\n\\n    if context.offline or (\\n        \\\"unknown\\\" in context._argparse_args and context._argparse_args.unknown\\n    ):\\n        # This is really messed up right now.  Dates all the way back to\\n        # https://github.com/conda/conda/commit/f761f65a82b739562a0d997a2570e2b8a0bdc783\\n        # TODO: revisit this later\\n        _supplement_index_with_cache(reduced_index)\\n\\n    # add feature records for the solver\\n    known_features = set()\\n    for rec in reduced_index.values():\\n        known_features.update((*rec.track_features, *rec.features))\\n    known_features.update(context.track_features)\\n    for ftr_str in known_features:\\n        rec = make_feature_record(ftr_str)\\n        reduced_index[rec] = rec\\n\\n    _supplement_index_with_system(reduced_index)\\n\\n    return reduced_index\\n\\n\\n\\\"\\\"\\\"Collection of functions to coerce conversion of types with an intelligent guess.\\\"\\\"\\\"\\nfrom collections.abc import Mapping\\nfrom itertools import chain\\nfrom re import IGNORECASE, compile\\n\\nfrom enum import Enum\\n\\nfrom ..deprecations import deprecated\\nfrom .compat import isiterable\\nfrom .decorators import memoizedproperty\\nfrom .exceptions import AuxlibError\\n\\n__all__ = [\\\"boolify\\\", \\\"typify\\\", \\\"maybecall\\\", \\\"listify\\\", \\\"numberify\\\"]\\n\\nBOOLISH_TRUE = (\\\"true\\\", \\\"yes\\\", \\\"on\\\", \\\"y\\\")\\nBOOLISH_FALSE = (\\\"false\\\", \\\"off\\\", \\\"n\\\", \\\"no\\\", \\\"non\\\", \\\"none\\\", \\\"\\\")\\nNULL_STRINGS = (\\\"none\\\", \\\"~\\\", \\\"null\\\", \\\"\\\\0\\\")\\nBOOL_COERCEABLE_TYPES = (int, bool, float, complex, list, set, dict, tuple)\\nNUMBER_TYPES = (int, float, complex)\\nNUMBER_TYPES_SET = {*NUMBER_TYPES}\\nSTRING_TYPES_SET = {str}\\n\\nNO_MATCH = object()\\n\\n\\nclass TypeCoercionError(AuxlibError, ValueError):\\n\\n    def __init__(self, value, msg, *args, **kwargs):\\n        self.value = value\\n        super().__init__(msg, *args, **kwargs)\\n\\n\\nclass _Regex:\\n\\n    @memoizedproperty\\n    def BOOLEAN_TRUE(self):\\n        return compile(r'^true$|^yes$|^on$', IGNORECASE), True\\n\\n    @memoizedproperty\\n    def BOOLEAN_FALSE(self):\\n        return compile(r'^false$|^no$|^off$', IGNORECASE), False\\n\\n    @memoizedproperty\\n    def NONE(self):\\n        return compile(r'^none$|^null$', IGNORECASE), None\\n\\n    @memoizedproperty\\n    def INT(self):\\n        return compile(r'^[-+]?\\\\d+$'), int\\n\\n    @memoizedproperty\\n    def BIN(self):\\n        return compile(r'^[-+]?0[bB][01]+$'), bin\\n\\n    @memoizedproperty\\n    def OCT(self):\\n        return compile(r'^[-+]?0[oO][0-7]+$'), oct\\n\\n    @memoizedproperty\\n    def HEX(self):\\n        return compile(r'^[-+]?0[xX][0-9a-fA-F]+$'), hex\\n\\n    @memoizedproperty\\n    def FLOAT(self):\\n        return compile(r'^[-+]?(\\\\d+(\\\\.\\\\d*)?|\\\\.\\\\d+)([eE][-+]?\\\\d+)?$'), float\\n\\n    @memoizedproperty\\n    def COMPLEX(self):\\n        return (compile(r'^(?:[-+]?(\\\\d+(\\\\.\\\\d*)?|\\\\.\\\\d+)([eE][-+]?\\\\d+)?)?'  # maybe first float\\n                        r'[-+]?(\\\\d+(\\\\.\\\\d*)?|\\\\.\\\\d+)([eE][-+]?\\\\d+)?j$'),     # second float with j\\n                complex)\\n\\n    @property\\n    def numbers(self):\\n        yield self.INT\\n        yield self.FLOAT\\n        yield self.BIN\\n        yield self.OCT\\n        yield self.HEX\\n        yield self.COMPLEX\\n\\n    @property\\n    def boolean(self):\\n        yield self.BOOLEAN_TRUE\\n        yield self.BOOLEAN_FALSE\\n\\n    @property\\n    def none(self):\\n        yield self.NONE\\n\\n    def convert_number(self, value_string):\\n        return self._convert(value_string, (self.numbers, ))\\n\\n    def convert(self, value_string):\\n        return self._convert(value_string, (self.boolean, self.none, self.numbers, ))\\n\\n    def _convert(self, value_string, type_list):\\n        return next((typish(value_string) if callable(typish) else typish\\n                     for regex, typish in chain.from_iterable(type_list)\\n                     if regex.match(value_string)),\\n                    NO_MATCH)\\n\\n\\n_REGEX = _Regex()\\n\\n\\ndef numberify(value):\\n    \\\"\\\"\\\"\\n\\n    Examples:\\n        >>> [numberify(x) for x in ('1234', 1234, '0755', 0o0755, False, 0, '0', True, 1, '1')]\\n          [1234, 1234, 755, 493, 0, 0, 0, 1, 1, 1]\\n        >>> [numberify(x) for x in ('12.34', 12.34, 1.2+3.5j, '1.2+3.5j')]\\n        [12.34, 12.34, (1.2+3.5j), (1.2+3.5j)]\\n\\n    \\\"\\\"\\\"\\n    if isinstance(value, bool):\\n        return int(value)\\n    if isinstance(value, NUMBER_TYPES):\\n        return value\\n    candidate = _REGEX.convert_number(value)\\n    if candidate is not NO_MATCH:\\n        return candidate\\n    raise TypeCoercionError(value, f\\\"Cannot convert {value} to a number.\\\")\\n\\n\\ndef boolify(value, nullable=False, return_string=False):\\n    \\\"\\\"\\\"Convert a number, string, or sequence type into a pure boolean.\\n\\n    Args:\\n        value (number, string, sequence): pretty much anything\\n\\n    Returns:\\n        bool: boolean representation of the given value\\n\\n    Examples:\\n        >>> [boolify(x) for x in ('yes', 'no')]\\n        [True, False]\\n        >>> [boolify(x) for x in (0.1, 0+0j, True, '0', '0.0', '0.1', '2')]\\n        [True, False, True, False, False, True, True]\\n        >>> [boolify(x) for x in (\\\"true\\\", \\\"yes\\\", \\\"on\\\", \\\"y\\\")]\\n        [True, True, True, True]\\n        >>> [boolify(x) for x in (\\\"no\\\", \\\"non\\\", \\\"none\\\", \\\"off\\\", \\\"\\\")]\\n        [False, False, False, False, False]\\n        >>> [boolify(x) for x in ([], set(), dict(), tuple())]\\n        [False, False, False, False]\\n        >>> [boolify(x) for x in ([1], set([False]), dict({'a': 1}), tuple([2]))]\\n        [True, True, True, True]\\n\\n    \\\"\\\"\\\"\\n    # cast number types naturally\\n    if isinstance(value, BOOL_COERCEABLE_TYPES):\\n        return bool(value)\\n    # try to coerce string into number\\n    val = str(value).strip().lower().replace(\\\".\\\", \\\"\\\", 1)\\n    if val.isnumeric():\\n        return bool(float(val))\\n    elif val in BOOLISH_TRUE:\\n        return True\\n    elif nullable and val in NULL_STRINGS:\\n        return None\\n    elif val in BOOLISH_FALSE:\\n        return False\\n    else:  # must be False\\n        try:\\n            return bool(complex(val))\\n        except ValueError:\\n            if isinstance(value, str) and return_string:\\n                return value\\n            raise TypeCoercionError(value, \\\"The value %r cannot be boolified.\\\" % value)\\n\\n\\n@deprecated(\\\"24.3\\\", \\\"24.9\\\")\\ndef boolify_truthy_string_ok(value):\\n    try:\\n        return boolify(value)\\n    except ValueError:\\n        assert isinstance(value, str), repr(value)\\n        return True\\n\\n\\ndef typify_str_no_hint(value):\\n    candidate = _REGEX.convert(value)\\n    return candidate if candidate is not NO_MATCH else value\\n\\n\\ndef typify(value, type_hint=None):\\n    \\\"\\\"\\\"Take a primitive value, usually a string, and try to make a more relevant type out of it.\\n    An optional type_hint will try to coerce the value to that type.\\n\\n    Args:\\n        value (Any): Usually a string, not a sequence\\n        type_hint (type or tuple[type]):\\n\\n    Examples:\\n        >>> typify('32')\\n        32\\n        >>> typify('32', float)\\n        32.0\\n        >>> typify('32.0')\\n        32.0\\n        >>> typify('32.0.0')\\n        '32.0.0'\\n        >>> [typify(x) for x in ('true', 'yes', 'on')]\\n        [True, True, True]\\n        >>> [typify(x) for x in ('no', 'FALSe', 'off')]\\n        [False, False, False]\\n        >>> [typify(x) for x in ('none', 'None', None)]\\n        [None, None, None]\\n\\n    \\\"\\\"\\\"\\n    # value must be a string, or there at least needs to be a type hint\\n    if isinstance(value, str):\\n        value = value.strip()\\n    elif type_hint is None:\\n        # can't do anything because value isn't a string and there's no type hint\\n        return value\\n\\n    # now we either have a stripped string, a type hint, or both\\n    # use the hint if it exists\\n    if isiterable(type_hint):\\n        if isinstance(type_hint, type) and issubclass(type_hint, Enum):\\n            try:\\n                return type_hint(value)\\n            except ValueError as e:\\n                try:\\n                    return type_hint[value]\\n                except KeyError:\\n                    raise TypeCoercionError(value, str(e))\\n        type_hint = set(type_hint)\\n        if not (type_hint - NUMBER_TYPES_SET):\\n            return numberify(value)\\n        elif not (type_hint - STRING_TYPES_SET):\\n            return str(value)\\n        elif not (type_hint - {bool, type(None)}):\\n            return boolify(value, nullable=True)\\n        elif not (type_hint - (STRING_TYPES_SET | {bool})):\\n            return boolify(value, return_string=True)\\n        elif not (type_hint - (STRING_TYPES_SET | {type(None)})):\\n            value = str(value)\\n            return None if value.lower() == 'none' else value\\n        elif not (type_hint - {bool, int}):\\n            return typify_str_no_hint(str(value))\\n        else:\\n            raise NotImplementedError()\\n    elif type_hint is not None:\\n        # coerce using the type hint, or use boolify for bool\\n        try:\\n            return boolify(value) if type_hint == bool else type_hint(value)\\n        except ValueError as e:\\n            # ValueError: invalid literal for int() with base 10: 'nope'\\n            raise TypeCoercionError(value, str(e))\\n    else:\\n        # no type hint, but we know value is a string, so try to match with the regex patterns\\n        #   if there's still no match, `typify_str_no_hint` will return `value`\\n        return typify_str_no_hint(value)\\n\\n\\ndef typify_data_structure(value, type_hint=None):\\n    if isinstance(value, Mapping):\\n        return type(value)((k, typify(v, type_hint)) for k, v in value.items())\\n    elif isiterable(value):\\n        return type(value)(typify(v, type_hint) for v in value)\\n    elif isinstance(value, str) and isinstance(type_hint, type) and issubclass(type_hint, str):\\n        # This block is necessary because if we fall through to typify(), we end up calling\\n        # .strip() on the str, when sometimes we want to preserve preceding and trailing\\n        # whitespace.\\n        return type_hint(value)\\n    else:\\n        return typify(value, type_hint)\\n\\n\\ndef maybecall(value):\\n    return value() if callable(value) else value\\n\\n\\n@deprecated(\\\"24.3\\\", \\\"24.9\\\")\\ndef listify(val, return_type=tuple):\\n    \\\"\\\"\\\"\\n    Examples:\\n        >>> listify('abc', return_type=list)\\n        ['abc']\\n        >>> listify(None)\\n        ()\\n        >>> listify(False)\\n        (False,)\\n        >>> listify(('a', 'b', 'c'), return_type=list)\\n        ['a', 'b', 'c']\\n    \\\"\\\"\\\"\\n    # TODO: flatlistify((1, 2, 3), 4, (5, 6, 7))\\n    if val is None:\\n        return return_type()\\n    elif isiterable(val):\\n        return return_type(val)\\n    else:\\n        return return_type((val, ))\\n\\n\\nfrom itertools import islice\\nfrom json import JSONEncoder, dumps, loads\\nfrom logging import getLogger, INFO, Formatter, StreamHandler, DEBUG\\nfrom sys import stderr\\n\\nfrom . import NullHandler\\n\\nlog = getLogger(__name__)\\nroot_log = getLogger()\\n\\nNullHandler = NullHandler\\n\\nDEBUG_FORMATTER = Formatter(\\n    \\\"[%(levelname)s] [%(asctime)s.%(msecs)03d] %(process)d %(name)s:%(funcName)s(%(lineno)d):\\\\n\\\"\\n    \\\"%(message)s\\\\n\\\",\\n    \\\"%Y-%m-%d %H:%M:%S\\\")\\n\\nINFO_FORMATTER = Formatter(\\n    \\\"[%(levelname)s] [%(asctime)s.%(msecs)03d] %(process)d %(name)s(%(lineno)d): %(message)s\\\\n\\\",\\n    \\\"%Y-%m-%d %H:%M:%S\\\")\\n\\n\\ndef set_root_level(level=INFO):\\n    root_log.setLevel(level)\\n\\n\\ndef attach_stderr(level=INFO):\\n    has_stderr_handler = any(handler.name == 'stderr' for handler in root_log.handlers)\\n    if not has_stderr_handler:\\n        handler = StreamHandler(stderr)\\n        handler.name = 'stderr'\\n        if level is not None:\\n            handler.setLevel(level)\\n        handler.setFormatter(DEBUG_FORMATTER if level == DEBUG else INFO_FORMATTER)\\n        root_log.addHandler(handler)\\n        return True\\n    else:\\n        return False\\n\\n\\ndef detach_stderr():\\n    for handler in root_log.handlers:\\n        if handler.name == 'stderr':\\n            root_log.removeHandler(handler)\\n            return True\\n    return False\\n\\n\\ndef initialize_logging(level=INFO):\\n    attach_stderr(level)\\n\\n\\nclass DumpEncoder(JSONEncoder):\\n    def default(self, obj):\\n        if hasattr(obj, 'dump'):\\n            return obj.dump()\\n        # Let the base class default method raise the TypeError\\n        return super().default(obj)\\n\\n\\n_DUMPS = DumpEncoder(indent=2, ensure_ascii=False, sort_keys=True).encode\\n\\n\\ndef jsondumps(obj):\\n    return _DUMPS(obj)\\n\\n\\ndef fullname(obj):\\n    try:\\n        return obj.__module__ + \\\".\\\" + obj.__class__.__name__\\n    except AttributeError:\\n        return obj.__class__.__name__\\n\\n\\nrequest_header_sort_dict = {\\n    'Host': '\\\\x00\\\\x00',\\n    'User-Agent': '\\\\x00\\\\x01',\\n}\\ndef request_header_sort_key(item):\\n    return request_header_sort_dict.get(item[0], item[0].lower())\\n\\n\\nresponse_header_sort_dict = {\\n    'Content-Length': '\\\\x7e\\\\x7e\\\\x61',\\n    'Connection': '\\\\x7e\\\\x7e\\\\x62',\\n}\\ndef response_header_sort_key(item):\\n    return response_header_sort_dict.get(item[0], item[0].lower())\\n\\n\\ndef stringify(obj, content_max_len=0):\\n    def bottle_builder(builder, bottle_object):\\n        builder.append(\\n            \\\"{} {}{} {}\\\".format(\\n                bottle_object.method,\\n                bottle_object.path,\\n                bottle_object.environ.get(\\\"QUERY_STRING\\\", \\\"\\\"),\\n                bottle_object.get(\\\"SERVER_PROTOCOL\\\"),\\n            )\\n        )\\n        builder += [f\\\"{key}: {value}\\\" for key, value in bottle_object.headers.items()]\\n        builder.append('')\\n        body = bottle_object.body.read().strip()\\n        if body:\\n            builder.append(body)\\n\\n    def requests_models_PreparedRequest_builder(builder, request_object):\\n        builder.append(\\n            \\\">>{} {} {}\\\".format(\\n                request_object.method,\\n                request_object.path_url,\\n                request_object.url.split(\\\":\\\", 1)[0].upper(),\\n            )\\n        )\\n        builder.extend(\\n            f\\\"> {key}: {value}\\\"\\n            for key, value in sorted(request_object.headers.items(), key=request_header_sort_key)\\n        )\\n        builder.append(\\\"\\\")\\n        if request_object.body:\\n            builder.append(request_object.body)\\n\\n    def requests_models_Response_builder(builder, response_object):\\n        builder.append(\\n            \\\"<<{} {} {}\\\".format(\\n                response_object.url.split(\\\":\\\", 1)[0].upper(),\\n                response_object.status_code,\\n                response_object.reason,\\n            )\\n        )\\n        builder.extend(\\n            f\\\"< {key}: {value}\\\"\\n            for key, value in sorted(response_object.headers.items(), key=response_header_sort_key)\\n        )\\n        elapsed = str(response_object.elapsed).split(\\\":\\\", 1)[-1]\\n        builder.append(f\\\"< Elapsed: {elapsed}\\\")\\n        if content_max_len:\\n            builder.append('')\\n            content_type = response_object.headers.get('Content-Type')\\n            if content_type == 'application/json':\\n                text = response_object.text\\n                if len(text) > content_max_len:\\n                    content = text\\n                else:\\n                    resp = loads(text)\\n                    resp = dict(islice(resp.items(), content_max_len))\\n                    content = dumps(resp, indent=2)\\n                content = content[:content_max_len] if len(content) > content_max_len else content\\n                builder.append(content)\\n                builder.append('')\\n            elif content_type is not None and (content_type.startswith('text/')\\n                                               or content_type == 'application/xml'):\\n                text = response_object.text\\n                content = text[:content_max_len] if len(text) > content_max_len else text\\n                builder.append(content)\\n\\n    try:\\n        name = fullname(obj)\\n        builder = ['']  # start with new line\\n        if name.startswith('bottle.'):\\n            bottle_builder(builder, obj)\\n        elif name.endswith('requests.models.PreparedRequest'):\\n            requests_models_PreparedRequest_builder(builder, obj)\\n        elif name.endswith('requests.models.Response'):\\n            if getattr(obj, 'request'):\\n                requests_models_PreparedRequest_builder(builder, obj.request)\\n            else:\\n                log.info(\\\"request is 'None' for Response object with url %s\\\", obj.url)\\n            requests_models_Response_builder(builder, obj)\\n        else:\\n            return None\\n        builder.append('')  # end with new line\\n        return \\\"\\\\n\\\".join(builder)\\n    except Exception as e:\\n        log.exception(e)\\n\\n\\n\\\"\\\"\\\"Common collection classes.\\\"\\\"\\\"\\nfrom functools import reduce\\nfrom collections.abc import Mapping, Set\\n\\nfrom .compat import isiterable\\nfrom ..deprecations import deprecated\\n\\ntry:\\n    from frozendict import frozendict\\nexcept ImportError:\\n    from .._vendor.frozendict import frozendict\\n\\n\\n@deprecated(\\\"24.9\\\", \\\"25.3\\\", addendum=\\\"Use `frozendict.deepfreeze` instead.\\\")\\ndef make_immutable(value):\\n    # this function is recursive, and if nested data structures fold back on themselves,\\n    #   there will likely be recursion errors\\n    if isinstance(value, Mapping):\\n        if isinstance(value, frozendict):\\n            return value\\n        return frozendict((k, make_immutable(v)) for k, v in value.items())\\n    elif isinstance(value, Set):\\n        if isinstance(value, frozenset):\\n            return value\\n        return frozenset(make_immutable(v) for v in value)\\n    elif isiterable(value):\\n        if isinstance(value, tuple):\\n            return value\\n        return tuple(make_immutable(v) for v in value)\\n    else:\\n        return value\\n\\n\\n# http://stackoverflow.com/a/14620633/2127762\\nclass AttrDict(dict):\\n    \\\"\\\"\\\"Sub-classes dict, and further allows attribute-like access to dictionary items.\\n\\n    Examples:\\n        >>> d = AttrDict({'a': 1})\\n        >>> d.a, d['a'], d.get('a')\\n        (1, 1, 1)\\n        >>> d.b = 2\\n        >>> d.b, d['b']\\n        (2, 2)\\n    \\\"\\\"\\\"\\n    def __init__(self, *args, **kwargs):\\n        super().__init__(*args, **kwargs)\\n        self.__dict__ = self\\n\\n\\ndef first(seq, key=bool, default=None, apply=lambda x: x):\\n    \\\"\\\"\\\"Give the first value that satisfies the key test.\\n\\n    Args:\\n        seq (iterable):\\n        key (callable): test for each element of iterable\\n        default: returned when all elements fail test\\n        apply (callable): applied to element before return, but not to default value\\n\\n    Returns: first element in seq that passes key, mutated with optional apply\\n\\n    Examples:\\n        >>> first([0, False, None, [], (), 42])\\n        42\\n        >>> first([0, False, None, [], ()]) is None\\n        True\\n        >>> first([0, False, None, [], ()], default='ohai')\\n        'ohai'\\n        >>> import re\\n        >>> m = first(re.match(regex, 'abc') for regex in ['b.*', 'a(.*)'])\\n        >>> m.group(1)\\n        'bc'\\n\\n        The optional `key` argument specifies a one-argument predicate function\\n        like that used for `filter()`.  The `key` argument, if supplied, must be\\n        in keyword form.  For example:\\n        >>> first([1, 1, 3, 4, 5], key=lambda x: x % 2 == 0)\\n        4\\n\\n    \\\"\\\"\\\"\\n    return next((apply(x) for x in seq if key(x)), default() if callable(default) else default)\\n\\n\\n@deprecated(\\\"24.3\\\", \\\"24.9\\\")\\ndef firstitem(map, key=lambda k, v: bool(k), default=None, apply=lambda k, v: (k, v)):\\n    return next((apply(k, v) for k, v in map if key(k, v)), default)\\n\\n\\ndef last(seq, key=bool, default=None, apply=lambda x: x):\\n    return next((apply(x) for x in reversed(seq) if key(x)), default)\\n\\n\\n@deprecated(\\\"24.3\\\", \\\"24.9\\\")\\ndef call_each(seq):\\n    \\\"\\\"\\\"Calls each element of sequence to invoke the side effect.\\n\\n    Args:\\n        seq:\\n\\n    Returns: None\\n\\n    \\\"\\\"\\\"\\n    try:\\n        reduce(lambda _, y: y(), seq)\\n    except TypeError as e:\\n        if str(e) != \\\"reduce() of empty sequence with no initial value\\\":\\n            raise\\n\\n\\nfrom logging import getLogger\\nfrom textwrap import dedent\\n\\nlog = getLogger(__name__)\\n\\n\\ndef dals(string):\\n    \\\"\\\"\\\"dedent and left-strip\\\"\\\"\\\"\\n    return dedent(string).lstrip()\\n\\n\\ndef _get_attr(obj, attr_name, aliases=()):\\n    try:\\n        return getattr(obj, attr_name)\\n    except AttributeError:\\n        for alias in aliases:\\n            try:\\n                return getattr(obj, alias)\\n            except AttributeError:\\n                continue\\n        else:\\n            raise\\n\\n\\ndef find_or_none(key, search_maps, aliases=(), _map_index=0):\\n    \\\"\\\"\\\"Return the value of the first key found in the list of search_maps,\\n    otherwise return None.\\n\\n    Examples:\\n        >>> from .collection import AttrDict\\n        >>> d1 = AttrDict({'a': 1, 'b': 2, 'c': 3, 'e': None})\\n        >>> d2 = AttrDict({'b': 5, 'e': 6, 'f': 7})\\n        >>> find_or_none('c', (d1, d2))\\n        3\\n        >>> find_or_none('f', (d1, d2))\\n        7\\n        >>> find_or_none('b', (d1, d2))\\n        2\\n        >>> print(find_or_none('g', (d1, d2)))\\n        None\\n        >>> find_or_none('e', (d1, d2))\\n        6\\n\\n    \\\"\\\"\\\"\\n    try:\\n        attr = _get_attr(search_maps[_map_index], key, aliases)\\n        return attr if attr is not None else find_or_none(key, search_maps[1:], aliases)\\n    except AttributeError:\\n        # not found in current map object, so go to next\\n        return find_or_none(key, search_maps, aliases, _map_index+1)\\n    except IndexError:\\n        # ran out of map objects to search\\n        return None\\n\\n\\ndef find_or_raise(key, search_maps, aliases=(), _map_index=0):\\n    try:\\n        attr = _get_attr(search_maps[_map_index], key, aliases)\\n        return attr if attr is not None else find_or_raise(key, search_maps[1:], aliases)\\n    except AttributeError:\\n        # not found in current map object, so go to next\\n        return find_or_raise(key, search_maps, aliases, _map_index+1)\\n    except IndexError:\\n        # ran out of map objects to search\\n        raise AttributeError()\\n\\n\\nfrom logging import getLogger\\nfrom ..deprecations import deprecated\\n\\nlog = getLogger(__name__)\\n\\n\\ndef Raise(exception):  # NOQA\\n    raise exception\\n\\n\\nclass AuxlibError:\\n    \\\"\\\"\\\"Mixin to identify exceptions associated with the auxlib package.\\\"\\\"\\\"\\n\\n\\n@deprecated(\\\"24.3\\\", \\\"24.9\\\")\\nclass AuthenticationError(AuxlibError, ValueError):\\n    pass\\n\\n\\n@deprecated(\\\"24.3\\\", \\\"24.9\\\")\\nclass NotFoundError(AuxlibError, KeyError):\\n    pass\\n\\n\\n@deprecated(\\\"24.3\\\", \\\"24.9\\\")\\nclass InitializationError(AuxlibError, EnvironmentError):\\n    pass\\n\\n\\n@deprecated(\\\"24.3\\\", \\\"24.9\\\")\\nclass SenderError(AuxlibError, IOError):\\n    pass\\n\\n\\n@deprecated(\\\"24.3\\\", \\\"24.9\\\")\\nclass AssignmentError(AuxlibError, AttributeError):\\n    pass\\n\\n\\nclass ValidationError(AuxlibError, TypeError):\\n\\n    def __init__(self, key, value=None, valid_types=None, msg=None):\\n        self.__cause__ = None  # in python3 don't chain ValidationError exceptions\\n        if msg is not None:\\n            super().__init__(msg)\\n        elif value is None:\\n            super().__init__(f\\\"Value for {key} cannot be None.\\\")\\n        elif valid_types is None:\\n            super().__init__(f\\\"Invalid value {value} for {key}\\\")\\n        else:\\n            super().__init__(\\n                f\\\"{key} must be of type {valid_types}, not {value!r}\\\"\\n            )\\n\\n\\nclass ThisShouldNeverHappenError(AuxlibError, AttributeError):\\n    pass\\n\\n\\nfrom collections.abc import Hashable\\nfrom types import GeneratorType\\n\\nfrom functools import wraps\\n\\n\\n# TODO: spend time filling out functionality and make these more robust\\n\\n\\ndef memoizemethod(method):\\n    \\\"\\\"\\\"\\n    Decorator to cause a method to cache it's results in self for each\\n    combination of inputs and return the cached result on subsequent calls.\\n    Does not support named arguments or arg values that are not hashable.\\n\\n    >>> class Foo (object):\\n    ...   @memoizemethod\\n    ...   def foo(self, x, y=0):\\n    ...     print('running method with', x, y)\\n    ...     return x + y + 3\\n    ...\\n    >>> foo1 = Foo()\\n    >>> foo2 = Foo()\\n    >>> foo1.foo(10)\\n    running method with 10 0\\n    13\\n    >>> foo1.foo(10)\\n    13\\n    >>> foo2.foo(11, y=7)\\n    running method with 11 7\\n    21\\n    >>> foo2.foo(11)\\n    running method with 11 0\\n    14\\n    >>> foo2.foo(11, y=7)\\n    21\\n    >>> class Foo (object):\\n    ...   def __init__(self, lower):\\n    ...     self.lower = lower\\n    ...   @memoizemethod\\n    ...   def range_tuple(self, upper):\\n    ...     print('running function')\\n    ...     return tuple(i for i in range(self.lower, upper))\\n    ...   @memoizemethod\\n    ...   def range_iter(self, upper):\\n    ...     print('running function')\\n    ...     return (i for i in range(self.lower, upper))\\n    ...\\n    >>> foo = Foo(3)\\n    >>> foo.range_tuple(6)\\n    running function\\n    (3, 4, 5)\\n    >>> foo.range_tuple(7)\\n    running function\\n    (3, 4, 5, 6)\\n    >>> foo.range_tuple(6)\\n    (3, 4, 5)\\n    >>> foo.range_iter(6)\\n    Traceback (most recent call last):\\n    TypeError: Can't memoize a generator or non-hashable object!\\n    \\\"\\\"\\\"\\n\\n    @wraps(method)\\n    def _wrapper(self, *args, **kwargs):\\n        # NOTE:  a __dict__ check is performed here rather than using the\\n        # built-in hasattr function because hasattr will look up to an object's\\n        # class if the attr is not directly found in the object's dict.  That's\\n        # bad for this if the class itself has a memoized classmethod for\\n        # example that has been called before the memoized instance method,\\n        # then the instance method will use the class's result cache, causing\\n        # its results to be globally stored rather than on a per instance\\n        # basis.\\n        if '_memoized_results' not in self.__dict__:\\n            self._memoized_results = {}\\n        memoized_results = self._memoized_results\\n\\n        key = (method.__name__, args, tuple(sorted(kwargs.items())))\\n        if key in memoized_results:\\n            return memoized_results[key]\\n        else:\\n            try:\\n                result = method(self, *args, **kwargs)\\n            except KeyError as e:\\n                if '__wrapped__' in str(e):\\n                    result = None  # is this the right thing to do?  happened during py3 conversion\\n                else:\\n                    raise\\n            if isinstance(result, GeneratorType) or not isinstance(result, Hashable):\\n                raise TypeError(\\\"Can't memoize a generator or non-hashable object!\\\")\\n            return memoized_results.setdefault(key, result)\\n\\n    return _wrapper\\n\\n\\ndef clear_memoized_methods(obj, *method_names):\\n    \\\"\\\"\\\"\\n    Clear the memoized method or @memoizedproperty results for the given\\n    method names from the given object.\\n\\n    >>> v = [0]\\n    >>> def inc():\\n    ...     v[0] += 1\\n    ...     return v[0]\\n    ...\\n    >>> class Foo(object):\\n    ...    @memoizemethod\\n    ...    def foo(self):\\n    ...        return inc()\\n    ...    @memoizedproperty\\n    ...    def g(self):\\n    ...       return inc()\\n    ...\\n    >>> f = Foo()\\n    >>> f.foo(), f.foo()\\n    (1, 1)\\n    >>> clear_memoized_methods(f, 'foo')\\n    >>> (f.foo(), f.foo(), f.g, f.g)\\n    (2, 2, 3, 3)\\n    >>> (f.foo(), f.foo(), f.g, f.g)\\n    (2, 2, 3, 3)\\n    >>> clear_memoized_methods(f, 'g', 'no_problem_if_undefined')\\n    >>> f.g, f.foo(), f.g\\n    (4, 2, 4)\\n    >>> f.foo()\\n    2\\n    \\\"\\\"\\\"\\n    for key in list(getattr(obj, '_memoized_results', {}).keys()):\\n        # key[0] is the method name\\n        if key[0] in method_names:\\n            del obj._memoized_results[key]\\n\\n    property_dict = obj._cache_\\n    for prop in method_names:\\n        inner_attname = '__%s' % prop\\n        if inner_attname in property_dict:\\n            del property_dict[inner_attname]\\n\\n\\ndef memoizedproperty(func):\\n    \\\"\\\"\\\"\\n    Decorator to cause a method to cache it's results in self for each\\n    combination of inputs and return the cached result on subsequent calls.\\n    Does not support named arguments or arg values that are not hashable.\\n\\n    >>> class Foo (object):\\n    ...   _x = 1\\n    ...   @memoizedproperty\\n    ...   def foo(self):\\n    ...     self._x += 1\\n    ...     print('updating and returning {0}'.format(self._x))\\n    ...     return self._x\\n    ...\\n    >>> foo1 = Foo()\\n    >>> foo2 = Foo()\\n    >>> foo1.foo\\n    updating and returning 2\\n    2\\n    >>> foo1.foo\\n    2\\n    >>> foo2.foo\\n    updating and returning 2\\n    2\\n    >>> foo1.foo\\n    2\\n    \\\"\\\"\\\"\\n    inner_attname = '__%s' % func.__name__\\n\\n    def new_fget(self):\\n        if not hasattr(self, '_cache_'):\\n            self._cache_ = {}\\n        cache = self._cache_\\n        if inner_attname not in cache:\\n            cache[inner_attname] = func(self)\\n        return cache[inner_attname]\\n\\n    return property(new_fget)\\n\\n\\nclass classproperty:  # pylint: disable=C0103\\n    # from celery.five\\n\\n    def __init__(self, getter=None, setter=None):\\n        if getter is not None and not isinstance(getter, classmethod):\\n            getter = classmethod(getter)\\n        if setter is not None and not isinstance(setter, classmethod):\\n            setter = classmethod(setter)\\n        self.__get = getter\\n        self.__set = setter\\n\\n        info = getter.__get__(object)  # just need the info attrs.\\n        self.__doc__ = info.__doc__\\n        self.__name__ = info.__name__\\n        self.__module__ = info.__module__\\n\\n    def __get__(self, obj, type_=None):\\n        if obj and type_ is None:\\n            type_ = obj.__class__\\n        return self.__get.__get__(obj, type_)()\\n\\n    def __set__(self, obj, value):\\n        if obj is None:\\n            return self\\n        return self.__set.__get__(obj)(value)\\n\\n    def setter(self, setter):\\n        return self.__class__(self.__get, setter)\\n\\n# memoize & clear:\\n#     class method\\n#     function\\n#     classproperty\\n#     property\\n#     staticproperty?\\n# memoizefunction\\n# memoizemethod\\n# memoizedproperty\\n\\n\\n\\\"\\\"\\\"Auxlib is an auxiliary library to the python standard library.\\n\\nThe aim is to provide core generic features for app development in python. Auxlib fills in some\\npython stdlib gaps much like `pytoolz <https://github.com/pytoolz/>`_ has for functional\\nprogramming, `pyrsistent <https://github.com/tobgu/pyrsistent/>`_ has for data structures, or\\n`boltons <https://github.com/mahmoud/boltons/>`_ has generally.\\n\\nMajor areas addressed include:\\n  - :ref:`packaging`: package versioning, with a clean and less invasive alternative to\\n    versioneer\\n  - :ref:`entity`: robust base class for type-enforced data models and transfer objects\\n  - :ref:`type_coercion`: intelligent type coercion utilities\\n  - :ref:`configuration`: a map implementation designed specifically to hold application\\n    configuration and context information\\n  - :ref:`factory`: factory pattern implementation\\n  - :ref:`path`: file path utilities especially helpful when working with various python\\n    package formats\\n  - :ref:`logz`: logging initialization routines to simplify python logging setup\\n  - :ref:`crypt`: simple, but correct, pycrypto wrapper\\n\\n[2021-11-09] Our version of auxlib has deviated from the upstream project by a significant amount\\n(especially compared with the other vendored packages). Further, the upstream project has low\\npopularity and is no longer actively maintained. Consequently it was decided to absorb, refactor,\\nand replace auxlib. As a first step of this process we moved conda._vendor.auxlib to conda.auxlib.\\n\\\"\\\"\\\"\\n\\n# don't mess up logging for library users\\nfrom logging import getLogger, Handler\\nclass NullHandler(Handler):  # NOQA\\n    def emit(self, record):\\n        pass\\n\\n\\ngetLogger('auxlib').addHandler(NullHandler())\\n\\n__all__ = [\\n    \\\"__version__\\\", \\\"__author__\\\",\\n    \\\"__email__\\\", \\\"__license__\\\", \\\"__copyright__\\\",\\n    \\\"__summary__\\\", \\\"__url__\\\",\\n]\\n\\n__version__ = \\\"0.0.43\\\"\\n\\n__author__ = 'Kale Franz'\\n__email__ = 'kale@franz.io'\\n__url__ = 'https://github.com/kalefranz/auxlib'\\n__license__ = \\\"ISC\\\"\\n__copyright__ = \\\"(c) 2015 Kale Franz. All rights reserved.\\\"\\n__summary__ = \\\"\\\"\\\"auxiliary library to the python standard library\\\"\\\"\\\"\\n\\n\\nclass _Null:\\n    \\\"\\\"\\\"\\n    Examples:\\n        >>> len(_Null())\\n        0\\n        >>> bool(_Null())\\n        False\\n        >>> _Null().__nonzero__()\\n        False\\n    \\\"\\\"\\\"\\n    def __nonzero__(self):\\n        return self.__bool__()\\n\\n    def __bool__(self):\\n        return False\\n\\n    def __len__(self):\\n        return 0\\n\\n    def __eq__(self, other):\\n        return isinstance(other, _Null)\\n\\n    def __hash__(self):\\n        return hash(_Null)\\n\\n    def __str__(self):\\n        return 'Null'\\n\\n    def __json__(self):\\n        return 'null'\\n\\n    to_json = __json__\\n\\n\\n# Use this NULL object when needing to distinguish a value from None\\n# For example, when parsing json, you may need to determine if a json key was given and set\\n#   to null, or the key didn't exist at all.  There could be a bit of potential confusion here,\\n#   because in python null == None, while here I'm defining NULL to mean 'not defined'.\\nNULL = _Null()\\n\\n\\n\\\"\\\"\\\"\\nThis module provides serializable, validatable, type-enforcing domain objects and data\\ntransfer objects. It has many of the same motivations as the python\\n`Marshmallow <http://marshmallow.readthedocs.org/en/latest/why.html>`_ package. It is most\\nsimilar to `Schematics <http://schematics.readthedocs.io/>`_.\\n\\n========\\nTutorial\\n========\\n\\nChapter 1: Entity and Field Basics\\n----------------------------------\\n\\n    >>> class Color(Enum):\\n    ...     blue = 0\\n    ...     black = 1\\n    ...     red = 2\\n    >>> class Car(Entity):\\n    ...     weight = NumberField(required=False)\\n    ...     wheels = IntField(default=4, validation=lambda x: 3 <= x <= 4)\\n    ...     color = EnumField(Color)\\n\\n    >>> # create a new car object\\n    >>> car = Car(color=Color.blue, weight=4242.46)\\n    >>> car\\n    Car(weight=4242.46, color=0)\\n\\n    >>> # it has 4 wheels, all by default\\n    >>> car.wheels\\n    4\\n\\n    >>> # but a car can't have 5 wheels!\\n    >>> #  the `validation=` field is a simple callable that returns a\\n    >>> #  boolean based on validity\\n    >>> car.wheels = 5\\n    Traceback (most recent call last):\\n    ValidationError: Invalid value 5 for wheels\\n\\n    >>> # we can call .dump() on car, and just get back a standard\\n    >>> #  python dict actually, it's an ordereddict to match attribute\\n    >>> #  declaration order\\n    >>> type(car.dump())\\n    <class '...OrderedDict'>\\n    >>> car.dump()\\n    OrderedDict([('weight', 4242.46), ('wheels', 4), ('color', 0)])\\n\\n    >>> # and json too (note the order!)\\n    >>> car.json()\\n    '{\\\"weight\\\": 4242.46, \\\"wheels\\\": 4, \\\"color\\\": 0}'\\n\\n    >>> # green cars aren't allowed\\n    >>> car.color = \\\"green\\\"\\n    Traceback (most recent call last):\\n    ValidationError: 'green' is not a valid Color\\n\\n    >>> # but black cars are!\\n    >>> car.color = \\\"black\\\"\\n    >>> car.color\\n    <Color.black: 1>\\n\\n    >>> # car.color really is an enum, promise\\n    >>> type(car.color)\\n    <enum 'Color'>\\n\\n    >>> # enum assignment can be with any of (and preferentially)\\n    >>> #   (1) an enum literal,\\n    >>> #   (2) a valid enum value, or\\n    >>> #   (3) a valid enum name\\n    >>> car.color = Color.blue; car.color.value\\n    0\\n    >>> car.color = 1; car.color.name\\n    'black'\\n\\n    >>> # let's do a round-trip marshalling of this thing\\n    >>> same_car = Car.from_json(car.json())  # or equally Car.from_json(json.dumps(car.dump()))\\n    >>> same_car == car\\n    True\\n\\n    >>> # actually, they're two different instances\\n    >>> same_car is not car\\n    True\\n\\n    >>> # this works too\\n    >>> cloned_car = Car(**car.dump())\\n    >>> cloned_car == car\\n    True\\n\\n    >>> # while we're at it, these are all equivalent too\\n    >>> car == Car.from_objects(car)\\n    True\\n    >>> car == Car.from_objects({\\\"weight\\\": 4242.46, \\\"wheels\\\": 4, \\\"color\\\": 1})\\n    True\\n    >>> car == Car.from_json('{\\\"weight\\\": 4242.46, \\\"color\\\": 1}')\\n    True\\n\\n    >>> # .from_objects() even lets you stack and combine objects\\n    >>> class DumbClass:\\n    ...     color = 0\\n    ...     wheels = 3\\n    >>> Car.from_objects(DumbClass(), dict(weight=2222, color=1))\\n    Car(weight=2222, wheels=3, color=0)\\n    >>> # and also pass kwargs that override properties pulled\\n    >>> #  off any objects\\n    >>> Car.from_objects(DumbClass(), {'weight': 2222, 'color': 1}, color=2, weight=33)\\n    Car(weight=33, wheels=3, color=2)\\n\\n\\nChapter 2: Entity and Field Composition\\n---------------------------------------\\n\\n    >>> # now let's get fancy\\n    >>> # a ComposableField \\\"nests\\\" another valid Entity\\n    >>> # a ListField's first argument is a \\\"generic\\\" type,\\n    >>> #   which can be a valid Entity, any python primitive\\n    >>> #   type, or a list of Entities/types\\n    >>> class Fleet(Entity):\\n    ...     boss_car = ComposableField(Car)\\n    ...     cars = ListField(Car)\\n\\n    >>> # here's our fleet of company cars\\n    >>> company_fleet = Fleet(boss_car=Car(color='red'), cars=[car, same_car, cloned_car])\\n    >>> company_fleet.pretty_json()  #doctest: +SKIP\\n    {\\n      \\\"boss_car\\\": {\\n        \\\"wheels\\\": 4\\n        \\\"color\\\": 2,\\n      },\\n      \\\"cars\\\": [\\n        {\\n          \\\"weight\\\": 4242.46,\\n          \\\"wheels\\\": 4\\n          \\\"color\\\": 1,\\n        },\\n        {\\n          \\\"weight\\\": 4242.46,\\n          \\\"wheels\\\": 4\\n          \\\"color\\\": 1,\\n        },\\n        {\\n          \\\"weight\\\": 4242.46,\\n          \\\"wheels\\\": 4\\n          \\\"color\\\": 1,\\n        }\\n      ]\\n    }\\n\\n    >>> # the boss' car is red of course (and it's still an Enum)\\n    >>> company_fleet.boss_car.color.name\\n    'red'\\n\\n    >>> # and there are three cars left for the employees\\n    >>> len(company_fleet.cars)\\n    3\\n\\n\\nChapter 3: Immutability\\n-----------------------\\n\\n    >>> class ImmutableCar(ImmutableEntity):\\n    ...     wheels = IntField(default=4, validation=lambda x: 3 <= x <= 4)\\n    ...     color = EnumField(Color)\\n    >>> icar = ImmutableCar.from_objects({'wheels': 3, 'color': 'blue'})\\n    >>> icar\\n    ImmutableCar(wheels=3, color=0)\\n\\n    >>> icar.wheels = 4\\n    Traceback (most recent call last):\\n    AttributeError: Assignment not allowed. ImmutableCar is immutable.\\n\\n    >>> class FixedWheelCar(Entity):\\n    ...     wheels = IntField(default=4, immutable=True)\\n    ...     color = EnumField(Color)\\n    >>> fwcar = FixedWheelCar.from_objects(icar)\\n    >>> fwcar.json()\\n    '{\\\"wheels\\\": 3, \\\"color\\\": 0}'\\n\\n    >>> # repainting the car is easy\\n    >>> fwcar.color = Color.red\\n    >>> fwcar.color.name\\n    'red'\\n\\n    >>> # can't really change the number of wheels though\\n    >>> fwcar.wheels = 18\\n    Traceback (most recent call last):\\n    AttributeError: The wheels field is immutable.\\n\\n\\nChapter X: The del and null Weeds\\n---------------------------------\\n\\n    >>> old_date = lambda: isoparse('1982-02-17')\\n    >>> class CarBattery(Entity):\\n    ...     # NOTE: default value can be a callable!\\n    ...     first_charge = DateField(required=False)  # default=None, nullable=False\\n    ...     latest_charge = DateField(default=old_date, nullable=True)  # required=True\\n    ...     expiration = DateField(default=old_date, required=False, nullable=False)\\n\\n    >>> # starting point\\n    >>> battery = CarBattery()\\n    >>> battery\\n    CarBattery()\\n    >>> battery.json()\\n    '{\\\"latest_charge\\\": \\\"1982-02-17T00:00:00\\\", \\\"expiration\\\": \\\"1982-02-17T00:00:00\\\"}'\\n\\n    >>> # first_charge is not assigned a default value. Once one is assigned, it can be deleted,\\n    >>> #   but it can't be made null.\\n    >>> battery.first_charge = isoparse('2016-03-23')\\n    >>> battery\\n    CarBattery(first_charge=datetime.datetime(2016, 3, 23, 0, 0))\\n    >>> battery.first_charge = None\\n    Traceback (most recent call last):\\n    ValidationError: Value for first_charge not given or invalid.\\n    >>> del battery.first_charge\\n    >>> battery\\n    CarBattery()\\n\\n    >>> # latest_charge can be null, but it can't be deleted. The default value is a callable.\\n    >>> del battery.latest_charge\\n    Traceback (most recent call last):\\n    AttributeError: The latest_charge field is required and cannot be deleted.\\n    >>> battery.latest_charge = None\\n    >>> battery.json()\\n    '{\\\"latest_charge\\\": null, \\\"expiration\\\": \\\"1982-02-17T00:00:00\\\"}'\\n\\n    >>> # expiration is assigned by default, can't be made null, but can be deleted.\\n    >>> battery.expiration\\n    datetime.datetime(1982, 2, 17, 0, 0)\\n    >>> battery.expiration = None\\n    Traceback (most recent call last):\\n    ValidationError: Value for expiration not given or invalid.\\n    >>> del battery.expiration\\n    >>> battery.json()\\n    '{\\\"latest_charge\\\": null}'\\n\\n\\n\\\"\\\"\\\"\\n\\nfrom collections.abc import Mapping, Sequence\\nfrom datetime import datetime\\nfrom enum import Enum\\nfrom functools import reduce\\nfrom json import JSONEncoder, dumps as json_dumps, loads as json_loads\\nfrom logging import getLogger\\nfrom pathlib import Path\\n\\nfrom boltons.timeutils import isoparse\\n\\nfrom . import NULL\\nfrom .compat import isiterable, odict\\nfrom .collection import AttrDict\\nfrom .exceptions import Raise, ValidationError\\nfrom .ish import find_or_raise\\nfrom .logz import DumpEncoder\\nfrom .type_coercion import maybecall\\n\\ntry:\\n    from frozendict import deepfreeze, frozendict\\n    from frozendict import getFreezeConversionMap as _getFreezeConversionMap\\n    from frozendict import register as _register\\n\\n    if Enum not in _getFreezeConversionMap():\\n        # leave enums as is, deepfreeze will flatten it into a dict\\n        # see https://github.com/Marco-Sulla/python-frozendict/issues/98\\n        _register(Enum, lambda x : x)\\n\\n    del _getFreezeConversionMap\\n    del _register\\nexcept ImportError:\\n    from .._vendor.frozendict import frozendict\\n    from ..auxlib.collection import make_immutable as deepfreeze\\n\\nlog = getLogger(__name__)\\n\\n__all__ = [\\n    \\\"Entity\\\", \\\"ImmutableEntity\\\", \\\"Field\\\",\\n    \\\"BooleanField\\\", \\\"BoolField\\\", \\\"IntegerField\\\", \\\"IntField\\\",\\n    \\\"NumberField\\\", \\\"StringField\\\", \\\"DateField\\\",\\n    \\\"EnumField\\\", \\\"ListField\\\", \\\"MapField\\\", \\\"ComposableField\\\",\\n]\\n\\nKEY_OVERRIDES_MAP = \\\"__key_overrides__\\\"\\n\\n\\nNOTES = \\\"\\\"\\\"\\n\\nCurrent deficiencies to schematics:\\n  - no get_mock_object method\\n  - no context-dependent serialization or MultilingualStringType\\n  - name = StringType(serialized_name='person_name', alternate_names=['human_name'])\\n  - name = StringType(serialize_when_none=False)\\n  - more flexible validation error messages\\n  - field validation can depend on other fields\\n  - 'roles' containing denylists for .dump() and .json()\\n    __roles__ = {\\n        EntityRole.registered_name: Denylist('field1', 'field2'),\\n        EntityRole.another_registered_name: Allowlist('field3', 'field4'),\\n    }\\n\\n\\nTODO:\\n  - alternate field names\\n  - add dump_if_null field option\\n  - add help/description parameter to Field\\n  - consider leveraging slots\\n  - collect all validation errors before raising\\n  - Allow returning string error message for validation instead of False\\n  - profile and optimize\\n  - use boltons instead of dateutil\\n  - correctly implement copy and deepcopy on fields and Entity, DictSafeMixin\\n    http://stackoverflow.com/questions/1500718/what-is-the-right-way-to-override-the-copy-deepcopy-operations-on-an-object-in-p\\n\\n\\nOptional Field Properties:\\n  - validation = None\\n  - default = None\\n  - required = True\\n  - in_dump = True\\n  - nullable = False\\n\\nBehaviors:\\n  - Nullable is a \\\"hard\\\" setting, in that the value is either always or never allowed to be None.\\n  - What happens then if required=False and nullable=False?\\n      - The object can be init'd without a value (though not with a None value).\\n        getattr throws AttributeError\\n      - Any assignment must be not None.\\n\\n\\n  - Setting a value to None doesn't \\\"unset\\\" a value.  (That's what del is for.)  And you can't\\n    del a value if required=True, nullable=False, default=None.\\n\\n  - If a field is not required, del does *not* \\\"unmask\\\" the default value.  Instead, del\\n    removes the value from the object entirely.  To get back the default value, need to recreate\\n    the object.  Entity.from_objects(old_object)\\n\\n\\n  - Disabling in_dump is a \\\"hard\\\" setting, in that with it disabled the field will never get\\n    dumped.  With it enabled, the field may or may not be dumped depending on its value and other\\n    settings.\\n\\n  - Required is a \\\"hard\\\" setting, in that if True, a valid value or default must be provided. None\\n    is only a valid value or default if nullable is True.\\n\\n  - In general, nullable means that None is a valid value.\\n    - getattr returns None instead of raising Attribute error\\n    - If in_dump, field is given with null value.\\n    - If default is not None, assigning None clears a previous assignment. Future getattrs return\\n      the default value.\\n    - What does nullable mean with default=None and required=True? Does instantiation raise\\n      an error if assignment not made on init? Can IntField(nullable=True) be init'd?\\n\\n  - If required=False and nullable=False, field will only be in dump if field!=None.\\n    Also, getattr raises AttributeError.\\n  - If required=False and nullable=True, field will be in dump if field==None.\\n\\n  - If in_dump is True, does default value get dumped:\\n    - if no assignment, default exists\\n    - if nullable, and assigned None\\n  - How does optional validation work with nullable and assigning None?\\n  - When does gettattr throw AttributeError, and when does it return None?\\n\\n\\n\\n\\\"\\\"\\\"\\n\\n\\nclass Field:\\n    \\\"\\\"\\\"\\n    Fields are doing something very similar to boxing and unboxing\\n    of c#/java primitives.  __set__ should take a \\\"primitive\\\" or \\\"raw\\\" value and create a \\\"boxed\\\"\\n    or \\\"programmatically usable\\\" value of it.  While __get__ should return the boxed value,\\n    dump in turn should unbox the value into a primitive or raw value.\\n\\n    Arguments:\\n        types_ (primitive literal or type or sequence of types):\\n        default (any, callable, optional):  If default is callable, it's guaranteed to return a\\n            valid value at the time of Entity creation.\\n        required (boolean, optional):\\n        validation (callable, optional):\\n        dump (boolean, optional):\\n    \\\"\\\"\\\"\\n\\n    # Used to track order of field declarations. Supporting python 2.7, so can't rely\\n    #   on __prepare__.  Strategy lifted from http://stackoverflow.com/a/4460034/2127762\\n    _order_helper = 0\\n\\n    def __init__(self, default=NULL, required=True, validation=None,\\n                 in_dump=True, default_in_dump=True, nullable=False, immutable=False, aliases=()):\\n        self._required = required\\n        self._validation = validation\\n        self._in_dump = in_dump\\n        self._default_in_dump = default_in_dump\\n        self._nullable = nullable\\n        self._immutable = immutable\\n        self._aliases = aliases\\n        if default is NULL:\\n            self._default = NULL\\n        else:\\n            self._default = default if callable(default) else self.box(None, None, default)\\n            self.validate(None, self.box(None, None, maybecall(default)))\\n\\n        self._order_helper = Field._order_helper\\n        Field._order_helper += 1\\n\\n    @property\\n    def name(self):\\n        try:\\n            return self._name\\n        except AttributeError:\\n            log.error(\\\"The name attribute has not been set for this field. \\\"\\n                      \\\"Call set_name at class creation time.\\\")\\n            raise\\n\\n    def set_name(self, name):\\n        self._name = name\\n        return self\\n\\n    def __get__(self, instance, instance_type):\\n        try:\\n            if instance is None:  # if calling from the class object\\n                val = getattr(instance_type, KEY_OVERRIDES_MAP)[self.name]\\n            else:\\n                val = instance.__dict__[self.name]\\n        except AttributeError:\\n            log.error(\\\"The name attribute has not been set for this field.\\\")\\n            raise AttributeError(\\\"The name attribute has not been set for this field.\\\")\\n        except KeyError:\\n            if self.default is NULL:\\n                raise AttributeError(f\\\"A value for {self.name} has not been set\\\")\\n            else:\\n                val = maybecall(self.default)  # default *can* be a callable\\n        if val is None and not self.nullable:\\n            # means the \\\"tricky edge case\\\" was activated in __delete__\\n            raise AttributeError(f\\\"The {self.name} field has been deleted.\\\")\\n        return self.unbox(instance, instance_type, val)\\n\\n    def __set__(self, instance, val):\\n        if self.immutable and instance._initd:\\n            raise AttributeError(f\\\"The {self.name} field is immutable.\\\")\\n        # validate will raise an exception if invalid\\n        # validate will return False if the value should be removed\\n        instance.__dict__[self.name] = self.validate(\\n            instance,\\n            self.box(instance, instance.__class__, val),\\n        )\\n\\n    def __delete__(self, instance):\\n        if self.immutable and instance._initd:\\n            raise AttributeError(f\\\"The {self.name} field is immutable.\\\")\\n        elif self.required:\\n            raise AttributeError(f\\\"The {self.name} field is required and cannot be deleted.\\\")\\n        elif not self.nullable:\\n            # tricky edge case\\n            # given a field Field(default='some value', required=False, nullable=False)\\n            # works together with Entity.dump() logic for selecting fields to include in dump\\n            # `if value is not None or field.nullable`\\n            instance.__dict__[self.name] = None\\n        else:\\n            instance.__dict__.pop(self.name, None)\\n\\n    def box(self, instance, instance_type, val):\\n        return val\\n\\n    def unbox(self, instance, instance_type, val):\\n        return val\\n\\n    def dump(self, instance, instance_type, val):\\n        return val\\n\\n    def validate(self, instance, val):\\n        \\\"\\\"\\\"\\n\\n        Returns:\\n            True: if val is valid\\n\\n        Raises:\\n            ValidationError\\n        \\\"\\\"\\\"\\n        # note here calling, but not assigning; could lead to unexpected behavior\\n        if isinstance(val, self._type) and (self._validation is None or self._validation(val)):\\n            return val\\n        elif val is NULL and not self.required:\\n            return val\\n        elif val is None and self.nullable:\\n            return val\\n        else:\\n            raise ValidationError(getattr(self, 'name', 'undefined name'), val)\\n\\n    @property\\n    def required(self):\\n        return self._required\\n\\n    @property\\n    def type(self):\\n        return self._type\\n\\n    @property\\n    def default(self):\\n        return self._default\\n\\n    @property\\n    def in_dump(self):\\n        return self._in_dump\\n\\n    @property\\n    def default_in_dump(self):\\n        return self._default_in_dump\\n\\n    @property\\n    def nullable(self):\\n        return self.is_nullable\\n\\n    @property\\n    def is_nullable(self):\\n        return self._nullable\\n\\n    @property\\n    def immutable(self):\\n        return self._immutable\\n\\n\\nclass BooleanField(Field):\\n    _type = bool\\n\\n    def box(self, instance, instance_type, val):\\n        return None if val is None else bool(val)\\n\\n\\nBoolField = BooleanField\\n\\n\\nclass IntegerField(Field):\\n    _type = int\\n\\n\\nIntField = IntegerField\\n\\n\\nclass NumberField(Field):\\n    _type = (int, float, complex)\\n\\n\\nclass StringField(Field):\\n    _type = str\\n\\n    def box(self, instance, instance_type, val):\\n        return str(val) if isinstance(val, NumberField._type) else val\\n\\n\\nclass DateField(Field):\\n    _type = datetime\\n\\n    def box(self, instance, instance_type, val):\\n        try:\\n            return isoparse(val) if isinstance(val, str) else val\\n        except ValueError as e:\\n            raise ValidationError(val, msg=e)\\n\\n    def dump(self, instance, instance_type, val):\\n        return None if val is None else val.isoformat()\\n\\n\\nclass EnumField(Field):\\n\\n    def __init__(self, enum_class, default=NULL, required=True, validation=None,\\n                 in_dump=True, default_in_dump=True, nullable=False, immutable=False, aliases=()):\\n        if not issubclass(enum_class, Enum):\\n            raise ValidationError(None, msg=\\\"enum_class must be an instance of Enum\\\")\\n        self._type = enum_class\\n        super().__init__(\\n            default, required, validation, in_dump, default_in_dump, nullable, immutable, aliases\\n        )\\n\\n    def box(self, instance, instance_type, val):\\n        if val is None:\\n            # let the required/nullable logic handle validation for this case\\n            return None\\n        try:\\n            # try to box using val as an Enum name\\n            return self._type(val)\\n        except ValueError as e1:\\n            try:\\n                # try to box using val as an Enum value\\n                return self._type[val]\\n            except KeyError:\\n                raise ValidationError(val, msg=e1)\\n\\n    def dump(self, instance, instance_type, val):\\n        return None if val in (None, NULL) else val.value\\n\\n\\nclass ListField(Field):\\n    _type = tuple\\n\\n    def __init__(self, element_type, default=NULL, required=True, validation=None,\\n                 in_dump=True, default_in_dump=True, nullable=False, immutable=False, aliases=()):\\n        self._element_type = element_type\\n        super().__init__(\\n            default, required, validation, in_dump, default_in_dump, nullable, immutable, aliases\\n        )\\n\\n    def box(self, instance, instance_type, val):\\n        if val is None:\\n            return None\\n        elif isinstance(val, str):\\n            raise ValidationError(\\n                f\\\"Attempted to assign a string to ListField {self.name}\\\"\\n            )\\n        elif isiterable(val):\\n            et = self._element_type\\n            if isinstance(et, type) and issubclass(et, Entity):\\n                return self._type(v if isinstance(v, et) else et(**v) for v in val)\\n            else:\\n                return deepfreeze(val) if self.immutable else self._type(val)\\n        else:\\n            raise ValidationError(\\n                val, msg=f\\\"Cannot assign a non-iterable value to {self.name}\\\"\\n            )\\n\\n    def unbox(self, instance, instance_type, val):\\n        return self._type() if val is None and not self.nullable else val\\n\\n    def dump(self, instance, instance_type, val):\\n        if isinstance(self._element_type, type) and issubclass(self._element_type, Entity):\\n            return self._type(v.dump() for v in val)\\n        else:\\n            return val\\n\\n    def validate(self, instance, val):\\n        val = super().validate(instance, val)\\n        if val:\\n            et = self._element_type\\n            self._type(Raise(ValidationError(self.name, el, et)) for el in val\\n                       if not isinstance(el, et))\\n        return val\\n\\n\\nclass MutableListField(ListField):\\n    _type = list\\n\\n\\nclass MapField(Field):\\n    _type = frozendict\\n\\n    def __init__(\\n        self,\\n        default=NULL,\\n        required=True,\\n        validation=None,\\n        in_dump=True,\\n        default_in_dump=True,\\n        nullable=False,\\n        immutable=True,\\n        aliases=(),\\n    ):\\n        super().__init__(\\n            default, required, validation, in_dump, default_in_dump, nullable, immutable, aliases\\n        )\\n\\n    def box(self, instance, instance_type, val):\\n        # TODO: really need to make this recursive to make any lists or maps immutable\\n        if val is None:\\n            return self._type()\\n        elif isiterable(val):\\n            val = deepfreeze(val)\\n            if not isinstance(val, Mapping):\\n                raise ValidationError(\\n                    val, msg=f\\\"Cannot assign a non-iterable value to {self.name}\\\"\\n                )\\n            return val\\n        else:\\n            raise ValidationError(\\n                val, msg=f\\\"Cannot assign a non-iterable value to {self.name}\\\"\\n            )\\n\\n\\nclass ComposableField(Field):\\n\\n    def __init__(self, field_class, default=NULL, required=True, validation=None,\\n                 in_dump=True, default_in_dump=True, nullable=False, immutable=False, aliases=()):\\n        self._type = field_class\\n        super().__init__(\\n            default, required, validation, in_dump, default_in_dump, nullable, immutable, aliases\\n        )\\n\\n    def box(self, instance, instance_type, val):\\n        if val is None:\\n            return None\\n        if isinstance(val, self._type):\\n            return val\\n        else:\\n            # assuming val is a dict now\\n            try:\\n                # if there is a key named 'self', have to rename it\\n                if hasattr(val, 'pop'):\\n                    val['slf'] = val.pop('self')\\n            except KeyError:\\n                pass  # no key of 'self', so no worries\\n            if isinstance(val, self._type):\\n                return val if isinstance(val, self._type) else self._type(**val)\\n            elif isinstance(val, Mapping):\\n                return self._type(**val)\\n            elif isinstance(val, Sequence) and not isinstance(val, str):\\n                return self._type(*val)\\n            else:\\n                return self._type(val)\\n\\n    def dump(self, instance, instance_type, val):\\n        return None if val is None else val.dump()\\n\\n\\nclass EntityType(type):\\n\\n    @staticmethod\\n    def __get_entity_subclasses(bases):\\n        try:\\n            return [base for base in bases if issubclass(base, Entity) and base is not Entity]\\n        except NameError:\\n            # NameError: global name 'Entity' is not defined\\n            return ()\\n\\n    def __new__(mcs, name, bases, dct):\\n        # if we're about to mask a field that's already been created with something that's\\n        #  not a field, then assign it to an alternate variable name\\n        non_field_keys = (\\n            key\\n            for key, value in dct.items()\\n            if not isinstance(value, Field) and not key.startswith(\\\"__\\\")\\n        )\\n        entity_subclasses = EntityType.__get_entity_subclasses(bases)\\n        if entity_subclasses:\\n            keys_to_override = [key for key in non_field_keys\\n                                if any(isinstance(base.__dict__.get(key), Field)\\n                                       for base in entity_subclasses)]\\n            dct[KEY_OVERRIDES_MAP] = {key: dct.pop(key) for key in keys_to_override}\\n        else:\\n            dct[KEY_OVERRIDES_MAP] = {}\\n\\n        return super().__new__(mcs, name, bases, dct)\\n\\n    def __init__(cls, name, bases, attr):\\n        super().__init__(name, bases, attr)\\n\\n        fields = odict()\\n        _field_sort_key = lambda x: x[1]._order_helper\\n        for clz in reversed(type.mro(cls)):\\n            clz_fields = (\\n                (name, field.set_name(name))\\n                for name, field in clz.__dict__.items()\\n                if isinstance(field, Field)\\n            )\\n            fields.update(sorted(clz_fields, key=_field_sort_key))\\n\\n        cls.__fields__ = frozendict(fields)\\n        if hasattr(cls, '__register__'):\\n            cls.__register__()\\n\\n    def __call__(cls, *args, **kwargs):\\n        instance = super().__call__(*args, **kwargs)\\n        setattr(instance, f\\\"_{cls.__name__}__initd\\\", True)\\n        return instance\\n\\n    @property\\n    def fields(cls):\\n        return cls.__fields__.keys()\\n\\n\\nclass Entity(metaclass=EntityType):\\n    __fields__ = odict()\\n    _lazy_validate = False\\n\\n    def __init__(self, **kwargs):\\n        for key, field in self.__fields__.items():\\n            try:\\n                setattr(self, key, kwargs[key])\\n            except KeyError:\\n                alias = next((ls for ls in field._aliases if ls in kwargs), None)\\n                if alias is not None:\\n                    setattr(self, key, kwargs[alias])\\n                elif key in getattr(self, KEY_OVERRIDES_MAP):\\n                    # handle case of fields inherited from subclass but overrode on class object\\n                    setattr(self, key, getattr(self, KEY_OVERRIDES_MAP)[key])\\n                elif field.required and field.default is NULL:\\n                    raise ValidationError(\\n                        key,\\n                        msg=\\\"{} requires a {} field. Instantiated with \\\"\\n                        \\\"{}\\\".format(self.__class__.__name__, key, kwargs),\\n                    )\\n            except ValidationError:\\n                if kwargs[key] is not None or field.required:\\n                    raise\\n        if not self._lazy_validate:\\n            self.validate()\\n\\n    @classmethod\\n    def from_objects(cls, *objects, **override_fields):\\n        init_vars = {}\\n        search_maps = tuple(AttrDict(o) if isinstance(o, dict) else o\\n                            for o in ((override_fields,) + objects))\\n        for key, field in cls.__fields__.items():\\n            try:\\n                init_vars[key] = find_or_raise(key, search_maps, field._aliases)\\n            except AttributeError:\\n                pass\\n\\n        return cls(**init_vars)\\n\\n    @classmethod\\n    def from_json(cls, json_str):\\n        return cls(**json_loads(json_str))\\n\\n    @classmethod\\n    def load(cls, data_dict):\\n        return cls(**data_dict)\\n\\n    def validate(self):\\n        # TODO: here, validate should only have to determine if the required keys are set\\n        try:\\n            reduce(\\n                lambda _, name: getattr(self, name),\\n                (name for name, field in self.__fields__.items() if field.required),\\n            )\\n        except TypeError as e:\\n            if str(e) == \\\"reduce() of empty sequence with no initial value\\\":\\n                pass\\n        except AttributeError as e:\\n            raise ValidationError(None, msg=e)\\n\\n    def __repr__(self):\\n        def _valid(key):\\n            # TODO: re-enable once aliases are implemented\\n            # if key.startswith('_'):\\n            #     return False\\n            if '__' in key:\\n                return False\\n            try:\\n                getattr(self, key)\\n                return True\\n            except AttributeError:\\n                return False\\n\\n        def _val(key):\\n            val = getattr(self, key)\\n            return repr(val.value) if isinstance(val, Enum) else repr(val)\\n\\n        def _sort_helper(key):\\n            field = self.__fields__.get(key)\\n            return field._order_helper if field is not None else -1\\n\\n        kwarg_str = \\\", \\\".join(\\n            f\\\"{key}={_val(key)}\\\" for key in sorted(self.__dict__, key=_sort_helper) if _valid(key)\\n        )\\n        return f\\\"{self.__class__.__name__}({kwarg_str})\\\"\\n\\n    @classmethod\\n    def __register__(cls):\\n        pass\\n\\n    def json(self, indent=None, separators=None, **kwargs):\\n        return json_dumps(self, indent=indent, separators=separators, cls=DumpEncoder, **kwargs)\\n\\n    def pretty_json(self, indent=2, separators=(',', ': '), **kwargs):\\n        return self.json(indent=indent, separators=separators, **kwargs)\\n\\n    def dump(self):\\n        return odict((field.name, field.dump(self, self.__class__, value))\\n                     for field, value in ((field, getattr(self, field.name, NULL))\\n                                          for field in self.__dump_fields())\\n                     if value is not NULL and not (value is field.default\\n                                                   and not field.default_in_dump))\\n\\n    @classmethod\\n    def __dump_fields(cls):\\n        if \\\"__dump_fields_cache\\\" not in cls.__dict__:\\n            cls.__dump_fields_cache = tuple(\\n                field for field in cls.__fields__.values() if field.in_dump\\n            )\\n        return cls.__dump_fields_cache\\n\\n    def __eq__(self, other):\\n        if self.__class__ != other.__class__:\\n            return False\\n        rando_default = 19274656290  # need an arbitrary but definite value if field does not exist\\n        return all(getattr(self, field, rando_default) == getattr(other, field, rando_default)\\n                   for field in self.__fields__)\\n\\n    def __hash__(self):\\n        return sum(hash(getattr(self, field, None)) for field in self.__fields__)\\n\\n    @property\\n    def _initd(self):\\n        return getattr(self, f\\\"_{self.__class__.__name__}__initd\\\", None)\\n\\n\\nclass ImmutableEntity(Entity):\\n\\n    def __setattr__(self, attribute, value):\\n        if self._initd:\\n            raise AttributeError(\\n                f\\\"Assignment not allowed. {self.__class__.__name__} is immutable.\\\"\\n            )\\n        super().__setattr__(attribute, value)\\n\\n    def __delattr__(self, item):\\n        if self._initd:\\n            raise AttributeError(f\\\"Deletion not allowed. {self.__class__.__name__} is immutable.\\\")\\n        super().__delattr__(item)\\n\\n\\nclass DictSafeMixin:\\n\\n    def __getitem__(self, item):\\n        return getattr(self, item)\\n\\n    def __setitem__(self, key, value):\\n        setattr(self, key, value)\\n\\n    def __delitem__(self, key):\\n        delattr(self, key)\\n\\n    def get(self, item, default=None):\\n        return getattr(self, item, default)\\n\\n    def __contains__(self, item):\\n        value = getattr(self, item, None)\\n        if value is None:\\n            return False\\n        field = self.__fields__[item]\\n        if isinstance(field, (MapField, ListField)):\\n            return len(value) > 0\\n        return True\\n\\n    def __iter__(self):\\n        for key in self.__fields__:\\n            if key in self:\\n                yield key\\n\\n    def items(self):\\n        for key in self.__fields__:\\n            if key in self:\\n                yield key, getattr(self, key)\\n\\n    def copy(self):\\n        return self.__class__(**self.dump())\\n\\n    def setdefault(self, key, default_value):\\n        if key not in self:\\n            setattr(self, key, default_value)\\n\\n    def update(self, E=None, **F):\\n        # D.update([E, ]**F) -> None.  Update D from dict/iterable E and F.\\n        # If E present and has a .keys() method, does:     for k in E: D[k] = E[k]\\n        # If E present and lacks .keys() method, does:     for (k, v) in E: D[k] = v\\n        # In either case, this is followed by: for k in F: D[k] = F[k]\\n        if E is not None:\\n            try:\\n                for k, v in E.items():\\n                    self[k] = v\\n            except AttributeError:\\n                for k, v in E:\\n                    self[k] = v\\n        for k in F:\\n            self[k] = F[k]\\n\\n\\nclass EntityEncoder(JSONEncoder):\\n    # json.dumps(obj, cls=SetEncoder)\\n    def default(self, obj):\\n        if hasattr(obj, 'dump'):\\n            return obj.dump()\\n        elif hasattr(obj, '__json__'):\\n            return obj.__json__()\\n        elif hasattr(obj, 'to_json'):\\n            return obj.to_json()\\n        elif hasattr(obj, 'as_json'):\\n            return obj.as_json()\\n        elif isinstance(obj, Enum):\\n            return obj.value\\n        elif isinstance(obj, Path):\\n            return str(obj)\\n        return JSONEncoder.default(self, obj)\\n\\n\\nfrom collections import OrderedDict as odict  # noqa: F401\\nimport os\\nfrom shlex import split\\n\\nfrom ..deprecations import deprecated\\n\\n\\ndeprecated.constant(\\\"24.3\\\", \\\"24.9\\\", \\\"NoneType\\\", type(None))\\ndeprecated.constant(\\\"24.3\\\", \\\"24.9\\\", \\\"primitive_types\\\", (str, int, float, complex, bool, type(None)))\\n\\n\\ndef isiterable(obj):\\n    # and not a string\\n    from collections.abc import Iterable\\n    return not isinstance(obj, str) and isinstance(obj, Iterable)\\n\\n\\n# shlex.split() is a poor function to use for anything general purpose (like calling subprocess).\\n# It mishandles Unicode in Python 3 but all is not lost. We can escape it, then escape the escapes\\n# then call shlex.split() then un-escape that.\\ndef shlex_split_unicode(to_split, posix=True):\\n    # shlex.split does its own un-escaping that we must counter.\\n    e_to_split = to_split.replace(\\\"\\\\\\\\\\\", \\\"\\\\\\\\\\\\\\\\\\\")\\n    return split(e_to_split, posix=posix)\\n\\n\\n@deprecated(\\\"24.3\\\", \\\"24.9\\\")\\ndef utf8_writer(fp):\\n    return fp\\n\\n\\ndef Utf8NamedTemporaryFile(\\n    mode=\\\"w+b\\\", buffering=-1, newline=None, suffix=None, prefix=None, dir=None, delete=True\\n):\\n    from tempfile import NamedTemporaryFile\\n\\n    if \\\"CONDA_TEST_SAVE_TEMPS\\\" in os.environ:\\n        delete = False\\n    encoding = None\\n    if \\\"b\\\" not in mode:\\n        encoding = \\\"utf-8\\\"\\n    return NamedTemporaryFile(\\n        mode=mode,\\n        buffering=buffering,\\n        encoding=encoding,\\n        newline=newline,\\n        suffix=suffix,\\n        prefix=prefix,\\n        dir=dir,\\n        delete=delete,\\n    )\\n\\n\\n#!/usr/bin/env python\\n# Copyright (c) 2005-2010 ActiveState Software Inc.\\n\\n\\\"\\\"\\\"Utilities for determining application-specific dirs.\\n\\nSee <http://github.com/ActiveState/appdirs> for details and usage.\\n\\\"\\\"\\\"\\n# Dev Notes:\\n# - MSDN on where to store app data files:\\n#   http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120\\n# - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html\\n# - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html\\nfrom ..deprecations import deprecated\\ndeprecated.module(\\\"24.3\\\", \\\"24.9\\\", addendum=\\\"Use `platformdirs` instead.\\\")\\n\\n\\n__version_info__ = (1, 2, 0)\\n__version__ = '.'.join(map(str, __version_info__))\\n\\n\\nimport sys\\nimport os\\n\\nPY3 = sys.version_info[0] == 3\\n\\nif PY3:\\n    unicode = str\\n\\nclass AppDirsError(Exception):\\n    pass\\n\\n\\n\\ndef user_data_dir(appname, appauthor=None, version=None, roaming=False):\\n    r\\\"\\\"\\\"Return full path to the user-specific data dir for this application.\\n\\n        \\\"appname\\\" is the name of application.\\n        \\\"appauthor\\\" (only required and used on Windows) is the name of the\\n            appauthor or distributing body for this application. Typically\\n            it is the owning company name.\\n        \\\"version\\\" is an optional version path element to append to the\\n            path. You might want to use this if you want multiple versions\\n            of your app to be able to run independently. If used, this\\n            would typically be \\\"<major>.<minor>\\\".\\n        \\\"roaming\\\" (boolean, default False) can be set True to use the Windows\\n            roaming appdata directory. That means that for users on a Windows\\n            network setup for roaming profiles, this user data will be\\n            sync'd on login. See\\n            <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>\\n            for a discussion of issues.\\n\\n    Typical user data directories are:\\n        Mac OS X:               ~/Library/Application Support/<AppName>\\n        Unix:                   ~/.config/<appname>    # or in $XDG_CONFIG_HOME if defined\\n        Win XP (not roaming):   C:\\\\Documents and Settings\\\\<username>\\\\Application Data\\\\<AppAuthor>\\\\<AppName>\\n        Win XP (roaming):       C:\\\\Documents and Settings\\\\<username>\\\\Local Settings\\\\Application Data\\\\<AppAuthor>\\\\<AppName>\\n        Win 7  (not roaming):   C:\\\\Users\\\\<username>\\\\AppData\\\\Local\\\\<AppAuthor>\\\\<AppName>\\n        Win 7  (roaming):       C:\\\\Users\\\\<username>\\\\AppData\\\\Roaming\\\\<AppAuthor>\\\\<AppName>\\n\\n    For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME. We don't\\n    use $XDG_DATA_HOME as that data dir is mostly used at the time of\\n    installation, instead of the application adding data during runtime.\\n    Also, in practice, Linux apps tend to store their data in\\n    \\\"~/.config/<appname>\\\" instead of \\\"~/.local/share/<appname>\\\".\\n    \\\"\\\"\\\"\\n    if sys.platform.startswith(\\\"win\\\"):\\n        if appauthor is None:\\n            raise AppDirsError(\\\"must specify 'appauthor' on Windows\\\")\\n        const = roaming and \\\"CSIDL_APPDATA\\\" or \\\"CSIDL_LOCAL_APPDATA\\\"\\n        path = os.path.join(_get_win_folder(const), appauthor, appname)\\n    elif sys.platform == 'darwin':\\n        path = os.path.join(\\n            os.path.expanduser('~/Library/Application Support/'),\\n            appname)\\n    else:\\n        path = os.path.join(\\n            os.getenv('XDG_CONFIG_HOME', os.path.expanduser(\\\"~/.config\\\")),\\n            appname.lower())\\n    if version:\\n        path = os.path.join(path, version)\\n    return path\\n\\n\\ndef site_data_dir(appname, appauthor=None, version=None):\\n    \\\"\\\"\\\"Return full path to the user-shared data dir for this application.\\n\\n        \\\"appname\\\" is the name of application.\\n        \\\"appauthor\\\" (only required and used on Windows) is the name of the\\n            appauthor or distributing body for this application. Typically\\n            it is the owning company name.\\n        \\\"version\\\" is an optional version path element to append to the\\n            path. You might want to use this if you want multiple versions\\n            of your app to be able to run independently. If used, this\\n            would typically be \\\"<major>.<minor>\\\".\\n\\n    Typical user data directories are:\\n        Mac OS X:   /Library/Application Support/<AppName>\\n        Unix:       /etc/xdg/<appname>\\n        Win XP:     C:\\\\Documents and Settings\\\\All Users\\\\Application Data\\\\<AppAuthor>\\\\<AppName>\\n        Vista:      (Fail! \\\"C:\\\\ProgramData\\\" is a hidden *system* directory on Vista.)\\n        Win 7:      C:\\\\ProgramData\\\\<AppAuthor>\\\\<AppName>   # Hidden, but writeable on Win 7.\\n\\n    For Unix, this is using the $XDG_CONFIG_DIRS[0] default.\\n\\n    WARNING: Do not use this on Windows. See the Vista-Fail note above for why.\\n    \\\"\\\"\\\"\\n    if sys.platform.startswith(\\\"win\\\"):\\n        if appauthor is None:\\n            raise AppDirsError(\\\"must specify 'appauthor' on Windows\\\")\\n        path = os.path.join(_get_win_folder(\\\"CSIDL_COMMON_APPDATA\\\"),\\n                            appauthor, appname)\\n    elif sys.platform == 'darwin':\\n        path = os.path.join(\\n            os.path.expanduser('/Library/Application Support'),\\n            appname)\\n    else:\\n        # XDG default for $XDG_CONFIG_DIRS[0]. Perhaps should actually\\n        # *use* that envvar, if defined.\\n        path = \\\"/etc/xdg/\\\"+appname.lower()\\n    if version:\\n        path = os.path.join(path, version)\\n    return path\\n\\n\\ndef user_cache_dir(appname, appauthor=None, version=None, opinion=True):\\n    r\\\"\\\"\\\"Return full path to the user-specific cache dir for this application.\\n\\n        \\\"appname\\\" is the name of application.\\n        \\\"appauthor\\\" (only required and used on Windows) is the name of the\\n            appauthor or distributing body for this application. Typically\\n            it is the owning company name.\\n        \\\"version\\\" is an optional version path element to append to the\\n            path. You might want to use this if you want multiple versions\\n            of your app to be able to run independently. If used, this\\n            would typically be \\\"<major>.<minor>\\\".\\n        \\\"opinion\\\" (boolean) can be False to disable the appending of\\n            \\\"Cache\\\" to the base app data dir for Windows. See\\n            discussion below.\\n\\n    Typical user cache directories are:\\n        Mac OS X:   ~/Library/Caches/<AppName>\\n        Unix:       ~/.cache/<appname> (XDG default)\\n        Win XP:     C:\\\\Documents and Settings\\\\<username>\\\\Local Settings\\\\Application Data\\\\<AppAuthor>\\\\<AppName>\\\\Cache\\n        Vista:      C:\\\\Users\\\\<username>\\\\AppData\\\\Local\\\\<AppAuthor>\\\\<AppName>\\\\Cache\\n\\n    On Windows the only suggestion in the MSDN docs is that local settings go in\\n    the `CSIDL_LOCAL_APPDATA` directory. This is identical to the non-roaming\\n    app data dir (the default returned by `user_data_dir` above). Apps typically\\n    put cache data somewhere *under* the given dir here. Some examples:\\n        ...\\\\Mozilla\\\\Firefox\\\\Profiles\\\\<ProfileName>\\\\Cache\\n        ...\\\\Acme\\\\SuperApp\\\\Cache\\\\1.0\\n    OPINION: This function appends \\\"Cache\\\" to the `CSIDL_LOCAL_APPDATA` value.\\n    This can be disabled with the `opinion=False` option.\\n    \\\"\\\"\\\"\\n    if sys.platform.startswith(\\\"win\\\"):\\n        if appauthor is None:\\n            raise AppDirsError(\\\"must specify 'appauthor' on Windows\\\")\\n        path = os.path.join(_get_win_folder(\\\"CSIDL_LOCAL_APPDATA\\\"),\\n                            appauthor, appname)\\n        if opinion:\\n            path = os.path.join(path, \\\"Cache\\\")\\n    elif sys.platform == 'darwin':\\n        path = os.path.join(\\n            os.path.expanduser('~/Library/Caches'),\\n            appname)\\n    else:\\n        path = os.path.join(\\n            os.getenv('XDG_CACHE_HOME', os.path.expanduser('~/.cache')),\\n            appname.lower())\\n    if version:\\n        path = os.path.join(path, version)\\n    return path\\n\\ndef user_log_dir(appname, appauthor=None, version=None, opinion=True):\\n    r\\\"\\\"\\\"Return full path to the user-specific log dir for this application.\\n\\n        \\\"appname\\\" is the name of application.\\n        \\\"appauthor\\\" (only required and used on Windows) is the name of the\\n            appauthor or distributing body for this application. Typically\\n            it is the owning company name.\\n        \\\"version\\\" is an optional version path element to append to the\\n            path. You might want to use this if you want multiple versions\\n            of your app to be able to run independently. If used, this\\n            would typically be \\\"<major>.<minor>\\\".\\n        \\\"opinion\\\" (boolean) can be False to disable the appending of\\n            \\\"Logs\\\" to the base app data dir for Windows, and \\\"log\\\" to the\\n            base cache dir for Unix. See discussion below.\\n\\n    Typical user cache directories are:\\n        Mac OS X:   ~/Library/Logs/<AppName>\\n        Unix:       ~/.cache/<appname>/log  # or under $XDG_CACHE_HOME if defined\\n        Win XP:     C:\\\\Documents and Settings\\\\<username>\\\\Local Settings\\\\Application Data\\\\<AppAuthor>\\\\<AppName>\\\\Logs\\n        Vista:      C:\\\\Users\\\\<username>\\\\AppData\\\\Local\\\\<AppAuthor>\\\\<AppName>\\\\Logs\\n\\n    On Windows the only suggestion in the MSDN docs is that local settings\\n    go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in\\n    examples of what some windows apps use for a logs dir.)\\n\\n    OPINION: This function appends \\\"Logs\\\" to the `CSIDL_LOCAL_APPDATA`\\n    value for Windows and appends \\\"log\\\" to the user cache dir for Unix.\\n    This can be disabled with the `opinion=False` option.\\n    \\\"\\\"\\\"\\n    if sys.platform == \\\"darwin\\\":\\n        path = os.path.join(\\n            os.path.expanduser('~/Library/Logs'),\\n            appname)\\n    elif sys.platform == \\\"win32\\\":\\n        path = user_data_dir(appname, appauthor, version); version=False\\n        if opinion:\\n            path = os.path.join(path, \\\"Logs\\\")\\n    else:\\n        path = user_cache_dir(appname, appauthor, version); version=False\\n        if opinion:\\n            path = os.path.join(path, \\\"log\\\")\\n    if version:\\n        path = os.path.join(path, version)\\n    return path\\n\\n\\nclass AppDirs(object):\\n    \\\"\\\"\\\"Convenience wrapper for getting application dirs.\\\"\\\"\\\"\\n    def __init__(self, appname, appauthor, version=None, roaming=False):\\n        self.appname = appname\\n        self.appauthor = appauthor\\n        self.version = version\\n        self.roaming = roaming\\n    @property\\n    def user_data_dir(self):\\n        return user_data_dir(self.appname, self.appauthor,\\n            version=self.version, roaming=self.roaming)\\n    @property\\n    def site_data_dir(self):\\n        return site_data_dir(self.appname, self.appauthor,\\n            version=self.version)\\n    @property\\n    def user_cache_dir(self):\\n        return user_cache_dir(self.appname, self.appauthor,\\n            version=self.version)\\n    @property\\n    def user_log_dir(self):\\n        return user_log_dir(self.appname, self.appauthor,\\n            version=self.version)\\n\\n\\n\\n\\n#---- internal support stuff\\n\\ndef _get_win_folder_from_registry(csidl_name):\\n    \\\"\\\"\\\"This is a fallback technique at best. I'm not sure if using the\\n    registry for this guarantees us the correct answer for all CSIDL_*\\n    names.\\n    \\\"\\\"\\\"\\n    import _winreg\\n\\n    shell_folder_name = {\\n        \\\"CSIDL_APPDATA\\\": \\\"AppData\\\",\\n        \\\"CSIDL_COMMON_APPDATA\\\": \\\"Common AppData\\\",\\n        \\\"CSIDL_LOCAL_APPDATA\\\": \\\"Local AppData\\\",\\n    }[csidl_name]\\n\\n    key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER,\\n        r\\\"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Explorer\\\\Shell Folders\\\")\\n    dir, type = _winreg.QueryValueEx(key, shell_folder_name)\\n    return dir\\n\\ndef _get_win_folder_with_pywin32(csidl_name):\\n    from win32com.shell import shellcon, shell\\n    dir = shell.SHGetFolderPath(0, getattr(shellcon, csidl_name), 0, 0)\\n    # Try to make this a unicode path because SHGetFolderPath does\\n    # not return unicode strings when there is unicode data in the\\n    # path.\\n    try:\\n        dir = unicode(dir)\\n\\n        # Downgrade to short path name if have highbit chars. See\\n        # <http://bugs.activestate.com/show_bug.cgi?id=85099>.\\n        has_high_char = False\\n        for c in dir:\\n            if ord(c) > 255:\\n                has_high_char = True\\n                break\\n        if has_high_char:\\n            try:\\n                import win32api\\n                dir = win32api.GetShortPathName(dir)\\n            except ImportError:\\n                pass\\n    except UnicodeError:\\n        pass\\n    return dir\\n\\ndef _get_win_folder_with_ctypes(csidl_name):\\n    import ctypes\\n\\n    csidl_const = {\\n        \\\"CSIDL_APPDATA\\\": 26,\\n        \\\"CSIDL_COMMON_APPDATA\\\": 35,\\n        \\\"CSIDL_LOCAL_APPDATA\\\": 28,\\n    }[csidl_name]\\n\\n    buf = ctypes.create_unicode_buffer(1024)\\n    ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf)\\n\\n    # Downgrade to short path name if have highbit chars. See\\n    # <http://bugs.activestate.com/show_bug.cgi?id=85099>.\\n    has_high_char = False\\n    for c in buf:\\n        if ord(c) > 255:\\n            has_high_char = True\\n            break\\n    if has_high_char:\\n        buf2 = ctypes.create_unicode_buffer(1024)\\n        if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024):\\n            buf = buf2\\n\\n    return buf.value\\n\\nif sys.platform == \\\"win32\\\":\\n    try:\\n        import win32com.shell\\n        _get_win_folder = _get_win_folder_with_pywin32\\n    except ImportError:\\n        try:\\n            import ctypes\\n            _get_win_folder = _get_win_folder_with_ctypes\\n        except ImportError:\\n            _get_win_folder = _get_win_folder_from_registry\\n\\n\\n\\n#---- self test code\\n\\nif __name__ == \\\"__main__\\\":\\n    appname = \\\"MyApp\\\"\\n    appauthor = \\\"MyCompany\\\"\\n\\n    props = (\\\"user_data_dir\\\", \\\"site_data_dir\\\", \\\"user_cache_dir\\\",\\n        \\\"user_log_dir\\\")\\n\\n    print(\\\"-- app dirs (without optional 'version')\\\")\\n    dirs = AppDirs(appname, appauthor, version=\\\"1.0\\\")\\n    for prop in props:\\n        print(\\\"%s: %s\\\" % (prop, getattr(dirs, prop)))\\n\\n    print(\\\"\\\\n-- app dirs (with optional 'version')\\\")\\n    dirs = AppDirs(appname, appauthor)\\n    for prop in props:\\n        print(\\\"%s: %s\\\" % (prop, getattr(dirs, prop)))\\n\\n\\nApache License\\n                           Version 2.0, January 2004\\n                        http://www.apache.org/licenses/\\n\\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\\n\\n   1. Definitions.\\n\\n      \\\"License\\\" shall mean the terms and conditions for use, reproduction,\\n      and distribution as defined by Sections 1 through 9 of this document.\\n\\n      \\\"Licensor\\\" shall mean the copyright owner or entity authorized by\\n      the copyright owner that is granting the License.\\n\\n      \\\"Legal Entity\\\" shall mean the union of the acting entity and all\\n      other entities that control, are controlled by, or are under common\\n      control with that entity. For the purposes of this definition,\\n      \\\"control\\\" means (i) the power, direct or indirect, to cause the\\n      direction or management of such entity, whether by contract or\\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\\n      outstanding shares, or (iii) beneficial ownership of such entity.\\n\\n      \\\"You\\\" (or \\\"Your\\\") shall mean an individual or Legal Entity\\n      exercising permissions granted by this License.\\n\\n      \\\"Source\\\" form shall mean the preferred form for making modifications,\\n      including but not limited to software source code, documentation\\n      source, and configuration files.\\n\\n      \\\"Object\\\" form shall mean any form resulting from mechanical\\n      transformation or translation of a Source form, including but\\n      not limited to compiled object code, generated documentation,\\n      and conversions to other media types.\\n\\n      \\\"Work\\\" shall mean the work of authorship, whether in Source or\\n      Object form, made available under the License, as indicated by a\\n      copyright notice that is included in or attached to the work\\n      (an example is provided in the Appendix below).\\n\\n      \\\"Derivative Works\\\" shall mean any work, whether in Source or Object\\n      form, that is based on (or derived from) the Work and for which the\\n      editorial revisions, annotations, elaborations, or other modifications\\n      represent, as a whole, an original work of authorship. For the purposes\\n      of this License, Derivative Works shall not include works that remain\\n      separable from, or merely link (or bind by name) to the interfaces of,\\n      the Work and Derivative Works thereof.\\n\\n      \\\"Contribution\\\" shall mean any work of authorship, including\\n      the original version of the Work and any modifications or additions\\n      to that Work or Derivative Works thereof, that is intentionally\\n      submitted to Licensor for inclusion in the Work by the copyright owner\\n      or by an individual or Legal Entity authorized to submit on behalf of\\n      the copyright owner. For the purposes of this definition, \\\"submitted\\\"\\n      means any form of electronic, verbal, or written communication sent\\n      to the Licensor or its representatives, including but not limited to\\n      communication on electronic mailing lists, source code control systems,\\n      and issue tracking systems that are managed by, or on behalf of, the\\n      Licensor for the purpose of discussing and improving the Work, but\\n      excluding communication that is conspicuously marked or otherwise\\n      designated in writing by the copyright owner as \\\"Not a Contribution.\\\"\\n\\n      \\\"Contributor\\\" shall mean Licensor and any individual or Legal Entity\\n      on behalf of whom a Contribution has been received by Licensor and\\n      subsequently incorporated within the Work.\\n\\n   2. Grant of Copyright License. Subject to the terms and conditions of\\n      this License, each Contributor hereby grants to You a perpetual,\\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\\n      copyright license to reproduce, prepare Derivative Works of,\\n      publicly display, publicly perform, sublicense, and distribute the\\n      Work and such Derivative Works in Source or Object form.\\n\\n   3. Grant of Patent License. Subject to the terms and conditions of\\n      this License, each Contributor hereby grants to You a perpetual,\\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\\n      (except as stated in this section) patent license to make, have made,\\n      use, offer to sell, sell, import, and otherwise transfer the Work,\\n      where such license applies only to those patent claims licensable\\n      by such Contributor that are necessarily infringed by their\\n      Contribution(s) alone or by combination of their Contribution(s)\\n      with the Work to which such Contribution(s) was submitted. If You\\n      institute patent litigation against any entity (including a\\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\\n      or a Contribution incorporated within the Work constitutes direct\\n      or contributory patent infringement, then any patent licenses\\n      granted to You under this License for that Work shall terminate\\n      as of the date such litigation is filed.\\n\\n   4. Redistribution. You may reproduce and distribute copies of the\\n      Work or Derivative Works thereof in any medium, with or without\\n      modifications, and in Source or Object form, provided that You\\n      meet the following conditions:\\n\\n      (a) You must give any other recipients of the Work or\\n          Derivative Works a copy of this License; and\\n\\n      (b) You must cause any modified files to carry prominent notices\\n          stating that You changed the files; and\\n\\n      (c) You must retain, in the Source form of any Derivative Works\\n          that You distribute, all copyright, patent, trademark, and\\n          attribution notices from the Source form of the Work,\\n          excluding those notices that do not pertain to any part of\\n          the Derivative Works; and\\n\\n      (d) If the Work includes a \\\"NOTICE\\\" text file as part of its\\n          distribution, then any Derivative Works that You distribute must\\n          include a readable copy of the attribution notices contained\\n          within such NOTICE file, excluding those notices that do not\\n          pertain to any part of the Derivative Works, in at least one\\n          of the following places: within a NOTICE text file distributed\\n          as part of the Derivative Works; within the Source form or\\n          documentation, if provided along with the Derivative Works; or,\\n          within a display generated by the Derivative Works, if and\\n          wherever such third-party notices normally appear. The contents\\n          of the NOTICE file are for informational purposes only and\\n          do not modify the License. You may add Your own attribution\\n          notices within Derivative Works that You distribute, alongside\\n          or as an addendum to the NOTICE text from the Work, provided\\n          that such additional attribution notices cannot be construed\\n          as modifying the License.\\n\\n      You may add Your own copyright statement to Your modifications and\\n      may provide additional or different license terms and conditions\\n      for use, reproduction, or distribution of Your modifications, or\\n      for any such Derivative Works as a whole, provided Your use,\\n      reproduction, and distribution of the Work otherwise complies with\\n      the conditions stated in this License.\\n\\n   5. Submission of Contributions. Unless You explicitly state otherwise,\\n      any Contribution intentionally submitted for inclusion in the Work\\n      by You to the Licensor shall be under the terms and conditions of\\n      this License, without any additional terms or conditions.\\n      Notwithstanding the above, nothing herein shall supersede or modify\\n      the terms of any separate license agreement you may have executed\\n      with Licensor regarding such Contributions.\\n\\n   6. Trademarks. This License does not grant permission to use the trade\\n      names, trademarks, service marks, or product names of the Licensor,\\n      except as required for reasonable and customary use in describing the\\n      origin of the Work and reproducing the content of the NOTICE file.\\n\\n   7. Disclaimer of Warranty. Unless required by applicable law or\\n      agreed to in writing, Licensor provides the Work (and each\\n      Contributor provides its Contributions) on an \\\"AS IS\\\" BASIS,\\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\\n      implied, including, without limitation, any warranties or conditions\\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\\n      PARTICULAR PURPOSE. You are solely responsible for determining the\\n      appropriateness of using or redistributing the Work and assume any\\n      risks associated with Your exercise of permissions under this License.\\n\\n   8. Limitation of Liability. In no event and under no legal theory,\\n      whether in tort (including negligence), contract, or otherwise,\\n      unless required by applicable law (such as deliberate and grossly\\n      negligent acts) or agreed to in writing, shall any Contributor be\\n      liable to You for damages, including any direct, indirect, special,\\n      incidental, or consequential damages of any character arising as a\\n      result of this License or out of the use or inability to use the\\n      Work (including but not limited to damages for loss of goodwill,\\n      work stoppage, computer failure or malfunction, or any and all\\n      other commercial damages or losses), even if such Contributor\\n      has been advised of the possibility of such damages.\\n\\n   9. Accepting Warranty or Additional Liability. While redistributing\\n      the Work or Derivative Works thereof, You may choose to offer,\\n      and charge a fee for, acceptance of support, warranty, indemnity,\\n      or other liability obligations and/or rights consistent with this\\n      License. However, in accepting such obligations, You may act only\\n      on Your own behalf and on Your sole responsibility, not on behalf\\n      of any other Contributor, and only if You agree to indemnify,\\n      defend, and hold each Contributor harmless for any liability\\n      incurred by, or claims asserted against, such Contributor by reason\\n      of your accepting any such warranty or additional liability.\\n\\n   END OF TERMS AND CONDITIONS\\n\\n   APPENDIX: How to apply the Apache License to your work.\\n\\n      To apply the Apache License to your work, attach the following\\n      boilerplate notice, with the fields enclosed by brackets \\\"{}\\\"\\n      replaced with your own identifying information. (Don't include\\n      the brackets!)  The text should be enclosed in the appropriate\\n      comment syntax for the file format. We also recommend that a\\n      file or class name and description of purpose be included on the\\n      same \\\"printed page\\\" as the copyright notice for easier\\n      identification within third-party archives.\\n\\n   Copyright {yyyy} {name of copyright owner}\\n\\n   Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n   you may not use this file except in compliance with the License.\\n   You may obtain a copy of the License at\\n\\n       http://www.apache.org/licenses/LICENSE-2.0\\n\\n   Unless required by applicable law or agreed to in writing, software\\n   distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n   See the License for the specific language governing permissions and\\n   limitations under the License.\\n\\n\\n\\n# -*- coding: utf-8 -*-\\n\\\"\\\"\\\"\\nConda's pure-python dependencies will be\\n`vendored <http://stackoverflow.com/questions/26217488/what-is-vendoring>`_\\nuntil conda 5.0 when conda will be isolated in its own private environment.\\n\\nIntroduction of dependencies for the 4.x series is discussed in\\nhttps://github.com/conda/conda/issues/2825.\\n\\\"\\\"\\\"\\n\\n\\nappdirs==1.2.0\\npy-cpuinfo==9.0.0\\ndistro==1.0.4\\nfrozendict==1.2\\n\\n\\n# Copyright 2015,2016 Nir Cohen\\n#\\n# Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n# you may not use this file except in compliance with the License.\\n# You may obtain a copy of the License at\\n#\\n# http://www.apache.org/licenses/LICENSE-2.0\\n#\\n# Unless required by applicable law or agreed to in writing, software\\n# distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n# See the License for the specific language governing permissions and\\n# limitations under the License.\\n\\n\\\"\\\"\\\"\\nThe ``distro`` package (``distro`` stands for Linux Distribution) provides\\ninformation about the Linux distribution it runs on, such as a reliable\\nmachine-readable distro ID, or version information.\\n\\nIt is a renewed alternative implementation for Python's original\\n:py:func:`platform.linux_distribution` function, but it provides much more\\nfunctionality. An alternative implementation became necessary because Python\\n3.5 deprecated this function, and Python 3.7 is expected to remove it\\naltogether. Its predecessor function :py:func:`platform.dist` was already\\ndeprecated since Python 2.6 and is also expected to be removed in Python 3.7.\\nStill, there are many cases in which access to Linux distribution information\\nis needed. See `Python issue 1322 <https://bugs.python.org/issue1322>`_ for\\nmore information.\\n\\\"\\\"\\\"\\nfrom ..deprecations import deprecated\\ndeprecated.module(\\\"24.3\\\", \\\"24.9\\\", addendum=\\\"Use `distro` instead.\\\")\\n\\nimport os\\nimport re\\nimport sys\\nimport json\\nimport shlex\\nimport logging\\nimport argparse\\nimport subprocess\\n\\n\\nif not sys.platform.startswith('linux'):\\n    raise ImportError('Unsupported platform: {0}'.format(sys.platform))\\n\\n_UNIXCONFDIR = os.environ.get('UNIXCONFDIR', '/etc')\\n_OS_RELEASE_BASENAME = 'os-release'\\n\\n#: Translation table for normalizing the \\\"ID\\\" attribute defined in os-release\\n#: files, for use by the :func:`distro.id` method.\\n#:\\n#: * Key: Value as defined in the os-release file, translated to lower case,\\n#:   with blanks translated to underscores.\\n#:\\n#: * Value: Normalized value.\\nNORMALIZED_OS_ID = {}\\n\\n#: Translation table for normalizing the \\\"Distributor ID\\\" attribute returned by\\n#: the lsb_release command, for use by the :func:`distro.id` method.\\n#:\\n#: * Key: Value as returned by the lsb_release command, translated to lower\\n#:   case, with blanks translated to underscores.\\n#:\\n#: * Value: Normalized value.\\nNORMALIZED_LSB_ID = {\\n    'enterpriseenterprise': 'oracle',  # Oracle Enterprise Linux\\n    'redhatenterpriseworkstation': 'rhel',  # RHEL 6, 7 Workstation\\n    'redhatenterpriseserver': 'rhel',  # RHEL 6, 7 Server\\n}\\n\\n#: Translation table for normalizing the distro ID derived from the file name\\n#: of distro release files, for use by the :func:`distro.id` method.\\n#:\\n#: * Key: Value as derived from the file name of a distro release file,\\n#:   translated to lower case, with blanks translated to underscores.\\n#:\\n#: * Value: Normalized value.\\nNORMALIZED_DISTRO_ID = {\\n    'redhat': 'rhel',  # RHEL 6.x, 7.x\\n}\\n\\n# Pattern for content of distro release file (reversed)\\n_DISTRO_RELEASE_CONTENT_REVERSED_PATTERN = re.compile(\\n    r'(?:[^)]*\\\\)(.*)\\\\()? *(?:STL )?([\\\\d.+\\\\-a-z]*\\\\d) *(?:esaeler *)?(.+)')\\n\\n# Pattern for base file name of distro release file\\n_DISTRO_RELEASE_BASENAME_PATTERN = re.compile(\\n    r'(\\\\w+)[-_](release|version)$')\\n\\n# Base file names to be ignored when searching for distro release file\\n_DISTRO_RELEASE_IGNORE_BASENAMES = (\\n    'debian_version',\\n    'lsb-release',\\n    'oem-release',\\n    _OS_RELEASE_BASENAME,\\n    'system-release'\\n)\\n\\n\\ndef linux_distribution(full_distribution_name=True):\\n    \\\"\\\"\\\"\\n    Return information about the current Linux distribution as a tuple\\n    ``(id_name, version, codename)`` with items as follows:\\n\\n    * ``id_name``:  If *full_distribution_name* is false, the result of\\n      :func:`distro.id`. Otherwise, the result of :func:`distro.name`.\\n\\n    * ``version``:  The result of :func:`distro.version`.\\n\\n    * ``codename``:  The result of :func:`distro.codename`.\\n\\n    The interface of this function is compatible with the original\\n    :py:func:`platform.linux_distribution` function, supporting a subset of\\n    its parameters.\\n\\n    The data it returns may not exactly be the same, because it uses more data\\n    sources than the original function, and that may lead to different data if\\n    the Linux distribution is not consistent across multiple data sources it\\n    provides (there are indeed such distributions ...).\\n\\n    Another reason for differences is the fact that the :func:`distro.id`\\n    method normalizes the distro ID string to a reliable machine-readable value\\n    for a number of popular Linux distributions.\\n    \\\"\\\"\\\"\\n    return _distro.linux_distribution(full_distribution_name)\\n\\n\\ndef id():\\n    \\\"\\\"\\\"\\n    Return the distro ID of the current Linux distribution, as a\\n    machine-readable string.\\n\\n    For a number of Linux distributions, the returned distro ID value is\\n    *reliable*, in the sense that it is documented and that it does not change\\n    across releases of the distribution.\\n\\n    This package maintains the following reliable distro ID values:\\n\\n    ==============  =========================================\\n    Distro ID       Distribution\\n    ==============  =========================================\\n    \\\"ubuntu\\\"        Ubuntu\\n    \\\"debian\\\"        Debian\\n    \\\"rhel\\\"          RedHat Enterprise Linux\\n    \\\"centos\\\"        CentOS\\n    \\\"fedora\\\"        Fedora\\n    \\\"sles\\\"          SUSE Linux Enterprise Server\\n    \\\"opensuse\\\"      openSUSE\\n    \\\"amazon\\\"        Amazon Linux\\n    \\\"arch\\\"          Arch Linux\\n    \\\"cloudlinux\\\"    CloudLinux OS\\n    \\\"exherbo\\\"       Exherbo Linux\\n    \\\"gentoo\\\"        GenToo Linux\\n    \\\"ibm_powerkvm\\\"  IBM PowerKVM\\n    \\\"kvmibm\\\"        KVM for IBM z Systems\\n    \\\"linuxmint\\\"     Linux Mint\\n    \\\"mageia\\\"        Mageia\\n    \\\"mandriva\\\"      Mandriva Linux\\n    \\\"parallels\\\"     Parallels\\n    \\\"pidora\\\"        Pidora\\n    \\\"raspbian\\\"      Raspbian\\n    \\\"oracle\\\"        Oracle Linux (and Oracle Enterprise Linux)\\n    \\\"scientific\\\"    Scientific Linux\\n    \\\"slackware\\\"     Slackware\\n    \\\"xenserver\\\"     XenServer\\n    ==============  =========================================\\n\\n    If you have a need to get distros for reliable IDs added into this set,\\n    or if you find that the :func:`distro.id` function returns a different\\n    distro ID for one of the listed distros, please create an issue in the\\n    `distro issue tracker`_.\\n\\n    **Lookup hierarchy and transformations:**\\n\\n    First, the ID is obtained from the following sources, in the specified\\n    order. The first available and non-empty value is used:\\n\\n    * the value of the \\\"ID\\\" attribute of the os-release file,\\n\\n    * the value of the \\\"Distributor ID\\\" attribute returned by the lsb_release\\n      command,\\n\\n    * the first part of the file name of the distro release file,\\n\\n    The so determined ID value then passes the following transformations,\\n    before it is returned by this method:\\n\\n    * it is translated to lower case,\\n\\n    * blanks (which should not be there anyway) are translated to underscores,\\n\\n    * a normalization of the ID is performed, based upon\\n      `normalization tables`_. The purpose of this normalization is to ensure\\n      that the ID is as reliable as possible, even across incompatible changes\\n      in the Linux distributions. A common reason for an incompatible change is\\n      the addition of an os-release file, or the addition of the lsb_release\\n      command, with ID values that differ from what was previously determined\\n      from the distro release file name.\\n    \\\"\\\"\\\"\\n    return _distro.id()\\n\\n\\ndef name(pretty=False):\\n    \\\"\\\"\\\"\\n    Return the name of the current Linux distribution, as a human-readable\\n    string.\\n\\n    If *pretty* is false, the name is returned without version or codename.\\n    (e.g. \\\"CentOS Linux\\\")\\n\\n    If *pretty* is true, the version and codename are appended.\\n    (e.g. \\\"CentOS Linux 7.1.1503 (Core)\\\")\\n\\n    **Lookup hierarchy:**\\n\\n    The name is obtained from the following sources, in the specified order.\\n    The first available and non-empty value is used:\\n\\n    * If *pretty* is false:\\n\\n      - the value of the \\\"NAME\\\" attribute of the os-release file,\\n\\n      - the value of the \\\"Distributor ID\\\" attribute returned by the lsb_release\\n        command,\\n\\n      - the value of the \\\"<name>\\\" field of the distro release file.\\n\\n    * If *pretty* is true:\\n\\n      - the value of the \\\"PRETTY_NAME\\\" attribute of the os-release file,\\n\\n      - the value of the \\\"Description\\\" attribute returned by the lsb_release\\n        command,\\n\\n      - the value of the \\\"<name>\\\" field of the distro release file, appended\\n        with the value of the pretty version (\\\"<version_id>\\\" and \\\"<codename>\\\"\\n        fields) of the distro release file, if available.\\n    \\\"\\\"\\\"\\n    return _distro.name(pretty)\\n\\n\\ndef version(pretty=False, best=False):\\n    \\\"\\\"\\\"\\n    Return the version of the current Linux distribution, as a human-readable\\n    string.\\n\\n    If *pretty* is false, the version is returned without codename (e.g.\\n    \\\"7.0\\\").\\n\\n    If *pretty* is true, the codename in parenthesis is appended, if the\\n    codename is non-empty (e.g. \\\"7.0 (Maipo)\\\").\\n\\n    Some distributions provide version numbers with different precisions in\\n    the different sources of distribution information. Examining the different\\n    sources in a fixed priority order does not always yield the most precise\\n    version (e.g. for Debian 8.2, or CentOS 7.1).\\n\\n    The *best* parameter can be used to control the approach for the returned\\n    version:\\n\\n    If *best* is false, the first non-empty version number in priority order of\\n    the examined sources is returned.\\n\\n    If *best* is true, the most precise version number out of all examined\\n    sources is returned.\\n\\n    **Lookup hierarchy:**\\n\\n    In all cases, the version number is obtained from the following sources.\\n    If *best* is false, this order represents the priority order:\\n\\n    * the value of the \\\"VERSION_ID\\\" attribute of the os-release file,\\n    * the value of the \\\"Release\\\" attribute returned by the lsb_release\\n      command,\\n    * the version number parsed from the \\\"<version_id>\\\" field of the first line\\n      of the distro release file,\\n    * the version number parsed from the \\\"PRETTY_NAME\\\" attribute of the\\n      os-release file, if it follows the format of the distro release files.\\n    * the version number parsed from the \\\"Description\\\" attribute returned by\\n      the lsb_release command, if it follows the format of the distro release\\n      files.\\n    \\\"\\\"\\\"\\n    return _distro.version(pretty, best)\\n\\n\\ndef version_parts(best=False):\\n    \\\"\\\"\\\"\\n    Return the version of the current Linux distribution as a tuple\\n    ``(major, minor, build_number)`` with items as follows:\\n\\n    * ``major``:  The result of :func:`distro.major_version`.\\n\\n    * ``minor``:  The result of :func:`distro.minor_version`.\\n\\n    * ``build_number``:  The result of :func:`distro.build_number`.\\n\\n    For a description of the *best* parameter, see the :func:`distro.version`\\n    method.\\n    \\\"\\\"\\\"\\n    return _distro.version_parts(best)\\n\\n\\ndef major_version(best=False):\\n    \\\"\\\"\\\"\\n    Return the major version of the current Linux distribution, as a string,\\n    if provided.\\n    Otherwise, the empty string is returned. The major version is the first\\n    part of the dot-separated version string.\\n\\n    For a description of the *best* parameter, see the :func:`distro.version`\\n    method.\\n    \\\"\\\"\\\"\\n    return _distro.major_version(best)\\n\\n\\ndef minor_version(best=False):\\n    \\\"\\\"\\\"\\n    Return the minor version of the current Linux distribution, as a string,\\n    if provided.\\n    Otherwise, the empty string is returned. The minor version is the second\\n    part of the dot-separated version string.\\n\\n    For a description of the *best* parameter, see the :func:`distro.version`\\n    method.\\n    \\\"\\\"\\\"\\n    return _distro.minor_version(best)\\n\\n\\ndef build_number(best=False):\\n    \\\"\\\"\\\"\\n    Return the build number of the current Linux distribution, as a string,\\n    if provided.\\n    Otherwise, the empty string is returned. The build number is the third part\\n    of the dot-separated version string.\\n\\n    For a description of the *best* parameter, see the :func:`distro.version`\\n    method.\\n    \\\"\\\"\\\"\\n    return _distro.build_number(best)\\n\\n\\ndef like():\\n    \\\"\\\"\\\"\\n    Return a space-separated list of distro IDs of distributions that are\\n    closely related to the current Linux distribution in regards to packaging\\n    and programming interfaces, for example distributions the current\\n    distribution is a derivative from.\\n\\n    **Lookup hierarchy:**\\n\\n    This information item is only provided by the os-release file.\\n    For details, see the description of the \\\"ID_LIKE\\\" attribute in the\\n    `os-release man page\\n    <http://www.freedesktop.org/software/systemd/man/os-release.html>`_.\\n    \\\"\\\"\\\"\\n    return _distro.like()\\n\\n\\ndef codename():\\n    \\\"\\\"\\\"\\n    Return the codename for the release of the current Linux distribution,\\n    as a string.\\n\\n    If the distribution does not have a codename, an empty string is returned.\\n\\n    Note that the returned codename is not always really a codename. For\\n    example, openSUSE returns \\\"x86_64\\\". This function does not handle such\\n    cases in any special way and just returns the string it finds, if any.\\n\\n    **Lookup hierarchy:**\\n\\n    * the codename within the \\\"VERSION\\\" attribute of the os-release file, if\\n      provided,\\n\\n    * the value of the \\\"Codename\\\" attribute returned by the lsb_release\\n      command,\\n\\n    * the value of the \\\"<codename>\\\" field of the distro release file.\\n    \\\"\\\"\\\"\\n    return _distro.codename()\\n\\n\\ndef info(pretty=False, best=False):\\n    \\\"\\\"\\\"\\n    Return certain machine-readable information items about the current Linux\\n    distribution in a dictionary, as shown in the following example:\\n\\n    .. sourcecode:: python\\n\\n        {\\n            'id': 'rhel',\\n            'version': '7.0',\\n            'version_parts': {\\n                'major': '7',\\n                'minor': '0',\\n                'build_number': ''\\n            },\\n            'like': 'fedora',\\n            'codename': 'Maipo'\\n        }\\n\\n    The dictionary structure and keys are always the same, regardless of which\\n    information items are available in the underlying data sources. The values\\n    for the various keys are as follows:\\n\\n    * ``id``:  The result of :func:`distro.id`.\\n\\n    * ``version``:  The result of :func:`distro.version`.\\n\\n    * ``version_parts -> major``:  The result of :func:`distro.major_version`.\\n\\n    * ``version_parts -> minor``:  The result of :func:`distro.minor_version`.\\n\\n    * ``version_parts -> build_number``:  The result of\\n      :func:`distro.build_number`.\\n\\n    * ``like``:  The result of :func:`distro.like`.\\n\\n    * ``codename``:  The result of :func:`distro.codename`.\\n\\n    For a description of the *pretty* and *best* parameters, see the\\n    :func:`distro.version` method.\\n    \\\"\\\"\\\"\\n    return _distro.info(pretty, best)\\n\\n\\ndef os_release_info():\\n    \\\"\\\"\\\"\\n    Return a dictionary containing key-value pairs for the information items\\n    from the os-release file data source of the current Linux distribution.\\n\\n    See `os-release file`_ for details about these information items.\\n    \\\"\\\"\\\"\\n    return _distro.os_release_info()\\n\\n\\ndef lsb_release_info():\\n    \\\"\\\"\\\"\\n    Return a dictionary containing key-value pairs for the information items\\n    from the lsb_release command data source of the current Linux distribution.\\n\\n    See `lsb_release command output`_ for details about these information\\n    items.\\n    \\\"\\\"\\\"\\n    return _distro.lsb_release_info()\\n\\n\\ndef distro_release_info():\\n    \\\"\\\"\\\"\\n    Return a dictionary containing key-value pairs for the information items\\n    from the distro release file data source of the current Linux distribution.\\n\\n    See `distro release file`_ for details about these information items.\\n    \\\"\\\"\\\"\\n    return _distro.distro_release_info()\\n\\n\\ndef os_release_attr(attribute):\\n    \\\"\\\"\\\"\\n    Return a single named information item from the os-release file data source\\n    of the current Linux distribution.\\n\\n    Parameters:\\n\\n    * ``attribute`` (string): Key of the information item.\\n\\n    Returns:\\n\\n    * (string): Value of the information item, if the item exists.\\n      The empty string, if the item does not exist.\\n\\n    See `os-release file`_ for details about these information items.\\n    \\\"\\\"\\\"\\n    return _distro.os_release_attr(attribute)\\n\\n\\ndef lsb_release_attr(attribute):\\n    \\\"\\\"\\\"\\n    Return a single named information item from the lsb_release command output\\n    data source of the current Linux distribution.\\n\\n    Parameters:\\n\\n    * ``attribute`` (string): Key of the information item.\\n\\n    Returns:\\n\\n    * (string): Value of the information item, if the item exists.\\n      The empty string, if the item does not exist.\\n\\n    See `lsb_release command output`_ for details about these information\\n    items.\\n    \\\"\\\"\\\"\\n    return _distro.lsb_release_attr(attribute)\\n\\n\\ndef distro_release_attr(attribute):\\n    \\\"\\\"\\\"\\n    Return a single named information item from the distro release file\\n    data source of the current Linux distribution.\\n\\n    Parameters:\\n\\n    * ``attribute`` (string): Key of the information item.\\n\\n    Returns:\\n\\n    * (string): Value of the information item, if the item exists.\\n      The empty string, if the item does not exist.\\n\\n    See `distro release file`_ for details about these information items.\\n    \\\"\\\"\\\"\\n    return _distro.distro_release_attr(attribute)\\n\\n\\nclass LinuxDistribution(object):\\n    \\\"\\\"\\\"\\n    Provides information about a Linux distribution.\\n\\n    This package creates a private module-global instance of this class with\\n    default initialization arguments, that is used by the\\n    `consolidated accessor functions`_ and `single source accessor functions`_.\\n    By using default initialization arguments, that module-global instance\\n    returns data about the current Linux distribution (i.e. the distro this\\n    package runs on).\\n\\n    Normally, it is not necessary to create additional instances of this class.\\n    However, in situations where control is needed over the exact data sources\\n    that are used, instances of this class can be created with a specific\\n    distro release file, or a specific os-release file, or without invoking the\\n    lsb_release command.\\n    \\\"\\\"\\\"\\n\\n    def __init__(self,\\n                 include_lsb=True,\\n                 os_release_file='',\\n                 distro_release_file=''):\\n        \\\"\\\"\\\"\\n        The initialization method of this class gathers information from the\\n        available data sources, and stores that in private instance attributes.\\n        Subsequent access to the information items uses these private instance\\n        attributes, so that the data sources are read only once.\\n\\n        Parameters:\\n\\n        * ``include_lsb`` (bool): Controls whether the\\n          `lsb_release command output`_ is included as a data source.\\n\\n          If the lsb_release command is not available in the program execution\\n          path, the data source for the lsb_release command will be empty.\\n\\n        * ``os_release_file`` (string): The path name of the\\n          `os-release file`_ that is to be used as a data source.\\n\\n          An empty string (the default) will cause the default path name to\\n          be used (see `os-release file`_ for details).\\n\\n          If the specified or defaulted os-release file does not exist, the\\n          data source for the os-release file will be empty.\\n\\n        * ``distro_release_file`` (string): The path name of the\\n          `distro release file`_ that is to be used as a data source.\\n\\n          An empty string (the default) will cause a default search algorithm\\n          to be used (see `distro release file`_ for details).\\n\\n          If the specified distro release file does not exist, or if no default\\n          distro release file can be found, the data source for the distro\\n          release file will be empty.\\n\\n        Public instance attributes:\\n\\n        * ``os_release_file`` (string): The path name of the\\n          `os-release file`_ that is actually used as a data source. The\\n          empty string if no distro release file is used as a data source.\\n\\n        * ``distro_release_file`` (string): The path name of the\\n          `distro release file`_ that is actually used as a data source. The\\n          empty string if no distro release file is used as a data source.\\n\\n        Raises:\\n\\n        * :py:exc:`IOError`: Some I/O issue with an os-release file or distro\\n          release file.\\n\\n        * :py:exc:`subprocess.CalledProcessError`: The lsb_release command had\\n          some issue (other than not being available in the program execution\\n          path).\\n\\n        * :py:exc:`UnicodeError`: A data source has unexpected characters or\\n          uses an unexpected encoding.\\n        \\\"\\\"\\\"\\n        self.os_release_file = os_release_file or \\\\\\n            os.path.join(_UNIXCONFDIR, _OS_RELEASE_BASENAME)\\n        self.distro_release_file = distro_release_file or ''  # updated later\\n        self._os_release_info = self._get_os_release_info()\\n        self._lsb_release_info = self._get_lsb_release_info() \\\\\\n            if include_lsb else {}\\n        self._distro_release_info = self._get_distro_release_info()\\n\\n    def __repr__(self):\\n        \\\"\\\"\\\"Return repr of all info\\n        \\\"\\\"\\\"\\n        return \\\\\\n            \\\"LinuxDistribution(\\\" \\\\\\n            \\\"os_release_file={0!r}, \\\" \\\\\\n            \\\"distro_release_file={1!r}, \\\" \\\\\\n            \\\"_os_release_info={2!r}, \\\" \\\\\\n            \\\"_lsb_release_info={3!r}, \\\" \\\\\\n            \\\"_distro_release_info={4!r})\\\".format(\\n                self.os_release_file,\\n                self.distro_release_file,\\n                self._os_release_info,\\n                self._lsb_release_info,\\n                self._distro_release_info)\\n\\n    def linux_distribution(self, full_distribution_name=True):\\n        \\\"\\\"\\\"\\n        Return information about the Linux distribution that is compatible\\n        with Python's :func:`platform.linux_distribution`, supporting a subset\\n        of its parameters.\\n\\n        For details, see :func:`distro.linux_distribution`.\\n        \\\"\\\"\\\"\\n        return (\\n            self.name() if full_distribution_name else self.id(),\\n            self.version(),\\n            self.codename()\\n        )\\n\\n    def id(self):\\n        \\\"\\\"\\\"Return the distro ID of the Linux distribution, as a string.\\n\\n        For details, see :func:`distro.id`.\\n        \\\"\\\"\\\"\\n        def normalize(distro_id, table):\\n            distro_id = distro_id.lower().replace(' ', '_')\\n            return table.get(distro_id, distro_id)\\n\\n        distro_id = self.os_release_attr('id')\\n        if distro_id:\\n            return normalize(distro_id, NORMALIZED_OS_ID)\\n\\n        distro_id = self.lsb_release_attr('distributor_id')\\n        if distro_id:\\n            return normalize(distro_id, NORMALIZED_LSB_ID)\\n\\n        distro_id = self.distro_release_attr('id')\\n        if distro_id:\\n            return normalize(distro_id, NORMALIZED_DISTRO_ID)\\n\\n        return ''\\n\\n    def name(self, pretty=False):\\n        \\\"\\\"\\\"\\n        Return the name of the Linux distribution, as a string.\\n\\n        For details, see :func:`distro.name`.\\n        \\\"\\\"\\\"\\n        name = self.os_release_attr('name') \\\\\\n            or self.lsb_release_attr('distributor_id') \\\\\\n            or self.distro_release_attr('name')\\n        if pretty:\\n            name = self.os_release_attr('pretty_name') \\\\\\n                or self.lsb_release_attr('description')\\n            if not name:\\n                name = self.distro_release_attr('name')\\n                version = self.version(pretty=True)\\n                if version:\\n                    name = name + ' ' + version\\n        return name or ''\\n\\n    def version(self, pretty=False, best=False):\\n        \\\"\\\"\\\"\\n        Return the version of the Linux distribution, as a string.\\n\\n        For details, see :func:`distro.version`.\\n        \\\"\\\"\\\"\\n        versions = [\\n            self.os_release_attr('version_id'),\\n            self.lsb_release_attr('release'),\\n            self.distro_release_attr('version_id'),\\n            self._parse_distro_release_content(\\n                self.os_release_attr('pretty_name')).get('version_id', ''),\\n            self._parse_distro_release_content(\\n                self.lsb_release_attr('description')).get('version_id', '')\\n        ]\\n        version = ''\\n        if best:\\n            # This algorithm uses the last version in priority order that has\\n            # the best precision. If the versions are not in conflict, that\\n            # does not matter; otherwise, using the last one instead of the\\n            # first one might be considered a surprise.\\n            for v in versions:\\n                if v.count(\\\".\\\") > version.count(\\\".\\\") or version == '':\\n                    version = v\\n        else:\\n            for v in versions:\\n                if v != '':\\n                    version = v\\n                    break\\n        if pretty and version and self.codename():\\n            version = u'{0} ({1})'.format(version, self.codename())\\n        return version\\n\\n    def version_parts(self, best=False):\\n        \\\"\\\"\\\"\\n        Return the version of the Linux distribution, as a tuple of version\\n        numbers.\\n\\n        For details, see :func:`distro.version_parts`.\\n        \\\"\\\"\\\"\\n        version_str = self.version(best=best)\\n        if version_str:\\n            version_regex = re.compile(r'(\\\\d+)\\\\.?(\\\\d+)?\\\\.?(\\\\d+)?')\\n            matches = version_regex.match(version_str)\\n            if matches:\\n                major, minor, build_number = matches.groups()\\n                return major, minor or '', build_number or ''\\n        return '', '', ''\\n\\n    def major_version(self, best=False):\\n        \\\"\\\"\\\"\\n        Return the major version number of the current distribution.\\n\\n        For details, see :func:`distro.major_version`.\\n        \\\"\\\"\\\"\\n        return self.version_parts(best)[0]\\n\\n    def minor_version(self, best=False):\\n        \\\"\\\"\\\"\\n        Return the minor version number of the Linux distribution.\\n\\n        For details, see :func:`distro.minor_version`.\\n        \\\"\\\"\\\"\\n        return self.version_parts(best)[1]\\n\\n    def build_number(self, best=False):\\n        \\\"\\\"\\\"\\n        Return the build number of the Linux distribution.\\n\\n        For details, see :func:`distro.build_number`.\\n        \\\"\\\"\\\"\\n        return self.version_parts(best)[2]\\n\\n    def like(self):\\n        \\\"\\\"\\\"\\n        Return the IDs of distributions that are like the Linux distribution.\\n\\n        For details, see :func:`distro.like`.\\n        \\\"\\\"\\\"\\n        return self.os_release_attr('id_like') or ''\\n\\n    def codename(self):\\n        \\\"\\\"\\\"\\n        Return the codename of the Linux distribution.\\n\\n        For details, see :func:`distro.codename`.\\n        \\\"\\\"\\\"\\n        return self.os_release_attr('codename') \\\\\\n            or self.lsb_release_attr('codename') \\\\\\n            or self.distro_release_attr('codename') \\\\\\n            or ''\\n\\n    def info(self, pretty=False, best=False):\\n        \\\"\\\"\\\"\\n        Return certain machine-readable information about the Linux\\n        distribution.\\n\\n        For details, see :func:`distro.info`.\\n        \\\"\\\"\\\"\\n        return dict(\\n            id=self.id(),\\n            version=self.version(pretty, best),\\n            version_parts=dict(\\n                major=self.major_version(best),\\n                minor=self.minor_version(best),\\n                build_number=self.build_number(best)\\n            ),\\n            like=self.like(),\\n            codename=self.codename(),\\n        )\\n\\n    def os_release_info(self):\\n        \\\"\\\"\\\"\\n        Return a dictionary containing key-value pairs for the information\\n        items from the os-release file data source of the Linux distribution.\\n\\n        For details, see :func:`distro.os_release_info`.\\n        \\\"\\\"\\\"\\n        return self._os_release_info\\n\\n    def lsb_release_info(self):\\n        \\\"\\\"\\\"\\n        Return a dictionary containing key-value pairs for the information\\n        items from the lsb_release command data source of the Linux\\n        distribution.\\n\\n        For details, see :func:`distro.lsb_release_info`.\\n        \\\"\\\"\\\"\\n        return self._lsb_release_info\\n\\n    def distro_release_info(self):\\n        \\\"\\\"\\\"\\n        Return a dictionary containing key-value pairs for the information\\n        items from the distro release file data source of the Linux\\n        distribution.\\n\\n        For details, see :func:`distro.distro_release_info`.\\n        \\\"\\\"\\\"\\n        return self._distro_release_info\\n\\n    def os_release_attr(self, attribute):\\n        \\\"\\\"\\\"\\n        Return a single named information item from the os-release file data\\n        source of the Linux distribution.\\n\\n        For details, see :func:`distro.os_release_attr`.\\n        \\\"\\\"\\\"\\n        return self._os_release_info.get(attribute, '')\\n\\n    def lsb_release_attr(self, attribute):\\n        \\\"\\\"\\\"\\n        Return a single named information item from the lsb_release command\\n        output data source of the Linux distribution.\\n\\n        For details, see :func:`distro.lsb_release_attr`.\\n        \\\"\\\"\\\"\\n        return self._lsb_release_info.get(attribute, '')\\n\\n    def distro_release_attr(self, attribute):\\n        \\\"\\\"\\\"\\n        Return a single named information item from the distro release file\\n        data source of the Linux distribution.\\n\\n        For details, see :func:`distro.distro_release_attr`.\\n        \\\"\\\"\\\"\\n        return self._distro_release_info.get(attribute, '')\\n\\n    def _get_os_release_info(self):\\n        \\\"\\\"\\\"\\n        Get the information items from the specified os-release file.\\n\\n        Returns:\\n            A dictionary containing all information items.\\n        \\\"\\\"\\\"\\n        if os.path.isfile(self.os_release_file):\\n            with open(self.os_release_file) as release_file:\\n                return self._parse_os_release_content(release_file)\\n        return {}\\n\\n    @staticmethod\\n    def _parse_os_release_content(lines):\\n        \\\"\\\"\\\"\\n        Parse the lines of an os-release file.\\n\\n        Parameters:\\n\\n        * lines: Iterable through the lines in the os-release file.\\n                 Each line must be a unicode string or a UTF-8 encoded byte\\n                 string.\\n\\n        Returns:\\n            A dictionary containing all information items.\\n        \\\"\\\"\\\"\\n        props = {}\\n        lexer = shlex.shlex(lines, posix=True)\\n        lexer.whitespace_split = True\\n\\n        # The shlex module defines its `wordchars` variable using literals,\\n        # making it dependent on the encoding of the Python source file.\\n        # In Python 2.6 and 2.7, the shlex source file is encoded in\\n        # 'iso-8859-1', and the `wordchars` variable is defined as a byte\\n        # string. This causes a UnicodeDecodeError to be raised when the\\n        # parsed content is a unicode object. The following fix resolves that\\n        # (... but it should be fixed in shlex...):\\n        if sys.version_info[0] == 2 and isinstance(lexer.wordchars, bytes):\\n            lexer.wordchars = lexer.wordchars.decode('iso-8859-1')\\n\\n        tokens = list(lexer)\\n        for token in tokens:\\n            # At this point, all shell-like parsing has been done (i.e.\\n            # comments processed, quotes and backslash escape sequences\\n            # processed, multi-line values assembled, trailing newlines\\n            # stripped, etc.), so the tokens are now either:\\n            # * variable assignments: var=value\\n            # * commands or their arguments (not allowed in os-release)\\n            if '=' in token:\\n                k, v = token.split('=', 1)\\n                if isinstance(v, bytes):\\n                    v = v.decode('utf-8')\\n                props[k.lower()] = v\\n                if k == 'VERSION':\\n                    # this handles cases in which the codename is in\\n                    # the `(CODENAME)` (rhel, centos, fedora) format\\n                    # or in the `, CODENAME` format (Ubuntu).\\n                    codename = re.search(r'(\\\\(\\\\D+\\\\))|,(\\\\s+)?\\\\D+', v)\\n                    if codename:\\n                        codename = codename.group()\\n                        codename = codename.strip('()')\\n                        codename = codename.strip(',')\\n                        codename = codename.strip()\\n                        # codename appears within paranthese.\\n                        props['codename'] = codename\\n                    else:\\n                        props['codename'] = ''\\n            else:\\n                # Ignore any tokens that are not variable assignments\\n                pass\\n        return props\\n\\n    def _get_lsb_release_info(self):\\n        \\\"\\\"\\\"\\n        Get the information items from the lsb_release command output.\\n\\n        Returns:\\n            A dictionary containing all information items.\\n        \\\"\\\"\\\"\\n        cmd = 'lsb_release -a'\\n        # conda customization: On Ubuntu 17.10, lsb_release calls the\\n        # system Python and it will not find our custom sysconfigdata\\n        env = os.environ.copy()\\n        if '_PYTHON_SYSCONFIGDATA_NAME' in env:\\n            del env['_PYTHON_SYSCONFIGDATA_NAME']\\n        process = subprocess.Popen(\\n            cmd,\\n            shell=True,\\n            stdout=subprocess.PIPE,\\n            stderr=subprocess.PIPE,\\n            env=env)\\n        stdout, stderr = process.communicate()\\n        stdout, stderr = stdout.decode('utf-8'), stderr.decode('utf-8')\\n        code = process.returncode\\n        if code == 0:\\n            content = stdout.splitlines()\\n            return self._parse_lsb_release_content(content)\\n        elif code == 127:  # Command not found\\n            return {}\\n        else:\\n            if sys.version_info[:2] >= (3, 5):\\n                raise subprocess.CalledProcessError(code, cmd, stdout, stderr)\\n            elif sys.version_info[:2] >= (2, 7):\\n                raise subprocess.CalledProcessError(code, cmd, stdout)\\n            elif sys.version_info[:2] == (2, 6):\\n                raise subprocess.CalledProcessError(code, cmd)\\n\\n    @staticmethod\\n    def _parse_lsb_release_content(lines):\\n        \\\"\\\"\\\"\\n        Parse the output of the lsb_release command.\\n\\n        Parameters:\\n\\n        * lines: Iterable through the lines of the lsb_release output.\\n                 Each line must be a unicode string or a UTF-8 encoded byte\\n                 string.\\n\\n        Returns:\\n            A dictionary containing all information items.\\n        \\\"\\\"\\\"\\n        props = {}\\n        for line in lines:\\n            line = line.decode('utf-8') if isinstance(line, bytes) else line\\n            kv = line.strip('\\\\n').split(':', 1)\\n            if len(kv) != 2:\\n                # Ignore lines without colon.\\n                continue\\n            k, v = kv\\n            props.update({k.replace(' ', '_').lower(): v.strip()})\\n        return props\\n\\n    def _get_distro_release_info(self):\\n        \\\"\\\"\\\"\\n        Get the information items from the specified distro release file.\\n\\n        Returns:\\n            A dictionary containing all information items.\\n        \\\"\\\"\\\"\\n        if self.distro_release_file:\\n            # If it was specified, we use it and parse what we can, even if\\n            # its file name or content does not match the expected pattern.\\n            distro_info = self._parse_distro_release_file(\\n                self.distro_release_file)\\n            basename = os.path.basename(self.distro_release_file)\\n            # The file name pattern for user-specified distro release files\\n            # is somewhat more tolerant (compared to when searching for the\\n            # file), because we want to use what was specified as best as\\n            # possible.\\n            match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename)\\n            if match:\\n                distro_info['id'] = match.group(1)\\n            return distro_info\\n        else:\\n            try:\\n                basenames = os.listdir(_UNIXCONFDIR)\\n                # We sort for repeatability in cases where there are multiple\\n                # distro specific files; e.g. CentOS, Oracle, Enterprise all\\n                # containing `redhat-release` on top of their own.\\n                basenames.sort()\\n            except OSError:\\n                # This may occur when /etc is not readable but we can't be\\n                # sure about the *-release files. Check common entries of\\n                # /etc for information. If they turn out to not be there the\\n                # error is handled in `_parse_distro_release_file()`.\\n                basenames = ['SuSE-release',\\n                             'arch-release',\\n                             'base-release',\\n                             'centos-release',\\n                             'fedora-release',\\n                             'gentoo-release',\\n                             'mageia-release',\\n                             'manjaro-release',\\n                             'oracle-release',\\n                             'redhat-release',\\n                             'sl-release',\\n                             'slackware-version']\\n            for basename in basenames:\\n                if basename in _DISTRO_RELEASE_IGNORE_BASENAMES:\\n                    continue\\n                match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename)\\n                if match:\\n                    filepath = os.path.join(_UNIXCONFDIR, basename)\\n                    distro_info = self._parse_distro_release_file(filepath)\\n                    if 'name' in distro_info:\\n                        # The name is always present if the pattern matches\\n                        self.distro_release_file = filepath\\n                        distro_info['id'] = match.group(1)\\n                        return distro_info\\n            return {}\\n\\n    def _parse_distro_release_file(self, filepath):\\n        \\\"\\\"\\\"\\n        Parse a distro release file.\\n\\n        Parameters:\\n\\n        * filepath: Path name of the distro release file.\\n\\n        Returns:\\n            A dictionary containing all information items.\\n        \\\"\\\"\\\"\\n        try:\\n            with open(filepath) as fp:\\n                # Only parse the first line. For instance, on SLES there\\n                # are multiple lines. We don't want them...\\n                return self._parse_distro_release_content(fp.readline())\\n        except (OSError, IOError):\\n            # Ignore not being able to read a specific, seemingly version\\n            # related file.\\n            # See https://github.com/nir0s/distro/issues/162\\n            return {}\\n\\n    @staticmethod\\n    def _parse_distro_release_content(line):\\n        \\\"\\\"\\\"\\n        Parse a line from a distro release file.\\n\\n        Parameters:\\n        * line: Line from the distro release file. Must be a unicode string\\n                or a UTF-8 encoded byte string.\\n\\n        Returns:\\n            A dictionary containing all information items.\\n        \\\"\\\"\\\"\\n        if isinstance(line, bytes):\\n            line = line.decode('utf-8')\\n        matches = _DISTRO_RELEASE_CONTENT_REVERSED_PATTERN.match(\\n            line.strip()[::-1])\\n        distro_info = {}\\n        if matches:\\n            # regexp ensures non-None\\n            distro_info['name'] = matches.group(3)[::-1]\\n            if matches.group(2):\\n                distro_info['version_id'] = matches.group(2)[::-1]\\n            if matches.group(1):\\n                distro_info['codename'] = matches.group(1)[::-1]\\n        elif line:\\n            distro_info['name'] = line.strip()\\n        return distro_info\\n\\n\\n_distro = LinuxDistribution()\\n\\n\\ndef main():\\n    logger = logging.getLogger(__name__)\\n    logger.setLevel(logging.DEBUG)\\n    logger.addHandler(logging.StreamHandler(sys.stdout))\\n\\n    parser = argparse.ArgumentParser(description=\\\"Linux distro info tool\\\")\\n    parser.add_argument(\\n        '--json',\\n        '-j',\\n        help=\\\"Output in machine readable format\\\",\\n        action=\\\"store_true\\\")\\n    args = parser.parse_args()\\n\\n    if args.json:\\n        logger.info(json.dumps(info(), indent=4, sort_keys=True))\\n    else:\\n        logger.info('Name: %s', name(pretty=True))\\n        distribution_version = version(pretty=True)\\n        logger.info('Version: %s', distribution_version)\\n        distribution_codename = codename()\\n        logger.info('Codename: %s', distribution_codename)\\n\\n\\nif __name__ == '__main__':\\n    main()\\n\\n\\n# This is the MIT license\\n\\nCopyright (c) 2010 ActiveState Software Inc.\\n\\nPermission is hereby granted, free of charge, to any person obtaining a\\ncopy of this software and associated documentation files (the\\n\\\"Software\\\"), to deal in the Software without restriction, including\\nwithout limitation the rights to use, copy, modify, merge, publish,\\ndistribute, sublicense, and/or sell copies of the Software, and to\\npermit persons to whom the Software is furnished to do so, subject to\\nthe following conditions:\\n\\nThe above copyright notice and this permission notice shall be included\\nin all copies or substantial portions of the Software.\\n\\nTHE SOFTWARE IS PROVIDED \\\"AS IS\\\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\\nOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\\n\\n\\n\\n#!/usr/bin/env python\\n# -*- coding: UTF-8 -*-\\n\\n# Copyright (c) 2014-2022 Matthew Brennan Jones <matthew.brennan.jones@gmail.com>\\n# Py-cpuinfo gets CPU info with pure Python\\n# It uses the MIT License\\n# It is hosted at: https://github.com/workhorsy/py-cpuinfo\\n#\\n# Permission is hereby granted, free of charge, to any person obtaining\\n# a copy of this software and associated documentation files (the\\n# \\\"Software\\\"), to deal in the Software without restriction, including\\n# without limitation the rights to use, copy, modify, merge, publish,\\n# distribute, sublicense, and/or sell copies of the Software, and to\\n# permit persons to whom the Software is furnished to do so, subject to\\n# the following conditions:\\n#\\n# The above copyright notice and this permission notice shall be included\\n# in all copies or substantial portions of the Software.\\n#\\n# THE SOFTWARE IS PROVIDED \\\"AS IS\\\", WITHOUT WARRANTY OF ANY KIND,\\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\\n# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\\n# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\\n# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\\n# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\\n\\nCPUINFO_VERSION = (9, 0, 0)\\nCPUINFO_VERSION_STRING = '.'.join([str(n) for n in CPUINFO_VERSION])\\n\\nimport os, sys\\nimport platform\\nimport multiprocessing\\nimport ctypes\\n\\n\\nCAN_CALL_CPUID_IN_SUBPROCESS = True\\n\\ng_trace = None\\n\\n\\nclass Trace(object):\\n\\tdef __init__(self, is_active, is_stored_in_string):\\n\\t\\tself._is_active = is_active\\n\\t\\tif not self._is_active:\\n\\t\\t\\treturn\\n\\n\\t\\tfrom datetime import datetime\\n\\t\\tfrom io import StringIO\\n\\n\\t\\tif is_stored_in_string:\\n\\t\\t\\tself._output = StringIO()\\n\\t\\telse:\\n\\t\\t\\tdate = datetime.now().strftime(\\\"%Y-%m-%d_%H-%M-%S-%f\\\")\\n\\t\\t\\tself._output = open('cpuinfo_trace_{0}.trace'.format(date), 'w')\\n\\n\\t\\tself._stdout = StringIO()\\n\\t\\tself._stderr = StringIO()\\n\\t\\tself._err = None\\n\\n\\tdef header(self, msg):\\n\\t\\tif not self._is_active: return\\n\\n\\t\\tfrom inspect import stack\\n\\t\\tframe = stack()[1]\\n\\t\\tfile = frame[1]\\n\\t\\tline = frame[2]\\n\\t\\tself._output.write(\\\"{0} ({1} {2})\\\\n\\\".format(msg, file, line))\\n\\t\\tself._output.flush()\\n\\n\\tdef success(self):\\n\\t\\tif not self._is_active: return\\n\\n\\t\\tfrom inspect import stack\\n\\t\\tframe = stack()[1]\\n\\t\\tfile = frame[1]\\n\\t\\tline = frame[2]\\n\\n\\t\\tself._output.write(\\\"Success ... ({0} {1})\\\\n\\\\n\\\".format(file, line))\\n\\t\\tself._output.flush()\\n\\n\\tdef fail(self, msg):\\n\\t\\tif not self._is_active: return\\n\\n\\t\\tfrom inspect import stack\\n\\t\\tframe = stack()[1]\\n\\t\\tfile = frame[1]\\n\\t\\tline = frame[2]\\n\\n\\t\\tif isinstance(msg, str):\\n\\t\\t\\tmsg = ''.join(['\\\\t' + line for line in msg.split('\\\\n')]) + '\\\\n'\\n\\n\\t\\t\\tself._output.write(msg)\\n\\t\\t\\tself._output.write(\\\"Failed ... ({0} {1})\\\\n\\\\n\\\".format(file, line))\\n\\t\\t\\tself._output.flush()\\n\\t\\telif isinstance(msg, Exception):\\n\\t\\t\\tfrom traceback import format_exc\\n\\t\\t\\terr_string = format_exc()\\n\\t\\t\\tself._output.write(\\\"\\\\tFailed ... ({0} {1})\\\\n\\\".format(file, line))\\n\\t\\t\\tself._output.write(''.join(['\\\\t\\\\t{0}\\\\n'.format(n) for n in err_string.split('\\\\n')]) + '\\\\n')\\n\\t\\t\\tself._output.flush()\\n\\n\\tdef command_header(self, msg):\\n\\t\\tif not self._is_active: return\\n\\n\\t\\tfrom inspect import stack\\n\\t\\tframe = stack()[3]\\n\\t\\tfile = frame[1]\\n\\t\\tline = frame[2]\\n\\t\\tself._output.write(\\\"\\\\t{0} ({1} {2})\\\\n\\\".format(msg, file, line))\\n\\t\\tself._output.flush()\\n\\n\\tdef command_output(self, msg, output):\\n\\t\\tif not self._is_active: return\\n\\n\\t\\tself._output.write(\\\"\\\\t\\\\t{0}\\\\n\\\".format(msg))\\n\\t\\tself._output.write(''.join(['\\\\t\\\\t\\\\t{0}\\\\n'.format(n) for n in output.split('\\\\n')]) + '\\\\n')\\n\\t\\tself._output.flush()\\n\\n\\tdef keys(self, keys, info, new_info):\\n\\t\\tif not self._is_active: return\\n\\n\\t\\tfrom inspect import stack\\n\\t\\tframe = stack()[2]\\n\\t\\tfile = frame[1]\\n\\t\\tline = frame[2]\\n\\n\\t\\t# List updated keys\\n\\t\\tself._output.write(\\\"\\\\tChanged keys ({0} {1})\\\\n\\\".format(file, line))\\n\\t\\tchanged_keys = [key for key in keys if key in info and key in new_info and info[key] != new_info[key]]\\n\\t\\tif changed_keys:\\n\\t\\t\\tfor key in changed_keys:\\n\\t\\t\\t\\tself._output.write('\\\\t\\\\t{0}: {1} to {2}\\\\n'.format(key, info[key], new_info[key]))\\n\\t\\telse:\\n\\t\\t\\tself._output.write('\\\\t\\\\tNone\\\\n')\\n\\n\\t\\t# List new keys\\n\\t\\tself._output.write(\\\"\\\\tNew keys ({0} {1})\\\\n\\\".format(file, line))\\n\\t\\tnew_keys = [key for key in keys if key in new_info and key not in info]\\n\\t\\tif new_keys:\\n\\t\\t\\tfor key in new_keys:\\n\\t\\t\\t\\tself._output.write('\\\\t\\\\t{0}: {1}\\\\n'.format(key, new_info[key]))\\n\\t\\telse:\\n\\t\\t\\tself._output.write('\\\\t\\\\tNone\\\\n')\\n\\n\\t\\tself._output.write('\\\\n')\\n\\t\\tself._output.flush()\\n\\n\\tdef write(self, msg):\\n\\t\\tif not self._is_active: return\\n\\n\\t\\tself._output.write(msg + '\\\\n')\\n\\t\\tself._output.flush()\\n\\n\\tdef to_dict(self, info, is_fail):\\n\\t\\treturn {\\n\\t\\t'output' : self._output.getvalue(),\\n\\t\\t'stdout' : self._stdout.getvalue(),\\n\\t\\t'stderr' : self._stderr.getvalue(),\\n\\t\\t'info' : info,\\n\\t\\t'err' : self._err,\\n\\t\\t'is_fail' : is_fail\\n\\t\\t}\\n\\nclass DataSource(object):\\n\\tbits = platform.architecture()[0]\\n\\tcpu_count = multiprocessing.cpu_count()\\n\\tis_windows = platform.system().lower() == 'windows'\\n\\tarch_string_raw = platform.machine()\\n\\tuname_string_raw = platform.uname()[5]\\n\\tcan_cpuid = True\\n\\n\\t@staticmethod\\n\\tdef has_proc_cpuinfo():\\n\\t\\treturn os.path.exists('/proc/cpuinfo')\\n\\n\\t@staticmethod\\n\\tdef has_dmesg():\\n\\t\\treturn len(_program_paths('dmesg')) > 0\\n\\n\\t@staticmethod\\n\\tdef has_var_run_dmesg_boot():\\n\\t\\tuname = platform.system().strip().strip('\\\"').strip(\\\"'\\\").strip().lower()\\n\\t\\treturn 'linux' in uname and os.path.exists('/var/run/dmesg.boot')\\n\\n\\t@staticmethod\\n\\tdef has_cpufreq_info():\\n\\t\\treturn len(_program_paths('cpufreq-info')) > 0\\n\\n\\t@staticmethod\\n\\tdef has_sestatus():\\n\\t\\treturn len(_program_paths('sestatus')) > 0\\n\\n\\t@staticmethod\\n\\tdef has_sysctl():\\n\\t\\treturn len(_program_paths('sysctl')) > 0\\n\\n\\t@staticmethod\\n\\tdef has_isainfo():\\n\\t\\treturn len(_program_paths('isainfo')) > 0\\n\\n\\t@staticmethod\\n\\tdef has_kstat():\\n\\t\\treturn len(_program_paths('kstat')) > 0\\n\\n\\t@staticmethod\\n\\tdef has_sysinfo():\\n\\t\\tuname = platform.system().strip().strip('\\\"').strip(\\\"'\\\").strip().lower()\\n\\t\\tis_beos = 'beos' in uname or 'haiku' in uname\\n\\t\\treturn is_beos and len(_program_paths('sysinfo')) > 0\\n\\n\\t@staticmethod\\n\\tdef has_lscpu():\\n\\t\\treturn len(_program_paths('lscpu')) > 0\\n\\n\\t@staticmethod\\n\\tdef has_ibm_pa_features():\\n\\t\\treturn len(_program_paths('lsprop')) > 0\\n\\n\\t@staticmethod\\n\\tdef has_wmic():\\n\\t\\treturncode, output = _run_and_get_stdout(['wmic', 'os', 'get', 'Version'])\\n\\t\\treturn returncode == 0 and len(output) > 0\\n\\n\\t@staticmethod\\n\\tdef cat_proc_cpuinfo():\\n\\t\\treturn _run_and_get_stdout(['cat', '/proc/cpuinfo'])\\n\\n\\t@staticmethod\\n\\tdef cpufreq_info():\\n\\t\\treturn _run_and_get_stdout(['cpufreq-info'])\\n\\n\\t@staticmethod\\n\\tdef sestatus_b():\\n\\t\\treturn _run_and_get_stdout(['sestatus', '-b'])\\n\\n\\t@staticmethod\\n\\tdef dmesg_a():\\n\\t\\treturn _run_and_get_stdout(['dmesg', '-a'])\\n\\n\\t@staticmethod\\n\\tdef cat_var_run_dmesg_boot():\\n\\t\\treturn _run_and_get_stdout(['cat', '/var/run/dmesg.boot'])\\n\\n\\t@staticmethod\\n\\tdef sysctl_machdep_cpu_hw_cpufrequency():\\n\\t\\treturn _run_and_get_stdout(['sysctl', 'machdep.cpu', 'hw.cpufrequency'])\\n\\n\\t@staticmethod\\n\\tdef isainfo_vb():\\n\\t\\treturn _run_and_get_stdout(['isainfo', '-vb'])\\n\\n\\t@staticmethod\\n\\tdef kstat_m_cpu_info():\\n\\t\\treturn _run_and_get_stdout(['kstat', '-m', 'cpu_info'])\\n\\n\\t@staticmethod\\n\\tdef sysinfo_cpu():\\n\\t\\treturn _run_and_get_stdout(['sysinfo', '-cpu'])\\n\\n\\t@staticmethod\\n\\tdef lscpu():\\n\\t\\treturn _run_and_get_stdout(['lscpu'])\\n\\n\\t@staticmethod\\n\\tdef ibm_pa_features():\\n\\t\\timport glob\\n\\n\\t\\tibm_features = glob.glob('/proc/device-tree/cpus/*/ibm,pa-features')\\n\\t\\tif ibm_features:\\n\\t\\t\\treturn _run_and_get_stdout(['lsprop', ibm_features[0]])\\n\\n\\t@staticmethod\\n\\tdef wmic_cpu():\\n\\t\\treturn _run_and_get_stdout(['wmic', 'cpu', 'get', 'Name,CurrentClockSpeed,L2CacheSize,L3CacheSize,Description,Caption,Manufacturer', '/format:list'])\\n\\n\\t@staticmethod\\n\\tdef winreg_processor_brand():\\n\\t\\tprocessor_brand = _read_windows_registry_key(r\\\"Hardware\\\\Description\\\\System\\\\CentralProcessor\\\\0\\\", \\\"ProcessorNameString\\\")\\n\\t\\treturn processor_brand.strip()\\n\\n\\t@staticmethod\\n\\tdef winreg_vendor_id_raw():\\n\\t\\tvendor_id_raw = _read_windows_registry_key(r\\\"Hardware\\\\Description\\\\System\\\\CentralProcessor\\\\0\\\", \\\"VendorIdentifier\\\")\\n\\t\\treturn vendor_id_raw\\n\\n\\t@staticmethod\\n\\tdef winreg_arch_string_raw():\\n\\t\\tarch_string_raw = _read_windows_registry_key(r\\\"SYSTEM\\\\CurrentControlSet\\\\Control\\\\Session Manager\\\\Environment\\\", \\\"PROCESSOR_ARCHITECTURE\\\")\\n\\t\\treturn arch_string_raw\\n\\n\\t@staticmethod\\n\\tdef winreg_hz_actual():\\n\\t\\thz_actual = _read_windows_registry_key(r\\\"Hardware\\\\Description\\\\System\\\\CentralProcessor\\\\0\\\", \\\"~Mhz\\\")\\n\\t\\thz_actual = _to_decimal_string(hz_actual)\\n\\t\\treturn hz_actual\\n\\n\\t@staticmethod\\n\\tdef winreg_feature_bits():\\n\\t\\tfeature_bits = _read_windows_registry_key(r\\\"Hardware\\\\Description\\\\System\\\\CentralProcessor\\\\0\\\", \\\"FeatureSet\\\")\\n\\t\\treturn feature_bits\\n\\n\\ndef _program_paths(program_name):\\n\\tpaths = []\\n\\texts = filter(None, os.environ.get('PATHEXT', '').split(os.pathsep))\\n\\tfor p in os.environ['PATH'].split(os.pathsep):\\n\\t\\tp = os.path.join(p, program_name)\\n\\t\\tif os.access(p, os.X_OK):\\n\\t\\t\\tpaths.append(p)\\n\\t\\tfor e in exts:\\n\\t\\t\\tpext = p + e\\n\\t\\t\\tif os.access(pext, os.X_OK):\\n\\t\\t\\t\\tpaths.append(pext)\\n\\treturn paths\\n\\ndef _run_and_get_stdout(command, pipe_command=None):\\n\\tfrom subprocess import Popen, PIPE\\n\\n\\tg_trace.command_header('Running command \\\"' + ' '.join(command) + '\\\" ...')\\n\\n\\t# Run the command normally\\n\\tif not pipe_command:\\n\\t\\tp1 = Popen(command, stdout=PIPE, stderr=PIPE, stdin=PIPE)\\n\\t# Run the command and pipe it into another command\\n\\telse:\\n\\t\\tp2 = Popen(command, stdout=PIPE, stderr=PIPE, stdin=PIPE)\\n\\t\\tp1 = Popen(pipe_command, stdin=p2.stdout, stdout=PIPE, stderr=PIPE)\\n\\t\\tp2.stdout.close()\\n\\n\\t# Get the stdout and stderr\\n\\tstdout_output, stderr_output = p1.communicate()\\n\\tstdout_output = stdout_output.decode(encoding='UTF-8')\\n\\tstderr_output = stderr_output.decode(encoding='UTF-8')\\n\\n\\t# Send the result to the logger\\n\\tg_trace.command_output('return code:', str(p1.returncode))\\n\\tg_trace.command_output('stdout:', stdout_output)\\n\\n\\t# Return the return code and stdout\\n\\treturn p1.returncode, stdout_output\\n\\ndef _read_windows_registry_key(key_name, field_name):\\n\\tg_trace.command_header('Reading Registry key \\\"{0}\\\" field \\\"{1}\\\" ...'.format(key_name, field_name))\\n\\n\\ttry:\\n\\t\\timport _winreg as winreg\\n\\texcept ImportError as err:\\n\\t\\ttry:\\n\\t\\t\\timport winreg\\n\\t\\texcept ImportError as err:\\n\\t\\t\\tpass\\n\\n\\tkey = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, key_name)\\n\\tvalue = winreg.QueryValueEx(key, field_name)[0]\\n\\twinreg.CloseKey(key)\\n\\tg_trace.command_output('value:', str(value))\\n\\treturn value\\n\\n# Make sure we are running on a supported system\\ndef _check_arch():\\n\\tarch, bits = _parse_arch(DataSource.arch_string_raw)\\n\\tif not arch in ['X86_32', 'X86_64', 'ARM_7', 'ARM_8',\\n\\t               'PPC_64', 'S390X', 'MIPS_32', 'MIPS_64',\\n\\t\\t\\t\\t   \\\"RISCV_32\\\", \\\"RISCV_64\\\"]:\\n\\t\\traise Exception(\\\"py-cpuinfo currently only works on X86 \\\"\\n\\t\\t                \\\"and some ARM/PPC/S390X/MIPS/RISCV CPUs.\\\")\\n\\ndef _obj_to_b64(thing):\\n\\timport pickle\\n\\timport base64\\n\\n\\ta = thing\\n\\tb = pickle.dumps(a)\\n\\tc = base64.b64encode(b)\\n\\td = c.decode('utf8')\\n\\treturn d\\n\\ndef _b64_to_obj(thing):\\n\\timport pickle\\n\\timport base64\\n\\n\\ttry:\\n\\t\\ta = base64.b64decode(thing)\\n\\t\\tb = pickle.loads(a)\\n\\t\\treturn b\\n\\texcept Exception:\\n\\t\\treturn {}\\n\\ndef _utf_to_str(input):\\n\\tif isinstance(input, list):\\n\\t\\treturn [_utf_to_str(element) for element in input]\\n\\telif isinstance(input, dict):\\n\\t\\treturn {_utf_to_str(key): _utf_to_str(value)\\n\\t\\t\\tfor key, value in input.items()}\\n\\telse:\\n\\t\\treturn input\\n\\ndef _copy_new_fields(info, new_info):\\n\\tkeys = [\\n\\t\\t'vendor_id_raw', 'hardware_raw', 'brand_raw', 'hz_advertised_friendly', 'hz_actual_friendly',\\n\\t\\t'hz_advertised', 'hz_actual', 'arch', 'bits', 'count',\\n\\t\\t'arch_string_raw', 'uname_string_raw',\\n\\t\\t'l2_cache_size', 'l2_cache_line_size', 'l2_cache_associativity',\\n\\t\\t'stepping', 'model', 'family',\\n\\t\\t'processor_type', 'flags',\\n\\t\\t'l3_cache_size', 'l1_data_cache_size', 'l1_instruction_cache_size'\\n\\t]\\n\\n\\tg_trace.keys(keys, info, new_info)\\n\\n\\t# Update the keys with new values\\n\\tfor key in keys:\\n\\t\\tif new_info.get(key, None) and not info.get(key, None):\\n\\t\\t\\tinfo[key] = new_info[key]\\n\\t\\telif key == 'flags' and new_info.get('flags'):\\n\\t\\t\\tfor f in new_info['flags']:\\n\\t\\t\\t\\tif f not in info['flags']: info['flags'].append(f)\\n\\t\\t\\tinfo['flags'].sort()\\n\\ndef _get_field_actual(cant_be_number, raw_string, field_names):\\n\\tfor line in raw_string.splitlines():\\n\\t\\tfor field_name in field_names:\\n\\t\\t\\tfield_name = field_name.lower()\\n\\t\\t\\tif ':' in line:\\n\\t\\t\\t\\tleft, right = line.split(':', 1)\\n\\t\\t\\t\\tleft = left.strip().lower()\\n\\t\\t\\t\\tright = right.strip()\\n\\t\\t\\t\\tif left == field_name and len(right) > 0:\\n\\t\\t\\t\\t\\tif cant_be_number:\\n\\t\\t\\t\\t\\t\\tif not right.isdigit():\\n\\t\\t\\t\\t\\t\\t\\treturn right\\n\\t\\t\\t\\t\\telse:\\n\\t\\t\\t\\t\\t\\treturn right\\n\\n\\treturn None\\n\\ndef _get_field(cant_be_number, raw_string, convert_to, default_value, *field_names):\\n\\tretval = _get_field_actual(cant_be_number, raw_string, field_names)\\n\\n\\t# Convert the return value\\n\\tif retval and convert_to:\\n\\t\\ttry:\\n\\t\\t\\tretval = convert_to(retval)\\n\\t\\texcept Exception:\\n\\t\\t\\tretval = default_value\\n\\n\\t# Return the default if there is no return value\\n\\tif retval is None:\\n\\t\\tretval = default_value\\n\\n\\treturn retval\\n\\ndef _to_decimal_string(ticks):\\n\\ttry:\\n\\t\\t# Convert to string\\n\\t\\tticks = '{0}'.format(ticks)\\n\\t\\t# Sometimes ',' is used as a decimal separator\\n\\t\\tticks = ticks.replace(',', '.')\\n\\n\\t\\t# Strip off non numbers and decimal places\\n\\t\\tticks = \\\"\\\".join(n for n in ticks if n.isdigit() or n=='.').strip()\\n\\t\\tif ticks == '':\\n\\t\\t\\tticks = '0'\\n\\n\\t\\t# Add decimal if missing\\n\\t\\tif '.' not in ticks:\\n\\t\\t\\tticks = '{0}.0'.format(ticks)\\n\\n\\t\\t# Remove trailing zeros\\n\\t\\tticks = ticks.rstrip('0')\\n\\n\\t\\t# Add one trailing zero for empty right side\\n\\t\\tif ticks.endswith('.'):\\n\\t\\t\\tticks = '{0}0'.format(ticks)\\n\\n\\t\\t# Make sure the number can be converted to a float\\n\\t\\tticks = float(ticks)\\n\\t\\tticks = '{0}'.format(ticks)\\n\\t\\treturn ticks\\n\\texcept Exception:\\n\\t\\treturn '0.0'\\n\\ndef _hz_short_to_full(ticks, scale):\\n\\ttry:\\n\\t\\t# Make sure the number can be converted to a float\\n\\t\\tticks = float(ticks)\\n\\t\\tticks = '{0}'.format(ticks)\\n\\n\\t\\t# Scale the numbers\\n\\t\\thz = ticks.lstrip('0')\\n\\t\\told_index = hz.index('.')\\n\\t\\thz = hz.replace('.', '')\\n\\t\\thz = hz.ljust(scale + old_index+1, '0')\\n\\t\\tnew_index = old_index + scale\\n\\t\\thz = '{0}.{1}'.format(hz[:new_index], hz[new_index:])\\n\\t\\tleft, right = hz.split('.')\\n\\t\\tleft, right = int(left), int(right)\\n\\t\\treturn (left, right)\\n\\texcept Exception:\\n\\t\\treturn (0, 0)\\n\\ndef _hz_friendly_to_full(hz_string):\\n\\ttry:\\n\\t\\thz_string = hz_string.strip().lower()\\n\\t\\thz, scale = (None, None)\\n\\n\\t\\tif hz_string.endswith('ghz'):\\n\\t\\t\\tscale = 9\\n\\t\\telif hz_string.endswith('mhz'):\\n\\t\\t\\tscale = 6\\n\\t\\telif hz_string.endswith('hz'):\\n\\t\\t\\tscale = 0\\n\\n\\t\\thz = \\\"\\\".join(n for n in hz_string if n.isdigit() or n=='.').strip()\\n\\t\\tif not '.' in hz:\\n\\t\\t\\thz += '.0'\\n\\n\\t\\thz, scale = _hz_short_to_full(hz, scale)\\n\\n\\t\\treturn (hz, scale)\\n\\texcept Exception:\\n\\t\\treturn (0, 0)\\n\\ndef _hz_short_to_friendly(ticks, scale):\\n\\ttry:\\n\\t\\t# Get the raw Hz as a string\\n\\t\\tleft, right = _hz_short_to_full(ticks, scale)\\n\\t\\tresult = '{0}.{1}'.format(left, right)\\n\\n\\t\\t# Get the location of the dot, and remove said dot\\n\\t\\tdot_index = result.index('.')\\n\\t\\tresult = result.replace('.', '')\\n\\n\\t\\t# Get the Hz symbol and scale\\n\\t\\tsymbol = \\\"Hz\\\"\\n\\t\\tscale = 0\\n\\t\\tif dot_index > 9:\\n\\t\\t\\tsymbol = \\\"GHz\\\"\\n\\t\\t\\tscale = 9\\n\\t\\telif dot_index > 6:\\n\\t\\t\\tsymbol = \\\"MHz\\\"\\n\\t\\t\\tscale = 6\\n\\t\\telif dot_index > 3:\\n\\t\\t\\tsymbol = \\\"KHz\\\"\\n\\t\\t\\tscale = 3\\n\\n\\t\\t# Get the Hz with the dot at the new scaled point\\n\\t\\tresult = '{0}.{1}'.format(result[:-scale-1], result[-scale-1:])\\n\\n\\t\\t# Format the ticks to have 4 numbers after the decimal\\n\\t\\t# and remove any superfluous zeroes.\\n\\t\\tresult = '{0:.4f} {1}'.format(float(result), symbol)\\n\\t\\tresult = result.rstrip('0')\\n\\t\\treturn result\\n\\texcept Exception:\\n\\t\\treturn '0.0000 Hz'\\n\\ndef _to_friendly_bytes(input):\\n\\timport re\\n\\n\\tif not input:\\n\\t\\treturn input\\n\\tinput = \\\"{0}\\\".format(input)\\n\\n\\tformats = {\\n\\t\\tr\\\"^[0-9]+B$\\\" : 'B',\\n\\t\\tr\\\"^[0-9]+K$\\\" : 'KB',\\n\\t\\tr\\\"^[0-9]+M$\\\" : 'MB',\\n\\t\\tr\\\"^[0-9]+G$\\\" : 'GB'\\n\\t}\\n\\n\\tfor pattern, friendly_size in formats.items():\\n\\t\\tif re.match(pattern, input):\\n\\t\\t\\treturn \\\"{0} {1}\\\".format(input[ : -1].strip(), friendly_size)\\n\\n\\treturn input\\n\\ndef _friendly_bytes_to_int(friendly_bytes):\\n\\tinput = friendly_bytes.lower()\\n\\n\\tformats = [\\n\\t\\t{'gib' : 1024 * 1024 * 1024},\\n\\t\\t{'mib' : 1024 * 1024},\\n\\t\\t{'kib' : 1024},\\n\\n\\t\\t{'gb' : 1024 * 1024 * 1024},\\n\\t\\t{'mb' : 1024 * 1024},\\n\\t\\t{'kb' : 1024},\\n\\n\\t\\t{'g' : 1024 * 1024 * 1024},\\n\\t\\t{'m' : 1024 * 1024},\\n\\t\\t{'k' : 1024},\\n\\t\\t{'b' : 1},\\n\\t]\\n\\n\\ttry:\\n\\t\\tfor entry in formats:\\n\\t\\t\\tpattern = list(entry.keys())[0]\\n\\t\\t\\tmultiplier = list(entry.values())[0]\\n\\t\\t\\tif input.endswith(pattern):\\n\\t\\t\\t\\treturn int(input.split(pattern)[0].strip()) * multiplier\\n\\n\\texcept Exception as err:\\n\\t\\tpass\\n\\n\\treturn friendly_bytes\\n\\ndef _parse_cpu_brand_string(cpu_string):\\n\\t# Just return 0 if the processor brand does not have the Hz\\n\\tif not 'hz' in cpu_string.lower():\\n\\t\\treturn ('0.0', 0)\\n\\n\\thz = cpu_string.lower()\\n\\tscale = 0\\n\\n\\tif hz.endswith('mhz'):\\n\\t\\tscale = 6\\n\\telif hz.endswith('ghz'):\\n\\t\\tscale = 9\\n\\tif '@' in hz:\\n\\t\\thz = hz.split('@')[1]\\n\\telse:\\n\\t\\thz = hz.rsplit(None, 1)[1]\\n\\n\\thz = hz.rstrip('mhz').rstrip('ghz').strip()\\n\\thz = _to_decimal_string(hz)\\n\\n\\treturn (hz, scale)\\n\\ndef _parse_cpu_brand_string_dx(cpu_string):\\n\\timport re\\n\\n\\t# Find all the strings inside brackets ()\\n\\tstarts = [m.start() for m in re.finditer(r\\\"\\\\(\\\", cpu_string)]\\n\\tends = [m.start() for m in re.finditer(r\\\"\\\\)\\\", cpu_string)]\\n\\tinsides = {k: v for k, v in zip(starts, ends)}\\n\\tinsides = [cpu_string[start+1 : end] for start, end in insides.items()]\\n\\n\\t# Find all the fields\\n\\tvendor_id, stepping, model, family = (None, None, None, None)\\n\\tfor inside in insides:\\n\\t\\tfor pair in inside.split(','):\\n\\t\\t\\tpair = [n.strip() for n in pair.split(':')]\\n\\t\\t\\tif len(pair) > 1:\\n\\t\\t\\t\\tname, value = pair[0], pair[1]\\n\\t\\t\\t\\tif name == 'origin':\\n\\t\\t\\t\\t\\tvendor_id = value.strip('\\\"')\\n\\t\\t\\t\\telif name == 'stepping':\\n\\t\\t\\t\\t\\tstepping = int(value.lstrip('0x'), 16)\\n\\t\\t\\t\\telif name == 'model':\\n\\t\\t\\t\\t\\tmodel = int(value.lstrip('0x'), 16)\\n\\t\\t\\t\\telif name in ['fam', 'family']:\\n\\t\\t\\t\\t\\tfamily = int(value.lstrip('0x'), 16)\\n\\n\\t# Find the Processor Brand\\n\\t# Strip off extra strings in brackets at end\\n\\tbrand = cpu_string.strip()\\n\\tis_working = True\\n\\twhile is_working:\\n\\t\\tis_working = False\\n\\t\\tfor inside in insides:\\n\\t\\t\\tfull = \\\"({0})\\\".format(inside)\\n\\t\\t\\tif brand.endswith(full):\\n\\t\\t\\t\\tbrand = brand[ :-len(full)].strip()\\n\\t\\t\\t\\tis_working = True\\n\\n\\t# Find the Hz in the brand string\\n\\thz_brand, scale = _parse_cpu_brand_string(brand)\\n\\n\\t# Find Hz inside brackets () after the brand string\\n\\tif hz_brand == '0.0':\\n\\t\\tfor inside in insides:\\n\\t\\t\\thz = inside\\n\\t\\t\\tfor entry in ['GHz', 'MHz', 'Hz']:\\n\\t\\t\\t\\tif entry in hz:\\n\\t\\t\\t\\t\\thz = \\\"CPU @ \\\" + hz[ : hz.find(entry) + len(entry)]\\n\\t\\t\\t\\t\\thz_brand, scale = _parse_cpu_brand_string(hz)\\n\\t\\t\\t\\t\\tbreak\\n\\n\\treturn (hz_brand, scale, brand, vendor_id, stepping, model, family)\\n\\ndef _parse_dmesg_output(output):\\n\\ttry:\\n\\t\\t# Get all the dmesg lines that might contain a CPU string\\n\\t\\tlines = output.split(' CPU0:')[1:] + \\\\\\n\\t\\t\\t\\toutput.split(' CPU1:')[1:] + \\\\\\n\\t\\t\\t\\toutput.split(' CPU:')[1:] + \\\\\\n\\t\\t\\t\\toutput.split('\\\\nCPU0:')[1:] + \\\\\\n\\t\\t\\t\\toutput.split('\\\\nCPU1:')[1:] + \\\\\\n\\t\\t\\t\\toutput.split('\\\\nCPU:')[1:]\\n\\t\\tlines = [l.split('\\\\n')[0].strip() for l in lines]\\n\\n\\t\\t# Convert the lines to CPU strings\\n\\t\\tcpu_strings = [_parse_cpu_brand_string_dx(l) for l in lines]\\n\\n\\t\\t# Find the CPU string that has the most fields\\n\\t\\tbest_string = None\\n\\t\\thighest_count = 0\\n\\t\\tfor cpu_string in cpu_strings:\\n\\t\\t\\tcount = sum([n is not None for n in cpu_string])\\n\\t\\t\\tif count > highest_count:\\n\\t\\t\\t\\thighest_count = count\\n\\t\\t\\t\\tbest_string = cpu_string\\n\\n\\t\\t# If no CPU string was found, return {}\\n\\t\\tif not best_string:\\n\\t\\t\\treturn {}\\n\\n\\t\\thz_actual, scale, processor_brand, vendor_id, stepping, model, family = best_string\\n\\n\\t\\t# Origin\\n\\t\\tif '  Origin=' in output:\\n\\t\\t\\tfields = output[output.find('  Origin=') : ].split('\\\\n')[0]\\n\\t\\t\\tfields = fields.strip().split()\\n\\t\\t\\tfields = [n.strip().split('=') for n in fields]\\n\\t\\t\\tfields = [{n[0].strip().lower() : n[1].strip()} for n in fields]\\n\\n\\t\\t\\tfor field in fields:\\n\\t\\t\\t\\tname = list(field.keys())[0]\\n\\t\\t\\t\\tvalue = list(field.values())[0]\\n\\n\\t\\t\\t\\tif name == 'origin':\\n\\t\\t\\t\\t\\tvendor_id = value.strip('\\\"')\\n\\t\\t\\t\\telif name == 'stepping':\\n\\t\\t\\t\\t\\tstepping = int(value.lstrip('0x'), 16)\\n\\t\\t\\t\\telif name == 'model':\\n\\t\\t\\t\\t\\tmodel = int(value.lstrip('0x'), 16)\\n\\t\\t\\t\\telif name in ['fam', 'family']:\\n\\t\\t\\t\\t\\tfamily = int(value.lstrip('0x'), 16)\\n\\n\\t\\t# Features\\n\\t\\tflag_lines = []\\n\\t\\tfor category in ['  Features=', '  Features2=', '  AMD Features=', '  AMD Features2=']:\\n\\t\\t\\tif category in output:\\n\\t\\t\\t\\tflag_lines.append(output.split(category)[1].split('\\\\n')[0])\\n\\n\\t\\tflags = []\\n\\t\\tfor line in flag_lines:\\n\\t\\t\\tline = line.split('<')[1].split('>')[0].lower()\\n\\t\\t\\tfor flag in line.split(','):\\n\\t\\t\\t\\tflags.append(flag)\\n\\t\\tflags.sort()\\n\\n\\t\\t# Convert from GHz/MHz string to Hz\\n\\t\\thz_advertised, scale = _parse_cpu_brand_string(processor_brand)\\n\\n\\t\\t# If advertised hz not found, use the actual hz\\n\\t\\tif hz_advertised == '0.0':\\n\\t\\t\\tscale = 6\\n\\t\\t\\thz_advertised = _to_decimal_string(hz_actual)\\n\\n\\t\\tinfo = {\\n\\t\\t'vendor_id_raw' : vendor_id,\\n\\t\\t'brand_raw' : processor_brand,\\n\\n\\t\\t'stepping' : stepping,\\n\\t\\t'model' : model,\\n\\t\\t'family' : family,\\n\\t\\t'flags' : flags\\n\\t\\t}\\n\\n\\t\\tif hz_advertised and hz_advertised != '0.0':\\n\\t\\t\\tinfo['hz_advertised_friendly'] = _hz_short_to_friendly(hz_advertised, scale)\\n\\t\\t\\tinfo['hz_actual_friendly'] = _hz_short_to_friendly(hz_actual, scale)\\n\\n\\t\\tif hz_advertised and hz_advertised != '0.0':\\n\\t\\t\\tinfo['hz_advertised'] = _hz_short_to_full(hz_advertised, scale)\\n\\t\\t\\tinfo['hz_actual'] = _hz_short_to_full(hz_actual, scale)\\n\\n\\t\\treturn {k: v for k, v in info.items() if v}\\n\\texcept Exception as err:\\n\\t\\tg_trace.fail(err)\\n\\t\\t#raise\\n\\n\\treturn {}\\n\\ndef _parse_arch(arch_string_raw):\\n\\timport re\\n\\n\\tarch, bits = None, None\\n\\tarch_string_raw = arch_string_raw.lower()\\n\\n\\t# X86\\n\\tif re.match(r'^i\\\\d86$|^x86$|^x86_32$|^i86pc$|^ia32$|^ia-32$|^bepc$', arch_string_raw):\\n\\t\\tarch = 'X86_32'\\n\\t\\tbits = 32\\n\\telif re.match(r'^x64$|^x86_64$|^x86_64t$|^i686-64$|^amd64$|^ia64$|^ia-64$', arch_string_raw):\\n\\t\\tarch = 'X86_64'\\n\\t\\tbits = 64\\n\\t# ARM\\n\\telif re.match(r'^armv8-a|aarch64|arm64$', arch_string_raw):\\n\\t\\tarch = 'ARM_8'\\n\\t\\tbits = 64\\n\\telif re.match(r'^armv7$|^armv7[a-z]$|^armv7-[a-z]$|^armv6[a-z]$', arch_string_raw):\\n\\t\\tarch = 'ARM_7'\\n\\t\\tbits = 32\\n\\telif re.match(r'^armv8$|^armv8[a-z]$|^armv8-[a-z]$', arch_string_raw):\\n\\t\\tarch = 'ARM_8'\\n\\t\\tbits = 32\\n\\t# PPC\\n\\telif re.match(r'^ppc32$|^prep$|^pmac$|^powermac$', arch_string_raw):\\n\\t\\tarch = 'PPC_32'\\n\\t\\tbits = 32\\n\\telif re.match(r'^powerpc$|^ppc64$|^ppc64le$', arch_string_raw):\\n\\t\\tarch = 'PPC_64'\\n\\t\\tbits = 64\\n\\t# SPARC\\n\\telif re.match(r'^sparc32$|^sparc$', arch_string_raw):\\n\\t\\tarch = 'SPARC_32'\\n\\t\\tbits = 32\\n\\telif re.match(r'^sparc64$|^sun4u$|^sun4v$', arch_string_raw):\\n\\t\\tarch = 'SPARC_64'\\n\\t\\tbits = 64\\n\\t# S390X\\n\\telif re.match(r'^s390x$', arch_string_raw):\\n\\t\\tarch = 'S390X'\\n\\t\\tbits = 64\\n\\telif arch_string_raw == 'mips':\\n\\t\\tarch = 'MIPS_32'\\n\\t\\tbits = 32\\n\\telif arch_string_raw == 'mips64':\\n\\t\\tarch = 'MIPS_64'\\n\\t\\tbits = 64\\n\\t# RISCV\\n\\telif re.match(r'^riscv$|^riscv32$|^riscv32be$', arch_string_raw):\\n\\t\\tarch = 'RISCV_32'\\n\\t\\tbits = 32\\n\\telif re.match(r'^riscv64$|^riscv64be$', arch_string_raw):\\n\\t\\tarch = 'RISCV_64'\\n\\t\\tbits = 64\\n\\n\\treturn (arch, bits)\\n\\ndef _is_bit_set(reg, bit):\\n\\tmask = 1 << bit\\n\\tis_set = reg & mask > 0\\n\\treturn is_set\\n\\n\\ndef _is_selinux_enforcing(trace):\\n\\t# Just return if the SE Linux Status Tool is not installed\\n\\tif not DataSource.has_sestatus():\\n\\t\\ttrace.fail('Failed to find sestatus.')\\n\\t\\treturn False\\n\\n\\t# Run the sestatus, and just return if it failed to run\\n\\treturncode, output = DataSource.sestatus_b()\\n\\tif returncode != 0:\\n\\t\\ttrace.fail('Failed to run sestatus. Skipping ...')\\n\\t\\treturn False\\n\\n\\t# Figure out if explicitly in enforcing mode\\n\\tfor line in output.splitlines():\\n\\t\\tline = line.strip().lower()\\n\\t\\tif line.startswith(\\\"current mode:\\\"):\\n\\t\\t\\tif line.endswith(\\\"enforcing\\\"):\\n\\t\\t\\t\\treturn True\\n\\t\\t\\telse:\\n\\t\\t\\t\\treturn False\\n\\n\\t# Figure out if we can execute heap and execute memory\\n\\tcan_selinux_exec_heap = False\\n\\tcan_selinux_exec_memory = False\\n\\tfor line in output.splitlines():\\n\\t\\tline = line.strip().lower()\\n\\t\\tif line.startswith(\\\"allow_execheap\\\") and line.endswith(\\\"on\\\"):\\n\\t\\t\\tcan_selinux_exec_heap = True\\n\\t\\telif line.startswith(\\\"allow_execmem\\\") and line.endswith(\\\"on\\\"):\\n\\t\\t\\tcan_selinux_exec_memory = True\\n\\n\\ttrace.command_output('can_selinux_exec_heap:', can_selinux_exec_heap)\\n\\ttrace.command_output('can_selinux_exec_memory:', can_selinux_exec_memory)\\n\\n\\treturn (not can_selinux_exec_heap or not can_selinux_exec_memory)\\n\\ndef _filter_dict_keys_with_empty_values(info, acceptable_values = {}):\\n\\tfiltered_info = {}\\n\\tfor key in info:\\n\\t\\tvalue = info[key]\\n\\n\\t\\t# Keep if value is acceptable\\n\\t\\tif key in acceptable_values:\\n\\t\\t\\tif acceptable_values[key] == value:\\n\\t\\t\\t\\tfiltered_info[key] = value\\n\\t\\t\\t\\tcontinue\\n\\n\\t\\t# Filter out None, 0, \\\"\\\", (), {}, []\\n\\t\\tif not value:\\n\\t\\t\\tcontinue\\n\\n\\t\\t# Filter out (0, 0)\\n\\t\\tif value == (0, 0):\\n\\t\\t\\tcontinue\\n\\n\\t\\t# Filter out -1\\n\\t\\tif value == -1:\\n\\t\\t\\tcontinue\\n\\n\\t\\t# Filter out strings that start with \\\"0.0\\\"\\n\\t\\tif type(value) == str and value.startswith('0.0'):\\n\\t\\t\\tcontinue\\n\\n\\t\\tfiltered_info[key] = value\\n\\n\\treturn filtered_info\\n\\nclass ASM(object):\\n\\tdef __init__(self, restype=None, argtypes=(), machine_code=[]):\\n\\t\\tself.restype = restype\\n\\t\\tself.argtypes = argtypes\\n\\t\\tself.machine_code = machine_code\\n\\t\\tself.prochandle = None\\n\\t\\tself.mm = None\\n\\t\\tself.func = None\\n\\t\\tself.address = None\\n\\t\\tself.size = 0\\n\\n\\tdef compile(self):\\n\\t\\tmachine_code = bytes.join(b'', self.machine_code)\\n\\t\\tself.size = ctypes.c_size_t(len(machine_code))\\n\\n\\t\\tif DataSource.is_windows:\\n\\t\\t\\t# Allocate a memory segment the size of the machine code, and make it executable\\n\\t\\t\\tsize = len(machine_code)\\n\\t\\t\\t# Alloc at least 1 page to ensure we own all pages that we want to change protection on\\n\\t\\t\\tif size < 0x1000: size = 0x1000\\n\\t\\t\\tMEM_COMMIT = ctypes.c_ulong(0x1000)\\n\\t\\t\\tPAGE_READWRITE = ctypes.c_ulong(0x4)\\n\\t\\t\\tpfnVirtualAlloc = ctypes.windll.kernel32.VirtualAlloc\\n\\t\\t\\tpfnVirtualAlloc.restype = ctypes.c_void_p\\n\\t\\t\\tself.address = pfnVirtualAlloc(None, ctypes.c_size_t(size), MEM_COMMIT, PAGE_READWRITE)\\n\\t\\t\\tif not self.address:\\n\\t\\t\\t\\traise Exception(\\\"Failed to VirtualAlloc\\\")\\n\\n\\t\\t\\t# Copy the machine code into the memory segment\\n\\t\\t\\tmemmove = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t)(ctypes._memmove_addr)\\n\\t\\t\\tif memmove(self.address, machine_code, size) < 0:\\n\\t\\t\\t\\traise Exception(\\\"Failed to memmove\\\")\\n\\n\\t\\t\\t# Enable execute permissions\\n\\t\\t\\tPAGE_EXECUTE = ctypes.c_ulong(0x10)\\n\\t\\t\\told_protect = ctypes.c_ulong(0)\\n\\t\\t\\tpfnVirtualProtect = ctypes.windll.kernel32.VirtualProtect\\n\\t\\t\\tres = pfnVirtualProtect(ctypes.c_void_p(self.address), ctypes.c_size_t(size), PAGE_EXECUTE, ctypes.byref(old_protect))\\n\\t\\t\\tif not res:\\n\\t\\t\\t\\traise Exception(\\\"Failed VirtualProtect\\\")\\n\\n\\t\\t\\t# Flush Instruction Cache\\n\\t\\t\\t# First, get process Handle\\n\\t\\t\\tif not self.prochandle:\\n\\t\\t\\t\\tpfnGetCurrentProcess = ctypes.windll.kernel32.GetCurrentProcess\\n\\t\\t\\t\\tpfnGetCurrentProcess.restype = ctypes.c_void_p\\n\\t\\t\\t\\tself.prochandle = ctypes.c_void_p(pfnGetCurrentProcess())\\n\\t\\t\\t# Actually flush cache\\n\\t\\t\\tres = ctypes.windll.kernel32.FlushInstructionCache(self.prochandle, ctypes.c_void_p(self.address), ctypes.c_size_t(size))\\n\\t\\t\\tif not res:\\n\\t\\t\\t\\traise Exception(\\\"Failed FlushInstructionCache\\\")\\n\\t\\telse:\\n\\t\\t\\tfrom mmap import mmap, MAP_PRIVATE, MAP_ANONYMOUS, PROT_WRITE, PROT_READ, PROT_EXEC\\n\\n\\t\\t\\t# Allocate a private and executable memory segment the size of the machine code\\n\\t\\t\\tmachine_code = bytes.join(b'', self.machine_code)\\n\\t\\t\\tself.size = len(machine_code)\\n\\t\\t\\tself.mm = mmap(-1, self.size, flags=MAP_PRIVATE | MAP_ANONYMOUS, prot=PROT_WRITE | PROT_READ | PROT_EXEC)\\n\\n\\t\\t\\t# Copy the machine code into the memory segment\\n\\t\\t\\tself.mm.write(machine_code)\\n\\t\\t\\tself.address = ctypes.addressof(ctypes.c_int.from_buffer(self.mm))\\n\\n\\t\\t# Cast the memory segment into a function\\n\\t\\tfunctype = ctypes.CFUNCTYPE(self.restype, *self.argtypes)\\n\\t\\tself.func = functype(self.address)\\n\\n\\tdef run(self):\\n\\t\\t# Call the machine code like a function\\n\\t\\tretval = self.func()\\n\\n\\t\\treturn retval\\n\\n\\tdef free(self):\\n\\t\\t# Free the function memory segment\\n\\t\\tif DataSource.is_windows:\\n\\t\\t\\tMEM_RELEASE = ctypes.c_ulong(0x8000)\\n\\t\\t\\tctypes.windll.kernel32.VirtualFree(ctypes.c_void_p(self.address), ctypes.c_size_t(0), MEM_RELEASE)\\n\\t\\telse:\\n\\t\\t\\tself.mm.close()\\n\\n\\t\\tself.prochandle = None\\n\\t\\tself.mm = None\\n\\t\\tself.func = None\\n\\t\\tself.address = None\\n\\t\\tself.size = 0\\n\\n\\nclass CPUID(object):\\n\\tdef __init__(self, trace=None):\\n\\t\\tif trace is None:\\n\\t\\t\\ttrace = Trace(False, False)\\n\\n\\t\\t# Figure out if SE Linux is on and in enforcing mode\\n\\t\\tself.is_selinux_enforcing = _is_selinux_enforcing(trace)\\n\\n\\tdef _asm_func(self, restype=None, argtypes=(), machine_code=[]):\\n\\t\\tasm = ASM(restype, argtypes, machine_code)\\n\\t\\tasm.compile()\\n\\t\\treturn asm\\n\\n\\tdef _run_asm(self, *machine_code):\\n\\t\\tasm = ASM(ctypes.c_uint32, (), machine_code)\\n\\t\\tasm.compile()\\n\\t\\tretval = asm.run()\\n\\t\\tasm.free()\\n\\t\\treturn retval\\n\\n\\t# http://en.wikipedia.org/wiki/CPUID#EAX.3D0:_Get_vendor_ID\\n\\tdef get_vendor_id(self):\\n\\t\\t# EBX\\n\\t\\tebx = self._run_asm(\\n\\t\\t\\tb\\\"\\\\x31\\\\xC0\\\",        # xor eax,eax\\n\\t\\t\\tb\\\"\\\\x0F\\\\xA2\\\"         # cpuid\\n\\t\\t\\tb\\\"\\\\x89\\\\xD8\\\"         # mov ax,bx\\n\\t\\t\\tb\\\"\\\\xC3\\\"             # ret\\n\\t\\t)\\n\\n\\t\\t# ECX\\n\\t\\tecx = self._run_asm(\\n\\t\\t\\tb\\\"\\\\x31\\\\xC0\\\",        # xor eax,eax\\n\\t\\t\\tb\\\"\\\\x0f\\\\xa2\\\"         # cpuid\\n\\t\\t\\tb\\\"\\\\x89\\\\xC8\\\"         # mov ax,cx\\n\\t\\t\\tb\\\"\\\\xC3\\\"             # ret\\n\\t\\t)\\n\\n\\t\\t# EDX\\n\\t\\tedx = self._run_asm(\\n\\t\\t\\tb\\\"\\\\x31\\\\xC0\\\",        # xor eax,eax\\n\\t\\t\\tb\\\"\\\\x0f\\\\xa2\\\"         # cpuid\\n\\t\\t\\tb\\\"\\\\x89\\\\xD0\\\"         # mov ax,dx\\n\\t\\t\\tb\\\"\\\\xC3\\\"             # ret\\n\\t\\t)\\n\\n\\t\\t# Each 4bits is a ascii letter in the name\\n\\t\\tvendor_id = []\\n\\t\\tfor reg in [ebx, edx, ecx]:\\n\\t\\t\\tfor n in [0, 8, 16, 24]:\\n\\t\\t\\t\\tvendor_id.append(chr((reg >> n) & 0xFF))\\n\\t\\tvendor_id = ''.join(vendor_id)\\n\\n\\t\\treturn vendor_id\\n\\n\\t# http://en.wikipedia.org/wiki/CPUID#EAX.3D1:_Processor_Info_and_Feature_Bits\\n\\tdef get_info(self):\\n\\t\\t# EAX\\n\\t\\teax = self._run_asm(\\n\\t\\t\\tb\\\"\\\\xB8\\\\x01\\\\x00\\\\x00\\\\x00\\\",   # mov eax,0x1\\\"\\n\\t\\t\\tb\\\"\\\\x0f\\\\xa2\\\"                # cpuid\\n\\t\\t\\tb\\\"\\\\xC3\\\"                    # ret\\n\\t\\t)\\n\\n\\t\\t# Get the CPU info\\n\\t\\tstepping_id = (eax >> 0) & 0xF # 4 bits\\n\\t\\tmodel = (eax >> 4) & 0xF # 4 bits\\n\\t\\tfamily_id = (eax >> 8) & 0xF # 4 bits\\n\\t\\tprocessor_type = (eax >> 12) & 0x3 # 2 bits\\n\\t\\textended_model_id = (eax >> 16) & 0xF # 4 bits\\n\\t\\textended_family_id = (eax >> 20) & 0xFF # 8 bits\\n\\t\\tfamily = 0\\n\\n\\t\\tif family_id in [15]:\\n\\t\\t\\tfamily = extended_family_id + family_id\\n\\t\\telse:\\n\\t\\t\\tfamily = family_id\\n\\n\\t\\tif family_id in [6, 15]:\\n\\t\\t\\tmodel = (extended_model_id << 4) + model\\n\\n\\t\\treturn {\\n\\t\\t\\t'stepping' : stepping_id,\\n\\t\\t\\t'model' : model,\\n\\t\\t\\t'family' : family,\\n\\t\\t\\t'processor_type' : processor_type\\n\\t\\t}\\n\\n\\t# http://en.wikipedia.org/wiki/CPUID#EAX.3D80000000h:_Get_Highest_Extended_Function_Supported\\n\\tdef get_max_extension_support(self):\\n\\t\\t# Check for extension support\\n\\t\\tmax_extension_support = self._run_asm(\\n\\t\\t\\tb\\\"\\\\xB8\\\\x00\\\\x00\\\\x00\\\\x80\\\" # mov ax,0x80000000\\n\\t\\t\\tb\\\"\\\\x0f\\\\xa2\\\"             # cpuid\\n\\t\\t\\tb\\\"\\\\xC3\\\"                 # ret\\n\\t\\t)\\n\\n\\t\\treturn max_extension_support\\n\\n\\t# http://en.wikipedia.org/wiki/CPUID#EAX.3D1:_Processor_Info_and_Feature_Bits\\n\\tdef get_flags(self, max_extension_support):\\n\\t\\t# EDX\\n\\t\\tedx = self._run_asm(\\n\\t\\t\\tb\\\"\\\\xB8\\\\x01\\\\x00\\\\x00\\\\x00\\\",   # mov eax,0x1\\\"\\n\\t\\t\\tb\\\"\\\\x0f\\\\xa2\\\"                # cpuid\\n\\t\\t\\tb\\\"\\\\x89\\\\xD0\\\"                # mov ax,dx\\n\\t\\t\\tb\\\"\\\\xC3\\\"                    # ret\\n\\t\\t)\\n\\n\\t\\t# ECX\\n\\t\\tecx = self._run_asm(\\n\\t\\t\\tb\\\"\\\\xB8\\\\x01\\\\x00\\\\x00\\\\x00\\\",   # mov eax,0x1\\\"\\n\\t\\t\\tb\\\"\\\\x0f\\\\xa2\\\"                # cpuid\\n\\t\\t\\tb\\\"\\\\x89\\\\xC8\\\"                # mov ax,cx\\n\\t\\t\\tb\\\"\\\\xC3\\\"                    # ret\\n\\t\\t)\\n\\n\\t\\t# Get the CPU flags\\n\\t\\tflags = {\\n\\t\\t\\t'fpu' : _is_bit_set(edx, 0),\\n\\t\\t\\t'vme' : _is_bit_set(edx, 1),\\n\\t\\t\\t'de' : _is_bit_set(edx, 2),\\n\\t\\t\\t'pse' : _is_bit_set(edx, 3),\\n\\t\\t\\t'tsc' : _is_bit_set(edx, 4),\\n\\t\\t\\t'msr' : _is_bit_set(edx, 5),\\n\\t\\t\\t'pae' : _is_bit_set(edx, 6),\\n\\t\\t\\t'mce' : _is_bit_set(edx, 7),\\n\\t\\t\\t'cx8' : _is_bit_set(edx, 8),\\n\\t\\t\\t'apic' : _is_bit_set(edx, 9),\\n\\t\\t\\t#'reserved1' : _is_bit_set(edx, 10),\\n\\t\\t\\t'sep' : _is_bit_set(edx, 11),\\n\\t\\t\\t'mtrr' : _is_bit_set(edx, 12),\\n\\t\\t\\t'pge' : _is_bit_set(edx, 13),\\n\\t\\t\\t'mca' : _is_bit_set(edx, 14),\\n\\t\\t\\t'cmov' : _is_bit_set(edx, 15),\\n\\t\\t\\t'pat' : _is_bit_set(edx, 16),\\n\\t\\t\\t'pse36' : _is_bit_set(edx, 17),\\n\\t\\t\\t'pn' : _is_bit_set(edx, 18),\\n\\t\\t\\t'clflush' : _is_bit_set(edx, 19),\\n\\t\\t\\t#'reserved2' : _is_bit_set(edx, 20),\\n\\t\\t\\t'dts' : _is_bit_set(edx, 21),\\n\\t\\t\\t'acpi' : _is_bit_set(edx, 22),\\n\\t\\t\\t'mmx' : _is_bit_set(edx, 23),\\n\\t\\t\\t'fxsr' : _is_bit_set(edx, 24),\\n\\t\\t\\t'sse' : _is_bit_set(edx, 25),\\n\\t\\t\\t'sse2' : _is_bit_set(edx, 26),\\n\\t\\t\\t'ss' : _is_bit_set(edx, 27),\\n\\t\\t\\t'ht' : _is_bit_set(edx, 28),\\n\\t\\t\\t'tm' : _is_bit_set(edx, 29),\\n\\t\\t\\t'ia64' : _is_bit_set(edx, 30),\\n\\t\\t\\t'pbe' : _is_bit_set(edx, 31),\\n\\n\\t\\t\\t'pni' : _is_bit_set(ecx, 0),\\n\\t\\t\\t'pclmulqdq' : _is_bit_set(ecx, 1),\\n\\t\\t\\t'dtes64' : _is_bit_set(ecx, 2),\\n\\t\\t\\t'monitor' : _is_bit_set(ecx, 3),\\n\\t\\t\\t'ds_cpl' : _is_bit_set(ecx, 4),\\n\\t\\t\\t'vmx' : _is_bit_set(ecx, 5),\\n\\t\\t\\t'smx' : _is_bit_set(ecx, 6),\\n\\t\\t\\t'est' : _is_bit_set(ecx, 7),\\n\\t\\t\\t'tm2' : _is_bit_set(ecx, 8),\\n\\t\\t\\t'ssse3' : _is_bit_set(ecx, 9),\\n\\t\\t\\t'cid' : _is_bit_set(ecx, 10),\\n\\t\\t\\t#'reserved3' : _is_bit_set(ecx, 11),\\n\\t\\t\\t'fma' : _is_bit_set(ecx, 12),\\n\\t\\t\\t'cx16' : _is_bit_set(ecx, 13),\\n\\t\\t\\t'xtpr' : _is_bit_set(ecx, 14),\\n\\t\\t\\t'pdcm' : _is_bit_set(ecx, 15),\\n\\t\\t\\t#'reserved4' : _is_bit_set(ecx, 16),\\n\\t\\t\\t'pcid' : _is_bit_set(ecx, 17),\\n\\t\\t\\t'dca' : _is_bit_set(ecx, 18),\\n\\t\\t\\t'sse4_1' : _is_bit_set(ecx, 19),\\n\\t\\t\\t'sse4_2' : _is_bit_set(ecx, 20),\\n\\t\\t\\t'x2apic' : _is_bit_set(ecx, 21),\\n\\t\\t\\t'movbe' : _is_bit_set(ecx, 22),\\n\\t\\t\\t'popcnt' : _is_bit_set(ecx, 23),\\n\\t\\t\\t'tscdeadline' : _is_bit_set(ecx, 24),\\n\\t\\t\\t'aes' : _is_bit_set(ecx, 25),\\n\\t\\t\\t'xsave' : _is_bit_set(ecx, 26),\\n\\t\\t\\t'osxsave' : _is_bit_set(ecx, 27),\\n\\t\\t\\t'avx' : _is_bit_set(ecx, 28),\\n\\t\\t\\t'f16c' : _is_bit_set(ecx, 29),\\n\\t\\t\\t'rdrnd' : _is_bit_set(ecx, 30),\\n\\t\\t\\t'hypervisor' : _is_bit_set(ecx, 31)\\n\\t\\t}\\n\\n\\t\\t# Get a list of only the flags that are true\\n\\t\\tflags = [k for k, v in flags.items() if v]\\n\\n\\t\\t# http://en.wikipedia.org/wiki/CPUID#EAX.3D7.2C_ECX.3D0:_Extended_Features\\n\\t\\tif max_extension_support >= 7:\\n\\t\\t\\t# EBX\\n\\t\\t\\tebx = self._run_asm(\\n\\t\\t\\t\\tb\\\"\\\\x31\\\\xC9\\\",            # xor ecx,ecx\\n\\t\\t\\t\\tb\\\"\\\\xB8\\\\x07\\\\x00\\\\x00\\\\x00\\\" # mov eax,7\\n\\t\\t\\t\\tb\\\"\\\\x0f\\\\xa2\\\"         # cpuid\\n\\t\\t\\t\\tb\\\"\\\\x89\\\\xD8\\\"         # mov ax,bx\\n\\t\\t\\t\\tb\\\"\\\\xC3\\\"             # ret\\n\\t\\t\\t)\\n\\n\\t\\t\\t# ECX\\n\\t\\t\\tecx = self._run_asm(\\n\\t\\t\\t\\tb\\\"\\\\x31\\\\xC9\\\",            # xor ecx,ecx\\n\\t\\t\\t\\tb\\\"\\\\xB8\\\\x07\\\\x00\\\\x00\\\\x00\\\" # mov eax,7\\n\\t\\t\\t\\tb\\\"\\\\x0f\\\\xa2\\\"         # cpuid\\n\\t\\t\\t\\tb\\\"\\\\x89\\\\xC8\\\"         # mov ax,cx\\n\\t\\t\\t\\tb\\\"\\\\xC3\\\"             # ret\\n\\t\\t\\t)\\n\\n\\t\\t\\t# Get the extended CPU flags\\n\\t\\t\\textended_flags = {\\n\\t\\t\\t\\t#'fsgsbase' : _is_bit_set(ebx, 0),\\n\\t\\t\\t\\t#'IA32_TSC_ADJUST' : _is_bit_set(ebx, 1),\\n\\t\\t\\t\\t'sgx' : _is_bit_set(ebx, 2),\\n\\t\\t\\t\\t'bmi1' : _is_bit_set(ebx, 3),\\n\\t\\t\\t\\t'hle' : _is_bit_set(ebx, 4),\\n\\t\\t\\t\\t'avx2' : _is_bit_set(ebx, 5),\\n\\t\\t\\t\\t#'reserved' : _is_bit_set(ebx, 6),\\n\\t\\t\\t\\t'smep' : _is_bit_set(ebx, 7),\\n\\t\\t\\t\\t'bmi2' : _is_bit_set(ebx, 8),\\n\\t\\t\\t\\t'erms' : _is_bit_set(ebx, 9),\\n\\t\\t\\t\\t'invpcid' : _is_bit_set(ebx, 10),\\n\\t\\t\\t\\t'rtm' : _is_bit_set(ebx, 11),\\n\\t\\t\\t\\t'pqm' : _is_bit_set(ebx, 12),\\n\\t\\t\\t\\t#'FPU CS and FPU DS deprecated' : _is_bit_set(ebx, 13),\\n\\t\\t\\t\\t'mpx' : _is_bit_set(ebx, 14),\\n\\t\\t\\t\\t'pqe' : _is_bit_set(ebx, 15),\\n\\t\\t\\t\\t'avx512f' : _is_bit_set(ebx, 16),\\n\\t\\t\\t\\t'avx512dq' : _is_bit_set(ebx, 17),\\n\\t\\t\\t\\t'rdseed' : _is_bit_set(ebx, 18),\\n\\t\\t\\t\\t'adx' : _is_bit_set(ebx, 19),\\n\\t\\t\\t\\t'smap' : _is_bit_set(ebx, 20),\\n\\t\\t\\t\\t'avx512ifma' : _is_bit_set(ebx, 21),\\n\\t\\t\\t\\t'pcommit' : _is_bit_set(ebx, 22),\\n\\t\\t\\t\\t'clflushopt' : _is_bit_set(ebx, 23),\\n\\t\\t\\t\\t'clwb' : _is_bit_set(ebx, 24),\\n\\t\\t\\t\\t'intel_pt' : _is_bit_set(ebx, 25),\\n\\t\\t\\t\\t'avx512pf' : _is_bit_set(ebx, 26),\\n\\t\\t\\t\\t'avx512er' : _is_bit_set(ebx, 27),\\n\\t\\t\\t\\t'avx512cd' : _is_bit_set(ebx, 28),\\n\\t\\t\\t\\t'sha' : _is_bit_set(ebx, 29),\\n\\t\\t\\t\\t'avx512bw' : _is_bit_set(ebx, 30),\\n\\t\\t\\t\\t'avx512vl' : _is_bit_set(ebx, 31),\\n\\n\\t\\t\\t\\t'prefetchwt1' : _is_bit_set(ecx, 0),\\n\\t\\t\\t\\t'avx512vbmi' : _is_bit_set(ecx, 1),\\n\\t\\t\\t\\t'umip' : _is_bit_set(ecx, 2),\\n\\t\\t\\t\\t'pku' : _is_bit_set(ecx, 3),\\n\\t\\t\\t\\t'ospke' : _is_bit_set(ecx, 4),\\n\\t\\t\\t\\t#'reserved' : _is_bit_set(ecx, 5),\\n\\t\\t\\t\\t'avx512vbmi2' : _is_bit_set(ecx, 6),\\n\\t\\t\\t\\t#'reserved' : _is_bit_set(ecx, 7),\\n\\t\\t\\t\\t'gfni' : _is_bit_set(ecx, 8),\\n\\t\\t\\t\\t'vaes' : _is_bit_set(ecx, 9),\\n\\t\\t\\t\\t'vpclmulqdq' : _is_bit_set(ecx, 10),\\n\\t\\t\\t\\t'avx512vnni' : _is_bit_set(ecx, 11),\\n\\t\\t\\t\\t'avx512bitalg' : _is_bit_set(ecx, 12),\\n\\t\\t\\t\\t#'reserved' : _is_bit_set(ecx, 13),\\n\\t\\t\\t\\t'avx512vpopcntdq' : _is_bit_set(ecx, 14),\\n\\t\\t\\t\\t#'reserved' : _is_bit_set(ecx, 15),\\n\\t\\t\\t\\t#'reserved' : _is_bit_set(ecx, 16),\\n\\t\\t\\t\\t#'mpx0' : _is_bit_set(ecx, 17),\\n\\t\\t\\t\\t#'mpx1' : _is_bit_set(ecx, 18),\\n\\t\\t\\t\\t#'mpx2' : _is_bit_set(ecx, 19),\\n\\t\\t\\t\\t#'mpx3' : _is_bit_set(ecx, 20),\\n\\t\\t\\t\\t#'mpx4' : _is_bit_set(ecx, 21),\\n\\t\\t\\t\\t'rdpid' : _is_bit_set(ecx, 22),\\n\\t\\t\\t\\t#'reserved' : _is_bit_set(ecx, 23),\\n\\t\\t\\t\\t#'reserved' : _is_bit_set(ecx, 24),\\n\\t\\t\\t\\t#'reserved' : _is_bit_set(ecx, 25),\\n\\t\\t\\t\\t#'reserved' : _is_bit_set(ecx, 26),\\n\\t\\t\\t\\t#'reserved' : _is_bit_set(ecx, 27),\\n\\t\\t\\t\\t#'reserved' : _is_bit_set(ecx, 28),\\n\\t\\t\\t\\t#'reserved' : _is_bit_set(ecx, 29),\\n\\t\\t\\t\\t'sgx_lc' : _is_bit_set(ecx, 30),\\n\\t\\t\\t\\t#'reserved' : _is_bit_set(ecx, 31)\\n\\t\\t\\t}\\n\\n\\t\\t\\t# Get a list of only the flags that are true\\n\\t\\t\\textended_flags = [k for k, v in extended_flags.items() if v]\\n\\t\\t\\tflags += extended_flags\\n\\n\\t\\t# http://en.wikipedia.org/wiki/CPUID#EAX.3D80000001h:_Extended_Processor_Info_and_Feature_Bits\\n\\t\\tif max_extension_support >= 0x80000001:\\n\\t\\t\\t# EBX\\n\\t\\t\\tebx = self._run_asm(\\n\\t\\t\\t\\tb\\\"\\\\xB8\\\\x01\\\\x00\\\\x00\\\\x80\\\" # mov ax,0x80000001\\n\\t\\t\\t\\tb\\\"\\\\x0f\\\\xa2\\\"         # cpuid\\n\\t\\t\\t\\tb\\\"\\\\x89\\\\xD8\\\"         # mov ax,bx\\n\\t\\t\\t\\tb\\\"\\\\xC3\\\"             # ret\\n\\t\\t\\t)\\n\\n\\t\\t\\t# ECX\\n\\t\\t\\tecx = self._run_asm(\\n\\t\\t\\t\\tb\\\"\\\\xB8\\\\x01\\\\x00\\\\x00\\\\x80\\\" # mov ax,0x80000001\\n\\t\\t\\t\\tb\\\"\\\\x0f\\\\xa2\\\"         # cpuid\\n\\t\\t\\t\\tb\\\"\\\\x89\\\\xC8\\\"         # mov ax,cx\\n\\t\\t\\t\\tb\\\"\\\\xC3\\\"             # ret\\n\\t\\t\\t)\\n\\n\\t\\t\\t# Get the extended CPU flags\\n\\t\\t\\textended_flags = {\\n\\t\\t\\t\\t'fpu' : _is_bit_set(ebx, 0),\\n\\t\\t\\t\\t'vme' : _is_bit_set(ebx, 1),\\n\\t\\t\\t\\t'de' : _is_bit_set(ebx, 2),\\n\\t\\t\\t\\t'pse' : _is_bit_set(ebx, 3),\\n\\t\\t\\t\\t'tsc' : _is_bit_set(ebx, 4),\\n\\t\\t\\t\\t'msr' : _is_bit_set(ebx, 5),\\n\\t\\t\\t\\t'pae' : _is_bit_set(ebx, 6),\\n\\t\\t\\t\\t'mce' : _is_bit_set(ebx, 7),\\n\\t\\t\\t\\t'cx8' : _is_bit_set(ebx, 8),\\n\\t\\t\\t\\t'apic' : _is_bit_set(ebx, 9),\\n\\t\\t\\t\\t#'reserved' : _is_bit_set(ebx, 10),\\n\\t\\t\\t\\t'syscall' : _is_bit_set(ebx, 11),\\n\\t\\t\\t\\t'mtrr' : _is_bit_set(ebx, 12),\\n\\t\\t\\t\\t'pge' : _is_bit_set(ebx, 13),\\n\\t\\t\\t\\t'mca' : _is_bit_set(ebx, 14),\\n\\t\\t\\t\\t'cmov' : _is_bit_set(ebx, 15),\\n\\t\\t\\t\\t'pat' : _is_bit_set(ebx, 16),\\n\\t\\t\\t\\t'pse36' : _is_bit_set(ebx, 17),\\n\\t\\t\\t\\t#'reserved' : _is_bit_set(ebx, 18),\\n\\t\\t\\t\\t'mp' : _is_bit_set(ebx, 19),\\n\\t\\t\\t\\t'nx' : _is_bit_set(ebx, 20),\\n\\t\\t\\t\\t#'reserved' : _is_bit_set(ebx, 21),\\n\\t\\t\\t\\t'mmxext' : _is_bit_set(ebx, 22),\\n\\t\\t\\t\\t'mmx' : _is_bit_set(ebx, 23),\\n\\t\\t\\t\\t'fxsr' : _is_bit_set(ebx, 24),\\n\\t\\t\\t\\t'fxsr_opt' : _is_bit_set(ebx, 25),\\n\\t\\t\\t\\t'pdpe1gp' : _is_bit_set(ebx, 26),\\n\\t\\t\\t\\t'rdtscp' : _is_bit_set(ebx, 27),\\n\\t\\t\\t\\t#'reserved' : _is_bit_set(ebx, 28),\\n\\t\\t\\t\\t'lm' : _is_bit_set(ebx, 29),\\n\\t\\t\\t\\t'3dnowext' : _is_bit_set(ebx, 30),\\n\\t\\t\\t\\t'3dnow' : _is_bit_set(ebx, 31),\\n\\n\\t\\t\\t\\t'lahf_lm' : _is_bit_set(ecx, 0),\\n\\t\\t\\t\\t'cmp_legacy' : _is_bit_set(ecx, 1),\\n\\t\\t\\t\\t'svm' : _is_bit_set(ecx, 2),\\n\\t\\t\\t\\t'extapic' : _is_bit_set(ecx, 3),\\n\\t\\t\\t\\t'cr8_legacy' : _is_bit_set(ecx, 4),\\n\\t\\t\\t\\t'abm' : _is_bit_set(ecx, 5),\\n\\t\\t\\t\\t'sse4a' : _is_bit_set(ecx, 6),\\n\\t\\t\\t\\t'misalignsse' : _is_bit_set(ecx, 7),\\n\\t\\t\\t\\t'3dnowprefetch' : _is_bit_set(ecx, 8),\\n\\t\\t\\t\\t'osvw' : _is_bit_set(ecx, 9),\\n\\t\\t\\t\\t'ibs' : _is_bit_set(ecx, 10),\\n\\t\\t\\t\\t'xop' : _is_bit_set(ecx, 11),\\n\\t\\t\\t\\t'skinit' : _is_bit_set(ecx, 12),\\n\\t\\t\\t\\t'wdt' : _is_bit_set(ecx, 13),\\n\\t\\t\\t\\t#'reserved' : _is_bit_set(ecx, 14),\\n\\t\\t\\t\\t'lwp' : _is_bit_set(ecx, 15),\\n\\t\\t\\t\\t'fma4' : _is_bit_set(ecx, 16),\\n\\t\\t\\t\\t'tce' : _is_bit_set(ecx, 17),\\n\\t\\t\\t\\t#'reserved' : _is_bit_set(ecx, 18),\\n\\t\\t\\t\\t'nodeid_msr' : _is_bit_set(ecx, 19),\\n\\t\\t\\t\\t#'reserved' : _is_bit_set(ecx, 20),\\n\\t\\t\\t\\t'tbm' : _is_bit_set(ecx, 21),\\n\\t\\t\\t\\t'topoext' : _is_bit_set(ecx, 22),\\n\\t\\t\\t\\t'perfctr_core' : _is_bit_set(ecx, 23),\\n\\t\\t\\t\\t'perfctr_nb' : _is_bit_set(ecx, 24),\\n\\t\\t\\t\\t#'reserved' : _is_bit_set(ecx, 25),\\n\\t\\t\\t\\t'dbx' : _is_bit_set(ecx, 26),\\n\\t\\t\\t\\t'perftsc' : _is_bit_set(ecx, 27),\\n\\t\\t\\t\\t'pci_l2i' : _is_bit_set(ecx, 28),\\n\\t\\t\\t\\t#'reserved' : _is_bit_set(ecx, 29),\\n\\t\\t\\t\\t#'reserved' : _is_bit_set(ecx, 30),\\n\\t\\t\\t\\t#'reserved' : _is_bit_set(ecx, 31)\\n\\t\\t\\t}\\n\\n\\t\\t\\t# Get a list of only the flags that are true\\n\\t\\t\\textended_flags = [k for k, v in extended_flags.items() if v]\\n\\t\\t\\tflags += extended_flags\\n\\n\\t\\tflags.sort()\\n\\t\\treturn flags\\n\\n\\t# http://en.wikipedia.org/wiki/CPUID#EAX.3D80000002h.2C80000003h.2C80000004h:_Processor_Brand_String\\n\\tdef get_processor_brand(self, max_extension_support):\\n\\t\\tprocessor_brand = \\\"\\\"\\n\\n\\t\\t# Processor brand string\\n\\t\\tif max_extension_support >= 0x80000004:\\n\\t\\t\\tinstructions = [\\n\\t\\t\\t\\tb\\\"\\\\xB8\\\\x02\\\\x00\\\\x00\\\\x80\\\", # mov ax,0x80000002\\n\\t\\t\\t\\tb\\\"\\\\xB8\\\\x03\\\\x00\\\\x00\\\\x80\\\", # mov ax,0x80000003\\n\\t\\t\\t\\tb\\\"\\\\xB8\\\\x04\\\\x00\\\\x00\\\\x80\\\"  # mov ax,0x80000004\\n\\t\\t\\t]\\n\\t\\t\\tfor instruction in instructions:\\n\\t\\t\\t\\t# EAX\\n\\t\\t\\t\\teax = self._run_asm(\\n\\t\\t\\t\\t\\tinstruction,  # mov ax,0x8000000?\\n\\t\\t\\t\\t\\tb\\\"\\\\x0f\\\\xa2\\\"   # cpuid\\n\\t\\t\\t\\t\\tb\\\"\\\\x89\\\\xC0\\\"   # mov ax,ax\\n\\t\\t\\t\\t\\tb\\\"\\\\xC3\\\"       # ret\\n\\t\\t\\t\\t)\\n\\n\\t\\t\\t\\t# EBX\\n\\t\\t\\t\\tebx = self._run_asm(\\n\\t\\t\\t\\t\\tinstruction,  # mov ax,0x8000000?\\n\\t\\t\\t\\t\\tb\\\"\\\\x0f\\\\xa2\\\"   # cpuid\\n\\t\\t\\t\\t\\tb\\\"\\\\x89\\\\xD8\\\"   # mov ax,bx\\n\\t\\t\\t\\t\\tb\\\"\\\\xC3\\\"       # ret\\n\\t\\t\\t\\t)\\n\\n\\t\\t\\t\\t# ECX\\n\\t\\t\\t\\tecx = self._run_asm(\\n\\t\\t\\t\\t\\tinstruction,  # mov ax,0x8000000?\\n\\t\\t\\t\\t\\tb\\\"\\\\x0f\\\\xa2\\\"   # cpuid\\n\\t\\t\\t\\t\\tb\\\"\\\\x89\\\\xC8\\\"   # mov ax,cx\\n\\t\\t\\t\\t\\tb\\\"\\\\xC3\\\"       # ret\\n\\t\\t\\t\\t)\\n\\n\\t\\t\\t\\t# EDX\\n\\t\\t\\t\\tedx = self._run_asm(\\n\\t\\t\\t\\t\\tinstruction,  # mov ax,0x8000000?\\n\\t\\t\\t\\t\\tb\\\"\\\\x0f\\\\xa2\\\"   # cpuid\\n\\t\\t\\t\\t\\tb\\\"\\\\x89\\\\xD0\\\"   # mov ax,dx\\n\\t\\t\\t\\t\\tb\\\"\\\\xC3\\\"       # ret\\n\\t\\t\\t\\t)\\n\\n\\t\\t\\t\\t# Combine each of the 4 bytes in each register into the string\\n\\t\\t\\t\\tfor reg in [eax, ebx, ecx, edx]:\\n\\t\\t\\t\\t\\tfor n in [0, 8, 16, 24]:\\n\\t\\t\\t\\t\\t\\tprocessor_brand += chr((reg >> n) & 0xFF)\\n\\n\\t\\t# Strip off any trailing NULL terminators and white space\\n\\t\\tprocessor_brand = processor_brand.strip(\\\"\\\\0\\\").strip()\\n\\n\\t\\treturn processor_brand\\n\\n\\t# http://en.wikipedia.org/wiki/CPUID#EAX.3D80000006h:_Extended_L2_Cache_Features\\n\\tdef get_cache(self, max_extension_support):\\n\\t\\tcache_info = {}\\n\\n\\t\\t# Just return if the cache feature is not supported\\n\\t\\tif max_extension_support < 0x80000006:\\n\\t\\t\\treturn cache_info\\n\\n\\t\\t# ECX\\n\\t\\tecx = self._run_asm(\\n\\t\\t\\tb\\\"\\\\xB8\\\\x06\\\\x00\\\\x00\\\\x80\\\"  # mov ax,0x80000006\\n\\t\\t\\tb\\\"\\\\x0f\\\\xa2\\\"              # cpuid\\n\\t\\t\\tb\\\"\\\\x89\\\\xC8\\\"              # mov ax,cx\\n\\t\\t\\tb\\\"\\\\xC3\\\"                   # ret\\n\\t\\t)\\n\\n\\t\\tcache_info = {\\n\\t\\t\\t'size_b' : (ecx & 0xFF) * 1024,\\n\\t\\t\\t'associativity' : (ecx >> 12) & 0xF,\\n\\t\\t\\t'line_size_b' : (ecx >> 16) & 0xFFFF\\n\\t\\t}\\n\\n\\t\\treturn cache_info\\n\\n\\tdef get_ticks_func(self):\\n\\t\\tretval = None\\n\\n\\t\\tif DataSource.bits == '32bit':\\n\\t\\t\\t# Works on x86_32\\n\\t\\t\\trestype = None\\n\\t\\t\\targtypes = (ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(ctypes.c_uint))\\n\\t\\t\\tget_ticks_x86_32 = self._asm_func(restype, argtypes,\\n\\t\\t\\t\\t[\\n\\t\\t\\t\\tb\\\"\\\\x55\\\",         # push bp\\n\\t\\t\\t\\tb\\\"\\\\x89\\\\xE5\\\",     # mov bp,sp\\n\\t\\t\\t\\tb\\\"\\\\x31\\\\xC0\\\",     # xor ax,ax\\n\\t\\t\\t\\tb\\\"\\\\x0F\\\\xA2\\\",     # cpuid\\n\\t\\t\\t\\tb\\\"\\\\x0F\\\\x31\\\",     # rdtsc\\n\\t\\t\\t\\tb\\\"\\\\x8B\\\\x5D\\\\x08\\\", # mov bx,[di+0x8]\\n\\t\\t\\t\\tb\\\"\\\\x8B\\\\x4D\\\\x0C\\\", # mov cx,[di+0xc]\\n\\t\\t\\t\\tb\\\"\\\\x89\\\\x13\\\",     # mov [bp+di],dx\\n\\t\\t\\t\\tb\\\"\\\\x89\\\\x01\\\",     # mov [bx+di],ax\\n\\t\\t\\t\\tb\\\"\\\\x5D\\\",         # pop bp\\n\\t\\t\\t\\tb\\\"\\\\xC3\\\"          # ret\\n\\t\\t\\t\\t]\\n\\t\\t\\t)\\n\\n\\t\\t\\t# Monkey patch func to combine high and low args into one return\\n\\t\\t\\told_func = get_ticks_x86_32.func\\n\\t\\t\\tdef new_func():\\n\\t\\t\\t\\t# Pass two uint32s into function\\n\\t\\t\\t\\thigh = ctypes.c_uint32(0)\\n\\t\\t\\t\\tlow = ctypes.c_uint32(0)\\n\\t\\t\\t\\told_func(ctypes.byref(high), ctypes.byref(low))\\n\\n\\t\\t\\t\\t# Shift the two uint32s into one uint64\\n\\t\\t\\t\\tretval = ((high.value << 32) & 0xFFFFFFFF00000000) | low.value\\n\\t\\t\\t\\treturn retval\\n\\t\\t\\tget_ticks_x86_32.func = new_func\\n\\n\\t\\t\\tretval = get_ticks_x86_32\\n\\t\\telif DataSource.bits == '64bit':\\n\\t\\t\\t# Works on x86_64\\n\\t\\t\\trestype = ctypes.c_uint64\\n\\t\\t\\targtypes = ()\\n\\t\\t\\tget_ticks_x86_64 = self._asm_func(restype, argtypes,\\n\\t\\t\\t\\t[\\n\\t\\t\\t\\tb\\\"\\\\x48\\\",         # dec ax\\n\\t\\t\\t\\tb\\\"\\\\x31\\\\xC0\\\",     # xor ax,ax\\n\\t\\t\\t\\tb\\\"\\\\x0F\\\\xA2\\\",     # cpuid\\n\\t\\t\\t\\tb\\\"\\\\x0F\\\\x31\\\",     # rdtsc\\n\\t\\t\\t\\tb\\\"\\\\x48\\\",         # dec ax\\n\\t\\t\\t\\tb\\\"\\\\xC1\\\\xE2\\\\x20\\\", # shl dx,byte 0x20\\n\\t\\t\\t\\tb\\\"\\\\x48\\\",         # dec ax\\n\\t\\t\\t\\tb\\\"\\\\x09\\\\xD0\\\",     # or ax,dx\\n\\t\\t\\t\\tb\\\"\\\\xC3\\\",         # ret\\n\\t\\t\\t\\t]\\n\\t\\t\\t)\\n\\n\\t\\t\\tretval = get_ticks_x86_64\\n\\t\\treturn retval\\n\\n\\tdef get_raw_hz(self):\\n\\t\\tfrom time import sleep\\n\\n\\t\\tticks_fn = self.get_ticks_func()\\n\\n\\t\\tstart = ticks_fn.func()\\n\\t\\tsleep(1)\\n\\t\\tend = ticks_fn.func()\\n\\n\\t\\tticks = (end - start)\\n\\t\\tticks_fn.free()\\n\\n\\t\\treturn ticks\\n\\ndef _get_cpu_info_from_cpuid_actual():\\n\\t'''\\n\\tWarning! This function has the potential to crash the Python runtime.\\n\\tDo not call it directly. Use the _get_cpu_info_from_cpuid function instead.\\n\\tIt will safely call this function in another process.\\n\\t'''\\n\\n\\tfrom io import StringIO\\n\\n\\ttrace = Trace(True, True)\\n\\tinfo = {}\\n\\n\\t# Pipe stdout and stderr to strings\\n\\tsys.stdout = trace._stdout\\n\\tsys.stderr = trace._stderr\\n\\n\\ttry:\\n\\t\\t# Get the CPU arch and bits\\n\\t\\tarch, bits = _parse_arch(DataSource.arch_string_raw)\\n\\n\\t\\t# Return none if this is not an X86 CPU\\n\\t\\tif not arch in ['X86_32', 'X86_64']:\\n\\t\\t\\ttrace.fail('Not running on X86_32 or X86_64. Skipping ...')\\n\\t\\t\\treturn trace.to_dict(info, True)\\n\\n\\t\\t# Return none if SE Linux is in enforcing mode\\n\\t\\tcpuid = CPUID(trace)\\n\\t\\tif cpuid.is_selinux_enforcing:\\n\\t\\t\\ttrace.fail('SELinux is enforcing. Skipping ...')\\n\\t\\t\\treturn trace.to_dict(info, True)\\n\\n\\t\\t# Get the cpu info from the CPUID register\\n\\t\\tmax_extension_support = cpuid.get_max_extension_support()\\n\\t\\tcache_info = cpuid.get_cache(max_extension_support)\\n\\t\\tinfo = cpuid.get_info()\\n\\n\\t\\tprocessor_brand = cpuid.get_processor_brand(max_extension_support)\\n\\n\\t\\t# Get the Hz and scale\\n\\t\\thz_actual = cpuid.get_raw_hz()\\n\\t\\thz_actual = _to_decimal_string(hz_actual)\\n\\n\\t\\t# Get the Hz and scale\\n\\t\\thz_advertised, scale = _parse_cpu_brand_string(processor_brand)\\n\\t\\tinfo = {\\n\\t\\t'vendor_id_raw' : cpuid.get_vendor_id(),\\n\\t\\t'hardware_raw' : '',\\n\\t\\t'brand_raw' : processor_brand,\\n\\n\\t\\t'hz_advertised_friendly' : _hz_short_to_friendly(hz_advertised, scale),\\n\\t\\t'hz_actual_friendly' : _hz_short_to_friendly(hz_actual, 0),\\n\\t\\t'hz_advertised' : _hz_short_to_full(hz_advertised, scale),\\n\\t\\t'hz_actual' : _hz_short_to_full(hz_actual, 0),\\n\\n\\t\\t'l2_cache_size' : cache_info['size_b'],\\n\\t\\t'l2_cache_line_size' : cache_info['line_size_b'],\\n\\t\\t'l2_cache_associativity' : cache_info['associativity'],\\n\\n\\t\\t'stepping' : info['stepping'],\\n\\t\\t'model' : info['model'],\\n\\t\\t'family' : info['family'],\\n\\t\\t'processor_type' : info['processor_type'],\\n\\t\\t'flags' : cpuid.get_flags(max_extension_support)\\n\\t\\t}\\n\\n\\t\\tinfo = _filter_dict_keys_with_empty_values(info)\\n\\t\\ttrace.success()\\n\\texcept Exception as err:\\n\\t\\tfrom traceback import format_exc\\n\\t\\terr_string = format_exc()\\n\\t\\ttrace._err = ''.join(['\\\\t\\\\t{0}\\\\n'.format(n) for n in err_string.split('\\\\n')]) + '\\\\n'\\n\\t\\treturn trace.to_dict(info, True)\\n\\n\\treturn trace.to_dict(info, False)\\n\\ndef _get_cpu_info_from_cpuid_subprocess_wrapper(queue):\\n\\torig_stdout = sys.stdout\\n\\torig_stderr = sys.stderr\\n\\n\\toutput = _get_cpu_info_from_cpuid_actual()\\n\\n\\tsys.stdout = orig_stdout\\n\\tsys.stderr = orig_stderr\\n\\n\\tqueue.put(_obj_to_b64(output))\\n\\ndef _get_cpu_info_from_cpuid():\\n\\t'''\\n\\tReturns the CPU info gathered by querying the X86 cpuid register in a new process.\\n\\tReturns {} on non X86 cpus.\\n\\tReturns {} if SELinux is in enforcing mode.\\n\\t'''\\n\\n\\tg_trace.header('Tying to get info from CPUID ...')\\n\\n\\tfrom multiprocessing import Process, Queue\\n\\n\\t# Return {} if can't cpuid\\n\\tif not DataSource.can_cpuid:\\n\\t\\tg_trace.fail('Can\\\\'t CPUID. Skipping ...')\\n\\t\\treturn {}\\n\\n\\t# Get the CPU arch and bits\\n\\tarch, bits = _parse_arch(DataSource.arch_string_raw)\\n\\n\\t# Return {} if this is not an X86 CPU\\n\\tif not arch in ['X86_32', 'X86_64']:\\n\\t\\tg_trace.fail('Not running on X86_32 or X86_64. Skipping ...')\\n\\t\\treturn {}\\n\\n\\ttry:\\n\\t\\tif CAN_CALL_CPUID_IN_SUBPROCESS:\\n\\t\\t\\t# Start running the function in a subprocess\\n\\t\\t\\tqueue = Queue()\\n\\t\\t\\tp = Process(target=_get_cpu_info_from_cpuid_subprocess_wrapper, args=(queue,))\\n\\t\\t\\tp.start()\\n\\n\\t\\t\\t# Wait for the process to end, while it is still alive\\n\\t\\t\\twhile p.is_alive():\\n\\t\\t\\t\\tp.join(0)\\n\\n\\t\\t\\t# Return {} if it failed\\n\\t\\t\\tif p.exitcode != 0:\\n\\t\\t\\t\\tg_trace.fail('Failed to run CPUID in process. Skipping ...')\\n\\t\\t\\t\\treturn {}\\n\\n\\t\\t\\t# Return {} if no results\\n\\t\\t\\tif queue.empty():\\n\\t\\t\\t\\tg_trace.fail('Failed to get anything from CPUID process. Skipping ...')\\n\\t\\t\\t\\treturn {}\\n\\t\\t\\t# Return the result, only if there is something to read\\n\\t\\t\\telse:\\n\\t\\t\\t\\toutput = _b64_to_obj(queue.get())\\n\\t\\t\\t\\timport pprint\\n\\t\\t\\t\\tpp = pprint.PrettyPrinter(indent=4)\\n\\t\\t\\t\\t#pp.pprint(output)\\n\\n\\t\\t\\t\\tif 'output' in output and output['output']:\\n\\t\\t\\t\\t\\tg_trace.write(output['output'])\\n\\n\\t\\t\\t\\tif 'stdout' in output and output['stdout']:\\n\\t\\t\\t\\t\\tsys.stdout.write('{0}\\\\n'.format(output['stdout']))\\n\\t\\t\\t\\t\\tsys.stdout.flush()\\n\\n\\t\\t\\t\\tif 'stderr' in output and output['stderr']:\\n\\t\\t\\t\\t\\tsys.stderr.write('{0}\\\\n'.format(output['stderr']))\\n\\t\\t\\t\\t\\tsys.stderr.flush()\\n\\n\\t\\t\\t\\tif 'is_fail' not in output:\\n\\t\\t\\t\\t\\tg_trace.fail('Failed to get is_fail from CPUID process. Skipping ...')\\n\\t\\t\\t\\t\\treturn {}\\n\\n\\t\\t\\t\\t# Fail if there was an exception\\n\\t\\t\\t\\tif 'err' in output and output['err']:\\n\\t\\t\\t\\t\\tg_trace.fail('Failed to run CPUID in process. Skipping ...')\\n\\t\\t\\t\\t\\tg_trace.write(output['err'])\\n\\t\\t\\t\\t\\tg_trace.write('Failed ...')\\n\\t\\t\\t\\t\\treturn {}\\n\\n\\t\\t\\t\\tif 'is_fail' in output and output['is_fail']:\\n\\t\\t\\t\\t\\tg_trace.write('Failed ...')\\n\\t\\t\\t\\t\\treturn {}\\n\\n\\t\\t\\t\\tif 'info' not in output or not output['info']:\\n\\t\\t\\t\\t\\tg_trace.fail('Failed to get return info from CPUID process. Skipping ...')\\n\\t\\t\\t\\t\\treturn {}\\n\\n\\t\\t\\t\\treturn output['info']\\n\\t\\telse:\\n\\t\\t\\t# FIXME: This should write the values like in the above call to actual\\n\\t\\t\\torig_stdout = sys.stdout\\n\\t\\t\\torig_stderr = sys.stderr\\n\\n\\t\\t\\toutput = _get_cpu_info_from_cpuid_actual()\\n\\n\\t\\t\\tsys.stdout = orig_stdout\\n\\t\\t\\tsys.stderr = orig_stderr\\n\\n\\t\\t\\tg_trace.success()\\n\\t\\t\\treturn output['info']\\n\\texcept Exception as err:\\n\\t\\tg_trace.fail(err)\\n\\n\\t# Return {} if everything failed\\n\\treturn {}\\n\\ndef _get_cpu_info_from_proc_cpuinfo():\\n\\t'''\\n\\tReturns the CPU info gathered from /proc/cpuinfo.\\n\\tReturns {} if /proc/cpuinfo is not found.\\n\\t'''\\n\\n\\tg_trace.header('Tying to get info from /proc/cpuinfo ...')\\n\\n\\ttry:\\n\\t\\t# Just return {} if there is no cpuinfo\\n\\t\\tif not DataSource.has_proc_cpuinfo():\\n\\t\\t\\tg_trace.fail('Failed to find /proc/cpuinfo. Skipping ...')\\n\\t\\t\\treturn {}\\n\\n\\t\\treturncode, output = DataSource.cat_proc_cpuinfo()\\n\\t\\tif returncode != 0:\\n\\t\\t\\tg_trace.fail('Failed to run cat /proc/cpuinfo. Skipping ...')\\n\\t\\t\\treturn {}\\n\\n\\t\\t# Various fields\\n\\t\\tvendor_id = _get_field(False, output, None, '', 'vendor_id', 'vendor id', 'vendor')\\n\\t\\tprocessor_brand = _get_field(True, output, None, None, 'model name', 'cpu', 'processor', 'uarch')\\n\\t\\tcache_size = _get_field(False, output, None, '', 'cache size')\\n\\t\\tstepping = _get_field(False, output, int, -1, 'stepping')\\n\\t\\tmodel = _get_field(False, output, int, -1, 'model')\\n\\t\\tfamily = _get_field(False, output, int, -1, 'cpu family')\\n\\t\\thardware = _get_field(False, output, None, '', 'Hardware')\\n\\n\\t\\t# Flags\\n\\t\\tflags = _get_field(False, output, None, None, 'flags', 'Features', 'ASEs implemented')\\n\\t\\tif flags:\\n\\t\\t\\tflags = flags.split()\\n\\t\\t\\tflags.sort()\\n\\n\\t\\t# Check for other cache format\\n\\t\\tif not cache_size:\\n\\t\\t\\ttry:\\n\\t\\t\\t\\tfor i in range(0, 10):\\n\\t\\t\\t\\t\\tname = \\\"cache{0}\\\".format(i)\\n\\t\\t\\t\\t\\tvalue = _get_field(False, output, None, None, name)\\n\\t\\t\\t\\t\\tif value:\\n\\t\\t\\t\\t\\t\\tvalue = [entry.split('=') for entry in value.split(' ')]\\n\\t\\t\\t\\t\\t\\tvalue = dict(value)\\n\\t\\t\\t\\t\\t\\tif 'level' in value and value['level'] == '3' and 'size' in value:\\n\\t\\t\\t\\t\\t\\t\\tcache_size = value['size']\\n\\t\\t\\t\\t\\t\\t\\tbreak\\n\\t\\t\\texcept Exception:\\n\\t\\t\\t\\tpass\\n\\n\\t\\t# Convert from MHz string to Hz\\n\\t\\thz_actual = _get_field(False, output, None, '', 'cpu MHz', 'cpu speed', 'clock', 'cpu MHz dynamic', 'cpu MHz static')\\n\\t\\thz_actual = hz_actual.lower().rstrip('mhz').strip()\\n\\t\\thz_actual = _to_decimal_string(hz_actual)\\n\\n\\t\\t# Convert from GHz/MHz string to Hz\\n\\t\\thz_advertised, scale = (None, 0)\\n\\t\\ttry:\\n\\t\\t\\thz_advertised, scale = _parse_cpu_brand_string(processor_brand)\\n\\t\\texcept Exception:\\n\\t\\t\\tpass\\n\\n\\t\\tinfo = {\\n\\t\\t'hardware_raw' : hardware,\\n\\t\\t'brand_raw' : processor_brand,\\n\\n\\t\\t'l3_cache_size' : _friendly_bytes_to_int(cache_size),\\n\\t\\t'flags' : flags,\\n\\t\\t'vendor_id_raw' : vendor_id,\\n\\t\\t'stepping' : stepping,\\n\\t\\t'model' : model,\\n\\t\\t'family' : family,\\n\\t\\t}\\n\\n\\t\\t# Make the Hz the same for actual and advertised if missing any\\n\\t\\tif not hz_advertised or hz_advertised == '0.0':\\n\\t\\t\\thz_advertised = hz_actual\\n\\t\\t\\tscale = 6\\n\\t\\telif not hz_actual or hz_actual == '0.0':\\n\\t\\t\\thz_actual = hz_advertised\\n\\n\\t\\t# Add the Hz if there is one\\n\\t\\tif _hz_short_to_full(hz_advertised, scale) > (0, 0):\\n\\t\\t\\tinfo['hz_advertised_friendly'] = _hz_short_to_friendly(hz_advertised, scale)\\n\\t\\t\\tinfo['hz_advertised'] = _hz_short_to_full(hz_advertised, scale)\\n\\t\\tif _hz_short_to_full(hz_actual, scale) > (0, 0):\\n\\t\\t\\tinfo['hz_actual_friendly'] = _hz_short_to_friendly(hz_actual, 6)\\n\\t\\t\\tinfo['hz_actual'] = _hz_short_to_full(hz_actual, 6)\\n\\n\\t\\tinfo = _filter_dict_keys_with_empty_values(info, {'stepping':0, 'model':0, 'family':0})\\n\\t\\tg_trace.success()\\n\\t\\treturn info\\n\\texcept Exception as err:\\n\\t\\tg_trace.fail(err)\\n\\t\\t#raise # NOTE: To have this throw on error, uncomment this line\\n\\t\\treturn {}\\n\\ndef _get_cpu_info_from_cpufreq_info():\\n\\t'''\\n\\tReturns the CPU info gathered from cpufreq-info.\\n\\tReturns {} if cpufreq-info is not found.\\n\\t'''\\n\\n\\tg_trace.header('Tying to get info from cpufreq-info ...')\\n\\n\\ttry:\\n\\t\\thz_brand, scale = '0.0', 0\\n\\n\\t\\tif not DataSource.has_cpufreq_info():\\n\\t\\t\\tg_trace.fail('Failed to find cpufreq-info. Skipping ...')\\n\\t\\t\\treturn {}\\n\\n\\t\\treturncode, output = DataSource.cpufreq_info()\\n\\t\\tif returncode != 0:\\n\\t\\t\\tg_trace.fail('Failed to run cpufreq-info. Skipping ...')\\n\\t\\t\\treturn {}\\n\\n\\t\\thz_brand = output.split('current CPU frequency is')[1].split('\\\\n')[0]\\n\\t\\ti = hz_brand.find('Hz')\\n\\t\\tassert(i != -1)\\n\\t\\thz_brand = hz_brand[0 : i+2].strip().lower()\\n\\n\\t\\tif hz_brand.endswith('mhz'):\\n\\t\\t\\tscale = 6\\n\\t\\telif hz_brand.endswith('ghz'):\\n\\t\\t\\tscale = 9\\n\\t\\thz_brand = hz_brand.rstrip('mhz').rstrip('ghz').strip()\\n\\t\\thz_brand = _to_decimal_string(hz_brand)\\n\\n\\t\\tinfo = {\\n\\t\\t\\t'hz_advertised_friendly' : _hz_short_to_friendly(hz_brand, scale),\\n\\t\\t\\t'hz_actual_friendly' : _hz_short_to_friendly(hz_brand, scale),\\n\\t\\t\\t'hz_advertised' : _hz_short_to_full(hz_brand, scale),\\n\\t\\t\\t'hz_actual' : _hz_short_to_full(hz_brand, scale),\\n\\t\\t}\\n\\n\\t\\tinfo = _filter_dict_keys_with_empty_values(info)\\n\\t\\tg_trace.success()\\n\\t\\treturn info\\n\\texcept Exception as err:\\n\\t\\tg_trace.fail(err)\\n\\t\\t#raise # NOTE: To have this throw on error, uncomment this line\\n\\t\\treturn {}\\n\\ndef _get_cpu_info_from_lscpu():\\n\\t'''\\n\\tReturns the CPU info gathered from lscpu.\\n\\tReturns {} if lscpu is not found.\\n\\t'''\\n\\n\\tg_trace.header('Tying to get info from lscpu ...')\\n\\n\\ttry:\\n\\t\\tif not DataSource.has_lscpu():\\n\\t\\t\\tg_trace.fail('Failed to find lscpu. Skipping ...')\\n\\t\\t\\treturn {}\\n\\n\\t\\treturncode, output = DataSource.lscpu()\\n\\t\\tif returncode != 0:\\n\\t\\t\\tg_trace.fail('Failed to run lscpu. Skipping ...')\\n\\t\\t\\treturn {}\\n\\n\\t\\tinfo = {}\\n\\n\\t\\tnew_hz = _get_field(False, output, None, None, 'CPU max MHz', 'CPU MHz')\\n\\t\\tif new_hz:\\n\\t\\t\\tnew_hz = _to_decimal_string(new_hz)\\n\\t\\t\\tscale = 6\\n\\t\\t\\tinfo['hz_advertised_friendly'] = _hz_short_to_friendly(new_hz, scale)\\n\\t\\t\\tinfo['hz_actual_friendly'] = _hz_short_to_friendly(new_hz, scale)\\n\\t\\t\\tinfo['hz_advertised'] = _hz_short_to_full(new_hz, scale)\\n\\t\\t\\tinfo['hz_actual'] = _hz_short_to_full(new_hz, scale)\\n\\n\\t\\tnew_hz = _get_field(False, output, None, None, 'CPU dynamic MHz', 'CPU static MHz')\\n\\t\\tif new_hz:\\n\\t\\t\\tnew_hz = _to_decimal_string(new_hz)\\n\\t\\t\\tscale = 6\\n\\t\\t\\tinfo['hz_advertised_friendly'] = _hz_short_to_friendly(new_hz, scale)\\n\\t\\t\\tinfo['hz_actual_friendly'] = _hz_short_to_friendly(new_hz, scale)\\n\\t\\t\\tinfo['hz_advertised'] = _hz_short_to_full(new_hz, scale)\\n\\t\\t\\tinfo['hz_actual'] = _hz_short_to_full(new_hz, scale)\\n\\n\\t\\tvendor_id = _get_field(False, output, None, None, 'Vendor ID')\\n\\t\\tif vendor_id:\\n\\t\\t\\tinfo['vendor_id_raw'] = vendor_id\\n\\n\\t\\tbrand = _get_field(False, output, None, None, 'Model name')\\n\\t\\tif brand:\\n\\t\\t\\tinfo['brand_raw'] = brand\\n\\t\\telse:\\n\\t\\t\\tbrand = _get_field(False, output, None, None, 'Model')\\n\\t\\t\\tif brand and not brand.isdigit():\\n\\t\\t\\t\\tinfo['brand_raw'] = brand\\n\\n\\t\\tfamily = _get_field(False, output, None, None, 'CPU family')\\n\\t\\tif family and family.isdigit():\\n\\t\\t\\tinfo['family'] = int(family)\\n\\n\\t\\tstepping = _get_field(False, output, None, None, 'Stepping')\\n\\t\\tif stepping and stepping.isdigit():\\n\\t\\t\\tinfo['stepping'] = int(stepping)\\n\\n\\t\\tmodel = _get_field(False, output, None, None, 'Model')\\n\\t\\tif model and model.isdigit():\\n\\t\\t\\tinfo['model'] = int(model)\\n\\n\\t\\tl1_data_cache_size = _get_field(False, output, None, None, 'L1d cache')\\n\\t\\tif l1_data_cache_size:\\n\\t\\t\\tl1_data_cache_size = l1_data_cache_size.split('(')[0].strip()\\n\\t\\t\\tinfo['l1_data_cache_size'] = _friendly_bytes_to_int(l1_data_cache_size)\\n\\n\\t\\tl1_instruction_cache_size = _get_field(False, output, None, None, 'L1i cache')\\n\\t\\tif l1_instruction_cache_size:\\n\\t\\t\\tl1_instruction_cache_size = l1_instruction_cache_size.split('(')[0].strip()\\n\\t\\t\\tinfo['l1_instruction_cache_size'] = _friendly_bytes_to_int(l1_instruction_cache_size)\\n\\n\\t\\tl2_cache_size = _get_field(False, output, None, None, 'L2 cache', 'L2d cache')\\n\\t\\tif l2_cache_size:\\n\\t\\t\\tl2_cache_size = l2_cache_size.split('(')[0].strip()\\n\\t\\t\\tinfo['l2_cache_size'] = _friendly_bytes_to_int(l2_cache_size)\\n\\n\\t\\tl3_cache_size = _get_field(False, output, None, None, 'L3 cache')\\n\\t\\tif l3_cache_size:\\n\\t\\t\\tl3_cache_size = l3_cache_size.split('(')[0].strip()\\n\\t\\t\\tinfo['l3_cache_size'] = _friendly_bytes_to_int(l3_cache_size)\\n\\n\\t\\t# Flags\\n\\t\\tflags = _get_field(False, output, None, None, 'flags', 'Features', 'ASEs implemented')\\n\\t\\tif flags:\\n\\t\\t\\tflags = flags.split()\\n\\t\\t\\tflags.sort()\\n\\t\\t\\tinfo['flags'] = flags\\n\\n\\t\\tinfo = _filter_dict_keys_with_empty_values(info, {'stepping':0, 'model':0, 'family':0})\\n\\t\\tg_trace.success()\\n\\t\\treturn info\\n\\texcept Exception as err:\\n\\t\\tg_trace.fail(err)\\n\\t\\t#raise # NOTE: To have this throw on error, uncomment this line\\n\\t\\treturn {}\\n\\ndef _get_cpu_info_from_dmesg():\\n\\t'''\\n\\tReturns the CPU info gathered from dmesg.\\n\\tReturns {} if dmesg is not found or does not have the desired info.\\n\\t'''\\n\\n\\tg_trace.header('Tying to get info from the dmesg ...')\\n\\n\\t# Just return {} if this arch has an unreliable dmesg log\\n\\tarch, bits = _parse_arch(DataSource.arch_string_raw)\\n\\tif arch in ['S390X']:\\n\\t\\tg_trace.fail('Running on S390X. Skipping ...')\\n\\t\\treturn {}\\n\\n\\t# Just return {} if there is no dmesg\\n\\tif not DataSource.has_dmesg():\\n\\t\\tg_trace.fail('Failed to find dmesg. Skipping ...')\\n\\t\\treturn {}\\n\\n\\t# If dmesg fails return {}\\n\\treturncode, output = DataSource.dmesg_a()\\n\\tif output is None or returncode != 0:\\n\\t\\tg_trace.fail('Failed to run \\\\\\\"dmesg -a\\\\\\\". Skipping ...')\\n\\t\\treturn {}\\n\\n\\tinfo = _parse_dmesg_output(output)\\n\\tg_trace.success()\\n\\treturn info\\n\\n\\n# https://openpowerfoundation.org/wp-content/uploads/2016/05/LoPAPR_DRAFT_v11_24March2016_cmt1.pdf\\n# page 767\\ndef _get_cpu_info_from_ibm_pa_features():\\n\\t'''\\n\\tReturns the CPU info gathered from lsprop /proc/device-tree/cpus/*/ibm,pa-features\\n\\tReturns {} if lsprop is not found or ibm,pa-features does not have the desired info.\\n\\t'''\\n\\n\\tg_trace.header('Tying to get info from lsprop ...')\\n\\n\\ttry:\\n\\t\\t# Just return {} if there is no lsprop\\n\\t\\tif not DataSource.has_ibm_pa_features():\\n\\t\\t\\tg_trace.fail('Failed to find lsprop. Skipping ...')\\n\\t\\t\\treturn {}\\n\\n\\t\\t# If ibm,pa-features fails return {}\\n\\t\\treturncode, output = DataSource.ibm_pa_features()\\n\\t\\tif output is None or returncode != 0:\\n\\t\\t\\tg_trace.fail('Failed to glob /proc/device-tree/cpus/*/ibm,pa-features. Skipping ...')\\n\\t\\t\\treturn {}\\n\\n\\t\\t# Filter out invalid characters from output\\n\\t\\tvalue = output.split(\\\"ibm,pa-features\\\")[1].lower()\\n\\t\\tvalue = [s for s in value if s in list('0123456789abcfed')]\\n\\t\\tvalue = ''.join(value)\\n\\n\\t\\t# Get data converted to Uint32 chunks\\n\\t\\tleft = int(value[0 : 8], 16)\\n\\t\\tright = int(value[8 : 16], 16)\\n\\n\\t\\t# Get the CPU flags\\n\\t\\tflags = {\\n\\t\\t\\t# Byte 0\\n\\t\\t\\t'mmu' : _is_bit_set(left, 0),\\n\\t\\t\\t'fpu' : _is_bit_set(left, 1),\\n\\t\\t\\t'slb' : _is_bit_set(left, 2),\\n\\t\\t\\t'run' : _is_bit_set(left, 3),\\n\\t\\t\\t#'reserved' : _is_bit_set(left, 4),\\n\\t\\t\\t'dabr' : _is_bit_set(left, 5),\\n\\t\\t\\t'ne' : _is_bit_set(left, 6),\\n\\t\\t\\t'wtr' : _is_bit_set(left, 7),\\n\\n\\t\\t\\t# Byte 1\\n\\t\\t\\t'mcr' : _is_bit_set(left, 8),\\n\\t\\t\\t'dsisr' : _is_bit_set(left, 9),\\n\\t\\t\\t'lp' : _is_bit_set(left, 10),\\n\\t\\t\\t'ri' : _is_bit_set(left, 11),\\n\\t\\t\\t'dabrx' : _is_bit_set(left, 12),\\n\\t\\t\\t'sprg3' : _is_bit_set(left, 13),\\n\\t\\t\\t'rislb' : _is_bit_set(left, 14),\\n\\t\\t\\t'pp' : _is_bit_set(left, 15),\\n\\n\\t\\t\\t# Byte 2\\n\\t\\t\\t'vpm' : _is_bit_set(left, 16),\\n\\t\\t\\t'dss_2.05' : _is_bit_set(left, 17),\\n\\t\\t\\t#'reserved' : _is_bit_set(left, 18),\\n\\t\\t\\t'dar' : _is_bit_set(left, 19),\\n\\t\\t\\t#'reserved' : _is_bit_set(left, 20),\\n\\t\\t\\t'ppr' : _is_bit_set(left, 21),\\n\\t\\t\\t'dss_2.02' : _is_bit_set(left, 22),\\n\\t\\t\\t'dss_2.06' : _is_bit_set(left, 23),\\n\\n\\t\\t\\t# Byte 3\\n\\t\\t\\t'lsd_in_dscr' : _is_bit_set(left, 24),\\n\\t\\t\\t'ugr_in_dscr' : _is_bit_set(left, 25),\\n\\t\\t\\t#'reserved' : _is_bit_set(left, 26),\\n\\t\\t\\t#'reserved' : _is_bit_set(left, 27),\\n\\t\\t\\t#'reserved' : _is_bit_set(left, 28),\\n\\t\\t\\t#'reserved' : _is_bit_set(left, 29),\\n\\t\\t\\t#'reserved' : _is_bit_set(left, 30),\\n\\t\\t\\t#'reserved' : _is_bit_set(left, 31),\\n\\n\\t\\t\\t# Byte 4\\n\\t\\t\\t'sso_2.06' : _is_bit_set(right, 0),\\n\\t\\t\\t#'reserved' : _is_bit_set(right, 1),\\n\\t\\t\\t#'reserved' : _is_bit_set(right, 2),\\n\\t\\t\\t#'reserved' : _is_bit_set(right, 3),\\n\\t\\t\\t#'reserved' : _is_bit_set(right, 4),\\n\\t\\t\\t#'reserved' : _is_bit_set(right, 5),\\n\\t\\t\\t#'reserved' : _is_bit_set(right, 6),\\n\\t\\t\\t#'reserved' : _is_bit_set(right, 7),\\n\\n\\t\\t\\t# Byte 5\\n\\t\\t\\t'le' : _is_bit_set(right, 8),\\n\\t\\t\\t'cfar' : _is_bit_set(right, 9),\\n\\t\\t\\t'eb' : _is_bit_set(right, 10),\\n\\t\\t\\t'lsq_2.07' : _is_bit_set(right, 11),\\n\\t\\t\\t#'reserved' : _is_bit_set(right, 12),\\n\\t\\t\\t#'reserved' : _is_bit_set(right, 13),\\n\\t\\t\\t#'reserved' : _is_bit_set(right, 14),\\n\\t\\t\\t#'reserved' : _is_bit_set(right, 15),\\n\\n\\t\\t\\t# Byte 6\\n\\t\\t\\t'dss_2.07' : _is_bit_set(right, 16),\\n\\t\\t\\t#'reserved' : _is_bit_set(right, 17),\\n\\t\\t\\t#'reserved' : _is_bit_set(right, 18),\\n\\t\\t\\t#'reserved' : _is_bit_set(right, 19),\\n\\t\\t\\t#'reserved' : _is_bit_set(right, 20),\\n\\t\\t\\t#'reserved' : _is_bit_set(right, 21),\\n\\t\\t\\t#'reserved' : _is_bit_set(right, 22),\\n\\t\\t\\t#'reserved' : _is_bit_set(right, 23),\\n\\n\\t\\t\\t# Byte 7\\n\\t\\t\\t#'reserved' : _is_bit_set(right, 24),\\n\\t\\t\\t#'reserved' : _is_bit_set(right, 25),\\n\\t\\t\\t#'reserved' : _is_bit_set(right, 26),\\n\\t\\t\\t#'reserved' : _is_bit_set(right, 27),\\n\\t\\t\\t#'reserved' : _is_bit_set(right, 28),\\n\\t\\t\\t#'reserved' : _is_bit_set(right, 29),\\n\\t\\t\\t#'reserved' : _is_bit_set(right, 30),\\n\\t\\t\\t#'reserved' : _is_bit_set(right, 31),\\n\\t\\t}\\n\\n\\t\\t# Get a list of only the flags that are true\\n\\t\\tflags = [k for k, v in flags.items() if v]\\n\\t\\tflags.sort()\\n\\n\\t\\tinfo = {\\n\\t\\t\\t'flags' : flags\\n\\t\\t}\\n\\t\\tinfo = _filter_dict_keys_with_empty_values(info)\\n\\t\\tg_trace.success()\\n\\t\\treturn info\\n\\texcept Exception as err:\\n\\t\\tg_trace.fail(err)\\n\\t\\treturn {}\\n\\n\\ndef _get_cpu_info_from_cat_var_run_dmesg_boot():\\n\\t'''\\n\\tReturns the CPU info gathered from /var/run/dmesg.boot.\\n\\tReturns {} if dmesg is not found or does not have the desired info.\\n\\t'''\\n\\n\\tg_trace.header('Tying to get info from the /var/run/dmesg.boot log ...')\\n\\n\\t# Just return {} if there is no /var/run/dmesg.boot\\n\\tif not DataSource.has_var_run_dmesg_boot():\\n\\t\\tg_trace.fail('Failed to find /var/run/dmesg.boot file. Skipping ...')\\n\\t\\treturn {}\\n\\n\\t# If dmesg.boot fails return {}\\n\\treturncode, output = DataSource.cat_var_run_dmesg_boot()\\n\\tif output is None or returncode != 0:\\n\\t\\tg_trace.fail('Failed to run \\\\\\\"cat /var/run/dmesg.boot\\\\\\\". Skipping ...')\\n\\t\\treturn {}\\n\\n\\tinfo = _parse_dmesg_output(output)\\n\\tg_trace.success()\\n\\treturn info\\n\\n\\ndef _get_cpu_info_from_sysctl():\\n\\t'''\\n\\tReturns the CPU info gathered from sysctl.\\n\\tReturns {} if sysctl is not found.\\n\\t'''\\n\\n\\tg_trace.header('Tying to get info from sysctl ...')\\n\\n\\ttry:\\n\\t\\t# Just return {} if there is no sysctl\\n\\t\\tif not DataSource.has_sysctl():\\n\\t\\t\\tg_trace.fail('Failed to find sysctl. Skipping ...')\\n\\t\\t\\treturn {}\\n\\n\\t\\t# If sysctl fails return {}\\n\\t\\treturncode, output = DataSource.sysctl_machdep_cpu_hw_cpufrequency()\\n\\t\\tif output is None or returncode != 0:\\n\\t\\t\\tg_trace.fail('Failed to run \\\\\\\"sysctl machdep.cpu hw.cpufrequency\\\\\\\". Skipping ...')\\n\\t\\t\\treturn {}\\n\\n\\t\\t# Various fields\\n\\t\\tvendor_id = _get_field(False, output, None, None, 'machdep.cpu.vendor')\\n\\t\\tprocessor_brand = _get_field(True, output, None, None, 'machdep.cpu.brand_string')\\n\\t\\tcache_size = _get_field(False, output, int, 0, 'machdep.cpu.cache.size')\\n\\t\\tstepping = _get_field(False, output, int, 0, 'machdep.cpu.stepping')\\n\\t\\tmodel = _get_field(False, output, int, 0, 'machdep.cpu.model')\\n\\t\\tfamily = _get_field(False, output, int, 0, 'machdep.cpu.family')\\n\\n\\t\\t# Flags\\n\\t\\tflags = _get_field(False, output, None, '', 'machdep.cpu.features').lower().split()\\n\\t\\tflags.extend(_get_field(False, output, None, '', 'machdep.cpu.leaf7_features').lower().split())\\n\\t\\tflags.extend(_get_field(False, output, None, '', 'machdep.cpu.extfeatures').lower().split())\\n\\t\\tflags.sort()\\n\\n\\t\\t# Convert from GHz/MHz string to Hz\\n\\t\\thz_advertised, scale = _parse_cpu_brand_string(processor_brand)\\n\\t\\thz_actual = _get_field(False, output, None, None, 'hw.cpufrequency')\\n\\t\\thz_actual = _to_decimal_string(hz_actual)\\n\\n\\t\\tinfo = {\\n\\t\\t'vendor_id_raw' : vendor_id,\\n\\t\\t'brand_raw' : processor_brand,\\n\\n\\t\\t'hz_advertised_friendly' : _hz_short_to_friendly(hz_advertised, scale),\\n\\t\\t'hz_actual_friendly' : _hz_short_to_friendly(hz_actual, 0),\\n\\t\\t'hz_advertised' : _hz_short_to_full(hz_advertised, scale),\\n\\t\\t'hz_actual' : _hz_short_to_full(hz_actual, 0),\\n\\n\\t\\t'l2_cache_size' : int(cache_size) * 1024,\\n\\n\\t\\t'stepping' : stepping,\\n\\t\\t'model' : model,\\n\\t\\t'family' : family,\\n\\t\\t'flags' : flags\\n\\t\\t}\\n\\n\\t\\tinfo = _filter_dict_keys_with_empty_values(info)\\n\\t\\tg_trace.success()\\n\\t\\treturn info\\n\\texcept Exception as err:\\n\\t\\tg_trace.fail(err)\\n\\t\\treturn {}\\n\\n\\ndef _get_cpu_info_from_sysinfo():\\n\\t'''\\n\\tReturns the CPU info gathered from sysinfo.\\n\\tReturns {} if sysinfo is not found.\\n\\t'''\\n\\n\\tinfo = _get_cpu_info_from_sysinfo_v1()\\n\\tinfo.update(_get_cpu_info_from_sysinfo_v2())\\n\\treturn info\\n\\ndef _get_cpu_info_from_sysinfo_v1():\\n\\t'''\\n\\tReturns the CPU info gathered from sysinfo.\\n\\tReturns {} if sysinfo is not found.\\n\\t'''\\n\\n\\tg_trace.header('Tying to get info from sysinfo version 1 ...')\\n\\n\\ttry:\\n\\t\\t# Just return {} if there is no sysinfo\\n\\t\\tif not DataSource.has_sysinfo():\\n\\t\\t\\tg_trace.fail('Failed to find sysinfo. Skipping ...')\\n\\t\\t\\treturn {}\\n\\n\\t\\t# If sysinfo fails return {}\\n\\t\\treturncode, output = DataSource.sysinfo_cpu()\\n\\t\\tif output is None or returncode != 0:\\n\\t\\t\\tg_trace.fail('Failed to run \\\\\\\"sysinfo -cpu\\\\\\\". Skipping ...')\\n\\t\\t\\treturn {}\\n\\n\\t\\t# Various fields\\n\\t\\tvendor_id = '' #_get_field(False, output, None, None, 'CPU #0: ')\\n\\t\\tprocessor_brand = output.split('CPU #0: \\\"')[1].split('\\\"\\\\n')[0].strip()\\n\\t\\tcache_size = '' #_get_field(False, output, None, None, 'machdep.cpu.cache.size')\\n\\t\\tstepping = int(output.split(', stepping ')[1].split(',')[0].strip())\\n\\t\\tmodel = int(output.split(', model ')[1].split(',')[0].strip())\\n\\t\\tfamily = int(output.split(', family ')[1].split(',')[0].strip())\\n\\n\\t\\t# Flags\\n\\t\\tflags = []\\n\\t\\tfor line in output.split('\\\\n'):\\n\\t\\t\\tif line.startswith('\\\\t\\\\t'):\\n\\t\\t\\t\\tfor flag in line.strip().lower().split():\\n\\t\\t\\t\\t\\tflags.append(flag)\\n\\t\\tflags.sort()\\n\\n\\t\\t# Convert from GHz/MHz string to Hz\\n\\t\\thz_advertised, scale = _parse_cpu_brand_string(processor_brand)\\n\\t\\thz_actual = hz_advertised\\n\\n\\t\\tinfo = {\\n\\t\\t'vendor_id_raw' : vendor_id,\\n\\t\\t'brand_raw' : processor_brand,\\n\\n\\t\\t'hz_advertised_friendly' : _hz_short_to_friendly(hz_advertised, scale),\\n\\t\\t'hz_actual_friendly' : _hz_short_to_friendly(hz_actual, scale),\\n\\t\\t'hz_advertised' : _hz_short_to_full(hz_advertised, scale),\\n\\t\\t'hz_actual' : _hz_short_to_full(hz_actual, scale),\\n\\n\\t\\t'l2_cache_size' : _to_friendly_bytes(cache_size),\\n\\n\\t\\t'stepping' : stepping,\\n\\t\\t'model' : model,\\n\\t\\t'family' : family,\\n\\t\\t'flags' : flags\\n\\t\\t}\\n\\n\\t\\tinfo = _filter_dict_keys_with_empty_values(info)\\n\\t\\tg_trace.success()\\n\\t\\treturn info\\n\\texcept Exception as err:\\n\\t\\tg_trace.fail(err)\\n\\t\\t#raise # NOTE: To have this throw on error, uncomment this line\\n\\t\\treturn {}\\n\\ndef _get_cpu_info_from_sysinfo_v2():\\n\\t'''\\n\\tReturns the CPU info gathered from sysinfo.\\n\\tReturns {} if sysinfo is not found.\\n\\t'''\\n\\n\\tg_trace.header('Tying to get info from sysinfo version 2 ...')\\n\\n\\ttry:\\n\\t\\t# Just return {} if there is no sysinfo\\n\\t\\tif not DataSource.has_sysinfo():\\n\\t\\t\\tg_trace.fail('Failed to find sysinfo. Skipping ...')\\n\\t\\t\\treturn {}\\n\\n\\t\\t# If sysinfo fails return {}\\n\\t\\treturncode, output = DataSource.sysinfo_cpu()\\n\\t\\tif output is None or returncode != 0:\\n\\t\\t\\tg_trace.fail('Failed to run \\\\\\\"sysinfo -cpu\\\\\\\". Skipping ...')\\n\\t\\t\\treturn {}\\n\\n\\t\\t# Various fields\\n\\t\\tvendor_id = '' #_get_field(False, output, None, None, 'CPU #0: ')\\n\\t\\tprocessor_brand = output.split('CPU #0: \\\"')[1].split('\\\"\\\\n')[0].strip()\\n\\t\\tcache_size = '' #_get_field(False, output, None, None, 'machdep.cpu.cache.size')\\n\\t\\tsignature = output.split('Signature:')[1].split('\\\\n')[0].strip()\\n\\t\\t#\\n\\t\\tstepping = int(signature.split('stepping ')[1].split(',')[0].strip())\\n\\t\\tmodel = int(signature.split('model ')[1].split(',')[0].strip())\\n\\t\\tfamily = int(signature.split('family ')[1].split(',')[0].strip())\\n\\n\\t\\t# Flags\\n\\t\\tdef get_subsection_flags(output):\\n\\t\\t\\tretval = []\\n\\t\\t\\tfor line in output.split('\\\\n')[1:]:\\n\\t\\t\\t\\tif not line.startswith('                ') and not line.startswith('\\t\\t'): break\\n\\t\\t\\t\\tfor entry in line.strip().lower().split(' '):\\n\\t\\t\\t\\t\\tretval.append(entry)\\n\\t\\t\\treturn retval\\n\\n\\t\\tflags = get_subsection_flags(output.split('Features: ')[1]) + \\\\\\n\\t\\t\\t\\tget_subsection_flags(output.split('Extended Features (0x00000001): ')[1]) + \\\\\\n\\t\\t\\t\\tget_subsection_flags(output.split('Extended Features (0x80000001): ')[1])\\n\\t\\tflags.sort()\\n\\n\\t\\t# Convert from GHz/MHz string to Hz\\n\\t\\tlines = [n for n in output.split('\\\\n') if n]\\n\\t\\traw_hz = lines[0].split('running at ')[1].strip().lower()\\n\\t\\thz_advertised = raw_hz.rstrip('mhz').rstrip('ghz').strip()\\n\\t\\thz_advertised = _to_decimal_string(hz_advertised)\\n\\t\\thz_actual = hz_advertised\\n\\n\\t\\tscale = 0\\n\\t\\tif raw_hz.endswith('mhz'):\\n\\t\\t\\tscale = 6\\n\\t\\telif raw_hz.endswith('ghz'):\\n\\t\\t\\tscale = 9\\n\\n\\t\\tinfo = {\\n\\t\\t'vendor_id_raw' : vendor_id,\\n\\t\\t'brand_raw' : processor_brand,\\n\\n\\t\\t'hz_advertised_friendly' : _hz_short_to_friendly(hz_advertised, scale),\\n\\t\\t'hz_actual_friendly' : _hz_short_to_friendly(hz_actual, scale),\\n\\t\\t'hz_advertised' : _hz_short_to_full(hz_advertised, scale),\\n\\t\\t'hz_actual' : _hz_short_to_full(hz_actual, scale),\\n\\n\\t\\t'l2_cache_size' : _to_friendly_bytes(cache_size),\\n\\n\\t\\t'stepping' : stepping,\\n\\t\\t'model' : model,\\n\\t\\t'family' : family,\\n\\t\\t'flags' : flags\\n\\t\\t}\\n\\n\\t\\tinfo = _filter_dict_keys_with_empty_values(info)\\n\\t\\tg_trace.success()\\n\\t\\treturn info\\n\\texcept Exception as err:\\n\\t\\tg_trace.fail(err)\\n\\t\\t#raise # NOTE: To have this throw on error, uncomment this line\\n\\t\\treturn {}\\n\\ndef _get_cpu_info_from_wmic():\\n\\t'''\\n\\tReturns the CPU info gathered from WMI.\\n\\tReturns {} if not on Windows, or wmic is not installed.\\n\\t'''\\n\\tg_trace.header('Tying to get info from wmic ...')\\n\\n\\ttry:\\n\\t\\t# Just return {} if not Windows or there is no wmic\\n\\t\\tif not DataSource.is_windows or not DataSource.has_wmic():\\n\\t\\t\\tg_trace.fail('Failed to find WMIC, or not on Windows. Skipping ...')\\n\\t\\t\\treturn {}\\n\\n\\t\\treturncode, output = DataSource.wmic_cpu()\\n\\t\\tif output is None or returncode != 0:\\n\\t\\t\\tg_trace.fail('Failed to run wmic. Skipping ...')\\n\\t\\t\\treturn {}\\n\\n\\t\\t# Break the list into key values pairs\\n\\t\\tvalue = output.split(\\\"\\\\n\\\")\\n\\t\\tvalue = [s.rstrip().split('=') for s in value if '=' in s]\\n\\t\\tvalue = {k: v for k, v in value if v}\\n\\n\\t\\t# Get the advertised MHz\\n\\t\\tprocessor_brand = value.get('Name')\\n\\t\\thz_advertised, scale_advertised = _parse_cpu_brand_string(processor_brand)\\n\\n\\t\\t# Get the actual MHz\\n\\t\\thz_actual = value.get('CurrentClockSpeed')\\n\\t\\tscale_actual = 6\\n\\t\\tif hz_actual:\\n\\t\\t\\thz_actual = _to_decimal_string(hz_actual)\\n\\n\\t\\t# Get cache sizes\\n\\t\\tl2_cache_size = value.get('L2CacheSize') # NOTE: L2CacheSize is in kilobytes\\n\\t\\tif l2_cache_size:\\n\\t\\t\\tl2_cache_size = int(l2_cache_size) * 1024\\n\\n\\t\\tl3_cache_size = value.get('L3CacheSize') # NOTE: L3CacheSize is in kilobytes\\n\\t\\tif l3_cache_size:\\n\\t\\t\\tl3_cache_size = int(l3_cache_size) * 1024\\n\\n\\t\\t# Get family, model, and stepping\\n\\t\\tfamily, model, stepping = '', '', ''\\n\\t\\tdescription = value.get('Description') or value.get('Caption')\\n\\t\\tentries = description.split(' ')\\n\\n\\t\\tif 'Family' in entries and entries.index('Family') < len(entries)-1:\\n\\t\\t\\ti = entries.index('Family')\\n\\t\\t\\tfamily = int(entries[i + 1])\\n\\n\\t\\tif 'Model' in entries and entries.index('Model') < len(entries)-1:\\n\\t\\t\\ti = entries.index('Model')\\n\\t\\t\\tmodel = int(entries[i + 1])\\n\\n\\t\\tif 'Stepping' in entries and entries.index('Stepping') < len(entries)-1:\\n\\t\\t\\ti = entries.index('Stepping')\\n\\t\\t\\tstepping = int(entries[i + 1])\\n\\n\\t\\tinfo = {\\n\\t\\t\\t'vendor_id_raw' : value.get('Manufacturer'),\\n\\t\\t\\t'brand_raw' : processor_brand,\\n\\n\\t\\t\\t'hz_advertised_friendly' : _hz_short_to_friendly(hz_advertised, scale_advertised),\\n\\t\\t\\t'hz_actual_friendly' : _hz_short_to_friendly(hz_actual, scale_actual),\\n\\t\\t\\t'hz_advertised' : _hz_short_to_full(hz_advertised, scale_advertised),\\n\\t\\t\\t'hz_actual' : _hz_short_to_full(hz_actual, scale_actual),\\n\\n\\t\\t\\t'l2_cache_size' : l2_cache_size,\\n\\t\\t\\t'l3_cache_size' : l3_cache_size,\\n\\n\\t\\t\\t'stepping' : stepping,\\n\\t\\t\\t'model' : model,\\n\\t\\t\\t'family' : family,\\n\\t\\t}\\n\\n\\t\\tinfo = _filter_dict_keys_with_empty_values(info)\\n\\t\\tg_trace.success()\\n\\t\\treturn info\\n\\texcept Exception as err:\\n\\t\\tg_trace.fail(err)\\n\\t\\t#raise # NOTE: To have this throw on error, uncomment this line\\n\\t\\treturn {}\\n\\ndef _get_cpu_info_from_registry():\\n\\t'''\\n\\tReturns the CPU info gathered from the Windows Registry.\\n\\tReturns {} if not on Windows.\\n\\t'''\\n\\n\\tg_trace.header('Tying to get info from Windows registry ...')\\n\\n\\ttry:\\n\\t\\t# Just return {} if not on Windows\\n\\t\\tif not DataSource.is_windows:\\n\\t\\t\\tg_trace.fail('Not running on Windows. Skipping ...')\\n\\t\\t\\treturn {}\\n\\n\\t\\t# Get the CPU name\\n\\t\\tprocessor_brand = DataSource.winreg_processor_brand().strip()\\n\\n\\t\\t# Get the CPU vendor id\\n\\t\\tvendor_id = DataSource.winreg_vendor_id_raw()\\n\\n\\t\\t# Get the CPU arch and bits\\n\\t\\tarch_string_raw = DataSource.winreg_arch_string_raw()\\n\\t\\tarch, bits = _parse_arch(arch_string_raw)\\n\\n\\t\\t# Get the actual CPU Hz\\n\\t\\thz_actual = DataSource.winreg_hz_actual()\\n\\t\\thz_actual = _to_decimal_string(hz_actual)\\n\\n\\t\\t# Get the advertised CPU Hz\\n\\t\\thz_advertised, scale = _parse_cpu_brand_string(processor_brand)\\n\\n\\t\\t# If advertised hz not found, use the actual hz\\n\\t\\tif hz_advertised == '0.0':\\n\\t\\t\\tscale = 6\\n\\t\\t\\thz_advertised = _to_decimal_string(hz_actual)\\n\\n\\t\\t# Get the CPU features\\n\\t\\tfeature_bits = DataSource.winreg_feature_bits()\\n\\n\\t\\tdef is_set(bit):\\n\\t\\t\\tmask = 0x80000000 >> bit\\n\\t\\t\\tretval = mask & feature_bits > 0\\n\\t\\t\\treturn retval\\n\\n\\t\\t# http://en.wikipedia.org/wiki/CPUID\\n\\t\\t# http://unix.stackexchange.com/questions/43539/what-do-the-flags-in-proc-cpuinfo-mean\\n\\t\\t# http://www.lohninger.com/helpcsuite/public_constants_cpuid.htm\\n\\t\\tflags = {\\n\\t\\t\\t'fpu' : is_set(0), # Floating Point Unit\\n\\t\\t\\t'vme' : is_set(1), # V86 Mode Extensions\\n\\t\\t\\t'de' : is_set(2), # Debug Extensions - I/O breakpoints supported\\n\\t\\t\\t'pse' : is_set(3), # Page Size Extensions (4 MB pages supported)\\n\\t\\t\\t'tsc' : is_set(4), # Time Stamp Counter and RDTSC instruction are available\\n\\t\\t\\t'msr' : is_set(5), # Model Specific Registers\\n\\t\\t\\t'pae' : is_set(6), # Physical Address Extensions (36 bit address, 2MB pages)\\n\\t\\t\\t'mce' : is_set(7), # Machine Check Exception supported\\n\\t\\t\\t'cx8' : is_set(8), # Compare Exchange Eight Byte instruction available\\n\\t\\t\\t'apic' : is_set(9), # Local APIC present (multiprocessor operation support)\\n\\t\\t\\t'sepamd' : is_set(10), # Fast system calls (AMD only)\\n\\t\\t\\t'sep' : is_set(11), # Fast system calls\\n\\t\\t\\t'mtrr' : is_set(12), # Memory Type Range Registers\\n\\t\\t\\t'pge' : is_set(13), # Page Global Enable\\n\\t\\t\\t'mca' : is_set(14), # Machine Check Architecture\\n\\t\\t\\t'cmov' : is_set(15), # Conditional MOVe instructions\\n\\t\\t\\t'pat' : is_set(16), # Page Attribute Table\\n\\t\\t\\t'pse36' : is_set(17), # 36 bit Page Size Extensions\\n\\t\\t\\t'serial' : is_set(18), # Processor Serial Number\\n\\t\\t\\t'clflush' : is_set(19), # Cache Flush\\n\\t\\t\\t#'reserved1' : is_set(20), # reserved\\n\\t\\t\\t'dts' : is_set(21), # Debug Trace Store\\n\\t\\t\\t'acpi' : is_set(22), # ACPI support\\n\\t\\t\\t'mmx' : is_set(23), # MultiMedia Extensions\\n\\t\\t\\t'fxsr' : is_set(24), # FXSAVE and FXRSTOR instructions\\n\\t\\t\\t'sse' : is_set(25), # SSE instructions\\n\\t\\t\\t'sse2' : is_set(26), # SSE2 (WNI) instructions\\n\\t\\t\\t'ss' : is_set(27), # self snoop\\n\\t\\t\\t#'reserved2' : is_set(28), # reserved\\n\\t\\t\\t'tm' : is_set(29), # Automatic clock control\\n\\t\\t\\t'ia64' : is_set(30), # IA64 instructions\\n\\t\\t\\t'3dnow' : is_set(31) # 3DNow! instructions available\\n\\t\\t}\\n\\n\\t\\t# Get a list of only the flags that are true\\n\\t\\tflags = [k for k, v in flags.items() if v]\\n\\t\\tflags.sort()\\n\\n\\t\\tinfo = {\\n\\t\\t'vendor_id_raw' : vendor_id,\\n\\t\\t'brand_raw' : processor_brand,\\n\\n\\t\\t'hz_advertised_friendly' : _hz_short_to_friendly(hz_advertised, scale),\\n\\t\\t'hz_actual_friendly' : _hz_short_to_friendly(hz_actual, 6),\\n\\t\\t'hz_advertised' : _hz_short_to_full(hz_advertised, scale),\\n\\t\\t'hz_actual' : _hz_short_to_full(hz_actual, 6),\\n\\n\\t\\t'flags' : flags\\n\\t\\t}\\n\\n\\t\\tinfo = _filter_dict_keys_with_empty_values(info)\\n\\t\\tg_trace.success()\\n\\t\\treturn info\\n\\texcept Exception as err:\\n\\t\\tg_trace.fail(err)\\n\\t\\treturn {}\\n\\ndef _get_cpu_info_from_kstat():\\n\\t'''\\n\\tReturns the CPU info gathered from isainfo and kstat.\\n\\tReturns {} if isainfo or kstat are not found.\\n\\t'''\\n\\n\\tg_trace.header('Tying to get info from kstat ...')\\n\\n\\ttry:\\n\\t\\t# Just return {} if there is no isainfo or kstat\\n\\t\\tif not DataSource.has_isainfo() or not DataSource.has_kstat():\\n\\t\\t\\tg_trace.fail('Failed to find isinfo or kstat. Skipping ...')\\n\\t\\t\\treturn {}\\n\\n\\t\\t# If isainfo fails return {}\\n\\t\\treturncode, flag_output = DataSource.isainfo_vb()\\n\\t\\tif flag_output is None or returncode != 0:\\n\\t\\t\\tg_trace.fail('Failed to run \\\\\\\"isainfo -vb\\\\\\\". Skipping ...')\\n\\t\\t\\treturn {}\\n\\n\\t\\t# If kstat fails return {}\\n\\t\\treturncode, kstat = DataSource.kstat_m_cpu_info()\\n\\t\\tif kstat is None or returncode != 0:\\n\\t\\t\\tg_trace.fail('Failed to run \\\\\\\"kstat -m cpu_info\\\\\\\". Skipping ...')\\n\\t\\t\\treturn {}\\n\\n\\t\\t# Various fields\\n\\t\\tvendor_id = kstat.split('\\\\tvendor_id ')[1].split('\\\\n')[0].strip()\\n\\t\\tprocessor_brand = kstat.split('\\\\tbrand ')[1].split('\\\\n')[0].strip()\\n\\t\\tstepping = int(kstat.split('\\\\tstepping ')[1].split('\\\\n')[0].strip())\\n\\t\\tmodel = int(kstat.split('\\\\tmodel ')[1].split('\\\\n')[0].strip())\\n\\t\\tfamily = int(kstat.split('\\\\tfamily ')[1].split('\\\\n')[0].strip())\\n\\n\\t\\t# Flags\\n\\t\\tflags = flag_output.strip().split('\\\\n')[-1].strip().lower().split()\\n\\t\\tflags.sort()\\n\\n\\t\\t# Convert from GHz/MHz string to Hz\\n\\t\\tscale = 6\\n\\t\\thz_advertised = kstat.split('\\\\tclock_MHz ')[1].split('\\\\n')[0].strip()\\n\\t\\thz_advertised = _to_decimal_string(hz_advertised)\\n\\n\\t\\t# Convert from GHz/MHz string to Hz\\n\\t\\thz_actual = kstat.split('\\\\tcurrent_clock_Hz ')[1].split('\\\\n')[0].strip()\\n\\t\\thz_actual = _to_decimal_string(hz_actual)\\n\\n\\t\\tinfo = {\\n\\t\\t'vendor_id_raw' : vendor_id,\\n\\t\\t'brand_raw' : processor_brand,\\n\\n\\t\\t'hz_advertised_friendly' : _hz_short_to_friendly(hz_advertised, scale),\\n\\t\\t'hz_actual_friendly' : _hz_short_to_friendly(hz_actual, 0),\\n\\t\\t'hz_advertised' : _hz_short_to_full(hz_advertised, scale),\\n\\t\\t'hz_actual' : _hz_short_to_full(hz_actual, 0),\\n\\n\\t\\t'stepping' : stepping,\\n\\t\\t'model' : model,\\n\\t\\t'family' : family,\\n\\t\\t'flags' : flags\\n\\t\\t}\\n\\n\\t\\tinfo = _filter_dict_keys_with_empty_values(info)\\n\\t\\tg_trace.success()\\n\\t\\treturn info\\n\\texcept Exception as err:\\n\\t\\tg_trace.fail(err)\\n\\t\\treturn {}\\n\\ndef _get_cpu_info_from_platform_uname():\\n\\n\\tg_trace.header('Tying to get info from platform.uname ...')\\n\\n\\ttry:\\n\\t\\tuname = DataSource.uname_string_raw.split(',')[0]\\n\\n\\t\\tfamily, model, stepping = (None, None, None)\\n\\t\\tentries = uname.split(' ')\\n\\n\\t\\tif 'Family' in entries and entries.index('Family') < len(entries)-1:\\n\\t\\t\\ti = entries.index('Family')\\n\\t\\t\\tfamily = int(entries[i + 1])\\n\\n\\t\\tif 'Model' in entries and entries.index('Model') < len(entries)-1:\\n\\t\\t\\ti = entries.index('Model')\\n\\t\\t\\tmodel = int(entries[i + 1])\\n\\n\\t\\tif 'Stepping' in entries and entries.index('Stepping') < len(entries)-1:\\n\\t\\t\\ti = entries.index('Stepping')\\n\\t\\t\\tstepping = int(entries[i + 1])\\n\\n\\t\\tinfo = {\\n\\t\\t\\t'family' : family,\\n\\t\\t\\t'model' : model,\\n\\t\\t\\t'stepping' : stepping\\n\\t\\t}\\n\\t\\tinfo = _filter_dict_keys_with_empty_values(info)\\n\\t\\tg_trace.success()\\n\\t\\treturn info\\n\\texcept Exception as err:\\n\\t\\tg_trace.fail(err)\\n\\t\\treturn {}\\n\\ndef _get_cpu_info_internal():\\n\\t'''\\n\\tReturns the CPU info by using the best sources of information for your OS.\\n\\tReturns {} if nothing is found.\\n\\t'''\\n\\n\\tg_trace.write('!' * 80)\\n\\n\\t# Get the CPU arch and bits\\n\\tarch, bits = _parse_arch(DataSource.arch_string_raw)\\n\\n\\tfriendly_maxsize = { 2**31-1: '32 bit', 2**63-1: '64 bit' }.get(sys.maxsize) or 'unknown bits'\\n\\tfriendly_version = \\\"{0}.{1}.{2}.{3}.{4}\\\".format(*sys.version_info)\\n\\tPYTHON_VERSION = \\\"{0} ({1})\\\".format(friendly_version, friendly_maxsize)\\n\\n\\tinfo = {\\n\\t\\t'python_version' : PYTHON_VERSION,\\n\\t\\t'cpuinfo_version' : CPUINFO_VERSION,\\n\\t\\t'cpuinfo_version_string' : CPUINFO_VERSION_STRING,\\n\\t\\t'arch' : arch,\\n\\t\\t'bits' : bits,\\n\\t\\t'count' : DataSource.cpu_count,\\n\\t\\t'arch_string_raw' : DataSource.arch_string_raw,\\n\\t}\\n\\n\\tg_trace.write(\\\"python_version: {0}\\\".format(info['python_version']))\\n\\tg_trace.write(\\\"cpuinfo_version: {0}\\\".format(info['cpuinfo_version']))\\n\\tg_trace.write(\\\"arch: {0}\\\".format(info['arch']))\\n\\tg_trace.write(\\\"bits: {0}\\\".format(info['bits']))\\n\\tg_trace.write(\\\"count: {0}\\\".format(info['count']))\\n\\tg_trace.write(\\\"arch_string_raw: {0}\\\".format(info['arch_string_raw']))\\n\\n\\t# Try the Windows wmic\\n\\t_copy_new_fields(info, _get_cpu_info_from_wmic())\\n\\n\\t# Try the Windows registry\\n\\t_copy_new_fields(info, _get_cpu_info_from_registry())\\n\\n\\t# Try /proc/cpuinfo\\n\\t_copy_new_fields(info, _get_cpu_info_from_proc_cpuinfo())\\n\\n\\t# Try cpufreq-info\\n\\t_copy_new_fields(info, _get_cpu_info_from_cpufreq_info())\\n\\n\\t# Try LSCPU\\n\\t_copy_new_fields(info, _get_cpu_info_from_lscpu())\\n\\n\\t# Try sysctl\\n\\t_copy_new_fields(info, _get_cpu_info_from_sysctl())\\n\\n\\t# Try kstat\\n\\t_copy_new_fields(info, _get_cpu_info_from_kstat())\\n\\n\\t# Try dmesg\\n\\t_copy_new_fields(info, _get_cpu_info_from_dmesg())\\n\\n\\t# Try /var/run/dmesg.boot\\n\\t_copy_new_fields(info, _get_cpu_info_from_cat_var_run_dmesg_boot())\\n\\n\\t# Try lsprop ibm,pa-features\\n\\t_copy_new_fields(info, _get_cpu_info_from_ibm_pa_features())\\n\\n\\t# Try sysinfo\\n\\t_copy_new_fields(info, _get_cpu_info_from_sysinfo())\\n\\n\\t# Try querying the CPU cpuid register\\n\\t# FIXME: This should print stdout and stderr to trace log\\n\\t_copy_new_fields(info, _get_cpu_info_from_cpuid())\\n\\n\\t# Try platform.uname\\n\\t_copy_new_fields(info, _get_cpu_info_from_platform_uname())\\n\\n\\tg_trace.write('!' * 80)\\n\\n\\treturn info\\n\\ndef get_cpu_info_json():\\n\\t'''\\n\\tReturns the CPU info by using the best sources of information for your OS.\\n\\tReturns the result in a json string\\n\\t'''\\n\\n\\timport json\\n\\n\\toutput = None\\n\\n\\t# If running under pyinstaller, run normally\\n\\tif getattr(sys, 'frozen', False):\\n\\t\\tinfo = _get_cpu_info_internal()\\n\\t\\toutput = json.dumps(info)\\n\\t\\toutput = \\\"{0}\\\".format(output)\\n\\t# if not running under pyinstaller, run in another process.\\n\\t# This is done because multiprocesing has a design flaw that\\n\\t# causes non main programs to run multiple times on Windows.\\n\\telse:\\n\\t\\tfrom subprocess import Popen, PIPE\\n\\n\\t\\tcommand = [sys.executable, __file__, '--json']\\n\\t\\tp1 = Popen(command, stdout=PIPE, stderr=PIPE, stdin=PIPE)\\n\\t\\toutput = p1.communicate()[0]\\n\\n\\t\\tif p1.returncode != 0:\\n\\t\\t\\treturn \\\"{}\\\"\\n\\n\\t\\toutput = output.decode(encoding='UTF-8')\\n\\n\\treturn output\\n\\ndef get_cpu_info():\\n\\t'''\\n\\tReturns the CPU info by using the best sources of information for your OS.\\n\\tReturns the result in a dict\\n\\t'''\\n\\n\\timport json\\n\\n\\toutput = get_cpu_info_json()\\n\\n\\t# Convert JSON to Python with non unicode strings\\n\\toutput = json.loads(output, object_hook = _utf_to_str)\\n\\n\\treturn output\\n\\ndef main():\\n\\tfrom argparse import ArgumentParser\\n\\timport json\\n\\n\\t# Parse args\\n\\tparser = ArgumentParser(description='Gets CPU info with pure Python')\\n\\tparser.add_argument('--json', action='store_true', help='Return the info in JSON format')\\n\\tparser.add_argument('--version', action='store_true', help='Return the version of py-cpuinfo')\\n\\tparser.add_argument('--trace', action='store_true', help='Traces code paths used to find CPU info to file')\\n\\targs = parser.parse_args()\\n\\n\\tglobal g_trace\\n\\tg_trace = Trace(args.trace, False)\\n\\n\\ttry:\\n\\t\\t_check_arch()\\n\\texcept Exception as err:\\n\\t\\tsys.stderr.write(str(err) + \\\"\\\\n\\\")\\n\\t\\tsys.exit(1)\\n\\n\\tinfo = _get_cpu_info_internal()\\n\\n\\tif not info:\\n\\t\\tsys.stderr.write(\\\"Failed to find cpu info\\\\n\\\")\\n\\t\\tsys.exit(1)\\n\\n\\tif args.json:\\n\\t\\tprint(json.dumps(info))\\n\\telif args.version:\\n\\t\\tprint(CPUINFO_VERSION_STRING)\\n\\telse:\\n\\t\\tprint('Python Version: {0}'.format(info.get('python_version', '')))\\n\\t\\tprint('Cpuinfo Version: {0}'.format(info.get('cpuinfo_version_string', '')))\\n\\t\\tprint('Vendor ID Raw: {0}'.format(info.get('vendor_id_raw', '')))\\n\\t\\tprint('Hardware Raw: {0}'.format(info.get('hardware_raw', '')))\\n\\t\\tprint('Brand Raw: {0}'.format(info.get('brand_raw', '')))\\n\\t\\tprint('Hz Advertised Friendly: {0}'.format(info.get('hz_advertised_friendly', '')))\\n\\t\\tprint('Hz Actual Friendly: {0}'.format(info.get('hz_actual_friendly', '')))\\n\\t\\tprint('Hz Advertised: {0}'.format(info.get('hz_advertised', '')))\\n\\t\\tprint('Hz Actual: {0}'.format(info.get('hz_actual', '')))\\n\\t\\tprint('Arch: {0}'.format(info.get('arch', '')))\\n\\t\\tprint('Bits: {0}'.format(info.get('bits', '')))\\n\\t\\tprint('Count: {0}'.format(info.get('count', '')))\\n\\t\\tprint('Arch String Raw: {0}'.format(info.get('arch_string_raw', '')))\\n\\t\\tprint('L1 Data Cache Size: {0}'.format(info.get('l1_data_cache_size', '')))\\n\\t\\tprint('L1 Instruction Cache Size: {0}'.format(info.get('l1_instruction_cache_size', '')))\\n\\t\\tprint('L2 Cache Size: {0}'.format(info.get('l2_cache_size', '')))\\n\\t\\tprint('L2 Cache Line Size: {0}'.format(info.get('l2_cache_line_size', '')))\\n\\t\\tprint('L2 Cache Associativity: {0}'.format(info.get('l2_cache_associativity', '')))\\n\\t\\tprint('L3 Cache Size: {0}'.format(info.get('l3_cache_size', '')))\\n\\t\\tprint('Stepping: {0}'.format(info.get('stepping', '')))\\n\\t\\tprint('Model: {0}'.format(info.get('model', '')))\\n\\t\\tprint('Family: {0}'.format(info.get('family', '')))\\n\\t\\tprint('Processor Type: {0}'.format(info.get('processor_type', '')))\\n\\t\\tprint('Flags: {0}'.format(', '.join(info.get('flags', ''))))\\n\\n\\nif __name__ == '__main__':\\n\\tmain()\\nelse:\\n\\tg_trace = Trace(False, False)\\n\\t_check_arch()\\n\\n\\n\\nimport sys\\nfrom .cpuinfo import *\\n\\nfrom ...deprecations import deprecated\\n\\ndeprecated.module(\\\"24.3\\\", \\\"24.9\\\")\\n\\n\\n\\nCopyright (c) 2012 Santiago Lezica\\n\\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \\\"Software\\\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\\n\\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\\n\\nTHE SOFTWARE IS PROVIDED \\\"AS IS\\\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\\n\\n\\nfrom ...deprecations import deprecated\\ndeprecated.module(\\\"24.9\\\", \\\"25.3\\\", addendum=\\\"Use `frozendict` instead.\\\")\\n\\nfrom collections.abc import Mapping\\n\\ntry:\\n    from collections import OrderedDict\\nexcept ImportError:  # python < 2.7\\n    OrderedDict = NotImplemented\\n\\n\\niteritems = getattr(dict, 'iteritems', dict.items) # py2-3 compatibility\\n\\n\\nclass frozendict(Mapping):\\n    \\\"\\\"\\\"\\n    An immutable wrapper around dictionaries that implements the complete :py:class:`collections.Mapping`\\n    interface. It can be used as a drop-in replacement for dictionaries where immutability is desired.\\n    \\\"\\\"\\\"\\n\\n    dict_cls = dict\\n\\n    def __init__(self, *args, **kwargs):\\n        self._dict = self.dict_cls(*args, **kwargs)\\n        self._hash = None\\n\\n    def __getitem__(self, key):\\n        return self._dict[key]\\n\\n    def __contains__(self, key):\\n        return key in self._dict\\n\\n    def copy(self, **add_or_replace):\\n        return self.__class__(self, **add_or_replace)\\n\\n    def __iter__(self):\\n        return iter(self._dict)\\n\\n    def __len__(self):\\n        return len(self._dict)\\n\\n    def __repr__(self):\\n        return '<%s %r>' % (self.__class__.__name__, self._dict)\\n\\n    def __hash__(self):\\n        if self._hash is None:\\n            h = 0\\n            for key, value in iteritems(self._dict):\\n                h ^= hash((key, value))\\n            self._hash = h\\n        return self._hash\\n\\n    def __json__(self):\\n        # Works with auxlib's EntityEncoder.\\n        return self._dict\\n\\n    def to_json(self):\\n        return self.__json__()\\n\\n\\nclass FrozenOrderedDict(frozendict):\\n    \\\"\\\"\\\"\\n    A frozendict subclass that maintains key order\\n    \\\"\\\"\\\"\\n\\n    dict_cls = OrderedDict\\n\\n\\nif OrderedDict is NotImplemented:\\n    del FrozenOrderedDict\",\"difficulty\":\"easy\",\"domain\":\"Code Repository Understanding\",\"length\":\"long\",\"question\":\"In the urls method of the Channel class, what does not determine the final URL list that is returned when both credentials and subdirs are provided?\",\"sub_domain\":\"Code repo QA\"}","display_format":"text","language":"","answer_status":"published","assets":[],"source_url":"https://huggingface.co/datasets/zai-org/LongBench-v2","history":"initial import","indexing_mode":"noindex","subproblems":[],"grids":[]}