Как защитить свою кодовую базу Python, чтобы гости не могли видеть определенные модули, но она все еще работает?
Мы начинаем новый проект на Python с несколькими запатентованными алгоритмами и чувствительными кусочками логики, которые мы хотели бы сохранить в секрете. У нас также будет несколько аутсайдеров (избранных представителей общественности), работающих над кодом. Мы не можем предоставить посторонним доступ к небольшим частным частям кода, но мы бы хотели, чтобы общедоступная версия работала достаточно хорошо для них.
Скажем, у нашего проекта, Foo, есть модуль, bar
с одной функцией, get_sauce()
, Что на самом деле происходит в get_sauce()
это секрет, но мы хотим публичную версию get_sauce()
вернуть приемлемый, хотя и неверный результат.
У нас также есть собственный сервер Subversion, поэтому мы полностью контролируем, кто и к чему имеет доступ.
Symlinks
Моя первая мысль была символической ссылки - вместо bar.py
, предоставлять bar_public.py
всем и bar_private.py
только для внутренних разработчиков. К сожалению, создание символических ссылок - это утомительная ручная работа, особенно когда на самом деле будет около двух десятков таких приватных модулей.
Что еще более важно, это затрудняет управление файлом authz Subversion, поскольку для каждого модуля, который мы хотим защитить, на сервере должно быть добавлено исключение. Кто-то может забыть сделать это и случайно проверить секреты... Тогда модуль находится в репозитории, и мы должны перестроить репозиторий без него и надеяться, что посторонний не загрузит его за это время.
Несколько репозиториев
Следующая мысль состояла в том, чтобы иметь два хранилища:
private
└── trunk/
├── __init__.py
└── foo/
├── __init__.py
└── bar.py
public
└── trunk/
├── __init__.py
└── foo/
├── __init__.py
├── bar.py
├── baz.py
└── quux.py
Идея состоит в том, что только внутренние разработчики смогут проверить оба private/
а также public/
, Внутренние разработчики установят свои PYTHONPATH=private/trunk:public/trunk
, но все остальные просто установят PYTHONPATH=public/trunk
, Тогда как инсайдеры, так и аутсайдеры могут from foo import bar
и получить правильный модуль, верно?
Давайте попробуем это:
% PYTHONPATH=private/trunk:public/trunk python
Python 2.5.1
Type "help", "copyright", "credits" or "license" for more information.
>>> import foo.bar
>>> foo.bar.sauce()
'a private bar'
>>> import foo.quux
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named quux
Я не эксперт по Python, но кажется, что Python уже решил о модуле foo
и ищет относительно этого:
>>> foo
<module 'foo' from '/path/to/private/trunk/foo/__init__.py'>
Даже не удаляя foo
помогает:
>>> import sys
>>> del foo
>>> del sys.modules['foo']
>>> import foo.quux
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named quux
Можете ли вы дать мне лучшее решение или предложение?
3 ответа
в __init__
метод foo
пакет, который вы можете изменить __path__
чтобы заставить его искать свои модули в других каталогах.
Так что создайте каталог под названием secret
и поместите его в свой личный репозиторий Subversion. В secret
поставить свою собственность bar.py
, в __init__.py
общественности foo
пакет положить в нечто вроде:
__path__.insert(0,'secret')
Это будет означать для пользователей, которые имеют частный репозиторий и поэтому secret
каталог, который они получат bar.py
как foo.bar
как secret
это первый каталог в пути поиска. Для других пользователей Python не найдет secret
и будет выглядеть как следующий каталог в __path__
и так нормально загрузится bar.py
от foo
,
Так это будет выглядеть примерно так:
private
└── trunk/
└── secret/
└── bar.py
public
└── trunk/
├── __init__.py
└── foo/
├── __init__.py
├── bar.py
├── baz.py
└── quux.py
Используйте какую-то систему плагинов и держите свои плагины при себе, но также имейте общедоступные плагины, которые поставляются с открытым кодом.
Системы плагинов имеются в большом количестве. Вы можете легко сделать из них простых. Если вы хотите что-то более продвинутое, я предпочитаю архитектуру компонентов Zope, но есть также такие опции, как setuptools entry_points и т. Д.
Какой из них использовать в вашем случае будет хорошим вторым вопросом.
Вот альтернативное решение, которое я заметил, читая документы для Flask:
flaskext/__init__.py
Единственная цель этого файла - пометить пакет как пакет пространства имен. Это необходимо для того, чтобы несколько модулей из разных пакетов PyPI могли находиться в одном пакете Python:
__import__('pkg_resources').declare_namespace(__name__)
Если вы хотите точно знать, что там происходит, обратитесь к документации по дистрибутиву или setuptools, в которой объясняется, как это работает.