如何通过已知协议将python程序与第三方程序接口?
我正在尝试使用Go文本协议(GTP-)与一个程序(GoGui-)接口,该协议有其文档。(链接也可以在以前的网站上找到。) 因此,我编写了一些代码,至少让GoGui确认我的程序的存在:如何通过已知协议将python程序与第三方程序接口?,python,interface,Python,Interface,我正在尝试使用Go文本协议(GTP-)与一个程序(GoGui-)接口,该协议有其文档。(链接也可以在以前的网站上找到。) 因此,我编写了一些代码,至少让GoGui确认我的程序的存在: import sys import engine import input_processing for line in sys.stdin: if line == 'name\n': sys.stdout.write(' Muizz-Bot\n\n') if line == 'p
import sys
import engine
import input_processing
for line in sys.stdin:
if line == 'name\n':
sys.stdout.write(' Muizz-Bot\n\n')
if line == 'protocol_version\n':
sys.stdout.write('2\n\n')
if line == 'version\n':
sys.stdout.write('')
现在,这本身似乎并不不合理,但会导致GoGui给我以下错误:
这当然是个问题。因此,我认为我在编程中犯了一个错误,但当我只是通过visual studio运行程序时,一切都按预期运行:
这让我觉得问题在于如何连接这两个应用程序,也许我应该看看stdin和stdout以外的其他功能。有人知道这里可能出了什么问题吗
编辑注释:我目前正在处理的命令解析代码(全部)如下所示:
import sys
commands = ['protocol_version', 'name', 'version', 'list_commands', 'known_command', 'quit', 'boardsize',
'clear_board', 'komi', 'play', 'genmove']
pre_game_out = ['2','Muizz-Bot','']
# Define all output functions
def list_commands():
out = '\n'.join(commands)
return(out)
def known():
return(True)
def quit():
return(None)
def boardsize():
return(None)
def clear_board():
return(None)
def komi():
return(None)
def play():
return(None)
def genmove():
return("A1")
# Create dictionary to point to all functions.
output = {'list_commands':list_commands, 'known_command':known, 'quit':quit, 'boardsize':boardsize,
'clear_board':clear_board, 'komi':komi, 'play':play, 'genmove':genmove}
# Define the function that will pass the commands and write outputs.
def parse(line):
if line.strip() in commands:
i = commands.index(line.strip())
if i<3:
sys.stdout.write('= '+ pre_game_out[i]+'\n\n')
sys.stdout.flush()
else:
sys.stdout.write('= ' + output[line.strip()]() + '\n\n')
sys.stdout.flush()
您没有刷新响应,因此不会将任何内容发送回调用方(因为命令不够大,无法触发自动缓冲区刷新)。另外,在协议文档中,它清楚地指出您的响应应该是
=response\n\n
的形式,因此即使您刷新它,它也可能不起作用
尝试以下方法:
import sys
for line in sys.stdin:
if line.strip() == 'name':
sys.stdout.write('= Muizz-Bot\n\n')
sys.stdout.flush()
elif line.strip() == 'protocol_version':
sys.stdout.write('= 2\n\n')
sys.stdout.flush()
elif line.strip() == 'version':
sys.stdout.write('=\n\n')
sys.stdout.flush()
您可能希望创建一个简单的函数来解析命令/响应,而不是重复代码。此外,这也可能(完全)不起作用,因为协议文档指出您需要实现相当多的命令(6.1必需的命令)
),但它应该让您开始
更新-这里有一种方法可以使它更易于管理并符合规范-您可以为每个命令创建一个函数,以便您可以轻松地添加/删除它们,例如:
def cmd_name(*args):
return "Muizz-Bot"
def cmd_protocol_version(*args):
return 2
def cmd_version(*args):
return ""
def cmd_list_commands(*args):
return " ".join(x[4:] for x in globals() if x[:4] == "cmd_")
def cmd_known_command(*args):
commands = {x[4:] for x in globals() if x[:4] == "cmd_"}
return "true" if args and args[0] in commands else "false"
# etc.
在这里,所有命令函数都以“cmd_u”(和cmd_list_commands()
和cmd_known_command()
使用该事实来检查全局名称空间中的命令函数),但您也可以将它们移动到其他模块,然后“扫描”该模块。使用这种结构,添加新命令非常容易,例如,要添加所需的quit
命令,您只需定义它:
def cmd_quit(*args):
raise EOFError() # we'll use EOFError to denote an exit state bellow
此外,我们将在下面处理命令需要返回错误的情况—您只需从函数中执行raisevalueerror(“错误响应”)
,它将作为错误返回
将命令集添加为函数后,只需解析输入命令,使用正确的参数调用正确的函数并打印回响应:
def call_command(command):
command = "".join(x for x in command if 31 < ord(x) < 127 or x == "\t") # 3.1.1
command = command.strip() # 3.1.4
if not command: # ... return if there's nothing to do
return
command = command.split() # split to get the [id], cmd, [arg1, arg2, ...] structure
try: # try to convert to int the first slice to check for command ID
command_id = int(command[0])
command_args = command[2:] if len(command) > 2 else [] # args or an empty list
command = command[1] # command name
except ValueError: # failed, no command ID present
command_id = "" # set it to blank
command_args = command[1:] if len(command) > 1 else [] # args or an empty list
command = command[0] # command name
# now, lets try to call our command as cmd_<command name> function and get its response
try:
response = globals()["cmd_" + command](*command_args)
if response != "": # response not empty, prepend it with space as per 3.4
response = " {}".format(response)
sys.stdout.write("={}{}\n\n".format(command_id, response))
except KeyError: # unknown command, return standard error as per 3.6
sys.stdout.write("?{} unknown command\n\n".format(command_id))
except ValueError as e: # the called function raised a ValueError
sys.stdout.write("?{} {}\n\n".format(command_id, e))
except EOFError: # a special case when we need to quit
sys.stdout.write("={}\n\n".format(command_id))
sys.stdout.flush()
sys.exit(0)
sys.stdout.flush() # flush the STDOUT
当然,正如我前面提到的,将命令打包到一个单独的模块中可能是一个更好的主意,但这至少可以让您了解如何进行“关注点分离”,这样您就不用担心如何响应命令,而不用担心它们如何被调用以及它们如何响应调用者了。也许可以使用套接字??这两个程序都在同一台机器上运行,协议需要通过stdin/out进行对话,所以几乎可以肯定第三方程序就是这样做的。请注意,我对编程特别是python非常陌生,因此在试图解释事情时,我可能需要比平时更多的人手,对不起,我完全错过了=。。。(取而代之的是3.4)但它的工作原理是这样的!不过我有两个问题:在if语句中使用line.strip(),是为了增加可读性,还是它还有更重要的用途?您使用的是elif语句,而不是if语句。这可能是一个非常理论化的问题,但我使用if语句的理由是,只需要检查一个等式,而不是多个等式;这将提高效率。这个推理有什么好处吗?或者elif有什么好处?@MitchellFaas-
str.strip()
清除两端的空白。但是,如果调用方发送了如下命令:14 name\n
(一个完全有效的命令),它将不起作用,因为它不处理存在ID的情况-请查看上面的更新,作为如何正确处理文档中描述的情况的示例。至于if
语句,您的理解是错误的-elif/else
语句只有在同一链中的前几条语句的计算结果不是True
时才进行计算。您的方法将在每个循环中对所有代码进行求值,无论其中一些代码是否为True
。您编写的代码目前超出了我的理解范围。我的计划是使用命令解析模块(所有代码都可以在原始帖子中找到),并使用字典识别需要执行的命令,然后运行该功能。今天,我将致力于设置参数,我将尝试在预处理过程中分离这些参数。
def call_command(command):
command = "".join(x for x in command if 31 < ord(x) < 127 or x == "\t") # 3.1.1
command = command.strip() # 3.1.4
if not command: # ... return if there's nothing to do
return
command = command.split() # split to get the [id], cmd, [arg1, arg2, ...] structure
try: # try to convert to int the first slice to check for command ID
command_id = int(command[0])
command_args = command[2:] if len(command) > 2 else [] # args or an empty list
command = command[1] # command name
except ValueError: # failed, no command ID present
command_id = "" # set it to blank
command_args = command[1:] if len(command) > 1 else [] # args or an empty list
command = command[0] # command name
# now, lets try to call our command as cmd_<command name> function and get its response
try:
response = globals()["cmd_" + command](*command_args)
if response != "": # response not empty, prepend it with space as per 3.4
response = " {}".format(response)
sys.stdout.write("={}{}\n\n".format(command_id, response))
except KeyError: # unknown command, return standard error as per 3.6
sys.stdout.write("?{} unknown command\n\n".format(command_id))
except ValueError as e: # the called function raised a ValueError
sys.stdout.write("?{} {}\n\n".format(command_id, e))
except EOFError: # a special case when we need to quit
sys.stdout.write("={}\n\n".format(command_id))
sys.stdout.flush()
sys.exit(0)
sys.stdout.flush() # flush the STDOUT
if __name__ == "__main__": # make sure we're executing instead of importing this script
while True: # main loop
try:
line = sys.stdin.readline() # read a line from STDIN
if not line: # reached the end of STDIN
break # exit the main loop
call_command(line) # call our command
except Exception: # too broad, but we don't care at this point as we're exiting
break # exit the main loop