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

invalid recursive type XXX

golang

zero valueを設定する際に循環してしまうと無限に再帰してしまい終わらないという話。

例えば以下のような定義はダメ。

自分自身で再帰

type Tree struct {
    Value int
    Left  Tree
    Right Tree
}

もちろん、ポインターにしてあげれば、nilがzero valueになるので無限に再帰はしなくなる。

type Tree struct {
    Value int
    Left  *Tree
    Right *Tree
}

相互再帰(N=2)

自分自身の型で再帰的に定義してはダメというルールではない。初期化時のiterationで循環参照が起きれば無限再帰が起きる。こういうのもダメ。

type L struct {
    R R
}

type R struct {
    L L
}

どれか1つでもnilになればそこが終端になるので以下はOK。

type L struct {
    R R
}

type R struct {
    L *L
}

N=3

もちろん3つ組の循環でもダメ。

type A struct {
    B B
}

type B struct {
    C C
}

type C struct {
    A A
}

循環しなければただの木なのでいつかは終端に辿り着く。

type T0 struct {
    T1 T1
}

type T1 struct {
    T2 T2
}

type T2 struct{}

httptestでmock server的なものを作る方法のメモ

golang

以下の3つが欲しい

  • get
  • post (form)
  • post (json)
package m

import (
    "bytes"
    "encoding/json"
    "net/http"
    "net/http/httptest"
    "net/url"
    "testing"

    "io/ioutil"

    "github.com/pkg/errors"
    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/require"
)

func TestGet(t *testing.T) {
    ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        assert.Exactly(t, "/foo", r.URL.Path)
        assert.Exactly(t, "1", r.URL.Query().Get("value"))
    }))
    defer ts.Close()

    _, err := http.Get(ts.URL + "/foo?value=1")
    require.NoError(t, err)
}

func TestPost(t *testing.T) {
    ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        assert.Exactly(t, "/foo", r.URL.Path)
        r.ParseForm()
        assert.Exactly(t, "1", r.Form.Get("value"))
    }))
    defer ts.Close()
    values := url.Values{}
    values.Add("value", "1")
    _, err := http.PostForm(ts.URL+"/foo", values)
    require.NoError(t, err)
}

func TestPostJSON(t *testing.T) {
    type data struct {
        Name string `json:"name"`
        Age  int    `json:"int"`
    }

    ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        assert.Exactly(t, "/foo", r.URL.Path)
        var val data
        parseJSONRequest(r, func(b []byte) error {
            return json.Unmarshal(b, &val)
        })
        assert.Exactly(t, "foo", val.Name)
        assert.Exactly(t, 20, val.Age)
    }))
    defer ts.Close()

    dataset := data{
        Name: "foo",
        Age:  20,
    }

    b, err := json.Marshal(dataset)
    require.NoError(t, err)
    req, err := http.NewRequest("POST", ts.URL+"/foo", bytes.NewBuffer(b))
    req.Header.Set("Content-Type", "application/json")

    _, err = (&http.Client{}).Do(req)
    require.NoError(t, err)
}

func parseJSONRequest(r *http.Request, parse func(body []byte) error) error {
    if r.Body == nil {
        return errors.New("missing form body")
    }
    ct := r.Header.Get("Content-Type")
    if ct != "application/json" {
        return errors.Errorf("invalid content type: %v", ct)
    }
    b, err := ioutil.ReadAll(r.Body)
    if err != nil {
        return err
    }
    return parse(b)
}

magicalimportというライブラリを作ってました

python magicalimport

magicalimportというライブラリを作ってました。

はじめに

これは何をするライブラリかというと物理的なファイル名を指定して、指定したファイルをpython moduleとしてimportするためのライブラリです。

用途

例えばconfigファイルの読み込みに便利かもしれません。

使い方

例えば、以下のようなファイル構造の時に、以下のようなfoo.pyがあった時に。

.
├── a
│   └── b
│       └── c
│           └── foo.py
└── main.py

a/b/c/foo.py

name = "foo"
_age = "*secret*"

main.pyでは以下の様なコードでfoo.pyを読み込むことができます。

from magicalimport import import_from_physical_path

foo = import_from_physical_path("./a/b/c/foo.py")

ちなみに、importするmodule名を指定する事もできて、 as_ オプションを付けます。sys.modulesに登録されるのでその後は普通にimportできます。

from magicalimport import import_from_physical_path

foo = import_from_physical_path("./a/b/c/foo.py", as_="foo2")
import foo2
# fooとfoo2は同じもの

注意点

moduleの階層構造に関係なくimportしているところがあるので読み込んだ先のファイルでrelative importなどは上手くいかないです。

例えば、以下のような設定ファイルの構造でlocal.py,test.pyがbase.pyの設定を共有したいときなどに。

config
├── base.py
├── test.py
└── local.py

普通にrelative importしたくなりますがこれは動きません。

from .base import *

以下の様に書く必要があります。star importする場合にはexpose_all_membersを使うと便利です。

# 以下はだいたい `from .base import *` と同じ
import magicalimport
import os.path


here = os.path.dirname(os.path.abspath(__file__))
base = magicalimport.import_from_physical_path(os.path.join(here, "./base.py"), as_="base")
magicalimport.expose_all_members(base)

moduleの"_"で始まるものはimportされないですが。

追記

python2もサポートしました

追記

here オプションをサポートしました。以下の様に書ける様になりました。

import magicalimport


base = magicalimport.import_from_physical_path("base.py", as_="base", here=__file__)
magicalimport.expose_all_members(base)

flaskでconfigファイルの指定に物理的なパスを指定できるようにしたくなったという話

python flask magicalimport

はじめに

flaskを使い始めたのだけれど。flaskのconfigファイルの指定が対象のflaskアプリの存在するモジュールの位置からの相対パスを指定するらしく使いづらい。

例えば、fooパッケージの中で、以下の様なコードがあった場合には、fooパッケージからの相対位置を指定する必要がある。

from flask import Flask
app = Flask(__name__)
app.config.from_python("<config path>")
app.run()

この __name__ を使っているパッケージの位置からの相対位置を指定する必要がある。だるい。これを止めて直接特定のファイルの位置を指定できるようにしようという話。

Appのconfig_classを変更すれば良い

はじめに手抜きをするために以下のmagicalimportというパッケージをインストールしておく*1

$ pip install magicalimport

configオブジェクトを作るファクトリーの設定を変更する必要がある。以下の様な形で作れば良い。

from flask import Flask, Config
import logging
import magicalimport
logger = logging.getLogger(__name__)


class CustomConfig(Config):
    def from_pyfile(self, path):
        return self.from_object(magicalimport.import_from_physical_path(path))


class App(Flask):
    config_class = CustomConfig

# or monkey patching
# Flask.config_class = CustomConfig


if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument("-c", "--config", default=None, required=True)
    args = parser.parse_args()

    app = App(__name__)
    if args.config:
        app.config.from_pyfile(args.config)

    logging.basicConfig(
        level=app.config["LOGGING_LEVEL"].upper(),
        format=app.config["LOGGING_FORMAT"]
    )
    logger.info("running.. (port=%s)", app.config["PORT"])
    app.run(port=app.config["PORT"])

あとは以下のように好きな位置のファイルを指定してアプリを起動する事ができる。

$ tree config
config
├── base.py
└── local.py

1 directory, 2 files

$ python foo/app.py --config=./config/local.py
asctime2016-10-02 00:52:40,828  loglevel:INFO   message:running.. (port=8888)
asctime2016-10-02 00:52:40,847  loglevel:INFO   message: * Running on http://127.0.0.1:8888/ (Press CTRL+C to quit)

やりましたね。

設定ファイルの内容

設定ファイルの内容は以下

config/base.py

PLAIN_LOGGING_FORMAT = "%(asctime)s %(levelname)s %(message)s"
LTSV_LOGGING_FORMAT = "asctime%(asctime)s\tloglevel:%(levelname)s\tmessage:%(message)s"

config/local.py

import magicalimport
import os.path
here = os.path.dirname(os.path.abspath(__file__))
base = magicalimport.import_from_physical_path(os.path.join(here, "./base.py"), as_="base")

LOGGING_LEVEL = "info"
LOGGING_FORMAT = base.LTSV_LOGGING_FORMAT


PORT = 8888 

*1:実はこのためについ先程作りました

makefileわりと便利だよという話

makefile memo shell

はじめに

makeはmacにもlinuxにもデフォルトであって便利*1。 とは言え、色々とわかりづらいところがある。 便利なところだけ見繕って使うと便利。

個人的なmakefileの使い方は2種類

個人的には以下のような2つの目的でmakefileを使っている。

  • 複数の目的のためのタスクを記述して手軽に実行できるようにする
  • 1つの目的のためにまじめに依存関係を定義して使う成果物を生成する

後者がたぶん本命だけれど。前者で使うことも多い。

複数の目的のためのタスクを記述して手軽に実行できるようにする

利用するコマンドのオプションを省略したい場合に使う。 脳みそのリソースの削減。細かなオプションの意味など後で見返した時に覚えていないことが多いのでメモがわりにmakefileを書く。

依存関係の書き方は以下の様な感じ。

<タスク名や生成されるファイル>: <依存するタスクや依存するファイル> ...
    <何か実行したい処理>

たいていcleanとdefaultを定義する。記述部分がごちゃっとしてきたら変数にする

SRC = x.txt y.txt

clean:
  rm -f *.out
default:
  python script.py ${SRC} > x.out

デフォルトの設定値(ただし実行時に変えたい)

以下のように実行時に値を渡したい場合がある。

$ make
10!!
# 10ではなく1にしたい
$ FOO=1 make
1!!

このような場合には ?= を使って設定する。実行するコマンド自体のエコー出力を消したい場合には @ をつける。

FOO ?= 10

default:
   @echo ${FOO}!!

phony

makefileにただclean,defaultと書くとそれが生成物としてのファイル名を表しているのか。タスク名を表しているのかわからない。同名のファイルなどがあると困る。なのでphonyを書いておく。

.PHONY clean default

1つの目的のためにまじめに依存関係を定義して使う

一連のタスク依存関係を持ちつつ連動して実行されて欲しい場合にまじめに依存関係を定義する。 対象となるものがファイルの場合には、成果物としてのファイルのutimeよりも依存先のファイルのutimeの方が新しい場合にのみ実行される。便利。

ただしこの目的の為に書く場合には、わりとまじめに使う場合が多いのでその場合はそれぞれの環境のツールを使うという感じでも良い気もする。とは言え、簡単なレベルならmakefileでもわりとどうにかなるので便利なことは便利。

以下のような謎の定義を書いておくと、依存関係にあるファイル拡張子によって実行する処理を記述できる。例えば、.pyの拡張子を持つファイルのpythonを実行した結果をリダイレクトした結果を.py.outに出力するなど。

%.py.out: %.py
  python $< > $@
  
hello.py.out: hello.py

clean:
  rm -f *.py.out

make -p

おもむろに make -p と実行してみると良い。内部で利用できる関係性を見る事ができる。

$ make -p # 一部だけ表示

%.py.out: %.py
#   commands to execute (from `Makefile', line 2):
    python $< > $@

ちなみに 無引数で実行した時に実行されるタスクは DEFAULT_GOALとして表示される。

$ make -p    # 一部だけ表示

# makefile
.DEFAULT_GOAL := hello.py.out

$ make clean
rm -f *.py.out
$ make
python hello.py > hello.py.out
# 2度目の実行はskip
$ make
make: `hello.py.out' is up to date.

シェルの実行結果をつかって定義を記述

シェルの実行結果をつかって定義を記述することもできる。 $(shell ...) を使う。

例えば以下のような状況で。

$ ls
Makefile  bye.py      hello.py

make を実行すると hello.py.outbye.py.out が生成されるようなmakefileは以下。

%.py.out: %.py
  python $< > $@

default: hello.py.out bye.py.out

hello.py.out: hello.py
bye.py.out: bye.py

clean:
  rm -f *.py.out
.PHONY: clean

これが面倒な場合には以下のようにも書ける。関係性が自明なのでhello.py.outなどの記述は省略できる。

%.py.out: %.py
  python $< > $@

default: hello.py.out bye.py.out

clean:
  rm -f *.py.out
.PHONY: clean

さらに依存先部分のhello.py.outなども書きたくない場合には $(shell ...) を使って以下の様にも書ける。 これはシェルスクリプトの実行結果に置き換えるというような意味。

%.py.out: %.py
  python $< > $@

default: $(shell ls *.py | sed 's/$$/.out/g')

clean:
  rm -f *.py.out
.PHONY: clean

注意点として $ は2つ必要(sedの記述は sed 's/$/.out/g' で良いところを)。 markdownで書いたreadmeなどを作ったりするのに便利。

よく分からない自動変数

今まで放置してきた謎の変数について紹介。以下の様になっている。

  • $@ 出力対象(生成されるファイル)の名前
  • $^ 入力対象(依存されるファイル)の名前全部
  • $< 入力対象の内1つ目のものの名前

おそらくよく使うのはこの辺の3つ。詳細を知りたければ このあたり

上の変数などを使って、出力結果をmarkdownでまとめてみたreadmeを作ってみる。

%.py.out: %.py
  python $< > $@

readme.md: $(shell ls *.py | sed 's/$$/.out/g')
  rm -f tmp
  touch tmp
  for i in $^; do echo $$i >> tmp; echo '```' >> tmp; cat $$i >> tmp; echo '```' >> tmp; done
  mv tmp $@

clean:
  rm -f *.py.out
  rm -f readme.md
  
.PHONY: clean

実行結果をまとめたreadmeを生成。

$ make
python bye.py > bye.py.out
python hello.py > hello.py.out
rm -f tmp
touch tmp
for i in bye.py.out hello.py.out; do echo $i >> tmp; echo '```' >> tmp; cat $i >> tmp; echo '```' >> tmp; done
mv tmp readme.md

$ cat readme.md
 bye.py.out
 ```
 bye
 ```
 hello.py.out
 ```
 hello
 ```

makefileわりと便利だよという話。

*1:macもdefaultでgnu makeっぽい

[python][logging] pythonのloggingモジュールの呼び出し元のファイル名:行番号を出力する機能が残念という話

はじめに

pythonの標準ライブラリのloggingモジュールを利用すると、ログ出力の中に呼び出し元の位置に関する情報(ファイル名や関数名など)を含める事ができる。 具体的にはloggerに渡すフォーマットに以下を含めることで、ログ出力時に呼び出した位置の情報が出力される。

  • %(pathname)s ログ出力が行わる実行コードが存在するファイルのパス名。
  • %(funcName)s ログ出力が行われた際のコンテキスト上の関数名。
  • %(lineno)s ログ出力が行われたファイルの行番号

便利で素晴らしい。一方で、ちょっとloggingモジュールのloggerに細工をしようと思った時にこの機能が上手く動作しなくなって悲しかったという話。

loggingモジュールので呼び出し元の位置をログに含める事ができる

例えば以下のようなファイル構造の時に、hello.pyで定義された hello() 上のログ出力にはhello.py中で呼び出されたということが分かる。

$ tree .
.
├── hello.py
└── main.py

hello.py

import logging
logger = logging.getLogger("*")


def hello():
    logger.info("hello")

main.py

import logging
import hello

format = "level:%(levelname)s\tmsg:%(message)s\tlocation:%(pathname)s(%(lineno)s)\tfn:%(funcName)s"
logging.basicConfig(level=logging.DEBUG, format=format)

hello.hello()

具体的には以下のようなログが出力される。

level:INFO   msg:hello   location:~/work/hello.py(6) fn:hello

期待通りhello.py上で実行されたものだということが分かる。一方で、loggerに対してもう少し機能を付加したいとloggerオブジェクトをwrapするようなオブジェクトを定義したときにこの機能が上手く動作しなくなる

元のloggerをwrapしたオブジェクトを作ってみる

例えば、何らかの処理を追加したいと思い。直接的にloggingモジュールのloggerを使うのではなく、loggerを内部に保持したオブジェクトを作りたいと思うことがある。以下のようなもの。

wrap.pyでLoggerWrapperを定義し、hello.pyではこのオブジェクトで包んだオブジェクトを利用してログ出力をしようとするように、コードを変えてみる。

wrap.py

class LoggerWrapper:
    def __init__(self, logger):
        self.logger = logger

    def info(self, *args, **kwargs):
        # do something
        self.logger.info(*args, **kwargs)

hello.py

import logging
import wrap


logger = logging.getLogger(__name__)
wlogger = wrap.LoggerWrapper(logger) 


def hello2():
    wlogger.info("hello")

この時にも、hello2() 上のログ出力の位置としてファイル名がhello.pyになってほしい。ところがそうはならない。LoggerWrapper上のLoggerWrapper.info() の位置がログ出力されてしまう。

level:INFO   msg:hello   location:~/work/wrap.py(7)  fn:info

原因

実行時の位置情報について期待していない結果が出力されてしまう原因はloggerの定義を見てみると分かる。例えばpython3.5.2上のloggingモジュールのコードを見ると以下の様になっている。

if hasattr(sys, '_getframe'):
    currentframe = lambda: sys._getframe(3)
else: #pragma: no cover
    def currentframe():
        """Return the frame object for the caller's stack frame."""
        try:
            raise Exception
        except Exception:
            return sys.exc_info()[2].tb_frame.f_back


## snip..
class Logger(Filterer):

    ## snip..
    def findCaller(self, stack_info=False):
        """
        Find the stack frame of the caller so that we can note the source
        file name, line number and function name.
        """
        f = currentframe()
        #On some versions of IronPython, currentframe() returns None if
        #IronPython isn't run with -X:Frames.
        if f is not None:
            f = f.f_back
        rv = "(unknown file)", 0, "(unknown function)", None
        while hasattr(f, "f_code"):
            co = f.f_code
            filename = os.path.normcase(co.co_filename)
            if filename == _srcfile:
                f = f.f_back
                continue
            sinfo = None
            if stack_info:
                sio = io.StringIO()
                sio.write('Stack (most recent call last):\n')
                traceback.print_stack(f, file=sio)
                sinfo = sio.getvalue()
                if sinfo[-1] == '\n':
                    sinfo = sinfo[:-1]
                sio.close()
            rv = (co.co_filename, f.f_lineno, co.co_name, sinfo)
            break
        return rv

内部の詳細は省略するとして、ログ出力の位置の構造を取得するのに、実行時のstackframeの情報を利用している。 この際に取得するstackframeのレベルが3で固定になってしまっているので、実行時に呼び出される logger.findCaller() の深さが変わってしまうようなコードを書いてしまうと上手く位置が取得できなくなってしまう(loggingモジュールのloggerは、何かのオブジェクトにwrapしたりすることはせずに直接使う必要が有るということ)。

かなしい。

もちろん以下のようにログ出力部分を関数で包んでしまっても上手く行かない。

def log_with_additional_info(message, *args, **kwargs):
    logger.info("additional: {}".format(message), *args, **kwargs)


def hello3():
    log_with_additional_info("hello")

hello3() ではなく log_with_additional_info() 上で使われたということになってしまう。

level:INFO   msg:additional: hello   location:~/work/hello.py(14)    fn:log_with_additional_info

もちろん、両者は共に loggingモジュールのloggerの logger.info() が呼ばれた位置 であることには違いないのだけれど。

以下の様なことができたらなーという感じ。

# 位置情報を取得する際にsys._getframe(3) ではなく sys._getframe(4)が呼ばれるようになる
logger.setFindCallerDepth(4)

gist https://gist.github.com/podhmo/056213a4cd51c2e611b963b36ae9cdbb

pythonのinteractive shellを終了した後 `stty sane` が必要だった問題が解決したという話

mac macports python

はじめに

手元の環境でpythonのinteractive shellを利用した後、コンソールで入力した文字列のエコー表示がされなくなる問題に悩まされていた。

$ python3.5
Python 3.5.2 (default, Sep 19 2016, 02:49:52) 
[GCC 4.2.1 Compatible Apple LLVM 7.3.0 (clang-703.0.31)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> ^D>>> 
$ # ここで入力した文字列が表示されなくなってしまう
$ stty sane #これで復活

readlineを有効にせずにbuildしていた模様

readlineを有効にせずにpythonをinstallしていた模様。

$ port installed python35
The following ports are currently installed:
   python35 @3.5.2_0 (active)
$ port variants python35
python35 has the variants:
   readline: Use readline instead of libedit
   universal: Build for multiple architectures
$ sudo port clean python35
$ sudo port install python35 +readline
The following ports are currently installed:
  python35 @3.5.2_0
  python35 @3.5.2_0+readline (active)

無事は stty sane は不要になった。