如何保护我的Python代码库,使访客无法看到某些模块但仍然能正常工作?
我们正在用Python开始一个新项目,这个项目里有一些专有的算法和敏感的逻辑,我们希望这些内容能保持私密。同时,我们也会有一些外部人员(公众中的特定成员)参与代码的工作。我们不能让外部人员接触到那些小的私密代码,但我们希望能有一个公共版本,能让他们正常使用。
假设我们的项目叫Foo,它有一个模块bar
,里面有一个函数get_sauce()
。实际上,get_sauce()
里发生的事情是保密的,但我们希望公共版本的get_sauce()
能返回一个可以接受的、虽然不完全正确的结果。
我们还自己运行一个Subversion服务器,这样我们可以完全控制谁能访问什么。
符号链接
我最初的想法是使用符号链接——也就是说,给大家提供bar_public.py
,而bar_private.py
只给内部开发者。不幸的是,创建符号链接是一项繁琐的手动工作,尤其是当我们有大约二十个这样的私密模块时。
更重要的是,这样会让管理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 个回答
我在阅读Flask的文档时,发现了一个替代的解决方案:
flaskext/__init__.py
这个文件的唯一作用是将这个包标记为命名空间包。这样做是为了让来自不同PyPI包的多个模块可以放在同一个Python包里:
__import__('pkg_resources').declare_namespace(__name__)
如果你想确切了解这里发生了什么,可以查看distribute或setuptools的文档,它们会解释这个是怎么工作的。
使用某种插件系统,把你的插件保留给自己,同时也可以有一些公开的插件,和开源代码一起发布。
插件系统有很多种。你可以很简单地自己做一个。如果你想要更高级一点的,我推荐使用Zope组件架构,不过还有其他选择,比如setuptools的entry_points等等。
选择哪种插件系统适合你,这个问题可以作为第二个问题来考虑。
在 foo
这个包的 __init__
方法里,你可以修改 __path__
,让它去其他目录找模块。
所以,先创建一个叫 secret
的目录,把它放到你的私人 Subversion 仓库里。在 secret
里放上你的专有文件 bar.py
。然后在公共的 foo
包的 __init__.py
文件里加上类似下面的内容:
__path__.insert(0,'secret')
这样一来,对于那些有私人仓库的用户来说,他们会在 foo
中找到 bar.py
,因为 secret
是搜索路径中的第一个目录。对于其他用户来说,Python 找不到 secret
,就会去 __path__
中的下一个目录,加载正常的 bar.py
文件。
所以它的结构大概是这样的:
private
└── trunk/
└── secret/
└── bar.py
public
└── trunk/
├── __init__.py
└── foo/
├── __init__.py
├── bar.py
├── baz.py
└── quux.py