def include_snippet(parameters, config): filename = find_include_file(parameters[0], config["include_directories"]) if filename == None: qa.Log( config, "File '%s' not found in search-path %s" % (parameters[0], config["include_directories"])) return "" lines = load_include_file(filename) if lines == None: return "" brush = "" if filename[-3:] == '.cf': brush = "cf3" begin = re.compile(parameters[1]) if len(parameters) < 3: end = re.compile("a^") # never matches anything else: end = re.compile(parameters[2]) markdown_lines = [] skip_block = True in_documentation = False for line in lines: if skip_block == False: if line.find("#[%-%]") == 0: skip_block = True continue # #@ interrupts code block, interpret documentation in example code if line.find("#@ ") == 0: line = line[3:] if not in_documentation: # terminate open code block, if any if len(markdown_lines): markdown_lines.append("```\n\n") in_documentation = True elif in_documentation: markdown_lines.append("\n```%s\n" % brush) in_documentation = False # ignore other comments, otherwise append if line[0] != '#': markdown_lines.append(line) if end.match(line) != None: break elif (begin.match(line) != None) or (line.find("#[%+%]") == 0): skip_block = False if line[0] != '#': markdown_lines.append(line) if not len(markdown_lines): raise Exception("Snippet not found: %s" % begin.pattern) return list() return prune_include_lines(markdown_lines, filename)
def library_include(parameters, config): markdown_lines = [] policy_filename = parameters[0] + ".json" policy_json = config.get(policy_filename) html_name = config.get("context_current_html") qa.LogProcessStart(config, "library_include: %s" % policy_filename) if policy_json == None: policy_path = config[ "project_directory"] + "/_generated/" + policy_filename if not os.path.exists(policy_path): qa.Log(config, "File does not exist: " + policy_path) return markdown_lines policy_json = json.load(open(policy_path, 'r')) config[policy_filename] = policy_json # for all bundles and bodies... for key in policy_json.keys(): element_list = policy_json[key] current_type = None for element in element_list: errorString = [] ignore = False namespace = element["namespace"] if namespace == "default": namespace = None name = element["name"] element_type = element.get("bundleType") if element_type == None: element_type = element.get("bodyType") if element_type == None: qa.Log(config, "element without type: " + name) continue # start a new block for changed types # Assumes that bundles and bodies are grouped by type in library.cf print_type = False if element_type != current_type: current_type = element_type print_type = True prototype = name if namespace: prototype = namespace + ":" + prototype title = prototype link_target = prototype + "()" if not namespace: link_target = parameters[0] + ":" + link_target linkresolver.addLinkToMap(prototype + "()", link_target, html_name + "#" + prototype, config) code_lines = [] documentation_lines = [] documentation_dict = dict() sourceLines = [] sourceLine = -1 try: source_path = element["sourcePath"] source_path = os.path.normpath(source_path) source_file = open(source_path, 'r') sourceLine = element["line"] - 1 # zero-based indexing sourceLines = source_file.readlines()[sourceLine:] except: qa.Log( config, "could not include code for element %s - check %s" % (name, source_path)) if len(sourceLines): headerLines = list() code_lines.append("\n```cf3\n") code_lines.append(sourceLines[0]) del sourceLines[0] in_code = False for line in sourceLines: if not in_code: line = line.lstrip() if line.find("{") == 0: in_code = True else: headerLines.append(line) if in_code: code_lines.append(line) # super-naive parser... if line.find("}\n") == 0: break code_lines.append("\n```\n") # scan comments for doxygen-style documentation if len(headerLines): current_tag = None current_param = None current_line = "" for headerLine in headerLines: if headerLine.find("#") != 0: continue headerLine = headerLine[1:].rstrip() # strip single whitespace, but maintain indentation if headerLine.find(" ") == 0: headerLine = headerLine[1:] if headerLine.lstrip().find("@") == 0: current_param = None headerLine = headerLine.lstrip()[1:] current_tag = headerLine tag_end = headerLine.find(" ") if tag_end != -1: current_tag = current_tag[:tag_end] headerLine = headerLine[tag_end + 1:] documentation_dict[current_tag] = "" if current_tag == None: continue if current_tag == "ignore": ignore = True break if current_tag == "param": if current_param == None: current_param = headerLine[:headerLine.find(" " )] headerLine = headerLine[len(current_param) + 1:] documentation_dict[ "param_" + current_param] = headerLine + "\n" else: documentation_dict[ "param_" + current_param] += headerLine + "\n" else: documentation_dict[ current_tag] += headerLine + "\n" brief = documentation_dict.get("brief", None) if brief: documentation_lines.append("**Description:** ") documentation_lines.append(brief) documentation_lines.append("\n") else: errorString.append("Missing description") return_doc = documentation_dict.get("return", None) if return_doc: documentation_lines.append("**Return value:** ") documentation_lines.append(return_doc) documentation_lines.append("\n") else: # no header lines errorString.append("No documentation") else: # no source lines errorString.append( "No source code or unable to read source code") if ignore: continue arguments = element["arguments"] argument_idx = 0 argument_lines = [] while argument_idx < len(arguments): if argument_idx == 0: prototype += "(" argument_lines.append("**Arguments:**\n\n") argument = arguments[argument_idx] prototype += argument argument_line = "* ```" + argument + "```" # if we have already found documentation for this, use it param_line = documentation_dict.get("param_" + argument) if param_line == None: errorString.append("No documentation for parameter %s" % argument) if param_line != None: argument_line += ": " + param_line # find out where the argument is being used elif key == "bundles": for promise_type in element["promiseTypes"]: promise_type_link = "`" + promise_type["name"] + "`" for context in promise_type["contexts"]: for promise in context["promises"]: promiser = promise["promiser"] if promiser.find("(" + argument + ")") != -1: argument_line += ", used as promiser of type " + promise_type_link argument_line += "\n" else: argument_line += resolveAttribute( promise["attributes"], argument) if len(argument_line): argument_line += " of " + promise_type_link + " promiser *" + promiser + "*" argument_line += "\n" elif key == "bodies": for context in element["contexts"]: argument_line += resolveAttribute( context["attributes"], argument) argument_line += "\n" argument_lines.append(argument_line) argument_idx += 1 if argument_idx == len(arguments): prototype += ")" else: prototype += ", " if print_type: link_map = config["link_map"] if ("`body %s`" % current_type) in link_map: printable_type = "[%s]%s bodies" % ( current_type, link_map["`body %s`" % current_type][0]) elif ("`bundle %s`" % current_type) in link_map: printable_type = "[%s]%s bundles" % ( current_type, link_map["`bundle %s`" % current_type][0]) elif ("`%s`" % current_type) in link_map: printable_type = "[%s]%s %s" % ( current_type, link_map["`%s`" % current_type][0], key) elif key == "bundles": printable_type = "%s [bundles][bundles]" % current_type else: printable_type = current_type markdown_lines.append("### %s\n" % printable_type) markdown_lines.append("\n") markdown_lines.append("#### " + title + " ####\n") markdown_lines.append("\n") markdown_lines.append("**Prototype:** `" + prototype + "`\n") markdown_lines.append("\n") if len(documentation_lines): markdown_lines.append(documentation_lines) markdown_lines.append("\n") if len(argument_lines): markdown_lines.append(argument_lines) markdown_lines.append("\n") if len(code_lines): markdown_lines.append("**Implementation:**\n") markdown_lines.append("\n") markdown_lines.append(code_lines) markdown_lines.append("\n") markdown_lines.append("\n") if len(errorString): locationString = "`in library `" + os.path.relpath( source_path) + "` (%d)" % sourceLine qa.LogMissingDocumentation(config, prototype, errorString, locationString) errorString = [] if len(markdown_lines) == 0: qa.Log(config, "Failure to include " + parameters[0]) return markdown_lines
def document_type(type, type_definition, excludes, config): link_map = config["link_map"] lines = [] type_status = type_definition.get("status", "normal") type_attributes = type_definition.get("attributes") attributes = sorted(type_attributes.keys()) for attribute in attributes: if attribute in excludes: continue attribute_definition = type_attributes.get(attribute) if attribute_definition == None: qa.Log( config, "cfdoc_macros: syntax_map - no definition for attribute %s in type %s" % (attribute, type)) continue attribute_status = attribute_definition.get("status", "normal") attribute_type = attribute_definition.get("type") attribute_range = attribute_definition.get("range") attribute_link = "`%s`" % attribute if attribute_status == "normal" and (not attribute_link in link_map): qa.LogMissingDocumentation(config, type + "/" + attribute, ["No documentation for attribute"], "") if attribute_type == "body": if ("`body %s`" % attribute) in link_map: attribute_type = "body [`%s`]%s" % ( attribute, link_map["`body %s`" % attribute][0]) elif ("`%s`" % attribute) in link_map: attribute_type = "body `%s`" % attribute else: attribute_type = "body `%s`" % attribute qa.LogMissingDocumentation(config, type + "/" + attribute, ["No documentation for body type"], "") elif attribute_type == "option": if attribute_range == "true,false,yes,no,on,off": attribute_type = "`boolean`" attribute_range = None else: attribute_type = "one of `%s`" % attribute_range.replace( ",", "`, `") attribute_range = None elif attribute_type == "context": attribute_type = "class expression" else: attribute_type = "`%s`" % attribute_type if attribute_status == "normal": attribute_status = "" else: attribute_status = "<sup>**%s**</sup>" % attribute_status if attribute_link in link_map: type_link = link_map.get("`%s`" % type) if not type_link: type_link = link_map.get("`body %s`" % type) if type_link: type_link = type_link[0] anchors = link_map[attribute_link] anchor = anchors[0] # pick the anchor that matches the currently documented type/attribute best if len(anchors) > 1: highscore = 0 for a in anchors: score = 0 if type in a: score += 1 if attribute in a: score += 1 if highscore < score: highscore = score anchor = a attribute_link = "[%s]%s" % (attribute_link, anchor) line = "* %s%s: %s" % (attribute_link, attribute_status, attribute_type) if attribute_range: line += " in range `%s`" % attribute_range lines.append(line + "\n") lines.append("\n") return lines
def document_syntax_map(tree, branch, config): lines = [] tree = tree[branch] # JSON structure in `tree` is: # * type -> dict # * status: normal|deprecated # * attributes -> dict # * attr_name -> dict # * attribute: attr_name # * status: normal|deprecated # * type: int, string, slist... # * range: regex # * visibility: (ignored) # first, collect everything that all types have and call it "common" types = sorted(tree.keys()) common_attributes = dict() if not "common" in types: try: common_attributes = copy.deepcopy( tree.get("classes").get("attributes")) except: qa.Log(config, "cfdoc_macros: syntax_map - no promise type classes?!") for common_attribute in common_attributes.keys(): type_count = 0 for type in types: if tree.get(type).get("attributes").get(common_attribute): type_count += 1 if type_count != len(types): del common_attributes[common_attribute] if len(common_attributes): common_definition = dict() common_definition["status"] = "normal" common_definition["attributes"] = common_attributes lines.append( "### [Common Attributes][Promise Types and Attributes#Common Attributes]\n\n" ) lines.append(document_type("common", common_definition, [], config)) excludes = common_attributes.keys() link_map = config["link_map"] for type in types: link = None if branch == "bodyTypes" and ("`body " + type + "`") in link_map: # hack for classes, common and file bodies - see _reference.md link = link_map.get("`body " + type + "`") else: link = link_map.get("`" + type + "`") if link: lines.append("### [%s]%s\n\n" % (type, link[0])) else: lines.append("### %s\n\n" % type) qa.LogMissingDocumentation(config, type, ["No documentation for type"], "") type_definition = tree.get(type) if type_definition == None: qa.Log( config, "cfdoc_macros: syntax_map - no definition for type %s" % type) continue lines.append(document_type(type, type_definition, excludes, config)) return lines
def promise_attribute(parameters, config): lines = [] if not "context_current_header" in config: sys.stdout.write( 'ERROR: no "context_current_header" in config when reading promise_attribute with %r parameters, expect broken output' % (parameters)) return lines header = config["context_current_header"] promise_types = config["syntax_map"]["promiseTypes"] body_types = config["syntax_map"]["bodyTypes"] if header[2].find("Attributes") != -1: # assumption for promise type definition # header[1] = promise type # header[2] = "Attributes" # header[3] = attribute name # header[4] = body attribute promise_type_def = promise_types[header[1]] attribute_def = promise_type_def["attributes"][header[3]] attribute_type = attribute_def["type"] if attribute_type == "body": if header[4]: body_type_def = body_types[header[3]] attribute_def = body_type_def["attributes"][header[4]] attribute_type = attribute_def["type"] else: lines.append("**Type:** `body %s`\n\n" % header[3]) return lines elif header[2] == "Control Promises" or header[2] == "Common Control": # assume body control promise # header[1] = component name (cf-*) # header[2] = "Control Promises" # header[3] = body attribute # cut off the cf- prefix and 'd'; this will leave the executor # general exception for body common control component_name = header[1][3:] if header[2] == "Common Control": component_name = "common" if component_name[-1] == 'd': component_name = component_name[:-1] if component_name == "exec": # ugl-hack component_name = "executor" body_type_def = body_types[component_name] attribute_def = body_type_def["attributes"][header[3]] attribute_type = attribute_def["type"] else: qa.Log( config, "Error: Document structure does not support promise_attribute macro!" ) return lines attribute_range = attribute_def.get("range") if attribute_type == "option": if attribute_range == "true,false,yes,no,on,off": lines.append("**Type:** `boolean`\n\n") else: attribute_values = attribute_range.split(",") lines.append("**Type:** (menu option)\n\n") lines.append("**Allowed input range:**\n\n") for attribute_value in attribute_values: lines.append("* ```%s```\n" % attribute_value) lines.append("\n") elif attribute_type == "bundle": lines.append("**Type:** `%s`\n\n" % attribute_type) else: lines.append("**Type:** `%s`\n\n" % attribute_type) if attribute_range == "": lines.append("**Allowed input range:** (arbitrary string)\n\n") else: lines.append("**Allowed input range:** `%s`\n\n" % attribute_range) if parameters: lines.append("**Default value:** %s\n\n" % parameters[0]) return lines