diff --git a/archivebox/cli/archivebox_config.py b/archivebox/cli/archivebox_config.py index 2eb2676f..897af5e0 100644 --- a/archivebox/cli/archivebox_config.py +++ b/archivebox/cli/archivebox_config.py @@ -1,48 +1,36 @@ #!/usr/bin/env python3 __package__ = 'archivebox.cli' -__command__ = 'archivebox config' import sys -import argparse -from pathlib import Path +import rich_click as click +from rich import print +from benedict import benedict -from typing import Optional, List, IO - -from archivebox.misc.util import docstring -from archivebox.config import DATA_DIR -from archivebox.misc.logging_util import SmartFormatter, accept_stdin +from archivebox.misc.util import docstring, enforce_types +from archivebox.misc.toml_util import CustomTOMLEncoder - -# @enforce_types -def config(config_options_str: Optional[str]=None, - config_options: Optional[List[str]]=None, - get: bool=False, - set: bool=False, - search: bool=False, - reset: bool=False, - out_dir: Path=DATA_DIR) -> None: +@enforce_types +def config(*keys, + get: bool=False, + set: bool=False, + search: bool=False, + reset: bool=False, + **kwargs) -> None: """Get and set your ArchiveBox project configuration values""" - from rich import print + import archivebox + from archivebox.misc.checks import check_data_folder + from archivebox.misc.logging_util import printable_config + from archivebox.config.collection import load_all_config, write_config_file, get_real_name check_data_folder() - if config_options and config_options_str: - stderr( - '[X] You should either pass config values as an arguments ' - 'or via stdin, but not both.\n', - color='red', - ) - raise SystemExit(2) - elif config_options_str: - config_options = config_options_str.split('\n') FLAT_CONFIG = archivebox.pm.hook.get_FLAT_CONFIG() CONFIGS = archivebox.pm.hook.get_CONFIGS() - config_options = config_options or [] - + config_options: list[str] = list(kwargs.pop('key=value', []) or keys or [f'{key}={val}' for key, val in kwargs.items()]) no_args = not (get or set or reset or config_options) matching_config = {} @@ -51,36 +39,47 @@ def config(config_options_str: Optional[str]=None, config_options = [get_real_name(key) for key in config_options] matching_config = {key: FLAT_CONFIG[key] for key in config_options if key in FLAT_CONFIG} for config_section in CONFIGS.values(): - aliases = config_section.aliases + aliases = getattr(config_section, 'aliases', {}) for search_key in config_options: # search all aliases in the section for alias_key, key in aliases.items(): if search_key.lower() in alias_key.lower(): - matching_config[key] = config_section.model_dump()[key] + matching_config[key] = dict(config_section)[key] # search all keys and values in the section - for existing_key, value in config_section.model_dump().items(): + for existing_key, value in dict(config_section).items(): if search_key.lower() in existing_key.lower() or search_key.lower() in str(value).lower(): matching_config[existing_key] = value print(printable_config(matching_config)) raise SystemExit(not matching_config) + elif get or no_args: if config_options: config_options = [get_real_name(key) for key in config_options] matching_config = {key: FLAT_CONFIG[key] for key in config_options if key in FLAT_CONFIG} failed_config = [key for key in config_options if key not in FLAT_CONFIG] if failed_config: - stderr() - stderr('[X] These options failed to get', color='red') - stderr(' {}'.format('\n '.join(config_options))) + print('\n[red][X] These options failed to get[/red]') + print(' {}'.format('\n '.join(config_options))) raise SystemExit(1) else: matching_config = FLAT_CONFIG - print(printable_config(matching_config)) + for config_section in CONFIGS.values(): + if hasattr(config_section, 'toml_section_header'): + print(f'[grey53]\\[{config_section.toml_section_header}][/grey53]') + else: + print('[grey53]\\[CONSTANTS] # (read-only)[/grey53]') + + kv_in_section = {key: val for key, val in dict(config_section).items() if key in matching_config} + print(benedict(kv_in_section).to_toml(encoder=CustomTOMLEncoder()).strip().replace('\n\n', '\n')) + print('[grey53]################################################################[/grey53]') + + raise SystemExit(not matching_config) + elif set: new_config = {} failed_options = [] @@ -88,15 +87,15 @@ def config(config_options_str: Optional[str]=None, if line.startswith('#') or not line.strip(): continue if '=' not in line: - stderr('[X] Config KEY=VALUE must have an = sign in it', color='red') - stderr(f' {line}') + print('[red][X] Config KEY=VALUE must have an = sign in it[/red]') + print(f' {line}') raise SystemExit(2) raw_key, val = line.split('=', 1) raw_key = raw_key.upper().strip() key = get_real_name(raw_key) if key != raw_key: - stderr(f'[i] Note: The config option {raw_key} has been renamed to {key}, please use the new name going forwards.', color='lightyellow') + print(f'[yellow][i] Note: The config option {raw_key} has been renamed to {key}, please use the new name going forwards.[/yellow]') if key in FLAT_CONFIG: new_config[key] = val.strip() @@ -113,82 +112,41 @@ def config(config_options_str: Optional[str]=None, for key, val in after.items(): if key in FLAT_CONFIG and (str(before[key]) != str(after[key])) and (key not in matching_config): side_effect_changes[key] = after[key] - # import ipdb; ipdb.set_trace() if side_effect_changes: - stderr() - stderr('[i] Note: This change also affected these other options that depended on it:', color='lightyellow') - print(' {}'.format(printable_config(side_effect_changes, prefix=' '))) + print(file=sys.stderr) + print('[yellow][i] Note: This change also affected these other options that depended on it:[/yellow]', file=sys.stderr) + print(' {}'.format(printable_config(side_effect_changes, prefix=' ')), file=sys.stderr) + if failed_options: - stderr() - stderr('[X] These options failed to set (check for typos):', color='red') - stderr(' {}'.format('\n '.join(failed_options))) + print() + print('[red][X] These options failed to set (check for typos):[/red]') + print(' {}'.format('\n '.join(failed_options))) raise SystemExit(1) + elif reset: - stderr('[X] This command is not implemented yet.', color='red') - stderr(' Please manually remove the relevant lines from your config file:') + print('[red][X] This command is not implemented yet.[/red]') + print(' Please manually remove the relevant lines from your config file:') raise SystemExit(2) + else: - stderr('[X] You must pass either --get or --set, or no arguments to get the whole config.', color='red') - stderr(' archivebox config') - stderr(' archivebox config --get SOME_KEY') - stderr(' archivebox config --set SOME_KEY=SOME_VALUE') + print('[red][X] You must pass either --get or --set, or no arguments to get the whole config.[/red]') + print(' archivebox config') + print(' archivebox config --get SOME_KEY') + print(' archivebox config --set SOME_KEY=SOME_VALUE') raise SystemExit(2) - - +@click.command() +@click.option('--search', is_flag=True, help='Search config KEYs, VALUEs, and ALIASES for the given term') +@click.option('--get', is_flag=True, help='Get the value for the given config KEYs') +@click.option('--set', is_flag=True, help='Set the given KEY=VALUE config values') +@click.option('--reset', is_flag=True, help='Reset the given KEY config values to their defaults') +@click.argument('KEY=VALUE', nargs=-1, type=str) @docstring(config.__doc__) -def main(args: Optional[List[str]]=None, stdin: Optional[IO]=None, pwd: Optional[str]=None) -> None: - parser = argparse.ArgumentParser( - prog=__command__, - description=config.__doc__, - add_help=True, - formatter_class=SmartFormatter, - ) - group = parser.add_mutually_exclusive_group() - parser.add_argument( - '--search', - action='store_true', - help="Search config KEYs, VALUEs, and ALIASES for the given term", - ) - group.add_argument( - '--get', #'-g', - action='store_true', - help="Get the value for the given config KEYs", - ) - group.add_argument( - '--set', #'-s', - action='store_true', - help="Set the given KEY=VALUE config values", - ) - group.add_argument( - '--reset', #'-s', - action='store_true', - help="Reset the given KEY config values to their defaults", - ) - parser.add_argument( - 'config_options', - nargs='*', - type=str, - help='KEY or KEY=VALUE formatted config values to get or set', - ) - command = parser.parse_args(args or ()) - - config_options_str = '' - if not command.config_options: - config_options_str = accept_stdin(stdin) - - config( - config_options_str=config_options_str, - config_options=command.config_options, - search=command.search, - get=command.get, - set=command.set, - reset=command.reset, - out_dir=Path(pwd) if pwd else DATA_DIR, - ) +def main(**kwargs) -> None: + config(**kwargs) if __name__ == '__main__': - main(args=sys.argv[1:], stdin=sys.stdin) + main() diff --git a/archivebox/pkgs/abx-plugin-default-binproviders/abx_plugin_default_binproviders.py b/archivebox/pkgs/abx-plugin-default-binproviders/abx_plugin_default_binproviders.py index 9dca52ef..53017bb7 100644 --- a/archivebox/pkgs/abx-plugin-default-binproviders/abx_plugin_default_binproviders.py +++ b/archivebox/pkgs/abx-plugin-default-binproviders/abx_plugin_default_binproviders.py @@ -12,6 +12,9 @@ from abx_pkg import ( apt = APT_BINPROVIDER = AptProvider() brew = BREW_BINPROVIDER = BrewProvider() env = ENV_BINPROVIDER = EnvProvider() +apt.setup() +brew.setup() +env.setup() @abx.hookimpl(tryfirst=True) diff --git a/archivebox/pkgs/abx-plugin-npm/abx_plugin_npm/binproviders.py b/archivebox/pkgs/abx-plugin-npm/abx_plugin_npm/binproviders.py index 72325083..c962a68b 100644 --- a/archivebox/pkgs/abx-plugin-npm/abx_plugin_npm/binproviders.py +++ b/archivebox/pkgs/abx-plugin-npm/abx_plugin_npm/binproviders.py @@ -36,3 +36,6 @@ SYS_NPM_BINPROVIDER = SystemNpmBinProvider() LIB_NPM_BINPROVIDER = LibNpmBinProvider() LIB_NPM_BINPROVIDER.setup() npm = LIB_NPM_BINPROVIDER + +LIB_NPM_BINPROVIDER.setup() +SYS_NPM_BINPROVIDER.setup() diff --git a/archivebox/pkgs/abx-plugin-pip/abx_plugin_pip/binproviders.py b/archivebox/pkgs/abx-plugin-pip/abx_plugin_pip/binproviders.py index 67a1aaab..38568009 100644 --- a/archivebox/pkgs/abx-plugin-pip/abx_plugin_pip/binproviders.py +++ b/archivebox/pkgs/abx-plugin-pip/abx_plugin_pip/binproviders.py @@ -72,6 +72,11 @@ LIB_PIP_BINPROVIDER = LibPipBinProvider() LIB_PIP_BINPROVIDER.setup() pip = LIB_PIP_BINPROVIDER +SYS_PIP_BINPROVIDER.setup() +PIPX_PIP_BINPROVIDER.setup() +VENV_PIP_BINPROVIDER.setup() +LIB_PIP_BINPROVIDER.setup() + # ensure python libraries are importable from these locations (if archivebox wasnt executed from one of these then they wont already be in sys.path) assert VENV_PIP_BINPROVIDER.pip_venv is not None assert LIB_PIP_BINPROVIDER.pip_venv is not None diff --git a/archivebox/pkgs/abx-plugin-playwright/abx_plugin_playwright/binproviders.py b/archivebox/pkgs/abx-plugin-playwright/abx_plugin_playwright/binproviders.py index 467e938c..1938e08f 100644 --- a/archivebox/pkgs/abx-plugin-playwright/abx_plugin_playwright/binproviders.py +++ b/archivebox/pkgs/abx-plugin-playwright/abx_plugin_playwright/binproviders.py @@ -164,3 +164,4 @@ class PlaywrightBinProvider(BinProvider): return (proc.stderr.strip() + "\n" + proc.stdout.strip()).strip() PLAYWRIGHT_BINPROVIDER = PlaywrightBinProvider() +PLAYWRIGHT_BINPROVIDER.setup() diff --git a/archivebox/pkgs/abx-plugin-puppeteer/abx_plugin_puppeteer/binproviders.py b/archivebox/pkgs/abx-plugin-puppeteer/abx_plugin_puppeteer/binproviders.py index e65855ae..c502b22d 100644 --- a/archivebox/pkgs/abx-plugin-puppeteer/abx_plugin_puppeteer/binproviders.py +++ b/archivebox/pkgs/abx-plugin-puppeteer/abx_plugin_puppeteer/binproviders.py @@ -115,7 +115,7 @@ class PuppeteerBinProvider(BinProvider): return (proc.stderr.strip() + "\n" + proc.stdout.strip()).strip() PUPPETEER_BINPROVIDER = PuppeteerBinProvider() - +PUPPETEER_BINPROVIDER.setup() # ALTERNATIVE INSTALL METHOD using Ansible: # install_playbook = self.plugin_dir / 'install_puppeteer.yml'