From 793d331eac465744538947402e7a0ba7e499a91f Mon Sep 17 00:00:00 2001 From: Stu Hood Date: Fri, 29 May 2020 13:32:17 -0700 Subject: [PATCH] Attempt to parse as TOML, and fall back to INI. (#9901) ### Problem Since rc files do not have file suffixes, they are always parsed as `ini` today, triggering a deprecation even for files that are already in `toml` format. ### Solution Attempt to parse unsuffixed config files first as `toml`, and then as `ini`. Trigger the deprecation only if they were successfully parsed as `ini`. [ci skip-rust-tests] [ci skip-jvm-tests] --- src/python/pants/option/config.py | 52 +++++++++++++++++------ src/python/pants/option/global_options.py | 6 ++- 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/src/python/pants/option/config.py b/src/python/pants/option/config.py index 2d868128e09..e65bcb22608 100644 --- a/src/python/pants/option/config.py +++ b/src/python/pants/option/config.py @@ -94,19 +94,29 @@ def _meta_load( for config_item in config_items: config_path = config_item.path if hasattr(config_item, "path") else config_item with open_ctx(config_item) as config_file: - content = config_file.read() - content_digest = sha1(content).hexdigest() + content_bytes = config_file.read() + content_digest = sha1(content_bytes).hexdigest() + content = content_bytes.decode() normalized_seed_values = cls._determine_seed_values(seed_values=seed_values) config_values: _ConfigValues if PurePath(config_path).suffix == ".toml": - toml_values = cast(Dict[str, Any], toml.loads(content.decode())) - toml_values["DEFAULT"] = { - **normalized_seed_values, - **toml_values.get("DEFAULT", {}), - } - config_values = _TomlValues(toml_values) + config_values = cls._meta_load_as_toml(content, normalized_seed_values) + elif PurePath(config_path).suffix == ".ini": + config_values = cls._meta_load_as_ini(content, normalized_seed_values) else: + try: + config_values = cls._meta_load_as_toml(content, normalized_seed_values) + except Exception as toml_e: + try: + config_values = cls._meta_load_as_ini(content, normalized_seed_values) + except Exception as ini_e: + raise cls.ConfigError( + f"Unsuffixed Config path {config_path} could not be parsed " + f"as either TOML or INI:\n TOML: {toml_e}\n INI: {ini_e}" + ) + + if isinstance(config_values, _IniValues): script_instructions = ( "curl -L -o migrate_to_toml_config.py 'https://git.io/Jv02R' && chmod +x " f"migrate_to_toml_config.py && ./migrate_to_toml_config.py {config_path}" @@ -141,12 +151,9 @@ def _meta_load( ] warn_or_error( removal_version="1.31.0.dev0", - deprecated_entity_description="INI config files (`pants.ini`)", + deprecated_entity_description=f"INI config files (`{config_path}`)", hint="\n".join(msg), ) - ini_parser = configparser.ConfigParser(defaults=normalized_seed_values) - ini_parser.read_string(content.decode()) - config_values = _IniValues(ini_parser) single_file_configs.append( _SingleFileConfig( @@ -155,6 +162,27 @@ def _meta_load( ) return _ChainedConfig(tuple(reversed(single_file_configs))) + @classmethod + def _meta_load_as_toml( + cls, config_content: str, normalized_seed_values: Dict[str, str] + ) -> "_TomlValues": + """Attempt to parse as TOML, raising an exception on failure.""" + toml_values = cast(Dict[str, Any], toml.loads(config_content)) + toml_values["DEFAULT"] = { + **normalized_seed_values, + **toml_values.get("DEFAULT", {}), + } + return _TomlValues(toml_values) + + @classmethod + def _meta_load_as_ini( + cls, config_content: str, normalized_seed_values: Dict[str, str] + ) -> "_IniValues": + """Attempt to parse as INI, raising an exception on failure.""" + ini_parser = configparser.ConfigParser(defaults=normalized_seed_values) + ini_parser.read_string(config_content) + return _IniValues(ini_parser) + @staticmethod def _determine_seed_values(*, seed_values: Optional[SeedValues] = None) -> Dict[str, str]: """We pre-populate several default values to allow %([key-name])s interpolation. diff --git a/src/python/pants/option/global_options.py b/src/python/pants/option/global_options.py index 60e27d86dbe..30076ce41ef 100644 --- a/src/python/pants/option/global_options.py +++ b/src/python/pants/option/global_options.py @@ -382,8 +382,10 @@ def register_bootstrap_options(cls, register): metavar="", daemon=False, default=["/etc/pantsrc", "~/.pants.rc"], - help="Override config with values from these files. " - "Later files override earlier ones.", + help=( + "Override config with values from these files, using syntax matching that of " + "`--pants-config-files`." + ), ) register( "--pythonpath",