resync oslo-incubator code

This commit syncs the following oslo incubator modules used by Trove
and all dependent changes.

__init__.py

6b048e79 Let oslotest manage the six.move setting for mox

context.py

411ba356 Simplify is_user_context method
9b73877b Add a RequestContext.from_dict method
85d1ce6e Python 3: enable tests/unit/middleware/test_request_id.py
c0d357bb Add model_query() to db.sqlalchemy.utils module

eventlet_backdoor.py

a3220c51 add list_opts to all modules with configuration options
5d40e143 Remove code that moved to oslo.i18n
90ae24bf Remove redundant default=None for config options
fcf517d7 Update oslo log messages with translation domains
ad17a697 Fix filter() usage due to python 3 compability
8b2b0b74 Use hacking import_exceptions for gettextutils._
12bcdb71 Remove vim header

log.py

943cb94a Merge "Make use_syslog=True log to syslog via /dev/log"
8345204c Merge "add list_opts to all modules with configuration options"
ac4330dd Make use_syslog=True log to syslog via /dev/log
df774ff4 Import PublishErrorsHandler from oslo.messaging
a3220c51 add list_opts to all modules with configuration options
6c706c5c Delete graduated serialization files
5d40e143 Remove code that moved to oslo.i18n
6ff6b4b4 Switch oslo-incubator to use oslo.utils and remove old modules
aa744115 log: add missing space in error message
037dee00 Set stevedore log level to WARN by default
759bd879 Merge "Set keystonemiddleware and routes.middleware to log on WARN level"
71d072f1 Merge "Except socket.error if syslog isn't running"
37c00918 Add unicode coercion of logged messages to ContextFormatter
66144135 Correct coercion of logged message to unicode

loopingcall.py

5d40e143 Remove code that moved to oslo.i18n
e3773930 Changes calcuation of variable delay
ab5d5f1c Use timestamp in loopingcall
bc48099a Log the function name of looping call
fb4e863c Remove deprecated LoopingCall
fcf517d7 Update oslo log messages with translation domains
8b2b0b74 Use hacking import_exceptions for gettextutils._
12bcdb71 Remove vim header

service.py

5d40e143 Remove code that moved to oslo.i18n
6ede600f rpc, notifier: remove deprecated modules

threadgroup.py

1523f000 threadgroup: don't log GreenletExit
5a1a0166 Make stop_timers() method public
fdc88831 Add graceful stop function to ThreadGroup.stop
5f8ace05 Merge "threadgroup: use threading rather than greenthread"
2d06d6ca Simple typo correction
4d18b57a threadgroup: use threading rather than greenthread
25ff65e9 Make wait & stop methods work on all threads
12bcdb71 Remove vim header
9d3c34b5 Add a link method to Thread

versionutils.py

5d40e143 Remove code that moved to oslo.i18n
1c3ecfcd Enhance versionutils.deprecated to work with classes
7d42c107 Merge "Add Kilo release name to versionutils"
9a462718 Add Kilo release name to versionutils
a2ad3a25 Allow deprecated decorator to specify no plan for removal
05ae498b Add JUNO as a target to versionutils module
de4adbc4 pep8: fixed multiple violations

Closes-Bug: #1366189
Change-Id: I2289452a9b838e22bb6d4dd1a854fbeb326042d2
This commit is contained in:
Amrith Kumar 2014-12-15 13:59:10 -05:00 committed by amrith
parent 4d31433b1e
commit 6ea94c4bf7
13 changed files with 281 additions and 117 deletions

View File

@ -17,6 +17,8 @@ import re
import webob.exc
import wsgi
from oslo.utils import strutils
from trove.common import exception
from trove.openstack.common import log as logging
from trove.common.i18n import _
@ -59,7 +61,7 @@ class TenantBasedAuth(object):
match_for_tenant = self.tenant_scoped_url.match(request.path_info)
if (match_for_tenant and
tenant_id == match_for_tenant.group('tenant_id')):
LOG.debug(logging.mask_password(
LOG.debug(strutils.mask_password(
_("Authorized tenant '%(tenant_id)s' request: "
"%(request)s") %
{'tenant_id': tenant_id, 'request': request}))

View File

@ -191,7 +191,11 @@ def poll_until(retriever, condition=lambda value: value,
raise loopingcall.LoopingCallDone(retvalue=obj)
if time_out is not None and time.time() - start_time > time_out:
raise exception.PollTimeOut
lc = loopingcall.LoopingCall(f=poll_and_check).start(sleep_time, True)
lc = loopingcall.FixedIntervalLoopingCall(
f=poll_and_check).start(
sleep_time, True)
return lc.wait()

View File

@ -15,6 +15,8 @@
import webob.exc
from oslo.utils import strutils
from trove.common import exception
from trove.common import pagination
from trove.common import wsgi
@ -82,8 +84,8 @@ class UserController(wsgi.Controller):
def create(self, req, body, tenant_id, instance_id):
"""Creates a set of users."""
LOG.info(_("Creating users for instance '%s'") % instance_id)
LOG.info(_("req : '%s'\n\n") % logging.mask_password(req))
LOG.info(_("body : '%s'\n\n") % logging.mask_password(body))
LOG.info(_("req : '%s'\n\n") % strutils.mask_password(req))
LOG.info(_("body : '%s'\n\n") % strutils.mask_password(body))
context = req.environ[wsgi.CONTEXT_KEY]
users = body['users']
try:
@ -135,7 +137,7 @@ class UserController(wsgi.Controller):
def update(self, req, body, tenant_id, instance_id, id):
"""Change attributes for one user."""
LOG.info(_("Updating user attributes for instance '%s'") % instance_id)
LOG.info(_("req : '%s'\n\n") % logging.mask_password(req))
LOG.info(_("req : '%s'\n\n") % strutils.mask_password(req))
context = req.environ[wsgi.CONTEXT_KEY]
id = correct_id_with_req(id, req)
username, hostname = unquote_user_host(id)
@ -157,7 +159,7 @@ class UserController(wsgi.Controller):
def update_all(self, req, body, tenant_id, instance_id):
"""Change the password of one or more users."""
LOG.info(_("Updating user passwords for instance '%s'") % instance_id)
LOG.info(_("req : '%s'\n\n") % logging.mask_password(req))
LOG.info(_("req : '%s'\n\n") % strutils.mask_password(req))
context = req.environ[wsgi.CONTEXT_KEY]
users = body['users']
model_users = []

View File

@ -15,6 +15,8 @@
import webob.exc
from oslo.utils import strutils
from trove.common import cfg
from trove.common import exception
from trove.common import pagination
@ -184,8 +186,8 @@ class InstanceController(wsgi.Controller):
# TODO(hub-cap): turn this into middleware
LOG.info(_LI("Creating a database instance for tenant '%s'"),
tenant_id)
LOG.debug("req : '%s'\n\n", logging.mask_password(req))
LOG.debug("body : '%s'\n\n", logging.mask_password(body))
LOG.debug("req : '%s'\n\n", strutils.mask_password(req))
LOG.debug("body : '%s'\n\n", strutils.mask_password(body))
context = req.environ[wsgi.CONTEXT_KEY]
datastore_args = body['instance'].get('datastore', {})
datastore, datastore_version = (
@ -273,8 +275,8 @@ class InstanceController(wsgi.Controller):
Updates the instance to set or unset one or more attributes.
"""
LOG.info(_LI("Editing instance for tenant id %s."), tenant_id)
LOG.debug("req: %s", logging.mask_password(req))
LOG.debug("body: %s", logging.mask_password(body))
LOG.debug("req: %s", strutils.mask_password(req))
LOG.debug("body: %s", strutils.mask_password(body))
context = req.environ[wsgi.CONTEXT_KEY]
instance = models.Instance.load(context, id)

View File

@ -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'))

View File

@ -0,0 +1,45 @@
# 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
"""
try:
import oslo.i18n
# NOTE(dhellmann): This reference to o-s-l-o will be replaced by the
# application name when this module is synced into the separate
# repository. It is OK to have more than one translation function
# using the same domain, since there will still only be one message
# catalog.
_translators = oslo.i18n.TranslatorFactory(domain='trove')
# The primary translation function using the well-known name "_"
_ = _translators.primary
# 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
except ImportError:
# NOTE(dims): Support for cases where a project wants to use
# code from trove-incubator, but is not ready to be internationalized
# (like tempest)
_ = _LI = _LW = _LE = _LC = lambda x: x

View File

@ -25,7 +25,7 @@ import uuid
def generate_request_id():
return 'req-%s' % str(uuid.uuid4())
return b'req-' + str(uuid.uuid4()).encode('ascii')
class RequestContext(object):
@ -77,6 +77,21 @@ class RequestContext(object):
'instance_uuid': self.instance_uuid,
'user_identity': user_idt}
@classmethod
def from_dict(cls, ctx):
return cls(
auth_token=ctx.get("auth_token"),
user=ctx.get("user"),
tenant=ctx.get("tenant"),
domain=ctx.get("domain"),
user_domain=ctx.get("user_domain"),
project_domain=ctx.get("project_domain"),
is_admin=ctx.get("is_admin", False),
read_only=ctx.get("read_only", False),
show_deleted=ctx.get("show_deleted", False),
request_id=ctx.get("request_id"),
instance_uuid=ctx.get("instance_uuid"))
def get_admin_context(show_deleted=False):
context = RequestContext(None,
@ -98,3 +113,10 @@ def get_context_from_function_and_args(function, args, kwargs):
return arg
return None
def is_user_context(context):
"""Indicates if the request context is a normal user."""
if not context or context.is_admin:
return False
return context.user_id and context.project_id

View File

@ -1,5 +1,3 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2012 OpenStack Foundation.
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
@ -18,6 +16,7 @@
from __future__ import print_function
import copy
import errno
import gc
import os
@ -31,7 +30,7 @@ import eventlet.backdoor
import greenlet
from oslo.config import cfg
from trove.openstack.common.gettextutils import _ # noqa
from trove.openstack.common._i18n import _LI
from trove.openstack.common import log as logging
help_for_backdoor_port = (
@ -43,7 +42,6 @@ help_for_backdoor_port = (
"chosen port is displayed in the service's log file.")
eventlet_backdoor_opts = [
cfg.StrOpt('backdoor_port',
default=None,
help="Enable eventlet backdoor. %s" % help_for_backdoor_port)
]
@ -52,6 +50,12 @@ CONF.register_opts(eventlet_backdoor_opts)
LOG = logging.getLogger(__name__)
def list_opts():
"""Entry point for oslo.config-generator.
"""
return [(None, copy.deepcopy(eventlet_backdoor_opts))]
class EventletBackdoorConfigValueError(Exception):
def __init__(self, port_range, help_msg, ex):
msg = ('Invalid backdoor_port configuration %(range)s: %(ex)s. '
@ -66,7 +70,7 @@ def _dont_use_this():
def _find_objects(t):
return filter(lambda o: isinstance(o, t), gc.get_objects())
return [o for o in gc.get_objects() if isinstance(o, t)]
def _print_greenthreads():
@ -139,8 +143,10 @@ def initialize_if_enabled():
# In the case of backdoor port being zero, a port number is assigned by
# listen(). In any case, pull the port number out here.
port = sock.getsockname()[1]
LOG.info(_('Eventlet backdoor listening on %(port)s for process %(pid)d') %
{'port': port, 'pid': os.getpid()})
LOG.info(
_LI('Eventlet backdoor listening on %(port)s for process %(pid)d') %
{'port': port, 'pid': os.getpid()}
)
eventlet.spawn_n(eventlet.backdoor.backdoor_server, sock,
locals=backdoor_locals)
return port

View File

@ -27,28 +27,27 @@ It also allows setting of formatting information through conf.
"""
import copy
import inspect
import itertools
import logging
import logging.config
import logging.handlers
import os
import socket
import sys
import traceback
from oslo.config import cfg
from oslo.serialization import jsonutils
from oslo.utils import importutils
import six
from six import moves
_PY26 = sys.version_info[0:2] == (2, 6)
from trove.openstack.common.gettextutils import _
from trove.openstack.common import importutils
from trove.openstack.common import jsonutils
from trove.openstack.common._i18n import _
from trove.openstack.common import local
# NOTE(flaper87): Pls, remove when graduating this module
# from the incubator.
from trove.openstack.common.strutils import mask_password # noqa
_DEFAULT_LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
@ -126,7 +125,9 @@ DEFAULT_LOG_LEVELS = ['amqp=WARN', 'amqplib=WARN', 'boto=WARN',
'qpid=WARN', 'sqlalchemy=WARN', 'suds=INFO',
'oslo.messaging=INFO', 'iso8601=WARN',
'requests.packages.urllib3.connectionpool=WARN',
'urllib3.connectionpool=WARN', 'websocket=WARN']
'urllib3.connectionpool=WARN', 'websocket=WARN',
"keystonemiddleware=WARN", "routes.middleware=WARN",
"stevedore=WARN"]
log_opts = [
cfg.StrOpt('logging_context_format_string',
@ -174,6 +175,16 @@ CONF.register_cli_opts(logging_cli_opts)
CONF.register_opts(generic_log_opts)
CONF.register_opts(log_opts)
def list_opts():
"""Entry point for oslo.config-generator."""
return [(None, copy.deepcopy(common_cli_opts)),
(None, copy.deepcopy(logging_cli_opts)),
(None, copy.deepcopy(generic_log_opts)),
(None, copy.deepcopy(log_opts)),
]
# our new audit level
# NOTE(jkoelker) Since we synthesized an audit level, make the logging
# module aware of it so it acts like other levels.
@ -300,11 +311,10 @@ class ContextAdapter(BaseLoggerAdapter):
self.warn(stdmsg, *args, **kwargs)
def process(self, msg, kwargs):
# NOTE(mrodden): catch any Message/other object and
# coerce to unicode before they can get
# to the python logging and possibly
# cause string encoding trouble
if not isinstance(msg, six.string_types):
# NOTE(jecarey): If msg is not unicode, coerce it into unicode
# before it can get to the python logging and
# possibly cause string encoding trouble
if not isinstance(msg, six.text_type):
msg = six.text_type(msg)
if 'extra' not in kwargs:
@ -429,12 +439,12 @@ def set_defaults(logging_context_format_string=None,
# later in a backwards in-compatible change
if default_log_levels is not None:
cfg.set_defaults(
log_opts,
default_log_levels=default_log_levels)
log_opts,
default_log_levels=default_log_levels)
if logging_context_format_string is not None:
cfg.set_defaults(
log_opts,
logging_context_format_string=logging_context_format_string)
log_opts,
logging_context_format_string=logging_context_format_string)
def _find_facility_from_conf():
@ -483,18 +493,6 @@ def _setup_logging_from_conf(project, version):
for handler in log_root.handlers:
log_root.removeHandler(handler)
if CONF.use_syslog:
facility = _find_facility_from_conf()
# TODO(bogdando) use the format provided by RFCSysLogHandler
# after existing syslog format deprecation in J
if CONF.use_syslog_rfc_format:
syslog = RFCSysLogHandler(address='/dev/log',
facility=facility)
else:
syslog = logging.handlers.SysLogHandler(address='/dev/log',
facility=facility)
log_root.addHandler(syslog)
logpath = _get_log_file_path()
if logpath:
filelog = logging.handlers.WatchedFileHandler(logpath)
@ -511,14 +509,9 @@ def _setup_logging_from_conf(project, version):
log_root.addHandler(streamlog)
if CONF.publish_errors:
try:
handler = importutils.import_object(
"trove.openstack.common.log_handler.PublishErrorsHandler",
logging.ERROR)
except ImportError:
handler = importutils.import_object(
"oslo.messaging.notify.log_handler.PublishErrorsHandler",
logging.ERROR)
handler = importutils.import_object(
"oslo.messaging.notify.log_handler.PublishErrorsHandler",
logging.ERROR)
log_root.addHandler(handler)
datefmt = CONF.log_date_format
@ -553,6 +546,22 @@ def _setup_logging_from_conf(project, version):
else:
logger.setLevel(level_name)
if CONF.use_syslog:
try:
facility = _find_facility_from_conf()
# TODO(bogdando) use the format provided by RFCSysLogHandler
# after existing syslog format deprecation in J
if CONF.use_syslog_rfc_format:
syslog = RFCSysLogHandler(address='/dev/log',
facility=facility)
else:
syslog = logging.handlers.SysLogHandler(address='/dev/log',
facility=facility)
log_root.addHandler(syslog)
except socket.error:
log_root.error('Unable to add syslog handler. Verify that syslog '
'is running.')
_loggers = {}
@ -622,6 +631,12 @@ class ContextFormatter(logging.Formatter):
def format(self, record):
"""Uses contextstring if request_id is set, otherwise default."""
# NOTE(jecarey): If msg is not unicode, coerce it into unicode
# before it can get to the python logging and
# possibly cause string encoding trouble
if not isinstance(record.msg, six.text_type):
record.msg = six.text_type(record.msg)
# store project info
record.project = self.project
record.version = self.version

View File

@ -1,5 +1,3 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# Copyright 2011 Justin Santa Barbara
@ -18,31 +16,36 @@
# under the License.
import sys
import time
from eventlet import event
from eventlet import greenthread
from trove.openstack.common.gettextutils import _ # noqa
from trove.openstack.common._i18n import _LE, _LW
from trove.openstack.common import log as logging
from trove.openstack.common import timeutils
LOG = logging.getLogger(__name__)
# NOTE(zyluo): This lambda function was declared to avoid mocking collisions
# with time.time() called in the standard logging module
# during unittests.
_ts = lambda: time.time()
class LoopingCallDone(Exception):
"""Exception to break out and stop a LoopingCall.
"""Exception to break out and stop a LoopingCallBase.
The poll-function passed to LoopingCall can raise this exception to
The poll-function passed to LoopingCallBase can raise this exception to
break out of the loop normally. This is somewhat analogous to
StopIteration.
An optional return-value can be included as the argument to the exception;
this return-value will be returned by LoopingCall.wait()
this return-value will be returned by LoopingCallBase.wait()
"""
def __init__(self, retvalue=True):
""":param retvalue: Value that LoopingCall.wait() should return."""
""":param retvalue: Value that LoopingCallBase.wait() should return."""
self.retvalue = retvalue
@ -74,21 +77,22 @@ class FixedIntervalLoopingCall(LoopingCallBase):
try:
while self._running:
start = timeutils.utcnow()
start = _ts()
self.f(*self.args, **self.kw)
end = timeutils.utcnow()
end = _ts()
if not self._running:
break
delay = interval - timeutils.delta_seconds(start, end)
if delay <= 0:
LOG.warn(_('task run outlasted interval by %s sec') %
-delay)
greenthread.sleep(delay if delay > 0 else 0)
delay = end - start - interval
if delay > 0:
LOG.warn(_LW('task %(func_name)s run outlasted '
'interval by %(delay).2f sec'),
{'func_name': repr(self.f), 'delay': delay})
greenthread.sleep(-delay if delay < 0 else 0)
except LoopingCallDone as e:
self.stop()
done.send(e.retvalue)
except Exception:
LOG.exception(_('in fixed duration looping call'))
LOG.exception(_LE('in fixed duration looping call'))
done.send_exception(*sys.exc_info())
return
else:
@ -100,11 +104,6 @@ class FixedIntervalLoopingCall(LoopingCallBase):
return self.done
# TODO(mikal): this class name is deprecated in Havana and should be removed
# in the I release
LoopingCall = FixedIntervalLoopingCall
class DynamicLoopingCall(LoopingCallBase):
"""A looping call which sleeps until the next known event.
@ -128,14 +127,15 @@ class DynamicLoopingCall(LoopingCallBase):
if periodic_interval_max is not None:
idle = min(idle, periodic_interval_max)
LOG.debug(_('Dynamic looping call sleeping for %.02f '
'seconds'), idle)
LOG.debug('Dynamic looping call %(func_name)s sleeping '
'for %(idle).02f seconds',
{'func_name': repr(self.f), 'idle': idle})
greenthread.sleep(idle)
except LoopingCallDone as e:
self.stop()
done.send(e.retvalue)
except Exception:
LOG.exception(_('in dynamic looping call'))
LOG.exception(_LE('in dynamic looping call'))
done.send_exception(*sys.exc_info())
return
else:

View File

@ -38,7 +38,7 @@ from eventlet import event
from oslo.config import cfg
from trove.openstack.common import eventlet_backdoor
from trove.openstack.common.gettextutils import _LE, _LI, _LW
from trove.openstack.common._i18n import _LE, _LI, _LW
from trove.openstack.common import log as logging
from trove.openstack.common import systemd
from trove.openstack.common import threadgroup

View File

@ -1,5 +1,3 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -13,10 +11,10 @@
# 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 threading
import eventlet
from eventlet import greenpool
from eventlet import greenthread
from trove.openstack.common import log as logging
from trove.openstack.common import loopingcall
@ -48,9 +46,12 @@ class Thread(object):
def wait(self):
return self.thread.wait()
def link(self, func, *args, **kwargs):
self.thread.link(func, *args, **kwargs)
class ThreadGroup(object):
"""The point of the ThreadGroup classis to:
"""The point of the ThreadGroup class is to:
* keep track of timers and greenthreads (making it easier to stop them
when need be).
@ -79,21 +80,28 @@ class ThreadGroup(object):
gt = self.pool.spawn(callback, *args, **kwargs)
th = Thread(gt, self)
self.threads.append(th)
return th
def thread_done(self, thread):
self.threads.remove(thread)
def stop(self):
current = greenthread.getcurrent()
for x in self.threads:
def _stop_threads(self):
current = threading.current_thread()
# Iterate over a copy of self.threads so thread_done doesn't
# modify the list while we're iterating
for x in self.threads[:]:
if x is current:
# don't kill the current thread.
continue
try:
x.stop()
except eventlet.greenlet.GreenletExit:
pass
except Exception as ex:
LOG.exception(ex)
def stop_timers(self):
for x in self.timers:
try:
x.stop()
@ -101,6 +109,23 @@ class ThreadGroup(object):
LOG.exception(ex)
self.timers = []
def stop(self, graceful=False):
"""stop function has the option of graceful=True/False.
* In case of graceful=True, wait for all threads to be finished.
Never kill threads.
* In case of graceful=False, kill threads immediately.
"""
self.stop_timers()
if graceful:
# In case of graceful=True, wait for all threads to be
# finished, never kill threads
self.wait()
else:
# In case of graceful=False(Default), kill threads
# immediately
self._stop_threads()
def wait(self):
for x in self.timers:
try:
@ -109,8 +134,11 @@ class ThreadGroup(object):
pass
except Exception as ex:
LOG.exception(ex)
current = greenthread.getcurrent()
for x in self.threads:
current = threading.current_thread()
# Iterate over a copy of self.threads so thread_done doesn't
# modify the list while we're iterating
for x in self.threads[:]:
if x is current:
continue
try:

View File

@ -18,9 +18,12 @@ Helpers for comparing version strings.
"""
import functools
import pkg_resources
import inspect
from trove.openstack.common.gettextutils import _
import pkg_resources
import six
from trove.openstack.common._i18n import _
from trove.openstack.common import log as logging
@ -52,18 +55,36 @@ class deprecated(object):
>>> @deprecated(as_of=deprecated.ICEHOUSE, remove_in=+1)
... def c(): pass
4. Specifying the deprecated functionality will not be removed:
>>> @deprecated(as_of=deprecated.ICEHOUSE, remove_in=0)
... def d(): pass
5. Specifying a replacement, deprecated functionality will not be removed:
>>> @deprecated(as_of=deprecated.ICEHOUSE, in_favor_of='f()', remove_in=0)
... def e(): pass
"""
# NOTE(morganfainberg): Bexar is used for unit test purposes, it is
# expected we maintain a gap between Bexar and Folsom in this list.
BEXAR = 'B'
FOLSOM = 'F'
GRIZZLY = 'G'
HAVANA = 'H'
ICEHOUSE = 'I'
JUNO = 'J'
KILO = 'K'
_RELEASES = {
# NOTE(morganfainberg): Bexar is used for unit test purposes, it is
# expected we maintain a gap between Bexar and Folsom in this list.
'B': 'Bexar',
'F': 'Folsom',
'G': 'Grizzly',
'H': 'Havana',
'I': 'Icehouse',
'J': 'Juno',
'K': 'Kilo',
}
_deprecated_msg_with_alternative = _(
@ -74,6 +95,12 @@ class deprecated(object):
'%(what)s is deprecated as of %(as_of)s and may be '
'removed in %(remove_in)s. It will not be superseded.')
_deprecated_msg_with_alternative_no_removal = _(
'%(what)s is deprecated as of %(as_of)s in favor of %(in_favor_of)s.')
_deprecated_msg_with_no_alternative_no_removal = _(
'%(what)s is deprecated as of %(as_of)s. It will not be superseded.')
def __init__(self, as_of, in_favor_of=None, remove_in=2, what=None):
"""Initialize decorator
@ -91,16 +118,34 @@ class deprecated(object):
self.remove_in = remove_in
self.what = what
def __call__(self, func):
def __call__(self, func_or_cls):
if not self.what:
self.what = func.__name__ + '()'
self.what = func_or_cls.__name__ + '()'
msg, details = self._build_message()
@functools.wraps(func)
def wrapped(*args, **kwargs):
msg, details = self._build_message()
LOG.deprecated(msg, details)
return func(*args, **kwargs)
return wrapped
if inspect.isfunction(func_or_cls):
@six.wraps(func_or_cls)
def wrapped(*args, **kwargs):
LOG.deprecated(msg, details)
return func_or_cls(*args, **kwargs)
return wrapped
elif inspect.isclass(func_or_cls):
orig_init = func_or_cls.__init__
# TODO(tsufiev): change `functools` module to `six` as
# soon as six 1.7.4 (with fix for passing `assigned`
# argument to underlying `functools.wraps`) is released
# and added to the trove-incubator requrements
@functools.wraps(orig_init, assigned=('__name__', '__doc__'))
def new_init(self, *args, **kwargs):
LOG.deprecated(msg, details)
orig_init(self, *args, **kwargs)
func_or_cls.__init__ = new_init
return func_or_cls
else:
raise TypeError('deprecated can be used only with functions or '
'classes')
def _get_safe_to_remove_release(self, release):
# TODO(dstanek): this method will have to be reimplemented once
@ -119,9 +164,19 @@ class deprecated(object):
if self.in_favor_of:
details['in_favor_of'] = self.in_favor_of
msg = self._deprecated_msg_with_alternative
if self.remove_in > 0:
msg = self._deprecated_msg_with_alternative
else:
# There are no plans to remove this function, but it is
# now deprecated.
msg = self._deprecated_msg_with_alternative_no_removal
else:
msg = self._deprecated_msg_no_alternative
if self.remove_in > 0:
msg = self._deprecated_msg_no_alternative
else:
# There are no plans to remove this function, but it is
# now deprecated.
msg = self._deprecated_msg_with_no_alternative_no_removal
return msg, details