Repository: conan-io/conan
Pull request: Fix stacktrace with nonexistent graph file in `conan list`
Changelog: Bugfix: Fix stacktrace with nonexistent graph file in `conan list`.
Docs: Omit

Closes https://github.com/conan-io/conan/issues/16290

Problem statement:
[bug] conan list --graph with non-existing file
### Describe the bug

version: 2.3.1
conan_path: D:\venv\Scripts\conan
python
  version: 3.9.13
  sys_version: 3.9.13 (tags/v3.9.13:6de2ca5, May 17 2022, 16:36:42) [MSC v.1929 64 bit (AMD64)]
  sys_executable: D:\venv\Scripts\python.exe
  is_frozen: False
  architecture: AMD64
system
  version: 10.0.22631
  platform: Windows-10-10.0.22631-SP0
  system: Windows
  release: 10
  cpu: Intel64 Family 6 Model 186 Stepping 3, GenuineIntel

### How to reproduce it

Simply run `conan list --graph nonexistingfile.json`. It gives:

```shell
ERROR: Traceback (most recent call last):
  File "D:\venv\lib\site-packages\conan\cli\cli.py", line 291, in main
    cli.run(args)
  File "D:\venv\lib\site-packages\conan\cli\cli.py", line 193, in run
    command.run(self._conan_api, args[0][1:])
  File "D:\venv\lib\site-packages\conan\cli\command.py", line 164, in run
    info = self._method(conan_api, parser, *args)
  File "D:\venv\lib\site-packages\conan\cli\commands\list.py", line 250, in list
    pkglist = MultiPackagesList.load_graph(graphfile, args.graph_recipes, args.graph_binaries)
  File "D:\venv\lib\site-packages\conan\api\model.py", line 93, in load_graph
    graph = json.loads(load(graphfile))
  File "D:\venv\lib\site-packages\conans\util\files.py", line 133, in load
    with open(path, 'r', encoding=encoding, newline="") as handle:
FileNotFoundError: [Errno 2] No such file or directory: 'D:\\nonexistingfile.json'

ERROR: [Errno 2] No such file or directory: 'D:\\nonexistingfile.json'
```

Same behaviour on Linux.

Hints from issue discussion:
Thanks for the report, we'll make it have a prettier error in these cases

Commit message: Sprinkle some light=True in pkglist tests

Patch to review:
diff --git a/conan/cli/commands/list.py b/conan/cli/commands/list.py
index 1fd4a88113d..ccda87aee8b 100644
--- a/conan/cli/commands/list.py
+++ b/conan/cli/commands/list.py
@@ -1,4 +1,5 @@
 import json
+import os
 
 from conan.api.conan_api import ConanAPI
 from conan.api.model import ListPattern, MultiPackagesList
@@ -240,6 +241,8 @@ def list(conan_api: ConanAPI, parser, *args):
             raise ConanException("Cannot define lru when loading a graph json file")
         if args.filter_profile or args.filter_settings or args.filter_options:
             raise ConanException("Filtering binaries cannot be done when loading a graph json file")
+        if not os.path.isfile(args.graph):
+            raise ConanException(f"Graph file not found: {args.graph}")
     if (args.graph_recipes or args.graph_binaries) and not args.graph:
         raise ConanException("--graph-recipes and --graph-binaries require a --graph input")
     if args.remote and args.lru:
diff --git a/conans/test/integration/command_v2/test_combined_pkglist_flows.py b/conans/test/integration/command_v2/test_combined_pkglist_flows.py
index fbd88317d0a..bf5d386781c 100644
--- a/conans/test/integration/command_v2/test_combined_pkglist_flows.py
+++ b/conans/test/integration/command_v2/test_combined_pkglist_flows.py
@@ -13,7 +13,7 @@ class TestListUpload:
 
     @pytest.fixture()
     def client(self):
-        c = TestClient(default_server_user=True)
+        c = TestClient(default_server_user=True, light=True)
         c.save({
             "zlib.py": GenConanfile("zlib"),
             "zli.py": GenConanfile("zli", "1.0.0")
@@ -54,7 +54,7 @@ def test_list_upload_empty_list(self, client):
 
 class TestGraphCreatedUpload:
     def test_create_upload(self):
-        c = TestClient(default_server_user=True)
+        c = TestClient(default_server_user=True, light=True)
         c.save({"zlib/conanfile.py": GenConanfile("zlib", "1.0"),
                 "app/conanfile.py": GenConanfile("app", "1.0").with_requires("zlib/1.0")})
         c.run("create zlib")
@@ -69,7 +69,7 @@ def test_create_upload(self):
 
 class TestExportUpload:
     def test_export_upload(self):
-        c = TestClient(default_server_user=True)
+        c = TestClient(default_server_user=True, light=True)
         c.save({"zlib/conanfile.py": GenConanfile("zlib", "1.0")})
         c.run("export zlib --format=pkglist", redirect_stdout="pkglist.json")
         c.run("upload --list=pkglist.json -r=default -c")
@@ -77,29 +77,34 @@ def test_export_upload(self):
 
 
 class TestCreateGraphToPkgList:
+    def test_graph_pkg_nonexistant(self):
+        c = TestClient(light=True)
+        c.run("list --graph=non-existent-file.json", assert_error=True)
+        assert "Graph file not found: non-existent-file.json" in c.out
+
     def test_graph_pkg_list_only_built(self):
-        c = TestClient()
+        c = TestClient(light=True)
         c.save({"zlib/conanfile.py": GenConanfile("zlib", "1.0"),
                 "app/conanfile.py": GenConanfile("app", "1.0").with_requires("zlib/1.0")
                                                               .with_settings("os")
                                                               .with_shared_option(False)})
         c.run("create zlib")
-        c.run("create app --format=json -s os=Windows", redirect_stdout="graph.json")
+        c.run("create app --format=json -s os=Linux", redirect_stdout="graph.json")
         c.run("list --graph=graph.json --graph-binaries=build --format=json")
         pkglist = json.loads(c.stdout)["Local Cache"]
         assert len(pkglist) == 1
         pkgs = pkglist["app/1.0"]["revisions"]["8263c3c32802e14a2f03a0b1fcce0d95"]["packages"]
         assert len(pkgs) == 1
-        pkg_app = pkgs["e0bcc80c3f095670b71e535c193114d0155426cb"]
+        pkg_app = pkgs["d8b3bdd894c3eb9bf2a3119ee0f8c70843ace0ac"]
         assert pkg_app["info"]["requires"] == ["zlib/1.0.Z"]
-        assert pkg_app["info"]["settings"] == {'os': 'Windows'}
+        assert pkg_app["info"]["settings"] == {'os': 'Linux'}
         assert pkg_app["info"]["options"] == {'shared': 'False'}
 
     def test_graph_pkg_list_all_recipes_only(self):
         """
         --graph-recipes=* selects all the recipes in the graph
         """
-        c = TestClient()
+        c = TestClient(light=True)
         c.save({"zlib/conanfile.py": GenConanfile("zlib", "1.0"),
                 "app/conanfile.py": GenConanfile("app", "1.0").with_requires("zlib/1.0")})
         c.run("create zlib")
@@ -114,7 +119,7 @@ def test_graph_pkg_list_python_requires(self):
         """
         include python_requires too
         """
-        c = TestClient(default_server_user=True)
+        c = TestClient(default_server_user=True, light=True)
         c.save({"pytool/conanfile.py": GenConanfile("pytool", "0.1"),
                 "zlib/conanfile.py": GenConanfile("zlib", "1.0").with_python_requires("pytool/0.1"),
                 "app/conanfile.py": GenConanfile("app", "1.0").with_requires("zlib/1.0")})
@@ -135,7 +140,7 @@ def test_graph_pkg_list_create_python_requires(self):
         """
         include python_requires too
         """
-        c = TestClient(default_server_user=True)
+        c = TestClient(default_server_user=True, light=True)
         c.save({"conanfile.py": GenConanfile("pytool", "0.1").with_package_type("python-require")})
         c.run("create . --format=json", redirect_stdout="graph.json")
         c.run("list --graph=graph.json --format=json")
@@ -146,7 +151,7 @@ def test_graph_pkg_list_create_python_requires(self):
 
 class TestGraphInfoToPkgList:
     def test_graph_pkg_list_only_built(self):
-        c = TestClient(default_server_user=True)
+        c = TestClient(default_server_user=True, light=True)
         c.save({"zlib/conanfile.py": GenConanfile("zlib", "1.0"),
                 "app/conanfile.py": GenConanfile("app", "1.0").with_requires("zlib/1.0")})
         c.run("create zlib")
@@ -258,7 +263,7 @@ def test_graph_2_pkg_list_remotes(self):
 class TestDownloadUpload:
     @pytest.fixture()
     def client(self):
-        c = TestClient(default_server_user=True)
+        c = TestClient(default_server_user=True, light=True)
         c.save({
             "zlib.py": GenConanfile("zlib"),
             "zli.py": GenConanfile("zli", "1.0.0")
@@ -315,7 +320,7 @@ def test_download_upload_only_recipes(self, client, prev_list):
 class TestListRemove:
     @pytest.fixture()
     def client(self):
-        c = TestClient(default_server_user=True)
+        c = TestClient(default_server_user=True, light=True)
         c.save({
             "zlib.py": GenConanfile("zlib"),
             "zli.py": GenConanfile("zli", "1.0.0")


Code review comments:
diff_hunk: @@ -240,6 +241,8 @@ def list(conan_api: ConanAPI, parser, *args):
             raise ConanException("Cannot define lru when loading a graph json file")
         if args.filter_profile or args.filter_settings or args.filter_options:
             raise ConanException("Filtering binaries cannot be done when loading a graph json file")
+        if not os.path.isfile(args.graph):
path: conan/cli/commands/list.py
text: @user1:
This is not as much a cli arg error as the others.
Maybe it would be better to make ``MultiPackageLists.load_graph(graph_file`` be the one that raises the error if ``graphfile`` doesn't exist? That will make a more robust API too.

@author:
Totally right