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('** Невозможно обратиться к полю внутреннего модуля!')
Конечно, здесь скрыт не сам модуль, а его содержимое, зато скрыто оно довольно жёстко.
