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