py2app подбирает .git subdir пакета во время сборки

Мы широко используем py2app на нашем объекте для создания автономных пакетов .app для легкого внутреннего развертывания без проблем с зависимостями. Что-то, что я заметил недавно, и понятия не имею, как это началось, заключается в том, что при создании .app py2app начал включать каталог .git нашей основной библиотеки.

commonLib, например, является нашим корневым пакетом библиотеки python, который является репозиторией git. Под этим пакетом находятся различные подпакеты, такие как база данных, утилита и т. Д.

commonLib/ |- .git/ # because commonLib is a git repo |- __init__.py |- database/ |- __init__.py |- utility/ |- __init__.py # ... etc 

В данном проекте, скажем, Foo, мы будем делать импорт, например, from commonLib import xyz чтобы использовать наши общие пакеты. Создание через py2app выглядит примерно так: python setup.py py2app

Поэтому недавняя проблема, которую я вижу, заключается в том, что при создании приложения для проекта Foo я увижу, что он включает все в commonLib / .git / в приложение, что является дополнительным вздутием. py2app имеет исключаемый вариант, но, похоже, это только для модулей python. Я не могу понять, что потребуется, чтобы исключить субтитры .git или, по сути, то, что заставляет его включать в первую очередь.

Кто-нибудь испытал это при использовании импорта пакета python, который является git-репо? Ничто не изменилось в наших файлах setup.py для каждого проекта, а commonLib всегда был git-репо. Поэтому единственное, что я могу представить как переменную, – это версия py2app и ее deps, которые, очевидно, были обновлены с течением времени.

редактировать

Я использую последний py2app 0.6.4 на данный момент. Кроме того, моя setup.py была сначала сгенерирована из py2applet некоторое время назад, но была настроена вручную и скопирована как шаблон для каждого нового проекта. Я использую PyQt4 / sip для каждого из этих проектов, так что это также заставляет меня задаться вопросом, есть ли проблема с одним из рецептов?

Обновить

С первого ответа я попытался исправить это, используя различные комбинации параметров exclude_package_data . Кажется, что ничто не заставляет каталог .git исключаться. Ниже приведен пример того, как выглядят мои файлы setup.py:

 from setuptools import setup from myApp import VERSION appname = 'MyApp' APP = ['myApp.py'] DATA_FILES = [] OPTIONS = { 'includes': 'atexit, sip, PyQt4.QtCore, PyQt4.QtGui', 'strip': True, 'iconfile':'ui/myApp.icns', 'resources':['src/myApp.png'], 'plist':{ 'CFBundleIconFile':'ui/myApp.icns', 'CFBundleIdentifier':'com.company.myApp', 'CFBundleGetInfoString': appname, 'CFBundleVersion' : VERSION, 'CFBundleShortVersionString' : VERSION } } setup( app=APP, data_files=DATA_FILES, options={'py2app': OPTIONS}, setup_requires=['py2app'], ) 

Я пробовал такие вещи, как:

 setup( ... exclude_package_data = { 'commonLib': ['.git'] }, #exclude_package_data = { '': ['.git'] }, #exclude_package_data = { 'commonLib/.git/': ['*'] }, #exclude_package_data = { '.git': ['*'] }, ... ) 

Обновление # 2

Я опубликовал свой собственный ответ, который делает monkeypatch на distutils. Его уродливые и не предпочтительные, но пока кто-то не может предложить мне лучшее решение, я думаю, это то, что у меня есть.

4 Solutions collect form web for “py2app подбирает .git subdir пакета во время сборки”

Я добавляю ответ на свой вопрос, чтобы документировать единственное, что я нашел, чтобы работать до сих пор. Мой подход состоял в том, чтобы обезопасить distutils, чтобы игнорировать определенные шаблоны при создании каталога или копировании файла. Это действительно не то, что я хотел сделать, но, как я уже сказал, это единственное, что работает до сих пор.

 ## setup.py ## import re # file_util has to come first because dir_util uses it from distutils import file_util, dir_util def wrapper(fn): def wrapped(src, *args, **kwargs): if not re.search(r'/\.git/?', src): fn(src, *args, **kwargs) return wrapped file_util.copy_file = wrapper(file_util.copy_file) dir_util.mkpath = wrapper(dir_util.mkpath) # now import setuptools so it uses the monkeypatched methods from setuptools import setup 

Надеюсь, кто-то прокомментирует это и скажет мне более высокий уровень, чтобы избежать этого. Но на данный момент я, вероятно, включу это в метод утилиты, такой как exclude_data_patterns(re_pattern) который будет использоваться повторно в моих проектах.

Я вижу два варианта исключения каталога .git.

  1. Создайте приложение из «чистой» проверки кода. При развертывании новой версии мы всегда строим свежий svn export на основе тега, чтобы гарантировать, что мы не поднимем ложные изменения / файлы. Вы могли бы попробовать эквивалент здесь – хотя эквивалент git кажется несколько более привлекательным .

  2. Измените файл setup.py чтобы массировать файлы, включенные в приложение. Это можно сделать с помощью функции exclude_package_data как описано в документации , или создать список data_files и передать его в setup .

Что касается того, почему это внезапно началось, знание версии py2app, которую вы используете, может помочь, так как будет знать содержимое вашего setup.py и, возможно, как это было сделано (вручную или с помощью py2applet).

У меня есть аналогичный опыт с Pyinstaller, поэтому я не уверен, что он применяется напрямую.

Pyinstaller создает «манифест» всех файлов, которые должны быть включены в дистрибутив, перед запуском процесса экспорта. Вы можете «массировать» этот манифест, как и второе предложение Марка, исключить любые файлы, которые вы хотите. Включая что-либо внутри .git или .git.

В конце концов, я застрял с проверкой моего кода перед созданием двоичного файла, поскольку было больше, чем просто. Git bloat (например, UML-документы и исходные файлы ресурсов для Qt). Касса гарантировала чистый результат, и я не испытывал проблем с автоматизацией этого процесса вместе с процессом создания установщика для двоичного файла.

На это есть хороший ответ, но у меня есть более сложный ответ, чтобы решить проблему, упомянутую здесь, с использованием подхода «белого списка». Чтобы патч обезьяны также работал для пакетов за пределами site-packages.zip мне приходилось использовать патч monkey также copy_tree (поскольку он импортирует copy_file внутри его функции), это помогает сделать автономное приложение.

Кроме того, я создаю рецепт белого списка для маркировки определенных пакетов zip-unsafe. Подход упрощает добавление фильтров, отличных от белого списка.

 import pkgutil from os.path import join, dirname, realpath from distutils import log # file_util has to come first because dir_util uses it from distutils import file_util, dir_util # noinspection PyUnresolvedReferences from py2app import util def keep_only_filter(base_mod, sub_mods): prefix = join(realpath(dirname(base_mod.filename)), '') all_prefix = [join(prefix, sm) for sm in sub_mods] log.info("Set filter for prefix %s" % prefix) def wrapped(mod): name = getattr(mod, 'filename', None) if name is None: # ignore anything that does not have file name return True name = join(realpath(dirname(name)), '') if not name.startswith(prefix): # ignore those that are not in this prefix return True for p in all_prefix: if name.startswith(p): return True # log.info('ignoring %s' % name) return False return wrapped # define all the filters we need all_filts = { 'mypackage': (keep_only_filter, [ 'subpackage1', 'subpackage2', ]), } def keep_only_wrapper(fn, is_dir=False): filts = [(f, k[1]) for (f, k) in all_filts.iteritems() if k[0] == keep_only_filter] prefixes = {} for f, sms in filts: pkg = pkgutil.get_loader(f) assert pkg, '{f} package not found'.format(f=f) p = join(pkg.filename, '') sp = [join(p, sm, '') for sm in sms] prefixes[p] = sp def wrapped(src, *args, **kwargs): name = src if not is_dir: name = dirname(src) name = join(realpath(name), '') keep = True for prefix, sub_prefixes in prefixes.iteritems(): if name == prefix: # let the root pass continue # if it is a package we have a filter for if name.startswith(prefix): keep = False for sub_prefix in sub_prefixes: if name.startswith(sub_prefix): keep = True break if keep: return fn(src, *args, **kwargs) return [] return wrapped file_util.copy_file = keep_only_wrapper(file_util.copy_file) dir_util.mkpath = keep_only_wrapper(dir_util.mkpath, is_dir=True) util.copy_tree = keep_only_wrapper(util.copy_tree, is_dir=True) class ZipUnsafe(object): def __init__(self, _module, _filt): self.module = _module self.filt = _filt def check(self, dist, mf): m = mf.findNode(self.module) if m is None: return None # Do not put this package in site-packages.zip if self.filt: return dict( packages=[self.module], filters=[self.filt[0](m, self.filt[1])], ) return dict( packages=[self.module] ) # Any package that is zip-unsafe (uses __file__ ,... ) should be added here # noinspection PyUnresolvedReferences import py2app.recipes for module in [ 'sklearn', 'mypackage', ]: filt = all_filts.get(module) setattr(py2app.recipes, module, ZipUnsafe(module, filt)) 
  • 'pip3 --version' не работает с SyntaxError
  • Как я могу заставить setuptools установить пакет, который не находится в PyPI?
  • setup.py: запустите build_ext перед чем-либо еще
  • Создайте различные типы рассылки с помощью setup.py
  • Утилита устаревания FORTRAN в Python. Можно ли использовать `setuptools` и` numpy.distutils`?
  • Яйца в пути до переменной окружения PYTHONPATH
  • Каков правильный способ установки pip, virtualenv и дистрибутива для Python?
  • Проект Python с использованием буферов протокола, проблемы с развертыванием
  • Как я могу перемещать глобальные пакеты в виртуальную среду после создания виртуальной среды?
  • добавлять файлы данных в проекты python setup.py
  • Создайте сценарий графического интерфейса запуска из Python setuptools (без консольного окна!)
  •  
    Interesting Posts for Van-Lav

    как использовать цикл для получения значений в каталоге dir ()?

    Как вернуться из функции, если она застряла на 90 секунд?

    Есть ли выигрыш в производительности от определения маршрутов в app.yaml по сравнению с одним большим отображением в WSGIApplication в AppEngine?

    python virtualenv: почему я могу импортировать старые модули в clean / new virtualenv

    Заполните нулевые значения массива 1d numpy с последними ненулевыми значениями

    Могу ли я иметь subprocess.call записать вывод вызова в строку?

    Несколько доменов и многопользовательский каталог в том же приложении Django

    Как запустить кусок кода во всех представлениях в django?

    Почему эти два выражения python дают разные результаты?

    uWSGI для загрузки и обработки файлов

    Эффективное чтение XML-файла объемом 800 ГБ в Python 2.7

    Как установить SciPy на 64-битную Windows?

    Как открыть несколько файлов с помощью «с открытым» в Python?

    .doc в pdf с использованием python

    как создать локальный собственный индекс pypi-репозитория без зеркала?

    Python - лучший язык программирования в мире.