我最初使用click作为模块编写了一个CLI,它运行良好。但是由于我的项目越来越大,我现在需要具有CLI可以使用的属性,因此我试图将其转换为类,但是这样做却遇到了错误。我的代码如下:
import click
import click_repl
import os
from prompt_toolkit.history import FileHistory
class CLI:
def __init__(self):
pass
@click.group(invoke_without_command=True)
@click.pass_context
def cli(self, ctx):
if ctx.invoked_subcommand is None:
ctx.invoke(self.repl)
@cli.command()
def foo(self):
print("foo")
@cli.command()
def repl(self):
prompt_kwargs = {
'history': FileHistory(os.path.expanduser('~/.repl_history'))
}
click_repl.repl(click.get_current_context(), prompt_kwargs)
def main(self):
while True:
try:
self.cli(obj={})
except SystemExit:
pass
if __name__ == "__main__":
foo = CLI()
foo.main()
在没有所有self
和class CLI:
的情况下,CLI可以按预期运行,但是作为一个类,它会遇到错误:TypeError: cli() missing 1 required positional argument: 'ctx'
我不明白为什么会这样。据我所知,调用self.cli()
应该自动传递self
,因此obj={}
应该作为ctx.obj传递,因此,如果cli
与Traceback (most recent call last):
File "C:/Users/user/.PyCharmCE2018.2/config/scratches/exec.py", line
37, in <module>
foo.main()
File "C:/Users/user/.PyCharmCE2018.2/config/scratches/exec.py", line
30, in main
self.cli(obj={})
File "C:\Users\user\AppData\Local\Programs\Python\Python37\lib\site- packages\click\core.py", line 764, in __call__
return self.main(*args, **kwargs)
File "C:\Users\user\AppData\Local\Programs\Python\Python37\lib\site-packages\click\core.py", line 717, in main
rv = self.invoke(ctx)
File "C:\Users\user\AppData\Local\Programs\Python\Python37\lib\site-packages\click\core.py", line 1114, in invoke
return Command.invoke(self, ctx)
File "C:\Users\user\AppData\Local\Programs\Python\Python37\lib\site-packages\click\core.py", line 956, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "C:\Users\user\AppData\Local\Programs\Python\Python37\lib\site-packages\click\core.py", line 555, in invoke
return callback(*args, **kwargs)
File "C:\Users\user\AppData\Local\Programs\Python\Python37\lib\site-packages\click\decorators.py", line 17, in new_func
return f(get_current_context(), *args, **kwargs)
TypeError: cli() missing 1 required positional argument: 'ctx'
没有任何区别是否包裹在一个类中。
有人可以向我解释为什么会发生这种情况,更重要的是,我该如何解决?
如果相关,这里是完整的错误堆栈跟踪:
pass_context
编辑:问题似乎出在pass_context
调用上。通常obj={}
会将当前上下文作为第一个参数提供给函数,以便将self
传递给上下文实例。但是由于我将点击组包装到了一个类中,所以第一个位置是由def cli()
引用占据的,因此当前上下文无法传递给该函数。有什么解决方法的想法吗?
我尝试通过以下方式更改@click.group(invoke_without_command=True)
def cli(self):
ctx = click.get_current_context()
ctx.obj = {}
if ctx.invoked_subcommand is None:
ctx.invoke(self.repl)
:
self
因此,为了避免与self.cli()
发生冲突,我不会通过调用传递上下文,但是如果尝试使用TypeError: cli() missing 1 required positional argument: 'self'
运行此错误,则会发生self.cli(self)
错误。
用TypeError: 'CLI' object is not iterable
调用会遇到time_vector = ['06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '00', '01', '02', '03', '04', '05', '06']
doublezeroes = "00"
timelist=list(map(lambda x:x+doublezeroes, time_vector))
print(timelist)
答案 0 :(得分:2)
恐怕点击库的设计目的不是作为一个类。 Click使用装饰器。不要掉以轻心的装饰。装饰器从字面上将您的函数作为参数并返回另一个函数。
例如:
@cli.command()
def foo(self):
符合
foo = cli.command()(foo)
因此,恐怕click不支持装饰绑定到类的函数,而只能装饰未绑定的函数。因此,基本上,您的答案的解决方案是,不要使用类。
您可能想知道现在如何组织代码。大多数语言将班级作为组织单位呈现给您。
Python更进一步,并为您提供了模块。基本上,文件是一个模块,在该文件中,您放入其中的所有内容都会自动与该文件作为模块关联。
因此,只需命名文件cli.py
并将您的属性创建为全局变量即可。这可能会给您带来其他问题,因为您不能在函数作用域中更改全局变量,但是可以使用类来包含变量。
class Variables:
pass
variables = Variables()
variables.something = "Something"
def f():
variables.something = "Nothing"