Как рекурсивно получить все подмодули в пакете python?


Задача

У меня есть такая структура папок:

- modules
    - root
        - abc
            hello.py
            __init__.py
        - xyz
            hi.py
            __init__.py
          blah.py
          __init__.py
      foo.py
      bar.py
      __init_.py

Вот то же самое в строковом формате:

"modules",
"modues/__init__.py",
"modules/foo.py",
"modules/bar.py",
"modules/root",
"modules/root/__init__.py",
"modules/root/blah,py",
"modules/root/abc",
"modules/root/abc/__init__.py",
"modules/root/abc/hello.py",
"modules/root/xyz",
"modules/root/xyz/__init__.py",
"modules/root/xyz/hi.py"

Я пытаюсь распечатать все модули в формате стиля импорта python. Пример вывода будет выглядеть так:

modules.foo
modules.bar
modules.root.blah
modules.root.abc.hello
modules.root.xyz.hi

Как я могу сделать это в python(если это возможно без сторонних библиотек) легко?

Что я пробовал

Пример Кода

import pkgutil

import modules

absolute_modules = []


def find_modules(module_path):
    for package in pkgutil.walk_packages(module_path):
        print(package)
        if package.ispkg:
            find_modules([package.name])
        else:
            absolute_modules.append(package.name)


if __name__ == "__main__":
    find_modules(modules.__path__)
    for module in absolute_modules:
        print(module)
Однако этот код будет выводить только "foo" и "bar". Но не "корень", а это субпакеты. Мне также трудно понять, как преобразовать это, чтобы сохранить его абсолютный стиль импорта. Текущий код получает только имя пакета / модуля, а не фактический абсолютный импорт.
2 2

2 ответа:

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

import os
import re

for root,dirname,filename in os.walk(os.getcwd()):
    pth_build=""
    if os.path.isfile(root+"/__init__.py"):
        for i in filename:
            if i <> "__init__.py" and i <> "__init__.pyc":
                if i.split('.')[1] == "py":
                    slot = list(set(root.split('\\')) -set(os.getcwd().split('\\')))
                    pth_build = slot[0]
                    del slot[0]
                    for j in slot:
                        pth_build = pth_build+"."+j
                    print pth_build +"."+ i.split('.')[0]

Этот код будет отображать:

modules.foo
modules.bar
modules.root.blah
modules.root.abc.hello
modules.root.xyz.hi

Если вы запустите его вне папки modules.

Так что я, наконец, понял, как сделать это чисто и заставить pkgutil позаботиться обо всем случае edge для вас. Этот код был основан на python help() функция, которая отображает только модули и пакеты верхнего уровня.

import importlib
import pkgutil

import sys

import modules


def find_abs_modules(module):
    path_list = []
    spec_list = []
    for importer, modname, ispkg in pkgutil.walk_packages(module.__path__):
        import_path = f"{module.__name__}.{modname}"
        if ispkg:
            spec = pkgutil._get_spec(importer, modname)
            importlib._bootstrap._load(spec)
            spec_list.append(spec)
        else:
            path_list.append(import_path)
    for spec in spec_list:
        del sys.modules[spec.name]
    return path_list


if __name__ == "__main__":
    print(sys.modules)
    print(find_abs_modules(modules))
    print(sys.modules)

Это будет работать даже для встроенных пакетов.