Search code examples
pythonpyparsing

pyparsing raise exception on emtpy delimitedList


I am trying to parse lists, like [1.0, 3.9], and I would like to raise a custom exception when the list is empty. I followed this https://stackoverflow.com/a/13409786/2528668 but without much success. Here is what I have so far:

class EmptyListError(ParseFatalException):
    """Exception raised by the parser for empty lists."""

    def __init__(self, s, loc, msg):
        super().__init__(s, loc, 'Empty lists not allowed \'{}\''.format(msg))


def hell_raiser(s, loc, toks):
    raise EmptyListError(s, loc, toks[0])


START, END = map(Suppress, '[]')
list_t = START + delimitedList(pyparsing_common.sci_real).setParseAction(lambda s, loc, toks: hell_raiser(s, loc, toks) if not toks else toks) + END


tests = """
[1.0, 1.0, 1.0]
[]
[     ]
""".splitlines()

for test in tests:
    if not test.strip():
        continue
    try:
        print(test.strip())
        result = list_t.parseString(test)
    except ParseBaseException as pe:
        print(pe)
    else:
        print(result)

which prints:

[1.0, 1.0, 1.0]
[1.0, 1.0, 1.0]
[]
Expected real number with scientific notation (at char 1), (line:1, col:2)
[     ]
Expected real number with scientific notation (at char 6), (line:1, col:7)

Solution

  • delimitedList will not match an empty list, so your parse action will never run. I changed your parser slightly to make the list inside []'s optional, and then run your hellRaiser parse action:

    list_t = START + Optional(delimitedList(pyparsing_common.sci_real)) + END
    
    list_t.setParseAction(lambda s, loc, toks: hell_raiser(s, loc, toks) if not toks else toks)
    

    Gets your desired output:

    [1.0, 1.0, 1.0]
    [1.0, 1.0, 1.0]
    []
    Empty lists not allowed '[]' (at char 0), (line:1, col:1)
    [     ]
    Empty lists not allowed '[]' (at char 0), (line:1, col:1)
    

    You could also replace your parse action with a boolean condition, in this case, simply bool - the built-in method will evaluate against the list of tokens, and if empty will fail the condition.

    list_t.addCondition(bool, message="Empty lists not allowed", fatal=True)
    

    Gets this:

    [1.0, 1.0, 1.0]
    [1.0, 1.0, 1.0]
    []
    Empty lists not allowed (at char 0), (line:1, col:1)
    [     ]
    Empty lists not allowed (at char 0), (line:1, col:1)
    

    Lastly, check out the runTests() method on ParserElement. I was writing that "test-the-string-and-dump-the-results-or-catch-the-exception" loop so many times, I decided to just add a testing convenience function.