ZODB使
ZODB
ZODB (Zope Object Database) Python

SQL
ORM)

pickle /


ZODB


ZODB

使

pickle
ZEO ZODB Python

ZODB使

$ pip install ZODB

ZODB使

In [2]: # %load 01_connection.py
...: import ZODB, ZODB.FileStorage
...:
...: storage = ZODB.FileStorage.FileStorage('mydata.fs')
...: db = ZODB.DB(storage)
...: connection = db.open()
...: root = connection.root
...:
In [3]: !ls mydata*
mydata.fs mydata.fs.index mydata.fs.lock mydata.fs.tmp

ZODB使 root

ZODB
FileStorage
1Data.fs
MappingStorage
DemoStorage
DirectoryStorage
1Data.fs.index
RelStorage
PostgreSQLMySQLOracle

ZODB

mydatabse.fs 使 db

open() root()

ZODB FileStorage

In [2]: # %load 02_connection_short.py
...: import ZODB
...:
...: db = ZODB.DB('mydata.fs')
...: connection = db.open()
...: root = connection.root
...:

In [2]: # %load 03_inmemory.py
...: import ZODB
...:
...: db = ZODB.DB(None)
...: connection = db.open()
...: root = connection.root
...:

connection()
In [2]: # %load 04_single_db.py
...: import ZODB
...:
...: connection = ZODB.connection('mydata.fs')
...: memory_connection = ZODB.connection(None)
...:

import ZODB
connection = ZODB.connection('mydata.fs')
root = connection.root


In [2]: # %load 05_store_objectt.py
...: from zodb_mydata import *
...:
...: root.member = ['Freddie', 'Brian', 'John', 'Roger']
...:

使
In [3]: root['member'] = ['Freddie', 'Brian', 'John', 'Roger']
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-3-ff584a2f0242> in <module>
----> 1 root['member'] = ['Freddie', 'Brian', 'John', 'Roger']
TypeError: 'RootConvenience' object does not support item assignment

ZODB
1
commit() close()

In [2]: # %load 06_commit.py
...: from zodb_mydata import *
...: import transaction
...:
...: root.member = ['Freddie', 'Brian', 'John', 'Roger']
...:
...: transaction.commit()
...: connection.close()
...:

Python
In [2]: # %load 07_check_data.py
...: from zodb_mydata import *
...:
...: root.member
...:
Out[2]: ['Freddie', 'Brian', 'John', 'Roger']

commit() abort() 使
In [2]: # %load 08_abort.py
...: from zodb_mydata import *
...: import transaction
...:
...: root.member = ['Adam', 'Brian', 'John', 'Roger']
...:
...: transaction.abort()
...: connection.close()
...:
\

In [2]: # %load 07_check_data.py
...: from zodb_mydata import *
...:
...: root.member
...:
Out[2]: ['Freddie', 'Brian', 'John', 'Roger']

使PythonSQLSQLZODB

ZODB

user_data = [
{ 'name': 'John',
'birthday': {'year': 1951, 'month': 8, 'day': 19},
'country-code': 'GB'
},
{ 'name': 'Freddie',
'birthday': {'year': 1946, 'month': 9, 'day': 5},
'country-code': 'GB'
},
{ 'name': 'Brian',
'birthday': {'year': 1947, 'month': 7, 'day': 19},
'country-code': 'GB'
},
{ 'name': 'Roger',
'birthday': {'year': 1949, 'month': 7, 'day': 26},
'country-code': 'GB'
},
]

ZODB
In [2]: # %load 10.stora_object.py
...: from zodb_mydata import *
...: from user_data import *
...:
...: root.devops = user_data
...:

BTree 使
In [2]: # %load 11.stora_object_btree.py
...: from zodb_mydata import *
...: from BTrees.OOBTree import BTree
...: from user_data import *
...:
...: root.users = BTree()
...: root.users['devops'] = user_data
...:

root.app_root = user_app()
root.task_root = task_app()

BTree ZODBBTreeOOBTree(Object Oriented Binary Tree )BTree使
BTree使

ZODBZODB(traverse)(:

BTree使ZODB
ZODBNewt DBZODBPostgreSQLPython

ZODB使

Python
1

data root.member
data Adam
v1 v2
ZODB data

In [1]: %load 20_dectect_change.py
...: import transaction
...:
...:
...: root.member = ['Freddie', 'Brian', 'John', 'Roger']
...: transaction.commit()
...: connection.close()
...:
...:
...: connection = ZODB.connection('mydata.fs')
...: root = connection.root
...: v1 = root.member
...: root.member[0] = 'Adam'
...: transaction.commit()
...: v2 = root.member
...: connection.close()
...:
...: connection = ZODB.connection('mydata.fs')
...: root = connection.root
...: v3 = root.member
...: connection.close()
...:
...: # print(v1)
...: # print(v2)
...: # print(v3)
...:
In [3]: print(v1)
['Adam', 'Brian', 'John', 'Roger']
In [4]: print(v2)
['Adam', 'Brian', 'John', 'Roger']
In [5]: print(v3)
['Freddie', 'Brian', 'John', 'Roger']

In [2]: # %load 21_reassigning.py
...: from zodb_mydata import *
...: import transaction
...:
...: member = root.member
...: member[0] = 'Adam'
...: root.member = member
...: v1 = member
...: transaction.commit()
...: connection.close()
...:
...: connection = ZODB.connection('mydata.fs')
...: root = connection.root
...: v2 = root.member
...: connection.close()
...:
...: # print(v1)
...: # print(v2)
...:
In [3]: print(v1)
['Adam', 'Brian', 'John', 'Roger']
In [4]: print(v2)
['Adam', 'Brian', 'John', 'Roger']

Persistent
ZODBPersistentPersistent使
PersistentPersistent.PersistentZODBPersistentZODBPersistentZODB

Persistent Member
import ZODB
from persistent import Persistent
class Member(Persistent):
def setName(self, name):
self.name = name
def getName(self)
return self.name

In [2]: # %load 22_persistent.py
...: from zodb_mydata import *
...: from zodb_members import Member
...: import transaction
...:
...: members = []
...:
...: for name in ['Freddie', 'Brian', 'John', 'Roger']:
...: member = Member()
...: member.setName(name)
...: members.append(member)
...:
...: root.members=members
...: transaction.commit()
...:
...: vocal = root.members[0]
...: vocal.setName('Adam')
...: transaction.commit()
...: connection.close()
...:

Persistent MemberZODB
commit()

In [2]: # %load 23_persistent_check.py
...: from zodb_mydata import *
...:
...: v1 = root.members
...: v2 = root.members[0].getName()
...:
...: connection.close()
...:
...: # print(v1)
...: # print(v2)
...:
...:
In [3]: print(v1)
[<zodb_members.Member object at 0x10e2923c0 oid 0xd in <Connection at 10e293400>>, <zodb_members.Member object at 0x10e292430 oid 0xe in <Connection at 10e293400>>, <zodb_members.Member object at 0x10e2924a0 oid 0xf in <Connection at 10e293400>>, <zodb_members.Member object at 0x10e292510 oid 0x10 in <Connection at 10e293400>>]
In [4]: print(v2)
Adam

ZODBPython(mutable object)使Persistent
zodb_members.py

import ZODB
from persistent import Persistent
class Member(Persistent):
def __init__(self):
self.members = []
def setName(self, name):
self.name = name
def getName(self):
return self.name
def add_member(self, name):
self.members.append(name)
self._p_changed = 1

add_member() ZODBmutable self.members self.members Persistent使 _p_changed 使ZODB

_p_changed 1ZODB

_p_changed P:ersistent

BTree
使 BTressZODB
ZODB使ZODBPythonZODBPython10使BTree

BTrees使2 O I 32 F 32BTrees.IOBTree

OOBTree
OBTree
OIBTree
IIBTree
IFBTree



BTree
Bucket
TreeSet
Set

BTreeBucket update() keys() TreeSetSet keys() items() BTreeTreeSets使BTreeTreeSet使PythonBucketSet1PythonBucketSet1/BTreeTreeSetsZODB

BTree TreeSet keys() values() items() BTree

key() values() items() iterkeys() itervalues() iteritems() BTreeTreeSet

BTree minKey() maxKey() bound 使 byValue() (使) key "" pool

In [2]: # %load 30_btrees.py
...: from BTrees.OOBTree import OOBTree
...:
...: t = OOBTree()
...: t.update({1: "heart", 2: "diamond", 3: "spade", 4: "club"})
...: s = t.keys()
...:
...: v1 = len(t)
...: v2 = t[2]
...: v3 = len(s)
...: v4 = s[-2]
...: v5 = list(s)
...: v6 = list(t.values())
...: v7 = list(t.values(1, 2))
...: v8 = list(t.values(2))
...:
...: # print(v1)
...: # print(v2)
...: # print(v3)
...: # print(v4)
...: # print(v5)
...: # print(v6)
...: # print(v7)
...: # print(v8)
...:
In [3]: print(v1)
4
In [4]: print(v2)
diamond
In [5]: print(v3)
4
In [6]: print(v4)
3
In [7]: print(v5)
[1, 2, 3, 4]
In [8]: print(v6)
['heart', 'diamond', 'spade', 'club']
In [9]: print(v7)
['heart', 'diamond']
In [10]: print(v8)
['diamond', 'spade', 'club']


In [2]: # %load 31_btrees_methods.py
...: from BTrees.OOBTree import OOBTree
...:
...: t = OOBTree()
...: t.update({1: "red", 2: "green", 3: "blue", 4: "spades"})
...:
...: v1 = list(t.values(min=1, max=4))
...: v2 = list(t.values(min=1, max=4, excludemin=True, excludemax=True))
...: v3 = t.minKey()
...: v4 = t.minKey(1.5)
...: v5 = t.has_key(4)
...: v6 = t.has_key(5)
...: v7 = 4 in t
...: v8 = 5 in t
...:
...: # print(v1)
...: # print(v2)
...: # print(v3)
...: # print(v4)
...: # print(v5, v6)
...: # print(v7, v8)
...:
In [3]: print(v1)
['red', 'green', 'blue', 'spades']
In [4]: print(v2)
['green', 'blue']
In [5]: print(v3)
1
In [6]: print(v4)
2
In [7]: print(v5, v6)
True False
In [8]: print(v7, v8)
True False

In [1]: %load 32_btrees_iteration.py
...: from BTrees.OOBTree import OOBTree
...:
...: t = OOBTree()
...: t.update({1: "red", 2: "green", 3: "blue", 4: "spades"})
...:
...: v1 = t.keys()
...: v2 = t
...: v3 = t.iteritems()
...:
...: def func1():
...: for k in t.keys():
...: print(f'{k} ', end='')
...:
...: def func2():
...: for k in t:
...: print(f'{k} ', end='')
...:
...: def func3():
...: for k in t.iteritems():
...: print(f'{k} ', end='')
...:
...: # print(v1)
...: # print(v2)
...: # print(v3)
...: # func1()
...: # func2()
...: # func3()
...:
In [3]: print(v1)
<OOBTreeItems object at 0x1080a0a70>
In [4]: print(v2)
<BTrees.OOBTree.OOBTree object at 0x106ee3640>
In [5]: print(v3)
<BTrees.OOBTree.OOTreeIterator object at 0x107070f90>
In [6]: func1()
1 2 3 4
In [7]: func2()
1 2 3 4
In [8]: func3()
(1, 'red') (2, 'green') (3, 'blue') (4, 'spades')

BTreePython dict 1dictBTree使

(Total orderin) 3

(Reflexive) x x == x
(Trichotomy) x y x < y x == y x > y 1
(Transitivity) x <= y y <= z x <= z

Python == != BTree使Pythondict使

BTree使intlongfloat8Unicode

28Unicode2 'x' < chr(255) and u'x' == 'x' chr(255) u'x' 1BTree使

1(mutability)BTreeBTree

In [2]: # %load 33_btrees_mutability.py
...: from BTrees.OOBTree import OOSet
...:
...: L1, L2, L3 = [1], [2], [3]
...: s = OOSet((L2, L3, L1))
...:
...: v1 = list(s.keys())
...: v2 = s.has_key([3])
...: v3 = L2[0] = 5
...: v4 = s.has_key([3])
...:
...: # print(s)
...: # print(v1)
...: # print(v2)
...: # print(v3)
...: # print(v4)
...:
In [3]: print(s)
OOSet([[1], [5], [3]])
In [4]: print(v1)
[[1], [5], [3]]
In [5]: print(v2)
True
In [6]: print(v3)
5
In [7]: print(v4)
False

使L2OOSet

Python2Python 使BTree使BTreeK1K2K1K2BTreeK1K2BTree

PythonBTreeBTree RuntimeError

In [2]: # %load 35_iteration_mutation.py
...: from BTrees.IIBTree import *
...: s = IISet(range(10))
...: v1 = list(s)
...:
...: def func1():
...: for i in s:
...: print(f'{i} ', end='')
...: s.remove(i)
...:
...: v2 = list(s)
...:
...: # print(v1)
...: # func1()
...: # print(v2)
...:
In [3]: print(v1)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [4]: func1()
0 2 4 6 8 ---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-4-d88c41ef3303> in <module>
----> 1 func1()
<ipython-input-2-683390a04f0e> in func1()
5
6 def func1():
----> 7 for i in s:
8 print(f'{i} ', end='')
9 s.remove(i)
RuntimeError: the bucket being iterated changed size
In [5]: print(v2)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [6]: func1()
1 5 9
In [7]: func1()
3 ---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-7-d88c41ef3303> in <module>
----> 1 func1()
<ipython-input-2-683390a04f0e> in func1()
5
6 def func1():
----> 7 for i in s:
8 print(f'{i} ', end='')
9 s.remove(i)
RuntimeError: the bucket being iterated changed size

PythonBTree
In [2]: # %load 36_iteration_safe_mutation.py
...: from BTrees.IIBTree import *
...: s = IISet(range(10))
...:
...: def func1():
...: for i in list(s.keys()):
...: print(f'{i} ', end='')
...: s.remove(i)
...:
...: # s
...: # s.keys()
...: # func1()
...: # s.keys()
...: # s
...:
In [3]: s
Out[3]: IISet([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
In [4]: s.keys()
Out[4]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [5]: func1()
0 1 2 3 4 5 6 7 8 9
In [6]: s.keys()
Out[6]: []

ZODB
ZODB便

6464
ZODB64ID使ID64

p64 u64
z64 64


In [2]: # %load 40_utils_int_str.py
...: import ZODB.utils
...:
...: v1 = ZODB.utils.p64(12345678901234567890)
...: v2 = ZODB.utils.u64(b'\xabT\xa9\x8c\xeb\x1f\n\xd2')
...: v3 = ZODB.utils.z64
...:
...: # print(v1)
...: # print(v2)
...: # print(v3)
...:
In [3]: print(v1)
b'\xabT\xa9\x8c\xeb\x1f\n\xd2'
In [4]: print(v2)
12345678901234567890
In [5]: print(v3)
b'\x00\x00\x00\x00\x00\x00\x00\x00'

ID
ZODBIDUTC newTid()

time.time()
import time
# time.time()
old_time = time.time
def fake_time():
return 1224825068.12
time.time = fake_time

fake_time
newTid() tid ID tid None

In [2]: # %load 41_newTid_timetamp.py
...: import ZODB.utils
...: import ZODB.TimeStamp
...: from fake_time import *
...:
...: tid1 = ZODB.utils.newTid(None)
...: tid2 = ZODB.utils.newTid(tid1)
...:
...: v1 = ZODB.TimeStamp.TimeStamp(tid1)
...: v2 = ZODB.utils.u64(tid1), ZODB.utils.u64(tid2)
...:
...: # print(tid1)
...: # print(tid2)
...: # print(v1)
...: # print(v2)
...:
In [3]: print(tid1)
b'\x03yi\xf7"\xa54\x88'
In [4]: print(tid2)
b'\x03yi\xf7"\xa54\x89'
In [5]: print(v1)
2008-10-24 05:11:08.120000
In [6]: print(v2)
(250347764454864008, 250347764454864009)

ZODB

import ZODB.utils
class Lock:
def acquire(self):
print('acquire')
def release(self):
print('release')
def __enter__(self):
return self.acquire()
def __exit__(self, *ignored):
return self.release()

In [2]: # %load 42_locking.py
...: from zodb_precondition import *
...:
...: class C:
...: _lock = Lock()
...: _lock_acquire = _lock.acquire
...: _lock_release = _lock.release
...:
...: @ZODB.utils.locked
...: def meth(self, *args, **kw):
...: print('meth %r %r' %(args, kw))
...:
...: # C().meth(1, 2, a=3)
...:
In [3]: C().meth(1, 2, a=3)
acquire
meth (1, 2) {'a': 3}
release



In [2]: # %load 43_precondition.py
...: from zodb_precondition import *
...:
...: class C:
...: def __init__(self):
...: self._lock = Lock()
...: self._opened = True
...: self._transaction = None
...:
...: def opened(self):
...: print('checking if open')
...: return self._opened
...:
...: def not_in_transaction(self):
...: print('checking if in a transaction')
...: return self._transaction is None
...:
...: @ZODB.utils.locked(opened, not_in_transaction)
...: def meth(self, *args, **kw):
...: print('meth %r %r' % (args, kw))
...:
...: c = C()
...: # c.meth(1, 2, a=3)
...: # c._transaction = 1
...: # c.meth(1, 2, a=3)
...: # c._opened = False
...: # c.meth(1, 2, a=3)
...:

In [3]: c.meth(1, 2, a=3)
acquire
checking if open
checking if in a transaction
meth (1, 2) {'a': 3}
release
In [4]: c._transaction = 1
In [5]: c.meth(1, 2, a=3)
acquire
checking if open
checking if in a transaction
release
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-5-dbd9bc3f75ec> in <module>
----> 1 c.meth(1, 2, a=3)
~/anaconda3/envs/class_database/lib/python3.9/site-packages/ZODB/utils.py in __call__(self, *args, **kw)
284 raise AssertionError(
285 "Failed precondition: ",
--> 286 precondition.__doc__.strip())
287
288 return func(*args, **kw)
AttributeError: 'NoneType' object has no attribute 'strip'

In [6]: c._opened = False
In [7]: c.meth(1, 2, a=3)
acquire
checking if open
release
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-7-dbd9bc3f75ec> in <module>
----> 1 c.meth(1, 2, a=3)
~/anaconda3/envs/class_database/lib/python3.9/site-packages/ZODB/utils.py in __call__(self, *args, **kw)
284 raise AssertionError(
285 "Failed precondition: ",
--> 286 precondition.__doc__.strip())
287
288 return func(*args, **kw)
AttributeError: 'NoneType' object has no attribute 'strip'

ZODB退
from zodb_mydata import *
root.users = BTree()
root.users['devops'] = user_data
transaction.commit()
connection.close()
user_data.append(adam)

root.users['devops'] = user_data
transaction.commit()
user_data.append(adam)
connection.close()

ZODB使
ZODBZConfig使storagesDB

<zodb>
<filestorage>
path /tmp/test-filestorage.fs
</filestorage>
</zodb>

ZODB.config
import ZODB.config
db = ZODB.config.databaseFromURL('/tmp/test.conf')
conn = db.open()

ZODB.config ZConfig


ZConfig
ZConfigZODB
ZConfigPython Apache HTTP Server 使 XML 使

ZConfigZopeZODB使使ZConfigPython
(#)
# This is comment

%include
%include default.conf

URL %include

key value # still part of the value

#

使


<section-type [name]>

section-typename
</section-type>

1

<my-section>
key-1 value-1
key-2 value-2
<another-section>
key-3 value-3
</another-section>
</my-section>

使

<section-type [name] />

ZConfig 使使 %define 使
%define name [value]

ZConfig 使ZConfig 使name value key-value value

使

ZConfig.substitution 使
( $ ) ( $ )( $$ )

keyvalue
%define name value
key $name

ZConfig Python os.getenv() ( $ ) (...) key ENVKEY
key $(ENVKEY)

使ZConfig使使

%import 使
%import Products.Ape

ZConfig 使 ZConfig 使

使使使

%import 使


<eventlog>
level verbose
<logfile>
path /var/log/myapp/events.log
</logfile>
</eventlog>


SMSPython
%import my.pager.loghandler
<eventlog>
level verbose
<logfile>
path /var/log/myapp/events.log
</logfile>
<pager>
number 1-800-555-1234
message Something broke!
</pager>
</eventlog>

ZODB
ZODB便調

ZEO
ZEOFileStorageZEO1FileStorageAPI

ZEOZEOPythonGIL

NEO
NEONEO(disaster resistance)

ZRS
ZRS(ZODB replicated storage)ZODB
ZRSZope20135

ZRS
ZRS

ZRS使
2

ZRS
使1ZRS


RelStorage
RelStorageZODBZEOZRSRelStorageMySQLPostgreSQLOracleSQLite使OID

FileStorageZEO
FileStorageUndoPacking
RelStorage使
1SQLite使ZODB
Blob
PickleRDBMS ZEO ZEO
PickleRDBMSZEO
PythonZODBZEORelStorageZEO
ZODB 5ID2
RelStorageZEOFileStorage
FileStorageRelStorage
SQL
PostgreSQLMySQLgevent
FileStorageRelStorage()(zodbconvert)RelStorage2ZODB使
(zodbpack)
zodburi
(ZPL 2.1).

zlibstorage
zlibstorage

beforestorage
beforestorageDemoStorage使


ZODBPythonPythonZODBSQL
調便