Browse Source
* 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 click3.0.0/version-upgrade
Jiajie Zhong
3 years ago
committed by
GitHub
10 changed files with 282 additions and 2 deletions
@ -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 |
@ -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