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

filegenという小さなライブラリを作りました

python

filegenというものを作りました。

filegenというものを作りました。特定のファイル構造を生成する処理の記述を簡略化するための小さなライブラリです。 (python3でしか確認していないのでpython2だと動かないかもしれないです)

動機

元々、コレ単体で使うつもりはなくて、mypylintの作成のために作っていた感じでした。 mypylintを作っている最中に、scaffold的な物を生成したいと思ったのですが、それを個別のファイルに分けて書くというのがだるいなーと思ったので。 コンセプト的には、「1つのスクリプトファイルで複数ファイルのファイル構造をを表せるようにする」というのが主な要素です。

そして、文法的にはsrcgenと同様にwithを使った形で構造を表せばファイルツリーの構造をインデントで判断できるので視覚的にも分かりやすくなるのではないかと期待してみたりしていました。

使い方

pipでインストールできます。

pip instlal filegen

以下の2つの操作が定義されています。

  • ファイル、ディレクトリを元にしたツリー構造の生成(with文利用)
  • 生成したツリー構造を特定の出力にdump

ツリー構造の生成

ツリー構造の生成は以下の様にして行います。

from filegen import Filegen
fg = Filegen()

with fg.dir("season"):
    with fg.file("spring.txt") as wf:
        wf.write(u"春")
    with fg.file("summer.txt") as wf:
        wf.write(u"夏")
    with fg.file("autumn.txt") as wf:
        wf.write(u"秋")
    with fg.file("winter.txt") as wf:
        wf.write(u"冬")

seasonというディレクトリのなかにspring,summer,autumn,winterのテキストが有る感じです。 (このサンプルではほぼほぼフラットな構造ですが、もちろんサブディレクトリは幾らでも切れます。)

生成した構造を特定の出力にdump

行える出力方法は以下の3つです

  • 文字列表現(sys.stdoutに出力)
  • 通常のファイル構造
  • python module用のファイル構造

文字列表現は確認用のもので Writer を使います。

from filegen import Writer
Writer().emit(fg)

以下の様な出力になります。

d:.
 d:./season
  f:./season/spring.txt
    春
  f:./season/summer.txt
    夏
  f:./season/autumn.txt
    秋
  f:./season/winter.txt
    冬

DirectoryMakerを使うとディレクトリ、ファイルが生成されます。

from filegen import DirectoryMaker
DirectoryMaker().emit(fg)

そのままファイルとディレクトリが生成されます。基本的には上書きされます。

$ tree
.
├── season
│   ├── autumn.txt
│   ├── spring.txt
│   ├── summer.txt
│   └── winter.txt
└── spring.py

おまけとして PythonModuleMaker というものを用意しました。これは、ディレクトリを生成する際に __init__.py を自動で作ってくれるというものです。

だるいのでショートカット関数をもたせた。

DirectoryMakerとかWriterとかimportしてくるのがだるかったので、ショートカットのメソッドをもたせました。それぞれ以下に対応しています。

|Filegen#to_string()|Writer#emit()|
|Filegen#to_directory()|DirectoryMaker#emit()|
|Filegen#to_python_module()|PythonModuleMaker#emit()|

このように定義するとツリー生成の処理と生成したツリーを利用する処理が密につながってしまうかなと思いましたが、そんなに大きなライブラリではないので使いやすさを優先したい気持ちがありました。

どうせなので引数を取って使えるようにしたかった。

LazyString,LazyPathという構造を用意してツリーのルートを変えられるようにしました。(内部的には単なるオブジェクトで__str__を定義してstr()で文字列化された段階で自身が保持する値を文字列化する。ツリーを構成した際のrootはこのLazyStringを使うようにして、Filegenのchange()が呼ばれたタイミングで内部に保持する文字列を変更する)

例えば以下のようなコードは最初は"."をrootに次に"/tmp/numbers"をrootにして出力します。

# -*- coding:utf-8 -*-
from filegen import Filegen

fg = Filegen()

with fg.dir("Ints"):
    for i in range(3):
        with fg.file("{}.txt".format(i)) as wf:
            wf.write(str(i))

fg.to_string()
fg.change("/tmp/numbers")
fg.to_string()

ファイル生成のrootが固定されるのが嫌だったので手軽にコマンドとして使えるようにした。

FilegenApplication というものを用意しました。これに生成したツリーを渡し、「引数として受け取った値をツリーのrootとしてファイル構造の生成」というような事ができるようにしました。

# script.py
if __name__ == "__main__":
    fg = Filegen()
    with fg.dir("foo"):
        with fg.file("bar.py") as wf:
            wf.write("# this is comment file")
        with fg.file("readme.txt") as wf:
            wf.write("# foo")
    FilegenApplication().run(fg.to_directory)

これは以下の様にして使えます。

$ python script.py /tmp
$ python script.py # 引数が渡されなかった場合にはcwdをrootにする