zlmdb 25.10.1__cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.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.

Files changed (87) hide show
  1. flatbuffers/__init__.py +19 -0
  2. flatbuffers/_version.py +17 -0
  3. flatbuffers/builder.py +776 -0
  4. flatbuffers/compat.py +86 -0
  5. flatbuffers/encode.py +42 -0
  6. flatbuffers/flexbuffers.py +1527 -0
  7. flatbuffers/number_types.py +181 -0
  8. flatbuffers/packer.py +42 -0
  9. flatbuffers/reflection/AdvancedFeatures.py +10 -0
  10. flatbuffers/reflection/BaseType.py +24 -0
  11. flatbuffers/reflection/Enum.py +169 -0
  12. flatbuffers/reflection/EnumVal.py +96 -0
  13. flatbuffers/reflection/Field.py +208 -0
  14. flatbuffers/reflection/KeyValue.py +56 -0
  15. flatbuffers/reflection/Object.py +175 -0
  16. flatbuffers/reflection/RPCCall.py +131 -0
  17. flatbuffers/reflection/Schema.py +206 -0
  18. flatbuffers/reflection/SchemaFile.py +77 -0
  19. flatbuffers/reflection/Service.py +145 -0
  20. flatbuffers/reflection/Type.py +98 -0
  21. flatbuffers/reflection/__init__.py +0 -0
  22. flatbuffers/table.py +129 -0
  23. flatbuffers/util.py +43 -0
  24. zlmdb/__init__.py +312 -0
  25. zlmdb/_database.py +990 -0
  26. zlmdb/_errors.py +31 -0
  27. zlmdb/_meta.py +27 -0
  28. zlmdb/_pmap.py +1667 -0
  29. zlmdb/_schema.py +137 -0
  30. zlmdb/_transaction.py +181 -0
  31. zlmdb/_types.py +1596 -0
  32. zlmdb/_version.py +27 -0
  33. zlmdb/cli.py +41 -0
  34. zlmdb/flatbuffers/__init__.py +5 -0
  35. zlmdb/flatbuffers/reflection/AdvancedFeatures.py +10 -0
  36. zlmdb/flatbuffers/reflection/BaseType.py +25 -0
  37. zlmdb/flatbuffers/reflection/Enum.py +252 -0
  38. zlmdb/flatbuffers/reflection/EnumVal.py +144 -0
  39. zlmdb/flatbuffers/reflection/Field.py +325 -0
  40. zlmdb/flatbuffers/reflection/KeyValue.py +84 -0
  41. zlmdb/flatbuffers/reflection/Object.py +260 -0
  42. zlmdb/flatbuffers/reflection/RPCCall.py +195 -0
  43. zlmdb/flatbuffers/reflection/Schema.py +301 -0
  44. zlmdb/flatbuffers/reflection/SchemaFile.py +112 -0
  45. zlmdb/flatbuffers/reflection/Service.py +213 -0
  46. zlmdb/flatbuffers/reflection/Type.py +148 -0
  47. zlmdb/flatbuffers/reflection/__init__.py +0 -0
  48. zlmdb/flatbuffers/reflection.fbs +152 -0
  49. zlmdb/lmdb/__init__.py +37 -0
  50. zlmdb/lmdb/__main__.py +25 -0
  51. zlmdb/lmdb/_config.py +10 -0
  52. zlmdb/lmdb/_lmdb_cffi.cpython-312-aarch64-linux-gnu.so +0 -0
  53. zlmdb/lmdb/cffi.py +2606 -0
  54. zlmdb/lmdb/tool.py +670 -0
  55. zlmdb/tests/lmdb/__init__.py +0 -0
  56. zlmdb/tests/lmdb/address_book.py +287 -0
  57. zlmdb/tests/lmdb/crash_test.py +339 -0
  58. zlmdb/tests/lmdb/cursor_test.py +333 -0
  59. zlmdb/tests/lmdb/env_test.py +919 -0
  60. zlmdb/tests/lmdb/getmulti_test.py +92 -0
  61. zlmdb/tests/lmdb/iteration_test.py +258 -0
  62. zlmdb/tests/lmdb/package_test.py +70 -0
  63. zlmdb/tests/lmdb/test_lmdb.py +188 -0
  64. zlmdb/tests/lmdb/testlib.py +185 -0
  65. zlmdb/tests/lmdb/tool_test.py +60 -0
  66. zlmdb/tests/lmdb/txn_test.py +575 -0
  67. zlmdb/tests/orm/MNodeLog.py +853 -0
  68. zlmdb/tests/orm/__init__.py +0 -0
  69. zlmdb/tests/orm/_schema_fbs.py +215 -0
  70. zlmdb/tests/orm/_schema_mnode_log.py +1201 -0
  71. zlmdb/tests/orm/_schema_py2.py +250 -0
  72. zlmdb/tests/orm/_schema_py3.py +307 -0
  73. zlmdb/tests/orm/_test_flatbuffers.py +144 -0
  74. zlmdb/tests/orm/_test_serialization.py +144 -0
  75. zlmdb/tests/orm/test_basic.py +217 -0
  76. zlmdb/tests/orm/test_etcd.py +275 -0
  77. zlmdb/tests/orm/test_pmap_indexes.py +466 -0
  78. zlmdb/tests/orm/test_pmap_types.py +90 -0
  79. zlmdb/tests/orm/test_pmaps.py +295 -0
  80. zlmdb/tests/orm/test_select.py +619 -0
  81. zlmdb-25.10.1.dist-info/METADATA +264 -0
  82. zlmdb-25.10.1.dist-info/RECORD +87 -0
  83. zlmdb-25.10.1.dist-info/WHEEL +7 -0
  84. zlmdb-25.10.1.dist-info/entry_points.txt +2 -0
  85. zlmdb-25.10.1.dist-info/licenses/LICENSE +137 -0
  86. zlmdb-25.10.1.dist-info/licenses/NOTICE +41 -0
  87. 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()