我正在分析一个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的调用过多,但是,如果项目没有下游作业,则会陷入递归状态。您能帮我弄清楚问题出在哪里吗?
答案 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
调用。return
也可能不是您想要的。以下代码改为使用堆栈来删除递归,并保证仅一次调用每个工作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对象以获取该信息。