nikkie-ftnextの日記

イベントレポートや読書メモを発信

GitHub ActionsでPythonのコードを整える

はじめに

聞いて聞いて!

isortって「I sort」(私が並べる)なんだよ! nikkieです。

タイトルの通り、GitHub ActionsでPythonのコードにblackisortをかけてコミットを作りGitHubリポジトリにプッシュする方法を調べました。

目次

背景

GitHubリポジトリで発生したイベントを契機に処理を実行できるGitHub Actions、便利ですよね。
私がよく設定するのはテストコードの実行なのですが、ふと「PEP 8に反したコードであれば、GitHub Actionsの処理の中で、しれっと直してしまえばいいのではないか」と思い付きました。

1人でPythonを書いている分にはPEP 8に反したコードをGitHubにプッシュすることはめったにありません1
複数人の開発シーンになると、PEP 8に反したコードがリポジトリにプッシュされることも(私の経験上)起こりえます(我々愚かな人類の"気をつける"には限界がありますからね)。
このあたりは、保存時にblackisortで整形するように全員のエディタを設定したり、pre-commitを導入したりといった仕組み化で解決できると思いますが、今回はCI側でしれっと整形するというアイデアを実装してみました。

CIツールの中でも手軽だと感じているGitHub Actionsでまず試してみました。
やり方(コマンド)を展開することで、GitHub Actions以外のCIツールでも設定できると思います。

結論:設定ファイルの例

このActionの動きは次のようになります:

  1. リポジトリにプッシュされたコードがisortで整形する必要があれば、整形してコミットを作りプッシュする
  2. また、blackで整形する必要があれば、整形してコミットを作りプッシュする

「整形する必要があれば、整形してコミットを作りプッシュする」の実装

blackの場合を例にすると、実装は次のようになります。
※単体でも動くように取り出したので、結論のyamlと比べると余計な処理が入っています

black --check --diff . || black -v .
git config --global user.name $GITHUB_ACTOR
git config --global user.email $GITHUB_ACTOR@users.noreply.github.com
git commit -am "[style] Format Python code with black in GitHub Actions"
git push

順番に見ていきましょう。

bash|| (OR) 演算子

「blackで整形する必要がある場合は整形する」は||演算子で表せます。

A、Bをコマンドとしたとき、A || B

  • コマンドAの終了コードが0のとき、コマンドBは実行されない(短絡評価
  • コマンドAの終了コードが0以外のとき、コマンドBを実行

という動作をします2

「blackで整形する必要があるか」の確認は、--checkオプションで実装できます。

  • 整形する必要なし:終了コードは0
  • 整形する必要あり:終了コードは0以外

以上から、A || Bを以下のように書けばいいですね。

  • コマンドAblack --check .
  • コマンドBblack .

整形したらどうなるかを示すために--checkと一緒に--diffオプションも指定しています。

整形しないときもコミットを作ってプッシュすると実装

「blackで整形する必要がある場合は、整形だけでなくコミットを作りプッシュまで行う」ように実装する案も浮かびます。
ですが、今回は整形する必要の有無によらず、コミットを作りプッシュするコマンドを実行します。

これは以下の理由によります:

  • ワーキングツリーがclean(ファイルに差分がない)のときにgit commitしてもエラー終了しない
  • リモートリポジトリに先行するコミットがないためになにもgit pushできなくてもエラー終了しない

分岐はblack --check --diff . || black -v .の部分だけと最小限にし、整形してもしていなくても常にコミットを作りプッシュするようにしました。

この実装には、以下のIssueのコメントを参考にしています。
https://github.com/actions/toolkit/issues/908#issuecomment-919609272

you can use black --check . as a condition

git remote set-urlは要らない?

GitHub Actionsでコミットを作り、プッシュする方法を調べると、git remote set-urlというコマンドも登場します。

git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY

この secrets.GITHUB_TOKEN に何を設定するかがよく分からず3調べていたところ、actions/checkout@v2の効能により、このコマンドが不要になることが分かりました。

Version 2 of checkout resolves the detached HEAD state issue and simplifies pushing to origin.

git configで設定する情報

コミットを作るのに必要なuserのnameとemailを設定します。

環境変数GITHUB_ACTORには、ワークフローを開始した人かアプリの名前4が入るそうです。
これを使うと、nikkieがコミットをプッシュしてワークフローを起動したとき、ワークフローが作るコミットもnikkieが作ったことになります

また、email@users.noreply.github.comの部分ですが、メールアドレスをプライベートにする仕組みでした。
GitHub上で有効にし、さらにローカルのリポジトリにも設定すると、メールアドレスを隠せるそうです5(今回初めて知りました!)

git configを1つのstepに切り出す

isortで整形するstepでもblackで整形するstepでも、コミット前にgit configで設定するのはDRYじゃないですよね。
これはGitHub Actionsの性質を使ってDRYにできました!(ここも「聞いて聞いて!」)

1つのジョブで実行中のすべてのステップは同じランナー上で実行されるので、ステップ間でファイルシステムを通じて情報をやりとりすることができます。(Software Design 2020年10月号 (Kindle の位置No.1934-1935))

git configコマンドで一度だけuserのnameとemailを設定すれば、その情報はファイルシステムに書き込まれているので、後続のステップですぐにgit commitできます。

blackisortの引数の指定ーpyproject.toml、便利!

blackisortに渡す引数もありますよね。

  • black -l 79 .(1行あたりの文字数を指定)
  • isort --profile black .(blackと競合しないように指定)

整形の有無のチェックでも整形時も引数を指定するのはDRYじゃないと感じます。

これをDRYにする方法としてpyproject.tomlに引数を書くという方法がありました!

[tool.black]
line-length = 79

[tool.isort]
profile = "black"

リポジトリルートのpyproject.tomlを見てblackisortが動くので、YAMLファイルのコマンドの記載がスッキリしました。

終わりに(次回予告?)

色々調べながら「Pythonのコードを整える」Actionをゼロから組み上げました!
ですが、GitHub Actionsでコードを整えるニーズって色んな人が感じていそうですよね。
実際、Actionを公開している方もいます。

  • psf/black@stable6
  • isort/isort-action

こういったActionを組合せても実現できたのですが、1箇所どうしても使いづらい点が解消できず、ゼロから組み上げることにしました。
試行錯誤の過程はまたの機会にアウトプットします。


  1. エディタの保存をトリガーにblackisortで整形するように設定しています。VSCodeの場合:https://code.visualstudio.com/docs/python/editing#_formatting
  2. シェルについては Shell Tools and Scripting · Missing Semester が最近のオススメです。
  3. 自動トークン認証 - GitHub Docsによると設定は不要のようです(どうりで情報が出てこなかったわけだ)
  4. 「The name of the person or app that initiated the workflow.」ref: https://docs.github.com/ja/actions/learn-github-actions/environment-variables#default-environment-variables
  5. コミットメールアドレスを設定する - GitHub Docs
  6. Blackのドキュメントで案内されています。GitHub Actions integration - Black 23.3.0 documentation

色づくターミナル出力の仕組みから

はじめに

2022年、"あきまして"おめでとうございます1
本年もよろしくお願いいたします🎍🐯

年末年始休みで、色々寄り道しながら技術的な調べ物をしています。
その中で、「ターミナルの出力にどうやって色を付けるんだろう」という疑問について調べて分かったことをまとめます。

※2021年はアドカレあんまりできなかったのですが、Python Advent Calendar 2021に空白を見つけたので、時を戻して、Python Advent Calendar 2021 10日目の記事ということにしちゃいます!

目次

TL; DR

  • エスケープシーケンス:特殊な文字や機能を表すための文字列
  • 色を付けたい文字列の前後に 特定のエスケープシーケンス を入れる
    • ANSIエスケープシーケンスとも言われる
  • ANSIエスケープシーケンスはESC[<色を表す整数>mで始まる。ESC[mまでの文字列に色が付く

ここにたどり着くまでを以下に記していきます。

Pythonで知っていることを思い出す

「ターミナルの出力にどうやって色を付けるんだろう」という疑問、さっぱり見当が付きませんでした🤯
そこで、まずはなじみ深いPythonで出力の色を変えた経験を思い出します。
私の場合は以下の2つが浮かびました。

Djangoのカスタムコマンド

django.core.management.base.BaseCommand を継承して作るカスタムコマンドです。
manage.py my_commandのように独自のコマンドを追加できます。

カスタムコマンドからの出力には色を付けられます。
上のドキュメントの例では、緑色 で出力されます。

self.stdout.write(self.style.SUCCESS('Successfully closed poll "%s"' % poll_id))

rich

色を付けた文字列で思い出したのが rich というライブラリ。
普段使いしているというより触ってみたいライブラリです。
GitHubのREADMEを参考にしたところ、簡単に出力に色が付きました!

>>> from rich.console import Console
>>> console = Console()
>>> console.print("Happy", "New", "Year", style="green")  # 続く文字は緑色で出力されます
Happy New Year

どうやって出力に色を付けている?

では、どのような仕組みで色を付けているのでしょう。
調べて分かったのは、色を指定するための文字列を付与した文字列を出力しているということです。
以下のようなイメージです(文字列1だけ色を変える例):

<ここからこの色に変更>文字列1<色を戻す>文字列2

色を指定するための文字列がどんなものか、先ほど紹介したPythonライブラリの中を覗いてみます。

色を指定するための文字列

Django

django.utilsの下にtermcolors.pyがあります2
その中のcolorize関数で、色を付与した文字列が返されます。

>>> from django.utils.termcolors import colorize
>>> colorize("Happy New Year", fg="green")
'\x1b[32mHappy New Year\x1b[0m'
>>> print(colorize("Happy New Year", fg="green"))
Happy New Year
  • \x1b[32mが「ここから32という色(緑)に変更」
  • \x1b[0mが「色を戻す」

という意味です。
色を付与した文字列をprint関数で出力すると、色付きの出力となります。

rich

richでも同様の実装をしているように思われます(使用経験はDjangoに比べると著しく少ないので読み違いをしているかもしれません)。
rich.style.Styleクラスのrenderメソッドで、テキストに色の変更や戻す文字列を付与していました3

f"\x1b[{attrs}m{text}\x1b[0m"

renderメソッド呼び出し例

>>> from rich.color import Color
>>> from rich.style import Style
>>> green = Color.from_ansi(2)
>>> green_style = Style.from_color(green)
>>> print(green_style.render("Happy New Year"))
Happy New Year

色の指定が機能する仕組み

'\x1b[32mHappy New Year\x1b[0m'のような文字列が機能する仕組みを見ていきます。

このような文字列にはエスケープシーケンスが含まれていると理解しました。

エスケープシーケンス (escape sequence) とは、コンピュータシステムにおいて、通常の文字列では表せない特殊な文字や機能を、規定された特別な文字の並びにより表したもの。(Wikipedia エスケープシーケンスより)

'\x1b[32mHappy New Year\x1b[0m'には画面制御のエスケープシーケンスが含まれていて、ANSIエスケープシーケンスと呼ばれるそうです4

ANSIエスケープシーケンスの構成要素を見ていくと、\x1b(16進数の1b)は10進では27です。
文字コードとしてはエスケープを表します。

>>> int("1b", 16)
27
>>> chr(27)
'\x1b'

WikipediaANSI escape code(英語)を参照すると、ANSIエスケープシーケンスの仕様が説明されていました。

  • ESC[の部分が CSI (Control Sequence Introducer)5
    • '\x1b['の部分のこと
  • CSI<整数>mSGR (Select Graphic Rendition)6
    • 画面の属性を設定する
    • 例えば、30-37の整数を指定すると、文字の色を指定したことになる7
    • '\x1b[32m'は32で緑色を指定
    • '\x1b[0m'は属性をオフにする指定8

文字の色の他に背景色や太字・斜体なども指定できるようです。
CSI、SGRを押さえておくと、ANSI escape code中の表を参照して、気になる指定を試せますね。

ターミナルへの出力への色づけ方

ここまでで、文字列にANSIエスケープシーケンスを付与してターミナルに出力すればよいと分かりました。

bashの例

参考:https://qiita.com/ko1nksm/items/095bdb8f0eca6d327233#1-echo-%E3%81%A7%E3%81%AF%E3%81%AA%E3%81%8F-printf-%E3%82%92%E4%BD%BF%E3%81%86

$ printf '\033[32m%s\033[m\n' 'Happy New Year'
Happy New Year

'\033'(8進数の33)は10進数の27です。
表記が異なりますが、エスケープを表していることに変わりありません

終わりの'\033[m'ですが、整数の省略は'\033[0m'として扱われるとWikipediaに記載がありました9

Pythonの例

"""example.py"""
from functools import partial


def my_colorize(text: str, color: int):
    return f"\x1b[{color}m{text}\x1b[0m"


colorize_green = partial(my_colorize, color=32)

print(colorize_green("Happy New Year"))
$ python example.py  # 続く文字は緑色で出力されます
Happy New Year

終わりに

「ターミナルの出力にどうやって色を付けるんだろう」がふと気になり、Pythonで知っていることを手がかりに調べ、ANSIエスケープシーケンスというものを知りました。
ターミナルで日頃目にしている色やフォントの変更、白状すると「魔法」でしたが、仕組みの一端が分かり、自分でも少しは使えそうに感じています。

動作環境


  1. https://github.com/django/django/blob/4.0/django/utils/termcolors.py

  2. https://github.com/willmcgugan/rich/blob/v10.16.1/rich/style.py#L705

  3. Wikipedia エスケープシーケンスに具体例として挙がっています

  4. https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_(Control_Sequence_Introducer)_sequences

  5. https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters

  6. SGRの表に「Set foreground color」とあります

  7. SGRの表に「All attributes off」とあります

  8. CSIのところで「no parameters at all in ESC[m acts like a 0 reset code」とあります。

JSON Linesをjqコマンドで扱う方法、『jqハンドブック』で完全に理解した!

※ダニング・クルーガー効果の最初の山の状態です。おそらく近い未来に「何も分からない…」となるでしょう!

はじめに

2021年、大変お世話になりました。nikkieです。

2021年、PyCon JP最優先で来たため、アウトプットが著しく衰えています。
少しずつでもリハビリしていこうと、小さくアウトプットです。
今回は、最近インプットしたjqコマンドについてです。

目次

前提

タイトルに含まれる2つについて確認しましょう。

JSON Lines

JSONが改行文字で区切られたファイル形式と理解しています。
例えば以下のように、JSON形式の行複数からなるファイルです1example.jsonlと名付けます)。

{"name": "Gilbert", "wins": [["straight", "7♣"], ["one pair", "10♥"]]}
{"name": "May", "wins": []}

詳細が気になる方は、以下をご覧ください。
「Examples」のページには、「Better than CSV」として、表形式のデータを曖昧さなしにちゃんと扱えると記載されています。

『jqハンドブック』

『jqハンドブック』をやっていく会 を知り、「この本、読んでみたい!」と思いました。

なぜ『jqハンドブック』を読みたかったのか?(jqコマンドで何がやりたかったのか?)

JSON Lines形式のファイルは、Pythonjsonlinesというライブラリで普段扱っています。

>>> with jsonlines.open("example.jsonl") as reader:
...   for line in reader:
...     print(line["name"], line["wins"])
...
Gilbert [['straight', '7♣'], ['one pair', '10♥']]
May []

jsonlinesは使いやすいと感じていますし、いくつかのキーをまたがった加工はやはりPythonスクリプトでやりたいです。
ただ、ちょっとした値の取り出しは、「そもそもPythonスクリプトを不要にできるのでは?」と思い付きました。

そこで、『jqハンドブック』でインプットし、ちょっとした値の取り出しをjqコマンドで完結させられるようになろうと取り組みました。

完全に理解した!

以下のようにして(Pythonを書かずに)できました!🙌
jqコマンドの出力をファイルにリダイレクトしておき、それを後続処理に渡すような使い方を想定しています。

# 各行のnameの取り出し
$ jq --raw-output '.name' example.jsonl
Gilbert
May
# JSON Linesに含まれるwinsの取り出し
$ jq --compact-output '.wins[]' example.jsonl
["straight","7♣"]
["one pair","10♥"]

以下を学びました:

  • jqコマンドの第1引数はフィルタ
  • フィルタにおけるイテレータ[]:配列の値の要素それぞれをループ(キー: 値に対しても使え、値のループになる)
  • --raw-output:出力をクォートで囲まない指定
  • --compact-output:縦長ではなく、横長に出力する指定

また、行によってはキーがない場合も、?をフィルタに使うことで、エラーを出さずに処理できます。
この例では2行目の.winsがnullであることによって送出される「Cannot iterate over null」を出ないようにしています。

{"name": "Gilbert", "wins": [["straight", "7♣"], ["one pair", "10♥"]]}
{"name": "May"}
$ jq --compact-output '.wins[]?' example.jsonl
["straight","7♣"]
["one pair","10♥"]

終わりに

恥を忍んで白状すると、jq '.' example.jsonlという使い方しか知らず、整形して表示する目的でしか使えていませんでした。
『jqハンドブック』で少しだけインプットしましたが、これからは文字通りフィルタとしてjqコマンドが使っていけそうでワクワクしています。

jqコマンドもgitコマンドのように、コマンドの仕様は2割程度しか知らなくても、使いたいケースの8割はカバーできそう2という感触を得ました。

動作環境

  • Python 3.9.4
  • jsonlines==3.0.0

  1. 例は https://jsonlines.org/examples/ より

  2. Clean Agile』の第6章の中で登場した「80/20ルール」を思い出しています。「Gitの機能の一部(20%程度)を使えば、ソース管理の日常的なニーズの80%はまかなえる (Kindle の位置No.2778-2779)」

Everlasting Diary、#C99A サークル参加断念のお知らせ

※この記事は、C99AのサークルカットQRコードから移動する先のページです。

はじめに

私?
行けないっ
でも、行けないの
(『アイの歌声を聴かせて』より)

引き続きアイうたにどっぷりのnikkieです。
年末のコミケ(C99A)参加について、大打撃(?)なお知らせ、明るみに出しちゃいます!

目次

伝えたいこと

  • 2021/12のC99AにEverlasting Diaryは通っていますが(金曜 東ト-14b)、当日は行きません
  • この判断の理由や経緯は以下に続きます。興味があればご覧ください

時系列整理:放置してたら通ってしまった

記憶が曖昧なのですが、COVID-19の感染状況を考慮し、C99Aまでに何回かの中止や延期があったと思います。

  • 2019年12月の技書博2の初サークル参加がとてもよかった1
  • コミケなるものにサークル参加してみむとす
  • 2020年12月時点でサークルカットを書いたと思われる(手元の下書きの作成日時から)
  • 2021年はPyCon JP 2021 座長に心を燃やしていた
    • C99Aの配置抽選対象にする旨のメールが来ていたが、気付いた時には辞退できない時期だった
  • 2021年11月Twitterを見ていたら、他の方の配置報告でサークルカットのスクショが目に入る。「あれ、私通ってない、これ?」(引き返せない!)

配置の「ト-14b」、トウマのト、そして14!とアイうたファンとしては歓喜でした。
通行証は家宝にします!

行かない理由1:産み出せない頒布物

当初の企画:賞味期限切れ

『推しBotデプロイガイド』というサークルカットを書いた時点では、以下をネタにするつもりだったのだと思います。

ところが、時間が経って心が離れてしまっており2、「心から書きたい」企画ではなくなってしまいました。

軌道修正試みるも、本にしたいレベルでまとまらない

今一番書けそうなのが、『アイの歌声を聴かせて』× 技術 ということで、まずはネタを作ろうといくつか実装してみた3のですが、Twitterに投稿して反応が得られたことで満足してしまいました。

「手を動かしてネタができたら後は書くだけ」だと思っていたのですが、「書くことがなくて残り時間では書けない」となってしまうのは予想外でした。
悩ましかったのは「技術同人誌(コピー本)にする上で付加価値どう付けよう」という点です。
コードはGitHubで公開4するんですよね。
そうなると「本にする意味ってなんだろう」と暗中模索でした。

行かない理由2:サトミ生誕祭とブッキング

  • C99Aのサークル入場はAM9時まで(通行証より)
  • コミケ終わりにサトミ生誕祭という楽しみ方がサポートされないスケジュール(新宿ピカデリー・立川シネマシティ)

頒布物用意が難航しているという状況も考慮して、選ばれたのは、アイうたでした。

終わりに

行かないという選択をしたあと、心が結構軽くなったんですよね。
アイうた観られるし、頒布物を用意する年末の時間をいま興味があるコードや本を読むのに使えるぞ、と。
幸せを感じた選択なので、これでいいんじゃないかなと思っています。

一年にわたる座長業、とにかくPyCon JP 2021の開催を優先して、結構消耗しました。
座長に手を挙げる際に「アウトプットの優先度は下がる」と腹をくくったので、今回の決断に後悔はないです。
回復期間に宛てて、「締切を設定して無理をするのをやりたい!」と思える時が再び来るのを待ちます。

『推しBotデプロイガイド』を楽しみにしていた方がいたら、申し訳ありません。
また先日オンライン勉強会の終わりに「次はC99Aで」と挨拶しましたが、ごめんなさい、生誕祭を優先します!(テヘペロ

ポンコツサークル主、約束されし祭に参ります!


画像はいらすとやさんより:https://www.irasutoya.com/2018/02/blog-post_101.html


  1. イベントレポート | #技書博 2で初めて一人で執筆して頒布し、いただいたフィードバックから技術同人誌の楽しさを知りました - nikkie-ftnextの日記

  2. 過去のLTで「好きな技術への想いは、自分が熱🔥を失わないうちに、伝えたほうがいい」と語ったのはやはりそのとおりでした。ref: LT補足: Stay home, but connect global Python users! #pyconhiro - nikkie-ftnextの日記(埋め込んだスライドの17ページ目)

  3. ほかには

  4. このリポジトリです:

イベントレポート | Tech BASE Okinawaに行ってきました! #TechBASEOkinawa

はじめに

こーんにーちはーーー!1
錦鯉、M-1優勝2おめでとうございます!nikkieです。

この記事は、Uzabase Advent Calendar 202120日目の記事です。
12/18(土)に沖縄で開催されたテックイベント「Tech BASE Okinawa」をレポートします!

目次

イベントの概要

Tech BASE Okinawaは、新しい技術の学びを体験できる、沖縄最大級のエンジニア向けTechイベントです。

12/18(土)に沖縄コンベンションセンターで開催されました。
このイベントの参加費は無料です。

なんで行ったの?

twadaさんはじめ、ユーザベースのFellow3トークが聞けるということで、「実質無料でしょ!」と沖縄行きを決めました。
技術好きには垂涎のイベント と感じたということです。

Pythonコミュニティで慣れ親しんだ「テックイベント遠征するなら爪痕残したいじゃん」の考えで、参加者LTにもエントリー!(これは別に書く予定です)

どんなイベントだったの?

会場入りすると、メインホールにはタイマーによるカウントダウンが。
カウントダウンが0になると、かっこいいオープニングムービーが流れ、テンション上がる!

オープニングの後は、twadaさんによる90分のキーノート!

その後、ランチタイム!
ゴーヤチャンプル美味しかったです(全然苦くなかったような・・)

f:id:nikkie-ftnext:20211220222128j:plain

午後のセッションは 4トラック 並列の3コマ!
興味のあるセッションを聞いたり、LTの準備をしたりしながら過ごしました。

LTは全部で3名。
沖縄 -> 東京(私) -> 北海道と北上する発表順でした。
リアルでやるLTは2年近くぶり、かつ、沖縄はアウェーということで、「久々の空気感を楽しもう」と臨みましたー。

その後はパネルディスカッション「沖縄テックの未来シナリオを考えよう!」。

  • 多様化するエンジニアの働き方
  • これから活躍するエンジニアに求められること
  • 沖縄の未来をどう描いていく?

フリー質問タイムには、Slidoにたくさんの質問が集まり、沖縄のエンジニアの熱を感じる 時間でした。

クロージングの後は、懇親会!!
これを読んでいる皆さん、リアルの懇親会ですよ! リ ア ル の 懇 親 会 !!

これはめちゃくちゃ楽しかったですねー。
タイムボックスは1時間なのですが大いに盛り上がり、なかなか閉まりませんでした(笑)

f:id:nikkie-ftnext:20211220222148j:plain

紅いもの天ぷら?があって、「甘い!美味しい!」って言っていたら、紅いものお土産情報も教えていただけました。

こんな感じで、たくさんインプットし、技術について語り合った1日となりました!

どんなトーク聞いたの?

聞いたトークの感想を手短にまとめます。
消化途中の部分もあり、帰りの飛行機などで原著を読み進めています。
過去に読んだ時より理解できる感覚があって楽しいんですよ、これが。
「ある程度消化したら別で記事を書きたいなー」と思っています。

堅牢なコードを書く - 例外、表明、契約による設計

twadaさんによるキーノートです。
テスト駆動開発(TDD)とペアとなる) 設計 で、コードの内側から質を上げていくという発表でした。

  • 自分以外は信頼しない防御的プログラミングより、予防的プログラミング
    • 型の制限
    • 値の宣言(Enum
    • 状態を制限して、別名参照問題に対処
    • 区間の表現(後述)
  • 中途半端に実行するより処理を失敗させる:攻撃的プログラミング
    • assert で書いて 暗黙の前提 を明示する(後述)
  • 契約プログラミング(Design by Contract: DbC
    • 失敗にはバグと例外がある
    • 失敗がバグか例外か切り分けられるようにする

区間の表現

区間・閉区間が自明でなくて分かりづらいというコードの問題点から始め、区間を表す型を作って、さらに簡単に使えるように実装するというのはお見事でした👏
日付とその日付を含むかの2属性を持った型をまず作り、そのインスタンス2つから、区間を表す型をインスタンス化するという設計アイデアは、私は一人では浮かばなかったと思うので、この場で学べてよかったです。

また、「正しい使い方を簡単に、誤った使い方を困難に4という考え方を知り、この一年くらいうっすら感じていた伸びしろがはっきりしたと思っています。
PyCon JPのスタッフ活動で実装したスクリプト群、私としてはみんな使って楽をするのが正しいと思うんですけど、なかなか使われないんですよ。
これは簡単になってないからなんだなと気づきました。

例外と表明をどう使い分ける?

  • 例外:起こるかもしれない(発生が予想できる)
  • 表明は起こり得ないことをチェック(誰も間違えない前提の表明=ありえない)

これが知られたのも収穫です。

キーノートで多く引かれたのは、以下の本です。

twadaさんの発表は資料を後追いすることが多かったのですが、実際に発表を聞くと"行間"が分かって、非常に学びがありました。
ほんと、行ってよかったです。

プログラミング言語とシステムデザイン - Clojure, Java, DDD, Clean Architecture

Fellowの矢野さんによるトークです。
システム設計の話 で、DDDやClean ArchitectureがClojureと掛け合わせるとどう意味が変わってくるかという発表でした。

  • Clean Architectureのあの図5の中央にあるUseCaseもEntityも、Clojureでは関数
    • 言語が変わったら消えてしまった
  • 👉 言語を超えて有効なものこそがシステムの基礎となるアーキテクチャ理論になる
    • 言語を超えて有効なものは、あの図ではなく、SOLID原則

私は過去あの図の箇所を中心に読んでいたのですが、矢野さんのトークをもとに『Clean Architecture』第Ⅲ部「設計の原則」(SOLID原則を扱うパート)を読み返すと、気付きがあってめちゃくちゃ面白いです。

国内におけるAIの最先端事例紹介 〜2022年のトレンド予想を添えて〜

ちゅらデータの真嘉比さんによるトークです。

最先端事例は追いきれていない感覚があった私としては、最先端の動きが掴めるトークでした。

「PyCon JP 2021やちゅらデータさん開催の振り返りではありがとうございました!」と対面でご挨拶もできました😃

終わりに

あっという間の1日でした!

会場からバスで帰る際はこのイベントで出会った方と話しながら帰ったんですが、縁によるこういう経験も2年ぶり(PyCon Singapore 2019以来)でめちゃくちゃ楽しかったです。
会食ありイベントなので準備はとても大変だったのではないかと想像しますが、参加者としてはオンサイト開催イベントのよさを満喫しました!

2022年もある予定とのことですので、興味持った方、来年一緒に遠征しましょう!

イベントで出会った皆さま、にふぇーでーびたん!

2022/01/04追記:資料公開されてます!

聞いたトークの復習や、聞けなかったトークの後追いができますね。

『エンジニアの知的生産術』で知り、 #pyconjp 2021ハイブリッド開催を言い出すきっかけになった考え方

はじめに

秘密はね、最後に明かされるんだよ。
アイの歌声を聴かせて』めっちゃいいですよね、nikkieです。

座長としてガッツリ関わったPyCon JP 2021についても秘密を明かしていこうと思います。
今回は「ハイブリッド開催」についてです。

※こちらは、PyCon JP 2021 Advent Calendarの2日目の記事ということにしちゃいます!

目次

簡潔に明かすと

  • 座長に立候補した時点ではオンライン開催でスタッフに相談する予定でした
  • 『エンジニアの知的生産術』を読んで最良の場合に何が得られるかで判断する楽観主義を知りました
  • その楽観主義で考え、最良の場合に得られるものが大きいハイブリッド開催への挑戦をスタッフに相談することにしました

2020年10月頃の考え:オンライン開催

PyCon JP 2021の座長に立候補した時点1(2020年9~10月)ではハイブリッド開催は考えていませんでした。
「2020同様オンラインでやるのを他のスタッフに相談しよう」くらいの気持ちでした。
2021年10月という開催日も決まっていなくて2、「あまり時間をかけずに形態と日を決めて、早く動き出さなければ」という気持ちでした。

最良の場合に何が得られるかで判断する楽観主義(『エンジニアの知的生産術』)

第2章「やる気を出すには」の中の「不確定要素がある場合の大小関係は?」で知った考え方です。
以下は読んで私が理解したことです。

この章自体はタスク管理について扱っています。
多くのタスクは結果が不確定で、実行してみないとどの程度のメリットが得られるか分かりません。
メリットの分散が大きいタスクもあれば、小さいタスクもあります(やってみないと分からないので、メリットについては見積りという認識です)3
そのようなタスクの中でどれを優先するかを判断する方法について論じています。

書籍で紹介されているのは「最良の場合に何が得られるかで判断する」方法です。
平均のメリットではなく、最良のメリットです。
メリットが数値化できるとして、タスクAは0〜80(平均40)、タスクBは40〜70(平均55)とします。
このとき選ぶのは、最良の場合のメリットが大きいAとなります。
では、なぜ平均を採用しないのでしょうか?(Bの方が平均のメリットが大きいですよね)

平均を採用しない理由は勘違いと関係します。
勘違いには2種類あります。

種類 説明
悲観的な勘違い 現実よりも悪い方向に勘違い 当たり確率が高いスロットマシンにも関わらず、数回の試行が全て外れた。そのために、当たり確率は0に近いと勘違い
楽観的な勘違い 現実よりも良い方向に勘違い 当たり確率が高くないスロットマシンで、数回の試行中にたまたま当たりが出た。そのため、よく当たると勘違い

ここでポイントとなるのは以下です(引用します)

悲観的な勘違いには気付くチャンスがありませんが、楽観的な勘違いにはあとから気付いて修正できるのです。

タスクのメリットを平均で比較すると、「今までに経験したことの平均で判断しているので、今までたまたま運が悪いと悲観的な勘違いをしてしまいます」。
悲観的な勘違いには気付けないので、「判断を楽観側に倒すことでバランスを取」ります。
この考え方の原理は、不確かなときは楽観的にと紹介されました。

2021年に入った時点の考え:ハイブリッド開催に挑戦したい

「不確かなときは楽観的に」、この考え方を知ってからPyCon JP 2021の形態を再び考えたとき、2020と同様にオンライン開催というのは平均で判断していて、悲観的な勘違いをしているかもしれないと気づきました。
そこで、(過去の実績はいったん脇において)最良の場合のメリットが大きい形態はなにか考え始め、ハイブリッドに至りました。

私の意見としては、2020年からのwith COVID-19はもう少し続き、PyCon JP 2019のような大型のオンサイトカンファレンスにはまだすぐには戻れないと考えています。
これまでとは違う形式のカンファレンスを模索するために、2021年にハイブリッドに挑戦することは2022以降のPyCon JPにもつながると考えました。

また、考え始めた時期は、勉強会・カンファレンスがCOVID-19以前のオンサイト開催からCOVID-19下のオンライン開催に切り替わったと言える状況で、ハイブリッドはあまり事例がない状況でした。
それを踏まえて、「エンジニアコミュニティとしても未知なハイブリッド開催に挑戦することで、Pythonコミュニティの外にも知見を共有できるというメリットがある。だから、ハイブリッド開催をやりたいと言おう」と腹をくくりました。

なお、途中で諦める可能性は最初から考慮していました。
途中で諦めるというのは最良の場合ではないですが、スタッフに知見は溜まって2022以降に活かされるので、「失敗」ではないでしょう。
また、2020と同様のオンラインカンファレンスは半年あれば準備できると実績から分かっています。
これらから、最良でない結果になることを恐れずにチャレンジしようと考えました。

最良でない結果のリスクヘッジとしては、ハイブリッド開催を小さく試すこともしました。
10/15(金)の午後半日だけとし、スタッフのリソースが厳しい4と分かったり、COVID-19感染状況を見て難しいと判断したりしたら、いつでも切り離せるようにしていました。

脱線:強化学習、そして、両利きの経営へ

「不確かなときは楽観的に」には学術的な裏付けもあるそうです。
『エンジニアの知的生産術』では、強化学習における「探索と利用のトレードオフ」が紹介されています。
このトレードオフを解決するために提案されている原理が「不確かなときは楽観的に」だそうです。

探索と利用とは

  • 探索:未経験の行動をする
  • 利用:過去の経験を活かす

と理解しました。
これらはトレードオフなので、「バランスよく実行する必要がある」そうです。
片方に偏ると

  • 「『過去の経験から一番良いと思う行動』ばかりをしていたのでは、もっと良い行動を見つけることができ」ない
    • 👉探索が足りない
  • 「『未経験の行動』ばかりをしていたのでは、過去の経験が活かせ」ない
    • 👉利用が足りない

となってしまいます。

英語では、探索はexploration、利用はexploitationですが、これを見て『両利きの経営』を思い出しました。

両利きの経営とは経営学の理論で、「成熟した大企業・中堅企業がイノベーションを起こす」ことを扱います。

企業活動における両利きは、主に「探索(exploration)」と「深化(exploitation)」という活動が、バランスよく高い次元で取れていることを指す(入山氏による解説より)

exploitationは「深化」と訳されていますね。

不確実性の高い探索を行いながらも、深化によって安定した収益を確保しつつ、そのバランスを取って二兎を追いながら両者を高いレベルで行うことが、「両利きの経営」である。(入山氏による解説より)

両利き(探索、深化)という概念のルーツは認知心理学とのことですが、強化学習にも経営学にもこれらの語が登場しているのは面白いです。

終わりに

PyCon JP 2021 ハイブリッド開催の秘密は『エンジニアの知的生産術』で知った「不確かなときは楽観的に」という考え方です!

私はこの考え方に基づき、「ハイブリッド開催にチャレンジしたい」ととにかく言い続けました5
ハイブリッド開催ということは、オンラインとオンサイトという2つの会場の準備や接続の設計が必要です。
この点は例年より大変で、大きな負荷がかかってしまったスタッフもいるかもしれません。
2日間の会期を終え、ハイブリッド開催を実現させたスタッフチームにはめちゃくちゃ感謝しています。
ありがとうございました!

参加した方からの、ハイブリッド開催への感想も嬉しく拝見しています。
ありがとうございます!

『エンジニアの知的生産術』を読んでいなかったら、いま感じている「ハイブリッド開催を実現した!」という手応えはなかったと思います。
一冊の本との出会いが、「堅実に(=未知を回避して)タスクの平均からオンライン開催」という私の考え方を変え、劇的な体験をさせてくれました!
こういうことがあるから読書って刺激的だなと思います。


  1. PyCon JP Blog: PyCon JP 2021 座長決定

  2. 決まったのは、(仮ですが)2021年2月です。PyCon JP Blog: PyCon JP 2021 カンファレンス開催日程仮決定のお知らせ

  3. 本の中にベル形の分布が出てきます。この図が「メリットの分散」をつかみやすいと思います。

  4. デスマーチにしたくない(持続可能な形にしたい)という想いもあり、リソース的にハイブリッド開催準備が厳しくなったら諦める考えでスタッフにも共有していました。このあたりは書けたら別の記事にします。

  5. 座長になって数ヶ月して、(2021のチームを機能させるには)「座長は〇〇したい」を言うのが必要なんだなと悟ったというのもあります(ただこれはシルバーバレットではないと思っていて、来年も座長が同じように動くのがベストかは分かりません)

JavaScriptにおける連想配列でハマりました。全てをお話しします

はじめに

秘密はね、最後に明かされるんだよ。
アイの歌声を聴かせて』めっちゃいいですよね、nikkieです。

Python大好き(=極振り)、裏返せば他の言語は全然書けない私、このたびJavaScriptでめっちゃハマりました。
それをネタに記事を書きます。

連想配列(キーと値のペアのデータ型)でハマりました。

目次

参考書籍

連想配列について3-4を参照しました。
読んだ上での私の理解を以下で書いています。

この本は何か1つプログラミング言語を使っている方が読むと、JavaScriptの特徴がつかみやすいのではないかと思います。
分厚くカバー範囲が広い感じで、リファレンス的に使えそうです。
「本を読んで入門するのが好きな方は、立ち読みして確認してみては」と思いました。

以下のJavaScriptのコードは、ブラウザの開発者ツールのコンソールで確認しています。
Pythonインタプリタの対話モードみたいでした。
コンソールだとletconstなしでも動くんですねー

JavaScriptにおける連想配列

Pythonでお世話になっている連想配列といえばdict
では、JavaScriptにおける連想配列1は?

以下の2つがあるそうです:

  • オブジェクト
  • Mapオブジェクト

JavaScriptのオブジェクト

名前と値をペアで持ちます。

obj = {apple: 100, "awesome banana": 50}

名前と値のペアは「プロパティ」と呼ばれ、オブジェクトのプロパティへのアクセスは次の2通りがあります。

  • ドット
  • []
obj.apple
// 100
 
obj["awesome banana"]
// 50

Mapオブジェクト

連想配列のオブジェクトでキーと値がペアになっています。
上で紹介したオブジェクトより処理性能がいいそうです。

map = new Map([["apple", 100]])
map.set("awesome banana", 50)

Mapの値はgetメソッドで取得します。

map.get("apple")
// 100
 
map.get("awesome banana")
// 50

ハマった箇所:Mapにオブジェクトのプロパティのアクセスを適用しても値は取れない

Mapの値をオブジェクトのプロパティのようにアクセスしようとして、だいぶハマりました。

map.apple
// undefined

map["awesome banana"]
// undefined

Mapの値の取得はgetメソッドです。
Mapもオブジェクトも連想配列として使えますが、Mapの値の取得はオブジェクトのプロパティの値の取得とは別物です。

[余談1] JavaScriptのオブジェクトの書き方

Pythonではできない書き方ができて、面白い!と思いました。

  • obj = {apple: 100}のように、名前に変数を指定できる
  • 変数があるとき、それを書くだけで、変数の名前と指す値からプロパティが作られる
lemon = 150
obj2 = {lemon}
obj2["lemon"]
// 150

[余談2] JSON: JavaScript Object Notation

Pythonを使うときは、標準ライブラリのjsonimportしまくっています。
JavaScriptのオブジェクトに入門したことで「JSONはまさしくJavaScriptのオブジェクトを表す文字列なんだな」と気づきました。

// オブジェクトを作るときの変数の扱いによらず、JSONとしては同じになる
JSON.stringify({apple: 100, "awesome banana": 50}) == JSON.stringify({"apple": 100, "awesome banana": 50})
// true

名前が文字列であるか否かによらず、JSONでは名前にはダブルクォートが付きます。

終わりに

JavaScriptの2つの連想配列、オブジェクトとMapとで、値へのアクセスの仕方が異なることが理解できていなくてハマりました。

  • オブジェクト:ドットまたは[]
  • Mapgetメソッド

慣れていないプログラミング言語を使うとこういうハマり方は付き物だと思いますが、ハマった分理解も深まり、普段使いの言語を見つめ直す機会にもなりますね。


  1. GASのコードのリファクタリングで必要になったのですが、対象のコードの話はまたどこかで