Python递归树示例

时间:2018-07-13 09:34:21

标签: python recursion

我正在分析一个Jenkins构建中的一些代码,该构建使用递归来获取jenkins的下游工作的网址

def get_all_downstream_jobs_urls(ds_jobs: set = None):
    global JENKINS_JOBS

    if not ds_jobs: 
         ds_jobs = set(); ds_jobs.update(extract_ds_job_url(get_ds_jobs(BASE_JOB_URL)))

    temp = ds_jobs 

    for _ in ds_jobs.copy():
        result = extract_ds_job_url(get_ds_jobs(_)) # <--- jenkins rest api call
        if result: temp.update(result); JENKINS_JOBS.update(temp);
        else: return temp

    return get_all_downstream_jobs_urls(temp)

这对于具有下游作业的项目是可行的,尽管它对Jenkins rest api的调用过多,但是,如果项目没有下游作业,则会陷入递归状态。您能帮我弄清楚问题出在哪里吗?

1 个答案:

答案 0 :(得分:1)

如果extract_ds_job_url(get_ds_jobs(BASE_JOB_URL))返回一个空集,则您将永远呼叫get_all_downstream_jobs_urls(temp)。这是因为for循环不会做任何事情。

顶部的测试应改为检查None

if ds_jobs is None:

ds_jobs为空的单独测试应结束递归:

if not ds_jobs:
    # no downstream jobs to process
    return set()

我不能保证其余的逻辑,但是代码中肯定有很多样式错误。我会将其重构为至少 摆脱一些错误:

  • JENKINS_JOBS永远不会反弹,因此global JENKINS_JOBS既多余又令人困惑,应将其删除。
  • 尚不清楚该函数为什么要更新全局返回结果集。它应该做一个,或者不做两个。
  • _by convention,是一个抛弃型变量。它表明该值将不被使用。但是这里的代码确实使用了它。应该将其重命名为job_url
  • 您真的不应该在生产代码中使用;。将代码放在单独的行上。
  • ds_jobs = set(),然后ds_jobs.update(...)就是ds_jobs = set(...)的详细拼写方式。
  • temp不是一个好的变量名,updated可能是一个更好的名字。分配时应将其制成副本,以便可以从updated = set(ds_jobs)循环中删除.copy()for调用。
  • 第一个工作URL不具有下游URL时的return也可能不是您想要的。
  • 如果您真的想要一棵下游URL,则递归调用应该尝试传递到目前为止收集的所有作业URL!对于已经检查过的工作网址,可能会再次调用jenkins API。

以下代码改为使用堆栈来删除递归,并保证仅一次调用每个工作URL的Jenkins API:

def get_all_downstream_jobs_urls():
    ds_jobs = set()
    stack = [extract_ds_job_url(get_ds_jobs(BASE_JOB_URL))]
    while stack:
        job_url = stack.pop()
        if job_url in ds_jobs:
            # already seen before, skip
            continue
        ds_jobs.add(job_url)
        # add downstream jobs to the stack for further processing
        stack.extend(extract_ds_job_url(get_ds_jobs(job_url)))
    return ds_jobs

最后但并非最不重要的一点是,我强烈怀疑使用jenkinsapi package之类的第三方库会使这一切变得更加简单; Jenkins API可能只允许您一次调用来查询此信息,而库可能会为您进行这样的调用,并为您提供易于解析的Python对象以获取该信息。