TSVZ 4.5__tar.gz → 4.6__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.
- {tsvz-4.5 → tsvz-4.6}/PKG-INFO +2 -2
- {tsvz-4.5 → tsvz-4.6}/README.md +1 -1
- {tsvz-4.5 → tsvz-4.6}/TSVZ.egg-info/PKG-INFO +2 -2
- {tsvz-4.5 → tsvz-4.6}/TSVZ.py +161 -28
- {tsvz-4.5 → tsvz-4.6}/TSVZ.egg-info/SOURCES.txt +0 -0
- {tsvz-4.5 → tsvz-4.6}/TSVZ.egg-info/dependency_links.txt +0 -0
- {tsvz-4.5 → tsvz-4.6}/TSVZ.egg-info/entry_points.txt +0 -0
- {tsvz-4.5 → tsvz-4.6}/TSVZ.egg-info/requires.txt +0 -0
- {tsvz-4.5 → tsvz-4.6}/TSVZ.egg-info/top_level.txt +0 -0
- {tsvz-4.5 → tsvz-4.6}/setup.cfg +0 -0
- {tsvz-4.5 → tsvz-4.6}/setup.py +0 -0
{tsvz-4.5 → tsvz-4.6}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: TSVZ
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.6
|
|
4
4
|
Summary: Key-value store kept in a TSV / CSV / NSV / PSV file, with tsvz-spec-v1 support
|
|
5
5
|
Home-page: https://github.com/yufei-pan/TSVZ
|
|
6
6
|
Author: Yufei Pan
|
|
@@ -34,7 +34,7 @@ key-value store in a delimiter-separated text file**. The first column of each
|
|
|
34
34
|
row is the key; the file behaves like an ordered dictionary that is persisted to
|
|
35
35
|
disk as you change it.
|
|
36
36
|
|
|
37
|
-
**Version 4.
|
|
37
|
+
**Version 4.6**, Python **3.6+**, one self-contained module (`TSVZ.py`): drop it
|
|
38
38
|
next to your code and `import TSVZ`, or install it.
|
|
39
39
|
|
|
40
40
|
```bash
|
{tsvz-4.5 → tsvz-4.6}/README.md
RENAMED
|
@@ -7,7 +7,7 @@ key-value store in a delimiter-separated text file**. The first column of each
|
|
|
7
7
|
row is the key; the file behaves like an ordered dictionary that is persisted to
|
|
8
8
|
disk as you change it.
|
|
9
9
|
|
|
10
|
-
**Version 4.
|
|
10
|
+
**Version 4.6**, Python **3.6+**, one self-contained module (`TSVZ.py`): drop it
|
|
11
11
|
next to your code and `import TSVZ`, or install it.
|
|
12
12
|
|
|
13
13
|
```bash
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: TSVZ
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.6
|
|
4
4
|
Summary: Key-value store kept in a TSV / CSV / NSV / PSV file, with tsvz-spec-v1 support
|
|
5
5
|
Home-page: https://github.com/yufei-pan/TSVZ
|
|
6
6
|
Author: Yufei Pan
|
|
@@ -34,7 +34,7 @@ key-value store in a delimiter-separated text file**. The first column of each
|
|
|
34
34
|
row is the key; the file behaves like an ordered dictionary that is persisted to
|
|
35
35
|
disk as you change it.
|
|
36
36
|
|
|
37
|
-
**Version 4.
|
|
37
|
+
**Version 4.6**, Python **3.6+**, one self-contained module (`TSVZ.py`): drop it
|
|
38
38
|
next to your code and `import TSVZ`, or install it.
|
|
39
39
|
|
|
40
40
|
```bash
|
{tsvz-4.5 → tsvz-4.6}/TSVZ.py
RENAMED
|
@@ -40,7 +40,7 @@ if os.name == 'nt':
|
|
|
40
40
|
elif os.name == 'posix':
|
|
41
41
|
import fcntl
|
|
42
42
|
|
|
43
|
-
version = '4.
|
|
43
|
+
version = '4.6'
|
|
44
44
|
__version__ = version
|
|
45
45
|
author = 'pan@zopyr.us'
|
|
46
46
|
COMMIT_DATE = '2026-10-12'
|
|
@@ -179,13 +179,18 @@ def get_terminal_size():
|
|
|
179
179
|
try:
|
|
180
180
|
import os
|
|
181
181
|
_tsize = os.get_terminal_size()
|
|
182
|
+
if not (_tsize[0] and _tsize[1]):
|
|
183
|
+
raise OSError('the terminal has no size set')
|
|
182
184
|
except Exception:
|
|
183
185
|
try:
|
|
184
186
|
import fcntl
|
|
185
187
|
import struct
|
|
186
188
|
import termios
|
|
187
189
|
packed = fcntl.ioctl(0, termios.TIOCGWINSZ, struct.pack('HHHH', 0, 0, 0, 0))
|
|
188
|
-
|
|
190
|
+
rows, columns = struct.unpack('HHHH', packed)[:2]
|
|
191
|
+
if not (rows and columns):
|
|
192
|
+
raise OSError('the terminal has no size set')
|
|
193
|
+
_tsize = (columns, rows)
|
|
189
194
|
except Exception:
|
|
190
195
|
import shutil
|
|
191
196
|
_tsize = shutil.get_terminal_size(fallback=(240, 50))
|
|
@@ -1654,6 +1659,34 @@ def _specFormatRecord(cells, delimiter, marker=False):
|
|
|
1654
1659
|
return delimiter.join([first] + [_specEncodeField(cell, delimiter) for cell in cells[1:]])
|
|
1655
1660
|
|
|
1656
1661
|
|
|
1662
|
+
def _specTombstones(keys, delimiter, strip):
|
|
1663
|
+
"""``(cells, marker)`` records that delete ``keys`` (spec §9.2) from a store whose stripping is ``strip``.
|
|
1664
|
+
|
|
1665
|
+
While stripping is on a reader strips a tombstone's key too (spec §9.1): the
|
|
1666
|
+
tombstone of a key whose encoded form ends in a space or tab, stored while
|
|
1667
|
+
stripping was off, would delete the stripped key instead. A batch holding
|
|
1668
|
+
one is written with stripping off, and stripping is turned back on after it
|
|
1669
|
+
(spec §9.6).
|
|
1670
|
+
"""
|
|
1671
|
+
rows = [([key], False) for key in keys]
|
|
1672
|
+
if strip and any(key.endswith((' ', '\t')) and _specEncodeField(key, delimiter, isKey=True).endswith((' ', '\t'))
|
|
1673
|
+
for key in keys):
|
|
1674
|
+
rows.insert(0, (['#_strip_trailing_whites_#', 'false'], True))
|
|
1675
|
+
rows.append((['#_strip_trailing_whites_#', 'true'], True))
|
|
1676
|
+
return rows
|
|
1677
|
+
|
|
1678
|
+
|
|
1679
|
+
def _specFormatRecords(rows, delimiter):
|
|
1680
|
+
"""``rows`` (``[(cells, marker), ...]``) as record lines, each with its '\\n'."""
|
|
1681
|
+
return ''.join(_specFormatRecord(cells, delimiter, marker=marker) + '\n' for cells, marker in rows)
|
|
1682
|
+
|
|
1683
|
+
|
|
1684
|
+
def _specTombstoneLines(key, delimiter, strip):
|
|
1685
|
+
"""The tombstone of ``key`` (spec §9.6) as lines joined by '\\n', without a last '\\n'."""
|
|
1686
|
+
return '\n'.join(_specFormatRecord(cells, delimiter, marker=marker)
|
|
1687
|
+
for cells, marker in _specTombstones([key], delimiter, strip))
|
|
1688
|
+
|
|
1689
|
+
|
|
1657
1690
|
class _SpecState(object):
|
|
1658
1691
|
"""Reader state of spec §12, carried across records and parts."""
|
|
1659
1692
|
|
|
@@ -2604,16 +2637,23 @@ def _specBackupPart(path, reporter):
|
|
|
2604
2637
|
_lockFile(f)
|
|
2605
2638
|
try:
|
|
2606
2639
|
first = backup = '{}.damaged-{}'.format(path, time.strftime('%Y%m%dT%H%M%S'))
|
|
2640
|
+
# Readable by whoever could read the part, writable by its owner only, never setuid,
|
|
2641
|
+
# setgid, sticky or executable (the umask still applies on top).
|
|
2642
|
+
st = os.fstat(f.fileno())
|
|
2643
|
+
mode = st.st_mode & 0o644
|
|
2607
2644
|
suffix = 0
|
|
2608
2645
|
while True:
|
|
2609
2646
|
try:
|
|
2610
|
-
|
|
2647
|
+
# never overwrite an earlier backup; O_BINARY: no newline translation on Windows
|
|
2648
|
+
flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, 'O_BINARY', 0)
|
|
2649
|
+
copy = os.fdopen(os.open(backup, flags, mode), 'wb')
|
|
2611
2650
|
break
|
|
2612
2651
|
except FileExistsError:
|
|
2613
2652
|
suffix += 1
|
|
2614
2653
|
backup = '{}.{}'.format(first, suffix)
|
|
2615
2654
|
try:
|
|
2616
2655
|
with copy:
|
|
2656
|
+
_matchOwner(copy.fileno(), st)
|
|
2617
2657
|
shutil.copyfileobj(f, copy)
|
|
2618
2658
|
copy.flush()
|
|
2619
2659
|
os.fsync(copy.fileno())
|
|
@@ -2672,6 +2712,22 @@ def _specSnapshot(fileName):
|
|
|
2672
2712
|
return parts, None
|
|
2673
2713
|
|
|
2674
2714
|
|
|
2715
|
+
def _matchOwner(fd, st):
|
|
2716
|
+
"""Give a new copy the owner and group of the file ``st`` describes. Where that is not allowed,
|
|
2717
|
+
a copy left in another group loses its group bits, so no one reads it who could not read the file."""
|
|
2718
|
+
if not hasattr(os, 'fchown'):
|
|
2719
|
+
return
|
|
2720
|
+
for uid in (st.st_uid, -1):
|
|
2721
|
+
try:
|
|
2722
|
+
os.fchown(fd, uid, st.st_gid)
|
|
2723
|
+
return
|
|
2724
|
+
except OSError:
|
|
2725
|
+
pass
|
|
2726
|
+
created = os.fstat(fd)
|
|
2727
|
+
if created.st_gid != st.st_gid:
|
|
2728
|
+
os.fchmod(fd, created.st_mode & 0o7707) # what the umask left, without the group bits
|
|
2729
|
+
|
|
2730
|
+
|
|
2675
2731
|
def _specRewriteInPlace(path, content, expect=None, memberSize=None):
|
|
2676
2732
|
"""Replace a part's bytes in place (same inode, spec §18.7 / §19.8).
|
|
2677
2733
|
|
|
@@ -2691,18 +2747,73 @@ def _specRewriteInPlace(path, content, expect=None, memberSize=None):
|
|
|
2691
2747
|
st = os.fstat(f.fileno())
|
|
2692
2748
|
if expect is not None and _specStamp(st) != expect:
|
|
2693
2749
|
return None
|
|
2694
|
-
|
|
2750
|
+
fd = f.fileno()
|
|
2751
|
+
# The whole part, for a failed rewrite to put back whenever it fails. Read unbuffered, as
|
|
2752
|
+
# the file is written below: a read-ahead in f would leave it a stale position to close at.
|
|
2753
|
+
old = _readFrom0(fd, st.st_size)
|
|
2754
|
+
if old == raw:
|
|
2695
2755
|
return False
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2756
|
+
try:
|
|
2757
|
+
_reserveSpace(fd, len(raw)) # spec §19.8: a full disk refuses the rewrite before any byte changes
|
|
2758
|
+
_writeFrom0(fd, raw)
|
|
2759
|
+
except BaseException as e:
|
|
2760
|
+
# Put the old bytes back rather than leave a half-rewritten part (or one a failed
|
|
2761
|
+
# reservation lengthened).
|
|
2762
|
+
try:
|
|
2763
|
+
_writeFrom0(fd, old)
|
|
2764
|
+
os.fsync(fd)
|
|
2765
|
+
except OSError as undo:
|
|
2766
|
+
damaged = '{}: the rewrite failed ({!r}) and the old bytes could not be put back ({}): the part is damaged'.format(
|
|
2767
|
+
path, e, undo)
|
|
2768
|
+
if isinstance(e, OSError):
|
|
2769
|
+
raise OSError(e.errno, damaged)
|
|
2770
|
+
_warn('TSVZ warning: ' + damaged) # an interrupt stays an interrupt
|
|
2771
|
+
raise
|
|
2772
|
+
os.fsync(fd)
|
|
2701
2773
|
return True
|
|
2702
2774
|
finally:
|
|
2775
|
+
try:
|
|
2776
|
+
_seekRawStart(f) # msvcrt unlocks from the raw position: the range it locked
|
|
2777
|
+
except OSError:
|
|
2778
|
+
pass
|
|
2703
2779
|
_unlockFile(f)
|
|
2704
2780
|
|
|
2705
2781
|
|
|
2782
|
+
def _reserveSpace(fd, length):
|
|
2783
|
+
"""Reserve ``length`` bytes from the start of the file before it is rewritten in place (spec
|
|
2784
|
+
§19.8). Where the platform or the file system cannot reserve space, nothing is reserved."""
|
|
2785
|
+
fallocate = getattr(os, 'posix_fallocate', None)
|
|
2786
|
+
if fallocate is None or length <= 0:
|
|
2787
|
+
return
|
|
2788
|
+
try:
|
|
2789
|
+
fallocate(fd, 0, length)
|
|
2790
|
+
except OSError as e:
|
|
2791
|
+
if e.errno not in (errno.EOPNOTSUPP, getattr(errno, 'ENOTSUP', errno.EOPNOTSUPP), errno.ENOSYS, errno.EINVAL):
|
|
2792
|
+
raise
|
|
2793
|
+
|
|
2794
|
+
|
|
2795
|
+
def _readFrom0(fd, length):
|
|
2796
|
+
"""Up to ``length`` bytes from the start of the file (unbuffered)."""
|
|
2797
|
+
os.lseek(fd, 0, os.SEEK_SET)
|
|
2798
|
+
chunks = []
|
|
2799
|
+
while length > 0:
|
|
2800
|
+
chunk = os.read(fd, length)
|
|
2801
|
+
if not chunk:
|
|
2802
|
+
break
|
|
2803
|
+
chunks.append(chunk)
|
|
2804
|
+
length -= len(chunk)
|
|
2805
|
+
return b''.join(chunks)
|
|
2806
|
+
|
|
2807
|
+
|
|
2808
|
+
def _writeFrom0(fd, data):
|
|
2809
|
+
"""Write ``data`` over the file from offset 0 and cut the file there (unbuffered)."""
|
|
2810
|
+
os.lseek(fd, 0, os.SEEK_SET)
|
|
2811
|
+
view = memoryview(data)
|
|
2812
|
+
while view:
|
|
2813
|
+
view = view[os.write(fd, view):]
|
|
2814
|
+
os.ftruncate(fd, len(data))
|
|
2815
|
+
|
|
2816
|
+
|
|
2706
2817
|
def _specPreamble(state, delimiter, compensate=False, version=True):
|
|
2707
2818
|
"""Marker lines that re-create ``state`` (spec §19.2.3a): version, non-default markers, defaults last.
|
|
2708
2819
|
|
|
@@ -2905,7 +3016,7 @@ def _specClearTabularFile(fileName, teeLogger=None, header='', verifyHeader=Fals
|
|
|
2905
3016
|
attempt.note('refused', None, 'clear skipped: a part of the store could not be read; nothing was written')
|
|
2906
3017
|
return False
|
|
2907
3018
|
if len(load.parts) > 1:
|
|
2908
|
-
tombstones =
|
|
3019
|
+
tombstones = _specFormatRecords(_specTombstones(load.data, delimiter, load.state.strip), delimiter)
|
|
2909
3020
|
if tombstones:
|
|
2910
3021
|
_specAppendPayload(load.active, tombstones.encode('utf-8'), attempt, memberSize=load.state.memberSize)
|
|
2911
3022
|
attempt.note('multipart', None, 'cleared a multi-part store by appending tombstones; older parts are not compacted')
|
|
@@ -3558,8 +3669,8 @@ class TSVZed(OrderedDict):
|
|
|
3558
3669
|
# the mapping by the caller, so self[key] must not be accessed.
|
|
3559
3670
|
self.dirty = True
|
|
3560
3671
|
if self._spec:
|
|
3561
|
-
# Spec §9.2: a tombstone is the lone key, never padded.
|
|
3562
|
-
self.appendQueue.append(
|
|
3672
|
+
# Spec §9.2: a tombstone is the lone key, never padded. One queue item, so it is committed whole.
|
|
3673
|
+
self.appendQueue.append(_specTombstoneLines(key, self.delimiter, self._specState.strip))
|
|
3563
3674
|
return self
|
|
3564
3675
|
if self.correctColumnNum > 1:
|
|
3565
3676
|
emptyLine = [key]+['']*(self.correctColumnNum-1)
|
|
@@ -3781,7 +3892,15 @@ memoryOnly:{self.memoryOnly}
|
|
|
3781
3892
|
return self.close()
|
|
3782
3893
|
|
|
3783
3894
|
def popitem(self, last=True):
|
|
3784
|
-
|
|
3895
|
+
if sys.version_info >= (3, 11):
|
|
3896
|
+
key, value = OrderedDict.popitem(self, last)
|
|
3897
|
+
else:
|
|
3898
|
+
# Before Python 3.11 OrderedDict.popitem goes through self[key] and del self[key], which strip the key.
|
|
3899
|
+
if not OrderedDict.__len__(self):
|
|
3900
|
+
raise KeyError('dictionary is empty')
|
|
3901
|
+
key = next(OrderedDict.__reversed__(self)) if last else next(OrderedDict.__iter__(self))
|
|
3902
|
+
value = OrderedDict.__getitem__(self, key)
|
|
3903
|
+
OrderedDict.__delitem__(self, key)
|
|
3785
3904
|
if not self.memoryOnly:
|
|
3786
3905
|
self.__appendEmptyLine(key)
|
|
3787
3906
|
self.lastUpdateTime = get_time_ns()
|
|
@@ -4486,7 +4605,7 @@ class TSVZedLite(MutableMapping):
|
|
|
4486
4605
|
self._specWrite(_specFormatRecord(value, self.delimiter, marker=True))
|
|
4487
4606
|
|
|
4488
4607
|
def _specWrite(self, line):
|
|
4489
|
-
"""Append one record to the active part; return its offset, or None for a compressed part."""
|
|
4608
|
+
"""Append one record (or lines written together) to the active part; return its offset, or None for a compressed part."""
|
|
4490
4609
|
data = (line + '\n').encode('utf-8', errors='replace')
|
|
4491
4610
|
if self.fileObj is None:
|
|
4492
4611
|
reporter = _Reporter(self._activePath)
|
|
@@ -4659,7 +4778,7 @@ class TSVZedLite(MutableMapping):
|
|
|
4659
4778
|
|
|
4660
4779
|
def __mapDeleteToFile(self,key):
|
|
4661
4780
|
if self._spec:
|
|
4662
|
-
self._specWrite(
|
|
4781
|
+
self._specWrite(_specTombstoneLines(key, self.delimiter, self._specState.strip))
|
|
4663
4782
|
return
|
|
4664
4783
|
# Persist a deletion by appending a tombstone (a lone key with no
|
|
4665
4784
|
# values); on read such a row deletes the key (see _processLine). The
|
|
@@ -4803,7 +4922,7 @@ class TSVZedLite(MutableMapping):
|
|
|
4803
4922
|
eprint(f"Key {key} not found")
|
|
4804
4923
|
return
|
|
4805
4924
|
self.indexes.pop(key, None)
|
|
4806
|
-
self.
|
|
4925
|
+
self.__mapDeleteToFile(key)
|
|
4807
4926
|
return
|
|
4808
4927
|
if key == DEFAULTS_INDICATOR_KEY:
|
|
4809
4928
|
self.__mapDeleteToFile(key)
|
|
@@ -5380,11 +5499,14 @@ class _ServeStore(object):
|
|
|
5380
5499
|
def _payload(self, rows, log):
|
|
5381
5500
|
"""Bytes for ``rows`` (``[(cells, marker), ...]``), formatted as the direct writers format them."""
|
|
5382
5501
|
if self.spec:
|
|
5383
|
-
return
|
|
5384
|
-
for cells, marker in rows).encode('utf-8')
|
|
5502
|
+
return _specFormatRecords(rows, self.delimiter).encode('utf-8')
|
|
5385
5503
|
return _legacyFormatPayload(self.path, [cells for cells, marker in rows], log, [''], True, self.verbose,
|
|
5386
5504
|
'utf8', self.strict, self.delimiter)[0]
|
|
5387
5505
|
|
|
5506
|
+
def _tombstone(self, key):
|
|
5507
|
+
"""Rows that delete ``key``: written with stripping off when it would strip ``key`` (spec §9.6)."""
|
|
5508
|
+
return _specTombstones([key], self.delimiter, self.state.strip) if self.spec else [([key], False)]
|
|
5509
|
+
|
|
5388
5510
|
def _durable(self, sync):
|
|
5389
5511
|
return sync or (self.spec and self.state.writeAck == 'disk')
|
|
5390
5512
|
|
|
@@ -5422,7 +5544,7 @@ class _ServeStore(object):
|
|
|
5422
5544
|
if row is None:
|
|
5423
5545
|
return None
|
|
5424
5546
|
row = list(row)
|
|
5425
|
-
queued = self._submit(
|
|
5547
|
+
queued = self._submit(self._tombstone(key), sync, log)
|
|
5426
5548
|
self._ack(queued, written=True) # the row is only popped once its tombstone is written
|
|
5427
5549
|
return row
|
|
5428
5550
|
|
|
@@ -5436,7 +5558,7 @@ class _ServeStore(object):
|
|
|
5436
5558
|
return None
|
|
5437
5559
|
key = next(reversed(self.data)) if last else next(iter(self.data))
|
|
5438
5560
|
row = list(self.data[key])
|
|
5439
|
-
queued = self._submit(
|
|
5561
|
+
queued = self._submit(self._tombstone(key), sync, log)
|
|
5440
5562
|
self._ack(queued, written=True)
|
|
5441
5563
|
return row
|
|
5442
5564
|
|
|
@@ -5621,21 +5743,32 @@ def _serveDispatch(store, line, bulk, log):
|
|
|
5621
5743
|
if operation in _SERVE_NEED_STORE and store.missing(log):
|
|
5622
5744
|
return 1, [], 'no such store' # spec §20.2.2
|
|
5623
5745
|
unreadable = (1, _SERVE_UNREADABLE)
|
|
5746
|
+
# Each read takes its status under the store lock it read under: another request's reload may
|
|
5747
|
+
# change ``unreadable`` once the lock is free.
|
|
5624
5748
|
if operation == 'read':
|
|
5625
|
-
|
|
5626
|
-
|
|
5749
|
+
with store.lock:
|
|
5750
|
+
rows = store.read(log)
|
|
5751
|
+
status = unreadable if store.unreadable else (0, '')
|
|
5627
5752
|
return status[0], [_specFormatRecord(row, '\t') for row in rows], status[1]
|
|
5628
5753
|
if operation == 'get':
|
|
5629
|
-
|
|
5630
|
-
|
|
5754
|
+
with store.lock:
|
|
5755
|
+
rows, missing = store.get(args, log)
|
|
5756
|
+
status = unreadable if store.unreadable else (3 if missing else 0, '')
|
|
5631
5757
|
return status[0], [_specFormatRecord(row, '\t') for row in rows], status[1]
|
|
5632
5758
|
if operation == 'has':
|
|
5633
|
-
|
|
5634
|
-
|
|
5759
|
+
with store.lock:
|
|
5760
|
+
found = store.has(args[0], log)
|
|
5761
|
+
return (1, [], _SERVE_UNREADABLE) if store.unreadable else (0 if found else 3, [], '')
|
|
5635
5762
|
if operation == 'len':
|
|
5636
|
-
|
|
5763
|
+
with store.lock:
|
|
5764
|
+
count = store.length(log)
|
|
5765
|
+
status = unreadable if store.unreadable else (0, '') # spec §4.10, §20.6: a stopped read failed
|
|
5766
|
+
return status[0], [str(count)], status[1]
|
|
5637
5767
|
if operation == 'keys':
|
|
5638
|
-
|
|
5768
|
+
with store.lock:
|
|
5769
|
+
keys = store.keys(log)
|
|
5770
|
+
status = unreadable if store.unreadable else (0, '')
|
|
5771
|
+
return status[0], [_specEncodeField(key, '\t', isKey=True) for key in keys], status[1]
|
|
5639
5772
|
if operation in ('set', 'append', 'delete'):
|
|
5640
5773
|
keysOnly = operation == 'delete'
|
|
5641
5774
|
if isBulk:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tsvz-4.5 → tsvz-4.6}/setup.cfg
RENAMED
|
File without changes
|
{tsvz-4.5 → tsvz-4.6}/setup.py
RENAMED
|
File without changes
|