zlmdb 25.10.1__cp311-cp311-macosx_10_9_universal2.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of zlmdb might be problematic. Click here for more details.
- flatbuffers/__init__.py +19 -0
- flatbuffers/_version.py +17 -0
- flatbuffers/builder.py +776 -0
- flatbuffers/compat.py +86 -0
- flatbuffers/encode.py +42 -0
- flatbuffers/flexbuffers.py +1527 -0
- flatbuffers/number_types.py +181 -0
- flatbuffers/packer.py +42 -0
- flatbuffers/reflection/AdvancedFeatures.py +10 -0
- flatbuffers/reflection/BaseType.py +24 -0
- flatbuffers/reflection/Enum.py +169 -0
- flatbuffers/reflection/EnumVal.py +96 -0
- flatbuffers/reflection/Field.py +208 -0
- flatbuffers/reflection/KeyValue.py +56 -0
- flatbuffers/reflection/Object.py +175 -0
- flatbuffers/reflection/RPCCall.py +131 -0
- flatbuffers/reflection/Schema.py +206 -0
- flatbuffers/reflection/SchemaFile.py +77 -0
- flatbuffers/reflection/Service.py +145 -0
- flatbuffers/reflection/Type.py +98 -0
- flatbuffers/reflection/__init__.py +0 -0
- flatbuffers/table.py +129 -0
- flatbuffers/util.py +43 -0
- zlmdb/__init__.py +312 -0
- zlmdb/_database.py +990 -0
- zlmdb/_errors.py +31 -0
- zlmdb/_meta.py +27 -0
- zlmdb/_pmap.py +1667 -0
- zlmdb/_schema.py +137 -0
- zlmdb/_transaction.py +181 -0
- zlmdb/_types.py +1596 -0
- zlmdb/_version.py +27 -0
- zlmdb/cli.py +41 -0
- zlmdb/flatbuffers/__init__.py +5 -0
- zlmdb/flatbuffers/reflection/AdvancedFeatures.py +10 -0
- zlmdb/flatbuffers/reflection/BaseType.py +25 -0
- zlmdb/flatbuffers/reflection/Enum.py +252 -0
- zlmdb/flatbuffers/reflection/EnumVal.py +144 -0
- zlmdb/flatbuffers/reflection/Field.py +325 -0
- zlmdb/flatbuffers/reflection/KeyValue.py +84 -0
- zlmdb/flatbuffers/reflection/Object.py +260 -0
- zlmdb/flatbuffers/reflection/RPCCall.py +195 -0
- zlmdb/flatbuffers/reflection/Schema.py +301 -0
- zlmdb/flatbuffers/reflection/SchemaFile.py +112 -0
- zlmdb/flatbuffers/reflection/Service.py +213 -0
- zlmdb/flatbuffers/reflection/Type.py +148 -0
- zlmdb/flatbuffers/reflection/__init__.py +0 -0
- zlmdb/flatbuffers/reflection.fbs +152 -0
- zlmdb/lmdb/__init__.py +37 -0
- zlmdb/lmdb/__main__.py +25 -0
- zlmdb/lmdb/_config.py +10 -0
- zlmdb/lmdb/_lmdb_cffi.cpython-311-darwin.so +0 -0
- zlmdb/lmdb/cffi.py +2606 -0
- zlmdb/lmdb/tool.py +670 -0
- zlmdb/tests/lmdb/__init__.py +0 -0
- zlmdb/tests/lmdb/address_book.py +287 -0
- zlmdb/tests/lmdb/crash_test.py +339 -0
- zlmdb/tests/lmdb/cursor_test.py +333 -0
- zlmdb/tests/lmdb/env_test.py +919 -0
- zlmdb/tests/lmdb/getmulti_test.py +92 -0
- zlmdb/tests/lmdb/iteration_test.py +258 -0
- zlmdb/tests/lmdb/package_test.py +70 -0
- zlmdb/tests/lmdb/test_lmdb.py +188 -0
- zlmdb/tests/lmdb/testlib.py +185 -0
- zlmdb/tests/lmdb/tool_test.py +60 -0
- zlmdb/tests/lmdb/txn_test.py +575 -0
- zlmdb/tests/orm/MNodeLog.py +853 -0
- zlmdb/tests/orm/__init__.py +0 -0
- zlmdb/tests/orm/_schema_fbs.py +215 -0
- zlmdb/tests/orm/_schema_mnode_log.py +1201 -0
- zlmdb/tests/orm/_schema_py2.py +250 -0
- zlmdb/tests/orm/_schema_py3.py +307 -0
- zlmdb/tests/orm/_test_flatbuffers.py +144 -0
- zlmdb/tests/orm/_test_serialization.py +144 -0
- zlmdb/tests/orm/test_basic.py +217 -0
- zlmdb/tests/orm/test_etcd.py +275 -0
- zlmdb/tests/orm/test_pmap_indexes.py +466 -0
- zlmdb/tests/orm/test_pmap_types.py +90 -0
- zlmdb/tests/orm/test_pmaps.py +295 -0
- zlmdb/tests/orm/test_select.py +619 -0
- zlmdb-25.10.1.dist-info/METADATA +264 -0
- zlmdb-25.10.1.dist-info/RECORD +87 -0
- zlmdb-25.10.1.dist-info/WHEEL +5 -0
- zlmdb-25.10.1.dist-info/entry_points.txt +2 -0
- zlmdb-25.10.1.dist-info/licenses/LICENSE +137 -0
- zlmdb-25.10.1.dist-info/licenses/NOTICE +41 -0
- zlmdb-25.10.1.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,919 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright 2013 The py-lmdb authors, all rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
|
5
|
+
# modification, are permitted only as authorized by the OpenLDAP
|
|
6
|
+
# Public License.
|
|
7
|
+
#
|
|
8
|
+
# A copy of this license is available in the file LICENSE in the
|
|
9
|
+
# top-level directory of the distribution or, alternatively, at
|
|
10
|
+
# <http://www.OpenLDAP.org/license.html>.
|
|
11
|
+
#
|
|
12
|
+
# OpenLDAP is a registered trademark of the OpenLDAP Foundation.
|
|
13
|
+
#
|
|
14
|
+
# Individual files and/or contributed packages may be copyright by
|
|
15
|
+
# other parties and/or subject to additional restrictions.
|
|
16
|
+
#
|
|
17
|
+
# This work also contains materials derived from public sources.
|
|
18
|
+
#
|
|
19
|
+
# Additional information about OpenLDAP can be obtained at
|
|
20
|
+
# <http://www.openldap.org/>.
|
|
21
|
+
#
|
|
22
|
+
|
|
23
|
+
from __future__ import absolute_import
|
|
24
|
+
from __future__ import with_statement
|
|
25
|
+
import os
|
|
26
|
+
import sys
|
|
27
|
+
import unittest
|
|
28
|
+
import weakref
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
from . import testlib
|
|
32
|
+
from .testlib import B
|
|
33
|
+
from .testlib import OCT
|
|
34
|
+
from .testlib import INT_TYPES
|
|
35
|
+
from .testlib import UnicodeType
|
|
36
|
+
except ImportError:
|
|
37
|
+
# Running as script, not as module
|
|
38
|
+
import testlib
|
|
39
|
+
from testlib import B
|
|
40
|
+
from testlib import OCT
|
|
41
|
+
from testlib import INT_TYPES
|
|
42
|
+
from testlib import UnicodeType
|
|
43
|
+
|
|
44
|
+
import zlmdb.lmdb as lmdb
|
|
45
|
+
|
|
46
|
+
# Whether we have the patch that allows env.copy* to take a txn
|
|
47
|
+
have_txn_patch = lmdb.version(subpatch=True)[3]
|
|
48
|
+
|
|
49
|
+
NO_READERS = UnicodeType("(no active readers)\n")
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
PAGE_SIZE = os.sysconf(os.sysconf_names["SC_PAGE_SIZE"])
|
|
53
|
+
except (AttributeError, KeyError, OSError):
|
|
54
|
+
PAGE_SIZE = 4096
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class VersionTest(unittest.TestCase):
|
|
58
|
+
def tearDown(self):
|
|
59
|
+
testlib.cleanup()
|
|
60
|
+
|
|
61
|
+
def test_version(self):
|
|
62
|
+
ver = lmdb.version()
|
|
63
|
+
assert len(ver) == 3
|
|
64
|
+
assert all(isinstance(i, INT_TYPES) for i in ver)
|
|
65
|
+
assert all(i >= 0 for i in ver)
|
|
66
|
+
|
|
67
|
+
def test_version_subpatch(self):
|
|
68
|
+
ver = lmdb.version(subpatch=True)
|
|
69
|
+
assert len(ver) == 4
|
|
70
|
+
assert all(isinstance(i, INT_TYPES) for i in ver)
|
|
71
|
+
assert all(i >= 0 for i in ver)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class OpenTest(unittest.TestCase):
|
|
75
|
+
def tearDown(self):
|
|
76
|
+
testlib.cleanup()
|
|
77
|
+
|
|
78
|
+
def test_bad_paths(self):
|
|
79
|
+
self.assertRaises(Exception, lambda: lmdb.open("/doesnt/exist/at/all"))
|
|
80
|
+
self.assertRaises(Exception, lambda: lmdb.open(testlib.temp_file()))
|
|
81
|
+
|
|
82
|
+
def test_ok_path(self):
|
|
83
|
+
path, env = testlib.temp_env()
|
|
84
|
+
assert os.path.exists(path)
|
|
85
|
+
assert os.path.exists(os.path.join(path, "data.mdb"))
|
|
86
|
+
assert os.path.exists(os.path.join(path, "lock.mdb"))
|
|
87
|
+
assert env.path() == path
|
|
88
|
+
|
|
89
|
+
def test_bad_size(self):
|
|
90
|
+
self.assertRaises(OverflowError, lambda: testlib.temp_env(map_size=-123))
|
|
91
|
+
|
|
92
|
+
def test_tiny_size(self):
|
|
93
|
+
_, env = testlib.temp_env(map_size=10)
|
|
94
|
+
|
|
95
|
+
def txn():
|
|
96
|
+
with env.begin(write=True) as txn:
|
|
97
|
+
txn.put(B("a"), B("a"))
|
|
98
|
+
|
|
99
|
+
self.assertRaises(lmdb.MapFullError, txn)
|
|
100
|
+
|
|
101
|
+
def test_subdir_false_junk(self):
|
|
102
|
+
path = testlib.temp_file()
|
|
103
|
+
fp = open(path, "wb")
|
|
104
|
+
fp.write(B("A" * 8192))
|
|
105
|
+
fp.close()
|
|
106
|
+
self.assertRaises(lmdb.InvalidError, lambda: lmdb.open(path, subdir=False))
|
|
107
|
+
|
|
108
|
+
def test_subdir_false_ok(self):
|
|
109
|
+
path = testlib.temp_file(create=False)
|
|
110
|
+
_, env = testlib.temp_env(path, subdir=False)
|
|
111
|
+
assert os.path.exists(path)
|
|
112
|
+
assert os.path.isfile(path)
|
|
113
|
+
assert os.path.isfile(path + "-lock")
|
|
114
|
+
assert not env.flags()["subdir"]
|
|
115
|
+
|
|
116
|
+
def test_subdir_true_noexist_nocreate(self):
|
|
117
|
+
path = testlib.temp_dir(create=False)
|
|
118
|
+
self.assertRaises(
|
|
119
|
+
lmdb.Error, lambda: testlib.temp_env(path, subdir=True, create=False)
|
|
120
|
+
)
|
|
121
|
+
assert not os.path.exists(path)
|
|
122
|
+
|
|
123
|
+
def test_subdir_true_noexist_create(self):
|
|
124
|
+
path = testlib.temp_dir(create=False)
|
|
125
|
+
path_, env = testlib.temp_env(path, subdir=True, create=True)
|
|
126
|
+
assert path_ == path
|
|
127
|
+
assert env.path() == path
|
|
128
|
+
|
|
129
|
+
def test_subdir_true_exist_nocreate(self):
|
|
130
|
+
path, env = testlib.temp_env()
|
|
131
|
+
assert lmdb.open(path, subdir=True, create=False).path() == path
|
|
132
|
+
|
|
133
|
+
def test_subdir_true_exist_create(self):
|
|
134
|
+
path, env = testlib.temp_env()
|
|
135
|
+
assert lmdb.open(path, subdir=True, create=True).path() == path
|
|
136
|
+
|
|
137
|
+
def test_readonly_false(self):
|
|
138
|
+
path, env = testlib.temp_env(readonly=False)
|
|
139
|
+
with env.begin(write=True) as txn:
|
|
140
|
+
txn.put(B("a"), B(""))
|
|
141
|
+
with env.begin() as txn:
|
|
142
|
+
assert txn.get(B("a")) == B("")
|
|
143
|
+
assert not env.flags()["readonly"]
|
|
144
|
+
|
|
145
|
+
def test_readonly_true_noexist(self):
|
|
146
|
+
path = testlib.temp_dir(create=False)
|
|
147
|
+
# Open readonly missing store should fail.
|
|
148
|
+
self.assertRaises(
|
|
149
|
+
lmdb.Error, lambda: lmdb.open(path, readonly=True, create=True)
|
|
150
|
+
)
|
|
151
|
+
# And create=True should not have mkdir'd it.
|
|
152
|
+
assert not os.path.exists(path)
|
|
153
|
+
|
|
154
|
+
def test_readonly_true_exist(self):
|
|
155
|
+
path, env = testlib.temp_env()
|
|
156
|
+
env2 = lmdb.open(path, readonly=True)
|
|
157
|
+
assert env2.path() == path
|
|
158
|
+
# Attempting a write txn should fail.
|
|
159
|
+
self.assertRaises(lmdb.ReadonlyError, lambda: env2.begin(write=True))
|
|
160
|
+
# Flag should be set.
|
|
161
|
+
assert env2.flags()["readonly"]
|
|
162
|
+
|
|
163
|
+
def test_metasync(self):
|
|
164
|
+
for flag in True, False:
|
|
165
|
+
path, env = testlib.temp_env(metasync=flag)
|
|
166
|
+
assert env.flags()["metasync"] == flag
|
|
167
|
+
|
|
168
|
+
def test_lock(self):
|
|
169
|
+
for flag in True, False:
|
|
170
|
+
path, env = testlib.temp_env(lock=flag)
|
|
171
|
+
lock_path = os.path.join(path, "lock.mdb")
|
|
172
|
+
assert env.flags()["lock"] == flag
|
|
173
|
+
assert flag == os.path.exists(lock_path)
|
|
174
|
+
|
|
175
|
+
def test_sync(self):
|
|
176
|
+
for flag in True, False:
|
|
177
|
+
path, env = testlib.temp_env(sync=flag)
|
|
178
|
+
assert env.flags()["sync"] == flag
|
|
179
|
+
|
|
180
|
+
def test_map_async(self):
|
|
181
|
+
for flag in True, False:
|
|
182
|
+
path, env = testlib.temp_env(map_async=flag)
|
|
183
|
+
assert env.flags()["map_async"] == flag
|
|
184
|
+
|
|
185
|
+
def test_mode_subdir_create(self):
|
|
186
|
+
if sys.platform == "win32":
|
|
187
|
+
# Mode argument is ignored on Windows; see lmdb.h
|
|
188
|
+
return
|
|
189
|
+
|
|
190
|
+
oldmask = os.umask(0)
|
|
191
|
+
try:
|
|
192
|
+
for mode in OCT("777"), OCT("755"), OCT("700"):
|
|
193
|
+
path = testlib.temp_dir(create=False)
|
|
194
|
+
env = lmdb.open(path, subdir=True, create=True, mode=mode)
|
|
195
|
+
fmode = mode & ~OCT("111")
|
|
196
|
+
assert testlib.path_mode(path) == mode
|
|
197
|
+
assert testlib.path_mode(path + "/data.mdb") == fmode
|
|
198
|
+
assert testlib.path_mode(path + "/lock.mdb") == fmode
|
|
199
|
+
finally:
|
|
200
|
+
os.umask(oldmask)
|
|
201
|
+
|
|
202
|
+
def test_mode_subdir_nocreate(self):
|
|
203
|
+
if sys.platform == "win32":
|
|
204
|
+
# Mode argument is ignored on Windows; see lmdb.h
|
|
205
|
+
return
|
|
206
|
+
|
|
207
|
+
oldmask = os.umask(0)
|
|
208
|
+
try:
|
|
209
|
+
for mode in OCT("777"), OCT("755"), OCT("700"):
|
|
210
|
+
path = testlib.temp_dir()
|
|
211
|
+
env = lmdb.open(path, subdir=True, create=False, mode=mode)
|
|
212
|
+
fmode = mode & ~OCT("111")
|
|
213
|
+
assert testlib.path_mode(path + "/data.mdb") == fmode
|
|
214
|
+
assert testlib.path_mode(path + "/lock.mdb") == fmode
|
|
215
|
+
finally:
|
|
216
|
+
os.umask(oldmask)
|
|
217
|
+
|
|
218
|
+
def test_readahead(self):
|
|
219
|
+
for flag in True, False:
|
|
220
|
+
path, env = testlib.temp_env(readahead=flag)
|
|
221
|
+
assert env.flags()["readahead"] == flag
|
|
222
|
+
|
|
223
|
+
def test_writemap(self):
|
|
224
|
+
for flag in True, False:
|
|
225
|
+
path, env = testlib.temp_env(writemap=flag)
|
|
226
|
+
assert env.flags()["writemap"] == flag
|
|
227
|
+
|
|
228
|
+
def test_meminit(self):
|
|
229
|
+
for flag in True, False:
|
|
230
|
+
path, env = testlib.temp_env(meminit=flag)
|
|
231
|
+
assert env.flags()["meminit"] == flag
|
|
232
|
+
|
|
233
|
+
def test_max_readers(self):
|
|
234
|
+
self.assertRaises(
|
|
235
|
+
lmdb.InvalidParameterError, lambda: testlib.temp_env(max_readers=0)
|
|
236
|
+
)
|
|
237
|
+
for val in 123, 234:
|
|
238
|
+
_, env = testlib.temp_env(max_readers=val)
|
|
239
|
+
assert env.info()["max_readers"] == val
|
|
240
|
+
|
|
241
|
+
def test_max_dbs(self):
|
|
242
|
+
self.assertRaises(OverflowError, lambda: testlib.temp_env(max_dbs=-1))
|
|
243
|
+
for val in 0, 10, 20:
|
|
244
|
+
_, env = testlib.temp_env(max_dbs=val)
|
|
245
|
+
dbs = [env.open_db(B("db%d" % i)) for i in range(val)]
|
|
246
|
+
self.assertRaises(lmdb.DbsFullError, lambda: env.open_db(B("toomany")))
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
class SetMapSizeTest(unittest.TestCase):
|
|
250
|
+
def tearDown(self):
|
|
251
|
+
testlib.cleanup()
|
|
252
|
+
|
|
253
|
+
def test_invalid(self):
|
|
254
|
+
_, env = testlib.temp_env()
|
|
255
|
+
env.close()
|
|
256
|
+
self.assertRaises(Exception, lambda: env.set_mapsize(999999))
|
|
257
|
+
|
|
258
|
+
def test_negative(self):
|
|
259
|
+
_, env = testlib.temp_env()
|
|
260
|
+
self.assertRaises(OverflowError, lambda: env.set_mapsize(-2015))
|
|
261
|
+
|
|
262
|
+
def test_applied(self):
|
|
263
|
+
_, env = testlib.temp_env(map_size=PAGE_SIZE * 8)
|
|
264
|
+
assert env.info()["map_size"] == PAGE_SIZE * 8
|
|
265
|
+
|
|
266
|
+
env.set_mapsize(PAGE_SIZE * 16)
|
|
267
|
+
assert env.info()["map_size"] == PAGE_SIZE * 16
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
class CloseTest(unittest.TestCase):
|
|
271
|
+
def tearDown(self):
|
|
272
|
+
testlib.cleanup()
|
|
273
|
+
|
|
274
|
+
def test_close(self):
|
|
275
|
+
_, env = testlib.temp_env()
|
|
276
|
+
# Attempting things should be ok.
|
|
277
|
+
txn = env.begin(write=True)
|
|
278
|
+
txn.put(B("a"), B(""))
|
|
279
|
+
cursor = txn.cursor()
|
|
280
|
+
list(cursor)
|
|
281
|
+
cursor.first()
|
|
282
|
+
it = iter(cursor)
|
|
283
|
+
|
|
284
|
+
env.close()
|
|
285
|
+
# Repeated calls are ignored:
|
|
286
|
+
env.close()
|
|
287
|
+
# Attempting to use invalid objects should crash.
|
|
288
|
+
self.assertRaises(Exception, lambda: txn.cursor())
|
|
289
|
+
self.assertRaises(Exception, lambda: txn.commit())
|
|
290
|
+
self.assertRaises(Exception, lambda: cursor.first())
|
|
291
|
+
self.assertRaises(Exception, lambda: list(it))
|
|
292
|
+
# Abort should be OK though.
|
|
293
|
+
txn.abort()
|
|
294
|
+
# Attempting to start new txn should crash.
|
|
295
|
+
self.assertRaises(Exception, lambda: env.begin())
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
class ContextManagerTest(unittest.TestCase):
|
|
299
|
+
def tearDown(self):
|
|
300
|
+
testlib.cleanup()
|
|
301
|
+
|
|
302
|
+
def test_ok(self):
|
|
303
|
+
path, env = testlib.temp_env()
|
|
304
|
+
with env as env_:
|
|
305
|
+
assert env_ is env
|
|
306
|
+
with env.begin() as txn:
|
|
307
|
+
txn.get(B("foo"))
|
|
308
|
+
self.assertRaises(Exception, lambda: env.begin())
|
|
309
|
+
|
|
310
|
+
def test_crash(self):
|
|
311
|
+
path, env = testlib.temp_env()
|
|
312
|
+
try:
|
|
313
|
+
with env as env_:
|
|
314
|
+
assert env_ is env
|
|
315
|
+
with env.begin() as txn:
|
|
316
|
+
txn.get(123)
|
|
317
|
+
except:
|
|
318
|
+
pass
|
|
319
|
+
self.assertRaises(Exception, lambda: env.begin())
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
class InfoMethodsTest(unittest.TestCase):
|
|
323
|
+
def tearDown(self):
|
|
324
|
+
testlib.cleanup()
|
|
325
|
+
|
|
326
|
+
def test_path(self):
|
|
327
|
+
path, env = testlib.temp_env()
|
|
328
|
+
assert path == env.path()
|
|
329
|
+
assert isinstance(env.path(), UnicodeType)
|
|
330
|
+
|
|
331
|
+
env.close()
|
|
332
|
+
self.assertRaises(Exception, lambda: env.path())
|
|
333
|
+
|
|
334
|
+
def test_stat(self):
|
|
335
|
+
_, env = testlib.temp_env()
|
|
336
|
+
stat = env.stat()
|
|
337
|
+
for k in "psize", "depth", "branch_pages", "overflow_pages", "entries":
|
|
338
|
+
assert isinstance(stat[k], INT_TYPES), k
|
|
339
|
+
assert stat[k] >= 0
|
|
340
|
+
|
|
341
|
+
assert stat["entries"] == 0
|
|
342
|
+
txn = env.begin(write=True)
|
|
343
|
+
txn.put(B("a"), B("b"))
|
|
344
|
+
txn.commit()
|
|
345
|
+
stat = env.stat()
|
|
346
|
+
assert stat["entries"] == 1
|
|
347
|
+
|
|
348
|
+
env.close()
|
|
349
|
+
self.assertRaises(Exception, lambda: env.stat())
|
|
350
|
+
|
|
351
|
+
def test_info(self):
|
|
352
|
+
_, env = testlib.temp_env()
|
|
353
|
+
info = env.info()
|
|
354
|
+
for k in (
|
|
355
|
+
"map_addr",
|
|
356
|
+
"map_size",
|
|
357
|
+
"last_pgno",
|
|
358
|
+
"last_txnid",
|
|
359
|
+
"max_readers",
|
|
360
|
+
"num_readers",
|
|
361
|
+
):
|
|
362
|
+
assert isinstance(info[k], INT_TYPES), k
|
|
363
|
+
assert info[k] >= 0
|
|
364
|
+
|
|
365
|
+
assert info["last_txnid"] == 0
|
|
366
|
+
txn = env.begin(write=True)
|
|
367
|
+
txn.put(B("a"), B(""))
|
|
368
|
+
txn.commit()
|
|
369
|
+
info = env.info()
|
|
370
|
+
assert info["last_txnid"] == 1
|
|
371
|
+
|
|
372
|
+
env.close()
|
|
373
|
+
self.assertRaises(Exception, lambda: env.info())
|
|
374
|
+
|
|
375
|
+
def test_flags(self):
|
|
376
|
+
_, env = testlib.temp_env()
|
|
377
|
+
info = env.flags()
|
|
378
|
+
for k in (
|
|
379
|
+
"subdir",
|
|
380
|
+
"readonly",
|
|
381
|
+
"metasync",
|
|
382
|
+
"sync",
|
|
383
|
+
"map_async",
|
|
384
|
+
"readahead",
|
|
385
|
+
"writemap",
|
|
386
|
+
):
|
|
387
|
+
assert isinstance(info[k], bool)
|
|
388
|
+
|
|
389
|
+
env.close()
|
|
390
|
+
self.assertRaises(Exception, lambda: env.flags())
|
|
391
|
+
|
|
392
|
+
def test_max_key_size(self):
|
|
393
|
+
_, env = testlib.temp_env()
|
|
394
|
+
mks = env.max_key_size()
|
|
395
|
+
assert isinstance(mks, INT_TYPES)
|
|
396
|
+
assert mks > 0
|
|
397
|
+
|
|
398
|
+
env.close()
|
|
399
|
+
self.assertRaises(Exception, lambda: env.max_key_size())
|
|
400
|
+
|
|
401
|
+
def test_max_readers(self):
|
|
402
|
+
_, env = testlib.temp_env()
|
|
403
|
+
mr = env.max_readers()
|
|
404
|
+
assert isinstance(mr, INT_TYPES)
|
|
405
|
+
assert mr > 0 and mr == env.info()["max_readers"]
|
|
406
|
+
|
|
407
|
+
env.close()
|
|
408
|
+
self.assertRaises(Exception, lambda: env.max_readers())
|
|
409
|
+
|
|
410
|
+
def test_readers(self):
|
|
411
|
+
_, env = testlib.temp_env(max_spare_txns=0)
|
|
412
|
+
r = env.readers()
|
|
413
|
+
assert isinstance(r, UnicodeType)
|
|
414
|
+
assert r == NO_READERS
|
|
415
|
+
|
|
416
|
+
rtxn = env.begin()
|
|
417
|
+
r2 = env.readers()
|
|
418
|
+
assert isinstance(env.readers(), UnicodeType)
|
|
419
|
+
assert env.readers() != r
|
|
420
|
+
|
|
421
|
+
env.close()
|
|
422
|
+
self.assertRaises(Exception, lambda: env.readers())
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
class OtherMethodsTest(unittest.TestCase):
|
|
426
|
+
def tearDown(self):
|
|
427
|
+
testlib.cleanup()
|
|
428
|
+
|
|
429
|
+
def test_copy(self):
|
|
430
|
+
_, env = testlib.temp_env()
|
|
431
|
+
txn = env.begin(write=True)
|
|
432
|
+
txn.put(B("a"), B("b"))
|
|
433
|
+
txn.commit()
|
|
434
|
+
|
|
435
|
+
dest_dir = testlib.temp_dir()
|
|
436
|
+
|
|
437
|
+
if have_txn_patch:
|
|
438
|
+
with env.begin() as txn:
|
|
439
|
+
self.assertRaises(Exception, lambda: env.copy(dest_dir, txn=txn))
|
|
440
|
+
|
|
441
|
+
env.copy(dest_dir, compact=True)
|
|
442
|
+
assert os.path.exists(dest_dir + "/data.mdb")
|
|
443
|
+
|
|
444
|
+
cenv = lmdb.open(dest_dir)
|
|
445
|
+
ctxn = cenv.begin()
|
|
446
|
+
assert ctxn.get(B("a")) == B("b")
|
|
447
|
+
|
|
448
|
+
env.close()
|
|
449
|
+
self.assertRaises(Exception, lambda: env.copy(testlib.temp_dir()))
|
|
450
|
+
|
|
451
|
+
def test_copy_compact(self):
|
|
452
|
+
_, env = testlib.temp_env()
|
|
453
|
+
txn = env.begin(write=True)
|
|
454
|
+
txn.put(B("a"), B("b"))
|
|
455
|
+
txn.commit()
|
|
456
|
+
|
|
457
|
+
dest_dir = testlib.temp_dir()
|
|
458
|
+
env.copy(dest_dir, compact=True)
|
|
459
|
+
assert os.path.exists(dest_dir + "/data.mdb")
|
|
460
|
+
|
|
461
|
+
cenv = lmdb.open(dest_dir)
|
|
462
|
+
ctxn = cenv.begin()
|
|
463
|
+
assert ctxn.get(B("a")) == B("b")
|
|
464
|
+
|
|
465
|
+
# Test copy with transaction provided
|
|
466
|
+
dest_dir = testlib.temp_dir()
|
|
467
|
+
with env.begin(write=True) as txn:
|
|
468
|
+
copy_txn = env.begin()
|
|
469
|
+
txn.put(B("b"), B("b"))
|
|
470
|
+
assert ctxn.get(B("b")) is None
|
|
471
|
+
|
|
472
|
+
if have_txn_patch:
|
|
473
|
+
env.copy(dest_dir, compact=True, txn=copy_txn)
|
|
474
|
+
assert os.path.exists(dest_dir + "/data.mdb")
|
|
475
|
+
|
|
476
|
+
cenv = lmdb.open(dest_dir)
|
|
477
|
+
ctxn = cenv.begin()
|
|
478
|
+
assert ctxn.get(B("a")) == B("b")
|
|
479
|
+
# Verify that the write that occurred outside the txn isn't seen in the
|
|
480
|
+
# copy
|
|
481
|
+
assert ctxn.get(B("b")) is None
|
|
482
|
+
|
|
483
|
+
else:
|
|
484
|
+
self.assertRaises(
|
|
485
|
+
Exception, lambda: env.copy(dest_dir, compact=True, txn=copy_txn)
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
env.close()
|
|
489
|
+
self.assertRaises(Exception, lambda: env.copy(testlib.temp_dir()))
|
|
490
|
+
|
|
491
|
+
def test_copyfd_compact(self):
|
|
492
|
+
path, env = testlib.temp_env()
|
|
493
|
+
txn = env.begin(write=True)
|
|
494
|
+
txn.put(B("a"), B("b"))
|
|
495
|
+
txn.commit()
|
|
496
|
+
|
|
497
|
+
dst_path = testlib.temp_file(create=False)
|
|
498
|
+
fp = open(dst_path, "wb")
|
|
499
|
+
env.copyfd(fp.fileno())
|
|
500
|
+
|
|
501
|
+
dstenv = lmdb.open(dst_path, subdir=False)
|
|
502
|
+
dtxn = dstenv.begin()
|
|
503
|
+
assert dtxn.get(B("a")) == B("b")
|
|
504
|
+
fp.close()
|
|
505
|
+
|
|
506
|
+
# Test copy with transaction provided
|
|
507
|
+
dst_path = testlib.temp_file(create=False)
|
|
508
|
+
fp = open(dst_path, "wb")
|
|
509
|
+
with env.begin(write=True) as txn:
|
|
510
|
+
copy_txn = env.begin()
|
|
511
|
+
txn.put(B("b"), B("b"))
|
|
512
|
+
assert dtxn.get(B("b")) is None
|
|
513
|
+
|
|
514
|
+
if have_txn_patch:
|
|
515
|
+
env.copyfd(fp.fileno(), compact=True, txn=copy_txn)
|
|
516
|
+
|
|
517
|
+
dstenv = lmdb.open(dst_path, subdir=False)
|
|
518
|
+
dtxn = dstenv.begin()
|
|
519
|
+
assert dtxn.get(B("a")) == B("b")
|
|
520
|
+
# Verify that the write that occurred outside the txn isn't seen in the
|
|
521
|
+
# copy
|
|
522
|
+
assert dtxn.get(B("b")) is None
|
|
523
|
+
dstenv.close()
|
|
524
|
+
|
|
525
|
+
else:
|
|
526
|
+
self.assertRaises(
|
|
527
|
+
Exception, lambda: env.copyfd(fp.fileno(), compact=True, txn=copy_txn)
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
env.close()
|
|
531
|
+
self.assertRaises(Exception, lambda: env.copyfd(fp.fileno()))
|
|
532
|
+
fp.close()
|
|
533
|
+
|
|
534
|
+
def test_copyfd(self):
|
|
535
|
+
path, env = testlib.temp_env()
|
|
536
|
+
txn = env.begin(write=True)
|
|
537
|
+
txn.put(B("a"), B("b"))
|
|
538
|
+
txn.commit()
|
|
539
|
+
|
|
540
|
+
dst_path = testlib.temp_file(create=False)
|
|
541
|
+
fp = open(dst_path, "wb")
|
|
542
|
+
|
|
543
|
+
if have_txn_patch:
|
|
544
|
+
with env.begin() as txn:
|
|
545
|
+
self.assertRaises(Exception, lambda: env.copyfd(fp.fileno(), txn=txn))
|
|
546
|
+
env.copyfd(fp.fileno())
|
|
547
|
+
|
|
548
|
+
dstenv = lmdb.open(dst_path, subdir=False)
|
|
549
|
+
dtxn = dstenv.begin()
|
|
550
|
+
assert dtxn.get(B("a")) == B("b")
|
|
551
|
+
|
|
552
|
+
env.close()
|
|
553
|
+
self.assertRaises(Exception, lambda: env.copyfd(fp.fileno()))
|
|
554
|
+
fp.close()
|
|
555
|
+
|
|
556
|
+
def test_sync(self):
|
|
557
|
+
_, env = testlib.temp_env()
|
|
558
|
+
env.sync(False)
|
|
559
|
+
env.sync(True)
|
|
560
|
+
env.close()
|
|
561
|
+
self.assertRaises(Exception, lambda: env.sync(False))
|
|
562
|
+
|
|
563
|
+
@staticmethod
|
|
564
|
+
def _test_reader_check_child(path):
|
|
565
|
+
"""Function to run in child process since we can't use fork() on
|
|
566
|
+
win32."""
|
|
567
|
+
env = lmdb.open(path, max_spare_txns=0)
|
|
568
|
+
txn = env.begin()
|
|
569
|
+
os._exit(0)
|
|
570
|
+
|
|
571
|
+
def test_reader_check(self):
|
|
572
|
+
if sys.platform == "win32":
|
|
573
|
+
# Stale writers are cleared automatically on Windows, see lmdb.h
|
|
574
|
+
return
|
|
575
|
+
|
|
576
|
+
path, env = testlib.temp_env(max_spare_txns=0)
|
|
577
|
+
rc = env.reader_check()
|
|
578
|
+
assert rc == 0
|
|
579
|
+
|
|
580
|
+
# We need to open a separate env since Transaction.abort() always calls
|
|
581
|
+
# reset for a read-only txn, the actual abort doesn't happen until
|
|
582
|
+
# __del__, when Transaction discovers there is no room for it on the
|
|
583
|
+
# freelist.
|
|
584
|
+
env1 = lmdb.open(path)
|
|
585
|
+
txn1 = env1.begin()
|
|
586
|
+
assert env.readers() != NO_READERS
|
|
587
|
+
assert env.reader_check() == 0
|
|
588
|
+
|
|
589
|
+
# Start a child, open a txn, then crash the child.
|
|
590
|
+
rc = os.spawnl(
|
|
591
|
+
os.P_WAIT,
|
|
592
|
+
sys.executable,
|
|
593
|
+
sys.executable,
|
|
594
|
+
__file__,
|
|
595
|
+
"test_reader_check_child",
|
|
596
|
+
path,
|
|
597
|
+
)
|
|
598
|
+
|
|
599
|
+
assert rc == 0
|
|
600
|
+
assert env.reader_check() == 1
|
|
601
|
+
assert env.reader_check() == 0
|
|
602
|
+
assert env.readers() != NO_READERS
|
|
603
|
+
|
|
604
|
+
txn1.abort()
|
|
605
|
+
env1.close()
|
|
606
|
+
assert env.readers() == NO_READERS
|
|
607
|
+
|
|
608
|
+
env.close()
|
|
609
|
+
self.assertRaises(Exception, lambda: env.reader_check())
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
class BeginTest(unittest.TestCase):
|
|
613
|
+
def tearDown(self):
|
|
614
|
+
testlib.cleanup()
|
|
615
|
+
|
|
616
|
+
def test_begin_closed(self):
|
|
617
|
+
_, env = testlib.temp_env()
|
|
618
|
+
env.close()
|
|
619
|
+
self.assertRaises(Exception, lambda: env.begin())
|
|
620
|
+
|
|
621
|
+
def test_begin_readonly(self):
|
|
622
|
+
_, env = testlib.temp_env()
|
|
623
|
+
txn = env.begin()
|
|
624
|
+
# Read txn can't write.
|
|
625
|
+
self.assertRaises(lmdb.ReadonlyError, lambda: txn.put(B("a"), B("")))
|
|
626
|
+
txn.abort()
|
|
627
|
+
|
|
628
|
+
def test_begin_write(self):
|
|
629
|
+
_, env = testlib.temp_env()
|
|
630
|
+
txn = env.begin(write=True)
|
|
631
|
+
# Write txn can write.
|
|
632
|
+
assert txn.put(B("a"), B(""))
|
|
633
|
+
txn.commit()
|
|
634
|
+
|
|
635
|
+
def test_bind_db(self):
|
|
636
|
+
_, env = testlib.temp_env()
|
|
637
|
+
main = env.open_db(None)
|
|
638
|
+
self.assertRaises(ValueError, lambda: env.open_db(None, dupsort=True))
|
|
639
|
+
sub = env.open_db(B("db1"))
|
|
640
|
+
|
|
641
|
+
txn = env.begin(write=True, db=sub)
|
|
642
|
+
assert txn.put(B("b"), B("")) # -> sub
|
|
643
|
+
assert txn.put(B("a"), B(""), db=main) # -> main
|
|
644
|
+
txn.commit()
|
|
645
|
+
|
|
646
|
+
txn = env.begin()
|
|
647
|
+
assert txn.get(B("a")) == B("")
|
|
648
|
+
assert txn.get(B("b")) is None
|
|
649
|
+
assert txn.get(B("a"), db=sub) is None
|
|
650
|
+
assert txn.get(B("b"), db=sub) == B("")
|
|
651
|
+
txn.abort()
|
|
652
|
+
|
|
653
|
+
def test_parent_readonly(self):
|
|
654
|
+
_, env = testlib.temp_env()
|
|
655
|
+
parent = env.begin()
|
|
656
|
+
# Nonsensical.
|
|
657
|
+
self.assertRaises(lmdb.InvalidParameterError, lambda: env.begin(parent=parent))
|
|
658
|
+
|
|
659
|
+
def test_parent(self):
|
|
660
|
+
_, env = testlib.temp_env()
|
|
661
|
+
parent = env.begin(write=True)
|
|
662
|
+
parent.put(B("a"), B("a"))
|
|
663
|
+
|
|
664
|
+
child = env.begin(write=True, parent=parent)
|
|
665
|
+
assert child.get(B("a")) == B("a")
|
|
666
|
+
assert child.put(B("a"), B("b"))
|
|
667
|
+
child.abort()
|
|
668
|
+
|
|
669
|
+
# put() should have rolled back
|
|
670
|
+
assert parent.get(B("a")) == B("a")
|
|
671
|
+
|
|
672
|
+
child = env.begin(write=True, parent=parent)
|
|
673
|
+
assert child.put(B("a"), B("b"))
|
|
674
|
+
child.commit()
|
|
675
|
+
|
|
676
|
+
# put() should be visible
|
|
677
|
+
assert parent.get(B("a")) == B("b")
|
|
678
|
+
|
|
679
|
+
def test_buffers(self):
|
|
680
|
+
_, env = testlib.temp_env()
|
|
681
|
+
txn = env.begin(write=True, buffers=True)
|
|
682
|
+
assert txn.put(B("a"), B("a"))
|
|
683
|
+
b = txn.get(B("a"))
|
|
684
|
+
assert b is not None
|
|
685
|
+
assert len(b) == 1
|
|
686
|
+
assert not isinstance(b, type(B("")))
|
|
687
|
+
txn.commit()
|
|
688
|
+
|
|
689
|
+
txn = env.begin(buffers=False)
|
|
690
|
+
b = txn.get(B("a"))
|
|
691
|
+
assert b is not None
|
|
692
|
+
assert len(b) == 1
|
|
693
|
+
assert isinstance(b, type(B("")))
|
|
694
|
+
txn.abort()
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
class OpenDbTest(unittest.TestCase):
|
|
698
|
+
def tearDown(self):
|
|
699
|
+
testlib.cleanup()
|
|
700
|
+
|
|
701
|
+
def test_main(self):
|
|
702
|
+
_, env = testlib.temp_env()
|
|
703
|
+
# Start write txn, so we cause deadlock if open_db attempts txn.
|
|
704
|
+
txn = env.begin(write=True)
|
|
705
|
+
# Now get main DBI, we should already be open.
|
|
706
|
+
db = env.open_db(None)
|
|
707
|
+
# w00t, no deadlock.
|
|
708
|
+
|
|
709
|
+
flags = db.flags(txn)
|
|
710
|
+
assert not flags["reverse_key"]
|
|
711
|
+
assert not flags["dupsort"]
|
|
712
|
+
txn.abort()
|
|
713
|
+
|
|
714
|
+
def test_unicode(self):
|
|
715
|
+
_, env = testlib.temp_env()
|
|
716
|
+
assert env.open_db(B("myindex")) is not None
|
|
717
|
+
self.assertRaises(TypeError, lambda: env.open_db(UnicodeType("myindex")))
|
|
718
|
+
|
|
719
|
+
def test_sub_notxn(self):
|
|
720
|
+
_, env = testlib.temp_env()
|
|
721
|
+
assert env.info()["last_txnid"] == 0
|
|
722
|
+
db1 = env.open_db(B("subdb1"))
|
|
723
|
+
assert env.info()["last_txnid"] == 1
|
|
724
|
+
db2 = env.open_db(B("subdb2"))
|
|
725
|
+
assert env.info()["last_txnid"] == 2
|
|
726
|
+
|
|
727
|
+
env.close()
|
|
728
|
+
self.assertRaises(Exception, lambda: env.open_db("subdb3"))
|
|
729
|
+
|
|
730
|
+
def test_sub_rotxn(self):
|
|
731
|
+
_, env = testlib.temp_env()
|
|
732
|
+
txn = env.begin(write=False)
|
|
733
|
+
self.assertRaises(lmdb.ReadonlyError, lambda: env.open_db(B("subdb"), txn=txn))
|
|
734
|
+
|
|
735
|
+
def test_sub_txn(self):
|
|
736
|
+
_, env = testlib.temp_env()
|
|
737
|
+
txn = env.begin(write=True)
|
|
738
|
+
db1 = env.open_db(B("subdb1"), txn=txn)
|
|
739
|
+
db2 = env.open_db(B("subdb2"), txn=txn)
|
|
740
|
+
for db in db1, db2:
|
|
741
|
+
assert db.flags(txn) == {
|
|
742
|
+
"dupfixed": False,
|
|
743
|
+
"dupsort": False,
|
|
744
|
+
"integerdup": False,
|
|
745
|
+
"integerkey": False,
|
|
746
|
+
"reverse_key": False,
|
|
747
|
+
}
|
|
748
|
+
txn.commit()
|
|
749
|
+
|
|
750
|
+
def test_reopen(self):
|
|
751
|
+
path, env = testlib.temp_env()
|
|
752
|
+
db1 = env.open_db(B("subdb1"))
|
|
753
|
+
env.close()
|
|
754
|
+
env = lmdb.open(path, max_dbs=10)
|
|
755
|
+
db1 = env.open_db(B("subdb1"))
|
|
756
|
+
|
|
757
|
+
FLAG_SETS = [
|
|
758
|
+
(flag, val)
|
|
759
|
+
for flag in ("reverse_key", "dupsort", "integerkey", "integerdup", "dupfixed")
|
|
760
|
+
for val in (True, False)
|
|
761
|
+
]
|
|
762
|
+
|
|
763
|
+
def test_flags(self):
|
|
764
|
+
path, env = testlib.temp_env()
|
|
765
|
+
txn = env.begin(write=True)
|
|
766
|
+
|
|
767
|
+
for flag, val in self.FLAG_SETS:
|
|
768
|
+
key = B("%s-%s" % (flag, val))
|
|
769
|
+
db = env.open_db(key, txn=txn, **{flag: val})
|
|
770
|
+
assert db.flags(txn)[flag] == val
|
|
771
|
+
assert db.flags(None)[flag] == val
|
|
772
|
+
assert db.flags()[flag] == val
|
|
773
|
+
self.assertRaises(TypeError, lambda: db.flags(1, 2, 3))
|
|
774
|
+
|
|
775
|
+
txn.commit()
|
|
776
|
+
# Test flag persistence.
|
|
777
|
+
env.close()
|
|
778
|
+
env = lmdb.open(path, max_dbs=10)
|
|
779
|
+
txn = env.begin(write=True)
|
|
780
|
+
|
|
781
|
+
for flag, val in self.FLAG_SETS:
|
|
782
|
+
key = B("%s-%s" % (flag, val))
|
|
783
|
+
db = env.open_db(key, txn=txn)
|
|
784
|
+
assert db.flags(txn)[flag] == val
|
|
785
|
+
|
|
786
|
+
def test_readonly_env_main(self):
|
|
787
|
+
path, env = testlib.temp_env()
|
|
788
|
+
env.close()
|
|
789
|
+
|
|
790
|
+
env = lmdb.open(path, readonly=True)
|
|
791
|
+
db = env.open_db(None)
|
|
792
|
+
|
|
793
|
+
def test_readonly_env_sub_noexist(self):
|
|
794
|
+
# https://github.com/dw/py-lmdb/issues/109
|
|
795
|
+
path, env = testlib.temp_env()
|
|
796
|
+
env.close()
|
|
797
|
+
|
|
798
|
+
env = lmdb.open(path, max_dbs=10, readonly=True)
|
|
799
|
+
self.assertRaises(
|
|
800
|
+
lmdb.NotFoundError, lambda: env.open_db(B("node_schedules"), create=False)
|
|
801
|
+
)
|
|
802
|
+
|
|
803
|
+
def test_readonly_env_sub_eperm(self):
|
|
804
|
+
# https://github.com/dw/py-lmdb/issues/109
|
|
805
|
+
path, env = testlib.temp_env()
|
|
806
|
+
env.close()
|
|
807
|
+
|
|
808
|
+
env = lmdb.open(path, max_dbs=10, readonly=True)
|
|
809
|
+
self.assertRaises(
|
|
810
|
+
lmdb.ReadonlyError, lambda: env.open_db(B("node_schedules"), create=True)
|
|
811
|
+
)
|
|
812
|
+
|
|
813
|
+
def test_readonly_env_sub(self):
|
|
814
|
+
# https://github.com/dw/py-lmdb/issues/109
|
|
815
|
+
path, env = testlib.temp_env()
|
|
816
|
+
assert env.open_db(B("node_schedules")) is not None
|
|
817
|
+
env.close()
|
|
818
|
+
|
|
819
|
+
env = lmdb.open(path, max_dbs=10, readonly=True)
|
|
820
|
+
db = env.open_db(B("node_schedules"), create=False)
|
|
821
|
+
assert db is not None
|
|
822
|
+
|
|
823
|
+
|
|
824
|
+
def reader_count(env):
|
|
825
|
+
"""Count the number of active readers."""
|
|
826
|
+
return env.readers().count("\n") - 1
|
|
827
|
+
|
|
828
|
+
|
|
829
|
+
class SpareTxnTest(unittest.TestCase):
|
|
830
|
+
def tearDown(self):
|
|
831
|
+
testlib.cleanup()
|
|
832
|
+
|
|
833
|
+
def test_none(self):
|
|
834
|
+
_, env = testlib.temp_env(max_spare_txns=0)
|
|
835
|
+
assert 0 == reader_count(env)
|
|
836
|
+
|
|
837
|
+
t1 = env.begin()
|
|
838
|
+
assert 1 == reader_count(env)
|
|
839
|
+
|
|
840
|
+
t2 = env.begin()
|
|
841
|
+
assert 2 == reader_count(env)
|
|
842
|
+
|
|
843
|
+
t1.abort()
|
|
844
|
+
del t1
|
|
845
|
+
assert 1 == reader_count(env)
|
|
846
|
+
|
|
847
|
+
t2.abort()
|
|
848
|
+
del t2
|
|
849
|
+
assert 0 == reader_count(env)
|
|
850
|
+
|
|
851
|
+
def test_one(self):
|
|
852
|
+
_, env = testlib.temp_env(max_spare_txns=1)
|
|
853
|
+
# 1 here, since CFFI uses a temporary reader during init.
|
|
854
|
+
assert 1 >= reader_count(env)
|
|
855
|
+
|
|
856
|
+
t1 = env.begin()
|
|
857
|
+
assert 1 == reader_count(env)
|
|
858
|
+
|
|
859
|
+
t2 = env.begin()
|
|
860
|
+
assert 2 == reader_count(env)
|
|
861
|
+
|
|
862
|
+
t1.abort()
|
|
863
|
+
del t1
|
|
864
|
+
assert 2 == reader_count(env) # 1 live, 1 cached
|
|
865
|
+
|
|
866
|
+
t2.abort()
|
|
867
|
+
del t2
|
|
868
|
+
assert 1 == reader_count(env) # 1 cached
|
|
869
|
+
|
|
870
|
+
t3 = env.begin()
|
|
871
|
+
assert 1 == reader_count(env) # 1 live
|
|
872
|
+
|
|
873
|
+
t3.abort()
|
|
874
|
+
del t3
|
|
875
|
+
assert 1 == reader_count(env) # 1 cached
|
|
876
|
+
|
|
877
|
+
|
|
878
|
+
class LeakTest(unittest.TestCase):
|
|
879
|
+
def tearDown(self):
|
|
880
|
+
testlib.cleanup()
|
|
881
|
+
|
|
882
|
+
def test_open_unref_does_not_leak(self):
|
|
883
|
+
temp_dir = testlib.temp_dir()
|
|
884
|
+
env = lmdb.open(temp_dir)
|
|
885
|
+
ref = weakref.ref(env)
|
|
886
|
+
env = None
|
|
887
|
+
testlib.debug_collect()
|
|
888
|
+
assert ref() is None
|
|
889
|
+
|
|
890
|
+
def test_open_close_does_not_leak(self):
|
|
891
|
+
temp_dir = testlib.temp_dir()
|
|
892
|
+
env = lmdb.open(temp_dir)
|
|
893
|
+
env.close()
|
|
894
|
+
ref = weakref.ref(env)
|
|
895
|
+
env = None
|
|
896
|
+
testlib.debug_collect()
|
|
897
|
+
assert ref() is None
|
|
898
|
+
|
|
899
|
+
def test_weakref_callback_invoked_once(self):
|
|
900
|
+
temp_dir = testlib.temp_dir()
|
|
901
|
+
env = lmdb.open(temp_dir)
|
|
902
|
+
env.close()
|
|
903
|
+
count = [0]
|
|
904
|
+
|
|
905
|
+
def callback(ref):
|
|
906
|
+
count[0] += 1
|
|
907
|
+
|
|
908
|
+
ref = weakref.ref(env, callback)
|
|
909
|
+
env = None
|
|
910
|
+
testlib.debug_collect()
|
|
911
|
+
assert ref() is None
|
|
912
|
+
assert count[0] == 1
|
|
913
|
+
|
|
914
|
+
|
|
915
|
+
if __name__ == "__main__":
|
|
916
|
+
if len(sys.argv) > 1 and sys.argv[1] == "test_reader_check_child":
|
|
917
|
+
OtherMethodsTest._test_reader_check_child(sys.argv[2])
|
|
918
|
+
else:
|
|
919
|
+
unittest.main()
|