Skip to main content

More than a collection, less than a database

Project description

nanotable

PyPI - Version PyPI - Python Version codecov

Nanotable is meant to bridge the gap between simple collections, such as list and dict, and full-on database tables. It lets you store a set of objects, index it by several keys, and more! It's fast, memory-efficient, and well-tested. The project draws inspiration from littletable, but is a completely original implementation. Its goal is to avoid feature bloat and maintain performance on par with built-in collections.

There are several situations where you might want to use Nanotable:

  • When you'd otherwise use a dict from an object's field to the object itself. Nanotable does that for you, and also provides additional features, such as checking for the existence of an element with a simple in check; hanging the value of the key field with Table.rekey, or catching accidental changes to the key field automatically.

  • When you'd otherwise use a bidict. That's a great library in its own right, but Nanotable provides some additional functionality such as storing extra non-hashable metadata along your objects (also see the previous point).

  • When you'd otherwise use a database. Nanotable spares you the computational and mental overhead. You probably have already written your own domain-specific version of Nanotable at some point in your life -- now you can use a well-tested library instead.

Installation

pip install nanotable

Usage

A basic usage example is given below:

from nanotable import Table

table = Table(of_dicts=True)\
    .primary_index_on("name")\
    .index_on("phone")

table.add({"name": "John Doe", "phone": "123-456-7890", "age": 25})
table.add({"name": "Jane Doe", "phone": "987-654-3210", "age": 26})
table.add({"name": "Barrack Obama", "age": "idk"})

table.at["Jane Doe"]  # {"name": "Jane Doe", "phone": "987-654-3210", "age": 26}
table.by.name["John Doe"]  # Same as above
table.by.phone["987-654-3210"]  # {"name": "Jane Doe", "phone": "987-654-3210", "age": 26}

table.remove(table.by.name["Barrack Obama"])

You can store any kind of object in the table. Specify of_dicts=True or getfield=getfield_item to use mappings (dict or anything with obj[key] item access); of_objects=True or getfield=getfield_attr to use objects with attributes (obj.key access); or any function with the signature (obj, key: str) -> Any | MISSING as getfield. You can also specify of=MyType to have the table infer either of_objects or of_dicts based on the anticipated element type.

Typing

The library is fully type-annotated. To make use of this, at the bare minimum you can specify the type of the objects you want to store in the table:

table = Table[Person](of_objects=True)
# or
table = Table[dict[str, Any]](of_dicts=True)

To add static typing to your indexes, you need to define a type with all of them:

class MyIndexes(Protocol):
    name: UniqueIndex[Person, str]
    phone: UniqueIndex[Person, str]

# The first template parameter is the object type;
# The second template parameter is protocol for `by`;
# The third template parameter is the primary index type.
table = Table[Person, MyIndexes, UniqueIndex[Person, str]](of_objects=True)
table.primary_index_on("name", required=True)
table.index_on("phone")

Indexes

TODO: UniqueIndex, MultiIndex

TODO: [sorted] extra and SortedUniqueIndex, SortedMultiIndex

Storage

TODO

Caveats

Indexed fields must be hashable, like with the built-in dict. This already imposes the restriction that they must be immutable (which is why you can't use, for example, a list as a dict key -- see here to learn why). With Nanotable, however, comes the additional restriction that the value of the indexed field itself mustn't be changed. For a dict this obviously isn't a concern since it stores keys and values separately, inaccessible to the user. Nanotable will try to detect this happening and warn you, but this unfortunately cannot be done reliably. If you wish to change an indexed field, the correct way to do that is to remove it from the table, change the field and re-add it. Nanotable provides a helper that does this for you:

with table.rekey(obj):
    obj.field = new_value

If a field is not indexed, this is unnecessary.

If you are certain that your code never modifies an indexed field of an object in a Table, you can disable the checks that issue the warning by setting nanotable.safety.disable_safety_checks to False. This provides a small performance improvement, with the downside that any potential bugs will be almost impossible to catch and will show up as subtly wrong results. It is recommended that you keep the safety checks on unless you know what you're doing.

Nanotable is also not thread-safe. When using a Table from multiple threads at once, use a synchronization primitive such as a threading.Lock to ensure that only one thread can interact with the table at a time. Multithreaded read-only access should theoretically be fine.

License

Nanotable is distributed under the terms of the MIT license. See the LICENSE.txt for details.

© 2026 abel1502

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nanotable-0.1.0.tar.gz (46.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nanotable-0.1.0-py3-none-any.whl (22.3 kB view details)

Uploaded Python 3

File details

Details for the file nanotable-0.1.0.tar.gz.

File metadata

  • Download URL: nanotable-0.1.0.tar.gz
  • Upload date:
  • Size: 46.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for nanotable-0.1.0.tar.gz
Algorithm Hash digest
SHA256 c973212a9ec77367c871d149a5c8beac9aa393ff20cba12ee903e5af30033e22
MD5 6818a3b4b34c78a44b336696acc21285
BLAKE2b-256 f9db4d3fc18e6e5ee947f403feb537a1a129e19be08f52ddd756f5068e749aad

See more details on using hashes here.

File details

Details for the file nanotable-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: nanotable-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 22.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for nanotable-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 791c0bc480c4c0941d900771a81137dfe0807fec0767e4b964c6bd20135bfe96
MD5 95a1bbac79f07d6d61a9b41d935f8570
BLAKE2b-256 eabcaeb4b085bb6cc8ef335e51720652f5df1e6fa8e68bb69d16a38c09a39f82

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page