How to convert grpc.ServerInterceptor to grcp.aio.ServerInterceptor - python-asyncio

I am trying to implement async ServerInterceptor [grcp.aio.ServerInterceptor]. My current synchronous ServerInterceptor looks like this https://github.com/zhyon404/python-grpc-prometheus/blob/master/python_grpc_prometheus/prometheus_server_interceptor.py#L48. When i try to use grpc.aio.ServerInterceptor and start the server,
My Server code
from grpc_opentracing import open_tracing_server_interceptor
from grpc_opentracing.grpcext import intercept_server
import PromServerInterceptor
class MyServicer():
async def _start_async_server(self, tracer=None,service, grpc_port=8083, http_port=8080):
tracing_interceptor = open_tracing_server_interceptor(tracer)
server = aio.server(nterceptors=(PromServerInterceptor(),))
server = intercept_server(server, tracing_interceptor)
my_service_pb2_grpc.add_MyServicer_to_server(service, server)
server.add_insecure_port("[::]:" + str(grpc_port))
await server.start()
logger.info("Started prometheus server at port %s", http_port)
prometheus_client.start_http_server(http_port)
await server.wait_for_termination()
def async_serve(self, tracer=None, service, grpc_port=8083, http_port=8080):
loop = asyncio.get_event_loop()
loop.create_task(self._start_async_server(service, tracer, grpc_port, http_port))
loop.run_forever()
Following are the lib versions i am using:
grpcio=1.32.0
grpcio-opentracing==1.1.4
I see the following error:
File "src/python/grpcio/grpc/_cython/_cygrpc/aio/server.pyx.pxi", line 646, in grpc._cython.cygrpc._handle_exceptions
File "src/python/grpcio/grpc/_cython/_cygrpc/aio/server.pyx.pxi", line 745, in _handle_rpc
File "src/python/grpcio/grpc/_cython/_cygrpc/aio/server.pyx.pxi", line 511, in _handle_unary_unary_rpc
File "src/python/grpcio/grpc/_cython/_cygrpc/aio/server.pyx.pxi", line 368, in _finish_handler_with_unary_response
File "prometheus_server_interceptor.py", line 93, in new_behavior
rsp = await behavior(request_or_iterator, service_context)
File "/anaconda3/lib/python3.7/site-packages/grpc_opentracing/grpcext/_interceptor.py", line 272, in adaptation
_UnaryServerInfo(self._method), handler)
File "/anaconda3/lib/python3.7/site-packages/grpc_opentracing/_server.py", line 145, in intercept_unary
timeout=servicer_context.time_remaining(),
AttributeError: 'grpc._cython.cygrpc._ServicerContext' object has no attribute 'time_remaining
Following is my PromServerInterceptor implemenation:
from grpc import aio
import grpc
from timeit import default_timer
from python_grpc_prometheus.server_metrics import (SERVER_HANDLED_LATENCY_SECONDS,
SERVER_HANDLED_COUNTER,
SERVER_STARTED_COUNTER,
SERVER_MSG_RECEIVED_TOTAL,
SERVER_MSG_SENT_TOTAL)
from python_grpc_prometheus.util import type_from_method
from python_grpc_prometheus.util import code_to_string
def _wrap_rpc_behavior(handler, fn):
if handler is None:
return None
if handler.request_streaming and handler.response_streaming:
behavior_fn = handler.stream_stream
handler_factory = grpc.stream_stream_rpc_method_handler
elif handler.request_streaming and not handler.response_streaming:
behavior_fn = handler.stream_unary
handler_factory = grpc.stream_unary_rpc_method_handler
elif not handler.request_streaming and handler.response_streaming:
behavior_fn = handler.unary_stream
handler_factory = grpc.unary_stream_rpc_method_handler
else:
behavior_fn = handler.unary_unary
handler_factory = grpc.unary_unary_rpc_method_handler
return handler_factory(fn(behavior_fn,
handler.request_streaming,
handler.response_streaming),
request_deserializer=handler.request_deserializer,
response_serializer=handler.response_serializer)
def split_call_details(handler_call_details, minimum_grpc_method_path_items=3):
parts = handler_call_details.method.split("/")
if len(parts) < minimum_grpc_method_path_items:
return '', '', False
grpc_service, grpc_method = parts[1:minimum_grpc_method_path_items]
return grpc_service, grpc_method, True
class PromServerInterceptor(aio.ServerInterceptor):
async def intercept_service(self, continuation, handler_call_details):
handler = await continuation(handler_call_details)
if handler is None:
return handler
# only support unary
if handler.request_streaming or handler.response_streaming:
return handler
grpc_service, grpc_method, ok = split_call_details(handler_call_details)
if not ok:
return continuation(handler_call_details)
grpc_type = type_from_method(handler.request_streaming, handler.response_streaming)
SERVER_STARTED_COUNTER.labels(
grpc_type=grpc_type,
grpc_service=grpc_service,
grpc_method=grpc_method).inc()
def latency_wrapper(behavior, request_streaming, response_streaming):
async def new_behavior(request_or_iterator, service_context):
start = default_timer()
SERVER_MSG_RECEIVED_TOTAL.labels(
grpc_type=grpc_type,
grpc_service=grpc_service,
grpc_method=grpc_method
).inc()
# default
code = code_to_string(grpc.StatusCode.UNKNOWN)
try:
rsp = await behavior(request_or_iterator, service_context)
if service_context._state.code is None:
code = code_to_string(grpc.StatusCode.OK)
else:
code = code_to_string(service_context._state.code)
SERVER_MSG_SENT_TOTAL.labels(
grpc_type=grpc_type,
grpc_service=grpc_service,
grpc_method=grpc_method
).inc()
return rsp
except grpc.RpcError as e:
if isinstance(e, grpc.Call):
code = code_to_string(e.code())
raise e
finally:
SERVER_HANDLED_COUNTER.labels(
grpc_type=grpc_type,
grpc_service=grpc_service,
grpc_method=grpc_method,
grpc_code=code
).inc()
SERVER_HANDLED_LATENCY_SECONDS.labels(
grpc_type=grpc_type,
grpc_service=grpc_service,
grpc_method=grpc_method).observe(max(default_timer() - start, 0))
return new_behavior
return _wrap_rpc_behavior(handler, latency_wrapper)

The interceptor from grpc.aio can look like:
class RequestIdInterceptor(grpc.aio.ServerInterceptor):
async def intercept_service(self, continuation, handler_call_details):
for (header, value) in handler_call_details.invocation_metadata:
if header == "request_id":
...
break
return await continuation(handler_call_details)
To convert it you need to add async for intercept_service and await for continuation.

Related

Python integration aiogram + Socket.IO Server

The question arose how to correctly call the functions seo.edit(), so.and(), sio.call() in the aiogram function.
Is it possible to implement such code somehow:
`
#dp.message_handler()
async def process_start_command(message: types.Message):
await sio.send(message.text)
await message.reply('Готово')
`
The connection between the Client and the Server was successfully created.
server.py:
from aiohttp import web
import socketio
sio = socketio.AsyncServer(logger=True, engineio_logger=True)
app = web.Application()
sio.attach(app)
#sio.on('message')
async def print_message(sid, message):
print('\n' + "ID: " , sid)
print(message)
#sio.event()
async def connect(sid, environ):
print(f'Соединение с {sid} установленно')
if __name__ == '__main__':
web.run_app(app)
client.py:
import asyncio
import socketio
sio = socketio.AsyncClient(logger=True, engineio_logger=True)
#sio.event
async def message(data):
print('Получил сообщение ', data)
async def main():
await sio.connect('http://localhost:8080')
await sio.wait()
if __name__ == '__main__':
asyncio.run(main())

RuntimeWarning: coroutine 'my_after' was never awaited (discord.py)

guys. my code gives the error "RuntimeWarning: coroutine 'my_after' was never awaited". I am using my_after and serverQueue functions to create a queue in my music bot. the problem occurs in "voice.play (FFmpegPCMAudio (URL, ** FFMPEG_OPTIONS), after = lambda e: my_after (voice, message))". everything works correctly only when there is one song in the queue. the rest should be automatically loaded from the queue, which does not happen. when one song ends, the next song is not playing due to an error. but if you use the "& skip" command, the next song in the queue will start playing.
async def play(video_link, voice, message):
with YoutubeDL(YDL_OPTIONS) as ydl:
info = ydl.extract_info(video_link, download = False)
print(info.get('title'))
URL = info['formats'][0]['url']
voice.play(FFmpegPCMAudio(URL, **FFMPEG_OPTIONS), after = lambda e: my_after(voice, message))
voice.is_playing()
await message.channel.send('**Now playing** - ' + info.get('title'))
queue = []
async def my_after(voice, message):
coro = await musicQueue(voice, message)
await asyncio.run_coroutine_threadsafe(coro).result()
async def serverQueue(voice, message):
if ( queue != [] and not voice.is_playing() ):
await play(queue.pop(0), voice, message)
async def skip(voice, message):
voice.stop()
await serverQueue(voice, message)
#client.event
async def on_message(message):
if message.author == client.user:
return
voice = get(client.voice_clients, guild=message.channel.guild)
if message.content.startswith('&' + 'join'):
await join(message)
if message.content.startswith('&' + 'play'):
arg = message.content[:0] + message.content[6:]
await join(message)
voice = get(client.voice_clients, guild=message.channel.guild)
if arg.startswith('https:'):
video_link = arg
if voice.is_playing() == False:
await play(video_link, voice, message)
else:
await message.channel.send('added to queue')
queue.append(video_link)
The error explains it. You didn't awaited the asynchronous function.
Change this:
after = lambda e: my_after(voice, message))
To this:
after = lambda e: asyncio.run(my_after(voice, message))

Discord.py how could you turn these reactions into buttons?

Hey how would you be able to turn these reactions into buttons, I would like something like this: https://cdn.upload.systems/uploads/A6AXyuYA.png
I dont understand buttons that much, im in a little hurry to do this for my friend but i take a while to learn things, any help would be appreciated!
The part I wanna turn reactions to buttons to is the config menu part.
Code:
import asyncio
import copy
import dateutil
import discord
from discord.ext import commands
from discord.ext.commands.view import StringView
from core import checks
from core.models import DummyMessage, PermissionLevel
from core.utils import normalize_alias
class Menu(commands.Cog):
"""Reaction-based menu for threads"""
def __init__(self, bot):
self.bot = bot
self.db = self.bot.plugin_db.get_partition(self)
#commands.Cog.listener()
async def on_thread_ready(self, thread, creator, category, initial_message):
"""Sends out menu to user"""
menu_config = await self.db.find_one({'_id': 'config'})
if menu_config:
message = DummyMessage(copy.copy(initial_message))
message.author = self.bot.modmail_guild.me
message.content = menu_config['content']
msg, _ = await thread.reply(message)
for r in menu_config['options']:
await msg.add_reaction(r)
await asyncio.sleep(0.3)
try:
reaction, _ = await self.bot.wait_for('reaction_add', check=lambda r, u: r.message == msg and u == thread.recipient and str(r.emoji) in menu_config['options'], timeout=120)
except asyncio.TimeoutError:
message.content = 'No reaction received in menu... timing out'
await thread.reply(message)
else:
alias = menu_config['options'][str(reaction.emoji)]
ctxs = []
if alias is not None:
ctxs = []
aliases = normalize_alias(alias)
for alias in aliases:
view = StringView(self.bot.prefix + alias)
ctx_ = commands.Context(prefix=self.bot.prefix, view=view, bot=self.bot, message=message)
ctx_.thread = thread
discord.utils.find(view.skip_string, await self.bot.get_prefix())
ctx_.invoked_with = view.get_word().lower()
ctx_.command = self.bot.all_commands.get(ctx_.invoked_with)
ctxs += [ctx_]
for ctx in ctxs:
if ctx.command:
old_checks = copy.copy(ctx.command.checks)
ctx.command.checks = [checks.has_permissions(PermissionLevel.INVALID)]
await self.bot.invoke(ctx)
ctx.command.checks = old_checks
continue
#checks.has_permissions(PermissionLevel.MODERATOR)
#commands.command()
async def configmenu(self, ctx):
"""Creates a new menu"""
config = {}
try:
await ctx.send('What is the menu message?')
m = await self.bot.wait_for('message', check=lambda x: ctx.message.channel == x.channel and ctx.message.author == x.author, timeout=300)
config['content'] = m.content
await ctx.send('How many options are available?')
m = await self.bot.wait_for('message', check=lambda x: ctx.message.channel == x.channel and ctx.message.author == x.author and x.content.isdigit(), timeout=300)
options_len = int(m.content)
config['options'] = {}
for _ in range(options_len):
await ctx.send('What is the option emoji?')
while True:
m = await self.bot.wait_for('message', check=lambda x: ctx.message.channel == x.channel and ctx.message.author == x.author, timeout=300)
try:
await m.add_reaction(m.content)
except discord.HTTPException:
await ctx.send('Invalid emoji. Send another.')
else:
emoji = m.content
break
await ctx.send('What is the option command? (e.g. `reply Transferring && move 1238343847384`)')
m = await self.bot.wait_for('message', check=lambda x: ctx.message.channel == x.channel and ctx.message.author == x.author, timeout=300)
config['options'][emoji] = m.content
except asyncio.TimeoutError:
await ctx.send('Timeout. Re-run the command to create a menu.')
else:
await self.db.find_one_and_update({'_id': 'config'}, {'$set': config}, upsert=True)
await ctx.send('Success')
#checks.has_permissions(PermissionLevel.MODERATOR)
#commands.command()
async def clearmenu(self, ctx):
"""Removes an existing menu"""
await self.db.find_one_and_delete({'_id': 'config'})
await ctx.send('Success')
def setup(bot):
bot.add_cog(Menu(bot))
First of all, you have to have the development version of discord.py, which you can install using pip install -U git+https://github.com/Rapptz/discord.py
in the command prompt.
To add buttons to messages, you add a view=your_view_here() argument to ctx.send.
To get the view, you subclass discord.ui.View and then initialise it:
class config_menu_view(discord.ui.View):
def __init__(self):
super().__init__()
And then you can add buttons to your view using the decorator #discord.ui.button(label='label[optional]', emoji=emoji[optional] you can read all the possible arguments in the documentation :
#discord.ui.button(label='Button 1')
async def button_1(self, button: discord.ui.Button, interaction: discord.Interaction):
await interaction.response.send_message('Button 1 was clicked', ephemeral=True)
#do whatever you want to do here if the button was clicked
And then you can send it.
All of this code together would be:
class config_menu_view(discord.ui.View):
def __init__(self):
super().__init__()
#discord.ui.button(label='Button 1')
async def button_1(self, button: discord.ui.Button, interaction: discord.Interaction):
await interaction.response.send_message('Button 1 was clicked', ephemeral=True)
#do whatever you want to do here if the button was clicked
#and then when you want to send the view, do this:
await ctx.send("Button click!", view=config_menu_view())
From this information you should be able to make your own code.
Great places to learn more about buttons are the examples directory in GitHub
and the documentation

Can i detect 'connection lost' in websockets using Python3.6 Sanic?

can i detect ( if yes how?) when my Python3.6 Sanic Web Server lost connection with client application (example: user closes web browser or network fails, etc...)
from sanic import Sanic
import sanic.response as response
app = Sanic()
#app.route('/')
async def index(request):
return await response.file('index.html')
#app.websocket('/wsgate')
async def feed(request, ws):
while True:
data = await ws.recv()
print('Received: ' + data)
res = doSomethingWithRecvdData(data)
await ws.send(res)
if __name__ == '__main__':
app.run(host="0.0.0.0", port=8000, debug=True)
Solved
from sanic import Sanic
import sanic.response as response
from websockets.exceptions import ConnectionClosed
app = Sanic()
#app.route('/')
async def index(request):
return await response.file('index.html')
#app.websocket('/wsgate')
async def feed(request, ws):
while True:
try:
data = await ws.recv()
except (ConnectionClosed):
print("Connection is Closed")
data = None
break
print('Received: ' + data)
res = doSomethingWithRecvdData(data)
await ws.send(res)
if __name__ == '__main__':
app.run(host="0.0.0.0", port=8000, debug=True)

PySide PyQt QDataWidgetMapper

I try to connect a QtGui.QPlainTextEdit to a Model with QDataWidgetMapper.
I dont get any errors, just nothing in the TextEdit.
I dont get it and i cant find good example Code.
Here is some ExampleCode.
I really hope that someone could help me.
from PySide import QtCore, QtGui
import sys
class ComponentsListModel(QtCore.QAbstractListModel):
def __init__(self, components=[], parent = None):
super(ComponentsListModel, self).__init__(parent=None)
self.components = components
self.list = parent
def rowCount(self, parent):
return len(self.components)
def data(self, index, role):
row = index.row()
if role == QtCore.Qt.DisplayRole:#index.isValid() and
value = self.components[row]
return value
class MainWindow(QtGui.QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self._build_ui()
def _build_ui(self):
self.layout = QtGui.QVBoxLayout()
self.listView = QtGui.QListView()
self.model = ComponentsListModel(components = ['1', '2', '3'])
self.listView.setModel(self.model)
self.text = QtGui.QPlainTextEdit()
self.layout.addWidget(self.listView)
self.layout.addWidget(self.text)
self.setLayout(self.layout)
self._mapper = QtGui.QDataWidgetMapper(self)
self._mapper.setModel(self.model)
self._mapper.setSubmitPolicy(QtGui.QDataWidgetMapper.AutoSubmit)
self._mapper.addMapping(self.text, 0)
self._mapper.toFirst()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
app.setStyle('Plastique')
mySW = MainWindow()
mySW.show()
sys.exit(app.exec_())
You will need to add a condition for Qt.EditRole in your data function inside ComponentsListModel class
if role == Qt.EditRole:
value = self.components[row]
return value

Resources