520 lines
13 KiB
Go
520 lines
13 KiB
Go
package pickle
|
|
|
|
import (
|
|
"container/list"
|
|
"fmt"
|
|
"reflect"
|
|
)
|
|
|
|
// This file contains custom types and interfaces for casting Python data types from Pickle.
|
|
//
|
|
// Custom Types:
|
|
// =============
|
|
// 1. ByteArray
|
|
// 2. Dict
|
|
// 3. Tuple
|
|
// 4. OrderedDict
|
|
// 5. List
|
|
// 6. Set
|
|
// 7. FrozenSet
|
|
// 8. Object
|
|
// 9. Reconstructor
|
|
// 10. GenericClass
|
|
|
|
// Interfaces:
|
|
// ===========
|
|
|
|
// Callable is implemented by any value that can be directly called to get a
|
|
// new value.
|
|
//
|
|
// It is usually implemented by Python-like functions (returning a value
|
|
// given some arguments), or classes (typically returning an instance given
|
|
// some constructor arguments).
|
|
type Callable interface {
|
|
// Call mimics a direct invocation on a Python value, such as a function
|
|
// or class (constructor).
|
|
Call(args ...interface{}) (interface{}, error)
|
|
}
|
|
|
|
// PyNewable is implemented by any value that has a Python-like
|
|
// "__new__" method.
|
|
//
|
|
// It is usually implemented by values representing Python classes.
|
|
type PyNewable interface {
|
|
// PyNew mimics Python invocation of the "__new__" method, usually
|
|
// provided by classes.
|
|
//
|
|
// See: https://docs.python.org/3/reference/datamodel.html#object.__new__
|
|
PyNew(args ...interface{}) (interface{}, error)
|
|
}
|
|
|
|
// PyStateSettable is implemented by any value that has a Python-like
|
|
// "__setstate__" method.
|
|
type PyStateSettable interface {
|
|
// PySetState mimics Python invocation of the "__setstate__" method.
|
|
//
|
|
// See: https://docs.python.org/3/library/pickle.html#object.__setstate__
|
|
PySetState(state interface{}) error
|
|
}
|
|
|
|
// PyDictSettable is implemented by any value that can store dictionary-like
|
|
// key/value pairs. It reflects Python behavior of setting a key/value pair on
|
|
// an object's "__dict__" attribute.
|
|
type PyDictSettable interface {
|
|
// PyDictSet mimics the setting of a key/value pair on an object's
|
|
//"__dict__" attribute.
|
|
//
|
|
// See: https://docs.python.org/3/library/stdtypes.html#object.__dict__
|
|
PyDictSet(key, value interface{}) error
|
|
}
|
|
|
|
// PyAttrSettable is implemented by any value on which an existing or new
|
|
// Python-like attribute can be set. In Python this is done with "setattr"
|
|
// builtin function.
|
|
type PyAttrSettable interface {
|
|
// PySetAttr mimics the setting of an arbitrary value to an object's
|
|
// attribute.
|
|
//
|
|
// In Python this is done with "setattr" function, to which object,
|
|
// attribute name, and value are passed. For an easy and clear
|
|
// implementation, here instead we require this method to be implemented
|
|
// on the "object" itself.
|
|
//
|
|
// See: https://docs.python.org/3/library/functions.html#setattr
|
|
PySetAttr(key string, value interface{}) error
|
|
}
|
|
|
|
// ByteArray:
|
|
//===========
|
|
|
|
// ByteArray simulates Python bytearray.
|
|
type ByteArray []byte
|
|
|
|
func NewByteArray() *ByteArray {
|
|
arr := make(ByteArray, 0)
|
|
return &arr
|
|
}
|
|
|
|
func NewByteArrayFromSlice(slice []byte) *ByteArray {
|
|
arr := ByteArray(slice)
|
|
return &arr
|
|
}
|
|
|
|
func (a *ByteArray) Get(i int) byte {
|
|
return (*a)[i]
|
|
}
|
|
|
|
func (a *ByteArray) Len() int {
|
|
return len(*a)
|
|
}
|
|
|
|
// Dict:
|
|
//======
|
|
|
|
// DictSetter is implemented by any value that exhibits a dict-like behaviour,
|
|
// allowing arbitrary key/value pairs to be set.
|
|
type DictSetter interface {
|
|
Set(key, value interface{})
|
|
}
|
|
|
|
// Dict represents a Python "dict" (builtin type).
|
|
//
|
|
// It is implemented as a slice, instead of a map, because in Go not
|
|
// all types can be map's keys (e.g. slices).
|
|
type Dict []*DictEntry
|
|
|
|
type DictEntry struct {
|
|
Key interface{}
|
|
Value interface{}
|
|
}
|
|
|
|
// NewDict makes and returns a new empty Dict.
|
|
func NewDict() *Dict {
|
|
d := make(Dict, 0)
|
|
return &d
|
|
}
|
|
|
|
// Set sets into the Dict the given key/value pair.
|
|
func (d *Dict) Set(key, value interface{}) {
|
|
*d = append(*d, &DictEntry{
|
|
Key: key,
|
|
Value: value,
|
|
})
|
|
}
|
|
|
|
// Get returns the value associated with the given key (if any), and whether
|
|
// the key is present or not.
|
|
func (d *Dict) Get(key interface{}) (interface{}, bool) {
|
|
for _, entry := range *d {
|
|
if reflect.DeepEqual(entry.Key, key) {
|
|
return entry.Value, true
|
|
}
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
// MustGet returns the value associated with the given key, if if it exists,
|
|
// otherwise it panics.
|
|
func (d *Dict) MustGet(key interface{}) interface{} {
|
|
value, ok := d.Get(key)
|
|
if !ok {
|
|
panic(fmt.Errorf("key not found in Dict: %#v", key))
|
|
}
|
|
return value
|
|
}
|
|
|
|
// Len returns the length of the Dict, that is, the amount of key/value pairs
|
|
// contained by the Dict.
|
|
func (d *Dict) Len() int {
|
|
return len(*d)
|
|
}
|
|
|
|
var _ DictSetter = &Dict{}
|
|
|
|
// Tuple:
|
|
// ======
|
|
|
|
type Tuple []interface{}
|
|
|
|
func NewTupleFromSlice(slice []interface{}) *Tuple {
|
|
t := Tuple(slice)
|
|
return &t
|
|
}
|
|
|
|
func (t *Tuple) Get(i int) interface{} {
|
|
return (*t)[i]
|
|
}
|
|
|
|
func (t *Tuple) Len() int {
|
|
return len(*t)
|
|
}
|
|
|
|
// OrderedDict:
|
|
// ============
|
|
|
|
// OrderedDictClass represent Python "collections.OrderedDict" class.
|
|
//
|
|
// This class allows the indirect creation of OrderedDict objects.
|
|
type OrderedDictClass struct{}
|
|
|
|
var _ Callable = &OrderedDictClass{}
|
|
|
|
// Call returns a new empty OrderedDict. It is equivalent to Python
|
|
// constructor "collections.OrderedDict()".
|
|
//
|
|
// No arguments are supported.
|
|
func (*OrderedDictClass) Call(args ...interface{}) (interface{}, error) {
|
|
if len(args) != 0 {
|
|
return nil, fmt.Errorf(
|
|
"OrderedDictClass.Call args not supported: %#v", args)
|
|
}
|
|
return NewOrderedDict(), nil
|
|
}
|
|
|
|
// OrderedDict is a minimal and trivial implementation of an ordered map,
|
|
// which represent a Python "collections.OrderedDict" object.
|
|
//
|
|
// It is composed by a simple unordered Map, and a List to keep the order of
|
|
// the entries. The former is useful for direct key lookups, the latter for
|
|
// iteration.
|
|
type OrderedDict struct {
|
|
// Map associates a key of any type (interface{}) to OrderedDictEntry
|
|
// pointer values. These values are shared with List.
|
|
Map map[interface{}]*OrderedDictEntry
|
|
// List is an ordered list of OrderedDictEntry pointers, which are
|
|
// also shared with Map.
|
|
List *list.List
|
|
// PyDict represents Python "object.__dict__" dictionary of attributes.
|
|
PyDict map[string]interface{}
|
|
}
|
|
|
|
var _ DictSetter = &OrderedDict{}
|
|
var _ PyDictSettable = &OrderedDict{}
|
|
|
|
// OrderedDictEntry is a single key/value pair stored in an OrderedDict.
|
|
//
|
|
// A pointer to an OrderedDictEntry is always shared between OrderedDict's Map
|
|
// and List.
|
|
type OrderedDictEntry struct {
|
|
// Key of a single OrderedDict's entry.
|
|
Key interface{}
|
|
// Value of a single OrderedDict's entry.
|
|
Value interface{}
|
|
// ListElement is a pointer to the OrderedDict's List Element which
|
|
// contains this very OrderedDictEntry.
|
|
ListElement *list.Element
|
|
}
|
|
|
|
// NewOrderedDict makes and returns a new empty OrderedDict.
|
|
func NewOrderedDict() *OrderedDict {
|
|
return &OrderedDict{
|
|
Map: make(map[interface{}]*OrderedDictEntry),
|
|
List: list.New(),
|
|
PyDict: make(map[string]interface{}),
|
|
}
|
|
}
|
|
|
|
// Set sets into the OrderedDict the given key/value pair. If the key does not
|
|
// exist yet, the new pair is positioned at the end (back) of the OrderedDict.
|
|
// If the key already exists, the existing associated value is replaced with the
|
|
// new one, and the original position is maintained.
|
|
func (o *OrderedDict) Set(k, v interface{}) {
|
|
if entry, ok := o.Map[k]; ok {
|
|
entry.Value = v
|
|
return
|
|
}
|
|
|
|
entry := &OrderedDictEntry{
|
|
Key: k,
|
|
Value: v,
|
|
}
|
|
entry.ListElement = o.List.PushBack(entry)
|
|
o.Map[k] = entry
|
|
}
|
|
|
|
// Get returns the value associated with the given key (if any), and whether
|
|
// the key is present or not.
|
|
func (o *OrderedDict) Get(k interface{}) (interface{}, bool) {
|
|
entry, ok := o.Map[k]
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
return entry.Value, true
|
|
}
|
|
|
|
// MustGet returns the value associated with the given key, if if it exists,
|
|
// otherwise it panics.
|
|
func (o *OrderedDict) MustGet(key interface{}) interface{} {
|
|
value, ok := o.Get(key)
|
|
if !ok {
|
|
panic(fmt.Errorf("key not found in OrderedDict: %#v", key))
|
|
}
|
|
return value
|
|
}
|
|
|
|
// Len returns the length of the OrderedDict, that is, the amount of key/value
|
|
// pairs contained by the OrderedDict.
|
|
func (o *OrderedDict) Len() int {
|
|
return len(o.Map)
|
|
}
|
|
|
|
// PyDictSet mimics the setting of a key/value pair on Python "__dict__"
|
|
// attribute of the OrderedDict.
|
|
func (o *OrderedDict) PyDictSet(key, value interface{}) error {
|
|
sKey, keyOk := key.(string)
|
|
if !keyOk {
|
|
return fmt.Errorf(
|
|
"OrderedDict.PyDictSet() requires string key: %#v", key)
|
|
}
|
|
o.PyDict[sKey] = value
|
|
return nil
|
|
}
|
|
|
|
// List:
|
|
// =====
|
|
|
|
// ListAppender is implemented by any value that exhibits a list-like
|
|
// behaviour, allowing arbitrary values to be appended.
|
|
type ListAppender interface {
|
|
Append(v interface{})
|
|
}
|
|
|
|
// List represents a Python "list" (builtin type).
|
|
type List []interface{}
|
|
|
|
var _ ListAppender = &List{}
|
|
|
|
// NewList makes and returns a new empty List.
|
|
func NewList() *List {
|
|
l := make(List, 0)
|
|
return &l
|
|
}
|
|
|
|
// NewListFromSlice makes and returns a new List initialized with the elements
|
|
// of the given slice.
|
|
//
|
|
// The new List is a simple type cast of the input slice; the slice is _not_
|
|
// copied.
|
|
func NewListFromSlice(slice []interface{}) *List {
|
|
l := List(slice)
|
|
return &l
|
|
}
|
|
|
|
// Append appends one element to the end of the List.
|
|
func (l *List) Append(v interface{}) {
|
|
*l = append(*l, v)
|
|
}
|
|
|
|
// Get returns the element of the List at the given index.
|
|
//
|
|
// It panics if the index is out of range.
|
|
func (l *List) Get(i int) interface{} {
|
|
return (*l)[i]
|
|
}
|
|
|
|
// Len returns the length of the List.
|
|
func (l *List) Len() int {
|
|
return len(*l)
|
|
}
|
|
|
|
// Set:
|
|
// ====
|
|
|
|
// SetAdder is implemented by any value that exhibits a set-like behaviour,
|
|
// allowing arbitrary values to be added.
|
|
type SetAdder interface {
|
|
Add(v interface{})
|
|
}
|
|
|
|
// Set represents a Python "set" (builtin type).
|
|
//
|
|
// It is implemented in Go as a map with empty struct values; the actual set
|
|
// of generic "interface{}" items is thus represented by all the keys.
|
|
type Set map[interface{}]setEmptyStruct
|
|
|
|
var _ SetAdder = &Set{}
|
|
|
|
type setEmptyStruct struct{}
|
|
|
|
// NewSet makes and returns a new empty Set.
|
|
func NewSet() *Set {
|
|
s := make(Set)
|
|
return &s
|
|
}
|
|
|
|
// NewSetFromSlice makes and returns a new Set initialized with the elements
|
|
// of the given slice.
|
|
func NewSetFromSlice(slice []interface{}) *Set {
|
|
s := make(Set, len(slice))
|
|
for _, item := range slice {
|
|
s[item] = setEmptyStruct{}
|
|
}
|
|
return &s
|
|
}
|
|
|
|
// Len returns the length of the Set.
|
|
func (s *Set) Len() int {
|
|
return len(*s)
|
|
}
|
|
|
|
// Add adds one element to the Set.
|
|
func (s *Set) Add(v interface{}) {
|
|
(*s)[v] = setEmptyStruct{}
|
|
}
|
|
|
|
// Has returns whether the given value is present in the Set (true)
|
|
// or not (false).
|
|
func (s *Set) Has(v interface{}) bool {
|
|
_, ok := (*s)[v]
|
|
return ok
|
|
}
|
|
|
|
// FrozenSet:
|
|
//===========
|
|
|
|
// FrozenSet represents a Python "frozenset" (builtin type).
|
|
//
|
|
// It is implemented in Go as a map with empty struct values; the actual set
|
|
// of generic "interface{}" items is thus represented by all the keys.
|
|
type FrozenSet map[interface{}]frozenSetEmptyStruct
|
|
|
|
type frozenSetEmptyStruct struct{}
|
|
|
|
// NewFrozenSetFromSlice makes and returns a new FrozenSet initialized
|
|
// with the elements of the given slice.
|
|
func NewFrozenSetFromSlice(slice []interface{}) *FrozenSet {
|
|
f := make(FrozenSet, len(slice))
|
|
for _, item := range slice {
|
|
f[item] = frozenSetEmptyStruct{}
|
|
}
|
|
return &f
|
|
}
|
|
|
|
// Len returns the length of the FrozenSet.
|
|
func (f *FrozenSet) Len() int {
|
|
return len(*f)
|
|
}
|
|
|
|
// Has returns whether the given value is present in the FrozenSet (true)
|
|
// or not (false).
|
|
func (f *FrozenSet) Has(v interface{}) bool {
|
|
_, ok := (*f)[v]
|
|
return ok
|
|
}
|
|
|
|
// Object:
|
|
//========
|
|
|
|
type ObjectClass struct{}
|
|
|
|
var _ PyNewable = &ObjectClass{}
|
|
|
|
func (o *ObjectClass) PyNew(args ...interface{}) (interface{}, error) {
|
|
if len(args) == 0 {
|
|
return nil, fmt.Errorf("ObjectClass.PyNew called with no arguments")
|
|
}
|
|
switch class := args[0].(type) {
|
|
case PyNewable:
|
|
return class.PyNew()
|
|
default:
|
|
return nil, fmt.Errorf(
|
|
"ObjectClass.PyNew unprocessable args: %#v", args)
|
|
}
|
|
}
|
|
|
|
// Reconstructor:
|
|
//===============
|
|
|
|
type Reconstructor struct{}
|
|
|
|
var _ Callable = &Reconstructor{}
|
|
|
|
func (r *Reconstructor) Call(args ...interface{}) (interface{}, error) {
|
|
if len(args) < 2 {
|
|
return nil, fmt.Errorf("Reconstructor: invalid arguments: %#v", args)
|
|
}
|
|
class := args[0]
|
|
switch base := args[1].(type) {
|
|
case PyNewable:
|
|
return base.PyNew(class)
|
|
default:
|
|
return nil, fmt.Errorf(
|
|
"Reconstructor: unprocessable arguments: %#v", args)
|
|
}
|
|
}
|
|
|
|
// GenericClass:
|
|
//==============
|
|
|
|
type GenericClass struct {
|
|
Module string
|
|
Name string
|
|
}
|
|
|
|
var _ PyNewable = &GenericClass{}
|
|
|
|
type GenericObject struct {
|
|
Class *GenericClass
|
|
ConstructorArgs []interface{}
|
|
}
|
|
|
|
func NewGenericClass(module, name string) *GenericClass {
|
|
return &GenericClass{Module: module, Name: name}
|
|
}
|
|
|
|
func (g *GenericClass) PyNew(args ...interface{}) (interface{}, error) {
|
|
return &GenericObject{
|
|
Class: g,
|
|
ConstructorArgs: args,
|
|
}, nil
|
|
}
|
|
|
|
// getThnnFunctionBackend is for historical pickle deserilaization, it is not used otherwise
|
|
type getThnnFunctionBackend struct{}
|
|
|
|
var _ Callable = &getThnnFunctionBackend{}
|
|
|
|
func (getThnnFunctionBackend) Call(_ ...interface{}) (interface{}, error) {
|
|
return nil, nil
|
|
}
|