Как добавить данные пакета рекурсивно в Python setup.py?
У меня есть новая библиотека, которая должна включать множество подпапок небольших файлов данных, и я пытаюсь добавить их в качестве данных пакета. Представь, что у меня есть моя библиотека так:
library
- foo.py
- bar.py
data
subfolderA
subfolderA1
subfolderA2
subfolderB
subfolderB1
...
Я хочу добавить все данные во все подпапки через setup.py, но мне кажется, что мне нужно вручную войти в каждую подпапку (их около 100) и добавить файл инициализации.py. Кроме того, setup.py найдет эти файлы рекурсивно или мне нужно вручную добавить все эти файлы в setup.py, например:
package_data={
'mypackage.data.folderA': ['*'],
'mypackage.data.folderA.subfolderA1': ['*'],
'mypackage.data.folderA.subfolderA2': ['*']
},
Я могу сделать это с помощью сценария, но, похоже, супер боль. Как я могу добиться этого в setup.py?
PS иерархия этих папок важна, потому что это база данных файлов материалов, и мы хотим, чтобы дерево файлов было сохранено, когда мы представляем их в графическом интерфейсе пользователю, поэтому было бы в наших интересах сохранить целостность этой файловой структуры.,
Ответы
Ответ 1
- Используйте Setuptools вместо distutils.
- Используйте файлы данных вместо данных пакета. Это не требует
__init__.py
. -
Создайте списки файлов и каталогов, используя стандартный код Python, вместо того, чтобы писать его буквально:
data_files = []
directories = glob.glob('data/subfolder?/subfolder??/')
for directory in directories:
files = glob.glob(directory+'*')
data_files.append((directory, files))
# then pass data_files to setup()
Ответ 2
Проблема с ответом glob
заключается в том, что он делает это только так. То есть он не является полностью рекурсивным. Проблема с ответом copy_tree
заключается в том, что файлы, которые будут скопированы, будут оставлены при удалении.
Правильное решение является рекурсивным, что позволит вам установить параметр package_data
в установочном вызове.
Я написал этот небольшой метод для этого:
import os
def package_files(directory):
paths = []
for (path, directories, filenames) in os.walk(directory):
for filename in filenames:
paths.append(os.path.join('..', path, filename))
return paths
extra_files = package_files('path_to/extra_files_dir')
setup(
...
packages = ['package_name'],
package_data={'': extra_files},
....
)
Вы заметите, что когда вы делаете pip uninstall package_name
, вы увидите, что ваши дополнительные файлы перечислены (как отслеживается с пакетом).
Ответ 3
Если у вас нет проблем с получением вашего файла setup.py dirty use distutils.dir_util.copy_tree
.
Вся проблема заключается в том, как исключить из нее файлы.
Вот код:
import os.path
from distutils import dir_util
from distutils import sysconfig
from distutils.core import setup
__packagename__ = 'x'
setup(
name = __packagename__,
packages = [__packagename__],
)
destination_path = sysconfig.get_python_lib()
package_path = os.path.join(destination_path, __packagename__)
dir_util.copy_tree(__packagename__, package_path, update=1, preserve_mode=0)
Некоторые заметки:
Этот код рекурсивно копирует исходный код в путь назначения.
Вы можете просто использовать те же setup(...)
, но использовать copy_tree()
, чтобы расширить каталог, который вы хотите, на путь установки.
Пути установки distutil по умолчанию можно найти в нем API.
Подробнее о copy_tree() модуле distutils можно найти здесь.
Ответ 4
Я могу предложить небольшой код для добавления data_files в setup():
data_files = []
start_point = os.path.join(__pkgname__, 'static')
for root, dirs, files in os.walk(start_point):
root_files = [os.path.join(root, i) for i in files]
data_files.append((root, root_files))
start_point = os.path.join(__pkgname__, 'templates')
for root, dirs, files in os.walk(start_point):
root_files = [os.path.join(root, i) for i in files]
data_files.append((root, root_files))
setup(
name = __pkgname__,
description = __description__,
version = __version__,
long_description = README,
...
data_files = data_files,
)
Ответ 5
Используйте glob, чтобы выбрать все подпапки в вашем setup.py
...
packages=['your_package'],
package_data={'your_package': ['data/**/*']},
...
Ответ 6
Мне удалось скопировать файлы с помощью регулярных выражений:
package_data={'example', ['assets/**/*']}