Empacotando pacotes de espaço de nomes¶
Os pacotes de espaço de nomes permitem que você divida os subpacotes e módulos dentro de um único pacote em vários pacotes de distribuição (referidos como distribuições em este documento para evitar ambiguidade). Por exemplo, se você tiver a seguinte estrutura de pacote:
mynamespace/
__init__.py
subpackage_a/
__init__.py
...
subpackage_b/
__init__.py
...
module_b.py
pyproject.toml
E você usa este pacote em seu código assim:
from mynamespace import subpackage_a
from mynamespace import subpackage_b
Então você pode quebrar esses subpacotes em duas distribuições separadas:
mynamespace-subpackage-a/
pyproject.toml
src/
mynamespace/
subpackage_a/
__init__.py
mynamespace-subpackage-b/
pyproject.toml
src/
mynamespace/
subpackage_b/
__init__.py
module_b.py
Cada subpacote pode agora ser instalado, usado e versionado separadamente.
Os pacotes de espaço de nomes podem ser úteis para uma grande coleção de pacotes vagamente relacionados (como um grande corpo de bibliotecas de cliente para vários produtos de uma única empresa). No entanto, os pacotes de espaço de nomes vêm com várias ressalvas e não são apropriados em todos os casos. Uma alternativa simples é usar um prefixo em todas as suas distribuições, como import mynamespace_subpackage_a (você pode até usar import mynamespace_subpackage_a as subpackage_a para manter o objeto de importação curto).
Criando um pacote de espaço de nomes¶
Atualmente, existem duas abordagens diferentes para a criação de pacotes de espaço de nomes, das quais a última é desencorajado:
Usar pacotes de espaço de nomes nativos. Este tipo de pacote de espaço de nomes é definido na PEP 420 e está disponível em Python 3.3 e posterior. Isso é recomendado se os pacotes em seu espaço de nomes precisarem apenas oferecer suporte ao Python 3 e à instalação via
pip.Use pacotes de espaço de nomes legados. Isso compreende pacotes de espaço de nomes no estilo pkgutil e pacotes de espaço de nomes no estilo pkg_resources.
Pacotes se espaço de nomes nativos¶
Python 3.3 adicionou pacotes de espaço de nomes implícitos a partir da PEP 420. Tudo o que é necessário para criar um pacote de espaço de nomes nativo é omitir __init__.py do diretório do pacote de espaço de nomes. Um exemplo de estrutura de arquivo (seguindo src-layout):
mynamespace-subpackage-a/
pyproject.toml # AND/OR setup.py, setup.cfg
src/
mynamespace/ # namespace package
# No __init__.py here.
subpackage_a/
# Regular import packages have an __init__.py.
__init__.py
module.py
É extremamente importante que toda distribuição que usa o pacote de espaço de nomes omita o __init__.py ou use um __init__.py no estilo pkgutil. Se alguma distribuição não o fizer, isso fará com que a lógica do espaço de nomes falhe e os outros subpacotes não serão importáveis.
A estrutura de diretórios src-layout permite a descoberta automática de pacotes pela maioria dos backends de construção. Veja layout src vs layout plano para mais informações. Se, no entanto, você mesmo deseja gerenciar exclusões ou inclusões de pacotes, isso é possível de ser configurado no pyproject.toml de nível superior:
[build-system]
...
[tool.setuptools.packages.find]
where = ["src/"]
include = ["mynamespace.subpackage_a"]
[project]
name = "mynamespace-subpackage-a"
...
O mesmo pode ser feito com um setup.cfg:
[options]
package_dir =
=src
packages = find_namespace:
[options.packages.find]
where = src
Ou setup.py:
from setuptools import setup, find_namespace_packages
setup(
name='mynamespace-subpackage-a',
...
packages=find_namespace_packages(where='src/', include=['mynamespace.subpackage_a']),
package_dir={'': 'src'},
)
Setuptools irá pesquisar na estrutura de diretórios por pacotes de espaço de nomes implícitos por padrão.
Um exemplo de trabalho completo de dois pacotes de espaço de nomes nativos pode ser encontrado no projeto exemplo de pacote de espaço de nomes nativo.
Nota
Como os pacotes de espaço de nomes nativos e no estilo pkgutil são amplamente compatíveis, você pode usar pacotes de espaço de nomes nativos nas distribuições que oferecem suporte apenas a Python 3 e pacotes de espaço de nomes no estilo pkgutil nas distribuições que precisam oferecer suporte a Python 2 e 3.
Pacotes de espaço de nomes legados¶
These two methods, that were used to create namespace packages prior to PEP 420, are now considered to be obsolete and should not be used unless you need compatibility with packages already using one of these methods.
Para migrar um pacote existente, todos os pacotes que compartilham o espaço de nomes devem ser migrados simultaneamente.
Aviso
Embora os pacotes de espaço de nomes nativos e pacotes de espaço de nomes no estilo pkgutil sejam amplamente compatíveis, os pacotes de espaço de nomes no estilo pkg_resources não são compatíveis com os outros métodos. Não é aconselhável usar métodos diferentes em distribuições diferentes que fornecem pacotes para o mesmo espaço de nomes.
Pacotes de espaço de nomes no estilo pkgutil¶
Python 2.3 introduced the pkgutil module and the
pkgutil.extend_path() function. This can be used to declare namespace
packages that need to be compatible with both Python 2.3+ and Python 3. This
was the recommended approach for the highest level of compatibility.
Para criar um pacote de espaço de nomes no estilo pkgutil, você precisa fornecer um arquivo __init__.py para o pacote de espaço de nomes:
mynamespace-subpackage-a/
src/
pyproject.toml # AND/OR setup.cfg, setup.py
mynamespace/
__init__.py # Namespace package __init__.py
subpackage_a/
__init__.py # Regular package __init__.py
module.py
O arquivo __init__.py para o pacote de espaço de nomes precisa conter o seguinte:
__path__ = __import__('pkgutil').extend_path(__path__, __name__)
Cada distribuição que usa o pacote de espaço de nomes deve incluir um __init__.py. Se alguma distribuição não o fizer, isso fará com que a lógica do espaço de nomes falhe e os outros subpacotes não serão importáveis. Qualquer código adicional no __init__.py ficará inacessível.
Um exemplo completamente funcional de dois pacotes de espaço de nomes no estilo pkgutil pode ser encontrado no projeto exemplo de espaço de nomes de pkgutil.
Pacotes de espaço de nomes no estilo pkg_resources¶
Aviso
The information in this section is obsolete and is no longer functional (as of Setuptools 82.0.0). It is only retained for historical reference.
pkg_resources has been deprecated and was fully removed in Setuptools 82.0.0.
Setuptools previously provided the pkg_resources.declare_namespace function and
the namespace_packages argument to setup(). Together
these could be used to declare namespace packages. While this approach is no
longer supported, it may still be encountered in environments using older
setuptools versions.
If you are creating a new distribution within an existing namespace package that
uses this method then it’s recommended to continue using this as the different
methods are not cross-compatible and it’s not advisable to try to migrate an
existing package.
Para criar um pacote de espaço de nomes no estilo pkg_resources, você precisa fornecer um arquivo __init __. Py para o pacote de espaço de nomes:
mynamespace-subpackage-a/
src/
pyproject.toml # AND/OR setup.cfg, setup.py
mynamespace/
__init__.py # Namespace package __init__.py
subpackage_a/
__init__.py # Regular package __init__.py
module.py
O arquivo __init__.py para o pacote de espaço de nomes precisa conter o seguinte:
__import__('pkg_resources').declare_namespace(__name__)
Cada distribuição que usa o pacote de espaço de nomes deve incluir um __init__.py. Se alguma distribuição não o fizer, isso fará com que a lógica do espaço de nomes falhe e os outros subpacotes não serão importáveis. Qualquer código adicional no __init__.py ficará inacessível.
Nota
Algumas recomendações mais antigas aconselham o seguinte no __init__.py de pacotes de espaço de nomes:
try:
__import__('pkg_resources').declare_namespace(__name__)
except ImportError:
__path__ = __import__('pkgutil').extend_path(__path__, __name__)
A ideia por trás disso era que, no caso raro de setuptools não estar disponível, os pacotes recorreriam aos pacotes no estilo pkgutil. Isso não é aconselhável porque os pacotes de espaço de nomes estilo pkgutil e pkg_resources não são compatíveis entre si. Se a presença de setuptools é uma preocupação, então o pacote deve depender explicitamente de setuptools via install_requires.
Finalmente, cada distribuição deve fornecer o argumento namespace_packages para setup() no setup.py. Por exemplo:
from setuptools import find_packages, setup
setup(
name='mynamespace-subpackage-a',
...
packages=find_packages()
namespace_packages=['mynamespace']
)