diff --git a/bson/json_util.py b/bson/json_util.py index ded7a48b8..0eb81694f 100644 --- a/bson/json_util.py +++ b/bson/json_util.py @@ -30,8 +30,8 @@ Example usage (serialization): >>> from bson.json_util import dumps >>> dumps([{'foo': [1, 2]}, ... {'bar': {'hello': 'world'}}, - ... {'code': Code("function x() { return 1; }")}, - ... {'bin': Binary("\x01\x02\x03\x04")}]) + ... {'code': Code("function x() { return 1; }", {})}, + ... {'bin': Binary(b"\x01\x02\x03\x04")}]) '[{"foo": [1, 2]}, {"bar": {"hello": "world"}}, {"code": {"$code": "function x() { return 1; }", "$scope": {}}}, {"bin": {"$binary": "AQIDBA==", "$type": "00"}}]' Example usage (deserialization): diff --git a/doc/conf.py b/doc/conf.py index a20356b0f..8e367cdac 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -66,9 +66,9 @@ pygments_style = 'sphinx' # -- Options for extensions ---------------------------------------------------- autoclass_content = 'init' -doctest_path = os.path.abspath('..') +doctest_path = [os.path.abspath('..')] -doctest_test_doctest_blocks = False +doctest_test_doctest_blocks = '' doctest_global_setup = """ from pymongo.mongo_client import MongoClient diff --git a/doc/examples/aggregation.rst b/doc/examples/aggregation.rst index e0cf14bf4..365c2191d 100644 --- a/doc/examples/aggregation.rst +++ b/doc/examples/aggregation.rst @@ -56,8 +56,11 @@ eg "$sort": ... {"$group": {"_id": "$tags", "count": {"$sum": 1}}}, ... {"$sort": SON([("count", -1), ("_id", -1)])} ... ] - >>> list(db.things.aggregate(pipeline)) - [{u'count': 3, u'_id': u'cat'}, {u'count': 2, u'_id': u'dog'}, {u'count': 1, u'_id': u'mouse'}] + >>> import pprint + >>> pprint.pprint(list(db.things.aggregate(pipeline))) + [{u'_id': u'cat', u'count': 3}, + {u'_id': u'dog', u'count': 2}, + {u'_id': u'mouse', u'count': 1}] To run an explain plan for this aggregation use the :meth:`~pymongo.database.Database.command` method:: @@ -118,7 +121,7 @@ iterate over the result collection: >>> result = db.things.map_reduce(mapper, reducer, "myresults") >>> for doc in result.find(): - ... print doc + ... pprint.pprint(doc) ... {u'_id': u'cat', u'value': 3.0} {u'_id': u'dog', u'value': 2.0} @@ -135,8 +138,12 @@ response to the map/reduce command, rather than just the result collection: .. doctest:: - >>> db.things.map_reduce(mapper, reducer, "myresults", full_response=True) - {u'counts': {u'input': 4, u'reduce': 2, u'emit': 6, u'output': 3}, u'timeMillis': ..., u'ok': ..., u'result': u'...'} + >>> pprint.pprint( + ... db.things.map_reduce(mapper, reducer, "myresults", full_response=True)) + {u'counts': {u'emit': 6, u'input': 4, u'output': 3, u'reduce': 2}, + u'ok': ..., + u'result': u'...', + u'timeMillis': ...} All of the optional map/reduce parameters are also supported, simply pass them as keyword arguments. In this example we use the `query` parameter to limit the @@ -144,9 +151,10 @@ documents that will be mapped over: .. doctest:: - >>> result = db.things.map_reduce(mapper, reducer, "myresults", query={"x": {"$lt": 2}}) - >>> for doc in result.find(): - ... print doc + >>> results = db.things.map_reduce( + ... mapper, reducer, "myresults", query={"x": {"$lt": 2}}) + >>> for doc in results.find(): + ... pprint.pprint(doc) ... {u'_id': u'cat', u'value': 1.0} {u'_id': u'dog', u'value': 1.0} @@ -157,8 +165,16 @@ specify a different database to store the result collection: .. doctest:: >>> from bson.son import SON - >>> db.things.map_reduce(mapper, reducer, out=SON([("replace", "results"), ("db", "outdb")]), full_response=True) - {u'counts': {u'input': 4, u'reduce': 2, u'emit': 6, u'output': 3}, u'timeMillis': ..., u'ok': ..., u'result': {u'db': ..., u'collection': ...}} + >>> pprint.pprint( + ... db.things.map_reduce( + ... mapper, + ... reducer, + ... out=SON([("replace", "results"), ("db", "outdb")]), + ... full_response=True)) + {u'counts': {u'emit': 6, u'input': 4, u'output': 3, u'reduce': 2}, + u'ok': ..., + u'result': {u'collection': ..., u'db': ...}, + u'timeMillis': ...} .. seealso:: The full list of options for MongoDB's `map reduce engine `_ @@ -186,7 +202,7 @@ Here we are doing a simple group and count of the occurrences of ``x`` values: ... >>> results = db.things.group(key={"x":1}, condition={}, initial={"count": 0}, reduce=reducer) >>> for doc in results: - ... print doc + ... pprint.pprint(doc) {u'count': 1.0, u'x': 1.0} {u'count': 2.0, u'x': 2.0} {u'count': 1.0, u'x': 3.0} diff --git a/doc/examples/bulk.rst b/doc/examples/bulk.rst index c0dd87943..786b1530a 100644 --- a/doc/examples/bulk.rst +++ b/doc/examples/bulk.rst @@ -27,7 +27,7 @@ bulk insert operations. >>> import pymongo >>> db = pymongo.MongoClient().bulk_example - >>> db.test.insert_many([{'i': i} for i in xrange(10000)]).inserted_ids + >>> db.test.insert_many([{'i': i} for i in range(10000)]).inserted_ids [...] >>> db.test.count() 10000 @@ -51,6 +51,7 @@ order provided for serial execution. The return value is a document describing the type and count of operations performed. .. doctest:: + :options: +NORMALIZE_WHITESPACE >>> from pprint import pprint >>> @@ -88,6 +89,7 @@ occurred and details about the failure - including the operation that caused the failure. .. doctest:: + :options: +NORMALIZE_WHITESPACE >>> from pymongo.errors import BulkWriteError >>> bulk = db.test.initialize_ordered_bulk_op() @@ -99,7 +101,7 @@ the failure. >>> try: ... bulk.execute() ... except BulkWriteError as bwe: - ... pprint(bwe.details) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE + ... pprint(bwe.details) ... {'nInserted': 0, 'nMatched': 1, @@ -127,6 +129,7 @@ constraint on _id. Since we are doing unordered execution the second and fourth operations succeed. .. doctest:: + :options: +NORMALIZE_WHITESPACE >>> bulk = db.test.initialize_unordered_bulk_op() >>> bulk.insert({'_id': 1}) @@ -136,7 +139,7 @@ and fourth operations succeed. >>> try: ... bulk.execute() ... except BulkWriteError as bwe: - ... pprint(bwe.details) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE + ... pprint(bwe.details) ... {'nInserted': 0, 'nMatched': 1, @@ -165,6 +168,7 @@ errors (e.g. wtimeout) will be reported after all operations are attempted, regardless of execution order. .. doctest:: + :options: +NORMALIZE_WHITESPACE >>> bulk = db.test.initialize_ordered_bulk_op() >>> bulk.insert({'a': 0}) diff --git a/doc/examples/custom_type.rst b/doc/examples/custom_type.rst index e91f9c53c..d7115c4b1 100644 --- a/doc/examples/custom_type.rst +++ b/doc/examples/custom_type.rst @@ -88,10 +88,12 @@ use them with PyMongo: .. doctest:: + >>> import pprint >>> db.test.insert({"custom": encode_custom(Custom(5))}) ObjectId('...') - >>> db.test.find_one() - {u'_id': ObjectId('...'), u'custom': {u'x': 5, u'_type': u'custom'}} + >>> pprint.pprint(db.test.find_one()) + {u'_id': ObjectId('...'), + u'custom': {u'_type': u'custom', u'x': 5}} >>> decode_custom(db.test.find_one()["custom"]) >>> decode_custom(db.test.find_one()["custom"]).x() @@ -142,8 +144,9 @@ After doing so we can save and restore :class:`Custom` instances seamlessly: {...} >>> db.test.insert({"custom": Custom(5)}) ObjectId('...') - >>> db.test.find_one() - {u'_id': ObjectId('...'), u'custom': } + >>> pprint.pprint(db.test.find_one()) + {u'_id': ObjectId('...'), + u'custom': } >>> db.test.find_one()["custom"].x() 5 @@ -159,8 +162,9 @@ This allows us to see what was actually saved to the database: .. doctest:: - >>> db.test.find_one() - {u'_id': ObjectId('...'), u'custom': {u'x': 5, u'_type': u'custom'}} + >>> pprint.pprint(db.test.find_one()) + {u'_id': ObjectId('...'), + u'custom': {u'_type': u'custom', u'x': 5}} which is the same format that we encode to with our :meth:`encode_custom` method! @@ -183,7 +187,7 @@ from :class:`~bson.binary.Binary` instances: >>> from bson.binary import Binary >>> def to_binary(custom): - ... return Binary(str(custom.x()), 128) + ... return Binary(str(custom.x()).encode(), 128) ... >>> def from_binary(binary): ... return Custom(int(binary)) @@ -229,8 +233,9 @@ seamlessly: >>> db.test.insert({"custom": Custom(5)}) ObjectId('...') - >>> db.test.find_one() - {u'_id': ObjectId('...'), u'custom': } + >>> pprint.pprint(db.test.find_one()) + {u'_id': ObjectId('...'), + u'custom': } >>> db.test.find_one()["custom"].x() 5 @@ -242,5 +247,5 @@ clearing out the manipulators and repeating our .. doctest:: >>> db = client.custom_type_example - >>> db.test.find_one() + >>> pprint.pprint(db.test.find_one()) {u'_id': ObjectId('...'), u'custom': Binary('5', 128)} diff --git a/doc/examples/geo.rst b/doc/examples/geo.rst index a01f69c2d..f2e90aab3 100644 --- a/doc/examples/geo.rst +++ b/doc/examples/geo.rst @@ -47,12 +47,13 @@ Using the geospatial index we can find documents near another point: .. doctest:: + >>> import pprint >>> for doc in db.places.find({"loc": {"$near": [3, 6]}}).limit(3): - ... repr(doc) # doctest: +ELLIPSIS + ... pprint.pprint(doc) ... - "{u'loc': [2, 5], u'_id': ObjectId('...')}" - "{u'loc': [4, 4], u'_id': ObjectId('...')}" - "{u'loc': [1, 2], u'_id': ObjectId('...')}" + {u'_id': ObjectId('...'), u'loc': [2, 5]} + {u'_id': ObjectId('...'), u'loc': [4, 4]} + {u'_id': ObjectId('...'), u'loc': [1, 2]} The $maxDistance operator requires the use of :class:`~bson.son.SON`: @@ -61,11 +62,11 @@ The $maxDistance operator requires the use of :class:`~bson.son.SON`: >>> from bson.son import SON >>> query = {"loc": SON([("$near", [3, 6]), ("$maxDistance", 100)])} >>> for doc in db.places.find(query).limit(3): - ... repr(doc) # doctest: +ELLIPSIS + ... pprint.pprint(doc) ... - "{u'loc': [2, 5], u'_id': ObjectId('...')}" - "{u'loc': [4, 4], u'_id': ObjectId('...')}" - "{u'loc': [1, 2], u'_id': ObjectId('...')}" + {u'_id': ObjectId('...'), u'loc': [2, 5]} + {u'_id': ObjectId('...'), u'loc': [4, 4]} + {u'_id': ObjectId('...'), u'loc': [1, 2]} It's also possible to query for all items within a given rectangle (specified by lower-left and upper-right coordinates): @@ -74,9 +75,9 @@ It's also possible to query for all items within a given rectangle >>> query = {"loc": {"$within": {"$box": [[2, 2], [5, 6]]}}} >>> for doc in db.places.find(query).sort('_id'): - ... repr(doc) - "{u'loc': [2, 5], u'_id': ObjectId('...')}" - "{u'loc': [4, 4], u'_id': ObjectId('...')}" + ... pprint.pprint(doc) + {u'_id': ObjectId('...'), u'loc': [2, 5]} + {u'_id': ObjectId('...'), u'loc': [4, 4]} Or circle (specified by center point and radius): @@ -84,11 +85,11 @@ Or circle (specified by center point and radius): >>> query = {"loc": {"$within": {"$center": [[0, 0], 6]}}} >>> for doc in db.places.find(query).sort('_id'): - ... repr(doc) # doctest: +ELLIPSIS + ... pprint.pprint(doc) ... - "{u'loc': [2, 5], u'_id': ObjectId('...')}" - "{u'loc': [1, 2], u'_id': ObjectId('...')}" - "{u'loc': [4, 4], u'_id': ObjectId('...')}" + {u'_id': ObjectId('...'), u'loc': [2, 5]} + {u'_id': ObjectId('...'), u'loc': [1, 2]} + {u'_id': ObjectId('...'), u'loc': [4, 4]} geoNear queries are also supported using :class:`~bson.son.SON`:: diff --git a/doc/examples/gridfs.rst b/doc/examples/gridfs.rst index 08be41ddf..db55bd2b5 100644 --- a/doc/examples/gridfs.rst +++ b/doc/examples/gridfs.rst @@ -42,7 +42,7 @@ interface (the :meth:`~gridfs.GridFS.put` and .. doctest:: - >>> a = fs.put("hello world") + >>> a = fs.put(b"hello world") :meth:`~gridfs.GridFS.put` creates a new file in GridFS, and returns the value of the file document's ``"_id"`` key. Given that ``"_id"`` diff --git a/doc/faq.rst b/doc/faq.rst index 6f0bdaa03..e14f066a7 100644 --- a/doc/faq.rst +++ b/doc/faq.rst @@ -214,19 +214,15 @@ PyMongo represents BSON documents as Python dicts by default, and the order of keys in dicts is not defined. That is, a dict declared with the "a" key first is the same, to Python, as one with "b" first: -.. doctest:: key-order - - >>> print {'a': 1.0, 'b': 1.0} + >>> print({'a': 1.0, 'b': 1.0}) {'a': 1.0, 'b': 1.0} - >>> print {'b': 1.0, 'a': 1.0} + >>> print({'b': 1.0, 'a': 1.0}) {'a': 1.0, 'b': 1.0} Therefore, Python dicts are not guaranteed to show keys in the order they are stored in BSON. Here, "a" is shown before "b": -.. doctest:: key-order - - >>> print collection.find_one() + >>> print(collection.find_one()) {u'_id': 1.0, u'subdocument': {u'a': 1.0, u'b': 1.0}} To preserve order when reading BSON, use the :class:`~bson.son.SON` class, @@ -234,10 +230,11 @@ which is a dict that remembers its key order. First, get a handle to the collection, configured to use :class:`~bson.son.SON` instead of dict: .. doctest:: key-order + :options: +NORMALIZE_WHITESPACE >>> from bson import CodecOptions, SON >>> opts = CodecOptions(document_class=SON) - >>> opts # doctest: +NORMALIZE_WHITESPACE + >>> opts CodecOptions(document_class=, tz_aware=False, uuid_representation=PYTHON_LEGACY, @@ -250,7 +247,7 @@ Now, documents and subdocuments in query results are represented with .. doctest:: key-order - >>> print collection_son.find_one() + >>> print(collection_son.find_one()) SON([(u'_id', 1.0), (u'subdocument', SON([(u'b', 1.0), (u'a', 1.0)]))]) The subdocument's actual storage layout is now visible: "b" is before "a". @@ -260,15 +257,11 @@ serialized **to** BSON. But MongoDB considers subdocuments equal only if their keys have the same order. So if you use a dict to query on a subdocument it may not match: -.. doctest:: key-order - >>> collection.find_one({'subdocument': {'a': 1.0, 'b': 1.0}}) is None True Swapping the key order in your query makes no difference: -.. doctest:: key-order - >>> collection.find_one({'subdocument': {'b': 1.0, 'a': 1.0}}) is None True @@ -276,8 +269,6 @@ Swapping the key order in your query makes no difference: There are two solutions. First, you can match the subdocument field-by-field: -.. doctest:: key-order - >>> collection.find_one({'subdocument.a': 1.0, ... 'subdocument.b': 1.0}) {u'_id': 1.0, u'subdocument': {u'a': 1.0, u'b': 1.0}} @@ -289,8 +280,6 @@ keys besides "a" and "b", whereas the previous query required an exact match. The second solution is to use a :class:`~bson.son.SON` to specify the key order: -.. doctest:: key-order - >>> query = {'subdocument': SON([('b', 1.0), ('a', 1.0)])} >>> collection.find_one(query) {u'_id': 1.0, u'subdocument': {u'a': 1.0, u'b': 1.0}} diff --git a/doc/tutorial.rst b/doc/tutorial.rst index c130205e2..cee6212f0 100644 --- a/doc/tutorial.rst +++ b/doc/tutorial.rst @@ -157,8 +157,13 @@ document from the posts collection: .. doctest:: - >>> posts.find_one() - {u'date': datetime.datetime(...), u'text': u'My first blog post!', u'_id': ObjectId('...'), u'author': u'Mike', u'tags': [u'mongodb', u'python', u'pymongo']} + >>> import pprint + >>> pprint.pprint(posts.find_one()) + {u'_id': ObjectId('...'), + u'author': u'Mike', + u'date': datetime.datetime(...), + u'tags': [u'mongodb', u'python', u'pymongo'], + u'text': u'My first blog post!'} The result is a dictionary matching the one that we inserted previously. @@ -171,8 +176,12 @@ our results to a document with author "Mike" we do: .. doctest:: - >>> posts.find_one({"author": "Mike"}) - {u'date': datetime.datetime(...), u'text': u'My first blog post!', u'_id': ObjectId('...'), u'author': u'Mike', u'tags': [u'mongodb', u'python', u'pymongo']} + >>> pprint.pprint(posts.find_one({"author": "Mike"})) + {u'_id': ObjectId('...'), + u'author': u'Mike', + u'date': datetime.datetime(...), + u'tags': [u'mongodb', u'python', u'pymongo'], + u'text': u'My first blog post!'} If we try with a different author, like "Eliot", we'll get no result: @@ -191,8 +200,12 @@ We can also find a post by its ``_id``, which in our example is an ObjectId: >>> post_id ObjectId(...) - >>> posts.find_one({"_id": post_id}) - {u'date': datetime.datetime(...), u'text': u'My first blog post!', u'_id': ObjectId('...'), u'author': u'Mike', u'tags': [u'mongodb', u'python', u'pymongo']} + >>> pprint.pprint(posts.find_one({"_id": post_id})) + {u'_id': ObjectId('...'), + u'author': u'Mike', + u'date': datetime.datetime(...), + u'tags': [u'mongodb', u'python', u'pymongo'], + u'text': u'My first blog post!'} Note that an ObjectId is not the same as its string representation: @@ -278,11 +291,23 @@ document in the ``posts`` collection: .. doctest:: >>> for post in posts.find(): - ... post + ... pprint.pprint(post) ... - {u'date': datetime.datetime(...), u'text': u'My first blog post!', u'_id': ObjectId('...'), u'author': u'Mike', u'tags': [u'mongodb', u'python', u'pymongo']} - {u'date': datetime.datetime(2009, 11, 12, 11, 14), u'text': u'Another post!', u'_id': ObjectId('...'), u'author': u'Mike', u'tags': [u'bulk', u'insert']} - {u'date': datetime.datetime(2009, 11, 10, 10, 45), u'text': u'and pretty easy too!', u'_id': ObjectId('...'), u'author': u'Eliot', u'title': u'MongoDB is fun'} + {u'_id': ObjectId('...'), + u'author': u'Mike', + u'date': datetime.datetime(...), + u'tags': [u'mongodb', u'python', u'pymongo'], + u'text': u'My first blog post!'} + {u'_id': ObjectId('...'), + u'author': u'Mike', + u'date': datetime.datetime(...), + u'tags': [u'bulk', u'insert'], + u'text': u'Another post!'} + {u'_id': ObjectId('...'), + u'author': u'Eliot', + u'date': datetime.datetime(...), + u'text': u'and pretty easy too!', + u'title': u'MongoDB is fun'} Just like we did with :meth:`~pymongo.collection.Collection.find_one`, we can pass a document to :meth:`~pymongo.collection.Collection.find` @@ -292,10 +317,18 @@ author is "Mike": .. doctest:: >>> for post in posts.find({"author": "Mike"}): - ... post + ... pprint.pprint(post) ... - {u'date': datetime.datetime(...), u'text': u'My first blog post!', u'_id': ObjectId('...'), u'author': u'Mike', u'tags': [u'mongodb', u'python', u'pymongo']} - {u'date': datetime.datetime(2009, 11, 12, 11, 14), u'text': u'Another post!', u'_id': ObjectId('...'), u'author': u'Mike', u'tags': [u'bulk', u'insert']} + {u'_id': ObjectId('...'), + u'author': u'Mike', + u'date': datetime.datetime(...), + u'tags': [u'mongodb', u'python', u'pymongo'], + u'text': u'My first blog post!'} + {u'_id': ObjectId('...'), + u'author': u'Mike', + u'date': datetime.datetime(...), + u'tags': [u'bulk', u'insert'], + u'text': u'Another post!'} Counting -------- @@ -327,10 +360,18 @@ than a certain date, but also sort the results by author: >>> d = datetime.datetime(2009, 11, 12, 12) >>> for post in posts.find({"date": {"$lt": d}}).sort("author"): - ... print post + ... pprint.pprint(post) ... - {u'date': datetime.datetime(2009, 11, 10, 10, 45), u'text': u'and pretty easy too!', u'_id': ObjectId('...'), u'author': u'Eliot', u'title': u'MongoDB is fun'} - {u'date': datetime.datetime(2009, 11, 12, 11, 14), u'text': u'Another post!', u'_id': ObjectId('...'), u'author': u'Mike', u'tags': [u'bulk', u'insert']} + {u'_id': ObjectId('...'), + u'author': u'Eliot', + u'date': datetime.datetime(...), + u'text': u'and pretty easy too!', + u'title': u'MongoDB is fun'} + {u'_id': ObjectId('...'), + u'author': u'Mike', + u'date': datetime.datetime(...), + u'tags': [u'bulk', u'insert'], + u'text': u'Another post!'} Here we use the special ``"$lt"`` operator to do a range query, and also call :meth:`~pymongo.cursor.Cursor.sort` to sort the results @@ -351,8 +392,8 @@ First, we'll need to create the index: >>> result = db.profiles.create_index([('user_id', pymongo.ASCENDING)], ... unique=True) - >>> list(db.profiles.index_information()) - [u'user_id_1', u'_id_'] + >>> sorted(list(db.profiles.index_information())) + [u'_id_', u'user_id_1'] Notice that we have two indexes now: one is the index on ``_id`` that MongoDB creates automatically, and the other is the index on ``user_id`` we just diff --git a/pymongo/mongo_client.py b/pymongo/mongo_client.py index 41c15e6c2..24fd36924 100644 --- a/pymongo/mongo_client.py +++ b/pymongo/mongo_client.py @@ -26,9 +26,9 @@ access: >>> from pymongo import MongoClient >>> c = MongoClient() >>> c.test_database - Database(MongoClient('localhost', 27017), u'test_database') + Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=False, connect=True), u'test_database') >>> c['test-database'] - Database(MongoClient('localhost', 27017), u'test-database') + Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=False, connect=True), u'test-database') """ import contextlib diff --git a/setup.py b/setup.py index d4882009f..ae1cf9c29 100755 --- a/setup.py +++ b/setup.py @@ -1,7 +1,6 @@ import os import platform import re -import subprocess import sys import warnings @@ -26,6 +25,12 @@ from distutils.errors import CCompilerError, DistutilsOptionError from distutils.errors import DistutilsPlatformError, DistutilsExecError from distutils.core import Extension +try: + import sphinx + _HAVE_SPHINX = True +except ImportError: + _HAVE_SPHINX = False + version = "3.4rc1.dev0" f = open("README.rst") @@ -125,6 +130,54 @@ class doc(Command): pass def run(self): + + if not _HAVE_SPHINX: + raise RuntimeError( + "You must install Sphinx to build or test the documentation.") + + if sys.version_info[0] >= 3: + import doctest + from doctest import OutputChecker as _OutputChecker + + # Match u or U (possibly followed by r or R), removing it. + # r/R can follow u/U but not precede it. Don't match the + # single character string 'u' or 'U'. + _u_literal_re = re.compile( + r"(\W|^)(?= 3: + # The docstrings are written with python 2.x in mind. + # To make the doctests pass in python 3 we have to + # strip the 'u' prefix from the expected results. The + # actual results won't have that prefix. + want = re.sub(_u_literal_re, r'\1\2', want) + # We also have to strip the 'b' prefix from the actual + # results since python 2.x expected results won't have + # that prefix. + got = re.sub(_b_literal_re, r'\1\2', got) + return super( + _StringPrefixFixer, self).check_output( + want, got, optionflags) + + def output_difference(self, example, got, optionflags): + if sys.version_info[0] >= 3: + example.want = re.sub( + _u_literal_re, r'\1\2', example.want) + got = re.sub(_b_literal_re, r'\1\2', got) + return super( + _StringPrefixFixer, self).output_difference( + example, got, optionflags) + + doctest.OutputChecker = _StringPrefixFixer + if self.test: path = "doc/_build/doctest" mode = "doctest" @@ -137,8 +190,7 @@ class doc(Command): except: pass - status = subprocess.call(["sphinx-build", "-E", - "-b", mode, "doc", path]) + status = sphinx.build_main(["-E", "-b", mode, "doc", path]) if status: raise RuntimeError("documentation step '%s' failed" % (mode,))