private module в python

Можно ли создать приватный модуль, который будет виден только в рамках пакета?


Ответы (2 шт):

Автор решения: hedgehogues

Нельзя. Но для того, чтобы придерживаться некоторых соглашений, используют __ для private и _ для protected.

введите сюда описание изображения

import setuptools
setuptools.setup(
    name='mypackage',
    version='0.0.0',
    packages=['mypackage'],
    package_data={
        'mypackage': ['data/hello.txt'],
    },
)
→ Ссылка
Автор решения: AivanF.

Легко сдаётесь-с! Если прям очень хочется скрыть своё внутреннее АПИ от рук посторонних, есть у меня для вас одно извращение :))

Допустим у нас такой мини-проект:

project/
├─ pack/
│  ├─ __init__.py
│  └─ hidden.py
└─ main.py

И хочется скрыть содержимое hidden.py от посторонних. Делаем так.


hidden.py

Обычный код с логикой:

import string, random

__all__ = [
    'make_secret_key', 'Super',
]

def make_secret_key():
    return ''.join(
        random.choice(string.ascii_uppercase + string.digits)
        for _ in range(10)
    )

class Super:
    def __init__(self, x='42'):
        self.x = x

    def __repr__(self):
        return f'Super({self.x})'

__init__.py

Техноколдовство здесь:

import sys

__all__ = [
    'make_some_object'
]

# Мелочь для удобства
class dotdict(dict):
    __getattr__ = dict.get
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

# Делаем замыкание
def prepare():
    from . import hidden

    # Почему-то эта манипуляция не очень полезна
    # и не мешает работе import pack.hidden
    sys.modules[hidden.__name__] = None

    # Зато можно почистить модуль от его содержимого
    # переложив его в локальную переменную
    local_hidden = dotdict()
    for name in hidden.__all__:
        local_hidden[name] = getattr(hidden, name)
        delattr(hidden, name)
    delattr(hidden, '__all__')

    # Будто ничего и не скрывалось!
    hidden = local_hidden

    # Здесь объявляем классы/функции/переменные,
    # для работы которых нужен скрываемый модуль
    def make_some_object():
        return hidden.Super(hidden.make_secret_key())

    # Перекладываем их в текущий модуль
    # При желании можно автоматизировать используя местный "__all__"
    this = sys.modules[__name__]
    setattr(this, 'make_some_object', make_some_object)
    delattr(this, 'prepare')

    # Вуаля, реальный hidden теперь бесполезен,
    # а local_hidden недоступен извне!

    # Возможно есть уязвимость к importlib.reload(pack.hidden)
    # но её можно постараться исправить

prepare()

main.py

Типа юзер, пытающийся использовать запретный модуль:

import pack.hidden as hidden

try:
    print(hidden.Super)
except AttributeError:
    print('** Невозможно обратиться к полю внутреннего модуля!')

Конечно, здесь скрыт не сам модуль, а его содержимое, зато скрыто оно довольно жёстко.

→ Ссылка