如何保护我的Python代码库,以便访客无法看到某些模块,但仍然有效?

时间:2009-09-18 08:05:22

标签: python svn project-management repository modularity

我们正在用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

您能为我提供更好的解决方案或建议吗?

3 个答案:

答案 0 :(得分:3)

In the __init__ method of the foo package you can change __path__ to make it look for its modules in other directories.

因此,创建一个名为secret的目录,并将其放在您的私有Subversion存储库中。在secret放置您的专有bar.py。在公共__init__.py包的foo中添加如下内容:

__path__.insert(0,'secret')

这对于拥有私有存储库的用户来说意味着secret目录,他们将获得bar.py所有权foo.bar作为secretsecret是第一个目录搜索路径。对于其他用户,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__)
     

如果您想确切知道那里发生了什么,请查看分发或设置工具文档,以解释其工作原理。