mirror of https://github.com/pypa/hatch.git
255 lines
11 KiB
Python
255 lines
11 KiB
Python
RESERVED_OPTIONS = {
|
|
'dependencies': list,
|
|
'dev-mode': bool,
|
|
'env-exclude': list,
|
|
'env-include': list,
|
|
'env-vars': dict,
|
|
'features': list,
|
|
'matrix-name-format': str,
|
|
'platforms': list,
|
|
'post-install-commands': list,
|
|
'pre-install-commands': list,
|
|
'python': str,
|
|
'scripts': dict,
|
|
'skip-install': bool,
|
|
'type': str,
|
|
}
|
|
|
|
|
|
def apply_overrides(env_name, source, condition, condition_value, options, new_config, option_types=None):
|
|
if option_types is None:
|
|
option_types = RESERVED_OPTIONS
|
|
|
|
for option, data in options.items():
|
|
_, separator, option = option.rpartition('set-')
|
|
overwrite = True if separator else False
|
|
|
|
# Prevent manipulation of reserved options
|
|
if option_types is not RESERVED_OPTIONS and option in RESERVED_OPTIONS:
|
|
continue
|
|
|
|
override_type = option_types.get(option)
|
|
if override_type in TYPE_OVERRIDES:
|
|
TYPE_OVERRIDES[override_type](
|
|
env_name, option, data, source, condition, condition_value, new_config, overwrite
|
|
)
|
|
|
|
|
|
def _apply_override_to_mapping(env_name, option, data, source, condition, condition_value, new_config, overwrite):
|
|
new_mapping = {}
|
|
if isinstance(data, str):
|
|
key, separator, value = data.partition('=')
|
|
if not separator:
|
|
value = condition_value
|
|
new_mapping[key] = value
|
|
elif isinstance(data, list):
|
|
for i, entry in enumerate(data, 1):
|
|
if isinstance(entry, str):
|
|
key, separator, value = entry.partition('=')
|
|
if not separator:
|
|
value = condition_value
|
|
new_mapping[key] = value
|
|
elif isinstance(entry, dict):
|
|
if 'key' not in entry:
|
|
raise ValueError(
|
|
f'Entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` '
|
|
f'must have an option named `key`'
|
|
)
|
|
key = entry['key']
|
|
if not isinstance(key, str):
|
|
raise TypeError(
|
|
f'Option `key` in entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.'
|
|
f'{condition}.{option}` must be a string'
|
|
)
|
|
elif not key:
|
|
raise ValueError(
|
|
f'Option `key` in entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.'
|
|
f'{condition}.{option}` cannot be an empty string'
|
|
)
|
|
value = entry.get('value', condition_value)
|
|
if not isinstance(value, str):
|
|
raise TypeError(
|
|
f'Option `value` in entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.'
|
|
f'{condition}.{option}` must be a string'
|
|
)
|
|
if _resolve_condition(env_name, option, source, condition, condition_value, entry, i):
|
|
new_mapping[key] = value
|
|
else:
|
|
raise TypeError(
|
|
f'Entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` '
|
|
f'must be a string or an inline table'
|
|
)
|
|
else:
|
|
raise TypeError(
|
|
f'Field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` must be a string or an array'
|
|
)
|
|
|
|
if overwrite:
|
|
new_config[option] = new_mapping
|
|
elif option in new_config:
|
|
new_config[option].update(new_mapping)
|
|
elif new_mapping:
|
|
new_config[option] = new_mapping
|
|
|
|
|
|
def _apply_override_to_array(env_name, option, data, source, condition, condition_value, new_config, overwrite):
|
|
if not isinstance(data, list):
|
|
raise TypeError(f'Field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` must be an array')
|
|
|
|
new_array = []
|
|
for i, entry in enumerate(data, 1):
|
|
if isinstance(entry, str):
|
|
new_array.append(entry)
|
|
elif isinstance(entry, dict):
|
|
if 'value' not in entry:
|
|
raise ValueError(
|
|
f'Entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` '
|
|
f'must have an option named `value`'
|
|
)
|
|
value = entry['value']
|
|
if not isinstance(value, str):
|
|
raise TypeError(
|
|
f'Option `value` in entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.'
|
|
f'{condition}.{option}` must be a string'
|
|
)
|
|
elif not value:
|
|
raise ValueError(
|
|
f'Option `value` in entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.'
|
|
f'{condition}.{option}` cannot be an empty string'
|
|
)
|
|
if _resolve_condition(env_name, option, source, condition, condition_value, entry, i):
|
|
new_array.append(value)
|
|
else:
|
|
raise TypeError(
|
|
f'Entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` '
|
|
f'must be a string or an inline table'
|
|
)
|
|
|
|
if overwrite:
|
|
new_config[option] = new_array
|
|
elif option in new_config:
|
|
new_config[option].extend(new_array)
|
|
elif new_array:
|
|
new_config[option] = new_array
|
|
|
|
|
|
def _apply_override_to_string(env_name, option, data, source, condition, condition_value, new_config, overwrite):
|
|
if isinstance(data, str):
|
|
new_config[option] = data
|
|
elif isinstance(data, dict):
|
|
if 'value' not in data:
|
|
raise ValueError(
|
|
f'Field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` '
|
|
f'must have an option named `value`'
|
|
)
|
|
value = data['value']
|
|
if not isinstance(value, str):
|
|
raise TypeError(
|
|
f'Option `value` in field `tool.hatch.envs.{env_name}.overrides.{source}.'
|
|
f'{condition}.{option}` must be a string'
|
|
)
|
|
if _resolve_condition(env_name, option, source, condition, condition_value, data):
|
|
new_config[option] = value
|
|
elif isinstance(data, list):
|
|
for i, entry in enumerate(data, 1):
|
|
if isinstance(entry, str):
|
|
new_config[option] = entry
|
|
break
|
|
elif isinstance(entry, dict):
|
|
if 'value' not in entry:
|
|
raise ValueError(
|
|
f'Entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` '
|
|
f'must have an option named `value`'
|
|
)
|
|
value = entry['value']
|
|
if not isinstance(value, str):
|
|
raise TypeError(
|
|
f'Option `value` in entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.'
|
|
f'{condition}.{option}` must be a string'
|
|
)
|
|
if _resolve_condition(env_name, option, source, condition, condition_value, entry, i):
|
|
new_config[option] = value
|
|
break
|
|
else:
|
|
raise TypeError(
|
|
f'Entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` '
|
|
f'must be a string or an inline table'
|
|
)
|
|
else:
|
|
raise TypeError(
|
|
f'Field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` '
|
|
f'must be a string, inline table, or an array'
|
|
)
|
|
|
|
|
|
def _apply_override_to_boolean(env_name, option, data, source, condition, condition_value, new_config, overwrite):
|
|
if isinstance(data, bool):
|
|
new_config[option] = data
|
|
elif isinstance(data, dict):
|
|
if 'value' not in data:
|
|
raise ValueError(
|
|
f'Field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` '
|
|
f'must have an option named `value`'
|
|
)
|
|
value = data['value']
|
|
if not isinstance(value, bool):
|
|
raise TypeError(
|
|
f'Option `value` in field `tool.hatch.envs.{env_name}.overrides.{source}.'
|
|
f'{condition}.{option}` must be a boolean'
|
|
)
|
|
if _resolve_condition(env_name, option, source, condition, condition_value, data):
|
|
new_config[option] = value
|
|
elif isinstance(data, list):
|
|
for i, entry in enumerate(data, 1):
|
|
if isinstance(entry, bool):
|
|
new_config[option] = entry
|
|
break
|
|
elif isinstance(entry, dict):
|
|
if 'value' not in entry:
|
|
raise ValueError(
|
|
f'Entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` '
|
|
f'must have an option named `value`'
|
|
)
|
|
value = entry['value']
|
|
if not isinstance(value, bool):
|
|
raise TypeError(
|
|
f'Option `value` in entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.'
|
|
f'{condition}.{option}` must be a boolean'
|
|
)
|
|
if _resolve_condition(env_name, option, source, condition, condition_value, entry, i):
|
|
new_config[option] = value
|
|
break
|
|
else:
|
|
raise TypeError(
|
|
f'Entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` '
|
|
f'must be a boolean or an inline table'
|
|
)
|
|
else:
|
|
raise TypeError(
|
|
f'Field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` '
|
|
f'must be a boolean, inline table, or an array'
|
|
)
|
|
|
|
|
|
def _resolve_condition(env_name, option, source, condition, condition_value, condition_config, condition_index=None):
|
|
location = 'field' if condition_index is None else f'entry #{condition_index} in field'
|
|
if 'if' in condition_config:
|
|
allowed_values = condition_config['if']
|
|
if not isinstance(allowed_values, list):
|
|
raise TypeError(
|
|
f'Option `if` in {location} `tool.hatch.envs.{env_name}.overrides.{source}.'
|
|
f'{condition}.{option}` must be an array'
|
|
)
|
|
if condition_value not in allowed_values:
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
TYPE_OVERRIDES = {
|
|
dict: _apply_override_to_mapping,
|
|
list: _apply_override_to_array,
|
|
str: _apply_override_to_string,
|
|
bool: _apply_override_to_boolean,
|
|
}
|