Python3 subprocess

筆者のアプリは python3 なのに Gio の subprocess を使っていた。
Gjs, Vala, C 等からでも参考になるよう、てか途中まで Gjs 製だった。

JXA では後で Swift をやるかもと NSTask を使っていた。
やらねーし、Xcode デカすぎだし Python のほうが面白いし。
PyObjC 版は Python3 の subprocess モジュールでいいんでね?

17.5. subprocess ? サブプロセス管理 ? Python 3.6.5 ドキュメント

知らない間に subprocess が超単純化されていた。
Popen とか call とか迷わず run だけでいい、破棄も wait もいらない。
unrar, 7za を使うコードを subprocess.run に書き換えしてみる。

#!/usr/bin/env python3

import objc, zipfile, re, subprocess
from AppKit import *

PICEXT = '\.(jpe?g|png|gif)$'

class ComipoliArchive:
    def __init__(self):
        self.status = 0
        self.namelist = []
        self.path = ''

    def new_archive(self, path, is_unrar, is_7za):
        self.path = path
        if re.search(r'\.(cbz|zip)$', path, re.I):
            self.status = 0
            self.namelist.clear()
            with zipfile.ZipFile(path) as o:
                l = o.namelist()
                l.sort()
                for name in l:
                    if re.search(PICEXT, name, re.I):
                        self.namelist.append(name)
        elif is_unrar and re.search(r'\.(cbr|rar)$', path, re.I):
            self.status = 1
            self.namelist.clear()
            cp = subprocess.run(['unrar', 'vt', '-p-', '--', path], stdout=subprocess.PIPE)
            lines = cp.stdout.decode('utf-8').split('\n')
            for line in lines:
                s = line.lstrip()
                if s.startswith('Name: '):
                    name = s[6:]
                    if re.search(PICEXT, name, re.I):
                        self.namelist.append(name)
        elif is_7za and re.search(r'\.(cb7|7z)$', path, re.I):
            self.status = 2
            self.namelist.clear()
            cp = subprocess.run(['7za', 'l', '-slt', path], stdout=subprocess.PIPE)
            lines = cp.stdout.decode('utf-8').split('\n')
            for line in lines:
                if line.startswith('Path'):
                    name = line[7:]
                    if re.search(PICEXT, name, re.I):
                        self.namelist.append(name)
        else:
            return False
        return True

    def __getitem__(self, num):
        if self.status == 0:
            with zipfile.ZipFile(self.path) as o:
                b = o.read(self.namelist[num])
                data = NSData.dataWithBytes_length_(b, len(b))
                return NSImage.alloc().initWithData_(data)
        elif self.status == 1:
            cp = subprocess.run(['unrar', 'p', '-inul', '-@', '--', self.path, self.namelist[num]], stdout=subprocess.PIPE)
            data = NSData.dataWithBytes_length_(cp.stdout, len(cp.stdout))
            return NSImage.alloc().initWithData_(data)
        elif self.status == 2:
            cp = subprocess.run(['7za', 'x', '-so', self.path, self.namelist[num]], stdout=subprocess.PIPE)
            data = NSData.dataWithBytes_length_(cp.stdout, len(cp.stdout))
            return NSImage.alloc().initWithData_(data)

    def __len__(self):
        return len(self.namelist)

丸ごと。

unrar の存在確認も subprocess でやろうと思ったけど。

Pythonで外部コマンドの存在チェック(`which`的な) – Qiita

self.is_unrar = shutil.which('unrar') != None

こっちのほうがいいや。
早速試すことにしよう。

WinRAR archiver, a powerful tool to process RAR and ZIP files

から macOS 版 rar のダウンロード。
インストーラーも Makefile も無いので手動でパスを通す。
終わったら cbr にしたい画像の入ったディレクトリで以下を。

rar a test.cbr -- *.jpg

直接 CBR ファイルを作れるよ。

イケた。

Python3 自体の知識が 3.2 くらいで止まっているわ。
最新 Python3 必須になるけど、いやそのほうがいいでしょ普通。
たまには公式サイトも見ることにしよう。