Задать вопрос
@kefirich

Как создать task в __init__ asyncio?

У меня есть дискорд бот. Я сделал логику запуска. Когда я пытаюсь запустить task в __init__, то появляется ошибка:

AttributeError: loop attribute cannot be accessed in non-async contexts. Consider using either an asynchronous main function and passing it to asyncio.run or using asynchronous initialisation hooks such as Client.setup_hook

Основной файл:

def __init__(self, *args, **kwargs):
		discord.Client.__init__(self, *args, **kwargs, intents=discord.Intents.all())
		self.token = kwargs.get('token')
		self.mongo_url = kwargs.get('mongo_url')
		self.mongo_name = kwargs.get('mongo_name')
		self.redis_url = kwargs.get('redis_url')
		self.db = Db(self.mongo_url, self.mongo_name, self.redis_url)
		self.plugin_manager = PluginManager(self)
		self.plugin_manager.load_all()
		self.loop = asyncio.get_event_loop()

	def run(self, *args):
		print('Running...')
		asyncio.run(self.start(*args))

Файл с ошибкой:

def __init__(self, bot):
		self.bot = bot
		self.db = bot.db
		self.commands = {}
		self.bg_tasks = {}

		for name, member in inspect.getmembers(self):
		# registering commands
			if hasattr(member, '_is_command'):
				self.commands[member.__name__] = member
			# registering bg_tasks
			if hasattr(member, '_bg_task'):
				self.bg_tasks[member.__name__] = member
				self.bot.loop.create_task(member())
		print("Registered {} commands / {} bg tasks".format(
			len(self.commands),
			len(self.bg_tasks)
		))

Я понимаю, что асинхронные функции нельзя запускать в __init__.
Я не понимаю asyncio.
  • Вопрос задан
  • 180 просмотров
Подписаться 1 Простой 10 комментариев
Пригласить эксперта
Ответы на вопрос 1
Vindicar
@Vindicar
RTFM!
Тебе довольно прямо сказано: "AttributeError: loop attribute cannot be accessed in non-async contexts."
Иными словами, атрибут Bot.loop (ссылку на рабочий цикл asyncio) можно читать, только если ты находишься внутри async def функции, прямо или косвенно.
Причина простая - asyncio.run() или эквивалентная функция как раз создаёт и запускает рабочий цикл asyncio (обычно называемый event loop или просто loop). Если рабочий цикл ещё не создан, то что должен вернуть атрибут Bot.loop? На этот вопрос просто нет правильного ответа. Поэтому доступ к атрибуту блокируется.

И насчёт решения тоже подсказано: "Consider using either an asynchronous main function and passing it to asyncio.run or using asynchronous initialisation hooks such as Client.setup_hook". Иными словами, тебе нужно сделать так, чтобы твой код, обращающийся к Bot.loop, выполнялся в асинхронной (async def) функции. Это можно сделать двумя способами.

Первый: вынести твой код из конструктора (который не может быть асинхронным) в отдельный метод. Например, Bot (и его предок Client) позволяют вызвать асинхронный метод on_ready() при запуске бота. Тут есть много оговорок - в частности, on_ready() может быть вызван неоднократно, если есть проблемы с соединением. Это нужно иметь ввиду.

Второй: завернуть вызов всего твоего конструктора в асинхронный метод. Упрощённо, вместо
import discord
from discord.ext import commands

intents = discord.Intents.default()
intents.members = True
intents.message_content = True

bot = commands.Bot(command_prefix='!', intents=intents)
# ...
bot.run('token')

можешь попробовать что-то вроде
import discord
from discord.ext import commands


async def main():
    # асинхронная функция может быть выполнена ТОЛЬКО внутри рабочего цикла
    # значит, рабочий цикл уже точно существует и выполняется
    intents = discord.Intents.default()
    intents.members = True
    intents.message_content = True
    # конструктор сам по себе не асинхронный, но он выполняется в асинхронном контексте
    bot = commands.Bot(command_prefix='!', intents=intents)  
    # ...
    # мы уже в асинхронной функции, поэтому используем await start() вместо run()
    await bot.start('token')  # main() не завершит работу, пока бот не завершит работу


if __name__ == '__main__':
    asyncio.run(main())  # создаём рабочий цикл. он будет работать, пока main() не завершит работу


Но я соглашусь с Everything_is_bad - сначала разберись, как работает asyncio. Строить сложные конструкции с несколькими долгоживущими корутинами методом проб и ошибок - это слишком муторно.

Ну и очень большой вопрос от меня: ты, я вижу, мастеришь систему плагинов. Чем тебя не устроили коги?
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы