Stimulator

機械学習とか好きな技術話とかエンジニア的な話とかを書く

poetryを利用した動的なバージョン管理とGitHub ActionsによるPyPIへのrelease

はじめに

この記事を読んで出来る事

  • poetryによる外部モジュールバージョン管理
  • poetry-dynamic-versioningによる動的なバージョン付与
  • GitHub Actionsを利用したPython周りの基本的なCI/CD設定
  • GitHubのReleaseタグ付与をTriggerとしたPyPIへのアップロード

今後私がPythonで何かライブラリ作ろうと思ったらこれを実施するぞというメモです

 

 

poetryによるモジュールバージョン管理

バージョンをGitHubのタグで管理したい事の方が多いはず。
setup.pyを利用する場合は、一般的にsetuptools_scmを使うが、poetryはsetup.pyのようにbuild時にスクリプトを組み込むのは基本できないので、poetryの実装にpatchを当てる形で動的にバージョンを取得する。そのためのライブラリが以下のpoetry-dynamic-versiong。patchを当てている箇所はここ
github.com

git repository以外にもsubversionに対応していたり、独自のバージョンフォーマットも扱えるので、基本的にはこちらで問題なさそう。

 
pyproject.tomlを書いていく。

[tool.poetry]
name = "module_name"
version = "0.0.0"  # using poetry-dynamic-versioning
description = "sample tool"
authors = ["vaaaaanquish <6syun9@gmail.com>"]
license = "MIT"
readme = "README.md"
homepage = "https://github.com/vaaaaanquish/module_nam"
repository = "https://github.com/vaaaaanquish/module_nam"
documentation = ""

[tool.poetry-dynamic-versioning]
enable = true
style = "pep440"

[tool.poetry.dependencies]
python = "^3.7"
pandas = "^0.25.0"
matplotlib = "*"

[tool.poetry.scripts]
my-script = 'module_name:main'

[build-system]
requires = ["poetry"]
build-backend = "poetry.masonry.api"

 
githubのrelease tagで 「v0.0.1」 というフォーマットを採用していれば、以下を追記するだけで良い

[tool.poetry-dynamic-versioning]
enable = true
style = "pep440"

tool.poetry配下のversionが残る事だけが気がかりだが、「0.0.0」のようにしておけば問題にはならなそう。現状外すとpoetry build時にエラーとなる。

「v0.0.0」以外のformatを使っている場合は、READMEのConfigurationを参考にpattern、もしくはformatを設定する。

tag v0.0.2が打たれたrelease
tag v0.0.2が打たれたrelease

 
poetry-dynamic-versiongを使う場合には、poetryのdependenciesとしてではなく、poetryと同じレイヤーのPythonでinstallする必要がある。

pip install poetry
pip install poetry-dynamic-versioning

# poetry install
poetry update
poetry build

出力は以下のようになり、github release tagと連動してmodule生成が出来ている事が確認できる。

Building module_name (0.0.2)
 - Building sdist
 - Built module_name-0.0.2.tar.gz

 - Building wheel
 - Built module_name-0.0.2.whl


 

PyPIへのアップロード

手元からPyPIにpushするにはpoetry publishを使う方法が一般的。
以下のような形でpublishすれば良い。

poetry publish --build --username {hoge} --password {piyo}

基本的にはあまり手元からやりたくないので、GitHubにmasterマージされたりしたタイミングで自動でPyPIにアップロードしてもらいたい。後述する。


 

GitHab Actionsを用いたCI/CD

GitHub Actionsの設定ファイルを書いて、release tagをトリガーにして動的にアップロードするようにする。

# This workflows will upload a Python Package using Twine when a release is created
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries

name: Upload Python Package

on:
  release:
    types: [created]

jobs:
  deploy:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: Set up Python
      uses: actions/setup-python@v1
      with:
        python-version: '3.x'
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install poetry poetry-dynamic-versioning twine
    - name: Build and publish
      env:
        TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
        TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
      run: |
        poetry publish --build --username $TWINE_USERNAME --password $TWINE_PASSWORD

releaseタグが打たれた事をトリガーにしてmasterのコードをPyPIにpushするやつ。.github/workflows/python_publish.ymlにしてrepositoryに含める。
repo内の Settings > Secrets からユーザ名とパスワードを追加する。

repo内の Settings &gt; Secrets からユーザ名とパスワードを追加する。
repo内の Settings > Secrets からユーザ名とパスワードを追加する。

 

その他GitHubでやること

flake8、yapfなりのフォーマッタのCIを別途作る。
Settings > Branch protection rule からCIの必須化とmasterにpushできない設定をしておく。

Branch protection rule
Branch protection rule


PRからGitHub Actionsのbuildタスクが通らないとmergeできない事を確認する。

PRからGitHub Actionsのbuildタスクを確認
PRからGitHub Actionsのbuildタスクを確認

これで一連の設定はおわり。

setup.cfgやsetup.py、pipfileで書いていたものがほぼ不要になるし、PyPIのREADMEが勝手にMarkdownレンダリングされたものに変わっていたり、本当にやることが減って何かと便利。pipenvより早い、PEP 518 で規格化されている。良い感じ。
 

Rustでterminalのwindow sizeを取得する

はじめに

RustでCLIツールを作る際にターミナル等のwindow size変更に応じて表示を変更したい場合がある。

その際に画面サイズの取得について調査したログ。

 

 

画面サイズの取得

画面サイズの取得には、libcのioctlを利用し、TIOCGWINSZをシステムコールする。
Cargo.tomlにはlibcを追記する。

[dependencies]
libc = "0.2"

 
ioctlにはファイルディスクリプタを渡す必要があり、libc::STDOUT_FILENOが使えるが、アプリケーション側にSIGWINCHが上手く送られない場合(CLIツール等を作成している場合)に、Terminalを制御するウィンドウのサイズ変更に動的に対応できない。そのため"/dev/tty"を読む方法も用意する。

use libc::{ioctl, winsize, TIOCGWINSZ, STDOUT_FILENO};
use std::{mem, fs::File, os::unix::io::IntoRawFd};
use std::{thread, time};


pub fn terminal_size() -> Option<winsize> {
    // STDOUT_FILENOか/dev/ttyを利用する
    let fd = if let Ok(file) = File::open("/dev/tty"){
        file.into_raw_fd()
    }else {
        STDOUT_FILENO
    };

    // ファイルディスクリプタに対してTIOCGWINSZをシステムコール
    let mut ws: winsize = unsafe { mem::zeroed() };
    if unsafe { ioctl(fd, TIOCGWINSZ, &mut ws) } == -1 {
        None
    } else {
        Some(ws)
    }
}

// 1秒毎に画面サイズを表示
fn main(){
    loop{
        match terminal_size(){
            Some(ws) => println!("h:{},w:{}", ws.ws_row, ws.ws_col),
            None => println!("failure")
        }
        thread::sleep(time::Duration::from_millis(1000));
    }
}

 
実際の実行例は以下のようになる。

f:id:vaaaaaanquish:20200323000859g:plain
vim terminal上での実行例


 

調査ログ

2020/03/23に調査したログ。

terminalサイズの取得を行うライブラリを調べると以下が出てくる。

大体実装は同じだが、全てSTDOUT_FILENOを使っている。
クロスプラットフォーム観点でWindows対応の参考になる。

rustのターミナルだと以下が出てくる

NixSignalやlibcを使って画面サイズが変化したSignal(SIGWINCH)をキャッチして画面サイズ変更に対応している。そういう方法もあるか。

 
最終的に参考にしたのは以下のクロスプラットフォームターミナルの実装。
github.com

 
以下のブログやissueも読んだ。
hermanradtke.com
github.com

機械学習モデリングの広辞苑的書籍「Kaggleで勝つデータ分析の技術」が良かったので筆者に媚を売る

- はじめに -

当ブログでは恒例になっている、献本されたので筆者に媚を売るシリーズです。

今回は10/9に発売予定の「Kaggleで勝つデータ分析の技術」という書籍なんですが、既に発売前にしてAmazonベストセラー1位。豪華著者陣とKaggleにおいては日本有数の起業と言っても過言ではない、DeNA株式会社の豪華レビュワー。筆者がブログを書いていたりu++さんがめちゃくちゃ丁寧な書評を書いていたり、Kaggle Grand Master各位の薦めツイートも出てきた段階で、もう私が媚を売る必要すらないと思いますが、良かったので感想だけでも残しておければと思います。

Kaggleで勝つデータ分析の技術

Kaggleで勝つデータ分析の技術

端的に言えば、テーブルデータにおける機械学習モデリング、データ分析の広辞苑+αな書籍で、筆者らがブログやSNS等で述べている通り「暗黙知を洗い出す」ような良書でした。

 

 

- どんな内容だったか -

前置きとして「広辞苑的な書籍」と表現した通り、Kaggle、データ分析における重要なワードとその適切な範囲までの解説がセットで広く書かれた書籍です。

詳細なアルゴリズムや数理的な背景までは説明していませんが、その分幅広く適切な深さの解説、コラム、ソースコードや過去コンテストの実例がついています。

f:id:vaaaaaanquish:20191005205334j:plain:w400
数式を最小限にしつつも実例を交えて各評価指標やアルゴリズムを紹介

コードや背景については分量は少なめですが、私が読んだ技術書の中でもズバ抜けて参考文献を非常に丁寧にまとめており、気になる所が後追い出来るようになっているだけでなく、ソースコードGitHubで公開されるとの事なので、基本的に読んだ後に手を動かして体感する事も可能そうです。


また、「決定木の気持ち」「過去コンペで話題になった手法」といったKaggler同士の中でのミームについても解説が入っています。Kaggle自体のコミュニティ性から言っても、技術情報や共通のミーム、周囲の戦略の収集は、コンテストに挑むにあたって重要な事でもあるので、今からでも書籍を読んで把握しておいて損はないでしょう。

f:id:vaaaaaanquish:20191005210157j:plain:w400
基本的なコンテストでの分析フロー等が図付きで解説されている

 
私自身、機械学習やデータ分析のブログやSNS投稿、スライド、発表や同人誌などで情報収集をする事は多々ありますが、「あなたの言ってる定義微妙に違ってない…?」と思う事は少なくありません(私が毎回正解を言っている確信がある訳でもありません)。そういった機械学習モデルや統計モデルを活用するコンテストやプロジェクトが一般的に広まってきたという段階で、各ワードに対する認識の共通化を図れる良い書籍です。

この一冊とGoogle翻訳があれば、Kaggle上のDiscussionやKagger Slack、オフラインイベントでの会話をかなり的確に把握できるようになるとも感じます。そういった場で、今まで曖昧に流してきたワードがあるような方にオススメの一冊です。

 
書籍のまえがきには、「ビジネス的な側面について触れない」とも書かれており、事実内容もその通りではありましたが、弊社の機械学習チームではこの書籍に出てくるような単語を前提の共通ワード、コンテキストとして扱う場面が多くあるなと読んでいて思いましたし、コンペに出る事を目的としない場合でも自力を上げる知識が多く含まれています

 
個人的には、これほど「タスクの評価」「モデルの評価」「Validationの設計」について広く用語がまとまった書籍は他に見たことがありませんし、実務でモデルの評価について考えている人はここに書いてある事は前提として一通り把握しておくべきだとも思います。実務だとここに事業的な評価やシステムの話が入ってくると思いますが、まず前提として知っておかないとモデルも構築できないと思います。

 
私自身、これを読みながら以下のようなツイートをしており、書籍と公開されるGithubを合わせれば、自然言語処理100本ノック、画像処理100本ノックに続く、手を動かして機械学習を広く知るコンテンツの1つともなりそうです。


知の高速道路が整備されるのは良いことです。

 

- 読んだらKaggleで勝てるか -

読んだだけでは勝てそうにありません。

 
この書籍は広辞苑のように大きく知識を広げ、ともすればコンテスト中に逆引きできる良書籍ですが、実際には多くの知見、バックグラウンドがその背景に詰め込まれた書籍でもあります。

例えば、「基本的なライブラリ(numpy, pandas, sklearn, ..., etc)の使い方」から「予測確率」「次元削減」といったワード、果ては「教師あり、なし学習」辺りまで、機械学習、統計の教科書的な知識は前提知識として必要としてきます。プラスでモデルの背景が分かっていないと「ん?」となる部分もいくつかあります。これは、それらがきちんとしている著者らが書いたからこそ正確かつ客観性、再現性のある書籍になっているという話でもあります。「舟を編む」ではないですが、これ程の機械学習、データ分析の範囲を適切な表現と深さで書いている本はほとんどありませんし、流石と言わんばかりです。
 
そういった事もあって、前提知識がないと出来ない事、例えば「アルゴリズムわからないので機械学習モデルどれ選べばいいか分からない」「可視化の結果どの方向性に行くのが正しいか分からない」という課題を解決してくれる書籍では無いので、「これでKaggle始めよう!」という人には前提として他にもいくつかの書籍やブログと一緒に読んでいく必要があるかなと思いました


全体が薄い訳ではなく、「xgboostのアルゴリズム、チューニング」「ベイズ最適化」については大きく3~5ページが取られています。筆者としてもKaggleで重要なBoostingとチューニングに限っては丁寧に解説したかったのだとも思います。特に近年では業界内でも有名なブログや企業、学術機関からも、不要なパラメータのチューニングやLeakageを代表に、コンテストでは一般的にバッドプラクティスとされる話が多く出回るようになりましたし、注力して書かれていると感じます。

逆に言うと、このxgboostの丁寧な解説と同量またはそれ以上のバックグラウンドが、他全ての章に書かれた技術についても本来ある訳でもあります。この書籍から分からなかった単語をググって、勉強して、コンテストに出てという形を想定した足掛かり的な書籍でもあると感じました。

 
当たり前ですが、実際に手を動かして、体感して、コンテストに時間を捧げてsubmitしない事には勝てないので、やっていきの気持ちが大事です。というか知ってるだけで勝てるなら私だってk…

 

- どんな書籍と読むと良さそうか -

基本的には、書籍内の参考文献が非常に優れておりその中のスライドやブログ、書籍を参考にすると良いと思います。実際この付録に書籍の3割くらいの価値が詰まっていると思います。

実際この付録のURL一覧だけでGitHubスター100はあげたいレベルです。

 
中でも、Kaggleでプラスになると考えて抜粋すると、「特徴量エンジニアリング」辺り。「データ解析のための統計モデリング入門」は一緒に買っておくと間違いないと思います。

機械学習のための特徴量エンジニアリング ―その原理とPythonによる実践 (オライリー・ジャパン)

機械学習のための特徴量エンジニアリング ―その原理とPythonによる実践 (オライリー・ジャパン)

はじパタやプロフェッショナルシリーズも悪くないですが、上の2つは直接的にKaggleや分析に効くいい本だと思います。
 
参考文献外だと「仕事ではじめる機械学習」「機械学習のエッセンス」はこの本の支えになる知識が多いと感じました。持ってなければ是非一緒に。

仕事ではじめる機械学習

仕事ではじめる機械学習

 
最近は書籍も多い(献本も多い)ですが、低いレベルのまとめ的な書籍も少なくないです。そういう意味でも「Kaggleで勝つデータ分析の技術」の厳選された参考文献と過去コンテスト情報は読んでおくべきだと思います。


 

- おわりに -

 
数年前、偶然同僚にKaggle Grand Masterが居たので気になってKaggleを始めましたが、まさかこんな良質で立派な書籍が出たりする程大きくなるとは…正直想像していませんでした。しかも、Expertになってからボーッとしてたら、数年で幾人もの人に追い抜かれ、何も言えない人になってしまいました。悔しいです。

DeNA株式会社に蔓延する攻撃的なSNSアカウントから「Expertが書いてる書評だから無意味」と言われそうなので、ブログはこの辺にしておきます。

 
媚を売られる側になれるよう、私も頑張ります。


Kaggleで勝つデータ分析の技術

Kaggleで勝つデータ分析の技術

 

追記:2019/10/05
らしいです。これは失礼しました。

xonshのEnvironment Variablesの全て

- はじめに -

以下の記事でxonshのEnvironment Variablesの大体の日本語訳を書いた。

xonshrcを書く - Stimulator

しかし、上記の記事は2017年次の物かつ、要約的な記事のため、より詳細に見て開発に以降できるよう本体へのコードや掴みどころをメモした記事を書いておく。

本家ドキュメントは下記なので参照のこと。

xon.sh
 

 
 

- Windowsに関連するもの -

Windowsの人はまずここ見て設定。
xon.sh

$ANSICON

Windowsのcmd.exe(コマンドプロンプト)において、ANSI escape sequencesを表示するためのアプリケーションであるansicon*1を利用するかどうかの設定。ansiconを利用する場合にTrueにしておくと、$TITLEをプロンプトのタイトルに設定するようになる。実装でいうと以下のような感じ。

import ctypes
ctypes.windll.kernel32.SetConsoleTitleW(env.get("TITLE"))

 

$INTENSIFY_COLORS_ON_WIN

Windowsのcmd.exeを利用する時に、colorを見やすくするかどうかのフラグ。青色がシアンに置き換わったりする。
そもそも近年でcolor style自体がかなり柔軟になったので、不要かも。細かく指定しない場合には使える。

 

$WIN_UNICODE_CONSOLE

Trueであれば、WindowsでのUnicodeサポートを有効にする。win_unicode_consoleなる外部ライブラリをimportしているだけ。以下必須。

pip install win_unicode_console

 

$PATHEXT

環境変数のPATHを見に行った時に、実行可能ファイルとして判定する拡張子のリスト。
".EXE"のように大文字で指定する。xonsh上でWINDOWS判定されていれば、[".COM", ".EXE", ".BAT", ".CMD"]がデフォルトで入るので、それ以外に必要であれば追加する。

 

- xonsh本体の動作に関するもの -

$XONSHRC

rcファイルへのpathのリスト。['~/xonshrc', '~/.config/xonsh/rc.xsh']のように複数あれば前方から複数読み込まれるし、ファイルがなければ読み込まれず終わる。

$VIRTUAL_ENV

アクティブなPythonへのPath。pythonやpipコマンドが指すところでもあり、xonsh用のpythonバージョン管理ツールであるvoxも参照するpathになる。
デフォルトでは変数が設定されていない状態なので注意。

 

$XONSH_HISTORY_SIZE

historyのサイズの指定。「(8128, "commands")」「"8128 commands"」のようなsetか文字列で、保存するコマンド数(commands)、保持する履歴ファイル数(files)、許可される秒数(s)、バイト数(b)の単位のどれかと値を指定する。

$XONSH_HISTORY_BACKEND

historyの保存形式。デフォルトはjsonだが、sqlを設定すればsqliteをバックエンドに選べる。設定は以下を見ると良い。
https://xon.sh/tutorial_history_backend.html

 

$SUPPRESS_BRANCH_TIMEOUT_MESSAGE

Trueであれば、VC_BRANCH_TIMEOUTに設定された時間を過ぎてもPROMPTの文字列、branch名、色の生成が終わって無かった場合に強制的にタイムアウトとしメッセージを表示する。PROMPTに{curr_branch}でブランチ名を表示させようとするが、git repoが巨大で…という時に起こる。

 

$VC_BRANCH_TIMEOUT

gitのブランチ名などをPROMPTに表示する時にタイムアウトする秒数。デカいrepo触ったりする時に設定しておくと良い。

 

$VC_HG_SHOW_BRANCH

Trueであれば、Mercurialのブランチを表示する。gitでなくMercurial使っている場合はこちら。

 

$UPDATE_OS_ENVIRON

Trueであれば、xonshのEnvが追加、削除された場合にその変更をos側の環境変数にも加える。デフォルトはfalse。
中身は「os.environ.update」を少し工夫して呼んでいるだけなので、個別にos側に追加したい時は不要。WindowsとかOSの環境変数が影響の大きい環境では使える場合があるかも。

 

$VI_MODE

Trueであれば、シェルの操作がvi風になる。iでinsert、escでnormalモードになるなど。
実装自体はprompt toolkitの機能で、現行のモードを判定するfilterとして実装されている以下の部分から追っていくと良い。
https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/prompt_toolkit/filters/cli.py

 

$XDG_DATA_HOME

英語では「Open desktop standard data home dir」となっているが、普通にセッションログやスクリプトの実行ログが入る場所へのpathというだけ。以下のXONSH_DATA_DIRに近いが、こちらのPathはxonsh用ではなく、一般的な環境全体用。
すこし踏み入ればpygmentsを使った実装になっていることが分かるが、難しい事はほとんどしておらず整備もあまりされていない印象。pygmentsすごい。

 

$XONSH_DATA_DIR

セッションログやスクリプトの実行ログが入る場所へのpath。
デフォルトでは「$XDG_DATA_HOME + "/xonsh"」 が設定されている。

 

$XONSH_CACHE_EVERYTHING

Trueであれば、全てのコード(コマンド含め)をキャッシュに保存する。
ここで言っているキャッシュというのは、historyではなく、そのセッションにおけるキャッシュで、できるだけコンパイルして保存しておいて次回のコマンド実行を早めるというもの。

 

$XONSH_CACHE_SCRIPTS

Trueであれば、スクリプト実行をキャッシュに保存する。
機構自体は上記のXONSH_CACHE_EVERYTHINGと同じだが、こちらはexecを通るようなスクリプトを実行した場合のキャッシュ。

 

$XONSH_TRACEBACK_LOGFILE

XONSH_SHOW_TRACEBACKがTrueの場合に、ログファイルとして残す先。ファイル名か不要の場合はNoneにしておく。

 

$XONSH_STORE_*

Trueであればhistoryに標準入出力を保存する。

$XONSH_STORE_STDIN

xonshの!()や![]オペレータを使って実行されたものをhistoryに保存する。

$XONSH_STORE_STDOUT

stderrやstdoutをhistoryに保存する。

$XONSH_DATETIME_FORMAT

ログやhistoryなど多くの場所で使われるdatetimeのフォーマット。デフォルトでは「"%Y-%m-%d %H:%M"」。
利用先は多いが、実装はtools.pyの中に収まっているだけでシンプル。

 

$XONSH_DEBUG

デバッグモードの指定。1であればimport情報、2であれば入力変換、コマンド置換の情報、3以上であればPLY解析メッセージと情報が増えていく。
よくissueを立てると「$XONSH_DEBUG=3 で一回ログ出して見て」とよく言われたりする。
実装自体は、参照されている部分が多いので変更は大変そうだが、追加はlogging足すだけなので参考に。

 

$XONSH_ENCODING

xonshのサブプロセスで利用されるエンコーディング。デフォルトでは「sys.getdefaultencoding()」が入る。
I/O制御で使われているTeeクラス、入力を作るreadline辺りが主に利用している。

 

$XONSH_ENCODING_ERRORS

Pythonエンコーディングエラーが出た時の処理。多くの場所で共通して使われている。
入る値は以下を参考に。
https://docs.python.org/3/library/codecs.html#error-handlers

 

$XONSH_PROC_FREQUENCY

連続したパイプラインを実行する時、キューを読み込むためにxonshプロセススレッドがスリープする秒数。
スリープタイムというよりは、コマンドを連続で実行した時、スレッド同士のキューが詰まった場合にタイムアウトする時間のイメージ。

 

- コマンド補完に関するもの -

$UPDATE_COMPLETIONS_ON_KEYPRESS

Trueであれば、キー入力時に毎回補完候補を出すようになる。例えば補完であればTABを押さなくてもキー入力毎に候補を表示できるようになる。
PROMPTの表示を評価するUPDATE_PROMPT_ON_KEYPRESSも別にあるので混同に注意。

f:id:vaaaaaanquish:20180622142703g:plain
キー入力時評価の例

実装自体はprompt toolkitの機能で、xonshはこの変数をフラグとしてptkの引数となるcomplete_while_typingに投げているだけである。その仕組み自体を把握するにはptkを追う必要がある。詳細は以下。
https://python-prompt-toolkit.readthedocs.io/en/latest/pages/asking_for_input.html?highlight=complete_while_typing#complete-while-typing

 

$AUTO_SUGGEST

Trueの時にfish shellのようなグレーアウトの補完候補を出す*2

f:id:vaaaaaanquish:20190824001450p:plain
AUTO_SUGGEST

この機能はxonshのコアライブラリであるpython prompt toolkit内の実装なので、そちらを参考にすると良い。
https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/docs/pages/asking_for_input.rst#auto-suggestion

表示されている補完候補を確定するときは「右矢印」「ctrl+e」がデフォルトで設定されている。変更する場合は以下が参考になる。
xonsh[ptk]で、Suggestionを確定するキーバインドを設定する - Qiita

下記のAUTO_SUGGEST_IN_COMPLETIONSとも関連しており、両者がTrueになっている場合、AUTO_SUGGESTで表示される候補をTab補完から除く等の機能があるので設定はよしなに。

 

$AUTO_SUGGEST_IN_COMPLETIONS

Trueの時、Tabキーで補完候補を表示する。$UPDATE_COMPLETIONS_ON_KEYPRESSがTrueの時は、Tab以外のキーでもキー入力時に補完候補が動的に表示される。

AUTO_SUGGEST同様、xonshというよりはpython prompt toolkitの機能であり、そちらを呼び出すかどうかのフラグにあたる。
https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/docs/pages/asking_for_input.rst#autocompletion

 

$COMPLETIONS_DISPLAY

Pythonのコードの補完を表示するか、もしくは表示形式をどうするか。

none or false

表示しない。stringの"none"や"false"でも良いし、PythonのNoneでも、boolのFalseでも良い。

single

1列で補完候補を表示する。stringで"single"。

f:id:vaaaaaanquish:20190824214546p:plain
$COMPLETIONS_DISPLAY = "single"

multi or true

複数列で表示する。デフォルト値。stringで"multi"、"true"とするか、boolのTrueを指定する。

f:id:vaaaaaanquish:20190824214823p:plain
$COMPLETIONS_DISPLAY = "multi"

readline

GNU Readlineの動作を再現した表示にする。stringの"readline"。

f:id:vaaaaaanquish:20190824214905p:plain
$COMPLETIONS_DISPLAY = "readline"


引数の型が柔軟なのは歴史的経緯。こちらはPythonの補完のみでbashの補完はこのEnvに依存していないので注意。

 

$COMPLETIONS_MENU_ROWS

補完を出す行数の指定。stringの"multi"だと5行で横幅最大まで補完を省略して表示する。int値を指定しても良い。

 

$COMPLETION_IN_THREAD

補完をasync/awaitで表示する。
実装自体はprompt toolkitのAppの引数にフラグを入れているだけで、非同期補完の詳細を見るにはptkのasync_promptの実装を見ると良い。
https://python-prompt-toolkit.readthedocs.io/en/master/pages/asking_for_input.html#asynchronous-completion
https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/prompt_toolkit/shortcuts/prompt.py#L909

 

$COMPLETION_QUERY_LIMIT

補完メニューの表示件数。最大件数では無く、これを超えたら補完が表示される閾値のイメージ。
xonshやprompt toolkitの機能というよりは、GNU Readlineのrl_completion_query_itemsをglobal変数として入れておくという実装なので、詳しくは以下を読む。
GNU Readline Library - Programming with GNU Readline

 

$COMPLETIONS_CONFIRM

タブ補完メニューが表示されている時、FalseだとEnterでコマンドを直接実行、TrueだとEnterでコマンドを確定のみして確認状態にする。
実装はprompt toolkitのkeybindにfilterを設定しているので、理解したければptkのkeybindingsを理解してからが良い。

 

$BASH_COMPLETIONS

bashのcompletionを利用するために、bash_completionスクリプトへのPATHを設定するための変数。sshの補完やgitの補完が含まれるため、基本的には必須だと思う。
listかtupleで最初に有効だったものが利用される。

例えばMacではbrewbashの補完が導入できる。

brew install bash-completion2

こちらは、"/usr/local/share/bash-completion/bash_completion" にファイルが配置される。デフォルトでBASH_COMPLETIONSにこのpathが入っているはずなので、基本的にはインストールだけで設定できるはず。

デフォルトだといくつか設定されており、それらを走査する実装になっているため、もし一意に絞れるようであれば絞るかソートで前に持ってきておくと良い。

 

$COMPLETIONS_BRACKETS

Trueの時、シェル入力時にPythonの括弧の補間を有効にする。実装自体はシンプルなので読めば大体分かると思う。

 

$FUZZY_PATH_COMPLETION

Trueであれば、pathをTab補完する時にfuzzyに補完する。あいまい補完。

> $FUZZY_PATH_COMPLETION = True
> ls desko 
./Desktop

具体的な実装としてはlevenshteinを使っており、以下を見れば良い。
https://github.com/xonsh/xonsh/blob/master/xonsh/tools.py#L854

 

$GLOB_SORTED

Trueであれば補完の結果をソートしてから表示する。

 

$SUBSEQUENCE_PATH_COMPLETION

Trueであれば、pathの補完において ~/w/t のような入力でも拡大解釈されて ~/work/tmp も候補に上がるようになる。
ディレクトリが長い時に雑に打って移動する時に使える。

 

$SUGGEST_COMMANDS

Trueであれば、無効なコマンドを入力した時に「もしかして?」を表示する。

f:id:vaaaaaanquish:20190825222513p:plain
$SUGGEST_COMMANDS=True

 

$SUGGEST_MAX_NUM

SUGGEST_COMMANDSがTrueの時に表示するコマンド数。負の値を入れておくと制限なしになる。

 

$SUGGEST_THRESHOLD

SUGGEST_COMMANDSやFUZZY_PATH_COMPLETIONでレーベンシュタイン距離を計算して、その閾値として扱う値。デフォルトは3。

 

$XONSH_AUTOPAIR

Trueであれば、括弧、括弧、引用符を自動挿入する。
keybindingsのfilterとして実装されているので、prompt toolkitにおけるfilterが何かを追ってから読むと大体実装もわかる。

 

$XONSH_HISTORY_MATCH_ANYWHERE

Trueであれば、上矢印で履歴を参照する時、現在の入力を検索語として接頭語以外でも補完する。
実装自体はprompt toolkitのbufferにおけるhistory matchなので、以下を参照すると良い。
https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/prompt_toolkit/buffer.py#L911

 

- ファイル、ディレクトリ操作に関するもの -

$AUTO_CD

Trueにしておくとcdコマンドを打たなくてもディレクトリ名だけで移動できる。

 

$CDPATH

bash, zsh等のCDPATHと同じ。cdコマンドが利用する相対パス。CDPATHによく使うディレクトリを入れておけば、cd hogeでショートカットのように移動できるので便利。一方で補間含めて全ての相対パスがこちらを参照するようになるので、使う場合は意図しないディレクトリに移動しないよう注意が必要。

 

$AUTO_PUSHD

DOSで言う所のディレクトリスタックに対して、cd時に自動でpushdする。pushd、popd、dirsコマンド*3を良く使うのであれば良い。

 

$DIRSTACK_SIZE

DOSで言う所のディレクトリスタックの最大保存数。実装自体は至極簡単なので読めば分かるはず。

  

$PUSHD_MINUS

ディレクトリスタックの操作pushd、popdに関するフラグ。Falseが通常のシェルコマンドのpushdで、Trueの場合はpushdとpopdが逆になる。

 

$PUSHD_SILENT

Trueならディレクトリスタックにpushdした時に表示する。デフォルトのFalseなら表示しない。

 

$DOTGLOB

Trueなら「*」や「**」を使ってglobした時にドットで始まるドットファイルを含むようにする。
実装はシンプルにreplaceで変換しているだけなので、読めば分かるはず。

 

- 表示に関するもの -

$PROMPT

xonshというよりはpython prompt toolkitの機能であり、ptk内のAppに対する引数に与える文字列に当たる。
https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/docs/pages/asking_for_input.rst#adding-a-bottom-toolbar

背景色や文字色も変更できるので、以下のドキュメントを参考にすると良い。
Asking for input (prompts) — prompt_toolkit 3.0.0 documentation
More about styling — prompt_toolkit 3.0.0 documentation

$PROMPTの表示。詳しく設定したい場合は、以下のチュートリアルを見ながら設定を進めると良い。
https://xon.sh/tutorial.html#customizing-the-prompt

 

$PROMPT_FIELDS

$PROMPTに対してオリジナルのフォーマットを関数等で指定できる。詳しく設定したい場合は、以下のチュートリアルを見ながら設定を進めると良い。
https://xon.sh/tutorial.html#customizing-the-prompt

 

$RIGHT_PROMPT

xonshというよりはpython prompt toolkitの機能であり、ptk内のAppに対する引数に与える文字列に当たる。
https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/docs/pages/asking_for_input.rst#adding-a-bottom-toolbar

背景色や文字色も変更できるので、以下のドキュメントを参考にすると良い。
Asking for input (prompts) — prompt_toolkit 3.0.0 documentation
More about styling — prompt_toolkit 3.0.0 documentation

右側のPROMPTに表示する文字列。詳しく設定したい場合は、以下のチュートリアルを見ながら設定を進めると良い。
https://xon.sh/tutorial.html#customizing-the-prompt

 

$BOTTOM_TOOLBAR

bottom toolbarを表示する文字列。空ならbottom toolbar無しになる。

f:id:vaaaaaanquish:20190824172521p:plain
bottom toolbar


xonshというよりはpython prompt toolkitの機能であり、ptk内のAppに対する引数に与える文字列に当たる。
https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/docs/pages/asking_for_input.rst#adding-a-bottom-toolbar

背景色や文字色も変更できるので、以下のドキュメントを参考にすると良い。
Asking for input (prompts) — prompt_toolkit 3.0.0 documentation
More about styling — prompt_toolkit 3.0.0 documentation

詳しく設定したい場合は、以下のチュートリアルを見ながら設定を進めると良い。
https://xon.sh/tutorial.html#customizing-the-prompt

 

$XONSH_GITSTATUS_*

xonshのgit statusのシンボル。gitコマンドがない場合はそれぞれsetされないので注意。

 

$XONSH_GITSTATUS_HASH

ハッシュ値。「git describe --always」と「git rev-parse --short HEAD」の値が違っていたら、XONSH_GITSTATUS_HASH+後者の値をHASHとして表示する実装。以下参照。
https://github.com/xonsh/xonsh/blob/master/xonsh/prompt/gitstatus.py#L89
デフォルト値は":"。

$XONSH_GITSTATUS_BRANCH

ブランチ名。デフォルト値は"{CYAN}"。

$XONSH_GITSTATUS_OPERATION

rebase中だとかmerge中だとかの状態シンボル。
[https://github.com/xonsh/xonsh/blob/master/xonsh/prompt/gitstatus.py#L104[
デフォルト値は"{CYAN}"。

$XONSH_GITSTATUS_STAGED

stageに乗っている時のシンボル。デフォルト値は"{RED}●"。

$XONSH_GITSTATUS_CONFLICTS:

コンフリクトが置きている時のシンボル。デフォルト値は"{RED}×"。

$XONSH_GITSTATUS_CHANGED

変更がある状態のシンボル。デフォルト値は"{BLUE}+"。

$XONSH_GITSTATUS_UNTRACKED

gitをトラックしていない時のシンボル。デフォルト値は"…"。

$XONSH_GITSTATUS_STASHED

stashした状態のシンボル。デフォルト値は"⚑"。

$XONSH_GITSTATUS_CLEAN

git上記の状態以外でclearな時のシンボル。デフォルト値は"{BOLD_GREEN}✓"。

$XONSH_GITSTATUS_AHEAD

masterブランチとのズレがaheadな状態を示すシンボル。デフォルト値は"↑·"。

$XONSH_GITSTATUS_BEHIND

masterブランチとのズレがbehindな状態を示すシンボル。デフォルト値は"↓·"。

 

$UPDATE_PROMPT_ON_KEYPRESS

Trueであれば、キー入力ごとにPROMPT表示を評価する。PROMPTに時計を実装したり、gitの状態を表示する時に有用。
実装もprompt toolkitにprompt周りの設定を関数で渡すか、stringで渡すかの違いでしかないのでシンプル。
実際評価しているのはptkなので、以下辺りから追っていくと良い。
https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/prompt_toolkit/shortcuts/prompt.py

 

$PROMPT_REFRESH_INTERVAL

PROMPTを更新する秒数を入れる。実装自体はprompt toolkitにあり、以下のサンプルのようにbottom_toolbarやrpromptでリアルタイムに更新される時計を作ったりできる。
https://github.com/prompt-toolkit/python-prompt-toolkit/blob/2.0/examples/prompts/clock-input.py

私が追加した変数なのだが、もちろん画面全体が指定秒でリフレッシュされて更新される訳なので、シェルの動作が重くなってしまう。(rpromptだけとか出来るようになっていくと良いな…)

 

$COLOR_INPUT

入力しているコマンドにsyntax highlightingをつける。実装自体は、prompt toolkit及びpygmentsによる実装なので以下を参考にすると良い。
https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/prompt_toolkit/lexers/pygments.py#L145

 

$COLOR_RESULTS

出力結果にsyntax highlightingをつける。実装自体は、上記のCOLOR_INPUTと同じくprompt toolkit及びpygmentsによる実装なので以下を参考にすると良い。
https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/prompt_toolkit/lexers/pygments.py#L145

 

$PRETTY_PRINT_RESULTS

Tanaka Akira氏が作っているrubyのprettyprint.rbを使って、出力を表示するかどうかのフラグ。
以下のドキュメントの通り、単体でもapiとして使えるようにはなっている。
https://xon.sh/api/pretty.html

 

$DYNAMIC_CWD_WIDTH

$PROMPTに表示するディレクトリを省略する時の文字数閾値
(float, str)のtupleで、0番目に閾値を入れる。float('inf')を入れる事もできる。
1番目にはstringの"%"かそれ以外が入る。"%"を入れた場合はShellの画面サイズに対する0番目の値のパーセンテージになる。"%"以外を入れたらシンプルに0番目がintになって文字数で閾値になる。

 

$DYNAMIC_CWD_ELISION_CHAR

$PROMPTに表示するディレクトリ名が長い場合に省略形として出す文字列。省略する時の文字数の閾値はDYNAMIC_CWD_WIDTHに依存。

 

$INDENT

複数行入力のインデント文字列

f:id:vaaaaaanquish:20190825181526p:plain
$INDENT="...."
実装は、改行時やkeybindで指定の文字列を挿入しているだけ。

 

$LS_COLORS

bash, zsh同様、lsコマンドの出力の色指定。基本的には他のシェルのものが参考になるし、実装を見て色名でのstring指定もできる。

 

$XONSH_COLOR_STYLE

xonshのcolorを統一的に変更できるthemeのような機能。適応できるstyleは「xonfig styles」で確認できる。
最近はPTK_STYLE_OVERRIDESやPROMPT_TOOLKIT_COLOR_DEPTHで柔軟に変更できるようになったが、このtheme機能も簡単に良い感じにできるので良さはある。

 

$PTK_STYLE_OVERRIDES

補完の表示色や、背景色、プロンプトの色などあらゆる色を設定するところ。

f:id:vaaaaaanquish:20190825200508p:plain
$PTK_STYLE_OVERRIDES
設定できる項目とデフォルト値の対応は以下の通り

  • "completion-menu": "bg:ansigray ansiblack"
  • "completion-menu.completion": ""
  • "completion-menu.completion.current": "bg:ansibrightblack ansiwhite"
  • "scrollbar.background": "bg:ansibrightblack"
  • "scrollbar.arrow": "bg:ansiblack ansiwhite bold"
  • "scrollbar.button": "bg:ansiblack"
  • "auto-suggestion": "ansibrightblack"
  • "aborting": "ansibrightblack"

デフォルト値参考:https://github.com/laloch/xonsh/blob/master/xonsh/style_tools.py#L347

 

$PROMPT_TOOLKIT_COLOR_DEPTH

色の深度を決定できる変数。ドキュメントには書いていないが、コード上では以下の値が指定できる。

  • "DEPTH_1_BIT"
  • "MONOCHROME"
  • "DEPTH_4_BIT"
  • "ANSI_COLORS_ONLY"
  • "DEPTH_8_BIT"
  • "DEFAULT"
  • "DEPTH_24_BIT"
  • "TRUE_COLOR"

 

$MULTILINE_PROMPT

コマンドを改行した際に表示される幅寄せ用の文字。

f:id:vaaaaaanquish:20190825184844p:plain
$MULTILINE_PROMPT="@"

 

$TITLE

ターミナルのタイトルを設定する。実装自体は以下のように非常にシンプルで、これで設定できないターミナルエミュレータを使っている場合は無意味。

if ON_WINDOWS and "ANSICON" not in env:
    kernel32.SetConsoleTitleW(t)
else:
    with open(1, "wb", closefd=False) as f:
        f.write("\x1b]0;{0}\x07".format(t).encode())
        f.flush()

iTerm2でタブ名が変わる例

f:id:vaaaaaanquish:20190825234117p:plain
$TITLE="Hoge"

 

$XONSH_SHOW_TRACEBACK

Trueであれば、xonshコマンドがエラーの時にtracebackを全て表示する。長くなるのでデフォルトはFalse。

 

$XONSH_STDERR_*

stderrの出力に接頭語、接尾語を付ける。stderrの色を設定したい時などに使える。
stdio周りはxonsh独自にTeeクラスが実装されていて、そこを通るのでそちらも参考に。

$XONSH_STDERR_POSTFIX

stderrの出力に付く接頭語。

$XONSH_STDERR_PREFIX

stderrの出力に付く接尾語。

 

- コマンドやxonshスクリプトの動作、変数の扱いに関するもの -

$EXPAND_ENV_VARS

Trueであれば「$var」「${var}」「%var%」をサブプロセスモードで自動で展開する。
実装自体はbuiltins.__xonsh__.envの中にあれば変換するし、なければそのまま文字列で利用するとなっている。

> $EXPAND_ENV_VARS=True
> echo "my home is $HOME"
my home is /Users/shunsuke.kawai

> $EXPAND_ENV_VARS=False
> echo "my home is $HOME"
my home is $HOME

あまりFalseにする必要はないと思うが、format string等で不具合が出た時にはこちらの設定を確認すると良い。

 

$FOREIGN_ALIASES_OVERRIDE

zsh上でxonshコマンドでxonshを起動する等、外部シェルから呼び出されている場合にxonshrc等で設定するaliasを外部シェルのaliasに対してOverrideするかどうか。
扱いが難しく xonshが起動した時点でaliasesの読み込みは行われてしまうので、xonshrcやxonsh上のコマンドとして設定しても動作しない。
呼び出し元のbashzsh環境変数としてFOREIGN_ALIASES_OVERRIDEを設定しておく必要があるので注意。

 

$FOREIGN_ALIASES_SUPPRESS_SKIP_MESSAGE

外部シェルのaliasが既に存在する時、xonsh上でOverrideしようとすると警告を出すaliasコマンドが一部存在する。その時のメッセージを表示しないようにするためのフラグ。
基本的にはTrueにしておいて問題ないと思う。

 

$HISTCONTROL

履歴に保存するコマンドを選択する。
setの中にstringで"ignoreerr"を入れておくと、終了ステータスが0以外の正常終了しなかったコマンドを履歴に追加しない設定となる。
setの中にstringで"ignoreerr"を入れておくと、同じコマンドを連続で入力した時に保存しない設定となる。

 

$LANG

historyファイル等で利用する文字コード。あくまでxonsh内での文字コードなので、デフォルトのutf-8以外にする必要に迫られる場合は殆どないはず。

 

$SHELL_TYPE

シェルのコアライブラリを選択する。以下の4つのstringが指定できる。

  • "readline"
  • "prompt_toolkit"
  • "random": ランダムにptkかreadlineを起動する
  • "best": 環境に応じてptkかreadlineを起動する

基本的にというか"prompt_toolkit"でないとxonshの恩恵の殆どが受けられないので一択だと思う…readlineだとほぼbash…randomとかどういう時に使うんだろう…

 

$RAISE_SUBPROC_ERROR

Trueであれば、サブプロセス経由のコマンドが異常終了した場合にsubprocess.CalledProcessErrorを発生させる。デフォルトはFalse。
何も表示せず、ステータスコードを返さず終了するようなモジュールを使う時に役立つかも…?

 

$XONSH_APPEND_NEWLINE

Trueであれば、コマンドの結果の末尾に改行を追加する。
普通のシェルの動作として使う場合はTrueで、「xonsh -c hoge.py」のようにしてxonshを動かす場合に余計な改行が入ってしまうのでFalseにするためのもの。
デフォルト値にはXONSH_INTERACTIVEの値が入っており、基本的にインタラクティブシェルだったらTrueになるようになっている。

 

- キーバインドや操作に関するもの -

$IGNOREEOF

Trueであれば、「ctrl + d」でシェルを終了しない。
コマンド入力を待機し続けるループをbreakするかしないかという実装。
https://github.com/xonsh/xonsh/blob/master/xonsh/ptk2/shell.py#L186

 

$MOUSE_SUPPORT

Trueであれば、シェル上でマウス操作を許可する。
実装自体はprompt toolkitの機能で、ptkの方を見に行くと良い。以下のexampleを実行すれば分かるように、マウスクリックでカーソルを移動させたりできる。
https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/examples/prompts/mouse-support.py
エディタやセレクターっぽい機能を実装する時にも使えるかも。

 

- フラグや変数として利用するもの -

$LOADED_RC_FILES

xonshrcが読み込まれたかどうかのフラグをlistにしたもの。n番目が何のrcファイルなんだ…

 

$OLDPWD

一つ前に居たディレクトリの文字列が入る。起動時はcdするまでEnvがセットすらされてないので注意が必要。

 

$PATH

各シェルで言う所のいわゆるPATH。binとかスクリプトは個々に入っているディレクトリを見に行って実行する。
listなのでappendとかで追加する。bulitins.__xonsh__.env["PATH"]からもよく参照される。

 

$TERM

基本的にはユーザが設定する事はなく、ターミナルエミュレータが与える変数。以下のようにWindowsエミュレータの判定とかに使われる。
https://github.com/xonsh/xonsh/blob/master/xonsh/base_shell.py#L500

例えば以下のissueのように、Windows+AnacondaをWSL上から触っていてターミナルエミュレータが認識できない場合等に強制的に指定する場合に使う。
Unknown Terminal type using conda env (Windows 10 --> WSL) · Issue #2525 · xonsh/xonsh · GitHub
指定する時はドキュメントの通り、早い段階(xonsh起動時の引数、xonshrcの上部)で設定しないと他設定が読み込まれる。

 

$XONSH_INTERACTIVE

xonshがインタラクティブシェルとして起動しているか、「xonsh -c hoge.py」のようなスクリプト実行形式で起動しているかのフラグ。

 

$XONSH_LOGIN

xonshがログインシェルとして起動しているかのフラグ。

 

$XONSH_SOURCE

xonshスクリプト実行時、スクリプトへの絶対pathが入る変数。スクリプト実行時意外はセットされていない。

- おわりに -

大体これで1.0.0までカバーできるはず。

今回「XDG_CONFIG_HOME」「XONSH_CONFIG_DIR」「XONSH_HISTORY_FILE」など非推奨やもう機能していないものを抜いているので注意。PRを出して改修するのが良さそう。

 

「FINAL FANTASY XVの人工知能」がなかなか面白かったので書評

- はじめに -

このブログの人気シリーズの一つ、「献本されて読んだら良かったので書評で作者に媚を売ろうシリーズ」です。

今回は、あるきっかけで「FINAL FANTASY XV人工知能 - ゲームAIから見える未来」(以下 FF本)という本を頂く形になりました。

FINAL FANTASY XV の人工知能 - ゲームAIから見える未来

FINAL FANTASY XV の人工知能 - ゲームAIから見える未来

私自身の専門もあって、普段はこの手の「人工知能本」と私が認識しているジャンルの本、例えば「よくわかる人工知能」「人工知能ビジネス徹底解説」といった旨の本を手に取る事はほぼなく、ましてや誤った知識を流布する事が多いジャンルでもあると認識していたが、このFF本はまた別の楽しみ方が出来たので、広めても良さそう思ったので書く。


 

- どんな人が楽しめそうか -

ゲームの攻略本を読むのが好きかつ、プログラミングの素養があると楽しめる本だと感じた。
日々学んでいるアルゴリズムやプログラミングの考え方が、ゲーム開発という現場でどう活かされているのか。どういったツール、環境が使われているのか。
あまり外に出てこないゲーム業界の事情や考え方が知れる、オタク大興奮みたいな書籍。

読むと楽しそうなのは以下辺りか

  • CSの学生
  • ゲームが好きな人
  • ゲームの説明書や攻略本を読むのが好きな人

ただ前提として、昨今流行りの機械学習Deep Learningの本ではないし、アルゴリズムの本でもないので、これで人工知能を使ったゲームが作れるとかそういう話ではない。技術目的で買うのは厳しい。どちらかというFFの本家技術同人っぽい雰囲気。

筆者のチームの一人として多くの講演等を行っている三宅陽一郎氏の話を聞いた事があるが、AIという言葉の定義自体が、ゲーム業界の内で使われる場合と外で使われる場合で大きく違っている。歴史的背景もあるし、互いに歩み寄っている部分もあるが、統計、機械学習分野と言葉の使い方が違うという雰囲気を書籍内でもちらほら感じるし、念頭に置いて読んだ方が良いと感じる。

(例えば、幅広くルールベースなものもAIとしてまとめられているし、神経系の理解が危うかったりArtificialと書いてある訳に"人口"が充てられている等、ある視点ではつらい表記は実際にいくつかある)

 
一方で、FINAL FANTASY XVという壮大なゲームの中で使われる、アルゴリズムや意思決定、実際のプロジェクトの動き、データ解析の一片が外から見られるという機会は、そうそう無いだろうし、自分がコンピュータに触れ始めた段階でこれを読んでいれば、擦り切れる程読んでゲーム開発者を目指したかも知れないと思うほど濃く鮮やかな本だと思う。


 

- 概要 -

「FFの本家技術同人」と表現した通り、各章を別々の担当者(エンジニアからデザイナー、リーダーまで)が書いている。
そのため、粒度も違えば、各章で「AIとゲームのこれから」のような節があるのが気になるが、近年テック企業が出してる同人誌はそんな感じなので普通か。

全体の概要は以下のような感じ

  • 1章: ゲームAI入門
    • 大まかなゲームAIの概要と歴史、書籍全体へのpath
    • 短いけど読み物として分かりやすい
  • 2章: 意思決定ツール
    • ゲームキャラクターのAIを作成するために考える事
    • 社内で使っている意思決定ツール、デバッグシステムの写真が出てくるのがなかなか
  • 3章: ナビゲーションシステム
    • パス検索やブロッキングを実際活用してるんやでというお話で興奮する
  • 4章: AIとアニメーション
    • キャラクターを重点的にPSD等を活かして感情を表現している話
    • 2章で出てくるツールをアニメーターも利用しながらキャラクターを構成しているのが伺える
  • 5章: 位置検索システム
    • マップにグリッドを取って検索する話が簡単に書かれている
    • CSで学ぶアルゴリズムがこういう所で活きるって事が実感できて良いと思う
  • AI座談会 プログラマー
    • これだけで同人誌1冊いけるくらい面白い
  • 6章: 仲間AI
    • キャラクターの個性をユーザに感じさせていく工夫の話
    • 「設定を体験に」「仲間:最も近く最も心地よく」などのテーマをどう実現するか丁寧に書かれている
    • 3人のキャラクターが主人公を囲むように動くFF XVというゲームの多大なる工夫を感じて良い
  • 7章: モンスターAI
    • NPCやモンスターの制御の話を簡潔に
  • 8章: 兵士AI
    • 7章の続きみたいな話
  • 9章: アビエントAI
    • 7章の続きみたいな話, 街中のモブのスクリプトを中心に
  • 10章: 写真AI
    • ゲーム内で撮れる写真機能において、キャラの個性や映えを設計する話
    • なかなか他の場面で考える事ないので面白い設定だなと思った
  • AI座談会 デザイナー編
    • ゲームデザイナーがゲームやキャラの作り方について語っている
    • 体験の設計の話はゲーム業界進んでるよなあと思った
  • 11章: 会話AI
    • 会話をユーザに面白いと思ってもらえるようなスクリプト構成の話
  • 12章: AIモード
    • 自律的に動くモードを作ってモブに適応した話
    • 自律モードを使う事の良い点、悪い点が書かれてあってなるほどなって感じ
  • 13章: ロギングと可視化
    • ゲームのbackendアーキテクチャとかロギングの話
    • フレームレートに影響を与えないようログを落とす
    • 統計量を取り異常な会話等を検知する
    • ナビゲーションマップを可視化して意図していない壁などを出さないように
    • 別で本一冊書いて欲しいくらい興味深い
  • 14章: これからのゲームAI
  • AI座談会 チームリーダー編
    • 良かった所メインだったのが気になった
    • チームリーダーの考えや工夫、試行錯誤が見えるともうちょっと良かった気もする

私がエンジニアということもあるかもしれないが、個人的には開発視点の話が面白かった。
「あー、ここもうちょっと書いて欲しい」と思っている時点で、この書籍の目的は達成されているのだと思う。
これから先は、SQUARE ENIXの門を叩くしかないのだろう。


昔、ゲームの攻略本を読んだだけで、手に入れるかも分からない剣やアイテムのモチーフを見て興奮したものだが、ゲーム会社が使っているツールやキャラに貼られたメッシュで似た感覚になれたのがとても良かった。

f:id:vaaaaaanquish:20190803225036j:plainf:id:vaaaaaanquish:20190803224944j:plain
実際のツールの写真

私は「技術書、ブログを書く時は必ず参考文献を、引用を」と常々言っているが、各章で参考文献をしっかり記載している所も好感を持てた。気になる所を追って読めればと思う。


 

- おわりに -

帯の松尾豊氏のコメントも「ゲームの未来を感じられる」とあるが、本当にそう感じた。

ゲーム業界からこういった技術本が出る事はこれまで稀で、実際のゲーム開発について知る機会というのは(いわゆるWebサービス開発やアプリ開発に比べて)少なかったように思う。

この書籍に書かれているような技術ロマンが新しい火種を作り、またゲーム業界を盛り上げるのだとすると、素晴らしい情報発信である。

機械学習観点では、今後ゲームとの距離が近くなると思っており、ゲーム開発におけるAIの歴史や考え方の発信を享受する事で、また新しい接点に繋げていければ良いと感じた一冊だった。


FINAL FANTASY XV の人工知能 - ゲームAIから見える未来

FINAL FANTASY XV の人工知能 - ゲームAIから見える未来

 

python prompt toolkitの紹介と動作を理解するメモ

- はじめに -

本記事はpython prompt toolkit(以下 ptk)の動作を理解するメモです。

ptkで出来ること、その概要を書いています。

- ptkとは -

ptkは、Pythonで実装されているCLI用のツールキットである。

  • コンソール上でのフルスクリーンアプリケーションの作成
  • dialogs、progress-barの生成
  • シェルのようなインタラクティブな入出力
  • 補完
  • clipboardの管理
  • 出力テキスト色の変更
  • シンタックスハイライト
  • EmacsとViのキーバインディング
  • マウスカーソルによる操作
  • 各種非同期処理

上記のような機能がサポートされており、コンソール上で様々なツールをPythonを利用して作成する事ができる。

github.com

Pure Pythonで書かれている事から、UnixWindowsなどクロスプラットフォームで動作する事、数の多いPyhtonライブラリを資産として利用できる事が強いメリットとして挙げられる。

    
examplesに様々なCLIアプリケーションの例とtutorialが示されており、小さなCLIツールから作成を始める事ができる。

examples: python-prompt-toolkit/examples at master · prompt-toolkit/python-prompt-toolkit · GitHub

 
例えば、シンプルなプログレスバーは、以下程のスクリプトで実装する事ができるだけでなく、プログレスバーの色付けや非同期化、ネスト化も簡易に実装する事ができる。

import time
from prompt_toolkit.shortcuts import ProgressBar

with ProgressBar() as pb:
    for i in pb(range(800)):
        time.sleep(.01)

f:id:vaaaaaanquish:20190706152405p:plainf:id:vaaaaaanquish:20190706152410p:plain


 
入出力のサポートでは、公式の補完の図が分かりやすいため引用しておく。

f:id:vaaaaaanquish:20190706152545p:plain
https://github.com/prompt-toolkit/python-prompt-toolkit

これらの機能は、Jupyter Notebookのに代表されるjupyter_console等でも利用されており、utilityとして多くのツールに導入されている。「xonsh」と呼ばれるシェルもまたこのptkをコアなライブラリとして作成されている。キーバインドや補完、表示の多くをこのptkに頼っている。

github.com

こういったシェル上でのI/O、Validate、表示形式を様々な形でサポートする他、ウィンドウサイズの計算も考慮されており、CLI上でのフルスクリーンアプリケーションを作成することも可能である。



フルスクリーンアプリケーションでは、CLIを縦横に分割したり、WebサイトのようにAlignさせたりといった画面上の計算をサポートしてくれる。

f:id:vaaaaaanquish:20190706140310p:plainf:id:vaaaaaanquish:20190706140317p:plain
vertical-split, horizontal-align

他にも例えばコンソール上で以下のようなものが「ウィジェット」としてサポートされており、Pythonで簡単に実装する事ができる。

f:id:vaaaaaanquish:20190706135444p:plainf:id:vaaaaaanquish:20190706135528p:plain
左:入力やチェックボックスを含むフルスクリーンダイアログアプリケーション
右:プログレスバーを含むフルスクリーンアプリケーション


  

ptkで作成されたツール

フルスクリーンアプリケーションを作成する機能を利用して、Python REPLの立ち位置のptpython、pure pythonなtmuxであるpymux、pure pythonvimであるpyvimをptkと同じ人物が作成している。

github.com

f:id:vaaaaaanquish:20190706154214p:plainf:id:vaaaaaanquish:20190706154204p:plain
ptkを利用した画面分割やフロートウィンドウ

github.com

github.com

f:id:vaaaaaanquish:20190706154001p:plainf:id:vaaaaaanquish:20190706154011p:plain
pyvim, pymux

 
ref: Gallery — prompt_toolkit 2.0.9 documentation


 

- ptkの描画を理解する -

ptkのテキスト描画の根幹はprint_formatted_textにある。また、画面の管理、描画はApplicationクラスが担当しており、こちらも重要である。

print_formatted_textは、組み込み関数のprint関数と互換性をもたせながら、クロスプラットフォーム上で色やフォーマットを変更して出力する関数である。多くの機能がこの関数によって表示されている。

シンプルに利用するには、以下のように書く。

from __future__ import unicode_literals, print_function
from prompt_toolkit import print_formatted_text
from prompt_toolkit.formatted_text import FormattedText
from prompt_toolkit.styles import Style

text = FormattedText([
    ('class:aaa', 'Hello\n'),
    ('class:bbb', 'ITALIC\n'),
    ('class:hoge', 'under line\n'),
    ('class:piyo', 'This is bold.')
])

style = Style.from_dict({
    'aaa': '#ff0066',
    'bbb': '#44ff00 italic',
    'hoge': 'underline',
    'puyo': 'bold'
})

print_formatted_text(text, style=style)

f:id:vaaaaaanquish:20190706182334p:plain
print_formatted_text

上記のようなPythonらしい記法以外にも、HTML風の記法や、ANSI、Pygments tokenを利用した書き方ができ、print_formatted_textはそれらをよしなに変換してくれる。ptk 1.xまでは完全にPygmentsに依存していたが、2.xから幅広い記法をサポートしたため、2.xや3.xを使うと良い。

ref: Printing (and using) formatted text — prompt_toolkit 2.0.7 documentation

 
クロスプラットフォームに対応する場合や、Class nameについてより詳しく知りたい場合は以下Stylingのページ、もしくは実際のコードを確認する。
ref: More about styling — prompt_toolkit 2.0.7 documentation
ref: python-prompt-toolkit/defaults.py at master · prompt-toolkit/python-prompt-toolkit · GitHub


 

Application

Applicationクラスは、上記までで紹介したようなI/O、style、keybindingやwidgetを含めて動くアプリケーションを生成するためのクラスである。ptkで作成されたxonsh、pyvim、pymuxがそうであるように、ptkで定義されたツールを一つのCLIアプリケーションの形にする事ができる。

このApplicationクラスがScreenオブジェクトをレンダリングする過程を知る事で、ptkを利用したCLIツール開発について理解する事ができる。

 

Screenオブジェクトのレンダリング

ApplicationクラスはScreenクラスを生成し画面を描画する。描画に利用されるwrite_to_screenメソッドの動きを見る前に、用語をまとめておく。

  • Container
    • HSplit VSplit FloatContainerなど画面上の場所を確保してくれるやつ
    • 中でもWindowというよしなに色々やってくれる便利なアダプターが楽で便利
      • WindowはUIControlを中に埋め込めるのでよく使う
  • UIControl
    • UI周りの動的な情報をContainerに渡してくれるやつ
    • 以下のようなものがある
      • 編集可能でスクロール可能なバッファ表示のためのBufferControl
      • テキスト表示のためのFormattedTextControl
      • More about Control
  • widgets
    • UIの中に入れる物として抽象化された概念、簡単にレイアウトが作れる
    • TextArea, Button, Frame, VerticalLine
  • Layout
    • 上記らをラップすると折返しや表示をよしなにやってくれる
    • styleもここに入る

概念の多きで言うと以下のようになる
Application > Screen > Layout > Container, UIControl > widgets
それぞれ継承したものを、再帰的に利用する事もできる(Containerの中にContainer等)。中でもContainerオブジェクトとUIControlオブジェクトを組み合わせた時の相互作用を理解する事は、Applicationを作る上で非常に重要である。

 
Screenオブジェクトが持つRendererオブジェクトがContainerを特定の画面上にレンダリングする流れは、シンプルに書くと以下のようになる。

  1. Containerが持つwrite_to_screenメソッドが一定の大きさの長方形を生成
  2. UIContentがスペースを計算
  3. RendererがLayoutを参考にContainerを正しい位置に描画していく

ref: https://python-prompt-toolkit.readthedocs.io/en/2.0.9/pages/advanced_topics/rendering_flow.html

上記用語と流れを理解した上で、シンプルなApplicationを作成してみると分かりやすい。

from prompt_toolkit import Application
from prompt_toolkit.buffer import Buffer
from prompt_toolkit.layout.containers import VSplit, Window
from prompt_toolkit.layout.controls import BufferControl, FormattedTextControl
from prompt_toolkit.layout.layout import Layout

buffer1 = Buffer()
root_container = VSplit([
    Window(content=BufferControl(buffer=buffer1)),
    Window(width=1, char='|'),
    Window(content=FormattedTextControl(text='Hello world')),
])

layout = Layout(root_container)

app = Application(layout=layout, full_screen=True)
app.run()

Vsplit(縦に2分割するcontainer)の中にWindow(入出力可能な便利container)を3つ入れている。それをLayerにいれてApplicationとしている。結果はフルスクリーンで以下のように表示される。

f:id:vaaaaaanquish:20190706211619p:plain
左右に画面が分割され、左画面がテキスト入力可能なBufferControl、右画面がText表示になる様子

 
ref: Building full screen applications — prompt_toolkit 2.0.7 documentation
source: python-prompt-toolkit/application.py at master · prompt-toolkit/python-prompt-toolkit · GitHub


 

KeybindingとFilter

さらに画面の操作を円滑にするKeybindingsやbottom toolbarを設置する事もできる。また各動作や状態をhookして動かせるFilterクラスも存在する。
ここでは省略するが、各々設置する事ができるので把握しておくと良い。

keybindings: More about key bindings — prompt_toolkit 2.0.7 documentation
filter: Filters — prompt_toolkit 2.0.7 documentation


 

関連

私自身もptkへのコミット他、xonshなどの開発に寄与している。
その他、破壊的変更の多かったptk 1.xからptk 2.xへの移行のための記事を書いてあるので参考にできると思う。
vaaaaaanquish.hatenablog.com

フルスクリーンアプリケーションで雑に時計とかも作ったので参考に
f:id:vaaaaaanquish:20190706213406p:plain
github.com


おわりに

ptkは1.xから2.xへの破壊的変更こそ多かったものの、3.xに向けてpython 3.6から導入されたasync/awitを利用した非同期処理や、レンダラの強化、各クラスの相互作用の整理が進んでおり、非常に使いやすくなりつつある。

Pythonの資産が扱えるのが非常に大きく、MLエンジンを載せた補完やCLIと連動するWebアプリケーション等が出てくる事を期待している。


 

xonsh拡張のxontribをつくる

- xontribとは -

xontribはxonshの拡張ライブラリ群である。
公開されているxontribを導入するには以下のようにライブラリをインストールしロードする。

pip install xontrib-hoge
xontrib load hoge

xontrib loadをxonshrcに記述しておくことで、xonshセッション起動時にロードすることもできる。

1年程前に公開されていた各xontribの紹介は以下記事に記載している。
vaaaaaanquish.hatenablog.com

 
xontribはpythonスクリプトもしくはxshスクリプトによって記載されたmoduleで、xontrib以下のnamespaceに配置される。tabcomplete拡張や外部Pythonライブラリのサポート、xonshモジュールのoverride等が作成、公開されており、その作成方法については以下チュートリアルに概要が書かれている。
xon.sh
本記事はこちらを参考にし、xontribを作成していくものである。


 

- cookiecutterによるtemplete利用 -

xontribのテンプレートとしてcookiecutterを利用する事ができる。

https://github.com/laerus/cookiecutter-xontribgithub.com


以下コマンドで対話的にライブラリの雛形が作成できる。

pip install cookiecutter
cookiecutter https://github.com/xonsh/xontrib-cookiecutter

対話の内容は以下のようになる

full_name [Your name]: vaaaaanquish
email [Your address email]: sample@exm.com
github_username [Your github username]: 6syun9
project_name [Name of the project]: xontrib-sample    # setup.pyに記載されるproject
project_slug [xontrib-samplelib]: sample              # xontrib loadするモジュール名
project_short_description [A short description of the project]: This is test
version [0.1.0]: 0.0.1

上記cookiecutterコマンドによってsampleディレクトリが作成される。

$ tree sample
.
├── LICENSE
├── README.rst
├── setup.py
└── xontrib
    └── sample.xsh

作成した後は、sample.xshを編集するだけである。
作成された時点ではsample.xshには内容がないため、以下のように変更する。

import xonsh

__all__ = ()
__version__ = '0.0.1'

print('hello xontrib sample')

xontribを読み込んだ時点でstringをoutputするサンプルとなる。
こちらを手元にinstallし、xontrib loadで読み込んでみる。
sampleディレクトリ内にあるsetup.pyを利用する。

$ python setup.py install
$ xontrib load sample
hello xontrib sample

ここから中身を拡充していくだけ。

 

- pypiで公開し使ってもらう -

pip installで導入できるようにpypiに登録する。
(アカウント作成が必要なので事前に行なう)
PyPI · The Python Package Index

twinを利用したアップロード

distを作成し公開するためのツールを導入する

pip install wheel twine

先程の作成したパッケージのroot dirのsetup.pyを利用してパッケージを作成、アップロードする。

python setup.py sdist bdist_wheel
twine upload --repository-url https://upload.pypi.org/legacy/ dist/*

数分後くらいにちゃんと登録されて pip install xontrib-sample できるようになる(この記事は解説なので実際にはやってない)。

pypiのREADMEや必要な外部ライブラリを定義する

最低限以下のようにしておくとなんとかなる。
https://github.com/6syun9/xontrib-readable-traceback/blob/master/setup.py

rstじゃなくてmdじゃないとgithubレンダリングしてくれない場合があるので、私はmdで管理している。

 

xonshのDocumentationに追加してもらう

公開されているxontribはxonshのドキュメント上に載せてもらう事ができる。
xon.sh

また xontrib list コマンドでも表示されるようになる。

こちらに載せてもらうには、本家xonshにpull requestsを送る必要がある。
例えば、以下のように「新しいxontrib作ったよ!」と xonsh/xontribs.json を編集した旨を伝えるpull requestsを作成する。
github.com

xonshにコントリビュートする時は news/TEMPLETE.rst をコピーして news/{{ブランチ名}}.rst なるファイルを作って、更新ログを書くルールになっているのでそちらも追加して終了。詳しくは以下Developer’s Guideを参照。

Developer’s Guide — xonsh 0.9.24 documentation


 

- xontrib作成において参考にできる所 -

「こういうの作ってみたいなあ…」と思った時に参考にできるよう私のメモとして記載しておく。

あるタイミングでコマンドを発火させたい

tab補完を拡張したい

promptの見た目を弄りたい

コマンドの保存、読み込み、引数実装

コマンドの出力結果を使いたい

xonsh自体の機能をoverrideしたい

別threadの実装

画像やgraphを表示したい

サブウインドウやalertを表示したい

ネタがない


 

- おわりに -

このメモとGoogleが使えればxontribが作れると思うので、みなさんもxontributerになりましょう。