Path: blob/main/test/lib/python3.9/site-packages/pip/_internal/commands/configuration.py
4804 views
import logging1import os2import subprocess3from optparse import Values4from typing import Any, List, Optional56from pip._internal.cli.base_command import Command7from pip._internal.cli.status_codes import ERROR, SUCCESS8from pip._internal.configuration import (9Configuration,10Kind,11get_configuration_files,12kinds,13)14from pip._internal.exceptions import PipError15from pip._internal.utils.logging import indent_log16from pip._internal.utils.misc import get_prog, write_output1718logger = logging.getLogger(__name__)192021class ConfigurationCommand(Command):22"""23Manage local and global configuration.2425Subcommands:2627- list: List the active configuration (or from the file specified)28- edit: Edit the configuration file in an editor29- get: Get the value associated with command.option30- set: Set the command.option=value31- unset: Unset the value associated with command.option32- debug: List the configuration files and values defined under them3334Configuration keys should be dot separated command and option name,35with the special prefix "global" affecting any command. For example,36"pip config set global.index-url https://example.org/" would configure37the index url for all commands, but "pip config set download.timeout 10"38would configure a 10 second timeout only for "pip download" commands.3940If none of --user, --global and --site are passed, a virtual41environment configuration file is used if one is active and the file42exists. Otherwise, all modifications happen to the user file by43default.44"""4546ignore_require_venv = True47usage = """48%prog [<file-option>] list49%prog [<file-option>] [--editor <editor-path>] edit5051%prog [<file-option>] get command.option52%prog [<file-option>] set command.option value53%prog [<file-option>] unset command.option54%prog [<file-option>] debug55"""5657def add_options(self) -> None:58self.cmd_opts.add_option(59"--editor",60dest="editor",61action="store",62default=None,63help=(64"Editor to use to edit the file. Uses VISUAL or EDITOR "65"environment variables if not provided."66),67)6869self.cmd_opts.add_option(70"--global",71dest="global_file",72action="store_true",73default=False,74help="Use the system-wide configuration file only",75)7677self.cmd_opts.add_option(78"--user",79dest="user_file",80action="store_true",81default=False,82help="Use the user configuration file only",83)8485self.cmd_opts.add_option(86"--site",87dest="site_file",88action="store_true",89default=False,90help="Use the current environment configuration file only",91)9293self.parser.insert_option_group(0, self.cmd_opts)9495def run(self, options: Values, args: List[str]) -> int:96handlers = {97"list": self.list_values,98"edit": self.open_in_editor,99"get": self.get_name,100"set": self.set_name_value,101"unset": self.unset_name,102"debug": self.list_config_values,103}104105# Determine action106if not args or args[0] not in handlers:107logger.error(108"Need an action (%s) to perform.",109", ".join(sorted(handlers)),110)111return ERROR112113action = args[0]114115# Determine which configuration files are to be loaded116# Depends on whether the command is modifying.117try:118load_only = self._determine_file(119options, need_value=(action in ["get", "set", "unset", "edit"])120)121except PipError as e:122logger.error(e.args[0])123return ERROR124125# Load a new configuration126self.configuration = Configuration(127isolated=options.isolated_mode, load_only=load_only128)129self.configuration.load()130131# Error handling happens here, not in the action-handlers.132try:133handlers[action](options, args[1:])134except PipError as e:135logger.error(e.args[0])136return ERROR137138return SUCCESS139140def _determine_file(self, options: Values, need_value: bool) -> Optional[Kind]:141file_options = [142key143for key, value in (144(kinds.USER, options.user_file),145(kinds.GLOBAL, options.global_file),146(kinds.SITE, options.site_file),147)148if value149]150151if not file_options:152if not need_value:153return None154# Default to user, unless there's a site file.155elif any(156os.path.exists(site_config_file)157for site_config_file in get_configuration_files()[kinds.SITE]158):159return kinds.SITE160else:161return kinds.USER162elif len(file_options) == 1:163return file_options[0]164165raise PipError(166"Need exactly one file to operate upon "167"(--user, --site, --global) to perform."168)169170def list_values(self, options: Values, args: List[str]) -> None:171self._get_n_args(args, "list", n=0)172173for key, value in sorted(self.configuration.items()):174write_output("%s=%r", key, value)175176def get_name(self, options: Values, args: List[str]) -> None:177key = self._get_n_args(args, "get [name]", n=1)178value = self.configuration.get_value(key)179180write_output("%s", value)181182def set_name_value(self, options: Values, args: List[str]) -> None:183key, value = self._get_n_args(args, "set [name] [value]", n=2)184self.configuration.set_value(key, value)185186self._save_configuration()187188def unset_name(self, options: Values, args: List[str]) -> None:189key = self._get_n_args(args, "unset [name]", n=1)190self.configuration.unset_value(key)191192self._save_configuration()193194def list_config_values(self, options: Values, args: List[str]) -> None:195"""List config key-value pairs across different config files"""196self._get_n_args(args, "debug", n=0)197198self.print_env_var_values()199# Iterate over config files and print if they exist, and the200# key-value pairs present in them if they do201for variant, files in sorted(self.configuration.iter_config_files()):202write_output("%s:", variant)203for fname in files:204with indent_log():205file_exists = os.path.exists(fname)206write_output("%s, exists: %r", fname, file_exists)207if file_exists:208self.print_config_file_values(variant)209210def print_config_file_values(self, variant: Kind) -> None:211"""Get key-value pairs from the file of a variant"""212for name, value in self.configuration.get_values_in_config(variant).items():213with indent_log():214write_output("%s: %s", name, value)215216def print_env_var_values(self) -> None:217"""Get key-values pairs present as environment variables"""218write_output("%s:", "env_var")219with indent_log():220for key, value in sorted(self.configuration.get_environ_vars()):221env_var = f"PIP_{key.upper()}"222write_output("%s=%r", env_var, value)223224def open_in_editor(self, options: Values, args: List[str]) -> None:225editor = self._determine_editor(options)226227fname = self.configuration.get_file_to_edit()228if fname is None:229raise PipError("Could not determine appropriate file.")230231try:232subprocess.check_call([editor, fname])233except FileNotFoundError as e:234if not e.filename:235e.filename = editor236raise237except subprocess.CalledProcessError as e:238raise PipError(239"Editor Subprocess exited with exit code {}".format(e.returncode)240)241242def _get_n_args(self, args: List[str], example: str, n: int) -> Any:243"""Helper to make sure the command got the right number of arguments"""244if len(args) != n:245msg = (246"Got unexpected number of arguments, expected {}. "247'(example: "{} config {}")'248).format(n, get_prog(), example)249raise PipError(msg)250251if n == 1:252return args[0]253else:254return args255256def _save_configuration(self) -> None:257# We successfully ran a modifying command. Need to save the258# configuration.259try:260self.configuration.save()261except Exception:262logger.exception(263"Unable to save configuration. Please report this as a bug."264)265raise PipError("Internal Error.")266267def _determine_editor(self, options: Values) -> str:268if options.editor is not None:269return options.editor270elif "VISUAL" in os.environ:271return os.environ["VISUAL"]272elif "EDITOR" in os.environ:273return os.environ["EDITOR"]274else:275raise PipError("Could not determine editor to use.")276277278