archivebox_config.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. #!/usr/bin/env python3
  2. __package__ = 'archivebox.cli'
  3. import sys
  4. import rich_click as click
  5. from rich import print
  6. from benedict import benedict
  7. from archivebox.misc.util import docstring, enforce_types
  8. from archivebox.misc.toml_util import CustomTOMLEncoder
  9. @enforce_types
  10. def config(*keys,
  11. get: bool=False,
  12. set: bool=False,
  13. search: bool=False,
  14. reset: bool=False,
  15. **kwargs) -> None:
  16. """Get and set your ArchiveBox project configuration values"""
  17. import archivebox
  18. from archivebox.misc.checks import check_data_folder
  19. from archivebox.misc.logging_util import printable_config
  20. from archivebox.config.collection import load_all_config, write_config_file, get_real_name
  21. check_data_folder()
  22. FLAT_CONFIG = archivebox.pm.hook.get_FLAT_CONFIG()
  23. CONFIGS = archivebox.pm.hook.get_CONFIGS()
  24. config_options: list[str] = list(kwargs.pop('key=value', []) or keys or [f'{key}={val}' for key, val in kwargs.items()])
  25. no_args = not (get or set or reset or config_options)
  26. matching_config = {}
  27. if search:
  28. if config_options:
  29. config_options = [get_real_name(key) for key in config_options]
  30. matching_config = {key: FLAT_CONFIG[key] for key in config_options if key in FLAT_CONFIG}
  31. for config_section in CONFIGS.values():
  32. aliases = getattr(config_section, 'aliases', {})
  33. for search_key in config_options:
  34. # search all aliases in the section
  35. for alias_key, key in aliases.items():
  36. if search_key.lower() in alias_key.lower():
  37. matching_config[key] = dict(config_section)[key]
  38. # search all keys and values in the section
  39. for existing_key, value in dict(config_section).items():
  40. if search_key.lower() in existing_key.lower() or search_key.lower() in str(value).lower():
  41. matching_config[existing_key] = value
  42. print(printable_config(matching_config))
  43. raise SystemExit(not matching_config)
  44. elif get or no_args:
  45. if config_options:
  46. config_options = [get_real_name(key) for key in config_options]
  47. matching_config = {key: FLAT_CONFIG[key] for key in config_options if key in FLAT_CONFIG}
  48. failed_config = [key for key in config_options if key not in FLAT_CONFIG]
  49. if failed_config:
  50. print('\n[red][X] These options failed to get[/red]')
  51. print(' {}'.format('\n '.join(config_options)))
  52. raise SystemExit(1)
  53. else:
  54. matching_config = FLAT_CONFIG
  55. for config_section in CONFIGS.values():
  56. if hasattr(config_section, 'toml_section_header'):
  57. print(f'[grey53]\\[{config_section.toml_section_header}][/grey53]')
  58. else:
  59. print('[grey53]\\[CONSTANTS] # (read-only)[/grey53]')
  60. kv_in_section = {key: val for key, val in dict(config_section).items() if key in matching_config}
  61. print(benedict(kv_in_section).to_toml(encoder=CustomTOMLEncoder()).strip().replace('\n\n', '\n'))
  62. print('[grey53]################################################################[/grey53]')
  63. raise SystemExit(not matching_config)
  64. elif set:
  65. new_config = {}
  66. failed_options = []
  67. for line in config_options:
  68. if line.startswith('#') or not line.strip():
  69. continue
  70. if '=' not in line:
  71. print('[red][X] Config KEY=VALUE must have an = sign in it[/red]')
  72. print(f' {line}')
  73. raise SystemExit(2)
  74. raw_key, val = line.split('=', 1)
  75. raw_key = raw_key.upper().strip()
  76. key = get_real_name(raw_key)
  77. if key != raw_key:
  78. print(f'[yellow][i] Note: The config option {raw_key} has been renamed to {key}, please use the new name going forwards.[/yellow]')
  79. if key in FLAT_CONFIG:
  80. new_config[key] = val.strip()
  81. else:
  82. failed_options.append(line)
  83. if new_config:
  84. before = FLAT_CONFIG
  85. matching_config = write_config_file(new_config)
  86. after = {**load_all_config(), **archivebox.pm.hook.get_FLAT_CONFIG()}
  87. print(printable_config(matching_config))
  88. side_effect_changes = {}
  89. for key, val in after.items():
  90. if key in FLAT_CONFIG and (str(before[key]) != str(after[key])) and (key not in matching_config):
  91. side_effect_changes[key] = after[key]
  92. if side_effect_changes:
  93. print(file=sys.stderr)
  94. print('[yellow][i] Note: This change also affected these other options that depended on it:[/yellow]', file=sys.stderr)
  95. print(' {}'.format(printable_config(side_effect_changes, prefix=' ')), file=sys.stderr)
  96. if failed_options:
  97. print()
  98. print('[red][X] These options failed to set (check for typos):[/red]')
  99. print(' {}'.format('\n '.join(failed_options)))
  100. raise SystemExit(1)
  101. elif reset:
  102. print('[red][X] This command is not implemented yet.[/red]')
  103. print(' Please manually remove the relevant lines from your config file:')
  104. raise SystemExit(2)
  105. else:
  106. print('[red][X] You must pass either --get or --set, or no arguments to get the whole config.[/red]')
  107. print(' archivebox config')
  108. print(' archivebox config --get SOME_KEY')
  109. print(' archivebox config --set SOME_KEY=SOME_VALUE')
  110. raise SystemExit(2)
  111. @click.command()
  112. @click.option('--search', is_flag=True, help='Search config KEYs, VALUEs, and ALIASES for the given term')
  113. @click.option('--get', is_flag=True, help='Get the value for the given config KEYs')
  114. @click.option('--set', is_flag=True, help='Set the given KEY=VALUE config values')
  115. @click.option('--reset', is_flag=True, help='Reset the given KEY config values to their defaults')
  116. @click.argument('KEY=VALUE', nargs=-1, type=str)
  117. @docstring(config.__doc__)
  118. def main(**kwargs) -> None:
  119. config(**kwargs)
  120. if __name__ == '__main__':
  121. main()