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

Handling warnings from C++ #4535

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

jiwaszki
Copy link
Contributor

Description

Providing a simple interface to handle warnings on the pybind side. Following up #601 issue.

  • New py::warning class that provide wrapper for custom warnings.
  • Added py::warnings namespace containing shorthand versions of Python C API warning categories.
  • Added raise_warning that allow to use warnings directly from py::warnings namespace or created by the user.

PS It is my first time contributing something "bigger" (finally! :)). Please, look at "TODO" section, there are some high-level decisions to be made. These are ones I have spotted. All reviews are welcome!

TODO:

  • Which order of arguments is better?
// First option - allows to default category, follows Python's warnings.warn convention
raise_warning(const char *message, handle category = warnings::runtime, ssize_t stack_level = 2);

// Second option - force to choose category, follows PyErr_WarnEx convention
raise_warning(handle category, const char *message, ssize_t stack_level = 2);
  • Which stack_level value is more relevant? C API is not defining default stack_level. According to Python docs on warnings.warn the default value is 1. However, there is also a good point on the default to be set to 2:

This makes the warning refer to deprecation()’s caller, rather than to the source of deprecation() itself (since the latter would defeat the purpose of the warning message).

  • Is py::warnings namespace a good name? Maybe py::warning_categories is more descriptive and not conflicting with py::warning? ... or should it be removed entirely?
  • Documentation entry (Let's move with this PR first. Is it possible to handle docs in a separate PR?).

Suggested changelog entry:

Added `py::warning` class, `py::warnings` namespace and `py::raise_warning()` which provide the interface for Python warnings.

Copy link
Collaborator

@EthanSteinberg EthanSteinberg left a comment

Choose a reason for hiding this comment

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

Looks like a nice feature! Just a couple of comments.

if (result == 1) {
return true;
}
if (result == -1) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should probably have a more visible error / warning here right? Isn't -1 from IsSubclass supposed to be propagated to users?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is new commit OK? I am just not sure what "guidelines" on errors are applied for this specific scenario.

PYBIND11_NAMESPACE_BEGIN(warnings)

// Warning class
static PyObject *warning_base = PyExc_Warning;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why aren't these const?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Or even constexpr

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. However, from what I understand constexpr won't work out of the box for non-const pointers. Changed to consts.

assert m.CustomWarning is not None
assert issubclass(m.CustomWarning, DeprecationWarning)

import warnings
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why isn't this import at the top of the file?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It was put there as local import for this specific test. Sure it can be moved around! Done.

@jiwaszki jiwaszki requested review from Skylion007 and EthanSteinberg and removed request for henryiii and Skylion007 March 17, 2023 08:29
Copy link
Collaborator

@EthanSteinberg EthanSteinberg left a comment

Choose a reason for hiding this comment

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

I think there was a bit of a misunderstanding. You made those pointer to constants (which is incorrect, as shown by those incorrect const casts) instead of constant pointers.

See

https://www.google.com/amp/s/www.geeksforgeeks.org/difference-between-constant-pointer-pointers-to-constant-and-constant-pointers-to-constants/amp/

"PyExc_Warning!");
}

if (PyErr_WarnEx(const_cast<PyObject *>(category), message, stack_level) == -1) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

These const casts aren't safe

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.

None yet

3 participants