Move files out of the namespace package
Move the public API out of oslo.middleware to oslo_middleware. Retain the ability to import from the old namespace package for backwards compatibility for this release cycle. bp/drop-namespace-packages Change-Id: If88c65c82b64f096a02f7ec62e019aea4de2f9d3
This commit is contained in:
parent
860cdee514
commit
8e06ca5bd0
|
@ -2,5 +2,5 @@
|
||||||
API
|
API
|
||||||
=====
|
=====
|
||||||
|
|
||||||
.. automodule:: oslo.middleware
|
.. automodule:: oslo_middleware
|
||||||
:members:
|
:members:
|
||||||
|
|
|
@ -10,14 +10,19 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
__all__ = ['CatchErrors',
|
import warnings
|
||||||
'CorrelationId',
|
|
||||||
'Debug',
|
|
||||||
'RequestId',
|
|
||||||
'RequestBodySizeLimiter']
|
|
||||||
|
|
||||||
from oslo.middleware.catch_errors import CatchErrors
|
from oslo_middleware import *
|
||||||
from oslo.middleware.correlation_id import CorrelationId
|
|
||||||
from oslo.middleware.debug import Debug
|
|
||||||
from oslo.middleware.request_id import RequestId
|
def deprecated():
|
||||||
from oslo.middleware.sizelimit import RequestBodySizeLimiter
|
new_name = __name__.replace('.', '_')
|
||||||
|
warnings.warn(
|
||||||
|
('The oslo namespace package is deprecated. Please use %s instead.' %
|
||||||
|
new_name),
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=3,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
deprecated()
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
# Copyright 2011 OpenStack Foundation.
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# 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
|
# not use this file except in compliance with the License. You may obtain
|
||||||
# a copy of the License at
|
# a copy of the License at
|
||||||
|
@ -13,44 +10,4 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
"""Base class(es) for WSGI Middleware."""
|
from oslo_middleware.base import * # noqa
|
||||||
|
|
||||||
import webob.dec
|
|
||||||
|
|
||||||
|
|
||||||
class Middleware(object):
|
|
||||||
"""Base WSGI middleware wrapper.
|
|
||||||
|
|
||||||
These classes require an application to be initialized that will be called
|
|
||||||
next. By default the middleware will simply call its wrapped app, or you
|
|
||||||
can override __call__ to customize its behavior.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def factory(cls, global_conf, **local_conf):
|
|
||||||
"""Factory method for paste.deploy."""
|
|
||||||
return cls
|
|
||||||
|
|
||||||
def __init__(self, application):
|
|
||||||
self.application = application
|
|
||||||
|
|
||||||
def process_request(self, req):
|
|
||||||
"""Called on each request.
|
|
||||||
|
|
||||||
If this returns None, the next application down the stack will be
|
|
||||||
executed. If it returns a response then that response will be returned
|
|
||||||
and execution will stop here.
|
|
||||||
"""
|
|
||||||
return None
|
|
||||||
|
|
||||||
def process_response(self, response):
|
|
||||||
"""Do whatever you'd like to the response."""
|
|
||||||
return response
|
|
||||||
|
|
||||||
@webob.dec.wsgify
|
|
||||||
def __call__(self, req):
|
|
||||||
response = self.process_request(req)
|
|
||||||
if response:
|
|
||||||
return response
|
|
||||||
response = req.get_response(self.application)
|
|
||||||
return self.process_response(response)
|
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
# Copyright (c) 2013 NEC Corporation
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# 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
|
# not use this file except in compliance with the License. You may obtain
|
||||||
# a copy of the License at
|
# a copy of the License at
|
||||||
|
@ -13,31 +10,4 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import logging
|
from oslo_middleware.catch_errors import * # noqa
|
||||||
|
|
||||||
import webob.dec
|
|
||||||
import webob.exc
|
|
||||||
|
|
||||||
from oslo.middleware import base
|
|
||||||
from oslo.middleware.i18n import _LE
|
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class CatchErrors(base.Middleware):
|
|
||||||
"""Middleware that provides high-level error handling.
|
|
||||||
|
|
||||||
It catches all exceptions from subsequent applications in WSGI pipeline
|
|
||||||
to hide internal errors from API response.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@webob.dec.wsgify
|
|
||||||
def __call__(self, req):
|
|
||||||
try:
|
|
||||||
response = req.get_response(self.application)
|
|
||||||
except Exception:
|
|
||||||
LOG.exception(_LE('An error occurred during '
|
|
||||||
'processing the request: %s'))
|
|
||||||
response = webob.exc.HTTPInternalServerError()
|
|
||||||
return response
|
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
# Copyright (c) 2013 Rackspace Hosting
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# 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
|
# not use this file except in compliance with the License. You may obtain
|
||||||
# a copy of the License at
|
# a copy of the License at
|
||||||
|
@ -13,15 +10,4 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import uuid
|
from oslo_middleware.correlation_id import * # noqa
|
||||||
|
|
||||||
from oslo.middleware import base
|
|
||||||
|
|
||||||
|
|
||||||
class CorrelationId(base.Middleware):
|
|
||||||
"Middleware that attaches a correlation id to WSGI request"
|
|
||||||
|
|
||||||
def process_request(self, req):
|
|
||||||
correlation_id = (req.headers.get("X_CORRELATION_ID") or
|
|
||||||
str(uuid.uuid4()))
|
|
||||||
req.headers['X_CORRELATION_ID'] = correlation_id
|
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
# Copyright 2011 OpenStack Foundation.
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# 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
|
# not use this file except in compliance with the License. You may obtain
|
||||||
# a copy of the License at
|
# a copy of the License at
|
||||||
|
@ -13,48 +10,4 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
"""Debug middleware"""
|
from oslo_middleware.debug import * # noqa
|
||||||
|
|
||||||
from __future__ import print_function
|
|
||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import six
|
|
||||||
import webob.dec
|
|
||||||
|
|
||||||
from oslo.middleware import base
|
|
||||||
|
|
||||||
|
|
||||||
class Debug(base.Middleware):
|
|
||||||
"""Helper class that returns debug information.
|
|
||||||
|
|
||||||
Can be inserted into any WSGI application chain to get information about
|
|
||||||
the request and response.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@webob.dec.wsgify
|
|
||||||
def __call__(self, req):
|
|
||||||
print(("*" * 40) + " REQUEST ENVIRON")
|
|
||||||
for key, value in req.environ.items():
|
|
||||||
print(key, "=", value)
|
|
||||||
print()
|
|
||||||
resp = req.get_response(self.application)
|
|
||||||
|
|
||||||
print(("*" * 40) + " RESPONSE HEADERS")
|
|
||||||
for (key, value) in six.iteritems(resp.headers):
|
|
||||||
print(key, "=", value)
|
|
||||||
print()
|
|
||||||
|
|
||||||
resp.app_iter = self.print_generator(resp.app_iter)
|
|
||||||
|
|
||||||
return resp
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def print_generator(app_iter):
|
|
||||||
"""Prints the contents of a wrapper string iterator when iterated."""
|
|
||||||
print(("*" * 40) + " BODY")
|
|
||||||
for part in app_iter:
|
|
||||||
sys.stdout.write(part)
|
|
||||||
sys.stdout.flush()
|
|
||||||
yield part
|
|
||||||
print()
|
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
# Copyright (c) 2013 NEC Corporation
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# 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
|
# not use this file except in compliance with the License. You may obtain
|
||||||
# a copy of the License at
|
# a copy of the License at
|
||||||
|
@ -13,28 +10,4 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from oslo_context import context
|
from oslo_middleware.request_id import * # noqa
|
||||||
import webob.dec
|
|
||||||
|
|
||||||
from oslo.middleware import base
|
|
||||||
|
|
||||||
|
|
||||||
ENV_REQUEST_ID = 'openstack.request_id'
|
|
||||||
HTTP_RESP_HEADER_REQUEST_ID = 'x-openstack-request-id'
|
|
||||||
|
|
||||||
|
|
||||||
class RequestId(base.Middleware):
|
|
||||||
"""Middleware that ensures request ID.
|
|
||||||
|
|
||||||
It ensures to assign request ID for each API request and set it to
|
|
||||||
request environment. The request ID is also added to API response.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@webob.dec.wsgify
|
|
||||||
def __call__(self, req):
|
|
||||||
req_id = context.generate_request_id()
|
|
||||||
req.environ[ENV_REQUEST_ID] = req_id
|
|
||||||
response = req.get_response(self.application)
|
|
||||||
if HTTP_RESP_HEADER_REQUEST_ID not in response.headers:
|
|
||||||
response.headers.add(HTTP_RESP_HEADER_REQUEST_ID, req_id)
|
|
||||||
return response
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
# Copyright (c) 2012 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# 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
|
# not use this file except in compliance with the License. You may obtain
|
||||||
# a copy of the License at
|
# a copy of the License at
|
||||||
|
@ -12,84 +10,4 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
"""
|
from oslo_middleware.sizelimit import * # noqa
|
||||||
Request Body limiting middleware.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
from oslo.config import cfg
|
|
||||||
from oslo.config import cfgfilter
|
|
||||||
import webob.dec
|
|
||||||
import webob.exc
|
|
||||||
|
|
||||||
from oslo.middleware import base
|
|
||||||
from oslo.middleware.i18n import _
|
|
||||||
|
|
||||||
|
|
||||||
_oldopts = [cfg.DeprecatedOpt('osapi_max_request_body_size',
|
|
||||||
group='DEFAULT'),
|
|
||||||
cfg.DeprecatedOpt('max_request_body_size',
|
|
||||||
group='DEFAULT')]
|
|
||||||
|
|
||||||
_opts = [
|
|
||||||
# default request size is 112k
|
|
||||||
cfg.IntOpt('max_request_body_size',
|
|
||||||
default=114688,
|
|
||||||
help='The maximum body size for each '
|
|
||||||
' request, in bytes.',
|
|
||||||
deprecated_opts=_oldopts)
|
|
||||||
]
|
|
||||||
|
|
||||||
CONF = cfgfilter.ConfigFilter(cfg.CONF)
|
|
||||||
CONF.register_opts(_opts, group='oslo_middleware')
|
|
||||||
|
|
||||||
|
|
||||||
class LimitingReader(object):
|
|
||||||
"""Reader to limit the size of an incoming request."""
|
|
||||||
def __init__(self, data, limit):
|
|
||||||
"""Initiates LimitingReader object.
|
|
||||||
|
|
||||||
:param data: Underlying data object
|
|
||||||
:param limit: maximum number of bytes the reader should allow
|
|
||||||
"""
|
|
||||||
self.data = data
|
|
||||||
self.limit = limit
|
|
||||||
self.bytes_read = 0
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
for chunk in self.data:
|
|
||||||
self.bytes_read += len(chunk)
|
|
||||||
if self.bytes_read > self.limit:
|
|
||||||
msg = _("Request is too large.")
|
|
||||||
raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
|
|
||||||
else:
|
|
||||||
yield chunk
|
|
||||||
|
|
||||||
def read(self, i=None):
|
|
||||||
# NOTE(jamielennox): We can't simply provide the default to the read()
|
|
||||||
# call as the expected default differs between mod_wsgi and eventlet
|
|
||||||
if i is None:
|
|
||||||
result = self.data.read()
|
|
||||||
else:
|
|
||||||
result = self.data.read(i)
|
|
||||||
self.bytes_read += len(result)
|
|
||||||
if self.bytes_read > self.limit:
|
|
||||||
msg = _("Request is too large.")
|
|
||||||
raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
class RequestBodySizeLimiter(base.Middleware):
|
|
||||||
"""Limit the size of incoming requests."""
|
|
||||||
|
|
||||||
@webob.dec.wsgify
|
|
||||||
def __call__(self, req):
|
|
||||||
max_size = CONF.oslo_middleware.max_request_body_size
|
|
||||||
if (req.content_length is not None and
|
|
||||||
req.content_length > max_size):
|
|
||||||
msg = _("Request is too large.")
|
|
||||||
raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
|
|
||||||
if req.content_length is None and req.is_body_readable:
|
|
||||||
limiter = LimitingReader(req.body_file, max_size)
|
|
||||||
req.body_file = limiter
|
|
||||||
return self.application
|
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
__all__ = ['CatchErrors',
|
||||||
|
'CorrelationId',
|
||||||
|
'Debug',
|
||||||
|
'RequestId',
|
||||||
|
'RequestBodySizeLimiter']
|
||||||
|
|
||||||
|
from oslo_middleware.catch_errors import CatchErrors
|
||||||
|
from oslo_middleware.correlation_id import CorrelationId
|
||||||
|
from oslo_middleware.debug import Debug
|
||||||
|
from oslo_middleware.request_id import RequestId
|
||||||
|
from oslo_middleware.sizelimit import RequestBodySizeLimiter
|
|
@ -0,0 +1,56 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""Base class(es) for WSGI Middleware."""
|
||||||
|
|
||||||
|
import webob.dec
|
||||||
|
|
||||||
|
|
||||||
|
class Middleware(object):
|
||||||
|
"""Base WSGI middleware wrapper.
|
||||||
|
|
||||||
|
These classes require an application to be initialized that will be called
|
||||||
|
next. By default the middleware will simply call its wrapped app, or you
|
||||||
|
can override __call__ to customize its behavior.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def factory(cls, global_conf, **local_conf):
|
||||||
|
"""Factory method for paste.deploy."""
|
||||||
|
return cls
|
||||||
|
|
||||||
|
def __init__(self, application):
|
||||||
|
self.application = application
|
||||||
|
|
||||||
|
def process_request(self, req):
|
||||||
|
"""Called on each request.
|
||||||
|
|
||||||
|
If this returns None, the next application down the stack will be
|
||||||
|
executed. If it returns a response then that response will be returned
|
||||||
|
and execution will stop here.
|
||||||
|
"""
|
||||||
|
return None
|
||||||
|
|
||||||
|
def process_response(self, response):
|
||||||
|
"""Do whatever you'd like to the response."""
|
||||||
|
return response
|
||||||
|
|
||||||
|
@webob.dec.wsgify
|
||||||
|
def __call__(self, req):
|
||||||
|
response = self.process_request(req)
|
||||||
|
if response:
|
||||||
|
return response
|
||||||
|
response = req.get_response(self.application)
|
||||||
|
return self.process_response(response)
|
|
@ -0,0 +1,43 @@
|
||||||
|
# Copyright (c) 2013 NEC Corporation
|
||||||
|
# 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 logging
|
||||||
|
|
||||||
|
import webob.dec
|
||||||
|
import webob.exc
|
||||||
|
|
||||||
|
from oslo_middleware import base
|
||||||
|
from oslo_middleware.i18n import _LE
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class CatchErrors(base.Middleware):
|
||||||
|
"""Middleware that provides high-level error handling.
|
||||||
|
|
||||||
|
It catches all exceptions from subsequent applications in WSGI pipeline
|
||||||
|
to hide internal errors from API response.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@webob.dec.wsgify
|
||||||
|
def __call__(self, req):
|
||||||
|
try:
|
||||||
|
response = req.get_response(self.application)
|
||||||
|
except Exception:
|
||||||
|
LOG.exception(_LE('An error occurred during '
|
||||||
|
'processing the request: %s'))
|
||||||
|
response = webob.exc.HTTPInternalServerError()
|
||||||
|
return response
|
|
@ -0,0 +1,27 @@
|
||||||
|
# Copyright (c) 2013 Rackspace Hosting
|
||||||
|
# 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 uuid
|
||||||
|
|
||||||
|
from oslo_middleware import base
|
||||||
|
|
||||||
|
|
||||||
|
class CorrelationId(base.Middleware):
|
||||||
|
"Middleware that attaches a correlation id to WSGI request"
|
||||||
|
|
||||||
|
def process_request(self, req):
|
||||||
|
correlation_id = (req.headers.get("X_CORRELATION_ID") or
|
||||||
|
str(uuid.uuid4()))
|
||||||
|
req.headers['X_CORRELATION_ID'] = correlation_id
|
|
@ -0,0 +1,60 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""Debug middleware"""
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import six
|
||||||
|
import webob.dec
|
||||||
|
|
||||||
|
from oslo_middleware import base
|
||||||
|
|
||||||
|
|
||||||
|
class Debug(base.Middleware):
|
||||||
|
"""Helper class that returns debug information.
|
||||||
|
|
||||||
|
Can be inserted into any WSGI application chain to get information about
|
||||||
|
the request and response.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@webob.dec.wsgify
|
||||||
|
def __call__(self, req):
|
||||||
|
print(("*" * 40) + " REQUEST ENVIRON")
|
||||||
|
for key, value in req.environ.items():
|
||||||
|
print(key, "=", value)
|
||||||
|
print()
|
||||||
|
resp = req.get_response(self.application)
|
||||||
|
|
||||||
|
print(("*" * 40) + " RESPONSE HEADERS")
|
||||||
|
for (key, value) in six.iteritems(resp.headers):
|
||||||
|
print(key, "=", value)
|
||||||
|
print()
|
||||||
|
|
||||||
|
resp.app_iter = self.print_generator(resp.app_iter)
|
||||||
|
|
||||||
|
return resp
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def print_generator(app_iter):
|
||||||
|
"""Prints the contents of a wrapper string iterator when iterated."""
|
||||||
|
print(("*" * 40) + " BODY")
|
||||||
|
for part in app_iter:
|
||||||
|
sys.stdout.write(part)
|
||||||
|
sys.stdout.flush()
|
||||||
|
yield part
|
||||||
|
print()
|
|
@ -20,7 +20,7 @@ __all__ = [
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
from oslo.middleware import sizelimit
|
from oslo_middleware import sizelimit
|
||||||
|
|
||||||
|
|
||||||
def list_opts():
|
def list_opts():
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Copyright (c) 2013 NEC Corporation
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from oslo_context import context
|
||||||
|
import webob.dec
|
||||||
|
|
||||||
|
from oslo_middleware import base
|
||||||
|
|
||||||
|
|
||||||
|
ENV_REQUEST_ID = 'openstack.request_id'
|
||||||
|
HTTP_RESP_HEADER_REQUEST_ID = 'x-openstack-request-id'
|
||||||
|
|
||||||
|
|
||||||
|
class RequestId(base.Middleware):
|
||||||
|
"""Middleware that ensures request ID.
|
||||||
|
|
||||||
|
It ensures to assign request ID for each API request and set it to
|
||||||
|
request environment. The request ID is also added to API response.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@webob.dec.wsgify
|
||||||
|
def __call__(self, req):
|
||||||
|
req_id = context.generate_request_id()
|
||||||
|
req.environ[ENV_REQUEST_ID] = req_id
|
||||||
|
response = req.get_response(self.application)
|
||||||
|
if HTTP_RESP_HEADER_REQUEST_ID not in response.headers:
|
||||||
|
response.headers.add(HTTP_RESP_HEADER_REQUEST_ID, req_id)
|
||||||
|
return response
|
|
@ -0,0 +1,95 @@
|
||||||
|
# Copyright (c) 2012 Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Request Body limiting middleware.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from oslo.config import cfg
|
||||||
|
from oslo.config import cfgfilter
|
||||||
|
import webob.dec
|
||||||
|
import webob.exc
|
||||||
|
|
||||||
|
from oslo_middleware import base
|
||||||
|
from oslo_middleware.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
_oldopts = [cfg.DeprecatedOpt('osapi_max_request_body_size',
|
||||||
|
group='DEFAULT'),
|
||||||
|
cfg.DeprecatedOpt('max_request_body_size',
|
||||||
|
group='DEFAULT')]
|
||||||
|
|
||||||
|
_opts = [
|
||||||
|
# default request size is 112k
|
||||||
|
cfg.IntOpt('max_request_body_size',
|
||||||
|
default=114688,
|
||||||
|
help='The maximum body size for each '
|
||||||
|
' request, in bytes.',
|
||||||
|
deprecated_opts=_oldopts)
|
||||||
|
]
|
||||||
|
|
||||||
|
CONF = cfgfilter.ConfigFilter(cfg.CONF)
|
||||||
|
CONF.register_opts(_opts, group='oslo_middleware')
|
||||||
|
|
||||||
|
|
||||||
|
class LimitingReader(object):
|
||||||
|
"""Reader to limit the size of an incoming request."""
|
||||||
|
def __init__(self, data, limit):
|
||||||
|
"""Initiates LimitingReader object.
|
||||||
|
|
||||||
|
:param data: Underlying data object
|
||||||
|
:param limit: maximum number of bytes the reader should allow
|
||||||
|
"""
|
||||||
|
self.data = data
|
||||||
|
self.limit = limit
|
||||||
|
self.bytes_read = 0
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
for chunk in self.data:
|
||||||
|
self.bytes_read += len(chunk)
|
||||||
|
if self.bytes_read > self.limit:
|
||||||
|
msg = _("Request is too large.")
|
||||||
|
raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
|
||||||
|
else:
|
||||||
|
yield chunk
|
||||||
|
|
||||||
|
def read(self, i=None):
|
||||||
|
# NOTE(jamielennox): We can't simply provide the default to the read()
|
||||||
|
# call as the expected default differs between mod_wsgi and eventlet
|
||||||
|
if i is None:
|
||||||
|
result = self.data.read()
|
||||||
|
else:
|
||||||
|
result = self.data.read(i)
|
||||||
|
self.bytes_read += len(result)
|
||||||
|
if self.bytes_read > self.limit:
|
||||||
|
msg = _("Request is too large.")
|
||||||
|
raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class RequestBodySizeLimiter(base.Middleware):
|
||||||
|
"""Limit the size of incoming requests."""
|
||||||
|
|
||||||
|
@webob.dec.wsgify
|
||||||
|
def __call__(self, req):
|
||||||
|
max_size = CONF.oslo_middleware.max_request_body_size
|
||||||
|
if (req.content_length is not None and
|
||||||
|
req.content_length > max_size):
|
||||||
|
msg = _("Request is too large.")
|
||||||
|
raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
|
||||||
|
if req.content_length is None and req.is_body_readable:
|
||||||
|
limiter = LimitingReader(req.body_file, max_size)
|
||||||
|
req.body_file = limiter
|
||||||
|
return self.application
|
|
@ -0,0 +1,47 @@
|
||||||
|
# Copyright (c) 2013 NEC Corporation
|
||||||
|
# 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 mock
|
||||||
|
from oslotest import base as test_base
|
||||||
|
import webob.dec
|
||||||
|
import webob.exc
|
||||||
|
|
||||||
|
from oslo_middleware import catch_errors
|
||||||
|
|
||||||
|
|
||||||
|
class CatchErrorsTest(test_base.BaseTestCase):
|
||||||
|
|
||||||
|
def _test_has_request_id(self, application, expected_code=None):
|
||||||
|
app = catch_errors.CatchErrors(application)
|
||||||
|
req = webob.Request.blank('/test')
|
||||||
|
res = req.get_response(app)
|
||||||
|
self.assertEqual(expected_code, res.status_int)
|
||||||
|
|
||||||
|
def test_success_response(self):
|
||||||
|
@webob.dec.wsgify
|
||||||
|
def application(req):
|
||||||
|
return 'Hello, World!!!'
|
||||||
|
|
||||||
|
self._test_has_request_id(application, webob.exc.HTTPOk.code)
|
||||||
|
|
||||||
|
def test_internal_server_error(self):
|
||||||
|
@webob.dec.wsgify
|
||||||
|
def application(req):
|
||||||
|
raise Exception()
|
||||||
|
|
||||||
|
with mock.patch.object(catch_errors.LOG, 'exception') as log_exc:
|
||||||
|
self._test_has_request_id(application,
|
||||||
|
webob.exc.HTTPInternalServerError.code)
|
||||||
|
self.assertEqual(1, log_exc.call_count)
|
|
@ -0,0 +1,53 @@
|
||||||
|
# Copyright (c) 2013 Rackspace Hosting
|
||||||
|
# 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 uuid
|
||||||
|
|
||||||
|
import mock
|
||||||
|
from oslotest import base as test_base
|
||||||
|
from oslotest import moxstubout
|
||||||
|
|
||||||
|
from oslo_middleware import correlation_id
|
||||||
|
|
||||||
|
|
||||||
|
class CorrelationIdTest(test_base.BaseTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(CorrelationIdTest, self).setUp()
|
||||||
|
self.stubs = self.useFixture(moxstubout.MoxStubout()).stubs
|
||||||
|
|
||||||
|
def test_process_request(self):
|
||||||
|
app = mock.Mock()
|
||||||
|
req = mock.Mock()
|
||||||
|
req.headers = {}
|
||||||
|
|
||||||
|
mock_uuid4 = mock.Mock()
|
||||||
|
mock_uuid4.return_value = "fake_uuid"
|
||||||
|
self.stubs.Set(uuid, 'uuid4', mock_uuid4)
|
||||||
|
|
||||||
|
middleware = correlation_id.CorrelationId(app)
|
||||||
|
middleware(req)
|
||||||
|
|
||||||
|
self.assertEqual(req.headers.get("X_CORRELATION_ID"), "fake_uuid")
|
||||||
|
|
||||||
|
def test_process_request_should_not_regenerate_correlation_id(self):
|
||||||
|
app = mock.Mock()
|
||||||
|
req = mock.Mock()
|
||||||
|
req.headers = {"X_CORRELATION_ID": "correlation_id"}
|
||||||
|
|
||||||
|
middleware = correlation_id.CorrelationId(app)
|
||||||
|
middleware(req)
|
||||||
|
|
||||||
|
self.assertEqual(req.headers.get("X_CORRELATION_ID"), "correlation_id")
|
|
@ -0,0 +1,37 @@
|
||||||
|
# Copyright (c) 2013 NEC Corporation
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
|
||||||
|
from oslotest import base as test_base
|
||||||
|
from testtools import matchers
|
||||||
|
import webob
|
||||||
|
import webob.dec
|
||||||
|
|
||||||
|
from oslo_middleware import request_id
|
||||||
|
|
||||||
|
|
||||||
|
class RequestIdTest(test_base.BaseTestCase):
|
||||||
|
def test_generate_request_id(self):
|
||||||
|
@webob.dec.wsgify
|
||||||
|
def application(req):
|
||||||
|
return req.environ[request_id.ENV_REQUEST_ID]
|
||||||
|
|
||||||
|
app = request_id.RequestId(application)
|
||||||
|
req = webob.Request.blank('/test')
|
||||||
|
res = req.get_response(app)
|
||||||
|
res_req_id = res.headers.get(request_id.HTTP_RESP_HEADER_REQUEST_ID)
|
||||||
|
self.assertThat(res_req_id, matchers.StartsWith(b'req-'))
|
||||||
|
# request-id in request environ is returned as response body
|
||||||
|
self.assertEqual(res_req_id, res.body)
|
|
@ -0,0 +1,108 @@
|
||||||
|
# Copyright (c) 2012 Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from oslotest import base as test_base
|
||||||
|
import six
|
||||||
|
import webob
|
||||||
|
|
||||||
|
from oslo.config import fixture as config
|
||||||
|
from oslo_middleware import sizelimit
|
||||||
|
|
||||||
|
|
||||||
|
class TestLimitingReader(test_base.BaseTestCase):
|
||||||
|
|
||||||
|
def test_limiting_reader(self):
|
||||||
|
BYTES = 1024
|
||||||
|
bytes_read = 0
|
||||||
|
data = six.StringIO("*" * BYTES)
|
||||||
|
for chunk in sizelimit.LimitingReader(data, BYTES):
|
||||||
|
bytes_read += len(chunk)
|
||||||
|
|
||||||
|
self.assertEqual(bytes_read, BYTES)
|
||||||
|
|
||||||
|
bytes_read = 0
|
||||||
|
data = six.StringIO("*" * BYTES)
|
||||||
|
reader = sizelimit.LimitingReader(data, BYTES)
|
||||||
|
byte = reader.read(1)
|
||||||
|
while len(byte) != 0:
|
||||||
|
bytes_read += 1
|
||||||
|
byte = reader.read(1)
|
||||||
|
|
||||||
|
self.assertEqual(bytes_read, BYTES)
|
||||||
|
|
||||||
|
def test_read_default_value(self):
|
||||||
|
BYTES = 1024
|
||||||
|
data_str = "*" * BYTES
|
||||||
|
data = six.StringIO(data_str)
|
||||||
|
reader = sizelimit.LimitingReader(data, BYTES)
|
||||||
|
res = reader.read()
|
||||||
|
self.assertEqual(data_str, res)
|
||||||
|
|
||||||
|
def test_limiting_reader_fails(self):
|
||||||
|
BYTES = 1024
|
||||||
|
|
||||||
|
def _consume_all_iter():
|
||||||
|
bytes_read = 0
|
||||||
|
data = six.StringIO("*" * BYTES)
|
||||||
|
for chunk in sizelimit.LimitingReader(data, BYTES - 1):
|
||||||
|
bytes_read += len(chunk)
|
||||||
|
|
||||||
|
self.assertRaises(webob.exc.HTTPRequestEntityTooLarge,
|
||||||
|
_consume_all_iter)
|
||||||
|
|
||||||
|
def _consume_all_read():
|
||||||
|
bytes_read = 0
|
||||||
|
data = six.StringIO("*" * BYTES)
|
||||||
|
reader = sizelimit.LimitingReader(data, BYTES - 1)
|
||||||
|
byte = reader.read(1)
|
||||||
|
while len(byte) != 0:
|
||||||
|
bytes_read += 1
|
||||||
|
byte = reader.read(1)
|
||||||
|
|
||||||
|
self.assertRaises(webob.exc.HTTPRequestEntityTooLarge,
|
||||||
|
_consume_all_read)
|
||||||
|
|
||||||
|
|
||||||
|
class TestRequestBodySizeLimiter(test_base.BaseTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestRequestBodySizeLimiter, self).setUp()
|
||||||
|
fixture = self.useFixture(config.Config(sizelimit.CONF))
|
||||||
|
self.MAX_REQUEST_BODY_SIZE = \
|
||||||
|
fixture.conf.oslo_middleware.max_request_body_size
|
||||||
|
|
||||||
|
@webob.dec.wsgify()
|
||||||
|
def fake_app(req):
|
||||||
|
return webob.Response(req.body)
|
||||||
|
|
||||||
|
self.middleware = sizelimit.RequestBodySizeLimiter(fake_app)
|
||||||
|
self.request = webob.Request.blank('/', method='POST')
|
||||||
|
|
||||||
|
def test_content_length_acceptable(self):
|
||||||
|
self.request.headers['Content-Length'] = self.MAX_REQUEST_BODY_SIZE
|
||||||
|
self.request.body = b"0" * self.MAX_REQUEST_BODY_SIZE
|
||||||
|
response = self.request.get_response(self.middleware)
|
||||||
|
self.assertEqual(response.status_int, 200)
|
||||||
|
|
||||||
|
def test_content_length_too_large(self):
|
||||||
|
self.request.headers['Content-Length'] = self.MAX_REQUEST_BODY_SIZE + 1
|
||||||
|
self.request.body = b"0" * (self.MAX_REQUEST_BODY_SIZE + 1)
|
||||||
|
response = self.request.get_response(self.middleware)
|
||||||
|
self.assertEqual(response.status_int, 413)
|
||||||
|
|
||||||
|
def test_request_too_large_no_content_length(self):
|
||||||
|
self.request.body = b"0" * (self.MAX_REQUEST_BODY_SIZE + 1)
|
||||||
|
self.request.headers['Content-Length'] = None
|
||||||
|
response = self.request.get_response(self.middleware)
|
||||||
|
self.assertEqual(response.status_int, 413)
|
|
@ -22,12 +22,13 @@ classifier =
|
||||||
[files]
|
[files]
|
||||||
packages =
|
packages =
|
||||||
oslo
|
oslo
|
||||||
|
oslo_middleware
|
||||||
namespace_packages =
|
namespace_packages =
|
||||||
oslo
|
oslo
|
||||||
|
|
||||||
[entry_points]
|
[entry_points]
|
||||||
oslo.config.opts =
|
oslo.config.opts =
|
||||||
oslo.middleware = oslo.middleware.opts:list_opts
|
oslo.middleware = oslo_middleware.opts:list_opts
|
||||||
|
|
||||||
[build_sphinx]
|
[build_sphinx]
|
||||||
source-dir = doc/source
|
source-dir = doc/source
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
# 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 imp
|
||||||
|
import os
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
import mock
|
||||||
|
from oslotest import base as test_base
|
||||||
|
import six
|
||||||
|
|
||||||
|
|
||||||
|
class DeprecationWarningTest(test_base.BaseTestCase):
|
||||||
|
|
||||||
|
@mock.patch('warnings.warn')
|
||||||
|
def test_warning(self, mock_warn):
|
||||||
|
import oslo.middleware
|
||||||
|
imp.reload(oslo.middleware)
|
||||||
|
self.assertTrue(mock_warn.called)
|
||||||
|
args = mock_warn.call_args
|
||||||
|
self.assertIn('oslo_middleware', args[0][0])
|
||||||
|
self.assertIn('deprecated', args[0][0])
|
||||||
|
self.assertTrue(issubclass(args[0][1], DeprecationWarning))
|
||||||
|
|
||||||
|
def test_real_warning(self):
|
||||||
|
with warnings.catch_warnings(record=True) as warning_msgs:
|
||||||
|
warnings.resetwarnings()
|
||||||
|
warnings.simplefilter('always', DeprecationWarning)
|
||||||
|
import oslo.middleware
|
||||||
|
|
||||||
|
# Use a separate function to get the stack level correct
|
||||||
|
# so we know the message points back to this file. This
|
||||||
|
# corresponds to an import or reload, which isn't working
|
||||||
|
# inside the test under Python 3.3. That may be due to a
|
||||||
|
# difference in the import implementation not triggering
|
||||||
|
# warnings properly when the module is reloaded, or
|
||||||
|
# because the warnings module is mostly implemented in C
|
||||||
|
# and something isn't cleanly resetting the global state
|
||||||
|
# used to track whether a warning needs to be
|
||||||
|
# emitted. Whatever the cause, we definitely see the
|
||||||
|
# warnings.warn() being invoked on a reload (see the test
|
||||||
|
# above) and warnings are reported on the console when we
|
||||||
|
# run the tests. A simpler test script run outside of
|
||||||
|
# testr does correctly report the warnings.
|
||||||
|
def foo():
|
||||||
|
oslo.middleware.deprecated()
|
||||||
|
|
||||||
|
foo()
|
||||||
|
self.assertEqual(1, len(warning_msgs))
|
||||||
|
msg = warning_msgs[0]
|
||||||
|
self.assertIn('oslo_middleware', six.text_type(msg.message))
|
||||||
|
self.assertEqual('test_warning.py', os.path.basename(msg.filename))
|
4
tox.ini
4
tox.ini
|
@ -1,6 +1,6 @@
|
||||||
[tox]
|
[tox]
|
||||||
minversion = 1.6
|
minversion = 1.6
|
||||||
envlist = py26,py27,py33,py34,pypy,pep8
|
envlist = py33,py34,py26,py27,pypy,pep8
|
||||||
# NOTE(dhellmann): We cannot set skipdist=True
|
# NOTE(dhellmann): We cannot set skipdist=True
|
||||||
# for oslo libraries because of the namespace package.
|
# for oslo libraries because of the namespace package.
|
||||||
#skipsdist = True
|
#skipsdist = True
|
||||||
|
@ -37,4 +37,4 @@ ignore = E123,E125,H305,H803,H904
|
||||||
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,__init__.py
|
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,__init__.py
|
||||||
|
|
||||||
[hacking]
|
[hacking]
|
||||||
import_exceptions = oslo.middleware.i18n
|
import_exceptions = oslo_middleware.i18n
|
||||||
|
|
Loading…
Reference in New Issue