~ubuntu-branches/ubuntu/utopic/python-ceilometerclient/utopic

« back to all changes in this revision

Viewing changes to ceilometerclient/openstack/common/strutils.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short, James Page, Chuck Short
  • Date: 2014-01-21 09:53:01 UTC
  • mfrom: (1.1.6)
  • Revision ID: package-import@ubuntu.com-20140121095301-cwxsrtdgddkzprjx
Tags: 1.0.8-0ubuntu1
[ James Page ]
* d/control: Add missing BD on python-babel. 

[ Chuck Short ]
* New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 re
 
21
import sys
 
22
import unicodedata
 
23
 
 
24
import six
 
25
 
 
26
from ceilometerclient.openstack.common.gettextutils import _  # noqa
 
27
 
 
28
 
 
29
# Used for looking up extensions of text
 
30
# to their 'multiplied' byte amount
 
31
BYTE_MULTIPLIERS = {
 
32
    '': 1,
 
33
    't': 1024 ** 4,
 
34
    'g': 1024 ** 3,
 
35
    'm': 1024 ** 2,
 
36
    'k': 1024,
 
37
}
 
38
BYTE_REGEX = re.compile(r'(^-?\d+)(\D*)')
 
39
 
 
40
TRUE_STRINGS = ('1', 't', 'true', 'on', 'y', 'yes')
 
41
FALSE_STRINGS = ('0', 'f', 'false', 'off', 'n', 'no')
 
42
 
 
43
SLUGIFY_STRIP_RE = re.compile(r"[^\w\s-]")
 
44
SLUGIFY_HYPHENATE_RE = re.compile(r"[-\s]+")
 
45
 
 
46
 
 
47
def int_from_bool_as_string(subject):
 
48
    """Interpret a string as a boolean and return either 1 or 0.
 
49
 
 
50
    Any string value in:
 
51
 
 
52
        ('True', 'true', 'On', 'on', '1')
 
53
 
 
54
    is interpreted as a boolean True.
 
55
 
 
56
    Useful for JSON-decoded stuff and config file parsing
 
57
    """
 
58
    return bool_from_string(subject) and 1 or 0
 
59
 
 
60
 
 
61
def bool_from_string(subject, strict=False):
 
62
    """Interpret a string as a boolean.
 
63
 
 
64
    A case-insensitive match is performed such that strings matching 't',
 
65
    'true', 'on', 'y', 'yes', or '1' are considered True and, when
 
66
    `strict=False`, anything else is considered False.
 
67
 
 
68
    Useful for JSON-decoded stuff and config file parsing.
 
69
 
 
70
    If `strict=True`, unrecognized values, including None, will raise a
 
71
    ValueError which is useful when parsing values passed in from an API call.
 
72
    Strings yielding False are 'f', 'false', 'off', 'n', 'no', or '0'.
 
73
    """
 
74
    if not isinstance(subject, six.string_types):
 
75
        subject = str(subject)
 
76
 
 
77
    lowered = subject.strip().lower()
 
78
 
 
79
    if lowered in TRUE_STRINGS:
 
80
        return True
 
81
    elif lowered in FALSE_STRINGS:
 
82
        return False
 
83
    elif strict:
 
84
        acceptable = ', '.join(
 
85
            "'%s'" % s for s in sorted(TRUE_STRINGS + FALSE_STRINGS))
 
86
        msg = _("Unrecognized value '%(val)s', acceptable values are:"
 
87
                " %(acceptable)s") % {'val': subject,
 
88
                                      'acceptable': acceptable}
 
89
        raise ValueError(msg)
 
90
    else:
 
91
        return False
 
92
 
 
93
 
 
94
def safe_decode(text, incoming=None, errors='strict'):
 
95
    """Decodes incoming str using `incoming` if they're not already unicode.
 
96
 
 
97
    :param incoming: Text's current encoding
 
98
    :param errors: Errors handling policy. See here for valid
 
99
        values http://docs.python.org/2/library/codecs.html
 
100
    :returns: text or a unicode `incoming` encoded
 
101
                representation of it.
 
102
    :raises TypeError: If text is not an instance of str
 
103
    """
 
104
    if not isinstance(text, six.string_types):
 
105
        raise TypeError("%s can't be decoded" % type(text))
 
106
 
 
107
    if isinstance(text, six.text_type):
 
108
        return text
 
109
 
 
110
    if not incoming:
 
111
        incoming = (sys.stdin.encoding or
 
112
                    sys.getdefaultencoding())
 
113
 
 
114
    try:
 
115
        return text.decode(incoming, errors)
 
116
    except UnicodeDecodeError:
 
117
        # Note(flaper87) If we get here, it means that
 
118
        # sys.stdin.encoding / sys.getdefaultencoding
 
119
        # didn't return a suitable encoding to decode
 
120
        # text. This happens mostly when global LANG
 
121
        # var is not set correctly and there's no
 
122
        # default encoding. In this case, most likely
 
123
        # python will use ASCII or ANSI encoders as
 
124
        # default encodings but they won't be capable
 
125
        # of decoding non-ASCII characters.
 
126
        #
 
127
        # Also, UTF-8 is being used since it's an ASCII
 
128
        # extension.
 
129
        return text.decode('utf-8', errors)
 
130
 
 
131
 
 
132
def safe_encode(text, incoming=None,
 
133
                encoding='utf-8', errors='strict'):
 
134
    """Encodes incoming str/unicode using `encoding`.
 
135
 
 
136
    If incoming is not specified, text is expected to be encoded with
 
137
    current python's default encoding. (`sys.getdefaultencoding`)
 
138
 
 
139
    :param incoming: Text's current encoding
 
140
    :param encoding: Expected encoding for text (Default UTF-8)
 
141
    :param errors: Errors handling policy. See here for valid
 
142
        values http://docs.python.org/2/library/codecs.html
 
143
    :returns: text or a bytestring `encoding` encoded
 
144
                representation of it.
 
145
    :raises TypeError: If text is not an instance of str
 
146
    """
 
147
    if not isinstance(text, six.string_types):
 
148
        raise TypeError("%s can't be encoded" % type(text))
 
149
 
 
150
    if not incoming:
 
151
        incoming = (sys.stdin.encoding or
 
152
                    sys.getdefaultencoding())
 
153
 
 
154
    if isinstance(text, six.text_type):
 
155
        return text.encode(encoding, errors)
 
156
    elif text and encoding != incoming:
 
157
        # Decode text before encoding it with `encoding`
 
158
        text = safe_decode(text, incoming, errors)
 
159
        return text.encode(encoding, errors)
 
160
 
 
161
    return text
 
162
 
 
163
 
 
164
def to_bytes(text, default=0):
 
165
    """Converts a string into an integer of bytes.
 
166
 
 
167
    Looks at the last characters of the text to determine
 
168
    what conversion is needed to turn the input text into a byte number.
 
169
    Supports "B, K(B), M(B), G(B), and T(B)". (case insensitive)
 
170
 
 
171
    :param text: String input for bytes size conversion.
 
172
    :param default: Default return value when text is blank.
 
173
 
 
174
    """
 
175
    match = BYTE_REGEX.search(text)
 
176
    if match:
 
177
        magnitude = int(match.group(1))
 
178
        mult_key_org = match.group(2)
 
179
        if not mult_key_org:
 
180
            return magnitude
 
181
    elif text:
 
182
        msg = _('Invalid string format: %s') % text
 
183
        raise TypeError(msg)
 
184
    else:
 
185
        return default
 
186
    mult_key = mult_key_org.lower().replace('b', '', 1)
 
187
    multiplier = BYTE_MULTIPLIERS.get(mult_key)
 
188
    if multiplier is None:
 
189
        msg = _('Unknown byte multiplier: %s') % mult_key_org
 
190
        raise TypeError(msg)
 
191
    return magnitude * multiplier
 
192
 
 
193
 
 
194
def to_slug(value, incoming=None, errors="strict"):
 
195
    """Normalize string.
 
196
 
 
197
    Convert to lowercase, remove non-word characters, and convert spaces
 
198
    to hyphens.
 
199
 
 
200
    Inspired by Django's `slugify` filter.
 
201
 
 
202
    :param value: Text to slugify
 
203
    :param incoming: Text's current encoding
 
204
    :param errors: Errors handling policy. See here for valid
 
205
        values http://docs.python.org/2/library/codecs.html
 
206
    :returns: slugified unicode representation of `value`
 
207
    :raises TypeError: If text is not an instance of str
 
208
    """
 
209
    value = safe_decode(value, incoming, errors)
 
210
    # NOTE(aababilov): no need to use safe_(encode|decode) here:
 
211
    # encodings are always "ascii", error handling is always "ignore"
 
212
    # and types are always known (first: unicode; second: str)
 
213
    value = unicodedata.normalize("NFKD", value).encode(
 
214
        "ascii", "ignore").decode("ascii")
 
215
    value = SLUGIFY_STRIP_RE.sub("", value).strip().lower()
 
216
    return SLUGIFY_HYPHENATE_RE.sub("-", value)