From 3e6d355663d301f21ab60362769054d0f06c217a Mon Sep 17 00:00:00 2001 From: Sami Vaarala Date: Mon, 16 Mar 2015 15:30:27 +0200 Subject: [PATCH] Buildsite changes to use YAML API docs --- website/buildsite.py | 140 +++++++++++++++++++++---------------------- 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/website/buildsite.py b/website/buildsite.py index 2905c1ce..d298e559 100644 --- a/website/buildsite.py +++ b/website/buildsite.py @@ -5,6 +5,7 @@ import os import sys +import traceback import time import datetime import shutil @@ -12,6 +13,8 @@ import re import tempfile import atexit import md5 +import json +import yaml from bs4 import BeautifulSoup, Tag colorize = True @@ -92,6 +95,13 @@ def stripNewline(x): return x[:-1] return x +def splitNewlineNoLastEmpty(x): + assert(x is not None) + res = x.split('\n') + if len(res) > 0 and res[-1] == '': + res = res[:-1] + return res + def validateAndParseHtml(data): # first parse as xml to get errors out ign_soup = BeautifulSoup(data, 'xml') @@ -186,36 +196,6 @@ def renderFancyStack(inp_line): return ' '.join(res) + '\n' # stack is a one-liner; spaces are for text browser rendering -def parseApiDoc(filename): - f = open(filename, 'rb') - parts = {} - state = None - for line in f.readlines(): - line = stripNewline(line) - if line.startswith('='): - state = line[1:] - elif state is not None: - if not parts.has_key(state): - parts[state] = [] - parts[state].append(line) - else: - if line != '': - raise Exception('unparsed non-empty line: %r' % line) - else: - # ignore - pass - f.close() - - # remove leading and trailing empty lines - for k in parts: - p = parts[k] - while len(p) > 0 and p[0] == '': - p.pop(0) - while len(p) > 0 and p[-1] == '': - p.pop() - - return parts - # C99: these are used if available type_repl_c99_32bit = [ ['duk_int_t', 'int' ], @@ -275,22 +255,22 @@ def substitutePrototypeTypes(line, repl): line = line.replace(t[0], t[1]) return line -def processApiDoc(parts, funcname, testrefs, used_tags): +def processApiDoc(doc, testrefs, used_tags): res = [] # the 'hidechar' span is to allow browser search without showing the char - res.append('

' % funcname) - res.append('.%s()' % (funcname, funcname)) - if floating_list_tags and parts.has_key('tags'): - p = sorted(parts['tags'], reverse=True) # reversed because floated to right (which reverses DOM order) + res.append('

' % doc['name']) + res.append('.%s()' % (doc['name'], doc['name'])) + if floating_list_tags and len(doc['tags']) > 0: + p = sorted(doc['tags'], reverse=True) # reversed because floated to right (which reverses DOM order) # For now, add the introduced version as a tag - if parts.has_key('introduced'): - p = [ parts['introduced'][0] ] + p - if parts.has_key('deprecated'): + if doc.has_key('introduced'): + p = [ doc['introduced'] ] + p + if doc.has_key('deprecated'): # XXX: must mark deprecation pass - if parts.has_key('removed'): + if doc.has_key('removed'): # XXX: must mark removal pass @@ -306,8 +286,8 @@ def processApiDoc(parts, funcname, testrefs, used_tags): res.append('
') - if parts.has_key('proto'): - p = parts['proto'] + if doc.has_key('proto'): + p = splitNewlineNoLastEmpty(doc['proto']) res.append('
') res.append('

Prototype

') alt_typing_c99 = [] @@ -334,10 +314,11 @@ def processApiDoc(parts, funcname, testrefs, used_tags): else: pass - if parts.has_key('stack'): - p = parts['stack'] + if doc.has_key('stack'): + p = splitNewlineNoLastEmpty(doc['stack']) res.append('
') res.append('

Stack

') + assert(len(p) > 0) for line in p: res.append('
' + \
 			           '%s' % htmlEscape(line) + \
@@ -351,8 +332,8 @@ def processApiDoc(parts, funcname, testrefs, used_tags):
 		res.append('
') # api-part res.append('') - if parts.has_key('summary'): - p = parts['summary'] + if doc.has_key('summary'): + p = splitNewlineNoLastEmpty(doc['summary']) res.append('
') res.append('

Summary

') @@ -376,8 +357,8 @@ def processApiDoc(parts, funcname, testrefs, used_tags): res.append('
') # api-part res.append('') - if parts.has_key('example'): - p = parts['example'] + if doc.has_key('example'): + p = splitNewlineNoLastEmpty(doc['example']) res.append('
') res.append('

Example

') res.append('
')
@@ -387,8 +368,8 @@ def processApiDoc(parts, funcname, testrefs, used_tags):
 		res.append('
') # api-part res.append('') - if parts.has_key('seealso'): - p = parts['seealso'] + if doc.has_key('seealso'): + p = doc['seealso'] res.append('
') res.append('

See also

') res.append('
    ') @@ -401,9 +382,9 @@ def processApiDoc(parts, funcname, testrefs, used_tags): if testcase_refs: res.append('
    ') res.append('

    Related test cases

    ') - if testrefs.has_key(funcname): + if testrefs.has_key(doc['name']): res.append('
      ') - for i in testrefs[funcname]: + for i in testrefs[doc['name']]: res.append('
    • %s
    • ' % htmlEscape(i)) res.append('
    ') else: @@ -411,15 +392,15 @@ def processApiDoc(parts, funcname, testrefs, used_tags): res.append('
    ') # api-part res.append('') - if not testrefs.has_key(funcname): + if not testrefs.has_key(doc['name']): res.append('
    This API call has no test cases.
    ') - if list_tags and parts.has_key('tags'): + if list_tags and len(doc['tags']) > 0: # FIXME: placeholder res.append('
    ') res.append('

    Tags

    ') res.append('

    ') - p = parts['tags'] + p = doc['tags'] for idx, val in enumerate(p): if idx > 0: res.append(' ') @@ -428,8 +409,8 @@ def processApiDoc(parts, funcname, testrefs, used_tags): res.append('

    ') # api-part res.append('') - if parts.has_key('fixme'): - p = parts['fixme'] + if doc.has_key('fixme'): + p = splitNewlineNoLastEmpty(doc['fixme']) res.append('
    ') for i in p: res.append(htmlEscape(i)) @@ -704,9 +685,7 @@ def createTagIndex(api_docs, used_tags): res.append('

    ' + htmlEscape(tag) + '

    ') res.append('
      ') for doc in api_docs: - if not doc['parts'].has_key('tags'): - continue - for i in doc['parts']['tags']: + for i in doc['tags']: if i != tag: continue res.append('
    • %s
    • ' % (htmlEscape(doc['name']), htmlEscape(doc['name']))) @@ -723,7 +702,7 @@ def generateApiDoc(apidocdir, apitestdir): tmpfiles = os.listdir(apidocdir) apifiles = [] for filename in tmpfiles: - if os.path.splitext(filename)[1] == '.txt': + if os.path.splitext(filename)[1] == '.yaml': apifiles.append(filename) apifiles.sort() #print(apifiles) @@ -743,19 +722,40 @@ def generateApiDoc(apidocdir, apitestdir): # scan api doc files used_tags = [] - api_docs = [] # [ { 'parts': xxx, 'name': xxx } ] + api_docs = [] # structure from YAML file directly for filename in apifiles: - parts = parseApiDoc(os.path.join(apidocdir, filename)) + apidoc = None + try: + with open(os.path.join(apidocdir, filename), 'rb') as f: + apidoc = yaml.safe_load(f) + if isinstance(apidoc, (str, unicode)): + apidoc = None + raise Exception('parsed as string') + except: + print 'WARNING: FAILED TO PARSE API DOC: ' + str(filename) + print traceback.format_exc() + pass + funcname = os.path.splitext(os.path.basename(filename))[0] - if parts.has_key('tags') and 'omit' in parts['tags']: - print 'Omit API doc: ' + str(funcname) - continue - if parts.has_key('tags'): - for i in parts['tags']: + + #print(json.dumps(apidoc, indent=4)) + + if apidoc is not None: + if not apidoc.has_key('tags'): + apidoc['tags'] = [] # ensures tags is present + apidoc['name'] = funcname # add funcname automatically + + if 'omit' in apidoc['tags']: + print 'Omit API doc: ' + str(funcname) + continue + + for i in apidoc['tags']: + assert(i is not None) if i not in used_tags: used_tags.append(i) - api_docs.append({ 'parts': parts, 'name': funcname }) + + api_docs.append(apidoc) used_tags.sort() @@ -805,11 +805,11 @@ def generateApiDoc(apidocdir, apitestdir): data = None try: - data = processApiDoc(doc['parts'], doc['name'], testrefs, used_tags) + data = processApiDoc(doc, testrefs, used_tags) res += data except: print repr(data) - print 'FAIL: ' + repr(filename) + print 'FAIL: ' + repr(doc['name']) raise print('used tags: ' + repr(used_tags))