From 5541f4390333aae07879d9996c72f2095eda19a3 Mon Sep 17 00:00:00 2001 From: liu-sheng Date: Tue, 1 Jul 2014 10:58:22 +0800 Subject: [PATCH] Use None instead of mutables in method params defaults Mutables in the method params defaults might cause errors and that's why it's anti-pattern in most of the cases and should be removed. Change-Id: I8bc284f12ce72082a0482410ec2c20c2fc087a4b Closes-Bug: #1327473 --- contrib/extraroute/extraroute/tests/test_extraroute.py | 3 ++- .../rackspace/rackspace/tests/test_cloud_loadbalancer.py | 2 +- heat/api/aws/utils.py | 4 +++- heat/api/cloudwatch/watch.py | 3 ++- heat/api/openstack/v1/actions.py | 4 ++-- heat/api/openstack/v1/resources.py | 3 ++- heat/engine/constraints.py | 4 ++-- heat/engine/dependencies.py | 3 ++- heat/engine/parameters.py | 6 ++++-- heat/engine/properties.py | 2 +- heat/engine/resource.py | 3 ++- heat/engine/resources/autoscaling.py | 3 ++- heat/engine/rsrc_defn.py | 4 ++-- heat/engine/watchrule.py | 4 ++-- heat/rpc/client.py | 8 ++++++-- heat/tests/test_api_cfn_v1.py | 3 ++- heat/tests/test_api_cloudwatch.py | 3 ++- heat/tests/test_api_ec2token.py | 9 +++++++-- heat/tests/test_api_openstack_v1.py | 4 +++- heat/tests/test_autoscaling.py | 6 ++++-- heat/tests/test_engine_service.py | 8 ++++++-- heat/tests/test_heatclient.py | 3 ++- heat/tests/test_metadata_refresh.py | 3 ++- heat/tests/test_multi_part.py | 3 ++- heat/tests/test_neutron.py | 7 +++++-- heat/tests/test_neutron_security_group.py | 3 ++- heat/tests/test_parameters.py | 5 +++-- heat/tests/test_security_group.py | 3 ++- heat/tests/test_waitcondition.py | 3 ++- heat/tests/utils.py | 6 ++++-- 30 files changed, 83 insertions(+), 42 deletions(-) diff --git a/contrib/extraroute/extraroute/tests/test_extraroute.py b/contrib/extraroute/extraroute/tests/test_extraroute.py index 545f270f2f..907e271dde 100644 --- a/contrib/extraroute/extraroute/tests/test_extraroute.py +++ b/contrib/extraroute/extraroute/tests/test_extraroute.py @@ -68,7 +68,8 @@ class NeutronExtraRouteTest(HeatTestCase): resource._register_class("OS::Neutron::ExtraRoute", extraroute.ExtraRoute) - def create_extraroute(self, t, stack, resource_name, properties={}): + def create_extraroute(self, t, stack, resource_name, properties=None): + properties = properties or {} t['Resources'][resource_name]['Properties'] = properties rsrc = extraroute.ExtraRoute( resource_name, diff --git a/contrib/rackspace/rackspace/tests/test_cloud_loadbalancer.py b/contrib/rackspace/rackspace/tests/test_cloud_loadbalancer.py index fe46592dcd..c251643366 100644 --- a/contrib/rackspace/rackspace/tests/test_cloud_loadbalancer.py +++ b/contrib/rackspace/rackspace/tests/test_cloud_loadbalancer.py @@ -51,7 +51,7 @@ class FakeManager(object): def find(self, *args, **kwargs): pass - def action(self, item, action_type, body={}): + def action(self, item, action_type, body=None): pass diff --git a/heat/api/aws/utils.py b/heat/api/aws/utils.py index 76ae3d3ff6..f5d6469d52 100644 --- a/heat/api/aws/utils.py +++ b/heat/api/aws/utils.py @@ -106,9 +106,11 @@ def get_param_value(params, key): raise exception.HeatMissingParameterError(key) -def reformat_dict_keys(keymap={}, inputdict={}): +def reformat_dict_keys(keymap=None, inputdict=None): ''' Utility function for mapping one dict format to another ''' + keymap = keymap or {} + inputdict = inputdict or {} return dict([(outk, inputdict[ink]) for ink, outk in keymap.items() if ink in inputdict]) diff --git a/heat/api/cloudwatch/watch.py b/heat/api/cloudwatch/watch.py index 8058285123..81bb4e60e3 100644 --- a/heat/api/cloudwatch/watch.py +++ b/heat/api/cloudwatch/watch.py @@ -183,13 +183,14 @@ class WatchController(object): """ self._enforce(req, 'ListMetrics') - def format_metric_data(d, fil={}): + def format_metric_data(d, fil=None): """ Reformat engine output into the AWS "Metric" format Takes an optional filter dict, which is traversed so a metric dict is only returned if all keys match the filter dict """ + fil = fil or {} dimensions = [ {'AlarmName': d[engine_api.WATCH_DATA_ALARM]}, {'Timestamp': d[engine_api.WATCH_DATA_TIME]} diff --git a/heat/api/openstack/v1/actions.py b/heat/api/openstack/v1/actions.py index 819edfe7bf..fb7f90840a 100644 --- a/heat/api/openstack/v1/actions.py +++ b/heat/api/openstack/v1/actions.py @@ -34,12 +34,12 @@ class ActionController(object): self.rpc_client = rpc_client.EngineClient() @util.identified_stack - def action(self, req, identity, body={}): + def action(self, req, identity, body=None): """ Performs a specified action on a stack, the body is expecting to contain exactly one item whose key specifies the action """ - + body = body or {} if len(body) < 1: raise exc.HTTPBadRequest(_("No action specified")) diff --git a/heat/api/openstack/v1/resources.py b/heat/api/openstack/v1/resources.py index d24f3f63ba..2266903bc7 100644 --- a/heat/api/openstack/v1/resources.py +++ b/heat/api/openstack/v1/resources.py @@ -21,7 +21,8 @@ from heat.rpc import api as engine_api from heat.rpc import client as rpc_client -def format_resource(req, res, keys=[]): +def format_resource(req, res, keys=None): + keys = keys or [] include_key = lambda k: k in keys if keys else True def transform(key, value): diff --git a/heat/engine/constraints.py b/heat/engine/constraints.py index d8315d34de..cc1f38eeb1 100644 --- a/heat/engine/constraints.py +++ b/heat/engine/constraints.py @@ -80,7 +80,7 @@ class Schema(collections.Mapping): def __init__(self, data_type, description=None, default=None, schema=None, - required=False, constraints=[], label=None): + required=False, constraints=None, label=None): self._len = None self.label = label self.type = data_type @@ -108,7 +108,7 @@ class Schema(collections.Mapping): utype=self.type) raise InvalidSchemaError(msg) - self.constraints = constraints + self.constraints = constraints or [] self.default = default def validate(self, context=None): diff --git a/heat/engine/dependencies.py b/heat/engine/dependencies.py index 05f7e3d78a..98fb483608 100644 --- a/heat/engine/dependencies.py +++ b/heat/engine/dependencies.py @@ -160,11 +160,12 @@ class Graph(collections.defaultdict): class Dependencies(object): '''Helper class for calculating a dependency graph.''' - def __init__(self, edges=[]): + def __init__(self, edges=None): ''' Initialise, optionally with a list of edges, in the form of (requirer, required) tuples. ''' + edges = edges or [] self._graph = Graph() for e in edges: self += e diff --git a/heat/engine/parameters.py b/heat/engine/parameters.py index 04568ad459..4c96a0ee55 100644 --- a/heat/engine/parameters.py +++ b/heat/engine/parameters.py @@ -54,7 +54,7 @@ class Schema(constr.Schema): ) def __init__(self, data_type, description=None, default=None, schema=None, - constraints=[], hidden=False, label=None): + constraints=None, hidden=False, label=None): super(Schema, self).__init__(data_type=data_type, description=description, default=default, @@ -403,11 +403,13 @@ class Parameters(collections.Mapping): 'AWS::StackId', 'AWS::StackName', 'AWS::Region' ) - def __init__(self, stack_identifier, tmpl, user_params={}): + def __init__(self, stack_identifier, tmpl, user_params=None): ''' Create the parameter container for a stack from the stack name and template, optionally setting the user-supplied parameter values. ''' + user_params = user_params or {} + def user_parameter(schema_item): name, schema = schema_item return Parameter(name, schema, diff --git a/heat/engine/properties.py b/heat/engine/properties.py index 0d4d5f354a..185d79900e 100644 --- a/heat/engine/properties.py +++ b/heat/engine/properties.py @@ -50,7 +50,7 @@ class Schema(constr.Schema): def __init__(self, data_type, description=None, default=None, schema=None, - required=False, constraints=[], + required=False, constraints=None, implemented=True, update_allowed=False, support_status=support.SupportStatus()): diff --git a/heat/engine/resource.py b/heat/engine/resource.py index 67f3e518b6..e2e6a48f9b 100644 --- a/heat/engine/resource.py +++ b/heat/engine/resource.py @@ -264,12 +264,13 @@ class Resource(object): return identifier.ResourceIdentifier(resource_name=self.name, **self.stack.identifier()) - def parsed_template(self, section=None, default={}): + def parsed_template(self, section=None, default=None): ''' Return the parsed template data for the resource. May be limited to only one section of the data, in which case a default value may also be supplied. ''' + default = default or {} if section is None: template = self.t else: diff --git a/heat/engine/resources/autoscaling.py b/heat/engine/resources/autoscaling.py index cf1748d255..e998a243d3 100644 --- a/heat/engine/resources/autoscaling.py +++ b/heat/engine/resources/autoscaling.py @@ -386,7 +386,7 @@ class InstanceGroup(stack_resource.StackResource): # nodes. self._lb_reload() - def _lb_reload(self, exclude=[]): + def _lb_reload(self, exclude=None): ''' Notify the LoadBalancer to reload its config to include the changes in instances we have just made. @@ -394,6 +394,7 @@ class InstanceGroup(stack_resource.StackResource): This must be done after activation (instance in ACTIVE state), otherwise the instances' IP addresses may not be available. ''' + exclude = exclude or [] if self.properties[self.LOAD_BALANCER_NAMES]: id_list = [inst.FnGetRefId() for inst in self.get_instances() if inst.FnGetRefId() not in exclude] diff --git a/heat/engine/rsrc_defn.py b/heat/engine/rsrc_defn.py index b1798b9e36..08fd75625e 100644 --- a/heat/engine/rsrc_defn.py +++ b/heat/engine/rsrc_defn.py @@ -36,7 +36,7 @@ class ResourceDefinitionCore(object): ) def __init__(self, name, resource_type, properties=None, metadata=None, - depends=[], deletion_policy=None, update_policy=None, + depends=None, deletion_policy=None, update_policy=None, description=''): """ Initialise with the parsed definition of a resource. @@ -53,7 +53,7 @@ class ResourceDefinitionCore(object): :param update_policy: A dictionary of supplied update policies :param description: A string describing the resource """ - + depends = depends or [] self.name = name self.resource_type = resource_type self.description = description diff --git a/heat/engine/watchrule.py b/heat/engine/watchrule.py index ffa55896cc..b28d40a673 100644 --- a/heat/engine/watchrule.py +++ b/heat/engine/watchrule.py @@ -48,7 +48,7 @@ class WatchRule(object): updated_at = timestamp.Timestamp(db_api.watch_rule_get, 'updated_at') def __init__(self, context, watch_name, rule, stack_id=None, - state=NODATA, wid=None, watch_data=[], + state=NODATA, wid=None, watch_data=None, last_evaluated=timeutils.utcnow()): self.context = context self.now = timeutils.utcnow() @@ -63,7 +63,7 @@ class WatchRule(object): period = int(rule['period']) self.timeperiod = datetime.timedelta(seconds=period) self.id = wid - self.watch_data = watch_data + self.watch_data = watch_data or [] self.last_evaluated = last_evaluated @classmethod diff --git a/heat/rpc/client.py b/heat/rpc/client.py index 6cf4b6e33a..8b36d7bce4 100644 --- a/heat/rpc/client.py +++ b/heat/rpc/client.py @@ -403,7 +403,10 @@ class EngineClient(object): config_id=config_id)) def create_software_config(self, cnxt, group, name, config, - inputs=[], outputs=[], options={}): + inputs=None, outputs=None, options=None): + inputs = inputs or [] + outputs = outputs or [] + options = options or {} return self.call(cnxt, self.make_msg('create_software_config', group=group, name=name, @@ -429,9 +432,10 @@ class EngineClient(object): deployment_id=deployment_id)) def create_software_deployment(self, cnxt, server_id, config_id=None, - input_values={}, action='INIT', + input_values=None, action='INIT', status='COMPLETE', status_reason='', stack_user_project_id=None): + input_values = input_values or {} return self.call(cnxt, self.make_msg( 'create_software_deployment', server_id=server_id, diff --git a/heat/tests/test_api_cfn_v1.py b/heat/tests/test_api_cfn_v1.py index 7cfa50c656..36b1bbd94b 100644 --- a/heat/tests/test_api_cfn_v1.py +++ b/heat/tests/test_api_cfn_v1.py @@ -61,8 +61,9 @@ class CfnStackControllerTest(HeatTestCase): 'deny_stack_user.json') self.addCleanup(self.m.VerifyAll) - def _dummy_GET_request(self, params={}): + def _dummy_GET_request(self, params=None): # Mangle the params dict into a query string + params = params or {} qs = "&".join(["=".join([k, str(params[k])]) for k in params]) environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': qs} req = Request(environ) diff --git a/heat/tests/test_api_cloudwatch.py b/heat/tests/test_api_cloudwatch.py index 98a864f658..9b7abe0720 100644 --- a/heat/tests/test_api_cloudwatch.py +++ b/heat/tests/test_api_cloudwatch.py @@ -31,8 +31,9 @@ class WatchControllerTest(HeatTestCase): the endpoint processing API requests after they are routed ''' - def _dummy_GET_request(self, params={}): + def _dummy_GET_request(self, params=None): # Mangle the params dict into a query string + params = params or {} qs = "&".join(["=".join([k, str(params[k])]) for k in params]) environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': qs} req = Request(environ) diff --git a/heat/tests/test_api_ec2token.py b/heat/tests/test_api_ec2token.py index d3bad982cc..70d02e9f20 100644 --- a/heat/tests/test_api_ec2token.py +++ b/heat/tests/test_api_ec2token.py @@ -33,8 +33,10 @@ class Ec2TokenTest(HeatTestCase): super(Ec2TokenTest, self).setUp() self.m.StubOutWithMock(requests, 'post') - def _dummy_GET_request(self, params={}, environ={}): + def _dummy_GET_request(self, params=None, environ=None): # Mangle the params dict into a query string + params = params or {} + environ = environ or {} qs = "&".join(["=".join([k, str(params[k])]) for k in params]) environ.update({'REQUEST_METHOD': 'GET', 'QUERY_STRING': qs}) req = Request(environ) @@ -179,9 +181,12 @@ class Ec2TokenTest(HeatTestCase): ec2 = ec2token.EC2Token(app='xyz', conf={}) self.assertEqual('xyz', ec2.__call__(dummy_req)) - def _stub_http_connection(self, headers={}, params={}, response=None, + def _stub_http_connection(self, headers=None, params=None, response=None, req_url='http://123:5000/v2.0/ec2tokens'): + headers = headers or {} + params = params or {} + class DummyHTTPResponse(object): text = response diff --git a/heat/tests/test_api_openstack_v1.py b/heat/tests/test_api_openstack_v1.py index c565f80d9e..83c5dbd59b 100644 --- a/heat/tests/test_api_openstack_v1.py +++ b/heat/tests/test_api_openstack_v1.py @@ -2612,7 +2612,9 @@ class EventControllerTest(ControllerTest, HeatTestCase): class RoutesTest(HeatTestCase): - def assertRoute(self, mapper, path, method, action, controller, params={}): + def assertRoute(self, mapper, path, method, action, controller, + params=None): + params = params or {} route = mapper.match(path, {'REQUEST_METHOD': method}) self.assertIsNotNone(route) self.assertEqual(action, route['action']) diff --git a/heat/tests/test_autoscaling.py b/heat/tests/test_autoscaling.py index 2e8555c7e4..312f27ac1e 100644 --- a/heat/tests/test_autoscaling.py +++ b/heat/tests/test_autoscaling.py @@ -185,7 +185,8 @@ class AutoScalingTest(HeatTestCase): instance.Instance.check_delete_complete( task).MultipleTimes().AndReturn(True) - def _stub_suspend(self, cookies=[], with_error=None): + def _stub_suspend(self, cookies=None, with_error=None): + cookies = cookies or [] self.m.StubOutWithMock(instance.Instance, 'handle_suspend') self.m.StubOutWithMock(instance.Instance, 'check_suspend_complete') if with_error: @@ -200,7 +201,8 @@ class AutoScalingTest(HeatTestCase): instance.Instance.check_suspend_complete( cookie).InAnyOrder().AndReturn(True) - def _stub_resume(self, cookies=[], with_error=None): + def _stub_resume(self, cookies=None, with_error=None): + cookies = cookies or [] self.m.StubOutWithMock(instance.Instance, 'handle_resume') self.m.StubOutWithMock(instance.Instance, 'check_resume_complete') if with_error: diff --git a/heat/tests/test_engine_service.py b/heat/tests/test_engine_service.py index 0fff307fd6..730b4227d8 100644 --- a/heat/tests/test_engine_service.py +++ b/heat/tests/test_engine_service.py @@ -2622,7 +2622,10 @@ class SoftwareConfigServiceTest(HeatTestCase): def _create_software_config( self, group='Heat::Shell', name='config_mysql', config=None, - inputs=[], outputs=[], options={}): + inputs=None, outputs=None, options=None): + inputs = inputs or [] + outputs = outputs or [] + options = options or {} return self.engine.create_software_config( self.ctx, group, name, config, inputs, outputs, options) @@ -2674,13 +2677,14 @@ class SoftwareConfigServiceTest(HeatTestCase): self.ctx, config_id) self.assertEqual(ex.exc_info[0], exception.NotFound) - def _create_software_deployment(self, config_id=None, input_values={}, + def _create_software_deployment(self, config_id=None, input_values=None, action='INIT', status='COMPLETE', status_reason='', config_group=None, server_id=str(uuid.uuid4()), config_name=None, stack_user_project_id=None): + input_values = input_values or {} if config_id is None: config = self._create_software_config(group=config_group, name=config_name) diff --git a/heat/tests/test_heatclient.py b/heat/tests/test_heatclient.py index 0ba1b4af91..204cc8f565 100644 --- a/heat/tests/test_heatclient.py +++ b/heat/tests/test_heatclient.py @@ -889,8 +889,9 @@ class KeystoneClientTest(HeatTestCase): user_id='duser123', project_id='aproject', credential_id='acredentialid') - def _stub_uuid(self, values=[]): + def _stub_uuid(self, values=None): # stub UUID.hex to return the values specified + values = values or [] self.m.StubOutWithMock(uuid, 'uuid4') for v in values: mock_uuid = self.m.CreateMockAnything() diff --git a/heat/tests/test_metadata_refresh.py b/heat/tests/test_metadata_refresh.py index 8eecefdb3d..d7bcde5c3f 100644 --- a/heat/tests/test_metadata_refresh.py +++ b/heat/tests/test_metadata_refresh.py @@ -136,7 +136,8 @@ class MetadataRefreshTest(HeatTestCase): # Note tests creating a stack should be decorated with @stack_delete_after # to ensure the stack is properly cleaned up - def create_stack(self, stack_name='test_stack', params={}): + def create_stack(self, stack_name='test_stack', params=None): + params = params or {} temp = template_format.parse(test_template_metadata) template = parser.Template(temp) ctx = utils.dummy_context() diff --git a/heat/tests/test_multi_part.py b/heat/tests/test_multi_part.py index 63f07807a2..4103cdcbfb 100644 --- a/heat/tests/test_multi_part.py +++ b/heat/tests/test_multi_part.py @@ -30,7 +30,8 @@ class MultipartMimeTest(HeatTestCase): self.ctx = utils.dummy_context() self.init_config() - def init_config(self, parts=[]): + def init_config(self, parts=None): + parts = parts or [] stack = parser.Stack( self.ctx, 'software_config_test_stack', template.Template({ diff --git a/heat/tests/test_neutron.py b/heat/tests/test_neutron.py index 2d2f46366e..5337dd7f33 100644 --- a/heat/tests/test_neutron.py +++ b/heat/tests/test_neutron.py @@ -1169,7 +1169,9 @@ class NeutronRouterTest(HeatTestCase): self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state) return rsrc - def create_router_interface(self, t, stack, resource_name, properties={}): + def create_router_interface(self, t, stack, resource_name, + properties=None): + properties = properties or {} t['Resources'][resource_name]['Properties'] = properties resource_defns = stack.t.resource_definitions(stack) rsrc = router.RouterInterface( @@ -1180,7 +1182,8 @@ class NeutronRouterTest(HeatTestCase): self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state) return rsrc - def create_gateway_router(self, t, stack, resource_name, properties={}): + def create_gateway_router(self, t, stack, resource_name, properties=None): + properties = properties or {} t['Resources'][resource_name]['Properties'] = properties resource_defns = stack.t.resource_definitions(stack) rsrc = router.RouterGateway( diff --git a/heat/tests/test_neutron_security_group.py b/heat/tests/test_neutron_security_group.py index 2236764f02..ebe5a3fede 100644 --- a/heat/tests/test_neutron_security_group.py +++ b/heat/tests/test_neutron_security_group.py @@ -116,7 +116,8 @@ Resources: stack.store() return stack - def assertResourceState(self, rsrc, ref_id, metadata={}): + def assertResourceState(self, rsrc, ref_id, metadata=None): + metadata = metadata or {} self.assertIsNone(rsrc.validate()) self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state) self.assertEqual(ref_id, rsrc.FnGetRefId()) diff --git a/heat/tests/test_parameters.py b/heat/tests/test_parameters.py index 7e8eb4eae4..2530995e65 100644 --- a/heat/tests/test_parameters.py +++ b/heat/tests/test_parameters.py @@ -361,8 +361,9 @@ params_schema = json.loads('''{ class ParametersTest(testtools.TestCase): - def new_parameters(self, stack_name, tmpl, user_params={}, stack_id=None, - validate_value=True): + def new_parameters(self, stack_name, tmpl, user_params=None, + stack_id=None, validate_value=True): + user_params = user_params or {} tmpl.update({'HeatTemplateFormatVersion': '2012-12-12'}) tmpl = template.Template(tmpl) params = tmpl.parameters( diff --git a/heat/tests/test_security_group.py b/heat/tests/test_security_group.py index 983a7cfea2..c5f6db8edd 100644 --- a/heat/tests/test_security_group.py +++ b/heat/tests/test_security_group.py @@ -156,7 +156,8 @@ Resources: stack.store() return stack - def assertResourceState(self, rsrc, ref_id, metadata={}): + def assertResourceState(self, rsrc, ref_id, metadata=None): + metadata = metadata or {} self.assertIsNone(rsrc.validate()) self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state) self.assertEqual(ref_id, rsrc.FnGetRefId()) diff --git a/heat/tests/test_waitcondition.py b/heat/tests/test_waitcondition.py index a54e5702a4..e6c27fd4f9 100644 --- a/heat/tests/test_waitcondition.py +++ b/heat/tests/test_waitcondition.py @@ -109,8 +109,9 @@ class WaitConditionTest(HeatTestCase): # Note tests creating a stack should be decorated with @stack_delete_after # to ensure the stack is properly cleaned up def create_stack(self, stack_id=None, - template=test_template_waitcondition, params={}, + template=test_template_waitcondition, params=None, stub=True): + params = params or {} temp = template_format.parse(template) template = parser.Template(temp) ctx = utils.dummy_context(tenant_id='test_tenant') diff --git a/heat/tests/utils.py b/heat/tests/utils.py index b80d191887..f107a48209 100644 --- a/heat/tests/utils.py +++ b/heat/tests/utils.py @@ -65,7 +65,8 @@ def reset_dummy_db(): def dummy_context(user='test_username', tenant_id='test_tenant_id', - password='password', roles=[], user_id=None): + password='password', roles=None, user_id=None): + roles = roles or [] return context.RequestContext.from_dict({ 'tenant_id': tenant_id, 'tenant': 'test_tenant', @@ -79,8 +80,9 @@ def dummy_context(user='test_username', tenant_id='test_tenant_id', }) -def parse_stack(t, params={}, stack_name='test_stack', stack_id=None, +def parse_stack(t, params=None, stack_name='test_stack', stack_id=None, timeout_mins=None): + params = params or {} ctx = dummy_context() template = parser.Template(t) stack = parser.Stack(ctx, stack_name, template,