Skip to content

Commit

Permalink
Add validation of list element types
Browse files Browse the repository at this point in the history
The current approach to lists of objects would just validate that a list
is being passed in. This change checks each element of the property value
against a list of types and tightens up the type checking.
  • Loading branch information
markpeek committed Mar 24, 2013
1 parent ac36bfc commit be6a78d
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 6 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ Currently supported AWS resource types:

Todo:
- Add additional validity checks
- Don't allow AWS helper override for list typed objects
- Add missing AWS resource types:
- AWS::CloudFormation::Authentication
- AWS::CloudFormation::CustomResource
Expand Down
49 changes: 48 additions & 1 deletion tests/test_basic.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import unittest
from troposphere import Template
from troposphere import AWSObject, Template
from troposphere.ec2 import Instance


Expand All @@ -23,5 +23,52 @@ def test_goodrequired(self):
Instance('ec2instance', ImageId="ami-xxxx", InstanceType="m1.small")


def call_correct(x):
return x


def call_incorrect(x):
raise ValueError


class FakeAWSObject(AWSObject):
props = {
'callcorrect': (call_correct, False),
'callincorrect': (call_incorrect, False),
'singlelist': (list, False),
'multilist': ([bool, int, float], False),
}

def __init__(self, name, **kwargs):
self.type = "Fake::AWS::Object"
sup = super(FakeAWSObject, self)
sup.__init__(name, self.type, "Properties", self.props, **kwargs)

class TestValidators(unittest.TestCase):

def test_callcorrect(self):
FakeAWSObject('fake', callcorrect=True)

def test_callincorrect(self):
with self.assertRaises(ValueError):
FakeAWSObject('fake', callincorrect=True)

def test_list(self):
FakeAWSObject('fake', singlelist=['a', 1])

def test_badlist(self):
with self.assertRaises(TypeError):
FakeAWSObject('fake', singlelist=True)

def test_multilist(self):
FakeAWSObject('fake', multilist=[1, True, 2, 0.3])

def test_badmultilist(self):
with self.assertRaises(TypeError):
FakeAWSObject('fake', multilist=True)
with self.assertRaises(TypeError):
FakeAWSObject('fake', multilist=[1, 'a'])


if __name__ == '__main__':
unittest.main()
30 changes: 26 additions & 4 deletions troposphere/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,21 +65,43 @@ def __setattr__(self, name, value):
return dict.__setattr__(self, name, value)
elif name in self.propnames:
# Check the type of the object and compare against what we were
# expecting. If it is a function, call it and assume the value
# will be checked there. Special case AWS helper functions.
# expecting.
expected_type = self.props[name][0]

# If it's a function, call it...
if isinstance(expected_type, types.FunctionType):
value = expected_type(value)
return self.properties.__setitem__(name, value)

# If it's a list of types, check against those types...
elif isinstance(expected_type, list):
# If we're expecting a list, then make sure it is a list
if not isinstance(value, list):
self._raise_type(name, value, expected_type)

# Iterate over the list and make sure it matches our
# type checks
for v in value:
if not isinstance(v, tuple(expected_type)):
self._raise_type(name, v, expected_type)
# Validated so assign it
return self.properties.__setitem__(name, value)

# Single type so check the type of the object and compare against
# what we were expecting. Special case AWS helper functions.
elif isinstance(value, expected_type) or \
isinstance(value, AWSHelperFn):
return self.properties.__setitem__(name, value)
else:
raise TypeError('%s is %s, expected %s' %
(name, type(value), expected_type))
self._raise_type(name, value, expected_type)

raise AttributeError("%s object does not support attribute %s" %
(self.type, name))

def _raise_type(self, name, value, expected_type):
raise TypeError('%s is %s, expected %s' %
(name, type(value), expected_type))

def JSONrepr(self):
for k, v in self.props.items():
if v[1] and k not in self.properties:
Expand Down

0 comments on commit be6a78d

Please sign in to comment.