Skip to content

Graalpy does not preserve order if OrderedDict is casted to Mapping in C Extension #553

@CloseChoice

Description

@CloseChoice

Hi,

I stumbled across this since we use graalpy on the pydantic test suite, where we interact with Python's C API through Rust. If going through that the order of an OrderedDict is not respected (it is for a dict though). Here are the steps to reproduce:

dict_printer.c:

#include <Python.h>

static PyObject* print_dict(PyObject* self, PyObject* args) {
    PyObject* dict;

    if (!PyArg_ParseTuple(args, "O", &dict)) {
        return NULL;
    }

    PyObject* mapping = PyObject_GetIter(PyMapping_Items(dict));
    if (mapping == NULL) {
        PyErr_SetString(PyExc_TypeError, "Object is not a valid mapping");
        return NULL;
    }

    PyObject* item;
    printf("Mapping contents:\n");
    while ((item = PyIter_Next(mapping)) != NULL) {
        PyObject* key = PyTuple_GetItem(item, 0);
        PyObject* value = PyTuple_GetItem(item, 1);

        PyObject* key_str = PyObject_Str(key);
        PyObject* value_str = PyObject_Str(value);

        const char* key_cstr = PyUnicode_AsUTF8(key_str);
        const char* value_cstr = PyUnicode_AsUTF8(value_str);

        printf("  %s: %s\n", key_cstr, value_cstr);

        Py_DECREF(key_str);
        Py_DECREF(value_str);
        Py_DECREF(item);
    }

    Py_DECREF(mapping);

    if (PyErr_Occurred()) {
        return NULL;
    }

    Py_RETURN_NONE;
}

static PyMethodDef DictPrinterMethods[] = {
    {"print_dict", print_dict, METH_VARARGS, "Print all elements of a dictionary/mapping"},
    {NULL, NULL, 0, NULL}
};

static struct PyModuleDef dictprintermodule = {
    PyModuleDef_HEAD_INIT,
    "dict_printer",
    "A module to print dictionary/mapping elements",
    -1,
    DictPrinterMethods
};

PyMODINIT_FUNC PyInit_dict_printer(void) {
    return PyModule_Create(&dictprintermodule);
}

setup.py

from setuptools import setup, Extension

module = Extension('dict_printer',
                   sources=['dict_printer.c'])

setup(name='dict_printer',
      version='1.0',
      description='C extension to print dictionary/mapping elements',
      ext_modules=[module])

Then run python setup.py build_ext --inplace and execute

from collections import OrderedDict
import dict_printer

my_dict = OrderedDict({'a': 1, 'b': 2, 'c': 3})
my_dict.move_to_end('a')
dict_printer.print_dict(my_dict)
print(my_dict)

This outputs with graalpy:

Mapping contents:
  a: 1
  b: 2
  c: 3
OrderedDict({'b': 2, 'c': 3, 'a': 1})

using CPython:

Mapping contents:
  b: 2
  c: 3
  a: 1
OrderedDict([('b', 2), ('c', 3), ('a', 1)])

Thanks.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions