Twisted Plugin System是编写可扩展扭曲应用程序的首选方法。
但是,由于插件系统的结构方式(插件进入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),但这也可能以奇怪的方式失败。
问题是:有没有人成功编写过setup.py来安装扭曲的插件,它适用于所有情况?
答案 0 :(得分:17)
我在下面记录了一个setup.py,只有当用户拥有pip< 1.2(例如在Ubuntu 12.04上)。如果每个人都拥有1.2或更高的点数,那么您唯一需要的是packages=[..., 'twisted.plugins']
。
通过阻止pip将“twisted
”行写入.egg-info/top_level.txt
,您可以继续使用packages=[..., 'twisted.plugins']
,并且有一个不会删除所有pip uninstall
的{{1}} {1}}。这涉及monkeypatching setuptools /分布在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 install
和pip install --user
对此进行了测试。使用任何安装方法,上面的monkeypatch和easy_install
都可以正常工作。
您可能想知道:我是否需要清除monkeypatch以避免搞乱下一次安装? (例如pip uninstall
;你不想影响Twisted的pip install --no-deps MyPackage Twisted
。)答案是否定的; monkeypatch不会影响其他安装,因为top_level.txt
会为每次安装产生新的pip
。
相关:请注意,在您的项目中,您必须not have a file python
。如果您在安装过程中看到此警告:
twisted/plugins/__init__.py
这是完全正常的,你应该不尝试通过添加package init file 'twisted/plugins/__init__.py' not found (or not a regular file)
来修复它。
答案 1 :(得分:3)
这是一篇博客文章,描述了使用'package_data'进行的操作:
http://chrismiles.livejournal.com/23399.html
以什么奇怪的方式可以失败?如果软件包的安装没有将软件包数据放入sys.path上的目录,则可能会失败。在这种情况下,Twisted插件加载器将找不到它。但是,我所知道的所有Python软件包安装都会将它们放在安装Python模块或软件包本身的同一目录中,这样就不会有问题了。
答案 2 :(得分:2)
也许您可以调整package_data的想法来改为使用data_files:它不需要您将twisted.plugins列为包,因为它使用绝对路径。不过,它仍然是一个混合物。
我对纯distutils的测试告诉我,它可以覆盖另一个发行版的文件。我想使用pkgutil.extend_path和distutils测试穷人的命名空间包,结果发现我可以使用spam.eggs / setup.py安装带有spam.ham / setup.py和spam/ham/__init__.py
的{{1}} 。目录不是问题,但文件将被愉快地覆盖。我认为这实际上是在distutils中的未定义行为,这些行为涓涓细流到setuptools和pip,所以pip可以IMO关闭为wontfix。
安装Twisted插件的常用方法是什么?用手把它放在这里?
答案 3 :(得分:1)
我使用这种方法:
.py
”和“.pyc
”版本放入包中的“twisted/plugins/
”文件夹中。
请注意,“.pyc
”文件可以为空,它应该存在。在setup.py
中指定将两个文件复制到库文件夹(确保不会覆盖现有插件!)。例如:
# setup.py
from distutils import sysconfig
LIB_PATH = sysconfig.get_python_lib()
# ...
plugin_name = '<your_package>/twisted/plugins/<plugin_name>'
# '.pyc' extension is necessary for correct plugins removing
data_files = [
(os.path.join(LIB_PATH, 'twisted', 'plugins'),
[''.join((plugin_name, extension)) for extension in ('.py', '.pyc')])
]
setup(
# ...
data_files=data_files
)