如何编写一个setup.py用于与setuptools,分发等一起使用的扭曲/扭曲插件?

时间:2011-09-01 19:14:26

标签: python twisted setuptools distutils distribute

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来安装扭曲的插件,它适用于所有情况?

4 个答案:

答案 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 installpip 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)

我使用这种方法:

  1. 将文件的“.py”和“.pyc”版本放入包中的“twisted/plugins/”文件夹中。 请注意,“.pyc”文件可以为空,它应该存在。
  2. 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
    )