Tracebacks in Jupyter notebooks

Andreas Freise, 16/04/2020

The following demonstrates how Finesse handles parse errors. When run from IPython or Jupyter the full traceback is suppressed and only the parser error itself is shown. When run from Python it always returns the full traceback.

You can print the latest full traceback with finesse.tb().

If you want to always see the full traceback, for example, during debugging, you can set this with finesse.show_tracebacks()

We use a very simple example model for the demonstration:

from finesse.env import is_interactive

print(is_interactive())
True
import finesse

code = """
l laser P=1
pd tmp laser.p1.o
"""

kat = finesse.Model()
kat.parse(code)

A syntax error in the katscript gives a parser error:

code = """
l1 laser P=1
pd tmp laser.p1.o
"""

kat = finesse.Model()
kat.parse(code)
KatSyntaxError: 	(use finesse.tb() to see the full traceback)
line 2: unknown element 'l1'. Did you mean 'l'?
   1: 
-->2: l1 laser P=1
      ^^
   3: pd tmp laser.p1.o

We can print the full traceback:

finesse.tb(colors=False)
---------------------------------------------------------------------------
KatSyntaxError                            Traceback (most recent call last)
Cell In[3], line 7
      1 code = """
      2 l1 laser P=1
      3 pd tmp laser.p1.o
      4 """
      6 kat = finesse.Model()
----> 7 kat.parse(code)

File /usr/local/lib/python3.13/site-packages/finesse/model.py:83, in locked_when_built.<locals>.wrapper(self, *args, **kwargs)
     79 if self.is_built:
     80     raise Exception(
     81         f"Model has been built for a simulation, cannot use {func} here"
     82     )
---> 83 return func(self, *args, **kwargs)

File /usr/local/lib/python3.13/site-packages/finesse/model.py:2264, in Model.parse(self, text, spec)
   2245 """Parses kat script and adds the resulting objects to the model.
   2246 
   2247 Parameters
   (...)
   2260 parse_legacy_file : Parse Finesse 2 kat script file.
   2261 """
   2262 from .script import parse
-> 2264 parse(text, model=self, spec=spec)

File /usr/local/lib/python3.13/site-packages/finesse/script/__init__.py:44, in parse(text, model, spec)
     41 from .compiler import KatCompiler
     43 compiler = KatCompiler(spec=spec)
---> 44 return compiler.compile(text, model=model)

File /usr/local/lib/python3.13/site-packages/finesse/script/compiler.py:166, in KatCompiler.compile(self, string, **kwargs)
    148 def compile(self, string, **kwargs):
    149     """Compile the contents of `string`.
    150 
    151     Parameters
   (...)
    164         The model compiled from reading `string`.
    165     """
--> 166     return self.compile_file(StringIO(string), **kwargs)

File /usr/local/lib/python3.13/site-packages/finesse/script/compiler.py:215, in KatCompiler.compile_file(self, fobj, model, resolve, build)
    212     self._reraise_parser_error_in_spec_context(e)
    214 # Build the parse graph.
--> 215 self._fill(script, ROOT_NODE_NAME)
    217 # At this stage there shouldn't be any dependencies between branches.
    218 assert self.graph.is_tree()

File /usr/local/lib/python3.13/site-packages/finesse/script/compiler.py:274, in KatCompiler._fill(self, value, path, **attributes)
    272     for order, argument in enumerate(value.arguments):
    273         argument_path = self.graph.item_node_name(order, path)
--> 274         self._fill(argument, argument_path)
    275         self.graph.add_edge(
    276             argument_path, path, type=KatEdgeType.ARGUMENT, order=order
    277         )
    279 return attributes

File /usr/local/lib/python3.13/site-packages/finesse/script/compiler.py:266, in KatCompiler._fill(self, value, path, **attributes)
    264 def _fill(self, value, path, **attributes):
    265     attributes = merge_attributes(
--> 266         attributes, self._item_node_attributes(value, path)
    267     )
    268     self.graph.add_node(path, **attributes)
    270     if hasattr(value, "arguments"):
    271         # Assemble arguments.

File /usr/local/lib/python3.13/functools.py:985, in singledispatchmethod.__get__.<locals>._method(*args, **kwargs)
    982 if not args:
    983     raise TypeError(f'{funcname} requires at least '
    984                     '1 positional argument')
--> 985 return dispatch(args[0].__class__).__get__(obj, cls)(*args, **kwargs)

File /usr/local/lib/python3.13/site-packages/finesse/script/compiler.py:341, in KatCompiler._(self, element, path)
    337     else:
    338         msg = _get_unknown_token_message(
    339             "element", directive_token.raw_value, self.spec.elements.keys()
    340         )
--> 341         raise KatSyntaxError(
    342             msg,
    343             self.script,
    344             directive_token,
    345         )
    346 return {
    347     "token": element.directive,
    348     "name_token": element.name,
    349     "type": KatNodeType.ELEMENT,
    350     "extra_tokens": element.extra,
    351 }

KatSyntaxError: 	(use finesse.tb() to see the full traceback)
line 2: unknown element 'l1'. Did you mean 'l'?
   1: 
-->2: l1 laser P=1
      ^^
   3: pd tmp laser.p1.o

To switch tracebacks on globally:

finesse.show_tracebacks(True)
False
code = """
l1 laser P=1
pd tmp laser.p1.o
"""

kat = finesse.Model()
kat.parse(code)
---------------------------------------------------------------------------
KatSyntaxError                            Traceback (most recent call last)
Cell In[6], line 7
      1 code = """
      2 l1 laser P=1
      3 pd tmp laser.p1.o
      4 """
      6 kat = finesse.Model()
----> 7 kat.parse(code)

File /usr/local/lib/python3.13/site-packages/finesse/model.py:83, in locked_when_built.<locals>.wrapper(self, *args, **kwargs)
     79 if self.is_built:
     80     raise Exception(
     81         f"Model has been built for a simulation, cannot use {func} here"
     82     )
---> 83 return func(self, *args, **kwargs)

File /usr/local/lib/python3.13/site-packages/finesse/model.py:2264, in Model.parse(self, text, spec)
   2245 """Parses kat script and adds the resulting objects to the model.
   2246 
   2247 Parameters
   (...)
   2260 parse_legacy_file : Parse Finesse 2 kat script file.
   2261 """
   2262 from .script import parse
-> 2264 parse(text, model=self, spec=spec)

File /usr/local/lib/python3.13/site-packages/finesse/script/__init__.py:44, in parse(text, model, spec)
     41 from .compiler import KatCompiler
     43 compiler = KatCompiler(spec=spec)
---> 44 return compiler.compile(text, model=model)

File /usr/local/lib/python3.13/site-packages/finesse/script/compiler.py:166, in KatCompiler.compile(self, string, **kwargs)
    148 def compile(self, string, **kwargs):
    149     """Compile the contents of `string`.
    150 
    151     Parameters
   (...)
    164         The model compiled from reading `string`.
    165     """
--> 166     return self.compile_file(StringIO(string), **kwargs)

File /usr/local/lib/python3.13/site-packages/finesse/script/compiler.py:215, in KatCompiler.compile_file(self, fobj, model, resolve, build)
    212     self._reraise_parser_error_in_spec_context(e)
    214 # Build the parse graph.
--> 215 self._fill(script, ROOT_NODE_NAME)
    217 # At this stage there shouldn't be any dependencies between branches.
    218 assert self.graph.is_tree()

File /usr/local/lib/python3.13/site-packages/finesse/script/compiler.py:274, in KatCompiler._fill(self, value, path, **attributes)
    272     for order, argument in enumerate(value.arguments):
    273         argument_path = self.graph.item_node_name(order, path)
--> 274         self._fill(argument, argument_path)
    275         self.graph.add_edge(
    276             argument_path, path, type=KatEdgeType.ARGUMENT, order=order
    277         )
    279 return attributes

File /usr/local/lib/python3.13/site-packages/finesse/script/compiler.py:266, in KatCompiler._fill(self, value, path, **attributes)
    264 def _fill(self, value, path, **attributes):
    265     attributes = merge_attributes(
--> 266         attributes, self._item_node_attributes(value, path)
    267     )
    268     self.graph.add_node(path, **attributes)
    270     if hasattr(value, "arguments"):
    271         # Assemble arguments.

File /usr/local/lib/python3.13/functools.py:985, in singledispatchmethod.__get__.<locals>._method(*args, **kwargs)
    982 if not args:
    983     raise TypeError(f'{funcname} requires at least '
    984                     '1 positional argument')
--> 985 return dispatch(args[0].__class__).__get__(obj, cls)(*args, **kwargs)

File /usr/local/lib/python3.13/site-packages/finesse/script/compiler.py:341, in KatCompiler._(self, element, path)
    337     else:
    338         msg = _get_unknown_token_message(
    339             "element", directive_token.raw_value, self.spec.elements.keys()
    340         )
--> 341         raise KatSyntaxError(
    342             msg,
    343             self.script,
    344             directive_token,
    345         )
    346 return {
    347     "token": element.directive,
    348     "name_token": element.name,
    349     "type": KatNodeType.ELEMENT,
    350     "extra_tokens": element.extra,
    351 }

KatSyntaxError: 
line 2: unknown element 'l1'. Did you mean 'l'?
   1: 
-->2: l1 laser P=1
      ^^
   3: pd tmp laser.p1.o

finesse.show_tracebacks() can also switch tracebacks off again:

finesse.show_tracebacks(False)
True