'asyncio not working on Google Cloud Functions

I have this function which works fine locally on my machine with python 3.8, but it throws runtime error on Google Cloud Functions.

def telegram_test(request):
    request_json = request.get_json()
    import datetime
    import pandas as pd
    from pyrogram import Client
    
    session_string = "...............38Q8uTHG5gHwyWD8nW6h................."
    # the rest of the authantication
    api_id = 32494131641215
    api_hash = "ioadsfsjnjksfgnfriuthg#qw]/zwq  ]w/\lc ec,"

    # one of bbc channels on telegram you want to access
    channel_name = 'pyrogram'

    # if you only want to get messages older than 7 days in unix style
    seven_days = int((datetime.datetime.now() - datetime.timedelta(days=7)).timestamp())
    # call telegram with parameters such as limit and date
    # save the result to dataframe

    with Client(session_string,api_id,api_hash, takeout=True,workers=2) as app:
        hist_iter = app.iter_history(channel_name,offset_date=seven_days, limit=100)    
        msglist = [msg.__dict__ for msg in hist_iter]
        df = pd.DataFrame(msglist)
        print(df.head(5))
 
    return f'it works!:{request_json}'

The error message I get from GCF log:

File "/opt/python3.8/lib/python3.8/asyncio/events.py", line 639, in get_event_loop raise RuntimeError('There is no current event loop in thread %r.' RuntimeError: There is no current event loop in thread 'ThreadPoolExecutor-0_0'.

Update

I updated the code, the runtime error gone. but I am getting time out error. I put the timeout 180 secondes, but still when I test the function times out on 60 seconds.

Here is the updated code. Is there something I am doing wrong?

async def foo():
    from datetime import datetime, timedelta
    from pandas import DataFrame
    from pyrogram import Client
    import asyncio

    session_string = "********zNmkubA4ibjsdjhsdfjlhweruifnjkldfioY5DE*********"    
    api_id = 325511548224831351
    api_hash = "jdffjgtrkjhfklmrtgjtrm;sesews;;wex"        
    channel_name = 'cnn'

    with Client(session_string, api_id, api_hash, takeout=True) as app:
        hist_iter = app.iter_history(
            channel_name, limit=10)
        msglist = [msg.__dict__ for msg in hist_iter]
        df = DataFrame(msglist)    
    return df
 
async def bar():
    return await foo() 

def test(request):
    from asyncio import run
    return run(bar())


Solution 1:[1]

The solution in the end was to change from Pyrogram to telethon and create the asyncio manaually before creating the client.

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)

Note: you need valid session string, otherwise when you test the function, it will wait for you to auth with mobile number. so first run this code locally and authenticate, then copy the session string to the cloud function.

Here is the full code:

from telethon.sessions import StringSession
from telethon import TelegramClient
from pandas import DataFrame
import datetime
import asyncio

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)

api_id = 101010101
api_hash = "jhafcgahagfbahgdbw17171736456gerf"
session_string = "hjksdhjbdsfhgbdsabeyitrgdsbfsdbdiyfhsbddasbdjdksf="
channel_name = 'bbcuzbek'
seven_days = int((datetime.datetime.now() -
                  datetime.timedelta(days=7)).timestamp())




client = TelegramClient(StringSession(session_string),
                        api_id, api_hash, loop=loop)

time_format = "%d/%m/%Y, %H:%M:%S"
download_date = datetime.datetime.now(
    tz=datetime.timezone.utc).strftime(time_format)

cols = ["id", "date", "text", "views", "download_date"]


async def foo():
    all_msgs = [[message.id, message.date.strftime(time_format), message.text, message.views, download_date] async for message in client.iter_messages(entity=channel_name, offset_date=seven_days, limit=10)]
    df = DataFrame(data=all_msgs, columns=cols)
    # write it to BQ
    # print(df)
    # async for message in client.iter_messages(entity=channel_name, offset_date=seven_days, limit=10):
    #     print(message.id, message.date, message.text, message.views)
    print("it runs")

    print(len(df))
    return None


def test(request):
    with client:
        return client.loop.run_until_complete(foo())

Solution 2:[2]

  1. bar() is redundant
  2. You're trying to return a dataframe. Is it a valid HTTP response?
  3. with -> async with
  4. hist_iter = app.iter_history() -> hist_iter = await app.iter_history()
  5. M.b. it waits for input? enter image description here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2