Skip to content

Functions

add_type_metadata(schema, yaml, debug=False)

Modified a list of yaml entries in place to add type information from a parsed schema.

Parameters:

Name Type Description Default
schema

List of schema representations from parse_schema.

required
yaml

List of yaml representations from parse_yaml.

required
debug

Print debug information

False

Returns: Nothing.

Source code in yamldoc/parser.py
def add_type_metadata(schema, yaml, debug = False):
    '''
    Modified a list of yaml entries in place to add type information
    from a parsed schema.

    Arguments:
        schema: List of schema representations from parse_schema.
        yaml: List of yaml representations from parse_yaml.
        debug: Print debug information

    Returns: 
        Nothing.
    '''
    # Loop over each value of the schema 
    for name, variables in schema.items():
        # Find the corresponding entry in the YAML.

        # Special case: if the name is base in the schema
        # these are top level variables
        # which need to be dealt with seperately.
        if name == "base": 
            # Look for top level entries
            for var, var_type in variables.items(): 
                for value in yaml:
                    if not value.isBase:
                        if var == value.key:
                            value.type = var_type
        else:
            for value in yaml:
                if value.isBase:
                    if name == value.name:
                        for var, var_type in variables.items():
                            for entry in value.entries:
                                if var == entry.key:
                                    if debug: print(f"Setting type of {var}")
                                    entry.type = var_type
                                    # If we find at least one
                                    # then we can say that
                                    # there's a schema.
                                    value.has_schema = True
                                    entry.has_schema = True

count_indent(line)

Count indentation level.

Parameters:

Name Type Description Default
line

A character string to be processed.

required
Source code in yamldoc/parser.py
def count_indent(line):
    '''
    Count indentation level.

    Arguments:
        line: A character string to be processed.
    '''
    return len(line) - len(line.lstrip(' '))

key_value(line)

Extract a key value pair from a single YAML line.

Parameters:

Name Type Description Default
line

A character string to be processed.

required

Returns:

Type Description

Key value pairing; alternatively, if not value i present, just the key.

Source code in yamldoc/parser.py
def key_value(line):
    '''
    Extract a key value pair from a single YAML line.

    Arguments:
        line: A character string to be processed.

    Returns:
        Key value pairing; alternatively, if not value i present, just the key.
    '''
    try:
        key, value = line.rstrip().lstrip(' ').split(":")
    except ValueError: 
        values = line.rstrip().lstrip(' ').split(":")
        key = values[0]
        value = ''.join(values[1:])
    if not value: return (key)
    return (key, value.lstrip(" "))

main(yaml_path, char="#'", debug=False, schema_path=None, title='Configuration Parameters Reference', description='Any information about this page goes here.')

Takes a given YAML file and optionally an associated schema, parsing each for their key value pairings and reports the results as a markdown document.

Parameters:

Name Type Description Default
yaml_path

Path to YAML file.

required
schema_path

Path to schema file.

None
char

Special character to identify comments to be included in YAMLDOC documentation.

"#'"
debug

Print debug information

False
title

Title of markdown generated.

'Configuration Parameters Reference'
description

Description given below the title in markdown.

'Any information about this page goes here.'

Returns: Nothing, prints to stdout.

Source code in yamldoc/parser.py
def main(yaml_path, char = "#'", debug = False, schema_path = None, title = "Configuration Parameters Reference", description = "Any information about this page goes here."):
    '''
    Takes a given YAML file and optionally an associated schema, parsing each for their key value pairings and reports the results as a markdown document.

    Arguments:
        yaml_path: Path to YAML file.
        schema_path: Path to schema file. 
        char: Special character to identify comments to be included in YAMLDOC documentation.
        debug: Print debug information
        title: Title of markdown generated.
        description: Description given below the title in markdown.

    Returns: 
        Nothing, prints to stdout.
    '''
    # If a schema has been specified, add the
    # type information to the rest of the 
    # variables.
    if schema_path is not None:
        schema, specials = parse_schema(schema_path, debug)
        yaml = parse_yaml(yaml_path, char, debug)

        # Edit the yaml in place with type information.
        add_type_metadata(schema, yaml, debug)

        if "_yamldoc_title" in specials:
            title = specials["_yamldoc_title"]

        if "_yamldoc_description" in specials:
            description = specials["_yamldoc_description"]


        # And do the printing
        print("# " + title + "\n\n" + description + "\n")

        # Build the table with top level yaml
        print("| Key | Value | Type | Information |")
        print("| :-: | :-: | :-: | :-- |") 
        for value in yaml:
            if not value.isBase:
                print(value.to_markdown(schema = True))

        print("\n\n")

        for value in yaml:
            if value.isBase:
                print(value.to_markdown(schema = True))

        print(f"---\nGenerated by [yamldoc](https://github.com/chris1221/yaml.doc) v{yamldoc.__version__} on {date.today()}")

    else: 
        print("# " + title + "\n\n" + description + "\n")

        yaml = parse_yaml(yaml_path, char, debug)
        # Build the table with top level yaml
        print("| Key | Value | Information |")
        print("| :-: | :-: | :-- |") 
        for value in yaml:
            if not value.isBase:
                print(value.to_markdown())

        print("\n\n")

        for value in yaml:
            if value.isBase:
                print(value.to_markdown())

        print(f"---\nGenerated by [yamldoc](https://github.com/chris1221/yaml.doc) v{yamldoc.__version__} on {date.today()}")

parse_schema(path_to_file, debug=False)

Parse a schema file to identify key value pairing of values and their associated types.

Parameters:

Name Type Description Default
path_to_file

Path to schema file.

required

Returns: Tuple of (schema, specials) where specials are unique YAMLDOC strings for the title and description of the desired markdown.

Source code in yamldoc/parser.py
def parse_schema(path_to_file, debug = False):
    '''
    Parse a schema file to identify key value pairing of 
    values and their associated types.

    Arguments:
        path_to_file: Path to schema file.

    Returns: Tuple of (schema, specials) where specials are unique YAMLDOC strings for the title and description of the desired markdown. 
    '''
    name = "base"

    indent = [0, 0]

    specials = {}
    indents = {}
    current = {}

    special_type_case = False

    with open(path_to_file) as schema:
        for line in [l for l in schema.readlines() if l.rstrip()]:
            if debug: print(line)
            indent = [indent[1], count_indent(line)]


            # The base level has to start with a special name
            # because it is not named in the schema.
            try:
                key, value = key_value(line)
            except ValueError:
                key = key_value(line)
                value = None

            # This is awkwardly placed but we have to deal
            # with a special case where lines 
            # start with a dash, and indicate that
            # variables have multiple types.
            # 
            # If this has been observed further inside the
            # loop, and the line does indeed start with a dash
            if special_type_case:
                # If the indent has decreased, then 
                # the list of types has finished.
                if indent[1] < indent[0]:
                    special_type_case = False
                    continue
                if line.lstrip(" ").startswith("-"):
                    value = line.lstrip(" ").lstrip("- ").rstrip()
                    current[parent][name].append(value)
                    continue
                else:
                    raise TypeError("You must specify a value for type.")

            # < Deal with top level specials.
            if key == "$schema":
                assert value is not None
                specials["schema"] = value

            if key == "_yamldoc_title": 
                assert value is not None
                specials[key] = value

            if key == "_yamldoc_description": 
                assert value is not None
                specials[key] = value


            if key == "description":
                assert value is not None
                specials["description"] = value
            # />

            # Top level properties option
            if value is None:
                if key == "properties":
                    current[name] = {} 
                    indents[name] = {} 
                    continue


            # Deal with increasing indent levels
            # The actual amount of indent is not
            # relevant (though it may be for 
            # parsing a valid YAML document.
            if indent[1] > indent[0]:
                if value is None:
                    # If the key is properties
                    # then we are starting a new object
                    # definition.
                    if key == "properties":
                        current[name] = {} 
                        indents[name] = {} 
                        continue

                    # This is a special case where
                    # there can be multiple types
                    # given for a particular variable.
                    elif key == "type":
                        current[parent][name] = []
                        indents[parent][name] = []
                        special_type_case = True
                        continue


                    # Otherwise, its the name of the
                    # actual object.
                    # Assign the key to be the "name"
                    # and relegate the previous name
                    # to the "parent".
                    else:
                        parent = name
                        name = key
                        continue

                # If it's giving the type of the object
                # then store that under the parent (meta)
                # object along with its indentation level. 
                if key == "type":
                    assert value is not None
                    if value != "object":
                        current[parent][name] = value
                        indents[parent][name] = indent[1]
                    continue

            if indent[1] < indent[0]:
                # We're just adding another value 
                if indent[1] == indents[parent][name]:
                    if value is None:
                        name = key
                        continue
                elif indent[1] < indents[parent][name]:
                    if value is None:
                        name = key
                        continue

        return current, specials 

parse_yaml(file_path, char="#'", debug=False)

Parse a YAML file and return a list of YAML classes.

Parameters:

Name Type Description Default
file_path

Path to the YAML file.

required
char

A character string used to identify yamldoc blocks.

"#'"
debug

Print debug information

False

Returns:

Type Description

List of YAML blocks.

Source code in yamldoc/parser.py
def parse_yaml(file_path, char = "#'", debug = False):
    '''
    Parse a YAML file and return a list of YAML classes. 

    Arguments:
        file_path: Path to the YAML file.
        char: A character string used to identify yamldoc blocks.
        debug: Print debug information

    Return:
        List of YAML blocks.
    '''
    # YAML files have key value pairings seperated by 
    # newlines. The most straightforward kind of things to parse will be
    # keyvalue pairs preceeded by comments with the Doxygen marker #'

    current_entry = None
    meta = ""
    things = []

    with open(file_path) as yaml:
        for line in [l for l in yaml.readlines() if l.rstrip()]:
            if debug: print(line.rstrip())

            # Either we haven't started yet
            # or we've just flushed the entry.
            if current_entry is None:
                # Find the number of leading spaces.
                # YAML only uses spaces.
                nspaces = len(line) - len(line.lstrip(' '))
                if debug: print("@\tFound " + str(nspaces) + " indent level.")

                if nspaces == 0:
                    current_entry = None
                    if line.startswith(char):
                        meta = meta + line.lstrip(char).rstrip()
                    else:
                        key, value = line.rstrip().split(":")

                        # If there is no value, this is the beginning of a 
                        # base entry (i.e. there are subentries to follow)
                        if not value.lstrip():
                            current_entry = yamldoc.entries.MetaEntry(key, meta)
                            if debug: print("@\tFound a meta entry.")
                            continue

                        # Otherwise continue on.
                        else:
                            things.append(yamldoc.entries.Entry(key, value.lstrip(' '), meta.lstrip()))
                            if debug: print("@\tFound an entry.")
                            meta = ""

            if current_entry is not None:
                if current_entry.isBase:

                    # If we're back at 0 indentation, the
                    # block is done and we need to quit.
                    if len(line) - len(line.lstrip(' ')) == 0:
                        things.append(current_entry)
                        current_entry = None
                        continue

                    # If not, continue parsing the sub entries.
                    if line.lstrip(' ').startswith(char):
                        meta = meta + line.lstrip().lstrip(char).rstrip()
                    else:
                        key, value = line.lstrip().rstrip().split(":")
                        current_entry.entries.append(yamldoc.entries.Entry(key, value.lstrip(' '), meta.lstrip()))
                        if debug: print("@\tFound an entry and deposited it in meta.")
                        meta = ""

        # The file might run out
        # before the final meta
        # entry is added.
        try:
            if current_entry.isBase:
                things.append(current_entry)
        except AttributeError:
            pass



    return things