Skip to content

Commit 628470a

Browse files
committed
Synced jsonutils from oslo-incubator
The sync includes change that drastically enhances performance on Python 2.6 with fresh simplejson library installed. The latest commit in oslo-incubator: - 0f4586c0076183c6356eec682c8a593648125abd The sync adds a new 'strutils' module that is now used in jsonutils. Change-Id: Ib3dc0b713ed90396919feba018772243b3b9c90f Closes-Bug: 1314129
1 parent 356c549 commit 628470a

File tree

3 files changed

+257
-5
lines changed

3 files changed

+257
-5
lines changed

openstack-common.conf

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ module=install_venv
88
module=local
99
module=log
1010
module=policy
11+
module=strutils
1112
module=timeutils
1213
module=with_venv
1314

openstack_dashboard/openstack/common/jsonutils.py

+17-5
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,29 @@
3131
'''
3232

3333

34+
import codecs
3435
import datetime
3536
import functools
3637
import inspect
3738
import itertools
38-
import json
39+
import sys
40+
41+
if sys.version_info < (2, 7):
42+
# On Python <= 2.6, json module is not C boosted, so try to use
43+
# simplejson module if available
44+
try:
45+
import simplejson as json
46+
except ImportError:
47+
import json
48+
else:
49+
import json
3950

4051
import six
4152
import six.moves.xmlrpc_client as xmlrpclib
4253

4354
from openstack_dashboard.openstack.common import gettextutils
4455
from openstack_dashboard.openstack.common import importutils
56+
from openstack_dashboard.openstack.common import strutils
4557
from openstack_dashboard.openstack.common import timeutils
4658

4759
netaddr = importutils.try_import("netaddr")
@@ -156,12 +168,12 @@ def dumps(value, default=to_primitive, **kwargs):
156168
return json.dumps(value, default=default, **kwargs)
157169

158170

159-
def loads(s):
160-
return json.loads(s)
171+
def loads(s, encoding='utf-8'):
172+
return json.loads(strutils.safe_decode(s, encoding))
161173

162174

163-
def load(s):
164-
return json.load(s)
175+
def load(fp, encoding='utf-8'):
176+
return json.load(codecs.getreader(encoding)(fp))
165177

166178

167179
try:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
# Copyright 2011 OpenStack Foundation.
2+
# All Rights Reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
5+
# not use this file except in compliance with the License. You may obtain
6+
# a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
# License for the specific language governing permissions and limitations
14+
# under the License.
15+
16+
"""
17+
System-level utilities and helper functions.
18+
"""
19+
20+
import math
21+
import re
22+
import sys
23+
import unicodedata
24+
25+
import six
26+
27+
from openstack_dashboard.openstack.common.gettextutils import _
28+
29+
30+
UNIT_PREFIX_EXPONENT = {
31+
'k': 1,
32+
'K': 1,
33+
'Ki': 1,
34+
'M': 2,
35+
'Mi': 2,
36+
'G': 3,
37+
'Gi': 3,
38+
'T': 4,
39+
'Ti': 4,
40+
}
41+
UNIT_SYSTEM_INFO = {
42+
'IEC': (1024, re.compile(r'(^[-+]?\d*\.?\d+)([KMGT]i?)?(b|bit|B)$')),
43+
'SI': (1000, re.compile(r'(^[-+]?\d*\.?\d+)([kMGT])?(b|bit|B)$')),
44+
}
45+
46+
TRUE_STRINGS = ('1', 't', 'true', 'on', 'y', 'yes')
47+
FALSE_STRINGS = ('0', 'f', 'false', 'off', 'n', 'no')
48+
49+
SLUGIFY_STRIP_RE = re.compile(r"[^\w\s-]")
50+
SLUGIFY_HYPHENATE_RE = re.compile(r"[-\s]+")
51+
52+
53+
def int_from_bool_as_string(subject):
54+
"""Interpret a string as a boolean and return either 1 or 0.
55+
56+
Any string value in:
57+
58+
('True', 'true', 'On', 'on', '1')
59+
60+
is interpreted as a boolean True.
61+
62+
Useful for JSON-decoded stuff and config file parsing
63+
"""
64+
return bool_from_string(subject) and 1 or 0
65+
66+
67+
def bool_from_string(subject, strict=False, default=False):
68+
"""Interpret a string as a boolean.
69+
70+
A case-insensitive match is performed such that strings matching 't',
71+
'true', 'on', 'y', 'yes', or '1' are considered True and, when
72+
`strict=False`, anything else returns the value specified by 'default'.
73+
74+
Useful for JSON-decoded stuff and config file parsing.
75+
76+
If `strict=True`, unrecognized values, including None, will raise a
77+
ValueError which is useful when parsing values passed in from an API call.
78+
Strings yielding False are 'f', 'false', 'off', 'n', 'no', or '0'.
79+
"""
80+
if not isinstance(subject, six.string_types):
81+
subject = six.text_type(subject)
82+
83+
lowered = subject.strip().lower()
84+
85+
if lowered in TRUE_STRINGS:
86+
return True
87+
elif lowered in FALSE_STRINGS:
88+
return False
89+
elif strict:
90+
acceptable = ', '.join(
91+
"'%s'" % s for s in sorted(TRUE_STRINGS + FALSE_STRINGS))
92+
msg = _("Unrecognized value '%(val)s', acceptable values are:"
93+
" %(acceptable)s") % {'val': subject,
94+
'acceptable': acceptable}
95+
raise ValueError(msg)
96+
else:
97+
return default
98+
99+
100+
def safe_decode(text, incoming=None, errors='strict'):
101+
"""Decodes incoming text/bytes string using `incoming` if they're not
102+
already unicode.
103+
104+
:param incoming: Text's current encoding
105+
:param errors: Errors handling policy. See here for valid
106+
values http://docs.python.org/2/library/codecs.html
107+
:returns: text or a unicode `incoming` encoded
108+
representation of it.
109+
:raises TypeError: If text is not an instance of str
110+
"""
111+
if not isinstance(text, (six.string_types, six.binary_type)):
112+
raise TypeError("%s can't be decoded" % type(text))
113+
114+
if isinstance(text, six.text_type):
115+
return text
116+
117+
if not incoming:
118+
incoming = (sys.stdin.encoding or
119+
sys.getdefaultencoding())
120+
121+
try:
122+
return text.decode(incoming, errors)
123+
except UnicodeDecodeError:
124+
# Note(flaper87) If we get here, it means that
125+
# sys.stdin.encoding / sys.getdefaultencoding
126+
# didn't return a suitable encoding to decode
127+
# text. This happens mostly when global LANG
128+
# var is not set correctly and there's no
129+
# default encoding. In this case, most likely
130+
# python will use ASCII or ANSI encoders as
131+
# default encodings but they won't be capable
132+
# of decoding non-ASCII characters.
133+
#
134+
# Also, UTF-8 is being used since it's an ASCII
135+
# extension.
136+
return text.decode('utf-8', errors)
137+
138+
139+
def safe_encode(text, incoming=None,
140+
encoding='utf-8', errors='strict'):
141+
"""Encodes incoming text/bytes string using `encoding`.
142+
143+
If incoming is not specified, text is expected to be encoded with
144+
current python's default encoding. (`sys.getdefaultencoding`)
145+
146+
:param incoming: Text's current encoding
147+
:param encoding: Expected encoding for text (Default UTF-8)
148+
:param errors: Errors handling policy. See here for valid
149+
values http://docs.python.org/2/library/codecs.html
150+
:returns: text or a bytestring `encoding` encoded
151+
representation of it.
152+
:raises TypeError: If text is not an instance of str
153+
"""
154+
if not isinstance(text, (six.string_types, six.binary_type)):
155+
raise TypeError("%s can't be encoded" % type(text))
156+
157+
if not incoming:
158+
incoming = (sys.stdin.encoding or
159+
sys.getdefaultencoding())
160+
161+
if isinstance(text, six.text_type):
162+
return text.encode(encoding, errors)
163+
elif text and encoding != incoming:
164+
# Decode text before encoding it with `encoding`
165+
text = safe_decode(text, incoming, errors)
166+
return text.encode(encoding, errors)
167+
else:
168+
return text
169+
170+
171+
def string_to_bytes(text, unit_system='IEC', return_int=False):
172+
"""Converts a string into an float representation of bytes.
173+
174+
The units supported for IEC ::
175+
176+
Kb(it), Kib(it), Mb(it), Mib(it), Gb(it), Gib(it), Tb(it), Tib(it)
177+
KB, KiB, MB, MiB, GB, GiB, TB, TiB
178+
179+
The units supported for SI ::
180+
181+
kb(it), Mb(it), Gb(it), Tb(it)
182+
kB, MB, GB, TB
183+
184+
Note that the SI unit system does not support capital letter 'K'
185+
186+
:param text: String input for bytes size conversion.
187+
:param unit_system: Unit system for byte size conversion.
188+
:param return_int: If True, returns integer representation of text
189+
in bytes. (default: decimal)
190+
:returns: Numerical representation of text in bytes.
191+
:raises ValueError: If text has an invalid value.
192+
193+
"""
194+
try:
195+
base, reg_ex = UNIT_SYSTEM_INFO[unit_system]
196+
except KeyError:
197+
msg = _('Invalid unit system: "%s"') % unit_system
198+
raise ValueError(msg)
199+
match = reg_ex.match(text)
200+
if match:
201+
magnitude = float(match.group(1))
202+
unit_prefix = match.group(2)
203+
if match.group(3) in ['b', 'bit']:
204+
magnitude /= 8
205+
else:
206+
msg = _('Invalid string format: %s') % text
207+
raise ValueError(msg)
208+
if not unit_prefix:
209+
res = magnitude
210+
else:
211+
res = magnitude * pow(base, UNIT_PREFIX_EXPONENT[unit_prefix])
212+
if return_int:
213+
return int(math.ceil(res))
214+
return res
215+
216+
217+
def to_slug(value, incoming=None, errors="strict"):
218+
"""Normalize string.
219+
220+
Convert to lowercase, remove non-word characters, and convert spaces
221+
to hyphens.
222+
223+
Inspired by Django's `slugify` filter.
224+
225+
:param value: Text to slugify
226+
:param incoming: Text's current encoding
227+
:param errors: Errors handling policy. See here for valid
228+
values http://docs.python.org/2/library/codecs.html
229+
:returns: slugified unicode representation of `value`
230+
:raises TypeError: If text is not an instance of str
231+
"""
232+
value = safe_decode(value, incoming, errors)
233+
# NOTE(aababilov): no need to use safe_(encode|decode) here:
234+
# encodings are always "ascii", error handling is always "ignore"
235+
# and types are always known (first: unicode; second: str)
236+
value = unicodedata.normalize("NFKD", value).encode(
237+
"ascii", "ignore").decode("ascii")
238+
value = SLUGIFY_STRIP_RE.sub("", value).strip().lower()
239+
return SLUGIFY_HYPHENATE_RE.sub("-", value)

0 commit comments

Comments
 (0)