Merge "Cleanup Oslo Incubator code"
This commit is contained in:
		@@ -1,7 +0,0 @@
 | 
			
		||||
[DEFAULT]
 | 
			
		||||
 | 
			
		||||
# The list of modules to copy from openstack-common
 | 
			
		||||
modules=gettextutils,jsonutils,strutils,timeutils
 | 
			
		||||
 | 
			
		||||
# The base module to hold the copy of openstack.common
 | 
			
		||||
base=tackerclient
 | 
			
		||||
@@ -10,3 +10,6 @@ python-keystoneclient!=1.8.0,!=2.1.0,>=1.6.0 # Apache-2.0
 | 
			
		||||
simplejson>=2.2.0 # MIT
 | 
			
		||||
six>=1.9.0 # MIT
 | 
			
		||||
Babel>=1.3 # BSD
 | 
			
		||||
 | 
			
		||||
oslo.i18n>=2.1.0  # Apache-2.0
 | 
			
		||||
oslo.utils>=3.5.0  # Apache-2.0
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										50
									
								
								tackerclient/common/_i18n.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								tackerclient/common/_i18n.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
			
		||||
# Copyright 2016 OpenStack Foundation
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
# not use this file except in compliance with the License. You may obtain
 | 
			
		||||
# a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
# License for the specific language governing permissions and limitations
 | 
			
		||||
# under the License.
 | 
			
		||||
 | 
			
		||||
"""oslo.i18n integration module.
 | 
			
		||||
 | 
			
		||||
See http://docs.openstack.org/developer/oslo.i18n/usage.html .
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import oslo_i18n
 | 
			
		||||
 | 
			
		||||
DOMAIN = "tackerclient"
 | 
			
		||||
 | 
			
		||||
_translators = oslo_i18n.TranslatorFactory(domain=DOMAIN)
 | 
			
		||||
 | 
			
		||||
# The primary translation function using the well-known name "_"
 | 
			
		||||
_ = _translators.primary
 | 
			
		||||
 | 
			
		||||
# The contextual translation function using the name "_C"
 | 
			
		||||
# requires oslo.i18n >=2.1.0
 | 
			
		||||
_C = _translators.contextual_form
 | 
			
		||||
 | 
			
		||||
# The plural translation function using the name "_P"
 | 
			
		||||
# requires oslo.i18n >=2.1.0
 | 
			
		||||
_P = _translators.plural_form
 | 
			
		||||
 | 
			
		||||
# Translators for log levels.
 | 
			
		||||
#
 | 
			
		||||
# The abbreviated names are meant to reflect the usual use of a short
 | 
			
		||||
# name like '_'. The "L" is for "log" and the other letter comes from
 | 
			
		||||
# the level.
 | 
			
		||||
_LI = _translators.log_info
 | 
			
		||||
_LW = _translators.log_warning
 | 
			
		||||
_LE = _translators.log_error
 | 
			
		||||
_LC = _translators.log_critical
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_available_languages():
 | 
			
		||||
        return oslo_i18n.get_available_languages(DOMAIN)
 | 
			
		||||
@@ -1,17 +0,0 @@
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
#    not use this file except in compliance with the License. You may obtain
 | 
			
		||||
#    a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#         http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
#    Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
import six
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
six.add_move(six.MovedModule('mox', 'mox', 'mox3.mox'))
 | 
			
		||||
@@ -1,320 +0,0 @@
 | 
			
		||||
# Copyright 2012 Red Hat, Inc.
 | 
			
		||||
# Copyright 2013 IBM Corp.
 | 
			
		||||
# All Rights Reserved.
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
#    not use this file except in compliance with the License. You may obtain
 | 
			
		||||
#    a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#         http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
#    Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
gettext for openstack-common modules.
 | 
			
		||||
 | 
			
		||||
Usual usage in an openstack.common module:
 | 
			
		||||
 | 
			
		||||
    from tackerclient.openstack.common.gettextutils import _
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import copy
 | 
			
		||||
import gettext
 | 
			
		||||
import logging
 | 
			
		||||
import os
 | 
			
		||||
import re
 | 
			
		||||
import UserString
 | 
			
		||||
 | 
			
		||||
from babel import localedata
 | 
			
		||||
import six
 | 
			
		||||
 | 
			
		||||
_localedir = os.environ.get('tackerclient'.upper() + '_LOCALEDIR')
 | 
			
		||||
_t = gettext.translation('tackerclient', localedir=_localedir, fallback=True)
 | 
			
		||||
 | 
			
		||||
_AVAILABLE_LANGUAGES = {}
 | 
			
		||||
USE_LAZY = False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def enable_lazy():
 | 
			
		||||
    """Convenience function for configuring _() to use lazy gettext
 | 
			
		||||
 | 
			
		||||
    Call this at the start of execution to enable the gettextutils._
 | 
			
		||||
    function to use lazy gettext functionality. This is useful if
 | 
			
		||||
    your project is importing _ directly instead of using the
 | 
			
		||||
    gettextutils.install() way of importing the _ function.
 | 
			
		||||
    """
 | 
			
		||||
    global USE_LAZY
 | 
			
		||||
    USE_LAZY = True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _(msg):
 | 
			
		||||
    if USE_LAZY:
 | 
			
		||||
        return Message(msg, 'tackerclient')
 | 
			
		||||
    else:
 | 
			
		||||
        return _t.ugettext(msg)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def install(domain, lazy=False):
 | 
			
		||||
    """Install a _() function using the given translation domain.
 | 
			
		||||
 | 
			
		||||
    Given a translation domain, install a _() function using gettext's
 | 
			
		||||
    install() function.
 | 
			
		||||
 | 
			
		||||
    The main difference from gettext.install() is that we allow
 | 
			
		||||
    overriding the default localedir (e.g. /usr/share/locale) using
 | 
			
		||||
    a translation-domain-specific environment variable (e.g.
 | 
			
		||||
    NOVA_LOCALEDIR).
 | 
			
		||||
 | 
			
		||||
    :param domain: the translation domain
 | 
			
		||||
    :param lazy: indicates whether or not to install the lazy _() function.
 | 
			
		||||
                 The lazy _() introduces a way to do deferred translation
 | 
			
		||||
                 of messages by installing a _ that builds Message objects,
 | 
			
		||||
                 instead of strings, which can then be lazily translated into
 | 
			
		||||
                 any available locale.
 | 
			
		||||
    """
 | 
			
		||||
    if lazy:
 | 
			
		||||
        # NOTE(mrodden): Lazy gettext functionality.
 | 
			
		||||
        #
 | 
			
		||||
        # The following introduces a deferred way to do translations on
 | 
			
		||||
        # messages in OpenStack. We override the standard _() function
 | 
			
		||||
        # and % (format string) operation to build Message objects that can
 | 
			
		||||
        # later be translated when we have more information.
 | 
			
		||||
        #
 | 
			
		||||
        # Also included below is an example LocaleHandler that translates
 | 
			
		||||
        # Messages to an associated locale, effectively allowing many logs,
 | 
			
		||||
        # each with their own locale.
 | 
			
		||||
 | 
			
		||||
        def _lazy_gettext(msg):
 | 
			
		||||
            """Create and return a Message object.
 | 
			
		||||
 | 
			
		||||
            Lazy gettext function for a given domain, it is a factory method
 | 
			
		||||
            for a project/module to get a lazy gettext function for its own
 | 
			
		||||
            translation domain (i.e. nova, glance, cinder, etc.)
 | 
			
		||||
 | 
			
		||||
            Message encapsulates a string so that we can translate
 | 
			
		||||
            it later when needed.
 | 
			
		||||
            """
 | 
			
		||||
            return Message(msg, domain)
 | 
			
		||||
 | 
			
		||||
        import __builtin__
 | 
			
		||||
        __builtin__.__dict__['_'] = _lazy_gettext
 | 
			
		||||
    else:
 | 
			
		||||
        localedir = '%s_LOCALEDIR' % domain.upper()
 | 
			
		||||
        gettext.install(domain,
 | 
			
		||||
                        localedir=os.environ.get(localedir),
 | 
			
		||||
                        unicode=True)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Message(UserString.UserString, object):
 | 
			
		||||
    """Class used to encapsulate translatable messages."""
 | 
			
		||||
    def __init__(self, msg, domain):
 | 
			
		||||
        # _msg is the gettext msgid and should never change
 | 
			
		||||
        self._msg = msg
 | 
			
		||||
        self._left_extra_msg = ''
 | 
			
		||||
        self._right_extra_msg = ''
 | 
			
		||||
        self.params = None
 | 
			
		||||
        self.locale = None
 | 
			
		||||
        self.domain = domain
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def data(self):
 | 
			
		||||
        # NOTE(mrodden): this should always resolve to a unicode string
 | 
			
		||||
        # that best represents the state of the message currently
 | 
			
		||||
 | 
			
		||||
        localedir = os.environ.get(self.domain.upper() + '_LOCALEDIR')
 | 
			
		||||
        if self.locale:
 | 
			
		||||
            lang = gettext.translation(self.domain,
 | 
			
		||||
                                       localedir=localedir,
 | 
			
		||||
                                       languages=[self.locale],
 | 
			
		||||
                                       fallback=True)
 | 
			
		||||
        else:
 | 
			
		||||
            # use system locale for translations
 | 
			
		||||
            lang = gettext.translation(self.domain,
 | 
			
		||||
                                       localedir=localedir,
 | 
			
		||||
                                       fallback=True)
 | 
			
		||||
 | 
			
		||||
        full_msg = (self._left_extra_msg +
 | 
			
		||||
                    lang.ugettext(self._msg) +
 | 
			
		||||
                    self._right_extra_msg)
 | 
			
		||||
 | 
			
		||||
        if self.params is not None:
 | 
			
		||||
            full_msg = full_msg % self.params
 | 
			
		||||
 | 
			
		||||
        return six.text_type(full_msg)
 | 
			
		||||
 | 
			
		||||
    def _save_dictionary_parameter(self, dict_param):
 | 
			
		||||
        full_msg = self.data
 | 
			
		||||
        # look for %(blah) fields in string;
 | 
			
		||||
        # ignore %% and deal with the
 | 
			
		||||
        # case where % is first character on the line
 | 
			
		||||
        keys = re.findall('(?:[^%]|^)?%\((\w*)\)[a-z]', full_msg)
 | 
			
		||||
 | 
			
		||||
        # if we don't find any %(blah) blocks but have a %s
 | 
			
		||||
        if not keys and re.findall('(?:[^%]|^)%[a-z]', full_msg):
 | 
			
		||||
            # apparently the full dictionary is the parameter
 | 
			
		||||
            params = copy.deepcopy(dict_param)
 | 
			
		||||
        else:
 | 
			
		||||
            params = {}
 | 
			
		||||
            for key in keys:
 | 
			
		||||
                try:
 | 
			
		||||
                    params[key] = copy.deepcopy(dict_param[key])
 | 
			
		||||
                except TypeError:
 | 
			
		||||
                    # cast uncopyable thing to unicode string
 | 
			
		||||
                    params[key] = unicode(dict_param[key])
 | 
			
		||||
 | 
			
		||||
        return params
 | 
			
		||||
 | 
			
		||||
    def _save_parameters(self, other):
 | 
			
		||||
        # we check for None later to see if
 | 
			
		||||
        # we actually have parameters to inject,
 | 
			
		||||
        # so encapsulate if our parameter is actually None
 | 
			
		||||
        if other is None:
 | 
			
		||||
            self.params = (other, )
 | 
			
		||||
        elif isinstance(other, dict):
 | 
			
		||||
            self.params = self._save_dictionary_parameter(other)
 | 
			
		||||
        else:
 | 
			
		||||
            # fallback to casting to unicode,
 | 
			
		||||
            # this will handle the problematic python code-like
 | 
			
		||||
            # objects that cannot be deep-copied
 | 
			
		||||
            try:
 | 
			
		||||
                self.params = copy.deepcopy(other)
 | 
			
		||||
            except TypeError:
 | 
			
		||||
                self.params = unicode(other)
 | 
			
		||||
 | 
			
		||||
        return self
 | 
			
		||||
 | 
			
		||||
    # overrides to be more string-like
 | 
			
		||||
    def __unicode__(self):
 | 
			
		||||
        return self.data
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return self.data.encode('utf-8')
 | 
			
		||||
 | 
			
		||||
    def __getstate__(self):
 | 
			
		||||
        to_copy = ['_msg', '_right_extra_msg', '_left_extra_msg',
 | 
			
		||||
                   'domain', 'params', 'locale']
 | 
			
		||||
        new_dict = self.__dict__.fromkeys(to_copy)
 | 
			
		||||
        for attr in to_copy:
 | 
			
		||||
            new_dict[attr] = copy.deepcopy(self.__dict__[attr])
 | 
			
		||||
 | 
			
		||||
        return new_dict
 | 
			
		||||
 | 
			
		||||
    def __setstate__(self, state):
 | 
			
		||||
        for (k, v) in state.items():
 | 
			
		||||
            setattr(self, k, v)
 | 
			
		||||
 | 
			
		||||
    # operator overloads
 | 
			
		||||
    def __add__(self, other):
 | 
			
		||||
        copied = copy.deepcopy(self)
 | 
			
		||||
        copied._right_extra_msg += other.__str__()
 | 
			
		||||
        return copied
 | 
			
		||||
 | 
			
		||||
    def __radd__(self, other):
 | 
			
		||||
        copied = copy.deepcopy(self)
 | 
			
		||||
        copied._left_extra_msg += other.__str__()
 | 
			
		||||
        return copied
 | 
			
		||||
 | 
			
		||||
    def __mod__(self, other):
 | 
			
		||||
        # do a format string to catch and raise
 | 
			
		||||
        # any possible KeyErrors from missing parameters
 | 
			
		||||
        self.data % other
 | 
			
		||||
        copied = copy.deepcopy(self)
 | 
			
		||||
        return copied._save_parameters(other)
 | 
			
		||||
 | 
			
		||||
    def __mul__(self, other):
 | 
			
		||||
        return self.data * other
 | 
			
		||||
 | 
			
		||||
    def __rmul__(self, other):
 | 
			
		||||
        return other * self.data
 | 
			
		||||
 | 
			
		||||
    def __getitem__(self, key):
 | 
			
		||||
        return self.data[key]
 | 
			
		||||
 | 
			
		||||
    def __getslice__(self, start, end):
 | 
			
		||||
        return self.data.__getslice__(start, end)
 | 
			
		||||
 | 
			
		||||
    def __getattribute__(self, name):
 | 
			
		||||
        # NOTE(mrodden): handle lossy operations that we can't deal with yet
 | 
			
		||||
        # These override the UserString implementation, since UserString
 | 
			
		||||
        # uses our __class__ attribute to try and build a new message
 | 
			
		||||
        # after running the inner data string through the operation.
 | 
			
		||||
        # At that point, we have lost the gettext message id and can just
 | 
			
		||||
        # safely resolve to a string instead.
 | 
			
		||||
        ops = ['capitalize', 'center', 'decode', 'encode',
 | 
			
		||||
               'expandtabs', 'ljust', 'lstrip', 'replace', 'rjust', 'rstrip',
 | 
			
		||||
               'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
 | 
			
		||||
        if name in ops:
 | 
			
		||||
            return getattr(self.data, name)
 | 
			
		||||
        else:
 | 
			
		||||
            return UserString.UserString.__getattribute__(self, name)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_available_languages(domain):
 | 
			
		||||
    """Lists the available languages for the given translation domain.
 | 
			
		||||
 | 
			
		||||
    :param domain: the domain to get languages for
 | 
			
		||||
    """
 | 
			
		||||
    if domain in _AVAILABLE_LANGUAGES:
 | 
			
		||||
        return copy.copy(_AVAILABLE_LANGUAGES[domain])
 | 
			
		||||
 | 
			
		||||
    localedir = '%s_LOCALEDIR' % domain.upper()
 | 
			
		||||
    find = lambda x: gettext.find(domain,
 | 
			
		||||
                                  localedir=os.environ.get(localedir),
 | 
			
		||||
                                  languages=[x])
 | 
			
		||||
 | 
			
		||||
    # NOTE(mrodden): en_US should always be available (and first in case
 | 
			
		||||
    # order matters) since our in-line message strings are en_US
 | 
			
		||||
    language_list = ['en_US']
 | 
			
		||||
    # NOTE(luisg): Babel <1.0 used a function called list(), which was
 | 
			
		||||
    # renamed to locale_identifiers() in >=1.0, the requirements master list
 | 
			
		||||
    # requires >=0.9.6, uncapped, so defensively work with both. We can remove
 | 
			
		||||
    # this check when the master list updates to >=1.0, and all projects udpate
 | 
			
		||||
    list_identifiers = (getattr(localedata, 'list', None) or
 | 
			
		||||
                        getattr(localedata, 'locale_identifiers'))
 | 
			
		||||
    locale_identifiers = list_identifiers()
 | 
			
		||||
    for i in locale_identifiers:
 | 
			
		||||
        if find(i) is not None:
 | 
			
		||||
            language_list.append(i)
 | 
			
		||||
    _AVAILABLE_LANGUAGES[domain] = language_list
 | 
			
		||||
    return copy.copy(language_list)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_localized_message(message, user_locale):
 | 
			
		||||
    """Gets a localized version of the given message in the given locale."""
 | 
			
		||||
    if isinstance(message, Message):
 | 
			
		||||
        if user_locale:
 | 
			
		||||
            message.locale = user_locale
 | 
			
		||||
        return unicode(message)
 | 
			
		||||
    else:
 | 
			
		||||
        return message
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LocaleHandler(logging.Handler):
 | 
			
		||||
    """Handler that can have a locale associated to translate Messages.
 | 
			
		||||
 | 
			
		||||
    A quick example of how to utilize the Message class above.
 | 
			
		||||
    LocaleHandler takes a locale and a target logging.Handler object
 | 
			
		||||
    to forward LogRecord objects to after translating the internal Message.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, locale, target):
 | 
			
		||||
        """Initialize a LocaleHandler
 | 
			
		||||
 | 
			
		||||
        :param locale: locale to use for translating messages
 | 
			
		||||
        :param target: logging.Handler object to forward
 | 
			
		||||
                       LogRecord objects to after translation
 | 
			
		||||
        """
 | 
			
		||||
        logging.Handler.__init__(self)
 | 
			
		||||
        self.locale = locale
 | 
			
		||||
        self.target = target
 | 
			
		||||
 | 
			
		||||
    def emit(self, record):
 | 
			
		||||
        if isinstance(record.msg, Message):
 | 
			
		||||
            # set the locale and resolve to a string
 | 
			
		||||
            record.msg.locale = self.locale
 | 
			
		||||
 | 
			
		||||
        self.target.emit(record)
 | 
			
		||||
@@ -1,67 +0,0 @@
 | 
			
		||||
# Copyright 2011 OpenStack Foundation.
 | 
			
		||||
# All Rights Reserved.
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
#    not use this file except in compliance with the License. You may obtain
 | 
			
		||||
#    a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#         http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
#    Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
Import related utilities and helper functions.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import sys
 | 
			
		||||
import traceback
 | 
			
		||||
from tackerclient.openstack.common.gettextutils import _
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def import_class(import_str):
 | 
			
		||||
    """Returns a class from a string including module and class."""
 | 
			
		||||
    mod_str, _sep, class_str = import_str.rpartition('.')
 | 
			
		||||
    try:
 | 
			
		||||
        __import__(mod_str)
 | 
			
		||||
        return getattr(sys.modules[mod_str], class_str)
 | 
			
		||||
    except (ValueError, AttributeError):
 | 
			
		||||
        raise ImportError(_('Class %s cannot be found (%s)') %
 | 
			
		||||
                          (class_str,
 | 
			
		||||
                           traceback.format_exception(*sys.exc_info())))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def import_object(import_str, *args, **kwargs):
 | 
			
		||||
    """Import a class and return an instance of it."""
 | 
			
		||||
    return import_class(import_str)(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def import_object_ns(name_space, import_str, *args, **kwargs):
 | 
			
		||||
    """Tries to import object from default namespace.
 | 
			
		||||
 | 
			
		||||
    Imports a class and return an instance of it, first by trying
 | 
			
		||||
    to find the class in a default namespace, then failing back to
 | 
			
		||||
    a full path if not found in the default namespace.
 | 
			
		||||
    """
 | 
			
		||||
    import_value = "%s.%s" % (name_space, import_str)
 | 
			
		||||
    try:
 | 
			
		||||
        return import_class(import_value)(*args, **kwargs)
 | 
			
		||||
    except ImportError:
 | 
			
		||||
        return import_class(import_str)(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def import_module(import_str):
 | 
			
		||||
    """Import a module."""
 | 
			
		||||
    __import__(import_str)
 | 
			
		||||
    return sys.modules[import_str]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def try_import(import_str, default=None):
 | 
			
		||||
    """Try to import a module and if it fails return default."""
 | 
			
		||||
    try:
 | 
			
		||||
        return import_module(import_str)
 | 
			
		||||
    except ImportError:
 | 
			
		||||
        return default
 | 
			
		||||
@@ -1,186 +0,0 @@
 | 
			
		||||
# Copyright 2010 United States Government as represented by the
 | 
			
		||||
# Administrator of the National Aeronautics and Space Administration.
 | 
			
		||||
# Copyright 2011 Justin Santa Barbara
 | 
			
		||||
# All Rights Reserved.
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
#    not use this file except in compliance with the License. You may obtain
 | 
			
		||||
#    a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#         http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
#    Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
'''
 | 
			
		||||
JSON related utilities.
 | 
			
		||||
 | 
			
		||||
This module provides a few things:
 | 
			
		||||
 | 
			
		||||
    1) A handy function for getting an object down to something that can be
 | 
			
		||||
    JSON serialized.  See to_primitive().
 | 
			
		||||
 | 
			
		||||
    2) Wrappers around loads() and dumps().  The dumps() wrapper will
 | 
			
		||||
    automatically use to_primitive() for you if needed.
 | 
			
		||||
 | 
			
		||||
    3) This sets up anyjson to use the loads() and dumps() wrappers if anyjson
 | 
			
		||||
    is available.
 | 
			
		||||
'''
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import codecs
 | 
			
		||||
import datetime
 | 
			
		||||
import functools
 | 
			
		||||
import inspect
 | 
			
		||||
import itertools
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
if sys.version_info < (2, 7):
 | 
			
		||||
    # On Python <= 2.6, json module is not C boosted, so try to use
 | 
			
		||||
    # simplejson module if available
 | 
			
		||||
    try:
 | 
			
		||||
        import simplejson as json
 | 
			
		||||
    except ImportError:
 | 
			
		||||
        import json
 | 
			
		||||
else:
 | 
			
		||||
    import json
 | 
			
		||||
 | 
			
		||||
import six
 | 
			
		||||
import six.moves.xmlrpc_client as xmlrpclib
 | 
			
		||||
 | 
			
		||||
from tackerclient.openstack.common import gettextutils
 | 
			
		||||
from tackerclient.openstack.common import importutils
 | 
			
		||||
from tackerclient.openstack.common import strutils
 | 
			
		||||
from tackerclient.openstack.common import timeutils
 | 
			
		||||
 | 
			
		||||
netaddr = importutils.try_import("netaddr")
 | 
			
		||||
 | 
			
		||||
_nasty_type_tests = [inspect.ismodule, inspect.isclass, inspect.ismethod,
 | 
			
		||||
                     inspect.isfunction, inspect.isgeneratorfunction,
 | 
			
		||||
                     inspect.isgenerator, inspect.istraceback, inspect.isframe,
 | 
			
		||||
                     inspect.iscode, inspect.isbuiltin, inspect.isroutine,
 | 
			
		||||
                     inspect.isabstract]
 | 
			
		||||
 | 
			
		||||
_simple_types = (six.string_types + six.integer_types
 | 
			
		||||
                 + (type(None), bool, float))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_primitive(value, convert_instances=False, convert_datetime=True,
 | 
			
		||||
                 level=0, max_depth=3):
 | 
			
		||||
    """Convert a complex object into primitives.
 | 
			
		||||
 | 
			
		||||
    Handy for JSON serialization. We can optionally handle instances,
 | 
			
		||||
    but since this is a recursive function, we could have cyclical
 | 
			
		||||
    data structures.
 | 
			
		||||
 | 
			
		||||
    To handle cyclical data structures we could track the actual objects
 | 
			
		||||
    visited in a set, but not all objects are hashable. Instead we just
 | 
			
		||||
    track the depth of the object inspections and don't go too deep.
 | 
			
		||||
 | 
			
		||||
    Therefore, convert_instances=True is lossy ... be aware.
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    # handle obvious types first - order of basic types determined by running
 | 
			
		||||
    # full tests on nova project, resulting in the following counts:
 | 
			
		||||
    # 572754 <type 'NoneType'>
 | 
			
		||||
    # 460353 <type 'int'>
 | 
			
		||||
    # 379632 <type 'unicode'>
 | 
			
		||||
    # 274610 <type 'str'>
 | 
			
		||||
    # 199918 <type 'dict'>
 | 
			
		||||
    # 114200 <type 'datetime.datetime'>
 | 
			
		||||
    #  51817 <type 'bool'>
 | 
			
		||||
    #  26164 <type 'list'>
 | 
			
		||||
    #   6491 <type 'float'>
 | 
			
		||||
    #    283 <type 'tuple'>
 | 
			
		||||
    #     19 <type 'long'>
 | 
			
		||||
    if isinstance(value, _simple_types):
 | 
			
		||||
        return value
 | 
			
		||||
 | 
			
		||||
    if isinstance(value, datetime.datetime):
 | 
			
		||||
        if convert_datetime:
 | 
			
		||||
            return timeutils.strtime(value)
 | 
			
		||||
        else:
 | 
			
		||||
            return value
 | 
			
		||||
 | 
			
		||||
    # value of itertools.count doesn't get caught by nasty_type_tests
 | 
			
		||||
    # and results in infinite loop when list(value) is called.
 | 
			
		||||
    if type(value) == itertools.count:
 | 
			
		||||
        return six.text_type(value)
 | 
			
		||||
 | 
			
		||||
    # FIXME(vish): Workaround for LP bug 852095. Without this workaround,
 | 
			
		||||
    #              tests that raise an exception in a mocked method that
 | 
			
		||||
    #              has a @wrap_exception with a notifier will fail. If
 | 
			
		||||
    #              we up the dependency to 0.5.4 (when it is released) we
 | 
			
		||||
    #              can remove this workaround.
 | 
			
		||||
    if getattr(value, '__module__', None) == 'mox':
 | 
			
		||||
        return 'mock'
 | 
			
		||||
 | 
			
		||||
    if level > max_depth:
 | 
			
		||||
        return '?'
 | 
			
		||||
 | 
			
		||||
    # The try block may not be necessary after the class check above,
 | 
			
		||||
    # but just in case ...
 | 
			
		||||
    try:
 | 
			
		||||
        recursive = functools.partial(to_primitive,
 | 
			
		||||
                                      convert_instances=convert_instances,
 | 
			
		||||
                                      convert_datetime=convert_datetime,
 | 
			
		||||
                                      level=level,
 | 
			
		||||
                                      max_depth=max_depth)
 | 
			
		||||
        if isinstance(value, dict):
 | 
			
		||||
            return dict((k, recursive(v)) for k, v in six.iteritems(value))
 | 
			
		||||
        elif isinstance(value, (list, tuple)):
 | 
			
		||||
            return [recursive(lv) for lv in value]
 | 
			
		||||
 | 
			
		||||
        # It's not clear why xmlrpclib created their own DateTime type, but
 | 
			
		||||
        # for our purposes, make it a datetime type which is explicitly
 | 
			
		||||
        # handled
 | 
			
		||||
        if isinstance(value, xmlrpclib.DateTime):
 | 
			
		||||
            value = datetime.datetime(*tuple(value.timetuple())[:6])
 | 
			
		||||
 | 
			
		||||
        if convert_datetime and isinstance(value, datetime.datetime):
 | 
			
		||||
            return timeutils.strtime(value)
 | 
			
		||||
        elif isinstance(value, gettextutils.Message):
 | 
			
		||||
            return value.data
 | 
			
		||||
        elif hasattr(value, 'iteritems'):
 | 
			
		||||
            return recursive(dict(value.iteritems()), level=level + 1)
 | 
			
		||||
        elif hasattr(value, '__iter__'):
 | 
			
		||||
            return recursive(list(value))
 | 
			
		||||
        elif convert_instances and hasattr(value, '__dict__'):
 | 
			
		||||
            # Likely an instance of something. Watch for cycles.
 | 
			
		||||
            # Ignore class member vars.
 | 
			
		||||
            return recursive(value.__dict__, level=level + 1)
 | 
			
		||||
        elif netaddr and isinstance(value, netaddr.IPAddress):
 | 
			
		||||
            return six.text_type(value)
 | 
			
		||||
        else:
 | 
			
		||||
            if any(test(value) for test in _nasty_type_tests):
 | 
			
		||||
                return six.text_type(value)
 | 
			
		||||
            return value
 | 
			
		||||
    except TypeError:
 | 
			
		||||
        # Class objects are tricky since they may define something like
 | 
			
		||||
        # __iter__ defined but it isn't callable as list().
 | 
			
		||||
        return six.text_type(value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def dumps(value, default=to_primitive, **kwargs):
 | 
			
		||||
    return json.dumps(value, default=default, **kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def loads(s, encoding='utf-8'):
 | 
			
		||||
    return json.loads(strutils.safe_decode(s, encoding))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def load(fp, encoding='utf-8'):
 | 
			
		||||
    return json.load(codecs.getreader(encoding)(fp))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    import anyjson
 | 
			
		||||
except ImportError:
 | 
			
		||||
    pass
 | 
			
		||||
else:
 | 
			
		||||
    anyjson._modules.append((__name__, 'dumps', TypeError,
 | 
			
		||||
                                       'loads', ValueError, 'load'))
 | 
			
		||||
    anyjson.force_implementation(__name__)
 | 
			
		||||
@@ -1,216 +0,0 @@
 | 
			
		||||
# Copyright 2011 OpenStack Foundation.
 | 
			
		||||
# All Rights Reserved.
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
#    not use this file except in compliance with the License. You may obtain
 | 
			
		||||
#    a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#         http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
#    Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
System-level utilities and helper functions.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import re
 | 
			
		||||
import sys
 | 
			
		||||
import unicodedata
 | 
			
		||||
 | 
			
		||||
import six
 | 
			
		||||
 | 
			
		||||
from tackerclient.openstack.common.gettextutils import _  # noqa
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Used for looking up extensions of text
 | 
			
		||||
# to their 'multiplied' byte amount
 | 
			
		||||
BYTE_MULTIPLIERS = {
 | 
			
		||||
    '': 1,
 | 
			
		||||
    't': 1024 ** 4,
 | 
			
		||||
    'g': 1024 ** 3,
 | 
			
		||||
    'm': 1024 ** 2,
 | 
			
		||||
    'k': 1024,
 | 
			
		||||
}
 | 
			
		||||
BYTE_REGEX = re.compile(r'(^-?\d+)(\D*)')
 | 
			
		||||
 | 
			
		||||
TRUE_STRINGS = ('1', 't', 'true', 'on', 'y', 'yes')
 | 
			
		||||
FALSE_STRINGS = ('0', 'f', 'false', 'off', 'n', 'no')
 | 
			
		||||
 | 
			
		||||
SLUGIFY_STRIP_RE = re.compile(r"[^\w\s-]")
 | 
			
		||||
SLUGIFY_HYPHENATE_RE = re.compile(r"[-\s]+")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def int_from_bool_as_string(subject):
 | 
			
		||||
    """Interpret a string as a boolean and return either 1 or 0.
 | 
			
		||||
 | 
			
		||||
    Any string value in:
 | 
			
		||||
 | 
			
		||||
        ('True', 'true', 'On', 'on', '1')
 | 
			
		||||
 | 
			
		||||
    is interpreted as a boolean True.
 | 
			
		||||
 | 
			
		||||
    Useful for JSON-decoded stuff and config file parsing
 | 
			
		||||
    """
 | 
			
		||||
    return bool_from_string(subject) and 1 or 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def bool_from_string(subject, strict=False):
 | 
			
		||||
    """Interpret a string as a boolean.
 | 
			
		||||
 | 
			
		||||
    A case-insensitive match is performed such that strings matching 't',
 | 
			
		||||
    'true', 'on', 'y', 'yes', or '1' are considered True and, when
 | 
			
		||||
    `strict=False`, anything else is considered False.
 | 
			
		||||
 | 
			
		||||
    Useful for JSON-decoded stuff and config file parsing.
 | 
			
		||||
 | 
			
		||||
    If `strict=True`, unrecognized values, including None, will raise a
 | 
			
		||||
    ValueError which is useful when parsing values passed in from an API call.
 | 
			
		||||
    Strings yielding False are 'f', 'false', 'off', 'n', 'no', or '0'.
 | 
			
		||||
    """
 | 
			
		||||
    if not isinstance(subject, six.string_types):
 | 
			
		||||
        subject = str(subject)
 | 
			
		||||
 | 
			
		||||
    lowered = subject.strip().lower()
 | 
			
		||||
 | 
			
		||||
    if lowered in TRUE_STRINGS:
 | 
			
		||||
        return True
 | 
			
		||||
    elif lowered in FALSE_STRINGS:
 | 
			
		||||
        return False
 | 
			
		||||
    elif strict:
 | 
			
		||||
        acceptable = ', '.join(
 | 
			
		||||
            "'%s'" % s for s in sorted(TRUE_STRINGS + FALSE_STRINGS))
 | 
			
		||||
        msg = _("Unrecognized value '%(val)s', acceptable values are:"
 | 
			
		||||
                " %(acceptable)s") % {'val': subject,
 | 
			
		||||
                                      'acceptable': acceptable}
 | 
			
		||||
        raise ValueError(msg)
 | 
			
		||||
    else:
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def safe_decode(text, incoming=None, errors='strict'):
 | 
			
		||||
    """Decodes incoming str using `incoming` if they're not already unicode.
 | 
			
		||||
 | 
			
		||||
    :param incoming: Text's current encoding
 | 
			
		||||
    :param errors: Errors handling policy. See here for valid
 | 
			
		||||
        values http://docs.python.org/2/library/codecs.html
 | 
			
		||||
    :returns: text or a unicode `incoming` encoded
 | 
			
		||||
                representation of it.
 | 
			
		||||
    :raises TypeError: If text is not an isntance of str
 | 
			
		||||
    """
 | 
			
		||||
    if not isinstance(text, six.string_types):
 | 
			
		||||
        raise TypeError("%s can't be decoded" % type(text))
 | 
			
		||||
 | 
			
		||||
    if isinstance(text, six.text_type):
 | 
			
		||||
        return text
 | 
			
		||||
 | 
			
		||||
    if not incoming:
 | 
			
		||||
        incoming = (sys.stdin.encoding or
 | 
			
		||||
                    sys.getdefaultencoding())
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        return text.decode(incoming, errors)
 | 
			
		||||
    except UnicodeDecodeError:
 | 
			
		||||
        # Note(flaper87) If we get here, it means that
 | 
			
		||||
        # sys.stdin.encoding / sys.getdefaultencoding
 | 
			
		||||
        # didn't return a suitable encoding to decode
 | 
			
		||||
        # text. This happens mostly when global LANG
 | 
			
		||||
        # var is not set correctly and there's no
 | 
			
		||||
        # default encoding. In this case, most likely
 | 
			
		||||
        # python will use ASCII or ANSI encoders as
 | 
			
		||||
        # default encodings but they won't be capable
 | 
			
		||||
        # of decoding non-ASCII characters.
 | 
			
		||||
        #
 | 
			
		||||
        # Also, UTF-8 is being used since it's an ASCII
 | 
			
		||||
        # extension.
 | 
			
		||||
        return text.decode('utf-8', errors)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def safe_encode(text, incoming=None,
 | 
			
		||||
                encoding='utf-8', errors='strict'):
 | 
			
		||||
    """Encodes incoming str/unicode using `encoding`.
 | 
			
		||||
 | 
			
		||||
    If incoming is not specified, text is expected to be encoded with
 | 
			
		||||
    current python's default encoding. (`sys.getdefaultencoding`)
 | 
			
		||||
 | 
			
		||||
    :param incoming: Text's current encoding
 | 
			
		||||
    :param encoding: Expected encoding for text (Default UTF-8)
 | 
			
		||||
    :param errors: Errors handling policy. See here for valid
 | 
			
		||||
        values http://docs.python.org/2/library/codecs.html
 | 
			
		||||
    :returns: text or a bytestring `encoding` encoded
 | 
			
		||||
                representation of it.
 | 
			
		||||
    :raises TypeError: If text is not an isntance of str
 | 
			
		||||
    """
 | 
			
		||||
    if not isinstance(text, six.string_types):
 | 
			
		||||
        raise TypeError(_("%s can't be encoded") % type(text).capitalize())
 | 
			
		||||
 | 
			
		||||
    if not incoming:
 | 
			
		||||
        incoming = (sys.stdin.encoding or
 | 
			
		||||
                    sys.getdefaultencoding())
 | 
			
		||||
 | 
			
		||||
    if isinstance(text, six.text_type):
 | 
			
		||||
        return text.encode(encoding, errors)
 | 
			
		||||
    elif text and encoding != incoming:
 | 
			
		||||
        # Decode text before encoding it with `encoding`
 | 
			
		||||
        text = safe_decode(text, incoming, errors)
 | 
			
		||||
        return text.encode(encoding, errors)
 | 
			
		||||
 | 
			
		||||
    return text
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_bytes(text, default=0):
 | 
			
		||||
    """Converts a string into an integer of bytes.
 | 
			
		||||
 | 
			
		||||
    Looks at the last characters of the text to determine
 | 
			
		||||
    what conversion is needed to turn the input text into a byte number.
 | 
			
		||||
    Supports "B, K(B), M(B), G(B), and T(B)". (case insensitive)
 | 
			
		||||
 | 
			
		||||
    :param text: String input for bytes size conversion.
 | 
			
		||||
    :param default: Default return value when text is blank.
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    match = BYTE_REGEX.search(text)
 | 
			
		||||
    if match:
 | 
			
		||||
        magnitude = int(match.group(1))
 | 
			
		||||
        mult_key_org = match.group(2)
 | 
			
		||||
        if not mult_key_org:
 | 
			
		||||
            return magnitude
 | 
			
		||||
    elif text:
 | 
			
		||||
        msg = _('Invalid string format: %s') % text
 | 
			
		||||
        raise TypeError(msg)
 | 
			
		||||
    else:
 | 
			
		||||
        return default
 | 
			
		||||
    mult_key = mult_key_org.lower().replace('b', '', 1)
 | 
			
		||||
    multiplier = BYTE_MULTIPLIERS.get(mult_key)
 | 
			
		||||
    if multiplier is None:
 | 
			
		||||
        msg = _('Unknown byte multiplier: %s') % mult_key_org
 | 
			
		||||
        raise TypeError(msg)
 | 
			
		||||
    return magnitude * multiplier
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_slug(value, incoming=None, errors="strict"):
 | 
			
		||||
    """Normalize string.
 | 
			
		||||
 | 
			
		||||
    Convert to lowercase, remove non-word characters, and convert spaces
 | 
			
		||||
    to hyphens.
 | 
			
		||||
 | 
			
		||||
    Inspired by Django's `slugify` filter.
 | 
			
		||||
 | 
			
		||||
    :param value: Text to slugify
 | 
			
		||||
    :param incoming: Text's current encoding
 | 
			
		||||
    :param errors: Errors handling policy. See here for valid
 | 
			
		||||
        values http://docs.python.org/2/library/codecs.html
 | 
			
		||||
    :returns: slugified unicode representation of `value`
 | 
			
		||||
    :raises TypeError: If text is not an instance of str
 | 
			
		||||
    """
 | 
			
		||||
    value = safe_decode(value, incoming, errors)
 | 
			
		||||
    # NOTE(aababilov): no need to use safe_(encode|decode) here:
 | 
			
		||||
    # encodings are always "ascii", error handling is always "ignore"
 | 
			
		||||
    # and types are always known (first: unicode; second: str)
 | 
			
		||||
    value = unicodedata.normalize("NFKD", value).encode(
 | 
			
		||||
        "ascii", "ignore").decode("ascii")
 | 
			
		||||
    value = SLUGIFY_STRIP_RE.sub("", value).strip().lower()
 | 
			
		||||
    return SLUGIFY_HYPHENATE_RE.sub("-", value)
 | 
			
		||||
@@ -1,186 +0,0 @@
 | 
			
		||||
# Copyright 2011 OpenStack Foundation.
 | 
			
		||||
# All Rights Reserved.
 | 
			
		||||
#
 | 
			
		||||
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 | 
			
		||||
#    not use this file except in compliance with the License. You may obtain
 | 
			
		||||
#    a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#         http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
#    Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
Time related utilities and helper functions.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import calendar
 | 
			
		||||
import datetime
 | 
			
		||||
 | 
			
		||||
import iso8601
 | 
			
		||||
import six
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# ISO 8601 extended time format with microseconds
 | 
			
		||||
_ISO8601_TIME_FORMAT_SUBSECOND = '%Y-%m-%dT%H:%M:%S.%f'
 | 
			
		||||
_ISO8601_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S'
 | 
			
		||||
PERFECT_TIME_FORMAT = _ISO8601_TIME_FORMAT_SUBSECOND
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def isotime(at=None, subsecond=False):
 | 
			
		||||
    """Stringify time in ISO 8601 format."""
 | 
			
		||||
    if not at:
 | 
			
		||||
        at = utcnow()
 | 
			
		||||
    st = at.strftime(_ISO8601_TIME_FORMAT
 | 
			
		||||
                     if not subsecond
 | 
			
		||||
                     else _ISO8601_TIME_FORMAT_SUBSECOND)
 | 
			
		||||
    tz = at.tzinfo.tzname(None) if at.tzinfo else 'UTC'
 | 
			
		||||
    st += ('Z' if tz == 'UTC' else tz)
 | 
			
		||||
    return st
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def parse_isotime(timestr):
 | 
			
		||||
    """Parse time from ISO 8601 format."""
 | 
			
		||||
    try:
 | 
			
		||||
        return iso8601.parse_date(timestr)
 | 
			
		||||
    except iso8601.ParseError as e:
 | 
			
		||||
        raise ValueError(unicode(e))
 | 
			
		||||
    except TypeError as e:
 | 
			
		||||
        raise ValueError(unicode(e))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def strtime(at=None, fmt=PERFECT_TIME_FORMAT):
 | 
			
		||||
    """Returns formatted utcnow."""
 | 
			
		||||
    if not at:
 | 
			
		||||
        at = utcnow()
 | 
			
		||||
    return at.strftime(fmt)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def parse_strtime(timestr, fmt=PERFECT_TIME_FORMAT):
 | 
			
		||||
    """Turn a formatted time back into a datetime."""
 | 
			
		||||
    return datetime.datetime.strptime(timestr, fmt)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def normalize_time(timestamp):
 | 
			
		||||
    """Normalize time in arbitrary timezone to UTC naive object."""
 | 
			
		||||
    offset = timestamp.utcoffset()
 | 
			
		||||
    if offset is None:
 | 
			
		||||
        return timestamp
 | 
			
		||||
    return timestamp.replace(tzinfo=None) - offset
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def is_older_than(before, seconds):
 | 
			
		||||
    """Return True if before is older than seconds."""
 | 
			
		||||
    if isinstance(before, six.string_types):
 | 
			
		||||
        before = parse_strtime(before).replace(tzinfo=None)
 | 
			
		||||
    return utcnow() - before > datetime.timedelta(seconds=seconds)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def is_newer_than(after, seconds):
 | 
			
		||||
    """Return True if after is newer than seconds."""
 | 
			
		||||
    if isinstance(after, six.string_types):
 | 
			
		||||
        after = parse_strtime(after).replace(tzinfo=None)
 | 
			
		||||
    return after - utcnow() > datetime.timedelta(seconds=seconds)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def utcnow_ts():
 | 
			
		||||
    """Timestamp version of our utcnow function."""
 | 
			
		||||
    return calendar.timegm(utcnow().timetuple())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def utcnow():
 | 
			
		||||
    """Overridable version of utils.utcnow."""
 | 
			
		||||
    if utcnow.override_time:
 | 
			
		||||
        try:
 | 
			
		||||
            return utcnow.override_time.pop(0)
 | 
			
		||||
        except AttributeError:
 | 
			
		||||
            return utcnow.override_time
 | 
			
		||||
    return datetime.datetime.utcnow()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def iso8601_from_timestamp(timestamp):
 | 
			
		||||
    """Returns a iso8601 formated date from timestamp."""
 | 
			
		||||
    return isotime(datetime.datetime.utcfromtimestamp(timestamp))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
utcnow.override_time = None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def set_time_override(override_time=datetime.datetime.utcnow()):
 | 
			
		||||
    """Overrides utils.utcnow.
 | 
			
		||||
 | 
			
		||||
    Make it return a constant time or a list thereof, one at a time.
 | 
			
		||||
    """
 | 
			
		||||
    utcnow.override_time = override_time
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def advance_time_delta(timedelta):
 | 
			
		||||
    """Advance overridden time using a datetime.timedelta."""
 | 
			
		||||
    assert(not utcnow.override_time is None)
 | 
			
		||||
    try:
 | 
			
		||||
        for dt in utcnow.override_time:
 | 
			
		||||
            dt += timedelta
 | 
			
		||||
    except TypeError:
 | 
			
		||||
        utcnow.override_time += timedelta
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def advance_time_seconds(seconds):
 | 
			
		||||
    """Advance overridden time by seconds."""
 | 
			
		||||
    advance_time_delta(datetime.timedelta(0, seconds))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def clear_time_override():
 | 
			
		||||
    """Remove the overridden time."""
 | 
			
		||||
    utcnow.override_time = None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def marshall_now(now=None):
 | 
			
		||||
    """Make an rpc-safe datetime with microseconds.
 | 
			
		||||
 | 
			
		||||
    Note: tzinfo is stripped, but not required for relative times.
 | 
			
		||||
    """
 | 
			
		||||
    if not now:
 | 
			
		||||
        now = utcnow()
 | 
			
		||||
    return dict(day=now.day, month=now.month, year=now.year, hour=now.hour,
 | 
			
		||||
                minute=now.minute, second=now.second,
 | 
			
		||||
                microsecond=now.microsecond)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def unmarshall_time(tyme):
 | 
			
		||||
    """Unmarshall a datetime dict."""
 | 
			
		||||
    return datetime.datetime(day=tyme['day'],
 | 
			
		||||
                             month=tyme['month'],
 | 
			
		||||
                             year=tyme['year'],
 | 
			
		||||
                             hour=tyme['hour'],
 | 
			
		||||
                             minute=tyme['minute'],
 | 
			
		||||
                             second=tyme['second'],
 | 
			
		||||
                             microsecond=tyme['microsecond'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def delta_seconds(before, after):
 | 
			
		||||
    """Return the difference between two timing objects.
 | 
			
		||||
 | 
			
		||||
    Compute the difference in seconds between two date, time, or
 | 
			
		||||
    datetime objects (as a float, to microsecond resolution).
 | 
			
		||||
    """
 | 
			
		||||
    delta = after - before
 | 
			
		||||
    try:
 | 
			
		||||
        return delta.total_seconds()
 | 
			
		||||
    except AttributeError:
 | 
			
		||||
        return ((delta.days * 24 * 3600) + delta.seconds +
 | 
			
		||||
                float(delta.microseconds) / (10 ** 6))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def is_soon(dt, window):
 | 
			
		||||
    """Determines if time is going to happen in the next window seconds.
 | 
			
		||||
 | 
			
		||||
    :params dt: the time
 | 
			
		||||
    :params window: minimum seconds to remain to consider the time not soon
 | 
			
		||||
 | 
			
		||||
    :return: True if expiration is within the given duration
 | 
			
		||||
    """
 | 
			
		||||
    soon = (utcnow() + datetime.timedelta(seconds=window))
 | 
			
		||||
    return normalize_time(dt) <= soon
 | 
			
		||||
@@ -14,9 +14,9 @@
 | 
			
		||||
#    under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
from tackerclient.common._i18n import _
 | 
			
		||||
from tackerclient.common import exceptions
 | 
			
		||||
from tackerclient.common import utils
 | 
			
		||||
from tackerclient.openstack.common.gettextutils import _
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
API_NAME = 'nfv-orchestration'
 | 
			
		||||
 
 | 
			
		||||
@@ -27,10 +27,10 @@ from cliff import show
 | 
			
		||||
from oslo_serialization import jsonutils
 | 
			
		||||
import six
 | 
			
		||||
 | 
			
		||||
from tackerclient.common._i18n import _
 | 
			
		||||
from tackerclient.common import command
 | 
			
		||||
from tackerclient.common import exceptions
 | 
			
		||||
from tackerclient.common import utils
 | 
			
		||||
from tackerclient.openstack.common.gettextutils import _
 | 
			
		||||
 | 
			
		||||
HEX_ELEM = '[0-9A-Fa-f]'
 | 
			
		||||
UUID_PATTERN = '-'.join([HEX_ELEM + '{8}', HEX_ELEM + '{4}',
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@
 | 
			
		||||
#    under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
from tackerclient.openstack.common.gettextutils import _
 | 
			
		||||
from tackerclient.common._i18n import _
 | 
			
		||||
from tackerclient.tacker import v1_0 as cmd_base
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								tox.ini
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								tox.ini
									
									
									
									
									
								
							@@ -28,4 +28,4 @@ commands = python setup.py testr --coverage --testr-args='{posargs}'
 | 
			
		||||
# E125 continuation line does not distinguish itself from next logical line
 | 
			
		||||
ignore = E125
 | 
			
		||||
show-source = true
 | 
			
		||||
exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,tools
 | 
			
		||||
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,tools
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user