From f984891e9cfb7e8e37773a769ebe91dc0b904222 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sun, 8 Oct 2023 20:47:56 -0400 Subject: [PATCH] add `dpgui` entry points and `dpdisp gui` cli (#372) See https://github.com/deepmodeling/dpgui/issues/270 for details. --------- Signed-off-by: Jinzhe Zeng Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .github/workflows/pyright.yml | 2 +- doc/cli.rst | 9 +++++ doc/conf.py | 1 + doc/index.rst | 1 + doc/machine.rst | 2 +- doc/resources.rst | 2 +- doc/task.rst | 2 +- dpdispatcher/dpdisp.py | 75 ++++++++++++++++++++++++++++++++++- dpdispatcher/gui.py | 31 +++++++++++++++ pyproject.toml | 15 ++++++- tests/test_cli.py | 9 +++++ tests/test_gui.py | 11 +++++ 12 files changed, 153 insertions(+), 7 deletions(-) create mode 100644 doc/cli.rst create mode 100644 dpdispatcher/gui.py create mode 100644 tests/test_cli.py create mode 100644 tests/test_gui.py diff --git a/.github/workflows/pyright.yml b/.github/workflows/pyright.yml index 3625d4df..20794731 100644 --- a/.github/workflows/pyright.yml +++ b/.github/workflows/pyright.yml @@ -11,7 +11,7 @@ jobs: - uses: actions/setup-python@v4 with: python-version: '3.11' - - run: pip install -e .[cloudserver] + - run: pip install -e .[cloudserver,gui] - uses: jakebailey/pyright-action@v1 with: version: 1.1.308 diff --git a/doc/cli.rst b/doc/cli.rst new file mode 100644 index 00000000..0326beb6 --- /dev/null +++ b/doc/cli.rst @@ -0,0 +1,9 @@ +.. _cli: + +Command line interface +====================== + +.. argparse:: + :module: dpdispatcher.dpdisp + :func: main_parser + :prog: dpdisp diff --git a/doc/conf.py b/doc/conf.py index 66771135..3e395d45 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -38,6 +38,7 @@ "sphinx.ext.intersphinx", "numpydoc", "sphinx.ext.autosummary", + "sphinxarg.ext", ] # Add any paths that contain templates here, relative to this directory. diff --git a/doc/index.rst b/doc/index.rst index c2517e1e..5318dceb 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -22,6 +22,7 @@ DPDispatcher will monitor (poke) until these jobs finish and download the result machine resources task + cli api/api .. toctree:: diff --git a/doc/machine.rst b/doc/machine.rst index a6291c21..e9df8c93 100644 --- a/doc/machine.rst +++ b/doc/machine.rst @@ -1,7 +1,7 @@ Machine parameters ====================================== .. note:: - One can load, modify, and export the input file by using our effective web-based tool `DP-GUI `_. All parameters below can be set in DP-GUI. By clicking "SAVE JSON", one can download the input file. + One can load, modify, and export the input file by using our effective web-based tool `DP-GUI `_ online or hosted using the :ref:`command line interface ` :code:`dpdisp gui`. All parameters below can be set in DP-GUI. By clicking "SAVE JSON", one can download the input file. .. dargs:: :module: dpdispatcher.arginfo diff --git a/doc/resources.rst b/doc/resources.rst index cb43af75..24111f23 100644 --- a/doc/resources.rst +++ b/doc/resources.rst @@ -1,7 +1,7 @@ Resources parameters ====================================== .. note:: - One can load, modify, and export the input file by using our effective web-based tool `DP-GUI `_. All parameters below can be set in DP-GUI. By clicking "SAVE JSON", one can download the input file for. + One can load, modify, and export the input file by using our effective web-based tool `DP-GUI `_ online or hosted using the :ref:`command line interface ` :code:`dpdisp gui`. All parameters below can be set in DP-GUI. By clicking "SAVE JSON", one can download the input file for. .. dargs:: :module: dpdispatcher.arginfo diff --git a/doc/task.rst b/doc/task.rst index c247b27c..80a53c4a 100644 --- a/doc/task.rst +++ b/doc/task.rst @@ -1,7 +1,7 @@ Task parameters ====================================== .. note:: - One can load, modify, and export the input file by using our effective web-based tool `DP-GUI `_. All parameters below can be set in DP-GUI. By clicking "SAVE JSON", one can download the input file. + One can load, modify, and export the input file by using our effective web-based tool `DP-GUI `_ online or hosted using the :ref:`command line interface ` :code:`dpdisp gui`. All parameters below can be set in DP-GUI. By clicking "SAVE JSON", one can download the input file. .. dargs:: :module: dpdispatcher.arginfo diff --git a/dpdispatcher/dpdisp.py b/dpdispatcher/dpdisp.py index f0310033..fe374f7f 100644 --- a/dpdispatcher/dpdisp.py +++ b/dpdispatcher/dpdisp.py @@ -1,8 +1,81 @@ #!/usr/bin/env python +import argparse +from typing import List, Optional + +from dpdispatcher.gui import start_dpgui + + +def main_parser() -> argparse.ArgumentParser: + """Dpdispatcher commandline options argument parser. + + Notes + ----- + This function is used by documentation. + + Returns + ------- + argparse.ArgumentParser + the argument parser + """ + parser = argparse.ArgumentParser( + description="dpdispatcher: Generate HPC scheduler systems jobs input scripts, submit these scripts to HPC systems, and poke until they finish", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + subparsers = parser.add_subparsers(title="Valid subcommands", dest="command") + ########################################## + # gui + parser_gui = subparsers.add_parser( + "gui", + help="Serve DP-GUI.", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser_gui.add_argument( + "-p", + "--port", + type=int, + default=6042, + help="The port to serve DP-GUI on.", + ) + parser_gui.add_argument( + "--bind_all", + action="store_true", + help=( + "Serve on all public interfaces. This will expose your DP-GUI instance " + "to the network on both IPv4 and IPv6 (where available)." + ), + ) + return parser + + +def parse_args(args: Optional[List[str]] = None): + """Dpdispatcher commandline options argument parsing. + + Parameters + ---------- + args : List[str] + list of command line arguments, main purpose is testing default option None + takes arguments from sys.argv + """ + parser = main_parser() + + parsed_args = parser.parse_args(args=args) + if parsed_args.command is None: + parser.print_help() + + return parsed_args def main(): - print("test") + args = parse_args() + if args.command == "gui": + start_dpgui( + port=args.port, + bind_all=args.bind_all, + ) + elif args.command is None: + pass + else: + raise RuntimeError(f"unknown command {args.command}") if __name__ == "__main__": diff --git a/dpdispatcher/gui.py b/dpdispatcher/gui.py new file mode 100644 index 00000000..8b6b9e0a --- /dev/null +++ b/dpdispatcher/gui.py @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: LGPL-3.0-or-later +"""DP-GUI entrypoint.""" + + +def start_dpgui(*, port: int, bind_all: bool, **kwargs): + """Host DP-GUI server. + + Parameters + ---------- + port : int + The port to serve DP-GUI on. + bind_all : bool + Serve on all public interfaces. This will expose your DP-GUI instance + to the network on both IPv4 and IPv6 (where available). + **kwargs + additional arguments + + Raises + ------ + ModuleNotFoundError + The dpgui package is not installed + """ + try: + from dpgui import ( + start_dpgui, + ) + except ModuleNotFoundError as e: + raise ModuleNotFoundError( + "To use DP-GUI, please install the dpgui package:\npip install dpgui" + ) from e + start_dpgui(port=port, bind_all=bind_all) diff --git a/pyproject.toml b/pyproject.toml index 6622d953..8eec86fa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,9 +37,14 @@ Homepage = "https://github.com/deepmodeling/dpdispatcher" documentation = "https://docs.deepmodeling.com/projects/dpdispatcher" repository = "https://github.com/deepmodeling/dpdispatcher" -[project.entry-points.console_scripts] +[project.scripts] dpdisp = "dpdispatcher.dpdisp:main" +[project.entry-points."dpgui"] +"DPDispatcher Machine" = "dpdispatcher.arginfo:machine_dargs" +"DPDispatcher Resources" = "dpdispatcher.arginfo:resources_dargs" +"DPDispatcher Task" = "dpdispatcher.arginfo:task_dargs" + [project.optional-dependencies] docs = [ 'sphinx', @@ -48,10 +53,16 @@ docs = [ 'numpydoc', 'deepmodeling_sphinx>=0.1.1', 'dargs>=0.3.1', + 'sphinx-argparse', ] cloudserver = ["oss2", "tqdm", "bohrium-sdk"] bohrium = ["oss2", "tqdm", "bohrium-sdk"] -test = [] +gui = [ + "dpgui", +] +test = [ + "dpgui", +] [tool.setuptools.packages.find] include = ["dpdispatcher*"] diff --git a/tests/test_cli.py b/tests/test_cli.py new file mode 100644 index 00000000..30c00626 --- /dev/null +++ b/tests/test_cli.py @@ -0,0 +1,9 @@ +import subprocess as sp +import unittest + + +class TestCLI(unittest.TestCase): + def test_cli(self): + sp.check_output(["dpdisp", "-h"]) + for subcommand in ("gui",): + sp.check_output(["dpdisp", subcommand, "-h"]) diff --git a/tests/test_gui.py b/tests/test_gui.py new file mode 100644 index 00000000..25fd7e66 --- /dev/null +++ b/tests/test_gui.py @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: LGPL-3.0-or-later +import unittest + +from dpgui import ( + generate_dpgui_templates, +) + + +class TestDPGUI(unittest.TestCase): + def test_dpgui_entrypoints(self): + self.assertTrue(len(generate_dpgui_templates()) > 0)