Skip to content

Commit b718b1d

Browse files
committed
py error msgs fix
1 parent c7053db commit b718b1d

File tree

4 files changed

+152
-135
lines changed

4 files changed

+152
-135
lines changed

openapiart/common.py

Lines changed: 116 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import importlib
22
import logging
33
import json
4+
from markupsafe import string
45
import yaml
56
import requests
67
import urllib3
@@ -234,35 +235,44 @@ def _clear_errors(self):
234235
else:
235236
self._validation_errors.clear()
236237

237-
def validate_mac(self, mac):
238+
def validate_mac(self, path, mac):
239+
msg = "value of `{}` must be a valid mac address, instead of `{}`".format(path, mac)
238240
if mac is None or not isinstance(mac, (str, unicode)) or mac.count(" ") != 0:
239-
return False
241+
self._append_error(msg)
240242
try:
241243
if len(mac) != 17:
242-
return False
243-
return all([0 <= int(oct, 16) <= 255 for oct in mac.split(":")])
244+
self._append_error(msg)
245+
if all([0 <= int(oct, 16) <= 255 for oct in mac.split(":")]) is False:
246+
self._append_error(msg)
244247
except Exception:
245-
return False
248+
self._append_error(msg)
246249

247-
def validate_ipv4(self, ip):
250+
def validate_ipv4(self, path, ip):
251+
msg = "value of `{}` must be a valid ipv4 address, instead of `{}`".format(path, ip)
248252
if ip is None or not isinstance(ip, (str, unicode)) or ip.count(" ") != 0:
249-
return False
253+
self._append_error(msg)
250254
if len(ip.split(".")) != 4:
251-
return False
255+
self._append_error(msg)
252256
try:
253-
return all([0 <= int(oct) <= 255 for oct in ip.split(".", 3)])
257+
if all([0 <= int(oct) <= 255 for oct in ip.split(".", 3)]) is False:
258+
self._append_error(msg)
254259
except Exception:
255-
return False
260+
self._append_error(msg)
256261

257-
def validate_ipv6(self, ip):
262+
def validate_ipv6(self, path, ip):
263+
msg = "value of `{}` must be a valid ipv6 address, instead of `{}`".format(path, ip)
258264
if ip is None or not isinstance(ip, (str, unicode)):
265+
self._append_error(msg)
259266
return False
260267
ip = ip.strip()
261268
if ip.count(" ") > 0 or ip.count(":") > 7 or ip.count("::") > 1 or ip.count(":::") > 0:
269+
self._append_error(msg)
262270
return False
263271
if (ip[0] == ":" and ip[:2] != "::") or (ip[-1] == ":" and ip[-2:] != "::"):
272+
self._append_error(msg)
264273
return False
265274
if ip.count("::") == 0 and ip.count(":") != 7:
275+
self._append_error(msg)
266276
return False
267277
if ip == "::":
268278
return True
@@ -273,68 +283,90 @@ def validate_ipv6(self, ip):
273283
else:
274284
ip = ip.replace("::", ":0:")
275285
try:
276-
return all([True if (0 <= int(oct, 16) <= 65535) and (1 <= len(oct) <= 4) else False for oct in ip.split(":")])
286+
verdict = all([
287+
True if (0 <= int(oct, 16) <= 65535) and (1 <= len(oct) <= 4) else False for oct in ip.split(":")
288+
])
289+
if verdict is False:
290+
self._append_error(msg)
277291
except Exception:
278-
return False
292+
self._append_error(msg)
279293

280-
def validate_hex(self, hex):
294+
def validate_hex(self, path, hex):
295+
msg = "value of `{}` must be a valid hex string, instead of `{}`".format(path, hex)
281296
if hex is None or not isinstance(hex, (str, unicode)):
282-
return False
297+
self._append_error(msg)
283298
try:
284299
int(hex, 16)
285300
return True
286301
except Exception:
287-
return False
302+
self._append_error(msg)
288303

289-
def validate_integer(self, value, min, max):
304+
def validate_integer(self, path, value):
290305
if value is None or not isinstance(value, int):
291-
return False
292-
if value < 0:
293-
return False
294-
if min is not None and value < min:
295-
return False
296-
if max is not None and value > max:
297-
return False
298-
return True
306+
self._append_error("value of `{}` must be a valid int type, instead of `{}`".format(
307+
path, value
308+
))
309+
310+
def validate_min_max(self, path, value, min, max):
311+
if isinstance(value, str):
312+
value = len(value)
313+
if (min is not None and value < min) or (max is not None and value > max):
314+
self._append_error("length of `{}` must be in the range of [{}, {}], instead of `{}`".format(
315+
path,
316+
min if min is not None else "",
317+
max if max is not None else "",
318+
value
319+
))
299320

300-
def validate_float(self, value):
301-
return isinstance(value, (int, float))
321+
def validate_float(self, path, value):
322+
if isinstance(value, (int, float)) is False:
323+
self._append_error("value of `{}` must be a valid float type, instead of `{}`".format(
324+
path, value
325+
))
302326

303-
def validate_string(self, value, min_length, max_length):
327+
def validate_string(self, path, value):
304328
if value is None or not isinstance(value, (str, unicode)):
305-
return False
306-
if min_length is not None and len(value) < min_length:
307-
return False
308-
if max_length is not None and len(value) > max_length:
309-
return False
310-
return True
329+
self._append_error("value of `{}` must be a valid string type, instead of `{}`".format(
330+
path, value
331+
))
311332

312-
def validate_bool(self, value):
313-
return isinstance(value, bool)
333+
def validate_bool(self, path, value):
334+
if isinstance(value, bool) is False:
335+
self._append_error("value of `{}` must be a valid bool type, instead of `{}`".format(
336+
path, value
337+
))
314338

315-
def validate_list(self, value, itemtype, min, max, min_length, max_length):
339+
def validate_list(self, path, value, itemtype, min, max):
316340
if value is None or not isinstance(value, list):
317341
return False
318342
v_obj = getattr(self, "validate_{}".format(itemtype), None)
319343
if v_obj is None:
320344
raise AttributeError("{} is not a valid attribute".format(itemtype))
321-
v_obj_lst = []
322-
for item in value:
323-
if itemtype == "integer":
324-
v_obj_lst.append(v_obj(item, min, max))
325-
elif itemtype == "string":
326-
v_obj_lst.append(v_obj(item, min_length, max_length))
345+
for ind, item in enumerate(value):
346+
if itemtype in ["integer", "string", "float"]:
347+
v_obj(path + "[{}]".format(ind), item)
348+
self.validate_min_max(path, item, min, max)
327349
else:
328-
v_obj_lst.append(v_obj(item))
329-
return v_obj_lst
350+
v_obj(path + "[{}]".format(ind), item)
330351

331-
def validate_binary(self, value):
332-
if value is None or not isinstance(value, (str, unicode)):
333-
return False
334-
return all([True if int(bin) == 0 or int(bin) == 1 else False for bin in value])
352+
def validate_binary(self, path, value):
353+
if value is None or not isinstance(value, (str, unicode)) or \
354+
all([True if int(bin) == 0 or int(bin) == 1 else False for bin in value]) is False:
355+
self._append_error("value of `{}` must be a valid binary string, instead of `{}`".format(
356+
path, value
357+
))
335358

336-
def types_validation(self, value, type_, err_msg, itemtype=None, min=None, max=None, min_length=None, max_length=None):
337-
type_map = {int: "integer", str: "string", float: "float", bool: "bool", list: "list", "int64": "integer", "int32": "integer", "double": "float"}
359+
def types_validation(self, value, type_, path, itemtype=None, min=None, max=None):
360+
type_map = {
361+
int: "integer",
362+
str: "string",
363+
float: "float",
364+
bool: "bool",
365+
list: "list",
366+
"int64": "integer",
367+
"int32": "integer",
368+
"double": "float"
369+
}
338370
if type_ in type_map:
339371
type_ = type_map[type_]
340372
if itemtype is not None and itemtype in type_map:
@@ -343,37 +375,9 @@ def types_validation(self, value, type_, err_msg, itemtype=None, min=None, max=N
343375
if v_obj is None:
344376
msg = "{} is not a valid or unsupported format".format(type_)
345377
raise TypeError(msg)
346-
if type_ == "list":
347-
verdict = v_obj(value, itemtype, min, max, min_length, max_length)
348-
if all(verdict) is True:
349-
return
350-
err_msg = "{} \n {} are not valid".format(err_msg, [value[index] for index, item in enumerate(verdict) if item is False])
351-
verdict = False
352-
elif type_ == "integer":
353-
verdict = v_obj(value, min, max)
354-
if verdict is True:
355-
return
356-
min_max = ""
357-
if min is not None:
358-
min_max = ", expected min {}".format(min)
359-
if max is not None:
360-
min_max = min_max + ", expected max {}".format(max)
361-
err_msg = "{} \n got {} of type {} {}".format(err_msg, value, type(value), min_max)
362-
elif type_ == "string":
363-
verdict = v_obj(value, min_length, max_length)
364-
if verdict is True:
365-
return
366-
msg = ""
367-
if min_length is not None:
368-
msg = ", expected min {}".format(min_length)
369-
if max_length is not None:
370-
msg = msg + ", expected max {}".format(max_length)
371-
err_msg = "{} \n got {} of type {} {}".format(err_msg, value, type(value), msg)
372-
else:
373-
verdict = v_obj(value)
374-
if verdict is False:
375-
self._append_error(err_msg)
376-
# raise TypeError(err_msg)
378+
v_obj(path, value) if type_ != "list" else v_obj(path, value, itemtype, min, max)
379+
if type_ in ["integer", "string", "float"]:
380+
self.validate_min_max(path, value, min, max)
377381

378382
def _raise_validation(self):
379383
errors = "\n".join(self._validation_errors)
@@ -393,6 +397,8 @@ class OpenApiObject(OpenApiBase, OpenApiValidator):
393397

394398
__slots__ = ("_properties", "_parent", "_choice")
395399

400+
_JSON_NAME = ""
401+
396402
_DEFAULTS = {}
397403
_TYPES = {}
398404
_REQUIRED = []
@@ -433,9 +439,6 @@ def _get_property(self, name, default_value=None, parent=None, choice=None):
433439
return self._properties[name]
434440
if isinstance(default_value, type) is True:
435441
self._set_choice(name)
436-
# if "_choice" in default_value.__slots__:
437-
# self._properties[name] = default_value(parent=parent, choice=choice)
438-
# else:
439442
self._properties[name] = default_value(parent=parent)
440443
if "_DEFAULTS" in dir(self._properties[name]) and "choice" in self._properties[name]._DEFAULTS:
441444
getattr(self._properties[name], self._properties[name]._DEFAULTS["choice"])
@@ -462,17 +465,15 @@ def _set_property(self, name, value, choice=None):
462465

463466
def _encode(self):
464467
"""Helper method for serialization"""
468+
self._validate(self._JSON_NAME)
465469
output = {}
466-
self._validate_required()
467470
for key, value in self._properties.items():
468-
self._validate_types(key, value)
469471
if isinstance(value, (OpenApiObject, OpenApiIter)):
470472
output[key] = value._encode()
471473
elif value is not None:
472474
if key in self._TYPES and "format" in self._TYPES[key] and self._TYPES[key]["format"] == "int64":
473475
value = str(value)
474476
output[key] = value
475-
self._raise_validation()
476477
return output
477478

478479
def _decode(self, obj):
@@ -501,9 +502,7 @@ def _decode(self, obj):
501502
if "format" in self._TYPES[property_name] and self._TYPES[property_name]["format"] == "int64":
502503
property_value = int(property_value)
503504
self._properties[property_name] = property_value
504-
self._validate_types(property_name, property_value)
505-
self._validate_required()
506-
self._raise_validation()
505+
self._validate(self._JSON_NAME)
507506
return self
508507

509508
def _get_child_class(self, property_name, is_property_list=False):
@@ -534,76 +533,73 @@ def clone(self):
534533
"""Creates a deep copy of the current object"""
535534
return self.__deepcopy__(None)
536535

537-
def _validate_required(self):
536+
def _validate_required(self, path):
538537
"""Validates the required properties are set
539538
Use getattr as it will set any defaults prior to validating
540539
"""
541540
if getattr(self, "_REQUIRED", None) is None:
542541
return
543542
for name in self._REQUIRED:
544543
if self._properties.get(name) is None:
545-
msg = "{} is a mandatory property of {}" " and should not be set to None".format(
546-
name,
547-
self.__class__,
544+
msg = "required field `{}.{}` must not be empty".format(
545+
path, name
548546
)
549-
# raise ValueError(msg)
550547
self._append_error(msg)
551548

552-
def _validate_types(self, property_name, property_value):
549+
def _validate_types(self, path, property_name, property_value):
553550
common_data_types = [list, str, int, float, bool]
554551
if property_name not in self._TYPES:
555-
# raise ValueError("Invalid Property {}".format(property_name))
556552
return
557553
details = self._TYPES[property_name]
558554
if property_value is None:
559555
return
560556
if "enum" in details and property_value not in details["enum"]:
561-
msg = "property {} shall be one of these" " {} enum, but got {} at {}"
562-
self._append_error(
563-
msg.format(property_name, details["enum"], property_value, self.__class__)
557+
msg = "enum field `{}` must be one of {}, instead of `{}`".format(
558+
path, details["enum"], property_value
564559
)
565-
# raise TypeError(msg.format(property_name, details["enum"], property_value, self.__class__))
560+
self._append_error(msg)
566561
if details["type"] in common_data_types and "format" not in details:
567-
msg = "property {} shall be of type {} at {}".format(property_name, details["type"], self.__class__)
568-
self.types_validation(property_value, details["type"], msg, details.get("itemtype"), details.get("minimum"), details.get("maximum"),
569-
details.get("minLength"), details.get("maxLength"))
562+
self.types_validation(
563+
property_value, details["type"], path, details.get("itemtype"),
564+
details.get("minimum", details.get("minLength")),
565+
details.get("maximum", details.get("maxLength"))
566+
)
570567

571568
if details["type"] not in common_data_types:
572569
class_name = details["type"]
573570
# TODO Need to revisit importlib
574571
module = importlib.import_module(self.__module__)
575572
object_class = getattr(module, class_name)
576573
if not isinstance(property_value, object_class):
577-
msg = "property {} shall be of type {}," " but got {} at {}"
574+
msg = "value of `{}` must be a valid {} type, instead of `{}`"
578575
self._append_error(
579-
msg.format(property_name, class_name, type(property_value), self.__class__)
576+
msg.format(path, class_name, type(property_value))
580577
)
581-
# raise TypeError(msg.format(property_name, class_name, type(property_value), self.__class__))
582578
if "format" in details:
583-
msg = "Invalid {} format on property {}, expected {} at {}".format(
584-
property_value, property_name, details["format"], self.__class__
585-
)
586579
_type = details["type"] if details["type"] is list else details["format"]
587-
self.types_validation(property_value, _type, msg, details["format"], details.get("minimum"), details.get("maximum"),
588-
details.get("minLength"), details.get("maxLength"))
580+
self.types_validation(
581+
property_value, _type, path, details["format"],
582+
details.get("minimum", details.get("minLength")),
583+
details.get("maximum", details.get("maxLength"))
584+
)
589585

590-
def _validate(self, skip_exception=False):
591-
self._validate_required()
586+
def _validate(self, path, skip_exception=False):
587+
self._validate_required(path)
592588
for key, value in self._properties.items():
593589
if isinstance(value, OpenApiObject):
594-
value._validate(True)
590+
value._validate(path + ".%s" % key, True)
595591
elif isinstance(value, OpenApiIter):
596-
for item in value:
592+
for ind, item in enumerate(value):
597593
if not isinstance(item, OpenApiObject):
598594
continue
599-
item._validate(True)
600-
self._validate_types(key, value)
595+
item._validate(path + ".%s[%d]" % (key, ind), True)
596+
self._validate_types(path + ".%s" % (key), key, value)
601597
if skip_exception:
602598
return self._validation_errors
603599
self._raise_validation()
604600

605601
def validate(self):
606-
return self._validate()
602+
return self._validate(self._JSON_NAME)
607603

608604
def get(self, name, with_default=False):
609605
"""

0 commit comments

Comments
 (0)