diff --git a/kiwi_scp/commands/cli.py b/kiwi_scp/commands/cli.py index 03e8e62..7b67478 100644 --- a/kiwi_scp/commands/cli.py +++ b/kiwi_scp/commands/cli.py @@ -1,20 +1,45 @@ import os +from pathlib import Path +import attr import click +from ..config import Config + class KiwiCLI(click.MultiCommand): + """Command Line Interface spread over multiple files in this directory""" + def list_commands(self, ctx): - result = [] - for filename in os.listdir(os.path.abspath(os.path.dirname(__file__))): - if filename.startswith("cmd_") and filename.endswith(".py"): - result.append(filename[4:-3]) - result.sort() - return result + """list all the commands defined by cmd_*.py files in this directory""" + + return ( + filename[4:-3] + for filename in os.listdir(os.path.abspath(os.path.dirname(__file__))) + if filename.startswith("cmd_") and filename.endswith(".py") + ) def get_command(self, ctx, name): + """import and return a specific command""" + try: mod = __import__(f"kiwi_scp.commands.cmd_{name}", None, None, ["cmd"]) except ImportError: return return mod.cmd + + +@attr.s +class KiwiCTX: + """this class is used as the commands' shared context""" + + instance: Path = attr.ib(factory=lambda: Path('.')) + + @property + def config(self) -> Config: + """shorthand: get the current configuration""" + + return Config.from_instance(self.instance) + + +pass_kiwi_ctx = click.make_pass_decorator(KiwiCTX, ensure=True) diff --git a/kiwi_scp/commands/cmd_init.py b/kiwi_scp/commands/cmd_init.py index 9f61706..2e38817 100644 --- a/kiwi_scp/commands/cmd_init.py +++ b/kiwi_scp/commands/cmd_init.py @@ -1,21 +1,20 @@ import click -from ..config import Config +from .cli import KiwiCTX, pass_kiwi_ctx @click.command( "init", - short_help="Initializes a repo." + short_help="Initializes kiwi-scp" ) @click.argument( "path", required=False, type=click.Path(resolve_path=True) ) -@click.pass_context -def cmd(ctx, path): - """Initializes a repository.""" - kiwi: Config = ctx.obj["cfg"] - click.echo("Hello init") - click.echo(kiwi.kiwi_yml) +@pass_kiwi_ctx +def cmd(ctx: KiwiCTX, path): + """Initialize or reconfigure a kiwi-scp instance""" + + click.echo(f"Hello init, kiwi version {ctx.config.version}") pass diff --git a/kiwi_scp/config.py b/kiwi_scp/config.py index 85efa01..025185d 100644 --- a/kiwi_scp/config.py +++ b/kiwi_scp/config.py @@ -1,3 +1,4 @@ +import functools import re from ipaddress import IPv4Network from pathlib import Path @@ -6,7 +7,7 @@ from typing import Optional, Dict, List, Any import yaml from pydantic import BaseModel, constr, root_validator, validator -from ._constants import RE_SEMVER, RE_VARNAME, HEADER_KIWI_CONF_NAME +from ._constants import RE_SEMVER, RE_VARNAME, HEADER_KIWI_CONF_NAME, KIWI_CONF_NAME # indent yaml lists @@ -138,6 +139,20 @@ class Config(BaseModel): cidr="10.22.46.0/24", ) + @classmethod + @functools.lru_cache(maxsize=5) + def from_instance(cls, instance: Path): + """parses an actual kiwi.yml from disk""" + + try: + with open(instance.joinpath(KIWI_CONF_NAME)) as kc: + yml = yaml.safe_load(kc) + return cls.parse_obj(yml) + + except FileNotFoundError: + # return the defaults if no kiwi.yml found + return cls() + @property def kiwi_dict(self) -> Dict[str, Any]: """write this object as a dictionary of strings""" @@ -201,7 +216,7 @@ class Config(BaseModel): try: return [str(value)] - except Exception as e: + except Exception: # undefined format raise ValueError("Invalid Shells Format") @@ -230,7 +245,7 @@ class Config(BaseModel): # handle single project name result.append({"name": str(entry)}) - except Exception as e: + except Exception: # undefined format raise ValueError("Invalid Projects Format") @@ -246,7 +261,7 @@ class Config(BaseModel): # handle as a single project name return [{"name": str(value)}] - except Exception as e: + except Exception: # undefined format raise ValueError("Invalid Projects Format") diff --git a/kiwi_scp/scripts/kiwi_next.py b/kiwi_scp/scripts/kiwi_next.py index de7c32c..5007843 100644 --- a/kiwi_scp/scripts/kiwi_next.py +++ b/kiwi_scp/scripts/kiwi_next.py @@ -1,21 +1,14 @@ import click -import yaml from kiwi_scp.commands.cli import KiwiCLI -from kiwi_scp.config import Config @click.command(cls=KiwiCLI) -@click.pass_context -def main(ctx): - """A complex command line interface.""" - - with open("./kiwi.yml") as kc: - yml = yaml.safe_load(kc) - ctx.ensure_object(dict) - ctx.obj["cfg"] = Config(**yml) +def main(): + """main entry point for command line interface""" click.echo("Hello main") + pass if __name__ == "__main__": diff --git a/poetry.lock b/poetry.lock index 978f60e..c35b800 100644 --- a/poetry.lock +++ b/poetry.lock @@ -10,7 +10,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" name = "attrs" version = "21.2.0" description = "Classes Without Boilerplate" -category = "dev" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" @@ -73,7 +73,7 @@ python-versions = "*" [[package]] name = "filelock" -version = "3.3.0" +version = "3.3.1" description = "A platform independent file lock." category = "dev" optional = false @@ -102,7 +102,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [[package]] name = "importlib-resources" -version = "5.2.2" +version = "5.3.0" description = "Read resources from Python packages" category = "dev" optional = false @@ -113,7 +113,7 @@ zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"] [[package]] name = "iniconfig" @@ -283,7 +283,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [metadata] lock-version = "1.1" python-versions = "^3.6.1" -content-hash = "eb1a3ab9af78ac7898062245858dbf9e9a27e82a6484f45f14b8db6c7fe812d6" +content-hash = "872024f4d335f920d178ef6b631785f4f6908f8f9f87d970932bd54dee7fd297" [metadata.files] atomicwrites = [ @@ -315,16 +315,16 @@ distlib = [ {file = "distlib-0.3.3.zip", hash = "sha256:d982d0751ff6eaaab5e2ec8e691d949ee80eddf01a62eaa96ddb11531fe16b05"}, ] filelock = [ - {file = "filelock-3.3.0-py3-none-any.whl", hash = "sha256:bbc6a0382fe8ec4744ecdf6683a2e07f65eb10ff1aff53fc02a202565446cde0"}, - {file = "filelock-3.3.0.tar.gz", hash = "sha256:8c7eab13dc442dc249e95158bcc12dec724465919bdc9831fdbf0660f03d1785"}, + {file = "filelock-3.3.1-py3-none-any.whl", hash = "sha256:2b5eb3589e7fdda14599e7eb1a50e09b4cc14f34ed98b8ba56d33bfaafcbef2f"}, + {file = "filelock-3.3.1.tar.gz", hash = "sha256:34a9f35f95c441e7b38209775d6e0337f9a3759f3565f6c5798f19618527c76f"}, ] importlib-metadata = [ {file = "importlib_metadata-4.8.1-py3-none-any.whl", hash = "sha256:b618b6d2d5ffa2f16add5697cf57a46c76a56229b0ed1c438322e4e95645bd15"}, {file = "importlib_metadata-4.8.1.tar.gz", hash = "sha256:f284b3e11256ad1e5d03ab86bb2ccd6f5339688ff17a4d797a0fe7df326f23b1"}, ] importlib-resources = [ - {file = "importlib_resources-5.2.2-py3-none-any.whl", hash = "sha256:2480d8e07d1890056cb53c96e3de44fead9c62f2ba949b0f2e4c4345f4afa977"}, - {file = "importlib_resources-5.2.2.tar.gz", hash = "sha256:a65882a4d0fe5fbf702273456ba2ce74fe44892c25e42e057aca526b702a6d4b"}, + {file = "importlib_resources-5.3.0-py3-none-any.whl", hash = "sha256:7a65eb0d8ee98eedab76e6deb51195c67f8e575959f6356a6e15fd7e1148f2a3"}, + {file = "importlib_resources-5.3.0.tar.gz", hash = "sha256:f2e58e721b505a79abe67f5868d99f8886aec8594c962c7490d0c22925f518da"}, ] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, diff --git a/pyproject.toml b/pyproject.toml index 1b61ceb..50cd2a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,14 +6,15 @@ authors = ["ldericher <40151420+ldericher@users.noreply.github.com>"] [tool.poetry.dependencies] python = "^3.6.1" -PyYAML = "^5.4.1" -pydantic = "^1.8.2" +attrs = "^21.2.0" click = "^8.0.3" +pydantic = "^1.8.2" +PyYAML = "^5.4.1" [tool.poetry.dev-dependencies] -virtualenv = "^20.8.1" pytest = "^6.2.5" toml = "^0.10.2" +virtualenv = "^20.8.1" [tool.poetry.scripts] kiwi = "kiwi_scp.scripts.kiwi:main"