Создание приложения командной строки оболочки с помощью Python и нажмите

Я использую click (http://click.pocoo.org/3/) для создания приложения командной строки, но я не знаю, как создать оболочку для этого приложения.
Допустим, я пишу программу под названием тест и у меня есть команды, называемые subtest1 и subtest2

я смог заставить его работать с терминала, как:

$ test subtest1
$ test subtest2

но то, о чем я думал, - это оболочка, поэтому я мог бы сделать:

$ test  
>> subtest1  
>> subtest2

Is это возможно с кнопкой?

4 ответов


это не невозможно с помощью click, но для этого также нет встроенной поддержки. Первое, что вам нужно сделать, это сделать ваш групповой обратный вызов вызываемым без подкоманды, передав invoke_without_command=True в декоратор группы (как описано здесь). Тогда ваш групповой обратный вызов должен был бы реализовать REPL. У Python есть cmd framework для этого в стандартной библиотеке. Создание доступных там подкоманд click включает переопределение cmd.Cmd.default, как в фрагмент кода ниже. Получение всех деталей правильно, как help, должно быть выполнимо в несколько строк.

import click
import cmd

class REPL(cmd.Cmd):
    def __init__(self, ctx):
        cmd.Cmd.__init__(self)
        self.ctx = ctx

    def default(self, line):
        subcommand = cli.commands.get(line)
        if subcommand:
            self.ctx.invoke(subcommand)
        else:
            return cmd.Cmd.default(self, line)

@click.group(invoke_without_command=True)
@click.pass_context
def cli(ctx):
    if ctx.invoked_subcommand is None:
        repl = REPL(ctx)
        repl.cmdloop()

@cli.command()
def a():
    """The `a` command prints an 'a'."""
    print "a"

@cli.command()
def b():
    """The `b` command prints a 'b'."""
    print "b"

if __name__ == "__main__":
    cli()

Я пытался сделать что-то похожее на OP, но с дополнительными опциями / вложенными под-под-командами. Первый ответ с использованием встроенного модуля cmd не работал в моем случае; возможно, с некоторыми более возиться.. Но я только что наткнулся на click-shell. У меня не было возможности протестировать его широко, но до сих пор он работает точно так, как ожидалось.


Я знаю, что это супер старый,но я работал над решением fpbhb для поддержки опций. Я уверен, что это может использовать дополнительную работу, но вот основной пример того, как это можно сделать:

import click
import cmd
import sys

from click import BaseCommand, UsageError


class REPL(cmd.Cmd):
    def __init__(self, ctx):
        cmd.Cmd.__init__(self)
        self.ctx = ctx

    def default(self, line):
        subcommand = line.split()[0]
        args = line.split()[1:]

        subcommand = cli.commands.get(subcommand)
        if subcommand:
            try:
                subcommand.parse_args(self.ctx, args)
                self.ctx.forward(subcommand)
            except UsageError as e:
                print(e.format_message())
        else:
            return cmd.Cmd.default(self, line)


@click.group(invoke_without_command=True)
@click.pass_context
def cli(ctx):
    if ctx.invoked_subcommand is None:
        repl = REPL(ctx)
        repl.cmdloop()


@cli.command()
@click.option('--foo', required=True)
def a(foo):
    print("a")
    print(foo)
    return 'banana'


@cli.command()
@click.option('--foo', required=True)
def b(foo):
    print("b")
    print(foo)

if __name__ == "__main__":
    cli()

теперь есть библиотека под названием click_repl что делает большую часть работы для вас. Думал, что поделюсь своими усилиями, чтобы это сработало.

одна трудность заключается в том, что вы должны сделать конкретную команду repl command, но мы можем перепрофилировать подход @fpbhb, чтобы разрешить вызов этой команды по умолчанию, если другой не предоставляется.

это полностью рабочий пример, который поддерживает все параметры щелчка, с историей команд, а также возможность вызовите команды непосредственно без ввода REPL:

import click
import click_repl
import os
from prompt_toolkit.history import FileHistory

@click.group(invoke_without_command=True)
@click.pass_context
def cli(ctx):
    """Pleasantries CLI"""
    if ctx.invoked_subcommand is None:
        ctx.invoke(repl)

@cli.command()
@click.option('--name', default='world')
def hello(name):
    """Say hello"""
    click.echo('Hello, {}!'.format(name))

@cli.command()
@click.option('--name', default='moon')
def goodnight(name):
    """Say goodnight"""
    click.echo('Goodnight, {}.'.format(name))

@cli.command()
def repl():
    """Start an interactive session"""
    prompt_kwargs = {
        'history': FileHistory(os.path.expanduser('~/.repl_history'))
    }
    click_repl.repl(click.get_current_context(), prompt_kwargs=prompt_kwargs)

if __name__ == '__main__':
    cli(obj={})

вот как это выглядит, чтобы использовать REPL:

$ python pleasantries.py
> hello
Hello, world!
> goodnight --name fpbhb
Goodnight, fpbhb.

и использовать подкоманды командной строки напрямую:

$ python pleasntries.py goodnight
Goodnight, moon.