如何保护我的 Python 代码库,以便访客看不到某些模块,但它仍然可以工作?

发布于 2024-08-04 22:22:34 字数 2144 浏览 10 评论 0原文

我们正在用 Python 启动一个新项目,其中包含一些我们希望保密的专有算法和敏感逻辑。我们还将有一些外部人员(精选的公众成员)参与代码工作。我们不能授予外部人员访问小型私有代码的权限,但我们希望公共版本能够为他们提供足够好的工作空间。

假设我们的项目 Foo 有一个模块 bar,其中包含一个函数 get_sauce()get_sauce() 中实际发生的情况是秘密的,但我们希望 get_sauce() 的公共版本返回可接受的(尽管不正确)结果。

我们还运行自己的 Subversion 服务器,因此我们可以完全控制谁可以访问什么。

符号链接

我的第一个想法是符号链接 - 向所有人提供 bar_public.pybar_private.py<,而不是 bar.py /code> 仅限内部开发人员。不幸的是,创建符号链接是一项乏味的手动工作——尤其是当确实有大约两打这样的私有模块时。

更重要的是,它使 Subversion authz 文件的管理变得困难,因为对于我们想要保护的每个模块,必须在服务器上添加例外。有人可能会忘记执行此操作并意外地检查机密...然后该模块位于存储库中,我们必须在没有它的情况下重建存储库,并希望外部人员在此期间没有下载它。

多个存储库

接下来的想法是拥有两个存储库:

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

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

We're starting a new project in Python with a few proprietary algorithms and sensitive bits of logic that we'd like to keep private. We also will have a few outsiders (select members of the public) working on the code. We cannot grant the outsiders access to the small, private bits of code, but we'd like a public version to work well enough for them.

Say that our project, Foo, has a module, bar, with one function, get_sauce(). What really happens in get_sauce() is secret, but we want a public version of get_sauce() to return an acceptable, albeit incorrect, result.

We also run our own Subversion server so we have total control over who can access what.

Symlinks

My first thought was symlinking — Instead of bar.py, provide bar_public.py to everybody and bar_private.py to internal developers only. Unfortunately, creating symlinks is tedious, manual work — especially when there are really going to be about two dozen of these private modules.

More importantly, it makes management of the Subversion authz file difficult, since for each module we want to protect an exception must be added on the server. Someone might forget to do this and accidentally check in secrets... Then the module is in the repo and we have to rebuild the repository without it and hope that an outsider didn't download it in the meantime.

Multiple repositories

The next thought was to have two repositories:

private
└── trunk/
    ├── __init__.py
    └── foo/
        ├── __init__.py
        └── bar.py
public
└── trunk/
    ├── __init__.py
    └── foo/
        ├── __init__.py
        ├── bar.py
        ├── baz.py
        └── quux.py

The idea is that only internal developers will be able to checkout both private/ and public/. Internal developers will set their PYTHONPATH=private/trunk:public/trunk, but everyone else will just set PYTHONPATH=public/trunk. Then, both insiders and outsiders can from foo import bar and get the right module, right?

Let's try this:

% 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

I'm not a Python expert, but it seems that Python has already made up its mind about module foo and searches relative to that:

>>> foo
<module 'foo' from '/path/to/private/trunk/foo/__init__.py'>

Not even deleting foo helps:

>>> 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

Can you provide me with a better solution or suggestion?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

自找没趣 2024-08-11 22:22:34

__init__ 方法中code>foo 包中,您可以更改 __path__ 以使其在其他目录中查找其模块。

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

__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

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

So create a directory called secret and put it in your private Subversion repository. In secret put your proprietary bar.py. In the __init__.py of the public foo package put in something like:

__path__.insert(0,'secret')

This will mean for users who have the private repository and so the secret directory they will get the proprietary bar.py as foo.bar as secret is the first directory in the search path. For other users, Python won't find secret and will look as the next directory in __path__ and so will load the normal bar.py from foo.

So it will look something like this:

   private
    └── trunk/
        └── secret/
            └── bar.py
    public
    └── trunk/
        ├── __init__.py
        └── foo/
            ├── __init__.py
            ├── bar.py
            ├── baz.py
            └── quux.py
作死小能手 2024-08-11 22:22:34

使用某种插件系统,并保留您的插件,但也可以使用开放代码附带的公开可用的插件。

插件系统比比皆是。您可以轻松地自己制作非常简单的东西。如果您想要更高级的东西,我更喜欢 Zope 组件架构,但也有像 setuptools entry_points 等选项。

在您的情况下使用哪一个将是一个很好的第二个问题。

Use some sort of plugin system, and keep your plugins to your self, but also have publically available plugins that gets shipped with the open code.

Plugin systems abound. You can easily make dead simple ones yourself. If you want something more advanced I prefer the Zope Component Architecture, but there are also options like setuptools entry_points, etc.

Which one to use in your case would be a good second question.

£冰雨忧蓝° 2024-08-11 22:22:34

这是我在阅读 Flask 的文档时注意到的替代解决方案:

flaskext/__init__.py

此文件的唯一目的是将包标记为名称空间包。这是必需的,以便来自不同 PyPI 包的多个模块可以驻留在同一个 Python 包中:

__import__('pkg_resources').declare_namespace(__name__)

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

Here's an alternate solution I noticed when reading the docs for Flask:

flaskext/__init__.py

The only purpose of this file is to mark the package as namespace package. This is required so that multiple modules from different PyPI packages can reside in the same Python package:

__import__('pkg_resources').declare_namespace(__name__)

If you want to know exactly what is happening there, checkout the distribute or setuptools docs which explain how this works.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文