仅使用 PyPI 包名称以编程方式获取模块名称
Get module name programmatically with only PyPI package name
我想根据软件包名称列表以编程方式安装和导入软件包。对于大多数包来说,这不是问题,因为包名和模块名是相同的。
但是,PyYAML
包是一个例外,因为它的模块被简单地调用 yaml
,而且可能还有更多例外。
这是我用来安装和导入 packages/modules 的 python 函数:
def install_and_import(package):
import importlib
try:
importlib.import_module(package) #needs module name!
except ImportError:
import pip
pip.main(['install', package]) #needs package name
finally:
globals()[package] = importlib.import_module(package)
为列表中的每个包调用函数,['backoff', 'pyyaml']
(从 requirements.txt
解析),我得到:
Collecting backoff
Installing collected packages: backoff
Successfully installed backoff-1.4.3
Collecting pyyaml
Installing collected packages: pyyaml
Successfully installed pyyaml-3.12
[...Trackback...]
ModuleNotFoundError: No module named 'pyyaml'
有没有办法,只给定包名(例如,pyyaml
),找出我实际需要导入的模块的名称(例如,yaml
)?
在模块名称中使用 distlib
(pip install distlib
) 和一个 hacky "guess" (这可以改进,但想在我必须得到之前给你我想出的东西回到其他事情上!)
import os.path
import sys
import distlib.database
def to_module(s):
parts = os.path.splitext(s)[0].split(os.sep)
if s.endswith('.py'):
if parts[-1] == '__init__':
parts.pop()
elif s.endswith('.so'):
parts[-1], _, _ = parts[-1].partition('.')
return '.'.join(parts)
def main():
dp = distlib.database.DistributionPath()
dist = dp.get_distribution(sys.argv[1])
for f, _, _ in dist.list_installed_files():
if f.endswith(('.py', '.so')):
print(to_module(f))
if __name__ == '__main__':
exit(main())
to_module
很容易解释,我使用 DistributionPath()
("installed" 模块的表示)来查询已安装的特定包。我从中列出文件,如果它们看起来像模块,则将它们转换为模块。请注意,这不会捕捉到 six
(动态添加 six.moves
模块)之类的东西,但它是一个非常好的 first-order 近似值。
我在这里也对 posix 做出假设,对于您需要调整的其他平台(例如 windows,我相信它将使用 .pyd
)。
示例输出:
$ python test.py pyyaml
_yaml
yaml
yaml.composer
yaml.constructor
yaml.cyaml
yaml.dumper
yaml.emitter
yaml.error
yaml.events
yaml.loader
yaml.nodes
yaml.parser
yaml.reader
yaml.representer
yaml.resolver
yaml.scanner
yaml.serializer
yaml.tokens
$ python test.py coverage
coverage.pickle2json
coverage.execfile
coverage.python
coverage.summary
coverage.html
coverage.plugin
coverage.pytracer
coverage.config
coverage.__main__
coverage.data
coverage.debug
coverage.annotate
coverage.backward
coverage.parser
coverage.misc
coverage.files
coverage.multiproc
coverage.backunittest
coverage.env
coverage
coverage.control
coverage.cmdline
coverage.results
coverage.version
coverage.plugin_support
coverage.templite
coverage.collector
coverage.xmlreport
coverage.report
coverage.phystokens
coverage.bytecode
coverage.tracer
coverage.fullcoverage.encodings
基于 Anthony Sottile 的 ,我创建了一个简化版本以从包中提供一个模块。大多数适合我的情况的软件包都有一个主模块。 (当然,处理具有多个 "main" 模块的更复杂的包会更好。)
在 Windows 上进行测试,我发现 .list_installed_files()
存在一些问题(其中一些已在 "solution" 中解决):
- os.sep 未根据分发类型正确拆分文件名。 (鸡蛋走 os.sep 而轮子走 posix 方向。)
- 对于某些发行版,您可以获得完整路径(看起来像鸡蛋)。这会导致疯狂的模块名称猜测(例如,'C:.Users.Username.AppData.RestOfPath.File')。
这会搜索第一个__init__.py
来通知模块名称。如果找不到,它只是 returns 包名称(对我来说涵盖了 90% 的情况)。
def package_to_module(package):
dp = distlib.database.DistributionPath(include_egg=True)
dist = dp.get_distribution(package)
if dist is None:
raise ModuleNotFoundError
module = package # until we figure out something better
for filename, _, _ in dist.list_installed_files():
if filename.endswith(('.py')):
parts = os.path.splitext(filename)[0].split(os.sep)
if len(parts) == 1: # windows sep varies with distribution type
parts = os.path.splitext(filename)[0].split('/')
if parts[-1].startswith('_') and not parts[-1].startswith('__'):
continue # ignore internals
elif filename.endswith('.py') and parts[-1] == '__init__':
module = parts[-2]
break
return module
一些示例:
>>> package_to_module("pyyaml")
'yaml'
>>> package_to_module("click")
'click'
>>> package_to_module("six")
'six'
>>> package_to_module("pip")
'pip'
>>> package_to_module("doesntexist")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in package_to_module
ModuleNotFoundError
我想根据软件包名称列表以编程方式安装和导入软件包。对于大多数包来说,这不是问题,因为包名和模块名是相同的。
但是,PyYAML
包是一个例外,因为它的模块被简单地调用 yaml
,而且可能还有更多例外。
这是我用来安装和导入 packages/modules 的 python 函数:
def install_and_import(package):
import importlib
try:
importlib.import_module(package) #needs module name!
except ImportError:
import pip
pip.main(['install', package]) #needs package name
finally:
globals()[package] = importlib.import_module(package)
为列表中的每个包调用函数,['backoff', 'pyyaml']
(从 requirements.txt
解析),我得到:
Collecting backoff
Installing collected packages: backoff
Successfully installed backoff-1.4.3
Collecting pyyaml
Installing collected packages: pyyaml
Successfully installed pyyaml-3.12
[...Trackback...]
ModuleNotFoundError: No module named 'pyyaml'
有没有办法,只给定包名(例如,pyyaml
),找出我实际需要导入的模块的名称(例如,yaml
)?
在模块名称中使用 distlib
(pip install distlib
) 和一个 hacky "guess" (这可以改进,但想在我必须得到之前给你我想出的东西回到其他事情上!)
import os.path
import sys
import distlib.database
def to_module(s):
parts = os.path.splitext(s)[0].split(os.sep)
if s.endswith('.py'):
if parts[-1] == '__init__':
parts.pop()
elif s.endswith('.so'):
parts[-1], _, _ = parts[-1].partition('.')
return '.'.join(parts)
def main():
dp = distlib.database.DistributionPath()
dist = dp.get_distribution(sys.argv[1])
for f, _, _ in dist.list_installed_files():
if f.endswith(('.py', '.so')):
print(to_module(f))
if __name__ == '__main__':
exit(main())
to_module
很容易解释,我使用 DistributionPath()
("installed" 模块的表示)来查询已安装的特定包。我从中列出文件,如果它们看起来像模块,则将它们转换为模块。请注意,这不会捕捉到 six
(动态添加 six.moves
模块)之类的东西,但它是一个非常好的 first-order 近似值。
我在这里也对 posix 做出假设,对于您需要调整的其他平台(例如 windows,我相信它将使用 .pyd
)。
示例输出:
$ python test.py pyyaml
_yaml
yaml
yaml.composer
yaml.constructor
yaml.cyaml
yaml.dumper
yaml.emitter
yaml.error
yaml.events
yaml.loader
yaml.nodes
yaml.parser
yaml.reader
yaml.representer
yaml.resolver
yaml.scanner
yaml.serializer
yaml.tokens
$ python test.py coverage
coverage.pickle2json
coverage.execfile
coverage.python
coverage.summary
coverage.html
coverage.plugin
coverage.pytracer
coverage.config
coverage.__main__
coverage.data
coverage.debug
coverage.annotate
coverage.backward
coverage.parser
coverage.misc
coverage.files
coverage.multiproc
coverage.backunittest
coverage.env
coverage
coverage.control
coverage.cmdline
coverage.results
coverage.version
coverage.plugin_support
coverage.templite
coverage.collector
coverage.xmlreport
coverage.report
coverage.phystokens
coverage.bytecode
coverage.tracer
coverage.fullcoverage.encodings
基于 Anthony Sottile 的
在 Windows 上进行测试,我发现 .list_installed_files()
存在一些问题(其中一些已在 "solution" 中解决):
- os.sep 未根据分发类型正确拆分文件名。 (鸡蛋走 os.sep 而轮子走 posix 方向。)
- 对于某些发行版,您可以获得完整路径(看起来像鸡蛋)。这会导致疯狂的模块名称猜测(例如,'C:.Users.Username.AppData.RestOfPath.File')。
这会搜索第一个__init__.py
来通知模块名称。如果找不到,它只是 returns 包名称(对我来说涵盖了 90% 的情况)。
def package_to_module(package):
dp = distlib.database.DistributionPath(include_egg=True)
dist = dp.get_distribution(package)
if dist is None:
raise ModuleNotFoundError
module = package # until we figure out something better
for filename, _, _ in dist.list_installed_files():
if filename.endswith(('.py')):
parts = os.path.splitext(filename)[0].split(os.sep)
if len(parts) == 1: # windows sep varies with distribution type
parts = os.path.splitext(filename)[0].split('/')
if parts[-1].startswith('_') and not parts[-1].startswith('__'):
continue # ignore internals
elif filename.endswith('.py') and parts[-1] == '__init__':
module = parts[-2]
break
return module
一些示例:
>>> package_to_module("pyyaml")
'yaml'
>>> package_to_module("click")
'click'
>>> package_to_module("six")
'six'
>>> package_to_module("pip")
'pip'
>>> package_to_module("doesntexist")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in package_to_module
ModuleNotFoundError