1 问题由来
很多企业之前自动化脚本都是tcl的,但是目前随着Robot Framework和python的流行,纷纷由tcl转向python,但是遗留的大量的tcl库没法使用,如果重新开发需要大量的人力和物力,如何才能让现存的大量tcl库能够在python里面直接使用呢?
本文介绍一种非常简单的方式,供大家参考。
2 解决方案
本方案基于以下的python语言工具:
●Python自带了tcl解释器
●Python对象模型中的__getattr__钩子函数
●functools中的partial函数
python是一门充满钩子的语言,当对象通过点操作符号(.)调用成员方法的时候,如果找不到该方法,就会调用__getattr__这个方法来查找。我们的方案的核心思想就是基于这一点。我们先定义一个类,重载这个类的__getattr__,在这个方法中返回一个执行对应tcl方法的函数。这样,我们就可以通过创建这个类的对象来访问响应的tcl方法。比如说创建了对象tclobj,想调用tcl方法expr,那就这样访问就可以了:tclobj.expr(“8 * 8”)。
其中的逻辑是这样的:tclobj调用expr的时候,在mro的列表里面找不到expr这个方法名字,就会调用__getattr__,在重载的这个函数里面,我们直接去tcl解释器里面查找这个函数,并返回一个经过自动封装的python函数。
在返回自动封装的python函数之前,用了functools中的partial函数对函数进行了封装,形成临时函数,再将这个函数返回。
3 实现代码
本例基于以下的假设来实现:
●tcl库文件就在python代码的文件夹下面或者在mytcllib.tcl文件里面
●python解释器版本支持f-string
from tkinter import Tcl
from functools import partial
import os
import sys
class tclrunner:
def __init__(self) -> None:
self.tclinterpreter = Tcl()
filepath = os.path.dirname( os.path.abspath(__file__) )
libfile = os.path.join(filepath, "mytcllib.tcl")
self.tclinterpreter.eval( f"lappend auto_path {{{filepath}}}" )
self.tclinterpreter.eval( f"source {{{libfile}}}" )
def runtcl(self, tclcode):
return self.tclinterpreter.eval( tclcode )
def __getattr__(self, tclcmd):
def tclfunction(cmd, *args):
script = cmd
for param in args:
param = str( param )
script += f" {{{param}}}"
print(f"tcl expresion is: {script}")
return self.runtcl( script )
return partial( tclfunction, tclcmd )
mytclobj = tclrunner()
res = mytclobj.add( 8, 8 )
print(res)
res = mytclobj.expr( "8 * 8" )
print(res)
mytcllib.tcl的内容:
puts "in tcl lib file"
proc add {a b} {
return [expr $a + $b]
}
puts [add 1 2]
python代码的10-13行是设置库函数的文件夹到tcl的auto_path里面,并把mytcllib.tcl里面的内容导入tcl。
python代码的18-28行是关键。在这里定义了tclfunction函数函数,经过partial封装,返回临时函数。
python代码31行,像调用python函数一样调用tcl中的自定义函数add,add是在tcl文件里面定义的。
python代码33行,像调用python函数一样调用tcl中的内置函数expr。