Méthodes MessagePack-Call Python (ou Python vers Ruby) à partir de Ruby à l'aide de RPC

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.

supposition

Qu'est-ce que MessagePack-RPC?

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.

Exemple minimal

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'`.

Exécution asynchrone

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()

Appeler matplotlib depuis Ruby

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é.

image

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. ..

Appeler les méthodes Rails depuis Python

À 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.

Traitement difficile à réaliser avec RPC

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

Recommended Posts

Méthodes MessagePack-Call Python (ou Python vers Ruby) à partir de Ruby à l'aide de RPC
Comment appeler Python ou Julia à partir de Ruby (implémentation expérimentale)
[Python] Conversion de DICOM en PNG ou CSV
Je souhaite envoyer un e-mail depuis Gmail en utilisant Python.
Changements de Python 3.0 à Python 3.5
Changements de Python 2 à Python 3.0
Python depuis ou import
Notification PUSH de Python vers Android à l'aide de l'API de Google
Copier les fichiers S3 de Python vers GCS à l'aide de GSUtil
Requête de Python vers Amazon Athena (à l'aide du profil nommé)
Aplatir à l'aide du rendement Python de
Publier de Python vers Slack
Anaconda mis à jour de 4.2.0 à 4.3.0 (python3.5 mis à jour vers python3.6)
Publier sur Twitter en utilisant Python
Commencez à Selenium en utilisant python
Passer de python2.7 à python3.6 (centos7)
Connectez-vous à sqlite depuis python
Conversion de Pandas DataFrame en System.Data.DataTable à l'aide de Python pour .NET
J'ai essayé d'utiliser la bibliothèque Python de Ruby avec PyCall
Appelez Matlab depuis Python pour optimiser
Comment installer Python à l'aide d'Anaconda
Utilisation de Rstan de Python avec PypeR
Remarques sur l'utilisation de MeCab depuis Python
Publication de Python sur la chronologie Facebook
Comment obtenir des abonnés et des abonnés de Python à l'aide de l'API Mastodon
[Lambda] [Python] Publier sur Twitter depuis Lambda!
Appeler popcount depuis Ruby / Python / C #
Connectez-vous à la base de données utf8mb4 à partir de python
Utiliser Cloud Storage depuis Python3 (Introduction)
Python (de la première fois à l'exécution)
Publier une image de Python sur Tumblr
Comment accéder à wikipedia depuis python
Python pour passer d'une autre langue
Exécutez Ansible à partir de Python à l'aide de l'API
Précautions lors de l'utilisation de phantomjs de python
Accéder aux feuilles de calcul à partir de Python à l'aide d'OAuth 2.0
Essayez d'utiliser Amazon DynamoDB à partir de Python
N'a pas changé de Python 2 à 3
Mettre à jour Mac Python de 2 à 3
De la préparation à l'analyse morphologique avec python en utilisant polyglotte au marquage des mots partiels
Comment gérer l'erreur OAuth2 lors de l'utilisation des API Google à partir de Python
Créez un outil qui secoue automatiquement furigana avec html en utilisant Mecab de Python3
Essayez d'utiliser le framework web de Python Django (1) - De l'installation au démarrage du serveur
Comment obtenir la valeur du magasin de paramètres dans lambda (en utilisant python)
[Introduction à Udemy Python3 + Application] 18. Méthode List
Introduction à la simulation d'événements discrets à l'aide de Python # 1
Lier des méthodes aux classes et instances Python
Comment mettre à jour Google Sheets à partir de Python
Manuel Python privé (mis à jour de temps en temps)
Je veux utiliser jar de python
Conversion de katakana en voyelle kana [python]
Notification push du serveur Python vers Android
Connectez-vous à Slack à l'aide de requêtes en Python
Référence du fichier INI en Python ou Ruby
Connexion de python à MySQL sur CentOS 6.4
J'ai essayé d'utiliser l'API UnityCloudBuild de Python
Portage et modification du solveur de doublets de python2 vers python3.
Essayez d'utiliser Excel en utilisant Python (Xlwings)
Comment accéder à RDS depuis Lambda (python)