Issue 31486: calling a _json.Encoder object raises a SystemError in case obj.items() returned a tuple (original) (raw)

the following code causes a SystemError:

import json.encoder class BadDict(dict): def items(self): return ()

encoder = json.encoder.c_make_encoder(None, None, None, None, 'foo', 'bar', True, None, None) encoder(obj=BadDict({'spam': 42}), _current_indent_level=4)

this is because encoder_call() (in Modules/_json.c) passes the 'obj' argument so that eventually encoder_listencode_dict() calls PyMapping_Items() on it. encoder_listencode_dict() assumes that PyMapping_Items() returned a list, and passes it to PyList_Sort().

ISTM that subclassing dict and implementing items() so that it returns a tuple is not unrealistic.

maybe we should silently convert the tuple that PyMapping_Items() returned to a list?

Oh, I was aware of this issue for long time, but didn't have good opportunity for fixing it. And yes, making PyMapping_Items() (and friends) always returning a list looks like a reasonable option to me. This could fix similar bugs in third-party extensions. In Python 2 PyMapping_Items() is documented as returning a list, but actually it can return an arbitrary type. Authors of extensions could be fooled by the documentation and use concrete list API.

The drawback of this solution is some performance degradation in rare case of items() returning a tuple.