Best Python code snippet using pytest-play_python
dox_parser.py
Source:dox_parser.py  
1#!/usr/bin/env python2"""Parser for SeqAn Doxygen dialect.3The Doxygen documentation has a regular grammar and thus can be parsed quite4easily.  We do not use a parser generator such as PLY since it is a bit5overkill (it can generate parser for some context-free grammars) and not6as straightforward to use as writing a simple parser by hand.7"""8import itertools9import operator10import os.path11import re12import sys13import termcolor14import raw_doc15import lexer16import dox_tokens17class MessagePrinter(object):18    """Allows to pretty print warning and error messages.19    @ivar ignore_dirs: The directories to ignore warnings for.20    @ivar counts: Dict mapping 'error' and 'warning' to counts.21    """22    def __init__(self, ignore_dirs=[]):23        self.ignore_dirs = [os.path.realpath(x) for x in ignore_dirs]24        self.counts = {'error': 0, 'warning': 0}25    def isIgnored(self, path):26        """Return whether path is below one of the ignored directories."""27        real_path = os.path.realpath(path)28        return any([os.path.commonprefix([real_path, x]) == x for x in self.ignore_dirs])29    def printTokenError(self, token, msg, level='error'):30        """Print user-friendly error at location token."""31        if self.isIgnored(token.file_name):32            return  # Is ignored.33        # Print location and error message.34        location = (token.file_name, token.lineno + 1, token.column)35        if sys.stderr.isatty():36            location_str = termcolor.colored('%s:%d:%d:' % location, 'white', attrs=['bold'])37            error_str = termcolor.colored('%s:' % level, 'red', attrs=['bold'])38            msg = termcolor.colored(msg, 'white', attrs=['bold'])39        else:40            location_str = '%s:%d:%d:' % location41            error_str = '%s:' % level42        print >>sys.stderr, '%s %s %s' % (location_str, error_str, msg)43        # Increase error counter.44        self.counts[level] += 145        if token.file_name == '<mem>':46            return  # do not attempt to access line below47        # Load line with error and print it with an indicator of the error.48        fcontents = open(token.file_name).read()49        lines = fcontents.splitlines()50        if token.lineno >= len(lines):51            return  # Invalid line number.52        print >>sys.stderr, '%s' % lines[token.lineno].rstrip()53        if sys.stderr.isatty():54            print >>sys.stderr, token.column * ' ' + termcolor.colored('^', 'green', attrs=['bold'])55        else:56            print >>sys.stderr, token.column * ' ' + '^'57    def printParserError(self, e):58        """Print user-friendly error message for ParserError e."""59        msg = e.msg60        if not e.msg:61            msg = 'Parse error'62        if e.token:63            self.printTokenError(e.token, e.msg)64        else:65            self.counts['error'] += 166            print >>sys.stderr, 'ERROR: %s' % msg67    def printStats(self):68        print >>sys.stderr, 'Issued %d warnings and %d errors.' % (self.counts['error'], self.counts['warning'])69    def numWarnings(self):70        return self.counts['warning']71    def numErrors(self):72        return self.counts['error']73class ParserError(Exception):74    """Raised when there is a parser error."""75    76    def __init__(self, token=None, msg=''):77        if msg and token:78            args = (token.file_name, token.lineno, token.column, repr(token.val), msg)79            message = ('Parse error at %s:%d (column %d) '80                       'at token "%s": %s)' % args)81        elif not msg and token:82            args = (token.lineno, token.column, repr(token.val))83            message = 'Parser error at %d:%d ("%s").' % args84        else:85            message = 'Parse error: %s' % (msg,)86        Exception.__init__(self, message)87        self.msg = msg88        self.token = token89def stripWhitespaceTokens(token_list, strip_lt_breaks=False):90    """Strip leading and trailing whitespace tokens from token_list."""91    types = ['SPACE']92    if strip_lt_breaks:93        types.append('BREAK')94    while token_list and token_list[0].type in types:95        token_list.pop(0)96    while token_list and token_list[-1].type in types:97        token_list.pop()98def normalizeWhitespaceTokens(token_list, strip_lt_breaks=False):99    """Normalize whitespace by replacing multiple consecutive spaces by one.100    """101    positions = [i for i, t in enumerate(token_list) if t.type == 'SPACE']102    for i in reversed(positions):103        token_list[i].val = ' '104    stripWhitespaceTokens(token_list, strip_lt_breaks)105class GenericSimpleClauseState(object):106    """Handler used in *DocState for handling simple text clauses clauses.107    """108    109    def __init__(self, parser, parent):110        self.parser = parser111        self.parent = parent112        self.tokens = []113        # The first token, usually starting the clause at all, set outside.114        self.first_token = None115        self.entry_class = None116        # Whether or not to strip leading and trailing breaks.117        self.strip_lt_breaks = False118        # Whether to normalize whitespace tokens in getEntry().119        self.normalize_tokens = True120    def entered(self, token):121        self.first_token = token122    def left(self):123        pass124    def getEntry(self):125        """Returns the Entry for the brief clause."""126        if self.normalize_tokens:127            normalizeWhitespaceTokens(self.tokens, self.strip_lt_breaks)128        return self.entry_class(self.first_token, raw_doc.RawText(self.tokens))129    def handle(self, token):130        # One or more empty lines end such a clause as well as another131        # clause-starting command.132        if token.type in ['EMPTYLINE', 'EOF']:133            self.parent.endClause()134        elif token.type in dox_tokens.CLAUSE_STARTING or \135             token.type in dox_tokens.ITEM_STARTING:136            self.parent.endClause(token)137        elif token.type == 'SPACE' and (not self.tokens or self.tokens[-1].type == 'BREAK'):138            return  # Skip space at beginning or after break139        elif token.type == 'BREAK' and (self.tokens and self.tokens[-1].type == 'SPACE'):140            self.tokens[-1] = token  # Replace space before break141        else:142            #print 'APPEND %s' % repr(token.val)143            self.tokens.append(token)144class ParagraphState(GenericSimpleClauseState):145    """Handler used in *DocState for handling tokens of a paragraph."""146    147    def __init__(self, parser, parent):148        GenericSimpleClauseState.__init__(self, parser, parent)149        self.entry_class = raw_doc.RawParagraph150class SignatureState(GenericSimpleClauseState):151    """Handler used in *DocState for handling tokens of a paragraph."""152    153    def __init__(self, parser, parent):154        GenericSimpleClauseState.__init__(self, parser, parent)155        self.entry_class = raw_doc.RawSignature156class CodeState(GenericSimpleClauseState):157    """Handler used in *DocState for handling tokens of a paragraph."""158    159    def __init__(self, parser, parent):160        GenericSimpleClauseState.__init__(self, parser, parent)161        self.entry_class = raw_doc.RawCode162        self.normalize_tokens = False163    def handle(self, token):164        # Code is only ended by @endcode.165        if token.type == 'COMMAND_ENDCODE':166            self.parent.endClause()167        else:168            self.tokens.append(token)169class HtmlOnlyState(GenericSimpleClauseState):170    """Handler used in *DocState for handling tokens of a paragraph."""171    172    def __init__(self, parser, parent):173        GenericSimpleClauseState.__init__(self, parser, parent)174        self.entry_class = raw_doc.RawHtmlOnly175        self.normalize_tokens = False176    def handle(self, token):177        # HTML-only section is only ended by @endhtmlonly.178        if token.type == 'COMMAND_ENDHTMLONLY':179            self.parent.endClause()180        else:181            self.tokens.append(token)182class DeprecatedState(GenericSimpleClauseState):183    """Handler for the @deprecated clause."""184    def __init__(self, parser, parent):185        GenericSimpleClauseState.__init__(self, parser, parent)186        self.entry_class = raw_doc.RawDeprecated187class NoteState(GenericSimpleClauseState):188    """Handler for the @note clause."""189    def __init__(self, parser, parent):190        GenericSimpleClauseState.__init__(self, parser, parent)191        self.entry_class = raw_doc.RawNote192class WarningState(GenericSimpleClauseState):193    """Handler for the @warning clause."""194    def __init__(self, parser, parent):195        GenericSimpleClauseState.__init__(self, parser, parent)196        self.entry_class = raw_doc.RawWarning197class AkaState(GenericSimpleClauseState):198    """Handler for the @aka clause."""199    def __init__(self, parser, parent):200        GenericSimpleClauseState.__init__(self, parser, parent)201        self.entry_class = raw_doc.RawAka202class InternalState(GenericSimpleClauseState):203    """Handler for the @internal clause."""204    def __init__(self, parser, parent):205        GenericSimpleClauseState.__init__(self, parser, parent)206        self.entry_class = raw_doc.RawInternal207class HeaderfileState(GenericSimpleClauseState):208    """Handler for the @headerfile clause."""209    def __init__(self, parser, parent):210        GenericSimpleClauseState.__init__(self, parser, parent)211        self.entry_class = raw_doc.RawHeaderfile212class ImplementsState(GenericSimpleClauseState):213    """Handler for the @implements clause."""214    def __init__(self, parser, parent):215        GenericSimpleClauseState.__init__(self, parser, parent)216        self.entry_class = raw_doc.RawImplements217class ExtendsState(GenericSimpleClauseState):218    """Handler for the @extends clause."""219    def __init__(self, parser, parent):220        GenericSimpleClauseState.__init__(self, parser, parent)221        self.entry_class = raw_doc.RawExtends222        self.strip_lt_breaks = True223class BriefState(GenericSimpleClauseState):224    """Handler for the @brief clause."""225    def __init__(self, parser, parent):226        GenericSimpleClauseState.__init__(self, parser, parent)227        self.entry_class = raw_doc.RawBrief228class SeeState(GenericSimpleClauseState):229    """Handler for the @see clause."""230    def __init__(self, parser, parent):231        GenericSimpleClauseState.__init__(self, parser, parent)232        self.entry_class = raw_doc.RawSee233class ParamState(GenericSimpleClauseState):234    """Handler used in *DocState for handling @param clauses.235    """236    237    def __init__(self, parser, parent):238        GenericSimpleClauseState.__init__(self, parser, parent)239        self.entry_class = raw_doc.RawParam240        # Parameters need more than one token list.241        self.in_out = None242        self.name = []243    def handle(self, token):244        # Special handling of the in/out token and the name, if any.245        #print '.... TOKEN', token, token.type == 'PARAM_IN_OUT'246        if token.type == 'PARAM_IN_OUT':247            self.in_out = token248        elif not self.name:249            if token.type != 'SPACE':250                self.name.append(token)251            # Skipping whitespace.252        else:253            GenericSimpleClauseState.handle(self, token)254    def getEntry(self):255        """Returns the Entry for the parameter."""256        normalizeWhitespaceTokens(self.tokens)257        return self.entry_class(self.first_token, raw_doc.RawText(self.name),258                                raw_doc.RawText(self.tokens), self.in_out)259class TParamState(ParamState):260    """Handler used in *DocState for handling @tparam clauses.261    """262    263    def __init__(self, parser, parent):264        ParamState.__init__(self, parser, parent)265        self.entry_class = raw_doc.RawTParam266class ReturnState(ParamState):267    """Handler used in *DocState for handling @return clauses.268    269    Stores return type in self.name (member variable inherited from270    ParamState).  Sorry for any confusion.  The flag self.type_read271    is used for storing whether the type has been read.272    """273    274    def __init__(self, parser, parent):275        ParamState.__init__(self, parser, parent)276        self.entry_class = raw_doc.RawReturn277        self.type_read = False278    def handle(self, token):279        if self.type_read:280            GenericSimpleClauseState.handle(self, token)281        else:282            if self.name and token.type in dox_tokens.WHITESPACE:283                self.type_read = True284            elif self.name or token.type not in dox_tokens.WHITESPACE:285                self.name.append(token)286class ThrowState(ParamState):287    """Handler used in *DocState for handling @throw clauses.288    289    Stores throw type in self.name (member variable inherited from290    ParamState).  Sorry for any confusion.  The flag self.type_read291    is used for storing whether the type has been read.292    """293    294    def __init__(self, parser, parent):295        ParamState.__init__(self, parser, parent)296        self.entry_class = raw_doc.RawThrow297        self.type_read = False298    def handle(self, token):299        if self.type_read:300            GenericSimpleClauseState.handle(self, token)301        else:302            if self.name and token.type in dox_tokens.WHITESPACE:303                self.type_read = True304            elif self.name or token.type not in dox_tokens.WHITESPACE:305                self.name.append(token)306class DataRaceState(GenericSimpleClauseState):307    """Handler used in *DocState for handling @datarace clauses.308        309       Inherits from GenericSimpleClauseState.310    """311    def __init__(self, parser, parent):312        GenericSimpleClauseState.__init__(self, parser, parent)313        self.entry_class = raw_doc.RawDataRace314        self.type_read = False315class SectionState(object):316    """Handler used in *DocState for handling @section clauses.317    """318    319    def __init__(self, parser, parent):320        self.first_token = None321        self.parser = parser322        self.parent = parent323        self.tokens = []324        self.level = 0325    def getEntry(self):326        """Returns the Entry for the template parameter."""327        return raw_doc.RawSection(self.first_token, raw_doc.RawText(self.tokens), self.level)328    def entered(self, token):329        self.first_token = token330    def left(self):331        pass332    def handle(self, token):333        # One or more empty lines end a @section raw_doc.Raw334        if token.type in ['EMPTYLINE', 'EOF']:335            self.parent.endClause()336        elif token.type in dox_tokens.CLAUSE_STARTING:337            self.parent.endClause(token)338        else:339            self.tokens.append(token)340class SubsectionState(SectionState):341    """Handler used in *DocState for handling @subsection clauses.342    """343    344    def __init__(self, parser, parent):345        SectionState.__init__(self, parser, parent)346        self.level = 1347class IncludeState(object):348    """Handler used in *DocState for handling @include clauses.349    """350    351    def __init__(self, parser, parent):352        self.first_token = None353        self.parser = parser354        self.parent = parent355        self.tokens = []356    def getEntry(self):357        return raw_doc.RawInclude(self.first_token, self.tokens)358    def entered(self, token):359        self.first_token = token360    def left(self):361        pass362    def handle(self, token):363        if token.type in dox_tokens.LINE_BREAKS or token.type == 'EOF':364            self.parent.endClause()365        elif token.type in dox_tokens.CLAUSE_STARTING:366            self.parent.endClause(token)367        else:368            if token.type != 'SPACE' or self.tokens:369                self.tokens.append(token)370class SnippetState(object):371    """Handler used in *DocState for handling @snippet clauses.372    """373    374    def __init__(self, parser, parent):375        self.first_token = None376        self.parser = parser377        self.parent = parent378        self.path_done = False  # True after first space after path379        self.path_tokens = []380        self.name_tokens = []381    def getEntry(self):382        return raw_doc.RawSnippet(self.first_token, self.path_tokens, self.name_tokens)383    def entered(self, token):384        self.first_token = token385    def left(self):386        pass387    def handle(self, token):388        if token.type in dox_tokens.LINE_BREAKS or token.type == 'EOF':389            self.parent.endClause()390        elif token.type in dox_tokens.CLAUSE_STARTING:391            self.parent.endClause(token)392        else:393            if token.type == 'SPACE':394                if not self.path_tokens:395                    return  # Ignore beginning space.396                elif self.path_tokens and not self.name_tokens:397                    self.path_done = True398                else:  # append to name399                    self.name_tokens.append(token)400            else:401                if self.path_done:402                    self.name_tokens.append(token)403                else:404                    self.path_tokens.append(token)405class TopLevelState(object):406    """Top level state, expecting a command starting a documentation item.407    408    Whitespace is skipped.409    """410    def __init__(self, parser):411        self.parser = parser412    def handle(self, token):413        # We ignore whitespace.414        if token.type in dox_tokens.WHITESPACE:415            return416        # We expect an item-starting token.417        if token.type in dox_tokens.ITEM_STARTING:418            state_map = {'COMMAND_CLASS' :       'class',419                         'COMMAND_CONCEPT' :     'concept',420                         'COMMAND_FUNCTION' :    'function',421                         'COMMAND_MACRO':        'macro',422                         'COMMAND_METAFUNCTION': 'metafunction',423                         'COMMAND_PAGE':         'page',424                         'COMMAND_MAINPAGE':     'mainpage',425                         'COMMAND_DEFGROUP':     'group',426                         'COMMAND_VARIABLE':     'var',427                         'COMMAND_VALUE':        'val',428                         'COMMAND_TAG':          'tag',429                         'COMMAND_TYPEDEF':      'typedef',430                         'COMMAND_ADAPTION':     'adaption',431                         'COMMAND_ENUM':         'enum',}432            self.parser.enterState(state_map[token.type], token)433            return434        msg = 'Expecting one of {%s.} but is %s' % (", ".join(dox_tokens.ITEM_STARTING), token.type)435        raise ParserError(token, msg)436class GenericDocState(object):437    """Generic token handler for top-level entries.438    """439    def __init__(self, parser, entry_class, state_name):440        self.parser = parser441        # Substate is one of "first_line" or "body".442        self.substate = 'first_line'443        self.state_name = state_name444        self.first_line_tokens = None445        self.entry_class = entry_class446        self.clause_state = None447        self.allowed_commands = dox_tokens.CLAUSE_STARTING448    def getEntry(self):449        return self.entry450    def entered(self, first_token):451        """Called when the state is entered successfully with @class etc.452        """453        #print >>sys.stderr, ">>>>ENTERING CLASS STATE"454        self.first_token = first_token455        self.first_line_tokens = []456        self.substate = 'first_line'457        self.entry = self.entry_class(first_token)458        # Variables for name/title separation.459        self.name_read = False460        self.name_tokens = []461        self.title_tokens = []462    def left(self):463        pass464    def handle(self, token):465        #print 'state = class, substate = %s, clause_state %s' % (self.substate, self.clause_state)466        if self.substate == 'first_line':467            # If we have a line break in the first line then we go to the body468            # of the class documentation.469            if token.type in dox_tokens.LINE_BREAKS or token.type == 'EOF':470                #print >>sys.stderr, [v.val for v in self.name_tokens], [v.val for v in self.type_tokens]471                normalizeWhitespaceTokens(self.name_tokens)472                normalizeWhitespaceTokens(self.title_tokens)473                self.entry.name = raw_doc.RawText(self.name_tokens)474                self.entry.title = raw_doc.RawText(self.title_tokens)475                self.substate = 'body'476                return477            # Skip space at the beginning of the type.478            if not self.name_tokens and token.type == 'SPACE':479                return480            # Otherwise, we collect the token's value.481            if self.name_read:482                #print >>sys.stderr, 'NAME', token.type, repr(token.val)483                self.title_tokens.append(token)484            else:485                if token.type == 'SPACE':486                    self.name_read = True487                else:488                    #print >>sys.stderr, 'TYPE', token.type, repr(token.val)489                    self.name_tokens.append(token)490        elif self.substate == 'body':491            # If we are already in a clause then continue to use the state.492            if not self.clause_state is None:493                self.clause_state.handle(token)494                return495            # In the body, we look for a item-generating token and will enter496            # the corresponding state if we hit it.497            if token.type in dox_tokens.ITEM_STARTING:498                # Leave the top-level class state and handle the token with the499                # top-level handler.  This will create the appropriate state.500                self.parser.leaveState(self.state_name)501                assert self.parser.states[0] == 'top'502                self.parser.handleToken(token)503                return504            # In a class documentation body, a clause-starting command triggers505            # parsing of that clause.506            if token.type in dox_tokens.CLAUSE_STARTING:507                state_map = {'COMMAND_SIGNATURE' : SignatureState(self.parser, self),508                             'COMMAND_CODE' : CodeState(self.parser, self),509                             'COMMAND_HTMLONLY' : HtmlOnlyState(self.parser, self),510                             'COMMAND_BRIEF' : BriefState(self.parser, self),511                             'COMMAND_EXTENDS' : ExtendsState(self.parser, self),512                             'COMMAND_HEADERFILE' : HeaderfileState(self.parser, self),513                             'COMMAND_DEPRECATED' : DeprecatedState(self.parser, self),514                             'COMMAND_NOTE' : NoteState(self.parser, self),515                             'COMMAND_WARNING' : WarningState(self.parser, self),516                             'COMMAND_AKA' : AkaState(self.parser, self),517                             'COMMAND_INTERNAL' : InternalState(self.parser, self),518                             'COMMAND_IMPLEMENTS' : ImplementsState(self.parser, self),519                             'COMMAND_SEE' : SeeState(self.parser, self),520                             'COMMAND_RETURN' : ReturnState(self.parser, self),521                             'COMMAND_THROW' : ThrowState(self.parser, self),522                             'COMMAND_DATARACE' : DataRaceState(self.parser, self),523                             'COMMAND_PARAM' : ParamState(self.parser, self),524                             'COMMAND_TPARAM' : TParamState(self.parser, self),525                             'COMMAND_SECTION' : SectionState(self.parser, self),526                             'COMMAND_SNIPPET' : SnippetState(self.parser, self),527                             'COMMAND_SUBSECTION' : SubsectionState(self.parser, self),528                             'COMMAND_INCLUDE' : IncludeState(self.parser, self),}529                if not token.type in self.allowed_commands:530                    msg = 'Invalid command %s, expecting one of %s.'531                    args = (repr(token.val), map(dox_tokens.transToken, self.allowed_commands))532                    raise ParserError(token, msg % args)533                if self.clause_state:534                    self.clause_state.left()535                self.clause_state = state_map[token.type]536                self.clause_state.entered(token)537                #print '>>> SWITCHING TO CLAUSE STATE %s' % self.clause_state538                return539            # Some commands are explicitely marked as non-paragraph, such as540            # the @endcode and @endhtmlonly token.  These are invalid tokens.541            if token.type in dox_tokens.NON_PARAGRAPH:542                raise ParserError(token, 'Invalid command!')543            # Any other token is an inline-token and part of a paragraph that544            # we will start.  The same is true for any word.545            self.clause_state = ParagraphState(self.parser, self)546            self.clause_state.handle(token)547        else:548            raise ParserError(msg='Invalid substate in @class!')549    def endClause(self, token=None):550        """Ends the current clause and appends its result to the documentation."""551        #print '>>> END CLAUSE(%s)' % token552        if self.clause_state.getEntry():553            entry = self.clause_state.getEntry()554            if entry.getType() in ['paragraph', 'section', 'include', 'code', 'htmlonly', 'snippet']:555                self.entry.addParagraph(entry)556            elif entry.getType() == 'signature':557                self.entry.addSignature(entry)558            elif entry.getType() == 'tparam':559                self.entry.addTParam(entry)560            elif entry.getType() == 'brief':561                self.entry.addBrief(entry)562            elif entry.getType() == 'param':563                self.entry.addParam(entry)564            elif entry.getType() == 'see':565                self.entry.addSee(entry)566            elif entry.getType() == 'return':567                self.entry.addReturn(entry)568            elif entry.getType() == 'throw':569                self.entry.addThrow(entry)570            elif entry.getType() == 'datarace':571                self.entry.addDataRace(entry)572            elif entry.getType() == 'extends':573                self.entry.addExtends(entry)574            elif entry.getType() == 'implements':575                self.entry.addImplements(entry)576            elif entry.getType() == 'headerfile':577                self.entry.addHeaderfile(entry)578            elif entry.getType() == 'deprecated':579                self.entry.addDeprecationMsg(entry)580            elif entry.getType() == 'note':581                self.entry.addNote(entry)582            elif entry.getType() == 'warning':583                self.entry.addWarning(entry)584            elif entry.getType() == 'aka':585                self.entry.addAka(entry)586            elif entry.getType() == 'internal':587                self.entry.addInternal(entry)588            else:589                assert False, '%s' % entry590        self.clause_state = None591        if token:592            self.handle(token)593class ClassDocState(GenericDocState):594    """State for documentation of a class."""595    596    def __init__(self, parser):597        GenericDocState.__init__(self, parser, raw_doc.RawClass, 'class')598        self.allowed_commands = set(['COMMAND_SIGNATURE', 'COMMAND_CODE', 'COMMAND_HTMLONLY',599                                     'COMMAND_SEE', 'COMMAND_BRIEF', 'COMMAND_TPARAM',600                                     'COMMAND_SECTION', 'COMMAND_SUBSECTION',601                                     'COMMAND_INCLUDE', 'COMMAND_SNIPPET', 'COMMAND_EXTENDS',602                                     'COMMAND_IMPLEMENTS', 'COMMAND_HEADERFILE',603                                     'COMMAND_DEPRECATED', 'COMMAND_NOTE', 'COMMAND_WARNING',604                                     'COMMAND_AKA', 'COMMAND_INTERNAL'])605class FunctionDocState(GenericDocState):606    """State for documentation of a function."""607    608    def __init__(self, parser):609        GenericDocState.__init__(self, parser, raw_doc.RawFunction, 'function')610        self.allowed_commands = set(['COMMAND_SIGNATURE', 'COMMAND_CODE', 'COMMAND_HTMLONLY',611                                     'COMMAND_SEE', 'COMMAND_BRIEF', 'COMMAND_TPARAM',612                                     'COMMAND_PARAM',613                                     'COMMAND_SECTION', 'COMMAND_SUBSECTION',614                                     'COMMAND_INCLUDE', 'COMMAND_SNIPPET', 'COMMAND_RETURN',615                                     'COMMAND_THROW', 'COMMAND_HEADERFILE', 'COMMAND_DEPRECATED', 616                                     'COMMAND_NOTE', 'COMMAND_WARNING', 'COMMAND_AKA', 617                                     'COMMAND_INTERNAL', 'COMMAND_DATARACE'])618class MacroDocState(GenericDocState):619    """State for documentation of a macro."""620    621    def __init__(self, parser):622        GenericDocState.__init__(self, parser, raw_doc.RawMacro, 'macro')623        self.allowed_commands = set(['COMMAND_SIGNATURE', 'COMMAND_CODE', 'COMMAND_HTMLONLY',624                                     'COMMAND_SEE', 'COMMAND_BRIEF', 'COMMAND_PARAM',625                                     'COMMAND_SECTION', 'COMMAND_SUBSECTION',626                                     'COMMAND_INCLUDE', 'COMMAND_SNIPPET', 'COMMAND_RETURN',627                                     'COMMAND_THROW', 'COMMAND_HEADERFILE', 'COMMAND_DEPRECATED',628                                     'COMMAND_NOTE', 'COMMAND_WARNING', 'COMMAND_AKA',629                                     'COMMAND_INTERNAL', 'COMMAND_DATARACE'])630class MetafunctionDocState(GenericDocState):631    """State for documentation of a metafunction."""632    633    def __init__(self, parser):634        GenericDocState.__init__(self, parser, raw_doc.RawMetafunction, 'metafunction')635        self.allowed_commands = set(['COMMAND_SIGNATURE', 'COMMAND_CODE', 'COMMAND_HTMLONLY',636                                     'COMMAND_SEE', 'COMMAND_BRIEF', 'COMMAND_TPARAM',637                                     'COMMAND_SECTION', 'COMMAND_SUBSECTION',638                                     'COMMAND_INCLUDE', 'COMMAND_SNIPPET', 'COMMAND_RETURN',639                                     'COMMAND_HEADERFILE', 'COMMAND_DEPRECATED', 'COMMAND_NOTE', 'COMMAND_WARNING',640                                     'COMMAND_AKA', 'COMMAND_INTERNAL'])641class ConceptDocState(GenericDocState):642    """State for documentation of a concept."""643    644    def __init__(self, parser):645        GenericDocState.__init__(self, parser, raw_doc.RawConcept, 'concept')646        self.allowed_commands = set(['COMMAND_SIGNATURE', 'COMMAND_CODE', 'COMMAND_HTMLONLY',647                                     'COMMAND_SEE', 'COMMAND_BRIEF',648                                     'COMMAND_SECTION', 'COMMAND_SUBSECTION',649                                     'COMMAND_INCLUDE', 'COMMAND_SNIPPET', 'COMMAND_EXTENDS',650                                     'COMMAND_HEADERFILE', 'COMMAND_DEPRECATED', 'COMMAND_NOTE', 'COMMAND_WARNING',651                                     'COMMAND_AKA', 'COMMAND_INTERNAL'])652class PageState(GenericDocState):653    """State for a documentation page."""654        655    def __init__(self, parser):656        GenericDocState.__init__(self, parser, raw_doc.RawPage, 'page')657        self.allowed_commands = set(['COMMAND_CODE', 'COMMAND_HTMLONLY',658                                     'COMMAND_SEE', 'COMMAND_BRIEF',659                                     'COMMAND_SECTION', 'COMMAND_SUBSECTION',660                                     'COMMAND_INCLUDE', 'COMMAND_SNIPPET'])661class MainPageState(GenericDocState):662    """State for a documentation main page."""663        664    def __init__(self, parser):665        GenericDocState.__init__(self, parser, raw_doc.RawMainPage, 'mainpage')666        self.allowed_commands = set(['COMMAND_CODE', 'COMMAND_HTMLONLY',667                                     'COMMAND_SEE', 'COMMAND_BRIEF',668                                     'COMMAND_SECTION', 'COMMAND_SUBSECTION',669                                     'COMMAND_INCLUDE', 'COMMAND_SNIPPET'])670    def entered(self, first_token):671        GenericDocState.entered(self, first_token)672        self.name_read = True673        self.name_tokens = [lexer.Token('IDENTIFIER', 'mainpage', 0, 0, 0)]674class GroupState(GenericDocState):675    def __init__(self, parser):676        GenericDocState.__init__(self, parser, raw_doc.RawGroup, 'group')677        self.allowed_commands = set(['COMMAND_CODE', 'COMMAND_HTMLONLY',678                                     'COMMAND_SEE', 'COMMAND_BRIEF',679                                     'COMMAND_SECTION', 'COMMAND_SUBSECTION',680                                     'COMMAND_INCLUDE', 'COMMAND_SNIPPET', 'COMMAND_AKA'])681class VariableState(GenericDocState):682    """State for a variable."""683        684    def __init__(self, parser, entry_class=raw_doc.RawVariable, state_name='var'):685        GenericDocState.__init__(self, parser, entry_class, state_name)686        self.allowed_commands = set(['COMMAND_SIGNATURE', 'COMMAND_CODE', 'COMMAND_HTMLONLY',687                                     'COMMAND_SEE', 'COMMAND_BRIEF',688                                     'COMMAND_SECTION', 'COMMAND_SUBSECTION',689                                     'COMMAND_INCLUDE', 'COMMAND_SNIPPET',690                                     'COMMAND_HEADERFILE', 'COMMAND_DEPRECATED', 'COMMAND_NOTE', 'COMMAND_WARNING',691                                     'COMMAND_AKA', 'COMMAND_INTERNAL'])692    def entered(self, first_token):693        GenericDocState.entered(self, first_token)694        self.type_read = False695        self.type_tokens = []696        self.name_tokens = []697    def left(self):698        if not self.type_tokens or not self.name_tokens:699            msg = ('Missing variable type or name! Must be given as "@var '700                   '<type> <name>".')701            raise ParserError(self.first_token, msg)702    def handle(self, token):703        # Handle first state here and the remaining in the parent class.704        #print >>sys.stderr, token.type, repr(token.val), self.type_read705        if self.substate == 'first_line':706            # If we have a line break in the first line then we go to the body707            # of the class documentation.708            if token.type in dox_tokens.LINE_BREAKS or token.type == 'EOF':709                #print >>sys.stderr, [v.val for v in self.name_tokens], [v.val for v in self.type_tokens]710                normalizeWhitespaceTokens(self.name_tokens)711                normalizeWhitespaceTokens(self.type_tokens)712                self.entry.name = raw_doc.RawText(self.name_tokens)713                if self.entry.name.tokens[-1].val.endswith(';'):  # remove semicolon714                    self.entry.name.tokens[-1].val = self.entry.name.tokens[-1].val[:-1]715                self.entry.type = raw_doc.RawText(self.type_tokens)716                self.substate = 'body'717                return718            # Skip space at the beginning of the type.719            if not self.type_tokens and token.type == 'SPACE':720                return721            # Otherwise, we collect the token's value.722            if self.type_read:723                #print >>sys.stderr, 'NAME', token.type, repr(token.val)724                self.name_tokens.append(token)725            else:726                if token.type == 'SPACE':727                    self.type_read = True728                else:729                    #print >>sys.stderr, 'TYPE', token.type, repr(token.val)730                    self.type_tokens.append(token)731        else:732            GenericDocState.handle(self, token)733class EnumValueState(VariableState):734    """State for an enum value."""735    def __init__(self, parser):736        VariableState.__init__(self, parser, raw_doc.RawEnumValue, 'val')737class TagState(GenericDocState):738    def __init__(self, parser):739        GenericDocState.__init__(self, parser, raw_doc.RawTag, 'tag')740        self.allowed_commands = set(['COMMAND_SIGNATURE', 'COMMAND_CODE', 'COMMAND_HTMLONLY',741                                     'COMMAND_SEE', 'COMMAND_BRIEF', 'COMMAND_TPARAM',742                                     'COMMAND_SECTION', 'COMMAND_SUBSECTION',743                                     'COMMAND_INCLUDE', 'COMMAND_SNIPPET',744                                     'COMMAND_HEADERFILE', 'COMMAND_DEPRECATED', 'COMMAND_NOTE', 'COMMAND_WARNING',745                                     'COMMAND_AKA', 'COMMAND_INTERNAL'])746class EnumState(GenericDocState):747    """State for an enum."""748        749    def __init__(self, parser):750        GenericDocState.__init__(self, parser, raw_doc.RawEnum, 'enum')751        self.allowed_commands = set(['COMMAND_SIGNATURE', 'COMMAND_CODE', 'COMMAND_HTMLONLY',752                                     'COMMAND_SEE', 'COMMAND_BRIEF',753                                     'COMMAND_SECTION', 'COMMAND_SUBSECTION',754                                     'COMMAND_INCLUDE', 'COMMAND_SNIPPET',755                                     'COMMAND_HEADERFILE', 'COMMAND_DEPRECATED', 'COMMAND_NOTE', 'COMMAND_WARNING',756                                     'COMMAND_AKA', 'COMMAND_INTERNAL'])757class AdaptionState(GenericDocState):758    """State for an adaption."""759        760    def __init__(self, parser):761        GenericDocState.__init__(self, parser, raw_doc.RawAdaption, 'adaption')762        self.allowed_commands = set(['COMMAND_SIGNATURE', 'COMMAND_CODE', 'COMMAND_HTMLONLY',763                                     'COMMAND_SEE', 'COMMAND_BRIEF',764                                     'COMMAND_SECTION', 'COMMAND_SUBSECTION',765                                     'COMMAND_INCLUDE', 'COMMAND_SNIPPET',766                                     'COMMAND_HEADERFILE', 'COMMAND_DEPRECATED', 'COMMAND_NOTE', 'COMMAND_WARNING',767                                     'COMMAND_AKA', 'COMMAND_INTERNAL'])768class TypedefState(GenericDocState):769    """State for an typedef."""770        771    def __init__(self, parser):772        GenericDocState.__init__(self, parser, raw_doc.RawTypedef, 'typedef')773        self.allowed_commands = set(['COMMAND_SIGNATURE', 'COMMAND_CODE', 'COMMAND_HTMLONLY',774                                     'COMMAND_SEE', 'COMMAND_BRIEF',775                                     'COMMAND_SECTION', 'COMMAND_SUBSECTION',776                                     'COMMAND_INCLUDE', 'COMMAND_SNIPPET',777                                     'COMMAND_HEADERFILE', 'COMMAND_DEPRECATED', 'COMMAND_NOTE', 'COMMAND_WARNING',778                                     'COMMAND_AKA', 'COMMAND_INTERNAL'])779class Parser(object):780    """The parser class takes tokens from a lexer.Lexer class.781    782    It generates a raw_doc.RawDoc object from this with783    raw_doc.RawEntry objects.784    """785    def __init__(self):786        self.states = ['top']787        self.handlers = {788            'top':          TopLevelState(self),789            'class':        ClassDocState(self),790            'function':     FunctionDocState(self),791            'macro':        MacroDocState(self),792            'metafunction': MetafunctionDocState(self),793            'concept':      ConceptDocState(self),794            'page':         PageState(self),795            'mainpage':     MainPageState(self),796            'group':        GroupState(self),797            'var':          VariableState(self),798            'val':          EnumValueState(self),799            'tag':          TagState(self),800            'enum':         EnumState(self),801            'adaption':     AdaptionState(self),802            'typedef':      TypedefState(self),803            }804        self.documentation = raw_doc.RawDoc()805    def parse(self, lexer):806        for token in lexer.tokens():807            self.handleToken(token)808        while len(self.states) > 1:809            self.leaveState(self.states[-1])810    def handleToken(self, token):811        #print 'Handling %s in states %s' % (token, self.states)812        self.handlers[self.states[-1]].handle(token)813    def enterState(self, state, first_token):814        #print 'entering state %s' % state815        self.states.append(state)816        self.handlers[state].entered(first_token)817    def leaveState(self, state):818        #print 'leaving state %s' % state819        if self.states[-1] == state:820            self.handlers[state].left()821            if self.handlers[state].getEntry():822                self.documentation.addEntry(self.handlers[state].getEntry())823            return self.states.pop()...test_engine.py
Source:test_engine.py  
1import pytest2import mock3def test_play_engine_constructor(request):4    from pytest_play.engine import PlayEngine5    executor = PlayEngine(request, {'foo': 'bar'})6    assert executor.request is request7    assert executor.variables == {'foo': 'bar'}8def test_get_file_contents(play, data_base_path):9    play.get_file_contents(data_base_path, 'included.yml')10def test_register_teardown(play, data_base_path):11    assert play._teardown == []12    import mock13    callback = mock.MagicMock()14    play.register_teardown_callback(callback)15    play.register_teardown_callback(callback)16    assert callback in play._teardown17    assert len(play._teardown) == 118    assert not callback.called19    play.teardown()20    assert callback.assert_called_once_with() is None21def test_executor_parametrize(dummy_executor):22    assert dummy_executor.parametrize('$foo') == 'bar'23@pytest.mark.parametrize("expr,expected", [24    ('{! variables["foo"].upper() !}', 'BAR')])25def test_executor_parametrize_expression(dummy_executor, expr, expected):26    assert dummy_executor.parametrize(27        expr) == expected28def test_execute(dummy_executor):29    execute_command_mock = mock.MagicMock()30    dummy_executor.execute_command = execute_command_mock31    yml_data = [32        {'provider': 'python', 'type': 'assert', 'expression': '1'},33        {'provider': 'python', 'type': 'assert', 'expression': '2'}34    ]35    dummy_executor.execute(yml_data)36    calls = [37        mock.call(yml_data[0]),38        mock.call(yml_data[1]),39    ]40    assert dummy_executor.execute_command.assert_has_calls(41        calls, any_order=False) is None42def test_execute_extra_vars(dummy_executor):43    execute_command_mock = mock.MagicMock()44    dummy_executor.execute_command = execute_command_mock45    yml_data = [46        {'provider': 'python', 'type': 'assert', 'expression': '1'},47        {'provider': 'python', 'type': 'assert',48         'expression': '2 == $does_not_exist'}49    ]50    assert 'does_not_exist' not in dummy_executor.variables51    dummy_executor.execute(yml_data, extra_variables={'does_not_exist': '2'})52    calls = [53        mock.call(yml_data[0]),54        mock.call(yml_data[1]),55    ]56    assert dummy_executor.execute_command.assert_has_calls(57        calls, any_order=False) is None58def test_execute_bad_type(dummy_executor):59    command = {'provider': 'python', 'typeXX': 'assert', 'expression': '1'}60    with pytest.raises(KeyError):61        dummy_executor.execute_command(command)62def test_execute_bad_command(dummy_executor):63    command = {'provider': 'python', 'type': 'assert', 'expressionXX': '1'}64    with pytest.raises(KeyError):65        dummy_executor.execute_command(command)66def test_execute_not_implemented_command(dummy_executor):67    command = {'provider': 'python', 'type': 'new_command',68               'param': 'http://1'}69    dummy_executor.COMMANDS = ['new_command']70    with pytest.raises(NotImplementedError):71        dummy_executor.execute_command(command)72def test_execute_condition_true(dummy_executor):73    command = {'provider': 'python',74               'type': 'assert',75               'expression': 'False',76               'skip_condition': '1 == 1'}77    dummy_executor.execute_command(command)78def test_execute_condition_false(dummy_executor):79    command = {'provider': 'python',80               'type': 'assert',81               'expression': 'True',82               'skip_condition': '1 == 0'}83    dummy_executor.execute_command(command)84def test_execute_get_basestring(dummy_executor):85    import yaml86    command = yaml.load("""87---88provider: python89type: assert90expression: 1 == 191    """)92    dummy_executor.execute_command(command)93def test_execute_get_basestring_param(dummy_executor):94    import yaml95    command = yaml.load("""96---97provider: python98type: assert99expression: "'$foo' == 'bar'"100""")101    dummy_executor.execute_command(command)102def test_new_provider_custom_command(dummy_executor):103    command = {'type': 'newCommand', 'provider': 'newprovider'}104    dummy_provider = mock.MagicMock()105    with pytest.raises(ValueError):106        dummy_executor.execute_command(command)107    dummy_executor.register_command_provider(108        dummy_provider, 'newprovider')109    # execute new custom command110    dummy_executor.execute_command(command)111    assert dummy_provider.assert_called_once_with(dummy_executor) is None112    assert dummy_provider \113        .return_value \114        .command_newCommand \115        .assert_called_once_with(command) is None116def test_execute_includes(dummy_executor, data_base_path):117    yml_data = [118        {'type': 'include', 'provider': 'include',119         'path': '{0}/{1}'.format(120             data_base_path, 'included.yml')},121        {'type': 'include', 'provider': 'include',122         'path': '{0}/{1}'.format(123             data_base_path, 'assertion.yml')},124    ]125    dummy_executor.execute(yml_data)126    assert dummy_executor.variables['included'] == 1127def test_default_command(play, data_base_path):128    play.variables['include'] = {'comment': 'default comment'}129    play.get_command_provider = mock.MagicMock()130    yml_data = [131        {"provider": "include", "type": "include",132         "path": "{0}/included.yml".format(data_base_path)},133    ]134    from copy import deepcopy135    expected_command = deepcopy(yml_data)[0]136    expected_command['comment'] = 'default comment'137    play.execute(yml_data)138    assert play \139        .get_command_provider \140        .return_value \141        .command_include \142        .assert_called_once_with(143            expected_command) is None144def test_default_command_override(play, data_base_path):145    play.variables['include'] = {'comment': 'default comment'}146    play.get_command_provider = mock.MagicMock()147    yml_data = [148        {"provider": "include", "type": "include",149         "comment": "override",150         "path": "{0}/included.yml".format(data_base_path)},151    ]152    from copy import deepcopy153    expected_command = deepcopy(yml_data)[0]154    expected_command['comment'] = 'override'155    play.execute(yml_data)156    assert play \157        .get_command_provider \158        .return_value \159        .command_include \160        .assert_called_once_with(161            expected_command) is None162def test_default_command_override_dict(play, data_base_path):163    play.variables['include'] = {164        'comment': {'comment': 'default comment'}}165    play.get_command_provider = mock.MagicMock()166    yml_data = [167        {"provider": "include", "type": "include",168         "comment": {"another": "override"},169         "path": "{0}/included.yml".format(data_base_path)},170    ]171    from copy import deepcopy172    expected_command = deepcopy(yml_data)[0]173    expected_command['comment'] = {174        'another': 'override', 'comment': 'default comment'}175    play.execute(yml_data)176    assert play \177        .get_command_provider \178        .return_value \179        .command_include \180        .assert_called_once_with(181            expected_command) is None182def test_default_command_override_dict_2(play, data_base_path):183    play.variables['include'] = {184        'comment': {'comment': 'default comment'}}185    play.get_command_provider = mock.MagicMock()186    yml_data = [187        {"provider": "include", "type": "include",188         "comment": {"another": "override", "comment": "other"},189         "path": "{0}/included.yml".format(data_base_path)},190    ]191    from copy import deepcopy192    expected_command = deepcopy(yml_data)[0]193    expected_command['comment'] = {194        'another': 'override', 'comment': 'other'}195    play.execute(yml_data)196    assert play \197        .get_command_provider \198        .return_value \199        .command_include \200        .assert_called_once_with(201            expected_command) is None202def test_default_command_override_dict_4(203        play, data_base_path):204    play.variables['include'] = {205        'comment': {'comment': 'default comment'}}206    play.get_command_provider = mock.MagicMock()207    yml_data = [208        {"provider": "include", "type": "include",209         "comment": "default comment",210         "path": "{0}/included.yml".format(data_base_path)},211    ]212    from copy import deepcopy213    expected_command = deepcopy(yml_data)[0]214    expected_command['comment'] = 'default comment'215    play.execute(yml_data)216    assert play \217        .get_command_provider \218        .return_value \219        .command_include \220        .assert_called_once_with(221            expected_command) is None222def test_default_command_override_dict_3(223        play, data_base_path):224    play.variables['include'] = {225        'comment': 'default comment'}226    play.get_command_provider = mock.MagicMock()227    yml_data = [228        {"provider": "include", "type": "include",229         "comment": {"another": "override", "comment": "other"},230         "path": "{0}/included.yml".format(data_base_path)},231    ]232    from copy import deepcopy233    expected_command = deepcopy(yml_data)[0]234    expected_command['comment'] = {235        'another': 'override', 'comment': 'other'}236    play.execute(yml_data)237    assert play \238        .get_command_provider \239        .return_value \240        .command_include \241        .assert_called_once_with(242            expected_command) is None243def test_include_string(play, data_base_path):244    play.variables['foo'] = 'bar'245    yml_data = """246---247- provider: include248  type: include249  path: "%s/included.yml"250- provider: python251  type: assert252  expression: "$included == 1"253- provider: python254  type: store_variable255  name: included256  expression: "2"257  comment: update included value from 1 to 2258- provider: python259  type: assert260  expression: "$included == 2"261    """ % data_base_path262    play.execute_raw(yml_data)263    assert play.variables['included'] == 2264def test_teardown(play):265    import mock266    play._teardown = [267        mock.MagicMock(side_effect=AttributeError()),268        mock.MagicMock()]269    play.teardown()270    assert play._teardown[0].assert_called_once_with() is None271    assert play._teardown[1].assert_called_once_with() is None272def test_elapsed_variable(play):273    command = {'provider': 'python',274               'type': 'assert',275               'expression': 'True', }276    assert '_elapsed' not in play.variables277    play.execute_command(command)...Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!
