MessagePack-Un résumé de la façon d'appeler des méthodes Python (ou vice versa) à partir de Ruby à l'aide de RPC.
«J'utilise principalement Ruby, mais je souhaite utiliser des bibliothèques de calculs scientifiques et technologiques telles que numpy et matplotlib de Python.
Si vous l'utilisez à de telles fins, vous pourrez peut-être profiter des avantages de chaque langue.
Protocole d'appel de procédure à distance. Sérialisez à l'aide de MessagePack.
Voir ici pour plus de détails sur les spécifications.
Des bibliothèques qui implémentent MessagePack-RPC dans différentes langues sont disponibles. Les deux suivants sont implémentés en Ruby et Python. Des implémentations de diverses autres langues ont été publiées.
Au fait, il semble que le plug-in de neovim puisse être implémenté à l'aide de MessagePack-RPC. L'avantage est que le plug-in peut être implémenté dans différentes langues tant qu'il est conforme aux spécifications MessagePack-RPC.
La version Ruby n'a presque aucune information dans le README même si vous regardez la page officielle de github, voici donc un bref résumé de son utilisation.
Tout d'abord, un exemple de version Ruby du serveur
server.rb
require 'msgpack/rpc'
class MyHandler
def add(x,y)
return x+y
end
end
svr = MessagePack::RPC::Server.new
svr.listen('localhost', 18800, MyHandler.new)
svr.run
Exemple de client de version Ruby
client.rb
require 'msgpack/rpc'
c = MessagePack::RPC::Client.new('localhost',18800)
result = c.call(:add, 1, 2)
puts result
Exécutez comme suit.
ruby server.rb & #démarrer le serveur
ruby client.rb #Le résultat du calcul s'affiche
Il peut être écrit en Python exactement de la même manière.
server.py
import msgpackrpc
class MyHandler(object):
def add(self, x, y):
return x+y
svr = msgpackrpc.Server( MyHandler(), unpack_encoding='utf-8' )
svr.listen( msgpackrpc.Address('localhost',18800) )
svr.start()
client.py
import msgpackrpc
client = msgpackrpc.Client(msgpackrpc.Address("localhost", 18800), unpack_encoding='utf-8')
result = client.call('add', 1, 2)
print( result )
Bien sûr, exécuter un serveur Ruby et un client Python (ou vice versa) donne exactement le même résultat.
Dans le cas de Python, il convient de noter que lorsque le client ou le serveur est initialisé, la valeur par défaut est ʻunpack_encoding = 'None', et les données transférées sont interprétées comme une chaîne d'octets. Dans l'exemple ci-dessus, il n'y a pas de problème car seules des valeurs numériques sont envoyées et reçues, mais si vous voulez envoyer et recevoir des chaînes de caractères, vous devez spécifier ʻunpack_encoding = 'utf-8'
. Sinon, les données reçues seront une chaîne d'octets et vous devrez appeler explicitement .decode ('utf-8')
dans votre programme.
À moins que vous ne souhaitiez envoyer des données binaires, il semble bon de spécifier par défaut ʻunpack_encoding = 'utf-8'`.
Le traitement côté serveur n'est pas limité au cas où il est terminé en un instant, et il peut y avoir des cas où vous souhaitez exécuter un traitement qui prend du temps. Un mécanisme d'exécution asynchrone est également fourni pour de tels cas.
La méthode appelée par call_async
retourne immédiatement le traitement et retourne un objet futur
.
L'objet Future renvoie la valeur si le résultat est renvoyé par le serveur lorsque # get
est appelé.
Si aucun résultat n'est renvoyé par le serveur, attendez que le résultat soit obtenu du serveur.
Il est rapide de voir l'exemple de code ci-dessous.
async_client.rb
require 'msgpack/rpc'
c = MessagePack::RPC::Client.new('localhost',18800)
puts "async call"
future1 = c.call_async(:delayed_add, 1, 1, 2)
future2 = c.call_async(:delayed_add, 1, 2, 3)
puts future2.get #L'ordre ne doit pas nécessairement être l'ordre d'appel
puts future1.get
async_server.rb
require 'msgpack/rpc'
class MyHandler
def delayed_add(t,x,y)
puts "delayed_add is called"
as = MessagePack::RPC::AsyncResult.new
Thread.new do
sleep t
as.result(x+y)
end
as
end
end
Vous pouvez également obtenir le résultat avec future.get ()
pour Python.
async_client.py
import msgpackrpc
client = msgpackrpc.Client(msgpackrpc.Address("localhost", 18800), unpack_encoding='utf-8')
future = client.call_async('delayed_add', 3, 1, 2)
print( future.get() )
async_server.py
import msgpackrpc
import threading, time
class MyHandler(object):
def delayed_add(self, t, x, y):
print("delayed_add is called")
ar = msgpackrpc.server.AsyncResult()
def sleep_add():
time.sleep(t)
ar.set_result(x+y)
thread = threading.Thread(target=sleep_add)
thread.start()
return ar
svr = msgpackrpc.Server( MyHandler(), unpack_encoding='utf-8' )
svr.listen( msgpackrpc.Address('localhost',18800) )
svr.start()
Comme exemple plus pratique, voici un exemple de dessin d'un tableau numérique Ruby avec matplotlib de Python.
plot_server.py
import msgpackrpc
import matplotlib.pyplot as plt
svr = msgpackrpc.Server( plt, unpack_encoding='utf-8' )
svr.listen( msgpackrpc.Address('localhost',18800) )
svr.start()
plot_client.rb
require 'msgpack/rpc'
c = MessagePack::RPC::Client.new('localhost',18800)
c.timeout = Float::INFINITY
xs = -3.14.step(3.14, 0.1).to_a
ys = xs.map {|x| Math.sin(x) }
c.call( :scatter, xs, ys )
c.call( :show )
La fenêtre suivante apparaîtra et le tracé sera dessiné.
Notez que le serveur ne renvoie aucun traitement tant que la fenêtre dessinée n'est pas fermée.
S'il est laissé tel quel, il expirera et une exception se produira du côté client, donc c.timeout = Float :: INFINITY
est défini.
Ici, les processus Ruby et Python sont démarrés manuellement à partir du shell, mais le démarrage de Python en tant que processus externe dans le programme Ruby semble être une interface soignée, comme s'il s'agissait de tracer directement à partir de Ruby. ..
À titre d'exemple dans la direction opposée, obtenez les informations dans l'application Rails à partir de Python
rails_server.rb
require 'msgpack/rpc'
require File.join( ENV['RAILS_ROOT'], 'config/environment' )
Object.class_eval do
def to_msgpack(*args) # to_Si msgpack n'est pas défini, pour_Faites-en une spécification à convertir en msgpack après avoir appelé s
to_s.to_msgpack(*args)
end
end
class MyRailsHandler
def get_book(name)
Book.where(name: name).first.as_json
# as_Faites-le Hash avec json.
end
end
svr = MessagePack::RPC::Server.new
svr.listen('localhost', 18800, MyRailsHandler.new)
svr.run
rails_client.py
import msgpackrpc
client = msgpackrpc.Client(msgpackrpc.Address("localhost", 18800), unpack_encoding='utf-8')
result = client.call('get_book', 'echo') #le résultat est un dictionnaire
print( result )
Le but est de définir to_msgpack (* args)
pour Object.
Pour les classes pour lesquelles to_msgpack
n'est pas défini, ʻObject # to_msgpack (* args)` défini ici est appelé. Cela permet de sérialiser les objets Time et autres.
RPC peut gérer un traitement simple qui n'appelle qu'une seule méthode comme nous l'avons vu jusqu'à présent. Cependant, dans les cas suivants, RPC ne semble pas le résoudre facilement.
--Si vous souhaitez passer un bloc à une fonction
-Par exemplearray.map {|x| f(x) }
Ne peut pas être transmis dans RPC lorsque vous souhaitez exécuter
Books.where (author:" foo "). Asc (: price) .first
qui est souvent vu dans Ruby. Si vous souhaitez appeler l'API Rails à partir de Python, il existe probablement de nombreuses restrictions.