PYTHON-1153 - Make docs build and test under python 3

This commit is contained in:
Bernie Hackett 2016-09-26 15:39:57 -07:00
parent f37be740e2
commit 2c232e78b2
11 changed files with 224 additions and 106 deletions

View File

@ -31,7 +31,7 @@ Example usage (serialization):
>>> dumps([{'foo': [1, 2]},
... {'bar': {'hello': 'world'}},
... {'code': Code("function x() { return 1; }")},
... {'bin': Binary("\x01\x02\x03\x04")}])
... {'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):

View File

@ -4,8 +4,17 @@
#
# This file is execfile()d with the current directory set to its containing dir.
import sys, os
sys.path[0:0] = [os.path.abspath('..')]
import os
import sys
_path = os.path.abspath('..')
sys.path[0:0] = [_path]
if sys.version_info[0] >= 3:
import glob
ver = '.'.join(map(str, sys.version_info[:2]))
_path = glob.glob(
os.path.join(os.path.abspath('..'), 'build', 'lib*' + ver))[0]
sys.path[0:0] = [_path]
import pymongo
@ -65,9 +74,9 @@ pygments_style = 'sphinx'
# -- Options for extensions ----------------------------------------------------
autoclass_content = 'init'
doctest_path = os.path.abspath('..')
doctest_path = [_path]
doctest_test_doctest_blocks = False
doctest_test_doctest_blocks = ''
doctest_global_setup = """
from pymongo.mongo_client import MongoClient

View File

@ -51,15 +51,20 @@ eg "$sort":
PyMongo version **>= 2.3**.
.. doctest::
:options: +NORMALIZE_WHITESPACE
>>> from bson.son import SON
>>> db.things.aggregate([
>>> import pprint
>>> pprint.pprint(db.things.aggregate([
... {"$unwind": "$tags"},
... {"$group": {"_id": "$tags", "count": {"$sum": 1}}},
... {"$sort": SON([("count", -1), ("_id", -1)])}
... ])
... ]))
...
{u'ok': 1.0, u'result': [{u'count': 3, u'_id': u'cat'}, {u'count': 2, u'_id': u'dog'}, {u'count': 1, u'_id': u'mouse'}]}
{u'ok': 1.0,
u'result': [{u'_id': u'cat', u'count': 3},
{u'_id': u'dog', u'count': 2},
{u'_id': u'mouse', u'count': 1}]}
As well as simple aggregations the aggregation framework provides projection
@ -115,7 +120,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}
@ -132,8 +137,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
@ -141,9 +150,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}
@ -155,8 +165,16 @@ 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 <http://www.mongodb.org/display/DOCS/MapReduce>`_
@ -184,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}

View File

@ -27,7 +27,7 @@ bulk insert operations.
>>> import pymongo
>>> db = pymongo.MongoClient().bulk_example
>>> db.test.insert(({'i': i} for i in xrange(10000)))
>>> db.test.insert(({'i': i} for i in range(10000)))
[...]
>>> db.test.count()
10000
@ -58,6 +58,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
>>>
@ -95,6 +96,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()
@ -106,7 +108,7 @@ the failure.
>>> try:
... bulk.execute()
... except BulkWriteError as bwe:
... pprint(bwe.details) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
... pprint(bwe.details)
...
{'nInserted': 0,
'nMatched': 1,
@ -134,6 +136,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})
@ -143,7 +146,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,
@ -172,6 +175,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})

View File

@ -68,10 +68,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"])
<Custom object at ...>
>>> decode_custom(db.test.find_one()["custom"]).x()
@ -122,8 +124,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': <Custom object at ...>}
>>> pprint.pprint(db.test.find_one())
{u'_id': ObjectId('...'),
u'custom': <Custom object at ...>}
>>> db.test.find_one()["custom"].x()
5
@ -139,8 +142,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!
@ -163,7 +167,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))
@ -209,8 +213,9 @@ seamlessly:
>>> db.test.insert({"custom": Custom(5)})
ObjectId('...')
>>> db.test.find_one()
{u'_id': ObjectId('...'), u'custom': <Custom object at ...>}
>>> pprint.pprint(db.test.find_one())
{u'_id': ObjectId('...'),
u'custom': <Custom object at ...>}
>>> db.test.find_one()["custom"].x()
5
@ -222,5 +227,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)}

View File

@ -49,12 +49,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`:
@ -63,11 +64,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):
@ -76,10 +77,10 @@ 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) # doctest: +ELLIPSIS
... pprint.pprint(doc)
...
"{u'loc': [2, 5], u'_id': ObjectId('...')}"
"{u'loc': [4, 4], u'_id': ObjectId('...')}"
{u'_id': ObjectId('...'), u'loc': [2, 5]}
{u'_id': ObjectId('...'), u'loc': [4, 4]}
Or circle (specified by center point and radius):
@ -87,11 +88,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`::

View File

@ -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"``

View File

@ -126,31 +126,25 @@ is displayed:
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:
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":
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,
which is a dict that remembers its key order. Now, documents and subdocuments
in query results are represented with :class:`~bson.son.SON` objects:
.. doctest:: key-order
in query results are represented with :class:`~bson.son.SON` objects::
>>> from bson.son import SON
>>> print collection.find_one(as_class=SON)
>>> print(collection.find_one(as_class=SON))
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".
@ -158,25 +152,19 @@ The subdocument's actual storage layout is now visible: "b" is before "a".
Because a dict's key order is not defined, you cannot predict how it will be
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
not match::
>>> 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
Swapping the key order in your query makes no difference::
>>> collection.find_one({'subdocument': {'b': 1.0, 'a': 1.0}}) is None
True
... because, as we saw above, Python considers the two dicts the same.
There are two solutions. First, you can match the subdocument field-by-field:
.. doctest:: key-order
There are two solutions. First, you can match the subdocument field-by-field::
>>> collection.find_one({'subdocument.a': 1.0,
... 'subdocument.b': 1.0})
@ -187,9 +175,7 @@ regardless of the order you specify them in Python or the order they are stored
in BSON. Additionally, this query now matches subdocuments with additional
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
The second solution is to use a :class:`~bson.son.SON` to specify the key order::
>>> query = {'subdocument': SON([('b', 1.0), ('a', 1.0)])}
>>> collection.find_one(query)

View File

@ -162,8 +162,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.
@ -176,8 +181,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:
@ -194,10 +203,14 @@ We can also find a post by its ``_id``, which in our example is an ObjectId:
.. doctest::
>>> 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']}
>>> post_id
ObjectId(...)
>>> 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:
@ -282,11 +295,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`
@ -296,10 +321,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
--------
@ -331,10 +364,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
@ -355,8 +396,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

View File

@ -645,12 +645,13 @@ class Collection(common.BaseObject):
>>> db.test.insert({"x": "y", "a": "b"})
ObjectId('...')
>>> list(db.test.find())
[{u'a': u'b', u'x': u'y', u'_id': ObjectId('...')}]
>>> import pprint
>>> pprint.pprint(list(db.test.find()))
[{u'_id': ObjectId('...'), u'a': u'b', u'x': u'y'}]
>>> db.test.update({"x": "y"}, {"$set": {"a": "c"}})
{...}
>>> list(db.test.find())
[{u'a': u'c', u'x': u'y', u'_id': ObjectId('...')}]
>>> pprint.pprint(list(db.test.find()))
[{u'_id': ObjectId('...'), u'a': u'c', u'x': u'y'}]
:Parameters:
- `spec`: a ``dict`` or :class:`~bson.son.SON` instance

View File

@ -2,7 +2,6 @@ import glob
import os
import platform
import re
import subprocess
import sys
import warnings
@ -22,17 +21,24 @@ except ImportError:
# we have to.
try:
from setuptools import setup
from setuptools.command.build_py import build_py
except ImportError:
from ez_setup import use_setuptools
use_setuptools()
from setuptools import setup
from setuptools.command.build_py import build_py
from distutils.cmd import Command
from distutils.command.build_ext import build_ext
from distutils.errors import CCompilerError
from distutils.errors import DistutilsPlatformError, DistutilsExecError
from distutils.core import Extension
try:
import sphinx
_HAVE_SPHINX = True
except ImportError:
_HAVE_SPHINX = False
version = "2.9.4.dev0"
f = open("README.rst")
@ -88,22 +94,70 @@ if "test" in sys.argv or "nosetests" in sys.argv:
should_run_tests = True
class doc(Command):
class doc(build_py):
description = "generate or test documentation"
user_options = [("test", "t",
"run doctests instead of generating documentation")]
build_py.user_options.append(
("test", "t", "run doctests instead of generating documentation"))
boolean_options = ["test"]
build_py.boolean_options.append('test')
def initialize_options(self):
self.test = False
def finalize_options(self):
pass
build_py.initialize_options(self)
def run(self):
if not _HAVE_SPHINX:
raise RuntimeError(
"You must install Sphinx to build or test the documentation.")
if PY3:
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|^)(?<![\'\"])[uU]([rR]?[\'\"])", re.UNICODE)
# Match b or B (possibly followed by r or R), removing.
# r/R can follow b/B but not precede it. Don't match the
# single character string 'b' or 'B'.
_b_literal_re = re.compile(
r"(\W|^)(?<![\'\"])[bB]([rR]?[\'\"])", re.UNICODE)
class _StringPrefixFixer(_OutputChecker):
def check_output(self, want, got, optionflags):
if sys.version_info[0] >= 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
# No need to run build_py for python 2.x.
build_py.run(self)
if self.test:
path = "doc/_build/doctest"
mode = "doctest"
@ -116,8 +170,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,))