ruby.wasmからのWebSocket - @ledsun blog にてruby.wasmからJavaScriptのWebSocket.sendを直接呼び出せませんでした。
Rubyから他言語のオブジェクトのメソッドを呼び出す先輩にはGitHub - mrkn/pycall.rb: Calling Python functions from the Ruby languageがあります。 PyCallではどのような仕様なっているのでしょうか? PyCallからPythonのオブジェクトのsendメソッドを直接呼び出せるのでしょうか?
結論。直接は呼び出せません。呼び出す時はPyCall.getattrを使います。
sendメソッドを直接呼び出すとmethod_missing
実行環境を準備します。
bundle init bundle add pycall
require 'pycall' client = PyCall.import_module 'http.client' conn = client.HTTPConnection('www.example.com') conn.send('GET / HTTP/1.0\n\n')
Pythonの標準ライブラリーに含まれる http.client --- HTTP プロトコルクライアント — Python 3.12.5 ドキュメント にsendメソッドがあります。 これで試してみます。
実行します。
> ruby test_send.rb /home/ledsun/.rbenv/versions/3.3.4/lib/ruby/gems/3.3.0/gems/pycall-1.5.2/lib/pycall/pyobject_wrapper.rb:54:in `method_missing': undefined method `GET / HTTP/1.0\n\n' for class <class 'http.client.HTTPConnection'> (NoMethodError) from test_send.rb:5:in `<main>'
method_missing': undefined methodGET / HTTP/1.0\n\n'
これはRubyのObject#send メソッドが呼ばれた時のエラーです。
エラーが起きた箇所のソースコードを見てみます。
https://github.com/mrkn/pycall.rb/blob/master/lib/pycall/pyobject_wrapper.rb#L35-L53
def method_missing(name, *args) name_str = name.to_s if name.kind_of?(Symbol) name_str.chop! if name_str.end_with?('=') case name when *OPERATOR_METHOD_NAMES.keys op_name = OPERATOR_METHOD_NAMES[name] if LibPython::Helpers.hasattr?(__pyptr__, op_name) LibPython::Helpers.define_wrapper_method(self, op_name) singleton_class.__send__(:alias_method, name, op_name) return self.__send__(name, *args) end else if LibPython::Helpers.hasattr?(__pyptr__, name_str) LibPython::Helpers.define_wrapper_method(self, name) return self.__send__(name, *args) end end super # << ここでエラーが起きています。 end
method_missingが呼ばれています。
これはObject#sendの引数で'GET / HTTP/1.0\n\n'が実行されて起きています。
試しにmethod_missingの先頭にデバッグ出力を足してみます。
def method_missing(name, *args) p "method_missing: #{name}" name_str = name.to_s if name.kind_of?(Symbol) ...
►ruby test_send.rb "method_missing: path" "method_missing: append" "method_missing: dict" "method_missing: list" "method_missing: slice" "method_missing: HTTPConnection" "method_missing: GET / HTTP/1.0\\n\\n" "method_missing: repr" /home/ledsun/.rbenv/versions/3.3.4/lib/ruby/gems/3.3.0/gems/pycall-1.5.2/lib/pycall/pyobject_wrapper.rb:53:in `method_missing': undefined method `GET / HTTP/1.0\n\n' for class <class 'http.client.HTTPConnection'> (NoMethodError) from test_send.rb:5:in `<main>'
やはり、引数にsendは来ていません。
追記:sendを呼び出すにはPyCall.getattrを使う
コメント欄にて id:mrkn さんからsendメソッドの呼び出し方を教えてもらいました。
さらに、他の間違いも教えてもらっています。
修正したスクリプトが次です。
require 'pycall' client = PyCall.import_module 'http.client' conn = client.HTTPConnection.new('www.example.com') PyCall.getattr(conn, 'send').("GET / HTTP/1.0\r\n\r\n".b)
これを実行するとエラーは起きません。
►ruby test_send.rb
HTTPConnection.sendの戻り値は特に処理していないので、表示は何もされません。