python namespaces package

¿Cómo creo un paquete de espacio de nombres en Python?



namespaces package (5)

En Python, un paquete de espacio de nombres le permite extender el código de Python entre varios proyectos. Esto es útil cuando desea liberar bibliotecas relacionadas como descargas separadas. Por ejemplo, con los directorios 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

el usuario final puede import namespace.module1 e import namespace.module2 .

¿Cuál es la mejor manera de definir un paquete de espacio de nombres para que más de un producto Python pueda definir módulos en ese espacio de nombres?


Esta es una vieja pregunta, pero alguien recientemente comentó en mi blog que mi publicación sobre los paquetes de espacio de nombres todavía era relevante, así que pensé que lo vincularía aquí ya que proporciona un ejemplo práctico de cómo hacerlo funcionar:

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

Eso vincula a este artículo para las principales agallas de lo que está pasando:

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

El __import__("pkg_resources").declare_namespace(__name__) es más o menos lo que impulsa la administración de complementos en TiddlyWeb y hasta ahora parece estar funcionando.


Hay un módulo estándar, llamado pkgutil , con el que puede ''agregar'' módulos a un espacio de nombres dado.

Con la estructura de directorios que ha proporcionado:

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

Debería poner esas dos líneas en Package-1/namespace/__init__.py y Package-2/namespace/__init__.py (*):

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

(* desde - a menos que establezca una dependencia entre ellos - no sabe cuál de ellos se reconocerá primero - vea PEP 420 para más información)

Como dice la pkgutil :

Esto agregará a __path__ del paquete todos los subdirectorios de directorios en sys.path nombrado después del paquete.

De ahora en adelante, debería poder distribuir esos dos paquetes de forma independiente.


TL; DR:

En Python 3.3 no tiene que hacer nada, simplemente no coloque ningún __init__.py en sus directorios de paquetes de espacio de nombres y simplemente funcionará. En pre-3.3, elija la solución pkgutil.extend_path() sobre pkg_resources.declare_namespace() one, porque es a prueba de futuro y ya es compatible con paquetes de espacio de nombres implícito.

Python 3.3 introduce paquetes de espacios de nombres implícitos, ver PEP 420 .

Esto significa que ahora hay tres tipos de objetos que pueden ser creados por un import foo :

  • Un módulo representado por un archivo foo.py
  • Un paquete regular, representado por un directorio foo contiene un archivo __init__.py
  • Un paquete de espacio de nombres, representado por uno o más directorios foo sin ningún archivo __init__.py

Los paquetes también son módulos, pero aquí me refiero a "módulo no incluido" cuando digo "módulo".

Primero escanea sys.path para un módulo o paquete regular. Si tiene éxito, deja de buscar y crea e inicia el módulo o paquete. Si no encontró ningún módulo o paquete regular, pero encontró al menos un directorio, crea e inicializa un paquete de espacio de nombres.

Los módulos y paquetes regulares tienen __file__ establecido en el archivo .py el que se crearon. Los paquetes regulares y de espacio de nombres tienen __path__ establecido en el directorio o directorios desde donde fueron creados.

Cuando import foo.bar , la búsqueda anterior ocurre primero para foo , luego si se encontró un paquete, la búsqueda de la bar se hace con foo.__path__ como la ruta de búsqueda en lugar de sys.path . Si se encuentra foo.bar , foo y foo.bar se crean y se inicializan.

Entonces, ¿cómo se mezclan los paquetes regulares y los paquetes de espacio de nombres? Normalmente no es así, pero el antiguo método de paquete de espacio de nombres explícito pkgutil se ha ampliado para incluir paquetes de espacio de nombres implícito.

Si tiene un paquete regular existente que tiene un __init__.py como este:

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

... el comportamiento heredado es agregar cualquier otro paquete regular en la ruta de búsqueda a su __path__ . Pero en Python 3.3, también agrega paquetes de espacio de nombres.

Entonces puede tener la siguiente estructura de directorio:

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

... y siempre que los dos __init__.py tengan las líneas extend_path (y path1 , path2 y path3 en su sys.path ) import package.foo , import package.bar e import package.baz funcionarán.

pkg_resources.declare_namespace(__name__) no se ha actualizado para incluir paquetes de espacios de nombres implícitos.


Usted tiene sus conceptos de espacio de nombres de Python al revés, no es posible en Python poner paquetes en módulos. Los paquetes contienen módulos, no al revés.

Un paquete de Python es simplemente una carpeta que contiene un archivo __init__.py . Un módulo es cualquier otro archivo en un paquete (o directamente en PYTHONPATH ) que tiene una extensión .py . Entonces en tu ejemplo tienes dos paquetes pero no hay módulos definidos. Si considera que un paquete es una carpeta del sistema de archivos y un módulo es un archivo, entonces verá por qué los paquetes contienen módulos y no al revés.

Entonces, en su ejemplo, suponiendo que Package-1 y Package-2 son carpetas en el sistema de archivos que ha puesto en la ruta de acceso de Python, puede tener lo siguiente:

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

Ahora tiene un namespace paquete con dos módulos module1 y module2 . y a menos que tenga una buena razón, probablemente debería colocar los módulos en la carpeta y tener solo eso en la ruta de Python como se muestra a continuación:

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