Repository: conan-io/conan

Issue:
[feature] Return default value for get_safe
<!-- What is your suggestion? Please be as specific as possible! -->

When an option or setting is optional but used in middle of the recipe, get_safe return None when that element is removed. However, sometimes, we need to check the return to replace it by a default value:

```
class FooConan(ConanFile):
    options = {"foo": [True, False]}
	...

    def configure(self):
        if self.settings.os == "Windows" or not self._is_foo_supported():
            del self.options.foo

    def build(self):
        cmake = CMake(self)
        cmake.definitions["ENABLE_FOO"] = self.options.get_safe("foo") or False
        cmake.configure()
        cmake.build()
```

It would be nice if `get_safe` could return a default value:

    cmake.definitions["ENABLE_FOO"] = self.options.get_safe("foo", False)


- [ ] I've read the [CONTRIBUTING guide](https://github.com/conan-io/conan/blob/develop/.github/CONTRIBUTING.md).



Discussion:

Resolving patch:
diff --git a/conans/model/options.py b/conans/model/options.py
--- a/conans/model/options.py
+++ b/conans/model/options.py
@@ -391,8 +391,8 @@ def __contains__(self, option):
     def loads(text):
         return PackageOptions(yaml.safe_load(text) or {})
 
-    def get_safe(self, field):
-        return self._data.get(field)
+    def get_safe(self, field, default=None):
+        return self._data.get(field, default)
 
     def validate(self):
         for child in self._data.values():
diff --git a/conans/model/settings.py b/conans/model/settings.py
--- a/conans/model/settings.py
+++ b/conans/model/settings.py
@@ -202,16 +202,16 @@ def __init__(self, definition=None, name="settings", parent_value=None):
         self._data = {str(k): SettingsItem(v, "%s.%s" % (name, k))
                       for k, v in definition.items()}
 
-    def get_safe(self, name):
+    def get_safe(self, name, default=None):
         try:
             tmp = self
             for prop in name.split("."):
                 tmp = getattr(tmp, prop, None)
         except ConanException:
-            return None
+            return default
         if tmp is not None and tmp.value and tmp.value != "None":  # In case of subsettings is None
             return str(tmp)
-        return None
+        return default
 
     def copy(self):
         """ deepcopy, recursive


Test patch:
diff --git a/conans/test/unittests/model/options_test.py b/conans/test/unittests/model/options_test.py
--- a/conans/test/unittests/model/options_test.py
+++ b/conans/test/unittests/model/options_test.py
@@ -227,6 +227,19 @@ def pattern_unmatch_test(self):
                                                      ("OpenSSL.*:fake_option", "FuzzBuzz"),
                                                      ])
 
+    def test_get_safe_options(self):
+        self.assertEqual(True, self.sut.get_safe("static"))
+        self.assertEqual(3, self.sut.get_safe("optimized"))
+        self.assertEqual("NOTDEF", self.sut.get_safe("path"))
+        self.assertEqual(None, self.sut.get_safe("unknown"))
+        self.sut.path = "None"
+        self.sut.static = False
+        self.assertEqual(False, self.sut.get_safe("static"))
+        self.assertEqual("None", self.sut.get_safe("path"))
+        self.assertEqual(False, self.sut.get_safe("static", True))
+        self.assertEqual("None", self.sut.get_safe("path", True))
+        self.assertEqual(True, self.sut.get_safe("unknown", True))
+
 
 class OptionsValuesPropagationUpstreamNone(unittest.TestCase):
 
diff --git a/conans/test/unittests/model/settings_test.py b/conans/test/unittests/model/settings_test.py
--- a/conans/test/unittests/model/settings_test.py
+++ b/conans/test/unittests/model/settings_test.py
@@ -100,6 +100,9 @@ def test_get_safe(self):
         self.assertEqual(settings.os, "Windows")
         self.assertEqual(settings.get_safe("compiler.version"), None)
         self.assertEqual(settings.get_safe("build_type"), None)
+        self.assertEqual("Release", settings.get_safe("build_type", "Release"))
+        self.assertEqual(False, settings.get_safe("build_type", False))
+        self.assertEqual("Windows", settings.get_safe("os", "Linux"))
 
     def test_none_subsetting(self):
         yml = """os: