farhawa
farhawa

Reputation: 10417

Python - print the line pointed by error message

To load a json, I use the json.loads() method with a try/except block to get where the error has been occured if it fail.

I am getting errors, in the form:

Expecting object: line 1 column 14117248 (char 14117247)

I am asking if there is a way to get the line/char pointed by this error.

Is there a way in python that return the line X column Y from a string?

Upvotes: 3

Views: 4324

Answers (2)

Martijn Pieters
Martijn Pieters

Reputation: 1124010

From Python 3.5 onwards, decoding can raise an JSONDecodeError exception. This exception contains a few extra attributes, use those to print out some context. I'd limit this to a small string. Your sample input string has at least 14117247 characters on the first line, probably because your input contains no line separators at all; it is probably not all that helpful to print all those characters:

import json

try:
    json.loads(some_string)
except json.JSONDecodeError as err:
    # grab a reasonable section, say 40 characters.
    start, stop = max(0, err.pos - 20), err.pos + 20
    snippet = err.doc[start:stop]
    print(err)
    print('... ' if start else '', snippet, ' ...' if stop < len(err.doc) else '', sep="")
    print('^'.rjust(21 if not start else 25))

This outputs 40 characters of context (with ellipsis to indicate where we cut the string short) and adds a ^ caret underneath the indicated position:

>>> import random, json
>>> some_string = json.dumps([{"foo": "bar"}] * 1_000_000)
>>> error_pos = random.randrange(len(some_string))
>>> error_pos = some_string.find(",", error_pos)  # to avoid injecting an error inside a string
>>> some_string = some_string[:error_pos] + "!" + some_string[error_pos:]
>>> try:
...     json.loads(some_string)
... except json.JSONDecodeError as err:
...     # grab a reasonable section, say 40 characters.
...     start, stop = max(0, err.pos - 20), err.pos + 20
...     snippet = err.doc[start:stop]
...     print(err)
...     print('... ' if start else '', snippet, ' ...' if stop < len(err.doc) else '', sep="")
...     print('^'.rjust(21 if not start else 25))
...
Expecting ',' delimiter: line 1 column 14153440 (char 14153439)
... ar"}, {"foo": "bar"}!, {"foo": "bar"}, { ...
                        ^

In Python 3.4 and earlier, a ValueError exception is raised. You can parse the message (the exception.args[0] value) for clues, but this requires manual string parsing. See the errmsg() function; reverse its formatting when parsing.

For this specific case however (where a value is expected), you'll need 3.4 or newer to get a line and column indicator; before 3.4 the best the module could do was give you the unhelpful message No JSON object could be decoded.

Upvotes: 7

shapiy
shapiy

Reputation: 1155

Most likely, no. At least in Python 2.7. loads does not provide any means for customizing how it behaves on errors.

try:
    value, end = scan_once(s, end)
except StopIteration:
    raise ValueError(errmsg("Expecting object", s, end))

But you can parse the location from the error message to read the line from your input. The error format can be either {0}: line {1} column {2} (char {3}) or {0}: line {1} column {2} - line {3} column {4} (char {5} - {6}).

Upvotes: 2

Related Questions