ネットで見つけた HDA や Python プラグインの不具合の直し方: ae_SVG 編

# はじめに

2024 年版 Houdini Advent Calendar の5日目のネタ。
去年は参加してなかった記憶なので、お久しぶりです。

何かいい実装がないかな？と思ってネットの海を泳いでいると、やはりいろいろとやってくれている人は既にいたりする。とてもありがたいし、見つけられただけで、「出来るかな？」が「出来そうかも！」にすぐ変わった時はラッキーでハッピーな気持ちになる。
一瞬だけ。

というのも、このアドカレも 2016 年から続いていてその情報を漁ればすぐわかるけども、やはり、情報は古くなるもの。
今の Houdini のバージョンでのそれらの知識は、どう動くかどうかはとりあえず一回動かしてみなければわからないもので、動くこともあるし全く動かないこともある。

ただ、折角の車輪がそこに転がっているので、それを直して使っていくといくというのは、直面する問題解決的のスピード向上な実利面でも手段としておさえておくのは重要だと思う。

という事で、今回は、そうしたネットで見つけた、「一回動かしてみたけどエラーが出て動いてないもの」の直し方の一例をあえてここでまとめてみようと思った。

こと Houdini に関する事では特に、意外とこういう事項を順を追ってまとめてる人って居ないと思うので、日々のエンジニアリングのうちの引き出しの一つとなるだろうし、これを読んだ方々の何かしらの助けになればと思う。

# 環境

* Windows 11
* Houdini 20.5.410
* Python 3.11

# 題材

今回は、[**ae_SVG**](https://github.com/Aeoll/ae_SVG
) という SVG ファイルを扱う HDA プラグインを題材に話を進めていく。

https://github.com/Aeoll/ae_SVG

:::note warn
特に注意というのも何だが、こういう際にネットで見つけた際のものを落として使ってみて、「動かない！」ってなった際に安易にそれらの作者を責めてはいけない。
**「先の道を開拓して頂いてありがとう」** の精神でかかわれるようでありたい。

なので今回、この先見の明を題材として上げさせていただく代わりに、Aeoll さんに謝辞を述べる。
:::

## 自分で試してみたい方はここから

エラーが起きている時点のレポジトリ状態はこちら。
自分で解決を試してみたい方はここの状態から始めてみよう。

https://github.com/Aeoll/ae_SVG/tree/d08804835b556a2a215554bd957e506cc45961fc

# 環境準備

## 対象プラグインの取得

まずは、対象レポジトリを Git クローン or ダウンロードしてくる。
どこでもいいが、とりあえず作業用のフォルダを作って配置しておく。

```powershell
git clone https://github.com/Aeoll/ae_SVG.git
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51857/20c6c576-14c5-63c4-e024-e4a9c64fd208.png)

:::note info
**Git/GitHub を覚えよう**
Qiita 界隈に居つくしてる方々には藪蛇だろうが、せっかくのアドカレの機会という事と、Houdini という題材を扱っている事で、多方からこの記事にやってくるかもしれない方々もいると想定しているので、あえてこちらに関しても言及したい。

こうしたサンプルは現代では頻繁に GitHub などの Git ベース上でのバージョン管理を行いながらコードを管理されていることが多い。それは Houdini 使いであれば、[SideFX Labs](https://github.com/sideeffects/SideFXLabs) でもそうであることから肌感として分かると思う。

こうしたツールは、自らの作業のバージョン (経歴) を保存できるだけでなく、複数の人々と一緒に作業をするために用意されている。

Git/GitHub を覚える事で、今回のような不具合を見つけた場合、それを直して、「これ不具合あったから直してみたよ！」って提案してみたり、「この実装は、現状は用途が限定的すぎるから、こっちの方がいいかも」といった感じで自ら直したものを具体的な形で示せるツールにもなる。
それらは転じて、そのツールの研磨のためにもなるし、自分のエンジニアリング力の成長の礎ともなるし、はたまた、ポートフォリオの一部ともなりえるだろう。
（特に Technical Artist/Technical Director の人たちにとってもね:exclamation: ）

この記事でそれらの技術に関しては一切触れないが、もしツールの使い方が分からなければ、ぜひ、調べてみて、覚えて行ってみて欲しい。
:::

## 起動環境の準備

### マイドキュメント環境のクリーンナップ

何か動作検証を行う場合は色々と条件を整理しておく必要があるので、Windows であればマイドキュメントなどは必ず初期状態にしておき、初期インストール状態を維持しておく。

もし、改編してしまっている場合は、一度以下のディレクトリを削除して、再度 Houdini を起動して初期環境を作成し直す。

```
C:\Users\<ユーザー名>\Documents\houdini<バージョン名>
```


### 起動ファイルの作成

マイドキュメントのところは常に綺麗にしておくというのは大事。
それは動作検証中や開発中では特に。
なので、マイドキュメントは触らないで、プラグインを読み込みつつ Houdini を起動するファイルを作成する。

Windows の場合は bat ファイルを作成するのが最も簡単な手だろう。
こちらのファイルをae_SVG を保存した階層に保存する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51857/93ae13af-1fe0-0694-ae3b-4d69d4956a97.png)

```bat
@echo off
setlocal

set HOUDINI_VERSION=20.5.410
set HOUDINI_EXE="C:\Program Files\Side Effects Software\Houdini %HOUDINI_VERSION%\bin\houdinifx.exe"

REM ae_SVG のディレクトリを設定
set "HOUDINI_PATH=%~dp0\ae_SVG;&"

REM DSO エラーレベルを設定 : 必要に応じて切り替える (今回はなくても OK)
set HOUDINI_DSO_ERROR=1

REM Houdini の機動
start "" %HOUDINI_EXE%

endlocal
```

:::note info
**Houdini の環境変数について**

Houdini の環境変数については、過去に書いた「[Houdiniと環境変数の魔導書 〜補助魔法導入で失敗しないために〜](https://qiita.com/takavfx/items/b58cae65984b15e5ad29)」を参照されたし。
:::

:::note info
**HOUDINI_DSO_ERROR**

今回のようなエラーは、見ての通り Python の Traceback エラーとなっている。
これが、もし C++ で書かれ、ビルド済みのバイナリなどの場合はより修正と原因のトラックが難しくなってくる。
こうした場合は、まずは環境変数に `HOUDINI_DSO_ERROR` を設定すると、バイナリ側のエラーをある程度アウトプットしてくれる。
もし、そちらで原因が分かった場合は、C++ 側のソースコードや、配置方法を直す必要がある。

```text:Command Line Tools
>hconfig -h HOUDINI_DSO_ERROR
HOUDINI_DSO_ERROR
Determines if DSO/DLL errors on plug-ins will be printed out to the
console. This can help in debugging custom operators.

By default this is disabled.

The existence of the variable in the environment (or setting its value
to 1) will cause dynamic linking errors to be output.

Setting the value to 2 will cause more verbose errors to be printed.

Setting the value to 3 will cause all DSO related messages to be
printed.

Setting the value to 4 will cause timing messages to be printed.
```
:::

# HDA の動作確認

とりあえずまずは一回動かしてみる。
launch.bat を実行して、Houdini を起動し、対象ノードを作成してみる。

すると次のようなエラーが発生した :exclamation::heart_eyes:

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51857/f778fcf7-f4ed-ef2f-6fac-9983b4490df8.png)

さらに、作成されたノードにも次のようにエラーが発生している :exclamation::heart_eyes:

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51857/c209b74e-fac6-d081-fba5-71a6ec1ba170.png)

よっしゃ:exclamation:問題解決に燃えてくるぜ:exclamation::metal:

# エラーを解決していく

## 1, エラーをよく読む

さて、では、実際のエラーが何かを読み解いていく。

まずは、最初にポップアップしたエラーダイアログをキャプチャしておく。
エラーダイアログは結局閉じて作業してしまうため、実際のそれぞれのエラーを把握出来ないため。

```
Traceback (most recent call last):
  File "ae::Sop/SVG_Import::2, PythonModule", line 10, in <module>
  File "C:\PROGRA~1/SIDEEF~1/HOUDIN~1.410/houdini/python3.11libs\toolutils.py", line 1886, in createModuleFromSection
    return _createModule(module_name,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\PROGRA~1/SIDEEF~1/HOUDIN~1.410/houdini/python3.11libs\toolutils.py", line 1868, in _createModule
    eval(code, module_dict)
  File "path", line 3, in <module>
ImportError: cannot import name 'MutableSequence' from 'collections' (C:\PROGRA~1\SIDEEF~1\HOUDIN~1.410\python311\Lib\collections\__init__.py)
```

ふむ、どうやら `ae::Sop/SVG_Import::2, PythonModule` の部分でエラーが発生しているらしい。
そして、実際に起きているエラーは `ImportError: cannot import name 'MutableSequence' from 'collections'` の部分であり、このコードは `C:\PROGRA~1\SIDEEF~1\HOUDIN~1.410\python311\Lib\collections\__init__.py` へのアクセス時にエラーがトリガーされている。

しかしながら、よくみると、このエラー起因となっているファイルは HDA プラグインのディレクトリに入っているものではなく、Houdini 側が取り込んでいる Python の、バッテリーインクルード[^1]された **collections** モジュールによるエラーだとわかる。
まず、この時点で **collections** モジュール自体が問題であるとは考えにくい。

[^1]:Python インストール時に最初からインストールされているライブラリ群のこと。他には `os` モジュール、`sys` モジュール、`datetime` モジュールなど様々なモジュールが存在する。

では、どこを見るか。
それは、エラーの最上段となっている `ae::Sop/SVG_Import::2, PythonModule` となる。

:::note info
**エラーのトリガータイミングとそれの追跡**
Houdini や Python の場合は、エラー追跡がしやすいように設計されている。
この仕組みは、プログラムの学習だけでなく、日々の開発作業においても特に助かる仕組みとなっている。
ある程度はこういったエラーの出方はパターン化されているので、まずはエラーを出してみて、上から眺めてみて、順に整理をしていくといいだろう。
結論は最下段で見つけられるが、Python の場合は特に、起因となっている再上段から探すと原因にたどり着きやすい。

しかしながら、これだけのフレームが整備されている状況でも追跡が困難になる時がある。
そういった場合には、まずは自らがおかれている環境を見直したり、そのツールが動く「『フレームワーク』や『ワークフロー』がどう動いているか」を先に見直すと良い。
:::

## 2, HDA をエディット可能にして、対象箇所を追う

`ae::Sop/SVG_Import::2, PythonModule` の部分を読み解くと、`ae::Sop/SVG_Import` はこのノード名であることが分かる。後ろの `::2` の部分はバージョン表記となっている。
それの、`PythonModule` の部でなんらかのエラーを起こしている様だ。

この `PythonModule` は、HDA 自体に埋め込む Python スクリプトとなっていて、この名前は既定されている。

という事で、まずはそこを見に行こう。

まずは、HDA ノードを **Allow Editing of Contents** を実行してい編集状態にする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51857/04eb84fc-d0a3-cc82-8f63-40a3eedbc3f0.png)

そして、`Type Properties...` から、**Edit Operrator Type Properties** ウィンドウを立ち上げる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51857/e4a18b8f-4464-32bf-f005-82b6dc8581e3.png)

立ち上げたら、Script タブに行き、PythonModule の中を見る。

:::note info
**Houdini ノードの Python 組み込み場所について**
HDA の Python 挙動の組み込みの中には、外部モジュール化されているもの、ボタンに埋め込んであるものなどある。
より真の部分の機能の組み込みとして実装される場合は、こちらに記載されることが多い。
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51857/4711bef9-5a59-c444-2ce6-916435118b49.png)

先のエラーに立ち戻ってみて、次のエラーをよく見てみる。


```
  File "C:\PROGRA~1/SIDEEF~1/HOUDIN~1.410/houdini/python3.11libs\toolutils.py", line 1886, in createModuleFromSection
```

以上の文から、toolutils.createModuleFromSection 関数でトラブルを起こしている様だ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51857/843c0a91-bcb3-99f2-85c6-a39283cf6271.png)

## 3, toolutils.createModuleFromSection の振る舞いを追ってみる

これは Houdini 側が用意してくれている HDA やツール回りのためのヘルパーメソッドとなっている。
既に機能を知っていれば問題ないが、もし知らない場合はその機能を調べる。

ぐぐると、次のようにヒットする。

https://www.sidefx.com/ja/docs/houdini/hom/hou/HDAModule.html

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51857/dc362169-0332-6281-ed7b-27e43a59706f.png)

つまるところ、この機能は、さらに内包されている Python スクリプト情報を読み取り、HDA の関数として扱うようにするためのヘルパメソッドであることが分かる。

左のパネルに注目すると、さらに二つの項目がある。
parser の方は、PythonModule の項目からコメントアウトされているため、使用されていないことが分かる。
しかしながら、path の方は有効になっているため、これが何かしらトラブルを起こしていることが分かる。

## 4, path Python スクリプトの中を見る

path 項目を見てみると、内部に別途 Python コードが埋め込まれていることがわかる。

そして、そのコードを読んでいくと、すぐ3行名に先ほどのエラーにも出ていた **collections** モジュールを扱っている箇所が出てくる。

どうやら、ここの扱いが悪いようだ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51857/9203244f-4804-5363-1d60-345b77f20611.png)

## 5, **collections** モジュールの `MutableSequence` について調べる

**collections** モジュールは、Python 自体にバッテリーインクルードされているデータコンテナを扱うためのモジュールの一つ。
機能の一つとして、例えば配列情報の中からどの項目が何個あるかなどを集計するといったものがあったりする。

先のエラーには、MutableSequence という関数を読み込 (import) めないというエラーが出ていた。

```
ImportError: cannot import name 'MutableSequence' from 'collections' 
```

これを調査すると、次のようなページがヒットする。

https://docs.python.org/ja/3.11/library/collections.abc.html#collections.abc.MutableSequence

よく読んでみると、この機能は、`collections` 直下ではなく、`collections.abc` の部分に含まれている様子。
なので、実際には、以下の様に読み込まれるべきであることが分かる。

```python
from collections.abc import MutableSequence
```

という事で該当コードを直してあげる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51857/5c6e6136-b86d-da67-a7dd-3af985a92237.png)


修正が終わったら、Accept か Apply を実行して保存する。

## 6, 動作の確認

では動作の確認をしてみる。
再度、**SVG Import** を作成してみる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51857/36aff400-48bb-4684-7af1-9170562c8489.png)

問題なく作成できた:exclamation::blush:

ただ、レンダーフラッグが立っていないので、実際に処理が走っているわけではない。
単に HDA の作成処理としては問題がなかったというだけであるのが現状だ。

という事で、レンダーフラッグを立ててみる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51857/7a7c9f87-619b-16b1-0e80-abae75911c2e.png)


エラーが出た:exclamation::exclamation::exclamation::exclamation::heart_eyes:

このエラーは、最初に確認した際出ていたエラーと同様のものであることが分かる。

ということで、今度はこのエラーを直す。

## 7, `/obj/geo1/SVG_Import2/SVG_IMPORT_UPDATED` Python SOP のエラーを直す

Houdini はとても親切なことに、エラーを確認する際、どのノードで起こったかも上流階層から簡単に追えるようになっている。

`/obj/geo1/SVG_Import2/SVG_IMPORT_UPDATED` となっている部分をクリックすると、直接原因となっている Python SOP ノードが選択状態で表示される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51857/a40f1634-6da3-fecb-4501-b0902c8e8511.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51857/9dd26630-e78e-71df-38a0-16d86fe41c12.png)

さらにエラーをよく読んでいくと、どうやらこの Python SOP で使われている **tinycss** というモジュールが見つからないというエラーが発生している事がわかる。

## 8, モジュールが見つからない問題の解決

「この **tinycss** モジュールはどこ？」という事で、HDA のレポジトリ階層を見てみる。
すると、`python3.7libs` という階層が存在することが分かる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51857/456e0a57-88a1-8c1a-7bb4-df56935f5326.png)

中身を見てみると、この中に **tinycss** モジュールが含まれていることもわかる。
どうやら、この **tinycss** モジュールを読み込みたい様だ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51857/df5661e7-d124-c11b-8360-955bd2f4270c.png)

さて、この `python3.7libs` は名前の通り、Houdini が **「Python 3.7 用のライブラリだよ」** と認識するための規定ディレクトリ名となっている。

つまり、Houdini 20.5.410 時点では **Python 3.11 が**組み込まれているため、これに対応したディレクトリを作る必要があることが分かる。

対応方法は簡単で、コピーして名前を変えるだけ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51857/06e7b861-feee-22e2-5e80-f5da97aa4f4a.png)

このフォルダは、Houdini の起動時のみ認識するようになっているため、Houdini を再度立ち上げなおす。

## 9, 動作確認

さて、では、再度 SVG Import ノードを作ってみる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51857/d8a54e04-bbbd-59b3-6d21-e0ca0cf88b64.png)

動いた:exclamation::exclamation::exclamation::exclamation::exclamation:
解決:exclamation::exclamation::exclamation::exclamation::exclamation::exclamation:


# さいごに

この広い世界の中での資産はたくさんある。
ただ、それが果たして今動くかは分からない。
しかしながら、それを直すことが近道なのなら、迷わずエラーに突っ込んでいってみよう(●´ω｀●)
## PS...

ちなみに、元レポジトリに PR を出してみたが…。
Aeoll さんは近年アクティブ状態ではないようなので反応してもらえるかは分からない…。。

https://github.com/Aeoll/ae_SVG/pull/13