Move files out of the namespace package

Move the public API out of oslo.serialization to
oslo_serialization. Retain the ability to import from the old namespace
package for backwards compatibility for this release cycle.

bp/drop-namespace-packages

Change-Id: Ic60f809ea00ac77b0753556c6fd00b97e64d57ff
This commit is contained in:
Doug Hellmann 2015-01-05 15:40:06 -05:00
parent 437eaf8db5
commit e8deb08b7f
9 changed files with 600 additions and 224 deletions

View File

@ -5,5 +5,5 @@
jsonutils
=========
.. automodule:: oslo.serialization.jsonutils
.. automodule:: oslo_serialization.jsonutils
:members:

View File

@ -0,0 +1,26 @@
# 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 warnings
def deprecated():
new_name = __name__.replace('.', '_')
warnings.warn(
('The oslo namespace package is deprecated. Please use %s instead.' %
new_name),
DeprecationWarning,
stacklevel=3,
)
deprecated()

View File

@ -1,8 +1,3 @@
# 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
@ -15,221 +10,4 @@
# 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
is_simplejson = False
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
# NOTE(mriedem): Make sure we have a new enough version of simplejson
# to support the namedobject_as_tuple argument. This can be removed
# in the Kilo release when python 2.6 support is dropped.
if 'namedtuple_as_object' in inspect.getargspec(json.dumps).args:
is_simplejson = True
else:
import json
except ImportError:
import json
else:
import json
import six
import six.moves.xmlrpc_client as xmlrpclib
from oslo.utils import encodeutils
from oslo.utils import importutils
from oslo.utils 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 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)
JSONEncoder = json.JSONEncoder
JSONDecoder = json.JSONDecoder
def dumps(obj, default=to_primitive, **kwargs):
"""Serialize ``obj`` to a JSON formatted ``str``.
:param obj: object to be serialized
:param default: function that returns a serializable version of an object
:param kwargs: extra named parameters, please see documentation \
of `json.dumps <https://docs.python.org/2/library/json.html#basic-usage>`_
:returns: json formatted string
"""
if is_simplejson:
kwargs['namedtuple_as_object'] = False
return json.dumps(obj, default=default, **kwargs)
def dump(obj, fp, *args, **kwargs):
"""Serialize ``obj`` as a JSON formatted stream to ``fp``
:param obj: object to be serialized
:param fp: a ``.write()``-supporting file-like object
:param args: extra arguments, please see documentation \
of `json.dump <https://docs.python.org/2/library/json.html#basic-usage>`_
:param kwargs: extra named parameters, please see documentation \
of `json.dump <https://docs.python.org/2/library/json.html#basic-usage>`_
"""
if is_simplejson:
kwargs['namedtuple_as_object'] = False
return json.dump(obj, fp, *args, **kwargs)
def loads(s, encoding='utf-8', **kwargs):
"""Deserialize ``s`` (a ``str`` or ``unicode`` instance containing a JSON
:param s: string to deserialize
:param encoding: encoding used to interpret the string
:param kwargs: extra named parameters, please see documentation \
of `json.loads <https://docs.python.org/2/library/json.html#basic-usage>`_
:returns: python object
"""
return json.loads(encodeutils.safe_decode(s, encoding), **kwargs)
def load(fp, encoding='utf-8', **kwargs):
"""Deserialize ``fp`` to a Python object.
:param fp: a ``.read()`` -supporting file-like object
:param encoding: encoding used to interpret the string
:param kwargs: extra named parameters, please see documentation \
of `json.loads <https://docs.python.org/2/library/json.html#basic-usage>`_
:returns: python object
"""
return json.load(codecs.getreader(encoding)(fp), **kwargs)
try:
import anyjson
except ImportError:
pass
else:
anyjson._modules.append((__name__, 'dumps', TypeError,
'loads', ValueError, 'load'))
anyjson.force_implementation(__name__)
from oslo_serialization.jsonutils import * # noqa

View File

View File

@ -0,0 +1,235 @@
# 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
is_simplejson = False
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
# NOTE(mriedem): Make sure we have a new enough version of simplejson
# to support the namedobject_as_tuple argument. This can be removed
# in the Kilo release when python 2.6 support is dropped.
if 'namedtuple_as_object' in inspect.getargspec(json.dumps).args:
is_simplejson = True
else:
import json
except ImportError:
import json
else:
import json
import six
import six.moves.xmlrpc_client as xmlrpclib
from oslo.utils import encodeutils
from oslo.utils import importutils
from oslo.utils 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 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)
JSONEncoder = json.JSONEncoder
JSONDecoder = json.JSONDecoder
def dumps(obj, default=to_primitive, **kwargs):
"""Serialize ``obj`` to a JSON formatted ``str``.
:param obj: object to be serialized
:param default: function that returns a serializable version of an object
:param kwargs: extra named parameters, please see documentation \
of `json.dumps <https://docs.python.org/2/library/json.html#basic-usage>`_
:returns: json formatted string
"""
if is_simplejson:
kwargs['namedtuple_as_object'] = False
return json.dumps(obj, default=default, **kwargs)
def dump(obj, fp, *args, **kwargs):
"""Serialize ``obj`` as a JSON formatted stream to ``fp``
:param obj: object to be serialized
:param fp: a ``.write()``-supporting file-like object
:param args: extra arguments, please see documentation \
of `json.dump <https://docs.python.org/2/library/json.html#basic-usage>`_
:param kwargs: extra named parameters, please see documentation \
of `json.dump <https://docs.python.org/2/library/json.html#basic-usage>`_
"""
if is_simplejson:
kwargs['namedtuple_as_object'] = False
return json.dump(obj, fp, *args, **kwargs)
def loads(s, encoding='utf-8', **kwargs):
"""Deserialize ``s`` (a ``str`` or ``unicode`` instance containing a JSON
:param s: string to deserialize
:param encoding: encoding used to interpret the string
:param kwargs: extra named parameters, please see documentation \
of `json.loads <https://docs.python.org/2/library/json.html#basic-usage>`_
:returns: python object
"""
return json.loads(encodeutils.safe_decode(s, encoding), **kwargs)
def load(fp, encoding='utf-8', **kwargs):
"""Deserialize ``fp`` to a Python object.
:param fp: a ``.read()`` -supporting file-like object
:param encoding: encoding used to interpret the string
:param kwargs: extra named parameters, please see documentation \
of `json.loads <https://docs.python.org/2/library/json.html#basic-usage>`_
:returns: python object
"""
return json.load(codecs.getreader(encoding)(fp), **kwargs)
try:
import anyjson
except ImportError:
pass
else:
anyjson._modules.append((__name__, 'dumps', TypeError,
'loads', ValueError, 'load'))
anyjson.force_implementation(__name__)

View File

View File

@ -0,0 +1,275 @@
# 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 collections
import datetime
import json
import mock
import netaddr
from oslotest import base as test_base
import simplejson
import six
import six.moves.xmlrpc_client as xmlrpclib
from oslo.i18n import fixture
from oslo_serialization import jsonutils
class JSONUtilsTestMixin(object):
json_impl = None
def setUp(self):
super(JSONUtilsTestMixin, self).setUp()
self.json_patcher = mock.patch.multiple(
jsonutils, json=self.json_impl,
is_simplejson=self.json_impl is simplejson)
self.json_impl_mock = self.json_patcher.start()
def tearDown(self):
self.json_patcher.stop()
super(JSONUtilsTestMixin, self).tearDown()
def test_dumps(self):
self.assertEqual('{"a": "b"}', jsonutils.dumps({'a': 'b'}))
def test_dumps_namedtuple(self):
n = collections.namedtuple("foo", "bar baz")(1, 2)
self.assertEqual('[1, 2]', jsonutils.dumps(n))
def test_dump(self):
expected = '{"a": "b"}'
json_dict = {'a': 'b'}
fp = six.StringIO()
jsonutils.dump(json_dict, fp)
self.assertEqual(expected, fp.getvalue())
def test_dump_namedtuple(self):
expected = '[1, 2]'
json_dict = collections.namedtuple("foo", "bar baz")(1, 2)
fp = six.StringIO()
jsonutils.dump(json_dict, fp)
self.assertEqual(expected, fp.getvalue())
def test_loads(self):
self.assertEqual({'a': 'b'}, jsonutils.loads('{"a": "b"}'))
def test_loads_unicode(self):
self.assertIsInstance(jsonutils.loads(b'"foo"'), six.text_type)
self.assertIsInstance(jsonutils.loads(u'"foo"'), six.text_type)
# 'test' in Ukrainian
i18n_str_unicode = u'"\u0442\u0435\u0441\u0442"'
self.assertIsInstance(jsonutils.loads(i18n_str_unicode), six.text_type)
i18n_str = i18n_str_unicode.encode('utf-8')
self.assertIsInstance(jsonutils.loads(i18n_str), six.text_type)
def test_loads_with_kwargs(self):
jsontext = u'{"foo": 3}'
result = jsonutils.loads(jsontext, parse_int=lambda x: 5)
self.assertEqual(5, result['foo'])
def test_load(self):
jsontext = u'{"a": "\u0442\u044d\u0441\u0442"}'
expected = {u'a': u'\u0442\u044d\u0441\u0442'}
for encoding in ('utf-8', 'cp1251'):
fp = six.BytesIO(jsontext.encode(encoding))
result = jsonutils.load(fp, encoding=encoding)
self.assertEqual(expected, result)
for key, val in result.items():
self.assertIsInstance(key, six.text_type)
self.assertIsInstance(val, six.text_type)
class JSONUtilsTestJson(JSONUtilsTestMixin, test_base.BaseTestCase):
json_impl = json
class JSONUtilsTestSimpleJson(JSONUtilsTestMixin, test_base.BaseTestCase):
json_impl = simplejson
class ToPrimitiveTestCase(test_base.BaseTestCase):
def setUp(self):
super(ToPrimitiveTestCase, self).setUp()
self.trans_fixture = self.useFixture(fixture.Translation())
def test_list(self):
self.assertEqual(jsonutils.to_primitive([1, 2, 3]), [1, 2, 3])
def test_empty_list(self):
self.assertEqual(jsonutils.to_primitive([]), [])
def test_tuple(self):
self.assertEqual(jsonutils.to_primitive((1, 2, 3)), [1, 2, 3])
def test_dict(self):
self.assertEqual(jsonutils.to_primitive(dict(a=1, b=2, c=3)),
dict(a=1, b=2, c=3))
def test_empty_dict(self):
self.assertEqual(jsonutils.to_primitive({}), {})
def test_datetime(self):
x = datetime.datetime(1920, 2, 3, 4, 5, 6, 7)
self.assertEqual(jsonutils.to_primitive(x),
'1920-02-03T04:05:06.000007')
def test_datetime_preserve(self):
x = datetime.datetime(1920, 2, 3, 4, 5, 6, 7)
self.assertEqual(jsonutils.to_primitive(x, convert_datetime=False), x)
def test_DateTime(self):
x = xmlrpclib.DateTime()
x.decode("19710203T04:05:06")
self.assertEqual(jsonutils.to_primitive(x),
'1971-02-03T04:05:06.000000')
def test_iter(self):
class IterClass(object):
def __init__(self):
self.data = [1, 2, 3, 4, 5]
self.index = 0
def __iter__(self):
return self
def next(self):
if self.index == len(self.data):
raise StopIteration
self.index = self.index + 1
return self.data[self.index - 1]
__next__ = next
x = IterClass()
self.assertEqual(jsonutils.to_primitive(x), [1, 2, 3, 4, 5])
def test_iteritems(self):
class IterItemsClass(object):
def __init__(self):
self.data = dict(a=1, b=2, c=3).items()
self.index = 0
def iteritems(self):
return self.data
x = IterItemsClass()
p = jsonutils.to_primitive(x)
self.assertEqual(p, {'a': 1, 'b': 2, 'c': 3})
def test_iteritems_with_cycle(self):
class IterItemsClass(object):
def __init__(self):
self.data = dict(a=1, b=2, c=3)
self.index = 0
def iteritems(self):
return self.data.items()
x = IterItemsClass()
x2 = IterItemsClass()
x.data['other'] = x2
x2.data['other'] = x
# If the cycle isn't caught, to_primitive() will eventually result in
# an exception due to excessive recursion depth.
jsonutils.to_primitive(x)
def test_instance(self):
class MysteryClass(object):
a = 10
def __init__(self):
self.b = 1
x = MysteryClass()
self.assertEqual(jsonutils.to_primitive(x, convert_instances=True),
dict(b=1))
self.assertEqual(jsonutils.to_primitive(x), x)
def test_typeerror(self):
x = bytearray # Class, not instance
if six.PY3:
self.assertEqual(jsonutils.to_primitive(x), u"<class 'bytearray'>")
else:
self.assertEqual(jsonutils.to_primitive(x), u"<type 'bytearray'>")
def test_nasties(self):
def foo():
pass
x = [datetime, foo, dir]
ret = jsonutils.to_primitive(x)
self.assertEqual(len(ret), 3)
self.assertTrue(ret[0].startswith(u"<module 'datetime' from ") or
ret[0].startswith(u"<module 'datetime' (built-in)"))
if six.PY3:
self.assertTrue(ret[1].startswith(
'<function ToPrimitiveTestCase.test_nasties.<locals>.foo at 0x'
))
else:
self.assertTrue(ret[1].startswith('<function foo at 0x'))
self.assertEqual(ret[2], '<built-in function dir>')
def test_depth(self):
class LevelsGenerator(object):
def __init__(self, levels):
self._levels = levels
def iteritems(self):
if self._levels == 0:
return iter([])
else:
return iter([(0, LevelsGenerator(self._levels - 1))])
l4_obj = LevelsGenerator(4)
json_l2 = {0: {0: '?'}}
json_l3 = {0: {0: {0: '?'}}}
json_l4 = {0: {0: {0: {0: '?'}}}}
ret = jsonutils.to_primitive(l4_obj, max_depth=2)
self.assertEqual(ret, json_l2)
ret = jsonutils.to_primitive(l4_obj, max_depth=3)
self.assertEqual(ret, json_l3)
ret = jsonutils.to_primitive(l4_obj, max_depth=4)
self.assertEqual(ret, json_l4)
def test_ipaddr(self):
thing = {'ip_addr': netaddr.IPAddress('1.2.3.4')}
ret = jsonutils.to_primitive(thing)
self.assertEqual({'ip_addr': '1.2.3.4'}, ret)
def test_message_with_param(self):
msg = self.trans_fixture.lazy('A message with param: %s')
msg = msg % 'test_domain'
ret = jsonutils.to_primitive(msg)
self.assertEqual(msg, ret)
def test_message_with_named_param(self):
msg = self.trans_fixture.lazy('A message with params: %(param)s')
msg = msg % {'param': 'hello'}
ret = jsonutils.to_primitive(msg)
self.assertEqual(msg, ret)

View File

@ -23,6 +23,7 @@ classifier =
packages =
oslo
oslo.serialization
oslo_serialization
namespace_packages =
oslo

61
tests/test_warning.py Normal file
View File

@ -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.serialization
imp.reload(oslo.serialization)
self.assertTrue(mock_warn.called)
args = mock_warn.call_args
self.assertIn('oslo_serialization', 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.serialization
# 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.serialization.deprecated()
foo()
self.assertEqual(1, len(warning_msgs))
msg = warning_msgs[0]
self.assertIn('oslo_serialization', six.text_type(msg.message))
self.assertEqual('test_warning.py', os.path.basename(msg.filename))