我尝试使用包click
在python中构建CLI。我使用的Python版本是3.6
这是我申请的主要内容:
import os
import click
cmd_folder = os.path.join(os.path.dirname(__file__), 'commands')
class IAMCLI(click.MultiCommand):
def list_commands(self, ctx):
rv = []
for filename in os.listdir(cmd_folder):
if filename.endswith('.py') and \
filename.startswith('cmd_'):
rv.append(filename[4:-3])
rv.sort()
return rv
def get_command(self, ctx, cmd_name):
ns = {}
fn = os.path.join(cmd_folder, 'cmd_{}.py'.format(cmd_name))
with open(fn) as f:
code = compile(f.read(), fn, 'exec')
eval(code, ns, ns)
return ns['cli']
@click.command(cls=IAMCLI)
@click.option('--env', default='dev', type=click.Choice(['dev', 'staging', 'production']),
help='AWS Environment')
@click.pass_context
def cli():
"""AWS IAM roles and policies management CLI."""
pass
if __name__ == '__main__':
cli()
这是树:
├── cli
│ ├── __init__.py
│ ├── aws
│ │ ├── __init__.py
│ │ ├── policy.py
│ │ └── role.py
│ ├── cli.py
│ └── commands
│ ├── __init__.py
│ └── cmd_dump.py
cmd_dump.py
看起来像这样:
import click
from cli.aws.role import fetch_roles
@click.command('dump', short_help='Dump IAM resources')
@click.pass_context
def cli():
pass
问题在于,当我尝试运行python cli/cli.py --help
时,这就是我得到的:
File "cli/commands/cmd_dump.py", line 3, in <module>
from cli.aws.role import fetch_roles
ModuleNotFoundError: No module named 'cli.aws'; 'cli' is not a package
有任何想法吗?
答案 0 :(得分:3)
在开始开发新的python项目时,我会尝试根据我的方法给出另一个答案。您是打算分发您的项目,还是只是与某人分享?如果你这样做,你怎么想 - 这个人是否会因为需要记住命令而感到高兴
$ python path/to/project/codebase/cli/cli.py --help
使用你的工具?他记住命令不是更容易吗
$ cli --help
代替?
我建议您立即开始打包项目 - 编写一个最小的安装脚本:
from setuptools import setup, find_packages
setup(
name='mypkg',
version='0.1',
packages=find_packages(),
install_requires=['click'],
entry_points={
'console_scripts': ['cli=cli.cli:cli'],
},
)
出现新要求时,您始终可以增强设置脚本。将安装脚本放在代码库的根目录中:
├── setup.py
├── cli
│ ├── __init__.py
│ ├── aws
...
现在从代码库根目录(python setup.py develop
脚本所在的位置)运行pip install --editable=.
甚至更好,setup.py
1 。您已在开发模式下安装项目,现在可以调用
$ cli --help
正确解析所有导入(这将解决您的问题)。但除此之外你还获得了更多 - 你获得了一种方法来打包你的项目以准备好分发给你的目标用户和一个干净的命令行界面,你的用户将以你刚才的方式调用它。
现在继续进行项目开发。如果您更改cli
命令的代码,它将立即应用,因此您不需要在每次更改任何内容时重新安装项目。
准备好项目开发并希望将其交付给用户后,请发出:
$ python setup.py bdist_wheel
这会将您的项目打包成可安装的wheel
文件(您需要安装wheel
包以便能够调用命令:pip install wheel --user
)。通常它将驻留在代码库根目录的dist
子目录中。将此文件提供给用户。要安装该文件,他将发出
$ pip install Downloads/mypkg-0.1-py3-none.whl --user
并且可以立即开始使用您的工具:
$ cli --help
这是一个非常简化的描述,有很多东西需要学习,但也有大量有用的材料可以指导您完成整个过程。
如果您想了解有关该主题的更多信息:作为快速入门参考,我建议您使用优秀的PyPA packaging guide。用于打包click
命令,their own docs are more than sufficient。
pip
进行分发和打包开发,因为它是一种标准工具。答案 1 :(得分:1)
不要在包内运行脚本!使包在代码中导入,但不在其中运行脚本。其余与导入错误无关。
例如:
├── cli # package
│ ├── __init__.py
│ ├── aws
│ │ ├── __init__.py
│ │ ├── policy.py
│ │ └── role.py
│ ├── cli.py
│ │ └── commands
│ │ ├── __init__.py
│ │ └── cmd_dump.py
├── run_this_module.py
执行run_this_module.py
的模块:
import cli
"""Do your code here"""
答案 2 :(得分:0)
我做了一百次。在多个级别命名包相同的东西很诱人,但是抵制!这些天我的软件包中往往有__main__.py
,这有助于解决您遇到的问题。
我怀疑你遇到了命名空间问题。尝试重命名您的包或名为cli.py
的内部文件。请务必重构任何试图使用这些内容的导入。
此外,将您的函数名称更改为cli
以外的内容。同样的问题。
├── cli <- rename this to 'my_app' or something else
│ ├── __init__.py
│ ├── aws
│ │ ├── __init__.py
│ │ ├── policy.py
│ │ └── role.py
│ ├── cli.py <- or maybe it is easier to rename this
│ └── commands
│ ├── __init__.py
│ └── cmd_dump.py