-
Notifications
You must be signed in to change notification settings - Fork 340
Expand file tree
/
Copy pathutils.py
More file actions
117 lines (89 loc) · 3.31 KB
/
utils.py
File metadata and controls
117 lines (89 loc) · 3.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import re
from collections import OrderedDict
from copy import deepcopy
from six import iteritems
from ._http import HTTPStatus
FIRST_CAP_RE = re.compile('(.)([A-Z][a-z]+)')
ALL_CAP_RE = re.compile('([a-z0-9])([A-Z])')
__all__ = ('merge', 'camel_to_dash', 'default_id', 'not_none', 'not_none_sorted', 'unpack')
def merge(first, second):
'''
Recursively merges two dictionaries.
Second dictionary values will take precedence over those from the first one.
Nested dictionaries are merged too.
:param dict first: The first dictionary
:param dict second: The second dictionary
:return: the resulting merged dictionary
:rtype: dict
'''
if not isinstance(second, dict):
return second
result = deepcopy(first)
for key, value in iteritems(second):
if key in result and isinstance(result[key], dict):
result[key] = merge(result[key], value)
else:
result[key] = deepcopy(value)
return result
def camel_to_dash(value):
'''
Transform a CamelCase string into a low_dashed one
:param str value: a CamelCase string to transform
:return: the low_dashed string
:rtype: str
'''
first_cap = FIRST_CAP_RE.sub(r'\1_\2', value)
return ALL_CAP_RE.sub(r'\1_\2', first_cap).lower()
def default_id(resource, method):
'''Default operation ID generator'''
return '{0}_{1}'.format(method, camel_to_dash(resource))
def not_none(data):
'''
Remove all keys where value is None
:param dict data: A dictionary with potentially some values set to None
:return: The same dictionary without the keys with values to ``None``
:rtype: dict
'''
return dict((k, v) for k, v in iteritems(data) if v is not None)
def not_none_sorted(data):
'''
Remove all keys where value is None
:param OrderedDict data: A dictionary with potentially some values set to None
:return: The same dictionary without the keys with values to ``None``
:rtype: OrderedDict
'''
return OrderedDict((k, v) for k, v in sorted(iteritems(data)) if v is not None)
def unpack(response, default_code=HTTPStatus.OK):
'''
Unpack a Flask standard response.
Flask response can be:
- a single value
- a 2-tuple ``(value, code)``
- a 3-tuple ``(value, code, headers)``
.. warning::
When using this function, you must ensure that the tuple is not the response data.
To do so, prefer returning list instead of tuple for listings.
:param response: A Flask style response
:param int default_code: The HTTP code to use as default if none is provided
:return: a 3-tuple ``(data, code, headers)``
:rtype: tuple
:raise ValueError: if the response does not have one of the expected format
'''
if not isinstance(response, tuple):
# data only
return response, default_code, {}
elif len(response) == 1:
# data only as tuple
return response[0], default_code, {}
elif len(response) == 2:
# data and code
data, code = response
return data, code, {}
elif len(response) == 3:
# data, code and headers
data, code, headers = response
return data, code or default_code, headers
else:
raise ValueError('Too many response values')