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

Make accessing dunder-names on a CTrait object an AttributeError #1469

Merged
merged 4 commits into from
Jun 17, 2021

Conversation

mdickinson
Copy link
Member

This PR changes attribute lookups on CTrait objects to raise AttributeError for names beginning and ending with a double underscore, and for which the normal attribute lookup rules don't return anything. Previously, those attribute lookups would return None, and this broke napoleon, which expected that a lookup of __qualname__ on a CTrait object would either raise or return a string.

Attribute lookups for names that don't begin and end with a double underscore are unchanged.

No documentation update: it's not currently documented that arbitrary attribute accesses on a CTrait object return None, and I'm not sure that I really want it to be - it's not a "feature" of Traits that I'd want to encourage people to rely on.

Fixes #1463.

Checklist

  • Tests
  • Update API reference (docs/source/traits_api_reference)
  • Update User manual (docs/source/traits_user_manual)
  • Update type annotation hints in traits-stubs

traits/ctraits.c Outdated Show resolved Hide resolved
@mdickinson mdickinson requested a review from rahulporuri June 15, 2021 07:42
Copy link
Contributor

@rahulporuri rahulporuri left a comment

Choose a reason for hiding this comment

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

traits/ctraits.c Outdated
else if (is_dunder) {
/* Retry the attribute lookup so that we get the correct exception
message. */
return PyObject_GenericGetAttr((PyObject *)obj, name);
Copy link
Contributor

Choose a reason for hiding this comment

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

IIUC, we're having to retry in this clause instead of simply removing the PyErr_Clear outside of the conditional statements because the other two clauses still need/want to use PyErr_Clear. Is that correct?

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm. This is really just laziness. What I want to do here is re-raise the original AttributeError. I don't want to create my own exception message, since the one from the interpreter is likely to be better (and may even change with more recent versions of Python - there's a lot of work on error messages right now).

I can't simply remove the PyErr_Clear because is_dunder_name might set an exception, and it's an error to set another exception if there's already an exception set (or at least, I can't find any evidence that it's okay to do so, and there's CPython core code that goes out of its way to avoid this).

The right way to handle this without the retry is to do a PyErr_Fetch / PyErr_Restore pairing. I'll aim to update the PR to do that.

Copy link
Member Author

Choose a reason for hiding this comment

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

and it's an error to set another exception if there's already an exception set (or at least, I can't find any evidence that it's okay to do so, and there's CPython core code that goes out of its way to avoid this).

Hmm, actually, looking into the source, this may be okay. Investigating ...

Copy link
Member Author

Choose a reason for hiding this comment

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

From the source, all the exception setting mechanisms end up at _PyErr_SetObject, which, if an exception is already set, applies implicit exception chaining. I think that's good enough for this case. I've reworked the code accordingly.

Copy link
Contributor

@rahulporuri rahulporuri Jun 17, 2021

Choose a reason for hiding this comment

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

From the source, ...

I didn't mean to send you down a rabbit hole 😅

traits/ctraits.c Show resolved Hide resolved
traits/tests/test_ctraits.py Show resolved Hide resolved
@rahulporuri
Copy link
Contributor

Still LGTM

@mdickinson
Copy link
Member Author

Still LGTM

Sorry, @rahulporuri: I think the above message came just a couple of minutes before I updated to simplify the exception handling. Could you do another quick check?

Copy link
Contributor

@rahulporuri rahulporuri left a comment

Choose a reason for hiding this comment

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

Still still LGTM 🎉

@mdickinson
Copy link
Member Author

@rahulporuri Thank you! Merging.

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.

non-HasTrait classes with attributes of type CTrait break Sphinx + Napoleon autodoc
2 participants