sftpserverモジュールをforkしていじった件

諸事情によりテスト用のsftpサーバーが必要になったのでsftpserverなるモジュールを見つけてきた。しかしこいつシングルスレッドなので、やりたいことのためにはmultiprocessingと併用する必要があったわけなんだけど、そうするとなんかエラーが出る。

raise AssertionError("PID check failed. RNG must be re-initialized after fork().
Hint: Try Random.atfork()")

で調べてみたところそのものズバリのエラーで困ってる人がいた。

python - multiprocess module with paramiko - Stack Overflow

どうやらparamikoのバグらしい。paramikoのforkで、pure python実装なsshモジュールだとこの問題は解決済みらしい。

ちなみにpyftpdlibはFTPSには対応しているけれど、SFTPには対応してないとのことだったので諦めた。issueに「SFTPにも対応しないの?」みたいなのもあったけど、「pyftpdlibがやるようなことじゃない」って蹴られてたので多分今後もそういう方向には行かないと思う。
あと、pyFileSystemというものがSFTPに対応していたので使ってみたけど、こいつもparamiko依存なので同じエラーが出る。

で、仕方がないのでforkしてちょこっと改良した。

kk6/sftpserver at spike · GitHub

変更点

  • 内部でのparamikoの使用をやめてsshに移行した
  • optparseをやめてargparseに移行した
  • なんとなくデーモン化したかったのでpython-daemonでデーモン起動できるようにした

今にして思えばデーモン化はちょっと余計だった気がしてpull-requestは飛ばしてない

テストで使う

unittestのsetupで、こんな感じでmultiprocessingと併用して立ち上げてやる

def setUp(self):
    from sftpserver import start_server
    from multiprocessing import Process

    args = (HOST, PORT, KEYFILE, 'DEBUG')
    self._process = Process(target=start_server, args=args)
    self._process.start()


def tearDown(self):
    self._process.terminate()