Commit 26f53db1 authored by Kirill Smelkov's avatar Kirill Smelkov

.

parent 9ce137f2
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
from ZODB.tests import testSerialize from ZODB.tests import testSerialize
from ZODB import serialize from ZODB import serialize
from zodbtools.util import escapeqq from zodbtools.util import escapeqq as qq
def main(): def main():
# dump to go what to expect # dump to go what to expect
...@@ -46,8 +46,8 @@ def main(): ...@@ -46,8 +46,8 @@ def main():
emit("\nvar _PyData_ClassName_Testv = [...]_PyDataClassName_TestEntry{") emit("\nvar _PyData_ClassName_Testv = [...]_PyDataClassName_TestEntry{")
for test in testv: for test in testv:
emit("\t{") emit("\t{")
emit("\t\t%s," % escapeqq(test)) emit("\t\t%s," % qq(test))
emit("\t\t%s," % escapeqq(r.getClassName(test))) emit("\t\t%s," % qq(r.getClassName(test)))
emit("\t},") emit("\t},")
emit('\t{"aaa", "?.?"},') # invalid emit('\t{"aaa", "?.?"},') # invalid
emit("}") emit("}")
......
// Copyright (C) 2016-2017 Nexedi SA and Contributors. // Copyright (C) 2016-2018 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com> // Kirill Smelkov <kirr@nexedi.com>
// //
// This program is free software: you can Use, Study, Modify and Redistribute // This program is free software: you can Use, Study, Modify and Redistribute
...@@ -22,21 +22,55 @@ package zodb ...@@ -22,21 +22,55 @@ package zodb
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
pickle "github.com/kisielk/og-rek" pickle "github.com/kisielk/og-rek"
) )
// PyData represents data stored into ZODB by Python applications. // PyData represents raw data stored into ZODB by Python applications.
// //
// The format is based on python pickles. Basically every serialized object has // The format is based on python pickles. Basically every serialized object has
// two parts: class description and object state. See // two parts: pickle with class description and pickle with object state. See
// //
// https://github.com/zopefoundation/ZODB/blob/a89485c1/src/ZODB/serialize.py // https://github.com/zopefoundation/ZODB/blob/a89485c1/src/ZODB/serialize.py
// //
// for format description. // for format description.
//
// PyData can be decoded into PyObject.
type PyData []byte type PyData []byte
// PyObject represents persistent Python object.
//
// PyObject can be decoded from PyData.
type PyObject struct {
PyClass pickle.Class // python class of this object
State interface{} // object state. python passes this to pyclass.__new__().__setstate__()
}
// Decode decodes raw ZODB python data into PyObject.
func (d PyData) Decode() (*PyObject, error) {
p := pickle.NewDecoder(bytes.NewReader([]byte(d)))
xklass, err := p.Decode()
if err != nil {
return nil, fmt.Errorf("pydata: decode: class description: %s", err)
}
klass, err := normPyClass(xklass)
if err != nil {
return nil, fmt.Errorf("pydata: decode: class description: %s", err)
}
state, err := p.Decode()
if err != nil {
return nil, fmt.Errorf("pydata: decode: object state: %s", err)
}
return &PyObject{PyClass: klass, State: state}, nil
}
// ClassName returns fully-qualified python class name used for object type. // ClassName returns fully-qualified python class name used for object type.
// //
// The format is "module.class". // The format is "module.class".
...@@ -49,23 +83,49 @@ func (d PyData) ClassName() string { ...@@ -49,23 +83,49 @@ func (d PyData) ClassName() string {
return "?.?" return "?.?"
} }
klass, err := normPyClass(xklass)
if err != nil {
return "?.?"
}
return klass.Module + "." + klass.Name
}
var errInvalidPyClass = errors.New("invalid py class description")
// normPyClass normalizes py class that has just been decoded from a serialized
// ZODB object or reference.
func normPyClass(xklass interface{}) (pickle.Class, error) {
// class description:
//
// - type(obj), or
// - (xklass, newargs|None) ; xklass = type(obj) | (modname, classname)
if t, ok := xklass.(pickle.Tuple); ok { if t, ok := xklass.(pickle.Tuple); ok {
if len(t) != 2 { // (klass, args) // t = (xklass, newargs|None)
return "?.?" if len(t) != 2 {
return pickle.Class{}, errInvalidPyClass
} }
xklass = t[0] xklass = t[0]
if t, ok := xklass.(pickle.Tuple); ok { if t, ok := xklass.(pickle.Tuple); ok {
// py: "old style reference" // t = (modname, classname)
if len(t) != 2 { if len(t) != 2 {
return "?.?" // (modname, classname) return pickle.Class{}, errInvalidPyClass
}
modname, ok1 := t[0].(string)
classname, ok2 := t[1].(string)
if !(ok1 && ok2) {
return pickle.Class{}, errInvalidPyClass
} }
return fmt.Sprintf("%s.%s", t...)
return pickle.Class{Module: modname, Name: classname}, nil
} }
} }
if klass, ok := xklass.(pickle.Class); ok { if klass, ok := xklass.(pickle.Class); ok {
return klass.Module + "." + klass.Name // klass = type(obj)
return klass, nil
} }
return "?.?" return pickle.Class{}, errInvalidPyClass
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment