This is an automated email from the ASF dual-hosted git repository.

caishunfeng pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/dolphinscheduler.git


The following commit(s) were added to refs/heads/dev by this push:
     new 3025f67  [python] Add mechanism cli only with version as subcommand 
(#8516)
3025f67 is described below

commit 3025f67d370e02ebae075a4ef68e1d7f0461b2d6
Author: Jiajie Zhong <[email protected]>
AuthorDate: Thu Feb 24 16:05:55 2022 +0800

    [python] Add mechanism cli only with version as subcommand (#8516)
    
    * Add basic cli mechanism with Click, for now just including
      one single subcommand `version`
    * Add general and easy test class in tests/testing/cli, and
      test to version
    * Add sphinx-click to general cli docs basic on click
---
 .../pydolphinscheduler/README.md                   |  4 +-
 .../docs/source/{index.rst => cli.rst}             | 32 ++++----
 .../pydolphinscheduler/docs/source/conf.py         |  2 +
 .../pydolphinscheduler/docs/source/index.rst       |  1 +
 .../pydolphinscheduler/setup.py                    |  7 ++
 .../src/pydolphinscheduler/cli/__init__.py         | 18 +++++
 .../src/pydolphinscheduler/cli/commands.py         | 48 ++++++++++++
 .../pydolphinscheduler/tests/cli/__init__.py       | 18 +++++
 .../pydolphinscheduler/tests/cli/test_version.py   | 59 ++++++++++++++
 .../pydolphinscheduler/tests/testing/cli.py        | 91 ++++++++++++++++++++++
 10 files changed, 259 insertions(+), 21 deletions(-)

diff --git a/dolphinscheduler-python/pydolphinscheduler/README.md 
b/dolphinscheduler-python/pydolphinscheduler/README.md
index 9cc524d..32a7ff6 100644
--- a/dolphinscheduler-python/pydolphinscheduler/README.md
+++ b/dolphinscheduler-python/pydolphinscheduler/README.md
@@ -40,8 +40,8 @@ your workflow by python code, aka workflow-as-codes.
 # Install
 $ pip install apache-dolphinscheduler
 
-# Check installation, it is success if you see version output, here we use 
0.1.0 as example
-$ python -c "import pydolphinscheduler; print(pydolphinscheduler.__version__)"
+# Verify installation is successful, it will show the version of 
apache-dolphinscheduler, here we use 0.1.0 as example
+$ pydolphinscheduler version
 0.1.0
 ```
 
diff --git a/dolphinscheduler-python/pydolphinscheduler/docs/source/index.rst 
b/dolphinscheduler-python/pydolphinscheduler/docs/source/cli.rst
similarity index 58%
copy from dolphinscheduler-python/pydolphinscheduler/docs/source/index.rst
copy to dolphinscheduler-python/pydolphinscheduler/docs/source/cli.rst
index b04c26f..60e8231 100644
--- a/dolphinscheduler-python/pydolphinscheduler/docs/source/index.rst
+++ b/dolphinscheduler-python/pydolphinscheduler/docs/source/cli.rst
@@ -15,28 +15,22 @@
    specific language governing permissions and limitations
    under the License.
 
-PyDolphinScheduler
-==================
+Command Line Interface
+======================
 
-**PyDolphinScheduler** is Python API for `Apache DolphinScheduler 
<https://dolphinscheduler.apache.org>`_,
-which allow you definition your workflow by Python code, aka workflow-as-codes.
+*PyDolphinScheduler* have mechanism call CLI(command line interface) to help 
user control it in Shell.
 
-I could go and find how to :ref:`install <start:getting started>` the project. 
Or if you want to see simply example
-then go and see :doc:`tutorial` for more detail.
+Prepare
+-------
 
+You have to :ref:`install PyDolphinScheduler <start:installing 
pydolphinscheduler>` first before you using
+its CLI
 
-.. toctree::
-   :maxdepth: 2
+Usage
+-----
 
-   start
-   tutorial
-   concept
-   tasks/index
-   api
+Here is basic usage about the command line of *PyDolphinScheduler*
 
-Indices and tables
-==================
-
-* :ref:`genindex`
-* :ref:`modindex`
-* :ref:`search`
+.. click:: pydolphinscheduler.cli.commands:cli
+   :prog: pydolphinscheduler
+   :nested: full
diff --git a/dolphinscheduler-python/pydolphinscheduler/docs/source/conf.py 
b/dolphinscheduler-python/pydolphinscheduler/docs/source/conf.py
index 5ee73a5..e22b3bb 100644
--- a/dolphinscheduler-python/pydolphinscheduler/docs/source/conf.py
+++ b/dolphinscheduler-python/pydolphinscheduler/docs/source/conf.py
@@ -55,6 +55,8 @@ extensions = [
     "sphinx.ext.viewcode",
     "sphinx.ext.autosectionlabel",
     "sphinx_rtd_theme",
+    # Documenting command line interface
+    "sphinx_click.ext",
 ]
 
 # Add any paths that contain templates here, relative to this directory.
diff --git a/dolphinscheduler-python/pydolphinscheduler/docs/source/index.rst 
b/dolphinscheduler-python/pydolphinscheduler/docs/source/index.rst
index b04c26f..ad4c93d 100644
--- a/dolphinscheduler-python/pydolphinscheduler/docs/source/index.rst
+++ b/dolphinscheduler-python/pydolphinscheduler/docs/source/index.rst
@@ -32,6 +32,7 @@ then go and see :doc:`tutorial` for more detail.
    tutorial
    concept
    tasks/index
+   cli
    api
 
 Indices and tables
diff --git a/dolphinscheduler-python/pydolphinscheduler/setup.py 
b/dolphinscheduler-python/pydolphinscheduler/setup.py
index cd6eb3e..117ffee 100644
--- a/dolphinscheduler-python/pydolphinscheduler/setup.py
+++ b/dolphinscheduler-python/pydolphinscheduler/setup.py
@@ -31,12 +31,14 @@ version = "0.1.0"
 
 # Start package required
 prod = [
+    "click>=8.0.0",
     "py4j~=0.10",
 ]
 
 doc = [
     "sphinx>=4.3",
     "sphinx_rtd_theme>=1.0",
+    "sphinx-click>=3.0",
 ]
 
 test = [
@@ -125,4 +127,9 @@ setup(
         "test": test,
         "doc": doc,
     },
+    entry_points={
+        "console_scripts": [
+            "pydolphinscheduler = pydolphinscheduler.cli.commands:cli",
+        ],
+    },
 )
diff --git 
a/dolphinscheduler-python/pydolphinscheduler/src/pydolphinscheduler/cli/__init__.py
 
b/dolphinscheduler-python/pydolphinscheduler/src/pydolphinscheduler/cli/__init__.py
new file mode 100644
index 0000000..5f30c83
--- /dev/null
+++ 
b/dolphinscheduler-python/pydolphinscheduler/src/pydolphinscheduler/cli/__init__.py
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+"""Commands line interface of pydolphinscheduler."""
diff --git 
a/dolphinscheduler-python/pydolphinscheduler/src/pydolphinscheduler/cli/commands.py
 
b/dolphinscheduler-python/pydolphinscheduler/src/pydolphinscheduler/cli/commands.py
new file mode 100644
index 0000000..a430f45
--- /dev/null
+++ 
b/dolphinscheduler-python/pydolphinscheduler/src/pydolphinscheduler/cli/commands.py
@@ -0,0 +1,48 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+"""Commands line interface's command of pydolphinscheduler."""
+
+import click
+from click import echo
+
+from pydolphinscheduler import __version__
+
+version_option_val = ["major", "minor", "micro"]
+
+
[email protected]()
+def cli():
+    """Apache DolphinScheduler Python API's command line interface."""
+
+
[email protected]()
[email protected](
+    "--part",
+    "-p",
+    required=False,
+    type=click.Choice(version_option_val, case_sensitive=False),
+    multiple=False,
+    help="The part of version your want to get.",
+)
+def version(part: str) -> None:
+    """Show current version of pydolphinscheduler."""
+    if part:
+        idx = version_option_val.index(part)
+        echo(f"{__version__.split('.')[idx]}")
+    else:
+        echo(f"{__version__}")
diff --git a/dolphinscheduler-python/pydolphinscheduler/tests/cli/__init__.py 
b/dolphinscheduler-python/pydolphinscheduler/tests/cli/__init__.py
new file mode 100644
index 0000000..f1a4396
--- /dev/null
+++ b/dolphinscheduler-python/pydolphinscheduler/tests/cli/__init__.py
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+"""Init command line interface tests."""
diff --git 
a/dolphinscheduler-python/pydolphinscheduler/tests/cli/test_version.py 
b/dolphinscheduler-python/pydolphinscheduler/tests/cli/test_version.py
new file mode 100644
index 0000000..df17759
--- /dev/null
+++ b/dolphinscheduler-python/pydolphinscheduler/tests/cli/test_version.py
@@ -0,0 +1,59 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+"""Test command line interface subcommand version."""
+
+import pytest
+
+from pydolphinscheduler import __version__
+from pydolphinscheduler.cli.commands import cli
+from tests.testing.cli import CliTestWrapper
+
+
+def test_version():
+    """Test whether subcommand `version` correct."""
+    cli_test = CliTestWrapper(cli, ["version"])
+    cli_test.assert_success(output=f"{__version__}")
+
+
[email protected](
+    "part, idx",
+    [
+        ("major", 0),
+        ("minor", 1),
+        ("micro", 2),
+    ],
+)
+def test_version_part(part: str, idx: int):
+    """Test subcommand `version` option `--part`."""
+    cli_test = CliTestWrapper(cli, ["version", "--part", part])
+    cli_test.assert_success(output=f"{__version__.split('.')[idx]}")
+
+
[email protected](
+    "option, output",
+    [
+        # not support option
+        (["version", "--not-support"], "No such option"),
+        # not support option value
+        (["version", "--part", "abc"], "Invalid value for '--part'"),
+    ],
+)
+def test_version_not_support_option(option, output):
+    """Test subcommand `version` not support option or option value."""
+    cli_test = CliTestWrapper(cli, option)
+    cli_test.assert_fail(ret_code=2, output=output, fuzzy=True)
diff --git a/dolphinscheduler-python/pydolphinscheduler/tests/testing/cli.py 
b/dolphinscheduler-python/pydolphinscheduler/tests/testing/cli.py
new file mode 100644
index 0000000..1585920
--- /dev/null
+++ b/dolphinscheduler-python/pydolphinscheduler/tests/testing/cli.py
@@ -0,0 +1,91 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+"""Utils of command line test."""
+
+
+import os
+
+from click.testing import CliRunner
+
+
+class CliTestWrapper:
+    """Wrap command click CliRunner.invoke."""
+
+    _dev_mode_env_name = "PY_DOLPHINSCHEDULER_DEV_MODE"
+    _dev_mode_true_val = {"true", "t", "1"}
+
+    def __init__(self, *args, **kwargs):
+        runner = CliRunner()
+        self.result = runner.invoke(*args, **kwargs)
+        self.show_result_output()
+
+    def _assert_output(self, output: str = None, fuzzy: bool = False):
+        """Assert between `CliRunner.invoke.result.output` and parameter 
`output`.
+
+        :param output: The output will check compare to the 
``CliRunner.invoke.output``.
+        :param fuzzy: A flag define whether assert :param:`output` in fuzzy or 
not.
+            Check if `CliRunner.invoke.output` contain :param:`output` is set 
``True``
+            and CliRunner.invoke.output equal to :param:`output` if we set it 
``False``.
+        """
+        if not output:
+            return
+        if fuzzy:
+            assert output in self.result.output
+        else:
+            assert self.result.output.rstrip("\n") == output
+
+    def show_result_output(self):
+        """Print `CliRunner.invoke.result` output content in debug mode.
+
+        It read variable named `PY_DOLPHINSCHEDULER_DEV_MODE` from env, when 
it set to `true` or `t` or `1`
+        will print result output when class :class:`CliTestWrapper` is 
initialization.
+        """
+        dev_mode = str(os.getenv(self._dev_mode_env_name))
+        if dev_mode.strip().lower() in self._dev_mode_true_val:
+            print(f"\n{self.result.output}\n")
+
+    def assert_success(self, output: str = None, fuzzy: bool = False):
+        """Assert test is success.
+
+        It would check whether `CliRunner.invoke.exit_code` equals to `0`, 
with no
+        exception at the same time. It's also can test the content of 
`CliRunner.invoke.output`.
+
+        :param output: The output will check compare to the 
``CliRunner.invoke.output``.
+        :param fuzzy: A flag define whether assert :param:`output` in fuzzy or 
not.
+            Check if `CliRunner.invoke.output` contain :param:`output` is set 
``True``
+            and CliRunner.invoke.output equal to :param:`output` if we set it 
``False``.
+        """
+        assert self.result.exit_code == 0
+        if self.result.exception:
+            raise self.result.exception
+        self._assert_output(output, fuzzy)
+
+    def assert_fail(self, ret_code: int, output: str = None, fuzzy: bool = 
False):
+        """Assert test is fail.
+
+        It would check whether `CliRunner.invoke.exit_code` equals to 
:param:`ret_code`,
+        and it will also can test the content of `CliRunner.invoke.output`.
+
+        :param ret_code: The returning code of this fail test.
+        :param output: The output will check compare to the 
``CliRunner.invoke.output``.
+        :param fuzzy: A flag define whether assert :param:`output` in fuzzy or 
not.
+            Check if `CliRunner.invoke.output` contain :param:`output` is set 
``True``
+            and CliRunner.invoke.output equal to :param:`output` if we set it 
``False``.
+        """
+        assert ret_code == self.result.exit_code
+        self._assert_output(output, fuzzy)

Reply via email to