Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.2.2
current_version = 0.3.0
commit = True
tag = True

Expand Down
22 changes: 22 additions & 0 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details

# Required
version: 2

# Set the OS, Python version, and other tools you might need
build:
os: ubuntu-24.04
tools:
python: "3.13"

# Build documentation in the "docs/" directory with Sphinx
sphinx:
configuration: docs/conf.py

python:
install:
- method: pip
path: .
- requirements: docs/requirements.txt

10 changes: 10 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.PHONY: docs tests

TESTS_PATH?=tests

docs:
rm -rf docs/_build && $(MAKE) -C docs html

tests:
PYTHONPATH=. pytest -s -vvvv -x $(TESTS_PATH)

104 changes: 7 additions & 97 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

[![PyPI - Version](https://img.shields.io/pypi/v/sqlalchemy-memory)](https://pypi.org/project/sqlalchemy-memory/)
[![CI](https://github.com/rundef/sqlalchemy-memory/actions/workflows/ci.yml/badge.svg)](https://github.com/rundef/sqlalchemy-memory/actions/workflows/ci.yml)
[![Documentation](https://app.readthedocs.org/projects/sqlalchemy-memory/badge/?version=latest)](https://sqlalchemy-memory.readthedocs.io/en/latest/)
[![PyPI - Downloads](https://img.shields.io/pypi/dm/sqlalchemy-memory)](https://pypistats.org/packages/sqlalchemy-memory)


Expand Down Expand Up @@ -34,6 +35,7 @@ Data is kept purely in RAM and is **volatile**: it is **not persisted across app
- **SQLAlchemy 2.0 support**: ORM & Core expressions, sync & async modes
- **Zero I/O overhead**: pure in‑RAM storage (`dict`/`list` under the hood)
- **Commit/rollback support**
- **Index support**: indexes are recognized and used for faster lookups
- **Merge and `get()` support**: like real SQLAlchemy behavior

## Installation
Expand All @@ -42,104 +44,10 @@ Data is kept purely in RAM and is **volatile**: it is **not persisted across app
pip install sqlalchemy-memory
```

## Quickstart
## Documentation

```python
from sqlalchemy import create_engine, select
from sqlalchemy.orm import sessionmaker, declarative_base, Mapped, mapped_column
from sqlalchemy_memory import MemorySession
[See the official documentation for usage examples](https://sqlalchemy-memory.readthedocs.io/en/latest/)

engine = create_engine("memory://")
Session = sessionmaker(
engine,
class_=MemorySession,
expire_on_commit=False,
)

Base = declarative_base()

class Item(Base):
__tablename__ = "items"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column()
def __repr__(self):
return f"Item(id={self.id} name={self.name})"

Base.metadata.create_all(engine)

# Use just like any other SQLAlchemy engine:
session = Session()

# Add & commit
item = Item(id=1, name="foo")
session.add(item)
session.commit()

# Query (no SQL under the hood: objects come straight back)
items = session.scalars(select(Item)).all()
print("Items", items)
assert items[0] is item
assert items[0].name == "foo"

# Delete & commit
session.delete(item)
session.commit()

# Confirm gone
assert session.scalars(select(Item)).all() == []
```

## Quickstart (async)

```python
import asyncio
from sqlalchemy import select
from sqlalchemy.ext.asyncio import create_async_engine
from sqlalchemy.orm import sessionmaker, declarative_base, Mapped, mapped_column
from sqlalchemy_memory import MemorySession, AsyncMemorySession

engine = create_async_engine("memory+asyncio://")
Session = sessionmaker(
engine,
class_=AsyncMemorySession,
sync_session_class=MemorySession,
expire_on_commit=False,
)

Base = declarative_base()

class Item(Base):
__tablename__ = "items"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column()

def __repr__(self):
return f"Item(id={self.id} name={self.name})"

Base.metadata.create_all(engine.sync_engine)

async def main():
async with Session() as session:
# Add & commit
item = Item(id=1, name="foo")
session.add(item)
await session.commit()

# Query (no SQL under the hood: objects come straight back)
items = (await session.scalars(select(Item))).all()
print("Items", items)
assert items[0] is item
assert items[0].name == "foo"

# Delete & commit
await session.delete(item)
await session.commit()

# Confirm gone
assert (await session.scalars(select(Item))).all() == []

asyncio.run(main())
```

## Status

Expand All @@ -155,11 +63,13 @@ Coming soon:

- Joins and relationships (limited)

- Compound indexes

- Better expression support in `update(...).values()` (e.g., +=)

## Testing

Simply run `pytest`
Simply run `make tests`

## License

Expand Down
20 changes: 20 additions & 0 deletions docs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build

# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
38 changes: 38 additions & 0 deletions docs/commit_rollback.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
Commit / Rollback
=================

sqlalchemy-memory fully supports transactional behavior, including commit and rollback operations. Changes are staged until committed, and can be safely reverted using rollback().

Commit
------

.. code-block:: python

with SessionFactory() as session:
session.add(Item(id=1, name="foo"))
session.commit()

item = session.get(Item, 1)
print(item.name) # foo
item.name = "updated"
session.commit()

print(item.name) # updated

Rollback
--------

Use `rollback()` to undo uncommitted changes:

.. code-block:: python

with SessionFactory() as session:
session.add(Item(id=1, name="foo"))
session.commit()

item = session.get(Item, 1)
print(item.name) # foo
item.name = "updated"
session.rollback()

print(item.name) # foo
53 changes: 53 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
import os
import sys
sys.path.insert(0, os.path.abspath('..'))


# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information

project = 'sqlalchemy_memory'
copyright = '2025, rundef'
author = 'rundef'

# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.napoleon", # for Google/NumPy-style docstrings
"sphinx.ext.viewcode",
"sphinx.ext.autosummary",
]
#autosummary_generate = True

templates_path = ['_templates']
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']



# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output

#html_theme = 'furo'
#html_static_path = ['_static']


html_theme = 'sphinx_rtd_theme'
html_theme_options = {
'logo_only': False,
'prev_next_buttons_location': 'bottom',
# Toc options
'collapse_navigation': True,
'sticky_navigation': True,
'navigation_depth': 4,
'includehidden': True,
'titles_only': False,
'navigation_with_keys': True,
'style_external_links': True,
}

12 changes: 12 additions & 0 deletions docs/delete.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Delete
======

Deleting objects from the memory store uses standard SQLAlchemy syntax.

.. code-block:: python

session.delete(obj) # orm style

stmt = delete(Item).where(Item.id < 3) # core style
result = session.execute(stmt)
print(result.rowcount)
Loading