クロスコア(CLR / JVM)Pythonコード

これは、Pythonを使用してC#からJavaに移植されたライブラリのテストをどこでも実行する方法について、非常に特化した短いメモです。



ポイントはこれです:商業的な理由でC#からJavaに移植された、大きくて太くて美しいライブラリがあります。 APIはほぼ同じままで、別の言語に切り替えると命名規則が自然に変わりました。 ライブラリクローンが元と同じように動作することを検証するテストの厚いバンドルを作成する必要がありました(言い換えると、回帰テスト)。 これを行うために、ライブラリコードの結果(一部のバイナリおよびxml-metadata)が比較されました。 テストは自明ではなく、多くのテストがあり、最も不愉快なものがありました。彼らは常に4人のチームによって一方から補足されていました。 しばらくの間、私はそれらをJavaに熱心に移植し、その後、チームがCLR(古いライブラリを使用)およびJVM(クローンを使用)ですぐに実行できる言語でテストを作成することを提案しました。 彼ら自身がしばらくPythonについて考えていたことが判明しました。 そして、これはそれが起こった方法です。



1.基本


さて、プリミティブについて。 CLRでは、IronPythonでコードを実行し、JVMではJythonでそれぞれ実行します。 幸いなことに、Jython自体は、ゲッターセッターではなくプロパティメカニズムを備えたロード可能なJavaクラスを提供します。 なんでいいの? Javaに移植するとき、sharpyyプロパティはゲッターセッターによって自然に置き換えられたためです。



2.どこにいるの?


上で実行しているランタイムを知ることは有用です。 ここではすべてが簡単です。

isDotNet = sys.version.find('IronPython') > -1
      
      







3.必要なライブラリを接続する


IronPythonとJythonでは、これはさまざまな方法で行われ(IronPythonでclrライブラリーを使用し、Jythonでパスに単純に追加します)、モジュールの命名規則が異なるため、統一します。



 def cpUseLibrary(lib_path, library): if isDotNet: sys.path.append(lib_path) import clr clr.AddReference('OurProduct20.' + library) else: sys.path.append(lib_path + '\\java_libs\\ourproduct20.' + library.lower() + '.jar')
      
      







これは、次の愚かな方法で作業コードで使用されます。

 cpUseLibrary(path_to_bins, 'Core') cpUseLibrary(path_to_bins, 'Formats.Common')
      
      







4.必要なクラスをロードします。


ここに小さな問題があります。 一般に、両方のインタープリターを使用すると、必要な名前空間/パッケージからの愚かなインポートが可能になりますが、パッケージの命名規則が異なることを考慮して、動的な設計が必要です。



 def cpImport(module, clazz, globs = globals()): # load class mname = ''; if isDotNet: mname = 'OurProduct.' + module else: mname = 'com.ourcompany.ourproduct.' + module.lower() pckg = __import__(mname, globals(), locals(), [clazz], -1) aliased_class = getattr(pckg, clazz) globs[clazz] = aliased_class
      
      







ポイントは、モジュールからインポートするためのPython内部メカニズムとして__import __()を使用することです。 実際、__ import __()の「どこかから何かをインポートする」というエントリが最終的に展開されます。 まだポイントに達していない現在の問題は、そのようなインポートに毎回グローバル()を渡す必要があることです(すべてのクロスプラットフォームメソッドが別のモジュールに移動されるため)。 次のようになります。

 cpImport('Core', 'OurProductLicense', globals()) cpImport('Formats', 'OurProductCommonFormats', globals())
      
      







あまり美しくありませんが、繰り返しますが、両方のランタイムで統一して動作します。



5.命名規則の問題を解決する


命名方法の違いは非常に簡単です。C#では大文字で始まり、Javaでは小文字で始まります。 メタプログラミングが可能なため、現代のスクリプト言語は便利です。 おそらくLispではなく、とにかく。 ここのRubyはさらに便利ですが、それほど悪くはありません。 cpImport()を追加します。



  aliased_class = getattr(pckg, clazz) # add uppercased aliases for its lowercased methods (unless there's already an uppercased method with the same name) original_methods = aliased_class.__dict__.copy() for name, method in original_methods.iteritems(): if name[0:1] in string.uppercase: continue newname = name[0:1].upper() + name[1:] if hasattr(aliased_class, newname): continue setattr(aliased_class, newname, method) globs[clazz] = aliased_class
      
      







ここでのポイントは、setattr()を使用してメソッドにエイリアスを書き込むことです。 実際、小文字で始まるすべてのメソッドについて、大文字で始まるクラス内にエイリアスを作成します。



一般的にはどのように見えますか。 Test.pyファイル:



 from cptest import isDotNet, cpUseLibrary, cpImport cpUseLibrary('Core') cpUseLibrary('Formats.Common') cpImport('Core', 'OurProductLicense', globals()) cpImport('Formats', 'OurProductCommonFormats', globals()) OurProductLicense.SetProductID(".NET Product ID" if isDotNet else "JavaProductID") OurProductCommonFormats.Initialize();
      
      







タンバリンと踊ることなく、1つのスクリプトから単純なipy test.py / jython test.pyによって実行されます。 なんて楽しい。



PS考えられる問題


まあ、彼らはかなり明白です。 幸運なことに、開発者は移植プロセスの間、名前の命名と移植の1つの規則に細心の注意を払っていました。 したがって、ライブラリ、メソッドなどは、上記のように簡単に呼び出すことができます。 それ以外の場合は、何らかの例外プレートを作成し、cpImport()メソッドでそれを考慮する必要があります。 しかし、一般的に、私は説明されたアプローチが本当に好きで、そのようなまれで、おそらく遭遇する問題に対する生きた解決策のように見えます。



All Articles