Pull to refresh

Как универсально организовать импорты в проекте, независимо от того, где находятся модули?

Reading time2 min
Views8.3K

Начнем с того, что это статья посягается на святой устой комьюнити Python разработчиков, устой звучит так "синтаксис python - идеален, стандартные библиотеки - идеальны, и полноценны, GIL - это неизбежная жертва для такого прекрасного языка как Python ... может быть в конце столетия люди придумают как его обойти, но, а пока так 🥺". Приносим глубокие извинения за такую статью, это чисто юмористичная статья, не стоит принимать ей в серьез.

В общем решить эту проблему можно 50 строчками, вот код для импорта модуля из любого места, без плясок с бубнами и `sys.path`

import importlib.util
from importlib.machinery import ModuleSpec
from os import sep
from os.path import splitext, join
from pathlib import Path, PosixPath
from types import ModuleType
from typing import Optional, Union


class ObjFrom:
    def __init__(self, module: ModuleType):
        self.module: ModuleType = module

    def From(self, *obj):
        """
        Выбрать определяя объекты из модуля

        :param obj:
        :return:
        """
        return tuple(v for k, v in self.module.__dict__.items() if k in obj)


def iimport(self_file: str = None,
            count_up: int = 0,
            module_name: str = None,
            *,
            absolute_path: Union[str, Path] = None) -> ObjFrom:
    """
    Импортировать файл как модуль `python`

    :param self_file: Обычно это __file__
    :param count_up: Насколько папок поднять вверх
    :param module_name: Имя импортируемого модуля
    :param absolute_path: Путь к `python` файлу
    :return: Модуль `python`
    """
    path: str = ''
    if absolute_path is not None:
        if isinstance(absolute_path, PosixPath):
            absolute_path = absolute_path.__str__()
        path = absolute_path
    else:
        path = join(sep.join(Path(self_file).parts[:(count_up + 1) * -1]), f"{module_name}.py")
    if splitext(path)[1] != ".py":
        raise ValueError(f"Файл должен иметь расширение .py")
    spec: Optional[ModuleSpec] = importlib.util.spec_from_file_location("my_module", path)
    __module: ModuleType = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(__module)
    return ObjFrom(__module)

Как пользоваться ?

А вот так. Вам нужен модулю из другой вселенной ? пожалуйста укажите к ней абсолютный путь и берите его.

ИмяМодуля = iimport(absolute_path='Путь').module

Хорошо, но писать абсолютный путь не всегда удобно. Например, наш модуль лежит на несколько уровней директорий вверх, и что тогда строчить абсолютный путь 😡 ? Ну нет, можно использовать такой кейс

ИмяМодуля = iimport(__file__, СколькоДиректорийВверх, 'ИмяМодуля').module

Чувствую как юношеская радость переполняет вас от такого нового открытия. Сколько же фич открывается перед тобой теперь ☺?? Но как говорят в ресторане - десерт подают в конце.

Если нам нужно получить конкретные объекты из модуля, то можно воспользоваться таким кейсом

Объект_1, Объект_N = iimport(absolute_path='Путь').From('Объект_1', 'Объект_N')

Статья закончена, высокий поклон публики и читателям которые дошли до этой строки. С уважением, ваш - бездарный программист, который ни в зуб ногой в Python :)

Tags:
Hubs:
Total votes 24: ↑13 and ↓11+4
Comments14

Articles