association proxies are not json serializable

Issue #3040 wontfix
metagriffin created an issue

this can be easily circumvented by simply casting them when needed, e.g. calling json.dumps(list(assoc_list)).

however, it would be great if these objects were directly serializable. the ORM instrumented objects, InstrumentedList, InstrumentedSet, and InstrumentedDict already achieve this because they respectively subclass list, set, and dict -- the json package handles them appropriately then.

Comments (8)

  1. Mike Bayer repo owner

    I'd want to get into the appropriate ABC logic here, rather than a straight list/dict subclass. I've only dealt with ABC's minimally so if someone wants to start off that would help.

  2. metagriffin reporter

    although i agree it would be good to be ABC-compatible, it would be odd if the Instrumented* objects were non-ABC-style, and the Association* objects were ABC-style.

    thus, my vote would be to get the Association* objects working with list/set/dict, then someone (with more ABC expertise) can upgrade both to ABC-style.

  3. Mike Bayer repo owner

    well it looks like python json doesn't recognize ABC's anyway. which is of course ridiculous.

  4. Mike Bayer repo owner

    many failures. feel free to work on them. patch:

    diff --git a/lib/sqlalchemy/ext/associationproxy.py b/lib/sqlalchemy/ext/associationproxy.py
    index 045645f..f243b40 100644
    --- a/lib/sqlalchemy/ext/associationproxy.py
    +++ b/lib/sqlalchemy/ext/associationproxy.py
    @@ -513,7 +512,7 @@ class _AssociationCollection(object):
             self.parent._inflate(self)
    
    
    -class _AssociationList(_AssociationCollection):
    +class _AssociationList(_AssociationCollection, list):
         """Generic, converting, list-to-list proxy."""
    
         def _create(self, value):
    @@ -705,7 +704,7 @@ class _AssociationList(_AssociationCollection):
     _NotProvided = util.symbol('_NotProvided')
    
    
    -class _AssociationDict(_AssociationCollection):
    +class _AssociationDict(_AssociationCollection, dict):
         """Generic, converting, dict-to-dict proxy."""
    
         def _create(self, key, value):
    @@ -853,7 +852,7 @@ class _AssociationDict(_AssociationCollection):
         del func_name, func
    
    
    -class _AssociationSet(_AssociationCollection):
    +class _AssociationSet(_AssociationCollection, set):
         """Generic, converting, set-to-set proxy."""
    
         def _create(self, value):
    

    failures:

    =================================== FAILURES ===================================
    ______________________ CustomSetTest.test_set_comparisons ______________________
    Traceback (most recent call last):
      File "/Users/classic/dev/sqlalchemy/test/ext/test_associationproxy.py", line 484, in test_set_comparisons
        control.intersection(other))
      File "/Users/classic/dev/sqlalchemy/test/../lib/sqlalchemy/testing/assertions.py", line 180, in eq_
        assert a == b, msg or "%r != %r" % (a, b)
    AssertionError: set([]) != set(['a', 'c', 'b'])
    _______________________ CustomSetTest.test_set_mutation ________________________
    Traceback (most recent call last):
      File "/Users/classic/dev/sqlalchemy/test/ext/test_associationproxy.py", line 517, in test_set_mutation
        self.assert_(p.children == control)
      File "/Users/classic/dev/sqlalchemy/test/../lib/sqlalchemy/testing/fixtures.py", line 40, in assert_
        assert val, msg
    AssertionError: None
    ------------------------------- Captured stdout --------------------------------
    Test set(['a', 'c', 'b']).update(set(['a', 'c', 'b'])):
    ('want', "set(['a', 'c', 'b'])")
    ('got', 'set([])')
    ______________________ CustomSetTest.test_set_operations _______________________
    Traceback (most recent call last):
      File "/Users/classic/dev/sqlalchemy/test/ext/test_associationproxy.py", line 415, in test_set_operations
        self.assert_(p1.children == set(['a', 'b', 'c']))
      File "/Users/classic/dev/sqlalchemy/test/../lib/sqlalchemy/testing/fixtures.py", line 40, in assert_
        assert val, msg
    AssertionError: None
    ______________ DictOfTupleUpdateTest.test_update_multi_elem_dict _______________
    Traceback (most recent call last):
      File "/Users/classic/dev/sqlalchemy/test/ext/test_associationproxy.py", line 1522, in test_update_multi_elem_dict
        eq_(a1.elements, {("B", 3): 'elem2', ("C", 4): "elem3"})
      File "/Users/classic/dev/sqlalchemy/test/../lib/sqlalchemy/testing/assertions.py", line 180, in eq_
        assert a == b, msg or "%r != %r" % (a, b)
    AssertionError: {('B', 3): 'elem2', ('C', 4): 'elem3'} != {('B', 3): 'elem2', ('C', 4): 'elem3'}
    ______________ DictOfTupleUpdateTest.test_update_multi_elem_list _______________
    Traceback (most recent call last):
      File "/Users/classic/dev/sqlalchemy/test/ext/test_associationproxy.py", line 1532, in test_update_multi_elem_list
        eq_(a1.elements, {("B", 3): 'elem2', ("C", 4): "elem3"})
      File "/Users/classic/dev/sqlalchemy/test/../lib/sqlalchemy/testing/assertions.py", line 180, in eq_
        assert a == b, msg or "%r != %r" % (a, b)
    AssertionError: {('B', 3): 'elem2', ('C', 4): 'elem3'} != {('B', 3): 'elem2', ('C', 4): 'elem3'}
    _______________ DictOfTupleUpdateTest.test_update_one_elem_dict ________________
    Traceback (most recent call last):
      File "/Users/classic/dev/sqlalchemy/test/ext/test_associationproxy.py", line 1517, in test_update_one_elem_dict
        eq_(a1.elements, {("B", 3): 'elem2'})
      File "/Users/classic/dev/sqlalchemy/test/../lib/sqlalchemy/testing/assertions.py", line 180, in eq_
        assert a == b, msg or "%r != %r" % (a, b)
    AssertionError: {('B', 3): 'elem2'} != {('B', 3): 'elem2'}
    _______________ DictOfTupleUpdateTest.test_update_one_elem_list ________________
    Traceback (most recent call last):
      File "/Users/classic/dev/sqlalchemy/test/ext/test_associationproxy.py", line 1527, in test_update_one_elem_list
        eq_(a1.elements, {("B", 3): 'elem2'})
      File "/Users/classic/dev/sqlalchemy/test/../lib/sqlalchemy/testing/assertions.py", line 180, in eq_
        assert a == b, msg or "%r != %r" % (a, b)
    AssertionError: {('B', 3): 'elem2'} != {('B', 3): 'elem2'}
    _____________________ ReconstitutionTest.test_pickle_dict ______________________
    Traceback (most recent call last):
      File "/Users/classic/dev/sqlalchemy/test/ext/test_associationproxy.py", line 1048, in test_pickle_dict
        assert p.kids == {'c1': 'c1', 'c2': 'c2'}
    AssertionError: assert {'c2': 'c2', 'c1': 'c1'} == {'c1': 'c1', 'c2': 'c2'}
      Common items:
      {'c1': 'c1', 'c2': 'c2'}
    ______________________ ReconstitutionTest.test_pickle_set ______________________
    Traceback (most recent call last):
      File "/Users/classic/dev/sqlalchemy/test/ext/test_associationproxy.py", line 1033, in test_pickle_set
        r1 = pickle.loads(pickle.dumps(p))
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1374, in dumps
        Pickler(file, protocol).dump(obj)
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 224, in dump
        self.save(obj)
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 331, in save
        self.save_reduce(obj=obj, *rv)
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 419, in save_reduce
        save(state)
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 286, in save
        f(self, obj) # Call unbound method with explicit self
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 649, in save_dict
        self._batch_setitems(obj.iteritems())
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 663, in _batch_setitems
        save(v)
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 286, in save
        f(self, obj) # Call unbound method with explicit self
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 562, in save_tuple
        save(element)
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 331, in save
        self.save_reduce(obj=obj, *rv)
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 419, in save_reduce
        save(state)
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 286, in save
        f(self, obj) # Call unbound method with explicit self
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 649, in save_dict
        self._batch_setitems(obj.iteritems())
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 663, in _batch_setitems
        save(v)
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 286, in save
        f(self, obj) # Call unbound method with explicit self
      File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 748, in save_global
        (obj, module, name))
    PicklingError: Can't pickle <function <lambda> at 0x1043f8b90>: it's not found as sqlalchemy.ext.associationproxy.<lambda>
    _________________________ SetTest.test_set_comparisons _________________________
    Traceback (most recent call last):
      File "/Users/classic/dev/sqlalchemy/test/ext/test_associationproxy.py", line 484, in test_set_comparisons
        control.intersection(other))
      File "/Users/classic/dev/sqlalchemy/test/../lib/sqlalchemy/testing/assertions.py", line 180, in eq_
        assert a == b, msg or "%r != %r" % (a, b)
    AssertionError: set([]) != set(['a', 'c', 'b'])
    __________________________ SetTest.test_set_mutation ___________________________
    Traceback (most recent call last):
      File "/Users/classic/dev/sqlalchemy/test/ext/test_associationproxy.py", line 517, in test_set_mutation
        self.assert_(p.children == control)
      File "/Users/classic/dev/sqlalchemy/test/../lib/sqlalchemy/testing/fixtures.py", line 40, in assert_
        assert val, msg
    AssertionError: None
    ------------------------------- Captured stdout --------------------------------
    Test set(['a', 'c', 'b']).update(set(['a', 'c', 'b'])):
    ('want', "set(['a', 'c', 'b'])")
    ('got', 'set([])')
    _________________________ SetTest.test_set_operations __________________________
    Traceback (most recent call last):
      File "/Users/classic/dev/sqlalchemy/test/ext/test_associationproxy.py", line 415, in test_set_operations
        self.assert_(p1.children == set(['a', 'b', 'c']))
      File "/Users/classic/dev/sqlalchemy/test/../lib/sqlalchemy/testing/fixtures.py", line 40, in assert_
        assert val, msg
    AssertionError: None
    
  5. Mike Bayer repo owner

    as always, if someone really wants to work on this, feel free. as it stands I have no plans to work with this

  6. Log in to comment