python - 创建包含独立可安装子包的 namespace 包

标签 python namespaces setuptools packaging

我目前正在尝试将一些相关的包组合到一个父包 (meta_package) 中。在这样做的同时,我还希望其中一个软件包可以作为独立软件包安装。为此,我创建了以下文件夹结构:

├── meta_package
│   ├── subpackage1
│   │   ├── module.py
│   │   ├── __init__.py
│   ├── subpackage2
│   │   ├── module.py
│   │   └── __init__.py
│   ├── subpackage3
│   │   ├── module.py
│   │   └── __init__.py
│   └── installable_subpackage
│       ├── README.md
│       ├── __init__.py
│       ├── requirements.txt
│       ├── setup.py
│       ├── installable_subpackage
│       │   ├── __init__.py
│       │   └── submodule
│       │       ├── __init__.py
│       │       └── module.py
虽然上面的结构达到了预期的效果,但是当定义子包为namespace packages或普通包,它引入了一个额外的 installable_subpackage目录。结果从installable_subpackage导入一个Class我必须使用以下导入语句:
from meta_package.installable_subpackage.installable_subpackage.submodule.module import Class
但是,我希望能够使用以下(较短的)导入语句导入类:
from meta_package.installable_subpackage.submodule.module import Class
我已经尝试过的
使用命名空间包
我尝试使用 namespace packages对于子包而不是使用普通包。然而,这并没有解决额外的文件夹问题,还引入了许多 python import traps .
在 installable_subpackage 中导入冗余文件夹(模块)。 初始化 .py 文件
我也尝试导入 installable_subpackage installable_subpackage.__init__.py 内的子模块文件:
import meta_package.installable_subpackage.installable_subpackage
然而,这似乎不像 meta_package.installable_subpackage.submodule 那样工作。导入路径没有指向 meta_package.installable_subpackage.installable_subpackage.submodule模块。我认为这是因为此方法仅适用于类而不适用于模块。
使用 setuptools 包和 package_dir 参数
最后,根据 to this issue ,我尝试使用 setuptools packagespackage_dir meta_package 中的参数setup.py 去掉多余的文件夹。为此,我使用了以下 setup.py:
from setuptools import setup, find_namespace_packages

setup(
    name="meta_package",
    ...
    packages=find_namespace_packages(include=["meta_package.*"]),
    package_dir={"meta_package.installable_subpackage": "meta_package/installable_subpackage/installable_subpackage"},
)

然而,这似乎也没有摆脱额外的文件夹。
问题
我试图实现的包装结构是否可行?此外,如果是这样,是鼓励还是不建议使用它?
系统信息
  • Python 版本:Python 3.8.5
  • 虚拟环境:Conda
  • 最佳答案

    正如 @sinoroc 所指出的:

    When using package_dir, you can"t really use find_namespace_packages, you have to either write the list manually or modify it before assigning it to packages.


    因此,为了实现所需的行为,我必须在将包列表提供给 setuptools.setup 之前修改包列表。方法。这可以通过多种方式完成。
    1.在包列表中添加一个虚拟的缩短包
    我们可以为每个冗余文件夹添加额外的(缩短的)模块条目到 packages列表。这可以通过使用以下 setup.py 文件来完成:
    setup.py 文件
    from setuptools import setup, find_namespace_packages
    
    # Retrieve package list
    PACKAGES = find_namespace_packages(include=["meta_package*"])+["meta_package.installable_subpackage"]
    
    setup(
        name="meta_package",
        ...
        packages=PACKAGES,
        package_dir={
            "meta_package.installable_subpackage": "meta_package/installable_subpackage/installable_subpackage",
        },
    )
    
    要为多个子包自动执行此操作,您可以使用以下代码:
    # Retrieve package list
    PACKAGES = find_namespace_packages(include=["meta_package*"])
    
    # Add extra virtual shortened package for each of namespace_pkgs that contain redundant folders
    namespace_pkgs = ["installable_subpackage"]
    exclusions = r"|".join(
        [r"\." + item + r"\.(?=" + item + r".)" for item in namespace_pkgs]
    )
    PACKAGE_DIR = {}
    for package in PACKAGES:
        sub_tmp = re.sub(exclusions, ".", package)
        if sub_tmp is not package:
            PACKAGE_DIR[sub_tmp] = package.replace(".", "/")
    PACKAGES.extend(PACKAGE_DIR.keys())
    
    setup(
        name="meta_package",
        ...
        packages=PACKAGES,
        package_dir=PACKAGE_DIR,
    )
    
    2. 修改包列表,使其只包含缩短的包
    或者,如果您想用较短的名称完全替换长模块名称,可以使用以下 setup.py :
    setup.py 文件
    from setuptools import setup, find_namespace_packages
    
    # Retrieve package list
    PACKAGES = find_namespace_packages(include=["meta_package*"])
    
    # Remove redundant folders from the package list
    PACKAGES = [re.sub(r"\.installable_subpackage\.(?=installable_subpackage.)", ".", package) for package in PACKAGES]
    
    setup(
        name="meta_package",
        ...
        packages=PACKAGES,
        package_dir={
            "meta_package.installable_subpackage": "meta_package/installable_subpackage/installable_subpackage",
        },
    )
    
    这也可以使用以下代码为多个子包自动完成:
    # Remove redundant folder from package list
    red_folders = ["installable_subpackage"]
    exclusions = r"|".join(
        [r"\." + item + r"\.(?=" + item + r".)" for item in red_folders]
    )
    PACKAGE_DIR = {}
    for index, package in enumerate(PACKAGES):
        sub_tmp = re.sub(exclusions, ".", package)
        if sub_tmp is not package:
            PACKAGES[index] = sub_tmp
    
    重要说明
  • 在使用上述方法时,重要的是要注意,如果在开发模式下安装了软件包,它们还不起作用(参见 this issue)。因此,最好使用第一种方法,因为开发人员仍然可以使用长模块名称,而用户也可以使用较短的模块名称。
  • 请记住,如果存在 __init__.py,上述方法将不起作用。命名空间包根文件夹中的文件(请参阅 setuputils documentation )。

  • 更新
    得到这个答案后,我试着把这个逻辑放到setup.cfg文件,以便它是 PEP517/518兼容的。在执行此操作时,我遇到了一些问题。可以在我在 the setuptools GitHub page 上创建的这个问题上找到解决方案。 .可以在 here 中找到示例存储库.

    关于python - 创建包含独立可安装子包的 namespace 包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63464555/

    相关文章:

    python - 不同错误代码的异常处理

    python - 从 wxpython 中的导入模块捕获标准输出并将其发送到 textctrl,而不阻塞 GUI

    python - 将两个一维数组连接成一个二维数组,并在Python中重新洗牌后再次将其打破

    python - setup.py, pip - 获取python包的原始执行路径

    python - 十进制转 JSON

    c#:在哪里放置我的 API 以及什么应该放入 MyCOmpanyName.MyProduct.Core(核心)命名空间?

    php - 使用相同的命名空间 php,调用未定义的函数

    c++ - 在为嵌入类定义方法时,是否有办法避免重复包含的类?

    python - 如何使用 install_requires 安装 git+https ://from setup. py

    python - 尝试运行 py.test 时收集 setup.py 时出错?