2011-09-01 30 views
26

Twisted Plugin System是编写可扩展扭曲应用程序的首选方式。但是,由于插件系统的结构(插件进入扭曲/插件目录,其应该是而不是是一个Python包),编写适当的setup.py来安装这些插件看起来是非常有用的,不重要的。如何为setuptools,distribute等工作的twistd/twisted插件编写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

的'package_data'失败的描述将是有益的。 – Glyph

+0

这并没有回答这个问题的显式部分,即如何指定要安装的文件,但是它确实解决了(重新)生成插件缓存(稍微清晰一些) )http://stackoverflow.com/questions/1321270/how-to-extend-distutils-with-a-simple-post-install-script – Glyph

回答

3

这里有一个博客条目,其描述了“package_data”这样做:

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

在可以失败是什么奇怪的方式?如果软件包的安装不会将软件包数据放入sys.path上的目录中,它可能会失败。在这种情况下,Twisted插件加载器不会找到它。然而,我所知道的所有Python包的安装都会将它们放到它们自己安装Python模块或包的相同目录中,所以这不会成为问题。

+3

像这样使用package_data的问题之一是,你仍然需要列出'包裹列表中的'twisted.plugins';这会导致'pip uninstall'吹掉你的整个Twisted安装。 – mithrandi

+0

有没有办法通知'pip'不要这样做? Pip错误跟踪器中是否存在一个错误报告? – Glyph

+1

我还没有打开一个错误报告,但它确实看起来像一个点的错误;它有一个记录,确切地说它安装了什么文件,所以我不明白为什么它需要删除它没有安装的其他文件/目录(有可能警告.py [co]文件) – mithrandi

2

也许你可以改变package_data的想法来改为使用data_files:它不需要你将twisted.plugins列为包,因为它使用绝对路径。尽管如此,它仍然是一个混乱。

我用纯粹的distutils测试告诉我,它可以覆盖其他发行版中的文件。我想用pkgutil.extend_path和distutils测试穷人的命名空间包,事实证明,我可以安装spam/ham/__init__.py spam.ham/setup.py和spam/eggs/__init__.py spam.eggs/setup.py。目录不是问题,但文件将被愉快地覆盖。我认为这实际上是distutils中的未定义行为,它涓流到setuptools和pip,因此pip可能会将IMO关闭为wontfix。

什么是安装扭曲插件的常用方法?手工放在这里?

+0

没有“通常”的方式来安装扭曲的插件 - 有一对夫妇不同的随机想法浮动,每个都有自己的缺点。这个问题是确定一个“正确”方式来完成它的一部分,至少对于缺点是什么以及如何应对它们有一些概念。 – Glyph

+0

Pip已关闭,因为wontfix :-) data_files方法是否有效?如果是这样,它似乎是这里列出的最有前途的方法。它与“patch write_toplevel_names”方法具有相同的缺点:只有在使用pip安装插件时才会正确卸载插件。 –

+2

Ivan告诉我(但由于SO的怪异声望限制无法评论) - 这种方法的一个问题是'pip uninstall'不会为你的.py data_file删除相应的.pyc文件。您也不能将.pyc添加到'data_files ='中,因为可能会设置'PYTHON_DONT_WRITE_BYTECODE'。 – Glyph

16

我记下了一个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不删除所有的twisted/。这涉及monkeypatching setuptools /分布在您的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="[email protected]", 
    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测试这一点。使用任何安装方法,上面的monkeypatch和pip uninstall都可以正常工作。

您可能想知道:我是否需要清除monkeypatch以避免搞乱下一次安装? (例如pip install --no-deps MyPackage Twisted;你不想影响Twisted的top_level.txt。)答案是否定的; monkeypatch不会影响其他安装,因为pip会为每次安装生成新的python

相关:请记住,在您的项目中,您必须not have a filetwisted/plugins/__init__.py。如果你看到在安装过程中这样的警告:

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

这是完全正常的,你应该试图通过增加一个__init__.py来解决它。

+1

Hacktastic!只有在使用pip来安装包含插件的项目(而不是easy_install或“python setup.py install”)时,插件文件的卸载才会发生,因为只有pip在安装时写入installed-files.txt 。不过,我认为这个问题的解决方案无法避免,因为如果项目被允许将文件放入其他项目的软件包中,那么拥有完整的已安装文件列表是正确卸载的唯一方法。 –

+0

这可以使用*小*更多的解释是一个完整的答案:'setup.py'看起来像什么?这是如何工作与原始setup.py?那么'easy_install'呢?用'--user'安装等等,但是,正如Carl所说的那样:hacktastic! (希望这证明它*是*可以修复https://github.com/pypa/pip/issues/355 - 至少在pip正在安装的情况下...) – Glyph

+1

根据雕文的评论更新回答 –

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 
    ) 
    
+0

什么平台,配置,'bdist_ *'插件等,你测试过吗? – Glyph

+1

Windows 7和Red Hat 6; Python 2.7; bdist_dumb(gztar格式);用'pip'安装。由于[this](http://docs.python.org/3.2/whatsnew/3.2.html#pep-3147-pyc-repository-directories),我相信这种方法适用于所有小于'3.2'的Python版本。 。 –

相关问题