python asyncio not spawning new thread - python-asyncio

I am working on a script to introduce a non blocking async fire and forget call. Thought of using the asyncio for that. I am running a below POC code before get into a full fledge implementation.It appears that the async function is still running on the main thread. Why is the below async function not creating a new thread name/id? I am expecting a new thread id like the threading module generates.
import asyncio
import threading
from threading import Thread
#Asyncio function
async def async_func():
print(f'async_func thread name: %s', threading.current_thread().name)
print(f'async_func thread id: %s', threading.current_thread().ident)
await asyncio.sleep(1)
# Threading
def non_async_func():
print(f'non_async_func thread name: %s', threading.current_thread().name)
print(f'non_async_func thread id: %s', threading.current_thread().ident)
def main():
print(f'main name: %s', threading.current_thread().name)
print(f'main id: %s', threading.current_thread().ident)
asyncio.run(async_func())
t = Thread(target=non_async_func)
print(f'main thread name: %s', threading.current_thread().name)
print(f'main thread id: %s', threading.current_thread().ident)
t.start()
if __name__ == '__main__':
main()
I am getting the below output:
main name: %s **MainThread**
main id: %s **4691527168**
**async_func thread name: %s MainThread**
**async_func thread id: %s 4691527168**
main thread name: %s **MainThread**
main thread id: %s **4691527168**
non_async_func thread name: %s **Thread-1 (non_async_func)**
non_async_func thread id: %s **123145474224128**

Related

How to fix "ERROR - No error handlers are registered, logging exception." with telegram.ext.dispatcher?

I downloaded bot which copy messages from telegram group and sending to my discord channel.
But when I am trying to send a message in a group, I am getting this error.
2022-12-31 08:25:19,877 - telegram.ext.dispatcher - ERROR - No error handlers are registered, logging exception.
Traceback (most recent call last):
File "C:\Python310\lib\site-packages\telegram\ext\dispatcher.py", line 557, in process_update
handler.handle_update(update, self, check, context)
File "C:\Python310\lib\site-packages\telegram\ext\handler.py", line 199, in handle_update
return self.callback(update, context)
File "C:\Discord-Telegram-Bot-main 1\main.py", line 46, in getTgAnnouncement
textUpdate = update.channel_post.text
AttributeError: 'NoneType' object has no attribute 'text'
I am using python 3.10
Python-telegram-bot 13.15
Below i will add the code
import requests
import json
import asyncio
import os
import json
import logging
from dotenv import load_dotenv
import discord
from discord.ext import commands
from telegram import Update
from telegram.ext import Updater,CallbackContext,MessageHandler,Filters
from telegram.utils import helpers
from telegram.ext.dispatcher import run_async
load_dotenv('token.env')
discordToken = os.getenv('DCTOKEN')# discord bot token
telegramToken = os.getenv('TGTOKEN')# telegram bot token
discordChannelId = os.getenv('DCCID')# discord announcement channel id
loop = asyncio.get_event_loop()
#--------------------------------------------------------------------------------------------------------------
bot = commands.Bot(
command_prefix="k!",
case_insensitive=True,
intents=discord.Intents.all(),
help_command=None
)
async def sendDcAnnouncement(textUpdate,nonTextUpdate):
announcementChannel = bot.get_channel(int(discordChannelId))
if textUpdate != None and nonTextUpdate != None:
await announcementChannel.send(textUpdate,file=discord.File(nonTextUpdate))
os.remove(nonTextUpdate)
elif textUpdate == None:
await announcementChannel.send(file=discord.File(nonTextUpdate))
os.remove(nonTextUpdate)
elif nonTextUpdate == None:
await announcementChannel.send(textUpdate)
def getTgAnnouncement(update: Update, context: CallbackContext):
textUpdate = None
nonTextUpdate = None
updateType = helpers.effective_message_type(update)
if updateType == 'text':
textUpdate = update.channel_post.text
else:
textUpdate = update.channel_post.caption
if updateType == 'photo':
nonTextUpdate = update.channel_post.photo[-1].get_file()['file_path']
elif updateType == 'video':
nonTextUpdate = update.channel_post.video.get_file()['file_path']
elif updateType == 'document':
nonTextUpdate = update.channel_post.document.get_file()['file_path']
elif updateType == 'voice':
nonTextUpdate = update.channel_post.voice.get_file()['file_path']
loop.create_task(sendDcAnnouncement(textUpdate,nonTextUpdate))
#bot.event
async def on_ready():
print(f'logged in as {bot.user}')
updater = Updater(token=telegramToken,use_context=True)
dispatcher = updater.dispatcher
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
getTgAnnouncement_handler = MessageHandler((~Filters.command),getTgAnnouncement)
dispatcher.add_handler(getTgAnnouncement_handler)
updater.start_polling()
bot.run(discordToken)
#--------------------------------------------------------------------------------------------------------------
I expect that this bot will send messages and that I will not get these errors.
MessageHandler handles all updates that have one of the fields message, edited_message, channel_post or edited_channel_post. So in getTGAnnouncement, it may very well be the case that update.channel_post is None.
If you want that handler to only handle channel_post update, please use Filters.update.channel_post.
As a side note: I see that you're using python-telegram-bot v13.x. Support for that version is ending soon in favor of the new v20.x.
Disclaimer: I'm currently the maintainer of python-telegram-bot.

Lots of ResourceWarning in FastApi with asyncpg

I have an async FastApi application with async sqlalchemy, source code:
database.py
from sqlalchemy import (
Column,
String,
)
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm.decl_api import DeclarativeMeta
from app.config import settings
engine = create_async_engine(settings.DATABASE_URL, pool_per_ping=True)
Base: DeclarativeMeta = declarative_base()
async_session = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
class Titles(Base):
__tablename__ = "titles"
id = Column(String(100), primary_key=True)
title = Column(String(100), unique=True)
async def get_session() -> AsyncSession:
async with async_session() as session:
yield session
routers.py
import .database
from fastapi_utils.cbv import cbv
from fastapi_utils.inferring_router import InferringRouter
router = InferringRouter()
async def get_titles(session: AsyncSession):
results = await session.execute(select(database.Titles)))
return results.scalars().all()
#cbv(router)
class TitlesView:
session: AsyncSession = Depends(database.get_session)
#router.get("/titles", status_code=HTTP_200_OK)
async def get(self) -> List[TitlesSchema]:
results = await get_titles(self.session)
return [TitlesSchema.from_orm(result) for result in results]
main.py
from fastapi import FastAPI
from app.routers import router
def create_app() -> FastAPI:
fast_api_app = FastAPI()
fast_api_app.include_router(router, prefix="/", tags=["Titles"])
return fast_api_app
app = create_app()
manage.py
import asyncio
import sys
from .database import async_session, Base, engine
async def init_models():
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all, checkfirst=True)
if __name__ == "__main__":
asyncio.run(init_models())
sys.stdout.write("Models initiated\n")
It runs with docker:
python manage.py
CMD ["uvicorn", "main:app", "--reload", "--host", "0.0.0.0", "--port", "8000", "--limit-max-requests", "10000"]
And right after i see message Models initiated, after init_models() func i see couple of warnings:
app_1 | Models initiated
app_1 | /usr/local/lib/python3.9/site-packages/asyncpg/connection.py:131: ResourceWarning: unclosed connection <asyncpg.connection.Connection object at 0x7efe5a613c80>; run in asyncio debug mode to show the traceback of connection origin
app_1 | /usr/local/lib/python3.9/asyncio/sslproto.py:320: ResourceWarning: unclosed transport <asyncio.sslproto._SSLProtocolTransport object at 0x7efe5a631700>
app_1 | /usr/local/lib/python3.9/asyncio/selector_events.py:704: ResourceWarning: unclosed transport <_SelectorSocketTransport fd=6>
app_1 | INFO: Uvicorn running on http://0.0.0.0:5000 (Press CTRL+C to quit)
app_1 | INFO: Started reloader process [15] using statreload
app_1 | INFO: Started server process [17]
app_1 | INFO: Waiting for application startup.
app_1 | INFO: Application startup complete.
And after i make changes, i see a bunch of warnings:
app_1 | WARNING: StatReload detected file change in 'ref_info/main.py'. Reloading...
app_1 | INFO: Shutting down
app_1 | INFO: Waiting for application shutdown.
app_1 | INFO: Application shutdown complete.
app_1 | INFO: Finished server process [15]
app_1 | sys:1: ResourceWarning: unclosed file <_io.TextIOWrapper name=0 mode='r' encoding='UTF-8'>
app_1 | INFO: Started server process [16]
app_1 | INFO: Waiting for application startup.
app_1 | INFO: Application startup complete.
Is it ok, and i need to hide it? Or i setted up smth wrong?
Ok, i solved it.
engine = create_async_engine(
settings.DATABASE_ASYNC_URI,
echo="debug" if settings.DEBUG else False,
)
async_session = sessionmaker(
bind=engine,
class_=AsyncSession,
autoflush=True,
autocommit=False,
expire_on_commit=False,
)
async def get_session() -> AsyncGenerator[AsyncSession, None]:
async with async_session() as session:
assert isinstance(session, AsyncSession)
yield session
async def connect() -> None:
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all, checkfirst=True)
async def disconnect() -> None:
if engine:
await engine.dispose()
Add connect and disconnect to your FastApi app startapp and shutdown events

How to use AsyncElasticsearch Connection Pool in FastApi?

The relevant parts of my code are as follows:
#app.on_event("startup")
async def startup():
es = Translator()
app.state.es_conn = es.connect()
#app.on_event('shutdown')
async def shutdown_event():
es = Translator()
await es.close(app.state.es_conn)
#app.post('/query')
async def query(req: Request):
es = Translator()
es.set_conn(req.app.state.es_conn)
return await es.search()
from elasticsearch import AsyncElasticsearch, AsyncTransport
class Translator(object):
def __init__(self):
self.__es = None
def connect(self):
return AsyncElasticsearch(
hosts='http://192.168.0.2:9200/',
transport_class=AsyncTransport)
def set_conn(self, conn):
self.__es = conn
async def close(self, conn):
await conn.close()
self.__es = None
async def search(self):
return await self.__es.search(index=index, body=body)
It runs in the following environment:
PyPy 7.3.7
fastapi 0.73.0
Elasticsearch 7.10.1
My question are:
Although the code can work normally, how can I detect whether it uses connection pool?
How to use connection pool correctly when asynchronous?
Can anyone help me? Thank you!

Delete outgoing message sent by telegram bot (Telegram, python)

i am stuck in my code as i do not know how to input/derive the message_id of the outgoing message forwarded by my bot.
Background: This is just a part of my code which i would subsequently integrate into the main code. Here, i am testing the functionality of forwarding messages + deleting them. I am able to successfully forward them out but i am stuck at deleting them. i am able to give the input of the chat_id but not able to do so for the message_id to delete. Is there a way to do it especially when i am gonna integrate to my main script which can have a few groups to manage. Please assist the noob in me. Thank you!
My script:
import logging
from telegram import ReplyKeyboardMarkup, ReplyKeyboardRemove, Update
from telegram.ext import (
Updater,
CommandHandler,
MessageHandler,
Filters,
ConversationHandler,
CallbackContext,
)
TOKEN = "PUT TOKEN HERE" #INPUT HERE
# Enable logging
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
logger = logging.getLogger(__name__)
MSG, DELETE_MSG = range(2)
def start(update: Update, context: CallbackContext) -> int:
update.message.reply_text(
'Hi! Please post the message you would like to share:')
return MSG
def send(update: Update, context: CallbackContext) -> int:
user = update.message.from_user
logger.info("Message of %s: %s", user.first_name, update.message.text)
print(update.message.message_id)
send = update.message.forward(chat_id= 'PUT CHAT ID OF OUTGOING GROUP HERE') #INPUT HERE
update.message.reply_text("Please delete")
return DELETE_MSG
def delete_msg(update: Update, context: CallbackContext) -> int:
user = update.message.from_user
logger.info("edit of %s: %s", user.first_name, update.message.text)
update.message.delete(chat_id='PUT CHAT ID OF OUTGOING GROUP HERE',
message_id='IM STUCK HERE') #INPUT HERE
return ConversationHandler.END
def cancel(update: Update, context: CallbackContext) -> int:
user = update.message.from_user
logger.info("User %s canceled the conversation.", user.first_name)
update.message.reply_text('Bye! I hope we can talk again some day.', reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
def main() -> None:
updater = Updater(TOKEN, use_context=True)
dispatcher = updater.dispatcher
conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', start)],
states={
MSG: [MessageHandler(~Filters.command, send)],
DELETE_MSG: [MessageHandler(~Filters.command, delete_msg)]
},
fallbacks=[CommandHandler('cancel', cancel)],
)
dispatcher.add_handler(conv_handler)
updater.start_polling()
updater.idle()
if __name__ == '__main__':
main()
update.message.reply_text("Please delete") must be a variable and, then, you'll be able to context.bot.deleteMessage the message_id of that. Just like this:
must_delete = update.message.reply_text("Please delete")
context.bot.deleteMessage (message_id = must_delete.message_id,
chat_id = update.message.chat_id)
Give it a try and let me know if this worked for you!!

ConversationHandler State did not pass in Python telegram bot

I am trying out the python telegram bot. I am trying to pass the state from button to questionTwo. However, when I run it, it can successfully pass through /start and answerOne. Yet, it will stop at answerOne by displaying it.
It will not display the second question and inline keyboard for me to select the answer, I believe the state didn't pass to questionTwo, but I am not sure which part is wrong. Thank you in advanceb
import logging
from telegram import (ReplyKeyboardMarkup, ReplyKeyboardRemove,InlineKeyboardButton, InlineKeyboardMarkup)
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters,
ConversationHandler,CallbackQueryHandler)
answerOne, answerTwo = range(2)
def start(update, context):
keyboard = [
[
InlineKeyboardButton("Option 1", callback_data='1'),
InlineKeyboardButton("Option 2", callback_data='2'),
],
[InlineKeyboardButton("Option 3", callback_data='3')],
]
reply_markup = InlineKeyboardMarkup(keyboard)
update.message.reply_text('Please choose:', reply_markup=reply_markup)
return answerOne
def button(update, context):
query = update.callback_query
# CallbackQueries need to be answered, even if no notification to the user is needed
# Some clients may have trouble otherwise. See https://core.telegram.org/bots/api#callbackquery
query.answer()
query.edit_message_text(text="Selected option: {}".format(query.data))
return questionTwo
def questionTwo(update, context):
keyboard = [
[
InlineKeyboardButton("Option 4", callback_data='4'),
InlineKeyboardButton("Option 5", callback_data='5'),
],
[InlineKeyboardButton("Option 6", callback_data='6')],
]
reply_markup = InlineKeyboardMarkup(keyboard)
update.message.reply_text('Please choose:', reply_markup=reply_markup)
return answerTwo
def answerTwo(update, context):
query = update.callback_query
# CallbackQueries need to be answered, even if no notification to the user is needed
# Some clients may have trouble otherwise. See https://core.telegram.org/bots/api#callbackquery
query.answer()
query.edit_message_text(text="Selected option: {}".format(query.data))
def cancel(update, context):
user = update.message.from_user
logger.info("User %s canceled the conversation.", user.first_name)
update.message.reply_text('Bye! I hope we can talk again some day.',
reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
def main():
# Create the Updater and pass it your bot's token.
# Make sure to set use_context=True to use the new context based callbacks
# Post version 12 this will no longer be necessary
updater = Updater('TOKEN', use_context=True)
# Get the dispatcher to register handlers
dp = updater.dispatcher
conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', start)],
states={
answerOne: [CallbackQueryHandler(button)],
questionTwo:[MessageHandler(Filters.text & ~Filters.command, questionTwo)],
answerTwo:[CallbackQueryHandler(answerTwo)],
},
fallbacks=[CommandHandler('cancel', cancel)]
)
dp.add_handler(conv_handler)
# Start the Bot
updater.start_polling()
# Run the bot until you press Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT. This should be used most of the time, since
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()
if __name__ == '__main__':
main()
import logging
from telegram import (ReplyKeyboardMarkup, ReplyKeyboardRemove,InlineKeyboardButton, InlineKeyboardMarkup)
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters,
ConversationHandler,CallbackQueryHandler)
answerOne, questionTwo = range(2)
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__)
def start(update, context):
keyboard = [
[
InlineKeyboardButton("Option 1", callback_data='1'),
InlineKeyboardButton("Option 2", callback_data='2'),
],
[InlineKeyboardButton("Option 3", callback_data='3')],
]
reply_markup = InlineKeyboardMarkup(keyboard)
update.message.reply_text('Please choose:', reply_markup=reply_markup)
return answerOne
def button(update, context):
query = update.callback_query
# CallbackQueries need to be answered, even if no notification to the user is needed
# Some clients may have trouble otherwise. See https://core.telegram.org/bots/api#callbackquery
query.answer()
query.edit_message_text(text="Selected option: {}".format(query.data))
return questionTwo
def questionTwo(update, context):
keyboard = [
[
InlineKeyboardButton("Option 4", callback_data='4'),
InlineKeyboardButton("Option 5", callback_data='5'),
],
[InlineKeyboardButton("Option 6", callback_data='6')],
]
reply_markup = InlineKeyboardMarkup(keyboard)
update.message.reply_text('Please choose:', reply_markup=reply_markup)
return answerOne
def cancel(update, context):
user = update.message.from_user
logger.info("User %s canceled the conversation.", user.first_name)
update.message.reply_text('Bye! I hope we can talk again some day.',
reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
def main():
# Create the Updater and pass it your bot's token.
# Make sure to set use_context=True to use the new context based callbacks
# Post version 12 this will no longer be necessary
updater = Updater('Token', use_context=True)
# Get the dispatcher to register handlers
dp = updater.dispatcher
conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', start)],
states={
answerOne: [CallbackQueryHandler(button)],
questionTwo:[MessageHandler(Filters.text & ~Filters.command, questionTwo)]
},
fallbacks=[CommandHandler('cancel', cancel)]
)
dp.add_handler(conv_handler)
# Start the Bot
updater.start_polling()
# Run the bot until you press Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT. This should be used most of the time, since
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()
if __name__ == '__main__':
main()

Resources