Repository: ansys/pyconverter-xml2py (language: python)

Issue:
Use the ``pydita-ast`` project as a CLI tool
Similarly to what we do at [ansys-templates](https://github.com/ansys/ansys-templates), we should also allow for CLI usage of this tool, such that you do not really have to run the ``xml2rst.py`` file by hand. Configurable options should also be accepted via CLI.

Have a look at how the CLI is enabled:
https://github.com/ansys/ansys-templates/blob/6fc1b57a468366f68a694b234520792edc737d67/pyproject.toml#L74

And the different options exposed:
https://github.com/ansys/ansys-templates/blob/main/src/ansys/templates/cli.py


Discussion / hints:
Thank you @RobPasMue, I will implement it as soon as possible.

Fix patch:
diff --git a/.github/workflows/autodoc_cicd.yml b/.github/workflows/autodoc_cicd.yml
index a559fa564..f9941dc9a 100644
--- a/.github/workflows/autodoc_cicd.yml
+++ b/.github/workflows/autodoc_cicd.yml
@@ -55,7 +55,7 @@ jobs:
       
       - name: "Create pydita-autogenerated doc"
         run: |
-          python ./xml2rst.py -p ${{ github.workspace }}/mapdl-cmd-doc -f ${{ github.workspace }}/tests/customized_functions
+          pydita-ast package -x ${{ github.workspace }}/mapdl-cmd-doc -f ${{ github.workspace }}/tests/customized_functions
       
       - name: Upload autogenerated doc artifacts
         uses: actions/upload-artifact@v3
diff --git a/README.rst b/README.rst
index dfd2671cb..d25a6dc4b 100644
--- a/README.rst
+++ b/README.rst
@@ -1,7 +1,8 @@
 PyDita-AST
 ==========
 
-A Python wrapper to convert XML documentation into RST files and the Sphinx documentation.
+A Python wrapper to convert XML documentation into Python source code
+with its related Sphinx documentation.
 
 |pyansys| |pypi| |GH-CI| |codecov| |MIT| |black|
 
@@ -65,14 +66,14 @@ Once the ``XML_directory`` is correctly organized, the converter can be run.
 
 .. code:: bash
 
-    python xml2rst.py -p XML_directory_path
+    pydita-ast package -x XML_directory_path
 
 If some functions need to be customized, the path containing the python 
 files can be specified.
 
 .. code:: bash
 
-    python xml2rst.py -p XML_directory_path -f customized_function_directory_path
+    pydita-ast package -x XML_directory_path -f customized_function_directory_path
 
 
 A new package is then automatically generated. Its default name is ``package``.
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 6c1aae402..5728a7802 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -50,7 +50,7 @@
 
 html_context = {
     "display_github": True,  # Integrate GitHub
-    "github_user": "pyansys",
+    "github_user": "ansys",
     "github_repo": REPOSITORY_NAME,
     "github_version": "main",
     "doc_path": "doc/source",
diff --git a/doc/source/user_guide/index.rst b/doc/source/user_guide/index.rst
index b2eaefbcd..4f9524aee 100644
--- a/doc/source/user_guide/index.rst
+++ b/doc/source/user_guide/index.rst
@@ -29,7 +29,7 @@ to run the converter:
 
 .. code:: bash
 
-    python xml2rst.py -p XML_directory_path
+    pydita-ast package -x XML_directory_path
 
 
 If you need to customize some functions, you can specify the path containing the
@@ -37,7 +37,7 @@ Python files:
 
 .. code:: bash
 
-    python xml2rst.py -p XML_directory_path -f customized_function_directory_path
+    pydita-ast package -x XML_directory_path -f customized_function_directory_path
 
 
 For more information, see :ref:`ref_source_code`.
diff --git a/doc/source/user_guide/source_code.rst b/doc/source/user_guide/source_code.rst
index 88cea1242..b95426121 100644
--- a/doc/source/user_guide/source_code.rst
+++ b/doc/source/user_guide/source_code.rst
@@ -37,7 +37,7 @@ generating the code:
 
 .. code:: bash
 
-    python xml2rst.py -p XML_directory_path -f customized_function_directory_path
+    pydita-ast package -x XML_directory_path -f customized_function_directory_path
 
 
 In this configuration, the provided code is used for the conversion.
diff --git a/pyproject.toml b/pyproject.toml
index e404fb946..e97805fbf 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -26,6 +26,7 @@ classifiers = [
     "Programming Language :: Python :: 3.11",
 ]
 dependencies = [
+    "click>=7.0,<9.0.0",
     "importlib-metadata >=4.0",
     "lxml==4.9.2",
     "py-asciimath==0.3.0",
@@ -35,6 +36,7 @@ dependencies = [
 
 [project.optional-dependencies]
 tests = [
+    "click==8.1.3",
     "numpy==1.25.0",
     "parse==1.19.1",
     "pytest==7.4.0",
@@ -66,6 +68,9 @@ Source = "https://github.com/ansys/pydita-ast/"
 Tracker = "https://github.com/ansys/pydita-ast/issues"
 Homepage = "https://github.com/ansys/pydita-ast"
 
+[project.scripts]
+pydita-ast = "pydita_ast.cli:main"
+
 [tool.black]
 line-length = 100
 
diff --git a/src/pydita_ast/cli.py b/src/pydita_ast/cli.py
new file mode 100644
index 000000000..c1e343b1e
--- /dev/null
+++ b/src/pydita_ast/cli.py
@@ -0,0 +1,102 @@
+# Copyright (c) 2023 ANSYS, Inc. All rights reserved.
+
+"""Command Line Interface for PyDita-AST."""
+
+import os
+
+import click
+from pydita_ast import __version__
+from pydita_ast import writer as wr
+
+
+def create_package(xml_path=None, functions_path=None, target_path=None):
+    """Create Python package based on a XML documentation.
+
+    Parameters
+    ----------
+    xml_path: str
+        Path to the directory that contains the XML documentation to convert.
+
+    functions_path: str, optional
+        Path to the directory that contains the functions that need to be customized.
+        The default value is None.
+
+    target_path: str, optional
+        Path to the directory where you want the autogenerated package to be created.
+        The default value is the current working directory.
+
+    """
+    if xml_path is None:
+        xml_path = os.environ.get("XML_PATH")
+    if xml_path is None:
+        raise RuntimeError(
+            "Missing the XML documentation path. Specify this with either xml_path or set the XML_PATH environment variable"  # noqa : E501
+        )
+    else:
+        xml_path = os.path.abspath(os.path.expanduser(xml_path))
+        if not os.path.isdir(xml_path):
+            raise FileExistsError(
+                "Please, enter a valid directory path that contains the XML documentation to convert."  # noqa : E501
+            )
+
+    if functions_path is None:
+        print(
+            "No customized functions path was entered. The default code generation will be applied to all the commands.",  # noqa : E501
+            "You can specify the customized functions by adding a path to the --func-path argument.",  # noqa : E501
+        )
+
+    else:
+        functions_path = os.path.abspath(os.path.expanduser(functions_path))
+        if not os.path.isdir(functions_path):
+            raise FileExistsError(
+                "Please, enter a valid directory path that contains the functions that need to be customized."  # noqa : E501
+            )
+
+    if target_path is None:
+        target_path = os.getcwd()
+
+    else:
+        os.makedirs(target_path, exist_ok=True)
+        print(f"The autogenerated package will be saved in {target_path}.")
+
+    commands, cmd_map, *_ = wr.convert(xml_path)
+    wr.write_source(commands, cmd_map, xml_path, target_path, functions_path)
+    package_path = os.path.join(target_path, "package")
+    wr.write_docs(commands, cmd_map, package_path)
+
+
+@click.group()
+def main():
+    """A Python wrapper to convert XML documentation into Python source code
+    with its related Sphinx documentation."""
+    pass
+
+
+@main.command()
+def version():
+    """Display current version."""
+    print(f"pydita_ast {__version__}")
+
+
+@main.command()
+@click.option(
+    "-x",
+    "--xml-path",
+    type=click.Path(exists=True),
+    help="Path to the directory that contains the XML documentation to convert.",
+)
+@click.option(
+    "-f",
+    "--func-path",
+    type=click.Path(exists=True),
+    help="Path to the directory that contains the functions that need to be customized.",
+)
+@click.option(
+    "-t",
+    "--targ-path",
+    type=click.Path(),
+    help="Path to the directory where you want the autogenerated package to be created.",
+)
+def package(xml_path, func_path, targ_path):
+    """Create a Python package from your XML documentation."""
+    create_package(xml_path, func_path, targ_path)
diff --git a/src/pydita_ast/writer.py b/src/pydita_ast/writer.py
index bbf3d1675..70cb727d0 100644
--- a/src/pydita_ast/writer.py
+++ b/src/pydita_ast/writer.py
@@ -1,3 +1,5 @@
+# Copyright (c) 2023 ANSYS, Inc. All rights reserved.
+
 import glob
 import os
 import shutil
diff --git a/xml2rst.py b/xml2rst.py
deleted file mode 100644
index 8b178e72b..000000000
--- a/xml2rst.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright (c) 2023 ANSYS, Inc. All rights reserved.
-
-from pydita_ast import writer as wr
-
-"""
-Parse XML command documentation.
-
-"""
-
-import argparse
-import os
-
-# from pathlib import Path
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser()
-    parser.add_argument("--xml-path", "-p", help="Documentation path")
-    parser.add_argument("--func-path", "-f", help="Customized functions path")
-    # not accepted
-    # parser.add_argument("--command", help="Convert a single command")
-    parser.add_argument("--no-clean", help="Do not remove the existing package directory")
-    parser.add_argument(
-        "--no_docs", action="store_true", help="Do not write to the autogenerated documentation"
-    )
-    args = parser.parse_args()
-
-    directory_path = args.xml_path
-    functions_path = args.func_path
-    if directory_path is None:
-        directory_path = os.environ.get("XML_PATH")
-    if directory_path is None:
-        raise RuntimeError(
-            "Missing the XML documentation path. Specify this with either --xml-path, -p, or set the XML_PATH environment variable"  # noqa : E501
-        )
-    if functions_path is None:
-        print(
-            "No customized functions path was entered. The default code generation will be applied to all the commands.",  # noqa : E501
-            "You can specify the customized functions by adding a path to the --func-path argument.",  # noqa : E501
-        )
-
-    else:
-        functions_path = os.path.abspath(os.path.expanduser(functions_path))
-
-    directory_path = os.path.abspath(os.path.expanduser(directory_path))
-    cur_path = os.getcwd()
-
-    # Verification
-    if not os.path.isdir(directory_path):
-        raise FileNotFoundError(f"Documentation path at {directory_path} does not exist")
-
-    commands, cmd_map, *_ = wr.convert(directory_path)
-    cmd_path = wr.write_source(commands, cmd_map, directory_path, cur_path, functions_path)
-    package_path = os.path.join(cur_path, "package")
-    doc_src = wr.write_docs(commands, cmd_map, package_path)


Test patch:
diff --git a/tests/test_cli.py b/tests/test_cli.py
new file mode 100644
index 000000000..1dd86b152
--- /dev/null
+++ b/tests/test_cli.py
@@ -0,0 +1,35 @@
+# Copyright (c) 2023 ANSYS, Inc. All rights reserved.
+
+from click.testing import CliRunner
+from pydita_ast.cli import main
+
+
+def test_cli_main_group():
+    runner = CliRunner()
+    result = runner.invoke(main, ["--help"])
+    assert result.exit_code == 0
+
+    assert (
+        """A Python wrapper to convert XML documentation into Python source code with"""
+        in result.output
+    )
+
+    assert "package  Create a Python package from your XML documentation." in result.output
+    assert "version  Display current version." in result.output
+
+
+def test_cli_main_package_group():
+    runner = CliRunner()
+    result = runner.invoke(main, ["package", "--help"])
+    assert result.exit_code == 0
+
+    assert "Create a Python package from your XML documentation." in result.output
+    assert (
+        "-f, --func-path PATH  Path to the directory that contains the functions that"
+        in result.output
+    )
+    assert (
+        "-t, --targ-path PATH  Path to the directory where you want the autogenerated"
+        in result.output
+    )
+    assert "-t, --targ-path PATH  Path to the directory where you want the autogenerated"
diff --git a/tests/test_custom_functions.py b/tests/test_custom_functions.py
index bf27311d5..de7391962 100644
--- a/tests/test_custom_functions.py
+++ b/tests/test_custom_functions.py
@@ -1,3 +1,5 @@
+# Copyright (c) 2023 ANSYS, Inc. All rights reserved.
+
 import os
 
 import pydita_ast.custom_functions as cf
diff --git a/tests/test_directory_format.py b/tests/test_directory_format.py
index 5dd80706e..12335d008 100644
--- a/tests/test_directory_format.py
+++ b/tests/test_directory_format.py
@@ -1,3 +1,5 @@
+# Copyright (c) 2023 ANSYS, Inc. All rights reserved.
+
 import os
 
 import pydita_ast.directory_format as ff
diff --git a/tests/test_load_xml_doc.py b/tests/test_load_xml_doc.py
index 01ccd4796..ab514b3fc 100644
--- a/tests/test_load_xml_doc.py
+++ b/tests/test_load_xml_doc.py
@@ -1,3 +1,5 @@
+# Copyright (c) 2023 ANSYS, Inc. All rights reserved.
+
 import pydita_ast.load_xml_doc as lxd
 
 
diff --git a/tests/test_writer.py b/tests/test_writer.py
index eec866101..50caa269a 100644
--- a/tests/test_writer.py
+++ b/tests/test_writer.py
@@ -1,3 +1,5 @@
+# Copyright (c) 2023 ANSYS, Inc. All rights reserved.
+
 import os
 import shutil
 
diff --git a/tests/test_xml_ast.py b/tests/test_xml_ast.py
index 939ff400d..4f97d52f9 100644
--- a/tests/test_xml_ast.py
+++ b/tests/test_xml_ast.py
@@ -1,4 +1,4 @@
-# import os
+# Copyright (c) 2023 ANSYS, Inc. All rights reserved.
 
 from lxml.html import fromstring
 import pydita_ast.ast_tree as ast