From 33d6b9a4560b8085dd1c242e5f0b8d2b41f35aed Mon Sep 17 00:00:00 2001
From: Thomas Schneider <thomas@fsmpi.rwth-aachen.de>
Date: Tue, 11 Apr 2023 17:40:00 +0200
Subject: [PATCH] Use Click for CLI

---
 pdm.lock          | 15 +++++++++++-
 pyproject.toml    |  1 +
 src/nctool/cli.py | 61 +++++++++++++++++++----------------------------
 3 files changed, 39 insertions(+), 38 deletions(-)

diff --git a/pdm.lock b/pdm.lock
index 323e7a8..dca5593 100644
--- a/pdm.lock
+++ b/pdm.lock
@@ -15,6 +15,15 @@ dependencies = [
     "pycparser",
 ]
 
+[[package]]
+name = "click"
+version = "8.1.3"
+requires_python = ">=3.7"
+summary = "Composable command line interface toolkit"
+dependencies = [
+    "colorama; platform_system == \"Windows\"",
+]
+
 [[package]]
 name = "colorama"
 version = "0.4.6"
@@ -103,7 +112,7 @@ dependencies = [
 
 [metadata]
 lock_version = "4.1"
-content_hash = "sha256:e49493c40f029631f0f923d9f3306cb05766876dfc284149bcf2bfdc891be04f"
+content_hash = "sha256:c98532200135ebeef1c30c0c059c06d6568956765158022c0eb9bb561d946e38"
 
 [metadata.files]
 "bcrypt 4.0.1" = [
@@ -195,6 +204,10 @@ content_hash = "sha256:e49493c40f029631f0f923d9f3306cb05766876dfc284149bcf2bfdc8
     {url = "https://files.pythonhosted.org/packages/f9/96/fc9e118c47b7adc45a0676f413b4a47554e5f3b6c99b8607ec9726466ef1/cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"},
     {url = "https://files.pythonhosted.org/packages/ff/fe/ac46ca7b00e9e4f9c62e7928a11bc9227c86e2ff43526beee00cdfb4f0e8/cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"},
 ]
+"click 8.1.3" = [
+    {url = "https://files.pythonhosted.org/packages/59/87/84326af34517fca8c58418d148f2403df25303e02736832403587318e9e8/click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
+    {url = "https://files.pythonhosted.org/packages/c2/f1/df59e28c642d583f7dacffb1e0965d0e00b218e0186d7858ac5233dce840/click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
+]
 "colorama 0.4.6" = [
     {url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
     {url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
diff --git a/pyproject.toml b/pyproject.toml
index ca7d28a..2fef79a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -13,6 +13,7 @@ dependencies = [
     "ncclient>=0.6.13",
     "lxml>=4.9.2",
     "tqdm>=4.65.0",
+    "click>=8.1.3",
 ]
 
 [project.scripts]
diff --git a/src/nctool/cli.py b/src/nctool/cli.py
index 90b0649..18b59e3 100644
--- a/src/nctool/cli.py
+++ b/src/nctool/cli.py
@@ -1,8 +1,9 @@
-import argparse
 from getpass import getpass
 import os
 import os.path
 
+import click
+
 import lxml.etree as et
 
 from ncclient import manager
@@ -10,23 +11,38 @@ from ncclient import manager
 from tqdm import tqdm
 
 
-def get_models(args):
+@click.group(context_settings=dict(help_option_names=["-h", "--help"]))
+def main():
+    pass
+
+
+@main.command()
+@click.option("-a", "--host", required=True, help="Device IP address or Hostname")
+@click.option(
+    "--port", type=int, default=830, show_default=True, help="Netconf agent port"
+)
+@click.option(
+    "-u", "--username", required=True, help="Device Username (netconf agent username)"
+)
+@click.option("-d", "--destination", default="models", help="Destination directory")
+def get_models(host, port, username, destination):
+    """Get YANG models from device"""
     try:
         password = getpass(prompt="Password (C-d for none/agent only): ")
     except EOFError:
         password = None
 
     with manager.connect(
-        host=args.host,
-        port=args.port,
-        username=args.username,
+        host=host,
+        port=port,
+        username=username,
         password=password,
         timeout=90,
         device_params={"name": "iosxe"},
     ) as m:
         caps = list(map(lambda x: x.strip(), m.server_capabilities))
         if not any(map(lambda x: x.startswith(manager.NETCONF_MONITORING_NS), caps)):
-            raise Exception("device does not support rfc6022")
+            raise click.ClickException("device does not support rfc6022")
 
         _filter = et.fromstring(
             f"""
@@ -38,7 +54,7 @@ def get_models(args):
 
         prefix = f"{{{manager.NETCONF_MONITORING_NS}}}"
 
-        os.makedirs(args.destination, exist_ok=True)
+        os.makedirs(destination, exist_ok=True)
 
         pbar = tqdm(
             res.data.findall(
@@ -64,34 +80,5 @@ def get_models(args):
             # Don’t pass format because IOS XE doesn’t eat its own definition.
             r = m.get_schema(identifier, version)
 
-            with open(os.path.join(args.destination, filename), "w") as fp:
+            with open(os.path.join(destination, filename), "w") as fp:
                 fp.write(r.data)
-
-
-def main():
-    parser = argparse.ArgumentParser()
-
-    parser.add_argument(
-        "-a", "--host", type=str, required=True, help="Device IP address or Hostname"
-    )
-    parser.add_argument(
-        "-u",
-        "--username",
-        type=str,
-        required=True,
-        help="Device Username (netconf agent username)",
-    )
-    parser.add_argument("--port", type=int, default=830, help="Netconf agent port")
-
-    subparsers = parser.add_subparsers(required=True)
-
-    parser_gm = subparsers.add_parser(
-        "get-models", aliases=["gm"], help="Get YANG models from device"
-    )
-    parser_gm.add_argument(
-        "-d", "--destination", default="models", help="Destination directory"
-    )
-    parser_gm.set_defaults(func=get_models)
-
-    args = parser.parse_args()
-    args.func(args)
-- 
GitLab