Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
W
wendelin.core
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
Kirill Smelkov
wendelin.core
Commits
6fbf83f5
Commit
6fbf83f5
authored
Jul 19, 2018
by
Kirill Smelkov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
.
parent
7bb6a8a8
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
232 additions
and
213 deletions
+232
-213
wcfs/zodb.go
wcfs/zodb.go
+14
-213
wcfs/zodbpy.go
wcfs/zodbpy.go
+218
-0
No files found.
wcfs/zodb.go
View file @
6fbf83f5
...
...
@@ -87,19 +87,6 @@ type Object interface {
PInvalidate
()
}
// PyObject is the interface that every in-RAM object representing Python ZODB object implements.
type
PyObject
interface
{
Object
PyClass
()
pickle
.
Class
// python class of this object
// PyState() interface{} // object state. python passes this to pyclass.__new__().__setstate__()
// XXX want to hide from PyObject. Rationale: we do not want e.g. PySetState to
// be available to user who holds PyObject interface: it is confusing to have
// both PActivate and PySetState at the same time.
// PyStateful
}
// ObjectState describe state of in-RAM object.
type
ObjectState
int
...
...
@@ -127,15 +114,6 @@ func (obj *object) PJar() *Connection { return obj.jar }
func
(
obj
*
object
)
POid
()
zodb
.
Oid
{
return
obj
.
oid
}
func
(
obj
*
object
)
PSerial
()
zodb
.
Tid
{
return
obj
.
serial
}
// pyObject is common base implementation for in-RAM representation of ZODB Python objects.
type
pyObject
struct
{
object
pyclass
pickle
.
Class
}
func
(
pyobj
*
pyObject
)
PyClass
()
pickle
.
Class
{
return
pyobj
.
pyclass
}
//func (pyobj *pyObject) PyState() interface{} { return pyobj.pystate }
// loadState indicates object's load state/result.
//
// when !ready the loading is in progress.
...
...
@@ -164,20 +142,6 @@ type Stateful interface {
//GetState() *mem.Buf
}
// PyStateful is the interface describing in-RAM object whose data state can be
// exchanged as Python data.
type
PyStateful
interface
{
//Stateful XXX no need here?
// PySetState should set state of the in-RAM object from Python data.
// Analog of __setstate__() in Python.
PySetState
(
pystate
interface
{})
error
// PyGetState should return state of the in-RAM object as Python data.
// Analog of __getstate__() in Python.
//PyGetState() interface{} XXX
}
// Connection represents a view of ZODB database.
//
...
...
@@ -241,7 +205,7 @@ type Connection struct {
// NOTE2 finalizers don't run on when they are attached to an object in cycle.
// Hopefully we don't have cycles with ZBtree/ZBucket XXX verify this
objmu
sync
.
Mutex
objtab
map
[
zodb
.
Oid
]
*
WeakRef
// oid -> WeakRef(
Py
Object)
objtab
map
[
zodb
.
Oid
]
*
WeakRef
// oid -> WeakRef(Object)
// hooks for application to influence live caching decisions.
...
...
@@ -257,182 +221,6 @@ type LiveCacheControl interface {
}
// ----------------------------------------
// Get returns in-RAM object corresponding to specified ZODB object according to current database view.
//
// If there is already in-RAM object that corresponds to oid, that in-RAM object is returned.
// Otherwise new in-RAM object is created and filled with object's class loaded from the database.
//
// The scope of the object returned is the Connection. XXX ok?
//
// The object's data is not neccessarily loaded after Get returns. Use
// PActivate to make sure the object ifs fully loaded.
func
(
conn
*
Connection
)
Get
(
ctx
context
.
Context
,
oid
zodb
.
Oid
)
(
PyObject
,
error
)
{
conn
.
objmu
.
Lock
()
// XXX -> rlock
wobj
:=
conn
.
objtab
[
oid
]
var
xobj
interface
{}
if
wobj
!=
nil
{
xobj
=
wobj
.
Get
()
}
conn
.
objmu
.
Unlock
()
// object was already there in objtab.
if
xobj
!=
nil
{
return
xobj
.
(
PyObject
),
nil
}
// object is not there in objtab - raw load it, get its class -> get(pyclass, oid)
pyclass
,
pystate
,
serial
,
err
:=
conn
.
loadpy
(
ctx
,
oid
)
if
err
!=
nil
{
return
nil
,
err
// XXX errctx
}
obj
,
err
:=
conn
.
get
(
pyclass
,
oid
)
if
err
!=
nil
{
return
nil
,
err
}
// XXX we are dropping just loaded pystate. Usually Get should be used
// to only load root object, so maybe that is ok.
//
// TODO -> use (pystate, serial) to activate.
_
,
_
=
pystate
,
serial
return
obj
,
nil
}
// wrongClassError is the error cause returned when object's class is not what was expected.
type
wrongClassError
struct
{
want
,
have
pickle
.
Class
}
func
(
e
*
wrongClassError
)
Error
()
string
{
return
fmt
.
Sprintf
(
"wrong class: want %q; have %q"
,
e
.
want
,
e
.
have
)
}
// get returns in-RAM object corresponding to specified ZODB object according to current database view.
//
// If there is already in-RAM object that corresponds to oid, that in-RAM object is returned.
// Otherwise new in-RAM object is created according to specified class.
//
// The object's data is not neccessarily loaded after get returns. Use
// PActivate to make sure the object is fully loaded.
//
// XXX object scope.
//
// Use-case: in ZODB references are (pyclass, oid), so new ghost is created
// without further loading anything.
func
(
conn
*
Connection
)
get
(
pyclass
pickle
.
Class
,
oid
zodb
.
Oid
)
(
PyObject
,
error
)
{
conn
.
objmu
.
Lock
()
// XXX -> rlock
wobj
:=
conn
.
objtab
[
oid
]
var
pyobj
PyObject
checkClass
:=
false
if
wobj
!=
nil
{
if
xobj
:=
wobj
.
Get
();
xobj
!=
nil
{
pyobj
=
xobj
.
(
PyObject
)
}
}
if
pyobj
==
nil
{
pyobj
=
conn
.
newGhost
(
pyclass
,
oid
)
conn
.
objtab
[
oid
]
=
NewWeakRef
(
pyobj
)
}
else
{
checkClass
=
true
}
conn
.
objmu
.
Unlock
()
if
checkClass
{
if
cls
:=
pyobj
.
PyClass
();
pyclass
!=
cls
{
return
nil
,
&
zodb
.
OpError
{
URL
:
conn
.
stor
.
URL
(),
Op
:
fmt
.
Sprintf
(
"@%s: get"
,
conn
.
at
),
// XXX abuse
Args
:
oid
,
Err
:
&
wrongClassError
{
pyclass
,
cls
},
}
}
}
return
pyobj
,
nil
}
// load loads object specified by oid.
//
// XXX must be called ... (XXX e.g. outside transaction boundary) so that there is no race on .at .
func
(
conn
*
Connection
)
load
(
ctx
context
.
Context
,
oid
zodb
.
Oid
)
(
_
*
mem
.
Buf
,
serial
zodb
.
Tid
,
_
error
)
{
return
conn
.
stor
.
Load
(
ctx
,
zodb
.
Xid
{
Oid
:
oid
,
At
:
conn
.
at
})
}
// loadpy loads object specified by oid and decodes it as a ZODB Python object.
//
// loadpy does not create any in-RAM object associated with Connection.
// It only returns decoded database data.
func
(
conn
*
Connection
)
loadpy
(
ctx
context
.
Context
,
oid
zodb
.
Oid
)
(
pyclass
pickle
.
Class
,
pystate
interface
{},
serial
zodb
.
Tid
,
_
error
)
{
buf
,
serial
,
err
:=
conn
.
stor
.
Load
(
ctx
,
zodb
.
Xid
{
Oid
:
oid
,
At
:
conn
.
at
})
if
err
!=
nil
{
return
pickle
.
Class
{},
nil
,
0
,
err
}
defer
buf
.
Release
()
pyclass
,
pystate
,
err
=
zodb
.
PyData
(
buf
.
Data
)
.
Decode
()
if
err
!=
nil
{
return
pickle
.
Class
{},
nil
,
0
,
err
// XXX err ctx
}
return
pyclass
,
pystate
,
serial
,
nil
}
// ---- pyclass -> new ghost ----
// path(pyclass) -> new(pyobj)
var
classTab
=
make
(
map
[
string
]
func
(
base
*
pyObject
)
PyObject
)
// registerPyClass registers python class to be transformed to Go instance
// created via classNew.
//
// must be called from global init().
func
registerPyClass
(
pyClassPath
string
,
classNew
func
(
base
*
pyObject
)
PyObject
)
{
classTab
[
pyClassPath
]
=
classNew
}
// newGhost creates new ghost object corresponding to pyclass and oid.
func
(
conn
*
Connection
)
newGhost
(
pyclass
pickle
.
Class
,
oid
zodb
.
Oid
)
PyObject
{
pyobj
:=
&
pyObject
{
object
:
object
{
jar
:
conn
,
oid
:
oid
,
serial
:
0
,
state
:
GHOST
},
pyclass
:
pyclass
,
}
// switch on pyclass and transform e.g. "zodb.BTree.Bucket" -> *ZBucket
classNew
:=
classTab
[
pyclass
.
Module
+
"."
+
pyclass
.
Name
]
var
instance
PyObject
if
classNew
!=
nil
{
instance
=
classNew
(
pyobj
)
}
else
{
instance
=
&
dummyPyInstance
{
pyObject
:
pyobj
}
}
pyobj
.
instance
=
instance
return
instance
}
// dummyPyInstance is used for python classes that were not registered.
type
dummyPyInstance
struct
{
*
pyObject
pystate
interface
{}
}
func
(
d
*
dummyPyInstance
)
DropState
()
{
d
.
pystate
=
nil
}
func
(
d
*
dummyPyInstance
)
PySetState
(
pystate
interface
{})
error
{
d
.
pystate
=
pystate
return
nil
}
// ---- activate/deactivate/invalidate ----
// PActivate implements Object.
...
...
@@ -547,3 +335,16 @@ func (obj *object) PInvalidate() {
obj
.
state
=
GHOST
obj
.
loading
=
nil
}
// ----------------------------------------
// XXX Connection.{Get,get} without py dependency?
// but then how to create a ghost of correct class? -> reflect.Type?
// load loads object specified by oid.
//
// XXX must be called ... (XXX e.g. outside transaction boundary) so that there is no race on .at .
func
(
conn
*
Connection
)
load
(
ctx
context
.
Context
,
oid
zodb
.
Oid
)
(
_
*
mem
.
Buf
,
serial
zodb
.
Tid
,
_
error
)
{
return
conn
.
stor
.
Load
(
ctx
,
zodb
.
Xid
{
Oid
:
oid
,
At
:
conn
.
at
})
}
wcfs/zodbpy.go
0 → 100644
View file @
6fbf83f5
// Copyright (c) 2001, 2002 Zope Foundation and Contributors.
// All Rights Reserved.
//
// Copyright (C) 2018 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This software is subject to the provisions of the Zope Public License,
// Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
// THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
// WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
// FOR A PARTICULAR PURPOSE
package
main
// Bits that should be in ZODB XXX -> zodb
// PyObject is the interface that every in-RAM object representing Python ZODB object implements.
type
PyObject
interface
{
Object
PyClass
()
pickle
.
Class
// python class of this object
// PyState() interface{} // object state. python passes this to pyclass.__new__().__setstate__()
// XXX want to hide from PyObject. Rationale: we do not want e.g. PySetState to
// be available to user who holds PyObject interface: it is confusing to have
// both PActivate and PySetState at the same time.
// PyStateful
}
// pyObject is common base implementation for in-RAM representation of ZODB Python objects.
type
pyObject
struct
{
object
pyclass
pickle
.
Class
}
func
(
pyobj
*
pyObject
)
PyClass
()
pickle
.
Class
{
return
pyobj
.
pyclass
}
//func (pyobj *pyObject) PyState() interface{} { return pyobj.pystate }
// PyStateful is the interface describing in-RAM object whose data state can be
// exchanged as Python data.
type
PyStateful
interface
{
//Stateful XXX no need here?
// PySetState should set state of the in-RAM object from Python data.
// Analog of __setstate__() in Python.
PySetState
(
pystate
interface
{})
error
// PyGetState should return state of the in-RAM object as Python data.
// Analog of __getstate__() in Python.
//PyGetState() interface{} XXX
}
// ---- pyclass -> new ghost ----
// path(pyclass) -> new(pyobj)
var
classTab
=
make
(
map
[
string
]
func
(
base
*
pyObject
)
PyObject
)
// registerPyClass registers python class to be transformed to Go instance
// created via classNew.
//
// must be called from global init().
func
registerPyClass
(
pyClassPath
string
,
classNew
func
(
base
*
pyObject
)
PyObject
)
{
classTab
[
pyClassPath
]
=
classNew
}
// newGhost creates new ghost object corresponding to pyclass and oid.
func
(
conn
*
Connection
)
newGhost
(
pyclass
pickle
.
Class
,
oid
zodb
.
Oid
)
PyObject
{
pyobj
:=
&
pyObject
{
object
:
object
{
jar
:
conn
,
oid
:
oid
,
serial
:
0
,
state
:
GHOST
},
pyclass
:
pyclass
,
}
// switch on pyclass and transform e.g. "zodb.BTree.Bucket" -> *ZBucket
classNew
:=
classTab
[
pyclass
.
Module
+
"."
+
pyclass
.
Name
]
var
instance
PyObject
if
classNew
!=
nil
{
instance
=
classNew
(
pyobj
)
}
else
{
instance
=
&
dummyPyInstance
{
pyObject
:
pyobj
}
}
pyobj
.
instance
=
instance
return
instance
}
// dummyPyInstance is used for python classes that were not registered.
type
dummyPyInstance
struct
{
*
pyObject
pystate
interface
{}
}
func
(
d
*
dummyPyInstance
)
DropState
()
{
d
.
pystate
=
nil
}
func
(
d
*
dummyPyInstance
)
PySetState
(
pystate
interface
{})
error
{
d
.
pystate
=
pystate
return
nil
}
// ----------------------------------------
// Get returns in-RAM object corresponding to specified ZODB object according to current database view.
//
// If there is already in-RAM object that corresponds to oid, that in-RAM object is returned.
// Otherwise new in-RAM object is created and filled with object's class loaded from the database.
//
// The scope of the object returned is the Connection. XXX ok?
//
// The object's data is not neccessarily loaded after Get returns. Use
// PActivate to make sure the object ifs fully loaded.
func
(
conn
*
Connection
)
Get
(
ctx
context
.
Context
,
oid
zodb
.
Oid
)
(
PyObject
,
error
)
{
conn
.
objmu
.
Lock
()
// XXX -> rlock
wobj
:=
conn
.
objtab
[
oid
]
var
xobj
interface
{}
if
wobj
!=
nil
{
xobj
=
wobj
.
Get
()
}
conn
.
objmu
.
Unlock
()
// object was already there in objtab.
if
xobj
!=
nil
{
return
xobj
.
(
PyObject
),
nil
}
// object is not there in objtab - raw load it, get its class -> get(pyclass, oid)
pyclass
,
pystate
,
serial
,
err
:=
conn
.
loadpy
(
ctx
,
oid
)
if
err
!=
nil
{
return
nil
,
err
// XXX errctx
}
obj
,
err
:=
conn
.
get
(
pyclass
,
oid
)
if
err
!=
nil
{
return
nil
,
err
}
// XXX we are dropping just loaded pystate. Usually Get should be used
// to only load root object, so maybe that is ok.
//
// TODO -> use (pystate, serial) to activate.
_
,
_
=
pystate
,
serial
return
obj
,
nil
}
// wrongClassError is the error cause returned when python object's class is not what was expected.
type
wrongClassError
struct
{
want
,
have
pickle
.
Class
}
func
(
e
*
wrongClassError
)
Error
()
string
{
return
fmt
.
Sprintf
(
"wrong class: want %q; have %q"
,
e
.
want
,
e
.
have
)
}
// get returns in-RAM object corresponding to specified ZODB object according to current database view.
//
// If there is already in-RAM object that corresponds to oid, that in-RAM object is returned.
// Otherwise new in-RAM object is created according to specified class.
//
// The object's data is not neccessarily loaded after get returns. Use
// PActivate to make sure the object is fully loaded.
//
// XXX object scope.
//
// Use-case: in ZODB references are (pyclass, oid), so new ghost is created
// without further loading anything.
func
(
conn
*
Connection
)
get
(
pyclass
pickle
.
Class
,
oid
zodb
.
Oid
)
(
PyObject
,
error
)
{
conn
.
objmu
.
Lock
()
// XXX -> rlock
wobj
:=
conn
.
objtab
[
oid
]
var
pyobj
PyObject
checkClass
:=
false
if
wobj
!=
nil
{
if
xobj
:=
wobj
.
Get
();
xobj
!=
nil
{
pyobj
=
xobj
.
(
PyObject
)
}
}
if
pyobj
==
nil
{
pyobj
=
conn
.
newGhost
(
pyclass
,
oid
)
conn
.
objtab
[
oid
]
=
NewWeakRef
(
pyobj
)
}
else
{
checkClass
=
true
}
conn
.
objmu
.
Unlock
()
if
checkClass
{
if
cls
:=
pyobj
.
PyClass
();
pyclass
!=
cls
{
return
nil
,
&
zodb
.
OpError
{
URL
:
conn
.
stor
.
URL
(),
Op
:
fmt
.
Sprintf
(
"@%s: get"
,
conn
.
at
),
// XXX abuse
Args
:
oid
,
Err
:
&
wrongClassError
{
pyclass
,
cls
},
}
}
}
return
pyobj
,
nil
}
// loadpy loads object specified by oid and decodes it as a ZODB Python object.
//
// loadpy does not create any in-RAM object associated with Connection.
// It only returns decoded database data.
func
(
conn
*
Connection
)
loadpy
(
ctx
context
.
Context
,
oid
zodb
.
Oid
)
(
pyclass
pickle
.
Class
,
pystate
interface
{},
serial
zodb
.
Tid
,
_
error
)
{
buf
,
serial
,
err
:=
conn
.
stor
.
Load
(
ctx
,
zodb
.
Xid
{
Oid
:
oid
,
At
:
conn
.
at
})
if
err
!=
nil
{
return
pickle
.
Class
{},
nil
,
0
,
err
}
defer
buf
.
Release
()
pyclass
,
pystate
,
err
=
zodb
.
PyData
(
buf
.
Data
)
.
Decode
()
if
err
!=
nil
{
return
pickle
.
Class
{},
nil
,
0
,
err
// XXX err ctx
}
return
pyclass
,
pystate
,
serial
,
nil
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment