def __init__(self, server_config=None, **kwargs): self._fields = { 'int': IntegerField(required=True), 'int_choices': IntegerField(choices=(1, 2), required=True), 'int_default': IntegerField(default=5, required=True), 'many': OneToManyField(SampleEntity, required=True), 'one': OneToOneField(SampleEntity, required=True), } super().__init__(server_config, **kwargs)
def __init__(self, server_config=None, **kwargs): self._fields = { 'name': StringField(), 'number': IntegerField(), 'unique': StringField(unique=True) } self._meta = {'api_path': 'foo'} super(SampleEntity, self).__init__(server_config, **kwargs)
def __init__(self, server_config=None, **kwargs): self._fields = { 'ignore_me': IntegerField(), 'many': OneToManyField(SampleEntity), 'none': OneToOneField(SampleEntity), 'one': OneToOneField(SampleEntity), } self._meta = {'api_path': ''} super(TestEntity, self).__init__(server_config, **kwargs)
class SampleEntity(orm.Entity): """Sample entity to be used in the tests""" name = StringField() value = IntegerField() class Meta(object): """Non-field attributes for this entity.""" # (too-few-public-methods) pylint:disable=R0903 api_path = 'foo'
def __init__(self, server_config=None, **kwargs): if server_config is None: server_config = _get_server_config() self._server_config = server_config # Subclasses usually define a set of fields and metadata before calling # `super`, but that's not always the case. if not hasattr(self, '_fields'): self._fields = {} self._fields.setdefault('id', IntegerField()) if not hasattr(self, '_meta'): self._meta = {} # Check that a valid set of field values has been passed in. if not set(kwargs.keys()).issubset(self._fields.keys()): raise NoSuchFieldError( 'Valid fields are {0}, but received {1} instead.'.format( self._fields.keys(), kwargs.keys())) # Iterate through the values passed in and assign them as instance # variable to `self`. Make sure to transform entity IDs into entity # objects. (This feature is described in the docstring.) for field_name, field_value in kwargs.items(): # e.g. ('admin', True) field = self._fields[field_name] # e.g. A BooleanField object if isinstance(field, OneToOneField): if field_value is None: setattr(self, field_name, field_value) else: setattr( self, field_name, _make_entity_from_id(field.gen_value(), field_value, self._server_config)) elif isinstance(field, OneToManyField): # `try:; …; except TypeError:; raise BadValueError(…)` better # follows the "ask forgiveness" principle. However, a TypeError # could be raised for any number of reasons. For example, # `field_value` could have a faulty __iter__ implementation. if not isinstance(field_value, Iterable): raise BadValueError( 'An inappropriate value was assigned to the "{0}" ' 'field. An iterable of entities and/or entity IDs ' 'should be assigned, but the following was given: {1}'. format(field_name, field_value)) setattr( self, field_name, _make_entities_from_ids(field.gen_value(), field_value, self._server_config)) else: setattr(self, field_name, field_value)
def __init__(self, server_config=None, **kwargs): self._fields = {'one': IntegerField(), 'two': IntegerField()} super(TestEntity, self).__init__(server_config, **kwargs)
class Entity(object): """A logical representation of a Foreman entity. This class is rather useless as is, and it is intended to be subclassed. Subclasses can specify two useful types of information: * fields * metadata Fields are represented by setting class attributes, and metadata is represented by settings attributes on the inner class named ``Meta``. """ # The id() builtin is still available within instance methods, class # methods, static methods, inner classes, and so on. However, id() is *not* # available at the current level of lexical scoping after this point. id = IntegerField() # pylint:disable=C0103 def __init__(self, **kwargs): fields = self.get_fields() for field_name, field_value in kwargs.items(): if field_name not in fields: raise NoSuchFieldError( '{0} is not a valid field. Valid fields are {1}.'.format( field_name, ', '.join(fields.keys()))) setattr(self, field_name, field_value) class Meta(object): # (too-few-public-methods) pylint:disable=R0903 """Non-field information about this entity. This class is a convenient place to store any non-field information about an entity. For example, the ``api_path`` variable is used by :meth:`robottelo.orm.Entity.path`. """ def path(self, which=None): """Return the path to the current entity. Return the path to base entities of this entity's type if: * ``which`` is ``'base'``, or * ``which`` is ``None`` and instance attribute ``id`` is unset. Return the path to this exact entity if instance attribute ``id`` is set and: * ``which`` is ``'self'``, or * ``which`` is ``None``. Raise :class:`NoSuchPathError` otherwise. Child classes may choose to extend this method, especially if a child entity offers more than the two URLs supported by default. If extended, then the extending class should check for custom parameters before calling ``super``:: def path(self, which): if which == 'custom': return urlparse.urljoin(...) super(ChildEntity, self).__init__(which) This will allow the extending method to accept a custom parameter without accidentally raising a :class:`NoSuchPathError`. :param str which: Optional. Valid arguments are 'self' and 'base'. :return: A fully qualified URL. :rtype: str :raises robottelo.orm.NoSuchPathError: If no path can be built. """ # (no-member) pylint:disable=E1101 # It is OK that member ``self.Meta.api_path`` is not found. Subclasses # are required to set that attribute if they wish to use this method. # # Beware of leading and trailing slashes: # # urljoin('example.com', 'foo') => 'foo' # urljoin('example.com/', 'foo') => 'example.com/foo' # urljoin('example.com', '/foo') => '/foo' # urljoin('example.com/', '/foo') => '/foo' base = urlparse.urljoin(helpers.get_server_url() + '/', self.Meta.api_path) if which == 'base' or (which is None and 'id' not in vars(self)): return base elif (which == 'self' or which is None) and 'id' in vars(self): return urlparse.urljoin(base + '/', str(self.id)) raise NoSuchPathError @classmethod def get_fields(cls): """Find all fields attributes of class ``cls``. :param cls: Any object. This method is only especially useful if that class has attributes that are subclasses of class ``Field``. :return: A dict mapping attribute names to ``Field`` objects. :rtype: dict """ # When `dir` is called on "a type or class object, the list contains # the names of its attributes, and recursively of the attributes of its # bases." In constrast, `vars(cls)` returns only the attributes of # `cls` while ignoring all parent classes. Thus, this fails for child # classes: # # for key, val in vars(cls).items(): # if isinstance(val, Field): # attrs[key] = val # attrs = {} for field_name in dir(cls): field = getattr(cls, field_name) if isinstance(field, Field): attrs[field_name] = field return attrs