为什么在迭代列表时需要@NonCPS?

时间:2016-10-22 20:48:35

标签: jenkins groovy jenkins-pipeline

我使用了以下Groovy代码:

// @NonCPS
def printList(params) {
    def jobs = [:]
    println params
    params.split(",").each { param ->
        println "Param: ${param}"
    }
}
text = "Foo,Bar,Baz,Qux"
printList(text)

groovy命令调用时按预期工作:

$ groovy test.groovy 
Foo,Bar,Baz,Qux
Param: Foo
Param: Bar
Param: Baz
Param: Qux

虽然在Jenkins的非沙盒作业中进行测试时,我得到了不同的结果:

[Pipeline] echo
Foo,Bar,Baz,Qux
[Pipeline] echo
Param: Foo
[Pipeline] End of Pipeline

问题是只打印了第一个项目,而不是全部。

但是,只有在使用@NonCPS辅助方法时,代码才能在Jenkins中正常工作。

为了正确迭代项目列表,为什么需要@NonCPS?没有使用@NonCPS(特别是在沙盒环境中)有没有解决方法?

2 个答案:

答案 0 :(得分:2)

我尽可能地理解CPS如何工作和实现,以及如何通过管道处理它(到目前为止没有潜入其代码),管道试图测试暂停的能力(备份) /..transfer../restore)执行(JVM)状态大多数代码指令虽然序列化接口。考虑到它是JVM< groovy<管道DSL ..为插件开发人员付出了很多努力。

接下来,我自己并不是Groovy或Java大师,尽管我能想到它的实现,但是groovy生成器(例如<a href="https://www.google.com/" target='_system'>...</a> )与python(yield)或C / C ++生成器中的非常相似(仿函数对象)。

只要可迭代的主题迭代器(例如列表索引或yield表达式状态)是隐式的,那么到目前为止,这些都不能通过管道轻松序列化。 因此,偏好Java风格的循环。

只要执行范围显式包含循环迭代器(&#39; i&#39;索引变量),备用Java风格(经典C)循环就更容易处理。

请不要认为这是一个权威的答案,前面提到的只是我对管道CPS的个人理解

答案 1 :(得分:2)

您还可以使用其他方法来获得相同的效果。例如,以下是可序列化的,不需要@NonCPS迭代名为mylist的列表:

def output(values = None, row_num = None):
    res = ""
    if not row_num:
        res += " " * 2
    else:
        res += row_num + "|"    
    for i in values:
        res += "{:5}".format(i) #{:5} allows for filler space when string length < 5
    return res

def print_overall(rnge):
    print(output(rnge))
    print(output(["-"*5]*len(rnge)))
    for row_num in rnge:
                #for every row number, make an list that maps values
                #where all the column numbers are added to the current row number
        lst = list(map(lambda col_num: row_num + col_num, rnge))
        print(output(lst, row_num = str(row_num)))


range_start = int(input("Enter start value:"))
range_end = int(input("Enter end value:"))

input_range = range(range_start, range_end+1) #+1 because range is exclusive at endpoint
print_overall(input_range)