Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/flask_restx/utils.py: 35%

37 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-03-26 06:03 +0000

1import re 

2 

3from collections import OrderedDict 

4from copy import deepcopy 

5 

6from ._http import HTTPStatus 

7 

8 

9FIRST_CAP_RE = re.compile("(.)([A-Z][a-z]+)") 

10ALL_CAP_RE = re.compile("([a-z0-9])([A-Z])") 

11 

12 

13__all__ = ( 

14 "merge", 

15 "camel_to_dash", 

16 "default_id", 

17 "not_none", 

18 "not_none_sorted", 

19 "unpack", 

20) 

21 

22 

23def merge(first, second): 

24 """ 

25 Recursively merges two dictionaries. 

26 

27 Second dictionary values will take precedence over those from the first one. 

28 Nested dictionaries are merged too. 

29 

30 :param dict first: The first dictionary 

31 :param dict second: The second dictionary 

32 :return: the resulting merged dictionary 

33 :rtype: dict 

34 """ 

35 if not isinstance(second, dict): 

36 return second 

37 result = deepcopy(first) 

38 for key, value in second.items(): 

39 if key in result and isinstance(result[key], dict): 

40 result[key] = merge(result[key], value) 

41 else: 

42 result[key] = deepcopy(value) 

43 return result 

44 

45 

46def camel_to_dash(value): 

47 """ 

48 Transform a CamelCase string into a low_dashed one 

49 

50 :param str value: a CamelCase string to transform 

51 :return: the low_dashed string 

52 :rtype: str 

53 """ 

54 first_cap = FIRST_CAP_RE.sub(r"\1_\2", value) 

55 return ALL_CAP_RE.sub(r"\1_\2", first_cap).lower() 

56 

57 

58def default_id(resource, method): 

59 """Default operation ID generator""" 

60 return "{0}_{1}".format(method, camel_to_dash(resource)) 

61 

62 

63def not_none(data): 

64 """ 

65 Remove all keys where value is None 

66 

67 :param dict data: A dictionary with potentially some values set to None 

68 :return: The same dictionary without the keys with values to ``None`` 

69 :rtype: dict 

70 """ 

71 return dict((k, v) for k, v in data.items() if v is not None) 

72 

73 

74def not_none_sorted(data): 

75 """ 

76 Remove all keys where value is None 

77 

78 :param OrderedDict data: A dictionary with potentially some values set to None 

79 :return: The same dictionary without the keys with values to ``None`` 

80 :rtype: OrderedDict 

81 """ 

82 return OrderedDict((k, v) for k, v in sorted(data.items()) if v is not None) 

83 

84 

85def unpack(response, default_code=HTTPStatus.OK): 

86 """ 

87 Unpack a Flask standard response. 

88 

89 Flask response can be: 

90 - a single value 

91 - a 2-tuple ``(value, code)`` 

92 - a 3-tuple ``(value, code, headers)`` 

93 

94 .. warning:: 

95 

96 When using this function, you must ensure that the tuple is not the response data. 

97 To do so, prefer returning list instead of tuple for listings. 

98 

99 :param response: A Flask style response 

100 :param int default_code: The HTTP code to use as default if none is provided 

101 :return: a 3-tuple ``(data, code, headers)`` 

102 :rtype: tuple 

103 :raise ValueError: if the response does not have one of the expected format 

104 """ 

105 if not isinstance(response, tuple): 

106 # data only 

107 return response, default_code, {} 

108 elif len(response) == 1: 

109 # data only as tuple 

110 return response[0], default_code, {} 

111 elif len(response) == 2: 

112 # data and code 

113 data, code = response 

114 return data, code, {} 

115 elif len(response) == 3: 

116 # data, code and headers 

117 data, code, headers = response 

118 return data, code or default_code, headers 

119 else: 

120 raise ValueError("Too many response values")