inmemory-datastore-stub
An in-memory Google Cloud Datastore stub for unit-testing python-ndb code — no emulator, no Java, no network, no gcloud component to install.
It implements the Datastore RPC surface in pure Python, so your tests run at in-process speed.
Why this package exists
This is a maintained fork of InMemoryCloudDatastoreStub by Phil Lopreiato, which has been unmaintained since January 2021 and only works with google-cloud-datastore 1.x. On the 2.x libraries it fails outright:
google.cloud.datastore_v1.protowas removed in datastore 2.x, so importing the original raisesModuleNotFoundError- datastore 2.x wraps every message in proto-plus, which does not expose the protobuf-only API the stub relies on (
WhichOneof,HasField,CopyFrom,SerializeToString) - ndb 2.x calls the RPC methods by snake_case name (
lookup,commit,run_query, …) while the original only exposes the 1.x CamelCase names - ndb 2.x expects a
databaseattribute on the client allocate_idswas never implemented
This fork fixes all of the above and is tested on Python 3.10–3.14.
Install
pip install inmemory-datastore-stub
Usage
The simplest form — use the stub-backed client directly:
from google.cloud import ndb
from inmemory_datastore_stub import Client
class Movie(ndb.Model):
title = ndb.StringProperty()
def test_movies():
with Client().context():
Movie(title="Roman Holiday").put()
assert Movie.query().count() == 1
As a pytest fixture:
import pytest
from inmemory_datastore_stub import Client
@pytest.fixture
def ndb_context():
with Client().context():
yield
If the code under test constructs its own ndb.Client(), patch it globally:
from inmemory_datastore_stub import patch_ndb
with patch_ndb():
import myapp # myapp calls ndb.Client() at import time
myapp.run()
patch_ndb() restores the original ndb.Client on exit.
Catching bugs the stub would otherwise hide
Two behaviours of real Datastore make code fail in production that passes against a naive in-memory stub. Both are reproduced here, and both are opt-in so they never break an existing suite.
Eventual consistency
Non-ancestor queries in Datastore do not see writes immediately. Turn that on and the stub holds fresh writes back until you call catch_up():
client = Client(eventual_consistency=True)
with client.context():
Movie(title="fresh").put()
assert Movie.query().fetch() == [] # non-ancestor query lags, as in production
assert movie.key.get().title == "fresh" # key lookups are strongly consistent
client.catch_up()
assert len(Movie.query().fetch()) == 1
Ancestor queries and key lookups stay strongly consistent, matching Datastore.
Composite index validation
Datastore refuses any query whose composite index is not declared in index.yaml. Point the stub at yours and it refuses them too — with the same index suggestion Datastore gives you, ready to paste:
client = Client(index_yaml="index.yaml")
NoMatchingIndexError: no matching index found. recommended index is:
indexes:
- kind: Movie
properties:
- name: genre
- name: title
Needs PyYAML: pip install 'inmemory-datastore-stub[indexes]'.
What is supported
put, get, delete, put_multi/get_multi/delete_multi, get_or_insert, allocate_ids, transactions, and ancestor queries.
Queries: equality, inequality, !=, IN, NOT_IN, OR, repeated properties, StructuredProperty and ComputedProperty, ordering by several properties in either direction, offset/limit, count, cursors and fetch_page, projection and keys-only queries, and GQL.
Not reproduced: index build state, query planning details, and Datastore's own size and rate limits. For those, use the official Datastore emulator.
License
MIT — see LICENSE. Original work © 2020 Phil Lopreiato; modifications © 2026 Stepan Shamaiev.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file inmemory_datastore_stub-1.0.0.tar.gz.
File metadata
- Download URL: inmemory_datastore_stub-1.0.0.tar.gz
- Upload date:
- Size: 66.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5aa79f3cae1f48b257ffdedd77ca4baa829557e6d6bb22dfe9f5f637bb2a8449
|
|
| MD5 |
7ff5e47585c76d3fe1243dc2d7e6117c
|
|
| BLAKE2b-256 |
65eb05b2a5f7b3885d5e9875d94461a0d860e3e986152332a03096ebb03d5149
|
Provenance
The following attestation bundles were made for inmemory_datastore_stub-1.0.0.tar.gz:
Publisher:
publish.yml on skippdot/inmemory-datastore-stub
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
inmemory_datastore_stub-1.0.0.tar.gz -
Subject digest:
5aa79f3cae1f48b257ffdedd77ca4baa829557e6d6bb22dfe9f5f637bb2a8449 - Sigstore transparency entry: 2514982732
- Sigstore integration time:
-
Permalink:
skippdot/inmemory-datastore-stub@77d88243f1d9417ed2985f64dcb6f4686f785a66 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/skippdot
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@77d88243f1d9417ed2985f64dcb6f4686f785a66 -
Trigger Event:
release
-
Statement type:
File details
Details for the file inmemory_datastore_stub-1.0.0-py3-none-any.whl.
File metadata
- Download URL: inmemory_datastore_stub-1.0.0-py3-none-any.whl
- Upload date:
- Size: 17.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0ab7456d6a22a3b77ee1592df47d860981de086523210077707ef36307f1a5e7
|
|
| MD5 |
6167bd2fce2d0dd337f690a154109733
|
|
| BLAKE2b-256 |
f70fdddd809173ae9f7e9f0fda8c5f99a27d12cb472b0f0d807816543da9ab44
|
Provenance
The following attestation bundles were made for inmemory_datastore_stub-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on skippdot/inmemory-datastore-stub
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
inmemory_datastore_stub-1.0.0-py3-none-any.whl -
Subject digest:
0ab7456d6a22a3b77ee1592df47d860981de086523210077707ef36307f1a5e7 - Sigstore transparency entry: 2514983759
- Sigstore integration time:
-
Permalink:
skippdot/inmemory-datastore-stub@77d88243f1d9417ed2985f64dcb6f4686f785a66 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/skippdot
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@77d88243f1d9417ed2985f64dcb6f4686f785a66 -
Trigger Event:
release
-
Statement type: