PyThermite is a performance-centered, native Python object indexer and graph datastore.
It allows you to index arbitrary Python objects, filter them by attributes and nested attributes, navigating graph-style relationships — all with O(1) lookups on attributes and O(log n) range queries.
-
Python-first design
Classes are the best way to enforce types. Class methods are the best way to mutate contents and communicate changes to a third-party datastore. -
100% dynamic schema
No schema or types are enforced at the datastore or query level. You can load any Python object, indexing all attributes. -
Indexable schema objects
Data objects areIndexabletypes. Updates made to the object immediately reflect on theIndex. -
Constant-time updates
Unlike other data stores, schema updates are O(1) with no restructuring needed. This encourages iterative loading, filtering, mutating, and exporting.
- In-memory indexing of arbitrary Python objects
- Filter and query with attribute lookups or composable expressions
- Graph-style traversal from root nodes
- Perfect for ad-hoc migrations, analytics, and breaking down JSON
from PyThermite import Index, Indexable, QueryExpr as Q
class Person(Indexable):
name: str
age: int
employer: 'Store'
wage: int
def change_salary(self, amount: int):
self.wage += amount
class Store(Indexable):
name: str
address: str
owner: Person
person_index = Index()
big_python_store = Store(
name="Big Python Store",
address="123 Python St",
)
alice = Person(name="Alice", age=30, employer=big_python_store, wage=70000)
bob = Person(name="Bob", age=25, employer=big_python_store, wage=50000)
person_index.add_object_many([bob, alice])
is_30 = person_index.reduced_query(Q.eq("age", 30))
assert len(is_30.collect()) == 1
assert is_30.collect()[0].name == "Alice"
del is_30
high_wage = person_index.reduced_query(Q.gt("wage", 60000))
assert len(high_wage.collect()) == 1
assert high_wage.collect()[0].name == "Alice"
del high_wage
person: Person
for person in person_index.reduced_query(Q.lt("wage", 55_000)).collect():
print(f"{person.name} works at {person.employer.name} and earns ${person.wage}")
person.change_salary(10_000)
high_wage = person_index.reduced_query(Q.ge("wage", 60000))
assert len(high_wage.collect()) == 2
assert {p.name for p in high_wage.collect()} == {"Alice", "Bob"}
del high_wage
# nested queries
employees = person_index.reduced_query(
Q.eq("employer.name", "Big Python Store")
).collect()
assert len(employees) == 2
assert {e.name for e in employees} == {"Alice", "Bob"}pip install pythermite