def test_dict_scheme_unknown_parameter(): error = pytest.raises( UnknownParameterError, lambda: _validate({ 1: True, False: "value", }, DictScheme({1: Bool()}))).value assert error.object_name == "[False]"
def test_dict_scheme_missing_parameter(): error = pytest.raises( MissingParameterError, lambda: _validate({ 2: "value", }, DictScheme({ 1: Bool(), 2: String() }))).value assert error.object_name == "[1]"
async def inner_wrapper(request, *args, **kwargs): try: data = await request.json() except (ValueError, TypeError): raise web.HTTPBadRequest(text="Could not decode JSON object.") if scheme: try: data = validate("request", data, DictScheme(scheme)) except ValidationError as e: raise web.HTTPBadRequest(text=e.get_message()) return await coro(request, data, *args, **kwargs)
def test_dict_scheme_modification(): _validate_modification({ "1": "10", "2": "20", "3": "30" }, DictScheme({ "1": ToInt(), "2": String(), "3": ToInt() }), { "1": 10, "2": "20", "3": 30 })
def test_dict_scheme_with_schema(): _validate({ False: "string", 1: True, "integer": 10, 4.4: 44.4, }, DictScheme({ False: String(), 1: Bool(), "integer": Integer(), 3.3: Float(optional=True), 4.4: Float(optional=True), }))
def _get_scheme(cls): """Returns resource scheme for proper validation. Attention: for internal use only! """ scheme = {} for base in cls.mro(): if bool( issubclass(base, BaseResource) and getattr(base, "__scheme__", None) ): scheme.update(base.__scheme__) return DictScheme(scheme, ignore_unknown=True)
class ExportsInfo(Sequence): """Information about volume exports.""" __scheme__ = List(DictScheme({ "sdcId": String(), "sdcIp": String(), "limitIops": Integer(), "limitBwInMbps": Integer() }), optional=True) def __init__(self, data=None): self._data = data or [] def __getitem__(self, index): return self._data[index] def __len__(self): return len(self._data) def __contains__(self, key): if isinstance(key, Sdc): return key["id"] in (e["sdcId"] for e in self._data) else: super(ExportsInfo, self).__contains__(key)
def test_dict_scheme_invalid_type(): error = pytest.raises(InvalidTypeError, lambda: _validate([], DictScheme({}))).value assert error.object_name == "" assert error.object_type == list
def test_dict_scheme_with_delete_unknown(): _validate_modification({ "known_key": 10, "unknown_key": 10 }, DictScheme({"known_key": Integer()}, delete_unknown=True), {"known_key": 10})
def test_dict_scheme_with_ignore_unknown(): _validate({ "known_key": 10, "unknown_key": 10, }, DictScheme({"known_key": Integer()}, ignore_unknown=True))
def test_dict_scheme_empty(): _validate({}, DictScheme({}))
class BaseResource(Mapping): """Base resource model.""" __scheme__ = { "id": String(), "links": List( DictScheme({ "href": String(), "rel": String() }), optional=True ) } """Data scheme for instance validation.""" __parents__ = None """ References to parent resources by fields. Example: frozenset([ ("parentField", "ResourceClassName") ]) """ __resource__ = None """ Custom resource name that overrides default name based on name of resource class. """ @classmethod def _get_name(cls): """Returns resource name. Attention: for internal use only! """ resource = cls.__resource__ if not resource: resource = camelize(underscore(cls.__name__)) return resource @classmethod def _get_scheme(cls): """Returns resource scheme for proper validation. Attention: for internal use only! """ scheme = {} for base in cls.mro(): if bool( issubclass(base, BaseResource) and getattr(base, "__scheme__", None) ): scheme.update(base.__scheme__) return DictScheme(scheme, ignore_unknown=True) @classmethod def one(cls, instance_id, **kwargs): """Returns instance of resource. :param instance_id: id of resource instance :returns: instance of resource """ return cls(instance_id, **kwargs) @pyscaleio.inject @classmethod def all(cls, client, instance_ids=None, **kwargs): """Returns list of resource instances. :param instance_ids: list of instance ids (optional) :returns: list of resource instances """ if not instance_ids: instances = client.get_instances_of(cls._get_name()) else: if isinstance(instance_ids, string_types): instance_ids = (instance_ids,) instances = client.perform_action_on_type( cls._get_name(), "queryBySelectedIds", {"ids": instance_ids}) return [cls(instance=instance, client=client) for instance in instances ] @pyscaleio.inject def __init__(self, client, instance_id=None, instance=None): self._client = client self._scheme = {} if instance_id and instance: raise exceptions.ScaleIONotBothParameters("instance_id", "instance") if instance_id: instance = self._client.get_instance_of(self._get_name(), instance_id) self._instance = self._validate(instance or {}) def __getitem__(self, key): return self._instance[key] def __iter__(self): return iter(self._instance) def __len__(self): return len(self._instance) @property def links(self): return self["links"] def _validate(self, instance): """Validates the instance if resource according to scheme. Attention: for internal use only! """ try: return validate("instance", instance, self._get_scheme()) except ValidationError as e: raise exceptions.ScaleIOValidationError(e) def update(self): """Updates resource instance.""" instance = self._client.get_instance_of(self._get_name(), self["id"]) instance = self._validate(instance) fields = set(list(instance) + list(self._instance)) for field in fields: try: instance[field] except KeyError: del self._instance[field] else: self._instance[field] = instance[field]
def validate(cls, data): """Validate Car document and return the instance.""" return cls(validate("Car", data, DictScheme(cls.__scheme__)))
class Car(MutableMapping): __slots__ = ("_object", ) __scheme__ = { "ownerName": OrdinaryString(), "serialNumber": Integer32(), "modelYear": Integer32(), # "serialNumber": Integer64(), # "modelYear": Integer64(), "code": OrdinaryString(), "vehicleCode": OrdinaryString(), "engine": DictScheme({ "capacity": Integer16(), "numCylinders": Integer8(), "maxRpm": Integer16(), "manufacturerCode": Char(), }), "fuelFigures": DictScheme({ "speed": Integer16(), "mpg": Float(), "usageDescription": OrdinaryString(), }), "performanceFigures": DictScheme({ "octaneRating": Integer16(), "acceleration": DictScheme({ "mph": Integer16(), "seconds": Float(), }), }), "manufacturer": OrdinaryString(), "model": OrdinaryString(), "activationCode": OrdinaryString(), } __database__ = "soldcars" __collection__ = "cars" __write_majority__ = WriteConcern(w="majority", wtimeout=5000) @classmethod def get_scheme(cls): """Return Car scheme.""" return cls.__scheme__ @classmethod def validate(cls, data): """Validate Car document and return the instance.""" return cls(validate("Car", data, DictScheme(cls.__scheme__))) @classmethod def database(cls): """Return database instance.""" return Motor().default()[cls.__database__] @classmethod def collection(cls, stale_ok=False, majority=False): """Return collection instance.""" kwargs = {} if stale_ok: kwargs["read_preference"] = ReadPreference.SECONDARY_PREFERRED elif majority: kwargs["write_concern"] = cls.__write_majority__ return aiomotor.AsyncIOMotorCollection(cls.database(), cls.__collection__, **kwargs) @classmethod async def ensure_index(cls): """Ensure collection index.""" await cls.collection().create_index( [("serialNumber", pymongo.ASCENDING)], unique=True) @classmethod async def one(cls, serial, add_query=None, fields=None, required=True, stale_ok=False): """Return a one Car document.""" query = {"serialNumber": serial} if add_query: query.update(add_query) car = await cls.collection(stale_ok=stale_ok ).find_one(query, projection=fields) if not car and required: raise CarNotFound(serial) if car: car.pop("_id") return cls(car) @classmethod def get_mocked(cls, override=None): """Mock a one Car document. Attention: use only in tests and cli tools to fake documents! """ def randstr(length): letters = string.ascii_lowercase return ''.join(random.choice(letters) for i in range(length)) def _mock(part): d = {} for key, value in part.items(): # NOTE: using mangled attributes is not a good way # but object_validator library was not intend to # convert or generate new data by the scheme. if isinstance(value, DictScheme): d[key] = _mock(value._DictScheme__scheme) elif isinstance(value, Integer): d[key] = random.randint(value._BasicNumber__min or 0, value._BasicNumber__max or 100) elif isinstance(value, Float): d[key] = random.uniform(value._BasicNumber__min or 0, value._BasicNumber__max or 100) d[key] = round(d[key], 2) elif isinstance(value, String): d[key] = randstr( random.randint(value._String__min_length, value._String__max_length)) return d mocked = _mock(cls.get_scheme()) mocked.update(override or {}) return cls(mocked) def __init__(self, data): self._object = data or {} def __getitem__(self, key): return self._object[key] def __setitem__(self, key, value): self._object[key] = value def __delitem__(self, key): self._object.pop(key) def __iter__(self): return iter(self._object) def __len__(self): return len(self._object) def asdict(self): """Return Car document as dictionary.""" return copy.deepcopy(self._object) def asjson(self): """Return Car document as JSON.""" return json.dumps(self._object) async def insert(self, majority=True): """Insert Car document in collection.""" try: document_id = self["_id"] = \ await self.collection(majority=majority).insert_one(self) except pymongo.errors.WTimeoutError: # FIXME: add retry on write timeout raise except pymongo.errors.DuplicateKeyError: raise CarAlreadyExists(self["serialNumber"]) return document_id
def _get_scheme(cls): """Returns config scheme.""" return DictScheme(cls.__scheme__)
}, { "id": 2, "name": "two", "value": 2.0, "zero": False, "dividers": [1, 2], "dividers_map": { 1: 1.0, 2: 2.0, }, }] SCHEME = List(DictScheme({ "id": Integer(choices=(0, 2)), "name": String(), "value": Float(), "zero": Bool(), "dividers": List(Integer()), "dividers_map": Dict(Integer(), Float()), })) def test_validate(): _validate("items", copy.deepcopy(ITEMS), SCHEME) def test_validate_invalid_type(): items = copy.deepcopy(ITEMS) items[1]["id"] = "string" error = pytest.raises(InvalidTypeError, lambda: _validate("items", items, SCHEME)