amazon-ion / ion-python

A Python implementation of Amazon Ion.
https://amazon-ion.github.io/ion-docs/
Apache License 2.0
261 stars 51 forks source link

Deep Copy fails / Save to DynamoDB fails #61

Open pettijohn opened 6 years ago

pettijohn commented 6 years ago

I can't save an Ion payload to DynamoDB with Boto3. It looks like Boto3 makes a deep copy prior to saving, which throws.

import boto3
import copy
from decimal import Decimal
from amazon.ion import simpleion
from io import StringIO

d = { "value": Decimal('1.1') }
payload = simpleion.dumps(d)
payloadValue = simpleion.load(StringIO(payload))
c = copy.deepcopy(payloadValue) # This fails
table = boto3.resource('dynamodb').Table('MyTable')
table.put_item(Item=payloadValue) # This fails because it makes a deep copy

Should an Ion struct be able to be deep copied? Stack below.

TypeError: __new__() missing 1 required positional argument: 'value'

Traceback (most recent call last):
  File "c:\Projects\\lambda\lambda_function.py", line 32, in <module>
    bug()
  File "c:\Projects\\lambda\lambda_function.py", line 29, in bug
    c = copy.deepcopy(payloadValue)
  File "C:\Users\\AppData\Local\Programs\Python\Python36\lib\copy.py", line 180, in deepcopy
    y = _reconstruct(x, memo, *rv)
  File "C:\Users\\AppData\Local\Programs\Python\Python36\lib\copy.py", line 280, in _reconstruct
    state = deepcopy(state, memo)
  File "C:\Users\\AppData\Local\Programs\Python\Python36\lib\copy.py", line 150, in deepcopy
    y = copier(x, memo)
  File "C:\Users\\AppData\Local\Programs\Python\Python36\lib\copy.py", line 240, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
  File "C:\Users\\AppData\Local\Programs\Python\Python36\lib\copy.py", line 180, in deepcopy
    y = _reconstruct(x, memo, *rv)
  File "C:\Users\\AppData\Local\Programs\Python\Python36\lib\copy.py", line 274, in _reconstruct
    y = func(*args)
  File "C:\Users\\AppData\Local\Programs\Python\Python36\lib\copy.py", line 273, in <genexpr>
    args = (deepcopy(arg, memo) for arg in args)
  File "C:\Users\\AppData\Local\Programs\Python\Python36\lib\copy.py", line 180, in deepcopy
    y = _reconstruct(x, memo, *rv)
  File "C:\Users\\AppData\Local\Programs\Python\Python36\lib\copy.py", line 274, in _reconstruct
    y = func(*args)
  File "C:\Users\\AppData\Local\Programs\Python\Python36\lib\copyreg.py", line 88, in __newobj__
    return cls.__new__(cls, *args)
Boyettw commented 6 years ago

It looks like you need to have implemented your own copy method for this to work: Two problems often exist with deep copy operations that don’t exist with shallow copy operations:

Recursive objects (compound objects that, directly or indirectly, contain a reference to themselves) may cause a recursive loop.
Because deep copy copies everything it may copy too much, such as data which is intended to be shared between copies.

The deepcopy() function avoids these problems by:

keeping a “memo” dictionary of objects already copied during the current copying pass; and
letting user-defined classes override the copying operation or the set of components copied.

copy just doesn't work on everything out of the box, you would need to implement copy for everything in the library.