HEAD /
name | last modified | size | description |
---|---|---|---|
archives/ | 2020-09-21 19:18 | ||
codes/ | 2011-04-22 02:17 | ||
images/ | 2015-06-18 01:22 | ||
patches/ | 2015-04-07 00:11 | ||
tags/ | 2016-06-25 11:45 |
HEAD / archives / 2020-09 / 2020-09-21.rst
Wayland 環境でマウスのボタンをキー入力にマッピングする方法
Wayland (libinput?) な環境で、マウスのボタンをクリックした時に、任意のキー入力がされたことにしたい。 hwdb の設定を上書きすればできそう。
例えば、自分の使っているマウスには「戻る」「進む」ボタンがあるが、これをそれぞれ "Home", "End" キーという事にする場合。
/proc/bus/input/devices を眺めて、マウスの名前と(現在の) event 番号を調べる:
... N: Name="Logitech MX Ergo" ... H: Handlers=sysrq kbd mouse0 event1 leds ...
ここでは名前は Logitech MX Ergo で、 event 番号は 1
evtest コマンドで、マウスのボタンに対応するコード(?)を調べる:
$ sudo evtest /dev/input/event1 ... Testing ... (interrupt to exit) Event: time 1600678269.823814, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90004 Event: time 1600678269.823814, type 1 (EV_KEY), code 275 (BTN_SIDE), value 1 Event: time 1600678269.953810, -------------- SYN_REPORT ------------ Event: time 1600678272.895815, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90005 Event: time 1600678272.895815, type 1 (EV_KEY), code 276 (BTN_EXTRA), value 1 Event: time 1600678272.895815, -------------- SYN_REPORT ------------
「戻る」ボタンが 90004, 「進む」ボタンが 90005
/etc/udev/hwdb.d/99-local.hwdb をこんな感じに書く:
evdev:name:Logitech MX Ergo:* KEYBOARD_KEY_90004=end KEYBOARD_KEY_90005=home
hwdb の更新 & 再読込:
$ sudo systemd-hwdb update && sudo udevadm trigger /dev/input/event1
HEAD / archives / 2019-06 / 2019-06-23.rst
ipython で twisted の deferred を await する
気付いてなかったけど、 ipython で await できるようになっていた 。
ドキュメントを読むに、なんか関数書けば対応を足せそうだったので、 twisted の deferred を await できるようにしてみる。
twisted は asyncio と違って、一度 reactor を動かしてしまうと止められなくなってしまうので、そのへんいい感じにやってくれる crochet を利用しつつ、以下のようなコードを置いておく。
$HOME/.ipython/profile_default/startup/99-twisted_runner.py:
try: import crochet from twisted.internet import defer except ImportError: pass else: def twisted_runner(deferred): crochet.setup() result = crochet.run_in_reactor( lambda: defer.ensureDeferred(deferred))() return result.wait(2**31)
後は %autoawait twisted_runner で有効にする。
こんな感じ:
In [1]: %autoawait twisted_runner In [2]: import treq In [3]: r = await treq.get('http://localhost') In [4]: data = await r.text() In [5]: print(data) <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> ...
HEAD / archives / 2018-12 / 2018-12-26.rst
terminal でカーソルを動かす方法
よくある手法として \r でカーソルを行頭に戻すというのがあって、例えば以下のような感じにして進捗表示に棒を回したりするけど:
$ spinner=('-' '\' '|' '/') $ for i in {1..100}; do > echo -n "${spinner[$(($i % 4))]}" > echo -ne '\r' > sleep 0.1 > done
しかし、じゃあコレを縦に3つ並べて回したい、となった時に \r だとどうしょうもないので、カーソルをもっと自由に動かす方法。
端末ごとに適切なエスケープシーケンスを吐いてやればカーソルを動かせる。 何が適切かは terminfo データベースに書いてある。
シェルからは tput コマンドを使って名前から対応するものを出力できる。 ということで、縦に3つ並べるのはこうなる:
$ spinner=('-' '\' '|' '/') $ for i in {1..100}; do > echo "${spinner[$(($i % 4))]}" > echo "${spinner[$(($i % 4))]}" > echo "${spinner[$(($i % 4))]}" > tput cuu 3 > sleep 0.1 > done
(tput cuu 3 でカーソルを3つ上に動かす。その他、何が出来るかは man terminfo を後で読む。)
python だと curses モジュールで同様のことが出来る:
import curses import sys import time SPINNER = list(r'-\|/') curses.setupterm() for i in range(100): print(SPINNER[i % 4]) print(SPINNER[i % 4]) print(SPINNER[i % 4]) sys.stdout.buffer.write(curses.tparm(curses.tigetstr('cuu'), 3)) sys.stdout.buffer.flush() time.sleep(0.1)
HEAD / archives / 2018-09 / 2018-09-08.rst
ipython で jedi での補完を有効にする方法
ipython は 6.0 から jedi での補完が出来るようになったのだけど、なんかその後に問題が見つかったとかでデフォルト無効になっている。 それを有効にする方法。
以下のような設定を置けば良い。
~/.ipython/profile_<name>/ipython_config.py:
c = get_config() c.Completer.use_jedi = True
HEAD / archives / 2018-07 / 2018-07-30.rst
flatpak を使って python application をパッケージングしてみる試み、その3
前回 は lxml のビルドにコケるのでしょうがなく自前で runtime を用意するということをしたのだけど、 現在は wheel が提供されるようになったので、そのような頑張りは不要となった。
ということで このような yaml を用意した上で、 flatpak-builder を実行すれば普通にアプリケーションがビルドできる:
$ flatpak-builder ../konnyaku-build org.u7fa9.konnyaku.yaml
ビルド後に --run オプションを付けて再実行すると、アプリケーションの動作確認が出来る:
$ flatpak-builder --run ../konnyaku-build org.u7fa9.konnyaku.yaml konnyaku
問題無さそうなら --repo を指定して、ビルド結果を repository に書き出す:
$ flatpak-builder --force-clean --repo=~/repos/flatpak/u7fa9.org ../konnyaku-build org.u7fa9.konnyaku.yaml
HEAD / archives / 2017-09 / 2017-09-23.rst
flatpak で linux 版 steam client を動かす
linux 版の steam client は、 ubuntu 用のバイナリしか無い(らしい)とか、 32bit 版のライブラリが要求される(らしい)とかで、特にちょっとマイナーなディストリにインストールするのはクソめんどくさい(らしい)。
ということで、 flatpak を使って動かす。
steam client は flathub にあるので、まずはこの repository を追加する:
$ flatpak --user remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
そしたら steam client をインストール:
$ flatpak --user install flathub com.valvesoftware.Steam
で、実行:
$ flatpak run com.valvesoftware.Steam
HEAD / archives / 2017-05 / 2017-05-20_1.rst
rust で WSGI サーバーを書いてみる
cpython crate は rust から python インタプリタを呼び出すことにも使うことができる。
ということで、試しに雑 WSGI サーバーを書いてみた。
https://github.com/nakamuray/wsgi-server
取り敢えずテキトウに用意した django アプリケーションの admin 画面が表示される程度には動いたので、満足。
HEAD / archives / 2017-05 / 2017-05-20.rst
rust で python 拡張モジュールを作る方法
cpython crate を利用することで、 rust で python 拡張モジュールを簡単に書くことができる。
また、 setuptools-rust を利用することで、そうして書かれたモジュールを含む python パッケージを簡単に書くことができる。
ということで、試しにフィボナッチ数を計算する拡張モジュール (関数) を書いてみた。
https://github.com/nakamuray/fib
せっかくなのでマルチスレッドで並列計算させるようにしてみたけど、ちゃんと CPU 400% とか使えてるようなので、満足。
HEAD / archives / 2017-02 / 2017-02-20.rst
flatpak に入れた python アプリケーションの起動がやけに遅い
先日 flatpak に python アプリケーションを入れてみたわけだけど、 flatpak をかぶせることによる起動コストの増加はどんなもんだろうか、 と気になったので、 time を使ってテキトウに計ってみる。
普通に実行した場合:
(venv) $ time konnyaku --help Usage: konnyaku [OPTIONS] COMMAND [ARGS]... Options: --debug / --no-debug --db-debug / --no-db-debug --help Show this message and exit. Commands: add add site check check site updates links list links list list sites modify modify site oneshot add & check but not save remove remove site show show site information konnyaku --help 0.74s user 0.06s system 99% cpu 0.806 total
0.8秒くらい。
続いて flatpak に入れたヤツ:
$ time flatpak run org.u7fa9.konnyaku --help Usage: konnyaku [OPTIONS] COMMAND [ARGS]... Options: --debug / --no-debug --db-debug / --no-db-debug --help Show this message and exit. Commands: add add site check check site updates links list links list list sites modify modify site oneshot add & check but not save remove remove site show show site information flatpak run org.u7fa9.konnyaku --help 0.06s user 0.02s system 2% cpu 2.899 total
2.9秒くらい。
思ってた以上に遅い。何だこれ。
調査
ひとまず python ではなくて、明らかに一瞬で実行が終わるはずのコマンドを実行させてみる:
$ time flatpak run --command=/bin/true org.u7fa9.konnyaku flatpak run --command=/bin/true org.u7fa9.konnyaku 0.06s user 0.02s system 36% cpu 0.229 total
0.2秒くらい。これが flatpak を立ち上げるのに必要なコスト。じゃあさっきの2秒近い差はどっから出てきた?
python アプリケーションで起動が思いのほか遅いというような場合、 .pyc ファイルを作成し忘れたケースが考えられる。 flatpak では runtime/application は read-only で mount されるため、パッケージング時に含めそびれると、実行時には作成できない (作成しても保存できない) ので、毎回 .py ファイルを読み込むことになる。
ファイルがあるか確認してみる。
$ flatpak run --command=/bin/sh org.u7fa9.konnyaku ~ $ ls -l /app/lib/python3.5/site-packages/konnyaku/ total 36 -rwxr--r-- 231 nakamuray nakamuray 0 Jan 1 1970 __init__.py drwxr-xr-x 2 nakamuray nakamuray 8 Jan 1 1970 __pycache__ -rwxr--r-- 2 nakamuray nakamuray 6281 Jan 1 1970 cli.py -rwxr--r-- 2 nakamuray nakamuray 1599 Jan 1 1970 concurrentutils.py -rwxr--r-- 2 nakamuray nakamuray 39 Jan 1 1970 exceptions.py -rwxr--r-- 2 nakamuray nakamuray 3287 Jan 1 1970 models.py -rwxr--r-- 2 nakamuray nakamuray 3594 Jan 1 1970 tasks.py ~ $ ls -l /app/lib/python3.5/site-packages/konnyaku/__pycache__/ total 31 -rwxr--r-- 2 nakamuray nakamuray 136 Jan 1 1970 __init__.cpython-35.pyc -rwxr--r-- 2 nakamuray nakamuray 6502 Jan 1 1970 cli.cpython-35.pyc -rwxr--r-- 2 nakamuray nakamuray 1915 Jan 1 1970 concurrentutils.cpython-35.pyc -rwxr--r-- 2 nakamuray nakamuray 310 Jan 1 1970 exceptions.cpython-35.pyc -rwxr--r-- 2 nakamuray nakamuray 3957 Jan 1 1970 models.cpython-35.pyc -rwxr--r-- 2 nakamuray nakamuray 3004 Jan 1 1970 tasks.cpython-35.pyc
ちゃんとある...が、なんだこのタイムスタンプは?
原因
どうやら flatpak (が内部で使ってる ostree) はファイルの更新日時等々を保存しないらしく、 そのためそこからファイルを取り出すと 1970-01-01 00:00:00 (UTC), unix time で "0" が更新日時として設定されるらしい。
さて、 python は .pyc ファイル中に元のソースファイルの更新日時を保存していて、 これが現在のソースファイルの日時と異なっている場合はファイル内容に変更があると見なし、 .pyc を読み込まないようになっている。
で、全てのファイルの更新日時が 1970-01-01 00:00:00 に 変更 されてしまったため、 全ての .pyc ファイルでこのチェックに引っかかるようになってしまい、 また先述したように新規で .pyc ファイルを作成することも出来ないので、 結果として毎回 .py ファイルを読み込むようになってしまっているようだ。
その分が起動時間の差に現れていると考えられる。
対策
.pyc ファイル中に記録されている更新日時情報が 1970-01-01 00:00:00 を指すように、データを書き換える。
この情報は 4-7 bytes の範囲に書かれているようなので、 dd を使ってゼロ埋めしてやればよい。
ということで、前回のパッケージングスクリプトに以下のような変更を加えて作り直す:
diff -r 43abc9291d3b flatpak/build-konnyaku-runtime.bash --- a/flatpak/build-konnyaku-runtime.bash Mon Feb 20 15:21:54 2017 +0900 +++ b/flatpak/build-konnyaku-runtime.bash Mon Feb 20 21:23:24 2017 +0900 @@ -53,6 +53,10 @@ run_in_rootfs apk update run_in_rootfs apk add "${RUNTIME_PACKAGES[@]}" || true +# XXX: fix .pyc's timestamp information to match "1970-01-01 00:00:00", +# files checked out from ostree will be. +run_in_rootfs find /usr/lib/python3.5 -name "*.pyc" -exec dd if=/dev/zero of="{}" bs=4 seek=1 count=1 conv=notrunc ";" 2>/dev/null + # create sdk based on runtime cp -a "${rootfs_dir}" "${sdk_dir}"/ diff -r 43abc9291d3b flatpak/build-konnyaku.bash --- a/flatpak/build-konnyaku.bash Mon Feb 20 15:21:54 2017 +0900 +++ b/flatpak/build-konnyaku.bash Mon Feb 20 21:23:24 2017 +0900 @@ -22,5 +22,10 @@ flatpak build "${build_location}" python3 -m venv /app flatpak build --share=network "${build_location}" /app/bin/pip install -U pip flatpak build --share=network "${build_location}" /app/bin/pip install "${URL}" + +# XXX: fix .pyc's timestamp information to match "1970-01-01 00:00:00", +# files checked out from ostree will be. +flatpak build "${build_location}" find /app -name "*.pyc" -exec dd if=/dev/zero of="{}" bs=4 seek=1 count=1 conv=notrunc ";" 2>/dev/null + flatpak build-finish "${BUILD_FINISH_ARGS[@]}" "${build_location}" flatpak build-export "${REPOSITORY}" "${build_location}" "${VERSION}"
結果
$ time flatpak run org.u7fa9.konnyaku --help Usage: konnyaku [OPTIONS] COMMAND [ARGS]... Options: --debug / --no-debug --db-debug / --no-db-debug --help Show this message and exit. Commands: add add site check check site updates links list links list list sites modify modify site oneshot add & check but not save remove remove site show show site information flatpak run org.u7fa9.konnyaku --help 0.06s user 0.02s system 7% cpu 1.139 total
直接実行した場合の0.8秒と比べて、0.3秒くらいの差。 true を実行した時の0.2秒にかなり近くなった。
余談
flatpak-builder にはコレと同様の対応処理が入っているようなので、 コイツで python アプリケーションが普通にパッケージングできるようになれば、こんなこと気にしなくてよくなるのかも。
HEAD / archives / 2017-02 / 2017-02-10.rst
Jinja2 の async support を試した
Jinja2 にバージョン 2.9 で async support が入ったとのこと。
- Environment 作成時に引数 enable_async=True を渡すことで有効になる
- テンプレート中の関数呼び出しが、 (結果が coroutine であれば?) 自動で await されるようになる
- Template.render_async() を呼ぶことで、テンプレートをレンダリングする coroutine が得られる
ということで動かしてみる:
import asyncio import jinja2 # テキトウに非同期な関数を用意する async def do_something(value: int) -> int: await asyncio.sleep(1) return value + 1 async def main(): # `enable_async=True` を渡して Environment 作成 env = jinja2.Environment(enable_async=True) env.globals.update({ 'do_something': do_something, }) tmpl = env.from_string(''' {#- テンプレート中では、非同期関数は普通の関数と同じように呼び出せる #} result: {{ do_something(1) }} ''') # .render_async() で coroutine が得られる coro = tmpl.render_async() print(repr(coro)) # coroutine を await することでレンダリング結果が手に入る txt = await coro print(txt) if __name__ == '__main__': loop = asyncio.get_event_loop() loop.run_until_complete(main())
実行結果:
<coroutine object Template.render_async at 0x7ff2038688e0> result: 2
さて、ドキュメントには asyncio がどうのこうのと書いてあるが、 これは Template.render() などの同期的なメソッドが呼ばれた際に、 内部で asyncio の event loop を取得して coroutine を実行し、結果を得ているという話っぽい。
なので、 .render_async() などを呼んでいる分には、 asyncio 以外の event loop でも使えそう。
ということで twisted と組み合わせて動かしてみる:
import jinja2 from twisted.internet import defer def sleep(delay: int) -> defer.Deferred: d = defer.Deferred() from twisted.internet import reactor reactor.callLater(delay, d.callback, None) return d # テキトウに非同期な関数を用意する async def do_something(value: int) -> int: await sleep(1) return value + 1 # Deferred を返す版も試しておく def do_something_deferred(value: int) -> defer.Deferred: d = sleep(1) d.addCallback(lambda _: value + 1) return d async def main(): # `enable_async=True` を渡して Environment 作成 env = jinja2.Environment(enable_async=True) env.globals.update({ 'do_something': do_something, 'do_something_deferred': do_something_deferred, }) tmpl = env.from_string(''' {#- テンプレート中では、非同期関数は普通の関数と同じように呼び出せる #} result: {{ do_something(1) }} {#- Deferred を返す関数も同様に、普通に呼び出せる #} result from deferred: {{ do_something_deferred(1) }} ''') # .render_async() で coroutine が得られる coro = tmpl.render_async() print(repr(coro)) # coroutine を await することでレンダリング結果が手に入る txt = await coro print(txt) if __name__ == '__main__': from twisted.internet import task task.react(lambda _: defer.ensureDeferred(main()))
実行結果:
<coroutine object Template.render_async at 0x7f16bbea5ca8> result: 2 result from deferred: 2