Skip to content

Commit 9b6ba57

Browse files
committed
Document that Either should not be used in new code (#1699)
This PR adds notes to the user manual and API documentation to discourage users from using Either in new code. (Union should be preferred.) (cherry picked from commit c5cfdde)
1 parent 1f003bb commit 9b6ba57

File tree

4 files changed

+72
-56
lines changed

4 files changed

+72
-56
lines changed

docs/source/traits_api_reference/trait_types.rst

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,10 +238,11 @@ Traits
238238
.. autoclass:: ToolbarButton
239239
:show-inheritance:
240240

241-
.. autoclass:: Either
241+
.. autoclass:: Union
242242
:show-inheritance:
243243

244-
.. autoclass:: Union
244+
.. autoclass:: Either
245+
:show-inheritance:
245246

246247
.. autoclass:: Symbol
247248
:show-inheritance:

docs/source/traits_user_manual/defining.rst

Lines changed: 61 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ pass it as an argument to the trait::
130130

131131
account_balance = Float(10.0)
132132

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

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

@@ -321,7 +321,7 @@ the table.
321321
+------------------+----------------------------------------------------------+
322322
| Disallow | n/a |
323323
+------------------+----------------------------------------------------------+
324-
| Either | Either( *val1*\ [, *val2*, ..., *valN*, |
324+
| Either [2]_ | Either( *val1*\ [, *val2*, ..., *valN*, |
325325
| | \*\*\ *metadata*] ) |
326326
+------------------+----------------------------------------------------------+
327327
| Enum | Enum( *values*\ [, \*\*\ *metadata*] ) |
@@ -408,7 +408,7 @@ the table.
408408
| Union | Union( *val1*\ [, *val2*, ..., *valN*, |
409409
| | \*\*\ *metadata*] ) |
410410
+------------------+----------------------------------------------------------+
411-
| UUID [4]_ | UUID( [\*\*\ *metadata*] ) |
411+
| UUID | UUID( [\*\*\ *metadata*] ) |
412412
+------------------+----------------------------------------------------------+
413413
| ValidatedTuple | ValidatedTuple( [\*\ *traits*, *fvalidate* = None, |
414414
| | *fvalidate_info* = '' , \*\*\ *metadata*] ) |
@@ -638,56 +638,12 @@ prefix. Instantiating the class produces the following::
638638
>>> print(bob.married)
639639
no
640640

641-
.. index:: Either trait
642-
643-
.. _either:
644-
645-
Either
646-
::::::
647-
Another predefined trait that merits special explanation is Either. The
648-
Either trait is intended for attributes that may take a value of more than
649-
a single trait type, including None. The default value of Either is None, even
650-
if None is not one of the types the user explicitly defines in the constructor,
651-
but a different default value can be provided using the ``default`` argument.
652-
653-
.. index::
654-
pair: Either trait; examples
655-
656-
The following is an example of using Either::
657-
658-
# either.py --- Example of Either predefined trait
659-
660-
from traits.api import HasTraits, Either, Str
661-
662-
class Employee(HasTraits):
663-
manager_name = Either(Str, None)
664-
665-
This example defines an Employee class, which has a **manager_name** trait
666-
attribute, which accepts either an Str instance or None as its value, and
667-
will raise a TraitError if a value of any other type is assigned. For example::
668-
669-
>>> from traits.api import HasTraits, Either, Str
670-
>>> class Employee(HasTraits):
671-
... manager_name = Either(Str, None)
672-
...
673-
>>> steven = Employee(manager_name="Jenni")
674-
>>> # Here steven's manager is named "Jenni"
675-
>>> steven.manager_name
676-
'Jenni'
677-
>>> eric = Employee(manager_name=None)
678-
>>> # Eric is the boss, so he has no manager.
679-
>>> eric.manager_name is None
680-
True
681-
>>> # Assigning a value that is neither a string nor None will fail.
682-
>>> steven.manager_name = 5
683-
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.
684-
685641
.. index:: Union trait
686642

687643
.. _union:
688644

689645
Union
690-
::::::
646+
:::::
691647
The Union trait accepts a value that is considered valid by at least one
692648
of the traits in its definition. It is a simpler and therefore less error-prone
693649
alternative to the `Either` trait, which allows more complex constructs and
@@ -719,7 +675,7 @@ attribute, which accepts either an Str instance or None as its value, a
719675
**salary** trait that accepts an instance of Salary or Float and will raise a
720676
TraitError if a value of any other type is assigned. For example::
721677

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

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

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

703+
.. index:: Either trait
704+
705+
.. _either:
706+
707+
Either
708+
::::::
709+
710+
.. note::
711+
The :class:`~.Either` trait type may eventually be deprecated, and should
712+
not be used in new code. Use the more well-behaved :class:`~.Union` trait
713+
type instead.
714+
715+
Another predefined trait that merits special explanation is Either. The
716+
Either trait is intended for attributes that may take a value of more than
717+
a single trait type, including None. The default value of Either is None, even
718+
if None is not one of the types the user explicitly defines in the constructor,
719+
but a different default value can be provided using the ``default`` argument.
720+
721+
.. index::
722+
pair: Either trait; examples
723+
724+
The following is an example of using Either::
725+
726+
# either.py --- Example of Either predefined trait
727+
728+
from traits.api import HasTraits, Either, Str
729+
730+
class Employee(HasTraits):
731+
manager_name = Either(Str, None)
732+
733+
This example defines an Employee class, which has a **manager_name** trait
734+
attribute, which accepts either an Str instance or None as its value, and
735+
will raise a TraitError if a value of any other type is assigned. For example::
736+
737+
>>> from traits.api import HasTraits, Either, Str
738+
>>> class Employee(HasTraits):
739+
... manager_name = Either(Str, None)
740+
...
741+
>>> steven = Employee(manager_name="Jenni")
742+
>>> # Here steven's manager is named "Jenni"
743+
>>> steven.manager_name
744+
'Jenni'
745+
>>> eric = Employee(manager_name=None)
746+
>>> # Eric is the boss, so he has no manager.
747+
>>> eric.manager_name is None
748+
True
749+
>>> # Assigning a value that is neither a string nor None will fail.
750+
>>> steven.manager_name = 5
751+
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.
752+
747753

748754
.. _migration_either_to_union:
749755

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

10431049
.. rubric:: Footnotes
1044-
.. [2] Most callable predefined traits are classes, but a few are functions.
1050+
.. [1] Most callable predefined traits are classes, but a few are functions.
10451051
The distinction does not make a difference unless you are trying to
10461052
extend an existing predefined trait. See the *Traits API Reference* for
10471053
details on particular traits, and see Chapter 5 for details on extending
10481054
existing traits.
1055+
.. [2] The :class:`~.Either` trait type is likely to be deprecated at some
1056+
point in the future. The :class:`~.Union` trait type should be preferred
1057+
to :class:`~.Either` in new code.
10491058
.. [3] The Function and Method trait types are now deprecated. See |Function|,
10501059
|Method|
1051-
.. [4] Available in Python 2.5.
10521060
10531061
..
10541062
external urls

traits/trait_numeric.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ class ArrayOrNone(CArray):
416416
""" A coercing trait whose value may be either a NumPy array or None.
417417
418418
This trait is designed to avoid the comparison issues with numpy arrays
419-
that can arise from the use of constructs like Either(None, Array).
419+
that can arise from the use of constructs like Union(None, Array).
420420
421421
The default value is None.
422422
"""

traits/trait_types.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4124,6 +4124,13 @@ def __init__(
41244124
class Either(TraitType):
41254125
""" A trait type whose value can be any of of a specified list of traits.
41264126
4127+
.. note::
4128+
4129+
This class has some unusual corner-case behaviours and is not
4130+
recommended for use in new code. It may eventually be deprecated and
4131+
removed. For new code, consider using the :class:`~.Union` trait type
4132+
instead.
4133+
41274134
Parameters
41284135
----------
41294136
*traits

0 commit comments

Comments
 (0)