Debianov / vtc-bot

The bot for management of the virtual transport company (TruckersMP).
GNU General Public License v2.0
1 stars 2 forks source link

Передача context #8

Open Debianov opened 1 year ago

Debianov commented 1 year ago

Проблема. Сейчас передача осуществляется через атрибут aconn.adapters.discord_context. Жизненно необходим, поскольку используется для преобразования id в объект. В дальнейшем также будет записываться в БД.

idealism44 commented 1 year ago

Обязательно ли использование адаптеров Loader и Dumper? В БД всё равно будут хранится только id целей: при загрузке отдаём id объектов, при выгрузке получаем id целей и при надобности образуем в Member или Channel. И делать это не в адаптерах, а отдельными функциями до и после взаимодействия с БД. Чтобы этим не занимался psycopg.


Почитал документацию psycopg и я могу вынести предположение, как сделать это с помощью адаптеров:

В теме про конфигурации адаптации данных есть абзац:

By default, connections obtain an adapters map from the global map exposed as psycopg.adapters: changing the content of this object will affect every connection created afterwards. You may specify a different template adapters map using the context parameter on connect().

Говорят, мол, по умолчанию каждое соединение к БД берёт адаптеры из глобальной карты адаптеров в psycopg.adapters. Если хочется добавить свои глобально, то добавляйте адаптеры в psycopg.adapters: при каждом соединении будут ваши адаптеры. Если хотите адаптеры, зависящие от контекста, указывайте context при получении соединения БД connect(). Тип context при получении соединения это psycopg.adapt.AdaptersMap, который хранит в себе адаптеры для соединения. Именно его вы используете, чтобы добавить свои адаптеры.

Получается, при новом соединении к БД можно указать в качестве context эту карту адаптеров, которую мы создадим перед указанием context, и в неё будут добавлены адаптеры, преобразующие данные в зависимости от контекста. Но как сделать сами адаптеры преобразовывающими от контекста? register_dumper() и register_loader() принимают только классы соответсвующих адаптеров: не получится сделать объект адаптера. И в этом адаптере должен быть ctx от бота, чтобы обрабатывать данные в зависимости от контекста.

Далее, моё предположение реализации:

Для динамического создания классов в Python есть функция type(). Её и буду использовать. При вызове нового соединения в качестве context будем указывать вызов функции, который вернёт psycopg.adapt.AdaptersMap, в котором будут наши контекстные адаптеры. Для создания контекстных адаптеров буду преобразовывать текущие DiscordObjectsDumper и DiscordObjectsLoader в новый класс со своим атрибутом ctx. В итоге получится что-то вроде такого:

class DiscordObjectsLoader:
    ctx = None

    def load(self, to_load):
        print("With context:", self.ctx)
        print("Loading:", to_load)

class DiscordObjectsDumper:
    ctx = None

    def dump(self, to_dump):
        print("With context:", self.ctx)
        print("Dumping:", to_dump)

def context_factory(ctx):
    return (
        type(
             'CtxDiscordObjectsLoader',
             (DiscordObjectsLoader,),
             {'ctx': ctx}
        ),
        type(
             'CtxDiscordObjectsDumper',
             (DiscordObjectsDumper,),
             {'ctx': ctx}
        )
    )

def context_adapters(ctx):
    adapters = []
    Loader, Dumper = context_factory(ctx)
    adapters.append(Loader)
    adapters.append(Dumper)
    return adapters

ctx = "my_ctx"

# with psycopg.connect(connection_string=..., context=context_adapters(ctx)):
# Load something
# Dump sometning
# hided
adapters = context_adapters(ctx)
Loader, Dumper = adapters
a = Loader()
a.load("foo")
b = Dumper()
b.dump("bar")
# hided

В зависимости от ctx будем получать разные сообщения:

With context: my_ctx
Loading: foo
With context: my_ctx
Dumping: bar

Так как вы используете только одно соединение, то можно адаптировать вариант под cursor: после создания курсора добавить в него адаптеры, так как он тоже поддерживает psycopg.adapt.AdaptersMap (почему).

Возможно я что-то упустил в документации, но мне больше нечего сказать.

Debianov commented 1 year ago

Довольно накладно выходит делать на каждый ctx по своему объекту. К сожалению, передать второй аргумент никак нельзя, передавать как-то кортежем через data вместе с основными данными к преобразованию не является нормой, поэтому решено убрать эти два класса из регистрации и вызывать их методы вручную.

Debianov commented 1 year ago

Убирать два этих класса тоже не вариант. Они нужны, иначе придётся прописывать обработку вручную во многих местах кода.

Debianov commented 1 year ago

Реализовано через Locator. commit

idealism44 commented 1 year ago

То что вы реализовали - это глобальная переменная, ссылку на которую вы дали в классе адаптера Loader. Так как бот асинхронный, то в момент выгрузки данных с ctx первого соединения может вмешаться второе, которое вставит своё ctx в Provider, и первое соединение теперь будет пользоваться ctx от второго соединения, что может послужить многим проблемам.

Убирать два этих класса тоже не вариант. Они нужны, иначе придётся прописывать обработку вручную во многих местах кода.

Можете рассказать, как вы взаимодействуете с полученными id объектов из БД? Зачем их превращать в Discord объекты? Я не понимаю всей ситуации и предлагаю решения основываясь только на тексте из этого issue, из-за чего решения могут не подойти к настоящей обстановке.

Debianov commented 1 year ago

Так как бот асинхронный, то в момент выгрузки данных с ctx первого соединения может вмешаться второе, которое вставит своё ctx в Provider, и первое соединение теперь будет пользоваться ctx от второго соединения, что может послужить многим проблемам.

Да, я собираюсь делать словарь с извлечением контекста по объекту сообщения.

Можете рассказать, как вы взаимодействуете с полученными id объектов из БД? Зачем их превращать в Discord объекты?

Ну хотя бы взять извлечение объектов из БД по схожим критериям для поиска совпадений.

idealism44 commented 1 year ago

Да, я собираюсь делать словарь с извлечением контекста по объекту сообщения.

Тогда можете, пожалуйста, это сделать? Или как-то покажите, что данная проблема ещё не решена, например оставить этот issue открытым. Ведь если всё так и оставить будут соответствующие проблемы.

Ну хотя бы взять извлечение объектов из БД по схожим критериям для поиска совпадений.

Классно придумано.

Debianov commented 1 year ago

оставить этот issue открытым

Понял