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
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Joshua
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