Python,从 package name 得到 module name
2016-06-03 14:05
405 查看
探索
怎样从包名得到模块名?可以把 pypi 上的所有包爬下来,然后解析出对应的模块就可以了。
其中解析模块显然是一个难点。因为我要的不仅仅是模块,还包括子模块。这会涉及到文件结构和安装逻辑,工作量比较大。
另一个能想到的思路是研究 pip。因为 pip 本身做的就是包名到模块名的过程。
pip
pip 命令有哪些参数?E:\workspace>pip -h Usage: pip <command> [options] Commands: install Install packages. uninstall Uninstall packages. freeze Output installed packages in requirements format. list List installed packages. show Show information about installed packages. search Search PyPI for packages. wheel Build wheels from your requirements. zip DEPRECATED. Zip individual packages. unzip DEPRECATED. Unzip individual packages. bundle DEPRECATED. Create pybundles. help Show help for commands. General Options: -h, --help Show help. -v, --verbose Give more output. Option is additive, and can be used up to 3 times. -V, --version Show version and exit. -q, --quiet Give less output. --log-file <path> Path to a verbose non-appending log, that only logs failures. This log is active by default at C:\Users\Administrator\pip\pip.log. --log <path> Path to a verbose appending log. This log is inactive by default. --proxy <proxy> Specify a proxy in the form [user:passwd@]proxy.server:port. --timeout <sec> Set the socket timeout (default 15 seconds). --exists-action <action> Default action when a path already exists: (s)witch, (i)gnore, (w)ipe, (b)ackup. --cert <path> Path to alternate CA bundle.
list 比较有意思,能列出所有的包名
show 能看到指定包名的信息
可是还是没能到达模块名这一步,import 的时候应该 import 什么?
突然想到 import 的机制中,是根据 sys.path 中的路径来查找的。
那 list 命令是通过什么来找到的包呢?感觉这个点可能成为突破口。
研究下 pip list 的运行过程。
win 下是 pip.exe,不好研究,祭出我的 msys2
$ cat /usr/bin/pip #!/usr/bin/python2 # EASY-INSTALL-ENTRY-SCRIPT: 'pip==8.1.2','console_scripts','pip' __requires__ = 'pip==8.1.2' import sys from pkg_resources import load_entry_point if __name__ == '__main__': sys.exit( load_entry_point('pip==8.1.2', 'console_scripts', 'pip')() )
load_entry_point又是什么啊?
In [1]: from pkg_resources import load_entry_point In [2]: load_entry_point? Signature: load_entry_point(dist, group, name) Docstring: Return `name` entry point of `group` for `dist` or raise ImportError File: d:\python27\lib\site-packages\pkg_resources\__init__.py Type: function
大概是一个模块实例化的函数
感觉这个模块会对我们的目标有帮助?
赶紧补一下
看源码了解到,它是解析了包中的
entry_points.txt文件
(group, name) 对应了文件中的 (section, key)
值是一个函数名
2016年6月12日 13:05:50 更新
跟踪
pip list命令到
pip/util.py:get_installed_distributions
代码如下
def get_installed_distributions(local_only=True, skip=('setuptools', 'pip', 'python', 'distribute'), include_editables=True, editables_only=False): """ Return a list of installed Distribution objects. If ``local_only`` is True (default), only return installations local to the current virtualenv, if in a virtualenv. ``skip`` argument is an iterable of lower-case project names to ignore; defaults to ('setuptools', 'pip', 'python'). [FIXME also skip virtualenv?] If ``editables`` is False, don't report editables. If ``editables_only`` is True , only report editables. """ if local_only: local_test = dist_is_local else: local_test = lambda d: True if include_editables: editable_test = lambda d: True else: editable_test = lambda d: not dist_is_editable(d) if editables_only: editables_only_test = lambda d: dist_is_editable(d) else: editables_only_test = lambda d: True return [d for d in pkg_resources.working_set if local_test(d) and d.key not in skip and editable_test(d) and editables_only_test(d) ]
发现其中的关键正是
pkg_resources.working_set,运行一下,发现里面存储了当前环境的所有包信息。
继续跟踪,找到
pkg_resources.py:find_on_path,大部分的 list 功能应该是由这个函数完成的。
def find_on_path(importer, path_item, only=False): """Yield distributions accessible on a sys.path directory""" path_item = _normalize_cached(path_item) if os.path.isdir(path_item) and os.access(path_item, os.R_OK): if path_item.lower().endswith('.egg'): # unpacked egg yield Distribution.from_filename( path_item, metadata=PathMetadata( path_item, os.path.join(path_item,'EGG-INFO') ) ) else: # scan for .egg and .egg-info in directory for entry in os.listdir(path_item): lower = entry.lower() if lower.endswith('.egg-info') or lower.endswith('.dist-info'): fullpath = os.path.join(path_item, entry) if os.path.isdir(fullpath): # egg-info directory, allow getting metadata metadata = PathMetadata(path_item, fullpath) else: metadata = FileMetadata(fullpath) yield Distribution.from_location( path_item,entry,metadata,precedence=DEVELOP_DIST ) elif not only and lower.endswith('.egg'): for dist in find_distributions(os.path.join(path_item, entry)): yield dist elif not only and lower.endswith('.egg-link'): entry_file = open(os.path.join(path_item, entry)) try: entry_lines = entry_file.readlines() finally: entry_file.close() for line in entry_lines: if not line.strip(): continue for item in find_distributions(os.path.join(path_item,line.rstrip())): yield item break
看一遍代码,问题的答案就呼之欲出了:
怎样得到已经安装的第三方库信息呢?
答:都写在 EGG INFO / DIST INFO!
但这和我们的需求还不太一样,现在看来 pip 处理的是包名。它的搜索、安装、存储都是通过包名来完成的。要想获得包名对应的模块名,还需要自己的操作。
我们可以发现,同目录下还有别的文件,这些文件中的信息都很有用
比如 top_level.txt 中存储了顶级模块名,配合 pkgutil,基本可以解决我们的问题了
现在我们可以做到:对于已经安装的包,通过包名找到所有模块
总结
以为很复杂的一个功能,到头来才发现都写到了包的声明文件里。再来一遍的话,什么样的建议可以避免或者快速解决这个问题呢?
答:如果我提交过自己的 python 第三方库,肯定就不会有这些疑问了。
还是要多探索,多经历!
相关文章推荐
- Python动态类型的学习---引用的理解
- Python3写爬虫(四)多线程实现数据爬取
- 垃圾邮件过滤器 python简单实现
- 下载并遍历 names.txt 文件,输出长度最长的回文人名。
- install and upgrade scrapy
- Scrapy的架构介绍
- Centos6 编译安装Python
- 使用Python生成Excel格式的图片
- 让Python文件也可以当bat文件运行
- [Python]推算数独
- Python中zip()函数用法举例
- Python中map()函数浅析
- Python将excel导入到mysql中
- Python在CAM软件Genesis2000中的应用
- 使用Shiboken为C++和Qt库创建Python绑定
- FREEBASIC 编译可被python调用的dll函数示例
- Python 七步捉虫法