Можно ли сгенерировать оператор импорта из объекта модуля в python? - PullRequest
1 голос
/ 06 апреля 2020

В Python легко проверить, является ли объект модулем с isinstance(obj, types.ModuleType). Мы также можем программно генерировать модули. Однако я заинтересован в том, чтобы пойти другим путем - создать код, который бы создал импорт, в результате чего модуль был добавлен в пространство имен globals / locals. По существу, функция assemble_import выглядит следующим образом:

def assemble_import(module: types.ModuleType, name: str) -> str:
    pass

Приблизительно удовлетворяет следующим условиям:

import statistics
assert assemble_import(statistics, 'statistics') = 'import statistics'

from os import path
assert assemble_import(path, 'path') = 'from os import path'

import collections.abc
abc = collections.abc
assert assemble_import(abc, 'abc') = 'from collections import abc'

import abc as xyz
assert assemble_import(xyz, 'xyz') = 'import abc as xyz'

Я бы не хотел бы, чтобы оно использовало абстрактное синтаксическое дерево, а скорее сам объект модуля. Что я пытался сделать до сих пор:

  • module.__spec__ - он возвращает ModuleSpec с атрибутом имени
  • module.__package__ - не уверен, почему, но в большинстве случаев он пуст
  • module.__loader__ - обычно SourceFileLoader, также имеет атрибут name

проблема с атрибутом name заключается в том, что он является 'posixpath' для os.path, тогда как from os import posixpath явно не работает. Кроме того, я не вижу, как получить родительский модуль (os в примере os.path).

Достигается ли работоспособное (хотя и не обязательно готовое / пуленепробиваемое) решение в Python? Другими словами, информация о структуре пакетов, необходимая для воссоздания кода импорта, сохранена / доступна?

1 Ответ

1 голос
/ 06 апреля 2020

Вы можете, и это довольно просто, но os.path будет немного странно:

def assemble_import(module, name):
    return 'import {} as {}'.format(module.__name__, name)

os.path странно, потому что это зависимый от платформы псевдоним для модуля ntpath или модуль posixpath. Эта функция будет использовать фактическое имя модуля, либо ntpath, либо posixpath. Если вы хотите трактовать os.path как os.path, вы можете использовать его в особом случае, хотя это может быть не лучшим выбором для проектирования.

Для актуальных субмодулей пакета, таких как collections.abc, эта функция будет обрабатывать их как подмодули содержащего их пакета:

>>> assemble_import(collections.abc, 'abc')
'import collections.abc as abc'

, но для os.path, это даст вам вывод типа

>>> assemble_import(os.path, 'path')
'import posixpath as path'

Если вы хотите импорт, который выглядит немного более похожим на то, что обычно пишет человек, вы можете добавить к функции несколько логик c:

def assemble_import(module, name):
    pname, _, mname = module.__name__.rpartition('.')
    if pname:
       statement = 'from {} import {}'.format(pname, mname)
    else:
       statement = 'import ' + mname
    if mname != name:
       statement += ' as ' + name
    return statement
...