Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pypots-cli dev #66

Merged
merged 1 commit into from
Apr 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions pypots/utils/commands/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def register_subcommand(parser: ArgumentParser):

@staticmethod
def execute_command(command: str, verbose: bool = True):
logger.info(f"Executing '{command}'...")
if verbose:
exec_result = subprocess.Popen(
command,
Expand All @@ -43,17 +44,14 @@ def execute_command(command: str, verbose: bool = True):
stderr=subprocess.PIPE,
shell=True,
)
if exec_result.returncode != 0:
if len(exec_result.stderr) > 0:
logger.error(exec_result.stderr)
if len(exec_result.stdout) > 0:
logger.error(exec_result.stdout)
raise RuntimeError()
return exec_result.returncode

if exec_result.returncode != 0:
raise RuntimeError(exec_result.stdout, exec_result.stderr)
return exec_result

@staticmethod
def check_if_under_root_dir(strict: bool = True):
""" Check if under the root dir of PyPOTS project.
"""Check if under the root dir of PyPOTS project.

Parameters
----------
Expand Down Expand Up @@ -85,6 +83,10 @@ def check_if_under_root_dir(strict: bool = True):

return check_result

@abstractmethod
def checkup(self):
raise NotImplementedError()

@abstractmethod
def run(self):
raise NotImplementedError()
19 changes: 11 additions & 8 deletions pypots/utils/commands/dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import os
import shutil
from argparse import ArgumentParser, Namespace
from argparse import Namespace

from pypots.utils.commands.base import BaseCommand
from pypots.utils.logging import logger
Expand Down Expand Up @@ -43,9 +43,10 @@ class DevCommand(BaseCommand):
"""

@staticmethod
def register_subcommand(parser: ArgumentParser):
def register_subcommand(parser):
sub_parser = parser.add_parser(
"dev", help="CLI tools helping develop PyPOTS code"
"dev",
help="CLI tools helping develop PyPOTS code",
)
sub_parser.add_argument(
"--build",
Expand All @@ -54,18 +55,21 @@ def register_subcommand(parser: ArgumentParser):
help="Build PyPOTS into a wheel and package the source code into a .tar.gz file for distribution",
)
sub_parser.add_argument(
"-c",
"--cleanup",
dest="cleanup",
action="store_true",
help="Delete all caches and building files",
)
sub_parser.add_argument(
"--run_tests",
"--run-tests",
dest="run_tests",
action="store_true",
help="Run all test cases",
)
sub_parser.add_argument(
"--show-coverage",
"--show_coverage",
dest="show_coverage",
action="store_true",
Expand All @@ -86,6 +90,7 @@ def register_subcommand(parser: ArgumentParser):
"matching is case-insensitive.",
)
sub_parser.add_argument(
"--lint-code",
"--lint_code",
dest="lint_code",
action="store_true",
Expand All @@ -109,7 +114,7 @@ def __init__(
self._show_coverage = show_coverage
self._lint_code = lint_code

def check_arguments(self):
def checkup(self):
"""Run some checks on the arguments to avoid error usages"""
self.check_if_under_root_dir()

Expand All @@ -133,9 +138,8 @@ def check_arguments(self):

def run(self):
"""Execute the given command."""

# check arguments first
self.check_arguments()
# run checks first
self.checkup()

try:
if self._cleanup:
Expand All @@ -151,7 +155,6 @@ def run(self):
if self._show_coverage
else pytest_command
)
logger.info(f"Executing '{command_to_run_test}'...")
self.execute_command(command_to_run_test)
if self._show_coverage:
self.execute_command("coverage report -m")
Expand Down
24 changes: 16 additions & 8 deletions pypots/utils/commands/doc.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@

import os
import shutil
from argparse import ArgumentParser, Namespace
from argparse import Namespace

from tsdb.data_processing import _download_and_extract

from pypots.utils.commands.base import BaseCommand
from pypots.utils.logging import logger
from tsdb.data_processing import _download_and_extract

CLONED_LATEST_PYPOTS = "temp_pypots_latest"

Expand Down Expand Up @@ -73,43 +74,51 @@ class DocCommand(BaseCommand):
"""

@staticmethod
def register_subcommand(parser: ArgumentParser):
def register_subcommand(parser):
sub_parser = parser.add_parser(
"doc", help="CLI tools helping build PyPOTS documentation"
"doc",
help="CLI tools helping build PyPOTS documentation",
allow_abbrev=True,
)

sub_parser.add_argument(
"--gene-rst",
"--gene_rst",
dest="gene_rst",
action="store_true",
help="Generate rst (reStructuredText) documentation according to the latest code on Github",
)
sub_parser.add_argument(
"-b",
"--branch",
type=str,
default="main",
choices=["main", "dev"],
help="Code on which branch will be used for documentation generating",
)
sub_parser.add_argument(
"--gene-html",
"--gene_html",
dest="gene_html",
action="store_true",
help="Generate the sphinx documentation into static HTML files",
)
sub_parser.add_argument(
"--view-doc",
"--view_doc",
dest="view_doc",
action="store_true",
help="Deploy the generated HTML documentation locally for view",
)
sub_parser.add_argument(
"-p",
"--port",
type=int,
default=9075,
help="Use which port to deploy the web server for doc view", # 9075 looks like "POTS", so use it as default
)
sub_parser.add_argument(
"-c",
"--cleanup",
dest="cleanup",
action="store_true",
Expand All @@ -134,7 +143,7 @@ def __init__(
self._port = port
self._cleanup = cleanup

def check_arguments(self):
def checkup(self):
"""Run some checks on the arguments to avoid error usages"""
self.check_if_under_root_dir()

Expand All @@ -146,9 +155,8 @@ def check_arguments(self):

def run(self):
"""Execute the given command."""

# check arguments first
self.check_arguments()
# run checks first
self.checkup()

try:
if self._cleanup:
Expand Down
136 changes: 136 additions & 0 deletions pypots/utils/commands/env.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
"""
CLI tools to help initialize environments for running and developing PyPOTS.
"""

# Created by Wenjie Du <[email protected]>
# License: GLP-v3

try:
# here try importing all dependencies in the scope `basic` defined in `setup.cfg`
import torch

# import numpy
# import sklearn
# import pandas
# import tensorboard
# import scipy
# import h5py
# import tsdb
# import pycorruptor
except ImportError:
raise ImportError(
"Torch not installed. Using this tool supposes that you've already installed `pypots` "
"with at least the scope of `basic` dependencies."
)

from argparse import ArgumentParser, Namespace

from setuptools.config import read_configuration

from pypots.utils.commands.base import BaseCommand
from pypots.utils.logging import logger


def env_command_factory(args: Namespace):
return EnvCommand(
args.install,
args.tool,
)


class EnvCommand(BaseCommand):
"""CLI tools helping users and developer setup python environments for running and developing PyPOTS.

Notes
-----
Using this tool supposes that you've already installed `pypots` with at least the scope of `basic` dependencies.
Please refer to file setup.cfg in PyPOTS project's root dir for definitions of different dependency scopes.

Examples
--------
$ pypots-cli env --scope full --tool pip
$ pypots-cli env --scope full --tool pip
$ pypots-cli env --scope dev --tool conda -n
"""

@staticmethod
def register_subcommand(parser: ArgumentParser):
sub_parser = parser.add_parser(
"env",
help="CLI tools helping users and developer setup python environments for running and developing PyPOTS",
allow_abbrev=True,
)

sub_parser.add_argument(
"--install",
dest="install",
type=str,
required=True,
choices=["dev", "full", "doc", "test", "optional"],
help="Install specified dependencies in the current python environment",
)
sub_parser.add_argument(
"--tool",
dest="tool",
type=str,
required=True,
choices=["conda", "pip"],
help="Setup the environment with pip or conda, have to be specific",
)

sub_parser.set_defaults(func=env_command_factory)

def __init__(
self,
install: bool,
tool: str,
):
self._install = install
self._tool = tool

def checkup(self):
"""Run some checks on the arguments to avoid error usages"""
self.check_if_under_root_dir()

def run(self):
"""Execute the given command."""
# run checks first
self.checkup()

setup_cfg = read_configuration("setup.cfg")

logger.info(
f"Installing the dependencies in scope `{self._install}` for you..."
)

if self._tool == "conda":
assert (
self.execute_command("which conda").returncode == 0
), "Conda not installed, cannot set --tool=conda, please check your conda."

self.execute_command(
"conda install pyg pytorch-scatter pytorch-sparse -c pyg"
)

dependencies = ""
for i in setup_cfg["options"]["extras_require"][self._install]:
dependencies += f"'{i}' "

if "torch-geometric" in dependencies:
dependencies = dependencies.replace("'torch-geometric'", "")
dependencies = dependencies.replace("'torch-scatter'", "")
dependencies = dependencies.replace("'torch-sparse'", "")

conda_comm = f"conda install {dependencies} -c conda-forge"
self.execute_command(conda_comm)

else: # self._tool == "pip"
torch_version = torch.__version__

self.execute_command(
f"pip install -e '.[optional]' -f 'https://data.pyg.org/whl/torch-{torch_version}.html'"
)

if self._install != "optional":
self.execute_command(f"pip install -e '.[{self._install}]'")
logger.info("Installation finished. Enjoy your play with PyPOTS! Bye ;-)")
2 changes: 2 additions & 0 deletions pypots/utils/commands/pypots_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from pypots.utils.commands.dev import DevCommand
from pypots.utils.commands.doc import DocCommand
from pypots.utils.commands.env import EnvCommand


def main():
Expand All @@ -20,6 +21,7 @@ def main():
# Register commands here
DevCommand.register_subcommand(commands_parser)
DocCommand.register_subcommand(commands_parser)
EnvCommand.register_subcommand(commands_parser)

# parse all arguments
args = parser.parse_args()
Expand Down