152 votes

Comment créer un package namespace en Python ?

En Python, un package namespace permet de répartir le code Python entre plusieurs projets. Cela est utile lorsque vous souhaitez publier des bibliothèques connexes sous forme de téléchargements séparés. Par exemple, avec les répertoires Package-1 y Package-2 en 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 y import namespace.module2 .

Quelle est la meilleure façon de définir un paquet d'espace de nom afin que plusieurs produits Python puissent définir des modules dans cet espace de nom ?

7 votes

Il me semble que module1 et module2 sont en fait des sous-paquets plutôt que des modules. D'après ce que j'ai compris, un module est en fait un fichier unique. Peut-être que subpkg1 et subpkg2 auraient plus de sens comme noms ?

94voto

clacke Points 1534

TL;DR :

Sous Python 3.3, vous n'avez rien à faire, il suffit de ne pas mettre de __init__.py dans les répertoires des paquets de votre espace de nom et cela fonctionnera tout simplement. Sur les versions antérieures à 3.3, choisissez l'option pkgutil.extend_path() sur le pkg_resources.declare_namespace() car il est à l'épreuve du temps et déjà compatible avec les paquets d'espaces de noms implicites.


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

Cela signifie qu'il y a maintenant trois types d'objets qui peuvent être créés par un agent de l'UE. import foo :

  • Un 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 d'espace de nom, représenté par un ou plusieurs répertoires foo sans aucune __init__.py fichiers

Les paquets sont aussi des modules, mais ici, quand je dis "module", je veux dire "module non paquet".

D'abord, il scanne sys.path pour un module ou un paquet régulier. S'il y parvient, il arrête la recherche et crée et initialise le module ou le paquet. S'il n'a pas trouvé de module ou de paquet régulier, mais qu'il a trouvé au moins un répertoire, il crée et initialise un paquet d'espace de nom.

Les modules et les paquets réguliers ont __file__ fixé à la .py à partir duquel ils ont été créés. Les paquets réguliers et les paquets d'espace de nom ont __path__ dans le ou les répertoires à partir desquels ils ont été créés.

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

Alors, comment les paquets ordinaires et les paquets de l'espace de nom se mélangent-ils ? Normalement, ils ne se mélangent pas, mais l'ancien système de pkgutil La méthode des paquets d'espace de noms explicites a été étendue pour inclure les paquets d'espace de noms implicites.

Si vous disposez d'un paquet régulier existant qui possède un fichier __init__.py comme ça :

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

... l'ancien comportement consiste à ajouter toute autre régulier sur le chemin recherché vers son __path__ . Mais dans Python 3.3, il ajoute également des packages d'espace de noms.

Vous pouvez donc 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 lignes (et path1 , path2 y path3 sont dans votre sys.path ) import package.foo , import package.bar y import package.baz fonctionneront tous.

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

4 votes

Qu'en est-il des outils d'installation ? Dois-je utiliser l'outil namespace_packages option ? Et l'option __import__('pkg_resources').declare_namespace(__name__) chose ?

4 votes

Dois-je ajouter namespace_packages=['package'] dans le setup.py ?

0 votes

@laurentlaport Bonne question ! Si vous êtes sur Python 3.3+, j'espère que non, mais expérimentez et revenez avec une réponse ;-) J'ai regardé setuptools.readthedocs.io/fr/latest/ mais il n'est pas clair s'il s'agit d'une documentation ancienne qui n'a pas été mise à jour ou si elle est toujours valable.

81voto

Mike Hordecki Points 14757

Il y a un module standard, appelé pkgutil avec lequel vous d'ajouter des modules à un espace de nom donné.

Avec la structure de répertoire que vous avez fournie :

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

Vous devriez mettre ces deux lignes dans les deux Package-1/namespace/__init__.py y Package-2/namespace/__init__.py (*) :

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

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

Comme le documentation dit :

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

À partir de maintenant, vous devriez être en mesure de distribuer ces deux paquets indépendamment.

18 votes

Quels sont les avantages et les inconvénients d'utiliser cette méthode par rapport à import__('pkg_resources').declare_namespace(__name ) ?

14 votes

D'abord, __import__ est considéré comme un mauvais style dans ce cas, car il peut être facilement remplacé par une simple déclaration d'importation. Plus précisément, pkg_resources est une bibliothèque non standard. Elle est fournie avec setuptools, donc ce n'est pas un problème, cependant. Une rapide recherche sur Google révèle que pkgutil a été introduit en 2.5 et que pkg_resources lui est antérieur. Néanmoins, pkgutil est une solution officiellement reconnue. L'inclusion de pkg_resources a, en fait, été rejetée dans le PEP 365.

0 votes

Leur fonctionnalité est probablement la même, cependant, car zope.interface utilise les deux dans son fichier __init__.py .

5voto

iElectric Points 2625

Cette section devrait être assez explicite.

En bref, mettez le code de l'espace de nom dans __init__.py , mise à jour setup.py pour déclarer un espace de nom, et vous êtes libre de partir.

13 votes

Vous devez toujours citer la partie pertinente d'un lien, au cas où le lien en question disparaîtrait.

2voto

cdent Points 51

Il s'agit d'une vieille question, mais quelqu'un a récemment fait remarquer sur mon blog que mon article sur les paquets d'espaces de noms était toujours d'actualité. J'ai donc pensé que je pourrais y faire un lien ici, car il fournit un exemple pratique de la façon de procéder :

https://web.archive.org/web/20150425043954/http://cdent.tumblr.com/post/216241761/python-namespace-packages-for-tiddlyweb

Ce lien renvoie à cet article pour l'essentiel de ce qui se passe :

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

El __import__("pkg_resources").declare_namespace(__name__) L'astuce consiste à piloter la gestion des plugins en TiddlyWeb et jusqu'à présent, ça semble fonctionner.

0 votes

Le lien siafoo est cassé, voici un lien vers une copie archivée : web.archive.org/web/20200926015931/http://www.siafoo.net/

-10voto

Tendayi Mawushe Points 10335

Vous avez vos concepts d'espace de nom Python à l'envers, il n'est pas possible en Python de mettre des paquets dans des modules. Les paquets contiennent les modules et non l'inverse.

Un paquetage Python est simplement un dossier contenant un fichier __init__.py dossier. Un module est n'importe quel autre fichier d'un paquetage (ou directement sur le serveur de l PYTHONPATH ) qui a un .py extension. Ainsi, dans votre exemple, vous avez deux paquets mais aucun module défini. Si vous considérez qu'un paquet est un dossier du système de fichiers et qu'un module est un fichier, vous comprenez pourquoi les paquets contiennent des modules et non l'inverse.

Ainsi, dans votre exemple, en supposant que Package-1 et Package-2 sont des dossiers sur le système de fichiers que vous avez placés 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 y module2 . et à moins que vous n'ayez une bonne raison, vous devriez probablement mettre les modules dans le dossier et n'avoir que cela dans le chemin python comme ci-dessous :

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

0 votes

Je parle de choses comme zope.x où un ensemble de paquets apparentés sont publiés sous forme de téléchargements séparés.

0 votes

Ok, mais quel est l'effet que vous essayez d'obtenir. Si les dossiers contenant les paquets concernés se trouvent tous sur le PYTHONPATH, l'interpréteur Python les trouvera pour vous sans effort supplémentaire de votre part.

5 votes

Si vous ajoutez à la fois Package-1 et Package-2 à PYTHONPATH, seul Package-1/namespace/ sera vu par Python.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X