solentware-base 5.1__tar.gz → 5.2__tar.gz

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.
Files changed (82) hide show
  1. {solentware-base-5.1/solentware_base.egg-info → solentware-base-5.2}/PKG-INFO +2 -3
  2. {solentware-base-5.1 → solentware-base-5.2}/README +1 -1
  3. {solentware-base-5.1 → solentware-base-5.2}/pyproject.toml +1 -2
  4. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/core/_database.py +71 -6
  5. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/core/_databasedu.py +105 -26
  6. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/core/_db.py +53 -51
  7. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/core/_db_tkinter.py +56 -64
  8. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/core/_dbdu.py +217 -31
  9. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/core/_dbdu_tkinter.py +305 -25
  10. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/core/_dpt.py +349 -170
  11. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/core/_lmdb.py +48 -48
  12. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/core/_lmdbdu.py +228 -24
  13. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/core/_nosql.py +46 -36
  14. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/core/_nosqldu.py +6 -11
  15. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/core/_sqlite.py +46 -46
  16. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/core/_sqlitedu.py +350 -25
  17. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/core/bytebit.py +0 -1
  18. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/core/constants.py +7 -0
  19. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/core/cursor.py +17 -7
  20. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/core/filespec.py +0 -1
  21. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/core/find.py +9 -4
  22. solentware-base-5.2/solentware_base/core/merge.py +166 -0
  23. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/core/recordset.py +251 -350
  24. solentware-base-5.2/solentware_base/core/recordsetbasecursor.py +120 -0
  25. solentware-base-5.2/solentware_base/core/recordsetcursor.py +167 -0
  26. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/core/tree.py +1 -4
  27. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/core/where.py +0 -4
  28. solentware-base-5.2/solentware_base/dptdu_database.py +234 -0
  29. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/gnudu_database.py +0 -3
  30. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/modulequery.py +0 -5
  31. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/ndbmdu_database.py +0 -3
  32. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/tools/base_3_to_4.py +4 -12
  33. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/tools/base_3_to_4_db.py +4 -14
  34. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/tools/base_3_to_4_sqlite.py +4 -12
  35. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/tools/berkeleydb_du_splice_fix.py +3 -4
  36. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/tools/compare_dpt_tape_files.py +0 -1
  37. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/tools/create_database.py +0 -1
  38. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/tools/sqlite_table_name_upgrade.py +0 -1
  39. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/tools/traverse_dpt_tape_files.py +0 -1
  40. {solentware-base-5.1 → solentware-base-5.2/solentware_base.egg-info}/PKG-INFO +2 -3
  41. {solentware-base-5.1 → solentware-base-5.2}/solentware_base.egg-info/SOURCES.txt +3 -2
  42. solentware-base-5.1/solentware_base/core/archivedu.py +0 -226
  43. solentware-base-5.1/solentware_base/core/archivedudpt.py +0 -117
  44. solentware-base-5.1/solentware_base/dptdu_database.py +0 -118
  45. {solentware-base-5.1 → solentware-base-5.2}/LICENCE +0 -0
  46. {solentware-base-5.1 → solentware-base-5.2}/MANIFEST.in +0 -0
  47. {solentware-base-5.1 → solentware-base-5.2}/setup.cfg +0 -0
  48. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/__init__.py +0 -0
  49. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/apsw_database.py +0 -0
  50. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/apswdu_database.py +0 -0
  51. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/berkeleydb_database.py +0 -0
  52. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/berkeleydbdu_database.py +0 -0
  53. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/bsddb3_database.py +0 -0
  54. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/bsddb3du_database.py +0 -0
  55. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/core/__init__.py +0 -0
  56. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/core/_bytebit.py +0 -0
  57. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/core/findvalues.py +0 -0
  58. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/core/record.py +0 -0
  59. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/core/segmentsize.py +0 -0
  60. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/core/wherevalues.py +0 -0
  61. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/db_tcl.py +0 -0
  62. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/db_tkinter_database.py +0 -0
  63. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/db_tkinterdu_database.py +0 -0
  64. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/dpt_database.py +0 -0
  65. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/dptfastload_database.py +0 -0
  66. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/gnu_database.py +0 -0
  67. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/gnu_module.py +0 -0
  68. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/lmdb_database.py +0 -0
  69. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/lmdbdu_database.py +0 -0
  70. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/ndbm_database.py +0 -0
  71. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/ndbm_module.py +0 -0
  72. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/sqlite3_database.py +0 -0
  73. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/sqlite3du_database.py +0 -0
  74. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/tools/__init__.py +0 -0
  75. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/tools/ui_base_3_to_4.py +0 -0
  76. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/unqlite_database.py +0 -0
  77. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/unqlitedu_database.py +0 -0
  78. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/vedis_database.py +0 -0
  79. {solentware-base-5.1 → solentware-base-5.2}/solentware_base/vedisdu_database.py +0 -0
  80. {solentware-base-5.1 → solentware-base-5.2}/solentware_base.egg-info/dependency_links.txt +0 -0
  81. {solentware-base-5.1 → solentware-base-5.2}/solentware_base.egg-info/top_level.txt +0 -0
  82. {solentware-base-5.1 → solentware-base-5.2}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: solentware-base
3
- Version: 5.1
3
+ Version: 5.2
4
4
  Summary: Bitmapped record number databases.
5
5
  Author-email: Roger Marsh <roger.marsh@solentware.co.uk>
6
6
  License: Licence for solentware_base package
@@ -23,7 +23,6 @@ Project-URL: Homepage, http://www.solentware.co.uk
23
23
  Project-URL: Repository, https://github.com/RogerMarsh/solentware-base.git
24
24
  Project-URL: Bug Tracker, https://github.com/RogerMarsh/solentware-base/issues
25
25
  Classifier: License :: OSI Approved :: BSD License
26
- Classifier: Programming Language :: Python :: 3.7
27
26
  Classifier: Programming Language :: Python :: 3.8
28
27
  Classifier: Programming Language :: Python :: 3.9
29
28
  Classifier: Programming Language :: Python :: 3.10
@@ -48,7 +47,7 @@ A record number database is seen as a set of fixed size segments, and an existen
48
47
 
49
48
  Where an index value refers to many records in a segment these references are encoded as a bitmap or a list of record numbers, in a single <index value> : <bitmap or list> record, rather than as many <index value> : <record number> records. When 'many' is small a list is favoured to save the space needed for large numbers of unset bits.
50
49
 
51
- The interfaces provided by the apsw, berkeleydb, bsddb3, dptdb, and sqlite3, Python modules are supported.
50
+ The interfaces provided by the apsw, berkeleydb, bsddb3, dptdb, lmdb, and sqlite3, Python modules are supported.
52
51
 
53
52
  The interfaces provided by the dbm.gnu, dbm.ndbm, unqlite, and vedis, Python modules are available for experiment.
54
53
 
@@ -12,7 +12,7 @@ A record number database is seen as a set of fixed size segments, and an existen
12
12
 
13
13
  Where an index value refers to many records in a segment these references are encoded as a bitmap or a list of record numbers, in a single <index value> : <bitmap or list> record, rather than as many <index value> : <record number> records. When 'many' is small a list is favoured to save the space needed for large numbers of unset bits.
14
14
 
15
- The interfaces provided by the apsw, berkeleydb, bsddb3, dptdb, and sqlite3, Python modules are supported.
15
+ The interfaces provided by the apsw, berkeleydb, bsddb3, dptdb, lmdb, and sqlite3, Python modules are supported.
16
16
 
17
17
  The interfaces provided by the dbm.gnu, dbm.ndbm, unqlite, and vedis, Python modules are available for experiment.
18
18
 
@@ -10,14 +10,13 @@ line-length = 79
10
10
 
11
11
  [project]
12
12
  name = "solentware-base"
13
- version = "5.1"
13
+ version = "5.2"
14
14
  authors = [
15
15
  { name="Roger Marsh", email="roger.marsh@solentware.co.uk" },
16
16
  ]
17
17
  description = "Bitmapped record number databases."
18
18
  classifiers = [
19
19
  "License :: OSI Approved :: BSD License",
20
- "Programming Language :: Python :: 3.7",
21
20
  "Programming Language :: Python :: 3.8",
22
21
  "Programming Language :: Python :: 3.9",
23
22
  "Programming Language :: Python :: 3.10",
@@ -24,6 +24,10 @@ from .recordset import (
24
24
  )
25
25
 
26
26
 
27
+ class DataSourceCursorError(Exception):
28
+ """Exception for database-specific DataSourceCursor setting."""
29
+
30
+
27
31
  # The Database class was added to the ignored-classes list of pylint.conf for
28
32
  # solentware_base at a time when all the no-member errors (E1101) referred
29
33
  # to it or one of it's subclasses with the same name in another module.
@@ -37,9 +41,7 @@ class Database:
37
41
  Property file_per_database returns False because all 'key:value' sets,
38
42
  and the associated inverted list indicies, are held in one file by
39
43
  default. Berkeley DB is the known example where it is reasonable to
40
- return True: override the class attribute, and the archive() and
41
- delete_archive() methods in .archivedu.Archivedu class, if this case
42
- is wanted.
44
+ return True: override the class attribute, if this case is wanted.
43
45
 
44
46
  """
45
47
 
@@ -133,7 +135,6 @@ class Database:
133
135
  self.delete(dbset, oldkey, instance.srvalue)
134
136
  key = self.put(dbset, newkey, instance.newrecord.srvalue)
135
137
  if key is not None:
136
-
137
138
  # put was append to record number database and
138
139
  # returned the new primary key. Adjust record key
139
140
  # for secondary updates.
@@ -223,7 +224,6 @@ class Database:
223
224
  putkey = instance.key.pack()
224
225
  instance.set_packed_value_and_indexes()
225
226
  if putkey is None:
226
-
227
227
  # reuse record number if possible
228
228
  putkey = self.get_lowest_freed_record_number(dbset)
229
229
  if putkey is not None:
@@ -231,7 +231,6 @@ class Database:
231
231
 
232
232
  key = self.put(dbset, putkey, instance.srvalue)
233
233
  if key is not None:
234
-
235
234
  # put was append to record number database and
236
235
  # returned the new primary key. Adjust record key
237
236
  # for secondary updates.
@@ -379,6 +378,72 @@ class Database:
379
378
  """Do nothing, present for compatibility with DPT."""
380
379
  del kwargs
381
380
 
381
+ def get_database_table_sizes(self, **kwargs):
382
+ """Return empty dict, present for compatibility with DPT."""
383
+ del kwargs
384
+ return {}
385
+
386
+ def close_datasourcecursor_recordset(self, datasourcecursor):
387
+ """Do nothing. Override if necessary.
388
+
389
+ Present for compatibility with DPT database engine.
390
+ """
391
+ del datasourcecursor
392
+
393
+ def set_datasourcecursor_recordset(self, datasourcecursor, recordset):
394
+ """Validate and set recordset as datasourcecursor's recordset.
395
+
396
+ The recordset and datasourcecursor must be associated with the same
397
+ database identity and be the identity of self.
398
+
399
+ This method exists because in DPT these checks are implied when the
400
+ recordset and cursor are created, and both objects are details of
401
+ the implementation.
402
+ """
403
+ if datasourcecursor.dbhome is not self:
404
+ raise DataSourceCursorError("DataSource is not for this database")
405
+ if datasourcecursor.recordset:
406
+ if (
407
+ datasourcecursor.recordset.dbidentity
408
+ == recordset.recordset.dbidentity
409
+ ):
410
+ datasourcecursor.recordset.recordset.close()
411
+ datasourcecursor.recordset = recordset
412
+ else:
413
+ raise DataSourceCursorError(
414
+ "New and existing Recordsets are for different databases"
415
+ )
416
+ elif datasourcecursor.dbidentity == recordset.recordset.dbidentity:
417
+ datasourcecursor.recordset = recordset
418
+ else:
419
+ raise DataSourceCursorError(
420
+ "New Recordset and DataSource are for different databases"
421
+ )
422
+
423
+ def get_datasourcecursor_recordset_cursor(self, dsc):
424
+ """Create and return cursor on dsc's recordset.
425
+
426
+ dsc not datasourcecursor to shorten argument name.
427
+
428
+ """
429
+ if dsc.dbhome is not self:
430
+ raise DataSourceCursorError("DataSource is not for this database")
431
+ if dsc.recordset:
432
+ if dsc.dbidentity == dsc.recordset.recordset.dbidentity:
433
+ cursor = dsc.recordset.dbhome.create_recordset_cursor(
434
+ dsc.recordset.recordset
435
+ )
436
+ else:
437
+ raise DataSourceCursorError(
438
+ "Recordset and DataSource are for different databases"
439
+ )
440
+ else:
441
+ dsc.recordset = dsc.dbhome.recordlist_nil(dsc.dbset)
442
+ cursor = dsc.recordset.dbhome.create_recordset_cursor(
443
+ dsc.recordset.recordset
444
+ )
445
+ return cursor
446
+
382
447
 
383
448
  class ExistenceBitmapControl:
384
449
  """Base class for managing existence bitmap of file in database.
@@ -13,6 +13,7 @@ from . import _database
13
13
  from .segmentsize import SegmentSize
14
14
  from .constants import SECONDARY
15
15
  from .bytebit import Bitarray
16
+ from . import merge
16
17
 
17
18
 
18
19
  class DatabaseduError(Exception):
@@ -22,25 +23,6 @@ class DatabaseduError(Exception):
22
23
  class Database(_database.Database):
23
24
  """Provide deferred update versions of the record update methods."""
24
25
 
25
- # Deferred updates are done inside transactions if possible, despite
26
- # it being slower, so backup should not be necessary in most cases.
27
- # Where deferred update within transactions is not available, or is
28
- # wanted perhaps, a subclass of _databasedu.Database should override
29
- # this attribute to 'True'.
30
- # This and it's property, and methods archive and delete_archive are
31
- # duplicated in the dptdu_database.Database hierarchy: enough for a
32
- # shared superclass for default backup stuff.
33
- _take_backup_before_deferred_update = False
34
-
35
- @property
36
- def take_backup_before_deferred_update(self):
37
- """Return True if temporary backups should protect deferred update.
38
-
39
- It is expected the archive and delete_archive methods will do this.
40
-
41
- """
42
- return self._take_backup_before_deferred_update
43
-
44
26
  def put_instance(self, dbset, instance):
45
27
  """Put new instance on database dbset.
46
28
 
@@ -66,7 +48,7 @@ class Database(_database.Database):
66
48
  instance.srkey = self.encode_record_number(putkey)
67
49
  srindex = instance.srindex
68
50
  segment, record_number = divmod(putkey, SegmentSize.db_segment_size)
69
- self.defer_add_record_to_ebm(dbset, segment, record_number)
51
+ self._defer_add_record_to_ebm(dbset, segment, record_number)
70
52
  pcb = instance.putcallbacks
71
53
  for secondary in srindex:
72
54
  if secondary not in self.specification[dbset][SECONDARY]:
@@ -74,12 +56,12 @@ class Database(_database.Database):
74
56
  pcb[secondary](instance, srindex[secondary])
75
57
  continue
76
58
  for j in srindex[secondary]:
77
- self.defer_add_record_to_field_value(
59
+ self._defer_add_record_to_field_value(
78
60
  dbset, secondary, j, segment, record_number
79
61
  )
80
62
 
81
63
  if record_number in self.deferred_update_points:
82
- self.write_existence_bit_map(dbset, segment)
64
+ self._write_existence_bit_map(dbset, segment)
83
65
  for secondary in self.specification[dbset][SECONDARY]:
84
66
  self.sort_and_write(dbset, secondary, segment)
85
67
  if record_number == max(self.deferred_update_points):
@@ -88,7 +70,45 @@ class Database(_database.Database):
88
70
  self.first_chunk[dbset] = False
89
71
  self.high_segment[dbset] = segment
90
72
 
91
- def defer_add_record_to_ebm(self, file, segment, record_number):
73
+ def index_instance(self, dbset, instance):
74
+ """Apply instance index values on database dbset.
75
+
76
+ This method assumes all primary databases are integer primary key,
77
+ and there is enough memory to do a segment at a time.
78
+
79
+ """
80
+ putkey = instance.key.pack()
81
+ if putkey is None:
82
+ # Apply index values without record number is not allowed.
83
+ raise DatabaseduError(
84
+ "Cannot apply index values without a record number"
85
+ )
86
+ instance.set_packed_value_and_indexes()
87
+ instance.srkey = self.encode_record_number(putkey)
88
+ srindex = instance.srindex
89
+ segment, record_number = divmod(putkey, SegmentSize.db_segment_size)
90
+ pcb = instance.putcallbacks
91
+ for secondary in srindex:
92
+ if secondary not in self.specification[dbset][SECONDARY]:
93
+ if secondary in pcb:
94
+ pcb[secondary](instance, srindex[secondary])
95
+ continue
96
+ for j in srindex[secondary]:
97
+ self._defer_add_record_to_field_value(
98
+ dbset, secondary, j, segment, record_number
99
+ )
100
+
101
+ if record_number in self.deferred_update_points:
102
+ for secondary in srindex:
103
+ assert secondary in self.specification[dbset][SECONDARY]
104
+ self.sort_and_write(dbset, secondary, segment)
105
+ if record_number == max(self.deferred_update_points):
106
+ self.first_chunk[dbset] = True
107
+ elif record_number == min(self.deferred_update_points):
108
+ self.first_chunk[dbset] = False
109
+ self.high_segment[dbset] = segment
110
+
111
+ def _defer_add_record_to_ebm(self, file, segment, record_number):
92
112
  """Add bit to existence bit map for new record and defer update."""
93
113
  assert file in self.specification
94
114
  try:
@@ -110,7 +130,7 @@ class Database(_database.Database):
110
130
  self.existence_bit_maps[file] = {}
111
131
  self.existence_bit_maps[file][segment] = ebm
112
132
 
113
- def defer_add_record_to_field_value(
133
+ def _defer_add_record_to_field_value(
114
134
  self, file, field, key, segment, record_number
115
135
  ):
116
136
  """Add record_number to cached segment for key."""
@@ -126,7 +146,6 @@ class Database(_database.Database):
126
146
  if values is None:
127
147
  value_segments[key] = [record_number]
128
148
  elif isinstance(values, list):
129
-
130
149
  # A (value, record_number) can be given many times.
131
150
  # Ensure a record_number appears in the list once only.
132
151
  if values[-1] != record_number:
@@ -159,7 +178,6 @@ class Database(_database.Database):
159
178
  for k in segvalues:
160
179
  value = segvalues[k]
161
180
  if isinstance(value, list):
162
-
163
181
  # A single record is presented as an integer: the
164
182
  # database engine will decide the transformation.
165
183
  if len(value) == 1:
@@ -191,3 +209,64 @@ class Database(_database.Database):
191
209
  Actions are specific to a database engine.
192
210
 
193
211
  """
212
+
213
+ def encode_number_for_sequential_file_dump(self, number, bytes_):
214
+ """Return big-endian encoding of number in bytes_ bytes."""
215
+ return number.to_bytes(bytes_, byteorder="big")
216
+
217
+ def encode_segment_for_sequential_file_dump(self, record_numbers):
218
+ """Return encoding of record numbers appropriate to record count."""
219
+ if len(record_numbers) > SegmentSize.db_upper_conversion_limit:
220
+ seg = SegmentSize.empty_bitarray.copy()
221
+ for bit in record_numbers:
222
+ seg[bit] = True
223
+ return seg.tobytes()
224
+ int_to_bytes = self._int_to_bytes
225
+ return b"".join([int_to_bytes[n] for n in record_numbers])
226
+
227
+ def set_int_to_bytes_lookup(self, lookup=True):
228
+ """Create lookup to convert integers to bytestring big-endian.
229
+
230
+ Set lookup table to None if bool(lookup) is False.
231
+
232
+ """
233
+ if lookup:
234
+ self._int_to_bytes = [
235
+ n.to_bytes(2, byteorder="big")
236
+ for n in range(SegmentSize.db_segment_size)
237
+ ]
238
+ else:
239
+ self._int_to_bytes = None
240
+
241
+ def merge_import(self, index_directory, file, field, commit_limit):
242
+ """Yield count of sorted items written to an index at intervals.
243
+
244
+ Sorted items from files in index_directory are written to index
245
+ field in file, and the count of items added is yielded every
246
+ commit_limit items.
247
+
248
+ """
249
+ writer = self.merge_writer(file, field)
250
+ merger = merge.Merge(index_directory)
251
+ commit_count = None
252
+ for commit_count, item in enumerate(merger.sorter()):
253
+ if not commit_count % commit_limit:
254
+ if commit_count:
255
+ writer.close_cursor()
256
+ self.commit()
257
+ self.deferred_update_housekeeping()
258
+ yield commit_count
259
+ self.start_transaction()
260
+ writer.make_new_cursor()
261
+ writer.write(item)
262
+ writer.close_cursor()
263
+ if commit_count is not None:
264
+ self.commit()
265
+ self.deferred_update_housekeeping()
266
+ self.start_transaction()
267
+
268
+ def next_sorted_item(self, index_directory):
269
+ """Yield sorted items from files in index_directory."""
270
+ merger = merge.Merge(index_directory)
271
+ for item in merger.sorter():
272
+ yield item