Skip to content

Commit

Permalink
Merge c8ebd65 into d0b9472
Browse files Browse the repository at this point in the history
  • Loading branch information
jjenthought committed Dec 11, 2018
2 parents d0b9472 + c8ebd65 commit a87aa9c
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 4 deletions.
2 changes: 2 additions & 0 deletions docs/source/traits_api_reference/has_traits.rst
Expand Up @@ -37,6 +37,8 @@ Classes

.. autoclass:: HasStrictTraits

.. autoclass:: HasRequiredTraits

.. autoclass:: HasPrivateTraits

.. autoclass:: SingletonHasTraits
Expand Down
32 changes: 32 additions & 0 deletions docs/source/traits_user_manual/advanced.rst
Expand Up @@ -301,6 +301,38 @@ exception, as does attempting to set an attribute that is not one of the three
defined attributes. In essence, TreeNode behaves like a type-checked data
structure.

.. index:: HasRequiredTraits class

.. _hasrequiredtraits:

HasRequiredTraits
'''''''''''''''''

This class builds on the functionality of HasStrictTraits and ensures
that any object attribute with `required=True` in its metadata must be passed
as an argument on object initialization.

An example of a class with required traits::

class RequiredTest(HasRequiredTraits):
required_trait = Any(required=True)
non_required_trait = Any()

All required traits have to be provided as arguments on creating a new
instance::

>>> new_instance = RequiredTest(required_trait=13.0)

Non-required traits can also still be provided as usual::

>>> new_instance = RequiredTest(required_trait=13.0, non_required_trait=14.0)

However, omitting a required trait will raise a TraitError::

>>> new_instance = RequiredTest(non_required_trait=14.0)
traits.trait_errors.TraitError: The following required traits were not
provided: required_trait.

.. index:: HasPrivateTraits class

.. _hasprivatetraits:
Expand Down
8 changes: 4 additions & 4 deletions traits/api.py
Expand Up @@ -65,10 +65,10 @@
from .trait_types import UUID, ValidatedTuple

from .has_traits import (HasTraits, HasStrictTraits, HasPrivateTraits,
Interface, SingletonHasTraits, SingletonHasStrictTraits,
SingletonHasPrivateTraits, MetaHasTraits, Vetoable, VetoableEvent,
implements, traits_super, on_trait_change, cached_property,
property_depends_on, provides, isinterface)
HasRequiredTraits, Interface, SingletonHasTraits,
SingletonHasStrictTraits, SingletonHasPrivateTraits, MetaHasTraits,
Vetoable, VetoableEvent, implements, traits_super, on_trait_change,
cached_property, property_depends_on, provides, isinterface)

try:
from .has_traits import ABCHasTraits, ABCHasStrictTraits, ABCMetaHasTraits
Expand Down
48 changes: 48 additions & 0 deletions traits/has_traits.py
Expand Up @@ -3455,6 +3455,54 @@ class HasStrictTraits ( HasTraits ):
"""
_ = Disallow # Disallow access to any traits not explicitly defined

#-------------------------------------------------------------------------------
# 'HasRequiredTraits' class:
#-------------------------------------------------------------------------------

class HasRequiredTraits(HasStrictTraits):
""" This class builds on the functionality of HasStrictTraits and ensures
that any object attribute with `required=True` in its metadata must be
passed as an argument on object initialization.
This can be useful in cases where an object has traits which are required
for it to function correctly.
Raises
------
TraitError
If a required trait is not passed as an argument
Examples
--------
A class with required traits:
>>> class RequiredTest(HasRequiredTraits):
>>> required_trait = Any(required=True)
>>> non_required_trait = Any()
Making an instance of a HasRequiredTraits class:
>>> test_instance = RequiredTest(required_trait=13, non_required_trait=11)
>>> test_instance2 = RequiredTest(required_trait=13)
Forgetting to specify a required trait:
>>> test_instance = RequiredTest(non_required_trait=11)
traits.trait_errors.TraitError: The following required traits were not
provided: required_trait.
"""

def __init__(self, **traits):

missing_required_traits = [
name for name in self.trait_names(required=True)
if name not in traits
]
if missing_required_traits:
raise TraitError(
"The following required traits were not provided: "
"{}.".format(', '.join(sorted(missing_required_traits)))
)

super(HasRequiredTraits, self).__init__(**traits)

#-------------------------------------------------------------------------------
# 'HasPrivateTraits' class:
#-------------------------------------------------------------------------------
Expand Down
30 changes: 30 additions & 0 deletions traits/tests/test_has_required_traits.py
@@ -0,0 +1,30 @@
import unittest
from traits.api import Int, Float, String, HasRequiredTraits, TraitError

class TestHasRequiredTraits(unittest.TestCase):

def test_trait_value_assignment(self):
test_instance = RequiredTest(
i_trait=4, f_trait=2.2, s_trait="test")
self.assertEqual(test_instance.i_trait, 4)
self.assertEqual(test_instance.f_trait, 2.2)
self.assertEqual(test_instance.s_trait, "test")
self.assertEqual(test_instance.non_req_trait, 4.4)
self.assertEqual(test_instance.normal_trait, 42.0)


def test_missing_required_trait(self):
with self.assertRaises(TraitError) as exc:
test_instance = RequiredTest(i_trait=3)
self.assertEqual(
exc.exception.args[0], "The following required traits were not "
"provided: f_trait, s_trait."
)


class RequiredTest(HasRequiredTraits):
i_trait = Int(required=True)
f_trait = Float(required=True)
s_trait = String(required=True)
non_req_trait = Float(4.4, required=False)
normal_trait = Float(42.0)

0 comments on commit a87aa9c

Please sign in to comment.