sampcd_processor.py 34.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
#   Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
T
tianshuo78520a 已提交
14 15

import os
16
import sys
T
tianshuo78520a 已提交
17 18 19 20
import subprocess


def find_all(srcstr, substr):
21 22 23 24 25 26 27 28 29 30 31 32 33
    '''
    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
    '''

T
tianshuo78520a 已提交
34
    indices = []
35

T
tianshuo78520a 已提交
36
    gotone = srcstr.find(substr)
37

T
tianshuo78520a 已提交
38
    while (gotone != -1):
39

T
tianshuo78520a 已提交
40
        indices.append(gotone)
41

T
tianshuo78520a 已提交
42
        gotone = srcstr.find(substr, gotone + 1)
43

T
tianshuo78520a 已提交
44 45 46 47
    return indices


def check_indent(cdline):
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
    '''
    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
    '''

T
tianshuo78520a 已提交
65 66 67 68 69 70 71 72
    indent = 0
    for c in cdline:
        if c == '\t':
            indent += 4
        elif c == ' ':
            indent += 1
        if c != ' ' and c != '\t':
            break
73

T
tianshuo78520a 已提交
74 75 76 77 78
    return indent


#srccom: raw comments in the source,including ''' and original indent

79 80 81 82 83 84 85

def sampcd_extract_and_run(srccom,
                           name,
                           logf,
                           htype="def",
                           hname="",
                           show_details=False):
T
tianshuo78520a 已提交
86
    '''
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
    Extract and run sample codes from source comment and
    the result will be returned.

    As an ultimate result, this function returns a list of 
    status codes for each sample code (in top-down order)
    found in srccom.

    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
    
T
tianshuo78520a 已提交
103
    there may be several examples in a source comment
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
    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.
                  

    

T
tianshuo78520a 已提交
126 127
    '''

128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
    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 = []

T
tianshuo78520a 已提交
158
    if (len(sampcd_begins) == 0):
159 160 161 162 163 164

        print_header(logf, htype, hname)
        '''
        detect sample codes using >>> to format
        and consider this situation as wrong
        '''
T
tianshuo78520a 已提交
165 166 167
        if (srccom.find("Examples:") != -1):
            print "----example code check----\n"
            logf.write("\n----example code check----\n")
168

T
tianshuo78520a 已提交
169 170 171 172 173 174 175 176 177 178
            if (srccom.find(">>>") != -1):
                logf.write(
                    "Deprecated sample code style:\n\n    Examples:\n\n        >>>codeline\n        >>>codeline\n\n\n "
                    + "Please use '.. code-block:: python' to " +
                    "format sample code.\n")
                print(
                    "Deprecated sample code style:\n\n    Examples:\n\n        >>>codeline\n        >>>codeline\n\n\n "
                    + "Please use '.. code-block:: python' to " +
                    "format sample code.\n")
                status.append(2)
179 180 181
                print "status code for all sample codes in " + name + " : " + str(
                    status)

T
tianshuo78520a 已提交
182 183 184 185
        else:
            print "No sample code!\n"
            logf.write("\nNo sample code!\n")
            status.append(1)
186 187
            print "status code for all sample codes in " + name + " : " + str(
                status)
T
tianshuo78520a 已提交
188 189

    for y in range(1, len(sampcd_begins) + 1):
190

T
tianshuo78520a 已提交
191
        sampcd_begin = sampcd_begins[y - 1]
192 193
        sampcd = srccom[sampcd_begin + len(" code-block:: python") + 1:]

T
tianshuo78520a 已提交
194
        sampcd = sampcd.split("\n")
195

T
tianshuo78520a 已提交
196 197 198
        #remove starting empty lines
        while sampcd[0].replace(' ', '').replace('\t', '') == '':
            sampcd.pop(0)
199 200 201

        #the mininmum indent, which is the indent of the first 
        #non-empty line
T
tianshuo78520a 已提交
202
        min_indent = check_indent(sampcd[0])
203

T
tianshuo78520a 已提交
204
        sampcd_to_write = []
205

T
tianshuo78520a 已提交
206
        for i in range(0, len(sampcd)):
207

T
tianshuo78520a 已提交
208
            cdline = sampcd[i]
209

T
tianshuo78520a 已提交
210 211 212
            #handle empty lines or those only with spaces/tabs
            if cdline.strip() == '':
                continue
213

T
tianshuo78520a 已提交
214 215 216
            this_indent = check_indent(cdline)
            if (this_indent < min_indent):
                break
217

T
tianshuo78520a 已提交
218 219 220
            else:
                cdline = cdline.replace('\t', '    ')
                sampcd_to_write.append(cdline[min_indent:])
221

T
tianshuo78520a 已提交
222
        sampcd = '\n'.join(sampcd_to_write)
223 224 225 226
        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
T
tianshuo78520a 已提交
227 228 229 230 231 232 233 234 235 236
        sampcd += '\nprint ' + '\"' + name + ' sample code is executed successfully!\"\n'

        if (len(sampcd_begins) > 1):
            tfname = name + "_example_" + str(y) + ".py"
        else:
            tfname = name + "_example" + ".py"

        tempf = open("samplecode_temp/" + tfname, 'w')
        tempf.write(sampcd)
        tempf.close()
237

T
tianshuo78520a 已提交
238
        cmd = ["python", "samplecode_temp/" + tfname]
239

T
tianshuo78520a 已提交
240 241
        subprc = subprocess.Popen(
            cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
242 243 244 245
        output, error = subprc.communicate()

        msg = "".join(output)
        err = "".join(error)
T
tianshuo78520a 已提交
246

247 248 249 250 251
        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)
T
tianshuo78520a 已提交
252
            print("Error Raised from Sample Code " + name + " :\n")
253 254
            print err
            print msg
T
tianshuo78520a 已提交
255
            logf.write("\nError Raised from Sample Code " + name + " :\n")
256 257
            logf.write("\n" + msg + "\n")

T
tianshuo78520a 已提交
258
            status.append(3)
259 260
            print "status code for all sample codes in " + name + str(status)
        #It works!
T
tianshuo78520a 已提交
261 262
        else:
            status.append(0)
263 264 265 266 267 268 269
            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)
T
tianshuo78520a 已提交
270 271

        #msg is the returned code execution report
272

T
tianshuo78520a 已提交
273 274 275 276 277 278
        os.remove("samplecode_temp/" + tfname)

    return status


def single_defcom_extract(start_from, srcls, is_class_begin=False):
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
    '''
    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.

    '''
T
tianshuo78520a 已提交
294
    i = start_from
295

T
tianshuo78520a 已提交
296
    fcombody = ""  #def comment body
297 298 299 300 301

    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
T
tianshuo78520a 已提交
302 303

    for x in range(i + 1, len(srcls)):
304

T
tianshuo78520a 已提交
305
        if is_class_begin:
306 307

            if (srcls[x].replace('\t', '    ').startswith('    def ')):
T
tianshuo78520a 已提交
308
                break
309

T
tianshuo78520a 已提交
310 311
        if ((srcls[x].startswith('def ') or srcls[x].startswith('class '))):
            break
312

T
tianshuo78520a 已提交
313
        else:
314

T
tianshuo78520a 已提交
315 316 317 318 319 320 321 322 323
            if (comstart == -1 and srcls[x].replace(" ", '').replace(
                    "\t", '').replace("\n", '').startswith("\"\"\"")):
                comstart = x
                comstyle = 2
                continue
            if (comstyle == 2 and comstart != -1 and
                    srcls[x].replace(" ", '').replace("\t", '').replace(
                        "\n", '').startswith("\"\"\"")):
                break
324

T
tianshuo78520a 已提交
325 326 327 328 329 330 331 332 333 334 335 336
            if (comstart == -1 and srcls[x].replace(" ", '').replace(
                    "\t", '').replace("\n", '').startswith("\'\'\'")):
                comstart = x
                comstyle = 1
                continue
            if (comstyle == 1 and comstart != -1 and
                    srcls[x].replace(" ", '').replace("\t", '').replace(
                        "\n", '').startswith("\'\'\'")):
                break
            if (comstart !=
                    -1):  #when the comments start, begin to add line to fcombody
                fcombody += srcls[x]
337

T
tianshuo78520a 已提交
338 339 340 341
    return fcombody


def print_header(logf, htype, name):
342

T
tianshuo78520a 已提交
343 344
    print htype + " name:" + name
    print "-----------------------"
345

T
tianshuo78520a 已提交
346 347 348 349
    logf.write("\n\n" + htype + " name:" + name + "\n")
    logf.write("-----------------------\n")


350 351
def srcf_print(srcfile):

T
tianshuo78520a 已提交
352 353 354 355 356 357
    print "source file name:" + srcfile.name
    print "---------------------------------------------------"

    logf.write("source file name:" + srcfile.name + "\n")
    logf.write("---------------------------------------------------\n\n")

358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385

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.
    '''

T
tianshuo78520a 已提交
386 387 388 389 390 391 392
    srcc = srcfile.read()

    #2. get defs and classes header line number
    #set file pointer to its beginning
    srcfile.seek(0, 0)
    srcls = srcfile.readlines()  #source lines

393 394 395
    if show_details:
        srcf_print(srcfile)

T
tianshuo78520a 已提交
396 397 398 399
    #1. fetch__all__ list
    allidx = srcc.find("__all__")

    if (allidx != -1):
400

T
tianshuo78520a 已提交
401
        alllist = []
402 403

        #get all list for layers/ops.py
T
tianshuo78520a 已提交
404
        if (srcfile.name.find("ops.py") != -1):
405

T
tianshuo78520a 已提交
406
            for ai in range(0, len(srcls)):
407

T
tianshuo78520a 已提交
408
                if (srcls[ai].startswith("__all__")):
409

T
tianshuo78520a 已提交
410 411 412 413 414 415
                    lb = srcls[ai].find('[')
                    rb = srcls[ai].find(']')
                    if (lb == -1):
                        continue
                    allele = srcls[ai][lb + 1:rb].replace("'", '').replace(
                        " ", '').replace("\"", '')
416

T
tianshuo78520a 已提交
417
                    alllist.append(allele)
418 419 420 421 422 423 424

            if '' in alllist:
                alllist.remove('')

            if show_details:
                show_alllist(alllist)

T
tianshuo78520a 已提交
425 426
        else:
            alllist_b = allidx + len("__all__")
427

T
tianshuo78520a 已提交
428 429 430 431 432 433 434
            allstr = srcc[alllist_b + srcc[alllist_b:].find("[") + 1:alllist_b +
                          srcc[alllist_b:].find("]")]
            allstr = allstr.replace("\n", '').replace(" ", '').replace(
                "'", '').replace("\"", '')
            alllist = allstr.split(',')
            if '' in alllist:
                alllist.remove('')
435 436 437 438

            if show_details:
                show_alllist(alllist)

T
tianshuo78520a 已提交
439 440
        api_alllist_count = len(alllist)
        api_count = 0
441

T
tianshuo78520a 已提交
442
        handled = []
443 444

        #get src contents in layers/ops.py
T
tianshuo78520a 已提交
445
        if (srcfile.name.find("ops.py") != -1):
446

T
tianshuo78520a 已提交
447
            for i in range(0, len(srcls)):
448

T
tianshuo78520a 已提交
449
                if srcls[i].find("__doc__") != -1:
450

T
tianshuo78520a 已提交
451
                    opname = srcls[i][:srcls[i].find("__doc__") - 1]
452

T
tianshuo78520a 已提交
453
                    if opname in wlist:
454 455 456 457 458 459 460 461 462 463 464 465

                        status_all[srcfile.name + '/' + opname] = [-2]

                        if show_details:
                            print_header(logf, "def", opname)
                            print opname + " is in white list, thus skipped"
                            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")

T
tianshuo78520a 已提交
466
                        continue
467

T
tianshuo78520a 已提交
468 469 470 471
                    comstart = i
                    for j in range(i, len(srcls)):
                        if (srcls[j].find("\"\"\"") != -1):
                            comstart = i
472

T
tianshuo78520a 已提交
473 474 475 476 477
                    opcom = ""
                    for j in range(comstart + 1, len(srcls)):
                        opcom += srcls[j]
                        if (srcls[j].find("\"\"\"") != -1):
                            break
478 479 480

                    status = sampcd_extract_and_run(opcom, opname, logf, "def",
                                                    opname, show_details)
T
tianshuo78520a 已提交
481
                    api_count += 1
482 483 484 485 486 487
                    status_all[srcfile.name + '/' + opname] = status

                    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
T
tianshuo78520a 已提交
488 489

        for i in range(0, len(srcls)):
490 491 492 493

            if srcls[i].startswith(
                    'def '):  #a function header is detected in line i

T
tianshuo78520a 已提交
494 495
                f_header = srcls[i].replace(" ", '')
                fn = f_header[len('def'):f_header.find('(')]  #function name
496

T
tianshuo78520a 已提交
497 498
                if fn in handled:
                    continue
499

T
tianshuo78520a 已提交
500
                if fn in alllist:
501

T
tianshuo78520a 已提交
502
                    api_count += 1
503 504 505 506 507 508 509 510 511 512 513 514 515 516

                    if fn in wlist or fn + "@" + srcfile.name in wlist:

                        status_all[srcfile.name + '/' + fn] = [-2]

                        if show_details:
                            print_header(logf, "def", fn)
                            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")

T
tianshuo78520a 已提交
517
                        continue
518

T
tianshuo78520a 已提交
519
                    fcombody = single_defcom_extract(i, srcls)
520 521 522
                    if (fcombody == ""):  #if no comment 
                        print_header(logf, "def", fn)
                        print "WARNING: no comments in function " + fn + ", but it deserves."
T
tianshuo78520a 已提交
523
                        logf.write("no comments in function " + fn + "\n\n")
524 525
                        status_all[srcfile.name + '/' + fn] = [-1]
                        print status_all[srcfile.name + '/' + fn]
T
tianshuo78520a 已提交
526
                        logf.write("\n" + "execution status" + str(status_all[
527 528
                            srcfile.name + '/' + fn]) + "\n")

T
tianshuo78520a 已提交
529 530
                        continue
                    else:
531 532 533 534
                        status = sampcd_extract_and_run(fcombody, fn, logf,
                                                        "def", fn, show_details)
                        status_all[srcfile.name + '/' + fn] = status

T
tianshuo78520a 已提交
535
                else:
536 537 538 539 540
                    if show_details:
                        print_header(logf, "def", fn)
                        print fn + " not in __all__ list"
                        logf.write(fn + " not in __all__ list\n\n")

T
tianshuo78520a 已提交
541
            if srcls[i].startswith('class '):
542

T
tianshuo78520a 已提交
543
                c_header = srcls[i].replace(" ", '')
544 545
                cn = c_header[len('class'):c_header.find('(')]  #class name

T
tianshuo78520a 已提交
546 547
                if cn in handled:
                    continue
548

T
tianshuo78520a 已提交
549
                if cn in alllist:
550

T
tianshuo78520a 已提交
551
                    api_count += 1
552 553 554 555 556 557 558 559 560 561 562 563 564 565 566

                    if cn in wlist or cn + "@" + srcfile.name in wlist:

                        status_all[srcfile.name + '/' + cn] = [-2]

                        if show_details:

                            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")

T
tianshuo78520a 已提交
567
                        continue
568 569

                    #class comment
T
tianshuo78520a 已提交
570
                    classcom = single_defcom_extract(i, srcls, True)
571

T
tianshuo78520a 已提交
572
                    if (classcom != ""):
573 574 575 576 577

                        status = sampcd_extract_and_run(
                            classcom, cn, logf, "class", cn, show_details)
                        status_all[srcfile.name + '/' + cn] = status

T
tianshuo78520a 已提交
578
                    else:
579
                        print "WARNING: no comments in class itself " + cn + ", but it deserves.\n"
T
tianshuo78520a 已提交
580 581
                        logf.write("no comments in class itself " + cn +
                                   "\n\n\n")
582 583
                        status_all[srcfile.name + '/' + cn] = [-1]
                        print status_all[srcfile.name + '/' + cn]
T
tianshuo78520a 已提交
584
                        logf.write("\n" + "execution status" + str(status_all[
585 586 587
                            srcfile.name + '/' + cn]) + "\n")

                    #handling methods in class bodies
T
tianshuo78520a 已提交
588 589 590
                    for x in range(
                            i + 1,
                            len(srcls)):  #from the next line of class header 
591

T
tianshuo78520a 已提交
592 593 594 595
                        if (srcls[x].startswith('def ') or
                                srcls[x].startswith('class ')):
                            break
                        else:
596 597
                            #member method def header
                            srcls[x] = srcls[x].replace('\t', '    ')
T
tianshuo78520a 已提交
598 599
                            if (srcls[x].startswith(
                                    '    def ')):  #detect a mehtod header..
600

T
tianshuo78520a 已提交
601 602 603 604
                                thisl = srcls[x]
                                indent = len(thisl) - len(thisl.lstrip())
                                mn = thisl[indent + len('def '):thisl.find(
                                    '(')]  #method name
605 606 607

                                name = cn + "." + mn  #full name

T
tianshuo78520a 已提交
608
                                if mn.startswith('_'):
609 610 611 612 613 614 615 616

                                    if show_details:

                                        print mn + " is hidden, not visible to users\n"
                                        logf.write(
                                            "\n" + mn +
                                            " is hidden, not visible to users\n")

T
tianshuo78520a 已提交
617
                                    continue
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635

                                if name in wlist or name + "@" + srcfile.name in wlist:

                                    status_all[srcfile.name + '/' + name] = [-2]

                                    if show_details:

                                        print name + " is in white list, thus skipped"
                                        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")

T
tianshuo78520a 已提交
636
                                    continue
637 638

                                thismethod = []  #method body lines
T
tianshuo78520a 已提交
639
                                thismethod.append(thisl[indent:])
640 641 642 643

                                #get all the lines of a single method body 
                                #into thismethod(list)
                                #and send it to single_defcom_extract
T
tianshuo78520a 已提交
644
                                for y in range(x + 1, len(srcls)):
645
                                    srcls[y] = srcls[y].replace('\t', '    ')
T
tianshuo78520a 已提交
646 647
                                    if (srcls[y].startswith('def ') or
                                            srcls[y].startswith('class ')):
648
                                        #end of method
T
tianshuo78520a 已提交
649
                                        break
650 651
                                    elif (srcls[y].startswith('    def ')):
                                        #end of method
T
tianshuo78520a 已提交
652 653 654
                                        break
                                    else:
                                        thismethod.append(srcls[y][indent:])
655

T
tianshuo78520a 已提交
656 657
                                thismtdcom = single_defcom_extract(0,
                                                                   thismethod)
658

T
tianshuo78520a 已提交
659
                                if (thismtdcom != ""):
660 661 662 663 664 665
                                    status = sampcd_extract_and_run(
                                        thismtdcom, name, logf, "method", name,
                                        show_details)
                                    status_all[srcfile.name + '/' +
                                               name] = status

T
tianshuo78520a 已提交
666
                                else:
667 668 669 670 671 672 673 674 675 676 677 678 679 680

                                    if show_details:
                                        print "no comments in method " + name + "\n"
                                        logf.write("no comments in method " +
                                                   name + "\n\n\n")
                                        status_all[srcfile.name + '/' +
                                                   name] = [-1]
                                        print status_all[srcfile.name + '/' +
                                                         name]
                                        logf.write(
                                            "\n" + "execution status" + str(
                                                status_all[srcfile.name + '/' +
                                                           name]) + "\n")

T
tianshuo78520a 已提交
681
                else:
682 683 684 685
                    if show_details:
                        print cn + " is not in __all__ list"
                        logf.write(cn + " is not in __all__ list\n\n")

T
tianshuo78520a 已提交
686 687 688 689 690 691
    return [
        srcfile.name + " all list length: " + str(api_alllist_count),
        "analysed api count: " + str(api_count)
    ]


692 693 694 695 696 697 698 699 700 701 702 703 704
'''
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
'''

T
tianshuo78520a 已提交
705 706 707 708 709 710 711 712 713 714 715 716 717 718 719
filenames = [
    "layers/control_flow.py", "layers/io.py", "layers/nn.py", "layers/ops.py",
    "layers/tensor.py", "layers/learning_rate_scheduler.py",
    "layers/detection.py", "layers/metric_op.py"
]
filenames += [
    "dygraph/layers.py", "dygraph/base.py", "dygraph/nn.py",
    "dygraph/tracer.py", "dygraph/profiler.py", "dygraph/parallel.py",
    "dygraph/checkpoint.py", "dygraph/learning_rate_scheduler.py",
    "dygraph/backward_strategy.py"
]

filenames += [
    "data_feeder.py", "dataset.py", "clip.py", "metrics.py", "executor.py",
    "initializer.py", "io.py", "nets.py", "optimizer.py", "profiler.py",
720 721
    "regularizer.py", "backward.py", "average.py", "unique_name.py",
    "framework.py", "evaluator.py", "param_attr.py"
T
tianshuo78520a 已提交
722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742
]

wlist_inneed = [
    "append_LARS", "BuildStrategy.debug_graphviz_path",
    "BuildStrategy.enable_sequential_execution",
    "BuildStrategy.fuse_elewise_add_act_ops",
    "BuildStrategy.fuse_relu_depthwise_conv",
    "BuildStrategy.gradient_scale_strategy", "BuildStrategy.reduce_strategy",
    "BuildStrategy.remove_unnecessary_lock", "BuildStrategy.sync_batch_norm",
    "DynamicRNN.step_input", "DynamicRNN.static_input", "DynamicRNN.block",
    "DynamicRNN.update_memory", "DynamicRNN.output",
    "transpiler.DistributeTranspilerConfig",
    "transpiler.DistributeTranspilerConfig.slice_var_up",
    "transpiler.DistributeTranspilerConfig.split_method",
    "transpiler.DistributeTranspilerConfig.min_block_size",
    "DistributeTranspilerConfig.slice_var_up",
    "DistributeTranspilerConfig.split_method", "ModelAverage.apply",
    "ModelAverage.restore", "DistributeTranspilerConfig",
    "DistributeTranspilerConfig.min_block_size",
    "ExecutionStrategy.allow_op_delay", "load", "Accuracy.update",
    "ChunkEvaluator.update", "ExecutionStrategy.num_iteration_per_drop_scope",
F
flame 已提交
743
    "ExecutionStrategy.num_threads", "CompiledProgram._with_inference_optimize",
T
tianshuo78520a 已提交
744 745 746 747 748 749 750 751
    "CompositeMetric.add_metric", "CompositeMetric.update",
    "CompositeMetric.eval", "DetectionMAP.get_map_var", "MetricBase",
    "MetricBase.reset", "MetricBase.get_config", "MetricBase.update",
    "MetricBase.eval", "Accuracy.eval", "Auc.update", "Auc.eval",
    "EditDistance.update", "EditDistance.eval",
    "ExponentialMovingAverage.apply", "ExponentialMovingAverage.restore",
    "ExponentialMovingAverage.update", "StaticRNN.step", "StaticRNN.step_input",
    "StaticRNN.step_output", "StaticRNN.update_memory", "DetectionMAP.reset",
752 753
    'StaticRNN.output', "cuda_places", "CUDAPinnedPlace", "CUDAPlace",
    "Program.parse_from_string"
T
tianshuo78520a 已提交
754 755 756
]

wlist_temp = [
757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834
    'ChunkEvaluator',
    'EditDistance',
    'ErrorClipByValue',
    'Program.clone',
    'cuda_pinned_places',
    'DataFeeder',
    'elementwise_floordiv',
    'Layer',
    'Layer.create_parameter',
    'Layer.create_variable',
    'Layer.sublayers',
    'Layer.add_parameter',
    'Layer.add_sublayer',
    'Layer.parameters',
    'Tracer',
    'Layer.full_name',
    'InMemoryDataset',
    'layer_norm',
    'bipartite_match',
    'double_buffer',
    'cumsum',
    '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'
T
tianshuo78520a 已提交
835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854
]
'''
white list of private API/ redundant API
'''
wlist_ignore = [
    'elementwise_pow', 'WeightedAverage.reset', 'ChunkEvaluator.eval',
    'NCE.forward', 'elementwise_div', 'BilinearTensorProduct.forward',
    'NoamDecay.step', 'elementwise_min', 'PiecewiseDecay.step',
    'Conv3DTranspose.forward', 'elementwise_add', 'IfElse.output',
    'IfElse.true_block', 'InverseTimeDecay.step', 'PolynomialDecay.step',
    'Precision.eval', 'enabled', 'elementwise_max', 'stop_gperf_profiler',
    'IfElse.false_block', 'WeightedAverage.add', 'Auc.trapezoid_area',
    'elementwise_mul', 'GroupNorm.forward', 'SpectralNorm.forward',
    'elementwise_sub', 'Switch.case', 'IfElse.input', 'prepare_context',
    'PRelu.forward', 'Recall.update', 'start_gperf_profiler',
    'TreeConv.forward', 'Conv2D.forward', 'Switch.default', 'elementwise_mod',
    'Precision.update', 'WeightedAverage.eval', 'Conv3D.forward',
    'Embedding.forward', 'Recall.eval', 'FC.forward', 'While.block'
]

855 856 857 858 859 860
# only white on CPU
gpu_not_white = [
    "deformable_conv", "cuda_places", "CUDAPinnedPlace", "CUDAPlace",
    "cuda_profiler"
]

T
tianshuo78520a 已提交
861
wlist = wlist_temp + wlist_inneed + wlist_ignore
862 863 864 865 866 867 868 869 870

if len(sys.argv) < 2:
    print "Error: inadequate number of arguments"
    print('''If you are going to run it on 
        "CPU: >>> python sampcd_processor.py cpu
        "GPU: >>> python sampcd_processor.py gpu
        ''')
    sys.exit("lack arguments")

T
tianshuo78520a 已提交
871
else:
872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006

    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!"