Python: partager un code commun parmi une famille de scripts

j'écris une famille de scripts Python dans un projet; chaque script est dans un sous-répertoire du projet, comme ceci:

projectroot
  |
  |- subproject1
  |    |
  |    |- script1.main.py
  |    `- script1.merger.py
  |
  |- subproject2
  |    |
  |    |- script2.main.py
  |    |- script2.matcher.py
  |    `- script2.merger.py
  |
  `- subproject3
       |
       |- script3.main.py
       |- script3.converter.py
       |- script3.matcher.py
       `- script3.merger.py

plusieurs scripts partagent un code. Le code partagé est le mieux considéré comme faisant partie du projet lui-même, et pas quelque chose que je compilerais séparément et que je ferais sortir d'une bibliothèque, ou que je laisserais tomber dans un PYTHONPATH sitewide. Je pourrais placer ce code à différents endroits, comme dans le projectroot répertoire lui-même, ou dans un répertoire enfant de projectroot appelé common (peut-être).

cependant, la plupart des façons auxquelles j'ai pensé jusqu'à présent impliquent de faire des paquets de mes sous-projets avec empty __init__.py des fichiers et en utilisant des importations relatives (ou en jouant de façon redondante avec sys.path dans chaque sous-projet. Pire encore, il semble que la construction d'une structure de paquets autour de cette famille de scripts va à l'encontre de l'avertissement suivant de la part du rejeté PEP-3122:

Attention! Cette PEP a été rejetée. Guido vues de course scripts à l'intérieur d'un paquet en tant qu'anti-pattern.

si les scripts d'un paquet sont anti-patternish, Comment puis-je configurer les choses de manière à garder le code commun dans le même projet? Ou un système basé sur un module et un paquet est-il acceptable ici? Qui est la meilleure approche? (FWIW, je préfère avoir un fichier tel que shared.py ou common.py dans le répertoire racine du projet, plutôt que de créer un répertoire utilitaire qui est apparenté aux sous-projets "réels".)

22
demandé sur Ray Toal 2013-08-06 21:53:40

3 réponses

je suggère de mettre des scripts de "lanceur" triviaux au niveau supérieur de votre projet, et de transformer chacun des dossiers de sous-projets en paquets. Les modules dans les paquets peuvent être importés les uns les autres ou le code commun peut être intégré dans un common package.

voici à quoi ressemblerait la structure, si nous supposons les différents merger les modules peuvent être refactorisés dans une version partagée:

projectroot
  |- script1.py # launcher scripts, see below for example code
  |- script2.py
  |- script3.py
  |
  |- common
  |    |- __init__.py
  |    |- merger.py # from other packages, use from ..common import merger to get this
  |
  |- subproject1
  |    |- __init__.py # this can be empty
  |    |- script1_main.py
  |
  |- subproject2
  |    |- __init__.py
  |    |- script2_main.py
  |    |- script2_matcher.py
  |
  |- subproject3
       |- __init__.py
       |- script3_main.py
       |- script3_converter.py
       |- script3_matcher.py

les scripts de lancement peuvent être très simple:

from subproject1 import script1_main

if __name__ == "__main__":
    script1_main.main()

C'est-à-dire, tout ce qu'il fait est d'importer le module "scriptN_main" approprié et d'y exécuter une fonction. L'utilisation d'un script simple peut aussi avoir de petits avantages pour la vitesse de démarrage du script, puisque le main module peut avoir son bytecode compilé caché à un .pyc fichier, alors que les scripts ne sont jamais mis en cache.

Note: I renommed your modules, swapping _ caractères . caractères. Vous ne pouvez pas avoir un . dans un identificateur (comme un module nom), puisque Python s'attend à ce qu'il indique l'accès aux attributs. Cela signifiait que ces modules ne pouvaient jamais être importés. (Je suppose que c'est un artefact des fichiers d'exemple seulement, pas quelque chose que vous avez dans votre code réel.)

22
répondu Blckknght 2014-01-13 21:33:07

Ma préférence serait un répertoire séparé" bin "ou" scripts", avec des sous-projets comme bibliothèques / paquets:

projectroot
  |
  |- scripts
  |
  |- lib
  |    |
  |    `- matcher.py
  |    `- merger.py
  |    `- subproject1
  |    `- subproject2
  |    `- subproject3

l'idée étant que vos scripts peuvent faire référence à n'importe quel sous-projet nécessaire comme les paquets habituels. Et vos sous-projets peuvent aussi faire référence les uns aux autres avec des importations.

Vous pouvez aussi avoir un script principal ou partagé qui configure les paquets de sous-projets pour vous, si cela vous aide.

0
répondu Matt S 2013-08-06 18:38:03

s'il vous Plaît utiliser setuptools pour distribuer les deux scripts et bibliothèques:

e.g.

from setuptools import setup

setup(
    # other arguments here... (e.g. packages / package_dir)
    entry_points = {
        'console_scripts': [
            'script1 = subproject1.script1:main',
            'script2 = subproject2.script2:main',
        ],
    }
)

si vous pouvez écrire tout votre code en tant que bibliothèques, et n'avez pas besoin de modules séparés pour avoir vos points d'entrée, alors c'est l'outil pour vous. Si vous avez des scripts, c'est bien aussi, mais vous aurez besoin d'un main fonction, vous pouvez de référence (voir l'exemple ci-dessus)

0
répondu dnozay 2013-08-15 16:44:29