Skip to content

Commit

Permalink
Compensates for #97's unintended changes to IonEvent.field_name behav…
Browse files Browse the repository at this point in the history
…ior (#105)
  • Loading branch information
Peter Cornell committed Oct 9, 2019
1 parent 7094dfd commit 38fbd2d
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 11 deletions.
17 changes: 14 additions & 3 deletions amazon/ion/simple_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,20 +104,31 @@ def from_value(cls, ion_type, value, annotations=()):
value.ion_annotations = annotations
return value

def to_event(self, event_type, field_name=None, depth=None):
def to_event(self, event_type, field_name=None, in_struct=False, depth=None):
"""Constructs an IonEvent from this _IonNature value.
Args:
event_type (IonEventType): The type of the resulting event.
field_name (Optional[text]): The field name associated with this value, if any.
field_name (Optional[text]): The field name associated with this value, if any. When ``None``
is specified and ``in_struct`` is ``True``, the returned event's ``field_name`` will
represent symbol zero (a ``SymbolToken`` with text=None and sid=0).
in_struct (Optional[True|False]): When ``True``, indicates the returned event ``field_name``
will be populated. When ``False``, ``field_name`` will be ``None``.
depth (Optional[int]): The depth of this value.
Returns:
An IonEvent with the properties from this value.
"""
value = self
if isinstance(self, IonPyNull):
if isinstance(self, IonPyNull) or self.ion_type.is_container:
value = None

if in_struct:
if not isinstance(field_name, SymbolToken):
field_name = SymbolToken(field_name, 0 if field_name is None else None)
else:
field_name = None

return IonEvent(event_type, ion_type=self.ion_type, value=value, field_name=field_name,
annotations=self.ion_annotations, depth=depth)

Expand Down
14 changes: 7 additions & 7 deletions amazon/ion/simpleion.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ def _ion_type(obj, from_type):
raise TypeError('Unknown scalar type %r' % (type(obj),))


def _dump(obj, writer, from_type, field=None):
def _dump(obj, writer, from_type, field=None, in_struct=False, depth=0):
null = is_null(obj)
try:
ion_type = obj.ion_type
Expand All @@ -208,23 +208,23 @@ def _dump(obj, writer, from_type, field=None):
ion_nature = False
if not null and ion_type.is_container:
if ion_nature:
event = obj.to_event(IonEventType.CONTAINER_START, field_name=field)
event = obj.to_event(IonEventType.CONTAINER_START, field_name=field, in_struct=in_struct, depth=depth)
else:
event = IonEvent(IonEventType.CONTAINER_START, ion_type, field_name=field)
event = IonEvent(IonEventType.CONTAINER_START, ion_type, field_name=field, depth=depth)
writer.send(event)
if ion_type is IonType.STRUCT:
for field, val in six.iteritems(obj):
_dump(val, writer, from_type, field)
_dump(val, writer, from_type, field, in_struct=True, depth=depth+1)
else:
for elem in obj:
_dump(elem, writer, from_type)
_dump(elem, writer, from_type, depth=depth+1)
event = _ION_CONTAINER_END_EVENT
else:
# obj is a scalar value
if ion_nature:
event = obj.to_event(IonEventType.SCALAR, field_name=field)
event = obj.to_event(IonEventType.SCALAR, field_name=field, in_struct=in_struct, depth=depth)
else:
event = IonEvent(IonEventType.SCALAR, ion_type, obj, field_name=field)
event = IonEvent(IonEventType.SCALAR, ion_type, obj, field_name=field, depth=depth)
writer.send(event)


Expand Down
92 changes: 92 additions & 0 deletions tests/test_events.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Copyright 2016 Amazon.com, Inc. or its affiliates. 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.
# A copy of the License is located at:
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file 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.

# Python 2/3 compatibility
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import pytest

from tests.event_aliases import *

from amazon.ion.util import coroutine
from amazon.ion.symbols import SymbolToken
from amazon.ion.simple_types import IonPySymbol
from amazon.ion.simpleion import _dump, loads, _FROM_TYPE


def _to_event_parameters():
return [
[loads('5'), [IonEvent(IonEventType.SCALAR, IonType.INT, 5, None, (), depth=0)]],
[loads('abc'), [IonEvent(IonEventType.SCALAR, IonType.SYMBOL, SymbolToken('abc', None), None, (), depth=0)]],
[loads('{abc: 1}'), [
IonEvent(IonEventType.CONTAINER_START, IonType.STRUCT, depth=0),
IonEvent(IonEventType.SCALAR, IonType.INT, 1, SymbolToken('abc', None), (), depth=1),
IonEvent(IonEventType.CONTAINER_END),
]],
[loads('$0'), [IonEvent(IonEventType.SCALAR, IonType.SYMBOL, SymbolToken(None, 0), None, (), depth=0)]],
[loads('{$0: $0}'), [
IonEvent(IonEventType.CONTAINER_START, IonType.STRUCT, depth=0),
IonEvent(IonEventType.SCALAR, IonType.SYMBOL, IonPySymbol(None, 0), SymbolToken(None, 0), (), depth=1),
IonEvent(IonEventType.CONTAINER_END),
]],
[loads('[1, 2, 3, [4, 5, 6], [7, 8, 9]]'), [
IonEvent(IonEventType.CONTAINER_START, IonType.LIST, depth=0),
IonEvent(IonEventType.SCALAR, IonType.INT, 1, depth=1),
IonEvent(IonEventType.SCALAR, IonType.INT, 2, depth=1),
IonEvent(IonEventType.SCALAR, IonType.INT, 3, depth=1),
IonEvent(IonEventType.CONTAINER_START, IonType.LIST, depth=1),
IonEvent(IonEventType.SCALAR, IonType.INT, 4, depth=2),
IonEvent(IonEventType.SCALAR, IonType.INT, 5, depth=2),
IonEvent(IonEventType.SCALAR, IonType.INT, 6, depth=2),
IonEvent(IonEventType.CONTAINER_END),
IonEvent(IonEventType.CONTAINER_START, IonType.LIST, depth=1),
IonEvent(IonEventType.SCALAR, IonType.INT, 7, depth=2),
IonEvent(IonEventType.SCALAR, IonType.INT, 8, depth=2),
IonEvent(IonEventType.SCALAR, IonType.INT, 9, depth=2),
IonEvent(IonEventType.CONTAINER_END),
IonEvent(IonEventType.CONTAINER_END),
]],

[5, [IonEvent(IonEventType.SCALAR, IonType.INT, 5, None, (), depth=0)]],
[u'abc', [IonEvent(IonEventType.SCALAR, IonType.STRING, "abc", None, (), depth=0)]],
[{'abc': 1}, [
IonEvent(IonEventType.CONTAINER_START, IonType.STRUCT, depth=0),
IonEvent(IonEventType.SCALAR, IonType.INT, 1, "abc", (), depth=1),
IonEvent(IonEventType.CONTAINER_END),
]],
]


def _to_event_test_name(params):
return str(params[0])


@pytest.mark.parametrize("params", _to_event_parameters(), ids=_to_event_test_name)
def test_to_event(params):
value, expected_events = params
events = []

@coroutine
def event_receiver():
event = yield
while True:
events.append(event)
event = yield

_dump(value, event_receiver(), _FROM_TYPE)

assert events == expected_events

7 changes: 6 additions & 1 deletion tests/test_simple_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,12 @@ def test_event_types(p):

event_output = p.type.from_event(p.event)
value_output = p.type.from_value(ion_type, value, p.event.annotations)
to_event_output = value_output.to_event(p.event.event_type, p.event.field_name, p.event.depth)
to_event_output = value_output.to_event(p.event.event_type, p.event.field_name, in_struct=True, depth=p.event.depth)
if p.event.ion_type.is_container:
# compensate for abuse of IonEvent.value, which is intended to be None for CONTAINER_START events,
# but is populated and relied upon by the logic of this test code
assert to_event_output.value is None
to_event_output = to_event_output._replace(value=p.event.value)
assert p.event == to_event_output

if p.type is IonPyNull:
Expand Down

0 comments on commit 38fbd2d

Please sign in to comment.