[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 clickui-next
parent
52ed895338
commit
3025f67d37
|
|
@ -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
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ then go and see :doc:`tutorial` for more detail.
|
|||
tutorial
|
||||
concept
|
||||
tasks/index
|
||||
cli
|
||||
api
|
||||
|
||||
Indices and tables
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
],
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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."""
|
||||
|
|
@ -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__}")
|
||||
|
|
@ -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."""
|
||||
|
|
@ -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)
|
||||
|
|
@ -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…
Reference in New Issue