読者です 読者をやめる 読者になる 読者になる

愛と勇気と缶ビール

ふしぎとぼくらはなにをしたらよいか

Pythonで async def / def 両対応のデコレータを書く

タイトルの通り。

単純に以下の様なdecoratorを書くと、async defをラップできない。

from functools import wraps
from datetime import datetime

def timetrack(func):
    @wraps(func)
    def inner(self, *args, **kwargs):
        start = datetime.now()
        return_value = func(self, *args, **kwargs)
        end = datetime.now()
        self.logger.info('%s takes %s sec', func.__name__, (end - start).total_seconds())
        return return_value
    return inner

かといって次のように書くと、今度は普通のdefが勝手にcoroutine functionになってしまって困る。

from functools import wraps
from datetime import datetime

def timetrack(func):
    @wraps(func)
    async def inner(self, *args, **kwargs):
        start = datetime.now()
        return_value = await func(self, *args, **kwargs)
        end = datetime.now()
        self.logger.info('%s takes %s sec', func.__name__, (end - start).total_seconds())
        return return_value
    return inner

なので、asyncio.iscoroutinefunctionを使って以下のように分岐するといける。

from functools import wraps
from datetime import datetime

def timetrack(func):
    if asyncio.iscoroutinefunction(func):
        @wraps(func)
        async def async_inner(self, *args, **kwargs):
            start = datetime.now()
            return_value = await func(self, *args, **kwargs)
            end = datetime.now()
            self.logger.info('%s takes %s sec', func.__name__, (end - start).total_seconds())
            return return_value
        return async_inner
    else:
        @wraps(func)
        def inner(self, *args, **kwargs):
            start = datetime.now()
            return_value = func(self, *args, **kwargs)
            end = datetime.now()
            self.logger.info('%s takes %s sec', func.__name__, (end - start).total_seconds())
            return return_value
        return inner

一般化したユーティリティみたいのを書けないこともない気がするけど、今回そこまでのモチベーションはなし。

Pythonのasyncioで(結果を待たない非同期実行|投げっぱなしジャーマン|fire and forget)する

Pythonは3.5から、C#みたいなasync/awaitを用いた非同期処理が可能になっている。

これ系のパターンでは「非同期処理を待つ」ためにawaitキーワードを使い、「(awaitを含む) 非同期処理が行われる関数をマークする」ためにasyncキーワードを使う。非同期処理の終了は行儀よくawaitで待ち受けるのが普通だが、時には単に非同期処理を投げっぱなしにしたいこともある。ログを書くとか、通知を飛ばすとか。

C#においてはasyncかつ返り値をvoidにすればawaitせずにコールできる非同期メソッドが書ける(逆に言うと、awaitする必要がある非同期メソッドはTaskかTaskを返すということになっている)のだが、pythonだとぱっと見どうすれば同じことが出来るのか分からない。その上、われわれ日本人にはどういう単語で検索すればいいかよくわからない。

知ってる人は「fire and forget」とかで検索すればいいのだが、これは一度どこかで見て覚えていなければどうしようもない類の知識なので、わざわざ日本語で検索しやすいようにとこんな記事を書いているのだ、という長い前置き。

次のようにすればオッケー。(以下のコードはそのままコピペして動くものではなく、あくまで「例」)

import asyncio

async def hogehoge():
     pass

asyncio.ensure_future(hogehoge())

使いようによってはensure_futureには落とし穴があったりするんだけど、それはまた別の機会に。

Fire OS 5.1.4でmicroSDカードにKindle本を保存できるようになってた

f:id:zentoo:20160609214328p:plain

分かる人にとってはタイトル見ただけで「アッー」という内容。

一応説明をすると、Kindle Fire系で安めの端末は内蔵ストレージの容量の少なさがネックになっていた。一応microSDカードで増設できたものの、「Kindle本は保存できない」「アプリも一部しか保存できない」という大きな制限があり、漫画など容量の大きな本をガンガンDLしたい、という向きは高めのお金を払ってハイエンドな端末を買うしかなかった。(ハイエンドなKindleって存在が微妙だけど、それはそれとして)

で、今回の制限突破によりお値段お安め系のKindleにmicroSDカード挿して漫画はそれで読む、という運用が一気にしやすくなった。

キャンペーン時に4000円くらいでFire買って、

Fire タブレット 8GB、ブラック

Fire タブレット 8GB、ブラック

64GBのmicroSD挿しておいた

ワイ大勝利。

WindowsのGUIアプリケーションを外部 (Linuxホスト) から実行したいんだ。どうしてもだ。

なんでそんなことをしたくなったのかは、聞かない約束だ。

しかるべきポートを開いておけば、リモートから何らかのコマンドを叩くこと自体は簡単にできる。WinRMを使ってもいいし、それこそsshdを入れてもいい。

しかしそれ系のリモートコマンドだと、GUIアプリケーションを実行することは出来ない。これは考えてみればあたり前で、GUIアプリケーションを動かせるようなセッション(ローカルでのログインなり、リモートデスクトップでのログインなり)とリモートコマンド用のセッションは分かれている。ここではセッションという言葉を適当に使っているけど気にしない。

発想を変えて、リモートからコマンドを送らずにサービス経由で特定の時間にGUIアプリケーションを起動しよう!と思ったところでこれはうまくいかない。最近のWindowsではサービスからGUIアプリを立ち上げることはできない。これをSession 0 Isolationとかいうらしい。

Application Compatibility: Session 0 Isolation

タスクスケジューラから実行する場合でも、やっぱり何らかのログインセッションがない状態で突然GUIアプリを立ち上げたりすることはできない。

ここに、PsExecというツールがある。

blog.treedown.net

これを使えば、特定のログインセッションにひも付けた形でアプリを起動できる。(沢山のポートを開放しなくちゃいけないのが残念だけど...)

つまり、「何らかのログインセッションを常時張っておくことを許容する」ならば、PsExec経由でGUIアプリを叩けるということだ。この場合のログインセッションは別にリモートデスクトップ接続でも構わないので、単につなぎっぱにしておけばよい。

セッションのIDはcmd.exeから

query session

で取れる。

残念ながらPsExecはWindowsでしか動かないツールなので、Linuxでは使えない。実はwinexeというそれっぽいツールがあるのだが、

orebibou.com

これにはPsExecのようにセッションIDを指定するような機能はない。つまり、例によってリモートからWindowsコマンドを叩けるだけのツールなのだ。が、しかし。

操作されるWindowsの方にPsExecのバイナリを置いておけば、それをwinexeから叩くこと自体は可能だ。つまり、winexe経由でPsExecを叩くことによりLinuxホストからWindowsのGUIアプリケーションを立ち上げることができる。もちろん、何らかのログインセッションを事前に張っておくこと前提で。

winexe -U "{username}%{password}" //{windows_ip_address} 'C:\PsExec \\localhost -u {username} -p "{password}" -i {session_id} notepad'

やった!notepadが起動した!

よかったですね。

h8300の開発環境はMacで作らない方がよい (12ステップで作る組み込みOS自作入門)

最近、この本をやってる。

12ステップで作る組込みOS自作入門

12ステップで作る組込みOS自作入門

MacでH8開発環境構築 - 12ステップ本を試す - satfyの日記

上記ブログのように頑張ればMacで開発できるのかもしれないが、総じて言えば今のMacでクロスコンパイル環境を整えようとするのは時間の無駄であることが分かった。

理由は例によってgccの中味がclangとかになっててめんどいから。

まあちゃんとやれば最終的には何とかなるのかもしれないが、とりあえず構築するならVirtualBox上でLinuxを動かして、USBシリアル変換ケーブルをVM側でも認識するようにした方がよっぽど早い。

なお上記の本にはcuやMinicom, Kermitなどいくつかのツールが紹介されているが、シリアル接続に使う端末エミュレータはscreenがよいと思う。

理由としては、僕がVMにsshして開発してるので、cuだと~(チルダ)がsshに取られてなんかどうにもめんどくさくなってどうにもならなくったので、screenを使うと普通になんとかなったというのと、やっぱり端末エミュレータとして頑張って開発されているので変なツールより洗練されていて使いやすいというのがある。(他のプロセスを起動するコマンドは~Cとか言われましても的な)

こういう本をやると、C言語が何のために作られたのかがよく分かる。