Как я могу получить имя модуля, в котором был объявлен экземпляр Table? - PullRequest
0 голосов
/ 14 ноября 2018

У меня есть таблица sqlalchemy, определенная так

from sqlalchemy.sql.schema import Table

my_table = Table(
    "my_table",
    metadata,
    Column("my_id", BigInteger(), primary_key=True),
    ...
)

Я пытаюсь проверить этот экземпляр, чтобы получить модуль, в котором он был создан. Я попытался использовать sqlalchemy.inspect(my_table).__module__, my_table.__module__ и inspect.getmodule(my_table), однако все три возвращают sqlalchemy.sql.schema, который является модулем, в котором определяется Table, а не my_table.

Как я могу получить имя модуля, в котором я создал экземпляр my_table?

Ответы [ 2 ]

0 голосов
/ 15 ноября 2018

Вы можете добавить функциональность, создав подкласс Table.В SQLAlchemy Table специально переопределяет Table.__init__(), чтобы сделать его недоступным:

def __init__(self, *args, **kw):
    """Constructor for :class:`~.schema.Table`.
    This method is a no-op.   See the top-level
    documentation for :class:`~.schema.Table`
    for constructor arguments.
    """
    # __init__ is overridden to prevent __new__ from
    # calling the superclass constructor.

Ключ в том, что он не вызывает super().__init__(), так что sqlalchemy можетпринять команду создания экземпляра и что бы вы ни делали, это необходимо сохранить.

from sqlalchemy.sql.schema import Table

class MyTable(Table):

    def __init__(self, *args, **kwargs):
        self._where_am_i = __file__

my_table = MyTable(
    "my_table",
    metadata,
    Column("my_id", BigInteger(), primary_key=True)
)

В этом случае MyTable.__init__() все еще блокирует конструктор суперкласса, но также добавляет атрибут к экземпляру, который будетимя модуля, в котором создается экземпляр класса.Я специально выбрал непонятное имя атрибута (_where_am_i), которое вряд ли будет перезаписано sqlalchemy, и использование __file__ возвращает путь к модулю (но вы можете сделать это как угодно).

Я тестировалкоторый вставляет и выбирает все еще работает:

import logging
from sqlalchemy.sql import select
logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)
logging.basicConfig(level=logging.INFO)
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
conn = engine.connect()
conn.execute(my_table.insert(), [{"my_id": i} for i in range(1, 6)])
s = select([my_table])
result = conn.execute(s)
for row in result:
    print(row)
# (1,)
# (2,)
# (3,)
# (4,)
# (5,)

И расположение экземпляра:

print(my_table._where_am_i)  # 53302898.py (that's the name of my module).

Внешний модуль:

# external_module.py

from sqlalchemy_app import Base
from sqlalchemy.sql.schema import Table
from sqlalchemy import Column, BigInteger

metadata = Base.metadata

class MyTable(Table):

    def __init__(self, *args, **kwargs):
        self._where_am_i = __file__

my_table = MyTable(
    "my_table",
    metadata,
    Column("my_id", BigInteger(), primary_key=True)
)

И:

# 53302898.py

from external_module import my_table

if __name__ == '__main__':
    print(my_table._where_am_i)  # prints C:\Users\peter_000\OneDrive\git\test\external_module.py

Обратите внимание, как он возвратил относительный путь к файлу в первом тесте и абсолютный путь к файлу в тесте внешнего модуля.Вы можете прочитать об этом здесь: Атрибут Python __file__ абсолютный или относительный? , но вы можете сделать так, чтобы этот атрибут _where_am_i возвращал все, что вам нужно для вашего приложения.

РЕДАКТИРОВАТЬ Приведенное выше решение требует создания подкласса Table class внутри модуля, в котором формируются экземпляры, в противном случае оно будет привязывать модуль, в котором создается экземпляр Class, а не экземпляры.Если вы хотите создать подкласс Table один раз в своем проекте, вам нужно передать местоположение конструктору.

Это работает:

class MyTable(Table):

    def __init__(self, *args, _where_am_i=None, **kwargs):
        self._where_am_i = _where_am_i

... но вы получитепредупреждение при создании экземпляра:

SAWarning: Can't validate argument '_where_am_i'; can't locate any SQLAlchemy dialect named '_where'.

Чтобы избежать этого, вам придется переопределить альтернативный конструктор sqlalchemy, Table._init(), удалить параметр location и затем делегировать обратно по цепочке:

class MyTable(Table):

    def _init(self, *args, _where_am_i=None, **kwargs):
        self._where_am_i = _where_am_i
        super()._init(*args, **kwargs)

Импорт из внешнего модуля:

# 53302898.py
from external_module import MyTable

my_table = MyTable(
    "my_table",
    metadata,
    Column("my_id", BigInteger(), primary_key=True),
    _where_am_i = __file__
)

if __name__ == '__main__':
    print(my_table._where_am_i)  # 53302898.py

Все вышеперечисленные тесты все еще проходят.

0 голосов
/ 14 ноября 2018

Вы не можете. Если вы ссылаетесь на индекс документации Python , вы видите, что есть три записи для __module__: одна для атрибут класса , одна для атрибут функции и один для атрибута метода . Только те типы объектов имеют модуль, в котором они были объявлены записанными. my_table не является ни одним из них; это просто экземпляр класса Table, поэтому на нем можно найти только __module__ Table.__module__.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...