Document

Document

class Document(text, reader, fname, tokens)

This is the mediator communicating with all other classes to generate rst output. :param string text: the source code to parse. :param reader: An instance of Reader. :param string fname: The file name of the source code. :param tokens: A sequence of tokens usable for the @if directive.

class Document(object):
    #Attributes
    <<Document.errors>>
    <<Document.blocks>>
    <<Document.blocks_included>>
    <<Document.compiled_blocks>>
    <<Document.sub_documents>>
    <<Document.tokens>>
    <<Document.macros>>
    <<Document.fname>>
    <<Document.reader>>
    <<Document.lines>>

    #Methods
    <<Document.__init__>>
    <<Document.process>>
    <<Document.get_subdoc>>
    <<Document.add_error>>
    <<Document.check_errors>>
    <<Document.collect_blocks>>
    <<Document.get_compiled_block>>
    <<Document.compile_block>>
errors

A list of errors found during generation.

errors = []
blocks

A dictionary of all found blocks: Name -> List of Lines

blocks = {}
blocks_included

A set containing all block names that have been included by an @include directive.

blocks_included = set()
compiled_blocks

A set containing all block names that have been already compiled.

compiled_blocks = set()
sub_documents

A cache dictionary of sub documents, referenced by @include directives: Filename -> Document

sub_documents = {}
tokens

A set of token names that can be used for the @if directive.

tokens = set()
macros

A dictionary containing the macros that can be used by the @subst directive: Macro name -> substitution.

macros = {}
fname

The file name of the document’s source.

fname = ""
reader

The instance of a Reader object.

reader = None
lines

A list of Line objects representing the whole documents split in lines.

lines = []
__init__(text, reader, fname, tokens)

The constructor.

def __init__(self, text, reader, fname, tokens):
    self.errors = []
    self.blocks = {}
    self.blocks_included = set()
    self.compiled_blocks = set()
    self.sub_documents = {}
    self.tokens = set(tokens or [])
    self.macros = { "__file__" : os.path.split(fname)[-1],
                    "__codeprefix__" : "" }
    self.fname = fname
    self.reader = reader
    self.lines = self.reader.process(fname, text)
process(show_warnings)

Processes the document and generates the output. :param bool show_warnings: If True warnings are emitted. :return: A string representing the rst output.

def process(self, show_warnings, fname):
    self.collect_blocks()

    # check if there are any lines in the file and add the according error message
    if not self.lines:
        self.add_error(0, "empty file", fname)
        self.check_errors()
    elif "" not in self.blocks:
        self.add_error(0, "no @start() directive found (I need one)")
        self.check_errors()

    try:
        text = self.get_compiled_block("")
    finally:
        self.check_errors()

    if show_warnings:
        <<show warnings>>
    text = self.reader.filter_output(text)
    return_text = None
    if text:
         return_text = "\n".join(map(operator.attrgetter("text"), text))
    return return_text

<<show warnings>>

self.blocks_included.add("")           #may not cause a warning
self.blocks_included.add("__macros__") #may not cause a warning
unincluded = set(self.blocks.keys())-self.blocks_included
if unincluded:
    logger.warning("The following block were not included:")
    warnings = [ (self.blocks[b][0].index, b) for b in unincluded ]
    warnings.sort(key=operator.itemgetter(0))
    for l, w in warnings:
        logger.warning("  %s(line %i)", w, l)
get_subdoc(rpath)

Tries to compile a document with the relative path rpath. :param string rpath: The relative path to the root containing document. :return: A Document reference to the sub document.

def get_subdoc(self, rpath):
    <<return from cache if possible>>
    <<insert macros function>>
    <<read the source file>>

    self.sub_documents[rpath] = doc
    return doc

<<return from cache if possible>>

try:
    return self.sub_documents[rpath]
except KeyError:
    pass

<<insert macros function>>

def insert_macros(subdoc):
    # if sub doc has no macros insert mine
    if ("__macros__" not in subdoc.blocks
        and "__macros__" in self.blocks):
        file_ = subdoc.macros["__file__"] # preserve __file__
        subdoc.macros.update(self.macros)
        subdoc.macros["__file__"] = file_

<<read the source file>>

head, tail = os.path.split(self.fname)
fpath = os.path.join(head, rpath)

try:
    #print "try open", fpath
    with open(fpath, "r") as f:
        text = f.read()
except IOError:
    doc = None
    logger.error("Could not open: %s", fpath)

else:
    #parse the file
    lexer = pm.get_lexer_for_filename(rpath)
    single_comment_markers,  block_comment_markers = get_comment_markers(lexer.name)
    reader = readers.get(lexer.name, Reader)(lexer, single_comment_markers,  block_comment_markers)

    doc = Document(text, reader, rpath, self.tokens)
    doc.collect_blocks()
    insert_macros(doc)
add_error(line, text)

Adds an error to the list. :param integer line_number: The line number that causes the error. :param string text: An error text. :param string fname: name of currently processed file (needed if no data in self.lines)

def add_error(self, line_number, text, fname=""):
    # determine if access to line_number is possible
    if line_number < len(self.lines):
        line = self.lines[line_number]
    else:
        # without a line in self.lines, antiweb would crash on appending it to the errors (errorlist)
        # we add a 'fake line' to prevent that issue
        line = Line(fname, -1, "")

    self.errors.append((line, text))
check_errors()

Raises a WebError exception if error were found.

def check_errors(self):
    if self.errors:
        raise WebError(self.errors)
collect_blocks()

Collects all text blocks.

def collect_blocks(self):
    blocks = [ d.collect_block(self, i)
               for i, l in enumerate(self.lines)
               for d in l.directives ]

    self.blocks = dict(list(filter(bool, blocks)))

    if "__macros__" in self.blocks:
        self.get_compiled_block("__macros__")
get_compiled_block(name)

Returns the compiled version of a text block. Compiled means: all directives where processed. :param string name: The name of the text block: :return: A list of Line objects representing the text block.

def get_compiled_block(self, name):
    if name not in self.blocks:
        return None

    if name in self.compiled_blocks:
        return self.blocks[name]

    return self.compile_block(name, self.blocks[name])
compile_block(name, block)

Compiles a text block. :param string name: The name of the block :param block: A list of Line objects representing the text block to compile. :return: A list of Line objects representing the compiled text block.

def compile_block(self, name, block):
    <<find_next_directive>>

    while True:
        directive_index = find_next_directive(block)
        if not directive_index: break
        directive, index = directive_index
        directive.process(self, block, index)

    self.compiled_blocks.add(name)
    return block

<<find_next_directive>>

def find_next_directive(block):
    # returns the next available directive
    min_line = [ (l.directives[0].priority, i)
                 for i, l in enumerate(block) if l.directives ]
    if not min_line:
        return None

    prio, index = min(min_line)
    return block[index].directives.pop(0), index

Line

class Line(fname, index, text[, directives[, type]])

This class represents a text line.

class Line(object):
    #Attributes
    <<Line._directives>>
    <<Line.fname>>
    <<Line.index>>
    <<Line.text>>
    <<Line.type>>

    #Methods
    <<Line.__init__>>
    <<Line.set>>
    <<Line.like>>
    <<Line.indented>>
    <<Line.change_indent>>
    <<Line.__len__>>
    <<Line.__repr__>>

    #Properties
    <<Line.indent>>
    <<Line.sindent>>
    <<Line.directives>>
    <<Line.directive>>
_directives

A list of Directive objects, sorted by their priority.

_directives = ()
fname

A string of the source’s file name the line belongs to.

fname = ""
index

The integer line index of the directive within the current block.

index = 0
text

A string containing the source line.

text = ""
type

A char representing the line type:

  • d stands for a document line
  • c stands for a code line
type = "d"
indent

An integer representing the line’s indentation.

@property
def indent(self):
    return len(self.text)-len(self.text.lstrip())
sindent

A string representation of the line’s indentation.

@property
def sindent(self):
    return " "*self.indent
directives

A sorted sequence of Directive objects.

@property
def directives(self):
    return self._directives


@directives.setter
def directives(self, value):
    self._directives = value[:]
    if self._directives:
        self._directives.sort(key=operator.attrgetter("priority"))
directive

The first of the contained Directive objects.

@property
def directive(self):
    return self.directives and self.directives[0]
__init__(name, index, text[, directives[, type]])

The constructor.

def __init__(self, fname, index, text, directives=(), type='d'):
    self.fname = fname
    self.index = index
    self.text = text
    self.directives = directives
    self.type = type
set([index=None[, type=None[, directives=None]]])

Changes the attributes index, type and directives at once.

Parameters:
  • index (integer) – the line index.
  • type (char) – Either 'd' or 'c'.
  • directives (list) – A list of DCirective objects.
Returns:

The Line object self.

def set(self, index=None, type=None, directives=None):
    if index is not None:
        self.index = index

    if type is not None:
        self.type = type

    if directives is not None:
        self.directives = directives

    return self



def clone(self, dline=None):

    if dline is not None:
        for d in self.directives:
            d.line = dline

    return Line(self.fname, self.index, self.text,
                self.directives[:], self.type)
like(text)

Clones the Line with a different text.

def like(self, text):
    return Line(self.fname, self.index, self.indented(text))
indented(text)

Returns the text, with the same indentation as self.

def indented(self, text):
    return self.sindent + text
change_indent(delta)

Changes the lines indentation.

def change_indent(self, delta):
    if delta < 0:
        delta = min(-delta, self.indent)
        self.text = self.text[delta:]

    elif delta > 0:
        self.text = " "*delta + self.text

    return self
__len__()

returns the length of the stripped text.

def __len__(self):
    return len(self.text.strip())
__repr__()

returns a textual representation of the line.

def __repr__(self):
    return "Line(%i, %s, %s)" % (self.index, self.text, str(self.directives))