Python: raise SyntaxError with lineno
Asked Answered
T

4

5

I am implementing a parser for a domain specific language, and want to be able to raise a SyntaxError. How do I set the filename, lineno and offset when raising this exception?

exception SyntaxError

Raised when the parser encounters a syntax error. This may occur in an import statement, in an exec statement, in a call to the built-in function eval() or input(), or when reading the initial script or standard input (also interactively).

Instances of this class have attributes filename, lineno, offset and text for easier access to the details. str() of the exception instance returns only the message.

Source: https://docs.python.org/3.2/library/exceptions.html#SyntaxError

Trigonous answered 15/11, 2015 at 8:31 Comment(4)
If this isn't actually a Python syntax error, you might be better creating your own exception.Vanhomrigh
I would have the same problem subclassing it. As it stands, I do believe SyntaxError is the most appropriate type for a syntax error with filename and lineno - I'd just be reinventing it otherwise.Trigonous
I don't mean subclassing SyntaxError, just your own Exception subclass (e.g. class MySyntaxError(Exception):) that you can add whatever attributes you like to.Vanhomrigh
Agree with golightly, and yet this doesn't seem settled by the wisdom of the stackoverflow crowd. #24038190 suggests that a SyntaxError 'might be confusing', but gives no further arguments as to what might confuse, so I remain unconvinced. SyntaxError seems like the best semantic fit, plus you get the 'sugar' of lineno, filename etc. 'for free'.Conversation
P
15

The answer is:

>>> raise SyntaxError('End quote missing', ("test.py", 1000, 11, "print 'bar"))                                                                             
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "test.py", line 1000
    print 'bar
              ^
SyntaxError: End quote missing

The second argument needs to be a 4-tuple.

Presume answered 1/4, 2016 at 23:25 Comment(1)
Since Python 3.10 SyntaxError also accepts end_lineno and end_offsetNuli
S
2

You can have a look here: https://github.com/python/cpython/blob/master/Objects/exceptions.c#L1273

I could come up only with this:

import traceback

print("Our exception")

try:
    print("(1)")
    raise SyntaxError('Test2', {'filename': "test.py", 'lineno': 1000, 'offset': 1, 'text': "My text ..."})
except SyntaxError as inst:
    print("(2)")
    print(inst.args)
    print("(3) Get filename %s" % inst.args[1]['filename'])
    print("(4) Traceback")
    traceback.print_tb(inst.__traceback__) 

The output is then:

Our exception
(1)
(2)
('Test2', {'offset': 1, 'filename': 'test.py', 'text': 'My text ...', 'lineno': 1000})
(3) Get filename test.py
(4) Traceback
  File "test.py", line 7, in <module>
    raise SyntaxError('Test2', {'filename': "test.py", 'lineno': 1000, 'offset': 1, 'text': "My text ..."})
Spheno answered 15/11, 2015 at 8:42 Comment(1)
This is a good answer for those who wants to elevate same set of syntax error information through the re-raise of SyntaxError. I found this extremely useful when trying to print out line number/offset of where the error happened in a user-friendly manner.Hyetograph
N
0

How about:

raise SyntaxError('filename: {}, lineno: {}, offset: {}'.format(
    filename, lineno, offset))
Nonpartisan answered 15/11, 2015 at 8:40 Comment(1)
Thanks for the catch. Had a syntax error in SyntaxError. ;)Templetempler
H
0

@MartyIX tipped me off with the solid, so I provided a working prototype:

full_filename = "test_python_config_file.py"

def process_config(this_file):

    try:
        my_python_config_importer(this_file)

    except SyntaxError as e:
        raise SyntaxError(
            f"Error invalid syntax at line number {e.end_lineno}"
            f" column offset {e.end_offset}",
            {
                "filename": this_file,
                "lineno": int(e.lineno),
                "offset": int(e.offset),
                "text": e.text,
                "end_lineno": int(e.end_lineno),
                "end_offset": int(e.end_offset),
            },
        ) from e

# main
try:
    process_config(filename)
except SyntaxError as my_e:
    # Viola!
    print("Line number", my_e.value.args[1]["lineno"])
    print("Line offset", my_e.value.args[1]["offset"])
    # extract other `value[1]` fields, as needed

Hyetograph answered 14/7 at 18:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.