Skip to content

Commit

Permalink
improve settings.yml error message (#16065)
Browse files Browse the repository at this point in the history
  • Loading branch information
memsharded committed Apr 11, 2024
1 parent ad0b39c commit a2a8ebe
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 20 deletions.
41 changes: 21 additions & 20 deletions conans/model/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,39 +18,44 @@ def undefined_field(name, field, fields=None, value=None):
return ConanException("\n".join(result))


class SettingsItem(object):
class SettingsItem:
""" represents a setting value and its child info, which could be:
- A range of valid values: [Debug, Release] (for settings.compiler.runtime of VS)
- List [None, "ANY"] to accept None or any value
- A dict {subsetting: definition}, e.g. {version: [], runtime: []} for VS
"""
def __init__(self, definition, name):
def __init__(self, definition, name, value):
self._definition = definition # range of possible values
self._name = name # settings.compiler
self._value = None # gcc
self._value = value # gcc

@staticmethod
def new(definition, name):
if definition is None:
raise ConanException(f"Definition of settings.yml '{name}' cannot be null")
if isinstance(definition, dict):
self._definition = {}
parsed_definitions = {}
# recursive
for k, v in definition.items():
# None string from yaml definition maps to python None, means not-defined value
k = str(k) if k is not None else None
self._definition[k] = Settings(v, name, k)
parsed_definitions[k] = Settings(v, name, k)
else:
# list or tuple of possible values, it can include "ANY"
self._definition = [str(v) if v is not None else None for v in definition]
parsed_definitions = [str(v) if v is not None else None for v in definition]
return SettingsItem(parsed_definitions, name, None)

def __contains__(self, value):
return value in (self._value or "")

def copy(self):
""" deepcopy, recursive
"""
result = SettingsItem({}, name=self._name)
result._value = self._value
if not isinstance(self._definition, dict):
result._definition = self._definition # Not necessary to copy this, not mutable
definition = self._definition # Not necessary to copy this, not mutable
else:
result._definition = {k: v.copy() for k, v in self._definition.items()}
return result
definition = {k: v.copy() for k, v in self._definition.items()}
return SettingsItem(definition, self._name, self._value)

def copy_conaninfo_settings(self):
""" deepcopy, recursive
Expand All @@ -66,15 +71,12 @@ def copy_conaninfo_settings(self):
- Settings that are "final" (lists), like build_type, or arch or compiler.version they
can get any value without issues.
"""
result = SettingsItem({}, name=self._name)
result._value = self._value
if not isinstance(self._definition, dict):
result._definition = self._definition[:] + ["ANY"]
definition = self._definition[:] + ["ANY"]
else:
result._definition = {k: v.copy_conaninfo_settings()
for k, v in self._definition.items()}
result._definition["ANY"] = Settings()
return result
definition = {k: v.copy_conaninfo_settings() for k, v in self._definition.items()}
definition["ANY"] = Settings()
return SettingsItem(definition, self._name, self._value)

def __bool__(self):
if not self._value:
Expand Down Expand Up @@ -188,8 +190,7 @@ def __init__(self, definition=None, name="settings", parent_value="settings"):
raise ConanException(f"Invalid settings.yml format: '{name}{val}' is not a dictionary")
self._name = name # settings, settings.compiler
self._parent_value = parent_value # gcc, x86
self._data = {k: SettingsItem(v, "%s.%s" % (name, k))
for k, v in definition.items()}
self._data = {k: SettingsItem.new(v, f"{name}.{k}") for k, v in definition.items()}
self._frozen = False

def serialize(self):
Expand Down
12 changes: 12 additions & 0 deletions conans/test/integration/settings/test_settings_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,15 @@ def test_settings_user_convert_list_dict():
c.run("install . -s arch=x86_64 -s arch.subarch=2 ")
assert "arch=x86_64" in c.out
assert "arch.subarch=2" in c.out


def test_settings_user_error():
c = TestClient()
settings_user = textwrap.dedent("""\
os:
Windows:
libc: null
""")
save(os.path.join(c.cache_folder, "settings_user.yml"), settings_user)
c.run("profile show", assert_error=True)
assert "ERROR: Definition of settings.yml 'settings.os.libc' cannot be null" in c.out

0 comments on commit a2a8ebe

Please sign in to comment.