Repository: conan-io/conan

Issue:
Default value for options with ANY
In options you can define a special value "ANY" as stated in the docs: https://docs.conan.io/en/latest/reference/conanfile/attributes.html#options-default-options

But in the example there is no ``default_options`` defined. Doing some tests Conan behaves like this:

- No default value: **ERROR**

*conanfile.py*
```
from conans import ConanFile

class MyConanFile(ConanFile):
    name = "MyConanFile"
    version = "1.0"
    options = {"shared": [True, False], "config": "ANY"}
    default_options = "shared=False",
```
```
$ conan create . danimtb/testing
ERROR: MyConanFile/1.0@danimtb/testing: 'config' value not defined
```
- No assigned value in ``default_options``: **WORKS**

*conanfile.py*
```
from conans import ConanFile

class MyConanFile(ConanFile):
    name = "MyConanFile"
    version = "1.0"
    options = {"shared": [True, False], "config": "ANY"}
    default_options = "shared=False", "config="
```
```
$ conan create . danimtb/testing
...
MyConanFile/1.0@danimtb/testing: Package '5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9' created
```

But the generated *conaninfo.txt* has ``config=`` and ``conan search`` returns ``config:`` both without value.

- Use ``None`` as default value: **Works**

*conanfile.py*
```
from conans import ConanFile

class MyConanFile(ConanFile):
    name = "MyConanFile"
    version = "1.0"
    options = {"shared": [True, False], "config": "ANY"}
    default_options = "shared=False", "config=None"
```
```
$ conan create . danimtb/testing
...
MyConanFile/1.0@danimtb/testing: Package '5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9' created
```

Generates *conaninfo.txt* with "config=None", ``conan search`` returns "config: None" and package ID is NOT affected.

I suggest to use the last option as default, encourage users to use ``"config": "ANY `` together with ``"config=None"`` default value and do not allow just "config=". Would be breaking but more clear.


Discussion:
I'm not sure why if an option admits any value has to automatically default to anything and not require a default like any other one.
you could use a defaulted value if you want or even None, but just "config=" is something very weird
"config=" is indeed weird.

What @lasote suggests is to change the current default behavior, and assign it a None value automatically without having to specify it in the conanfile, did I understand correctly?
You didn't understand correctly. I was asking why we should do that for the ANY options and not for others.
I think the feature should be: raise when "config=" and allow "config=None"
Yes, that is what I suggested in the original comment. By:

> I suggest to use the last option as default, encourage users to use "config": "ANY together with "config=None" default value and do not allow just "config=".

I meant: "I suggest to use the last case indicated above" -> raise when "config=" and allow "config=None"
Just to be sure, you expect all recipes to handle the "config would be empty" case in the following way:
`if self.options.config == "None":` instead of the current `if self.options.config:` ?
@ericLemanissier no, this is not related.
@ericLemanissier it should be ``if self.options.config:`` and default value when "config=None" should be NoneType
ok, my bad, `if self.options.config:` takes the else branch when `default_options= "config=None"`
However `str(self.options.config)` is equal to the "None" string, instead of being an empty string
@ericLemanissier probably you are right, @danimtb performed some tests and `default_options= "config=None"` => `self.options.config == "None"` and `self.options.config != None`
I think this is a bug but a little dangerous.

Resolving patch:
diff --git a/conans/model/options.py b/conans/model/options.py
--- a/conans/model/options.py
+++ b/conans/model/options.py
@@ -165,7 +165,11 @@ def __init__(self, values=None):
             new_values = []
             for v in values:
                 option, value = v.split("=", 1)
-                new_values.append((option.strip(), value.strip()))
+                option, value = option.strip(), value.strip()
+                if not value:
+                    raise ConanException("Please define a default value for '%s' in"
+                                         "'default_options'" % option)
+                new_values.append((option, value))
             values = new_values
 
         # handle list of tuples (name, value)
@@ -270,7 +274,11 @@ def loads(text):
             if not line:
                 continue
             name, value = line.split("=", 1)
-            result.append((name.strip(), value.strip()))
+            name, value = name.strip(), value.strip()
+            if not value:
+                raise ConanException("Please define a default value for '%s' in"
+                                     "'default_options'" % name)
+            result.append((name, value))
         return OptionsValues(result)
 
     @property


Test patch:
diff --git a/conans/test/integration/options_test.py b/conans/test/integration/options_test.py
--- a/conans/test/integration/options_test.py
+++ b/conans/test/integration/options_test.py
@@ -65,6 +65,72 @@ class ConanLib(ConanFile):
         conaninfo = load(os.path.join(client.current_folder, CONANINFO))
         self.assertNotIn("zlib:shared=True", conaninfo)
 
+    def default_options_test(self):
+        client = TestClient()
+        conanfile = """
+from conans import ConanFile
+
+class MyConanFile(ConanFile):
+    name = "MyConanFile"
+    version = "1.0"
+    options = {"config": %s}
+    default_options = "config=%s"
+
+    def configure(self):
+        if self.options.config:
+            self.output.info("Boolean evaluation")
+        if self.options.config is None:
+            self.output.info("None evaluation")
+        if self.options.config == "None":
+            self.output.info("String evaluation")
+"""
+        # Using "ANY" as possible options
+        client.save({"conanfile.py": conanfile % ("\"ANY\"", "")})
+        error = client.run("create . danimtb/testing", ignore_error=True)
+        self.assertTrue(error)
+        self.assertIn("Please define a default value for 'config' in'default_options'", client.out)
+        client.save({"conanfile.py": conanfile % ("\"ANY\"", "None")})
+        client.run("create . danimtb/testing")
+        self.assertNotIn("Boolean evaluation", client.out)
+        self.assertNotIn("None evaluation", client.out)
+        self.assertIn("String evaluation", client.out)
+
+        # Using None as possible options
+        client.save({"conanfile.py": conanfile % ("[None]", "")})
+        error = client.run("create . danimtb/testing", ignore_error=True)
+        self.assertTrue(error)
+        self.assertIn("Please define a default value for 'config' in'default_options'", client.out)
+        client.save({"conanfile.py": conanfile % ("[None]", "None")})
+        client.run("create . danimtb/testing")
+        self.assertNotIn("Boolean evaluation", client.out)
+        self.assertNotIn("None evaluation", client.out)
+        self.assertIn("String evaluation", client.out)
+
+        # Using "None" as possible options
+        client.save({"conanfile.py": conanfile % ("[\"None\"]", "")})
+        error = client.run("create . danimtb/testing", ignore_error=True)
+        self.assertTrue(error)
+        self.assertIn("Please define a default value for 'config' in'default_options'", client.out)
+        client.save({"conanfile.py": conanfile % ("[\"None\"]", "None")})
+        client.run("create . danimtb/testing")
+        self.assertNotIn("Boolean evaluation", client.out)
+        self.assertNotIn("None evaluation", client.out)
+        self.assertIn("String evaluation", client.out)
+        client.save({"conanfile.py": conanfile % ("[\"None\"]", "\\\"None\\\"")})
+        error = client.run("create . danimtb/testing", ignore_error=True)
+        self.assertTrue(error)
+        self.assertIn("'\"None\"' is not a valid 'options.config' value", client.out)
+
+        # Using "ANY" as possible options and "otherstringvalue" as default
+        client.save({"conanfile.py": conanfile % ("[\"otherstringvalue\"]", "")})
+        error = client.run("create . danimtb/testing", ignore_error=True)
+        self.assertTrue(error)
+        self.assertIn("Please define a default value for 'config' in'default_options'", client.out)
+        client.save({"conanfile.py": conanfile % ("\"ANY\"", "otherstringvalue")})
+        client.run("create . danimtb/testing")
+        self.assertIn("Boolean evaluation", client.out)
+        self.assertNotIn("None evaluation", client.out)
+        self.assertNotIn("String evaluation", client.out)
     def general_scope_options_test(self):
         # https://github.com/conan-io/conan/issues/2538
         client = TestClient()