New upstream version 1.8.0
Ondřej Nový
3 years ago
4 | 4 | Development Lead |
5 | 5 | ---------------- |
6 | 6 | |
7 | * A. Jesse Jiryu Davis <jesse@mongodb.com> | |
7 | * A\. Jesse Jiryu Davis <jesse@mongodb.com> | |
8 | 8 | |
9 | 9 | Contributors |
10 | 10 | ------------ |
11 | 11 | |
12 | * Sean Purcell | |
12 | 13 | * George Wilson |
13 | 14 | * Shane Harvey |
1 | 1 | |
2 | 2 | Changelog |
3 | 3 | ========= |
4 | ||
5 | Next Release | |
6 | ------------ | |
7 | ||
8 | MockupDB supports Python 3.4 through 3.8; it no longer supports Python 2.6 or | |
9 | Python 3.3. | |
10 | ||
11 | New method ``MockupDB.append_responder`` to add an autoresponder of last resort. | |
12 | ||
13 | Fix a bug in ``interactive_server`` with ``all_ok=True``. It had returned an | |
14 | empty isMaster response, causing drivers to throw errors like "Server at | |
15 | localhost:27017 reports wire version 0, but this version of PyMongo requires at | |
16 | least 2 (MongoDB 2.6)." | |
17 | ||
18 | Stop logging "OSError: [WinError 10038] An operation was attempted on something | |
19 | that is not a socket" on Windows after a client disconnects. | |
20 | ||
21 | Parse OP_MSGs with any number of sections in any order. This allows write | |
22 | commands from the mongo shell, which sends sections in the opposite order from | |
23 | drivers. Handle OP_MSGs with checksums, such as those sent by the mongo shell | |
24 | beginning in 4.2. | |
4 | 25 | |
5 | 26 | 1.7.0 (2018-12-02) |
6 | 27 | ------------------ |
98 | 98 | 2. If the pull request adds functionality, the docs should be updated. Put |
99 | 99 | your new functionality into a function with a docstring, and add the |
100 | 100 | feature to the list in README.rst. |
101 | 3. The pull request should work for Python 2.6, 2.7, 3.3, and 3.4. Check that | |
101 | 3. The pull request should work for Python 2.7 and 3.4+. Check that | |
102 | 102 | tests pass in all versions with `tox`. |
103 | 103 | |
104 | 104 | Tips |
0 | Metadata-Version: 1.1 | |
0 | Metadata-Version: 1.2 | |
1 | 1 | Name: mockupdb |
2 | Version: 1.7.0 | |
2 | Version: 1.8.0 | |
3 | 3 | Summary: MongoDB Wire Protocol server library |
4 | 4 | Home-page: https://github.com/ajdavis/mongo-mockup-db |
5 | 5 | Author: A. Jesse Jiryu Davis |
6 | 6 | Author-email: jesse@mongodb.com |
7 | 7 | License: Apache License, Version 2.0 |
8 | Description-Content-Type: UNKNOWN | |
9 | 8 | Description: ======== |
10 | 9 | MockupDB |
11 | 10 | ======== |
20 | 19 | |
21 | 20 | Changelog |
22 | 21 | ========= |
22 | ||
23 | Next Release | |
24 | ------------ | |
25 | ||
26 | MockupDB supports Python 3.4 through 3.8; it no longer supports Python 2.6 or | |
27 | Python 3.3. | |
28 | ||
29 | New method ``MockupDB.append_responder`` to add an autoresponder of last resort. | |
30 | ||
31 | Fix a bug in ``interactive_server`` with ``all_ok=True``. It had returned an | |
32 | empty isMaster response, causing drivers to throw errors like "Server at | |
33 | localhost:27017 reports wire version 0, but this version of PyMongo requires at | |
34 | least 2 (MongoDB 2.6)." | |
35 | ||
36 | Stop logging "OSError: [WinError 10038] An operation was attempted on something | |
37 | that is not a socket" on Windows after a client disconnects. | |
38 | ||
39 | Parse OP_MSGs with any number of sections in any order. This allows write | |
40 | commands from the mongo shell, which sends sections in the opposite order from | |
41 | drivers. Handle OP_MSGs with checksums, such as those sent by the mongo shell | |
42 | beginning in 4.2. | |
23 | 43 | |
24 | 44 | 1.7.0 (2018-12-02) |
25 | 45 | ------------------ |
141 | 161 | Classifier: License :: OSI Approved :: Apache Software License |
142 | 162 | Classifier: Natural Language :: English |
143 | 163 | Classifier: Programming Language :: Python :: 2 |
144 | Classifier: Programming Language :: Python :: 2.6 | |
145 | 164 | Classifier: Programming Language :: Python :: 2.7 |
146 | 165 | Classifier: Programming Language :: Python :: 3 |
147 | Classifier: Programming Language :: Python :: 3.3 | |
148 | 166 | Classifier: Programming Language :: Python :: 3.4 |
167 | Classifier: Programming Language :: Python :: 3.5 | |
168 | Classifier: Programming Language :: Python :: 3.6 | |
169 | Classifier: Programming Language :: Python :: 3.7 | |
170 | Classifier: Programming Language :: Python :: 3.8 | |
171 | Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.* |
56 | 56 | default_role = 'py:obj' |
57 | 57 | |
58 | 58 | doctest_global_setup = """ |
59 | try: | |
60 | from collections import OrderedDict | |
61 | except: | |
62 | from ordereddict import OrderedDict # Python 2.6, "pip install ordereddict" | |
59 | from collections import OrderedDict | |
63 | 60 | from mockupdb import * |
64 | 61 | """ |
65 | 62 |
3 | 3 | |
4 | 4 | Install MockupDB with pip: |
5 | 5 | |
6 | $ python -m pip install mongo-mockup-db | |
6 | $ python -m pip install mockupdb |
19 | 19 | >>> server = MockupDB() |
20 | 20 | >>> port = server.run() # Returns the TCP port number it listens on. |
21 | 21 | >>> from pymongo import MongoClient |
22 | >>> client = MongoClient(server.uri) | |
22 | >>> client = MongoClient(server.uri, connectTimeoutMS=999999) | |
23 | 23 | |
24 | 24 | When the client connects it calls the "ismaster" command, then blocks until |
25 | the server responds. MockupDB receives the "ismaster" command but does not | |
26 | respond until you tell it to: | |
25 | the server responds. By default it throws an error if the server doesn't | |
26 | respond in 10 seconds, so set a longer timeout. | |
27 | ||
28 | MockupDB receives the "ismaster" command but does not respond until you tell it | |
29 | to: | |
27 | 30 | |
28 | 31 | >>> request = server.receives() |
29 | 32 | >>> request.command_name |
18 | 18 | |
19 | 19 | __author__ = 'A. Jesse Jiryu Davis' |
20 | 20 | __email__ = 'jesse@mongodb.com' |
21 | __version__ = '1.7.0' | |
21 | __version__ = '1.8.0' | |
22 | 22 | |
23 | 23 | import atexit |
24 | 24 | import contextlib |
26 | 26 | import errno |
27 | 27 | import functools |
28 | 28 | import inspect |
29 | import operator | |
29 | 30 | import os |
30 | 31 | import random |
31 | 32 | import select |
38 | 39 | import weakref |
39 | 40 | import sys |
40 | 41 | from codecs import utf_8_decode as _utf_8_decode |
42 | from collections import OrderedDict | |
41 | 43 | |
42 | 44 | try: |
43 | 45 | from queue import Queue, Empty |
48 | 50 | from collections.abc import Mapping |
49 | 51 | except: |
50 | 52 | from collections import Mapping |
51 | ||
52 | try: | |
53 | from collections import OrderedDict | |
54 | except: | |
55 | from ordereddict import OrderedDict # Python 2.6, "pip install ordereddict" | |
56 | 53 | |
57 | 54 | try: |
58 | 55 | from io import StringIO |
202 | 199 | self._event = threading.Event() |
203 | 200 | |
204 | 201 | def result(self, timeout=None): |
205 | self._event.wait(timeout) | |
206 | # wait() always returns None in Python 2.6. | |
207 | if not self._event.is_set(): | |
202 | if not self._event.wait(timeout): | |
208 | 203 | raise AssertionError('timed out waiting for Future') |
209 | 204 | return self._result |
210 | 205 | |
274 | 269 | |
275 | 270 | OP_MSG_FLAGS = OrderedDict([ |
276 | 271 | ('checksumPresent', 1), |
277 | ('moreToCome', 2)]) | |
272 | ('moreToCome', 2), | |
273 | ('exhaustAllowed', 16)]) | |
274 | ||
275 | _ALL_OP_MSG_FLAGS = functools.reduce(operator.or_, OP_MSG_FLAGS.values()) | |
278 | 276 | |
279 | 277 | _UNPACK_BYTE = struct.Struct("<b").unpack |
280 | 278 | _UNPACK_INT = struct.Struct("<i").unpack |
627 | 625 | Takes the client message as bytes, the client and server socket objects, |
628 | 626 | and the client request id. |
629 | 627 | """ |
628 | payload_document = OrderedDict() | |
630 | 629 | flags, = _UNPACK_UINT(msg[:4]) |
631 | 630 | pos = 4 |
632 | first_payload_type, = _UNPACK_BYTE(msg[pos:pos + 1]) | |
633 | pos += 1 | |
634 | first_payload_size, = _UNPACK_INT(msg[pos:pos + 4]) | |
635 | if flags != 0 and flags != 2: | |
636 | raise ValueError('OP_MSG flag must be 0 or 2 not %r' % (flags,)) | |
637 | if first_payload_type != 0: | |
638 | raise ValueError('First OP_MSG payload type must be 0 not %r' % ( | |
639 | first_payload_type,)) | |
640 | ||
641 | # Parse the initial document and add the optional payload type 1. | |
642 | payload_document = bson.decode_all(msg[pos:pos + first_payload_size], | |
643 | CODEC_OPTIONS)[0] | |
644 | pos += first_payload_size | |
645 | if len(msg) != pos: | |
631 | if flags & ~_ALL_OP_MSG_FLAGS: | |
632 | raise ValueError( | |
633 | 'OP_MSG flags has reserved bits set.' | |
634 | ' Allowed flags: 0x%x. Provided flags: 0x%x' % ( | |
635 | _ALL_OP_MSG_FLAGS, flags)) | |
636 | ||
637 | checksum_present = flags & OP_MSG_FLAGS['checksumPresent'] | |
638 | checksum = None | |
639 | if checksum_present: | |
640 | msg_len_without_checksum = len(msg) - 4 | |
641 | else: | |
642 | msg_len_without_checksum = len(msg) | |
643 | ||
644 | while pos < msg_len_without_checksum: | |
646 | 645 | payload_type, = _UNPACK_BYTE(msg[pos:pos + 1]) |
647 | 646 | pos += 1 |
648 | if payload_type != 1: | |
649 | raise ValueError('Second OP_MSG payload type must be 1 not %r' | |
650 | % (payload_type,)) | |
651 | section_size, = _UNPACK_INT(msg[pos:pos + 4]) | |
652 | if len(msg) != pos + section_size: | |
653 | raise ValueError('More than two OP_MSG sections unsupported') | |
654 | pos += 4 | |
655 | identifier, pos = _get_c_string(msg, pos) | |
656 | documents = bson.decode_all(msg[pos:], CODEC_OPTIONS) | |
657 | payload_document[identifier] = documents | |
647 | payload_size, = _UNPACK_INT(msg[pos:pos + 4]) | |
648 | if payload_type == 0: | |
649 | doc = bson.decode_all(msg[pos:pos + payload_size], | |
650 | CODEC_OPTIONS)[0] | |
651 | payload_document.update(doc) | |
652 | pos += payload_size | |
653 | elif payload_type == 1: | |
654 | section_size, = _UNPACK_INT(msg[pos:pos + 4]) | |
655 | pos += 4 | |
656 | identifier, pos = _get_c_string(msg, pos) | |
657 | # Section starts w/ 4-byte size prefix, identifier ends w/ nil. | |
658 | documents_len = section_size - len(identifier) - 1 - 4 | |
659 | documents = bson.decode_all(msg[pos:pos + documents_len], | |
660 | CODEC_OPTIONS) | |
661 | payload_document[identifier] = documents | |
662 | pos += documents_len | |
663 | ||
664 | remaining = len(msg) - pos | |
665 | if checksum_present: | |
666 | if remaining != 4: | |
667 | raise ValueError( | |
668 | 'OP_MSG has checksumPresent flag set, expected 4 bytes' | |
669 | ' remaining but have %d bytes remaining' % (remaining,)) | |
670 | ||
671 | checksum = _UNPACK_UINT(msg[pos:pos+4])[0] | |
672 | else: | |
673 | if remaining != 0: | |
674 | raise ValueError( | |
675 | 'OP_MSG has no checksumPresent flag, expected 0 bytes' | |
676 | ' remaining but have %d bytes remaining' % (remaining,)) | |
658 | 677 | |
659 | 678 | database = payload_document['$db'] |
660 | 679 | return OpMsg(payload_document, namespace=database, flags=flags, |
661 | _client=client, request_id=request_id, | |
680 | _client=client, request_id=request_id, checksum=checksum, | |
662 | 681 | _server=server) |
663 | 682 | |
664 | 683 | def __init__(self, *args, **kwargs): |
684 | checksum = kwargs.pop('checksum', None) | |
665 | 685 | super(OpMsg, self).__init__(*args, **kwargs) |
686 | self._checksum = checksum | |
666 | 687 | if len(self._docs) > 1: |
667 | 688 | raise_args_err('OpMsg too many documents', ValueError) |
668 | 689 | |
671 | 692 | """True if this OpMsg can read from a secondary.""" |
672 | 693 | read_preference = self.doc.get('$readPreference') |
673 | 694 | return read_preference and read_preference.get('mode') != 'primary' |
695 | ||
696 | @property | |
697 | def checksum(self): | |
698 | """The provided checksum, if set, else None.""" | |
699 | return self._checksum | |
674 | 700 | |
675 | 701 | slave_okay = slave_ok |
676 | 702 | """Synonym for `.slave_ok`.""" |
1496 | 1522 | >>> client.close() |
1497 | 1523 | >>> s.stop() |
1498 | 1524 | """ |
1525 | return self._insert_responder("top", matcher, *args, **kwargs) | |
1526 | ||
1527 | subscribe = autoresponds | |
1528 | """Synonym for `.autoresponds`.""" | |
1529 | ||
1530 | @_synchronized | |
1531 | def append_responder(self, matcher, *args, **kwargs): | |
1532 | """Add a responder of last resort. | |
1533 | ||
1534 | Like `.autoresponds`, but instead of adding a responder to the top of | |
1535 | the stack, add it to the bottom. This responder will be called if no | |
1536 | others match. | |
1537 | """ | |
1538 | return self._insert_responder("bottom", matcher, *args, **kwargs) | |
1539 | ||
1540 | def _insert_responder(self, where, matcher, *args, **kwargs): | |
1499 | 1541 | responder = _AutoResponder(self, matcher, *args, **kwargs) |
1500 | self._autoresponders.append(responder) | |
1542 | if where == "top": | |
1543 | self._autoresponders.append(responder) | |
1544 | elif where == "bottom": | |
1545 | self._autoresponders.insert(0, responder) | |
1546 | else: | |
1547 | raise RuntimeError("Invalid 'where': %r" % (where,)) | |
1548 | ||
1501 | 1549 | try: |
1502 | 1550 | request = self._request_q.peek(block=False) |
1503 | 1551 | except Empty: |
1507 | 1555 | self._request_q.get_nowait() # Pop it. |
1508 | 1556 | |
1509 | 1557 | return responder |
1510 | ||
1511 | subscribe = autoresponds | |
1512 | """Synonym for `.autoresponds`.""" | |
1513 | 1558 | |
1514 | 1559 | @_synchronized |
1515 | 1560 | def cancel_responder(self, responder): |
1614 | 1659 | server_thread.start() |
1615 | 1660 | except socket.error as error: |
1616 | 1661 | if error.errno not in ( |
1617 | errno.EAGAIN, errno.EBADF, errno.EWOULDBLOCK): | |
1662 | errno.EAGAIN, errno.EBADF, errno.ENOTSOCK, | |
1663 | errno.EWOULDBLOCK): | |
1618 | 1664 | raise |
1619 | 1665 | except select.error as error: |
1620 | if error.args[0] == errno.EBADF: | |
1666 | if error.args[0] in (errno.EBADF, errno.ENOTSOCK): | |
1621 | 1667 | # Closed. |
1622 | 1668 | break |
1623 | 1669 | else: |
1642 | 1688 | else: |
1643 | 1689 | self._request_q.put(request) |
1644 | 1690 | except socket.error as error: |
1645 | if error.errno in (errno.ECONNRESET, errno.EBADF): | |
1691 | if error.errno in (errno.ECONNRESET, errno.EBADF, | |
1692 | errno.ENOTSOCK): | |
1646 | 1693 | # We hung up, or the client did. |
1647 | 1694 | break |
1648 | 1695 | raise |
1649 | 1696 | except select.error as error: |
1650 | if error.args[0] == errno.EBADF: | |
1697 | if error.args[0] in (errno.EBADF, errno.ENOTSOCK): | |
1651 | 1698 | # Closed. |
1652 | 1699 | break |
1653 | 1700 | else: |
2015 | 2062 | auto_ismaster=True, |
2016 | 2063 | uds_path=uds_path) |
2017 | 2064 | if all_ok: |
2018 | server.autoresponds({}) | |
2065 | server.append_responder({}) | |
2019 | 2066 | server.autoresponds('whatsmyuri', you='localhost:12345') |
2020 | 2067 | server.autoresponds({'getLog': 'startupWarnings'}, |
2021 | 2068 | log=['hello from %s!' % name]) |
0 | 0 | -----BEGIN PRIVATE KEY----- |
1 | MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAK53miP9GczBWXnq | |
2 | NxHwQkgVqsDuesjwJbWilMK4gf3fjnf2PN3qDpnGbZbPD0ij8975pIKtSPoDycFm | |
3 | A8Mogip0yU2Lv2lL56CWthSBftOFDL2CWIsmuuURFXZPiVLtLytfI9oLASZFlywW | |
4 | Cs83qEDTvdW8VoVhVsxV1JFDnpXLAgMBAAECgYBoGBgxrMt97UazhNkCrPT/CV5t | |
5 | 6lv8E7yMGMrlOyzkCkR4ssQyK3o2qbutJTGbR6czvIM5LKbD9Qqlh3ZrNHokWmTR | |
6 | VQQpJxt8HwP5boQvwRHg9+KSGr4JvRko1qxFs9C7Bzjt4r9VxdjhwZPdy0McGI/z | |
7 | yPXyQHjqBayrHV1EwQJBANorfCKeIxLhH3LAeUZuRS8ACldJ2N1kL6Ov43/v+0S/ | |
8 | OprQeBTODuTds3sv7FCT1aYDTOe6JLNOwN2i4YVOMBsCQQDMuCozrwqftD17D06P | |
9 | 9+lRXUekY5kFBs5j28Xnl8t8jnuxsXtQUTru660LD0QrmDNSauhpEmlpJknicnGt | |
10 | hmwRAkEA12MI6bBPlir0/jgxQqxI1w7mJqj8Vg27zpEuO7dzzLoyJHddpcSNBbwu | |
11 | npaAakiZK42klj26T9+XHvjYRuAbMwJBAJ5WnwWEkGH/pUHGEAyYQdSVojDKe/MA | |
12 | Vae0tzguFswK5C8GyArSGRPsItYYA7D4MlG/sGx8Oh2C6MiFndkJzBECQDcP1y4r | |
13 | Qsek151t1zArLKH4gG5dQAeZ0Lc2VeC4nLMUqVwrHcZDdd1RzLlSaH3j1MekFVfT | |
14 | 6v6rrcNLEVbeuk4= | |
1 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC+DyBLgFcu8SJ/ | |
2 | yHzppcxcTqLxMA+iMiTsqMpELEMxENSt5102reD9vkjEQPXW7oAZof4Dx+qOSe8n | |
3 | kSSLaDYjyvnoy7XBajZbQjksP7gBR2Cl8wISrGS3+atZ5uPMl6N6dFI4HvXIcDBx | |
4 | XnH+VkV6iyhO9aCmQI6jkQD1vJkg3J4jEJ3IDulqbzhk7tnSv4Hrs345TcbWrmB3 | |
5 | bcs+rCPfGxDfVyimCtbt478iEl/Im5Cz+ectNOrCZ++Xb6459qQ0v8CV04rUorwg | |
6 | gT+/2GgCN+8ZlvDmqE6dy539TZq+ZfrpRAviqxUFvVWIXzg5PyX8K3Vk2of6rcck | |
7 | Q8epO5/bAgMBAAECggEAP32EF1S/SyIomTFbcR3+39MxIYshndhMd3aHYzC6HXj2 | |
8 | 40VH4U1CvOFFI7JjrbIsvuNbnN264F+YccpNv/hHJbvXskni5MLbd67utHZwvJSg | |
9 | l69PQPewCblw4W59KMp7RRv4n2DQUG4R8L1RLVqaiS5Vf9MUIJWuULvO60heixg0 | |
10 | jmKkmCX2QBoQW/j5C7Bprdzo8DLNX84EPe0K6+pqCUVbz/E/66kJ/i76a47wRIec | |
11 | YPlZaeZ0AIPqZ+QigC5ZaKwbIo6wFMoaCavBTY53w48pWPi/LL6I0ACsAihv0Zlt | |
12 | Ik4nk6cSSC+mtc9GPZSp4Dq5svvkl8D6RIpYwCT9yQKBgQDk2fEi1JqTvt78Zuxu | |
13 | A+MQQJtNoSQmkYByCTUy/4+y71Yz6cHfR0n0vpwOSv7CNPmuC3sQ5Fo7+FsNMTAI | |
14 | +riIjybtgenN9ljgLHHcoPN2E++NEQUCwvJgYyyfPV9rAAHNN62d+Inu3XZf3rPh | |
15 | uvuN3JJB6D1UQU2xinNu9LjL/QKBgQDUmxcYsfc/WNd/zQXJ31TeS5bFe6tSGwVO | |
16 | +3XYfVmq4Vi8HAef6a5mjKybqEyrOpn7WPMWL2lLa6bcoA2785zN+uqe9whm0uup | |
17 | FydnyFr5Cjr+yptCfR7b8jj7syT+MvVUacOu/Vt2x3g8cr8QUuUbiIPKqQqb4UEY | |
18 | 2HKEPrdmtwKBgHcZgWgqEyRPEodzHRqIRVSA+xIkicbUtG8koZ4f6G4sJsWvoukL | |
19 | lc6coGTD3N+/aC2O5gY9gURylRhBgAk8SmsvbQfwM3iv+0L3fm5fCTVrXKEiuWPd | |
20 | hvxowKFC9HSgNU/S6TUsUsSQVvm/0gfpIt+KakeIkNpXfhKmxjp5e+8VAoGBAKxd | |
21 | 1LrbxhWgpI5jnTbOjtLuu5z+J6aYa5ReQGu1LNZifnt7yh626QMRN/u21fnYt/BU | |
22 | bDhnVdmkvJKQXLItzsocjM02gKREinT7ZaI5iK/xwGTDxF6CbFtrpRFDa1F/5PB8 | |
23 | Ev8zP00saOmxKgBFBKRu6FKM/CHm3M0U5rsa0bw/AoGBANknEpinPgmMDjPOJmZp | |
24 | i+BQTKwQirxaFWPHs/89RXsIPg5kfnMw9Pz3QmaVsV17+4dRaDqNhXiw0ZkSAPsX | |
25 | gWyI+6AmOFsnPBrFs/Ne7fjo33reuGIvUrtWIpYRyKvohDAmcEDFTS/DGmhQsya/ | |
26 | e+K9MCpuenwOBs+mL9EPPS9s | |
15 | 27 | -----END PRIVATE KEY----- |
16 | 28 | -----BEGIN CERTIFICATE----- |
17 | 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 | |
33 | 49 | -----END CERTIFICATE----- |
0 | Metadata-Version: 1.1 | |
0 | Metadata-Version: 1.2 | |
1 | 1 | Name: mockupdb |
2 | Version: 1.7.0 | |
2 | Version: 1.8.0 | |
3 | 3 | Summary: MongoDB Wire Protocol server library |
4 | 4 | Home-page: https://github.com/ajdavis/mongo-mockup-db |
5 | 5 | Author: A. Jesse Jiryu Davis |
6 | 6 | Author-email: jesse@mongodb.com |
7 | 7 | License: Apache License, Version 2.0 |
8 | Description-Content-Type: UNKNOWN | |
9 | 8 | Description: ======== |
10 | 9 | MockupDB |
11 | 10 | ======== |
20 | 19 | |
21 | 20 | Changelog |
22 | 21 | ========= |
22 | ||
23 | Next Release | |
24 | ------------ | |
25 | ||
26 | MockupDB supports Python 3.4 through 3.8; it no longer supports Python 2.6 or | |
27 | Python 3.3. | |
28 | ||
29 | New method ``MockupDB.append_responder`` to add an autoresponder of last resort. | |
30 | ||
31 | Fix a bug in ``interactive_server`` with ``all_ok=True``. It had returned an | |
32 | empty isMaster response, causing drivers to throw errors like "Server at | |
33 | localhost:27017 reports wire version 0, but this version of PyMongo requires at | |
34 | least 2 (MongoDB 2.6)." | |
35 | ||
36 | Stop logging "OSError: [WinError 10038] An operation was attempted on something | |
37 | that is not a socket" on Windows after a client disconnects. | |
38 | ||
39 | Parse OP_MSGs with any number of sections in any order. This allows write | |
40 | commands from the mongo shell, which sends sections in the opposite order from | |
41 | drivers. Handle OP_MSGs with checksums, such as those sent by the mongo shell | |
42 | beginning in 4.2. | |
23 | 43 | |
24 | 44 | 1.7.0 (2018-12-02) |
25 | 45 | ------------------ |
141 | 161 | Classifier: License :: OSI Approved :: Apache Software License |
142 | 162 | Classifier: Natural Language :: English |
143 | 163 | Classifier: Programming Language :: Python :: 2 |
144 | Classifier: Programming Language :: Python :: 2.6 | |
145 | 164 | Classifier: Programming Language :: Python :: 2.7 |
146 | 165 | Classifier: Programming Language :: Python :: 3 |
147 | Classifier: Programming Language :: Python :: 3.3 | |
148 | 166 | Classifier: Programming Language :: Python :: 3.4 |
167 | Classifier: Programming Language :: Python :: 3.5 | |
168 | Classifier: Programming Language :: Python :: 3.6 | |
169 | Classifier: Programming Language :: Python :: 3.7 | |
170 | Classifier: Programming Language :: Python :: 3.8 | |
171 | Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.* |
7 | 7 | except ImportError: |
8 | 8 | from distutils.core import setup |
9 | 9 | |
10 | if sys.version_info[:2] < (2, 7): | |
11 | raise RuntimeError("Python version >= 2.7 required.") | |
10 | 12 | |
11 | 13 | with open('README.rst') as readme_file: |
12 | 14 | readme = readme_file.read() |
14 | 16 | with open('CHANGELOG.rst') as changelog_file: |
15 | 17 | changelog = changelog_file.read().replace('.. :changelog:', '') |
16 | 18 | |
17 | requirements = ['pymongo>=3'] | |
18 | test_requirements = [] | |
19 | ||
20 | if sys.version_info[:2] == (2, 6): | |
21 | requirements.append('ordereddict') | |
22 | test_requirements.append('unittest2') | |
23 | ||
24 | 19 | setup( |
25 | 20 | name='mockupdb', |
26 | version='1.7.0', | |
21 | version='1.8.0', | |
27 | 22 | description="MongoDB Wire Protocol server library", |
28 | 23 | long_description=readme + '\n\n' + changelog, |
29 | 24 | author="A. Jesse Jiryu Davis", |
32 | 27 | packages=['mockupdb'], |
33 | 28 | package_dir={'mockupdb': 'mockupdb'}, |
34 | 29 | include_package_data=True, |
35 | install_requires=requirements, | |
30 | install_requires=['pymongo>=3'], | |
36 | 31 | license="Apache License, Version 2.0", |
37 | 32 | zip_safe=False, |
38 | 33 | keywords=["mongo", "mongodb", "wire protocol", "mockupdb", "mock"], |
34 | python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*", | |
39 | 35 | classifiers=[ |
40 | 36 | 'Development Status :: 5 - Production/Stable', |
41 | 37 | 'Intended Audience :: Developers', |
42 | 38 | "License :: OSI Approved :: Apache Software License", |
43 | 39 | 'Natural Language :: English', |
44 | 40 | "Programming Language :: Python :: 2", |
45 | 'Programming Language :: Python :: 2.6', | |
46 | 'Programming Language :: Python :: 2.7', | |
47 | 'Programming Language :: Python :: 3', | |
48 | 'Programming Language :: Python :: 3.3', | |
49 | 'Programming Language :: Python :: 3.4', | |
41 | "Programming Language :: Python :: 2.7", | |
42 | "Programming Language :: Python :: 3", | |
43 | "Programming Language :: Python :: 3.4", | |
44 | "Programming Language :: Python :: 3.5", | |
45 | "Programming Language :: Python :: 3.6", | |
46 | "Programming Language :: Python :: 3.7", | |
47 | "Programming Language :: Python :: 3.8", | |
50 | 48 | ], |
51 | 49 | test_suite='tests', |
52 | tests_require=test_requirements | |
50 | tests_require=[] | |
53 | 51 | ) |
0 | 0 | # -*- coding: utf-8 -*- |
1 | ||
2 | import unittest | |
3 | ||
4 | try: | |
5 | import unittest2 as unittest | |
6 | except ImportError: | |
7 | pass |
8 | 8 | import ssl |
9 | 9 | import sys |
10 | 10 | import tempfile |
11 | import unittest | |
12 | from struct import Struct | |
11 | 13 | |
12 | 14 | if sys.version_info[0] < 3: |
13 | 15 | from io import BytesIO as StringIO |
19 | 21 | except ImportError: |
20 | 22 | from Queue import Queue |
21 | 23 | |
22 | from bson import (Binary, Code, DBRef, Decimal128, MaxKey, MinKey, ObjectId, | |
23 | Regex, SON, Timestamp) | |
24 | from bson import (Binary, BSON, Code, DBRef, Decimal128, MaxKey, MinKey, | |
25 | ObjectId, Regex, SON, Timestamp) | |
24 | 26 | from bson.codec_options import CodecOptions |
25 | 27 | from pymongo import MongoClient, message, WriteConcern |
26 | 28 | |
27 | 29 | from mockupdb import (go, going, Command, CommandBase, Matcher, MockupDB, |
28 | Request, OpInsert, OpMsg, OpQuery, QUERY_FLAGS) | |
29 | from tests import unittest # unittest2 on Python 2.6. | |
30 | Request, OpInsert, OP_MSG_FLAGS, OpMsg, OpQuery, | |
31 | QUERY_FLAGS) | |
30 | 32 | |
31 | 33 | |
32 | 34 | @contextlib.contextmanager |
367 | 369 | self.assertEqual(3, response['ok']) |
368 | 370 | |
369 | 371 | |
372 | class TestOpMsg(unittest.TestCase): | |
373 | def setUp(self): | |
374 | self.server = MockupDB(auto_ismaster={'maxWireVersion': 6}) | |
375 | self.server.run() | |
376 | self.addCleanup(self.server.stop) | |
377 | self.client = MongoClient(self.server.uri) | |
378 | ||
379 | def test_flags(self): | |
380 | doc = SON([('foo', 1), ('$db', 'mydb')]) | |
381 | obj = BSON.encode(doc) | |
382 | ||
383 | for flag_name, flag_bit in OP_MSG_FLAGS.items(): | |
384 | # MockupDB strips 16-byte header then calls unpack on body. | |
385 | message_body = b''.join([ | |
386 | Struct('<I').pack(flag_bit), # flagBits | |
387 | Struct('<b').pack(0), # section kind | |
388 | obj, | |
389 | ]) | |
390 | ||
391 | if flag_name == 'checksumPresent': | |
392 | message_body += Struct('<I').pack(1234) | |
393 | ||
394 | op_msg = OpMsg.unpack(msg=message_body, | |
395 | client=None, | |
396 | server=None, | |
397 | request_id=0) | |
398 | ||
399 | self.assertEqual(op_msg.flags, flag_bit) | |
400 | self.assertEqual(op_msg.doc, doc) | |
401 | self.assertEqual(op_msg.namespace, 'mydb') | |
402 | ||
403 | if flag_name == 'checksumPresent': | |
404 | self.assertEqual(op_msg.checksum, 1234) | |
405 | else: | |
406 | self.assertEqual(op_msg.checksum, None) | |
407 | ||
408 | ||
370 | 409 | if __name__ == '__main__': |
371 | 410 | unittest.main() |