标签 Makefile 下的文章

Python脚本命令行变量的实现

我们知道Make工具是支持命令行变量的,这种手段为我们提供了很好的灵活性,我们可以通过敲入不同的命令行参数来决定Makefile脚本的行为。

make [variable1=value1 variable2=value2 ... ... ]。

#
# Makefile
#

CMODE = 64-bit

ifeq ($(CMODE), 64-bit)
    CFLAGS += -m64
endif
  
all:
    gcc $(CFLAGS) -o foo foo.c

$> make
gcc -m64 -o foo foo.c

$> make CMODE=32-bit
gcc -o foo foo.c

近期我们的一个Python脚本工具也有类似的需求了,但Python脚本原生并不支持这种命令行变量,我们来看看是否可以利用Python提供的机制实现一种可以满足我们需求的命令行变量。

我们的期望结果如下:

$> foo.py fruit=apple

# foo.py

flag = '' #这个定义可以有,也可以没有,如果有,可以理解为默认值

….

if flag == 'apple':
    ….
elif flag == 'orange':
    ….
elif flag == 'banana':
    ….
else:
    ….

Python是动态语言,提供了注入eval、exec等在运行时执行代码的能力。我们要实现命令行变量的机制,离不开这些能力的支持。eval用于求值表达式,而x=y是语句,我们只能用exec。

【#1】

import sys

if __name__ == '__main__':
    c = len(sys.argv)
    if c <= 1:
        print "found zero command variable"
        exit(0)

    exec(sys.argv[1])

    if fruit == 'apple':
        print 'this is apple'
    elif fruit == 'oracle':
        print 'this is orange'
    elif fruit == 'banana':
        print 'this is banana'
    else:
        print 'other fruit'

$> foo.py fruit=apple

Traceback (most recent call last):
  File "./foo.py", line 18, in <module>
    exec(sys.argv[1])
  File "<string>", line 1, in <module>
NameError: name 'apple' is not defined

上面的例子执行后,提示'apple'没有定义。执行foo.py fruit="apple"得到的也是同样的错误。从内部输出来看,无论是fruit=apple还是fruit="apple",exec的参数始终都 是fruit=apple,导致exec抱怨apple这个符号没有定义。

我们打开一个Python命令行交互窗口,做如下测试:

$> python
Python 2.7.3 (default, Aug  1 2012, 05:14:39)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> exec("fruit=apple")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
NameError: name 'apple' is not defined
>>> exec("fruit='apple'")
>>> fruit
'apple'
>>> exec("num=1")
>>> num
1

通过这个小实验可以看出,我们不能将命令行参数直接原封不动的传给exec,我们要对其进行一下加工,加工的效果如下:

fruit=apple => fruit='apple'
num=1 => num=1

【2】

import sys

def __convert(source):
    (var, sep, val) = source.partition("=")
    if val.isdigit():
        return source
    return  var + "=" + "\'" + val + "\'"

if __name__ == '__main__':
    c = len(sys.argv)
    if c <= 1:
        print "found zero command variable"
        exit(0)

    exec( __convert(sys.argv[1]))

    if fruit == 'apple':
        print 'this is apple'
    elif fruit == 'orange':
        print 'this is orange'
    elif fruit == 'banana':
        print 'this is banana'
    else:
        print 'other fruit'

__convert函数对命令行的参数做了转换,对于数值类的var直接原封不动的返回,否则对于值为字符串的var,将其val用''包裹起来后返回。我们来测试一下新程序:

$> foo.py fruit=apple
this is apple
$> foo.py fruit=orange
this is orange
$> foo.py fruit=watermelon
other fruit

从输出结果来看,我们的预期是达到了^_^。上面的程序只是示例性质的,Python的exec具有运行时执行动态代码的能力,我们在获得这种强大能力的 同时,也面临着巨大的风险。一旦恶意代码从外部传入被exec执行,将带来严重的后果。因此对于exec要执行的代码务必要预先进行必要的形式校验。

lcut 0.3.0版本发布

lcut单元测试框架在我的项目中应用已经有一段时间了,项目组的同事对lcut的使用也是越来越熟悉,这不今天一位同事还提出了一个新需求,需求大致是这样的。

 
在实际项目中,经常遇到这类情况:
 
int bar(…) {
    int ret;
 
    ret = foo(…);
    /* assert ret */
    …
 
    ret = foo(…);
    /* assert ret */
    …
 
    ret = foo(…);
    /* assert ret */
    …
}
 
上述代码中被测函数接口bar的实现中多次调用了某函数foo。这样当我们用mock方式测试bar这个函数时,可能需要多次重复设置foo的返回值以及输出函数的值,就像这样:
 
void tc_test_bar_return_ok(lcut_tc_t *tc, void *data) {
    LCUT_RETV_RETURN(foo, 0);
    LCUT_RETV_RETURN(foo, 0);
    LCUT_RETV_RETURN(foo, 0);
 
    LCUT_ARG_RETURN(foo, 1);
    LCUT_ARG_RETURN(foo, 1);
    LCUT_ARG_RETURN(foo, 1);
 
    LCUT_INT_EQUAL(tc, 0, bar(…));
    …
}
 
我的同事希望lcut能提供一个接口:支持一次调用,设置多次mock obj的返回值或输出参数,使用这样的接口后,上述代码就可以简化为:
 
void tc_test_bar_return_ok(lcut_tc_t *tc, void *data) {
    LCUT_RETV_RETURN_COUNT(foo, 0, 3);
    LCUT_ARG_RETURN_COUNT(foo, 1, 3);
 
    LCUT_INT_EQUAL(tc, 0, bar(…));
}
 
这个需求提的非常好,看起来更像是一种语法糖(syntactic sugar),用于简化代码编写。于是乎下午我就为lcut增加了两个有用的宏:LCUT_RETV_RETURN_COUNT和LCUT_ARG_RETURN_COUNT。
 
正如上面所说,这两个宏可在一次调用中多次设置某个mock obj的返回值和输出参数值,两个宏的原型如下:
 
#define LCUT_RETV_RETURN_COUNT(fcname, value, count) do { \
        lcut_mock_obj_return(#fcname, (void*)value, __FUNCTION__, __LINE__, __FILE__, MOCK_RETV, count); \
    } while(0);
 
#define LCUT_ARG_RETURN_COUNT(fcname, value, count) do { \
        lcut_mock_obj_return(#fcname, (void*)value, __FUNCTION__, __LINE__, __FILE__, MOCK_ARG, count); \
    } while(0);
 
只是比之前提供的LCUT_RETV_RETURN和LCUT_ARG_RETURN多了一个宏参数count。count用于指出对mocked obj进行多少次返回值或输出参数的设置。
 
另外当count传入-1时,其语义为无论mocked object被使用多少次,其返回值或输出参数值都是一样的,即使用LCUT_RETV_RETURN_COUNT或LCUT_ARG_RETURN_COUNT时设置的那个值,直到下一次调用这两个宏进行重新设置时,值才会发生变化。例如上面的例子我们也可以改写为:
 
void tc_test_bar_return_ok(lcut_tc_t *tc, void *data) {
    LCUT_RETV_RETURN_COUNT(foo, 0, -1);
    LCUT_ARG_RETURN_COUNT(foo, 1, -1);
 
    LCUT_INT_EQUAL(tc, 0, bar(…));
}
 
这样无论后续再调用多少次bar函数,foo的返回值总是0,输出参数也总是1。
 
增加了这两个宏后,lcut的版本号也小升了一位,最新版本是lcut-0.3.0-rc1,其中还增加了一个针对lcut mock功能的example – mock_test.c。同时Google Code上的lcut guide也做了更新,对新增的宏的用法进行了简要说明。
 
就这样,lcut 0.3.0版本算是发布了,后续还会经过内部的细致测试,如果没有什么问题,就会去掉rc。
如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言进阶课 AI原生开发工作流实战 Go语言精进之路1 Go语言精进之路2 Go语言第一课 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats