/
common.py
247 lines (189 loc) · 6.32 KB
/
common.py
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# encoding: utf-8
# This file contains commonly used parts of external libraries. The idea is
# to help in removing helpers from being used as a dependency by many files
# but at the same time making it easy to change for example the json lib
# used.
#
# NOTE: This file is specificaly created for
# from ckan.common import x, y, z to be allowed
from collections import MutableMapping
import flask
import pylons
import six
from werkzeug.local import Local, LocalProxy
from flask_babel import (gettext as flask_ugettext,
ngettext as flask_ungettext)
from pylons.i18n import (ugettext as pylons_ugettext,
ungettext as pylons_ungettext)
from pylons import response
import simplejson as json
current_app = flask.current_app
try:
from collections import OrderedDict # from python 2.7
except ImportError:
from sqlalchemy.util import OrderedDict
def is_flask_request():
u'''
A centralized way to determine whether we are in the context of a
request being served by Flask or Pylons
'''
try:
pylons.request.environ
pylons_request_available = True
except TypeError:
pylons_request_available = False
return (flask.request and
(flask.request.environ.get(u'ckan.app') == u'flask_app' or
not pylons_request_available))
def streaming_response(
data, mimetype=u'application/octet-stream', with_context=False):
iter_data = iter(data)
if is_flask_request():
# Removal of context variables for pylon's app is prevented
# inside `pylons_app.py`. It would be better to decide on the fly
# whether we need to preserve context, but it won't affect performance
# in any visible way and we are going to get rid of pylons anyway.
# Flask allows to do this in easy way.
if with_context:
iter_data = flask.stream_with_context(iter_data)
resp = flask.Response(iter_data, mimetype=mimetype)
else:
response.app_iter = iter_data
resp = response.headers['Content-type'] = mimetype
return resp
def ugettext(*args, **kwargs):
if is_flask_request():
return flask_ugettext(*args, **kwargs)
else:
return pylons_ugettext(*args, **kwargs)
_ = ugettext
def ungettext(*args, **kwargs):
if is_flask_request():
return flask_ungettext(*args, **kwargs)
else:
return pylons_ungettext(*args, **kwargs)
class CKANConfig(MutableMapping):
u'''Main CKAN configuration object
This is a dict-like object that also proxies any changes to the
Flask and Pylons configuration objects.
The actual `config` instance in this module is initialized in the
`load_environment` method with the values of the ini file or env vars.
'''
def __init__(self, *args, **kwargs):
self.store = dict()
self.update(dict(*args, **kwargs))
def __getitem__(self, key):
return self.store[key]
def __iter__(self):
return iter(self.store)
def __len__(self):
return len(self.store)
def __repr__(self):
return self.store.__repr__()
def copy(self):
return self.store.copy()
def clear(self):
self.store.clear()
try:
flask.current_app.config.clear()
except RuntimeError:
pass
try:
pylons.config.clear()
# Pylons set this default itself
pylons.config[u'lang'] = None
except TypeError:
pass
def __setitem__(self, key, value):
self.store[key] = value
try:
flask.current_app.config[key] = value
except RuntimeError:
pass
try:
pylons.config[key] = value
except TypeError:
pass
def __delitem__(self, key):
del self.store[key]
try:
del flask.current_app.config[key]
except RuntimeError:
pass
try:
del pylons.config[key]
except TypeError:
pass
def _get_request():
if is_flask_request():
return flask.request
else:
return pylons.request
class CKANRequest(LocalProxy):
u'''Common request object
This is just a wrapper around LocalProxy so we can handle some special
cases for backwards compatibility.
LocalProxy will forward to Flask or Pylons own request objects depending
on the output of `_get_request` (which essentially calls
`is_flask_request`) and at the same time provide all objects methods to be
able to interact with them transparently.
'''
@property
def params(self):
u''' Special case as Pylons' request.params is used all over the place.
All new code meant to be run just in Flask (eg views) should always
use request.args
'''
try:
return super(CKANRequest, self).params
except AttributeError:
return self.args
def _get_c():
if is_flask_request():
return flask.g
else:
return pylons.c
def _get_session():
if is_flask_request():
return flask.session
else:
return pylons.session
local = Local()
# This a proxy to the bounded config object
local(u'config')
# Thread-local safe objects
config = local.config = CKANConfig()
# Proxies to already thread-local safe objects
request = CKANRequest(_get_request)
# Provide a `c` alias for `g` for backwards compatibility
g = c = LocalProxy(_get_c)
session = LocalProxy(_get_session)
truthy = frozenset([u'true', u'yes', u'on', u'y', u't', u'1'])
falsy = frozenset([u'false', u'no', u'off', u'n', u'f', u'0'])
def asbool(obj):
if isinstance(obj, six.string_types):
obj = obj.strip().lower()
if obj in truthy:
return True
elif obj in falsy:
return False
else:
raise ValueError(u"String is not true/false: {}".format(obj))
return bool(obj)
def asint(obj):
try:
return int(obj)
except (TypeError, ValueError):
raise ValueError(u"Bad integer value: {}".format(obj))
def aslist(obj, sep=None, strip=True):
if isinstance(obj, six.string_types):
lst = obj.split(sep)
if strip:
lst = [v.strip() for v in lst]
return lst
elif isinstance(obj, (list, tuple)):
return obj
elif obj is None:
return []
else:
return [obj]