-
Notifications
You must be signed in to change notification settings - Fork 85
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 Complex trait type validation duck-typed. #1594
Conversation
Note that (traits) mdickinson@mirzakhani traits % python
Python 3.10.0 (default, Nov 7 2021, 21:08:03) [Clang 12.0.5 (clang-1205.0.22.11)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from traits.api import BaseComplex, Complex, HasTraits
>>> class HasComplexTraits(HasTraits):
... bc = BaseComplex()
... c = Complex()
...
>>> obj = HasComplexTraits()
>>> import numpy as np
>>> obj.bc = np.float32(1.0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/mdickinson/Enthought/ETS/traits/traits/trait_types.py", line 396, in validate
self.error(object, name, value)
File "/Users/mdickinson/Enthought/ETS/traits/traits/base_trait_handler.py", line 74, in error
raise TraitError(
traits.trait_errors.TraitError: The 'bc' trait of a HasComplexTraits instance must be a complex number, but a value of 1.0 <class 'numpy.float32'> was specified.
>>> obj.c = np.float32(1.0)
>>> obj.bc = np.complex128(0)
>>> obj.bc = np.complex64(0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/mdickinson/Enthought/ETS/traits/traits/trait_types.py", line 396, in validate
self.error(object, name, value)
File "/Users/mdickinson/Enthought/ETS/traits/traits/base_trait_handler.py", line 74, in error
raise TraitError(
traits.trait_errors.TraitError: The 'bc' trait of a HasComplexTraits instance must be a complex number, but a value of 0j <class 'numpy.complex64'> was specified.
>>> obj.c = np.complex64(0) |
@@ -409,7 +407,7 @@ class Complex(BaseComplex): | |||
""" | |||
|
|||
#: The C-level fast validator to use: | |||
fast_validate = complex_fast_validate |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like complex_fast_validate
isnt used anywhere now. int_fast_validate
and float_fast_validate
aren't used anywhere either. are we keeping it around mainly because it is part of the public API?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are we keeping it around mainly because it is part of the public API
No, I'm just keeping them around to keep this PR simple and focused. I do indeed plan a follow-up PR that removes all three of int_fast_validate
, float_fast_validate
and complex_fast_validate
. They're not part of the public API - they're not exposed in any api
module. That alone should mean that we don't need to justify removing them, but I did also check all projects available to me to confirm that nothing I can see is using any of these.
I confess I was hoping to go further and remove the use of numpy
in trait_types
, but the Bool
trait messes that up.
IIUC, this is because |
Yes. |
This PR changes the validation of the
Complex
trait type to use a duck-typed approach instead of the current white-list-of-accepted-types approach. All values that were previously accepted in aComplex
trait will continue to be accepted, but objects whose type implements__complex__
will now also be accepted. Essentially,Complex
will accept any non-string-type that thecomplex
built-in accepts.This parallels similar changes made a while ago for the
Int
andFloat
trait types (see #104, #393), both of which already do duck-typed checks. It makes it clearer that the behaviour of theComplex
trait type does not depend on whether NumPy is available or not, and it ensures thatComplex
will accept complex-like objects from other frameworks.Implementation notes:
ValidateTraits
enumeration, but the "complex" there refers to compound trait types (e.g., created usingEither
) rather than complex numbers. So the new enumeration value is calledcomplex_number
instead ofcomplex
.BaseComplex
validation, it's rather hard to write pure Python code that matches the behaviour we want (essentially, we want to accept exactly the set of things that the std. lib.cmath
module functions accept - that's roughly the same as things that thecomplex
constructor accepts, but excluding strings). Instead, I've added a C implementation of that logic, and a private function_validate_complex_number
intraits.ctraits
that exposes the logic to Python.Checklist
docs/source/traits_api_reference
)docs/source/traits_user_manual
)- [ ] Update type annotation hints inN/Atraits-stubs