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

Document that Either should not be used in new code #1699

Merged
merged 4 commits into from
Aug 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/source/traits_api_reference/trait_types.rst
Original file line number Diff line number Diff line change
Expand Up @@ -238,10 +238,11 @@ Traits
.. autoclass:: ToolbarButton
:show-inheritance:

.. autoclass:: Either
.. autoclass:: Union
Copy link
Member Author

Choose a reason for hiding this comment

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

Deliberately swapping the order so that Union appears before Either in the docs.

There's also a drive-by fix here: :show-inheritance: was missing for Union, for no reason that I can discern.

:show-inheritance:

.. autoclass:: Union
.. autoclass:: Either
:show-inheritance:

.. autoclass:: Symbol
:show-inheritance:
Expand Down
114 changes: 61 additions & 53 deletions docs/source/traits_user_manual/defining.rst
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ pass it as an argument to the trait::

account_balance = Float(10.0)

Most predefined traits are callable, [2]_ and can accept a default value and
Most predefined traits are callable [1]_, and can accept a default value and
possibly other arguments; all that are callable can also accept metadata as
keyword arguments. (See :ref:`other-predefined-traits` for information on trait
signatures, and see :ref:`trait-metadata` for information on metadata
Expand Down Expand Up @@ -269,7 +269,7 @@ the table.
.. index:: PythonValue(), Range(), ReadOnly(), Regex()
.. index:: Set() String(), This, Time()
.. index:: ToolbarButton(), true, Tuple(), Type()
.. index:: undefined, UUID(), ValidatedTuple(), WeakRef()
.. index:: undefined, Union(), UUID(), ValidatedTuple(), WeakRef()
Copy link
Member Author

Choose a reason for hiding this comment

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

Another drive-by fix: Union was missing from this list of index entries.


.. _predefined-traits-beyond-simple-types-table:

Expand Down Expand Up @@ -321,7 +321,7 @@ the table.
+------------------+----------------------------------------------------------+
| Disallow | n/a |
+------------------+----------------------------------------------------------+
| Either | Either( *val1*\ [, *val2*, ..., *valN*, |
| Either [2]_ | Either( *val1*\ [, *val2*, ..., *valN*, |
| | \*\*\ *metadata*] ) |
+------------------+----------------------------------------------------------+
| Enum | Enum( *values*\ [, \*\*\ *metadata*] ) |
Expand Down Expand Up @@ -408,7 +408,7 @@ the table.
| Union | Union( *val1*\ [, *val2*, ..., *valN*, |
| | \*\*\ *metadata*] ) |
+------------------+----------------------------------------------------------+
| UUID [4]_ | UUID( [\*\*\ *metadata*] ) |
| UUID | UUID( [\*\*\ *metadata*] ) |
Copy link
Member Author

Choose a reason for hiding this comment

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

Dropped the [4] footnote, which talked about Python 2.5; I've re-used it for Either.

Copy link
Contributor

Choose a reason for hiding this comment

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

Nitpick: footnote 4 comes before footnote 3 (attached to Function and Method) in the text.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks; fixed. At the same time, I fixed that the current footnotes start from the third natural number, rather than the second as is more conventional.

+------------------+----------------------------------------------------------+
| ValidatedTuple | ValidatedTuple( [\*\ *traits*, *fvalidate* = None, |
| | *fvalidate_info* = '' , \*\*\ *metadata*] ) |
Expand Down Expand Up @@ -638,56 +638,12 @@ prefix. Instantiating the class produces the following::
>>> print(bob.married)
no

.. index:: Either trait

.. _either:

Either
::::::
Another predefined trait that merits special explanation is Either. The
Either trait is intended for attributes that may take a value of more than
a single trait type, including None. The default value of Either is None, even
if None is not one of the types the user explicitly defines in the constructor,
but a different default value can be provided using the ``default`` argument.

.. index::
pair: Either trait; examples

The following is an example of using Either::

# either.py --- Example of Either predefined trait

from traits.api import HasTraits, Either, Str

class Employee(HasTraits):
manager_name = Either(Str, None)

This example defines an Employee class, which has a **manager_name** trait
attribute, which accepts either an Str instance or None as its value, and
will raise a TraitError if a value of any other type is assigned. For example::

>>> from traits.api import HasTraits, Either, Str
>>> class Employee(HasTraits):
... manager_name = Either(Str, None)
...
>>> steven = Employee(manager_name="Jenni")
>>> # Here steven's manager is named "Jenni"
>>> steven.manager_name
'Jenni'
>>> eric = Employee(manager_name=None)
>>> # Eric is the boss, so he has no manager.
>>> eric.manager_name is None
True
>>> # Assigning a value that is neither a string nor None will fail.
>>> steven.manager_name = 5
traits.trait_errors.TraitError: The 'manager_name' trait of an Employee instance must be a string or None, but a value of 5 <type 'int'> was specified.

.. index:: Union trait

.. _union:

Union
::::::
:::::
mdickinson marked this conversation as resolved.
Show resolved Hide resolved
The Union trait accepts a value that is considered valid by at least one
of the traits in its definition. It is a simpler and therefore less error-prone
alternative to the `Either` trait, which allows more complex constructs and
Expand Down Expand Up @@ -719,7 +675,7 @@ attribute, which accepts either an Str instance or None as its value, a
**salary** trait that accepts an instance of Salary or Float and will raise a
TraitError if a value of any other type is assigned. For example::

>>> from traits.api import HasTraits, Either, Str
>>> from traits.api import HasTraits, Str, Union
>>> class Employee(HasTraits):
... manager_name = Union(Str, None)
...
Expand All @@ -731,7 +687,7 @@ TraitError if a value of any other type is assigned. For example::

The following example illustrates the difference between `Either` and `Union`::

>>> from traits.api import HasTraits, Either, Union, Str
>>> from traits.api import Either, HasTraits, Str, Union
>>> class IntegerClass(HasTraits):
... primes = Either([2], None, {'3':6}, 5, 7, 11)
...
Expand All @@ -744,6 +700,56 @@ The following example illustrates the difference between `Either` and `Union`::
... primes = Union([2], None, {'3':6}, 5, 7, 11)
ValueError: Union trait declaration expects a trait type or an instance of trait type or None, but got [2] instead

.. index:: Either trait

.. _either:

Either
::::::

.. note::
The :class:`~.Either` trait type may eventually be deprecated, and should
not be used in new code. Use the more well-behaved :class:`~.Union` trait
type instead.

Another predefined trait that merits special explanation is Either. The
Either trait is intended for attributes that may take a value of more than
a single trait type, including None. The default value of Either is None, even
if None is not one of the types the user explicitly defines in the constructor,
but a different default value can be provided using the ``default`` argument.

.. index::
pair: Either trait; examples

The following is an example of using Either::

# either.py --- Example of Either predefined trait

from traits.api import HasTraits, Either, Str

class Employee(HasTraits):
manager_name = Either(Str, None)

This example defines an Employee class, which has a **manager_name** trait
attribute, which accepts either an Str instance or None as its value, and
will raise a TraitError if a value of any other type is assigned. For example::

>>> from traits.api import HasTraits, Either, Str
>>> class Employee(HasTraits):
... manager_name = Either(Str, None)
...
>>> steven = Employee(manager_name="Jenni")
>>> # Here steven's manager is named "Jenni"
>>> steven.manager_name
'Jenni'
>>> eric = Employee(manager_name=None)
>>> # Eric is the boss, so he has no manager.
>>> eric.manager_name is None
True
>>> # Assigning a value that is neither a string nor None will fail.
>>> steven.manager_name = 5
traits.trait_errors.TraitError: The 'manager_name' trait of an Employee instance must be a string or None, but a value of 5 <type 'int'> was specified.


.. _migration_either_to_union:

Expand Down Expand Up @@ -1041,14 +1047,16 @@ the metadata attribute::
print(t.trait( 'any' ).is_trait_type( Str )) # False

.. rubric:: Footnotes
.. [2] Most callable predefined traits are classes, but a few are functions.
.. [1] Most callable predefined traits are classes, but a few are functions.
The distinction does not make a difference unless you are trying to
extend an existing predefined trait. See the *Traits API Reference* for
details on particular traits, and see Chapter 5 for details on extending
existing traits.
.. [2] The :class:`~.Either` trait type is likely to be deprecated at some
point in the future. The :class:`~.Union` trait type should be preferred
to :class:`~.Either` in new code.
.. [3] The Function and Method trait types are now deprecated. See |Function|,
|Method|
.. [4] Available in Python 2.5.

..
external urls
Expand Down
2 changes: 1 addition & 1 deletion traits/trait_numeric.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ class ArrayOrNone(CArray):
""" A coercing trait whose value may be either a NumPy array or None.

This trait is designed to avoid the comparison issues with numpy arrays
that can arise from the use of constructs like Either(None, Array).
that can arise from the use of constructs like Union(None, Array).

The default value is None.
"""
Expand Down
7 changes: 7 additions & 0 deletions traits/trait_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -4124,6 +4124,13 @@ def __init__(
class Either(TraitType):
""" A trait type whose value can be any of of a specified list of traits.
.. note::
This class has some unusual corner-case behaviours and is not
recommended for use in new code. It may eventually be deprecated and
removed. For new code, consider using the :class:`~.Union` trait type
instead.
Parameters
----------
*traits
Expand Down