Python 3.6.0隐式命名空间包

时间:2016-12-23 13:43:22

标签: python namespaces package python-3.6

我在Python 3.6.0rc1中发现了隐式命名空间包的奇怪行为。如果我错了或者它是Python 3.6错误,请你告诉我吗?

我正在使用命名空间包marrow,它有两个独立的包marrow.utilmarrow.mailer。第二个取决于第一个。

假设我们在marrow.util中为Python 2.7,3.5和3.6安装了site-packages

$ ls -la /usr/lib/python*/site-packages/marrow
/usr/lib/python2.7/site-packages/marrow:
total 24
drwxr-xr-x.   3 root root  4096 Dec 23 12:23 .
drwxr-xr-x. 196 root root 16384 Dec 23 12:23 ..
drwxr-xr-x.   3 root root  4096 Dec 23 12:23 util

/usr/lib/python3.5/site-packages/marrow:
total 12
drwxr-xr-x.  3 root root 4096 Dec 23 12:24 .
drwxr-xr-x. 99 root root 4096 Dec 23 12:24 ..
drwxr-xr-x.  4 root root 4096 Dec 23 12:24 util

/usr/lib/python3.6/site-packages/marrow:
total 12
drwxr-xr-x.  3 root root 4096 Dec 23 14:25 .
drwxr-xr-x. 37 root root 4096 Dec 23 14:25 ..
drwxr-xr-x.  4 root root 4096 Dec 23 14:25 util

此处没有__init__.py个文件,这是正确的,因为marrow是命名空间包。您可以在安装期间看到此日志消息:

Skipping installation of <deleted>/site-packages/marrow/__init__.py (namespace package)

然后你在其他目录中构建了marrow命名空间包marrow.mailer的第二部分(但未安装)。例如:

$ pwd
/builddir/build/BUILD/marrow.mailer-4.0.2

$ ls
coverage.xml debuglinks.list elfbins.list LICENSE.txt marrow.mailer.egg-info README.textile setup.py debugfiles.list debugsources.list example marrow PKG-INFO setup.cfg test

$ ls marrow/
__init__.py  __init__.pyc  mailer  __pycache__

当我在此文件夹中运行Python 2.7.12或3.5.2并尝试导入marrow.util(来自site-packages)时,它按预期工作。

$ pwd
/builddir/build/BUILD/marrow.mailer-4.0.2

$ python2
Python 2.7.12 (default, Sep 29 2016, 12:52:02) 
[GCC 6.2.1 20160916 (Red Hat 6.2.1-2)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import marrow.util
>>>

$ python3.5
Python 3.5.2 (default, Sep 14 2016, 11:28:32) 
[GCC 6.2.1 20160901 (Red Hat 6.2.1-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import marrow.util
>>>

但是当我尝试使用Python 3.6导入相同的模块时,它失败了:

$ python3.6
Python 3.6.0rc1 (default, Dec 10 2016, 14:50:33) 
[GCC 6.2.1 20160916 (Red Hat 6.2.1-2)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import marrow.util
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'marrow.util'
>>> 

当我尝试在Mock中将marrow.mailer构建为RPM包时,我发现了这个问题。使用Python 2.7和3.5一切正常,但Python 3.6无法从site-packages导入marrow.util,因此marrow.mailer的测试在RPM构建期间失败。

失败测试的示例回溯:

Traceback:
test/test_addresses.py:8: in <module>
    from marrow.mailer.address import Address, AddressList, AutoConverter
marrow/mailer/__init__.py:12: in <module>
    from marrow.mailer.message import Message
marrow/mailer/message.py:21: in <module>
    from marrow.mailer.address import Address, AddressList, AutoConverter
marrow/mailer/address.py:12: in <module>
    from marrow.util.compat import basestring, unicode, unicodestr, native
E   ModuleNotFoundError: No module named 'marrow.util'

我在Changelog for Python 3.6中找不到与此问题相关的任何内容。

感谢您的帮助。

编辑:我在Python 3.6中检查了sys.path,一切看起来都不错:

$ python3.6
Python 3.6.0rc1 (default, Dec 10 2016, 14:50:33) 
[GCC 6.2.1 20160916 (Red Hat 6.2.1-2)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
['', '/usr/lib64/python36.zip', '/usr/lib64/python3.6', '/usr/lib64/python3.6/lib-dynload', '/usr/lib64/python3.6/site-packages', '/usr/lib/python3.6/site-packages']
>>>

编辑2:

因为我仍然找不到任何解决方案而我没有任何回复,所以我创建了一个简单的Bash脚本,它可以重现我的情况。 您唯一需要的是Python 3.5和Python 3.6。

#!/bin/bash

# Change this to run script with different Python
#PYTHON=python3.5                   # system Python 3.5
PYTHON=~/temp/Python-3.6.0/python # compiled Python 3.6

# Create venv and activate
$PYTHON -m venv venv
source ./venv/bin/activate

# Install marrow.util package as a part of namespace package marrow
pip install marrow.util

# Create simple folder structure
mkdir -p marrow/mailer

# Create structure of __init__.py files
# For namespace package with related content
cat >> marrow/__init__.py << EOL
try: # pragma: no cover
    __import__('pkg_resources').declare_namespace(__name__)
except ImportError: # pragma: no cover
    __import__('pkgutil').extend_path(__path__, __name__)
EOL

# For mailer module just with print()
cat >> marrow/mailer/__init__.py << EOL
print('Imported!!!')
EOL

# Testing
# Importing marrow.util installed via pip in venv
$PYTHON -c "import marrow.util"
# Importing marrow.mailer created manually in PWD
$PYTHON -c "import marrow.mailer"

# deactivate venv
deactivate

如果使用Python 3.5执行此脚本,您将看到Python 3.5可以通过pip导入marrow.util,但无法在本地文件夹中导入marrow.mailer。 但是Python 3.6可以导入本地模块marrow.mailer,但它无法导入模块marrow.util

1 个答案:

答案 0 :(得分:0)

这些软件包未使用隐式命名空间(“本机名称空间”),或者,如果您确实有版本,请固定您的依赖项以确保您不混合使用新旧样式的名称空间。它们是完全不兼容的方法。

您似乎在您的MCVE示例代码中尝试构建一个名称空间包({marrow/__init__.py通过旧的Python 2显式声明替换技巧进行声明),也就是A.K.A。 pkg-resources-style namespace packages。这需要一个setup.py(实际包装)的参数以及通过软件包安装来安装元数据。具体来说,此方法涉及.pth个文件技巧(在$VIRTUAL_ENV/lib/python3.?/site-packages中查找)(如果是在“开发中”安装的)以及提取/解压缩到该安装路径中。否则,就没有真正的名称空间,并且没有这种较旧的样式,也将找不到您的代码。 (第一个(已安装的)将获胜。)

在REPL中,您可以导入名称空间,例如import marrow,然后检查marrow.__path__,以发现/发现了哪些东西作为诊断辅助;我当前在这台计算机上的WIP虚拟环境中有m.packagem.schemam.interface,这与我最近在构建这些版本时是很有意义的。更native namespacing的更现代方法的确允许进行更多自由形式的混合,这种想法是,没有__init__.py只是的文件夹是一个命名空间,它会自动合并到{{ 1}},但是,这并不是以前使用命名空间的方式。 (所有参与者都需要该存根PYTHONPATH,并且在所有命名空间级别上都没有其他代码。)

我正在使整个Marrow生态系统现代化(如上所述,我已经开始了其中的一些工作),以消除Python 2的遗产,并开始采用新的Python 3结构和方法,包括现代命名空间。对于仍然需要旧名称空间或在Python 2上需要旧代码的所有代码,应将所有内容和依赖项的主要版本颠簸保持固定在这些版本以下。

(我正在购买本地的Python 3.6和3.5进行进一步研究。)