Денис Катаев
tinkoff.ru
и WSDL друганя его
XML-RPC
JSON-RPC
и многообразие HTTP методов
уже немного БЕСИТ
Поверх HTTP/2
.proto файлы
RPC protocol
vs Thrift
template = "Итак, мы переводим {sum} {name}"
slots = {"sum": "1000", "name": "Азамату"}
prefix.wav + azamat.wav + 1000_rub.wavsyntax = "proto3";text to speech
syntax = "proto3";
message TemplateInput {
string template = 1;
map<string, string> slots = 2;
string backup_phrase = 3;
};t = TemplateInput(
template='Итак, мы переводим {sum} {name}',
slots={'sum': '1000', 'name': 'Азамату'}
)
b = t.SerializeToString()
b'\n.\xd0\x98\xd1\x82\xd0\xb0\xd0\xba, \xd0\xbc\xd1\x8b \xd0\xbf\xd0\xb5\xd1\x80\xd0\xb5\xd0\xb2\xd0\xbe\xd0\xb4\xd0\xb8\xd0\xbc {sum} {name}\x12\x16\n\x04name\x12\x0e\xd0\x90\xd0\xb7\xd0\xb0\xd0\xbc\xd0\xb0\xd1\x82\xd1\x83\x12\x0b\n\x03sum\x12\x041000't = TemplateInput(
template='Итак, мы переводим {sum} {name}',
slots={'sum': '1000', 'name': 'Азамату'}
)
b = t.SerializeToString()t = TemplateInput(
template='Итак, мы переводим {sum} {name}',
slots={'sum': '1000', 'name': 'Азамату'}
)
b = t.SerializeToString()
enum AudioEncoding {
ENCODING_UNSPECIFIED = 0;
LINEAR16 = 1;
reserved "FLAC";
reserved 2;
}Сахар
message Foo {
string test = 1;
}
message Bar {
string test = 1;
}
message Result {
string type = 1;
oneof object {
Foo foo = 2;
Bar bar = 3;
}
}Массивы
message Voice {
repeated string language_codes = 1;
string name = 2;
}message TemplateInput {
string template = 1;
map<string, string> slots = 2;
string backup_phrase = 3;
};
package tinkoff.tts;
import "google/protobuf/duration.proto";🤢 .gitmodules 🤢
FROM znly/protoc as proto
FROM python:3.7 as build_proto
COPY --from=proto /protobuf/google/ /protobuf/google/
RUN pip install --no-cache grpcio-tools
COPY ./tts.proto /source/tts.proto
FROM python:3.7-alpine
COPY --from=build_proto \
/source/output/tts_pb2.py \
/your_project/tts_pb2.pycopy compiled data from docker for local develop
protobuf:
command: sh -c "cp -r /your_project/tts_pb2.py /mnt"
build:
context: protobuf
dockerfile: Dockerfile
volumes:
- ./:/mnt
_TEMPLATEINPUT = _descriptor.Descriptor(
name='TemplateInput',
full_name='tinkoff.cloud.tts.v1.TemplateInput',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='template', full_name='tinkoff.TemplateInput.template', index=0,
number=1, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='slots', full_name='tinkoff.TemplateInput.slots', index=1,
number=2, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='backup_phrase', full_name='tinkoff.TemplateInput.backup_phrase', index=2,
number=3, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
],
extensions=[
],
nested_types=[_TEMPLATEINPUT_SLOTSENTRY, ],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=366,
serialized_end=531,
)
from tts_pb2 import (
TemplateInput,
LINEAR16
)
from tts_pb2 import TemplateInput
tmpl = TemplateInput(
template='some str',
slots={}
)from tts_pb2 import TemplateInput
tmpl = TemplateInput()
# 👇 не работает!!!
tmpl.slots = {} # 👈 не работает!!!
# Только изменение
tmpl.slots.update(test=1)from tts_pb2 import TemplateInput
tmpl = TemplateInput()
tmpl.no_field = 1 # raises AttributeError
tmpl.template = 1234 # raises TypeErrorfrom tts_pb2 import TemplateInput
tmpl.IsInitialized()
tmpl.__str__()
tmpl.CopyFrom(other_mgs)
tmpl.Clear()from tts_pb2 import TemplateInput
tmpl = TemplateInput(template='test')
blob: bytes = tmpl.SerializeToString()
tmpl = TemplateInput()
tmpl.ParseFromString(data: bytes)С++ over Cython на борту
syntax = "proto3";
package tinkoff.tts;
import "google/api/annotations.proto";
service TextToSpeech {
rpc Synthesize (SynthesizeRequest)
returns (SynthesizeResponse);
}
rpc StreamingSynthesize (SynthesizeRequest)
returns (stream StreamingSpeechResponse);
}
message TemplateInput {
string template = 1;
map<string, string> slots = 2;
string backup_phrase = 3;
};
message SynthesizeSpeechRequest {
SynthesisInput input = 1;
VoiceSelectionParams voice = 2;
AudioConfig audio_config = 3;
TemplateInput template_input = 4;
}
*_pb2_grpc.py
from tts_pb2_grpc import TextToSpeechServicer
class Servicer(TextToSpeechServicer):
def Synthesize(self, request, context):
pass
def StreamingSynthesize(self, request, context):
pass
from grpc import ServicerContext
from tts_pb2 import (SynthesizeRequest,
SynthesizeResponse,
StreamingSynthesizeResponse)
from tts_pb2_grpc import TextToSpeechServicer
class Servicer(TextToSpeechServicer):
def Synthesize(
self, request: SynthesizeRequest,
context: ServicerContext
) -> SynthesizeResponse:
return SynthesizeResponse()
def StreamingSynthesize(
self, request: SynthesizeRequest,
context: ServicerContext
) -> StreamingSynthesizeResponse:
yield StreamingSynthesizeResponse()
from concurrent import futures
import grpc
from tts_pb2_grpc import (
add_TextToSpeechServicer_to_server)
from code import Servicer
servicer = Servicer()
pool = futures.ThreadPoolExecutor()
server = grpc.server(pool)
add_TextToSpeechServicer_to_server(
servicer, server)
server.add_insecure_port(address)import signal
import time
import sys
server.run()
def stop(timeout=None):
server.stop(timeout)
pool.shutdown(wait=bool(timeout))
sys.exit()
signal.signal(signal.SIGTERM, lambda *args: stop(15))
signal.signal(signal.SIGINT, lambda *args: stop())
while True:
time.sleep(_ONE_DAY_IN_SECONDS)add_TextToSpeechServicer_to_server(
servicer, server)
add_SpeechToTextServicer_to_server(
servicer, server)
import gprc
from tts_pb2_grpc import TextToSpeechStub
endpoint = 'localhost:50051'
channel = grpc.insecure_channel(endpoint)
stub = TextToSpeechStub(channel)from tts_pb2 import (
SynthesizeRequest, SynthesizeResponse
)
request = SynthesizeRequest()
response = stub.Synthesize(request)
assert isinstance(response, SynthesizeResponse)headers
jwt
from hypothesis_protobuf import modules_to_strategies
import tts_pb2
strategies = modules_to_strategies(tts_pb2)
@given(instant_message=strategies)
def test_instant_message_processor(instant_message):
assert process_message(instant_message)stubs
pip install pytest-grpc
def _end_unary_response_blocking(state, call, with_call, deadline):
if state.code is grpc.StatusCode.OK:
if with_call:
rendezvous = _Rendezvous(state, call, None, deadline)
return state.response, rendezvous
else:
return state.response
else:
> raise _Rendezvous(state, None, None, deadline)
E grpc._channel._Rendezvous: <_Rendezvous of RPC that terminated with:
E status = StatusCode.UNKNOWN
E details = "Exception calling application: Some error"
E debug_error_string = "{"created":"@1552312084.410564000","description":"Error received from peer","file":"src/core/lib/surface/call.cc","file_line":1039,"grpc_message":"Exception calling application: Some error","grpc_status":2}"
E >
по желанию
grpc_stub = <stub.test_pb2_grpc.EchoServiceStub object at #>
def test_example(grpc_stub):
request = EchoRequest()
> response = grpc_stub.error_handler(request)
test_example.py:35:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../pytest_grpc/plugin.py:79: in fake_handler
return real_method(request, context)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
def error_handler(self, request: EchoRequest, context):
> raise RuntimeError('Some error')
E RuntimeError: Some error
src/servicer.py:10: RuntimeErrorimport pytest
from stub.test_pb2 import EchoRequest
@pytest.fixture(scope='module')
def grpc_add_to_server():
from stub.test_pb2_grpc import (
add_EchoServiceServicer_to_server
)
return add_EchoServiceServicer_to_server@pytest.fixture(scope='module')
def grpc_servicer():
from servicer import Servicer
return Servicer()
@pytest.fixture(scope='module')
def grpc_stub_cls(grpc_channel):
from stub.test_pb2_grpc import EchoServiceStub
return EchoServiceStubdef test_some(grpc_stub):
request = EchoRequest()
response = grpc_stub.handler(request)
assert response.name == f'test-{request.name}'
def test_example(grpc_stub):
request = EchoRequest()
response = grpc_stub.error_handler(request)
assert response.name == f'test-{request.name}'# с тредами и ближе к проду
$ py.test
# без транспорта, вызовы напрямую
# но чище трейсы
$ py.test --grpc-fake-server
Хорошая альтернатива