-
Notifications
You must be signed in to change notification settings - Fork 49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improves the deserialization speed by reducing object initialization overhead #284
Changes from 1 commit
0ac1d68
9b737ea
4c8317a
7963212
b6fe2df
a8c5514
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,20 +6,18 @@ | |
|
||
|
||
class IonPyNull_new(object): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I may discover a reasonable "why" later, but barring that, ditch the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay, I will keep this comment unsolved until renaming them. I named them EDIT: Once we approve other comments within this PR, I'll rename these classes to their appropriate names There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please confirm this is still the plan. [refers to resolved conversation about removing _new classes before actually merging] There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, I'll rename it once we address other comments. I'm using these class names for easier before/after performance comparison... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry I missed the updated question. Would you also refer to removing the old IonPyObject? yeah I think we can completely switch to the _new IonPyObject and rename them to the official I left draft comments and tried to send all of them at once but seems the comment doesn't cache draft once I click There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The latest commit b6fe2df renames |
||
ion_annotations = () | ||
ion_type = IonType.NULL # TODO initialized to NULL type first, what's the real type? | ||
|
||
__name__ = 'IonPyNull_new' | ||
__qualname__ = 'IonPyNull_new' | ||
ion_type = IonType.NULL | ||
|
||
def __init__(self, ion_type=None, value=None, annotations=()): | ||
self.ion_type = ion_type | ||
def __init__(self, ion_type=IonType.NULL, value=None, annotations=()): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we drop the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried to maintain the constructors' signatures identical to
|
||
self.ion_type = ion_type # TODO initialized to NULL type first, what's the real type? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what is the action here? I would say it's OK as it is... or suggest that for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed. |
||
self.ion_annotations = annotations | ||
|
||
def __bool__(self): | ||
return False | ||
|
||
def _copy(self): | ||
def __copy__(self): | ||
args, kwargs = self._to_constructor_args(self) | ||
value = self.__class__(*args, **kwargs) | ||
value.ion_type = self.ion_type | ||
|
@@ -68,18 +66,16 @@ def to_event(self, event_type, field_name=None, in_struct=False, depth=None): | |
|
||
|
||
class IonPyDecimal_new(Decimal): | ||
ion_annotations = () | ||
ion_type = IonType.DECIMAL | ||
|
||
__name__ = 'IonPyDecimal_new' | ||
__qualname__ = 'IonPyDecimal_new' | ||
ion_type = IonType.DECIMAL | ||
|
||
def __new__(cls, ion_type, value, annotations=()): | ||
def __new__(cls, ion_type=IonType.DECIMAL, value=None, annotations=()): | ||
v = super().__new__(cls, value) | ||
v.ion_annotations = annotations | ||
return v | ||
|
||
def _copy(self): | ||
def __copy__(self): | ||
args, kwargs = self._to_constructor_args(self) | ||
value = self.__class__(*args, **kwargs) | ||
value.ion_type = self.ion_type | ||
|
@@ -128,18 +124,16 @@ def to_event(self, event_type, field_name=None, in_struct=False, depth=None): | |
|
||
|
||
class IonPyInt_new(int): | ||
ion_annotations = () | ||
ion_type = IonType.INT | ||
|
||
__name__ = 'IonPyInt_new' | ||
__qualname__ = 'IonPyInt_new' | ||
ion_type = IonType.INT | ||
|
||
def __new__(cls, ion_type, value, annotations=()): | ||
def __new__(cls, ion_type=IonType.INT, value=None, annotations=()): | ||
v = super().__new__(cls, value) | ||
v.ion_annotations = annotations | ||
return v | ||
|
||
def _copy(self): | ||
def __copy__(self): | ||
args, kwargs = self._to_constructor_args(self) | ||
value = self.__class__(*args, **kwargs) | ||
value.ion_type = self.ion_type | ||
|
@@ -188,21 +182,19 @@ def to_event(self, event_type, field_name=None, in_struct=False, depth=None): | |
|
||
|
||
class IonPyBool_new(int): | ||
ion_annotations = () | ||
ion_type = IonType.BOOL | ||
|
||
__name__ = 'IonPyBool_new' | ||
__qualname__ = 'IonPyBool_new' | ||
ion_type = IonType.BOOL | ||
|
||
def __repr__(self): | ||
return str(bool(self)) | ||
|
||
def __new__(cls, ion_type, value, annotations=()): | ||
def __new__(cls, ion_type=IonType.BOOL, value=None, annotations=()): | ||
v = super().__new__(cls, value) | ||
v.ion_annotations = annotations | ||
return v | ||
|
||
def _copy(self): | ||
def __copy__(self): | ||
args, kwargs = self._to_constructor_args(self) | ||
value = self.__class__(*args, **kwargs) | ||
value.ion_type = self.ion_type | ||
|
@@ -251,18 +243,16 @@ def to_event(self, event_type, field_name=None, in_struct=False, depth=None): | |
|
||
|
||
class IonPyFloat_new(float): | ||
ion_annotations = () | ||
ion_type = IonType.FLOAT | ||
|
||
__name__ = 'IonPyFloat_new' | ||
__qualname__ = 'IonPyFloat_new' | ||
ion_type = IonType.FLOAT | ||
|
||
def __new__(cls, ion_type, value, annotations=()): | ||
def __new__(cls, ion_type=IonType.FLOAT, value=None, annotations=()): | ||
v = super().__new__(cls, value) | ||
v.ion_annotations = annotations | ||
return v | ||
|
||
def _copy(self): | ||
def __copy__(self): | ||
args, kwargs = self._to_constructor_args(self) | ||
value = self.__class__(*args, **kwargs) | ||
value.ion_type = self.ion_type | ||
|
@@ -311,18 +301,16 @@ def to_event(self, event_type, field_name=None, in_struct=False, depth=None): | |
|
||
|
||
class IonPyText_new(str): | ||
ion_annotations = () | ||
ion_type = IonType.STRING | ||
|
||
__name__ = 'IonPyText_new' | ||
__qualname__ = 'IonPyText_new' | ||
ion_type = IonType.STRING | ||
|
||
def __new__(cls, ion_type=None, value=None, annotations=()): | ||
def __new__(cls, ion_type=IonType.STRING, value=None, annotations=()): | ||
v = super().__new__(cls, value) | ||
v.ion_annotations = annotations | ||
return v | ||
|
||
def _copy(self): | ||
def __copy__(self): | ||
args, kwargs = self._to_constructor_args(self) | ||
value = self.__class__(*args, **kwargs) | ||
value.ion_type = self.ion_type | ||
|
@@ -371,19 +359,17 @@ def to_event(self, event_type, field_name=None, in_struct=False, depth=None): | |
|
||
|
||
class IonPySymbol_new(SymbolToken): | ||
ion_annotations = () | ||
ion_type = IonType.SYMBOL | ||
|
||
__name__ = 'IonPySymbol_new' | ||
__qualname__ = 'IonPySymbol_new' | ||
ion_type = IonType.SYMBOL | ||
|
||
# a good signature: IonPySymbol(ion_type, symbol_token, annotation) | ||
def __new__(cls, ion_type, value, annotations=()): | ||
def __new__(cls, ion_type=IonType.SYMBOL, value=None, annotations=()): | ||
v = super().__new__(cls, *value) | ||
v.ion_annotations = annotations | ||
return v | ||
|
||
def _copy(self): | ||
def __copy__(self): | ||
args, kwargs = self._to_constructor_args(self) | ||
value = self.__class__(*args, **kwargs) | ||
value.ion_type = self.ion_type | ||
|
@@ -437,19 +423,19 @@ def to_event(self, event_type, field_name=None, in_struct=False, depth=None): | |
|
||
|
||
class IonPyList_new(list): | ||
ion_annotations = () | ||
ion_type = IonType.LIST | ||
|
||
__name__ = 'IonPyList_new' | ||
__qualname__ = 'IonPyList_new' | ||
ion_type = IonType.LIST | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. like with null I would not have the class attribute for List. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed. |
||
|
||
def __init__(self, ion_type, value, annotations=()): | ||
def __init__(self, ion_type=IonType.LIST, value=None, annotations=()): | ||
if value is None: | ||
value = [] | ||
super().__init__(value) | ||
self.ion_annotations = annotations | ||
# it's possible to be Sexp | ||
self.ion_type = ion_type | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the constructor still can control ion_type as it's possible to be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we call this IonPySequence or something, then? Or does this just need to conform to the existing naming? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, It won't break any current use case since we can call it any name we want (E.g., we are calling it an unofficial name with the Or maybe we can redeclare it to ionPyList in
|
||
|
||
def _copy(self): | ||
def __copy__(self): | ||
args, kwargs = self._to_constructor_args(self) | ||
value = self.__class__(*args, **kwargs) | ||
value.ion_type = self.ion_type | ||
|
@@ -498,22 +484,17 @@ def to_event(self, event_type, field_name=None, in_struct=False, depth=None): | |
|
||
|
||
class IonPyDict_new(MutableMapping): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If there is existing pydoc about the contract for setting, deleting and accessing fields with multiple mappings please ensure it is moved. If not, now is a great time to add it! :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good! I'm working on adding a method documents according to the current behavior. |
||
ion_annotations = () | ||
ion_type = IonType.STRUCT | ||
|
||
__name__ = 'IonPyDict_new' | ||
__qualname__ = 'IonPyDict_new' | ||
ion_type = IonType.STRUCT | ||
|
||
# one good initialization example: my_dict = IonPyDict(ion_type, value, annotations) | ||
def __init__(self, *args, **kwargs): | ||
def __init__(self, ion_type=IonType.STRUCT, value=None, annotations=()): | ||
super().__init__() | ||
self.ion_annotations = annotations | ||
self.__store = {} | ||
cheqianh marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Per the resolved comment thread this was to change to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for catching this. Reverted by accident when I benchmark before/after commits. |
||
if args is not None: | ||
if len(args) > 1 and args[1] is not None: | ||
for key, value in iter(args[1].items()): | ||
self.__store[key] = [value] | ||
if len(args) > 2 and args[2] is not None: | ||
self.ion_annotations = args[2] | ||
if value is not None: | ||
for key, value in iter(value.items()): | ||
self.__store[key] = [value] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. doesn't this overwrite when There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The parameter |
||
|
||
def __getitem__(self, key): | ||
return self.__store[key][len(self.__store[key]) - 1] # Return only one in order not to break clients | ||
|
@@ -557,7 +538,7 @@ def items(self): | |
output.append((k, v)) | ||
return output | ||
|
||
def _copy(self): | ||
def __copy__(self): | ||
args, kwargs = self._to_constructor_args(self) | ||
value = self.__class__(*args, **kwargs) | ||
value.ion_type = self.ion_type | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
from typing import Sequence, NamedTuple | ||
|
||
from amazon.ion.ion_py_objects import IonPyDict_new as Multimap | ||
|
||
from tests import parametrize | ||
|
||
|
||
class _P(NamedTuple): | ||
pairs: Sequence | ||
expected_all_values: Sequence | ||
expected_single_value: Sequence | ||
expected_total_len: int | ||
|
||
def __str__(self): | ||
return '{name}'.format(name=self.pairs) | ||
|
||
|
||
ALL_DATA = _P( | ||
pairs=[('a', 1), ('a', 2), ('a', 3), ('a', [4, 5, 6]), ('b', 0), ('c', {'x': 'z', 'r': 's'})], | ||
expected_all_values=[('a', [1, 2, 3, [4, 5, 6]]), ('b', [0]), ('c', [{'x': 'z', 'r': 's'}])], | ||
expected_single_value=[('a', [4, 5, 6]), ('b', 0), ('c', {'x': 'z', 'r': 's'})], | ||
expected_total_len=6 | ||
) | ||
|
||
|
||
def _create_multimap_with_items(pairs): | ||
m = Multimap() | ||
for pair in pairs: | ||
m.add_item(pair[0], pair[1]) | ||
return m | ||
|
||
|
||
@parametrize( | ||
ALL_DATA | ||
) | ||
def test_add_item(p): | ||
m = _create_multimap_with_items(p.pairs) | ||
for expected in p.expected_all_values: | ||
assert list([x for x in m.get_all_values(expected[0])]) == expected[1] | ||
for expected in p.expected_single_value: | ||
assert m[expected[0]] == expected[1] | ||
assert p.expected_total_len == len(m) | ||
|
||
|
||
@parametrize( | ||
(ALL_DATA, ["a"], 2), | ||
(ALL_DATA, ["b"], 5), | ||
(ALL_DATA, ["c"], 5), | ||
(ALL_DATA, ["a", "b"], 1), | ||
(ALL_DATA, ["a", "b", "c"], 0) | ||
) | ||
def test_delete_item(item): | ||
m = _create_multimap_with_items(item[0].pairs) | ||
p, items_to_remove, len_after_removal = item | ||
for to_remove in items_to_remove: | ||
del m[to_remove] | ||
assert len(m) == item[2] | ||
|
||
|
||
@parametrize( | ||
{}, | ||
{"a": 1}, | ||
{"a": 1, "b": 2, "c": [1, 2, {3: 4}]} | ||
) | ||
def test_constructor(d): | ||
m = Multimap(None, d) | ||
for k, v in iter(d.items()): | ||
assert m[k] == v | ||
assert len(m) == len(d) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method will be deprecated with other old methods later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right... But what will replace it? I ask because I think that would be either just trying to get the
iontype
andannotations
or testing if the two objectshasattr
them. So why not do that now and avoid the series ofor
s?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it, refactored it to check if both ion_type and ion_annotations attributes exist.