如何为支持setuptools和distribute等的twistd/twisted插件编写setup.py?

26 投票
4 回答
5613 浏览
提问于 2025-04-17 01:08

Twisted 插件系统 是编写可扩展的 Twisted 应用程序的推荐方式。

不过,由于插件系统的结构(插件放在一个叫做 twisted/plugins 的文件夹里,而这个文件夹不能是一个 Python 包),所以写一个合适的 setup.py 来安装这些插件并不是一件简单的事。

我看到有些人尝试把 'twisted.plugins' 加到 distutils 的 setup 命令中的 'packages' 选项里,但因为它其实并不是一个真正的包,所以会出现一些问题(比如,有些工具会自动添加一个 __init__.py 文件)。

还有一些人似乎使用 'package_data' 来解决这个问题(例如,http://bazaar.launchpad.net/~glyph/divmod.org/trunk/view/head:/Epsilon/epsilon/setuphelper.py),但这也可能会以奇怪的方式失败。

问题是:有没有人成功写过一个可以在所有情况下安装 Twisted 插件的 setup.py?

4 个回答

2

也许你可以考虑用data_files来替代package_data的想法:这样就不需要把twisted.plugins列为包,因为它使用的是绝对路径。不过,这样做还是有点儿不太好。

我用纯distutils做了一些测试,发现可以覆盖来自其他发行版的文件。我想测试一下用pkgutil.extend_path和distutils实现的简单命名空间包,结果发现我可以用spam.ham/setup.py安装spam/ham/__init__.py,还可以用spam.eggs/setup.py安装spam/eggs/__init__.py。目录没问题,但文件会被直接覆盖。我觉得这在distutils中其实是未定义的行为,这个问题会影响到setuptools和pip,所以我认为pip可以把这个问题标记为不会修复。

安装Twisted插件的通常方法是什么?是手动把它放在这里吗?

3

这里有一篇博客文章,讲的是如何使用 'package_data' 来实现这个功能:

http://chrismiles.livejournal.com/23399.html

那这个方法可能会以什么奇怪的方式失败呢?如果安装包的时候,没有把包的数据放到一个在 sys.path 里的目录下,就会失败。在这种情况下,Twisted 插件加载器就找不到这些数据。不过,我知道的所有 Python 包的安装方式,都会把数据放到和 Python 模块或包本身相同的目录里,所以这应该不是个问题。

17

下面我会介绍一个只有在用户使用的 pip 版本低于 1.2(比如在 Ubuntu 12.04 上)时才需要的 setup.py 文件。如果大家的 pip 版本是 1.2 或更新的,那么你只需要在代码里加上 packages=[..., 'twisted.plugins'] 就可以了。

通过阻止 pip 在 .egg-info/top_level.txt 文件中写入 "twisted" 这一行,你可以继续使用 packages=[..., 'twisted.plugins'],并且在使用 pip uninstall 时不会把整个 twisted/ 文件夹都删掉。这需要在你的 setup.py 文件的开头进行一些修改。下面是一个示例的 setup.py 文件:

from distutils.core import setup

# When pip installs anything from packages, py_modules, or ext_modules that
# includes a twistd plugin (which are installed to twisted/plugins/),
# setuptools/distribute writes a Package.egg-info/top_level.txt that includes
# "twisted".  If you later uninstall Package with `pip uninstall Package`,
# pip <1.2 removes all of twisted/ instead of just Package's twistd plugins.
# See https://github.com/pypa/pip/issues/355 (now fixed)
#
# To work around this problem, we monkeypatch
# setuptools.command.egg_info.write_toplevel_names to not write the line
# "twisted".  This fixes the behavior of `pip uninstall Package`.  Note that
# even with this workaround, `pip uninstall Package` still correctly uninstalls
# Package's twistd plugins from twisted/plugins/, since pip also uses
# Package.egg-info/installed-files.txt to determine what to uninstall,
# and the paths to the plugin files are indeed listed in installed-files.txt.
try:
    from setuptools.command import egg_info
    egg_info.write_toplevel_names
except (ImportError, AttributeError):
    pass
else:
    def _top_level_package(name):
        return name.split('.', 1)[0]

    def _hacked_write_toplevel_names(cmd, basename, filename):
        pkgs = dict.fromkeys(
            [_top_level_package(k)
                for k in cmd.distribution.iter_distribution_names()
                if _top_level_package(k) != "twisted"
            ]
        )
        cmd.write_file("top-level names", filename, '\n'.join(pkgs) + '\n')

    egg_info.write_toplevel_names = _hacked_write_toplevel_names

setup(
    name='MyPackage',
    version='1.0',
    description="You can do anything with MyPackage, anything at all.",
    url="http://example.com/",
    author="John Doe",
    author_email="jdoe@example.com",
    packages=['mypackage', 'twisted.plugins'],
    # You may want more options here, including install_requires=,
    # package_data=, and classifiers=
)

# Make Twisted regenerate the dropin.cache, if possible.  This is necessary
# because in a site-wide install, dropin.cache cannot be rewritten by
# normal users.
try:
    from twisted.plugin import IPlugin, getPlugins
except ImportError:
    pass
else:
    list(getPlugins(IPlugin))

我已经用 pip installpip install --usereasy_install 测试过这个方法。无论使用哪种安装方式,上面的修改和 pip uninstall 都能正常工作。

你可能会问:我需要清除这个修改以避免影响下次安装吗?(比如 pip install --no-deps MyPackage Twisted;你不想影响 Twisted 的 top_level.txt 文件。)答案是不用;这个修改不会影响其他安装,因为 pip 每次安装时都会启动一个新的 python 进程。

相关提示:请记住,在你的项目中,必须 不要有一个文件 twisted/plugins/__init__.py。如果在安装过程中看到这个警告:

package init file 'twisted/plugins/__init__.py' not found (or not a regular file)

这是完全正常的,你不需要通过添加一个 __init__.py 文件来尝试修复它。

撰写回答