Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pyosys: catch boost::python::error_already_set #4699

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

georgerennie
Copy link
Contributor

This catches exceptions from internal passes, printing them in a readable manner where the user would otherwise see an unspecified boost exception.

e.g. consider the following pass in pass.py:

import libyosys as ys

class MyPass(ys.Pass):
    def __init__(self):
        super().__init__("my_pass", "")

    def py_help(self):
        raise Exception("broken")

p = MyPass()

Previously, when invoked with yosys -m pass.py -p "help my_pass", yosys would error with

-- Running command `help my_pass' --
terminate called after throwing an instance of 'boost::python::error_already_set'

but with this change it instead shows

-- Running command `help my_pass' --
Traceback (most recent call last):
  File "<...>/pass.py", line 8, in py_help
    raise Exception("broken")
Exception: broken
ERROR: Python interpreter encountered an exception.

@povik
Copy link
Member

povik commented Nov 4, 2024

I think we should catch it here instead:

text += f"\n\t\t\t\t{return_stmt}" + call_string

For two reasons:

  • the Yosys internals are not written to have exceptions propagating, so we should catch it as soon as we leave Python
  • this will cover uses which don't go through main(), i.e. libyosys, or calling Python passes from Python

@povik
Copy link
Member

povik commented Nov 4, 2024

To illustrate it a little better, the generated code looks like

		void py_execute(boost::python::list args, Design* design)
		{
			if (boost::python::override py_execute = this->get_override("py_execute"))
				py_execute(args, design); // xxx
			else
				Pass::py_execute(args, design);
		}

and // xxx marks the spot for catching the exception

@georgerennie
Copy link
Contributor Author

Ahh sure, the main reason I hadn't done that was because I didn't want to get too into the mess of py_wrap_generator :P but I ended up doing that anyway so im happy to change it so its done that way

* This catches exceptions from internal passes, printing them in a
  readable manner where the user would otherwise see an unspecified
  boost exception
@georgerennie
Copy link
Contributor Author

Have moved it into the generator. The generated c++ diff looks as follows with the other py_* functions taking the same form as py_help

43a44,48
> 	[[noreturn]] static void log_python_exception_as_error() {
> 		PyErr_Print();
> 		log_error("Python interpreter encountered an exception.\n");
> 	}
> 
447,449c452,458
< 			if (boost::python::override py_help = this->get_override("py_help"))
< 				py_help();
< 			else
---
> 			if (boost::python::override py_help = this->get_override("py_help")) {
> 				try {
> 					py_help();
> 				} catch (boost::python::error_already_set &) {
> 					log_python_exception_as_error();
> 				}
> 			} else {
450a460
> 			}

Copy link
Member

@povik povik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codewise looks good to me

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants