创建 name_space 个包含独立可安装子包的包
Create name_space package that contains standalone installable sub-packages
我目前正在尝试将多个相关包组合到一个父包中 (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
但是,我希望能够使用以下(较短的)导入语句导入 Class:
from meta_package.installable_subpackage.submodule.module import Class
我已经尝试过的
使用 name_space 个软件包
我尝试使用 namespace packages for the sub-packages instead of using normal packages. This, however, did not solve the extra folder problem and also introduced a number of python import traps。
在installable_subpackage.init.py文件
中导入冗余文件夹(模块)
我还尝试在 installable_subpackage.__init__.py
文件中导入 installable_subpackage
子模块:
import meta_package.installable_subpackage.installable_subpackage
但这似乎不起作用,因为 meta_package.installable_subpackage.submodule
导入路径没有指向 meta_package.installable_subpackage.installable_subpackage.submodule
模块。我认为这是因为此方法仅适用于 Classes 而不适用于模块。
使用安装工具包和 package_dir 参数
最后,根据to this issue,我尝试在 meta_package
setup.py 中使用 setuptools
packages
和 package_dir
参数摆脱额外的文件夹。为此,我使用了以下 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版本:Python3.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",
},
)
要为 sub-packages 个数字自动执行此操作,您可以使用以下代码:
# 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",
},
)
这也可以使用以下代码自动完成 sub-packages 的数量:
# 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 compatible. While doing this, I ran into some problems. The solution to this can be found on this issue I created on the setuptools GitHub page. An example repository can be found here.
我目前正在尝试将多个相关包组合到一个父包中 (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
但是,我希望能够使用以下(较短的)导入语句导入 Class:
from meta_package.installable_subpackage.submodule.module import Class
我已经尝试过的
使用 name_space 个软件包
我尝试使用 namespace packages for the sub-packages instead of using normal packages. This, however, did not solve the extra folder problem and also introduced a number of python import traps。
在installable_subpackage.init.py文件
中导入冗余文件夹(模块)我还尝试在 installable_subpackage.__init__.py
文件中导入 installable_subpackage
子模块:
import meta_package.installable_subpackage.installable_subpackage
但这似乎不起作用,因为 meta_package.installable_subpackage.submodule
导入路径没有指向 meta_package.installable_subpackage.installable_subpackage.submodule
模块。我认为这是因为此方法仅适用于 Classes 而不适用于模块。
使用安装工具包和 package_dir 参数
最后,根据to this issue,我尝试在 meta_package
setup.py 中使用 setuptools
packages
和 package_dir
参数摆脱额外的文件夹。为此,我使用了以下 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版本:Python3.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",
},
)
要为 sub-packages 个数字自动执行此操作,您可以使用以下代码:
# 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",
},
)
这也可以使用以下代码自动完成 sub-packages 的数量:
# 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 compatible. While doing this, I ran into some problems. The solution to this can be found on this issue I created on the setuptools GitHub page. An example repository can be found here.