Обновление вызова функции в Python Coroutine - PullRequest
0 голосов
/ 27 мая 2018

У меня есть простой класс для извлечения URL-адресов при периодическом обновлении токена доступа с использованием нового цикла в отдельном потоке:

import asyncio
import aiohttp
from threading import Thread
from random import randint

LOOP = asyncio.get_event_loop()

NEW_LOOP = asyncio.new_event_loop()
t = Thread(target=NEW_LOOP.run_forever)
t.start()

class SOME_CLASS(object):
    def __init__(self, loop=LOOP, new_loop=NEW_LOOP):
        self.token = None
        self.loop = loop
        self.new_loop = new_loop
        self.semaphore = asyncio.Semaphore(3)
        self.session = aiohttp.ClientSession(loop=self.loop)

        asyncio.run_coroutine_threadsafe(self.get_token(), self.new_loop)

    def _get_headers(self):
        headers = {
                    'x-access-token': str(self.token),
                    'content-type': 'application/json',
                  }

        return headers

    async def get_token(self):
        while True:
            self.token = randint(1, 100)
            await asyncio.sleep(2)        

    async def fetch(self, url):
        async with self.semaphore:
            headers = self._get_headers()
            async with self.session.get(url, headers=headers) as response:
                await response.read()
                return headers

    async def fetch_all(self):
        urls = ['https://httpbin.org/get?x={i}'for i in range(1000)]
        futures = asyncio.as_completed([self.fetch(url) for url in urls])
        for future in futures:
            await asyncio.sleep(1)
            headers = await future
            print(f'headers: {headers}  token: {self.token}')

    def run(self):
        self.loop.run_until_complete(self.fetch_all())

if __name__ == '__main__':
    sc = SOME_CLASS()
    sc.run()

Однако я заметил, что хотя self.token действительно обновляется каждый раздве секунды токен, сохраненный в headers, остается неизменным.Кажется, что self._get_headers() вызывается заблаговременно, а не после того, как он получает семафор:

headers: {'x-access-token': '8', 'content-type': 'application/json'}  token: 8
headers: {'x-access-token': '8', 'content-type': 'application/json'}  token: 8
headers: {'x-access-token': '8', 'content-type': 'application/json'}  token: 78
headers: {'x-access-token': '8', 'content-type': 'application/json'}  token: 78
headers: {'x-access-token': '8', 'content-type': 'application/json'}  token: 41
headers: {'x-access-token': '8', 'content-type': 'application/json'}  token: 56
headers: {'x-access-token': '8', 'content-type': 'application/json'}  token: 56
headers: {'x-access-token': '8', 'content-type': 'application/json'}  token: 74
headers: {'x-access-token': '8', 'content-type': 'application/json'}  token: 74
headers: {'x-access-token': '8', 'content-type': 'application/json'}  token: 4
headers: {'x-access-token': '8', 'content-type': 'application/json'}  token: 4
headers: {'x-access-token': '8', 'content-type': 'application/json'}  token: 10
headers: {'x-access-token': '8', 'content-type': 'application/json'}  token: 10
headers: {'x-access-token': '8', 'content-type': 'application/json'}  token: 44
headers: {'x-access-token': '8', 'content-type': 'application/json'}  token: 44
headers: {'x-access-token': '8', 'content-type': 'application/json'}  token: 28
headers: {'x-access-token': '8', 'content-type': 'application/json'}  token: 28

Как мне убедиться, что токен действительно обновляется в headers прямо перед запросом httpотосланный?

Ответы [ 2 ]

0 голосов
/ 27 мая 2018

У вас есть сложная оболочка вокруг основной проблемы Python.Переменная self.token является простым целым числом.Значение, хранящееся в словаре headers, является строковым представлением этого простого целого числа.Когда вы переназначаете self.token другому целому числу, программа не может узнать, какие другие объекты вы хотели бы изменить одновременно.

Одно из решений - сделать self.token объектом свнутреннее состояние.На этот объект можно ссылаться в разных местах вашей программы.Когда вы изменяете его внутреннее состояние, оно, конечно, вступает в силу немедленно.Вот небольшая программа, иллюстрирующая это, с class Token.Я надеюсь, что вы можете адаптировать его к более сложной ситуации.Я не думаю, что использование сопрограмм влияет на это.Я думаю, что если вы присваиваете значение x-access-token для self.token, которое теперь является экземпляром Token, вы в большинстве случаев найдете решение.

import random

# This way doesn't work
class MyClass:
    def __init__(self):
        self.token = random.randint(1, 100)

    def change_token(self):
        self.token = random.randint(1, 100)

c = MyClass()
a = c.token
for _ in range(10):
    c.change_token()
    print(a, c.token)  # a does not change when c.token does

# This works
class Token:
    def __init__(self, n):
        self.token = n

    def __repr__(self):
        return str(self.token)

    def set_token(self, n):
        self.token = n

class MyClass2:
    def __init__(self):
        self.token = Token(random.randint(1, 100))

    def change_token(self):
        self.token.set_token(random.randint(1, 100))

print()
c = MyClass2()
a = c.token
for _ in range(10):
    c.change_token()
    print(a, c.token)  # a and c.token are the same object
0 голосов
/ 27 мая 2018

asyncio.sleep(2) имеет значение, равное 0,2, поместите отладочную печать в выборку и увидите, что она обновлена ​​

...