Кодирование Pyro4 в JSON/Base64

Итак, у меня есть простое приложение, использующее Pyro4 (Python Remote Objects). Существует открытый класс API, и у меня есть файл, который вызывает функцию api.parse(input,userid), которая возвращает некоторый JSON в зависимости от ввода. Однако вместо того, чтобы просто возвращать результат в виде строки (как это делалось ранее), он возвращает {'data': 'eyJlcnJvciI6ICJDb21tYW5kIG5vdCByZWNvZ25pc2VkIn0=', 'encoding': 'base64'} , где base64 — это строка JSON, которую должен вернуть parse.

Я очень смущен тем, почему это не работает - я тестировал это ранее, и проблем не было, строка была просто возвращена без странной кодировки base64. Единственное, о чем я могу думать, это то, что я изменил сети (подключение в школе к домашнему подключению), но я не думаю, что это должно быть проблемой? Я подготовил MVE некоторого кода, указывающего на проблему.

тестовый сервер.py

import Pyro4;
import json;

@Pyro4.expose
class API:
    def parse(self,buff,userid):
        return prep({"error":"Command not recognised"});

def prep(obj):
    return json.dumps(obj).encode("utf-8");

# Pyro stuff
daemon = Pyro4.Daemon()                # make a Pyro daemon
ns = Pyro4.locateNS()                  # find the name server
uri = daemon.register(API)   # register the greeting maker as a Pyro object
ns.register("testAPI", uri)   # register the object with a name in the name server
daemon.requestLoop()

testclient.py

import Pyro4;
import json;

api = Pyro4.Proxy("PYRONAME:testAPI");
resp = api.parse("whatever","something");
print(resp); # Produces {'data': 'eyJlcnJvciI6ICJDb21tYW5kIG5vdCByZWNvZ25pc2VkIn0=', 'encoding': 'base64'}
# I just want b'{"error":"Command not recognised"}'

Примечание. Печать на этапе применения prep в parse() дает ожидаемый результат b'{"error":"Command not recognised"}'. Я использую команду python3 -m Pyro4.naming для запуска сервера имен, если это тоже имеет значение. Я думаю, что, вероятно, есть какая-то глобальная настройка/константа, которую я не установил правильно или что-то в этом роде. Приветствуются все ответы, спасибо!


person James Paterson    schedule 01.03.2017    source источник
comment
Также просто примечание - да, я мог бы просто декодировать строку base64, но я хочу знать, почему это происходит (и если я пишу код для декодирования base64, и он переключается обратно, мне придется повторно отлаживать Это).   -  person James Paterson    schedule 02.03.2017


Ответы (2)


Протокол сериализации по умолчанию, который использует Pyro, — serpent, и это текстовый протокол. Это означает, что он не может передавать двоичные данные (байты), если он не кодирует их в текстовый формат, что он и делает способом, который вы обнаружили.

Доступна небольшая вспомогательная функция (serpent.tobytes), которую вы можете использовать в своем клиентском коде, которая автоматически обнаруживает и преобразует ответ, если это необходимо, см. информационное поле в этом абзаце руководства: https://pythonhosted.org/Pyro4/tipstricks.html?highlight=base64#binary-data-transfer-file-transfer

Конечно, если вы в первую очередь делаете данные, отправляемые вашим сервером, строками, в этом нет необходимости. (Это невозможно сделать, если данные действительно являются бинарными, например изображение, звуковой клип или что-то еще)

В вашем случае с текстом json он преобразуется в байты, если вы encode его. Как вы обнаружили, в этом нет никакой необходимости, просто оставьте его как строку, и у вас не возникнет проблем. (что оставляет меня с вопросом, почему вы все еще выполняете кодирование байтов utf-8 в своем клиенте?)

person Irmen de Jong    schedule 02.03.2017
comment
Спасибо за более понятный и подробный ответ. Позже данные отправляются через сокетное соединение (для которого нужны байты), поэтому я хотел выполнить всю обработку за один шаг. - person James Paterson; 02.03.2017
comment
Хорошо, есть еще одна вещь: почему вы вообще используете json-кодирование на своем сервере? Просто верните объект ответа как есть, и пусть клиент решает, что с ним делать! - person Irmen de Jong; 02.03.2017
comment
Это немного сложнее. У меня есть эта конфигурация сервера/клиента, которая затем отправляется другому клиенту. Например: Datastore ---(Pyro)---›Frontend---(Socket)---›Client, поэтому я не просто использую объект RMI напрямую. - person James Paterson; 02.03.2017

Оказывается, Pyro не любит пытаться отправлять необработанные байты — он скорее конвертирует байты в base64, а затем отправляет их как JSON. Итак, чтобы исправить, я изменил:

def prep(obj):
    return json.dumps(obj).encode("utf-8");

to

def prep(obj):
    return json.dumps(obj);

И поместите бит кодирования в клиент.

person James Paterson    schedule 01.03.2017