Подтвердить что ты не робот

Как написать setup.py для twistd/twisted plugin, который работает с setuptools, распространяет и т.д.?

Twisted Plugin System является предпочтительным способом записи расширяемых скрученных приложений.

Однако из-за того, что система плагина структурирована (плагины входят в каталог с витыми/плагинами, который не должен быть пакетом Python), запись правильного файла setup.py для установки этих плагинов представляется нетривиальной.

Я видел некоторые попытки добавить "twisted.plugins" в "пакет" ключа команды настройки distutils, но поскольку это не пакет, происходят плохие вещи (например, __init__.py добавлены некоторыми инструментами).

Другие попытки, похоже, используют вместо этого 'package_data' (например, http://bazaar.launchpad.net/~glyph/divmod.org/trunk/view/head:/Epsilon/epsilon/setuphelper.py), но это также может потерпеть неудачу в странных путях.

Вопрос: кто-нибудь успешно написал setup.py для установки витых плагинов, которые работают во всех случаях?

4b9b3361

Ответ 1

Я документирую 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 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 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 install, pip install --user и easy_install. При любом способе установки вышеуказанные monkeypatch и pip uninstall работают нормально.

Возможно, вам интересно: мне нужно очистить файл monkeypatch, чтобы избежать перебора следующей установки? (например, pip install --no-deps MyPackage Twisted, вы не хотите влиять на Twisted top_level.txt.) Ответ - нет; monkeypatch не влияет на другую установку, потому что pip создает новую python для каждой установки.

Связано: имейте в виду, что в вашем проекте вы должны not иметь файл twisted/plugins/__init__.py. Если вы видите это предупреждение во время установки:

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

это абсолютно нормально, и вы должны не попытаться исправить его, добавив __init__.py.

Ответ 2

Вот запись в блоге, которая описывает это с помощью "package_data":

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

В чем странные пути могут потерпеть неудачу? Он может выйти из строя, если установка пакета не помещает данные пакета в каталог, находящийся на sys.path. В этом случае загрузчик плагинов Twisted не нашел бы его. Тем не менее, все установки пакетов Python, о которых я знаю, будут помещены в тот же каталог, где они устанавливают сами модули или пакеты Python, поэтому это не будет проблемой.

Ответ 3

Возможно, вы могли бы адаптировать идею package_data для использования data_files: вместо этого вам не нужно было бы перечислять twisted.plugins в качестве пакета, поскольку он использует абсолютные пути. Тем не менее, это все равно будет kludge.

Мои тесты с чистыми distutils сказали мне, что можно перезаписать файлы из другого дистрибутива. Я хотел протестировать пакеты с белым mans namespace, используя pkgutil.extend_path и distutils, и оказывается, что я могу установить spam/ham/__init__.py с помощью spam.ham/setup.py и spam/eggs/__init__.py с помощью spam.eggs/setup.py. Каталоги не являются проблемой, но файлы будут перезаписаны. Я думаю, что это на самом деле поведение undefined в distutils, которое стекает до setuptools и pip, поэтому pip может закрыть IMO как wontfix.

Каков обычный способ установки Twisted plugins? Брось-сюда-сюда вручную?

Ответ 4

Я использую этот подход:

  • Поместите '.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
    )