Browse Source

[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
3.0.0/version-upgrade
Jiajie Zhong 3 years ago committed by GitHub
parent
commit
3025f67d37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      dolphinscheduler-python/pydolphinscheduler/README.md
  2. 36
      dolphinscheduler-python/pydolphinscheduler/docs/source/cli.rst
  3. 2
      dolphinscheduler-python/pydolphinscheduler/docs/source/conf.py
  4. 1
      dolphinscheduler-python/pydolphinscheduler/docs/source/index.rst
  5. 7
      dolphinscheduler-python/pydolphinscheduler/setup.py
  6. 18
      dolphinscheduler-python/pydolphinscheduler/src/pydolphinscheduler/cli/__init__.py
  7. 48
      dolphinscheduler-python/pydolphinscheduler/src/pydolphinscheduler/cli/commands.py
  8. 18
      dolphinscheduler-python/pydolphinscheduler/tests/cli/__init__.py
  9. 59
      dolphinscheduler-python/pydolphinscheduler/tests/cli/test_version.py
  10. 91
      dolphinscheduler-python/pydolphinscheduler/tests/testing/cli.py

4
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
```

36
dolphinscheduler-python/pydolphinscheduler/docs/source/cli.rst

@ -0,0 +1,36 @@
.. 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.
Command Line Interface
======================
*PyDolphinScheduler* have mechanism call CLI(command line interface) to help user control it in Shell.
Prepare
-------
You have to :ref:`install PyDolphinScheduler <start:installing pydolphinscheduler>` first before you using
its CLI
Usage
-----
Here is basic usage about the command line of *PyDolphinScheduler*
.. click:: pydolphinscheduler.cli.commands:cli
:prog: pydolphinscheduler
:nested: full

2
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.

1
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

7
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",
],
},
)

18
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."""

48
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"]
@click.group()
def cli():
"""Apache DolphinScheduler Python API's command line interface."""
@cli.command()
@click.option(
"--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__}")

18
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."""

59
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__}")
@pytest.mark.parametrize(
"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]}")
@pytest.mark.parametrize(
"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)

91
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)
Loading…
Cancel
Save