原文链接:
amir.rachum.com/blog/2017/0…
(随心所欲翻译.)
这篇文章将会聊聊python代码中的入口点.很多人可能知道,入口点一般会放在setup.py
文件中,经过打包后可以作为命令行使用,不过很少有人会去用它.接下来,我将会介绍如何使用入口点生成python包,之后就可以让别人在代码中或者命令行下使用.
安装过python包的都知道使用pip install <somepackages>
, setup.py
就是定义了你自己开发的包的相关信息,然后可以打包上传到python的包管理中心供别人下载.而入口点entry_points
可以让包作为命令行工具使用.
Snek,Inc.
恭喜你!你刚刚被任命为"snek半导体和软件公司"的CEO,作为CEO,你的第一个工作是让员工开发出snek的原型系统,很快,工程师就做出来了,它是一个python脚本,snek.py
:
ascii_snek = """\
--..,_ _,.--.
`'.'. .'`__ o `;__.
'.'. .'.'` '---'` `
'.`'--....--'`.'
`'--....--'`
"""
def main():
print(ascii_snek)
if __name__ == '__main__':
main()
在公司产品发布的那一天,你演示了这个原型系统,运行很顺利:
$ python snek.py
--..,_ _,.--.
`'.'. .'`__ o `;__.
'.'. .'.'` '---'` `
'.`'--....--'`.'
`'--....--'`
SaaS--Snek as a Service
不幸的是,用户不会使用python,他们只想在电脑的命名行下直接使用Snek的服务,于是工程师们加班加点作出了下一个版本,让snek在安装时自动生成控制台脚本,这样就可以在命令行下运行了.他们是怎么做的?只需要在setup.py
脚本中包含包名,依赖等信息,像下面这样:
下面的代码就是指定了应该从snek脚本的main函数开始执行.
from setuptools import setup
setup(
name='snek',
entry_points={
'console_scripts': [
'snek = snek:main',
],
}
)
工程师说,控制台脚本就是特殊的入口点,setuptools
在你的包被别人安装时,读取其中的内容并生成某些脚本文件.现在,让我们从源代码中安装(当然你可以发布为python库,让别人使用pip下载安装):
$ python setup.py develop
running develop
running egg_info
writing snek.egg-info\PKG-INFO
writing dependency_links to snek.egg-info\dependency_links.txt
writing entry points to snek.egg-info\entry_points.txt
writing top-level names to snek.egg-info\top_level.txt
reading manifest file 'snek.egg-info\SOURCES.txt'
writing manifest file 'snek.egg-info\SOURCES.txt'
running build_ext
Creating c:\program files (x86)\py36-32\lib\site-packages\snek.egg-link (link to .)
snek 0.0.0 is already the active version in easy-install.pth
Installing snek-script.py script to C:\Program Files (x86)\Py36-32\Scripts
Installing snek.exe script to C:\Program Files (x86)\Py36-32\Scripts
Installing snek.exe.manifest script to C:\Program Files (x86)\Py36-32\Scripts
Installed c:\users\rachum\notebooks
Processing dependencies for snek==0.0.0
Finished processing dependencies for snek==0.0.0
在公司的年度产品会上,你展示了这个amazing的产品:
$ snek
--..,_ _,.--.
`'.'. .'`__ o `;__.
'.'. .'.'` '---'` `
'.`'--....--'`.'
`'--....--'`
Snek for Everyone
没有人不喜欢Snek,公司的IPO超过了60亿美元.有些高级用户希望为他们量身定制高级的版本.工程师又加班加点做了出来:
"""Print an ASCII Snek.
Usage:
snek [--type=TYPE]
"""
import docopt
normal_snek = """\
--..,_ _,.--.
`'.'. .'`__ o `;__.
'.'. .'.'` '---'` `
'.`'--....--'`.'
`'--....--'`
"""
fancy_snek = """\
_,..,,,_
'``````^~"-,_`"-,_
.-~c~-. `~:. ^-.
`~~~-.c ; `:. `-, _.-~~^^~:.
`. ; _,--~~~~-._ `:. ~. .~ `.
.` ;' .:` `: `:. ` _.:-,. `.
.' .: :' _.-~^~-. `. `..' .: `. '
: .' _:' .-' `. :. .: .'`. : ;
: `-' .:' `. `^~~^` .:. `. ; ;
`-.__,-~ ~-. ,' ': '.__.` :'
~--..--' ':. .:'
':..___.:'
"""
def get_sneks():
return {
'normal': normal_snek,
'fancy': fancy_snek,
}
def main():
args = docopt.docopt(__doc__)
snek_type = args['--type'] or 'normal'
print(get_sneks()[snek_type])
if __name__ == '__main__':
main()
他们加了一个豪华版的Snek,这让高级用户特别开心.
$ snek
--..,_ _,.--.
`'.'. .'`__ o `;__.
'.'. .'.'` '---'` `
'.`'--....--'`.'
`'--....--'`
$ snek --type fancy
_,..,,,_
'``````^~"-,_`"-,_
.-~c~-. `~:. ^-.
`~~~-.c ; `:. `-, _.-~~^^~:.
`. ; _,--~~~~-._ `:. ~. .~ `.
.` ;' .:` `: `:. ` _.:-,. `.
.' .: :' _.-~^~-. `. `..' .: `. '
: .' _:' .-' `. :. .: .'`. : ;
: `-' .:' `. `^~~^` .:. `. ; ;
`-.__,-~ ~-. ,' ': '.__.` :'
~--..--' ':. .:'
':..___.:'
Snek International Community
全球数百万人都在使用snek,人们对snek不同版本的需求越来越高,而且一些snek的专业用户(程序员)要求snek可以定制,他们想开发自己的snek版本.
"""Print an ASCII Snek.
Usage:
snek [--type=TYPE]
"""
import docopt
import pkg_resources
normal_snek = """\
--..,_ _,.--.
`'.'. .'`__ o `;__.
'.'. .'.'` '---'` `
'.`'--....--'`.'
`'--....--'`
"""
fancy_snek = """\
_,..,,,_
'``````^~"-,_`"-,_
.-~c~-. `~:. ^-.
`~~~-.c ; `:. `-, _.-~~^^~:.
`. ; _,--~~~~-._ `:. ~. .~ `.
.` ;' .:` `: `:. ` _.:-,. `.
.' .: :' _.-~^~-. `. `..' .: `. '
: .' _:' .-' `. :. .: .'`. : ;
: `-' .:' `. `^~~^` .:. `. ; ;
`-.__,-~ ~-. ,' ': '.__.` :'
~--..--' ':. .:'
':..___.:'
"""
def get_sneks():
sneks = {
'normal': normal_snek,
'fancy': fancy_snek,
}
for entry_point in pkg_resources.iter_entry_points('snek_types'):
sneks[entry_point.name] = entry_point.load()
return sneks
def main():
args = docopt.docopt(__doc__)
snek_type = args['--type'] or 'normal'
print(get_sneks()[snek_type])
if __name__ == '__main__':
main()
他们增加了snek的基础架构,当snek运行时,使用被称作snek_types
的入口点注册其他类型的snek,这样就能动态的在控制台输出不同的snek版本.
具体来说,get_sneks
中的pkg_resources.iter_entry_points('snek_types')
遍历所有注册到入口点的snek名,然后加入snek版本中.于是,开发者们加入了一个可爱版本的snek,命名为cute_snek.py
:
cute_snek = r"""
/^\/^\
_|__| O|
\/ /~ \_/ \
\____|__________/ \
\_______ \
`\ \ \
| | \
/ / \
/ / \
/ / \ \
/ / \ \
/ / _----_ \ \
/ / _-~ ~-_ | |
( ( _-~ _--_ ~-_ _/ |
\ ~-____-~ _-~ ~-_ ~-_-~ /
~-_ _-~ ~-_ _-~
~--______-~ ~-___-~
"""
如何找到这个可爱版本的snek呢?像下面这样定义setup.py
:
代码中指定了从cute_snek.py
脚本寻找cute_snek.
from setuptools import setup
setup(
name='cute_snek',
entry_points={
'snek_types': [
'cute = cute_snek:cute_snek',
],
}
)
然后像之前一样打包:
$ cd cute_snek && python setup.py develop
running develop
running egg_info
writing cute_snek.egg-info\PKG-INFO
writing dependency_links to cute_snek.egg-info\dependency_links.txt
writing entry points to cute_snek.egg-info\entry_points.txt
writing top-level names to cute_snek.egg-info\top_level.txt
reading manifest file 'cute_snek.egg-info\SOURCES.txt'
writing manifest file 'cute_snek.egg-info\SOURCES.txt'
running build_ext
Creating c:\program files (x86)\py36-32\lib\site-packages\cute-snek.egg-link (link to .)
cute-snek 0.0.0 is already the active version in easy-install.pth
Installed c:\users\rachum\cute_snek
Processing dependencies for cute-snek==0.0.0
Finished processing dependencies for cute-snek==0.0.0
现在我们就能在命令行下成功的运行这个可爱版本的snek,这是从
cute_snek
中动态加载的:
$ snek --type cute
/^\/^\
_|__| O|
\/ /~ \_/ \
\____|__________/ \
\_______ \
`\ \ \
| | \
/ / \
/ / \
/ / \ \
/ / \ \
/ / _----_ \ \
/ / _-~ ~-_ | |
( ( _-~ _--_ ~-_ _/ |
\ ~-____-~ _-~ ~-_ ~-_-~ /
~-_ _-~ ~-_ _-~
~--______-~ ~-___-~
既然cute_snek可以动态的加载,那么所有类型的snek应该都可以动态加载,于是工程师们又修改了代码:
所有的snek都从snek_types
中加载.
from setuptools import setup
setup(
name='cute_snek',
entry_points={
'snek_types': [
'cute = cute_snek:cute_snek',
'normal = snek:normal_snek',
'fancy = snek:fancy_snek',
],
}
)
# 同时修改之前文件中的get_sneks()函数:
def get_sneks():
#sneks = {
# 'normal': normal_snek,
# 'fancy': fancy_snek,
#}
# 将snek修改为空字典
snek = {}
for entry_point in pkg_resources.iter_entry_points('snek_types'):
sneks[entry_point.name] = entry_point.load()
return sneks
现在重新打包snek:
$ python setup.py develop
running develop
running egg_info
writing snek.egg-info\PKG-INFO
writing dependency_links to snek.egg-info\dependency_links.txt
writing entry points to snek.egg-info\entry_points.txt
writing top-level names to snek.egg-info\top_level.txt
reading manifest file 'snek.egg-info\SOURCES.txt'
writing manifest file 'snek.egg-info\SOURCES.txt'
running build_ext
Creating c:\program files (x86)\py36-32\lib\site-packages\snek.egg-link (link to .)
snek 0.0.0 is already the active version in easy-install.pth
Installing snek-script.py script to C:\Program Files (x86)\Py36-32\Scripts
Installing snek.exe script to C:\Program Files (x86)\Py36-32\Scripts
Installing snek.exe.manifest script to C:\Program Files (x86)\Py36-32\Scripts
Installed c:\users\rachum\notebooks
Processing dependencies for snek==0.0.0
Finished processing dependencies for snek==0.0.0
大功告成!!!
$ snek
--..,_ _,.--.
`'.'. .'`__ o `;__.
'.'. .'.'` '---'` `
'.`'--....--'`.'
`'--....--'`
$ snek --type fancy
_,..,,,_
'``````^~"-,_`"-,_
.-~c~-. `~:. ^-.
`~~~-.c ; `:. `-, _.-~~^^~:.
`. ; _,--~~~~-._ `:. ~. .~ `.
.` ;' .:` `: `:. ` _.:-,. `.
.' .: :' _.-~^~-. `. `..' .: `. '
: .' _:' .-' `. :. .: .'`. : ;
: `-' .:' `. `^~~^` .:. `. ; ;
`-.__,-~ ~-. ,' ': '.__.` :'
~--..--' ':. .:'
':..___.:'
$ snek --type cute
/^\/^\
_|__| O|
\/ /~ \_/ \
\____|__________/ \
\_______ \
`\ \ \
| | \
/ / \
/ / \
/ / \ \
/ / \ \
/ / _----_ \ \
/ / _-~ ~-_ | |
( ( _-~ _--_ ~-_ _/ |
\ ~-____-~ _-~ ~-_ ~-_-~ /
~-_ _-~ ~-_ _-~
~--______-~ ~-___-~
到此为止,你应该大概知道了如何使用python中的入口点了吧.