zlmdb 25.10.1__cp314-cp314-manylinux_2_34_x86_64.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-314-x86_64-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 +5 -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,92 @@
1
+ from __future__ import absolute_import
2
+ from __future__ import with_statement
3
+ import unittest
4
+
5
+ from . import testlib
6
+ import struct
7
+ from .testlib import KEYSFIXED, ITEMS_MULTI_FIXEDKEY
8
+ from .testlib import putBigDataMultiFixed
9
+
10
+
11
+ class GetMultiTestBase(unittest.TestCase):
12
+ def tearDown(self):
13
+ testlib.cleanup()
14
+
15
+ def setUp(self, dupsort=None, dupfixed=None):
16
+ self.db_key = "testdb".encode("utf-8")
17
+ self.path, self.env = testlib.temp_env(max_dbs=1)
18
+ self.txn = self.env.begin(write=True)
19
+ self.db = self.env.open_db(
20
+ key=self.db_key, txn=self.txn, dupsort=dupsort, dupfixed=dupfixed
21
+ )
22
+ putBigDataMultiFixed(self.txn, db=self.db)
23
+ self.c = self.txn.cursor(db=self.db)
24
+
25
+ def matchList(self, ls_a, ls_b):
26
+ return (not (ls_a or ls_b)) or (
27
+ ls_a and ls_b and all(map(lambda x, y: x == y, ls_a, ls_b))
28
+ )
29
+
30
+
31
+ class GetMultiTestNoDupsortNoDupfixed(GetMultiTestBase):
32
+ ITEMS2_MULTI_NODUP = ITEMS_MULTI_FIXEDKEY[1::2]
33
+
34
+ def setUp(self, dupsort=False, dupfixed=False):
35
+ super(GetMultiTestNoDupsortNoDupfixed, self).setUp(
36
+ dupsort=dupsort, dupfixed=dupfixed
37
+ )
38
+
39
+ def testGetMulti(self):
40
+ test_list = self.c.getmulti(KEYSFIXED)
41
+ self.assertEqual(self.matchList(test_list, self.ITEMS2_MULTI_NODUP), True)
42
+
43
+
44
+ class GetMultiTestDupsortNoDupfixed(GetMultiTestBase):
45
+ def setUp(self, dupsort=True, dupfixed=False):
46
+ super(GetMultiTestDupsortNoDupfixed, self).setUp(
47
+ dupsort=dupsort, dupfixed=dupfixed
48
+ )
49
+
50
+ def testGetMulti(self):
51
+ test_list = self.c.getmulti(KEYSFIXED, dupdata=True)
52
+ self.assertEqual(self.matchList(test_list, ITEMS_MULTI_FIXEDKEY), True)
53
+
54
+
55
+ class GetMultiTestDupsortDupfixed(GetMultiTestBase):
56
+ def setUp(self, dupsort=True, dupfixed=True):
57
+ super(GetMultiTestDupsortDupfixed, self).setUp(
58
+ dupsort=dupsort, dupfixed=dupfixed
59
+ )
60
+
61
+ def testGetMulti(self):
62
+ test_list = self.c.getmulti(KEYSFIXED, dupdata=True, dupfixed_bytes=1)
63
+ self.assertEqual(self.matchList(test_list, ITEMS_MULTI_FIXEDKEY), True)
64
+
65
+
66
+ class GetMultiTestDupsortDupfixedKeyfixed(GetMultiTestBase):
67
+ def setUp(self, dupsort=True, dupfixed=True):
68
+ super(GetMultiTestDupsortDupfixedKeyfixed, self).setUp(
69
+ dupsort=dupsort, dupfixed=dupfixed
70
+ )
71
+
72
+ def testGetMulti(self):
73
+ val_bytes = 1
74
+ arr = bytearray(
75
+ self.c.getmulti(
76
+ KEYSFIXED, dupdata=True, dupfixed_bytes=val_bytes, keyfixed=True
77
+ )
78
+ )
79
+ asserts = []
80
+ for i, kv in enumerate(ITEMS_MULTI_FIXEDKEY):
81
+ key, val = kv
82
+ asserts.extend(
83
+ (
84
+ struct.pack("b", arr[i * 2]) == key,
85
+ struct.pack("b", arr[i * 2 + 1]) == val,
86
+ )
87
+ )
88
+ self.assertEqual(all(asserts), True)
89
+
90
+
91
+ if __name__ == "__main__":
92
+ unittest.main()
@@ -0,0 +1,258 @@
1
+ #! /usr/bin/env python
2
+ #
3
+ # Copyright 2013 The py-lmdb authors, all rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted only as authorized by the OpenLDAP
7
+ # Public License.
8
+ #
9
+ # A copy of this license is available in the file LICENSE in the
10
+ # top-level directory of the distribution or, alternatively, at
11
+ # <http://www.OpenLDAP.org/license.html>.
12
+ #
13
+ # OpenLDAP is a registered trademark of the OpenLDAP Foundation.
14
+ #
15
+ # Individual files and/or contributed packages may be copyright by
16
+ # other parties and/or subject to additional restrictions.
17
+ #
18
+ # This work also contains materials derived from public sources.
19
+ #
20
+ # Additional information about OpenLDAP can be obtained at
21
+ # <http://www.openldap.org/>.
22
+ #
23
+
24
+ # test delete(dupdata)
25
+
26
+ from __future__ import absolute_import
27
+ from __future__ import with_statement
28
+ import unittest
29
+
30
+ from . import testlib
31
+ from .testlib import B
32
+ from .testlib import BT
33
+ from .testlib import KEYS, ITEMS, KEYS2, ITEMS2
34
+ from .testlib import putData, putBigData
35
+
36
+
37
+ class IterationTestBase(unittest.TestCase):
38
+ def tearDown(self):
39
+ testlib.cleanup()
40
+
41
+ def setUp(self):
42
+ self.path, self.env = testlib.temp_env() # creates 10 databases
43
+ self.txn = self.env.begin(write=True)
44
+ putData(self.txn)
45
+ self.c = self.txn.cursor()
46
+ self.empty_entry = (B(""), B(""))
47
+
48
+ def matchList(self, ls_a, ls_b):
49
+ return all(map(lambda x, y: x == y, ls_a, ls_b))
50
+
51
+
52
+ class IterationTestBase2(unittest.TestCase):
53
+ """This puts more data than its predecessor"""
54
+
55
+ def tearDown(self):
56
+ testlib.cleanup()
57
+
58
+ def setUp(self):
59
+ self.path, self.env = testlib.temp_env() # creates 10 databases
60
+ self.txn = self.env.begin(write=True)
61
+ putBigData(self.txn) # HERE!
62
+ self.c = self.txn.cursor()
63
+ self.empty_entry = ("", "")
64
+
65
+ def matchList(self, ls_a, ls_b):
66
+ return all(map(lambda x, y: x == y, ls_a, ls_b))
67
+
68
+
69
+ class IterationTest(IterationTestBase):
70
+ def testFromStart(self):
71
+ # From start
72
+ self.c.first()
73
+ self.assertEqual(self.c.key(), KEYS[0]) # start of db
74
+ test_list = [i for i in iter(self.c)]
75
+ self.assertEqual(self.matchList(test_list, ITEMS), True)
76
+ self.assertEqual(self.c.item(), self.empty_entry) # end of db
77
+
78
+ def testFromStartWithIternext(self):
79
+ # From start with iternext
80
+ self.c.first()
81
+ self.assertEqual(self.c.key(), KEYS[0]) # start of db
82
+ test_list = [i for i in self.c.iternext()]
83
+ # remaining elements in db
84
+ self.assertEqual(self.matchList(test_list, ITEMS), True)
85
+ self.assertEqual(self.c.item(), self.empty_entry) # end of db
86
+
87
+ def testFromStartWithNext(self):
88
+ # From start with next
89
+ self.c.first()
90
+ self.assertEqual(self.c.key(), KEYS[0]) # start of db
91
+ test_list = []
92
+ while 1:
93
+ test_list.append(self.c.item())
94
+ if not self.c.next():
95
+ break
96
+ self.assertEqual(self.matchList(test_list, ITEMS), True)
97
+
98
+ def testFromExistentKeySetKey(self):
99
+ self.c.first()
100
+ self.c.set_key(KEYS[1])
101
+ self.assertEqual(self.c.key(), KEYS[1])
102
+ test_list = [i for i in self.c.iternext()]
103
+ self.assertEqual(self.matchList(test_list, ITEMS[1:]), True)
104
+
105
+ def testFromExistentKeySetRange(self):
106
+ self.c.first()
107
+ self.c.set_range(KEYS[1])
108
+ self.assertEqual(self.c.key(), KEYS[1])
109
+ test_list = [i for i in self.c.iternext()]
110
+ self.assertEqual(self.matchList(test_list, ITEMS[1:]), True)
111
+
112
+ def testFromNonExistentKeySetRange(self):
113
+ self.c.first()
114
+ self.c.set_range(B("c"))
115
+ self.assertEqual(self.c.key(), B("d"))
116
+ test_list = [i for i in self.c.iternext()]
117
+ test_items = [i for i in ITEMS if i[0] > B("c")]
118
+ self.assertEqual(self.matchList(test_list, test_items), True)
119
+
120
+ def testFromLastKey(self):
121
+ self.c.last()
122
+ self.assertEqual(self.c.key(), KEYS[-1])
123
+ test_list = [i for i in self.c.iternext()]
124
+ self.assertEqual(self.matchList(test_list, ITEMS[-1:]), True)
125
+
126
+ def testFromNonExistentKeyPastEnd(self):
127
+ self.c.last()
128
+ self.assertEqual(self.c.key(), KEYS[-1])
129
+ # next() fails, leaving iterator in an unpositioned state.
130
+ self.c.next()
131
+ self.assertEqual(self.c.item(), self.empty_entry)
132
+ # iternext() from an unpositioned state proceeds from start of DB.
133
+ test_list = list(self.c.iternext())
134
+ self.assertEqual(test_list, ITEMS)
135
+
136
+
137
+ class ReverseIterationTest(IterationTestBase):
138
+ def testFromStartRev(self):
139
+ # From start
140
+ self.c.first()
141
+ self.assertEqual(self.c.key(), KEYS[0]) # start of db
142
+ test_list = [i for i in self.c.iterprev()]
143
+ self.assertEqual(self.matchList(test_list, ITEMS[:1][::-1]), True)
144
+ self.assertEqual(self.c.item(), self.empty_entry) # very start of db
145
+
146
+ def testFromExistentKeySetKeyRev(self):
147
+ self.c.first()
148
+ self.c.set_key(KEYS[2])
149
+ self.assertEqual(self.c.key(), KEYS[2])
150
+ test_list = [i for i in self.c.iterprev()]
151
+ self.assertEqual(self.matchList(test_list, ITEMS[:3][::-1]), True)
152
+
153
+ def testFromExistentKeySetRangeRev(self):
154
+ self.c.first()
155
+ self.c.set_range(KEYS[2])
156
+ self.assertEqual(self.c.key(), KEYS[2])
157
+ test_list = [i for i in self.c.iterprev()]
158
+ self.assertEqual(self.matchList(test_list, ITEMS[:3][::-1]), True)
159
+
160
+ def testFromNonExistentKeySetRangeRev(self):
161
+ self.c.first()
162
+ self.c.set_range(B("c"))
163
+ self.assertEqual(self.c.key(), B("d"))
164
+ test_list = [i for i in self.c.iterprev()]
165
+ test_items = [i for i in ITEMS if i[0] <= B("d")]
166
+ test_items = test_items[::-1]
167
+ self.assertEqual(self.matchList(test_list, test_items), True)
168
+
169
+ def testFromLastKeyRev(self):
170
+ self.c.last()
171
+ self.assertEqual(self.c.key(), KEYS[-1])
172
+ test_list = [i for i in self.c.iterprev()]
173
+ self.assertEqual(self.matchList(test_list, ITEMS[::-1]), True)
174
+
175
+ def testFromLastKeyWithPrevRev(self):
176
+ self.c.last()
177
+ self.assertEqual(self.c.key(), KEYS[-1]) # end of db
178
+ test_list = []
179
+ while 1:
180
+ test_list.append(self.c.item())
181
+ if not self.c.prev():
182
+ break
183
+ self.assertEqual(self.matchList(test_list, ITEMS[::-1]), True)
184
+
185
+ def testFromNonExistentKeyPastEndRev(self):
186
+ self.c.first()
187
+ self.assertEqual(self.c.key(), KEYS[0])
188
+ # prev() fails, leaving iterator in an unpositioned state.
189
+ self.c.prev()
190
+ self.assertEqual(self.c.item(), self.empty_entry)
191
+ # iterprev() from an unpositioned state proceeds from end of DB.
192
+ test_list = list(self.c.iterprev())
193
+ self.assertEqual(test_list, ITEMS[::-1])
194
+
195
+
196
+ class IterationTestWithDupsBase(unittest.TestCase):
197
+ def tearDown(self):
198
+ testlib.cleanup()
199
+
200
+ def setUp(self):
201
+ self.path, self.env = testlib.temp_env()
202
+ db = self.env.open_db(B("db1"), dupsort=True)
203
+ self.txn = self.env.begin(db, write=True)
204
+ for _ in range(2):
205
+ putData(self.txn)
206
+ self.c = self.txn.cursor()
207
+ self.empty_entry = ("", "")
208
+
209
+ def matchList(self, ls_a, ls_b):
210
+ return all(map(lambda x, y: x == y, ls_a, ls_b))
211
+
212
+
213
+ class IterationTestWithDups(IterationTestWithDupsBase):
214
+ pass
215
+
216
+
217
+ class SeekIterationTest(IterationTestBase2):
218
+ def testForwardIterationSeek(self):
219
+ self.c.first()
220
+ test_list = []
221
+ for i in self.c.iternext():
222
+ test_list.append(i)
223
+ # skips d and e
224
+ if self.c.key() == B("baa"):
225
+ self.c.set_key(B("e"))
226
+ test_item = [i for i in ITEMS2 if i[0] not in (B("d"), B("e"))]
227
+ self.assertEqual(test_list, test_item)
228
+
229
+ def testPutDuringIteration(self):
230
+ self.c.first()
231
+ test_list = []
232
+ c = self.txn.cursor()
233
+ for i in c.iternext():
234
+ test_list.append(i)
235
+ # adds 'i' upon seeing 'e'
236
+ if c.key() == B("e"):
237
+ self.c.put(B("i"), B(""))
238
+ test_item = ITEMS2 + [(B("i"), B(""))]
239
+ self.assertEqual(test_list, test_item)
240
+
241
+ def testDeleteDuringIteration(self):
242
+ self.c.first()
243
+ test_list = []
244
+ for i in self.c.iternext():
245
+ # deletes 'e' upon seeing it
246
+ if self.c.key() == B("e"):
247
+ # Causes 'e' to be deleted, and advances cursor to next
248
+ # element.
249
+ self.c.delete()
250
+ i = self.c.item()
251
+ test_list.append(i)
252
+
253
+ test_item = [i for i in ITEMS2 if i[0] != B("e")]
254
+ self.assertEqual(test_list, test_item)
255
+
256
+
257
+ if __name__ == "__main__":
258
+ unittest.main()
@@ -0,0 +1,70 @@
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
+ import unittest
25
+
26
+ import zlmdb.lmdb as lmdb
27
+
28
+
29
+ class PackageExportsTest(unittest.TestCase):
30
+ """
31
+ Ensure the list of exported names matches a predefined list. Designed to
32
+ ensure future interface changes to cffi.py and cpython.c don't break
33
+ consistency of "from lmdb import *".
34
+ """
35
+
36
+ def test_exports(self):
37
+ assert sorted(lmdb.__all__) == [
38
+ "BadDbiError",
39
+ "BadRslotError",
40
+ "BadTxnError",
41
+ "BadValsizeError",
42
+ "CorruptedError",
43
+ "Cursor",
44
+ "CursorFullError",
45
+ "DbsFullError",
46
+ "DiskError",
47
+ "Environment",
48
+ "Error",
49
+ "IncompatibleError",
50
+ "InvalidError",
51
+ "InvalidParameterError",
52
+ "KeyExistsError",
53
+ "LockError",
54
+ "MapFullError",
55
+ "MapResizedError",
56
+ "MemoryError",
57
+ "NotFoundError",
58
+ "PageFullError",
59
+ "PageNotFoundError",
60
+ "PanicError",
61
+ "ReadersFullError",
62
+ "ReadonlyError",
63
+ "TlsFullError",
64
+ "Transaction",
65
+ "TxnFullError",
66
+ "VersionMismatchError",
67
+ "_Database",
68
+ "enable_drop_gil",
69
+ "version",
70
+ ]
@@ -0,0 +1,188 @@
1
+ ###############################################################################
2
+ #
3
+ # The MIT License (MIT)
4
+ #
5
+ # Copyright (c) typedef int GmbH
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in
15
+ # all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ # THE SOFTWARE.
24
+ #
25
+ ###############################################################################
26
+
27
+ import random
28
+ import os
29
+ import zlmdb.lmdb as lmdb
30
+ import struct
31
+ import platform
32
+
33
+ import pytest
34
+
35
+ try:
36
+ from tempfile import TemporaryDirectory
37
+ except ImportError:
38
+ from backports.tempfile import TemporaryDirectory # type:ignore
39
+
40
+
41
+ def test_lmdb_create():
42
+ """
43
+ Test creation of LMDB database.
44
+ """
45
+ with TemporaryDirectory() as dbpath:
46
+ env = lmdb.open(dbpath)
47
+
48
+ with env.begin() as txn:
49
+ assert txn.id() == 0
50
+
51
+
52
+ def test_lmdb_insert_empty_key_raises():
53
+ """
54
+ Test that LMDB raises on inserting record with empty (bytes) key.
55
+ """
56
+ with TemporaryDirectory() as dbpath:
57
+ env = lmdb.open(dbpath)
58
+
59
+ with env.begin(write=True) as txn:
60
+ key = b""
61
+ value = random.randint(0, 2**32 - 1)
62
+ data = struct.pack("<L", value)
63
+ with pytest.raises(lmdb.BadValsizeError):
64
+ txn.put(key, data)
65
+
66
+
67
+ @pytest.mark.skipif(
68
+ platform.python_implementation() == "PyPy",
69
+ reason="behavioral difference on pypy (does not raise, but runs into traceback)",
70
+ )
71
+ def test_lmdb_insert_null_key_raises():
72
+ """
73
+ Test that LMDB raises on inserting record with NULL key.
74
+ """
75
+ with TemporaryDirectory() as dbpath:
76
+ env = lmdb.open(dbpath)
77
+
78
+ with env.begin(write=True) as txn:
79
+ key = None
80
+ value = random.randint(0, 2**32 - 1)
81
+ data = struct.pack("<L", value)
82
+ with pytest.raises(lmdb.BadValsizeError):
83
+ txn.put(key, data)
84
+
85
+
86
+ def test_lmdb_insert_empty_value_ok():
87
+ """
88
+ Test that LMDB allows to insert record with empty (bytes) value.
89
+ """
90
+ with TemporaryDirectory() as dbpath:
91
+ env = lmdb.open(dbpath)
92
+
93
+ with env.begin(write=True) as txn:
94
+ key = b"foo"
95
+ data = b""
96
+ txn.put(key, data)
97
+
98
+ with env.begin() as txn:
99
+ cursor = txn.cursor()
100
+ assert cursor.first()
101
+ assert cursor.value() == b""
102
+ total = 1
103
+ while cursor.next():
104
+ total += 1
105
+ assert total == 1
106
+
107
+
108
+ @pytest.mark.skipif(
109
+ platform.python_implementation() == "PyPy",
110
+ reason="behavioral difference on pypy (does not raise, but runs into traceback)",
111
+ )
112
+ def test_lmdb_insert_null_value_ok():
113
+ """
114
+ Test that LMDB allows to insert record with NULL value and
115
+ returns b'' value when retrieving the record (!).
116
+ """
117
+ with TemporaryDirectory() as dbpath:
118
+ env = lmdb.open(dbpath)
119
+
120
+ with env.begin(write=True) as txn:
121
+ key = b"foo"
122
+ data = None
123
+ txn.put(key, data)
124
+
125
+ with env.begin() as txn:
126
+ cursor = txn.cursor()
127
+ assert cursor.first()
128
+ assert cursor.value() == b""
129
+ total = 1
130
+ while cursor.next():
131
+ total += 1
132
+ assert total == 1
133
+
134
+
135
+ def test_lmdb_insert():
136
+ """
137
+ Test inserting and retrieving a couple of records.
138
+ """
139
+ with TemporaryDirectory() as dbpath:
140
+ n = 100
141
+ total1 = 0
142
+ env = lmdb.open(dbpath)
143
+
144
+ with env.begin(write=True) as txn:
145
+ for i in range(n):
146
+ key = "key-{}".format(i).encode("utf8")
147
+ value = random.randint(0, 2**32 - 1)
148
+ total1 += value
149
+ data = struct.pack("<L", value)
150
+ txn.put(key, data)
151
+
152
+ with env.begin() as txn:
153
+ cursor = txn.cursor()
154
+ assert cursor.first()
155
+
156
+ count = 1
157
+ total2 = struct.unpack("<L", cursor.value())[0]
158
+
159
+ while cursor.next():
160
+ count += 1
161
+ total2 += struct.unpack("<L", cursor.value())[0]
162
+
163
+ assert count == n
164
+ assert total1 == total2
165
+
166
+
167
+ def test_lmdb_delete():
168
+ """
169
+ Test inserting and deleting again a couple of records.
170
+ """
171
+ with TemporaryDirectory() as dbpath:
172
+ n = 100
173
+ env = lmdb.open(dbpath)
174
+
175
+ with env.begin(write=True) as txn:
176
+ for i in range(n):
177
+ key = "key-{}".format(i).encode("utf8")
178
+ value = os.urandom(16)
179
+ txn.put(key, value)
180
+
181
+ with env.begin(write=True) as txn:
182
+ for i in range(n):
183
+ key = "key-{}".format(i).encode("utf8")
184
+ assert txn.delete(key)
185
+
186
+ with env.begin() as txn:
187
+ cursor = txn.cursor()
188
+ assert not cursor.first()