4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

メモリ上への一時ファイルの作り方

Last updated at Posted at 2019-06-02

概要

一時ファイルを作成して、別の処理に渡して直ぐに破棄することがよくある。通常、tempfileライブラリーをインポートすればいい。
だが、ディスクへのI/Oを減らそうとすれば、RAMDISKに書き込む方がいい。/tmp以下にうまいことtmpfsというRAMDISKが割当られている場合は問題ない。
web上にてmemory-tempfileという新しいライブラリがあったので修正しながら使ってみた。

環境

LinuxMint19.1+
python3

まずはテスト

pip3 install memory-tempfile

とすると導入できる。

#memory_tempfileを使う場合
 #必ず初期化後、インスタンスでメソッドを用いる
 from memory_tempfile import __version__


def test_version():
    assert __version__ == '0.1.0'


def example1():
    from memory_tempfile import MemoryTempfile

    mem_tempfile = MemoryTempfile()
    print(mem_tempfile.fallback)

    with mem_tempfile.NamedTemporaryFile(suffix='.jpg', delete=False) as td:
        print(td.name)
        pass

example1()

以上のファイルを実行するとエラーが出て動かない。そこで、memory_tempfileのライブラリが格納されている場所(例:~/.locla/lib/python3.6/memory_tempfile/memory_tempfile.py等)を見る。

原因

memory_tempはtempfileのオーバーライドであることが分かった。
そして、init内で何処がRAMDISKとして利用できるか調べて、tempfileのメソッド取る引数のdirを設定している。
(例:NamedTemporaryFile(dir=/hoge/hoge))
dirにはRAMDISKを指定したい。(例:/dev/shm等)だけどうまく動作していないようである。
修正は3箇所
* OSの判定
* RAMDISKの場所
* オーバーライドの引数

OSの判定

osがLinuxならば引っかかる用に直した。(import reが必要)

if platform.platform() == "Linux":

修正後

os_platform = platform.platform()

        if re.search("Linux", os_platform):

RAMDISKの場所

次に、RAMDiskを調べる部分である。
ファイルシステムとしてtempfs,ramfsというのがRAMDISKになっている。だが、調べるべき部分が変数mpのリストの中で8番目となっている。中身をみると9番目がファイルシステムを示している。
さらに、dict形式のfor文において、items()をつけないとうまく行かない。この場合はself.usable_pathsが辞書形式である。self.usable_pathsはキーとしてファイルの場所を示し、値としてRAMDISKかどうかを保持している。(#の部分の付近が修正した場所である。)

try:
                    dev = os.stat(path).st_dev
                    major, minor = os.major(dev), os.minor(dev)
                    mp = mnt_info.get("{}:{}".format(major, minor))
                    if mp and mp[8] in self.filesystem_types:
                        self.usable_paths[path] = mp
                except FileNotFoundError:
                    pass

            for k, v in self.usable_paths:
                if not v:
                    del self.usable_paths[k]

            if len(self.usable_paths) > 0:
                self.tempdir = self.usable_paths.keys()[0]

修正後

try:
                    dev = os.stat(path).st_dev
                    major, minor = os.major(dev), os.minor(dev)
                    mp = mnt_info.get("{}:{}".format(major, minor))
                    if mp[9] in self.filesystem_types:  #I think that 9 number is "tmpfs" or "ramfs"
                        self.usable_paths[path] = mp[9]
                except FileNotFoundError:
                    pass

            tmp_del_element = []
            for k,v in self.usable_paths.items():
                if not v:
                    tmp_del_element.append(k)  #select dir(check False)
            for l in tmp_del_element:
                del self.usable_paths[l]

            if len(self.usable_paths) > 0:
                list_usable_paths_keys = list(self.usable_paths.keys())  #OrderdDict.keys() is not list type
                self.tempdir = list_usable_paths_keys[0]  #We get ramdisk dir!

これで、RAMDISKのディレクトリがself.tempdirに入るようになった。
だが、オーバーライドの部分に問題がある。

オーバーライドの修正

引数にてbufferingをNoneから-1にする。
tempfileのメソッドをオーバーライドしている。これまで2つの修正によりself.tmpdirにRAMDISKの場所は指定できた。それでも、一部のメソッドにおいて問題がある。
* SpooledTemporaryFile
* NamedTemporaryFile
* TemporaryFile
公式を見ると、引数のbufferingはNoneである。だが、open関数の引数に準拠すると書いている。open()によるとbufferingは整数であり、デフォルトは-1となっている。
bufferingを指定しなければ、pythonに怒られない。しかし、オーバーライドでNoneを指定しているためエラーが出てしまう。
公式ではNone指定がデフォルトとなっているため齟齬が生じているようだ。これは公式が違うのかどうかは私自身はわからない。

    def SpooledTemporaryFile(self, max_size=0, mode='w+b', buffering=None, encoding=None, newline=None,
                             suffix=None, prefix=None, dir=None):
        return tempfile.SpooledTemporaryFile(max_size=max_size, mode=mode, buffering=buffering, encoding=encoding,
                                             newline=newline, suffix=suffix, prefix=prefix,
                                             dir=self.tempdir if not dir else dir)

    def NamedTemporaryFile(self, mode='w+b', buffering=None, encoding=None, newline=None,
                           suffix=None, prefix=None, dir=None, delete=True):
        return tempfile.NamedTemporaryFile(mode=mode, buffering=buffering, encoding=encoding, newline=newline,
                                           suffix=suffix, prefix=prefix, dir=self.tempdir if not dir else dir,
                                           delete=delete)

    def TemporaryFile(self, mode='w+b', buffering=None, encoding=None, newline=None,
                      suffix=None, prefix=None, dir=None):
        return tempfile.TemporaryFile(mode=mode, buffering=buffering, encoding=encoding, newline=newline,
                                      suffix=suffix, prefix=prefix, dir=self.tempdir if not dir else dir)

修正後

    #bufferingはopenの引数と同義なので、Noneでなくて-1のはず
    def SpooledTemporaryFile(self, max_size=0, mode='w+b', buffering=-1, encoding=None, newline=None,
                             suffix=None, prefix=None, dir=None):
        return tempfile.SpooledTemporaryFile(max_size=max_size, mode=mode, buffering=buffering, encoding=encoding,
                                             newline=newline, suffix=suffix, prefix=prefix,
                                             dir=self.tempdir if not dir else dir)

    def NamedTemporaryFile(self, mode='w+b', buffering=-1, encoding=None, newline=None,
                           suffix=None, prefix=None, dir=None, delete=True):
        return tempfile.NamedTemporaryFile(mode=mode, buffering=buffering, encoding=encoding, newline=newline, suffix=suffix, dir=self.tempdir if not dir else dir, delete=delete)

    def TemporaryFile(self, mode='w+b', buffering=-1, encoding=None, newline=None,
                      suffix=None, prefix=None, dir=None):
        return tempfile.TemporaryFile(mode=mode, buffering=buffering, encoding=encoding, newline=newline,
                                      suffix=suffix, prefix=prefix, dir=self.tempdir if not dir else dir)

利用方法

以上の修正をすると利用できるが注意点として、
tempfileと同様には使えない。一旦、MemoeryTempFile()としてインスタンスを作成した上で各メソッドが利用できる。
私の場合だと/run/user/1000にファイルが作成されていた。もちろん再起動すると無くなっている。
メモリ上の一時ファイル利用の一助になれば思う。

参照場所

公式ドキュメント
memory-tempfile
2019年2月にできたばかりで、現在でも初期バージョンのままである。
2020年1月現在は公式も更新されて2.2.2となり修正が入っているようだ。実験はしてないが、ソースも見る限り問題はないようだ。そして、ドキュメントにもこの記事と同様な指摘がなされている部分がある。

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?