

Pythonバージョンは3.7.3です。  
Pythonの非同期処理について勉強するためスクリプトを書いているのですが、

実行すると下記のエラーが出力されます。

[code]

    lyrics.py:75: RuntimeWarning: coroutine 'Lyric.lyric_from_genius.<locals>.main_loop.<locals>.get_lyric_soup' was never awaited
      self.__gtask += [get_lyric_soup(url)]
    RuntimeWarning: Enable tracemalloc to get the object allocation traceback
    
[/code]

待つ処理は入れているはずなのですが、なぜエラーになるのかわかりません。  
詳しい方お力を貸して頂けないのでしょうか？  
よろしくお願いします。

[code]

    import asyncio
    import requests
    from bs4 import BeautifulSoup, Comment
    
    
    #Lyrics__Container-sc-1ynbvzw-2
    
    class Lyric():
        def __init__(self, artist, song_name, max_recur=3):
            self.artist = artist
            self.song_name = song_name
            self.__gtask = []
            self.__canceled = False
            self.__lyric = ''
            #何回再帰的に行うか。リクエスト5と再帰3で合計15回までリクエストを投げる。
            self.__max_recur = max_recur
            self.genius_url = self.make_genius_url(artist, song_name)
            lyric = self.lyric_from_genius(self.genius_url, self.__max_recur)
            print(lyric)
        
        def make_genius_url(self, artist, song_name):
            search_song = f'{artist} {song_name}'
            search_song = search_song.replace(' ', '-')
            print(search_song)
            return f'https://genius.com/{search_song}-lyrics'
        
    
        def get_soup(self, url):
            r = requests.get(url)
            if r.status_code == 200:
                soup = BeautifulSoup(r.content, 'lxml')
                return soup
            else:
                return False
        
        def scrape_genius(self, url):
            if not self.__canceled:
                soup = self.get_soup(url)
            if soup and not self.__canceled:
                lyric_soup = soup.select('.song_body-lyrics .lyrics p')
                if lyric_soup:
                    self.__canceled = True
                    tags = lyric_soup[0].find_all(['a', 'i'])
                    for tag in tags:
                        tag.unwrap()
                    print('ここから歌詞です。')
                    print(lyric_soup[0].text)
                    self.__lyric = lyric_soup[0].text
                    self.__gtask.cancel()
                else:
                    print('歌詞情報を取得出来なかった。')
            else:
                if self.__canceled:
                    print('歌詞取得した')
                else:
                    print('歌詞情報がない')
                self.__gtask.cancel()
    
    
    
        def lyric_from_genius(self, url, depth):
            print('再帰の回数確認depth: ', self.__max_recur - depth)
    
            async def main_loop(url):
                async def get_lyric_soup(url):
                    await self.loop.run_in_executor(None, self.scrape_genius, url)
                
                #main_loopの処理       
                for _ in range(1):#同時にリクエスト投げる数を調整する。
                    self.__gtask += [get_lyric_soup(url)]
                
                self.__gtask = asyncio.gather(*self.__gtask)
                return await self.__gtask
            try:
                self.loop = asyncio.get_event_loop()
                self.loop.run_until_complete(main_loop(url))
            except asyncio.CancelledError as e:
                print("*** CancelledError ***", e)
            finally:
                if self.__lyric or depth <= 0:
                    return self.__lyric
                else:
                    print('１回のリクエストで曲情報が取れなかった。')
                    return self.lyric_from_genius(url, depth - 1)
            
            
    
    
    Lyric = Lyric('kamal', 'blue')
    
[/code]

**追記**  
エラーが出るのは歌詞情報が１回目のイベントループで取得出来ずに再帰処理で2回目の処理に移った際に出ます。

**追記2 await追加**  
確かにawaitをたくさん入れたらエラーは出なくなったのですが今度、曲情報を追加した際のタスクをキャンセルする処理`self.__gtask.cancel()`が上手く行かなくなった気がします。  
あとすいません、なぜここまでawaitが必要なのでしょうか？

[code]

            try:
                self.loop = asyncio.get_event_loop()
                self.loop.run_until_complete(main_loop(url))
            except asyncio.CancelledError as e:
                print("*** CancelledError ***", e)
    
[/code]

コメントの指摘による、修正箇所

[code]

                #main_loopの処理       
                for _ in range(1):#同時にリクエスト投げる数を調整する。
                    self.__gtask += [await get_lyric_soup(url)]
                
                self.__gtask = asyncio.gather(*self.__gtask)
                return await self.__gtask
    
[/code]

**追記3**  
なんでか全くわからないのですがmain loopの中身を下記のように記述する事で上手く動作しています。  
どうしてか教えてくださるととても助かります。

[code]

                #main_loopの処理
                tasks = [get_lyric_soup(url) for _ in range(1)]
                self.__gtask = asyncio.gather(*tasks)
                return await self.__gtask
    
[/code]


CC BY-SA 4.0
2021-02-03T03:38:38.300
73771
2021-02-03T08:14:45.143
2021-02-03T08:14:45.143
22565
22565
question
python python3 非同期
Python asyncio で非同期にしたプログラムでwas never awaitedとエラーが出る。