Inclusion de fichiers non Python avec setup.py

200

Comment puis-je faire setup.pyinclure un fichier qui ne fait pas partie du code? (Plus précisément, c'est un fichier de licence, mais cela pourrait être autre chose.)

Je veux pouvoir contrôler l'emplacement du fichier. Dans le dossier source d'origine, le fichier se trouve à la racine du package. (c'est-à-dire au même niveau que le plus haut __init__.py.) Je veux qu'il reste exactement là lorsque le package est installé, quel que soit le système d'exploitation. Comment je fais ça?

Ram Rachum
la source
comment tu fais ça en ce moment? votre question précédente indique que vous savez comment ajouter le fichier de licence, alors quel est votre code qui "ne fonctionne pas"?
SilentGhost
2
data_files = [('', ['lgpl2.1_license.txt',]),]le place dans le dossier Python26.
Ram Rachum
Après quelques commentaires négatifs, j'ai relu votre question et réalisé ce qui me manquait. J'ai mis à jour ma réponse pour fournir une solution non piratée à votre question qui ne nécessite aucun module supplémentaire (comme setuptools ou distribuer).
Evan Plaice
Merci Evan. Cependant, je suis parfaitement d'accord avec l'utilisation de setuptools, car c'est si répandu.
Ram Rachum

Réponses:

224

La meilleure façon de procéder est probablement d'utiliser la setuptools package_datadirective. Cela signifie utiliser setuptools(ou distribute) au lieu de distutils, mais il s'agit d'une "mise à niveau" très transparente.

Voici un exemple complet (mais non testé):

from setuptools import setup, find_packages

setup(
    name='your_project_name',
    version='0.1',
    description='A description.',
    packages=find_packages(exclude=['ez_setup', 'tests', 'tests.*']),
    package_data={'': ['license.txt']},
    include_package_data=True,
    install_requires=[],
)

Notez les lignes spécifiques qui sont essentielles ici:

package_data={'': ['license.txt']},
include_package_data=True,

package_dataest un nom dictde package (vide = tous les packages) à une liste de modèles (peut inclure des globes). Par exemple, si vous souhaitez uniquement spécifier des fichiers dans votre package, vous pouvez également le faire:

package_data={'yourpackage': ['*.txt', 'path/to/resources/*.txt']}

La solution ici est définitivement de ne pas renommer vos non- pyfichiers avec une .pyextension.

Voir la présentation de Ian Bicking pour plus d'informations.

MISE À JOUR: Une autre [meilleure] approche

Une autre approche qui fonctionne bien si vous souhaitez simplement contrôler le contenu de la distribution source ( sdist) et avoir des fichiers en dehors du package (par exemple, le répertoire de niveau supérieur) consiste à ajouter un MANIFEST.infichier. Voir la documentation Python pour le format de ce fichier.

Depuis la rédaction de cette réponse, j'ai constaté que l'utilisation MANIFEST.inest généralement une approche moins frustrante pour s'assurer que votre distribution source ( tar.gz) possède les fichiers dont vous avez besoin.

Par exemple, si vous souhaitez inclure le à requirements.txtpartir du niveau supérieur, incluez récursivement le répertoire "données" de niveau supérieur:

include requirements.txt
recursive-include data *

Néanmoins, pour que ces fichiers soient copiés au moment de l'installation dans le dossier du package à l'intérieur de site-packages, vous devrez fournir include_package_data=Truela setup()fonction. Voir Ajout de fichiers non codés pour plus d'informations.

Hans L
la source
5
package_data est également disponible pour les scripts de configuration pure distutils depuis Python 2.3.
Éric Araujo
15
Cette réponse semble raisonnable, mais ne fonctionne pas pour moi. Étant donné que package_data est notoirement peu fiable (nécessite la coordination de MANIFEST.in et setup.py pour ajouter des fichiers à sdist et les installer, en tant qu'étapes distinctes) et l'auteur de cette réponse note qu'il "n'est pas testé", n'importe qui peut d'autre confirmer si cela fonctionne pour eux? Mon fichier de LICENCE est inclus dans le sdist, mais n'est pas installé lorsque j'exécute "python setup.py install" ni "pip install Package"
Jonathan Hartley
11
La présentation de Ian Bicking montre uniquement comment installer les données de package pour les fichiers qui se trouvent dans un package. Mon fichier de LICENCE est au plus haut niveau de mon projet, c'est-à-dire pas dans aucun package. Puis-je toujours utiliser package_data? L'utilisation de fichiers_données n'est pas un démarreur, car elle place les fichiers dans un emplacement à l'échelle du système. pas associé à mon projet, et pour aggraver, l'emplacement change selon que j'exécute "setup.py install" ou "pip install", à partir du même sdist.
Jonathan Hartley
8
Je suppose que la raison pour laquelle cela ne fonctionne pas pour moi est que le fichier ne se trouve dans aucun package - c'est un fichier de LICENCE au niveau supérieur du référentiel, et ne peut donc pas être installé en utilisant 'package_data'
Jonathan Hartley
7
Cette réponse ne fonctionne pas pour moi. Les fichiers supplémentaires ne sont pas mis dans l'archive tar ...
lpapp
44

Pour accomplir ce que vous décrivez, il faudra deux étapes ...

  • Le fichier doit être ajouté à l'archive tar source
  • setup.py doit être modifié pour installer le fichier de données sur le chemin source

Étape 1: pour ajouter le fichier à l'archive tar source, incluez-le dans le MANIFEST

Créez un modèle MANIFEST dans le dossier qui contient setup.py

Le MANIFEST est essentiellement un fichier texte avec une liste de tous les fichiers qui seront inclus dans l'archive tar source.

Voici à quoi ressemble le MANIFEST pour mon projet:

  • CHANGELOG.txt
  • INSTALL.txt
  • LICENSE.txt
  • pypreprocessor.py
  • README.txt
  • setup.py
  • test.py
  • TODO.txt

Note: Bien que sdist n'ajouter quelques fichiers automatiquement , je préfère les spécifier explicitement pour être sûr au lieu de prédire ce qu'il fait et ne fonctionne pas.

Étape 2: pour installer le fichier de données dans le dossier source, modifiez setup.py

Puisque vous cherchez à ajouter un fichier de données (LICENSE.txt) au dossier d'installation source, vous devez modifier le chemin d'installation des données pour qu'il corresponde au chemin d'installation source. Cela est nécessaire car, par défaut, les fichiers de données sont installés à un emplacement différent de celui des fichiers source.

Pour modifier le répertoire d'installation des données afin qu'il corresponde au répertoire d'installation source ...

Tirez les informations du répertoire d'installation de distutils avec:

from distutils.command.install import INSTALL_SCHEMES

Modifiez le répertoire d'installation des données pour qu'il corresponde au répertoire d'installation source:

for scheme in INSTALL_SCHEMES.values():
    scheme['data'] = scheme['purelib']

Et, ajoutez le fichier de données et l'emplacement à setup ():

data_files=[('', ['LICENSE.txt'])]

Remarque: Les étapes ci-dessus doivent accomplir exactement ce que vous avez décrit de manière standard sans nécessiter de bibliothèques d'extension.

Plie d'Evan
la source
10
MANIFEST ne contrôle que les fichiers inclus dans l'archive tar source (produit par sdist). Les fichiers qui y sont répertoriés ne seront pas installés.
David Cournapeau
@David Je ne savais pas à quel point j'étais loin dans ma première approche. J'ai mis à jour la réponse pour être correcte afin d'accomplir ce que la question demandait sans nécessiter de bibliothèques tierces supplémentaires.
Evan Plaice
3
@ Éric Une raison particulière pourquoi? et, avez-vous une alternative d'installation viable qui ne nécessite pas de packages tiers (comme setup_tools) pour fonctionner. J'ai choisi distutils plutôt que setuptools car il est inclus avec une installation vanilla de python et je construisais des modules pour PYPI. Il devrait y avoir une meilleure façon de le faire maintenant en utilisant distutils2 mais je n'ai pas touché au python depuis un bon moment donc je ne saurais pas comment. Puisque vous semblez bien connaître les distutils2, je pense qu'il serait avantageux pour le reste d'entre nous d'avoir une alternative appropriée aux distutils2.
Evan Plaice
6
Comme cela a été mentionné dans d'autres threads package_datane fonctionne pas si le fichier n'est pas dans le package.
Gringo Suave
2
@ ÉricAraujo: Ce n'est pas une mauvaise idée d'utiliser cette solution car il n'y a pas d'autre moyen. C'est une mauvaise conception de distutils - c'est vrai. Mais c'est de facto une API publique qui ne changera jamais, car elle cassera beaucoup de choses. Espérons que distutils2 fournira de meilleures méthodes recommandées.
anatoly techtonik
15

créer MANIFEST.inà la racine du projet avec recursive-includele répertoire requis ou includeavec le nom du fichier.

include LICENSE
include README.rst
recursive-include package/static *
recursive-include package/templates *

la documentation peut être trouvée ici

Tout est possible
la source
7

Je voulais poster un commentaire sur l'une des questions mais je n'ai pas assez de réputation pour le faire>.>

Voici ce qui a fonctionné pour moi (je l'ai trouvé après avoir référé les documents):

package_data={
    'mypkg': ['../*.txt']
},

include_package_data: False

La dernière ligne était, étrangement, également cruciale pour moi (vous pouvez également omettre cet argument de mot clé - cela fonctionne de la même manière).

Ce qu'il fait, c'est qu'il copie tous les fichiers texte dans votre répertoire racine ou racine (un niveau au-dessus du package que mypkgvous souhaitez distribuer).

J'espère que cela t'aides!

rv.kvetch
la source
Je cherchais un moyen de ne pas avoir à créer un MANIFEST.in, cela a fonctionné pour moi. La dernière ligne a également été cruciale pour moi. Mes lignes étaientinclude_package_data=False, package_data={ "": ["../CHANGELOG.md"] },
Mendhak
7

Nous sommes en 2019, et voici ce qui fonctionne - malgré les conseils ici et là, ce que j'ai trouvé sur Internet à mi-chemin documenté utilise setuptools_scm, transmis en tant qu'options setuptools.setup. Cela inclura tous les fichiers de données qui sont versionnés sur votre VCS, que ce soit git ou tout autre, dans le paquetage wheel, et fera "pip install" à partir du référentiel git pour apporter ces fichiers.

Donc, je viens d'ajouter ces deux lignes à l'appel de configuration sur "setup.py". Aucune installation supplémentaire ou importation requise:

    setup_requires=['setuptools_scm'],
    include_package_data=True,

Pas besoin de lister manuellement package_data, ou dans un fichier MANIFEST.in - s'il est versionné, il est inclus dans le package. Les documents sur "setuptools_scm" mettent l'accent sur la création d'un numéro de version à partir de la position de validation, et ignorent la partie vraiment importante de l'ajout des fichiers de données. (Je m'en fiche si mon fichier de roue intermédiaire est nommé "* 0.2.2.dev45 + g3495a1f" ou utilisera le numéro de version codé en dur "0.3.0dev0" que j'ai tapé - mais en laissant des fichiers cruciaux au programme pour travailler derrière est quelque peu important)

jsbueno
la source
7

Étape 1: créez un MANIFEST.infichier dans le même dossier avec setup.py

Étape 2: inclure le chemin d'accès relatif aux fichiers que vous souhaitez ajouterMANIFEST.in

include README.rst
include docs/*.txt
include funniest/data.json

Étape 3: définissez include_package_data=Truela setup()fonction pour copier ces fichiers dans site-package

La référence est ici.

debuglife
la source
5

Dans setup.py sous setup (:

setup(
   name = 'foo library'
   ...
  package_data={
   'foolibrary.folderA': ['*'],     # All files from folder A
   'foolibrary.folderB': ['*.txt']  #All text files from folder B
   },
Dashing Adam Hughes
la source
1
Cela ne fait en fait rien pour atteindre l'objectif du PO. Tout ce que vous écrivez package_datan'aura aucune influence sur ce qui se setup.py installpasse, sauf si vous modifiez la commande d'installation elle-même. À moins que ces fichiers ne se trouvent dans le répertoire du package, ce que vous souhaitez généralement éviter.
wvxvw
3

Voici une réponse plus simple qui a fonctionné pour moi.

Tout d'abord, selon le commentaire d'un développeur Python ci-dessus, setuptools n'est pas requis:

package_data is also available to pure distutils setup scripts 
since 2.3.  Éric Araujo

C'est génial car mettre une exigence setuptools sur votre package signifie que vous devrez également l'installer. En bref:

from distutils.core import setup

setup(
    # ...snip...
    packages          = ['pkgname'],
    package_data      = {'pkgname': ['license.txt']},
)
Gringo Suave
la source
1
Il se plaindra que le répertoire pkgamen'existe pas
Anthony Kong
1

Je voulais juste faire un suivi sur quelque chose que j'ai trouvé en travaillant avec Python 2.7 sur Centos 6. L'ajout des packages_data ou data_files comme mentionné ci-dessus n'a pas fonctionné pour moi. J'ai ajouté un MANIFEST.IN avec les fichiers que je voulais qui mettaient les fichiers non-python dans l'archive tar, mais je ne les ai pas installés sur la machine cible via RPM.

Au final, j'ai pu récupérer les fichiers dans ma solution en utilisant les "options" du setup / setuptools. Les fichiers d'options vous permettent de modifier différentes sections du fichier de spécifications à partir de setup.py. Comme suit.

from setuptools import setup


setup(
    name='theProjectName',
    version='1',
    packages=['thePackage'],
    url='',
    license='',
    author='me',
    author_email='[email protected]',
    description='',
    options={'bdist_rpm': {'install_script': 'filewithinstallcommands'}},
)

fichier - MANIFEST.in:

include license.txt

file - filewithinstallcommands:

mkdir -p $RPM_BUILD_ROOT/pathtoinstall/
#this line installs your python files
python setup.py install -O1 --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES
#install license.txt into /pathtoinstall folder
install -m 700 license.txt $RPM_BUILD_ROOT/pathtoinstall/
echo /pathtoinstall/license.txt >> INSTALLED_FILES
Scott Bowers
la source
-12

J'ai trouvé une solution de contournement: j'ai renommé mon lgpl2.1_license.txten lgpl2.1_license.txt.pyet j'ai mis des triples guillemets autour du texte. Maintenant, je n'ai plus besoin d'utiliser l' data_filesoption ni de spécifier de chemins absolus. En faire un module Python est moche, je sais, mais je le considère moins moche que de spécifier des chemins absolus.

Ram Rachum
la source
7
Voir mon post. Ça n'a pas à être moche. Il est juste difficile de trouver un bon exemple sur le net car une bonne documentation pour configurer les packages est difficile à trouver.
Evan Plaice