我们正在用Python创建一个新项目,其中包含一些专有算法和敏感的逻辑位,我们希望保密。我们还将有一些外部人员(选择公众成员)从事代码工作。我们不能允许外人访问小的私有代码,但我们希望公共版本能够很好地为他们工作。
假设我们的项目Foo有一个模块bar
,其中包含一个函数get_sauce()
。 get_sauce()
中真正发生的事情是秘密的,但我们希望get_sauce()
的公开版本返回一个可接受的,尽管不正确的结果。
我们还运行自己的Subversion服务器,因此我们完全可以控制谁可以访问。
符号链接
我的第一个想法是符号链接 - 而不是bar.py
,而是向所有人提供bar_public.py
,仅向内部开发者提供bar_private.py
。不幸的是,创建符号链接是繁琐的手工工作 - 特别是当真的会有大约24个私有模块时。
更重要的是,它使Subversion authz文件的管理变得困难,因为我们要保护的每个模块都必须在服务器上添加异常。有人可能会忘记这样做,并意外地检查秘密......然后模块在repo中,我们必须在没有它的情况下重建存储库,并希望局外人不会在此期间下载它。
多个存储库
下一个想法是有两个存储库:
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
您能为我提供更好的解决方案或建议吗?
答案 0 :(得分:3)
因此,创建一个名为secret
的目录,并将其放在您的私有Subversion存储库中。在secret
放置您的专有bar.py
。在公共__init__.py
包的foo
中添加如下内容:
__path__.insert(0,'secret')
这对于拥有私有存储库的用户来说意味着secret
目录,他们将获得bar.py
所有权foo.bar
作为secret
,secret
是第一个目录搜索路径。对于其他用户,Python将找不到__path__
,并将看作bar.py
中的下一个目录,因此将从foo
加载正常的 private
└── trunk/
└── secret/
└── bar.py
public
└── trunk/
├── __init__.py
└── foo/
├── __init__.py
├── bar.py
├── baz.py
└── quux.py
。
所以它看起来像这样:
{{1}}
答案 1 :(得分:2)
使用某种插件系统,并将插件保留给自己,但也有公开可用的插件随附开放代码。
插件系统比比皆是。你可以自己轻松地制作简单的。如果你想要更先进的东西,我更喜欢Zope组件架构,但也有setuptools entry_points等选项。
在你的情况下使用哪一个将是一个很好的第二个问题。
答案 2 :(得分:0)
这是我在阅读Flask的文档时注意到的另一种解决方案:
<强>
flaskext/__init__.py
强>此文件的唯一目的是将包标记为命名空间包。这是必需的,以便来自不同PyPI包的多个模块可以驻留在同一个Python包中:
__import__('pkg_resources').declare_namespace(__name__)
如果您想确切知道那里发生了什么,请查看分发或设置工具文档,以解释其工作原理。