Skip to main content

ログ取得ツール

Category: プログラミング

GitHub Copilotについて

無料プランの宣伝が来ていたのと、vscodeをアップデートしたら勝手に拡張が入って上部にアイコンが現れたので、ちょっと有効化して使ってみることにした。何日か使ってみた感想。

思った以上に賢いのは分かった。ただ、もちろん思った通りのものが出てくるわけじゃないので、提案を無考慮で受け止めるような習慣がつくとやばいね。

chatでリファクタリングを頼むと普通に上手いと思う。好みの部分で自分とは違ったりするけど、悪くない。だけど入力に対して結果が異なるような変更提案が来ることもある。それなりのレベルでチェックしてくれるunittest書いてからじゃないと、既存コードをいじらせるのはまずいと感じる。やり始めると、とてもじゃないが受け止めきれないスピードと分量になって目で問題を見つけられなくなってしまう。

あとは、適切なログを出してよ、みたいな頼みはよく聞いてくれる。平均的な人間と比べても、割とマシなログを出す。このへんは人間にもセンスのない奴がいるからね。えらいぞコピ郎。

DO NOT EDITを解するエディタ

自動生成されるソースとかには、先頭のコメントに「DO NOT EDIT」のような注意書きが書かれることがある。

こないだ自分のソースに含まれる、GoMockで生成したunittest用のモックファイルで引数の型が変わったのでunittestを通すためには修正が必要になった。で、vscodeでチャチャッと修正したら、見慣れない警告が出たんです。

見てみると先頭に「DO NOT EDIT」の記述が。なるほどと思って手で入れた修正をキャンセルして再度自動生成して差し替えたら、無事にビルドも通るように。

しかしこの警告は新しいなと思って。言われてみれば納得の便利さだ。DO NOT EDITは別にファイルのパーミッションがread onlyなわけではなくて、単なる紳士協定のお願いみたいなもんなんだけど、いちいち見ないことが多い。ビルドエラーの出力をクリックしてジャンプして開かれた場所にちょうどよくDO NOT EDITが書かれているわけではなくて、ファイルの先頭付近という滅多に見ないエリアに書かれているわけだ。そういうの読むのは機械側の仕事だよね。ここまで来たら、エディタはそのファイルをread onlyで開いて欲しい気もする。

レイトレ (2)

(1)の続き。POV-Rayで同じ画像を出そうとしてみた。POV-Rayというのは最近の若い人は知らないかもしれませんが、私が子供の頃からあったレイトレ言語のコンパイラみたいなソフトウェアで、今からすると非常に貧弱だった当時のマシンでも頑張ってレイトレしてくれていた。懐かしいなー。

PythonでPOV-Rayの言語構造を出力するライブラリでもないかなーと思って探してみたら、vaporyっていうモジュールが見つかった。

作ってみたのがこれ→ https://github.com/wtnb75/rtow1/blob/master/povray/where-next.py

やってみると、PyPyで1時間以上かかっていた画像はサイズを増やしても1秒と経たずに出力される。POV-Rayすげーなおい。圧倒的ではないですか。

なるほど。

まあでも、もうちょっと時間を見つけてPythonで次章(次週)も頑張りますよ。序盤から「君がこの道を選んだからには…」みたいな文が出てきていて、割と熱くなれそうだしね。

GitHub Actions、かなり最高に近いけど

最近GitHubでActionsを使っています。個人開発の話です。会社ではちょっと違うやつを使ってるんで。

Actionsはベータに当選(?)して使えるようになってから少し使って、本リリースされたあと最近になって頻繁に使うようになった。過去に作ったPython関連のリポジトリに適用して回る作業をしたり。これで、PyPIにリリースしたりgithub-pagesにビルド結果を上げたりといったタスクが非常に簡単になった。

自分が使っているルールは割と単純で、

  • masterに上げる=github pagesにビルド結果が上がる
    • 最近peaceiris/actions-gh-pages@v2にforceOrphanオプションができて、gh-pagesに履歴を残さない設定が可能になった
  • ブランチに上げる=テストが走り、PRのページにCIの結果が上がる
  • 先頭に「v」を使ったタグがつく=vを抜いたバージョンのwheelを作ってPyPIにアップロード

って感じ。バージョン番号は適当に0.0.1からインクリメントしているという、まあ、、不良開発者です。本当はセマンティックバージョニングにするのがいいんでしょうけどね。非互換があっても末尾インクリメントだけ! だってユーザほとんどいない&カネもくれないのに、めんどくさいじゃん。

Rustの印象

何を隠そうこの私、はるか昔の転職前はCとC++を主戦場にしていましたが、転職後はRuby, Python, Golangあたりで生きてきました。あとSQLとクソJavaか。

自宅ではMacとRaspberry Piで動かしたいことも多いし、普通にdocker(つまりLinuxね)の中で動かしたいこともある…となるとGolangがベストかなぁと思っていたんです。

一方で、Golang(マルチプラットフォーム、シングルバイナリのデプロイ)に対抗しうる存在として、Rustという言語があります。手続き型でシステムプログラミング向けに使えて、割と独特の考え方で安全側に倒したもの…ということで、Golangにあるようなユルさがないので共通の住人は少ないという印象。似た感じだとJuliaだのNimなんかも悪くはないんでしょうけど。

で、私は最近Rustも使ってみてるんですよ。そして今後もちょくちょく使っていこうと思っているんですよ。

…ということで、ファーストインプレッションを以下に。

Pythonのclickで共通オプション

またもやPythonねたです。前回言っていたimportのフォーマットは、dirimportという形で公開してみました。pip install dirimport …使ってみてもらえればと。今後の自分のrequirements-dev.txtの常連になるか?

今回はclickについて。我々の日常はPythonでclickを使ってサブコマンドを作っていく毎日だが、、、そこにはパターンというものがある。

まずはサブコマンドの定型句がある。君たちはこれを何度書かせるつもりかね?

@click.group(invoke_without_command=True)
@click.pass_context
def cli(ctx):
    if ctx.invoked_subcommand is None:
        print(ctx.get_help())

で、こんな感じでサブコマンドを書いていく。

@cli.command()
@click.option("--verbose/--no-verbose")
def subcmd(verbose):
    pass

ここで、サブコマンド間の共通のオプションっていうものが考えられる。これは毎回いくつもの共通オプションを書くのはめんどくさいので、1つのデコレータにマージするテクニックがある。デコレータ…毎回ググらないと書ける気がしないやつね。

_common_options = [
  click.option("--verbose/--no-verbose"),
  click.option("--arg1"),
  click.option("--arg2"),
]

def common_option(func):
    for option in reversed(_common_options):
        func = option(func)
    return func


@cli.command()
@common_option
@click.option("--other-option")
def subcmd(verbose, arg1, arg2, other_option):
   pass

しかし全部のサブコマンドに共通ってわけじゃなくて、複数の共通セットがあってサブコマンドの種類ごとに変わります、みたいなことになると、、、これで個別にデコレータを増やしていくのもきつくなる。どうするか…

Pythonを使った開発について

最近はPythonを使うことが多くなった。一時期はGolangという感じだったが、やはりPythonは偉い。仕事でもJavaを追放してPythonに絞っていこうとしている。このブログでもPythonネタをたまにぶち込むと思いますがよろしくお願いします。

Pythonで何かを開発するときに、まあベストプラクティス的なものがいくつかあるんだけど、そのうちの「ディレクトリ構造」に関して、自分としてはこれがいいんじゃないかな、というのが掴めてきたので、紹介します。世の中で言われているものとはちょっと異なりますので、その点はご了承ください。

まず、リポジトリのトップディレクトリに.envrcというファイルを置いて、中身は1行、[ -f bin/activate ] && source bin/activate と記します。direnvとvenvを使うというわけですね。この1行だけで、見る人が見れば分かるしdirenvを使ってる人は必ず気づく。

.gitignoreにはbin, lib, share, __pycache__, pyvenv.cfg などを入れていきます。

pyobjcの衝撃

最近知ったんですが、Pythonにpyobjcというモジュールがあると。で、macOSのAPIをたくさん叩くことができるらしいと。

ちょっと試してみたら、、、

import Foundation
Foundation.NSLog(“hello”)
2018-12-07 21:42:46.620 python[66235:6413829] hello

何これ?? すごくない? Pythonのログハンドラだってこの通り。

import logging  
import Foundation  
  
class NSHandler(logging.Handler):  
    def emit(self, record):  
        Foundation.NSLog(self.format(record))  
  
if __name__ == "__main__":  
    logging.basicConfig(level=logging.INFO, handlers=[NSHandler()])  
    log = logging.getLogger(__name__)  
    log.info("hello")

こいつは、何かと楽しめるかもしれないねー。いろんなモジュール(なんちゃらKit)に対応しているみたいだし、試してみようっと。

Golangのcgo呼び出しのコスト

cgoでC言語の関数を呼び出す場合、普通の関数呼び出しの10倍くらいコストがかかりますね。ffiと同じくらいと見ればいいのかな? もっとかかってるのかも。

例えばこんな感じにしておいて、、、

https://gist.github.com/wtnb75/08aefa2a10208ccbf53b5e4cb105a1cc

MBPで試してみると、こんな感じでした。

BenchmarkCSin-4 20000000 106 ns/op
BenchmarkSin-4 100000000 22.8 ns/op
BenchmarkMSin-4 100000000 20.0 ns/op

cgoを使って呼び出すことで5倍のコストがかかる。あとGolangの中で関数呼び出しが1段増えるだけだと3ns程度しか増えないのね。ライブラリ関数の呼び出しは内部関数の10倍、cgoはさらにその5倍…とかいう話なのかなこれは。

Golangはテストコードの中で直接cgoを使うことができない、という制約もあります。最初はxxx_test.goをいきなり作って、そこでcgoを使おうと思ったんですけど…直接cgoで書けないので、main.goを書く羽目になりました。

Golangで処理時間の短いC言語のライブラリを呼ぶのは得策ではなく、移植した方が良いだろうって話。

Pythonの__pycache__

ごく簡単なプログラムをPythonで書いたとして、テストを書くほどでもない…でもちょっと走らせたい部分があるのだが…そういう時はdoctestという仕組みが便利。

関数やクラスのところに文字列で、REPLのところをコピペした感じで書いておく。

"""
>>> 1+2
3
>>> foo("test")
"'test'"
"""
def foo(bar):
    return repr(bar)

↑こんな感じね。で、

  • python -m doctest your-file.py

みたいな感じで実行すると、テストしてくれる。-vとかをつけて実行するといちいち結果を表示してくれるという優れもの。

こういう美味しい話には穴があって、doctestを走らせると、doctestが該当ファイルをモジュールとして読み込むためか、__pycache__ディレクトリができてしまうのだ。

# ls
test.py
# python test.py
# ls
test.py
# python -m doctest test.py
# ls
__pycache__ test.py

これはダサい。

解決法は、.zshrcや.bashrcにこう書いておく。

  • export PYTHONDONTWRITEBYTECODE=1

すると、__pycache__の生成は抑制される。