Repository: conan-io/conan

Issue:
Add options to make conan inspect more machine friendly
I would like to make `conan inspect` more machine friendly.  Here are two things that would help me:

Add an option where the label will not be included on the attribute.  For example, currently if you run
```
$ conan inspect . -a name
name: service-common
```
Then a script would have to parse the actual name from the output.  I would like to have an option that would remove the label, like:
```
$ conan inspect . --no-label -a name
service-common
```
Also, I'd like a way to have the JSON output sent to stdout.  For example:
```
$ conan inspect . -j -
{"name": "service-common", "version": null, "url": "http://bitbucket-idb:7990/scm/deal/falcon.git", "homepage": null, "license": "Copyright (c) 2018 Tradeweb", "author": null, "description": "Falcon Base Service common library", "topics": null, "generators": ["cmake"], "exports": [], "exports_sources": [], "short_paths": false, "apply_env": true, "build_policy": null}
```

Thanks,


Discussion:
Hi @sourcedelica 

Regarding the last one, would it be ok if there is other output in stdout, before the  actual json? Or should it be completely silented? 
In that case it should be completely silenced, so you could use the standard output of the `conan inspect` command directly.
And what if something fails? Where should the output be directed for later inspection?

I guess we should discuss this, as this affects the whole design of the UI and the internal conan API.
We are using the conan_api directly and face similars problems regarding machine readability.
Since e.g. the generators can be defined as string, tuple, list, array in the conanfile, we currently need to do a lot of checks to get the list of generators. 
You'll likely face the same problem when getting the output into json format. Maybe the api could also be adjusted to always return a list when the value can be a list (options, default_options, settings, generators, exports, ...)

For the command, maybe you could reuse the format that is used in some api methods (e.g. users_list) but provide the error details:
`{"error":"errormessage", "inspect":{"name":...}} `
Of course the goal is to have the same format for all methods in conan 2.0. So this might not be good enough.
The second part of the json to the `stdout`, in nix systems, can be done with `conan inspect . --json /dev/stdout`. I think this is enough for now. If you really want that feature I would like to separate it in a different one because as @memsharded said, it implies several changes in the api.

The things I think we can do:

1) Add a ``--raw`` argument that will return only the values, one per `-a` specified, in a new line, in order.
2) If ``--json`` is specified do not output anything to the stdout.
That sounds good to me.
Blocked until: https://github.com/conan-io/conan/issues/4225
> Add a --raw argument that will return only the values

Some of our CI automation is currently done with strange long commands like `export CONANREF=$(python -c "import json; j=json.load(open('packageInfo.json')); print(j['installed'][0]['recipe']['id'])")`

So this would be very welcome.
I second that feature request! Last time I had to extract some recipe information for scripting purposes I ended with code such as the following:

```bash
NAME=$(conan inspect . -a name | sed -e "s/^name: //")
VERSION=$(conan inspect . -a version | sed -e "s/^version: //")
```

Maybe there are even simpler/cleaner ways to implement the post processing but I would hope that we can get rid of it completely at some point.
Ok, could we do at least the ``--raw`` part @lasote? That would be very simple to implement and alleviate some of these issues.
Yes, but take into account that some output can always be printed for unexpected reasons to the stdout, like the issues with the console encoding, the issues with the pre-commands launched in every Windows shell and so on. 
Let's do it anyway, a `--raw` argument to the `conan inspect` command that prints only the values of the specified fields, in order and one field per line.
What do you think that should be the output for `options`, `settings` and `default_options`? Currently, it is:

```bash
settings: ('os', 'arch', 'build_type', 'compiler')
options:
    bar: [True, False]
    foo: [True, False]
default_options:
    bar: True
    foo: True
```

Resolving patch:
diff --git a/conans/client/command.py b/conans/client/command.py
--- a/conans/client/command.py
+++ b/conans/client/command.py
@@ -208,10 +208,21 @@ def inspect(self, *args):
                             action=OnceArgument)
         parser.add_argument("-j", "--json", default=None, action=OnceArgument,
                             help='json output file')
+        parser.add_argument('--raw', default=None, action=OnceArgument,
+                            help='Print just the value of the requested attribute')
 
         args = parser.parse_args(*args)
-        result = self._conan.inspect(args.path_or_reference, args.attribute, args.remote)
-        Printer(self._user_io.out).print_inspect(result)
+
+        if args.raw and args.attribute:
+            raise ConanException("Argument '--raw' is incompatible with '-a'")
+
+        if args.raw and args.json:
+            raise ConanException("Argument '--raw' is incompatible with '--json'")
+
+        attributes = [args.raw, ] if args.raw else args.attribute
+
+        result = self._conan.inspect(args.path_or_reference, attributes, args.remote)
+        Printer(self._user_io.out).print_inspect(result, raw=args.raw)
         if args.json:
             json_output = json.dumps(result)
             if not os.path.isabs(args.json):
diff --git a/conans/client/printer.py b/conans/client/printer.py
--- a/conans/client/printer.py
+++ b/conans/client/printer.py
@@ -20,7 +20,7 @@ class Printer(object):
     def __init__(self, out):
         self._out = out
 
-    def print_inspect(self, inspect):
+    def print_inspect(self, inspect, raw=False):
         for k, v in inspect.items():
             if k == "default_options":
                 if isinstance(v, str):
@@ -29,12 +29,18 @@ def print_inspect(self, inspect):
                     v = OptionsValues(v)
                 elif isinstance(v, list):
                     v = OptionsValues(tuple(v))
-            if isinstance(v, (dict, OptionsValues)):
-                self._out.writeln("%s:" % k)
-                for ok, ov in sorted(v.items()):
-                    self._out.writeln("    %s: %s" % (ok, ov))
+                elif isinstance(v, dict):
+                    v = OptionsValues(v)
+
+            if raw:
+                self._out.write(str(v))
             else:
-                self._out.writeln("%s: %s" % (k, str(v)))
+                if isinstance(v, (dict, OptionsValues)):
+                    self._out.writeln("%s:" % k)
+                    for ok, ov in sorted(v.items()):
+                        self._out.writeln("    %s: %s" % (ok, ov))
+                else:
+                    self._out.writeln("%s: %s" % (k, str(v)))
 
     def print_info(self, data, _info, package_filter=None, show_paths=False, show_revisions=False):
         """ Print in console the dependency information for a conan file


Test patch:
diff --git a/conans/test/functional/command/inspect_test.py b/conans/test/functional/command/inspect_test.py
--- a/conans/test/functional/command/inspect_test.py
+++ b/conans/test/functional/command/inspect_test.py
@@ -338,3 +338,81 @@ def build(self):
     bar: True
     foo: True
 """, client.out)
+
+
+class InspectRawTest(unittest.TestCase):
+    conanfile = textwrap.dedent("""
+        from conans import ConanFile
+        
+        class Pkg(ConanFile):
+            name = "MyPkg"
+            version = "1.2.3"
+            author = "John Doe"
+            url = "https://john.doe.com"
+            settings = "os", "arch", "build_type", "compiler"
+            options = {"foo": [True, False], "bar": [True, False]}
+            default_options = "foo=True", "bar=True"
+            
+            _private = "Nothing"
+
+            def build(self):
+                pass
+        """)
+
+    def test_no_field_or_multiple(self):
+        client = TestClient()
+        client.save({"conanfile.py": self.conanfile})
+        client.run("inspect . --raw", assert_error=True)
+        client.run("inspect . --raw=name --raw=version", assert_error=True)
+
+    def test_incompatible_commands(self):
+        client = TestClient()
+        client.save({"conanfile.py": self.conanfile})
+
+        client.run("inspect . --raw=name -a=version", assert_error=True)
+        self.assertIn("ERROR: Argument '--raw' is incompatible with '-a'", client.out)
+
+        client.run("inspect . --raw=name --json=tmp.json", assert_error=True)
+        self.assertIn("ERROR: Argument '--raw' is incompatible with '--json'", client.out)
+
+    def test_invalid_field(self):
+        client = TestClient()
+        client.save({"conanfile.py": self.conanfile})
+        client.run("inspect . --raw=not_exists", assert_error=True)
+        self.assertIn("ERROR: 'Pkg' object has no attribute 'not_exists'", client.out)
+
+    def test_private_field(self):
+        client = TestClient()
+        client.save({"conanfile.py": self.conanfile})
+        client.run("inspect . --raw=_private")
+        self.assertEqual("Nothing", client.out)
+
+    def test_settings(self):
+        client = TestClient()
+        client.save({"conanfile.py": self.conanfile})
+        client.run("inspect . --raw=settings")
+        self.assertEqual("('os', 'arch', 'build_type', 'compiler')", client.out)
+
+    def test_options_dict(self):
+        client = TestClient()
+        client.save({"conanfile.py": self.conanfile})
+        client.run("inspect . --raw=options")
+        self.assertEqual("{'foo': [True, False], 'bar': [True, False]}", client.out)
+
+    def test_default_options_list(self):
+        client = TestClient()
+        client.save({"conanfile.py": self.conanfile})
+        client.run("inspect . --raw=default_options")
+        self.assertEqual("bar=True\nfoo=True", client.out)
+
+    def test_default_options_dict(self):
+        client = TestClient()
+        conanfile = textwrap.dedent("""
+            from conans import ConanFile
+            
+            class Lib(ConanFile):
+                default_options = {"dict": True, "list": False}
+            """)
+        client.save({"conanfile.py": conanfile})
+        client.run("inspect . --raw=default_options")
+        self.assertEqual("dict=True\nlist=False", client.out)