lsm


Namelsm JSON
Version 0.5.10 PyPI version JSON
download
home_pagehttps://github.com/mosquito/python-lsm/
SummaryPython bindings for SQLite's LSM key/value engine
upload_time2025-02-27 16:54:55
maintainerNone
docs_urlNone
authorDmitry Orlov
requires_python<4,>=3.9
licenseApache Software License
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            lsm
===

Fast Python bindings for [SQLite's LSM key/value store](http://www.sqlite.org/src4/doc/trunk/www/lsmusr.wiki>).
The LSM storage engine was initially written as part of the experimental
SQLite4 rewrite (now abandoned). More recently, the LSM source code was moved
into the SQLite3 [source tree](https://www.sqlite.org/cgi/src/dir?ci=e148cdad35520e66&name=ext/lsm1)
and has seen some improvements and fixes. This project uses the LSM code from
the SQLite3 source tree.

Features:

* Embedded zero-conf database.
* Keys support in-order traversal using cursors.
* Transactional (including nested transactions).
* Single writer/multiple reader MVCC based transactional concurrency model.
* On-disk database stored in a single file.
* Data is durable in the face of application or power failure.
* Thread-safe.
* Releases GIL for read and write operations
  (each connection has own mutex)
* Page compression (lz4 or zstd)
* Zero dependency static library
* Python 3.x.

Limitations:

The source for Python lsm is
[hosted on GitHub](https://github.com/mosquito/python-lsm).

If you encounter any bugs in the library, please
[open an issue](https://github.com/mosquito/python-lsm/issues/new),
including a description of the bug and any related traceback.

## Quick-start

Below is a sample interactive console session designed to show some of the
basic features and functionality of the ``lsm`` Python library.

To begin, instantiate a `LSM` object, specifying a path to a database file.

<!--  name: test_example_db -->
```python
from lsm import LSM
db = LSM('test.ldb')
assert db.open()
```

More pythonic variant is using context manager:

<!--  name: test_example_db_context_manager -->
```python
from lsm import LSM
with LSM("test.ldb") as db:
    assert db.info()
```

Not opened database will raise a RuntimeError:

<!--  name: test_example_db -->
```python
import pytest
from lsm import LSM

db = LSM('test.ldb')

with pytest.raises(RuntimeError):
    db.info()
```

### Binary/string mode

You should select mode for opening the database with ``binary: bool = True``
argument.

For example when you want to store strings just pass ``binary=False``:

<!--  name: test_binary_mode -->
```python
from lsm import LSM
with LSM("test_0.ldb", binary=False) as db:
    # must be str for keys and values
    db['foo'] = 'bar'
    assert db['foo'] == "bar"
```

Otherwise, you must pass keys and values ad ``bytes`` (default behaviour):

<!--  name: test_string_mode -->
```python
from lsm import LSM

with LSM("test.ldb") as db:
    db[b'foo'] = b'bar'
    assert db[b'foo'] == b'bar'
```

### Key/Value Features

``lsm`` is a key/value store, and has a dictionary-like API:

<!--  name: test_getitem -->
```python
from lsm import LSM
with LSM("test.ldb", binary=False) as db:
    db['foo'] = 'bar'
    assert db['foo'] == 'bar'
```

Database apply changes as soon as possible:

<!--  name: test_get_del_item -->
```python
import pytest
from lsm import LSM

with LSM("test.ldb", binary=False) as db:
    for i in range(4):
         db[f'k{i}'] = str(i)

    assert 'k3' in db
    assert 'k4' not in db
    del db['k3']

    with pytest.raises(KeyError):
        print(db['k3'])
```

By default, when you attempt to look up a key, ``lsm`` will search for an
exact match. You can also search for the closest key, if the specific key you
are searching for does not exist:

<!--  name: test_get_del_item_seek_mode -->
```python
import pytest
from lsm import LSM, SEEK_LE, SEEK_GE, SEEK_LEFAST


with LSM("test.ldb", binary=False) as db:
    for i in range(4):
        db[f'k{i}'] = str(i)

    # Here we will match "k1".
    assert db['k1xx', SEEK_LE] == '1'

    # Here we will match "k1" but do not fetch a value
    # In this case the value will always be ``True`` or there will
    # be an exception if the key is not found
    assert db['k1xx', SEEK_LEFAST] is True

    with pytest.raises(KeyError):
        print(db['000', SEEK_LEFAST])

    # Here we will match "k2".
    assert db['k1xx', SEEK_GE] == "2"
```

`LSM` supports other common dictionary methods such as:

* `keys()`
* `values()`
* `items()`
* `update()`


### Slices and Iteration

The database can be iterated through directly, or sliced. When you are slicing
the database the start and end keys need not exist -- ``lsm`` will find the
closest key (details can be found in the [LSM.fetch_range()](https://lsm-db.readthedocs.io/en/latest/api.html#lsm.LSM.fetch_range)
documentation).

<!--
    name: test_slices;
-->
```python
from lsm import LSM

with LSM("test_slices.ldb", binary=False) as db:

    # clean database
    for key in db.keys():
        del db[key]

    db['foo'] = 'bar'

    for i in range(3):
        db[f'k{i}'] = str(i)

    # Can easily iterate over the database items
    assert (
        sorted(item for item in db.items()) == [
            ('foo', 'bar'), ('k0', '0'), ('k1', '1'), ('k2', '2')
        ]
    )

    # However, you will not read the entire database into memory, as special
    # iterator objects are used.
    assert str(db['k0':'k99']).startswith("<lsm_slice object at")

    # But you can cast it to the list for example
    assert list(db['k0':'k99']) == [('k0', '0'), ('k1', '1'), ('k2', '2')]
```

You can use open-ended slices. If the lower- or upper-bound is outside the
range of keys an empty list is returned.


<!--
    name: test_slices;
    case: open_ended_slices
-->
```python
with LSM("test_slices.ldb", binary=False, readonly=True) as db:
    assert list(db['k0':]) == [('k0', '0'), ('k1', '1'), ('k2', '2')]
    assert list(db[:'k1']) == [('foo', 'bar'), ('k0', '0'), ('k1', '1')]
    assert list(db[:'aaa']) == []
```

To retrieve keys in reverse order or stepping over more than one item,
simply use a third slice argument as usual.
Negative step value means reverse order, but first and second arguments
must be ordinarily ordered.

<!--
    name: test_slices;
    case: reverse_slices
-->
```python
with LSM("test_slices.ldb", binary=False, readonly=True) as db:
    assert list(db['k0':'k99':2]) == [('k0', '0'), ('k2', '2')]
    assert list(db['k0'::-1]) == [('k2', '2'), ('k1', '1'), ('k0', '0')]
    assert list(db['k0'::-2]) == [('k2', '2'), ('k0', '0')]
    assert list(db['k0'::3]) == [('k0', '0')]
```

You can also **delete** slices of keys, but note that delete **will not**
include the keys themselves:

<!--
    name: test_slices;
    case: del_slice
-->
```python
with LSM("test_slices.ldb", binary=False) as db:
    del db['k0':'k99']

    # Note that 'k0' still exists.
    assert list(db.items()) == [('foo', 'bar'), ('k0', '0')]
```

### Cursors

While slicing may cover most use-cases, for finer-grained control you can use
cursors for traversing records.

<!--
    name: test_cursors;
    case: iterate_over_one_item
-->
```python
from lsm import LSM, SEEK_GE, SEEK_LE

with LSM("test_cursors.ldb", binary=False) as db:
    del db["a":"z"]

    db["spam"] = "spam"

    with db.cursor() as cursor:
        cursor.seek('spam')
        key, value = cursor.retrieve()
        assert key == 'spam'
        assert value == 'spam'
```

Seeking over cursors:

<!--
    name: test_cursors;
    case: iterate_over_multiple_items
-->
```python
with LSM("test_cursors.ldb", binary=False) as db:
    db.update({'k0': '0', 'k1': '1', 'k2': '2', 'k3': '3', 'foo': 'bar'})

    with db.cursor() as cursor:

        cursor.first()
        key, value = cursor.retrieve()
        assert key == "foo"
        assert value == "bar"

        cursor.last()
        key, value = cursor.retrieve()
        assert key == "spam"
        assert value == "spam"

        cursor.previous()
        key, value = cursor.retrieve()
        assert key == "k3"
        assert value == "3"

```

Finding the first match that is greater than or equal to `'k0'` and move
forward until the key is less than `'k99'`

<!--
    name: test_cursors;
    case: iterate_ge_until_k99
-->
```python
with LSM("test_cursors.ldb", binary=False) as db:
    with db.cursor() as cursor:
        cursor.seek("k0", SEEK_GE)
        results = []

        while cursor.compare("k99") > 0:
            key, value = cursor.retrieve()
            results.append((key, value))
            cursor.next()

    assert results == [('k0', '0'), ('k1', '1'), ('k2', '2'), ('k3', '3')]

```

Finding the last match that is lower than or equal to `'k99'` and move
backward until the key is less than `'k0'`

<!--
    name: test_cursors;
    case: iterate_le_until_k0
-->
```python
with LSM("test_cursors.ldb", binary=False) as db:
    with db.cursor() as cursor:
        cursor.seek("k99", SEEK_LE)
        results = []

        while cursor.compare("k0") >= 0:
            key, value = cursor.retrieve()
            results.append((key, value))
            cursor.previous()

    assert results == [('k3', '3'), ('k2', '2'), ('k1', '1'), ('k0', '0')]
```

It is very important to close a cursor when you are through using it. For this
reason, it is recommended you use the `LSM.cursor()` context-manager, which
ensures the cursor is closed properly.

### Transactions

``lsm`` supports nested transactions. The simplest way to use transactions
is with the `LSM.transaction()` method, which returns a context-manager:

<!-- name: test_transactions -->
```python
from lsm import LSM

with LSM("test_tx.ldb", binary=False) as db:
    del db["a":"z"]
    for i in range(10):
        db[f"k{i}"] = f"{i}"


with LSM("test_tx.ldb", binary=False) as db:
    with db.transaction() as tx1:
        db['k1'] = '1-mod'

        with db.transaction() as tx2:
            db['k2'] = '2-mod'
            tx2.rollback()

    assert db['k1'] == '1-mod'
    assert db['k2'] == '2'
```

You can commit or roll-back transactions part-way through a wrapped block:

<!-- name: test_transactions_2 -->
```python
from lsm import LSM

with LSM("test_tx_2.ldb", binary=False) as db:
    del db["a":"z"]
    for i in range(10):
        db[f"k{i}"] = f"{i}"

with LSM("test_tx_2.ldb", binary=False) as db:
    with db.transaction() as txn:
        db['k1'] = 'outer txn'

        # The write operation is preserved.
        txn.commit()

        db['k1'] = 'outer txn-2'

        with db.transaction() as txn2:
            # This is committed after the block ends.
            db['k1'] = 'inner-txn'

        assert db['k1'] == "inner-txn"

        # Rolls back both the changes from txn2 and the preceding write.
        txn.rollback()

        assert db['k1'] == 'outer txn', db['k1']
```


If you like, you can also explicitly call `LSM.begin()`, `LSM.commit()`, and
`LSM.rollback()`.

<!-- name: test_transactions_db -->
```python
from lsm import LSM

# fill db
with LSM("test_db_tx.ldb", binary=False) as db:
    del db["k":"z"]
    for i in range(10):
        db[f"k{i}"] = f"{i}"


with LSM("test_db_tx.ldb", binary=False) as db:
    # start transaction
    db.begin()
    db['k1'] = '1-mod'

    # nested transaction
    db.begin()
    db['k2'] = '2-mod'
    # rolling back nested transaction
    db.rollback()

    # comitting top-level transaction
    db.commit()

    assert db['k1'] == '1-mod'
    assert db['k2'] == '2'
```

### Thanks to

* [@coleifer](https://github.com/coleifer) - this project was inspired by
[coleifer/python-lsm-db](https://github.com/coleifer/python-lsm-db).

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/mosquito/python-lsm/",
    "name": "lsm",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<4,>=3.9",
    "maintainer_email": null,
    "keywords": null,
    "author": "Dmitry Orlov",
    "author_email": "me@mosquito.su",
    "download_url": "https://files.pythonhosted.org/packages/af/05/029d5a563e734878fed1d081801155ba4ec458c538eec1dfe1acaf894c2f/lsm-0.5.10.tar.gz",
    "platform": null,
    "description": "lsm\n===\n\nFast Python bindings for [SQLite's LSM key/value store](http://www.sqlite.org/src4/doc/trunk/www/lsmusr.wiki>).\nThe LSM storage engine was initially written as part of the experimental\nSQLite4 rewrite (now abandoned). More recently, the LSM source code was moved\ninto the SQLite3 [source tree](https://www.sqlite.org/cgi/src/dir?ci=e148cdad35520e66&name=ext/lsm1)\nand has seen some improvements and fixes. This project uses the LSM code from\nthe SQLite3 source tree.\n\nFeatures:\n\n* Embedded zero-conf database.\n* Keys support in-order traversal using cursors.\n* Transactional (including nested transactions).\n* Single writer/multiple reader MVCC based transactional concurrency model.\n* On-disk database stored in a single file.\n* Data is durable in the face of application or power failure.\n* Thread-safe.\n* Releases GIL for read and write operations\n  (each connection has own mutex)\n* Page compression (lz4 or zstd)\n* Zero dependency static library\n* Python 3.x.\n\nLimitations:\n\nThe source for Python lsm is\n[hosted on GitHub](https://github.com/mosquito/python-lsm).\n\nIf you encounter any bugs in the library, please\n[open an issue](https://github.com/mosquito/python-lsm/issues/new),\nincluding a description of the bug and any related traceback.\n\n## Quick-start\n\nBelow is a sample interactive console session designed to show some of the\nbasic features and functionality of the ``lsm`` Python library.\n\nTo begin, instantiate a `LSM` object, specifying a path to a database file.\n\n<!--  name: test_example_db -->\n```python\nfrom lsm import LSM\ndb = LSM('test.ldb')\nassert db.open()\n```\n\nMore pythonic variant is using context manager:\n\n<!--  name: test_example_db_context_manager -->\n```python\nfrom lsm import LSM\nwith LSM(\"test.ldb\") as db:\n    assert db.info()\n```\n\nNot opened database will raise a RuntimeError:\n\n<!--  name: test_example_db -->\n```python\nimport pytest\nfrom lsm import LSM\n\ndb = LSM('test.ldb')\n\nwith pytest.raises(RuntimeError):\n    db.info()\n```\n\n### Binary/string mode\n\nYou should select mode for opening the database with ``binary: bool = True``\nargument.\n\nFor example when you want to store strings just pass ``binary=False``:\n\n<!--  name: test_binary_mode -->\n```python\nfrom lsm import LSM\nwith LSM(\"test_0.ldb\", binary=False) as db:\n    # must be str for keys and values\n    db['foo'] = 'bar'\n    assert db['foo'] == \"bar\"\n```\n\nOtherwise, you must pass keys and values ad ``bytes`` (default behaviour):\n\n<!--  name: test_string_mode -->\n```python\nfrom lsm import LSM\n\nwith LSM(\"test.ldb\") as db:\n    db[b'foo'] = b'bar'\n    assert db[b'foo'] == b'bar'\n```\n\n### Key/Value Features\n\n``lsm`` is a key/value store, and has a dictionary-like API:\n\n<!--  name: test_getitem -->\n```python\nfrom lsm import LSM\nwith LSM(\"test.ldb\", binary=False) as db:\n    db['foo'] = 'bar'\n    assert db['foo'] == 'bar'\n```\n\nDatabase apply changes as soon as possible:\n\n<!--  name: test_get_del_item -->\n```python\nimport pytest\nfrom lsm import LSM\n\nwith LSM(\"test.ldb\", binary=False) as db:\n    for i in range(4):\n         db[f'k{i}'] = str(i)\n\n    assert 'k3' in db\n    assert 'k4' not in db\n    del db['k3']\n\n    with pytest.raises(KeyError):\n        print(db['k3'])\n```\n\nBy default, when you attempt to look up a key, ``lsm`` will search for an\nexact match. You can also search for the closest key, if the specific key you\nare searching for does not exist:\n\n<!--  name: test_get_del_item_seek_mode -->\n```python\nimport pytest\nfrom lsm import LSM, SEEK_LE, SEEK_GE, SEEK_LEFAST\n\n\nwith LSM(\"test.ldb\", binary=False) as db:\n    for i in range(4):\n        db[f'k{i}'] = str(i)\n\n    # Here we will match \"k1\".\n    assert db['k1xx', SEEK_LE] == '1'\n\n    # Here we will match \"k1\" but do not fetch a value\n    # In this case the value will always be ``True`` or there will\n    # be an exception if the key is not found\n    assert db['k1xx', SEEK_LEFAST] is True\n\n    with pytest.raises(KeyError):\n        print(db['000', SEEK_LEFAST])\n\n    # Here we will match \"k2\".\n    assert db['k1xx', SEEK_GE] == \"2\"\n```\n\n`LSM` supports other common dictionary methods such as:\n\n* `keys()`\n* `values()`\n* `items()`\n* `update()`\n\n\n### Slices and Iteration\n\nThe database can be iterated through directly, or sliced. When you are slicing\nthe database the start and end keys need not exist -- ``lsm`` will find the\nclosest key (details can be found in the [LSM.fetch_range()](https://lsm-db.readthedocs.io/en/latest/api.html#lsm.LSM.fetch_range)\ndocumentation).\n\n<!--\n    name: test_slices;\n-->\n```python\nfrom lsm import LSM\n\nwith LSM(\"test_slices.ldb\", binary=False) as db:\n\n    # clean database\n    for key in db.keys():\n        del db[key]\n\n    db['foo'] = 'bar'\n\n    for i in range(3):\n        db[f'k{i}'] = str(i)\n\n    # Can easily iterate over the database items\n    assert (\n        sorted(item for item in db.items()) == [\n            ('foo', 'bar'), ('k0', '0'), ('k1', '1'), ('k2', '2')\n        ]\n    )\n\n    # However, you will not read the entire database into memory, as special\n    # iterator objects are used.\n    assert str(db['k0':'k99']).startswith(\"<lsm_slice object at\")\n\n    # But you can cast it to the list for example\n    assert list(db['k0':'k99']) == [('k0', '0'), ('k1', '1'), ('k2', '2')]\n```\n\nYou can use open-ended slices. If the lower- or upper-bound is outside the\nrange of keys an empty list is returned.\n\n\n<!--\n    name: test_slices;\n    case: open_ended_slices\n-->\n```python\nwith LSM(\"test_slices.ldb\", binary=False, readonly=True) as db:\n    assert list(db['k0':]) == [('k0', '0'), ('k1', '1'), ('k2', '2')]\n    assert list(db[:'k1']) == [('foo', 'bar'), ('k0', '0'), ('k1', '1')]\n    assert list(db[:'aaa']) == []\n```\n\nTo retrieve keys in reverse order or stepping over more than one item,\nsimply use a third slice argument as usual.\nNegative step value means reverse order, but first and second arguments\nmust be ordinarily ordered.\n\n<!--\n    name: test_slices;\n    case: reverse_slices\n-->\n```python\nwith LSM(\"test_slices.ldb\", binary=False, readonly=True) as db:\n    assert list(db['k0':'k99':2]) == [('k0', '0'), ('k2', '2')]\n    assert list(db['k0'::-1]) == [('k2', '2'), ('k1', '1'), ('k0', '0')]\n    assert list(db['k0'::-2]) == [('k2', '2'), ('k0', '0')]\n    assert list(db['k0'::3]) == [('k0', '0')]\n```\n\nYou can also **delete** slices of keys, but note that delete **will not**\ninclude the keys themselves:\n\n<!--\n    name: test_slices;\n    case: del_slice\n-->\n```python\nwith LSM(\"test_slices.ldb\", binary=False) as db:\n    del db['k0':'k99']\n\n    # Note that 'k0' still exists.\n    assert list(db.items()) == [('foo', 'bar'), ('k0', '0')]\n```\n\n### Cursors\n\nWhile slicing may cover most use-cases, for finer-grained control you can use\ncursors for traversing records.\n\n<!--\n    name: test_cursors;\n    case: iterate_over_one_item\n-->\n```python\nfrom lsm import LSM, SEEK_GE, SEEK_LE\n\nwith LSM(\"test_cursors.ldb\", binary=False) as db:\n    del db[\"a\":\"z\"]\n\n    db[\"spam\"] = \"spam\"\n\n    with db.cursor() as cursor:\n        cursor.seek('spam')\n        key, value = cursor.retrieve()\n        assert key == 'spam'\n        assert value == 'spam'\n```\n\nSeeking over cursors:\n\n<!--\n    name: test_cursors;\n    case: iterate_over_multiple_items\n-->\n```python\nwith LSM(\"test_cursors.ldb\", binary=False) as db:\n    db.update({'k0': '0', 'k1': '1', 'k2': '2', 'k3': '3', 'foo': 'bar'})\n\n    with db.cursor() as cursor:\n\n        cursor.first()\n        key, value = cursor.retrieve()\n        assert key == \"foo\"\n        assert value == \"bar\"\n\n        cursor.last()\n        key, value = cursor.retrieve()\n        assert key == \"spam\"\n        assert value == \"spam\"\n\n        cursor.previous()\n        key, value = cursor.retrieve()\n        assert key == \"k3\"\n        assert value == \"3\"\n\n```\n\nFinding the first match that is greater than or equal to `'k0'` and move\nforward until the key is less than `'k99'`\n\n<!--\n    name: test_cursors;\n    case: iterate_ge_until_k99\n-->\n```python\nwith LSM(\"test_cursors.ldb\", binary=False) as db:\n    with db.cursor() as cursor:\n        cursor.seek(\"k0\", SEEK_GE)\n        results = []\n\n        while cursor.compare(\"k99\") > 0:\n            key, value = cursor.retrieve()\n            results.append((key, value))\n            cursor.next()\n\n    assert results == [('k0', '0'), ('k1', '1'), ('k2', '2'), ('k3', '3')]\n\n```\n\nFinding the last match that is lower than or equal to `'k99'` and move\nbackward until the key is less than `'k0'`\n\n<!--\n    name: test_cursors;\n    case: iterate_le_until_k0\n-->\n```python\nwith LSM(\"test_cursors.ldb\", binary=False) as db:\n    with db.cursor() as cursor:\n        cursor.seek(\"k99\", SEEK_LE)\n        results = []\n\n        while cursor.compare(\"k0\") >= 0:\n            key, value = cursor.retrieve()\n            results.append((key, value))\n            cursor.previous()\n\n    assert results == [('k3', '3'), ('k2', '2'), ('k1', '1'), ('k0', '0')]\n```\n\nIt is very important to close a cursor when you are through using it. For this\nreason, it is recommended you use the `LSM.cursor()` context-manager, which\nensures the cursor is closed properly.\n\n### Transactions\n\n``lsm`` supports nested transactions. The simplest way to use transactions\nis with the `LSM.transaction()` method, which returns a context-manager:\n\n<!-- name: test_transactions -->\n```python\nfrom lsm import LSM\n\nwith LSM(\"test_tx.ldb\", binary=False) as db:\n    del db[\"a\":\"z\"]\n    for i in range(10):\n        db[f\"k{i}\"] = f\"{i}\"\n\n\nwith LSM(\"test_tx.ldb\", binary=False) as db:\n    with db.transaction() as tx1:\n        db['k1'] = '1-mod'\n\n        with db.transaction() as tx2:\n            db['k2'] = '2-mod'\n            tx2.rollback()\n\n    assert db['k1'] == '1-mod'\n    assert db['k2'] == '2'\n```\n\nYou can commit or roll-back transactions part-way through a wrapped block:\n\n<!-- name: test_transactions_2 -->\n```python\nfrom lsm import LSM\n\nwith LSM(\"test_tx_2.ldb\", binary=False) as db:\n    del db[\"a\":\"z\"]\n    for i in range(10):\n        db[f\"k{i}\"] = f\"{i}\"\n\nwith LSM(\"test_tx_2.ldb\", binary=False) as db:\n    with db.transaction() as txn:\n        db['k1'] = 'outer txn'\n\n        # The write operation is preserved.\n        txn.commit()\n\n        db['k1'] = 'outer txn-2'\n\n        with db.transaction() as txn2:\n            # This is committed after the block ends.\n            db['k1'] = 'inner-txn'\n\n        assert db['k1'] == \"inner-txn\"\n\n        # Rolls back both the changes from txn2 and the preceding write.\n        txn.rollback()\n\n        assert db['k1'] == 'outer txn', db['k1']\n```\n\n\nIf you like, you can also explicitly call `LSM.begin()`, `LSM.commit()`, and\n`LSM.rollback()`.\n\n<!-- name: test_transactions_db -->\n```python\nfrom lsm import LSM\n\n# fill db\nwith LSM(\"test_db_tx.ldb\", binary=False) as db:\n    del db[\"k\":\"z\"]\n    for i in range(10):\n        db[f\"k{i}\"] = f\"{i}\"\n\n\nwith LSM(\"test_db_tx.ldb\", binary=False) as db:\n    # start transaction\n    db.begin()\n    db['k1'] = '1-mod'\n\n    # nested transaction\n    db.begin()\n    db['k2'] = '2-mod'\n    # rolling back nested transaction\n    db.rollback()\n\n    # comitting top-level transaction\n    db.commit()\n\n    assert db['k1'] == '1-mod'\n    assert db['k2'] == '2'\n```\n\n### Thanks to\n\n* [@coleifer](https://github.com/coleifer) - this project was inspired by\n[coleifer/python-lsm-db](https://github.com/coleifer/python-lsm-db).\n",
    "bugtrack_url": null,
    "license": "Apache Software License",
    "summary": "Python bindings for SQLite's LSM key/value engine",
    "version": "0.5.10",
    "project_urls": {
        "Documentation": "https://github.com/mosquito/python-lsm/",
        "Homepage": "https://github.com/mosquito/python-lsm/",
        "Say Thanks!": "https://saythanks.io/to/mosquito",
        "Source": "https://github.com/mosquito/python-lsm/",
        "Tracker": "https://github.com/mosquito/python-lsm/issues"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "520f8fa0ff7a67fd959b1d1c493cd7a888698cbb2378f601d4f23b5068256d28",
                "md5": "b5d06609d04e07defae71e3f9e484a86",
                "sha256": "2735e8def90e2c3ddf71552e0a21dfbd90a3bd2d55c462ca03f8130b0010b0d4"
            },
            "downloads": -1,
            "filename": "lsm-0.5.10-cp310-cp310-macosx_10_9_universal2.whl",
            "has_sig": false,
            "md5_digest": "b5d06609d04e07defae71e3f9e484a86",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": "<4,>=3.9",
            "size": 1199863,
            "upload_time": "2025-02-27T16:56:13",
            "upload_time_iso_8601": "2025-02-27T16:56:13.316287Z",
            "url": "https://files.pythonhosted.org/packages/52/0f/8fa0ff7a67fd959b1d1c493cd7a888698cbb2378f601d4f23b5068256d28/lsm-0.5.10-cp310-cp310-macosx_10_9_universal2.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "772d6e4feb6707e1ae2e0f2c27ba4ef761479c634370f9b22f13403c4c4600c8",
                "md5": "5d22b54bf4d9e7edc2fe06aae9cf46ae",
                "sha256": "63d549b3397f4d8ad1d95c59bda3345be11571af88c8d05b8d860e369c2db165"
            },
            "downloads": -1,
            "filename": "lsm-0.5.10-cp310-cp310-manylinux_2_34_x86_64.whl",
            "has_sig": false,
            "md5_digest": "5d22b54bf4d9e7edc2fe06aae9cf46ae",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": "<4,>=3.9",
            "size": 1051299,
            "upload_time": "2025-02-27T16:56:08",
            "upload_time_iso_8601": "2025-02-27T16:56:08.804296Z",
            "url": "https://files.pythonhosted.org/packages/77/2d/6e4feb6707e1ae2e0f2c27ba4ef761479c634370f9b22f13403c4c4600c8/lsm-0.5.10-cp310-cp310-manylinux_2_34_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "6ebb97075074ee34f8e9b96a57a5de8309e332aa909899e3d925be404a9eaece",
                "md5": "860bd5f08c9636593a5dc89b9321821f",
                "sha256": "93eaf0b9c0fa7621aff8025985768fe5b0b03dd7fd03905ee916c03ea7e6cad3"
            },
            "downloads": -1,
            "filename": "lsm-0.5.10-cp310-cp310-win_amd64.whl",
            "has_sig": false,
            "md5_digest": "860bd5f08c9636593a5dc89b9321821f",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": "<4,>=3.9",
            "size": 249144,
            "upload_time": "2025-02-27T16:56:07",
            "upload_time_iso_8601": "2025-02-27T16:56:07.980940Z",
            "url": "https://files.pythonhosted.org/packages/6e/bb/97075074ee34f8e9b96a57a5de8309e332aa909899e3d925be404a9eaece/lsm-0.5.10-cp310-cp310-win_amd64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "69d1201978c03999cde64e2a203cd097831c10b85b61576825f7e1844e4e4aae",
                "md5": "59e076094143b640195ab7973cda72d5",
                "sha256": "137e1001a16e26f5cd2c87060c342a30c789ba0e2d173448222b847f1acb8d87"
            },
            "downloads": -1,
            "filename": "lsm-0.5.10-cp311-cp311-macosx_10_9_universal2.whl",
            "has_sig": false,
            "md5_digest": "59e076094143b640195ab7973cda72d5",
            "packagetype": "bdist_wheel",
            "python_version": "cp311",
            "requires_python": "<4,>=3.9",
            "size": 1200150,
            "upload_time": "2025-02-27T16:55:57",
            "upload_time_iso_8601": "2025-02-27T16:55:57.448923Z",
            "url": "https://files.pythonhosted.org/packages/69/d1/201978c03999cde64e2a203cd097831c10b85b61576825f7e1844e4e4aae/lsm-0.5.10-cp311-cp311-macosx_10_9_universal2.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "d2acc97dd47eaa94c823b2a0b768ee72e70a52d3639502a6ec5e5d26977fbc09",
                "md5": "e3e7069ce1b750096d78ff31f9b517a5",
                "sha256": "3d152942c47a911d18cb151b21e84802f92d162881438f8c833903f66b85d610"
            },
            "downloads": -1,
            "filename": "lsm-0.5.10-cp311-cp311-manylinux_2_34_x86_64.whl",
            "has_sig": false,
            "md5_digest": "e3e7069ce1b750096d78ff31f9b517a5",
            "packagetype": "bdist_wheel",
            "python_version": "cp311",
            "requires_python": "<4,>=3.9",
            "size": 1053095,
            "upload_time": "2025-02-27T16:56:12",
            "upload_time_iso_8601": "2025-02-27T16:56:12.058946Z",
            "url": "https://files.pythonhosted.org/packages/d2/ac/c97dd47eaa94c823b2a0b768ee72e70a52d3639502a6ec5e5d26977fbc09/lsm-0.5.10-cp311-cp311-manylinux_2_34_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "147c5d53cb5742220181f030d75521067c0520a88af21b5cc645c0dcf06bdaef",
                "md5": "bee5952ea5a35f6ab256ded1123e4707",
                "sha256": "6e88f8d784a442c64f1fbf78b96eadc6e0327ad229c386a95a9454a633699411"
            },
            "downloads": -1,
            "filename": "lsm-0.5.10-cp311-cp311-win_amd64.whl",
            "has_sig": false,
            "md5_digest": "bee5952ea5a35f6ab256ded1123e4707",
            "packagetype": "bdist_wheel",
            "python_version": "cp311",
            "requires_python": "<4,>=3.9",
            "size": 249274,
            "upload_time": "2025-02-27T16:56:07",
            "upload_time_iso_8601": "2025-02-27T16:56:07.636078Z",
            "url": "https://files.pythonhosted.org/packages/14/7c/5d53cb5742220181f030d75521067c0520a88af21b5cc645c0dcf06bdaef/lsm-0.5.10-cp311-cp311-win_amd64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "7cb6f2c2d4fff96d26a1df5ca1c677ccbf515fe20cdb4b23f403329981508304",
                "md5": "bdf996811144aeceb6cec5ae080ec7ef",
                "sha256": "6ac95c245b11a1bdcc5f047f9cacdbf3fc4a0141d9592be51eae76b5a4b4aee6"
            },
            "downloads": -1,
            "filename": "lsm-0.5.10-cp312-cp312-macosx_10_13_universal2.whl",
            "has_sig": false,
            "md5_digest": "bdf996811144aeceb6cec5ae080ec7ef",
            "packagetype": "bdist_wheel",
            "python_version": "cp312",
            "requires_python": "<4,>=3.9",
            "size": 1201913,
            "upload_time": "2025-02-27T16:56:12",
            "upload_time_iso_8601": "2025-02-27T16:56:12.272499Z",
            "url": "https://files.pythonhosted.org/packages/7c/b6/f2c2d4fff96d26a1df5ca1c677ccbf515fe20cdb4b23f403329981508304/lsm-0.5.10-cp312-cp312-macosx_10_13_universal2.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "1d52df9925a88b52b541313699c58d5c953e48fec3b50c5e41bf5cbfcbcb353f",
                "md5": "5730632a085be536b85af0752f734c2a",
                "sha256": "1f7c8db72ffd2bdc930785a0c4897be5766b36d8d9914a5ad88a8919c4abba5c"
            },
            "downloads": -1,
            "filename": "lsm-0.5.10-cp312-cp312-manylinux_2_34_x86_64.whl",
            "has_sig": false,
            "md5_digest": "5730632a085be536b85af0752f734c2a",
            "packagetype": "bdist_wheel",
            "python_version": "cp312",
            "requires_python": "<4,>=3.9",
            "size": 1057197,
            "upload_time": "2025-02-27T16:56:13",
            "upload_time_iso_8601": "2025-02-27T16:56:13.785904Z",
            "url": "https://files.pythonhosted.org/packages/1d/52/df9925a88b52b541313699c58d5c953e48fec3b50c5e41bf5cbfcbcb353f/lsm-0.5.10-cp312-cp312-manylinux_2_34_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "55958c1e7f2143ea419c9729bba30041214a14e954bec678a6237a59006ece00",
                "md5": "1079938fb6067ce772b00b9b3b8ce0ac",
                "sha256": "f5476e680357683393d56bb128ed46cf0c7ca1e6435cc1771435eda8010f65fb"
            },
            "downloads": -1,
            "filename": "lsm-0.5.10-cp312-cp312-win_amd64.whl",
            "has_sig": false,
            "md5_digest": "1079938fb6067ce772b00b9b3b8ce0ac",
            "packagetype": "bdist_wheel",
            "python_version": "cp312",
            "requires_python": "<4,>=3.9",
            "size": 249640,
            "upload_time": "2025-02-27T16:56:30",
            "upload_time_iso_8601": "2025-02-27T16:56:30.867942Z",
            "url": "https://files.pythonhosted.org/packages/55/95/8c1e7f2143ea419c9729bba30041214a14e954bec678a6237a59006ece00/lsm-0.5.10-cp312-cp312-win_amd64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "d33a774c2bd145ff10bd0a1bde4879fbfd7346260c5e8deb0615d1b28535d0dd",
                "md5": "8cab9496a937ce19d13c2cb838d2f9af",
                "sha256": "194dba0ccfe02e6dcb7b8abc50a2f559ac2ad469af8f38ae81a56f3abba72364"
            },
            "downloads": -1,
            "filename": "lsm-0.5.10-cp313-cp313-macosx_10_13_universal2.whl",
            "has_sig": false,
            "md5_digest": "8cab9496a937ce19d13c2cb838d2f9af",
            "packagetype": "bdist_wheel",
            "python_version": "cp313",
            "requires_python": "<4,>=3.9",
            "size": 1201887,
            "upload_time": "2025-02-27T16:56:27",
            "upload_time_iso_8601": "2025-02-27T16:56:27.806838Z",
            "url": "https://files.pythonhosted.org/packages/d3/3a/774c2bd145ff10bd0a1bde4879fbfd7346260c5e8deb0615d1b28535d0dd/lsm-0.5.10-cp313-cp313-macosx_10_13_universal2.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "8049158cef2c5c10d13a3cd20862554e11f0e430b278a980ee231fe1e828e4da",
                "md5": "c12775d3c8cbe2231fc6237d420114a3",
                "sha256": "32033522de2b72f33d7badefa369e430f2987c9c6f7aee5a8430f44c61e7e43e"
            },
            "downloads": -1,
            "filename": "lsm-0.5.10-cp313-cp313-manylinux_2_34_x86_64.whl",
            "has_sig": false,
            "md5_digest": "c12775d3c8cbe2231fc6237d420114a3",
            "packagetype": "bdist_wheel",
            "python_version": "cp313",
            "requires_python": "<4,>=3.9",
            "size": 1056976,
            "upload_time": "2025-02-27T16:56:19",
            "upload_time_iso_8601": "2025-02-27T16:56:19.366956Z",
            "url": "https://files.pythonhosted.org/packages/80/49/158cef2c5c10d13a3cd20862554e11f0e430b278a980ee231fe1e828e4da/lsm-0.5.10-cp313-cp313-manylinux_2_34_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "acbff78d11e05ea8855d95fa200de6f378050f204058533b56480ad43c89e332",
                "md5": "9ca8f0b9f9c0457c2cdee1560d4fcdfb",
                "sha256": "116b66034ac0650d0e965356bc2e88ac69a753187b0c2593f33665ccd3b99bf2"
            },
            "downloads": -1,
            "filename": "lsm-0.5.10-cp313-cp313-win_amd64.whl",
            "has_sig": false,
            "md5_digest": "9ca8f0b9f9c0457c2cdee1560d4fcdfb",
            "packagetype": "bdist_wheel",
            "python_version": "cp313",
            "requires_python": "<4,>=3.9",
            "size": 249650,
            "upload_time": "2025-02-27T16:56:39",
            "upload_time_iso_8601": "2025-02-27T16:56:39.672025Z",
            "url": "https://files.pythonhosted.org/packages/ac/bf/f78d11e05ea8855d95fa200de6f378050f204058533b56480ad43c89e332/lsm-0.5.10-cp313-cp313-win_amd64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "839699145e7a94d8b2763d5c33dec6e8e7e06f69695e9ced00a97fb23df5d230",
                "md5": "5abb0d3c9f3fb6d8d90f9acb43e1bcb4",
                "sha256": "3bc1eb331877563b7061e81ae765adcf775e44f7e86ebba9d23aedb5d4db26e1"
            },
            "downloads": -1,
            "filename": "lsm-0.5.10-cp39-cp39-macosx_10_9_universal2.whl",
            "has_sig": false,
            "md5_digest": "5abb0d3c9f3fb6d8d90f9acb43e1bcb4",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": "<4,>=3.9",
            "size": 1199984,
            "upload_time": "2025-02-27T16:56:44",
            "upload_time_iso_8601": "2025-02-27T16:56:44.380765Z",
            "url": "https://files.pythonhosted.org/packages/83/96/99145e7a94d8b2763d5c33dec6e8e7e06f69695e9ced00a97fb23df5d230/lsm-0.5.10-cp39-cp39-macosx_10_9_universal2.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "d55c62e8195417803797fecdfee6538c0b4632249efa4ced7fec56c8a689c5c8",
                "md5": "1460b3bb6b0629956b75bb790b5d772a",
                "sha256": "8f0792bebe41bf2f24d460c0179d3b75b6de831eb10800be0fc36851c5af4ec8"
            },
            "downloads": -1,
            "filename": "lsm-0.5.10-cp39-cp39-manylinux_2_34_x86_64.whl",
            "has_sig": false,
            "md5_digest": "1460b3bb6b0629956b75bb790b5d772a",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": "<4,>=3.9",
            "size": 1051287,
            "upload_time": "2025-02-27T16:56:20",
            "upload_time_iso_8601": "2025-02-27T16:56:20.588203Z",
            "url": "https://files.pythonhosted.org/packages/d5/5c/62e8195417803797fecdfee6538c0b4632249efa4ced7fec56c8a689c5c8/lsm-0.5.10-cp39-cp39-manylinux_2_34_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "0da4cd5f47079c1b66357dad3e522923e32fa08724fc9f2b844309948bc90172",
                "md5": "8e27824d17ec21accbbe180e32ede3ac",
                "sha256": "85208bff639387d112dcfd4d278a4adc1dcff5c860110ecd14047feeec71900e"
            },
            "downloads": -1,
            "filename": "lsm-0.5.10-cp39-cp39-win_amd64.whl",
            "has_sig": false,
            "md5_digest": "8e27824d17ec21accbbe180e32ede3ac",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": "<4,>=3.9",
            "size": 339918,
            "upload_time": "2025-02-27T16:56:26",
            "upload_time_iso_8601": "2025-02-27T16:56:26.859279Z",
            "url": "https://files.pythonhosted.org/packages/0d/a4/cd5f47079c1b66357dad3e522923e32fa08724fc9f2b844309948bc90172/lsm-0.5.10-cp39-cp39-win_amd64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "af05029d5a563e734878fed1d081801155ba4ec458c538eec1dfe1acaf894c2f",
                "md5": "b2cbe77072443e29cd31dc7fdfe50359",
                "sha256": "809f75d1a1e96c6e980ad51836e61f72c5221a53ff58e915a266c6ffe7ce20d9"
            },
            "downloads": -1,
            "filename": "lsm-0.5.10.tar.gz",
            "has_sig": false,
            "md5_digest": "b2cbe77072443e29cd31dc7fdfe50359",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<4,>=3.9",
            "size": 896151,
            "upload_time": "2025-02-27T16:54:55",
            "upload_time_iso_8601": "2025-02-27T16:54:55.471566Z",
            "url": "https://files.pythonhosted.org/packages/af/05/029d5a563e734878fed1d081801155ba4ec458c538eec1dfe1acaf894c2f/lsm-0.5.10.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-02-27 16:54:55",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "mosquito",
    "github_project": "python-lsm",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "tox": true,
    "lcname": "lsm"
}
        
Elapsed time: 0.46110s