2017-06-24 13 views
0

ゲームエンジンのコードをcythonに移行する際に、私はVertex Buffer Object(Vbo)クラスを移植しています。このVboクラスを使用して、3DモデルデータをGPUに送信します。コード(vbo.pyx)は、現在、次のようになります。cythonでcontextlibを使用する

cimport gl 
from enum import Enum 
import contextlib 

class VboTarget(Enum): 
    ARRAY = gl.GL_ARRAY_BUFFER 
    INDEX = gl.GL_ELEMENT_ARRAY_BUFFER 

cdef class Vbo: 
    cdef readonly gl.GLuint id_ 
    cdef readonly double[:] data 
    cdef readonly int target 

    def __init__(self, data=None, target=VboTarget.ARRAY): 
     gl.glewInit() 
     gl.glGenBuffers(1, &self.id_) 
     self.target = target.value 
     if data is not None: 
      self.data = data 

    @contextlib.contextmanager 
    def bind(self): 
     gl.glBindBuffer(self.target, self.id_) 
     try: 
      yield 
     finally: 
      gl.glBindBuffer(self.target, 0) 

    def set_data(self, new_data): 
     self.data = new_data 

    def update(self):#perform gpu update 
     with self.bind(): 
      gl.glBufferData(self.target, self.data.nbytes, &self.data[0], gl.GL_DYNAMIC_DRAW) 

が、私はそれをきれいにし、自動的に発生するGPUに結合し、バインド解除、そのバッファを確保することになるだろうとcontextlibを使用したいと思います。 cythonコードはエラーなしでコンパイルされます。

Traceback (most recent call last): 
    File "main.py", line 2, in <module> 
    import vbo 
    File "vbo.pyx", line 21, in init vbo (vbo.c:15766) 
    @contextlib.contextmanager 
    File "C:\Python27\lib\contextlib.py", line 82, in contextmanager 
    @wraps(func) 
    File "C:\Python27\lib\functools.py", line 33, in update_wrapper 
    setattr(wrapper, attr, getattr(wrapped, attr)) 
AttributeError: 'method_descriptor' object has no attribute '__module__' 

私はこのメッセージをどのように解釈するか本当にわからない。私は私のpythonコードにこのcythonモジュールをインポートするときしかし、私は次のエラーメッセージが表示されます。もし私がcdef classcontextlibデコレータを使用することはできますか? contextlibはcythonと互換性がありますか?


アップデート:ここで ではなく__enter____exit__を使用して代替バージョンである:(Cython 0.25.1が、あるよう

cimport gl 
from enum import Enum 
from cpython cimport array 
import array 

class VboTarget(Enum): 
    ARRAY = gl.GL_ARRAY_BUFFER 
    INDEX = gl.GL_ELEMENT_ARRAY_BUFFER 

cdef class Vbo: 
    cdef readonly gl.GLuint id_ 
    cdef readonly float[:] data 
    cdef readonly int target 

    def __init__(self, data=None, target=VboTarget.ARRAY): 
     gl.glewInit() 
     gl.glGenBuffers(1, &self.id_) 
     self.target = target.value 
     if data is not None: 
      self.data = data 

    def set_data(self, float[:] new_data): 
     self.data = new_data 

    def update(self):#perform gpu update 
     with self: 
      gl.glBufferData(self.target, self.data.nbytes, &self.data[0], gl.GL_STATIC_DRAW) 

    def __enter__(self): 
     gl.glBindBuffer(self.target, self.id_) 

    def __exit__(self, exc_type, exc_val, exc_tb): 
     gl.glBindBuffer(self.target, 0) 
+0

'cdef'クラスの代わりにCythonで通常のクラスを使用すると、' contextlib'はほぼ確実に互換性があります。 'cdef'クラスで動作するかどうかはまだ分かりません。複雑に見えます! – DavidW

+0

@DavidW代わりに通常のクラスを使用すると、私のコードで 'cdef'拡張タイプを使用できなくなります。 Vbosは私がPythonだけを使用していたパフォーマンスのボトルネックの1つでしたので、可能な限り多くの変数をタイプしたいと思っています。 'cdef'クラスの代わりに' __enter__'と '__exit__'を使って' contextlib'を使うこともできますが、 'my_vbo.bind() 'ではなく' with my_vbo: 'のようなものを呼び出すでしょう。理想的ではありません。 – CodeSurgeon

+1

はい - 通常のクラスには型付き属性はありません。私はあなたが望むインターフェイスと 'cdef'クラスを保持する' bind() 'の' __enter__'属性と '__exit__'属性を持つ普通のクラスを返すことができたと思います。 ) – DavidW

答えて

2

はあなたのコードの簡易版を試してみました、私のために働くように見えますPython 3.6.1):

import contextlib 

cdef class Vbo: 

    def __init__(self): 
     pass 

    @contextlib.contextmanager 
    def bind(self): 
     self.do_something() 
     try: 
      yield 
     finally: 
      print("Finally") 

    def do_something(self): 
     print("something") 

もっと複雑な例の変更は実際には影響しませんが、 gl.pxdがないので、テストするのが非常に難しいです。あなたのCythonバージョンが最新のものであることを確認する価値があるかもしれません...(0120) Python 3.6 has an AttributeError catch blockとPython 2.7 doesn't catch the errorです。したがって、私はそれがCythonの動作の変化だとは思わないので、おそらく実際にはバグではありません。


コメントで述べたように、あなたは同じ動作を取得するために__enter____exit__と非cdefclassを使用することができます。

cdef class Vbo: 

    def __init__(self): 
     pass 

    def bind(self): 
     class C: 
      def __enter__(self2): 
       # note that I can access "self" from the enclosing function 
       # provided I rename the parameter passed to __enter__ 
       self.do_something() # gl.BindBuffer(self.target, self.id_) for you 

      def __exit__(self2, exc_type, exc_val, exc_tb): 
       print("Done") # gl.glBindBuffer(self.target, 0) 
     return C() 

    def do_something(self): 
     print("something") 
ので

要約で - 私は再現することはできませんあなたの問題ですが、ここに別の方法があります...

+0

ありがとうございました! 'cython --version'は私が0.25.2を使用していることを示していましたが、Python 2.7.12を使っています。あなたの最初のサンプルは私の質問と同じエラーを、興味深いことに十分に与えます。しかし、あなたの2番目の解決策は、私の 'gl *'関数を使って完全に動作します!私は 'self'を' self2'に改名することを考えなかったでしょう。 – CodeSurgeon

+1

名前 'self'はPythonの慣例に過ぎません。必要に応じていつでも変更することができます。 'contextlib'問題についてはバグ報告をhttps://github.com/cython/cython/issues/に提出する価値があるかもしれませんが、これは役に立たない回帰と思われるためです。 – DavidW

+0

それはそれをクリアする!私は今、そこにバグレポートを提出したので、うまくいけば、これは解決されるでしょう。 – CodeSurgeon

1

これは実際には最新のCythonマスターCythonバージョン0.26b0)。 @ DavidWの答えに記述されているコンテキストライブラリコードの簡略化されたバージョンは、binding=Trueのヒントがソースファイルの先頭に適用されている限り完全に機能します。このCythonの問題の議論はhereで見つけることができます。

関連する問題