diff --git a/iam/app.py b/iam/app.py index f3a1358..752c439 100644 --- a/iam/app.py +++ b/iam/app.py @@ -12,7 +12,7 @@ from . import exceptions as error from .catalog import Catalog from .framework import DictItem, scoped_ident from .idents import Idents -from .lib.utils import import_module, open_yaml, get_root_pkg_dir +from .lib.utils import get_root_pkg_dir, import_module, open_yaml from .meta import NAME, VERSION from .providers import Providers @@ -169,7 +169,6 @@ class App: "Disable shell" return self.catalog.shell_disable(**kwargs) - def shell_install(self, shell="bash", completion=True): "Show your install script for shell" @@ -193,4 +192,4 @@ class App: out.append(f" source {completion_file}") out.append(f"fi") out.append(f"### IAM ###") - return '\n'.join(out) \ No newline at end of file + return "\n".join(out) diff --git a/iam/catalog.py b/iam/catalog.py index d149094..15e7290 100644 --- a/iam/catalog.py +++ b/iam/catalog.py @@ -1,18 +1,18 @@ - -import os import logging +import os from collections import namedtuple from pprint import pprint from types import SimpleNamespace -import sh # TO BE REMOVED !!!! import click +import sh from . import exceptions as error from .framework import DictCtrl, DictItem, KeyValue, KeyValueExtra from .idents import Ident -from .lib.utils import format_render, jinja_render, uniq, jinja_template_vars, empty +from .lib.utils import (empty, format_render, jinja_render, + jinja_template_vars, uniq) logger = logging.getLogger(__name__) cli_logger = logging.getLogger("iam.cli") @@ -54,19 +54,14 @@ class ServiceCommand(DictItem): default_attrs = { "desc": "Service without description", - "cmd": "", # Direct commands to launch - - "shell": "", # Will be run in a shell (or specific shell below) - + "shell": "", # Will be run in a shell (or specific shell below) # "shell_sh": "", # "shell_bash": "", # "shell_zsh": "", # "shell_fish": "", # "shell_bash": "", - # "source_output": False, # True for shell commands by default ! - } # Overrides @@ -76,7 +71,6 @@ class ServiceCommand(DictItem): "Transform short form into long form" self.service = self.parent - # def payload_transform(self, name, kwargs=None): # "Transform short form into long form" # self.service = self.parent @@ -111,7 +105,6 @@ class Service(DictItem): self.catalog = self.parent.catalog def init(self): - ret = {} for cmd_name, cmd in self.commands.items(): ret[cmd_name] = ServiceCommand(cmd_name, cmd, parent=self) @@ -229,7 +222,7 @@ class Service(DictItem): out = cmd.format(**env_var) except KeyError as err: msg = f"Missing variable: {err} for service: {res.key}" - logger.warning (msg) + logger.warning(msg) out = msg # raise IamException(msg) @@ -245,9 +238,9 @@ class Service(DictItem): def get_cmds(self): "Get all services commands objects" - return [cmd for name, cmd in self.commands.items() if not name.startswith("shell")] - - + return [ + cmd for name, cmd in self.commands.items() if not name.startswith("shell") + ] def list_cmds(self): "List all services commands" @@ -268,7 +261,6 @@ class Service(DictItem): # cmd_name = cmd_name[len(shell_prefix):] name = f"{cmd_name}_{prefix}" else: - name = cmd_name.replace("_", " ") target[name] = conf.desc @@ -293,13 +285,11 @@ class Services(DictCtrl): items_class = Service - RESERVED_CMD_PREFIX=["kind", "res", "svc", "shell", "run"] + RESERVED_CMD_PREFIX = ["kind", "res", "svc", "shell", "run"] def prepare(self): self.catalog = self.parent - - def get_svc_command(self, cmd): "Return the command to be run" @@ -311,13 +301,12 @@ class Services(DictCtrl): # Retrieve command list cmds = self.list_cmds() - cmd_names = [cmd.name for cmd in cmds ] - + cmd_names = [cmd.name for cmd in cmds] # Find best matching command cmd_split_idx = None cmd = None - curr=None + curr = None for index, part in enumerate(cmd_parts): curr = f"{curr} {part}" if curr is not None else part if curr in cmd_names: @@ -328,15 +317,13 @@ class Services(DictCtrl): # Validate result if not cmd: - _choices = ','.join(cmd_names) + _choices = ",".join(cmd_names) _msg = f"No such services command named: {cmd_req}, please choose one of: {_choices}" raise error.UnknownServiceCommand(_msg) args = cmd_parts[cmd_split_idx:] return cmd, args - - def list_cmds(self): "List all services commands" @@ -351,18 +338,17 @@ class Services(DictCtrl): for cmd in ret: prefix = cmd.name.split(" ")[0] if prefix in self.RESERVED_CMD_PREFIX: - _msg = f'Forbidden prefix! {cmd}' - raise Exception (_msg) - conf.append(cmd.name ) + _msg = f"Forbidden prefix! {cmd}" + raise Exception(_msg) + conf.append(cmd.name) dump = uniq(conf) if conf != dump: - _msg = f'Duplicates values! {conf}' - raise Exception (_msg) + _msg = f"Duplicates values! {conf}" + raise Exception(_msg) return ret - def get_linked_resources(self): """ Like get method, but only grab enabled services @@ -791,7 +777,6 @@ class Context(DictItem): self._vars = { "ident": self.ident.name, "user": self.ident.name, - "config_dir": root_dir, "scripts_dir": os.path.join(root_dir, "scripts"), "bin_dir": os.path.join(root_dir, "bin"), @@ -912,14 +897,12 @@ class Catalog: # return self.services.get_loading_order(reverse=reverse) - # Shell helpers # ================ def shell_enable(self, run_start=True): "Enable shell" - actions = ["shell_enable"] if run_start: actions.insert(0, "shell_start") @@ -928,7 +911,6 @@ class Catalog: return self._shell_action(actions) - def shell_disable(self, run_stop=False): "Disable shell" @@ -940,7 +922,6 @@ class Catalog: return self._shell_action(actions) - def _shell_action(self, action_names, reverse=False): "Run command order" @@ -993,22 +974,20 @@ class Catalog: # pprint (ctx_vars) cmd = jinja_render(cmd_shell, ctx_vars) - output_code.append(f"# Loading: {action_name} {res.name} ({service.name})") + output_code.append( + f"# Loading: {action_name} {res.name} ({service.name})" + ) output_code.append(f"# =====================") output_code.append(cmd) output_code.append("\n") return "\n".join(output_code) - - - def run_svc_cmd(self, cmd): "Run a command against the services" cmd, args = self.services.get_svc_command(cmd) - # pprint (cmd.cmd) # pprint (args) @@ -1037,7 +1016,7 @@ class Catalog: # "loop": res_vars, # } # ) - + # pprint (ctx_vars) new_env = dict(os.environ) @@ -1048,15 +1027,13 @@ class Catalog: logger.warning(f"Duplicate cmd and shell for {service.name}") out = None - - if cmd.shell: mode = "shell" payload = cmd.shell # Scan for missing vars !!! - tmp = jinja_template_vars(payload) + tmp = jinja_template_vars(payload) prompt_vars = [] for var_name in tmp: if var_name not in ctx_vars: @@ -1074,11 +1051,11 @@ class Catalog: answered_items = {} for missed in prompt_vars: default = ctx_vars.get(missed, None) - result = click.prompt(f'Select value for {missed}', default=default) + result = click.prompt(f"Select value for {missed}", default=default) answered_items[missed] = result ctx_vars.update(answered_items) - + real_cmd = jinja_render(payload, ctx_vars) # print ("RUN SHELL") @@ -1105,15 +1082,13 @@ class Catalog: if sh_args: out = cmd(sh_args, _fg=True, _env=new_env) else: - out = cmd( _fg=True, _env=new_env) + out = cmd(_fg=True, _env=new_env) # print (out) else: raise Exception("MIssing cmd or shell in config !") - return out - - # print ("RUN CMD", mode, "\n\n\n====\n", real_cmd) \ No newline at end of file + # print ("RUN CMD", mode, "\n\n\n====\n", real_cmd) diff --git a/iam/cli_click.py b/iam/cli_click.py index d657668..3547b01 100755 --- a/iam/cli_click.py +++ b/iam/cli_click.py @@ -45,7 +45,7 @@ else: import rich_click as click from rich_click import RichGroup - from rich import box #, print + from rich import box # , print from rich.console import Console # Overrides defaults from rich.pretty import pprint @@ -99,7 +99,7 @@ DEFAULT_LOG_LEVEL = 30 # warning DEBUG = os.environ.get("IAM_DEBUG", "False") DEFAULT_IDENT = os.environ.get("IAM_IDENT", os.environ.get("SHELL_IDENT", "")) DEFAULT_FORMAT = os.environ.get("IAM_FORMAT", "table") -DEFAULT_HELP_OPTIONS=["-h", "--help"] +DEFAULT_HELP_OPTIONS = ["-h", "--help"] DEFAULT_SHELL = os.environ.get("SHELL", "bash") # list_view.formats_enum @@ -222,8 +222,7 @@ def get_var(name, default=None): # @click.group(cls=NestedHelpGroup, context_settings=CONTEXT_SETTINGS) -@click.group(cls=NestedHelpGroup -) +@click.group(cls=NestedHelpGroup) @global_options @format_options @click.version_option() @@ -686,7 +685,11 @@ def cli__shell(): @format_options @click.pass_context def shell_idents( - ctx, fmt_name=None, fmt_fields=None, fmt_sort=None, verbose=None, + ctx, + fmt_name=None, + fmt_fields=None, + fmt_sort=None, + verbose=None, ): "Shell identities" columns = ["name"] @@ -736,11 +739,15 @@ def shell_order( ) - - @cli__shell.command("enable") @click.option( - "skip_start", "--no-start", "-b", is_flag=True, show_default=False, default=False, help="Disable background process start" + "skip_start", + "--no-start", + "-b", + is_flag=True, + show_default=False, + default=False, + help="Disable background process start", ) @click.pass_context def shell_enable(ctx, skip_start=True, verbose=None): @@ -750,10 +757,15 @@ def shell_enable(ctx, skip_start=True, verbose=None): print(app.shell_enable(run_start=not skip_start)) - @cli__shell.command("disable") @click.option( - "run_stop", "--kill", "-k", is_flag=True, show_default=False, default=False, help="Kill background process on leave" + "run_stop", + "--kill", + "-k", + is_flag=True, + show_default=False, + default=False, + help="Kill background process on leave", ) @click.pass_context def shell_disable(ctx, run_stop=False, verbose=None): @@ -765,7 +777,13 @@ def shell_disable(ctx, run_stop=False, verbose=None): @cli__shell.command("kill") @click.option( - "all_idents", "--all", "-a", is_flag=True, show_default=False, default=False, help="Kill on all users" + "all_idents", + "--all", + "-a", + is_flag=True, + show_default=False, + default=False, + help="Kill on all users", ) @click.pass_context def shell_kill(ctx, all_idents=False, verbose=None): @@ -785,20 +803,30 @@ def shell_kill(ctx, all_idents=False, verbose=None): ret.append(app.shell_enable(run_start=False)) ret.append(app.shell_disable(run_stop=True)) - print('\n'.join(ret)) - + print("\n".join(ret)) @cli__shell.command("switch") @click.argument("ident", required=False) # @format_options @click.option( - "skip_start", "--no-start", "-b", is_flag=True, show_default=False, default=False, help="Disable background process start" + "skip_start", + "--no-start", + "-b", + is_flag=True, + show_default=False, + default=False, + help="Disable background process start", ) @click.option( - "run_stop", "--kill", "-k", is_flag=True, show_default=False, default=False, help="Kill background process on leave" + "run_stop", + "--kill", + "-k", + is_flag=True, + show_default=False, + default=False, + help="Kill background process on leave", ) - @click.pass_context def shell_switch(ctx, ident="", run_stop=False, skip_start=True, verbose=None): "Enable identity in shell" @@ -808,9 +836,8 @@ def shell_switch(ctx, ident="", run_stop=False, skip_start=True, verbose=None): src_ident = app.catalog.ident.name dst_ident = ident - if src_ident == dst_ident: - print (">&2 echo 'No need to change'") + print(">&2 echo 'No need to change'") else: ret = [] @@ -824,32 +851,25 @@ def shell_switch(ctx, ident="", run_stop=False, skip_start=True, verbose=None): ret.append(app.shell_enable(run_start=not skip_start)) # Output - print('\n'.join(ret)) - - - + print("\n".join(ret)) class TmpGroup(click.Group): def format_help(self, ctx, formatter): - val = "List of available commands!" formatter.write(val) - -@cli_app.command("run", +@cli_app.command( + "run", # cls=TmpGroup, context_settings=dict( - ignore_unknown_options=True, - allow_extra_args=True, - help_option_names=[] - ) + ignore_unknown_options=True, allow_extra_args=True, help_option_names=[] + ), ) -@click.argument('cmd_params', nargs=-1, type=click.UNPROCESSED) +@click.argument("cmd_params", nargs=-1, type=click.UNPROCESSED) @click.pass_context def cli_app_run(ctx, cmd_params): - # print("Will run cmd:", cmd_params) # Check first help parameter only @@ -867,7 +887,6 @@ def cli_app_run(ctx, cmd_params): click.echo(ctx.get_help()) return - # Get instanciated app app = ctx.obj.app @@ -875,7 +894,6 @@ def cli_app_run(ctx, cmd_params): ret = app.catalog.services.list_cmds() data = [[x.name, x.desc] for x in ret] - # Render data ctx.obj.render_list( data, @@ -889,14 +907,13 @@ def cli_app_run(ctx, cmd_params): ) return - + # Run the output ret = app.catalog.run_svc_cmd(cmd=cmd_params) - print (ret) + print(ret) # pprint (ret, expand_all=True) - # @cli_app.command("run2", # context_settings=dict( # ignore_unknown_options=True, @@ -912,9 +929,7 @@ def cli_app_run(ctx, cmd_params): @cli__shell.command("install") -@click.argument("shell", - required=False, - default=DEFAULT_SHELL) +@click.argument("shell", required=False, default=DEFAULT_SHELL) @click.pass_context def shell_install(ctx, shell=None, verbose=None): "Install iam in your shell" @@ -926,7 +941,6 @@ def shell_install(ctx, shell=None, verbose=None): # print("-- %< --" * 8) - # Exception handler # =============================== def clean_terminate(err): diff --git a/iam/framework.py b/iam/framework.py index 4967f82..9f4361f 100644 --- a/iam/framework.py +++ b/iam/framework.py @@ -90,15 +90,12 @@ def get_app_logger(loggers=None, level="WARNING", colors=False, format="default" }, # Where logs come from "loggers": { - # Used to catch ALL logs "": { # root logger "handlers": ["default"], "level": "WARNING", "propagate": False, }, - - # # Used to catch all logs of myapp and sublibs # 'myapp': { # 'handlers': ['default'], diff --git a/iam/lib/cli_views.py b/iam/lib/cli_views.py index 1c30944..8d652ba 100644 --- a/iam/lib/cli_views.py +++ b/iam/lib/cli_views.py @@ -21,7 +21,6 @@ except ModuleNotFoundError: # duckdb support => https://stackoverflow.com/a/70538527 - # MISSING_PKGS = [] # try: # from rich.console import Console @@ -343,7 +342,6 @@ class _RootView: # logger.warning(_msg) # # raise Exception(_msg) - # Fetch renderer method # --------------------------- fmt_name = fmt_.name diff --git a/iam/lib/click_utils.py b/iam/lib/click_utils.py index 5b01395..cf4f8f4 100644 --- a/iam/lib/click_utils.py +++ b/iam/lib/click_utils.py @@ -1,4 +1,5 @@ import logging + import click logger = logging.getLogger(__name__) @@ -14,7 +15,7 @@ class NestedHelpGroup(click.Group): instead of just the parent. Optionnaly accept to hides groups or not. This class is aimed to serve simple resource based CLIs, à la [cliff](), - but with the click library. + but with the click library. This class provides: * Recursive command listing @@ -49,7 +50,6 @@ class NestedHelpGroup(click.Group): """ - # For partial name resolution def resolve_command(self, ctx, args): "Return the full command name if changed" @@ -59,7 +59,6 @@ class NestedHelpGroup(click.Group): logger.debug(f"Rewrite command '{_}' to '{cmd.name}'") return cmd.name, cmd, args - def get_command(self, ctx, cmd_name: str): """Given a context and a command name, this returns a :class:`Command` object if it exists or returns ``None``. @@ -67,16 +66,21 @@ class NestedHelpGroup(click.Group): # Resolve name part by part parts = cmd_name.split(" ") - len_parts = len(parts) -1 + len_parts = len(parts) - 1 curr = self for idx, part in enumerate(parts): match = curr.commands.get(part) - + # Look for shortcut if last part if match is None: # Look for direct children only - matches = [x for x in self._resolve_children(self, ctx=ctx, ignore_groups=False, deep=0) - if x.startswith(cmd_name)] + matches = [ + x + for x in self._resolve_children( + self, ctx=ctx, ignore_groups=False, deep=0 + ) + if x.startswith(cmd_name) + ] # Look for possible matches if not matches: @@ -84,8 +88,10 @@ class NestedHelpGroup(click.Group): elif len(matches) == 1: match = click.Group.get_command(self, ctx, matches[0]) else: - ctx.fail(f"Too many matches for {cmd_name}: {', '.join(sorted(matches))}") - + ctx.fail( + f"Too many matches for {cmd_name}: {', '.join(sorted(matches))}" + ) + # Iterate over next child! curr = match @@ -97,7 +103,9 @@ class NestedHelpGroup(click.Group): return sorted(self._resolve_children(self, ctx=ctx, ignore_groups=True)) @classmethod - def _resolve_children(cls, obj, ctx=None, ignore_groups=False, _parent=None, deep=-1): + def _resolve_children( + cls, obj, ctx=None, ignore_groups=False, _parent=None, deep=-1 + ): "Resolve recursively all children" # Source: Adapted from https://stackoverflow.com/a/56159096 @@ -119,14 +127,17 @@ class NestedHelpGroup(click.Group): # Recursive loop if deep != 0: - deep = deep -1 + deep = deep - 1 ret.extend( cls._resolve_children( - child, ctx=ctx, ignore_groups=ignore_groups, _parent=full_name, deep=deep + child, + ctx=ctx, + ignore_groups=ignore_groups, + _parent=full_name, + deep=deep, ) ) return ret return [] - diff --git a/iam/lib/utils.py b/iam/lib/utils.py index d9fec06..706a6ba 100644 --- a/iam/lib/utils.py +++ b/iam/lib/utils.py @@ -1,8 +1,8 @@ -import sys import importlib import json import logging import os +import sys import tomllib from pprint import pprint @@ -83,6 +83,7 @@ def prune(items): raise Exception(f"Function prune requires a list or a dict, got: {items}") return ret + # from jinja2 import Environment, FileSystemLoader, meta # env = Environment(loader=FileSystemLoader('templates')) @@ -91,9 +92,9 @@ def prune(items): from jinja2 import Environment, PackageLoader, meta -def jinja_template_vars(payload): - env = Environment() #loader=PackageLoader('templates')) +def jinja_template_vars(payload): + env = Environment() # loader=PackageLoader('templates')) parsed_content = env.parse(payload) return meta.find_undeclared_variables(parsed_content) @@ -250,6 +251,7 @@ def iterate_any(payload): # raise Exception(f"Could not iterate over: {payload}") + def get_root_pkg_dir(name): """Return the dir where the actual paasify source code lives""" @@ -260,9 +262,8 @@ def get_root_pkg_dir(name): target = os.path.join(cand, name) if os.path.isdir(target): return target - - return None + return None def get_pkg_dir(name): diff --git a/iam/plugins/local.py b/iam/plugins/local.py index f4483cc..6436339 100644 --- a/iam/plugins/local.py +++ b/iam/plugins/local.py @@ -7,6 +7,4 @@ yml_dir = get_pkg_dir(__name__) plugin_conf = open_yaml(os.path.join(yml_dir, "local.yml"))[0] - - all = plugin_conf.get("providers", {}) diff --git a/run_ci.sh b/run_ci.sh new file mode 100755 index 0000000..abb4e26 --- /dev/null +++ b/run_ci.sh @@ -0,0 +1,5 @@ +#!/bin/bash + + +black iam/ +isort iam/