Revamp code related to "pretty" JSON serialization, fix lurking

crash bugs.
stix2.0
Michael Chisholm 2018-06-14 21:29:50 -04:00
parent 858a9752df
commit f09960d7ff
3 changed files with 63 additions and 79 deletions

View File

@ -270,10 +270,8 @@ class _STIXBase(collections.Mapping):
overridden: indent=4, separators=(",", ": "), item_sort_key=sort_by. overridden: indent=4, separators=(",", ": "), item_sort_key=sort_by.
""" """
if pretty: if pretty:
properties = self.object_properties()
def sort_by(element): def sort_by(element):
return find_property_index(self, properties, element) return find_property_index(self, *element)
kwargs.update({'indent': 4, 'separators': (",", ": "), 'item_sort_key': sort_by}) kwargs.update({'indent': 4, 'separators': (",", ": "), 'item_sort_key': sort_by})

View File

@ -178,8 +178,7 @@ def test_deduplicate(stix_objs1):
def test_find_property_index(object, tuple_to_find, expected_index): def test_find_property_index(object, tuple_to_find, expected_index):
assert stix2.utils.find_property_index( assert stix2.utils.find_property_index(
object, object,
[], *tuple_to_find
tuple_to_find
) == expected_index ) == expected_index
@ -208,4 +207,4 @@ def test_find_property_index(object, tuple_to_find, expected_index):
}, ('key_one', 1), 0) }, ('key_one', 1), 0)
]) ])
def test_iterate_over_values(dict_value, tuple_to_find, expected_index): def test_iterate_over_values(dict_value, tuple_to_find, expected_index):
assert stix2.utils._iterate_over_values(dict_value.values(), tuple_to_find) == expected_index assert stix2.utils._find_property_in_seq(dict_value.values(), *tuple_to_find) == expected_index

View File

@ -165,87 +165,74 @@ def _get_dict(data):
raise ValueError("Cannot convert '%s' to dictionary." % str(data)) raise ValueError("Cannot convert '%s' to dictionary." % str(data))
def _iterate_over_values(dict_values, tuple_to_find): def _find(seq, val):
"""Loop recursively over dictionary values""" """
from .base import _STIXBase Search sequence 'seq' for val. This behaves like str.find(): if not found,
for pv in dict_values: -1 is returned instead of throwing an exception.
if isinstance(pv, list):
for item in pv: :param seq: The sequence to search
if isinstance(item, _STIXBase): :param val: The value to search for
index = find_property_index( :return: The index of the value if found, or -1 if not found
item, """
item.object_properties(), try:
tuple_to_find return seq.index(val)
) except ValueError:
if index is not None: return -1
return index
elif isinstance(item, dict):
for idx, val in enumerate(sorted(item)):
if (tuple_to_find[0] == val and
item.get(val) == tuple_to_find[1]):
return idx
elif isinstance(pv, dict):
if pv.get(tuple_to_find[0]) is not None:
for idx, item in enumerate(sorted(pv.keys())):
if ((item == tuple_to_find[0] and str.isdigit(item)) and
(pv[item] == tuple_to_find[1])):
return int(tuple_to_find[0])
elif pv[item] == tuple_to_find[1]:
return idx
for item in pv.values():
if isinstance(item, _STIXBase):
index = find_property_index(
item,
item.object_properties(),
tuple_to_find
)
if index is not None:
return index
elif isinstance(item, dict):
index = find_property_index(
item,
item.keys(),
tuple_to_find
)
if index is not None:
return index
def find_property_index(obj, properties, tuple_to_find): def _find_property_in_seq(seq, search_key, search_value):
"""Recursively find the property in the object model, return the index """
according to the ``properties`` OrderedDict when working with `stix2` Helper for find_property_index(): search for the property in all elements
objects. If it's a list look for individual objects. Returns and integer of the given sequence.
indicating its location.
Notes: :param seq: The sequence
This method is intended to pretty print `stix2` properties for better :param search_key: Property name to find
visual feedback when working with the library. :param search_value: Property value to find
:return: A property index, or -1 if the property was not found
"""
idx = -1
for elem in seq:
idx = find_property_index(elem, search_key, search_value)
if idx >= 0:
break
Warnings: return idx
This method may not be able to produce the same output if called
multiple times and makes a best effort attempt to print the properties
according to the STIX technical specification.
See Also:
py:meth:`stix2.base._STIXBase.serialize` for more information.
def find_property_index(obj, search_key, search_value):
"""
Search (recursively) for the given key and value in the given object.
Return an index for the key, relative to whatever object it's found in.
:param obj: The object to search (list, dict, or stix object)
:param search_key: A search key
:param search_value: A search value
:return: An index; -1 if the key and value aren't found
""" """
from .base import _STIXBase from .base import _STIXBase
try:
if isinstance(obj, _STIXBase): # Special-case keys which are numbers-as-strings, e.g. for cyber-observable
if tuple_to_find[1] in obj._inner.values(): # mappings. Use the int value of the key as the index.
return properties.index(tuple_to_find[0]) if search_key.isdigit():
elif isinstance(obj, dict): return int(search_key)
for idx, val in enumerate(sorted(obj)):
if (tuple_to_find[0] == val and if isinstance(obj, _STIXBase):
obj.get(val) == tuple_to_find[1]): if search_key in obj and obj[search_key] == search_value:
return idx idx = _find(obj.object_properties(), search_key)
raise ValueError else:
except ValueError: idx = _find_property_in_seq(obj.values(), search_key, search_value)
if isinstance(obj, _STIXBase): elif isinstance(obj, dict):
return _iterate_over_values(obj._inner.values(), tuple_to_find) if search_key in obj and obj[search_key] == search_value:
elif isinstance(obj, dict): idx = _find(sorted(obj), search_key)
return _iterate_over_values(obj.values(), tuple_to_find) else:
idx = _find_property_in_seq(obj.values(), search_key, search_value)
elif isinstance(obj, list):
idx = _find_property_in_seq(obj, search_key, search_value)
else:
# Don't know how to search this type
idx = -1
return idx
def new_version(data, **kwargs): def new_version(data, **kwargs):