В цій статті розглянемо як на Python писати зручну консольну утиліту. Для прикладу напишемо SQL REPL розміром не більше 20 рядків коду. Ми використаємо 4 бібліотеки: Prompt Toolkit, Click, Pygments та Fuzzy Finder.
Python Prompt Toolkit
Для консольних додатків, ця програма — як швейцарський ніж для солдата — вона замінює readline, curses та інші. Давайте її встановимо:
pip install prompt_toolkit
Ми почнемо з простого REPL'у. Зазвичай він приймає текст від користувача, оброблює його і повертає результат. Для нашого прикладу ми побудуємо REPL, який просто буде повторювати введений текст:
from prompt_toolkit import prompt
while 1:
user_input = prompt('>')
print(user_input)
Ось і все, мінімальний REPL у нас вже є. Давайте його трохи поліпшимо.
Історія
Перш за все, давайте додамо історію команд.
from prompt_toolkit import prompt
from prompt_toolkit.history import FileHistory
while 1:
user_input = prompt('>',
history=FileHistory('history.txt'),
)
print(user_input)
Ми додали історію до нашої оболонки. Вона зберігається між перезапусками програми. Можна використовувати стрілки вниз та вгору для переміщення по історії та Ctrl+R для пошуку.
Пропозиції
Також було б непогано додати в нашу оболонку пропозиції команд на основі історії (вперше така функція з'явилась в fish shell). Давайте це й зробимо:
from prompt_toolkit import prompt
from prompt_toolkit.history import FileHistory
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
while 1:
user_input = prompt('>',
history=FileHistory('history.txt'),
auto_suggest=AutoSuggestFromHistory(),
)
print(user_input)
Так, все робиться так просто, всього лише один новий аргумент.
Автодоповнення
Реалізуємо доповнення команд: по натисканню на Tab користувачу показуватиметься список з можливих варіантів.
Як наша оболонка здогадається, що хоче ввести користувач? Ми просто передамо їй список слів, які слід доповнювати. В даному випадку це команди SQL:
from prompt_toolkit import prompt
from prompt_toolkit.history import FileHistory
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
from prompt_toolkit.contrib.completers import WordCompleter
SQLCompleter = WordCompleter(['select', 'from', 'insert', 'update', 'delete', 'drop'],
ignore_case=True)
while 1:
user_input = prompt('SQL>',
history=FileHistory('history.txt'),
auto_suggest=AutoSuggestFromHistory(),
completer=SQLCompleter,
)
print(user_input)
Як бачите, все робиться досить просто і без власних велосипедів.
У нас є працюючий REPL з історією, доповненням та пропозиціями. І все це менш ніж за 10 рядків коду.
Click
Click — набір інструментів для роботи з аргументами командного рядка. Але зараз ми будемо використовувати утиліти, що він надає. Встановимо:
pip install click
Пагінатор
Пагінатор — unix-утиліта, що розділяє текст і відображає його по одній сторінці. Прикладами таких програм є less, more, most. Відображення тексту по сторінці за раз — не гарний тон, а необхідність.
Повернемося до останнього прикладу. Замість використання print()
ми можемо використовувати click.echo_via_pager()
. Ця функція візьме на себе всю роботу по розподілу на виведенню тексту в консоль. Також вона не залежить від ОС, тому буде працювати і на Unix, і на Windows.
from prompt_toolkit import prompt
from prompt_toolkit.history import FileHistory
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
from prompt_toolkit.contrib.completers import WordCompleter
import click
SQLCompleter = WordCompleter(['select', 'from', 'insert', 'update', 'delete', 'drop'],
ignore_case=True)
while 1:
user_input = prompt(u'SQL>',
history=FileHistory('history.txt'),
auto_suggest=AutoSuggestFromHistory(),
completer=SQLCompleter,
)
click.echo_via_pager(user_input)
Редактор
Іншою фічою click є можливість запускати текстовий редактор, коли користувачу потрібно ввести текст. Наприклад, коли запит надто великий.
import click
message = click.edit()
Fuzzy Finder
Ця бібліотека дозволяє показувати користувачу коректні варіанти автодоповнення, в той час як він набрав мінімум тексту. Встановимо:
pip install fuzzyfinder
API цієї бібліотеки дуже простий. Ви передаєте рядок та список можливих варіантів доповнення, а Fuzzy Finder повертає вам список можливих варіантів в порядку відповідності. Наприклад:
>>> from fuzzyfinder import fuzzyfinder
>>> suggestions = fuzzyfinder('abc', ['abcd', 'defabca', 'aagbec', 'xyz', 'qux'])
>>> list(suggestions)
['abcd', 'defabca', 'aagbec']
Давайте додамо його до нашого SQL REPL. Для цього нам потрібно написати власний доповнювач замість стандартного WordCompleter
.
from prompt_toolkit import prompt
from prompt_toolkit.history import FileHistory
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
from prompt_toolkit.completion import Completer, Completion
import click
from fuzzyfinder import fuzzyfinder
SQLKeywords = ['select', 'from', 'insert', 'update', 'delete', 'drop']
class SQLCompleter(Completer):
def get_completions(self, document, complete_event):
word_before_cursor = document.get_word_before_cursor(WORD=True)
matches = fuzzyfinder(word_before_cursor, SQLKeywords)
for m in matches:
yield Completion(m, start_position=-len(word_before_cursor))
while 1:
user_input = prompt(u'SQL>',
history=FileHistory('history.txt'),
auto_suggest=AutoSuggestFromHistory(),
completer=SQLCompleter(),
)
click.echo_via_pager(user_input)
Pygments
Тепер настав час додати підсвітку синтаксису до нашого додатку. Ми ж все таки будуємо SQL REPL, то чому б не виділяти ключові слова SQL?
Pygments — бібліотека для підсвітки синтаксису з вбудованою підтримкою більш ніж трьохсот мов. Підсвітка синтаксису робить ваш додаток зручнішим, адже користувачам простіше помітити помилку, незакриті лапки чи дужки.
Перш за все, встановимо бібліотеку:
pip install pygments
Додамо їх підтримку до нашого додатку:
from prompt_toolkit import prompt
from prompt_toolkit.history import FileHistory
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
from prompt_toolkit.completion import Completer, Completion
import click
from fuzzyfinder import fuzzyfinder
from pygments.lexers.sql import SqlLexer
SQLKeywords = ['select', 'from', 'insert', 'update', 'delete', 'drop']
class SQLCompleter(Completer):
def get_completions(self, document, complete_event):
word_before_cursor = document.get_word_before_cursor(WORD=True)
matches = fuzzyfinder(word_before_cursor, SQLKeywords)
for m in matches:
yield Completion(m, start_position=-len(word_before_cursor))
while 1:
user_input = prompt(u'SQL>',
history=FileHistory('history.txt'),
auto_suggest=AutoSuggestFromHistory(),
completer=SQLCompleter(),
lexer=SqlLexer,
)
click.echo_via_pager(user_input)
Prompt Toolkit має вбудовану підтримку Pygments, тому вам потрібно лише додати один аргумент.
Ще немає коментарів