jsonify inventory (#32990)

* jsonify inventory
* smarter import, dont pass kwargs where not needed
* added datetime
* Eventual plan for json utilities to migrate to common/json_utils when we split
  basic.py no need to move jsonify to another file now as we'll do that later.
* json_dict_bytes_to_unicode and json_dict_unicode_to_bytes will also
  change names and move to common/text.py at that time (not to json).
  Their purpose is to recursively change the elements of a container
  (dict, list, set, tuple) into text or bytes, not to json encode or
  decode (they could be a generic precursor to that but are not limited
  to that.)
* Reimplement the private _SetEncoder which changes sets and datetimes
  into objects that are json serializable into a private function
  instead.  Functions are more flexible, less overhead, and simpler than
  an object.
* Remove code that handled simplejson-1.5.x and earlier.  Raise an error
  if that's the case instead.
  * We require python-2.6 or better which has the json module builtin to
    the stdlib.  So this is only an issue if the stdlib json has been
    overridden by a third party module and the simplejson on the system
    is 1.5.x or less.  (1.5 was released on 2007-01-18)
pull/33168/head
Brian Coca 7 years ago committed by Toshio Kuratomi
parent bd5dc01d65
commit ebd08d2a01

@ -178,8 +178,8 @@ class InventoryCLI(CLI):
from ansible.parsing.yaml.dumper import AnsibleDumper from ansible.parsing.yaml.dumper import AnsibleDumper
results = yaml.dump(stuff, Dumper=AnsibleDumper, default_flow_style=False) results = yaml.dump(stuff, Dumper=AnsibleDumper, default_flow_style=False)
else: else:
import json from ansible.module_utils.basic import jsonify
results = json.dumps(stuff, sort_keys=True, indent=4) results = jsonify(stuff, sort_keys=True, indent=4)
return results return results

@ -61,7 +61,7 @@ import errno
import datetime import datetime
from collections import deque from collections import deque
from collections import Mapping, MutableMapping, Sequence, MutableSequence, Set, MutableSet from collections import Mapping, MutableMapping, Sequence, MutableSequence, Set, MutableSet
from itertools import repeat, chain from itertools import chain, repeat
try: try:
import syslog import syslog
@ -111,6 +111,11 @@ except ImportError:
except SyntaxError: except SyntaxError:
print('\n{"msg": "SyntaxError: probably due to installed simplejson being for a different python version", "failed": true}') print('\n{"msg": "SyntaxError: probably due to installed simplejson being for a different python version", "failed": true}')
sys.exit(1) sys.exit(1)
else:
sj_version = json.__version__.split('.')
if sj_version < ['1', '6']:
# Version 1.5 released 2007-01-18 does not have the encoding parameter which we need
print('\n{"msg": "Error: Ansible requires the stdlib json or simplejson >= 1.6. Neither was found!", "failed": true}')
AVAILABLE_HASH_ALGORITHMS = dict() AVAILABLE_HASH_ALGORITHMS = dict()
try: try:
@ -736,15 +741,32 @@ def get_flags_from_attributes(attributes):
return ''.join(flags) return ''.join(flags)
class AnsibleFallbackNotFound(Exception): def _json_encode_fallback(obj):
pass if isinstance(obj, Set):
return list(obj)
elif isinstance(obj, datetime.datetime):
return obj.isoformat()
raise TypeError("Cannot json serialize %s" % to_native(obj))
class _SetEncoder(json.JSONEncoder): def jsonify(data, **kwargs):
def default(self, obj): for encoding in ("utf-8", "latin-1"):
if isinstance(obj, Set): try:
return list(obj) return json.dumps(data, encoding=encoding, default=_json_encode_fallback, **kwargs)
return super(_SetEncoder, self).default(obj) # Old systems using old simplejson module does not support encoding keyword.
except TypeError:
try:
new_data = json_dict_bytes_to_unicode(data, encoding=encoding)
except UnicodeDecodeError:
continue
return json.dumps(new_data, default=_json_encode_fallback, **kwargs)
except UnicodeDecodeError:
continue
raise UnicodeError('Invalid unicode encoding encountered')
class AnsibleFallbackNotFound(Exception):
pass
class AnsibleModule(object): class AnsibleModule(object):
@ -2180,19 +2202,10 @@ class AnsibleModule(object):
self.fail_json(msg=to_native(e)) self.fail_json(msg=to_native(e))
def jsonify(self, data): def jsonify(self, data):
for encoding in ("utf-8", "latin-1"): try:
try: return jsonify(data)
return json.dumps(data, encoding=encoding, cls=_SetEncoder) except UnicodeError as e:
# Old systems using old simplejson module does not support encoding keyword. self.fail_json(msg=to_text(e))
except TypeError:
try:
new_data = json_dict_bytes_to_unicode(data, encoding=encoding)
except UnicodeDecodeError:
continue
return json.dumps(new_data, cls=_SetEncoder)
except UnicodeDecodeError:
continue
self.fail_json(msg='Invalid unicode encoding encountered')
def from_json(self, data): def from_json(self, data):
return json.loads(data) return json.loads(data)

@ -23,7 +23,6 @@
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# #
try: try:
import json import json
except ImportError: except ImportError:

@ -85,7 +85,9 @@ class TestModuleUtilsBasic(ModuleTestCase):
if name == 'json': if name == 'json':
raise ImportError raise ImportError
elif name == 'simplejson': elif name == 'simplejson':
return MagicMock() sj = MagicMock()
sj.__version__ = '3.10.0'
return sj
return realimport(name, *args, **kwargs) return realimport(name, *args, **kwargs)
self.clear_modules(['json', 'ansible.module_utils.basic']) self.clear_modules(['json', 'ansible.module_utils.basic'])

Loading…
Cancel
Save