同一命名空间中的包:无法在安装脚本中导入模块
Packages in same namespace: can't import module in setup script
我很好奇下面的情况。假设我有两个名为 project_alpha
和 project_bravo
的项目,它们都定义了一个顶级命名空间包 mymeta
。布局:
project_alpha/
-> mymeta/
-> __init__.py
-> project_alpha/
-> __init__.py
-> version.py
-> setup.py
project_bravo/
-> mymeta/
-> __init__.py
-> project_bravo/
-> __init__.py
-> version.py
-> setup.py
两个 mymeta/__init__.py
都只包含行 __import__('pkg_resources').declare_namespace(__name__)
(根据 namespace section in setuptools docs)。两个 version.py
的内容:
__version_info__ = (0, 9, 9, 'dev0')
__version__ = '.'.join((str(entry) for entry in __version_info__))
project_alpha
的 setup.py
脚本非常简单。声明命名空间包mymeta
,版本取自version
模块:
# project_alpha
from setuptools import find_packages, setup
from mymeta.project_alpha.version import __version__
setup(
name='mymeta.project-alpha',
version=__version__,
namespace_packages=['mymeta'],
packages=find_packages(),
)
project_bravo
的 setup.py
脚本具有相同的结构,但有一点不同:project_bravo
在构建时依赖于 project_alpha
:
from setuptools import find_packages, setup
from mymeta.project_bravo.version import __version__
setup(
name='mymeta.project-bravo',
version=__version__,
namespace_packages=['mymeta'],
setup_requires=['mymeta.project-alpha'],
packages=find_packages(),
)
构建 project_bravo
时,出现以下错误:
~/project_bravo $ python setup.py sdist
Traceback (most recent call last):
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 156, in save_modules
yield saved
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 197, in setup_context
yield
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 246, in run_setup
DirectorySandbox(setup_dir).run(runner)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 276, in run
return func()
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 245, in runner
_execfile(setup_script, ns)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 47, in _execfile
exec(code, globals, locals)
File "/tmp/easy_install-ahmxos98/mymeta.project-alpha-0.9.9.dev0/setup.py", line 6, in <module>
try:
ImportError: No module named 'mymeta.project_alpha'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "setup.py", line 22, in <module>
packages=find_packages(),
File "/usr/lib64/python3.5/distutils/core.py", line 108, in setup
_setup_distribution = dist = klass(attrs)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/dist.py", line 315, in __init__
self.fetch_build_eggs(attrs['setup_requires'])
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/dist.py", line 361, in fetch_build_eggs
replace_conflicting=True,
File "/tmp/tstenv/lib/python3.5/site-packages/pkg_resources/__init__.py", line 853, in resolve
dist = best[req.key] = env.best_match(req, ws, installer)
File "/tmp/tstenv/lib/python3.5/site-packages/pkg_resources/__init__.py", line 1125, in best_match
return self.obtain(req, installer)
File "/tmp/tstenv/lib/python3.5/site-packages/pkg_resources/__init__.py", line 1137, in obtain
return installer(requirement)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/dist.py", line 429, in fetch_build_egg
return cmd.easy_install(req)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/command/easy_install.py", line 665, in easy_install
return self.install_item(spec, dist.location, tmpdir, deps)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/command/easy_install.py", line 695, in install_item
dists = self.install_eggs(spec, download, tmpdir)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/command/easy_install.py", line 876, in install_eggs
return self.build_and_install(setup_script, setup_base)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/command/easy_install.py", line 1115, in build_and_install
self.run_setup(setup_script, setup_base, args)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/command/easy_install.py", line 1101, in run_setup
run_setup(setup_script, args)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 249, in run_setup
raise
File "/usr/lib64/python3.5/contextlib.py", line 77, in __exit__
self.gen.throw(type, value, traceback)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 197, in setup_context
yield
File "/usr/lib64/python3.5/contextlib.py", line 77, in __exit__
self.gen.throw(type, value, traceback)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 168, in save_modules
saved_exc.resume()
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 143, in resume
six.reraise(type, exc, self._tb)
File "/tmp/tstenv/lib/python3.5/site-packages/pkg_resources/_vendor/six.py", line 685, in reraise
raise value.with_traceback(tb)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 156, in save_modules
yield saved
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 197, in setup_context
yield
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 246, in run_setup
DirectorySandbox(setup_dir).run(runner)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 276, in run
return func()
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 245, in runner
_execfile(setup_script, ns)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 47, in _execfile
exec(code, globals, locals)
File "/tmp/easy_install-ahmxos98/mymeta.project-alpha-0.9.9.dev0/setup.py", line 6, in <module>
try:
ImportError: No module named 'mymeta.project_alpha'
很遗憾,我不明白这里的错误。跟进口订单有关系吧?如果我在 project_bravo/setup.py
中注释掉 mymeta.project_bravo.version
的导入并将 version
替换为一些硬编码字符串,突然构建成功...
编辑:我介绍了这个问题的解决方法。我没有尝试直接导入版本,而是 exec
版本模块以避免导入问题。当然,这不是一个合适的解决方案,因此不会将其作为答案发布,但仍然在这里:
__version__ = None # if the exec fails, leave the version unset, the resulting build version will be 0.0.0
version_script_path = os.path.relpath(os.path.join(os.path.dirname(__file__), 'mymeta', 'project_alpha', 'version.py'))
with open(version_script_path) as version_script:
exec(version_script.read())
读取并执行版本脚本后,版本被初始化,因此不需要从mymeta
包中导入任何东西。
如评论中所述:命令
$ python setup.py sdist
for mymeta.project_bravo
从私有 pypi 存储库下载 mymeta.project_alpha
的 egg。
命名空间包的导入非常微妙。
例如我发现当我在我的环境中定期安装 ns.a
和 ns.b
并在 site-packages 目录的 .pth 文件中提供 ns.d
的导入路径时,它不会工作,无论。但是,当我符号链接到 site-packeges/ns 目录中的 ns.d
时,它将起作用。
如果我安装 none 个组件并在 site-packages 目录的 .pth 中提供所有路径,一切正常。
如果我定期安装所有组件,一切正常。
就在我混概念的时候不行。
因此我怀疑这也可能是这里的问题:导入路径的不同策略。
您可能想尝试将 __init__.py
文件修改为:
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
__import__('pkg_resources').declare_namespace(__name__)
我对我做命名空间包的决定感到遗憾。我再也不会这样做了。
差不多一年后,我再次遇到这个问题,python>=3.3
的解决方案是使用 PEP 420 中指定的隐式命名空间包。项目结构几乎没有变化,只是 pkgutil 样式命名空间包 mymeta
的 __init__.py
都消失了:
project_alpha/
-> mymeta/
-> project_alpha/
-> __init__.py
-> version.py
-> setup.py
project_bravo/
-> mymeta/
-> project_bravo/
-> __init__.py
-> version.py
-> setup.py
为了让 setuptools
满意,还需要调整两个安装脚本:
...
setup(
...
packages=['mymeta.' + pkg for pkg in find_packages('mymeta')],
)
现在在构建 project-bravo
时可以正确解析导入。
我很好奇下面的情况。假设我有两个名为 project_alpha
和 project_bravo
的项目,它们都定义了一个顶级命名空间包 mymeta
。布局:
project_alpha/
-> mymeta/
-> __init__.py
-> project_alpha/
-> __init__.py
-> version.py
-> setup.py
project_bravo/
-> mymeta/
-> __init__.py
-> project_bravo/
-> __init__.py
-> version.py
-> setup.py
两个 mymeta/__init__.py
都只包含行 __import__('pkg_resources').declare_namespace(__name__)
(根据 namespace section in setuptools docs)。两个 version.py
的内容:
__version_info__ = (0, 9, 9, 'dev0')
__version__ = '.'.join((str(entry) for entry in __version_info__))
project_alpha
的 setup.py
脚本非常简单。声明命名空间包mymeta
,版本取自version
模块:
# project_alpha
from setuptools import find_packages, setup
from mymeta.project_alpha.version import __version__
setup(
name='mymeta.project-alpha',
version=__version__,
namespace_packages=['mymeta'],
packages=find_packages(),
)
project_bravo
的 setup.py
脚本具有相同的结构,但有一点不同:project_bravo
在构建时依赖于 project_alpha
:
from setuptools import find_packages, setup
from mymeta.project_bravo.version import __version__
setup(
name='mymeta.project-bravo',
version=__version__,
namespace_packages=['mymeta'],
setup_requires=['mymeta.project-alpha'],
packages=find_packages(),
)
构建 project_bravo
时,出现以下错误:
~/project_bravo $ python setup.py sdist
Traceback (most recent call last):
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 156, in save_modules
yield saved
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 197, in setup_context
yield
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 246, in run_setup
DirectorySandbox(setup_dir).run(runner)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 276, in run
return func()
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 245, in runner
_execfile(setup_script, ns)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 47, in _execfile
exec(code, globals, locals)
File "/tmp/easy_install-ahmxos98/mymeta.project-alpha-0.9.9.dev0/setup.py", line 6, in <module>
try:
ImportError: No module named 'mymeta.project_alpha'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "setup.py", line 22, in <module>
packages=find_packages(),
File "/usr/lib64/python3.5/distutils/core.py", line 108, in setup
_setup_distribution = dist = klass(attrs)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/dist.py", line 315, in __init__
self.fetch_build_eggs(attrs['setup_requires'])
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/dist.py", line 361, in fetch_build_eggs
replace_conflicting=True,
File "/tmp/tstenv/lib/python3.5/site-packages/pkg_resources/__init__.py", line 853, in resolve
dist = best[req.key] = env.best_match(req, ws, installer)
File "/tmp/tstenv/lib/python3.5/site-packages/pkg_resources/__init__.py", line 1125, in best_match
return self.obtain(req, installer)
File "/tmp/tstenv/lib/python3.5/site-packages/pkg_resources/__init__.py", line 1137, in obtain
return installer(requirement)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/dist.py", line 429, in fetch_build_egg
return cmd.easy_install(req)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/command/easy_install.py", line 665, in easy_install
return self.install_item(spec, dist.location, tmpdir, deps)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/command/easy_install.py", line 695, in install_item
dists = self.install_eggs(spec, download, tmpdir)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/command/easy_install.py", line 876, in install_eggs
return self.build_and_install(setup_script, setup_base)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/command/easy_install.py", line 1115, in build_and_install
self.run_setup(setup_script, setup_base, args)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/command/easy_install.py", line 1101, in run_setup
run_setup(setup_script, args)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 249, in run_setup
raise
File "/usr/lib64/python3.5/contextlib.py", line 77, in __exit__
self.gen.throw(type, value, traceback)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 197, in setup_context
yield
File "/usr/lib64/python3.5/contextlib.py", line 77, in __exit__
self.gen.throw(type, value, traceback)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 168, in save_modules
saved_exc.resume()
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 143, in resume
six.reraise(type, exc, self._tb)
File "/tmp/tstenv/lib/python3.5/site-packages/pkg_resources/_vendor/six.py", line 685, in reraise
raise value.with_traceback(tb)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 156, in save_modules
yield saved
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 197, in setup_context
yield
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 246, in run_setup
DirectorySandbox(setup_dir).run(runner)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 276, in run
return func()
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 245, in runner
_execfile(setup_script, ns)
File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 47, in _execfile
exec(code, globals, locals)
File "/tmp/easy_install-ahmxos98/mymeta.project-alpha-0.9.9.dev0/setup.py", line 6, in <module>
try:
ImportError: No module named 'mymeta.project_alpha'
很遗憾,我不明白这里的错误。跟进口订单有关系吧?如果我在 project_bravo/setup.py
中注释掉 mymeta.project_bravo.version
的导入并将 version
替换为一些硬编码字符串,突然构建成功...
编辑:我介绍了这个问题的解决方法。我没有尝试直接导入版本,而是 exec
版本模块以避免导入问题。当然,这不是一个合适的解决方案,因此不会将其作为答案发布,但仍然在这里:
__version__ = None # if the exec fails, leave the version unset, the resulting build version will be 0.0.0
version_script_path = os.path.relpath(os.path.join(os.path.dirname(__file__), 'mymeta', 'project_alpha', 'version.py'))
with open(version_script_path) as version_script:
exec(version_script.read())
读取并执行版本脚本后,版本被初始化,因此不需要从mymeta
包中导入任何东西。
如评论中所述:命令
$ python setup.py sdist
for mymeta.project_bravo
从私有 pypi 存储库下载 mymeta.project_alpha
的 egg。
命名空间包的导入非常微妙。
例如我发现当我在我的环境中定期安装 ns.a
和 ns.b
并在 site-packages 目录的 .pth 文件中提供 ns.d
的导入路径时,它不会工作,无论。但是,当我符号链接到 site-packeges/ns 目录中的 ns.d
时,它将起作用。
如果我安装 none 个组件并在 site-packages 目录的 .pth 中提供所有路径,一切正常。
如果我定期安装所有组件,一切正常。
就在我混概念的时候不行。
因此我怀疑这也可能是这里的问题:导入路径的不同策略。
您可能想尝试将 __init__.py
文件修改为:
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
__import__('pkg_resources').declare_namespace(__name__)
我对我做命名空间包的决定感到遗憾。我再也不会这样做了。
差不多一年后,我再次遇到这个问题,python>=3.3
的解决方案是使用 PEP 420 中指定的隐式命名空间包。项目结构几乎没有变化,只是 pkgutil 样式命名空间包 mymeta
的 __init__.py
都消失了:
project_alpha/
-> mymeta/
-> project_alpha/
-> __init__.py
-> version.py
-> setup.py
project_bravo/
-> mymeta/
-> project_bravo/
-> __init__.py
-> version.py
-> setup.py
为了让 setuptools
满意,还需要调整两个安装脚本:
...
setup(
...
packages=['mymeta.' + pkg for pkg in find_packages('mymeta')],
)
现在在构建 project-bravo
时可以正确解析导入。