Comment inclure des fichiers supplémentaires dans un package Python

PythonBeginner
Pratiquer maintenant

Introduction

Les packages Python sont un moyen puissant d'organiser et de distribuer du code. Bien que les scripts Python (fichiers .py) constituent le cœur d'un package, vous avez souvent besoin d'inclure des fichiers supplémentaires tels que des fichiers de configuration, des fichiers de données, des modèles ou de la documentation. Ce tutoriel vous guidera à travers le processus de création d'un package Python qui inclut ces ressources supplémentaires, rendant votre package plus polyvalent et utile.

À la fin de ce lab, vous aurez créé un package Python complet avec des fichiers supplémentaires et appris comment accéder à ces fichiers depuis votre code.

Création d'une structure de package Python de base

Commençons par créer une structure de package Python de base. Un package est essentiellement un répertoire contenant des modules Python et un fichier spécial __init__.py qui indique à Python que ce répertoire doit être traité comme un package.

Créer la structure du répertoire du package

Tout d'abord, créons les répertoires nécessaires pour notre package :

mkdir -p ~/project/mypackage/data

Cette commande crée un répertoire appelé mypackage avec un sous-répertoire data pour stocker nos fichiers supplémentaires.

Maintenant, naviguons vers notre répertoire de projet :

cd ~/project

Créer les fichiers de base du package

Chaque package Python a besoin d'un fichier __init__.py dans son répertoire racine. Créons ce fichier :

touch mypackage/__init__.py

Ce fichier vide indique à Python que le répertoire mypackage est un package.

Ensuite, créons un module Python simple dans notre package :

echo 'def greet():
    print("Hello from mypackage!")' > mypackage/greeting.py

Ajouter un fichier de données au package

Maintenant, ajoutons un fichier de données à notre package. Il peut s'agir d'un fichier de configuration, d'un fichier CSV ou de tout autre type de fichier dont votre package a besoin :

echo 'This is sample data for our package.' > mypackage/data/sample.txt

Créons également un fichier de configuration :

echo '[config]
debug = true
log_level = INFO' > mypackage/config.ini

Vérifier la structure de votre package

Vous pouvez vérifier la structure de votre package avec la commande suivante :

find mypackage -type f | sort

Vous devriez voir une sortie similaire à :

mypackage/__init__.py
mypackage/config.ini
mypackage/data/sample.txt
mypackage/greeting.py

Ceci est une structure de package Python de base avec quelques fichiers supplémentaires non-Python. Dans les prochaines étapes, nous apprendrons comment inclure ces fichiers lors de la distribution du package et comment y accéder depuis votre code.

Création d'un script de configuration pour votre package

Pour inclure correctement des fichiers supplémentaires dans votre package Python, vous devez créer un fichier setup.py. Ce fichier est utilisé par les outils d'empaquetage de Python pour construire et installer votre package.

Comprendre setup.py

Le fichier setup.py contient des métadonnées sur votre package, telles que son nom, sa version, son auteur et ses dépendances. Il spécifie également quels fichiers doivent être inclus lors de la distribution du package.

Créons un fichier setup.py de base dans le répertoire racine de votre projet :

cd ~/project

Maintenant, créez le fichier setup.py avec le contenu suivant :

cat > setup.py << 'EOF'
from setuptools import setup, find_packages

setup(
    name="mypackage",
    version="0.1",
    packages=find_packages(),
    
    ## Include data files
    package_data={
        "mypackage": ["config.ini", "data/*.txt"],
    },
    
    ## Metadata
    author="Your Name",
    author_email="your.email@example.com",
    description="A simple Python package with additional files",
)
EOF

Comprendre la configuration des données du package

Le paramètre package_data est essentiel pour inclure des fichiers supplémentaires dans votre package. Il prend un dictionnaire où :

  • Les clés sont les noms des packages (ou "" pour tous les packages)
  • Les valeurs sont des listes de motifs de fichiers relatifs au répertoire du package

Dans notre exemple, nous incluons :

  • Le fichier config.ini à la racine de notre package
  • Tous les fichiers .txt dans le répertoire data

Les motifs de fichiers prennent en charge les caractères génériques comme * pour correspondre à plusieurs fichiers avec des noms ou des extensions similaires.

Tester votre configuration de configuration

Créons un environnement virtuel pour tester notre package :

python3 -m venv ~/project/venv
source ~/project/venv/bin/activate

Maintenant, installons notre package en mode développement :

cd ~/project
pip install -e .

L'indicateur -e signifie "mode éditable", ce qui signifie que vous pouvez modifier le code de votre package sans avoir à le réinstaller à chaque fois.

Vous devriez voir une sortie indiquant que votre package a été installé avec succès :

Successfully installed mypackage-0.1

Vérifions l'installation de notre package :

python -c "import mypackage.greeting; mypackage.greeting.greet()"

Cela devrait afficher :

Hello from mypackage!

Vous avez maintenant créé avec succès un package Python avec un script de configuration qui inclut des fichiers supplémentaires. Dans l'étape suivante, nous apprendrons comment accéder à ces fichiers depuis votre code Python.

Accéder aux fichiers supplémentaires dans votre package

Maintenant que nous avons inclus des fichiers supplémentaires dans notre package, nous devons apprendre à y accéder depuis notre code Python. Il existe plusieurs façons de le faire, mais la méthode la plus fiable consiste à utiliser le module pkg_resources du package setuptools.

Création d'un module pour accéder aux fichiers supplémentaires

Créons un nouveau module dans notre package qui démontre comment accéder aux fichiers supplémentaires :

cd ~/project

Créez un nouveau fichier appelé fileaccess.py dans le répertoire mypackage :

cat > mypackage/fileaccess.py << 'EOF'
import os
import pkg_resources

def get_config_path():
    """Return the path to the config.ini file."""
    return pkg_resources.resource_filename('mypackage', 'config.ini')

def read_config():
    """Read and return the content of the config.ini file."""
    config_path = get_config_path()
    with open(config_path, 'r') as f:
        return f.read()

def get_sample_data_path():
    """Return the path to the sample.txt file."""
    return pkg_resources.resource_filename('mypackage', 'data/sample.txt')

def read_sample_data():
    """Read and return the content of the sample.txt file."""
    data_path = get_sample_data_path()
    with open(data_path, 'r') as f:
        return f.read()

def list_package_data():
    """List all files included in the package data."""
    ## Get the package directory
    package_dir = os.path.dirname(pkg_resources.resource_filename('mypackage', '__init__.py'))
    
    ## List files in the main package directory
    main_files = [f for f in os.listdir(package_dir) 
                  if os.path.isfile(os.path.join(package_dir, f))]
    
    ## List files in the data directory
    data_dir = os.path.join(package_dir, 'data')
    data_files = [f'data/{f}' for f in os.listdir(data_dir) 
                 if os.path.isfile(os.path.join(data_dir, f))]
    
    return main_files + data_files
EOF

Mettre à jour le fichier __init__.py

Mettons à jour le fichier __init__.py pour exposer nos nouvelles fonctions :

cat > mypackage/__init__.py << 'EOF'
from mypackage.greeting import greet
from mypackage.fileaccess import (
    get_config_path,
    read_config,
    get_sample_data_path,
    read_sample_data,
    list_package_data
)

__all__ = [
    'greet',
    'get_config_path',
    'read_config',
    'get_sample_data_path',
    'read_sample_data',
    'list_package_data'
]
EOF

Tester les fonctions d'accès aux fichiers

Créons un script pour tester nos fonctions d'accès aux fichiers :

cat > ~/project/test_package.py << 'EOF'
import mypackage

## Test greeting function
print("Testing greeting function:")
mypackage.greet()
print()

## Test config file access
print("Config file path:")
print(mypackage.get_config_path())
print("\nConfig file content:")
print(mypackage.read_config())
print()

## Test data file access
print("Sample data file path:")
print(mypackage.get_sample_data_path())
print("\nSample data file content:")
print(mypackage.read_sample_data())
print()

## List all package data
print("All package data files:")
for file in mypackage.list_package_data():
    print(f"- {file}")
EOF

Maintenant, exécutez le script de test :

cd ~/project
python test_package.py

Vous devriez voir une sortie similaire à :

Testing greeting function:
Hello from mypackage!

Config file path:
/home/labex/project/mypackage/config.ini

Config file content:
[config]
debug = true
log_level = INFO

Sample data file path:
/home/labex/project/mypackage/data/sample.txt

Sample data file content:
This is sample data for our package.

All package data files:
- __init__.py
- config.ini
- fileaccess.py
- greeting.py
- data/sample.txt

Comprendre pkg_resources

Le module pkg_resources fournit un moyen d'accéder aux ressources à l'intérieur des packages installés. La fonction resource_filename renvoie le chemin d'un fichier à l'intérieur d'un package, quel que soit l'endroit où le package est installé.

Cette approche garantit que votre code peut accéder aux fichiers supplémentaires, que :

  • Vous exécutiez depuis le répertoire source pendant le développement
  • Il est installé dans un environnement virtuel
  • Il est installé à l'échelle du système
  • Il est distribué et installé sur une autre machine

Cela rend votre package plus portable et fiable, car il ne repose pas sur des chemins codés en dur ou des chemins relatifs qui pourraient changer en fonction de la façon dont le package est utilisé.

Construire et distribuer votre package

Maintenant que nous avons créé un package Python avec des fichiers supplémentaires et confirmé que nous pouvons y accéder, apprenons à construire et à distribuer ce package.

Mise à jour du script de configuration

Avant de construire le package, mettons à jour notre fichier setup.py pour inclure plus de métadonnées et de dépendances :

cd ~/project
cat > setup.py << 'EOF'
from setuptools import setup, find_packages

setup(
    name="mypackage",
    version="0.1.0",
    packages=find_packages(),
    
    ## Include data files
    package_data={
        "mypackage": ["config.ini", "data/*.txt"],
    },
    
    ## Dependencies
    install_requires=[
        "setuptools",
    ],
    
    ## Metadata
    author="Your Name",
    author_email="your.email@example.com",
    description="A simple Python package with additional files",
    keywords="sample, package, data",
    url="https://example.com/mypackage",
    classifiers=[
        "Development Status :: 3 - Alpha",
        "Intended Audience :: Developers",
        "Programming Language :: Python :: 3",
        "Programming Language :: Python :: 3.8",
        "Programming Language :: Python :: 3.9",
        "Programming Language :: Python :: 3.10",
    ],
    python_requires=">=3.6",
)
EOF

Construction des distributions source et wheel

Les packages Python peuvent être distribués dans plusieurs formats, mais les plus courants sont :

  1. Distribution source (sdist) : Une archive tar contenant le code source et les fichiers supplémentaires
  2. Distribution wheel (bdist_wheel) : Un package pré-construit qui peut être installé sans construction

Construisons les deux types de distributions :

## Assurez-vous d'avoir les derniers outils de construction
pip install --upgrade setuptools wheel

## Construire les distributions
python setup.py sdist bdist_wheel

Vous devriez voir une sortie indiquant que les distributions ont été créées, et de nouveaux fichiers devraient apparaître dans le répertoire dist.

Vérifions le contenu du répertoire dist :

ls -l dist

Vous devriez voir au moins deux fichiers :

  • Un fichier .tar.gz (la distribution source)
  • Un fichier .whl (la distribution wheel)

Installation du package à partir des fichiers de distribution

Maintenant, testons l'installation du package à partir de l'un des fichiers de distribution. Tout d'abord, désinstallons notre version de développement :

pip uninstall -y mypackage

Maintenant, installons la distribution wheel :

pip install dist/mypackage-0.1.0-py3-none-any.whl

Vous devriez voir une sortie indiquant que le package a été installé avec succès.

Vérifions que le package est installé et que nous pouvons toujours accéder aux fichiers supplémentaires :

python -c "import mypackage; print(mypackage.read_config())"

Cela devrait afficher le contenu du fichier config.ini :

[config]
debug = true
log_level = INFO

Publication de votre package

Dans un scénario réel, vous publieriez généralement votre package sur l'index des packages Python (PyPI) afin que d'autres puissent l'installer en utilisant pip install mypackage. Cela impliquerait :

  1. Créer un compte sur PyPI (https://pypi.org/)
  2. Utiliser des outils comme twine pour télécharger vos distributions :
    pip install twine
    twine upload dist/*

Cependant, pour ce lab, nous nous arrêterons à la création des distributions localement. Vous avez maintenant un package Python complet avec des fichiers supplémentaires qui peuvent être distribués et installés par d'autres.

Résumé de ce que vous avez créé

  • Un package Python avec des modules et des fichiers supplémentaires
  • Un script de configuration qui inclut ces fichiers dans la distribution
  • Des fonctions pour accéder à ces fichiers depuis votre code
  • Des fichiers de distribution source et wheel prêts pour la distribution

Cette structure fournit une base solide pour tout package Python que vous pourriez vouloir créer à l'avenir.

Résumé

Dans ce lab, vous avez appris à :

  1. Créer une structure de package Python de base avec des fichiers non-Python supplémentaires
  2. Configurer votre setup.py pour inclure ces fichiers dans la distribution du package
  3. Accéder aux fichiers supplémentaires depuis votre code Python en utilisant le module pkg_resources
  4. Construire des distributions source et wheel de votre package pour la distribution

Vous avez maintenant les connaissances nécessaires pour créer des packages Python plus complets qui incluent non seulement du code Python, mais aussi des fichiers de configuration, des fichiers de données, des modèles et d'autres ressources. Cette capacité est essentielle pour le développement d'applications réelles où le code Python doit souvent fonctionner avec des fichiers externes.

Quelques points clés à retenir de ce lab :

  • Utilisez le paramètre package_data dans setup() pour inclure des fichiers supplémentaires
  • Utilisez pkg_resources.resource_filename() pour accéder de manière fiable à ces fichiers depuis votre code
  • Construisez à la fois des distributions source et wheel pour une compatibilité maximale
  • Gardez la structure de votre package organisée pour faciliter la maintenance

Ces connaissances seront précieuses au fur et à mesure que vous continuerez à développer des applications et des packages Python plus complexes.