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

Use pybind11 in ttconv module #25253

Merged
merged 2 commits into from Mar 30, 2023
Merged

Conversation

thomasjpfan
Copy link
Contributor

PR Summary

Towards #23846

This PR migrates the ttconv module to using pybind11. Most of the conversion uses pybind11 data structures. Some notes for reviewers:

  1. The Python C-API was required to converting bytes into Unicode: pybind11 reference.
  2. pybind11::handle is a thin wrapper around PyObject * that does not perform any reference counting.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Thank you for opening your first PR into Matplotlib!

If you have not heard from us in a while, please feel free to ping @matplotlib/developers or anyone who has commented on the PR. Most of our reviewers are volunteers and sometimes things fall through the cracks.

You can also join us on gitter for real-time discussion.

For details on testing, writing docs, and our review process, please see the developer guide

We strive to be a welcoming and open project. Please follow our Code of Conduct.

src/_ttconv.cpp Outdated
Py_DECREF(result);
PyObject* decoded = PyUnicode_DecodeLatin1(a, strlen(a), "");
if (decoded == NULL) {
throw py::exception();
Copy link
Contributor

@oscargus oscargus Feb 20, 2023

Choose a reason for hiding this comment

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

Thank you very much for this! There are more suitable reviewers than me (@ianthomas23 and @anntzer comes to mind), but I think it would be good to replace this exception with "something else" (to get rid of the py_exceptions.h import, which we want to get rid of as part of the PyBind11 conversion). I think that std::exception() is the way to go, possibly with some sensible error message. Although, of course, that general exception is not good etc, but at least it will be consistent with the current behavior.

(This exception is not tested in the original code either.)

Copy link
Contributor

@anntzer anntzer Feb 20, 2023

Choose a reason for hiding this comment

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

This should be py::error_already_set (https://pybind11.readthedocs.io/en/stable/advanced/exceptions.html#handling-exceptions-from-python-in-c).

Agreed this should be made to work without py_exceptions.h.

We should probably switch the whole thing to write to binary streams (and do something like fh.flush(); convert_ttf_to_ps(..., fh.buffer, ...); fh.buffer.flush() on the Python calling side), but that can be another PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I switched to using error_already_set. Note that this PR is keeping the original behavior on main which catches this exception and ignores it:

matplotlib/src/_ttconv.cpp

Lines 139 to 142 in de8e9d0

catch (const py::exception &)
{
return NULL;
}

Copy link
Contributor

@anntzer anntzer Feb 20, 2023

Choose a reason for hiding this comment

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

catches this exception and ignores it

No, returning NULL is the way for C-API functions to signal that an exception has been set and should be propagated to python-land...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah I see. I misinterpreted the original implementation. Thank you for the explanation!

anntzer
anntzer previously approved these changes Feb 20, 2023
@anntzer anntzer dismissed their stale review February 20, 2023 22:31

Missed something.

src/_ttconv.cpp Outdated
pybind11::arg("filename"),
pybind11::arg("output"),
pybind11::arg("fonttype"),
pybind11::arg("glyph_ids"),
Copy link
Contributor

Choose a reason for hiding this comment

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

default value for glyph_ids is missing.

src/_ttconv.cpp Outdated
const char *filename,
pybind11::object &output,
int fonttype,
pybind11::iterable glyph_ids)
Copy link
Contributor

@anntzer anntzer Feb 20, 2023

Choose a reason for hiding this comment

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

Just make this argument a std::vector<int> and don't bother with doing the conversion yourself?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

With the default value of None, I think this needs to be pybind11::iterable*, to account for the nullptr. If we need to use std::vector<int>, then we need std::optional, which requires C++17:

XREF: The note section from: https://pybind11.readthedocs.io/en/stable/advanced/functions.html#allow-prohibiting-none-arguments

@anntzer
Copy link
Contributor

anntzer commented Feb 20, 2023

Frankly, I would not bother with changing ttconv; I'd rather just see some work on reviving #20866 to move everything to fonttools (which we already did for pdf in #20391).

@oscargus
Copy link
Contributor

I would not bother with changing ttconv;

Although I agree in principle, the question is if/when that will happen... If it is before 3.8 is released (i.e., in July(?)), the current PR may not be worthwhile, but considering that it has been around for 14 months it is not obvious that it will happen.

Anyway, good that you bring it up, so your call @thomasjpfan . We appreciate your work, but there is a risk that it will be in vain in the sense that it will be removed before the next release. On the other hand, I still think it is worthwhile to merge this if finished as one do not know if it will be replaced and there are only details remaining. Sorry we didn't make it clear in #23846 that for this particular module there was another possible route.

@thomasjpfan
Copy link
Contributor Author

but there is a risk that it will be in vain in the sense that it will be removed before the next release.

I am okay with the changes made in this PR being removed because of #20866.

On the other hand, I still think it is worthwhile to merge

I agree that it makes sense to move forward with this PR anyways to get the benefits of using pybind11 as stated in #23846.

Copy link
Member

@ianthomas23 ianthomas23 left a comment

Choose a reason for hiding this comment

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

I've left a few comments but I'd be happy for this to merged as it is.

src/_ttconv.cpp Outdated Show resolved Hide resolved
catch (const py::exception &)
{
return NULL;
throw std::runtime_error(e.getMessage());
Copy link
Member

Choose a reason for hiding this comment

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

I am concerned that we don't exercise this line in the test suite as it would be good to confirm that TTExceptions propagate through pybind11 as expected. But given that we've evidently never had such a test it doesn't have to be done here, it can be punted to "future work".

}
catch (...)
{
PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
return NULL;
throw std::runtime_error("Unknown C++ exception");
Copy link
Member

Choose a reason for hiding this comment

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

This catch all isn't really needed as pybind11 catches C++ exceptions and converts them to Python ones. But I like the approach that support for pybind11 should happen with minimal changes to C++ code so it is fine to leave it as it is.

@oscargus
Copy link
Contributor

I fixed the merge conflict (in the browser...). Is there anything left here @anntzer? (Now, assuming that we are not fully sure that #20866 will be implemented in time for 3.8 and that we want to swap to PyBind11 "ASAP".)

@anntzer
Copy link
Contributor

anntzer commented Mar 30, 2023

From a quick look it seems fine. Let's just get this in modulo CI.

@oscargus oscargus merged commit e5fc57c into matplotlib:main Mar 30, 2023
37 of 38 checks passed
@oscargus
Copy link
Contributor

Thanks @thomasjpfan !

@tacaswell tacaswell added this to the v3.8.0 milestone Mar 30, 2023
@oscargus oscargus mentioned this pull request Mar 30, 2023
13 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Development

Successfully merging this pull request may close these issues.

None yet

6 participants