python - 通过 setup.py 安装的程序中的 Python 导入
问题描述
考虑一个具有以下结构的模型项目:
.
├── mod1
│ ├── good_day.py
│ ├── __init__.py
│ └── say_hello.py
└── setup.py
say_hello.py
包含一个带有方法的类:
class SayHello():
def sayIt(self):
print("Hello World!")
good_day.py
SayHello
是一个使用类的小程序:
#!/usr/bin/python3
from say_hello import SayHello
hello = SayHello()
hello.sayIt()
print("And have a good day!")
该程序执行预期的操作:
$ ./good_day.py
Hello World!
And have a good day!
现在想象这个程序要以一个包的形式分发,这就是setup.py
进来的地方:
#!/usr/bin/python3
# coding=utf8
from setuptools import setup, find_packages
setup(
name = "good-day",
version = "0.1",
packages = find_packages(),
entry_points={
'console_scripts': [
'good_day=mod1.good_day',
],
},
)
但是在虚拟环境中安装程序的练习以悲剧告终:
$ python3 -m venv env
$ source env/bin/activate
(env) $ python3 setup.py install
running install
running bdist_egg
running egg_info
writing good_day.egg-info/PKG-INFO
writing dependency_links to good_day.egg-info/dependency_links.txt
writing entry points to good_day.egg-info/entry_points.txt
writing top-level names to good_day.egg-info/top_level.txt
reading manifest file 'good_day.egg-info/SOURCES.txt'
writing manifest file 'good_day.egg-info/SOURCES.txt'
installing library code to build/bdist.linux-x86_64/egg
running install_lib
running build_py
creating build/lib
creating build/lib/mod1
copying mod1/say_hello.py -> build/lib/mod1
copying mod1/__init__.py -> build/lib/mod1
copying mod1/good_day.py -> build/lib/mod1
creating build/bdist.linux-x86_64/egg
creating build/bdist.linux-x86_64/egg/mod1
copying build/lib/mod1/say_hello.py -> build/bdist.linux-x86_64/egg/mod1
copying build/lib/mod1/__init__.py -> build/bdist.linux-x86_64/egg/mod1
copying build/lib/mod1/good_day.py -> build/bdist.linux-x86_64/egg/mod1
byte-compiling build/bdist.linux-x86_64/egg/mod1/say_hello.py to say_hello.cpython-38.pyc
byte-compiling build/bdist.linux-x86_64/egg/mod1/__init__.py to __init__.cpython-38.pyc
byte-compiling build/bdist.linux-x86_64/egg/mod1/good_day.py to good_day.cpython-38.pyc
creating build/bdist.linux-x86_64/egg/EGG-INFO
copying good_day.egg-info/PKG-INFO -> build/bdist.linux-x86_64/egg/EGG-INFO
copying good_day.egg-info/SOURCES.txt -> build/bdist.linux-x86_64/egg/EGG-INFO
copying good_day.egg-info/dependency_links.txt -> build/bdist.linux-x86_64/egg/EGG-INFO
copying good_day.egg-info/entry_points.txt -> build/bdist.linux-x86_64/egg/EGG-INFO
copying good_day.egg-info/top_level.txt -> build/bdist.linux-x86_64/egg/EGG-INFO
zip_safe flag not set; analyzing archive contents...
creating 'dist/good_day-0.1-py3.8.egg' and adding 'build/bdist.linux-x86_64/egg' to it
removing 'build/bdist.linux-x86_64/egg' (and everything under it)
Processing good_day-0.1-py3.8.egg
Removing /home/user/src/py-mods/env/lib/python3.8/site-packages/good_day-0.1-py3.8.egg
Copying good_day-0.1-py3.8.egg to /home/user/src/py-mods/env/lib/python3.8/site-packages
good-day 0.1 is already the active version in easy-install.pth
Installing good_day script to /home/user/src/py-mods/env/bin
Installed /home/user/src/py-mods/env/lib/python3.8/site-packages/good_day-0.1-py3.8.egg
Processing dependencies for good-day==0.1
Finished processing dependencies for good-day==0.1
$ good_day
Traceback (most recent call last):
File "/home/user/src/py-mods/env/bin/good_day", line 11, in <module>
load_entry_point('good-day==0.1', 'console_scripts', 'good_day')()
File "/home/user/src/py-mods/env/lib/python3.8/site-packages/pkg_resources/__init__.py", line 489, in load_entry_point
return get_distribution(dist).load_entry_point(group, name)
File "/home/user/src/py-mods/env/lib/python3.8/site-packages/pkg_resources/__init__.py", line 2852, in load_entry_point
return ep.load()
File "/home/user/src/py-mods/env/lib/python3.8/site-packages/pkg_resources/__init__.py", line 2443, in load
return self.resolve()
File "/home/user/src/py-mods/env/lib/python3.8/site-packages/pkg_resources/__init__.py", line 2449, in resolve
module = __import__(self.module_name, fromlist=['__name__'], level=0)
File "<frozen zipimport>", line 259, in load_module
File "/home/user/src/py-mods/env/lib/python3.8/site-packages/good_day-0.1-py3.8.egg/mod1/good_day.py", line 3, in <module>
ModuleNotFoundError: No module named 'say_hello'
本质上,一旦安装,程序只能在指令明确引用模块时才能找到SayHello
类:import
from mod1.say_hello import SayHello
虽然此导入修复了通过setup
它安装的程序,但它会破坏直接执行:
$ mod1/good_day.py
Traceback (most recent call last):
File "mod1/good_day.py", line 3, in <module>
from mod1.say_hello import SayHello
ModuleNotFoundError: No module named 'mod1'
是什么赋予了?是否有任何公式可以成功导入已安装的程序及其直接执行?
更新:下面是直接使用AKX的建议运行程序的结果。首先是导入语句中的完整路径:
$ mod1/good_day.py
Traceback (most recent call last):
File "mod1/good_day.py", line 3, in <module>
from mod1.say_hello import SayHello
ModuleNotFoundError: No module named 'mod1'
然后使用相对导入路径:
$ mod1/good_day.py
Traceback (most recent call last):
File "mod1/good_day.py", line 3, in <module>
from .say_hello import SayHello
ImportError: attempted relative import with no known parent package
解决方案
您使用导入语法完全忽略了包,并使用例如python mod1/good_day.py
添加mod1/
到sys.path
首先运行事物,这就是绝对导入首先起作用的原因。
mod1/good_day.py
应该是例如
from mod1.say_hello import SayHello
# or "from .say_hello import SayHello"
def main():
hello = SayHello()
hello.sayIt()
print("And have a good day!")
if __name__ == "__main__":
main()
之后你可以运行
python -m mod1.good_day
事情应该会奏效。
入口点也需要稍微调整(添加main
):
'console_scripts': [
'good_day=mod1.good_day:main',
],
使用此配置,good_day
在安装和使用该入口点时应该可以工作。
推荐阅读
- algorithm - 给定从 1 到 k 的数字,选择 d 个数字,它们的总和等于 v
- autocomplete - Racer 不会自动完成前奏中的代码
- c# - 如何修复 - 应用程序因退出代码 80131506 而崩溃
- sql - CASE 表达式中的语法错误,无法纠正
- kotlin - return@map 的工作原理
- java - 确定从客户端到服务器的有效负载传输的 HTTP 方法
- terminal - 为什么终端将转义表示为 ^[?
- vb.net - 活动代表
- git - Git - 为什么这个命令不会在我的存储库中添加文件?
- docker - 带有本地服务器的 Docker/Kubernetes