iSarabjitDhiman / TweeterPy

TweeterPy is a python library to extract data from Twitter. TweeterPy API lets you scrape data from a user's profile like username, userid, bio, followers/followings list, profile media, tweets, etc.
MIT License
145 stars 20 forks source link

RuntimeError('asyncio.run() cannot be called from a running event loop') #15

Closed EtorixDev closed 1 year ago

EtorixDev commented 1 year ago

I want to use this for a Discord bot which is already async, but this error occurs. I was able to avoid this by using the current main branch, but I read you were planning on merging the async branch into main. It would be great if you could create a solution that doesn't utilize asyncio.run in request_util.py, or perhaps allows the user to specify if an event loop already exists, however that would be done.

iSarabjitDhiman commented 1 year ago

Hey @Etorix0005 Yes its possible and sounds great to me. But if we do this, make sure you await the result manually on your side.

import asyncio
from tweeterpy import TweeterPy
from tweeterpy import config

#make sure to set USING_EVENT_LOOP to True
config.USING_EVENT_LOOP = True
twitter = TweeterPy()
twitter.login("user","password")

async def main():
    #your code here
    #now you have to manually await the result. Say you want to get user tweets
    return await twitter.get_user_tweets("elonmusk",total=50)

tweets = asyncio.run(main()) 

Let me know how it goes. Feel free to close the issue if it has been resolved.

EtorixDev commented 1 year ago

That appears to work. Thanks for the fix!

nballen-tx commented 1 year ago

Looks like the above code doesn't work any more. Tried both True and False

import asyncio
from tweeterpy import TweeterPy
from tweeterpy import config

#make sure to set USING_EVENT_LOOP to True
config.USING_EVENT_LOOP = False #True
twitter = TweeterPy()
twitter.login()

async def main():
    #your code here
    #now you have to manually await the result. Say you want to get user tweets
    return await twitter.get_user_tweets("elonmusk",total=50)

tweets = asyncio.run(main())
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
Input In [1], in <cell line: 15>()
     10 async def main():
     11     #your code here
     12     #now you have to manually await the result. Say you want to get user tweets
     13     return await twitter.get_user_tweets("elonmusk",total=50)
---> 15 tweets = asyncio.run(main())

File /opt/conda/lib/python3.9/asyncio/runners.py:33, in run(main, debug)
      9 """Execute the coroutine and return the result.
     10 
     11 This function runs the passed coroutine, taking care of
   (...)
     30     asyncio.run(main())
     31 """
     32 if events._get_running_loop() is not None:
---> 33     raise RuntimeError(
     34         "asyncio.run() cannot be called from a running event loop")
     36 if not coroutines.iscoroutine(main):
     37     raise ValueError("a coroutine was expected, got {!r}".format(main))

RuntimeError: asyncio.run() cannot be called from a running event loop
nballen-tx commented 1 year ago

And this doesn't work either.

import asyncio
from tweeterpy import TweeterPy
from tweeterpy import config

#make sure to set USING_EVENT_LOOP to True
config.USING_EVENT_LOOP = False

twitter = TweeterPy()
twitter.login()

twitter.get_user_tweets("elonmusk",total=50)
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
Input In [4], in <cell line: 1>()
----> 1 twitter.get_user_tweets("elonmusk",total=50)

File /opt/conda/lib/python3.9/site-packages/tweeterpy/tweeterpy.py:82, in TweeterPy.login_decorator.<locals>.wrapper(self, *args, **kwargs)
     80 if not self.logged_in():
     81     self.login()
---> 82 return original_function(self, *args, **kwargs)

File /opt/conda/lib/python3.9/site-packages/tweeterpy/tweeterpy.py:260, in TweeterPy.get_user_tweets(self, user_id, with_replies, end_cursor, total, from_date, to_date, **kwargs)
    256     del variables['withQuickPromoteEligibilityTweetFields']
    257 pagination_data = {"end_cursor": end_cursor, "total": total,
    258                    "from_date": from_date, "to_date": to_date}
--> 260 return self._generate_request_data(query_endpoint, default_variables=variables, custom_variables=variables_data, pagination=pagination_data, features="additional", **kwargs)

File /opt/conda/lib/python3.9/site-packages/tweeterpy/tweeterpy.py:53, in TweeterPy._generate_request_data(self, endpoint, default_variables, custom_variables, pagination, features, return_payload, **kwargs)
     51 if pagination:
     52     request_payload.update({"pagination_data": pagination})
---> 53 return make_request(request_payload=request_payload, **kwargs)

File /opt/conda/lib/python3.9/site-packages/tweeterpy/request_util.py:55, in make_request(url, method, params, request_payload, session, timeout, **kwargs)
     53         if config.USING_EVENT_LOOP:
     54             return make_concurrent_requests(request_payload)
---> 55         return asyncio.run(make_concurrent_requests(request_payload))
     56 else:
     57     request_payload = {"method":method,"url":url,"params":params} | kwargs

File /opt/conda/lib/python3.9/asyncio/runners.py:33, in run(main, debug)
      9 """Execute the coroutine and return the result.
     10 
     11 This function runs the passed coroutine, taking care of
   (...)
     30     asyncio.run(main())
     31 """
     32 if events._get_running_loop() is not None:
---> 33     raise RuntimeError(
     34         "asyncio.run() cannot be called from a running event loop")
     36 if not coroutines.iscoroutine(main):
     37     raise ValueError("a coroutine was expected, got {!r}".format(main))

RuntimeError: asyncio.run() cannot be called from a running event loop
iSarabjitDhiman commented 1 year ago

Looks like the above code doesn't work any more. Tried both True and False

import asyncio
from tweeterpy import TweeterPy
from tweeterpy import config

#make sure to set USING_EVENT_LOOP to True
config.USING_EVENT_LOOP = False #True
twitter = TweeterPy()
twitter.login()

async def main():
    #your code here
    #now you have to manually await the result. Say you want to get user tweets
    return await twitter.get_user_tweets("elonmusk",total=50)

tweets = asyncio.run(main())
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
Input In [1], in <cell line: 15>()
     10 async def main():
     11     #your code here
     12     #now you have to manually await the result. Say you want to get user tweets
     13     return await twitter.get_user_tweets("elonmusk",total=50)
---> 15 tweets = asyncio.run(main())

File /opt/conda/lib/python3.9/asyncio/runners.py:33, in run(main, debug)
      9 """Execute the coroutine and return the result.
     10 
     11 This function runs the passed coroutine, taking care of
   (...)
     30     asyncio.run(main())
     31 """
     32 if events._get_running_loop() is not None:
---> 33     raise RuntimeError(
     34         "asyncio.run() cannot be called from a running event loop")
     36 if not coroutines.iscoroutine(main):
     37     raise ValueError("a coroutine was expected, got {!r}".format(main))

RuntimeError: asyncio.run() cannot be called from a running event loop

I just noticed its set to False.

config.USING_EVENT_LOOP

It has to be True.

iSarabjitDhiman commented 1 year ago

And this doesn't work either.

import asyncio
from tweeterpy import TweeterPy
from tweeterpy import config

#make sure to set USING_EVENT_LOOP to True
config.USING_EVENT_LOOP = False

twitter = TweeterPy()
twitter.login()

twitter.get_user_tweets("elonmusk",total=50)
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
Input In [4], in <cell line: 1>()
----> 1 twitter.get_user_tweets("elonmusk",total=50)

File /opt/conda/lib/python3.9/site-packages/tweeterpy/tweeterpy.py:82, in TweeterPy.login_decorator.<locals>.wrapper(self, *args, **kwargs)
     80 if not self.logged_in():
     81     self.login()
---> 82 return original_function(self, *args, **kwargs)

File /opt/conda/lib/python3.9/site-packages/tweeterpy/tweeterpy.py:260, in TweeterPy.get_user_tweets(self, user_id, with_replies, end_cursor, total, from_date, to_date, **kwargs)
    256     del variables['withQuickPromoteEligibilityTweetFields']
    257 pagination_data = {"end_cursor": end_cursor, "total": total,
    258                    "from_date": from_date, "to_date": to_date}
--> 260 return self._generate_request_data(query_endpoint, default_variables=variables, custom_variables=variables_data, pagination=pagination_data, features="additional", **kwargs)

File /opt/conda/lib/python3.9/site-packages/tweeterpy/tweeterpy.py:53, in TweeterPy._generate_request_data(self, endpoint, default_variables, custom_variables, pagination, features, return_payload, **kwargs)
     51 if pagination:
     52     request_payload.update({"pagination_data": pagination})
---> 53 return make_request(request_payload=request_payload, **kwargs)

File /opt/conda/lib/python3.9/site-packages/tweeterpy/request_util.py:55, in make_request(url, method, params, request_payload, session, timeout, **kwargs)
     53         if config.USING_EVENT_LOOP:
     54             return make_concurrent_requests(request_payload)
---> 55         return asyncio.run(make_concurrent_requests(request_payload))
     56 else:
     57     request_payload = {"method":method,"url":url,"params":params} | kwargs

File /opt/conda/lib/python3.9/asyncio/runners.py:33, in run(main, debug)
      9 """Execute the coroutine and return the result.
     10 
     11 This function runs the passed coroutine, taking care of
   (...)
     30     asyncio.run(main())
     31 """
     32 if events._get_running_loop() is not None:
---> 33     raise RuntimeError(
     34         "asyncio.run() cannot be called from a running event loop")
     36 if not coroutines.iscoroutine(main):
     37     raise ValueError("a coroutine was expected, got {!r}".format(main))

RuntimeError: asyncio.run() cannot be called from a running event loop

Here too its set to False. Set it to True.

Check the line 55 did throw the error, but if it was set to True, it would go to the line 53, the if condition. Let me know how it goes.

nballen-tx commented 1 year ago

Ok - set it to True instead as you commented, but still doesn't work.

import asyncio
from tweeterpy import TweeterPy
from tweeterpy import config

#make sure to set USING_EVENT_LOOP to True
config.USING_EVENT_LOOP = True
twitter = TweeterPy()
twitter.login()

async def main():
    #your code here
    #now you have to manually await the result. Say you want to get user tweets
    return await twitter.get_user_tweets("elonmusk",total=50)

tweets = asyncio.run(main())
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
Input In [2], in <cell line: 15>()
     10 async def main():
     11     #your code here
     12     #now you have to manually await the result. Say you want to get user tweets
     13     return await twitter.get_user_tweets("elonmusk",total=50)
---> 15 tweets = asyncio.run(main())

File ~\anaconda3\lib\asyncio\runners.py:33, in run(main, debug)
      9 """Execute the coroutine and return the result.
     10 
     11 This function runs the passed coroutine, taking care of
   (...)
     30     asyncio.run(main())
     31 """
     32 if events._get_running_loop() is not None:
---> 33     raise RuntimeError(
     34         "asyncio.run() cannot be called from a running event loop")
     36 if not coroutines.iscoroutine(main):
     37     raise ValueError("a coroutine was expected, got {!r}".format(main))

RuntimeError: asyncio.run() cannot be called from a running event loop
iSarabjitDhiman commented 1 year ago

Ok - set it to True instead as you commented, but still doesn't work.

import asyncio
from tweeterpy import TweeterPy
from tweeterpy import config

#make sure to set USING_EVENT_LOOP to True
config.USING_EVENT_LOOP = True
twitter = TweeterPy()
twitter.login()

async def main():
    #your code here
    #now you have to manually await the result. Say you want to get user tweets
    return await twitter.get_user_tweets("elonmusk",total=50)

tweets = asyncio.run(main())
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
Input In [2], in <cell line: 15>()
     10 async def main():
     11     #your code here
     12     #now you have to manually await the result. Say you want to get user tweets
     13     return await twitter.get_user_tweets("elonmusk",total=50)
---> 15 tweets = asyncio.run(main())

File ~\anaconda3\lib\asyncio\runners.py:33, in run(main, debug)
      9 """Execute the coroutine and return the result.
     10 
     11 This function runs the passed coroutine, taking care of
   (...)
     30     asyncio.run(main())
     31 """
     32 if events._get_running_loop() is not None:
---> 33     raise RuntimeError(
     34         "asyncio.run() cannot be called from a running event loop")
     36 if not coroutines.iscoroutine(main):
     37     raise ValueError("a coroutine was expected, got {!r}".format(main))

RuntimeError: asyncio.run() cannot be called from a running event loop

Well, I see you are in conda envirnoment, maybe using jupyter notebook ? In that case, it is already in an event loop, so the issue is not related to the library but to the envirornment you are running code from.

Fix 1 : Instead of running asyncio.run(main()) just await the result.

tweets = await main()

Fix 2 : Apply this patch by installing nest-asyncio

pip install nest-asyncio

import asyncio
import nest_asyncio
nest_asyncio.apply()
from tweeterpy import TweeterPy

#here goes your code, I guess you can use asyncio.run if applying this fix.

Fix 3 :

Try to use it in some other envirnoment like ipython, vscode or something else.

Keep me updated. ✌️

nballen-tx commented 1 year ago

Thanks - I am in the anaconda jupyter notebook environment.

So you think that is causing the event loop issue?

iSarabjitDhiman commented 1 year ago

Thanks - I am in the anaconda jupyter notebook environment.

So you think that is causing the event loop issue?

Correct. The jupyter notebook already uses some event loop as mentioned here. You can try to use some other environment or can apply any of the fixes I shared above.

nballen-tx commented 1 year ago

Thanks - I am in the anaconda jupyter notebook environment. So you think that is causing the event loop issue?

Correct. The jupyter notebook already uses some event loop as mentioned here. You can try to use some other environment or can apply any of the fixes I shared above.

Thank you, let me take another look on this.