Python for .NET の「Python for .NET」のほう、「.NET for Python」でなくて (少し進展)

これの続き。

これと同じノリで、C# コードを書く、ビルドする、実行する、を全部一個のスクリプトでまかなっちゃう、面倒なので。

で、「寸止め」版:

 1 # -*- coding: utf-8 -*-
 2 # ---------------------------------------------------
 3 from __future__ import absolute_import
 4 from __future__ import unicode_literals
 5 from __future__ import print_function
 6 
 7 
 8 if __name__ == '__main__':
 9     # ファイルがバラけてると実験が鬱陶しいので C# コードをここに書いてしまって
10     # ビルドも全部やってしまう。
11     import subprocess, os, io, sys
12 
13     # C# コード
14     #   公式サイト説明に欠けてるのは:
15     #       using Python.Runtime;
16     #   PythonEngine.Initialize(); がいるのかいらないのか、入ってなくても
17     #   動いた。CPython の C API のノリなら必要なのだが?
18     #
19     io.open("PyFromCSExam.cs", "w").write("""
20 using System;  // for Console, etc.
21 using System.Collections.Generic;  // for List
22 using Python.Runtime;
23 
24 namespace PyFromCSExam
25 {
26     public class Exam
27     {
28         static void Main(string[] args)
29         {
30             using (Py.GIL())
31             {
32                 dynamic np = Py.Import("numpy");
33                 Console.WriteLine(np.cos(np.pi * 2));
34         
35                 dynamic sin = np.sin;
36                 Console.WriteLine(sin(5));
37         
38                 double c = np.cos(5) + sin(5);
39                 Console.WriteLine(c);
40         
41                 dynamic a = np.array(new List<float> { 1, 2, 3 });
42                 Console.WriteLine(a.dtype);
43         
44                 dynamic b = np.array(new List<float> { 6, 5, 4 }, dtype: np.int32);
45                 Console.WriteLine(b.dtype);
46         
47                 Console.WriteLine(a * b);
48                 Console.ReadKey();
49             }
50         }
51     }
52 }
53 """)
54     # ビルド
55 
56     #     site-packages にある Python.Runtime.dll
57     import site
58     cands = site.getsitepackages()
59     for canddir in cands[1:]:
60         pythonengine_dll = os.path.join(canddir, "Python.Runtime.dll")
61         if os.path.exists(pythonengine_dll):
62             break
63     import shutil
64     shutil.copyfile(pythonengine_dll, os.path.basename(pythonengine_dll))
65     subprocess.check_call(
66         [
67             # csc がない、と言われる場合で「ほんとうは持ってる」場合は、
68             # BuildTools 2007 なら、スタートメニューから「x64_x86 Cross Tools
69             # Command Prompt for VS 2017」(等)を探して起動し、その環境から。
70             "csc.exe",
71             "-nologo",
72             "-target:exe",  # as console app
73             "-out:PyFromCSExam.exe",
74             "-reference:" + os.path.basename(pythonengine_dll),
75             #"-reference:" + pythonengine_dll,
76             #"-lib:" + pythonengine_dll,
77             "PyFromCSExam.cs",
78             ]
79         )
80     # 実行しちゃいなよ
81     subprocess.check_call(
82         [
83             "PyFromCSExam.exe",
84             ])

「寸止め」つーか「ダメ」ポイントが3つ:

  1. Python.Runtime.dllをカレントディレクトリにコピーする以外の手段が見つかってない。
    (Python.Runtime.dll 自身が別のものに依存しているからっぽいがよくわからん。)
  2. ワタシの環境では Python 2.7 の python27.dll がグローバルに可視(「パスが通ってる」)ので 2.7 では動くが、Python 3.5 環境の方がそうなってないので、python35.dll が見つからない、となって動かない。
  3. 「動く」ほうの 2.7 も、4つ目の Console.WriteLine のあとに落っこちる。

この3つのうち、1. と 2. はプロジェクトによっては問題じゃなかったりする。つまり「エンドユーザが Python をインストールしてようがしてまいが」という何か .NET アプリケーションを開発して「配る」なり「売る」んであれば、自分のインストールフォルダにこれら依存物は一緒に放り込むであろうから。でも多分措置出来ると思うので、頑張ってはみる。

最後のは清く正しく .NET 例外が説明してくれてる(元となったのは python 側の例外らしい(「TypeError : long() argument must be a string or a number」))。公式のサンプルはどうやら型の扱いを間違っているみたい。すぐに措置出来ると信じているが、保障は出来ない。まぁ頑張ってみる。

続く。