27 июня Яндекс проводил онлайн-хакатон по разработке навыков для Алисы.

Решил и я принять в нем участие. Ранее навыки для Алисы я уже делал, но хостил их все на Google App Engine. Тут же я решил изучить что-то новое в рамках Хакатон. Яндекс активно продвигает свои Функции в Яндекс.Облаке для разработки навыков. Для навыков они бесплатны (правда, бесплатно не всё).

Да и Google App Engine теперь требует подключить аккаунт для оплаты, чтобы приложение на сервер залить.

Решил я попробовать навык в Яндекс.Облаке разместить. К тому же, решил я, навык должен быть простым — чтобы за день в рамках Хакатона успеть сделать. Тут Функции в Облаке очень подходят — к сторонним сервисам обращаться не надо (они в функциях платные), данные можно хранить в самом навыке, внешняя БД не нужна.

Раньше я старался делать полезные навыки — чтобы оплатить парковку голосом, например (в Яндекс.Навигаторе) или узнать, когда автобус/троллейбус/трамвай придет на ближайшую остановку. Это требовало интеграций со сторонними сервисами, долгой разработки, а Яндексу, судя по премии Алисы, больше игровые-развлекательные навыки по душе. Потому в этот раз я решил делать игру.

Для работы с Функциями предлагается либо все делать локально, а потом файлы в Функции загружать, либо же редактировать файлы в онлайн-редакторе. Онлайн-редакторы я люблю ;), потому сначала попробовал воспользоваться им. Однако после двух-трех-четырех правок и сохранения новых версий, от этой идеи решил отказаться — уж очень неудобно, что нажав на Сохранить, тебя редиректит на другой экран. Итого каждая правка — это куча лишних кликов.

С командой строкой жить чуть проще. Но файлы нужно каждый раз нужно в zip добавлять, а лишь потом в Облако грузить. Руками — неудобно.

Так уж сложилось, что моя IDE — это Sublime Text 3. Недавно Google отказался от Google App Engine Launcher и с ним остался единственный вариант — загрузка файлов через командную строку. Тогда-то я и узнал о существовании build systems в Sublime Text — нажимаешь Ctrl/Cmd+B и Sublime выполняет нужную тебе команду. Для GAE я тогда сделал набор команд, решил, что и тут что-то подобное нужно.

Сначала была сделана функциональность просто для загрузки файлов.

Для GAE я делал так, чтобы передаваемые параметры (а именно — название проекта) читалось из файла проекта Sublime Text. Тут же для экономии времени название функции, точка входа и остальные параметры просто зашиты в build system. Не очень хорошо, но для моих целей подходило.



Но если все тестировать на боевом сервере, то и логи надо как-то удобно смотреть. Потому для загрузки и отображения логов было добавлена отдельная команда.

Увы, если логи просто отобразить, то ориентироваться в них довольно сложно.

Пришлось немного подшаманить с командой (чтобы unicode-строки отображались корректно — но и то, работает это не всегда), с самим кодом (чтобы JSON выводить в читаемом виде):

    logging.getLogger().setLevel(logging.DEBUG)
    logging.debug('REQUEST: ')
    for line in json.dumps(event['request'], indent=4).split('\n'):
        logging.debug(line)

и создать отдельный файл синтаксиса, чтобы подсвечивать ошибки в логе.



Отдельная удобная фича — Sublime Text умеет подсвечивать и саму строку, если нашла ее в коде.

Итого получилось следующее —

Файл Yandex Cloud.sublime-build
// Install Yandex CLI - https://cloud.yandex.ru/docs/cli/quickstart#install
//
// http://www.sublimetext.com/docs/3/build_systems.html
// https://cloud.yandex.ru/docs/functions/operations/function/version-manage#version-create
{
    "file_patterns": ["*.py"],
    "syntax": "Packages/User/YCLog.sublime-syntax",
    "file_regex": "File \\\"/function/code/(...*?)\\\", line ([0-9]*)", 
    "variants":
        [
            {
                "name": "Upload",
                "shell_cmd": "zip -u -0 yc_upload.zip *.py && yc serverless function version create --function-name=my-function-name --runtime=python27 --entrypoint=main.handler --memory=128m --execution-timeout=2s --source-path=yc_upload.zip",
            },
            {
                "name": "Logs",
                "shell_cmd": "printf '%b\n' \"\\$(yc serverless function logs alice-guess-the-language)\""
            }
        ]
}


Файл YCLog.sublime-syntax
%YAML 1.2
---
# See http://www.sublimetext.com/docs/3/syntax.html
name: YC Log
file_extensions: [log]
scope: source.example-c
contexts:
  main:
    # Request identifiers
    - match: '\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} (START|END|REPORT) RequestID: .*'
      scope: storage.type.string.c

    # Dates
    - match: '\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}'
      scope: comment.line.c

    - match: '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{2,3}Z    [0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}'
      scope: comment.line.c

    # Log level
    - match: '\[(INFO|DEBUG)\]'
      scope: comment.line.example-c

    # Log level
    - match: '\[(ERROR|WARNING)\]'
      scope: keyword.control.flow.break.c


    # Strings begin and end with quotes, and use backslashes as an escape
    # character
    - match: '"'
      scope: punctuation.definition.string.begin.c
      push: double_quoted_string


  double_quoted_string:
    - meta_scope: string.quoted.double.example-c
    - match: '\\.'
      scope: constant.character.escape.example-c
    - match: '"'
      scope: punctuation.definition.string.end.example-c
      pop: true


Редактировать код Функций в Яндекс.Облаке стало гораздо приятнее.

P.S. Мой навык — игра Угадай язык.