Skip to content

Enum types are not picklable if defined in nested namespaces #1989

@BCSharp

Description

@BCSharp

This used to work on .NET:

import clr
import pickle

clr.AddReference("System.Text.Json")
clr.AddReference("Microsoft.Scripting")

import System.Text.Json
import Microsoft.Scripting

data = [
    System.DayOfWeek.Wednesday,  # still works
    System.Text.Json.JsonValueKind.Object,  # nested namespace
    Microsoft.Scripting.Severity.Warning,  # different namespace than System
]

for value in data:
    print(pickle.dumps(value))

It does not work on .NET 8.0 and higher.

It was relying on clr.Deserialize, which was included if IronPython was compiled with FEATURE_SERIALIZATION (on by default). The implementation was using BinaryFormatter, which is now unsupported. With clr.Deserialize and clr.Serialize gone, pickling relies on a generic object __reduce_ex__ implementation. It fails for types that are placed in nested namespaces, because __module__ attribute on those object is not the actual module as in imported sys.modules but the complete dotted-path namespace.

Two ideas come to mind on how to resolve this regression:

  1. Tweak pickle.py in StdLib to be more mindful of IronPython namespaces; for instance a translation of (module, name) from e.g. (System.Text.Json, JsonValueKind) to (System, Text.Json.JsonValueKind) is simple and would do the trick, but it will require using pickle protocol >= 4, which allows for dotted (nested) type names.

  2. Capture the resolution of __reduce_ex__ in OneOffRTesolver like it used to be when FEATURE_SERIALIZATION was defined, and redirect to EnumOps.SerializeReduce. This will handle all enums, but not other types that may have worked in case of option 1, OTOH, StdLib is untouched.

(or the combination of the two).

Both options make objects pickled by IronPython/.NET 6.0 not unpicklable (not loadable) on .NET 8.0 and higher, but that is the consequence of BinaryFormatter being gone. AFAIK, pickle file format is not meant to be binary-portable across Python versions, so one can argue that this is better than the alternative of not being able to pickle at all. Universal pickling of an arbitrary .NET object is probably not feasible, but it is feasible and desirable for simple built-in types. For user types for which pickle support is needed, the developer can implement their own __reduce_ex__.

See also #29

Metadata

Metadata

Assignees

No one assigned

    Labels

    .NET interopIssues relating to how IronPython interacts with .NET typesregression

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions