API Reference

Simulate a MongoDB server.

Request Spec

TODO

Matcher Spec

TODO

Reply Spec

TODO

class mockupdb.MockupDB(port=None, verbose=False, request_timeout=10, auto_ismaster=None, ssl=False)

A simulated mongod or mongos.

Call run to start the server, and always close it to avoid exceptions during interpreter shutdown.

See the tutorial for comprehensive examples.

Optional parameters:
 
  • port: listening port number. If not specified, choose some unused port and return the port number from run.
  • verbose: if True, print requests and replies to stdout.
  • request_timeout: seconds to wait for the next client request, or else assert. Default 10 seconds. Pass int(1e6) to disable.
  • auto_ismaster: pass True to autorespond {'ok': 1} to ismaster requests, or pass a dict or OpReply.
  • ssl: pass True to require SSL.
address

The listening (host, port).

address_string

The listening “host:port”.

autoresponds(*args, **kwargs)

Send a canned reply to all matching client requests.

matcher is a Matcher or a command name, or an instance of OpInsert, OpQuery, etc.

>>> s = MockupDB()
>>> port = s.run()
>>>
>>> from pymongo import MongoClient
>>> client = MongoClient(s.uri)
>>> responder = s.autoresponds('ismaster')
>>> client.admin.command('ismaster') == {'ok': 1}
True

The remaining arguments are a reply spec:

>>> responder = s.autoresponds('bar', ok=0, errmsg='err')
>>> client.db.command('bar')
Traceback (most recent call last):
...
OperationFailure: command SON([('bar', 1)]) on namespace db.$cmd failed: err
>>> responder = s.autoresponds(OpQuery(namespace='db.collection'),
...                            [{'_id': 1}, {'_id': 2}])
>>> list(client.db.collection.find()) == [{'_id': 1}, {'_id': 2}]
True
>>> responder = s.autoresponds(OpQuery, {'a': 1}, {'a': 2})
>>> list(client.db.collection.find()) == [{'a': 1}, {'a': 2}]
True

Remove an autoresponder like:

>>> responder.cancel()

If the request currently at the head of the queue matches, it is popped and replied to. Future matching requests skip the queue.

>>> future = go(client.db.command, 'baz')
>>> responder = s.autoresponds('baz', {'key': 'value'})
>>> future() == {'ok': 1, 'key': 'value'}
True

Responders are applied in order, most recently added first, until one matches:

>>> responder = s.autoresponds('baz')
>>> client.db.command('baz') == {'ok': 1}
True
>>> responder.cancel()
>>> # The previous responder takes over again.
>>> client.db.command('baz') == {'ok': 1, 'key': 'value'}
True

You can pass a request handler in place of the reply spec. Return True if you handled the request:

>>> responder = s.autoresponds('baz', lambda r: r.ok(a=2))

The standard Request.ok, replies, fail, hangup and so on all return True to make them suitable as handler functions.

>>> client.db.command('baz') == {'ok': 1, 'a': 2}
True

If the request is not handled, it is checked against the remaining responders, or enqueued if none match.

You can pass the handler as the only argument so it receives all requests. For example you could log them, then return None to allow other handlers to run:

>>> def logger(request):
...     if not request.matches('ismaster'):
...         print('logging: %r' % request)
>>> responder = s.autoresponds(logger)
>>> client.db.command('baz') == {'ok': 1, 'a': 2}
logging: Command({"baz": 1}, flags=SlaveOkay, namespace="db")
True

The synonym subscribe better expresses your intent if your handler never returns True:

>>> subscriber = s.subscribe(logger)
cancel_responder(*args, **kwargs)

Cancel a responder that was registered with autoresponds.

command_err(*args, **kwargs)

Call command_err on the currently enqueued request.

fail(*args, **kwargs)

Call fail on the currently enqueued request.

gets(*args, **kwargs)

Synonym for receives.

got(*args, **kwargs)

Does request match the given request spec?

>>> s = MockupDB(auto_ismaster=True)
>>> port = s.run()
>>> s.got(timeout=0)  # No request enqueued.
False
>>> from pymongo import MongoClient
>>> client = MongoClient(s.uri)
>>> future = go(client.db.command, 'foo')
>>> s.got('foo')
True
>>> s.got(Command('foo', namespace='db'))
True
>>> s.got(Command('foo', key='value'))
False
>>> s.ok()
>>> future() == {'ok': 1}
True
>>> s.stop()
hangs_up()

Synonym for hangup.

hangup()

Call hangup on the currently enqueued request.

host

The listening hostname.

label

Label for logging, or None.

ok(*args, **kwargs)

Synonym for replies.

pop(*args, **kwargs)

Synonym for receives.

port

The listening port.

receive(*args, **kwargs)

Synonym for receives.

receives(*args, **kwargs)

Pop the next Request and assert it matches.

Returns None if the server is stopped.

Pass a Request or request pattern to specify what client request to expect. See the tutorial for examples. Pass timeout as a keyword argument to override this server’s request_timeout.

replies(*args, **kwargs)

Call reply on the currently enqueued request.

reply(*args, **kwargs)

Synonym for replies.

request

The currently enqueued Request, or None.

Warning

This property is useful to check what the current request is, but the pattern server.request.replies() is dangerous: you must follow it with server.pop() or the current request remains enqueued. Better to reply with server.pop().replies() than server.request.replies() or any variation on it.

requests_count

Number of requests this server has received.

Includes autoresponded requests.

run(*args, **kwargs)

Begin serving. Returns the bound port.

running

If this server is started and not stopped.

send(*args, **kwargs)

Synonym for replies.

sends(*args, **kwargs)

Synonym for replies.

stop(*args, **kwargs)

Stop serving. Always call this to clean up after yourself.

subscribe(*args, **kwargs)

Synonym for autoresponds.

uri

Connection string to pass to MongoClient.

verbose

If verbose logging is turned on.

wait(*args, **kwargs)

Synonym for got.

mockupdb.go(fn, *args, **kwargs)

Launch an operation on a thread and get a handle to its future result.

>>> from time import sleep
>>> def print_sleep_print(duration):
...     sleep(duration)
...     print('hello from background thread')
...     sleep(duration)
...     print('goodbye from background thread')
...     return 'return value'
...
>>> future = go(print_sleep_print, 0.1)
>>> sleep(0.15)
hello from background thread
>>> print('main thread')
main thread
>>> result = future()
goodbye from background thread
>>> result
'return value'
mockupdb.going(*args, **kwds)

Launch a thread and wait for its result before exiting the code block.

>>> with going(lambda: 'return value') as future:
...    pass
>>> future()  # Won't block, the future is ready by now.
'return value'

Or discard the result:

>>> with going(lambda: "don't care"):
...    pass

If an exception is raised within the context, the result is lost:

>>> with going(lambda: 'return value') as future:
...    assert 1 == 0
Traceback (most recent call last):
...
AssertionError
mockupdb.wait_until(predicate, success_description, timeout=10)

Wait up to 10 seconds (by default) for predicate to be true.

E.g.:

wait_until(lambda: client.primary == (‘a’, 1),
‘connect to the primary’)

If the lambda-expression isn’t true after 10 seconds, we raise AssertionError(“Didn’t ever connect to the primary”).

Returns the predicate’s first true value.

mockupdb.interactive_server(port=27017, verbose=True, all_ok=False, name='MockupDB', ssl=False)

A MockupDB that the mongo shell can connect to.

Call run on the returned server, and clean it up with stop.

If all_ok is True, replies {ok: 1} to anything unmatched by a specific responder.

class mockupdb.Request(*args, **kwargs)

Base class for Command, OpInsert, and so on.

Some useful asserts you can do in tests:

>>> {'_id': 0} in OpInsert({'_id': 0})
True
>>> {'_id': 1} in OpInsert({'_id': 0})
False
>>> {'_id': 1} in OpInsert([{'_id': 0}, {'_id': 1}])
True
>>> {'_id': 1} == OpInsert([{'_id': 0}, {'_id': 1}])[1]
True
>>> 'field' in Command(field=1)
True
>>> 'field' in Command()
False
>>> 'field' in Command('ismaster')
False
>>> Command(ismaster=False)['ismaster'] is False
True
assert_matches(*args, **kwargs)

Assert this matches a matcher spec and return self.

client_port

Client connection’s TCP port.

command_err(code=1, errmsg='MockupDB command failure', *args, **kwargs)

Error reply to a command.

Returns True so it is suitable as an autoresponds handler.

doc

The request document, if there is exactly one.

Use this for queries, commands, and legacy deletes. Legacy writes may have many documents, OP_GET_MORE and OP_KILL_CURSORS have none.

docs

The request documents, if any.

fail(err='MockupDB query failure', *args, **kwargs)

Reply to a query with the QueryFailure flag and an ‘$err’ key.

Returns True so it is suitable as an autoresponds handler.

flags

The request flags or None.

hangs_up()

Synonym for hangup.

hangup()

Close the connection.

Returns True so it is suitable as an autoresponds handler.

matches(*args, **kwargs)

True if this matches a matcher spec.

namespace

The operation namespace or None.

ok(*args, **kwargs)

Synonym for replies.

replies(*args, **kwargs)

Send an OpReply to the client.

The default reply to a command is {'ok': 1}, otherwise the default is empty (no documents).

Returns True so it is suitable as an autoresponds handler.

reply(*args, **kwargs)

Synonym for replies.

request_id

The request id or None.

send(*args, **kwargs)

Synonym for replies.

sends(*args, **kwargs)

Synonym for replies.

server

The MockupDB server.

slave_ok

True if the SlaveOkay wire protocol flag is set.

slave_okay

Synonym for slave_ok.

class mockupdb.Command(*args, **kwargs)

A command the client executes on the server.

command_name

The command name or None.

>>> Command({'count': 'collection'}).command_name
'count'
>>> Command('aggregate', 'collection', cursor=absent).command_name
'aggregate'
replies_to_gle(**kwargs)

Send a getlasterror response.

Defaults to {ok: 1, err: null}. Add or override values by passing keyword arguments.

Returns True so it is suitable as an autoresponds handler.

class mockupdb.OpQuery(*args, **kwargs)

A query (besides a command) the client executes on the server.

>>> OpQuery({'i': {'$gt': 2}}, fields={'j': False})
OpQuery({"i": {"$gt": 2}}, fields={"j": false})
fields

Client query’s fields selector or None.

num_to_return

Client query’s numToReturn or None.

num_to_skip

Client query’s numToSkip or None.

classmethod unpack(msg, client, server, request_id)

Parse message and return an OpQuery or Command.

Takes the client message as bytes, the client and server socket objects, and the client request id.

class mockupdb.OpGetMore(**kwargs)

An OP_GET_MORE the client executes on the server.

cursor_id

The client message’s cursorId field.

num_to_return

The client message’s numToReturn field.

classmethod unpack(msg, client, server, request_id)

Parse message and return an OpGetMore.

Takes the client message as bytes, the client and server socket objects, and the client request id.

class mockupdb.OpKillCursors(**kwargs)

An OP_KILL_CURSORS the client executes on the server.

cursor_ids

List of cursor ids the client wants to kill.

classmethod unpack(msg, client, server, _)

Parse message and return an OpKillCursors.

Takes the client message as bytes, the client and server socket objects, and the client request id.

class mockupdb.OpInsert(*args, **kwargs)

A legacy OP_INSERT the client executes on the server.

classmethod unpack(msg, client, server, request_id)

Parse message and return an OpInsert.

Takes the client message as bytes, the client and server socket objects, and the client request id.

class mockupdb.OpUpdate(*args, **kwargs)

A legacy OP_UPDATE the client executes on the server.

classmethod unpack(msg, client, server, request_id)

Parse message and return an OpUpdate.

Takes the client message as bytes, the client and server socket objects, and the client request id.

class mockupdb.OpDelete(*args, **kwargs)

A legacy OP_DELETE the client executes on the server.

classmethod unpack(msg, client, server, request_id)

Parse message and return an OpDelete.

Takes the client message as bytes, the client and server socket objects, and the client request id.

class mockupdb.OpReply(*args, **kwargs)

A reply from MockupDB to the client.

doc

Contents of reply.

Useful for replies to commands; replies to other messages may have no documents or multiple documents.

docs

The reply documents, if any.

reply_bytes(request)

Take a Request and return an OP_REPLY message as bytes.

update(*args, **kwargs)

Update the document. Same as dict().update().

>>> reply = OpReply({'ismaster': True})
>>> reply.update(maxWireVersion=3)
>>> reply.doc['maxWireVersion']
3
>>> reply.update({'maxWriteBatchSize': 10, 'msg': 'isdbgrid'})
class mockupdb.Matcher(*args, **kwargs)

Matches a subset of Request objects.

Initialized with a request spec.

Used by receives to assert the client sent the expected request, and by got to test if it did and return True or False. Used by autoresponds to match requests with autoresponses.

matches(*args, **kwargs)

Take a request spec and return True or False.

The empty matcher matches anything:

>>> Matcher().matches({'a': 1})
True
>>> Matcher().matches({'a': 1}, {'a': 1})
True
>>> Matcher().matches('ismaster')
True

A matcher’s document matches if its key-value pairs are a subset of the request’s:

>>> Matcher({'a': 1}).matches({'a': 1})
True
>>> Matcher({'a': 2}).matches({'a': 1})
False
>>> Matcher({'a': 1}).matches({'a': 1, 'b': 1})
True

Prohibit a field:

>>> Matcher({'field': absent})
Matcher(Request({"field": {"absent": 1}}))
>>> Matcher({'field': absent}).matches({'field': 1})
False
>>> Matcher({'field': absent}).matches({'otherField': 1})
True

Order matters if you use an OrderedDict:

>>> doc0 = OrderedDict([('a', 1), ('b', 1)])
>>> doc1 = OrderedDict([('b', 1), ('a', 1)])
>>> Matcher(doc0).matches(doc0)
True
>>> Matcher(doc0).matches(doc1)
False

The matcher must have the same number of documents as the request:

>>> Matcher().matches()
True
>>> Matcher([]).matches([])
True
>>> Matcher({'a': 2}).matches({'a': 1}, {'a': 1})
False

By default, it matches any opcode:

>>> m = Matcher()
>>> m.matches(OpQuery)
True
>>> m.matches(OpInsert)
True

You can specify what request opcode to match:

>>> m = Matcher(OpQuery)
>>> m.matches(OpInsert, {'_id': 1})
False
>>> m.matches(OpQuery, {'_id': 1})
True

Commands are queries on some database’s “database.$cmd” namespace. They are specially prohibited from matching regular queries:

>>> Matcher(OpQuery).matches(Command)
False
>>> Matcher(Command).matches(Command)
True
>>> Matcher(OpQuery).matches(OpQuery)
True
>>> Matcher(Command).matches(OpQuery)
False

The command name is matched case-insensitively:

>>> Matcher(Command('ismaster')).matches(Command('IsMaster'))
True

You can match properties specific to certain opcodes:

>>> m = Matcher(OpGetMore, num_to_return=3)
>>> m.matches(OpGetMore())
False
>>> m.matches(OpGetMore(num_to_return=2))
False
>>> m.matches(OpGetMore(num_to_return=3))
True
>>> m = Matcher(OpQuery(namespace='db.collection'))
>>> m.matches(OpQuery)
False
>>> m.matches(OpQuery(namespace='db.collection'))
True

It matches any wire protocol header bits you specify:

>>> m = Matcher(flags=QUERY_FLAGS['SlaveOkay'])
>>> m.matches(OpQuery({'_id': 1}))
False
>>> m.matches(OpQuery({'_id': 1}, flags=QUERY_FLAGS['SlaveOkay']))
True

If you match on flags, be careful to also match on opcode. For example, if you simply check that the flag in bit position 0 is set:

>>> m = Matcher(flags=INSERT_FLAGS['ContinueOnError'])

... you will match any request with that flag:

>>> m.matches(OpDelete, flags=DELETE_FLAGS['SingleRemove'])
True

So specify the opcode, too:

>>> m = Matcher(OpInsert, flags=INSERT_FLAGS['ContinueOnError'])
>>> m.matches(OpDelete, flags=DELETE_FLAGS['SingleRemove'])
False
prototype

The prototype Request used to match actual requests with.