python 命名空间与包:使包成为默认命名空间

python namespaces vs packages: making a package the default namespace

我有一个包含总体命名空间的项目,其中包含包。这是文件夹结构:

pypackage
├── pypackage             <-- Source code for use in this project.
|   |
│   ├── bin               <-- Module: Cli entry point into pyproject.
|   |   ├── __init__.py
|   |   └── pypackage.py
|   |
|   └── core              <-- Module: Core functionality.
|       ├── __init__.py
|       └── pypackage.py
|
├── tests             
├── README.md         
└── setup.py          

很简单。如果我想导入它,我使用:

from pypackage.core import pypackage

效果很好,因为我的 setup.py 看起来像这样:

from setuptools import setup, find_packages
...
NAME = 'pypackage'
setup(
    name=NAME,
    namespace_packages=[NAME],
    packages=[f'{NAME}.{p}' for p in find_packages(where=NAME)],
    entry_points={
        "console_scripts": [
            f'{NAME} = {NAME}.bin.{NAME}:cli',
        ]
    },
    ...
)

但是,我有遗留代码导入这个 pypackage,而它曾经只是一个独立的 python 文件。像这样:

import pypackage

那么,我该怎么做才能使命名空间和子包的结构保持不变,但仍然以旧方式导入它?我如何打开它:

from pypackage.core import pypackage

进入这个:

import pypackage

换句话说,当我将 pypackage 导入外部项目时,如何将 pypackage.core.pypackage 模块别名为 pypackage

您可以通过导入顶级包来在新包中添加 'old' 名称。

pypackage/__init__.py 中作为全局变量导入的名称是 pypackage 包中的属性。利用它可以访问 'legacy' 个位置:

# add all public names from pypackage.core.pypackage to the top level for
# legacy package use
from .core.pypackage import *

现在任何使用 import pypackage 的代码都可以使用 pypackage.foopypackage.bar,如果实际上这些对象是在 pypackage.core.pypackage 中定义的话。

现在,因为 pypackagesetuptools namespace package,所以您遇到了不同的问题;命名空间包用于安装多个单独的发行版,因此顶级包必须是 或仅包含最小 __init__.py 文件(使用空目录创建的命名空间包需要Python3.3).

如果您是唯一使用此命名空间的发行版发布者,您可以在这里稍微作弊并在 core 中使用 单个 __init__.py 文件] 包可以使用 pkg-util-style __init__.py file 和我上面使用的附加导入,但是你 不能在其他分发包 中使用任何 __init__.py 文件或要求它们全部使用完全相同的 __init__.py 内容。协调是关键。

或者您将不得不使用不同的方法。将 pypackage 保留为遗留包装器模块,并重命名新的包格式以使用可以与旧模块相邻的新的、不同的顶级名称。此时,您可以直接将遗留包作为额外的顶级模块包含在您的项目中。

如果我使用包,Martin Pieters 的想法是正确的,但命名空间包是一个设置工具的事情。

所以那没有用。经过更多研究,我了解到没有办法做我想做的事情。因此,如果我真的想这样做,我必须将所有内容转换为常规包层次结构而不是命名空间包,然后使用马丁的解决方案。

我决定修改遗留代码,而不是以新方式导入它。