From 31678394c8f84be9a559c90904b6425ca815d554 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Sun, 13 Feb 2022 20:40:56 +0000 Subject: [PATCH 01/10] Add in code needed to import under the click namespace. --- README.md | 67 +++++--------------------------------- src/rich_click/__init__.py | 3 ++ src/rich_click/core.py | 10 ++++++ 3 files changed, 22 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 800d84d1..9da842bf 100644 --- a/README.md +++ b/README.md @@ -31,65 +31,16 @@ python -m pip install rich-click ## Usage -There are two main ways to set up `rich-click` formatting for your tool: -monkey patching or declarative. -Which you choose will depend on your use-case and your personal disposition! - -Note that the intention is to maintain most / all of the normal click functionality and arguments. -If you spot something that is missing once you start using the plugin, please -create an issue about it. - -### The path of least typing - -Monkey patching is [probably bad](https://en.wikipedia.org/wiki/Monkey_patch#Pitfalls) -and you should only use this method if you are a Responsible Developer. -It's also good if you're lazy, as it requires very little typing. - -Assuming that you're already using click, you only need to add three lines: - -```python -import rich_click -click.Command.format_help = rich_click.rich_format_help -click.Group.format_help = rich_click.rich_format_help -``` - -_(if you're not click groups, only 2 lines!)_ - -This _overwrites_ the default `click` methods with those from the `rich-click` package. -As such, no other changes are needed - just continue to use `click` as you would -normally and it will use these custom methods to print your help output. - -### The good and proper way - -If using monkey-patching in your project makes your palms sweaty and your pulse race, -then you'll be pleased to know that you can also use `rich-click` in a nicely -declarative and verbose manner: +To use `rich-click`, import it instead of `click` but under the same namespace: ```python -import click -import rich_click - -class RichClickGroup(click.Group): - def format_help(self, ctx, formatter): - rich_click.rich_format_help(self, ctx, formatter) -class RichClickCommand(click.Command): - def format_help(self, ctx, formatter): - rich_click.rich_format_help(self, ctx, formatter) - -@click.group(cls=RichClickGroup) -@click.option('--debug/--no-debug', default=False) -def cli(debug): - click.echo(f"Debug mode is {'on' if debug else 'off'}") - -@cli.command(cls=RichClickCommand) -def sync(): - click.echo('Syncing') +import rich_click as click ``` -_(example based on the [click docs](https://click.palletsprojects.com/en/8.0.x/commands/))_ +That's it. Then continue to use `click` as you would normally. -Here we are making new `Group` and `Command` child classes that are based on click. -We define our custom `format_help()` functions and then tell click to to use these classes with the `cls` argument. +The intention is to maintain most / all of the normal click functionality and arguments. +If you spot something that is missing once you start using the plugin, please create an issue about it. ## Command groups and sorting @@ -105,7 +56,7 @@ In this example, we create two groups of commands for the base command of `mytoo Any subcommands not listed will automatically be printed in a panel at the end labelled "Commands" as usual. ```python -rich_click.core.COMMAND_GROUPS = { +click.core.COMMAND_GROUPS = { "mytool": [ { "name": "Commands for uploading", @@ -125,7 +76,7 @@ If you use nested subcommands, you can specify multiple base paths using the base dictionary keys: ```python -rich_click.core.COMMAND_GROUPS = { +click.core.COMMAND_GROUPS = { "mytool": ["commands": ["sync", "auth"]], "mytool sync": [ { @@ -148,13 +99,13 @@ You can customise most things that are related to formatting. For example, to limit the maximum width of the help output to 100 characters: ```python -rich_click.core.MAX_WIDTH = 100 +click.core.MAX_WIDTH = 100 ``` To print the option flags in a different colour, use: ```python -rich_click.core.STYLE_OPTION = "magenta" +click.core.STYLE_OPTION = "magenta" ```
Full list of config options diff --git a/src/rich_click/__init__.py b/src/rich_click/__init__.py index 443cfb93..9fb18ce6 100644 --- a/src/rich_click/__init__.py +++ b/src/rich_click/__init__.py @@ -8,4 +8,7 @@ __version__ = "0.3.0.dev0" +from click import * from .core import rich_format_help +from .core import Group +from .core import Command diff --git a/src/rich_click/core.py b/src/rich_click/core.py index 4a6b6e2b..f495e83e 100644 --- a/src/rich_click/core.py +++ b/src/rich_click/core.py @@ -298,3 +298,13 @@ def rich_format_help(obj, ctx, formatter): console.print( Padding(Align(highlighter(epilogue), width=MAX_WIDTH, pad=False), 1) ) + + +class Group(click.Group): + def format_help(self, ctx, formatter): + rich_format_help(self, ctx, formatter) + + +class Command(click.Command): + def format_help(self, ctx, formatter): + rich_format_help(self, ctx, formatter) From 79f67dd30080faa914f30f470876427339223318 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Sun, 13 Feb 2022 20:44:32 +0000 Subject: [PATCH 02/10] Rename core.py to rich_click.py for more intuitive settings naming --- README.md | 10 +++++----- src/rich_click/__init__.py | 6 +++--- src/rich_click/{core.py => rich_click.py} | 0 3 files changed, 8 insertions(+), 8 deletions(-) rename src/rich_click/{core.py => rich_click.py} (100%) diff --git a/README.md b/README.md index 9da842bf..5460d080 100644 --- a/README.md +++ b/README.md @@ -50,13 +50,13 @@ It accepts a list of commands which means you can also choose a custom sorting o For example, you can produce something that looks like this: ![command groups](docs/images/command_groups.png) -To do this, set `rich_click.core.COMMAND_GROUPS`. +To do this, set `click.rich_click.COMMAND_GROUPS`. In this example, we create two groups of commands for the base command of `mytool`. Any subcommands not listed will automatically be printed in a panel at the end labelled "Commands" as usual. ```python -click.core.COMMAND_GROUPS = { +click.rich_click.COMMAND_GROUPS = { "mytool": [ { "name": "Commands for uploading", @@ -76,7 +76,7 @@ If you use nested subcommands, you can specify multiple base paths using the base dictionary keys: ```python -click.core.COMMAND_GROUPS = { +click.rich_click.COMMAND_GROUPS = { "mytool": ["commands": ["sync", "auth"]], "mytool sync": [ { @@ -99,13 +99,13 @@ You can customise most things that are related to formatting. For example, to limit the maximum width of the help output to 100 characters: ```python -click.core.MAX_WIDTH = 100 +click.rich_click.MAX_WIDTH = 100 ``` To print the option flags in a different colour, use: ```python -click.core.STYLE_OPTION = "magenta" +click.rich_click.STYLE_OPTION = "magenta" ```
Full list of config options diff --git a/src/rich_click/__init__.py b/src/rich_click/__init__.py index 9fb18ce6..d19685cc 100644 --- a/src/rich_click/__init__.py +++ b/src/rich_click/__init__.py @@ -9,6 +9,6 @@ __version__ = "0.3.0.dev0" from click import * -from .core import rich_format_help -from .core import Group -from .core import Command +from .rich_click import rich_format_help +from .rich_click import Group +from .rich_click import Command diff --git a/src/rich_click/core.py b/src/rich_click/rich_click.py similarity index 100% rename from src/rich_click/core.py rename to src/rich_click/rich_click.py From 69cbef2a31044d15e00ffc5d0a509ebf338438d6 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Sun, 13 Feb 2022 23:05:56 +0000 Subject: [PATCH 03/10] Add basic example for testing the import-as method --- examples/03_simple_importas.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 examples/03_simple_importas.py diff --git a/examples/03_simple_importas.py b/examples/03_simple_importas.py new file mode 100644 index 00000000..f8ac8f61 --- /dev/null +++ b/examples/03_simple_importas.py @@ -0,0 +1,34 @@ +import rich_click as click + +# The rest of the code is vanilla click usage +@click.group() +@click.option("--debug/--no-debug", "-d/-n", default=False, help="Enable debug mode") +def cli(debug): + """ + My amazing tool does all the things. + + This is a minimal example based on documentation + from the 'click' package. + + You can try using --help at the top level and also for + specific group subcommands. + """ + click.echo(f"Debug mode is {'on' if debug else 'off'}") + + +@cli.command() +@click.option("--all", is_flag=True, help="Sync all the things?") +def sync(): + """Synchronise all your files between two places""" + click.echo("Syncing") + + +@cli.command() +@click.option("--all", is_flag=True, help="Get everything") +def download(): + """Pretend to download some files from somewhere""" + click.echo("Downloading") + + +if __name__ == "__main__": + cli() From edb2156884ef90311a2907f729a54520eafbb213 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Wed, 16 Feb 2022 16:38:00 +0100 Subject: [PATCH 04/10] Try defining functions as suggested by @grst --- src/rich_click/__init__.py | 25 ++++++++++++++++++++++--- src/rich_click/rich_click.py | 14 ++++++++++++-- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/rich_click/__init__.py b/src/rich_click/__init__.py index d214dad3..0236e808 100644 --- a/src/rich_click/__init__.py +++ b/src/rich_click/__init__.py @@ -9,7 +9,26 @@ __version__ = "0.4.0.dev0" from click import * -from .rich_click import rich_format_help +from .rich_click import RichGroup +from .rich_click import RichCommand from .rich_click import rich_format_error -from .rich_click import Group -from .rich_click import Command + +## TODO: Replace with inheritance / custom function model as below +# Monkey patch click error formatting function +ClickException.show = rich_format_error +UsageError.show = rich_format_error + + +def group(*args, cls=RichGroup, **kwargs): + from click import group as click_group + + return click_group(*args, cls=cls, **kwargs) + + +def command(*args, cls=RichCommand, **kwargs): + from click import command as click_command + + return click_command(*args, cls=cls, **kwargs) + + ## TODO: This works for simple top-level commands, + # but not commands nested within a group diff --git a/src/rich_click/rich_click.py b/src/rich_click/rich_click.py index b3f08283..e65540a7 100644 --- a/src/rich_click/rich_click.py +++ b/src/rich_click/rich_click.py @@ -362,11 +362,21 @@ def rich_format_error(self): ) -class Group(click.Group): +class RichGroup(click.Group): def format_help(self, ctx, formatter): rich_format_help(self, ctx, formatter) -class Command(click.Command): +class RichCommand(click.Command): def format_help(self, ctx, formatter): rich_format_help(self, ctx, formatter) + + +class RichClickException(click.ClickException): + def show(self, file): + rich_format_error(self, file) + + +class RichUsageError(click.UsageError): + def show(self, file): + rich_format_error(self, file) From a6c678e122104a8033ae7ed8bc1d2b03b34fb74b Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Wed, 16 Feb 2022 16:45:06 +0100 Subject: [PATCH 05/10] Update example for testing import overwrites --- examples/03_simple_importas.py | 42 +++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/examples/03_simple_importas.py b/examples/03_simple_importas.py index f8ac8f61..5a371ade 100644 --- a/examples/03_simple_importas.py +++ b/examples/03_simple_importas.py @@ -1,8 +1,13 @@ -import rich_click as click - -# The rest of the code is vanilla click usage -@click.group() -@click.option("--debug/--no-debug", "-d/-n", default=False, help="Enable debug mode") +import click +import rich_click + +#### +## Rich-click cli stuff +#### +@rich_click.group() +@rich_click.option( + "--debug/--no-debug", "-d/-n", default=False, help="Enable debug mode" +) def cli(debug): """ My amazing tool does all the things. @@ -13,22 +18,33 @@ def cli(debug): You can try using --help at the top level and also for specific group subcommands. """ - click.echo(f"Debug mode is {'on' if debug else 'off'}") + print(f"Debug mode is {'on' if debug else 'off'}") @cli.command() -@click.option("--all", is_flag=True, help="Sync all the things?") -def sync(): +@rich_click.option("--all", is_flag=True, help="Sync all the things?") +def sync(all): """Synchronise all your files between two places""" - click.echo("Syncing") + print("Syncing") @cli.command() -@click.option("--all", is_flag=True, help="Get everything") -def download(): +@rich_click.option("--all", is_flag=True, help="Get everything") +def download(all): """Pretend to download some files from somewhere""" - click.echo("Downloading") + print("Downloading") + + +#### +## Vanilla click import cli stuff +#### +@click.command() +@click.option("--all", is_flag=True, help="Sync all the things?") +def original(all): + """Synchronise all your files between two places""" + print("Syncing") if __name__ == "__main__": - cli() + # cli() # Use rich-click, should be fancy output + original() # Use vanilla click, should be simple output From e102d97f027a7efc1580e59b202d6f0389f15f93 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Wed, 16 Feb 2022 17:05:37 +0100 Subject: [PATCH 06/10] Overwrite 'command_class' in the Group class --- src/rich_click/rich_click.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rich_click/rich_click.py b/src/rich_click/rich_click.py index e65540a7..a56fb42b 100644 --- a/src/rich_click/rich_click.py +++ b/src/rich_click/rich_click.py @@ -362,12 +362,14 @@ def rich_format_error(self): ) -class RichGroup(click.Group): +class RichCommand(click.Command): def format_help(self, ctx, formatter): rich_format_help(self, ctx, formatter) -class RichCommand(click.Command): +class RichGroup(click.Group): + command_class = RichCommand + def format_help(self, ctx, formatter): rich_format_help(self, ctx, formatter) From 1955dea68437a191a721271fe947c85c7e7f8b25 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Wed, 16 Feb 2022 17:07:08 +0100 Subject: [PATCH 07/10] Remove comment --- src/rich_click/__init__.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/rich_click/__init__.py b/src/rich_click/__init__.py index 0236e808..7441b669 100644 --- a/src/rich_click/__init__.py +++ b/src/rich_click/__init__.py @@ -29,6 +29,3 @@ def command(*args, cls=RichCommand, **kwargs): from click import command as click_command return click_command(*args, cls=cls, **kwargs) - - ## TODO: This works for simple top-level commands, - # but not commands nested within a group From 43842174adfa83a6945e61df474346142cb03359 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Thu, 17 Feb 2022 14:57:10 +0100 Subject: [PATCH 08/10] Handle error messages without monkey patching --- examples/03_simple_importas.py | 4 ++-- src/rich_click/__init__.py | 6 ----- src/rich_click/rich_click.py | 44 ++++++++++++++++++++++++---------- 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/examples/03_simple_importas.py b/examples/03_simple_importas.py index 5a371ade..8a3895e4 100644 --- a/examples/03_simple_importas.py +++ b/examples/03_simple_importas.py @@ -46,5 +46,5 @@ def original(all): if __name__ == "__main__": - # cli() # Use rich-click, should be fancy output - original() # Use vanilla click, should be simple output + cli() # Use rich-click, should be fancy output + # original() # Use vanilla click, should be simple output diff --git a/src/rich_click/__init__.py b/src/rich_click/__init__.py index 7441b669..3b5ce20c 100644 --- a/src/rich_click/__init__.py +++ b/src/rich_click/__init__.py @@ -11,12 +11,6 @@ from click import * from .rich_click import RichGroup from .rich_click import RichCommand -from .rich_click import rich_format_error - -## TODO: Replace with inheritance / custom function model as below -# Monkey patch click error formatting function -ClickException.show = rich_format_error -UsageError.show = rich_format_error def group(*args, cls=RichGroup, **kwargs): diff --git a/src/rich_click/rich_click.py b/src/rich_click/rich_click.py index a56fb42b..40c5fc49 100644 --- a/src/rich_click/rich_click.py +++ b/src/rich_click/rich_click.py @@ -9,6 +9,7 @@ from rich.text import Text from rich.theme import Theme import re +import sys # Default styles STYLE_OPTION = "bold cyan" @@ -337,9 +338,6 @@ def rich_format_error(self): """ Custom function to overwrite default click error printing. """ - # TODO: The click function has more complex code for UsageErrors: - # https://github.com/pallets/click/blob/6411f425fae545f42795665af4162006b36c5e4a/src/click/exceptions.py#L62-L82 - # Should bring this over too. console = Console( theme=Theme( { @@ -351,6 +349,16 @@ def rich_format_error(self): ), highlighter=highlighter, ) + if self.ctx is not None: + console.print(self.ctx.get_usage()) + if self.ctx is not None and self.ctx.command.get_help_option(self.ctx) is not None: + console.print( + "Try [blue]'{command} {option}'[/] for help.".format( + command=self.ctx.command_path, option=self.ctx.help_option_names[0] + ), + style="dim", + ) + console.print( Panel( highlighter(self.format_message()), @@ -363,6 +371,17 @@ def rich_format_error(self): class RichCommand(click.Command): + standalone_mode = False + + def main(self, *args, standalone_mode=True, **kwargs): + try: + return super().main(*args, standalone_mode=False, **kwargs) + except click.ClickException as e: + if not standalone_mode: + raise + rich_format_error(e) + sys.exit(e.exit_code) + def format_help(self, ctx, formatter): rich_format_help(self, ctx, formatter) @@ -370,15 +389,14 @@ def format_help(self, ctx, formatter): class RichGroup(click.Group): command_class = RichCommand + def main(self, *args, standalone_mode=True, **kwargs): + try: + return super().main(*args, standalone_mode=False, **kwargs) + except click.ClickException as e: + if not standalone_mode: + raise + rich_format_error(e) + sys.exit(e.exit_code) + def format_help(self, ctx, formatter): rich_format_help(self, ctx, formatter) - - -class RichClickException(click.ClickException): - def show(self, file): - rich_format_error(self, file) - - -class RichUsageError(click.UsageError): - def show(self, file): - rich_format_error(self, file) From b212775a53255c3281a4614e627a5dfe4db968d5 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Thu, 17 Feb 2022 16:23:35 +0100 Subject: [PATCH 09/10] Fix: Handle error where show_default but no default set --- src/rich_click/rich_click.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/rich_click/rich_click.py b/src/rich_click/rich_click.py index 40c5fc49..3eb0d376 100644 --- a/src/rich_click/rich_click.py +++ b/src/rich_click/rich_click.py @@ -117,10 +117,11 @@ def _get_parameter_help(param, ctx): # param.default is the value, but click is a bit clever in choosing what to show here # eg. --debug/--no-debug, default=False will show up as [default: no-debug] instead of [default: False] # To avoid duplicating loads of code, let's just pull out the string from click with a regex - default_str = re.search( - r"\[default: (.*)\]", param.get_help_record(ctx)[-1] - ).group(1) - yield Text(DEFAULT_STRING.format(default_str), style=STYLE_OPTION_DEFAULT) + default_str = re.search(r"\[default: (.*)\]", param.get_help_record(ctx)[-1]) + if default_str: + yield Text( + DEFAULT_STRING.format(default_str.group(1)), style=STYLE_OPTION_DEFAULT + ) # Required? if param.required: From 1cc1a1b6e52ddf2c6d6aba90c009b7abae07006b Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Thu, 17 Feb 2022 16:26:25 +0100 Subject: [PATCH 10/10] Update readme, changelog, examples --- CHANGELOG.md | 2 ++ README.md | 19 +++++++------- examples/01_simple.py | 43 +++++++++++++++++++++++++++++++ examples/01_simple_monkeypatch.py | 40 ---------------------------- examples/02_simple_declarative.py | 16 +++--------- 5 files changed, 57 insertions(+), 63 deletions(-) create mode 100644 examples/01_simple.py delete mode 100644 examples/01_simple_monkeypatch.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 83f78f11..1ed39f58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Version 0.4.0.dev0 +Major change: New usage, so that we can avoid having to do monkey patching [#10](https://github.com/ewels/rich-click/pull/10). + - Add ability to create groups of options with separate panels - Show positional arguments in their own panel by default - Add config `GROUP_ARGUMENTS_OPTIONS` option to group with options diff --git a/README.md b/README.md index 36305842..817e2def 100644 --- a/README.md +++ b/README.md @@ -8,18 +8,17 @@ The intention of `rich-click` is to provide attractive help output from click, formatted with rich, with minimal customisation required. +## Features + +- Rich formatting of click help and error messages +- Nice styles be default, with simple import +- Ability to give custom sort order for options and commands +- Group commands and options into named panels +- Extensive styling and behaviour customisation available + ## Screenshots - - - - - - - - - -
Native click helpWith rich-click
+![rich-click](docs/images/example_with_rich-click.png) ## Installation diff --git a/examples/01_simple.py b/examples/01_simple.py new file mode 100644 index 00000000..5bf1747f --- /dev/null +++ b/examples/01_simple.py @@ -0,0 +1,43 @@ +import rich_click + + +@rich_click.group() +@rich_click.option( + "--debug/--no-debug", "-d/-n", default=False, help="Enable debug mode" +) +def cli(debug): + """ + My amazing tool does all the things. + + This is a minimal example based on documentation + from the 'click' package. + + You can try using --help at the top level and also for + specific group subcommands. + """ + print(f"Debug mode is {'on' if debug else 'off'}") + + +@cli.command() +@rich_click.option( + "--type", + required=True, + default="files", + show_default=True, + help="Type of file to sync", +) +@rich_click.option("--all", is_flag=True, help="Sync all the things?") +def sync(type, all): + """Synchronise all your files between two places""" + print("Syncing") + + +@cli.command() +@rich_click.option("--all", is_flag=True, help="Get everything") +def download(all): + """Pretend to download some files from somewhere""" + print("Downloading") + + +if __name__ == "__main__": + cli() diff --git a/examples/01_simple_monkeypatch.py b/examples/01_simple_monkeypatch.py deleted file mode 100644 index 7958e9cc..00000000 --- a/examples/01_simple_monkeypatch.py +++ /dev/null @@ -1,40 +0,0 @@ -import click -import rich_click - -click.Group.format_help = rich_click.rich_format_help -click.Command.format_help = rich_click.rich_format_help - -# The rest of the code is vanilla click usage - - -@click.group() -@click.option("--debug/--no-debug", "-d/-n", default=False, help="Enable debug mode") -def cli(debug): - """ - My amazing tool does all the things. - - This is a minimal example based on documentation - from the 'click' package. - - You can try using --help at the top level and also for - specific group subcommands. - """ - click.echo(f"Debug mode is {'on' if debug else 'off'}") - - -@cli.command() -@click.option("--all", is_flag=True, help="Sync all the things?") -def sync(): - """Synchronise all your files between two places""" - click.echo("Syncing") - - -@cli.command() -@click.option("--all", is_flag=True, help="Get everything") -def download(): - """Pretend to download some files from somewhere""" - click.echo("Downloading") - - -if __name__ == "__main__": - cli() diff --git a/examples/02_simple_declarative.py b/examples/02_simple_declarative.py index e8d984f5..1a1fc719 100644 --- a/examples/02_simple_declarative.py +++ b/examples/02_simple_declarative.py @@ -1,18 +1,8 @@ import click -import rich_click +from rich_click import RichGroup, RichCommand -class RichClickGroup(click.Group): - def format_help(self, ctx, formatter): - rich_click.rich_format_help(self, ctx, formatter) - - -class RichClickCommand(click.Command): - def format_help(self, ctx, formatter): - rich_click.rich_format_help(self, ctx, formatter) - - -@click.group(cls=RichClickGroup) +@click.group(cls=RichGroup) @click.option("--debug/--no-debug", default=False) def cli(debug): """ @@ -27,7 +17,7 @@ def cli(debug): click.echo(f"Debug mode is {'on' if debug else 'off'}") -@cli.command(cls=RichClickCommand) +@cli.command(cls=RichCommand) def sync(): """Synchronise all your files between two places""" click.echo("Syncing")