From 4d2ab3de65591566cb56853c760ac382eb8d8dce Mon Sep 17 00:00:00 2001 From: Brian Curtin Date: Sun, 8 Feb 2015 11:26:32 -0600 Subject: [PATCH] Add Resource.from_name For the Volume v2 proxy, we need to be able to create volume types from a name, which will then be assigned an ID. This change adds `from_name`, much the same as the existing `from_id`, and reuses the underlying implementation to create or return the Resource depending on which attribute you want to create from. Change-Id: Icb8b14cf346cbdff026bcc6741ebf863d5c8255f --- openstack/resource.py | 49 +++++++++++++++++++++++++++----- openstack/tests/test_resource.py | 6 ++++ 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/openstack/resource.py b/openstack/resource.py index 95b76f30..3f410f64 100644 --- a/openstack/resource.py +++ b/openstack/resource.py @@ -231,20 +231,55 @@ class Resource(collections.MutableMapping): return cls(kwargs, loaded=True) @classmethod - def from_id(cls, value): - """Create an instance from an ID or return an existing instance. - - Instance creation is done via cls.new. - """ + def _from_attr(cls, attribute, value): # This method is useful in the higher level, in cases where operations # need to depend on having Resource objects, but the API is flexible # in taking text values which represent those objects. if isinstance(value, cls): return value elif isinstance(value, six.string_types): - return cls.new(**{cls.id_attribute: value}) + return cls.new(**{attribute: value}) else: - raise ValueError("value must be %s instance or id" % cls.__name__) + raise ValueError("value must be %s instance or %s" % ( + cls.__name__, attribute)) + + @classmethod + def from_id(cls, value): + """Create an instance from an ID or return an existing instance. + + New instances are created with :meth:`~openstack.resource.Resource.new` + + :param value: If ``value`` is an instance of this Resource type, + it is returned. + If ``value`` is an ID which an instance of this + Resource type can be created with, one is created + and returned. + + :rtype: :class:`~openstack.resource.Resource` or the + appropriate subclass. + :raises: :exc:`ValueError` if ``value`` is not an instance of + this Resource type or a valid ``id``. + """ + return cls._from_attr(cls.id_attribute, value) + + @classmethod + def from_name(cls, value): + """Create an instance from a name or return an existing instance. + + New instances are created with :meth:`~openstack.resource.Resource.new` + + :param value: If ``value`` is an instance of this Resource type, + it is returned. + If ``value`` is a name which an instance of this + Resource type can be created with, one is created + and returned. + + :rtype: :class:`~openstack.resource.Resource` or the + appropriate subclass. + :raises: :exc:`ValueError` if ``value`` is not an instance of + this Resource type or a valid ``name``. + """ + return cls._from_attr(cls.name_attribute, value) ## # MUTABLE MAPPING IMPLEMENTATION diff --git a/openstack/tests/test_resource.py b/openstack/tests/test_resource.py index 1214ba95..cd4b0a59 100644 --- a/openstack/tests/test_resource.py +++ b/openstack/tests/test_resource.py @@ -401,6 +401,12 @@ class ResourceTests(base.TestTransportBase): del t.id self.assertTrue(Test.id_attribute not in t._attrs) + def test_from_name_with_name(self): + name = "Ernie Banks" + + obj = FakeResource.from_name(name) + self.assertEqual(obj.name, name) + def test_from_id_with_name(self): name = "Sandy Koufax"