Я бы предложил изменить порядок запуска кода инициализации. Это можно сделать с помощью ...
Пользовательский класс:
class LoadInitForCommands(click.Group):
def command(self, *args, **kwargs):
def decorator(f):
# call the original decorator
cmd = click.command(*args, **kwargs)(f)
self.add_command(cmd)
orig_invoke = cmd.invoke
def invoke(ctx):
# Custom init code is here
ctx.obj = {}
if cmd.name != "init":
config = yaml.load(open(".configrc").read())
ctx.obj.update({key: config[key] for key in config})
# call the original invoke()
return orig_invoke(ctx)
# hook the command's invoke
cmd.invoke = invoke
return cmd
return decorator
Использование пользовательского класса:
Передайте пользовательский класс в click.group()
, используя параметр cls
, например:
@click.group(cls=LoadInitForCommands)
def cli():
""""""
Как это работает?
Это работает, потому что click - это хорошо спроектированная OO-инфраструктура. Декоратор @click.group()
обычно создает экземпляр объекта click.Group
, но позволяет переопределить это поведение параметром cls
. Так что относительно просто унаследовать от click.Group
в нашем собственном классе и перебрать нужные методы.
В этом случае мы подключаем декоратор command()
, и в этом блоке мы перезаписываем команду invoke()
. Это позволяет читать файл инициализации после того, как флаг --help
уже обработан.
Обратите внимание, что этот код предназначен для упрощения использования многих команд, для которых --help
будет доступен до чтения init. В примере в вопросе есть только одна команда, которая нуждается в инициализации. Если это всегда так, то этот ответ может быть привлекательным.
Тестовый код:
import click
import yaml
@click.group(cls=LoadInitForCommands)
def cli():
""""""
@cli.command()
@click.pass_context
def init(ctx):
print("Initialize project.")
@cli.command()
@click.option("--dryrun", type=bool, is_flag=True,
help="Run in read-only mode")
@click.pass_context
def run(ctx, dryrun):
print("Run main program here.")
if __name__ == "__main__":
commands = (
'init',
'run --help',
'run',
'--help',
)
import sys, time
time.sleep(1)
print('Click Version: {}'.format(click.__version__))
print('Python Version: {}'.format(sys.version))
for cmd in commands:
try:
time.sleep(0.1)
print('-----------')
print('> ' + cmd)
time.sleep(0.1)
cli(cmd.split(), obj={})
except BaseException as exc:
if str(exc) != '0' and \
not isinstance(exc, (click.ClickException, SystemExit)):
raise
Результаты:
Click Version: 6.7
Python Version: 3.6.3 (v3.6.3:2c5fed8, Oct 3 2017, 18:11:49) [MSC v.1900 64 bit (AMD64)]
-----------
> init
Initialize project.
-----------
> run --help
Usage: test.py run [OPTIONS]
Options:
--dryrun Run in read-only mode
--help Show this message and exit.
-----------
> run
Traceback (most recent call last):
File "C:\Users\stephen\AppData\Local\JetBrains\PyCharm 2018.3\helpers\pydev\pydevd.py", line 1741, in <module>
main()
File "C:\Users\stephen\AppData\Local\JetBrains\PyCharm 2018.3\helpers\pydev\pydevd.py", line 1735, in main
globals = debugger.run(setup['file'], None, None, is_module)
File "C:\Users\stephen\AppData\Local\JetBrains\PyCharm 2018.3\helpers\pydev\pydevd.py", line 1135, in run
pydev_imports.execfile(file, globals, locals) # execute the script
File "C:\Users\stephen\AppData\Local\JetBrains\PyCharm 2018.3\helpers\pydev\_pydev_imps\_pydev_execfile.py", line 18, in execfile
exec(compile(contents+"\n", file, 'exec'), glob, loc)
File "C:/Users/stephen/Documents/src/testcode/test.py", line 77, in <module>
cli(cmd.split(), obj={})
File "C:\Users\stephen\AppData\Local\Programs\Python\Python36\lib\site-packages\click\core.py", line 722, in __call__
return self.main(*args, **kwargs)
File "C:\Users\stephen\AppData\Local\Programs\Python\Python36\lib\site-packages\click\core.py", line 697, in main
rv = self.invoke(ctx)
File "C:\Users\stephen\AppData\Local\Programs\Python\Python36\lib\site-packages\click\core.py", line 1066, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "C:/Users/stephen/Documents/src/testcode/test.py", line 26, in invoke
config = yaml.load(open(".configrc").read())
FileNotFoundError: [Errno 2] No such file or directory: '.configrc'