Comment créer un paquet namespace en Python?

en Python, un paquet namespace vous permet de répartir le code Python entre plusieurs projets. Ceci est utile lorsque vous voulez libérer des bibliothèques liées comme téléchargements séparés. Par exemple, avec les répertoires Package-1 et Package-2 dans PYTHONPATH ,

Package-1/namespace/__init__.py
Package-1/namespace/module1/__init__.py
Package-2/namespace/__init__.py
Package-2/namespace/module2/__init__.py

l'utilisateur final peut import namespace.module1 et import namespace.module2 .

Quelle est la meilleure façon de définir un paquet namespace pour que plus d'un produit Python puisse définir des modules dans ce paquet? espace de noms?

116
demandé sur joeforker 2009-11-04 21:27:10

5 réponses

TL; DR:

sur Python 3.3 vous n'avez rien à faire, il suffit de ne pas mettre de __init__.py dans vos répertoires package namespace et cela fonctionnera. Sur pre-3.3, choisissez la solution pkgutil.extend_path() plutôt que la solution pkg_resources.declare_namespace() , car elle est à l'épreuve du futur et déjà compatible avec les paquets d'espaces de noms implicites.


Python introduit les paquets d'espaces de noms implicites, voir PEP 420 .

cela signifie qu'il y a maintenant trois types d'objets qui peuvent être créés par un import foo :

  • module représenté par un foo.py fichier
  • un paquet régulier, représenté par un répertoire foo contenant un __init__.py fichier
  • un paquet namespace, représenté par un ou plusieurs répertoires foo sans aucun __init__.py fichiers
Les paquets

sont aussi des modules, mais ici je veux dire" module non-package "quand je dis"module".

tout d'abord, il scanne sys.path pour un module ou un paquet régulier. S'il réussit, il arrête la recherche et crée et initalise le module ou le paquet. S'il n'a trouvé aucun module ou paquet régulier, mais qu'il a trouvé au moins un répertoire, il crée et initialise un paquet namespace.

les Modules et les paquets réguliers ont __file__ .py fichier à partir duquel ils ont été créés. Les paquets réguliers et namespace ont __path__ placé dans le répertoire ou les répertoires à partir desquels ils ont été créés.

quand vous faites import foo.bar , la recherche ci-dessus se fait d'abord pour foo , puis si un paquet a été trouvé, la recherche pour bar se fait avec foo.__path__ comme le chemin de recherche au lieu de sys.path . Si foo.bar , foo et foo.bar sont créés et initialisés.

alors comment les paquets réguliers et les paquets namespace se mélangent-ils? Normalement ils ne le font pas, mais l'ancienne méthode de paquet de namespace explicite pkgutil a été étendue pour inclure les paquets de namespace implicites.

si vous avez un paquet régulier existant qui a un __init__.py comme ceci:

from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)

... le comportement d'héritage est d'ajouter n'importe quel autre régulier paquets sur le chemin cherché à son __path__ . Mais dans Python 3.3, il ajoute aussi des paquets namespace.

ainsi vous pouvez avoir la structure de répertoire suivante:

├── path1
│   └── package
│       ├── __init__.py
│       └── foo.py
├── path2
│   └── package
│       └── bar.py
└── path3
    └── package
        ├── __init__.py
        └── baz.py

... et aussi longtemps que les deux __init__.py ont le extend_path et path1 , path2 et path3 sont dans votre sys.path ) import package.foo , import package.bar et import package.baz .

pkg_resources.declare_namespace(__name__) n'a pas été mis à jour pour inclure les paquets d'espace de noms implicites.

48
répondu clacke 2014-12-21 02:44:44

Il ya un module standard, appelé pkgutil , avec lequel vous peut " ajouter des modules à un espace de noms donné.

avec la structure du répertoire que vous avez fourni:

Package-1/namespace/__init__.py
Package-1/namespace/module1/__init__.py
Package-2/namespace/__init__.py
Package-2/namespace/module2/__init__.py

vous devez mettre ces deux lignes dans les deux Package-1/namespace/__init__.py et Package-2/namespace/__init__.py ( * ):

from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)

(*car-à moins de déclarer une dépendance entre eux - vous ne savez pas lequel d'entre eux sera reconnu en premier - voir PEP 420 pour plus d'information)

comme le documentation dit:

cela ajoutera au paquet __path__ tous les sous-répertoires des répertoires sur sys.path nommé d'après le paquet.

désormais, vous devriez pouvoir distribuer ces deux paquets indépendamment.

75
répondu Mike Hordecki 2015-03-02 23:39:45

Cette section devrait être assez explicite.

en bref, mettez le code d'espace de noms dans __init__.py , mettez à jour setup.py pour déclarer un espace de noms, et vous êtes libre d'aller.

5
répondu iElectric 2013-01-28 19:47:05

C'est une vieille question, mais quelqu'un a récemment commenté sur mon blog que mon message sur les paquets namespace était toujours pertinent, donc j'ai pensé que je voudrais le lier ici car il fournit un exemple pratique de la façon de le faire aller:

http://cdent.tumblr.com/post/216241761/python-namespace-packages-for-tiddlyweb

qui renvoie à cet article pour les principaux boyaux de ce qui se passe:

http://www.siafoo.net/article/77#multiple-distributions-one-virtual-package

le truc du __import__("pkg_resources").declare_namespace(__name__) est à peu près le moteur de la gestion des plugins dans TiddlyWeb et semble jusqu'à présent fonctionner.

3
répondu cdent 2013-02-05 15:30:52

vous avez vos concepts D'espace de noms Python à l'envers, il n'est pas possible en python de mettre des paquets dans des modules. Les paquets contiennent des modules non l'inverse.

un paquet Python est simplement un dossier contenant un fichier __init__.py . Un module est tout autre fichier d'un paquet (ou directement sur le PYTHONPATH ) qui a une extension .py . Donc dans votre exemple, vous avez deux paquets, mais pas les modules définis. Si vous considérez qu'un paquet est un système de fichiers dossier et un module est un fichier puis vous voyez pourquoi les paquets contiennent des modules et non l'inverse.

donc dans votre exemple en supposant que les paquets-1 et paquet-2 sont des dossiers sur le système de fichiers que vous avez mis sur le chemin Python, vous pouvez avoir ce qui suit:

Package-1/
  namespace/
  __init__.py
  module1.py
Package-2/
  namespace/
  __init__.py
  module2.py

vous avez maintenant un paquet namespace avec deux modules module1 et module2 . et à moins que vous n'ayez une bonne raison, vous devriez probablement mettre les modules dans le dossier et avoir seulement sur le chemin python comme ci-dessous:

Package-1/
  namespace/
  __init__.py
  module1.py
  module2.py
-8
répondu Tendayi Mawushe 2009-11-04 18:58:42