Repository: conan-io/conan

Issue:
[bug] Obscure error in CMakeDeps on malformed requirement string passed to self.cpp_info.requires
### Describe the bug

Conan produces a cryptic "too many values to unpack (expected 2)" error
```
======== Testing the package ========
Removing previously existing 'test_package' build folder: /__w/1/s/packages/sim_blocks/test_package/build/gcc-11-x86_64-release
foo/1.2.3 (test package): Test package build: build/gcc-11-x86_64-release
foo/1.2.3 (test package): Test package build folder: /__w/1/s/packages/sim_blocks/test_package/build/gcc-11-x86_64-release
foo/1.2.3 (test package): Writing generators to /__w/1/s/packages/sim_blocks/test_package/build/gcc-11-x86_64-release/generators
foo/1.2.3 (test package): Generator 'CMakeDeps' calling 'generate()'
ERROR: Error in generator 'CMakeDeps': error generating context for 'foo/1.2.3': too many values to unpack (expected 2)
```
when `self.cpp_info.requires` contains malformed requirement strings with multiple `::` separators. The error occurs deep in the CMakeDeps generator and doesn't clearly indicate that the issue is with malformed requirement syntax in the conanfile. A useful error pointing out the malformed string would have helped a lot with catching the underlying issue here!

Version Info:

```
version: 2.9.1
conan_path: /home/tom/.pyenv/versions/conan2env/bin/conan
python
  version: 3.10.12
  sys_version: 3.10.12 (main, Jul 30 2024, 13:07:00) [GCC 11.4.0]
  sys_executable: /home/tom/.pyenv/versions/3.10.12/envs/conan2env/bin/python
  is_frozen: False
  architecture: x86_64
system
  version: #63~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Tue Apr 22 19:00:15 UTC 2
  platform: Linux-6.8.0-60-generic-x86_64-with-glibc2.35
  system: Linux
  release: 6.8.0-60-generic
  cpu: x86_64
```

### How to reproduce it

1. Start from `conan new cmake_exe -d name=foo -d version=1.2.3`
1. Add `generators = "CMakeDeps", "CMakeToolchain"` to `test_package/conanfile.py`
1. Add the following the `conanfile.py`
    ```py
    requires = "gtest/1.14.0"
    def package_info(self):
        self.cpp_info.requires = [
            "gtest::this"  # <- How we encountered bug: Missed comma here
            "is::problem"
        ]
    ```
1. `conan create .`
1. Observer error
  ```py
  ======== Testing the package ========
  foo/1.2.3 (test package): Test package build: 
  foo/1.2.3 (test package): Test package build folder: ./test_package
  foo/1.2.3 (test package): Writing generators to ./test_package
  foo/1.2.3 (test package): Generator 'CMakeDeps' calling 'generate()'
  ERROR: Error in generator 'CMakeDeps': error generating context for 'foo/1.2.3': too many values to unpack (expected 2)
  ```


Discussion:
Hi @transegn 

Thanks for your feedback.

While it is true that the error message is not very clear, this wouldn't be a bug, but expected behavior. The inputs were incorrect, and the generator ``CMakeDeps`` managed to identify it and report the package ``foo/1.2.3`` had some error. 

The issue is that Python is very flexible, and the exposed API surface via ``conanfile.py`` is very large. Conan cannot be checking for all possible errors that could happen to report them adequately. Because users could do:

- Introduce an incorrect ``cpp_info.requires`` type, such as a dictionary or a custom object
- Introduce any incorrect value as ``requires`` items, not only in content/format of the value, but also different types.
- Introduce many different variants of format errors in the values, such as ``gtest:this``, ``gtest;this`` (semi-colon), ``gtest:::this``, etc, etc.

Every single check that is done in the code will be executed for every ``cpp_info.requires`` and for every ``cpp_info.components[comp].requires`` component information, for every package ``package_info()`` method in the dependency graph, once per instance of the package in the dependency graph (so repeated nodes such as tool-requires and test-requires will execute once per node), with an extra overhead, to try to protect against very occasional user errors.

As always, this is a trade-off, in this case a trade-off between performance (and code complexity and maintenance) and UX for user protection against syntax errors.

So I am afraid this cannot be classified as a bug to be fixed with a ``.requires`` validity check.

But I agree it would be worth to try to improve the specific message of the consumer side. I'll have a quick look if the error message in that specific consumer generator ``CMakeDeps`` might be improved to add some extra hint about the specific string. Thanks for your reproduction steps, that really help!
Closed by https://github.com/conan-io/conan/pull/18552 for next Conan 2.18.
The error message will print now the specific offending string with the double "::". Thanks for the feedback! 



Patch:
diff --git a/conan/internal/model/cpp_info.py b/conan/internal/model/cpp_info.py
index bf60b1b241f..3d6fab03f9a 100644
--- a/conan/internal/model/cpp_info.py
+++ b/conan/internal/model/cpp_info.py
@@ -836,7 +836,7 @@ def required_components(self):
                 if r not in ret:
                     ret.append(r)
         # Then split the names
-        ret = [r.split("::") if "::" in r else (None, r) for r in ret]
+        ret = [r.split("::", 1) if "::" in r else (None, r) for r in ret]
         return ret
 
     def deduce_full_cpp_info(self, conanfile):


Test patch:
diff --git a/test/integration/test_components_error.py b/test/integration/test_components_error.py
index bc27f58a50c..8d925008951 100644
--- a/test/integration/test_components_error.py
+++ b/test/integration/test_components_error.py
@@ -167,3 +167,26 @@ def package_info(self):
     t.run('create top.py --name=top2 --version=version')
     t.run('create consumer.py --name=wrong --version=version')
     # This runs without crashing, because it is not chcking that top::other doesn't exist
+
+
+def test_component_double_colon_error_message():
+    c = TestClient()
+    t2 = textwrap.dedent("""
+        from conan import ConanFile
+
+        class t2Conan(ConanFile):
+            name = "t2"
+            version = "0.1.0"
+            requires = "t1/0.1.0"
+            def package_info(self):
+                self.cpp_info.requires.append("t1::comp1::other")
+        """)
+
+    c.save({"t1/conanfile.py": GenConanfile("t1", "0.1.0"),
+            "t2/conanfile.py": t2,
+            "t2/test_package/conanfile.py": GenConanfile().with_settings("build_type")
+                                                          .with_generator("CMakeDeps")
+                                                          .with_test("pass")})
+    c.run("create t1")
+    c.run("create t2", assert_error=True)
+    assert "Component 't1::comp1::other' not found in 't1' package requirement" in c.out