web-dev-qa-db-fra.com

Comment ajouter des données de package récursivement dans Python setup.py?

J'ai une nouvelle bibliothèque qui doit inclure beaucoup de sous-dossiers de petits fichiers de données, et j'essaie de les ajouter en tant que données de package. Imaginez que j'ai ma bibliothèque comme ça:

 library
    - foo.py
    - bar.py
 data
   subfolderA
      subfolderA1
      subfolderA2
   subfolderB
      subfolderB1 
      ...

Je veux ajouter toutes les données dans tous les sous-dossiers via setup.py, mais il semble que je dois manuellement aller dans chaque sous-dossier (il y en a environ 100) et ajouter un init. fichier py. De plus, setup.py trouvera-t-il ces fichiers de manière récursive, ou dois-je les ajouter manuellement dans setup.py comme:

package_data={
  'mypackage.data.folderA': ['*'],
  'mypackage.data.folderA.subfolderA1': ['*'],
  'mypackage.data.folderA.subfolderA2': ['*']
   },

Je peux le faire avec un script, mais cela semble être une super douleur. Comment puis-je y parvenir dans setup.py?

PS, la hiérarchie de ces dossiers est importante car il s'agit d'une base de données de fichiers de matériaux et nous voulons que l'arborescence de fichiers soit préservée lorsque nous les présentons dans une interface graphique à l'utilisateur, il serait donc à notre avantage de garder cette structure de fichiers intacte .

38
Dashing Adam Hughes
  1. Utilisez Setuptools au lieu de distutils.
  2. Utilisez fichiers de données au lieu des données du package. Ceux-ci ne nécessitent pas __init__.py.
  3. Générez les listes de fichiers et de répertoires en utilisant le code standard Python, au lieu de l'écrire littéralement:

    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()
    
25
Kevin

Le problème avec la réponse glob est qu'elle ne fait pas grand chose. C'est à dire. ce n'est pas entièrement récursif. Le problème avec le copy_tree la réponse est que les fichiers copiés seront laissés pour compte lors d'une désinstallation.

La bonne solution est une solution récursive qui vous permettra de définir le package_data paramètre dans l'appel de configuration.

J'ai écrit cette petite méthode pour ce faire:

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},
    ....
)

Vous remarquerez que lorsque vous effectuez une pip uninstall package_name, que vous verrez vos fichiers supplémentaires répertoriés (comme suivis avec le package).

33
Sandy Chapman

Si vous n'avez aucun problème à obtenir votre code setup.py sale, utilisez distutils.dir_util.copy_tree.
Tout le problème est de savoir comment en exclure des fichiers.
Voici le code:

import os.path
from distutils import dir_util
from distutils import sysconfig
from distutils.core import setup

__package= '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)

Quelques notes :

  • Ce code copie récursivement le code source dans le chemin de destination.
  • Vous pouvez simplement utiliser la même setup(...) mais utilisez copy_tree() pour étendre le répertoire souhaité dans le chemin d'installation.
  • Les chemins par défaut de l'installation de distutil se trouvent dans son API .
  • Plus d'informations sur le module copy_tree () de distutils peuvent être trouvées ici .
  • 4
    Heartagramir

    Utilisez glob pour sélectionner tous les sous-dossiers de votre setup.py

    ...
    packages=['your_package'],
    package_data={'your_package': ['data/**/*']},
    ...
    
    2
    gbonetti

    Je peux suggérer un petit code pour ajouter des fichiers de données dans 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,
    )
    
    2
    Stan