【Maya】Mayaでsubprocessを使う(クロスプラットフォーム対応)

より正確に言うとsubprocessをGUIアプリケーションからクロスプラットフォームで使う方法です。
とはいえ実際のところMacだと特に困らないので、以下の内容はWindows向けの対応方法になります。

「ハンドルが無効です」エラーに対応する

GUIアプリケーションからsubprocessを使うとき、Macだと特に問題ないんですけど、Windowsで何も考えずに使ったりするとこんなエラーが起きることがあります。

Traceback (most recent call last):
  File "<maya console>", line 18, in sptest
  File "C:\Program Files\Autodesk\Maya2018\bin\python27.zip\subprocess.py", line 702, in __init__
    errread, errwrite), to_close = self._get_handles(stdin, stdout, stderr)
  File "C:\Program Files\Autodesk\Maya2018\bin\python27.zip\subprocess.py", line 833, in _get_handles
    p2cread = self._make_inheritable(p2cread)
  File "C:\Program Files\Autodesk\Maya2018\bin\python27.zip\subprocess.py", line 884, in _make_inheritable
    _subprocess.DUPLICATE_SAME_ACCESS)
WindowsError: [Error 6] ハンドルが無効です。

このあたりの問題ですね。
Issue 1124861: subprocess fails on GetStdHandle in interactive GUI - Python tracker
Issue 3905: subprocess failing in GUI applications on Windows - Python tracker

対処方法としてはstdinを指定してあげることです。

具体的にはこう。

import os
import subprocess

def exec_subprocess(cmd):
    devnull = open(os.devnull, "wb")
    p = subprocess.Popen(
        cmd,
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
        stdin=devnull)
    devnull.close()
    stdout, stderr = p.communicate()

今回はdevnullを指定することで回避しました。
python3系ならsubprocess.DEVNULLがあるみたいですけど、我々Mayaユーザーには関係のない話ですね!かなしみ。

実行の度にウィンドウが出るのを回避する

Windowsだと上のようなサブプロセスを実行するたびに一瞬だけウィンドウが表示されてしまいます。邪魔…!
というわけで消してやりましょう。

import os

startupinfo = None
if os.name == "nt":
    startupinfo = subprocess.STARTUPINFO()
    startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW

このようにstartupinfoをwindowsのときだけ作ってやって、ウィンドウを非表示にします。
あとはこれをsubprocess.Popenのstartupinfo引数に渡してやるだけです。
というわけで上のやつと合体させると最終的にはこう。

import os
import subprocess

def exec_subprocess(cmd):
    startupinfo = None
    if os.name == "nt":
        startupinfo = subprocess.STARTUPINFO()
        startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
    devnull = open(os.devnull, "wb")
    p = subprocess.Popen(
        cmd,
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
        stdin=devnull,
        startupinfo=startupinfo)
    devnull.close()
    stdout, stderr = p.communicate()

おわり

クロスプラットフォーム対応って書きましたけど、あくまでsubprocess実行部分の話なので、コマンドによっては上のコードのcmd部分はOSごとに変える必要はあります。

subprocessをラップしてくれてる系モジュールだとモジュールの根幹に手を入れないといけなかったりしてしんどいわけですが、実際MayaでPySvnを使おうとして上の問題に遭遇したので、今度(気が向いたら)MayaでPySvnを使ってSVN操作する方法を書きます。

スポンサーリンク