提交 d53fa53b 编写于 作者: H Hao Wang 提交者: Dong Daxiang

CI - Improve example code check (#19170)


* add exception exit on error example codes test=develop
上级 b837689e
...@@ -1006,7 +1006,7 @@ function example() { ...@@ -1006,7 +1006,7 @@ function example() {
pip install /paddle/build/python/dist/*.whl pip install /paddle/build/python/dist/*.whl
paddle version paddle version
cd ${PADDLE_ROOT}/python/paddle/fluid cd ${PADDLE_ROOT}/python/paddle/fluid
python sampcd_processor.py python sampcd_processor.py cpu
if [ "$?" != "0" ];then if [ "$?" != "0" ];then
echo "Code instance execution failed" echo "Code instance execution failed"
exit 1 exit 1
......
#!/usr/bin/env python2 # Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
# -*- coding: utf-8 -*- #
""" # Licensed under the Apache License, Version 2.0 (the "License");
Created on Fri Jun 14 14:10:36 2019 # you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
@author: haowang101779990 #
""" # http://www.apache.org/licenses/LICENSE-2.0
""" #
This script is for scraping and executing sample codes in the # Unless required by applicable law or agreed to in writing, software
comments of paddle .py source file in order to validate the # distributed under the License is distributed on an "AS IS" BASIS,
sample codes. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
Put this script at directory fluid/ # limitations under the License.
log July 4 : CPU is implemented, wlist is added,
transpiler module need to be finished
"""
import os import os
import sys
import subprocess import subprocess
def find_all(srcstr, substr): def find_all(srcstr, substr):
'''
to find all desired substring in the source string
and return their starting indices as a list
Args:
srcstr(str): the parent string
substr(str): substr
Returns:
list: a list of the indices of the substrings
found
'''
indices = [] indices = []
gotone = srcstr.find(substr) gotone = srcstr.find(substr)
while (gotone != -1): while (gotone != -1):
indices.append(gotone) indices.append(gotone)
gotone = srcstr.find(substr, gotone + 1) gotone = srcstr.find(substr, gotone + 1)
return indices return indices
def check_indent(cdline): def check_indent(cdline):
'''
to check the indent of a given code line
to get the number of starting blank chars,
e.t. blankspaces and \t
\t will be interpreted as 4 single blankspaces,
e.t. '\t'=' '
Args:
cdline(str) : a single line of code from the source file
Returns:
int : the indent of the number of interpreted
blankspaces
'''
indent = 0 indent = 0
for c in cdline: for c in cdline:
if c == '\t': if c == '\t':
...@@ -39,33 +70,102 @@ def check_indent(cdline): ...@@ -39,33 +70,102 @@ def check_indent(cdline):
indent += 1 indent += 1
if c != ' ' and c != '\t': if c != ' ' and c != '\t':
break break
return indent return indent
#srccom: raw comments in the source,including ''' and original indent #srccom: raw comments in the source,including ''' and original indent
def sampcd_extract_and_run(srccom, name, logf):
sampcd_begins = find_all(srccom, ".. code-block:: python")
#no sample code
#have sample code but not formatted by code block
status = []
def sampcd_extract_and_run(srccom,
name,
logf,
htype="def",
hname="",
show_details=False):
''' '''
status: Extract and run sample codes from source comment and
the result will be returned.
3:error sample code
2:have sample code but format is wrong As an ultimate result, this function returns a list of
1:no sample code status codes for each sample code (in top-down order)
0:successful found in srccom.
-1:no comments found
-2:in white list status code deciphering:
3:error sample code
2:have sample code but format is wrong
1:no sample code
0:successful
-1:no comments found
-2:in white list
there may be several examples in a source comment there may be several examples in a source comment
so status is a list to contain the states so status deserves a list to contain the states.
For instance, some API has three example codes,
code 1 is successful, code 2 is error, code 3 is successful
so the list to return is [0,3,0]
Args:
srccom(str): the source comment of some API whose
example codes will be extracted and run.
name(str): the name of the API.
logf(file): for logging the output in case they are
flushed.
htype(str): the type of hint banners, def/class/method.
hname(str): the name of the hint banners , e.t. def hname.
show_details(bool): Set it to False to print wrong sample
codes only.
Returns:
list: the status code of all the sample codes found in srccom.
''' '''
def sampcd_header_print(name, sampcd, htype, hname, logf):
'''
print hint banner headers.
Args:
name(str): the name of the API.
sampcd(str): sample code string
htype(str): the type of hint banners, def/class/method.
hname(str): the name of the hint banners , e.t. def hname.
logf(file): for logging the output in case they are
flushed.
'''
print_header(logf, htype, hname)
print "Sample code " + str(y) + " extracted for " + name + " :"
print "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"
print(sampcd)
print "----example code check----\n"
print "executing sample code ....."
print "execution result:"
logf.write("\nSample code extracted for " + name + " :\n")
logf.write("\n" + sampcd + "\n")
logf.write("\n----example code check----\n")
logf.write("\nexecuting sample code .....\n")
logf.write("\nexecution result:\n")
sampcd_begins = find_all(srccom, " code-block:: python")
status = []
if (len(sampcd_begins) == 0): if (len(sampcd_begins) == 0):
print_header(logf, htype, hname)
'''
detect sample codes using >>> to format
and consider this situation as wrong
'''
if (srccom.find("Examples:") != -1): if (srccom.find("Examples:") != -1):
print "----example code check----\n" print "----example code check----\n"
logf.write("\n----example code check----\n") logf.write("\n----example code check----\n")
if (srccom.find(">>>") != -1): if (srccom.find(">>>") != -1):
logf.write( logf.write(
"Deprecated sample code style:\n\n Examples:\n\n >>>codeline\n >>>codeline\n\n\n " "Deprecated sample code style:\n\n Examples:\n\n >>>codeline\n >>>codeline\n\n\n "
...@@ -76,49 +176,56 @@ def sampcd_extract_and_run(srccom, name, logf): ...@@ -76,49 +176,56 @@ def sampcd_extract_and_run(srccom, name, logf):
+ "Please use '.. code-block:: python' to " + + "Please use '.. code-block:: python' to " +
"format sample code.\n") "format sample code.\n")
status.append(2) status.append(2)
print "status code for all sample codes in " + name + " : " + str(
status)
else: else:
print "No sample code!\n" print "No sample code!\n"
logf.write("\nNo sample code!\n") logf.write("\nNo sample code!\n")
status.append(1) status.append(1)
print "status code for all sample codes in " + name + " : " + str(
status)
for y in range(1, len(sampcd_begins) + 1): for y in range(1, len(sampcd_begins) + 1):
sampcd_begin = sampcd_begins[y - 1] sampcd_begin = sampcd_begins[y - 1]
sampcd = srccom[sampcd_begin + len(".. code-block:: python") + 1:] sampcd = srccom[sampcd_begin + len(" code-block:: python") + 1:]
sampcd = sampcd.split("\n") sampcd = sampcd.split("\n")
#remove starting empty lines #remove starting empty lines
while sampcd[0].replace(' ', '').replace('\t', '') == '': while sampcd[0].replace(' ', '').replace('\t', '') == '':
sampcd.pop(0) sampcd.pop(0)
#the mininmum indent, which is the indent of the first
#non-empty line
min_indent = check_indent(sampcd[0]) min_indent = check_indent(sampcd[0])
sampcd_to_write = [] sampcd_to_write = []
for i in range(0, len(sampcd)): for i in range(0, len(sampcd)):
cdline = sampcd[i] cdline = sampcd[i]
#handle empty lines or those only with spaces/tabs #handle empty lines or those only with spaces/tabs
if cdline.strip() == '': if cdline.strip() == '':
continue continue
this_indent = check_indent(cdline) this_indent = check_indent(cdline)
if (this_indent < min_indent): if (this_indent < min_indent):
break break
else: else:
cdline = cdline.replace('\t', ' ') cdline = cdline.replace('\t', ' ')
sampcd_to_write.append(cdline[min_indent:]) sampcd_to_write.append(cdline[min_indent:])
sampcd = '\n'.join(sampcd_to_write) sampcd = '\n'.join(sampcd_to_write)
sampcd = '\nimport os\n' + 'os.environ["CUDA_VISIBLE_DEVICES"] = ""\n' + sampcd if sys.argv[1] == "cpu":
sampcd = '\nimport os\n' + 'os.environ["CUDA_VISIBLE_DEVICES"] = ""\n' + sampcd
if sys.argv[1] == "gpu":
sampcd = '\nimport os\n' + 'os.environ["CUDA_VISIBLE_DEVICES"] = "0"\n' + sampcd
sampcd += '\nprint ' + '\"' + name + ' sample code is executed successfully!\"\n' sampcd += '\nprint ' + '\"' + name + ' sample code is executed successfully!\"\n'
print "\n"
print "Sample code " + str(y) + " extracted for " + name + " :"
print "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"
print(sampcd)
logf.write("\nSample code extracted for " + name + " :\n")
logf.write("\n" + sampcd + "\n")
print "----example code check----\n"
print "executing sample code ....."
logf.write("\n----example code check----\n")
logf.write("\nexecuting sample code .....\n")
if (len(sampcd_begins) > 1): if (len(sampcd_begins) > 1):
tfname = name + "_example_" + str(y) + ".py" tfname = name + "_example_" + str(y) + ".py"
else: else:
...@@ -127,50 +234,84 @@ def sampcd_extract_and_run(srccom, name, logf): ...@@ -127,50 +234,84 @@ def sampcd_extract_and_run(srccom, name, logf):
tempf = open("samplecode_temp/" + tfname, 'w') tempf = open("samplecode_temp/" + tfname, 'w')
tempf.write(sampcd) tempf.write(sampcd)
tempf.close() tempf.close()
cmd = ["python", "samplecode_temp/" + tfname] cmd = ["python", "samplecode_temp/" + tfname]
subprc = subprocess.Popen( subprc = subprocess.Popen(
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output = subprc.communicate() output, error = subprc.communicate()
print "execution result:"
logf.write("\nexecution result:\n") msg = "".join(output)
msg = "\n".join(output) err = "".join(error)
if (msg.find("sample code is executed successfully!") == -1): if (subprc.returncode != 0):
print("\nSample code error found in " + name + ":\n")
sampcd_header_print(name, sampcd, htype, hname, logf)
print "subprocess return code: " + str(subprc.returncode)
print("Error Raised from Sample Code " + name + " :\n") print("Error Raised from Sample Code " + name + " :\n")
print err
print msg
logf.write("\nError Raised from Sample Code " + name + " :\n") logf.write("\nError Raised from Sample Code " + name + " :\n")
logf.write("\n" + msg + "\n")
status.append(3) status.append(3)
print "status code for all sample codes in " + name + str(status)
#It works!
else: else:
status.append(0) status.append(0)
if show_details:
sampcd_header_print(name, sampcd, htype, hname, logf)
print "subprocess return code: " + str(subprc.returncode)
print msg
logf.write("\n" + msg + "\n")
print "status code for all sample codes in " + name + " : " + str(
status)
#msg is the returned code execution report #msg is the returned code execution report
print msg
logf.write("\n" + msg + "\n")
os.remove("samplecode_temp/" + tfname) os.remove("samplecode_temp/" + tfname)
print status
logf.write("\n" + "execution status" + str(status) + "\n")
return status return status
'''
to extract a def function/class comments body
start_from: the line num of "def" header
'''
def single_defcom_extract(start_from, srcls, is_class_begin=False): def single_defcom_extract(start_from, srcls, is_class_begin=False):
'''
to extract a def function/class/method comments body
Args:
start_from(int): the line num of "def" header
srcls(list): the source file in lines
is_class_begin(bool): whether the start_from is a beginning a class. \
For a sole class body itself may end up with its method if it has no
docstring. But the body of \
a common def function can only be ended up by a none-indented def/class
Returns:
string : the extracted comment body, inclusive of its quote marks.
'''
i = start_from i = start_from
fcombody = "" #def comment body fcombody = "" #def comment body
comstart = -1
comstyle = 0 comstart = -1 # the starting line index of comment mark "'''" or """"""
#if it is not -1, it indicates the loop is in the comment body
comstyle = 0 # comment mark style ,comments quoted with ''' is coded as 1
# comments quoted with """ is coded as 2
for x in range(i + 1, len(srcls)): for x in range(i + 1, len(srcls)):
if is_class_begin: if is_class_begin:
if (srcls[x].startswith(' def ')):
if (srcls[x].replace('\t', ' ').startswith(' def ')):
break break
if ((srcls[x].startswith('def ') or srcls[x].startswith('class '))): if ((srcls[x].startswith('def ') or srcls[x].startswith('class '))):
break break
else: else:
if (comstart == -1 and srcls[x].replace(" ", '').replace( if (comstart == -1 and srcls[x].replace(" ", '').replace(
"\t", '').replace("\n", '').startswith("\"\"\"")): "\t", '').replace("\n", '').startswith("\"\"\"")):
comstart = x comstart = x
...@@ -180,6 +321,7 @@ def single_defcom_extract(start_from, srcls, is_class_begin=False): ...@@ -180,6 +321,7 @@ def single_defcom_extract(start_from, srcls, is_class_begin=False):
srcls[x].replace(" ", '').replace("\t", '').replace( srcls[x].replace(" ", '').replace("\t", '').replace(
"\n", '').startswith("\"\"\"")): "\n", '').startswith("\"\"\"")):
break break
if (comstart == -1 and srcls[x].replace(" ", '').replace( if (comstart == -1 and srcls[x].replace(" ", '').replace(
"\t", '').replace("\n", '').startswith("\'\'\'")): "\t", '').replace("\n", '').startswith("\'\'\'")):
comstart = x comstart = x
...@@ -192,24 +334,55 @@ def single_defcom_extract(start_from, srcls, is_class_begin=False): ...@@ -192,24 +334,55 @@ def single_defcom_extract(start_from, srcls, is_class_begin=False):
if (comstart != if (comstart !=
-1): #when the comments start, begin to add line to fcombody -1): #when the comments start, begin to add line to fcombody
fcombody += srcls[x] fcombody += srcls[x]
return fcombody return fcombody
def print_header(logf, htype, name): def print_header(logf, htype, name):
print "\n"
print htype + " name:" + name print htype + " name:" + name
print "-----------------------" print "-----------------------"
logf.write("\n\n" + htype + " name:" + name + "\n") logf.write("\n\n" + htype + " name:" + name + "\n")
logf.write("-----------------------\n") logf.write("-----------------------\n")
def srccoms_extract(srcfile, logf, status_all, wlist): def srcf_print(srcfile):
print "source file name:" + srcfile.name print "source file name:" + srcfile.name
print "---------------------------------------------------" print "---------------------------------------------------"
logf.write("source file name:" + srcfile.name + "\n") logf.write("source file name:" + srcfile.name + "\n")
logf.write("---------------------------------------------------\n\n") logf.write("---------------------------------------------------\n\n")
def show_alllist(alllist):
print "__all__:" + str(alllist) + "\n"
logf.write("__all__:" + str(alllist) + "\n\n")
def srccoms_extract(srcfile, logf, status_all, wlist, show_details):
'''
Given a source file ``srcfile``, this function will
extract its API(doc comments) and run sample codes in the
API.
Args:
srcfile(file): the source file
logf(file): log recording file
status_all(dict): record all the sample code execution states.
wlist(list): white list
show_details(bool): if show_details is True, the whole process will be printed for you
to debug it locally
Returns:
string: the length of __all__ list in srcfile versus the exact number of
analysed API to make sure no API is missed in this srcfile and it
is useful for statistic practices.
'''
srcc = srcfile.read() srcc = srcfile.read()
#2. get defs and classes header line number #2. get defs and classes header line number
...@@ -217,24 +390,41 @@ def srccoms_extract(srcfile, logf, status_all, wlist): ...@@ -217,24 +390,41 @@ def srccoms_extract(srcfile, logf, status_all, wlist):
srcfile.seek(0, 0) srcfile.seek(0, 0)
srcls = srcfile.readlines() #source lines srcls = srcfile.readlines() #source lines
if show_details:
srcf_print(srcfile)
#1. fetch__all__ list #1. fetch__all__ list
allidx = srcc.find("__all__") allidx = srcc.find("__all__")
if (allidx != -1): if (allidx != -1):
alllist = [] alllist = []
#get all list for layers/ops.py
if (srcfile.name.find("ops.py") != -1): if (srcfile.name.find("ops.py") != -1):
for ai in range(0, len(srcls)): for ai in range(0, len(srcls)):
if (srcls[ai].startswith("__all__")): if (srcls[ai].startswith("__all__")):
lb = srcls[ai].find('[') lb = srcls[ai].find('[')
rb = srcls[ai].find(']') rb = srcls[ai].find(']')
if (lb == -1): if (lb == -1):
continue continue
allele = srcls[ai][lb + 1:rb].replace("'", '').replace( allele = srcls[ai][lb + 1:rb].replace("'", '').replace(
" ", '').replace("\"", '') " ", '').replace("\"", '')
alllist.append(allele) alllist.append(allele)
alllist.remove('')
if '' in alllist:
alllist.remove('')
if show_details:
show_alllist(alllist)
else: else:
alllist_b = allidx + len("__all__") alllist_b = allidx + len("__all__")
allstr = srcc[alllist_b + srcc[alllist_b:].find("[") + 1:alllist_b + allstr = srcc[alllist_b + srcc[alllist_b:].find("[") + 1:alllist_b +
srcc[alllist_b:].find("]")] srcc[alllist_b:].find("]")]
allstr = allstr.replace("\n", '').replace(" ", '').replace( allstr = allstr.replace("\n", '').replace(" ", '').replace(
...@@ -242,181 +432,276 @@ def srccoms_extract(srcfile, logf, status_all, wlist): ...@@ -242,181 +432,276 @@ def srccoms_extract(srcfile, logf, status_all, wlist):
alllist = allstr.split(',') alllist = allstr.split(',')
if '' in alllist: if '' in alllist:
alllist.remove('') alllist.remove('')
print "__all__:" + str(alllist) + "\n"
logf.write("__all__:" + str(alllist) + "\n\n") if show_details:
show_alllist(alllist)
api_alllist_count = len(alllist) api_alllist_count = len(alllist)
api_count = 0 api_count = 0
handled = [] handled = []
#get src contents in layers/ops.py
if (srcfile.name.find("ops.py") != -1): if (srcfile.name.find("ops.py") != -1):
for i in range(0, len(srcls)): for i in range(0, len(srcls)):
if srcls[i].find("__doc__") != -1: if srcls[i].find("__doc__") != -1:
opname = srcls[i][:srcls[i].find("__doc__") - 1] opname = srcls[i][:srcls[i].find("__doc__") - 1]
print_header(logf, "def", opname)
if opname in wlist: if opname in wlist:
print opname + " is in white list, thus skipped"
logf.write("\n" + opname + status_all[srcfile.name + '/' + opname] = [-2]
" is in white list, thus skipped\n")
status_all[opname] = [-2] if show_details:
print status_all[opname] print_header(logf, "def", opname)
logf.write("\n" + "execution status" + str(status_all[ print opname + " is in white list, thus skipped"
opname]) + "\n") logf.write("\n" + opname +
" is in white list, thus skipped\n")
print status_all[srcfile.name + '/' + opname]
logf.write("\n" + "execution status" + str(
status_all[srcfile.name + '/' + opname]) + "\n")
continue continue
comstart = i comstart = i
for j in range(i, len(srcls)): for j in range(i, len(srcls)):
if (srcls[j].find("\"\"\"") != -1): if (srcls[j].find("\"\"\"") != -1):
comstart = i comstart = i
opcom = "" opcom = ""
for j in range(comstart + 1, len(srcls)): for j in range(comstart + 1, len(srcls)):
opcom += srcls[j] opcom += srcls[j]
if (srcls[j].find("\"\"\"") != -1): if (srcls[j].find("\"\"\"") != -1):
break break
if opname in wlist:
print opname + " is in white list, thus skipped" status = sampcd_extract_and_run(opcom, opname, logf, "def",
logf.write("\n" + opname + opname, show_details)
" is in white list, thus skipped\n")
status_all[opname] = [-2]
print status_all[opname]
logf.write("\n" + "execution status" + str(status_all[
opname]) + "\n")
continue
status = sampcd_extract_and_run(opcom, opname, logf)
api_count += 1 api_count += 1
status_all[opname] = status status_all[srcfile.name + '/' + opname] = status
handled.append(opname)
handled.append(
opname) #ops.py also has normal formatted functions
#use list 'handled' to mark the functions have been handled here
#which will be ignored in the following step
for i in range(0, len(srcls)): for i in range(0, len(srcls)):
if srcls[i].startswith('def '):
if srcls[i].startswith(
'def '): #a function header is detected in line i
f_header = srcls[i].replace(" ", '') f_header = srcls[i].replace(" ", '')
fn = f_header[len('def'):f_header.find('(')] #function name fn = f_header[len('def'):f_header.find('(')] #function name
if fn in handled: if fn in handled:
continue continue
print_header(logf, "def", fn)
if fn in alllist: if fn in alllist:
api_count += 1 api_count += 1
if fn in wlist:
print fn + " is in white list, thus skipped" if fn in wlist or fn + "@" + srcfile.name in wlist:
logf.write("\n" + fn +
" is in white list, thus skipped\n") status_all[srcfile.name + '/' + fn] = [-2]
status_all[fn] = [-2]
print status_all[fn] if show_details:
logf.write("\n" + "execution status" + str(status_all[ print_header(logf, "def", fn)
fn]) + "\n") print fn + " is in white list, thus skipped"
logf.write("\n" + fn +
" is in white list, thus skipped\n")
print status_all[srcfile.name + '/' + fn]
logf.write("\n" + "execution status" + str(
status_all[srcfile.name + '/' + fn]) + "\n")
continue continue
fcombody = single_defcom_extract(i, srcls) fcombody = single_defcom_extract(i, srcls)
if (fcombody == ""): if (fcombody == ""): #if no comment
print "no comments in function " + fn print_header(logf, "def", fn)
print "WARNING: no comments in function " + fn + ", but it deserves."
logf.write("no comments in function " + fn + "\n\n") logf.write("no comments in function " + fn + "\n\n")
status_all[fn] = [-1] status_all[srcfile.name + '/' + fn] = [-1]
print status_all[fn] print status_all[srcfile.name + '/' + fn]
logf.write("\n" + "execution status" + str(status_all[ logf.write("\n" + "execution status" + str(status_all[
fn]) + "\n") srcfile.name + '/' + fn]) + "\n")
continue continue
else: else:
status = sampcd_extract_and_run(fcombody, fn, logf) status = sampcd_extract_and_run(fcombody, fn, logf,
status_all[fn] = status "def", fn, show_details)
status_all[srcfile.name + '/' + fn] = status
else: else:
print fn + " not in __all__ list" if show_details:
logf.write(fn + " not in __all__ list\n\n") print_header(logf, "def", fn)
print fn + " not in __all__ list"
logf.write(fn + " not in __all__ list\n\n")
if srcls[i].startswith('class '): if srcls[i].startswith('class '):
print srcls[i]
c_header = srcls[i].replace(" ", '') c_header = srcls[i].replace(" ", '')
cn = c_header[len('class'):c_header.find('(')] #function name cn = c_header[len('class'):c_header.find('(')] #class name
if cn in handled: if cn in handled:
continue continue
print_header(logf, "class", cn)
if cn in alllist: if cn in alllist:
api_count += 1 api_count += 1
if cn in wlist:
print cn + " is in white list, thus skipped" if cn in wlist or cn + "@" + srcfile.name in wlist:
logf.write("\n" + cn +
" is in white list, thus skipped\n") status_all[srcfile.name + '/' + cn] = [-2]
status_all[cn] = [-2]
print status_all[cn] if show_details:
logf.write("\n" + "execution status" + str(status_all[
cn]) + "\n") print cn + " is in white list, thus skipped"
logf.write("\n" + cn +
" is in white list, thus skipped\n")
print status_all[srcfile.name + '/' + cn]
logf.write("\n" + "execution status" + str(
status_all[srcfile.name + '/' + cn]) + "\n")
continue continue
allcoms = []
#class comment
classcom = single_defcom_extract(i, srcls, True) classcom = single_defcom_extract(i, srcls, True)
allcoms.append(classcom)
if (classcom != ""): if (classcom != ""):
status = sampcd_extract_and_run(classcom, cn, logf)
status_all[cn] = status status = sampcd_extract_and_run(
classcom, cn, logf, "class", cn, show_details)
status_all[srcfile.name + '/' + cn] = status
else: else:
print "no comments in class itself " + cn + "\n" print "WARNING: no comments in class itself " + cn + ", but it deserves.\n"
logf.write("no comments in class itself " + cn + logf.write("no comments in class itself " + cn +
"\n\n\n") "\n\n\n")
status_all[cn] = [-1] status_all[srcfile.name + '/' + cn] = [-1]
print status_all[cn] print status_all[srcfile.name + '/' + cn]
logf.write("\n" + "execution status" + str(status_all[ logf.write("\n" + "execution status" + str(status_all[
cn]) + "\n") srcfile.name + '/' + cn]) + "\n")
#handling methods in class bodies
for x in range( for x in range(
i + 1, i + 1,
len(srcls)): #from the next line of class header len(srcls)): #from the next line of class header
if (srcls[x].startswith('def ') or if (srcls[x].startswith('def ') or
srcls[x].startswith('class ')): srcls[x].startswith('class ')):
break break
else: else:
#member method def header
srcls[x] = srcls[x].replace('\t', ' ')
if (srcls[x].startswith( if (srcls[x].startswith(
' def ')): #detect a mehtod header.. ' def ')): #detect a mehtod header..
thisl = srcls[x] thisl = srcls[x]
indent = len(thisl) - len(thisl.lstrip()) indent = len(thisl) - len(thisl.lstrip())
mn = thisl[indent + len('def '):thisl.find( mn = thisl[indent + len('def '):thisl.find(
'(')] #method name '(')] #method name
name = cn + "." + mn
print_header(logf, "method", name) name = cn + "." + mn #full name
if mn.startswith('_'): if mn.startswith('_'):
print mn + "is hidden, not visible to users"
logf.write( if show_details:
"\n" + mn +
"is hidden, not visible to users\n") print mn + " is hidden, not visible to users\n"
logf.write(
"\n" + mn +
" is hidden, not visible to users\n")
continue continue
if name in wlist:
print name + " is in white list, thus skipped" if name in wlist or name + "@" + srcfile.name in wlist:
logf.write(
"\n" + name + status_all[srcfile.name + '/' + name] = [-2]
" is in white list, thus skipped\n")
status_all[name] = [-2] if show_details:
print status_all[name]
logf.write("\n" + "execution status" + str( print name + " is in white list, thus skipped"
status_all[name]) + "\n") logf.write(
"\n" + name +
" is in white list, thus skipped\n")
print status_all[srcfile.name + '/' +
name]
logf.write(
"\n" + "execution status" + str(
status_all[srcfile.name + '/' +
name]) + "\n")
continue continue
thismethod = []
thismtdstr = "" thismethod = [] #method body lines
thismethod.append(thisl[indent:]) thismethod.append(thisl[indent:])
thismtdstr += thisl[indent:]
#get all the lines of a single method body
#into thismethod(list)
#and send it to single_defcom_extract
for y in range(x + 1, len(srcls)): for y in range(x + 1, len(srcls)):
srcls[y] = srcls[y].replace('\t', ' ')
if (srcls[y].startswith('def ') or if (srcls[y].startswith('def ') or
srcls[y].startswith('class ')): srcls[y].startswith('class ')):
#end of method
break break
elif (srcls[y].lstrip().startswith('def ')): elif (srcls[y].startswith(' def ')):
#end of method
break break
else: else:
thismethod.append(srcls[y][indent:]) thismethod.append(srcls[y][indent:])
thismtdstr += srcls[y][indent:]
thismtdcom = single_defcom_extract(0, thismtdcom = single_defcom_extract(0,
thismethod) thismethod)
allcoms.append(thismtdcom)
if (thismtdcom != ""): if (thismtdcom != ""):
status = sampcd_extract_and_run(thismtdcom, status = sampcd_extract_and_run(
name, logf) thismtdcom, name, logf, "method", name,
status_all[name] = status show_details)
status_all[srcfile.name + '/' +
name] = status
else: else:
print "no comments in method " + name + "\n"
logf.write("no comments in method " + name + if show_details:
"\n\n\n") print "no comments in method " + name + "\n"
status_all[name] = [-1] logf.write("no comments in method " +
print status_all[name] name + "\n\n\n")
logf.write("\n" + "execution status" + str( status_all[srcfile.name + '/' +
status_all[name]) + "\n") name] = [-1]
print status_all[srcfile.name + '/' +
name]
logf.write(
"\n" + "execution status" + str(
status_all[srcfile.name + '/' +
name]) + "\n")
else: else:
print cn + " is not in __all__ list" if show_details:
logf.write(cn + " is not in __all__ list\n\n") print cn + " is not in __all__ list"
logf.write(cn + " is not in __all__ list\n\n")
return [ return [
srcfile.name + " all list length: " + str(api_alllist_count), srcfile.name + " all list length: " + str(api_alllist_count),
"analysed api count: " + str(api_count) "analysed api count: " + str(api_count)
] ]
'''
Important constant lists:
filenames : the modules pending for check .
wlist : a list of API that should not trigger the example check .
It is composed of wlist_temp + wlist_inneed + wlist_ignore.
show_details: a boolean value to indicate whether it should be run
in debugging mode.
status_all: a status list containing all the execution status of all
APIs
srcfile: the source .py code file
'''
filenames = [ filenames = [
"layers/control_flow.py", "layers/io.py", "layers/nn.py", "layers/ops.py", "layers/control_flow.py", "layers/io.py", "layers/nn.py", "layers/ops.py",
"layers/tensor.py", "layers/learning_rate_scheduler.py", "layers/tensor.py", "layers/learning_rate_scheduler.py",
...@@ -432,8 +717,8 @@ filenames += [ ...@@ -432,8 +717,8 @@ filenames += [
filenames += [ filenames += [
"data_feeder.py", "dataset.py", "clip.py", "metrics.py", "executor.py", "data_feeder.py", "dataset.py", "clip.py", "metrics.py", "executor.py",
"initializer.py", "io.py", "nets.py", "optimizer.py", "profiler.py", "initializer.py", "io.py", "nets.py", "optimizer.py", "profiler.py",
"regularizer.py", "backward.py", "average.py", "profiler.py", "regularizer.py", "backward.py", "average.py", "unique_name.py",
"unique_name.py" "framework.py", "evaluator.py", "param_attr.py"
] ]
wlist_inneed = [ wlist_inneed = [
...@@ -464,32 +749,89 @@ wlist_inneed = [ ...@@ -464,32 +749,89 @@ wlist_inneed = [
"ExponentialMovingAverage.apply", "ExponentialMovingAverage.restore", "ExponentialMovingAverage.apply", "ExponentialMovingAverage.restore",
"ExponentialMovingAverage.update", "StaticRNN.step", "StaticRNN.step_input", "ExponentialMovingAverage.update", "StaticRNN.step", "StaticRNN.step_input",
"StaticRNN.step_output", "StaticRNN.update_memory", "DetectionMAP.reset", "StaticRNN.step_output", "StaticRNN.update_memory", "DetectionMAP.reset",
'StaticRNN.output' 'StaticRNN.output', "cuda_places", "CUDAPinnedPlace", "CUDAPlace",
"Program.parse_from_string"
] ]
wlist_temp = [ wlist_temp = [
'elementwise_floordiv', 'Layer', 'Layer.create_parameter', 'ChunkEvaluator',
'Layer.create_variable', 'Layer.sublayers', 'Layer.add_parameter', 'EditDistance',
'Layer.add_sublayer', 'Layer.parameters', 'Tracer', 'Layer.full_name', 'ErrorClipByValue',
'InMemoryDataset', 'layer_norm', 'bipartite_match', 'double_buffer', 'Program.clone',
'cumsum', 'thresholded_relu', 'group_norm', 'random_crop', 'py_func', 'cuda_pinned_places',
'row_conv', 'hard_shrink', 'ssd_loss', 'retinanet_target_assign', 'DataFeeder',
'InMemoryDataset.global_shuffle', 'InMemoryDataset.get_memory_data_size', 'elementwise_floordiv',
'DetectionMAP', 'hash', 'InMemoryDataset.set_queue_num', 'LayerNorm', 'Layer',
'Preprocessor', 'chunk_eval', 'GRUUnit', 'ExponentialMovingAverage', 'Layer.create_parameter',
'QueueDataset.global_shuffle', 'NumpyArrayInitializer', 'Layer.create_variable',
'create_py_reader_by_data', 'InMemoryDataset.local_shuffle', 'Layer.sublayers',
'InMemoryDataset.get_shuffle_data_size', 'size', 'edit_distance', 'nce', 'Layer.add_parameter',
'BilinearInitializer', 'NaturalExpDecay', 'noam_decay', 'Layer.add_sublayer',
'retinanet_detection_output', 'Pool2D', 'PipelineOptimizer', 'Layer.parameters',
'generate_mask_labels', 'isfinite', 'Tracer',
'InMemoryDataset.set_fleet_send_batch_size', 'cuda_profiler', 'unfold', 'Layer.full_name',
'Executor', 'InMemoryDataset.load_into_memory', 'ExponentialDecay', 'InMemoryDataset',
'BatchNorm', 'deformable_conv', 'InMemoryDataset.preload_into_memory', 'layer_norm',
'py_reader', 'linear_lr_warmup', 'InMemoryDataset.wait_preload_done', 'bipartite_match',
'CosineDecay', 'roi_perspective_transform', 'unique', 'ones_like', 'double_buffer',
'LambOptimizer', 'InMemoryDataset.release_memory', 'Conv2DTranspose', 'cumsum',
'QueueDataset.local_shuffle' 'thresholded_relu',
'group_norm',
'random_crop',
'py_func',
'row_conv',
'hard_shrink',
'ssd_loss',
'retinanet_target_assign',
'InMemoryDataset.global_shuffle',
'InMemoryDataset.get_memory_data_size',
'DetectionMAP',
'hash',
'InMemoryDataset.set_queue_num',
'LayerNorm',
'Preprocessor',
'chunk_eval',
'GRUUnit',
'ExponentialMovingAverage',
'QueueDataset.global_shuffle',
'NumpyArrayInitializer',
'create_py_reader_by_data',
'InMemoryDataset.local_shuffle',
'InMemoryDataset.get_shuffle_data_size',
'size',
'edit_distance',
'nce',
'BilinearInitializer',
'NaturalExpDecay',
'noam_decay',
'retinanet_detection_output',
'Pool2D',
'PipelineOptimizer',
'generate_mask_labels',
'isfinite',
'InMemoryDataset.set_fleet_send_batch_size',
'cuda_profiler',
'unfold',
'Executor',
'InMemoryDataset.load_into_memory',
'ExponentialDecay',
'BatchNorm',
'deformable_conv',
'InMemoryDataset.preload_into_memory',
'py_reader',
'linear_lr_warmup',
'InMemoryDataset.wait_preload_done',
'CosineDecay',
'roi_perspective_transform',
'unique',
'ones_like',
'LambOptimizer',
'InMemoryDataset.release_memory',
'Conv2DTranspose',
'QueueDataset.local_shuffle',
# wrong in dygraph/checkpoint.py ok in io.py [duplicated name]
'save_persistables@dygraph/checkpoint.py',
'load_persistables@dygraph/checkpoint.py'
] ]
''' '''
white list of private API/ redundant API white list of private API/ redundant API
...@@ -510,52 +852,155 @@ wlist_ignore = [ ...@@ -510,52 +852,155 @@ wlist_ignore = [
'Embedding.forward', 'Recall.eval', 'FC.forward', 'While.block' 'Embedding.forward', 'Recall.eval', 'FC.forward', 'While.block'
] ]
# only white on CPU
gpu_not_white = [
"deformable_conv", "cuda_places", "CUDAPinnedPlace", "CUDAPlace",
"cuda_profiler"
]
wlist = wlist_temp + wlist_inneed + wlist_ignore wlist = wlist_temp + wlist_inneed + wlist_ignore
status_all = {}
logf = open("log.txt", 'w') if len(sys.argv) < 2:
statusf = open("status.txt", 'w') print "Error: inadequate number of arguments"
print('''If you are going to run it on
if not os.path.isdir("./samplecode_temp"): "CPU: >>> python sampcd_processor.py cpu
os.mkdir("./samplecode_temp") "GPU: >>> python sampcd_processor.py gpu
for filename in filenames: ''')
srcfile = open(filename, 'r') sys.exit("lack arguments")
counts = srccoms_extract(srcfile, logf, status_all, wlist)
logf.write("\n\n" + str(counts) + "\n\n")
srcfile.close()
for root, dirs, files in os.walk("./samplecode_temp"):
for fntemp in files:
os.remove("./samplecode_temp/" + fntemp)
os.rmdir("./samplecode_temp")
statusf.write("status_all:\n" + str(status_all))
status_groups = {-2: [], -1: [], 0: [], 1: [], 2: [], 3: []}
ci_pass = True
for key in status_all:
statusl = status_all[key]
for ele in statusl:
if (ele != 0 and ele != -2):
ci_pass = False
break
if len(statusl) == 1:
status_groups[statusl[0]].append(key)
else:
for u in range(0, len(statusl)):
status_groups[statusl[u]].append(key + '_' + str(u + 1))
statusf.write('\n\ngrouped apis:\n' + str(status_groups) + '\n')
statusf.close()
logf.close()
temp_wlistf = open("tempwlist.txt", 'w')
wlist_temp = status_groups[1] + status_groups[2] + status_groups[
3] + status_groups[-1]
temp_wlistf.write(str(wlist_temp))
temp_wlistf.close()
print str(wlist_temp)
if not ci_pass:
print "Mistakes found in sample codes, refer to the log for details"
exit(1)
else: else:
print "Sample code check is successful!"
show_details = False
if sys.argv[1] == "gpu":
for _gnw in gpu_not_white:
wlist.remove(_gnw)
elif sys.argv[1] != "cpu":
print("Unrecognized argument:'" + sys.argv[1] + "' , 'cpu' or 'gpu' is "
+ "desired\n")
sys.exit("Invalid arguments")
if len(sys.argv) == 3:
if sys.argv[2] == "sd":
show_details = True
else:
print("Unrecognized argument:'" + sys.argv[2] + "' , 'sd' is " +
"desired\n")
sys.exit("Invalid arguments")
print("* * * * * * * * * * * * * * * * * * * * * * * *\n" +
"* *\n" +
"* API check -- Example Code Cheker *\n" +
"* *\n" +
"* *\n" +
"* This process is meant to check *\n" +
"* all example codes per CI to ensure *\n" +
"* the example codes can be run successfully *\n" +
"* *\n" +
"* *\n" +
"* Refer to the comments for detailed *\n" +
"* introduction *\n" +
"* *\n" +
"* *\n" +
"* * * * * * * * * * * * * * * * * * * * * * * *\n")
status_all = {}
#a file to record the terminal output
logf = open("example-code-check-log.txt", 'w')
# a temp directory to store temporary sample code file
# subprocess needs a single file to run the code
if not os.path.isdir("./samplecode_temp"):
os.mkdir("./samplecode_temp")
to_check = filenames
for filename in to_check:
srcfile = open(filename, 'r')
counts = srccoms_extract(srcfile, logf, status_all, wlist, show_details)
if show_details:
logf.write("\n\n" + str(counts) + "\n\n")
srcfile.close()
# clear temp files
for root, dirs, files in os.walk("./samplecode_temp"):
for fntemp in files:
os.remove("./samplecode_temp/" + fntemp)
os.rmdir("./samplecode_temp")
status_groups = {-2: [], -1: [], 0: [], 1: [], 2: [], 3: []}
ci_pass = True
for key in status_all:
statusl = status_all[key]
for ele in statusl:
if (ele != 0 and ele != -2 and ele != -1):
ci_pass = False
break
if len(statusl) == 1:
status_groups[statusl[0]].append(key)
else:
for u in range(0, len(statusl)):
status_groups[statusl[u]].append(key + '_' + str(u + 1))
logf.close()
print(
"\n\n------------------End of the Check-------------------------------------------\n\n"
)
errorapisl = status_groups[1] + status_groups[2] + status_groups[3]
if len(errorapisl) > 0:
print "Error raised from: " + str(errorapisl)
if not ci_pass:
print(
"\nOh no.. Mistakes found in sample codes, refer to the log for details\n\n"
)
print('''
- How to run it locally?
Simply put this script under directory:
Paddle/python/paddle/fluid/
and run in python 2.7 (as some interfaces of subprocess may
not work in python 3)
You must specify the device type to run the sample code on:
CPU: >>> python sampcd_processor.py cpu
GPU: >>> python sampcd_processor.py gpu
- How to debug?
This script has an option for showing the details of
the execution status:
>>> python sampcd_processor.py cpu sd
- NOTE:
Please ensure your are using
.. code-block:: python
[sample code starts here]
ONLY 1 BLANKSPACE between '::' and 'python'
''')
exit(1)
else:
print "Sample code check is successful!"
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册