Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Follow conventions for Conan 2.0 CLI output #12235

Merged
merged 15 commits into from
Oct 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions conan/api/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,3 +187,18 @@ def error(self, msg):

def flush(self):
self.stream.flush()


def cli_out_write(data, fg=None, bg=None, endline="\n", indentation=0):
"""
Output to be used by formatters to dump information to stdout
"""

fg_ = fg or ''
bg_ = bg or ''
if color_enabled(sys.stdout):
data = f"{' ' * indentation}{fg_}{bg_}{data}{Style.RESET_ALL}{endline}"
else:
data = f"{' ' * indentation}{data}{endline}"

sys.stdout.write(data)
4 changes: 2 additions & 2 deletions conan/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
from inspect import getmembers

from conan.api.conan_api import ConanAPIV2
from conan.api.output import ConanOutput, Color
from conan.cli.command import ConanSubCommand, cli_out_write
from conan.api.output import ConanOutput, Color, cli_out_write
from conan.cli.command import ConanSubCommand
from conan.cli.exit_codes import SUCCESS, ERROR_MIGRATION, ERROR_GENERAL, USER_CTRL_C, \
ERROR_SIGTERM, USER_CTRL_BREAK, ERROR_INVALID_CONFIGURATION, ERROR_INVALID_SYSTEM_REQUIREMENTS
from conans import __version__ as client_version
Expand Down
44 changes: 18 additions & 26 deletions conan/cli/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def _fill_text(self, text, width, indent):

class BaseConanCommand(object):
def __init__(self, method, formatters=None):
self._formatters = {}
self._formatters = {"text": lambda x: None}
czoido marked this conversation as resolved.
Show resolved Hide resolved
self._method = method
self._name = None
self._parser = None
Expand All @@ -96,9 +96,17 @@ def __init__(self, method, formatters=None):
def _init_log_levels(self):
add_log_level_args(self._parser)

@property
def _help_formatters(self):
"""
Formatters that are shown as available in help, 'text' formatter
should not appear
"""
return [formatter for formatter in list(self._formatters) if formatter != "text"]

def _init_formatters(self):
if self._formatters:
help_message = "Select the output format: {}".format(", ".join(list(self._formatters)))
if self._help_formatters:
help_message = "Select the output format: {}".format(", ".join(list(self._help_formatters)))
self._parser.add_argument('-f', '--format', action=OnceArgument, help=help_message)

@property
Expand All @@ -119,25 +127,20 @@ def parser(self):

def _format(self, parser, info, *args):
parser_args, _ = parser.parse_known_args(*args)

default_format = "text"
try:
formatarg = parser_args.format
formatarg = parser_args.format or default_format
except AttributeError:
return

if formatarg is None:
return
formatarg = default_format

try:
formatter = self._formatters[formatarg]
except KeyError:
raise ConanException("{} is not a known format: {}".format(formatarg,
list(self._formatters)))
raise ConanException("{} is not a known format. Supported formatters are: {}".format(
formatarg, ", ".join(self._help_formatters)))

if info is None:
raise ConanException(f"Format {formatarg} was specified, but command didn't return "
"anything to format")
result = formatter(info)
cli_out_write(result, endline="")
formatter(info)


class ConanArgumentParser(argparse.ArgumentParser):
Expand Down Expand Up @@ -222,14 +225,3 @@ def decorator(f):
return cmd

return decorator


def cli_out_write(data, fg=None, bg=None, endline="\n", indentation=0):
fg_ = fg or ''
bg_ = bg or ''
if color_enabled(sys.stdout):
data = f"{' ' * indentation}{fg_}{bg_}{data}{Style.RESET_ALL}{endline}"
else:
data = f"{' ' * indentation}{data}{endline}"

sys.stdout.write(data)
9 changes: 7 additions & 2 deletions conan/cli/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from json import JSONEncoder

from conan.api.model import Remote
from conan.api.output import cli_out_write
from conans.errors import ConanException
from conans.model.package_ref import PkgReference
from conans.model.recipe_ref import RecipeReference
Expand Down Expand Up @@ -30,9 +31,13 @@ def default(self, o):
return JSONEncoder.default(self, o)


def json_formatter(data):
def default_json_formatter(data):
myjson = json.dumps(data, indent=4, cls=ConanJSONEncoder)
return myjson
cli_out_write(myjson)


def default_text_formatter(data):
cli_out_write(data)


def add_log_level_args(subparser):
Expand Down
11 changes: 5 additions & 6 deletions conan/cli/commands/cache.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
from conan.api.conan_api import ConanAPIV2
from conan.cli.command import conan_command, COMMAND_GROUPS, conan_subcommand, cli_out_write
from conan.cli.commands.list import json_formatter
from conan.cli.command import conan_command, COMMAND_GROUPS, conan_subcommand
from conan.cli.commands import default_text_formatter
from conans.errors import ConanException
from conans.model.package_ref import PkgReference
from conans.model.recipe_ref import RecipeReference


@conan_command(group=COMMAND_GROUPS['consumer'], formatters={"json": json_formatter})
@conan_command(group=COMMAND_GROUPS['consumer'])
def cache(conan_api: ConanAPIV2, parser, *args):
"""Performs file operations in the local cache (of recipes and packages)"""
pass


@conan_subcommand()
@conan_subcommand(formatters={"text": default_text_formatter})
czoido marked this conversation as resolved.
Show resolved Hide resolved
def cache_path(conan_api: ConanAPIV2, parser, subparser, *args):
"""
Shows the path af a given reference
Expand Down Expand Up @@ -48,8 +48,7 @@ def cache_path(conan_api: ConanAPIV2, parser, subparser, *args):
path = method(pref.ref)
else:
path = method(pref)

cli_out_write(path)
return path


def _get_recipe_reference(reference):
Expand Down
27 changes: 13 additions & 14 deletions conan/cli/commands/config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from conan.api.output import ConanOutput, Color
import sys

from conan.api.output import cli_out_write
from conan.cli.command import conan_command, COMMAND_GROUPS, conan_subcommand
from conan.cli.commands import json_formatter
from conan.cli.commands import default_json_formatter, default_text_formatter
from conans.model.conf import BUILT_IN_CONFS
from conans.util.config_parser import get_bool_from_text

Expand Down Expand Up @@ -40,25 +42,22 @@ def config_install(conan_api, parser, subparser, *args):
target_folder=args.target_folder)


@conan_subcommand(formatters={"text": lambda x: x})
def list_text_formatter(confs):
for k, v in confs.items():
cli_out_write(f"{k}: {v}")


@conan_subcommand(formatters={"text": default_text_formatter})
def config_home(conan_api, parser, subparser, *args):
"""
Gets the Conan home folder
"""
home = conan_api.config.home()
ConanOutput().info(f"Current Conan home: {home}")
return home
return conan_api.config.home()


@conan_subcommand(formatters={"json": json_formatter})
@conan_subcommand(formatters={"text": list_text_formatter, "json": default_json_formatter})
def config_list(conan_api, parser, subparser, *args):
"""
Prints all the Conan available configurations: core and tools.
"""
out = ConanOutput()
confs = BUILT_IN_CONFS
out.writeln(f"Supported Conan global.conf and [conf] properties:",
fg=Color.BRIGHT_CYAN)
for k, v in confs.items():
out.writeln(f"{k}: {v}")
return confs
return BUILT_IN_CONFS
5 changes: 2 additions & 3 deletions conan/cli/commands/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import os
import shutil

from conan.api.output import ConanOutput
from conan.api.output import ConanOutput, cli_out_write
from conan.cli.command import conan_command, COMMAND_GROUPS, OnceArgument
from conan.cli.commands.export import common_args_export
from conan.cli.commands.install import _get_conanfile_path
Expand All @@ -12,13 +12,12 @@
from conan.cli.formatters.graph import print_graph_basic, print_graph_packages
from conans.client.conanfile.build import run_build_method
from conans.errors import ConanException, conanfile_exception_formatter
from conans.model.graph_lock import Lockfile
from conans.util.files import chdir, mkdir


def json_create(info):
deps_graph = info
return json.dumps({"graph": deps_graph.serialize()}, indent=4)
cli_out_write(json.dumps({"graph": deps_graph.serialize()}, indent=4))


@conan_command(group=COMMAND_GROUPS['creator'], formatters={"json": json_create})
Expand Down
4 changes: 2 additions & 2 deletions conan/cli/commands/export.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import json
import os

from conan.api.output import ConanOutput
from conan.api.output import ConanOutput, cli_out_write
from conan.cli.command import conan_command, COMMAND_GROUPS, OnceArgument
from conan.cli.commands.install import _get_conanfile_path
from conan.cli.common import get_lockfile, add_reference_args
Expand All @@ -13,7 +13,7 @@ def common_args_export(parser):


def json_export(ref):
return json.dumps({"reference": ref.repr_notime()})
cli_out_write(json.dumps({"reference": ref.repr_notime()}))


@conan_command(group=COMMAND_GROUPS['creator'], formatters={"json": json_export})
Expand Down
3 changes: 2 additions & 1 deletion conan/cli/commands/export_pkg.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import json
import os

from conan.api.output import cli_out_write
from conan.cli.command import conan_command, COMMAND_GROUPS
from conan.cli.commands.install import _get_conanfile_path
from conan.cli.common import get_lockfile, add_profiles_args, get_profiles_from_args, \
Expand All @@ -9,7 +10,7 @@

def json_export_pkg(info):
deps_graph = info
return json.dumps({"graph": deps_graph.serialize()}, indent=4)
cli_out_write(json.dumps({"graph": deps_graph.serialize()}, indent=4))


@conan_command(group=COMMAND_GROUPS['creator'], formatters={"json": json_export_pkg})
Expand Down
13 changes: 5 additions & 8 deletions conan/cli/commands/graph.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import json
import os

from conan.api.output import ConanOutput
from conan.api.output import ConanOutput, cli_out_write
from conan.cli.command import conan_command, COMMAND_GROUPS, conan_subcommand, \
Extender
from conan.cli.commands import make_abs_path
Expand All @@ -22,19 +22,18 @@ def graph(conan_api, parser, *args):

def cli_build_order(build_order):
# TODO: Very simple cli output, probably needs to be improved
output = ConanOutput()
for level in build_order:
for item in level:
for package_level in item['packages']:
for package in package_level:
output.writeln(f"{item['ref']}:{package['package_id']} - {package['binary']}")
cli_out_write(f"{item['ref']}:{package['package_id']} - {package['binary']}")


def json_build_order(build_order):
return json.dumps(build_order, indent=4)
cli_out_write(json.dumps(build_order, indent=4))


@conan_subcommand(formatters={"json": json_build_order})
@conan_subcommand(formatters={"text": cli_build_order, "json": json_build_order})
def graph_build_order(conan_api, parser, subparser, *args):
"""
Computes the build order of a dependency graph
Expand All @@ -53,11 +52,10 @@ def graph_build_order(conan_api, parser, subparser, *args):
out.title("Computing the build order")
install_graph = InstallGraph(deps_graph)
install_order_serialized = install_graph.install_build_order()
cli_build_order(install_order_serialized)
return install_order_serialized


@conan_subcommand(formatters={"json": json_build_order})
@conan_subcommand(formatters={"text": cli_build_order, "json": json_build_order})
def graph_build_order_merge(conan_api, parser, subparser, *args):
"""
Merges more than 1 build-order file
Expand All @@ -72,7 +70,6 @@ def graph_build_order_merge(conan_api, parser, subparser, *args):
result.merge(install_graph)

install_order_serialized = result.install_build_order()
cli_build_order(install_order_serialized)
return install_order_serialized


Expand Down
26 changes: 14 additions & 12 deletions conan/cli/commands/inspect.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import json
import inspect as python_inspect

from conan.api.output import ConanOutput

from conan.api.output import cli_out_write
from conan.cli.command import conan_command, COMMAND_GROUPS, conan_subcommand
from conan.cli.commands import default_json_formatter


def _inspect_json_formatter(data):
return json.dumps(data, indent=4)
def inspect_text_formatter(data):
for name, value in data.items():
if value is None:
continue
if isinstance(value, dict):
cli_out_write(f"{name}:")
for k, v in value.items():
cli_out_write(f" {k}: {v}")
else:
cli_out_write("{}: {}".format(name, value))


@conan_command(group=COMMAND_GROUPS['consumer'])
Expand All @@ -16,15 +25,14 @@ def inspect(conan_api, parser, *args):
"""


@conan_subcommand(formatters={"json": _inspect_json_formatter})
@conan_subcommand(formatters={"text": inspect_text_formatter, "json": default_json_formatter})
def inspect_path(conan_api, parser, subparser, *args, **kwargs):
"""
Returns the specified attribute/s of a conanfile
"""
subparser.add_argument("path", help="Path to a folder containing a recipe (conanfile.py)")

args = parser.parse_args(*args)
out = ConanOutput()
conanfile = conan_api.graph.load_conanfile_class(args.path)
ret = {}

Expand All @@ -35,11 +43,5 @@ def inspect_path(conan_api, parser, subparser, *args, **kwargs):
ret[name] = value
if value is None:
continue
if isinstance(value, dict):
out.writeln(f"{name}:")
for k, v in value.items():
out.writeln(f" {k}: {v}")
else:
out.writeln("{}: {}".format(name, value))

return ret
4 changes: 2 additions & 2 deletions conan/cli/commands/install.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import json
import os

from conan.api.output import ConanOutput
from conan.api.output import ConanOutput, cli_out_write
from conan.cli.command import conan_command, Extender, COMMAND_GROUPS
from conan.cli.commands import make_abs_path
from conan.cli.common import _add_common_install_arguments, _help_build_policies, \
Expand All @@ -14,7 +14,7 @@

def json_install(info):
deps_graph = info
return json.dumps({"graph": deps_graph.serialize()}, indent=4)
cli_out_write(json.dumps({"graph": deps_graph.serialize()}, indent=4))


def _get_conanfile_path(path, cwd, py):
Expand Down
Loading