class Profile(fields.WithFields): name = fields.field() thumbnail = fields.field(type=Image) # Of type image def __init__(self, name, thumbnail): self.name = name self.thumbnail = thumbnail
def test_field_basics(): # pylint: disable=unnecessary-dunder-call # Can't use full stop in field name with pytest.raises(ValueError): fields.FieldProperties(store_as='my.field') # Try getting an invalid attribute field = fields.field(type=Image) with pytest.raises(AttributeError): field.area # However, this is fine with dynamic fields field = fields.field(dynamic=True) assert isinstance(field.area, fields.Field) # The dynamic property should be propagated assert isinstance(field.area.height, fields.Field) # By default a field is read/write/deletable depending on the object field = fields.field(attr='value') ns = argparse.Namespace() field.__set__(ns, 5) assert field.__get__(ns) == 5 field.__delete__(ns) assert 'value' not in ns.__dict__ # however, if it is converted to a property it becomes read-only until a setter is given ns = argparse.Namespace() ns.value = 5 field = fields.field(attr='value')(lambda obj: getattr(obj, 'value')) assert field.__get__(ns) == 5 with pytest.raises(AttributeError): field.__set__(ns, 10) # But if we add the setter... field.setter(lambda obj, value: setattr(obj, 'value', value)) field.__set__(ns, 10) assert field.__get__(ns) == 10 # Now check deleting with pytest.raises(AttributeError): field.__delete__(ns) field.deleter(lambda obj: delattr(obj, 'value')) field.__delete__(ns) with pytest.raises(AttributeError): field.__get__(ns) # Test unreadable attribute field = fields.field(attr='unexistent') class Test: pass with pytest.raises(AttributeError): field.__get__(Test())
class Image(fields.WithFields): width = fields.field() height = fields.field() _fmt = mincepy.field(store_as='format') def __init__(self, width: int, height: int, fmt: str, creator=None): super().__init__() self.width = width self.height = height self._fmt = fmt self._creator = creator def area(self): return self.width * self.height @mincepy.field(store_as='_creator') def creator(self): return self._creator @creator.setter def creator(self, value): self._creator = value
class Animal(fields.WithFields): def __init__(self): self._specie = None def set_specie(self, specie): self._specie = specie def get_specie(self): return self._specie def del_specie(self): del self._specie specie = fields.field()(get_specie, set_specie, del_specie)
def test_nested_fields(): assert expr.query_expr(Profile.thumbnail.width == 64) == { 'thumbnail.width': 64 } assert expr.query_expr(Profile.thumbnail == {'width': 64, 'height': 128}) == \ {'thumbnail': {'width': 64, 'height': 128}} # Add a query context tag_eq_holiday = fields.field('tag') == 'holiday' Profile.thumbnail.set_query_context(tag_eq_holiday) # check the query context is being used assert expr.query_expr(Profile.thumbnail == {'width': 64, 'height': 128}) == \ {'$and': [{'tag': 'holiday'}, {'thumbnail': {'width': 64, 'height': 128}}]} # now check that it is carried over to 'width' assert expr.query_expr(Profile.thumbnail.width == 64) == \ {'$and': [{'tag': 'holiday'}, {'thumbnail.width': 64}]}
class Car(fields.WithFields): make = fields.field('_make') colour = fields.field(default='red')