Skip to content

Commit

Permalink
Merge pull request #51 from jasongrout/instances
Browse files Browse the repository at this point in the history
Take some of the magic out of traits by insisting traits be instances, not classes
  • Loading branch information
minrk committed Jul 23, 2015
2 parents 5f0a9a4 + cba92f8 commit 684b5fa
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 25 deletions.
2 changes: 1 addition & 1 deletion traitlets/config/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ def _flags_changed(self, name, old, new):
subapp = Instance('traitlets.config.application.Application', allow_none=True)

# extra command-line arguments that don't set config values
extra_args = List(Unicode)
extra_args = List(Unicode())


def __init__(self, **kwargs):
Expand Down
78 changes: 55 additions & 23 deletions traitlets/tests/test_traitlets.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import re
import sys
from unittest import TestCase
import warnings

import nose.tools as nt
from nose import SkipTest
Expand Down Expand Up @@ -140,7 +141,7 @@ def test_metaclass(self):
self.assertEqual(type(HasTraits), MetaHasTraits)

class A(HasTraits):
a = Int
a = Int()

a = A()
self.assertEqual(type(a.__class__), MetaHasTraits)
Expand Down Expand Up @@ -191,8 +192,8 @@ def notify2(self, name, old, new):
def test_notify_all(self):

class A(HasTraits):
a = Int
b = Float
a = Int()
b = Float()

a = A()
a.on_trait_change(self.notify1)
Expand All @@ -215,8 +216,8 @@ class A(HasTraits):
def test_notify_one(self):

class A(HasTraits):
a = Int
b = Float
a = Int()
b = Float()

a = A()
a.on_trait_change(self.notify1, 'a')
Expand All @@ -229,10 +230,10 @@ class A(HasTraits):
def test_subclass(self):

class A(HasTraits):
a = Int
a = Int()

class B(A):
b = Float
b = Float()

b = B()
self.assertEqual(b.a,0)
Expand All @@ -245,10 +246,10 @@ class B(A):
def test_notify_subclass(self):

class A(HasTraits):
a = Int
a = Int()

class B(A):
b = Float
b = Float()

b = B()
b.on_trait_change(self.notify1, 'a')
Expand All @@ -265,7 +266,7 @@ class B(A):
def test_static_notify(self):

class A(HasTraits):
a = Int
a = Int()
_notify1 = []
def _a_changed(self, name, old, new):
self._notify1.append((name, old, new))
Expand All @@ -278,7 +279,7 @@ def _a_changed(self, name, old, new):
self.assertTrue(('a',0,10) in a._notify1)

class B(A):
b = Float
b = Float()
_notify2 = []
def _b_changed(self, name, old, new):
self._notify2.append((name, old, new))
Expand All @@ -303,7 +304,7 @@ def callback4(name, old, new, obj):
self.cb = (name, old, new, obj)

class A(HasTraits):
a = Int
a = Int()

a = A()
a.on_trait_change(callback0, 'a')
Expand Down Expand Up @@ -376,8 +377,8 @@ class TestHasTraits(TestCase):

def test_trait_names(self):
class A(HasTraits):
i = Int
f = Float
i = Int()
f = Float()
a = A()
self.assertEqual(sorted(a.trait_names()),['f','i'])
self.assertEqual(sorted(A.class_trait_names()),['f','i'])
Expand All @@ -399,8 +400,8 @@ class A(HasTraits):

def test_traits(self):
class A(HasTraits):
i = Int
f = Float
i = Int()
f = Float()
a = A()
self.assertEqual(a.traits(), dict(i=A.i, f=A.f))
self.assertEqual(A.class_traits(), dict(i=A.i, f=A.f))
Expand Down Expand Up @@ -665,7 +666,7 @@ class TestThis(TestCase):

def test_this_class(self):
class Foo(HasTraits):
this = This
this = This()

f = Foo()
self.assertEqual(f.this, None)
Expand Down Expand Up @@ -771,7 +772,7 @@ def tearDown(self):

class AnyTrait(HasTraits):

value = Any
value = Any()

class AnyTraitTest(TraitTestBase):

Expand Down Expand Up @@ -974,7 +975,7 @@ class TestTCPAddress(TraitTestBase):

class ListTrait(HasTraits):

value = List(Int)
value = List(Int())

class TestList(TraitTestBase):

Expand Down Expand Up @@ -1036,7 +1037,7 @@ class TestUnionListTrait(HasTraits):

class LenListTrait(HasTraits):

value = List(Int, [0], minlen=1, maxlen=2)
value = List(Int(), [0], minlen=1, maxlen=2)

class TestLenList(TraitTestBase):

Expand Down Expand Up @@ -1071,7 +1072,7 @@ def coerce(self, value):
def test_invalid_args(self):
self.assertRaises(TypeError, Tuple, 5)
self.assertRaises(TypeError, Tuple, default_value='hello')
t = Tuple(Int, CBytes, default_value=(1,5))
t = Tuple(Int(), CBytes(), default_value=(1,5))

class LooseTupleTrait(HasTraits):

Expand All @@ -1093,12 +1094,12 @@ def coerce(self, value):
def test_invalid_args(self):
self.assertRaises(TypeError, Tuple, 5)
self.assertRaises(TypeError, Tuple, default_value='hello')
t = Tuple(Int, CBytes, default_value=(1,5))
t = Tuple(Int(), CBytes(), default_value=(1,5))


class MultiTupleTrait(HasTraits):

value = Tuple(Int, Bytes, default_value=[99,b'bottles'])
value = Tuple(Int(), Bytes(), default_value=[99,b'bottles'])

class TestMultiTuple(TraitTestBase):

Expand Down Expand Up @@ -1675,3 +1676,34 @@ class C(HasTraits):

c = C(t='b')
assert c.t == 'b'


class TestTraitType(TestCase):

def test_trait_types_deprecated(self):
with warnings.catch_warnings():
warnings.filterwarnings('error')
with self.assertRaises(DeprecationWarning):
class C(HasTraits):
t = Int

def test_trait_types_list_deprecated(self):
with warnings.catch_warnings():
warnings.filterwarnings('error')
with self.assertRaises(DeprecationWarning):
class C(HasTraits):
t = List(Int)

def test_trait_types_tuple_deprecated(self):
with warnings.catch_warnings():
warnings.filterwarnings('error')
with self.assertRaises(DeprecationWarning):
class C(HasTraits):
t = Tuple(Int)

def test_trait_types_dict_deprecated(self):
with warnings.catch_warnings():
warnings.filterwarnings('error')
with self.assertRaises(DeprecationWarning):
class C(HasTraits):
t = Dict(Int)
9 changes: 8 additions & 1 deletion traitlets/traitlets.py
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,7 @@ def __new__(mcls, name, bases, classdict):
v.name = k
elif inspect.isclass(v):
if issubclass(v, TraitType):
warn("Traits should be given as instances, not types (for example, `Int()`, not `Int`)", DeprecationWarning, stacklevel=2)
vinst = v()
vinst.name = k
classdict[k] = vinst
Expand Down Expand Up @@ -1524,6 +1525,8 @@ def __init__(self, trait=None, default_value=None, **metadata):
raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))

if is_trait(trait):
if isinstance(trait, type):
warn("Traits should be given as instances, not types (for example, `Int()`, not `Int`)", DeprecationWarning)
self._trait = trait() if isinstance(trait, type) else trait
elif trait is not None:
raise TypeError("`trait` must be a Trait or None, got %s" % repr_type(trait))
Expand Down Expand Up @@ -1676,7 +1679,7 @@ def __init__(self, *traits, **metadata):
Create a fixed-type tuple with Traits:
``t = Tuple(Int, Str, CStr)``
``t = Tuple(Int(), Str(), CStr())``
would be length 3, with Int,Str,CStr for each element.
Expand Down Expand Up @@ -1717,6 +1720,8 @@ def __init__(self, *traits, **metadata):

self._traits = []
for trait in traits:
if isinstance(trait, type):
warn("Traits should be given as instances, not types (for example, `Int()`, not `Int`)", DeprecationWarning, stacklevel=2)
t = trait() if isinstance(trait, type) else trait
self._traits.append(t)

Expand Down Expand Up @@ -1796,6 +1801,8 @@ def __init__(self, trait=None, traits=None, default_value=Undefined,

# Case where a type of TraitType is provided rather than an instance
if is_trait(trait):
if isinstance(trait, type):
warn("Traits should be given as instances, not types (for example, `Int()`, not `Int`)", DeprecationWarning, stacklevel=2)
self._trait = trait() if isinstance(trait, type) else trait
elif trait is not None:
raise TypeError("`trait` must be a Trait or None, got %s" % repr_type(trait))
Expand Down

0 comments on commit 684b5fa

Please sign in to comment.