Comment inclure des données de package avec setuptools / distribuer?

136

Lorsque j'utilise setuptools / distribuer, je ne peux pas demander au programme d'installation d'extraire des package_datafichiers. Tout ce que j'ai lu dit que ce qui suit est la bonne façon de le faire. Quelqu'un peut-il conseiller s'il vous plaît?

setup(
   name='myapp',
   packages=find_packages(),
   package_data={
      'myapp': ['data/*.txt'],
   },
   include_package_data=True,
   zip_safe=False,
   install_requires=['distribute'],
)

myapp/data/est l'emplacement des fichiers de données.

cmcginty
la source
2
J'ai le même problème ... Spécifier manuellement a data_filesrésolu le problème. Mais cela est sujet aux erreurs et ne me semble pas «juste». Quelqu'un peut-il vérifier qu'il est vraiment nécessaire de dupliquer la configuration dans les deux package_dataet data_files?
exhuma
github.com/wimglenn/resources-example Affiche une structure de projet setuptools moderne, qui peut correctement empaqueter des fichiers de données dans des roues et des sdists en utilisant pyproject.toml. Aucun setup.pyfichier requis.
wim

Réponses:

289

Je me rends compte que c'est une vieille question, mais pour les gens qui trouvent leur chemin ici via Google: package_datac'est un mensonge bas et sale . Il n'est utilisé que lors de la construction de packages binaires ( python setup.py bdist ...) mais pas lors de la construction de packages sources ( python setup.py sdist ...). C'est, bien sûr, ridicule - on s'attendrait à ce que la construction d'une distribution source aboutisse à une collection de fichiers qui pourraient être envoyés à quelqu'un d'autre pour construire la distribution binaire.

Dans tous les cas, l'utilisation MANIFEST.infonctionnera à la fois pour les distributions binaires et sources.

larsks
la source
97
Je fais des recherches sur cette question depuis une heure et j'essaie de nombreuses approches. Comme vous le dites, ça package_datamarche pour bdistet pas sdist. Cependant , MANIFEST.infonctionne pour sdist, mais pas pour bdist! Par conséquent, le mieux que j'ai pu trouver est d'inclure à la fois package_dataet MANIFEST.inafin de tenir compte des deux bdistet sdist.
Wesley Baugh
7
J'en ai trouvé un autre pour soutenir @WesleyBaugh. Dans stackoverflow.com/a/2969087/261718 , utilisez MANIFEST.inpour les fichiers que vous n'installez pas, comme la documentation, et package_datapour les fichiers que vous utilisez qui ne sont pas du code Python (comme une image ou un modèle).
Drake Guan
12
J'utilise sdist et j'ai dû inclure à la fois MANIFEST.in et package_data . Il semble que MANIFEST.incontrôle ce qui est inclus dans la distribution, et package_data contrôle ce qui est ensuite copié dans le répertoire site_packages lors de l'installation. De manière confuse, les chemins dans MANIFEST.insont relatifs à l'emplacement de setup.py, et package_datasont relatifs à la racine des packages individuels (par exemple, les modules).
Edward Newell
9
"Modifié dans la version 2.7: Tous les fichiers qui correspondent à package_data seront ajoutés au fichier MANIFEST si aucun modèle n'est fourni. Voir Spécifier les fichiers à distribuer." de distutils . Ainsi, vous ne verrez le comportement des fichiers en package_dataétant automatiquement inclus dans le ZIP que si vous n'avez pas de fichier MANIFEST.in existant , et seulement si vous utilisez 2.7+.
Johnus
29
Sérieusement, j'ai l'impression que ce billet est une séance de thérapie de groupe pour les gens qui utilisent setuptools et découvrent à quel point horrible ils se sont trouvés dans la vie.
Matt Joyce
32

J'ai juste eu ce même problème. La solution, était simplement de supprimer include_package_data=True.

Après avoir lu ici , j'ai réalisé que l' include_package_dataobjectif était d'inclure des fichiers à partir du contrôle de version , par opposition à simplement "inclure des données de package" comme son nom l'indique. À partir de la documentation:

Les fichiers de données [de include_package_data] doivent être sous contrôle CVS ou Subversion

...

Si vous souhaitez un contrôle plus fin sur les fichiers inclus (par exemple, si vous avez des fichiers de documentation dans les répertoires de vos packages et que vous souhaitez les exclure de l'installation), vous pouvez également utiliser le package_datamot - clé.

Retirer cet argument l'a corrigé, ce qui est par coïncidence pourquoi cela a également fonctionné lorsque vous êtes passé à distutils, car il ne prend pas cet argument.

Joe
la source
2
Mon expérience diffère, j'ai eu le même problème sans inclure l' include_package_data=Trueentrée. La seule solution pour moi est d'ajouter une entrée dans le manifeste comme suggéré ci-dessus. Remarquez que j'utilisais setuptools, peut-être que votre version fonctionne avec «distribuer»?
TimStaley
4
La raison réelle pour laquelle la suppression include_package_datarésout le problème se trouve plus loin dans le texte d'origine - Si vous utilisez l' include_package_dataargument spécifique à setuptools , les fichiers spécifiés par package_datane seront pas automatiquement ajoutés au manifeste à moins qu'ils ne soient répertoriés dans le MANIFEST.infichier.
Piotr Dobrogost le
Quel est le cas d'utilisation d'avoir package_datadéfini une liste non vide et de spécifier include_package_data=False? Et pourquoi auriez-vous besoin de spécifier des fichiers deux fois dans MANIFEST.inet package_data?
Herbert
21

Suivre la recommandation de @Joe de supprimer la include_package_data=Trueligne a également fonctionné pour moi.

Pour élaborer un peu plus, je n'ai pas de MANIFEST.in dossier. J'utilise Git et non CVS.

Le référentiel prend ce genre de forme:

/myrepo
    - .git/
    - setup.py
    - myproject
        - __init__.py
        - some_mod
            - __init__.py
            - animals.py
            - rocks.py
        - config
            - __init__.py
            - settings.py
            - other_settings.special
            - cool.huh
            - other_settings.xml
        - words
            - __init__.py
            word_set.txt

setup.py:

from setuptools import setup, find_packages
import os.path

setup (
    name='myproject',
    version = "4.19",
    packages = find_packages(),  
    # package_dir={'mypkg': 'src/mypkg'},  # didnt use this.
    package_data = {
        # If any package contains *.txt or *.rst files, include them:
        '': ['*.txt', '*.xml', '*.special', '*.huh'],
    },

#
    # Oddly enough, include_package_data=True prevented package_data from working.
    # include_package_data=True, # Commented out.
    data_files=[
#               ('bitmaps', ['bm/b1.gif', 'bm/b2.gif']),
        ('/opt/local/myproject/etc', ['myproject/config/settings.py', 'myproject/config/other_settings.special']),
        ('/opt/local/myproject/etc', [os.path.join('myproject/config', 'cool.huh')]),
#
        ('/opt/local/myproject/etc', [os.path.join('myproject/config', 'other_settings.xml')]),
        ('/opt/local/myproject/data', [os.path.join('myproject/words', 'word_set.txt')]),
    ],

    install_requires=[ 'jsonschema',
        'logging', ],

     entry_points = {
        'console_scripts': [
            # Blah...
        ], },
)

Je cours python setup.py sdistpour une distribution source (je n'ai pas essayé le binaire).

Et quand à l'intérieur d'un tout nouvel environnement virtuel, j'ai un myproject-4.19.tar.gzfichier et j'utilise

(venv) pip install ~/myproject-4.19.tar.gz
...

Et à part tout ce qui est installé dans mon environnement virtuel site-packages, ces fichiers de données spéciaux sont installés sur /opt/local/myproject/dataet /opt/local/myproject/etc.

HeyWatchThis
la source
16

include_package_data=True travaillé pour moi.

Si vous utilisez git, n'oubliez pas d'inclure setuptools-gitdans install_requires. Beaucoup moins ennuyeux que d'avoir un Manifestchemin ou d'inclure tous les chemins package_data(dans mon cas, c'est une application django avec toutes sortes de statiques)

(collé le commentaire que j'ai fait, comme k3-rnc l'a mentionné, il est en fait utile tel quel)

Vincent
la source
7

Mise à jour : Cette réponse est ancienne et les informations ne sont plus valides. Toutes les configurations setup.py doivent utiliser import setuptools. J'ai ajouté une réponse plus complète sur https://stackoverflow.com/a/49501350/64313


J'ai résolu ce problème en passant aux distutils. On dirait que distribuer est obsolète et / ou cassé.

from distutils.core import setup

setup(
   name='myapp',
   packages=['myapp'],
   package_data={
      'myapp': ['data/*.txt'],
   },
)
cmcginty
la source
2
distribuer n'est pas obsolète, il remplace distutils. Je ne sais pas pourquoi vous aviez le problème, mais ce n'est pas la raison.
agf
1
C'est la réponse que j'ai reçue de l'IRC, alors qui dois-je croire? Si vous avez un exemple de travail utilisant distribuer, je vous serais reconnaissant alors.
cmcginty
6
clarification: distribuer est destiné à remplacer setuptools, les deux sont construits sur distutils. distutils lui-même sera éventuellement remplacé par un nouveau package, appelé "distutils2" en python2 et "packaging" en python3
Kevin Horn
1
Le passage aux distutils a résolu mon problème où il include_package_data=Truen'était pas honoré. Donc, avec ce paramètre, vous n'avez besoin que de MANIFEST.in - pas besoin de dupliquer votre liste de fichiers dans le package_dataparamètre.
Daniel Sokolowski
4

Ancienne question et pourtant ... la gestion des paquets de python laisse vraiment à désirer. J'ai donc eu le cas d'utilisation de l'installation en utilisant pip localement dans un répertoire spécifié et j'ai été surpris que les chemins package_data et data_files ne fonctionnent pas. Je n'avais pas envie d'ajouter encore un autre fichier au référentiel, alors j'ai fini par tirer parti de l'option data_files et setup.py --install-data; quelque chose comme ça

pip install . --install-option="--install-data=$PWD/package" -t package  
Mat Baker
la source
3

J'ai eu le même problème pendant quelques jours, mais même ce fil n'a pas pu m'aider car tout était déroutant. J'ai donc fait mes recherches et trouvé la solution suivante:

Fondamentalement, dans ce cas, vous devez faire:

from setuptools import setup

setup(
   name='myapp',
   packages=['myapp'],
   package_dir={'myapp':'myapp'}, # the one line where all the magic happens
   package_data={
      'myapp': ['data/*.txt'],
   },
)

L'autre réponse complète de stackoverflow ici

moctarjallo
la source
J'ai essayé cela, mais rien n'est copié.
gerrit
3

Supprimez simplement la ligne:

include_package_data=True,

à partir de votre script de configuration, et cela fonctionnera très bien. (Testé tout à l'heure avec les derniers setuptools.)

Ian
la source
C'est fou mais ça marche à la fois avec sdistet bdist_wheel, avez-vous vérifié pourquoi?
Szabolcs
1
Je peux en effet confirmer que cela sdistignore package_dataquand cela est défini.
Sander Steffann le
À ce stade, cela fait des mois, mais il me semble que je me souviens d'avoir fouillé dans le code, de m'être perdu deux fois, d'avoir pris un peigne EXTRÊMEMENT fin pour la documentation et d'avoir obtenu satisfaction. Apparemment, divers exemples de scripts contiennent cet indicateur et cela ne cause aucun mal de tête.
Ian le
1

Utilisation de setup.cfg (setuptools ≥ 30.3.0)

À partir de setuptools 30.3.0 (publié le 08/12/2016), vous pouvez garder votre setup.pytout petit et déplacer la configuration dans un setup.cfgfichier. Avec cette approche, vous pouvez mettre les données de votre package dans une [options.package_data]section:

[options.package_data]
* = *.txt, *.rst
hello = *.msg

Dans ce cas, votre setup.pypeut être aussi court que:

from setuptools import setup
setup()

Pour plus d'informations, voir la configuration de l'installation à l'aide des fichiers setup.cfg .

On parle de dépréciersetup.cfg en faveur de pyproject.tomlcomme proposé dans la PEP 518 , mais cela reste provisoire à partir du 21/02/2020.

gerrit
la source
Cette réponse néglige de mentionner le fichier MANIFEST donc je pense qu'il ne fonctionnera pas réellement avec les sdists. Uniquement avec roues. Vous devriez le mentionner.
wim
@wim Je n'ai pas assez de compréhension de MANIFEST, sdist et roues pour répondre à cela. Cela a fonctionné pour moi en utilisant pip install.
gerrit le
C'est parce que pip install, pour une version assez moderne de pip, va d'abord construire une roue, puis l'installer. Toujours pour de nombreux utilisateurs, cette approche échouera silencieusement à inclure les données du package. Voir la réponse acceptée et les commentaires en dessous pour plus de détails à ce sujet. L'utilisation de a setup.cfgest vraiment une manière différente d'écrire ce que l'OP faisait déjà setup.pydans la question (en passant l' package_dataargument mot - clé dans l'appel à setup), donc je ne pense pas que cela soit particulièrement utile comme réponse à cette question . Cela ne règle pas du tout le problème sous-jacent.
wim