画像処理ソフトでBlenderのPython APIみたいなものを作りたかった（２）

本記事は[この記事](https://qiita.com/ambara/items/30bf69b780485bff1436)の続きです．
画像処理ソフトとタイトルにありますが本記事では前記事の画像処理ソフトウェアには触れません．

実装は[Github上](https://github.com/murata721/imagepy)のtest内にあります．

## 前記事の問題点
以前の実装ではメインプロセスでソフトウェア部分をサブプロセスで追加したスクリプトを処理していました．この方法ではメインプロセスとサブプロセス間でのやり取りが標準入出力である必要があり，参照渡しが出来ませんでした．
<div align="center">
<img width=500 alt=process.png src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/624506/8b77fd5b-0799-80ed-c05c-bf7e8315291f.png>
</div>
また，subprocessを用いるとインジェクション攻撃の心配をしなくてはなりません．
## 改善策
## importの仕様
前記事の追記でも書いていますが，importを用いることでこの問題を解決します．
importはスクリプトを実行することが出来るので，例として以下のように書くことが出来ます．

```python:Hello.py
print("Hello")
```

```python:main.py
import Hello
```

main.py 実行時の出力

```
Hello
```
しかし，これではmain.py内で定義された変数を参照渡しすることが出来ません．
そのため，main.py内で定義された変数を渡すためには以下のように関数を定義します．

```python:func_num_show.py
def num_show(num):
    print(num)
```

```python:main.py
main_num = 42
import func_num_show
func_num_show.num_show(main_num)
```
main.py 実行時の出力

```
42
```
これは普段皆さんが使うライブラリと同じような使い方になります．

## 実装
では実際にどのように実装したのか見ていきましょう．
<div align="center">
<img width=500 alt=process.png src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/624506/076a5fe5-daaf-e805-cad3-10a2cb4b5f07.png>
</div>

今回とった方法では自作のスクリプトに「importで渡すための関数」の記述を含めないようにするため，文字列処理でスクリプトをラッピングしています．

まず，渡したい変数を定義します．次に，任意の処理を書いたスクリプトをライブラリに変換するため文字列処理を行います．

```python:main.py
from rapper import rapper

main_a = 15
main_b = 30

rapper.rap_my_script("process.py")

"""後略"""
```

rapper.py内のrap_my_script()で文字列処理を行い簡易的なライブラリであるrapper/tmp.pyを作ります．

```python:rapper/rapper.py
def rap_my_script(script_path,
                  rapper_path="rapper/rapper.txt",
                  write_path="rapper/tmp.py"):

    with open(rapper_path) as f:
        s_rapper = f.read()

    with open(script_path) as f:
        s_read_list = f.readlines()

    s_read = "    ".join(s_read_list)

    tmp_s = s_rapper.format(s_read)

    with open(write_path, mode='w') as f:
        f.write(tmp_s)
```
この時 rapper/rapper.txt は以下のようになっており，上記関数内の tmp_s = s_rapper.format(s_read)で自作スクリプトの内容を関数化しています．

```python:rapper/rapper.txt
def read_and_process(*args):
    {0}
    return rap_obj
```

その後 main.py でrapper/tmp.pyをimportしてread_and_process()関数を実行します．
この時read_and_process()関数の引数に渡したい変数を入れることでrapper/tmp.pyに参照渡しすることが出来ます．

```python:main.py
"""前略"""
# read created tmp.py
from rapper import tmp
return_obj = tmp.read_and_process(main_a, main_b)
```
ちなみに，今回の自作スクリプトは以下のように変数を解釈するrap_classを介して，変数を読み取っています．
処理としては，引数の一つ目に+100して，表示しているだけです．

```python:process.py
from rapper import rap_class
rap_obj = rap_class.rap(args)

rap_obj.nums[0] += 100
rap_obj.print_nums()
```
rap_classはrapper/rap_class.py内で以下のように定義されています．

```python:rapeer/rap_class.py
class rap:
    def __init__(self, num_tuple):
        self.nums = list(num_tuple)

    def print_nums(self):
        print(self.nums)
```
これでmain.pyを実行すると以下の出力を得ます．

```
[115, 30]
```
ちゃんと自作スクリプトが変数を受け取ったうえで処理していることがわかります．
ちなみに，rapper/tmp.py内のread_and_process()関数は戻り値にrap_classオブジェクトを取るため，自作スクリプトで処理した内容をmain.pyで受け取ることが出来ます．

## 微妙な点
この仕組みによって自作スクリプトを書くだけでmain.pyで定義された変数を扱うことが出来るため当初の目的は達成されたように思われます．
しかし，自作スクリプト内に必ず以下の一分を含めなくてはなりません．

```
rap_obj = rap_class.rap(args)
```
この時，スクリプト内でargsは定義されていないため，エディターによっては未定義の変数として（実際にはmain.pyを実行できるのみの関わらず）注意を受けます．
このargsを除くための手段が思い浮かばなかったのでこのまま画像処理ソフトウェアのほうに実装しようと考えていますが，どうにかできる方法をご存じの方がいたらぜひお教え下さい．