diff --git a/bson/int64.py b/bson/int64.py index 77e981230..4fce5ad7b 100644 --- a/bson/int64.py +++ b/bson/int64.py @@ -14,13 +14,7 @@ """A BSON wrapper for long (int in python3)""" -from bson.py3compat import PY3 - -if PY3: - long = int - - -class Int64(long): +class Int64(int): """Representation of the BSON int64 type. This is necessary because every integral number is an :class:`int` in diff --git a/bson/py3compat.py b/bson/py3compat.py deleted file mode 100644 index 84d1ea00f..000000000 --- a/bson/py3compat.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright 2009-present MongoDB, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you -# may not use this file except in compliance with the License. You -# may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. See the License for the specific language governing -# permissions and limitations under the License. - -"""Utility functions and definitions for python3 compatibility.""" - -import sys - -PY3 = sys.version_info[0] == 3 - -if PY3: - import codecs - import collections.abc as abc - import _thread as thread - from abc import ABC, abstractmethod - from io import BytesIO as StringIO - - def abstractproperty(func): - return property(abstractmethod(func)) - - MAXSIZE = sys.maxsize - - imap = map - - def b(s): - # BSON and socket operations deal in binary data. In - # python 3 that means instances of `bytes`. In python - # 2.7 you can create an alias for `bytes` using - # the b prefix (e.g. b'foo'). - # See http://python3porting.com/problems.html#nicer-solutions - return codecs.latin_1_encode(s)[0] - - def bytes_from_hex(h): - return bytes.fromhex(h) - - def iteritems(d): - return iter(d.items()) - - def itervalues(d): - return iter(d.values()) - - def reraise(exctype, value, trace=None): - raise exctype(str(value)).with_traceback(trace) - - def reraise_instance(exc_instance, trace=None): - raise exc_instance.with_traceback(trace) - - def _unicode(s): - return s - - text_type = str - string_type = str - integer_types = int -else: - import collections as abc - import thread - from abc import ABCMeta, abstractproperty - - from itertools import imap - try: - from cStringIO import StringIO - except ImportError: - from StringIO import StringIO - - ABC = ABCMeta('ABC', (object,), {}) - - MAXSIZE = sys.maxint - - def b(s): - # See comments above. In python 2.x b('foo') is just 'foo'. - return s - - def bytes_from_hex(h): - return h.decode('hex') - - def iteritems(d): - return d.iteritems() - - def itervalues(d): - return d.itervalues() - - def reraise(exctype, value, trace=None): - _reraise(exctype, str(value), trace) - - def reraise_instance(exc_instance, trace=None): - _reraise(exc_instance, None, trace) - - # "raise x, y, z" raises SyntaxError in Python 3 - exec("""def _reraise(exc, value, trace): - raise exc, value, trace -""") - - _unicode = unicode - - string_type = basestring - text_type = unicode - integer_types = (int, long) diff --git a/test/barrier.py b/test/barrier.py deleted file mode 100644 index 7a614ca07..000000000 --- a/test/barrier.py +++ /dev/null @@ -1,193 +0,0 @@ -# Backport of the threading.Barrier class from python 3.8, with small -# changes to support python 2.7. -# https://github.com/python/cpython/blob/v3.8.2/Lib/threading.py#L562-L728 - -from threading import (Condition, - Lock) - -from pymongo.monotonic import time as _time - - -# Backport Condition.wait_for from 3.8.2 -# https://github.com/python/cpython/blob/v3.8.2/Lib/threading.py#L318-L339 -def wait_for(condition, predicate, timeout=None): - """Wait until a condition evaluates to True. - - predicate should be a callable which result will be interpreted as a - boolean value. A timeout may be provided giving the maximum time to - wait. - - """ - endtime = None - waittime = timeout - result = predicate() - while not result: - if waittime is not None: - if endtime is None: - endtime = _time() + waittime - else: - waittime = endtime - _time() - if waittime <= 0: - break - condition.wait(waittime) - result = predicate() - return result - - -# A barrier class. Inspired in part by the pthread_barrier_* api and -# the CyclicBarrier class from Java. See -# http://sourceware.org/pthreads-win32/manual/pthread_barrier_init.html and -# http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/ -# CyclicBarrier.html -# for information. -# We maintain two main states, 'filling' and 'draining' enabling the barrier -# to be cyclic. Threads are not allowed into it until it has fully drained -# since the previous cycle. In addition, a 'resetting' state exists which is -# similar to 'draining' except that threads leave with a BrokenBarrierError, -# and a 'broken' state in which all threads get the exception. -class Barrier(object): - """Implements a Barrier. - Useful for synchronizing a fixed number of threads at known synchronization - points. Threads block on 'wait()' and are simultaneously awoken once they - have all made that call. - """ - - def __init__(self, parties, action=None, timeout=None): - """Create a barrier, initialised to 'parties' threads. - 'action' is a callable which, when supplied, will be called by one of - the threads after they have all entered the barrier and just prior to - releasing them all. If a 'timeout' is provided, it is used as the - default for all subsequent 'wait()' calls. - """ - self._cond = Condition(Lock()) - self._action = action - self._timeout = timeout - self._parties = parties - self._state = 0 #0 filling, 1, draining, -1 resetting, -2 broken - self._count = 0 - - def wait(self, timeout=None): - """Wait for the barrier. - When the specified number of threads have started waiting, they are all - simultaneously awoken. If an 'action' was provided for the barrier, one - of the threads will have executed that callback prior to returning. - Returns an individual index number from 0 to 'parties-1'. - """ - if timeout is None: - timeout = self._timeout - with self._cond: - self._enter() # Block while the barrier drains. - index = self._count - self._count += 1 - try: - if index + 1 == self._parties: - # We release the barrier - self._release() - else: - # We wait until someone releases us - self._wait(timeout) - return index - finally: - self._count -= 1 - # Wake up any threads waiting for barrier to drain. - self._exit() - - # Block until the barrier is ready for us, or raise an exception - # if it is broken. - def _enter(self): - while self._state in (-1, 1): - # It is draining or resetting, wait until done - self._cond.wait() - #see if the barrier is in a broken state - if self._state < 0: - raise BrokenBarrierError - assert self._state == 0 - - # Optionally run the 'action' and release the threads waiting - # in the barrier. - def _release(self): - try: - if self._action: - self._action() - # enter draining state - self._state = 1 - self._cond.notify_all() - except: - #an exception during the _action handler. Break and reraise - self._break() - raise - - # Wait in the barrier until we are released. Raise an exception - # if the barrier is reset or broken. - def _wait(self, timeout): - if not wait_for(self._cond, lambda : self._state != 0, timeout): - #timed out. Break the barrier - self._break() - raise BrokenBarrierError - if self._state < 0: - raise BrokenBarrierError - assert self._state == 1 - - # If we are the last thread to exit the barrier, signal any threads - # waiting for the barrier to drain. - def _exit(self): - if self._count == 0: - if self._state in (-1, 1): - #resetting or draining - self._state = 0 - self._cond.notify_all() - - def reset(self): - """Reset the barrier to the initial state. - Any threads currently waiting will get the BrokenBarrier exception - raised. - """ - with self._cond: - if self._count > 0: - if self._state == 0: - #reset the barrier, waking up threads - self._state = -1 - elif self._state == -2: - #was broken, set it to reset state - #which clears when the last thread exits - self._state = -1 - else: - self._state = 0 - self._cond.notify_all() - - def abort(self): - """Place the barrier into a 'broken' state. - Useful in case of error. Any currently waiting threads and threads - attempting to 'wait()' will have BrokenBarrierError raised. - """ - with self._cond: - self._break() - - def _break(self): - # An internal error was detected. The barrier is set to - # a broken state all parties awakened. - self._state = -2 - self._cond.notify_all() - - @property - def parties(self): - """Return the number of threads required to trip the barrier.""" - return self._parties - - @property - def n_waiting(self): - """Return the number of threads currently waiting at the barrier.""" - # We don't need synchronization here since this is an ephemeral result - # anyway. It returns the correct value in the steady state. - if self._state == 0: - return self._count - return 0 - - @property - def broken(self): - """Return True if the barrier is in a broken state.""" - return self._state == -2 - -# exception raised by the Barrier class -class BrokenBarrierError(RuntimeError): - pass diff --git a/test/qcheck.py b/test/qcheck.py index 4d039f75b..0135497c0 100644 --- a/test/qcheck.py +++ b/test/qcheck.py @@ -12,22 +12,18 @@ # See the License for the specific language governing permissions and # limitations under the License. -import random -import traceback import datetime +import random import re import sys +import traceback + sys.path[0:0] = [""] -from bson.binary import Binary from bson.dbref import DBRef from bson.objectid import ObjectId -from bson.py3compat import MAXSIZE, PY3, iteritems from bson.son import SON -if PY3: - unichr = chr - gen_target = 100 reduction_attempts = 10 examples = 5 @@ -59,7 +55,7 @@ def gen_int(): def gen_float(): - return lambda: (random.random() - 0.5) * MAXSIZE + return lambda: (random.random() - 0.5) * sys.maxsize def gen_boolean(): @@ -74,12 +70,8 @@ def gen_printable_string(gen_length): return lambda: "".join(gen_list(gen_printable_char(), gen_length)()) -if PY3: - def gen_char(set=None): - return lambda: bytes([random.randint(0, 255)]) -else: - def gen_char(set=None): - return lambda: chr(random.randint(0, 255)) +def gen_char(set=None): + return lambda: bytes([random.randint(0, 255)]) def gen_string(gen_length): @@ -87,7 +79,7 @@ def gen_string(gen_length): def gen_unichar(): - return lambda: unichr(random.randint(1, 0xFFF)) + return lambda: chr(random.randint(1, 0xFFF)) def gen_unicode(gen_length): @@ -150,15 +142,9 @@ def gen_dbref(): def gen_mongo_value(depth, ref): - bintype = Binary - if PY3: - # If we used Binary in python3 tests would fail since we - # decode BSON binary subtype 0 to bytes. Testing this with - # bytes in python3 makes a lot more sense. - bintype = bytes choices = [gen_unicode(gen_range(0, 50)), gen_printable_string(gen_range(0, 50)), - my_map(gen_string(gen_range(0, 1000)), bintype), + my_map(gen_string(gen_range(0, 1000)), bytes), gen_int(), gen_float(), gen_boolean(), @@ -195,7 +181,7 @@ def simplify(case): # TODO this is a hack return (True, simplified) else: # simplify a value - simplified_items = list(iteritems(simplified)) + simplified_items = list(simplified.items()) if not len(simplified_items): return (False, case) (key, value) = random.choice(simplified_items) diff --git a/test/test_binary.py b/test/test_binary.py index 39de987c1..34e80bfa4 100644 --- a/test/test_binary.py +++ b/test/test_binary.py @@ -17,6 +17,7 @@ import array import base64 import copy +import mmap import pickle import platform import sys @@ -29,11 +30,12 @@ import bson from bson import decode, encode from bson.binary import * from bson.codec_options import CodecOptions -from bson.py3compat import PY3 from bson.son import SON + from pymongo.common import validate_uuid_representation from pymongo.mongo_client import MongoClient from pymongo.write_concern import WriteConcern + from test import client_context, unittest, IntegrationTest from test.utils import ignore_deprecations @@ -328,14 +330,9 @@ class TestBinary(unittest.TestCase): b1 = Binary(b'123', 2) # For testing backwards compatibility with pre-2.4 pymongo - if PY3: - p = (b"\x80\x03cbson.binary\nBinary\nq\x00C\x03123q\x01\x85q" - b"\x02\x81q\x03}q\x04X\x10\x00\x00\x00_Binary__subtypeq" - b"\x05K\x02sb.") - else: - p = (b"ccopy_reg\n_reconstructor\np0\n(cbson.binary\nBinary\np1\nc" - b"__builtin__\nstr\np2\nS'123'\np3\ntp4\nRp5\n(dp6\nS'_Binary" - b"__subtype'\np7\nI2\nsb.") + p = (b"\x80\x03cbson.binary\nBinary\nq\x00C\x03123q\x01\x85q" + b"\x02\x81q\x03}q\x04X\x10\x00\x00\x00_Binary__subtypeq" + b"\x05K\x02sb.") if not sys.version.startswith('3.0'): self.assertEqual(b1, pickle.loads(p)) @@ -357,16 +354,11 @@ class TestBinary(unittest.TestCase): self.assertEqual(b0, Binary(memoryview(b'123'), 2)) self.assertEqual(b0, Binary(bytearray(b'123'), 2)) - # mmap.mmap and array.array only expose the - # buffer interface in python 3.x - if PY3: - # No mmap module in Jython - import mmap - with mmap.mmap(-1, len(b'123')) as mm: - mm.write(b'123') - mm.seek(0) - self.assertEqual(b0, Binary(mm, 2)) - self.assertEqual(b0, Binary(array.array('B', b'123'), 2)) + with mmap.mmap(-1, len(b'123')) as mm: + mm.write(b'123') + mm.seek(0) + self.assertEqual(b0, Binary(mm, 2)) + self.assertEqual(b0, Binary(array.array('B', b'123'), 2)) class TestUuidSpecExplicitCoding(unittest.TestCase): @@ -377,9 +369,7 @@ class TestUuidSpecExplicitCoding(unittest.TestCase): @staticmethod def _hex_to_bytes(hexstring): - if PY3: - return bytes.fromhex(hexstring) - return hexstring.decode("hex") + return bytes.fromhex(hexstring) # Explicit encoding prose test #1 def test_encoding_1(self): @@ -482,9 +472,7 @@ class TestUuidSpecImplicitCoding(IntegrationTest): @staticmethod def _hex_to_bytes(hexstring): - if PY3: - return bytes.fromhex(hexstring) - return hexstring.decode("hex") + return bytes.fromhex(hexstring) def _get_coll_w_uuid_rep(self, uuid_rep): codec_options = self.client.codec_options.with_options( diff --git a/test/test_bson.py b/test/test_bson.py index ad726f71b..6637ca5da 100644 --- a/test/test_bson.py +++ b/test/test_bson.py @@ -16,14 +16,19 @@ """Test the bson module.""" +import array import collections import datetime +import mmap import os import re import sys import tempfile import uuid +from collections import abc, OrderedDict +from io import BytesIO + sys.path[0:0] = [""] import bson @@ -42,23 +47,18 @@ from bson.codec_options import CodecOptions from bson.int64 import Int64 from bson.objectid import ObjectId from bson.dbref import DBRef -from bson.py3compat import abc, iteritems, PY3, StringIO, text_type from bson.son import SON from bson.timestamp import Timestamp from bson.errors import (InvalidBSON, - InvalidDocument, - InvalidStringData) + InvalidDocument) from bson.max_key import MaxKey from bson.min_key import MinKey from bson.tz_util import (FixedOffset, utc) -from test import qcheck, SkipTest, unittest +from test import qcheck, unittest from test.utils import ExceptionCatchingThread -if PY3: - long = int - class NotADict(abc.MutableMapping): """Non-dict type that implements the mapping protocol.""" @@ -134,7 +134,7 @@ class TestBSON(unittest.TestCase): helper({}) helper({"test": u"hello"}) self.assertTrue(isinstance(decoder(encoder( - {"hello": "world"}))["hello"], text_type)) + {"hello": "world"}))["hello"], str)) helper({"mike": -10120}) helper({"long": Int64(10)}) helper({"really big long": 2147483648}) @@ -293,7 +293,7 @@ class TestBSON(unittest.TestCase): b"\x6f\x20\x77\x6F\x72\x6C\x64\x00\x00" b"\x05\x00\x00\x00\x00"))) self.assertEqual([{"test": u"hello world"}, {}], - list(decode_file_iter(StringIO( + list(decode_file_iter(BytesIO( b"\x1B\x00\x00\x00\x0E\x74\x65\x73\x74" b"\x00\x0C\x00\x00\x00\x68\x65\x6C\x6C" b"\x6f\x20\x77\x6F\x72\x6C\x64\x00\x00" @@ -305,14 +305,11 @@ class TestBSON(unittest.TestCase): self.assertEqual(docs, decode_all(bytearray(bs))) self.assertEqual(docs, decode_all(memoryview(bs))) self.assertEqual(docs, decode_all(memoryview(b'1' + bs + b'1')[1:-1])) - if PY3: - import array - import mmap - self.assertEqual(docs, decode_all(array.array('B', bs))) - with mmap.mmap(-1, len(bs)) as mm: - mm.write(bs) - mm.seek(0) - self.assertEqual(docs, decode_all(mm)) + self.assertEqual(docs, decode_all(array.array('B', bs))) + with mmap.mmap(-1, len(bs)) as mm: + mm.write(bs) + mm.seek(0) + self.assertEqual(docs, decode_all(mm)) def test_decode_buffer_protocol(self): doc = {'foo': 'bar'} @@ -321,21 +318,18 @@ class TestBSON(unittest.TestCase): self.assertEqual(doc, decode(bytearray(bs))) self.assertEqual(doc, decode(memoryview(bs))) self.assertEqual(doc, decode(memoryview(b'1' + bs + b'1')[1:-1])) - if PY3: - import array - import mmap - self.assertEqual(doc, decode(array.array('B', bs))) - with mmap.mmap(-1, len(bs)) as mm: - mm.write(bs) - mm.seek(0) - self.assertEqual(doc, decode(mm)) + self.assertEqual(doc, decode(array.array('B', bs))) + with mmap.mmap(-1, len(bs)) as mm: + mm.write(bs) + mm.seek(0) + self.assertEqual(doc, decode(mm)) def test_invalid_decodes(self): # Invalid object size (not enough bytes in document for even # an object size of first object. # NOTE: decode_all and decode_iter don't care, not sure if they should? self.assertRaises(InvalidBSON, list, - decode_file_iter(StringIO(b"\x1B"))) + decode_file_iter(BytesIO(b"\x1B"))) bad_bsons = [ # An object size that's too small to even include the object size, @@ -366,7 +360,7 @@ class TestBSON(unittest.TestCase): with self.assertRaises(InvalidBSON, msg=msg): list(decode_iter(data)) with self.assertRaises(InvalidBSON, msg=msg): - list(decode_file_iter(StringIO(data))) + list(decode_file_iter(BytesIO(data))) with tempfile.TemporaryFile() as scratch: scratch.write(data) scratch.seek(0, os.SEEK_SET) @@ -498,10 +492,7 @@ class TestBSON(unittest.TestCase): # Since `bytes` are stored as Binary you can't use them # as keys in python 3.x. Using binary data as a key makes # no sense in BSON anyway and little sense in python. - if PY3: - self.assertRaises(InvalidDocument, encode, doc) - else: - self.assertTrue(encode(doc)) + self.assertRaises(InvalidDocument, encode, doc) def test_datetime_encode_decode(self): # Negative timestamps @@ -604,13 +595,6 @@ class TestBSON(unittest.TestCase): self.assertEqual(d, decode(encode(d))) def test_bad_encode(self): - if not PY3: - # Python3 treats this as a unicode string which won't raise - # an exception. If we passed the string as bytes instead we - # still wouldn't get an error since we store bytes as BSON - # binary subtype 0. - self.assertRaises(InvalidStringData, encode, - {"lalala": '\xf4\xe0\xf0\xe1\xc0 Color Touch'}) # Work around what seems like a regression in python 3.5.0. # See http://bugs.python.org/issue25222 if sys.version_info[:2] < (3, 5): @@ -622,13 +606,13 @@ class TestBSON(unittest.TestCase): self.assertRaises(Exception, encode, evil_data) def test_overflow(self): - self.assertTrue(encode({"x": long(9223372036854775807)})) + self.assertTrue(encode({"x": 9223372036854775807})) self.assertRaises(OverflowError, encode, - {"x": long(9223372036854775808)}) + {"x": 9223372036854775808}) - self.assertTrue(encode({"x": long(-9223372036854775808)})) + self.assertTrue(encode({"x": -9223372036854775808})) self.assertRaises(OverflowError, encode, - {"x": long(-9223372036854775809)}) + {"x": -9223372036854775809}) def test_small_long_encode_decode(self): encoded1 = encode({'x': 256}) @@ -682,25 +666,10 @@ class TestBSON(unittest.TestCase): # b'a\xe9' == u"aé".encode("iso-8859-1") iso8859_bytes = b'a\xe9' y = {"hello": iso8859_bytes} - if PY3: - # Stored as BSON binary subtype 0. - out = decode(encode(y)) - self.assertTrue(isinstance(out['hello'], bytes)) - self.assertEqual(out['hello'], iso8859_bytes) - else: - # Python 2. - try: - encode(y) - except InvalidStringData as e: - self.assertTrue(repr(iso8859_bytes) in str(e)) - - # The next two tests only make sense in python 2.x since - # you can't use `bytes` type as document keys in python 3.x. - x = {u"aéあ".encode("utf-8"): u"aéあ".encode("utf-8")} - self.assertEqual(w, decode(encode(x))) - - z = {iso8859_bytes: "hello"} - self.assertRaises(InvalidStringData, encode, z) + # Stored as BSON binary subtype 0. + out = decode(encode(y)) + self.assertTrue(isinstance(out['hello'], bytes)) + self.assertEqual(out['hello'], iso8859_bytes) def test_null_character(self): doc = {"a": "\x00"} @@ -767,24 +736,20 @@ class TestBSON(unittest.TestCase): class _myfloat(float): pass - class _myunicode(text_type): + class _myunicode(str): pass d = {'a': _myint(42), 'b': _myfloat(63.9), 'c': _myunicode('hello world') } d2 = decode(encode(d)) - for key, value in iteritems(d2): + for key, value in d2.items(): orig_value = d[key] orig_type = orig_value.__class__.__bases__[0] self.assertEqual(type(value), orig_type) self.assertEqual(value, orig_type(value)) def test_ordered_dict(self): - try: - from collections import OrderedDict - except ImportError: - raise SkipTest("No OrderedDict") d = OrderedDict([("one", 1), ("two", 2), ("three", 3), ("four", 4)]) self.assertEqual( d, decode(encode(d), CodecOptions(document_class=OrderedDict))) diff --git a/test/test_bson_corpus.py b/test/test_bson_corpus.py index 780ea49a3..6590d00a3 100644 --- a/test/test_bson_corpus.py +++ b/test/test_bson_corpus.py @@ -33,7 +33,6 @@ from bson.decimal128 import Decimal128 from bson.dbref import DBRef from bson.errors import InvalidBSON, InvalidId from bson.json_util import JSONMode -from bson.py3compat import text_type, b from bson.son import SON from test import unittest @@ -56,7 +55,7 @@ _NON_PARSE_ERRORS = set([ _DEPRECATED_BSON_TYPES = { # Symbol - '0x0E': text_type, + '0x0E': str, # Undefined '0x06': type(None), # DBPointer @@ -122,7 +121,7 @@ def create_test(case_spec): encode_extjson = to_extjson encode_bson = to_bson - cB = binascii.unhexlify(b(valid_case['canonical_bson'])) + cB = binascii.unhexlify(valid_case['canonical_bson'].encode('utf8')) cEJ = valid_case['canonical_extjson'] rEJ = valid_case.get('relaxed_extjson') dEJ = valid_case.get('degenerate_extjson') @@ -139,7 +138,7 @@ def create_test(case_spec): if deprecated: if 'converted_bson' in valid_case: converted_bson = binascii.unhexlify( - b(valid_case['converted_bson'])) + valid_case['converted_bson'].encode('utf8')) self.assertEqual(encode_bson(decoded_bson), converted_bson) self.assertJsonEqual( encode_extjson(decode_bson(converted_bson)), @@ -167,7 +166,7 @@ def create_test(case_spec): # Test round-tripping degenerate bson. if 'degenerate_bson' in valid_case: - dB = binascii.unhexlify(b(valid_case['degenerate_bson'])) + dB = binascii.unhexlify(valid_case['degenerate_bson'].encode('utf8')) self.assertEqual(encode_bson(decode_bson(dB)), cB) # Test round-tripping degenerate extended json. @@ -186,7 +185,7 @@ def create_test(case_spec): for decode_error_case in case_spec.get('decodeErrors', []): with self.assertRaises(InvalidBSON): decode_bson( - binascii.unhexlify(b(decode_error_case['bson']))) + binascii.unhexlify(decode_error_case['bson'].encode('utf8'))) for parse_error_case in case_spec.get('parseErrors', []): if bson_type == '0x13': diff --git a/test/test_change_stream.py b/test/test_change_stream.py index 851b599f9..293c3bfc1 100644 --- a/test/test_change_stream.py +++ b/test/test_change_stream.py @@ -23,7 +23,6 @@ import threading import time import uuid -from contextlib import contextmanager from itertools import product sys.path[0:0] = [''] @@ -33,7 +32,6 @@ from bson.binary import (ALL_UUID_REPRESENTATIONS, Binary, STANDARD, PYTHON_LEGACY) -from bson.py3compat import iteritems from bson.raw_bson import DEFAULT_RAW_BSON_OPTIONS, RawBSONDocument from pymongo import MongoClient @@ -1093,7 +1091,7 @@ class TestAllScenarios(unittest.TestCase): def assert_dict_is_subset(self, superdict, subdict): """Check that subdict is a subset of superdict.""" exempt_fields = ["documentKey", "_id", "getMore"] - for key, value in iteritems(subdict): + for key, value in subdict.items(): if key not in superdict: self.fail('Key %s not found in %s' % (key, superdict)) if isinstance(value, dict): @@ -1111,7 +1109,7 @@ class TestAllScenarios(unittest.TestCase): def check_event(self, event, expectation_dict): if event is None: self.fail() - for key, value in iteritems(expectation_dict): + for key, value in expectation_dict.items(): if isinstance(value, dict): self.assert_dict_is_subset(getattr(event, key), value) else: @@ -1149,9 +1147,9 @@ def get_change_stream(client, scenario_def, test): cs_pipeline = test["changeStreamPipeline"] options = test["changeStreamOptions"] cs_options = {} - for key, value in iteritems(options): + for key, value in options.items(): cs_options[camel_to_snake(key)] = value - + # Create and return change stream return cs_target.watch(pipeline=cs_pipeline, **cs_options) @@ -1203,12 +1201,12 @@ def create_test(scenario_def, test): for change, expected_changes in zip(changes, test["result"]["success"]): self.assert_dict_is_subset(change, expected_changes) self.assertEqual(len(changes), len(test["result"]["success"])) - + finally: # Check for expected events results = self.listener.results for idx, expectation in enumerate(test.get("expectations", [])): - for event_type, event_desc in iteritems(expectation): + for event_type, event_desc in expectation.items(): results_key = event_type.split("_")[1] event = results[results_key][idx] if len(results[results_key]) > idx else None self.check_event(event, event_desc) diff --git a/test/test_client.py b/test/test_client.py index df8122bba..add8a789c 100644 --- a/test/test_client.py +++ b/test/test_client.py @@ -24,6 +24,7 @@ import socket import struct import sys import time +import _thread as thread import threading import warnings @@ -31,7 +32,6 @@ sys.path[0:0] = [""] from bson import encode from bson.codec_options import CodecOptions, TypeEncoder, TypeRegistry -from bson.py3compat import thread from bson.son import SON from bson.tz_util import utc import pymongo @@ -90,7 +90,6 @@ from test.utils import (assertRaisesExactly, rs_client, rs_or_single_client, rs_or_single_client_noauth, - server_is_master_with_slave, single_client, wait_until) diff --git a/test/test_collection.py b/test/test_collection.py index c2b06ca37..db6202154 100644 --- a/test/test_collection.py +++ b/test/test_collection.py @@ -19,7 +19,6 @@ import contextlib import re import sys -import threading from codecs import utf_8_decode from collections import defaultdict @@ -32,7 +31,6 @@ from bson.regex import Regex from bson.code import Code from bson.codec_options import CodecOptions from bson.objectid import ObjectId -from bson.py3compat import itervalues from bson.son import SON from pymongo import (ASCENDING, DESCENDING, GEO2D, GEOHAYSTACK, GEOSPHERE, HASHED, TEXT) @@ -376,7 +374,7 @@ class TestCollection(IntegrationTest): reindexed = db.test.reindex() if 'raw' in reindexed: # mongos - for result in itervalues(reindexed['raw']): + for result in reindexed['raw'].values(): check_result(result) else: check_result(reindexed) @@ -1349,12 +1347,6 @@ class TestCollection(IntegrationTest): self.assertIn('E11000 duplicate key error', str(ctx.exception)) - if sys.version_info[0] == 2: - # Test unicode(error) conversion. - self.assertIn('E11000 duplicate key error', - unicode(ctx.exception)) - - def test_wtimeout(self): # Ensure setting wtimeout doesn't disable write concern altogether. # See SERVER-12596. diff --git a/test/test_crud_v1.py b/test/test_crud_v1.py index 48df16df9..c93d6a22e 100644 --- a/test/test_crud_v1.py +++ b/test/test_crud_v1.py @@ -14,14 +14,11 @@ """Test the collection module.""" -import json import os -import re import sys sys.path[0:0] = [""] -from bson.py3compat import iteritems from pymongo import operations, WriteConcern from pymongo.command_cursor import CommandCursor from pymongo.cursor import Cursor @@ -35,7 +32,7 @@ from pymongo.operations import (InsertOne, UpdateOne, UpdateMany) -from test import unittest, client_context, IntegrationTest +from test import unittest, IntegrationTest from test.utils import (camel_to_snake, camel_to_upper_camel, camel_to_snake_args, drop_collections, TestCreator) @@ -113,7 +110,7 @@ def run_operation(collection, test): # PyMongo accepts sort as list of tuples. if arg_name == "sort": sort_dict = arguments[arg_name] - arguments[arg_name] = list(iteritems(sort_dict)) + arguments[arg_name] = list(sort_dict.items()) # Named "key" instead not fieldName. if arg_name == "fieldName": arguments["key"] = arguments.pop(arg_name) diff --git a/test/test_cursor.py b/test/test_cursor.py index 1d8aae2af..8cb98c0ef 100644 --- a/test/test_cursor.py +++ b/test/test_cursor.py @@ -27,7 +27,6 @@ sys.path[0:0] = [""] from bson import decode_all from bson.code import Code -from bson.py3compat import PY3 from bson.son import SON from pymongo import (ASCENDING, DESCENDING, @@ -40,7 +39,6 @@ from pymongo.errors import (ConfigurationError, InvalidOperation, OperationFailure) from pymongo.read_concern import ReadConcern -from pymongo.read_preferences import ReadPreference from test import (client_context, unittest, IntegrationTest) @@ -49,9 +47,6 @@ from test.utils import (EventListener, rs_or_single_client, WhiteListEventListener) -if PY3: - long = int - class TestCursor(IntegrationTest): def test_deepcopy_cursor_littered_with_regexes(self): @@ -167,7 +162,7 @@ class TestCursor(IntegrationTest): coll.insert_one({"amalia": 2}) coll.find().max_time_ms(None) - coll.find().max_time_ms(long(1)) + coll.find().max_time_ms(1) cursor = coll.find().max_time_ms(999) self.assertEqual(999, cursor._Cursor__max_time_ms) @@ -215,7 +210,7 @@ class TestCursor(IntegrationTest): coll.insert_one({"amalia": 2}) coll.find().max_await_time_ms(None) - coll.find().max_await_time_ms(long(1)) + coll.find().max_await_time_ms(1) # When cursor is not tailable_await cursor = coll.find() @@ -414,7 +409,7 @@ class TestCursor(IntegrationTest): self.assertRaises(TypeError, db.test.find().limit, None) self.assertRaises(TypeError, db.test.find().limit, "hello") self.assertRaises(TypeError, db.test.find().limit, 5.5) - self.assertTrue(db.test.find().limit(long(5))) + self.assertTrue(db.test.find().limit(5)) db.test.drop() db.test.insert_many([{"x": i} for i in range(100)]) @@ -560,7 +555,7 @@ class TestCursor(IntegrationTest): self.assertRaises(TypeError, db.test.find().batch_size, "hello") self.assertRaises(TypeError, db.test.find().batch_size, 5.5) self.assertRaises(ValueError, db.test.find().batch_size, -1) - self.assertTrue(db.test.find().batch_size(long(5))) + self.assertTrue(db.test.find().batch_size(5)) a = db.test.find() for _ in a: break @@ -684,7 +679,7 @@ class TestCursor(IntegrationTest): self.assertRaises(TypeError, db.test.find().skip, "hello") self.assertRaises(TypeError, db.test.find().skip, 5.5) self.assertRaises(ValueError, db.test.find().skip, -5) - self.assertTrue(db.test.find().skip(long(5))) + self.assertTrue(db.test.find().skip(5)) db.drop_collection("test") @@ -1059,7 +1054,7 @@ class TestCursor(IntegrationTest): self.assertEqual(5, len(list(self.db.test.find()[20:25]))) self.assertEqual(5, len(list( - self.db.test.find()[long(20):long(25)]))) + self.db.test.find()[20:25]))) for a, b in zip(count(20), self.db.test.find()[20:25]): self.assertEqual(a, b['i']) @@ -1104,7 +1099,7 @@ class TestCursor(IntegrationTest): self.assertEqual(50, self.db.test.find()[50]['i']) self.assertEqual(50, self.db.test.find().skip(50)[0]['i']) self.assertEqual(50, self.db.test.find().skip(49)[1]['i']) - self.assertEqual(50, self.db.test.find()[long(50)]['i']) + self.assertEqual(50, self.db.test.find()[50]['i']) self.assertEqual(99, self.db.test.find()[99]['i']) self.assertRaises(IndexError, lambda x: self.db.test.find()[x], -1) diff --git a/test/test_custom_types.py b/test/test_custom_types.py index bd9a83552..e48acedff 100644 --- a/test/test_custom_types.py +++ b/test/test_custom_types.py @@ -17,6 +17,7 @@ import datetime import sys import tempfile + from collections import OrderedDict from decimal import Decimal from random import random @@ -39,7 +40,6 @@ from bson.codec_options import (CodecOptions, TypeCodec, TypeDecoder, from bson.errors import InvalidDocument from bson.int64 import Int64 from bson.raw_bson import RawBSONDocument -from bson.py3compat import text_type from gridfs import GridIn, GridOut @@ -110,7 +110,7 @@ UNINT_CODECOPTS = CodecOptions(type_registry=TypeRegistry( class UppercaseTextDecoder(TypeDecoder): - bson_type = text_type + bson_type = str def transform_bson(self, value): return value.upper() diff --git a/test/test_database.py b/test/test_database.py index 262fcf7df..de0415f4b 100644 --- a/test/test_database.py +++ b/test/test_database.py @@ -21,13 +21,11 @@ import warnings sys.path[0:0] = [""] -from bson.code import Code from bson.codec_options import CodecOptions from bson.int64 import Int64 from bson.regex import Regex from bson.dbref import DBRef from bson.objectid import ObjectId -from bson.py3compat import string_type, text_type, PY3 from bson.son import SON from pymongo import (ALL, auth, @@ -63,10 +61,6 @@ from test.utils import (EventListener, from test.test_custom_types import DECIMAL_CODECOPTS -if PY3: - long = int - - class TestDatabaseNoConnect(unittest.TestCase): """Test Database features on a client that does not connect. """ @@ -432,10 +426,10 @@ class TestDatabase(IntegrationTest): # These basically clue us in to server changes. self.assertTrue(isinstance(info[0]['responseLength'], int)) self.assertTrue(isinstance(info[0]['millis'], int)) - self.assertTrue(isinstance(info[0]['client'], string_type)) - self.assertTrue(isinstance(info[0]['user'], string_type)) - self.assertTrue(isinstance(info[0]['ns'], string_type)) - self.assertTrue(isinstance(info[0]['op'], string_type)) + self.assertTrue(isinstance(info[0]['client'], str)) + self.assertTrue(isinstance(info[0]['user'], str)) + self.assertTrue(isinstance(info[0]['ns'], str)) + self.assertTrue(isinstance(info[0]['op'], str)) self.assertTrue(isinstance(info[0]["ts"], datetime.datetime)) @client_context.require_no_mongos @@ -513,7 +507,7 @@ class TestDatabase(IntegrationTest): self.assertRaises(TypeError, auth._password_digest, None) self.assertTrue(isinstance(auth._password_digest("mike", "password"), - text_type)) + str)) self.assertEqual(auth._password_digest("mike", "password"), u"cd7e45b3b2767dc2fa9b6b548457ed00") self.assertEqual(auth._password_digest("mike", "password"), @@ -828,7 +822,7 @@ class TestDatabase(IntegrationTest): def test_long(self): db = self.client.pymongo_test db.test.drop() - db.test.insert_one({"x": long(9223372036854775807)}) + db.test.insert_one({"x": 9223372036854775807}) retrieved = db.test.find_one()['x'] self.assertEqual(Int64(9223372036854775807), retrieved) self.assertIsInstance(retrieved, Int64) diff --git a/test/test_decimal128.py b/test/test_decimal128.py index e72c1273e..b242b7dfa 100644 --- a/test/test_decimal128.py +++ b/test/test_decimal128.py @@ -14,22 +14,14 @@ """Tests for Decimal128.""" -import codecs -import glob -import json -import os.path import pickle import sys -from binascii import unhexlify -from decimal import Decimal, DecimalException +from decimal import Decimal sys.path[0:0] = [""] -from bson import BSON from bson.decimal128 import Decimal128, create_decimal128_context -from bson.json_util import dumps, loads -from bson.py3compat import b from test import client_context, unittest class TestDecimal128(unittest.TestCase): diff --git a/test/test_discovery_and_monitoring.py b/test/test_discovery_and_monitoring.py index 601c0b515..52527464d 100644 --- a/test/test_discovery_and_monitoring.py +++ b/test/test_discovery_and_monitoring.py @@ -41,7 +41,6 @@ from test.utils import (assertion_context, cdecimal_patched, CMAPListener, client_context, - Barrier, get_pool, HeartbeatEventListener, server_name_to_type, @@ -263,7 +262,7 @@ class TestIgnoreStaleErrors(IntegrationTest): def test_ignore_stale_connection_errors(self): N_THREADS = 5 - barrier = Barrier(N_THREADS, timeout=30) + barrier = threading.Barrier(N_THREADS, timeout=30) client = rs_or_single_client(minPoolSize=N_THREADS) self.addCleanup(client.close) diff --git a/test/test_encryption.py b/test/test_encryption.py index 28ba331e3..90da8b3dc 100644 --- a/test/test_encryption.py +++ b/test/test_encryption.py @@ -31,7 +31,6 @@ from bson.binary import (Binary, STANDARD, UUID_SUBTYPE) from bson.codec_options import CodecOptions -from bson.py3compat import _unicode from bson.errors import BSONError from bson.json_util import JSONOptions from bson.son import SON @@ -455,7 +454,7 @@ AZURE_CREDS = { GCP_CREDS = { 'email': os.environ.get('FLE_GCP_EMAIL', ''), - 'privateKey': _unicode(os.environ.get('FLE_GCP_PRIVATEKEY', ''))} + 'privateKey': os.environ.get('FLE_GCP_PRIVATEKEY', '')} class TestSpec(SpecRunner): @@ -1307,7 +1306,7 @@ class TestAzureEncryption(AzureGCPEncryptionTestMixin, 'AQGVERPgAAAAAAAAAAAAAAAC5DbBSwPwfSlBrDtRuglvNvCXD1KzDuCKY2P+4bRFtHDjpTOE2XuytPAUaAbXf1orsPq59PVZmsbTZbt2CB8qaQ==') def test_automatic(self): - expected_document_extjson = textwrap.dedent(""" + expected_document_extjson = textwrap.dedent(""" {"secret_azure": { "$binary": { "base64": "AQGVERPgAAAAAAAAAAAAAAAC5DbBSwPwfSlBrDtRuglvNvCXD1KzDuCKY2P+4bRFtHDjpTOE2XuytPAUaAbXf1orsPq59PVZmsbTZbt2CB8qaQ==", @@ -1333,7 +1332,7 @@ class TestGCPEncryption(AzureGCPEncryptionTestMixin, 'ARgj/gAAAAAAAAAAAAAAAAACwFd+Y5Ojw45GUXNvbcIpN9YkRdoHDHkR4kssdn0tIMKlDQOLFkWFY9X07IRlXsxPD8DcTiKnl6XINK28vhcGlg==') def test_automatic(self): - expected_document_extjson = textwrap.dedent(""" + expected_document_extjson = textwrap.dedent(""" {"secret_gcp": { "$binary": { "base64": "ARgj/gAAAAAAAAAAAAAAAAACwFd+Y5Ojw45GUXNvbcIpN9YkRdoHDHkR4kssdn0tIMKlDQOLFkWFY9X07IRlXsxPD8DcTiKnl6XINK28vhcGlg==", diff --git a/test/test_errors.py b/test/test_errors.py index 968c8ebd7..dd5fc1189 100644 --- a/test/test_errors.py +++ b/test/test_errors.py @@ -45,10 +45,7 @@ class TestErrors(PyMongoTestCase): self.assertIn("full error", traceback.format_exc()) def _test_unicode_strs(self, exc): - if sys.version_info[0] == 2: - self.assertEqual("unicode \xf0\x9f\x90\x8d, full error: {" - "'errmsg': u'unicode \\U0001f40d'}", str(exc)) - elif 'PyPy' in sys.version: + if 'PyPy' in sys.version: # PyPy displays unicode in repr differently. self.assertEqual("unicode \U0001f40d, full error: {" "'errmsg': 'unicode \\U0001f40d'}", str(exc)) diff --git a/test/test_grid_file.py b/test/test_grid_file.py index 2de173cab..705b9f032 100644 --- a/test/test_grid_file.py +++ b/test/test_grid_file.py @@ -20,10 +20,12 @@ import datetime import sys import zipfile + +from io import BytesIO + sys.path[0:0] = [""] from bson.objectid import ObjectId -from bson.py3compat import StringIO from gridfs import GridFS from gridfs.grid_file import (DEFAULT_CHUNK_SIZE, _SEEK_CUR, @@ -234,7 +236,7 @@ class TestGridFile(IntegrationTest): cursor = GridOutCursor(self.db.fs, {}) cursor_clone = cursor.clone() - + cursor_dict = cursor.__dict__.copy() cursor_dict.pop('_Cursor__session') cursor_clone_dict = cursor_clone.__dict__.copy() @@ -301,7 +303,7 @@ class TestGridFile(IntegrationTest): five = GridIn(self.db.fs, chunk_size=2) five.write(b"hello") - buffer = StringIO(b" world") + buffer = BytesIO(b" world") five.write(buffer) five.write(b" and mongodb") five.close() @@ -567,7 +569,7 @@ Bye""")) def test_prechunked_string(self): def write_me(s, chunk_size): - buf = StringIO(s) + buf = BytesIO(s) infile = GridIn(self.db.fs) while True: to_write = buf.read(chunk_size) @@ -646,7 +648,7 @@ Bye""")) self.assertIn("getMore", listener.started_command_names()) def test_zip(self): - zf = StringIO() + zf = BytesIO() z = zipfile.ZipFile(zf, "w") z.writestr("test.txt", b"hello world") z.close() diff --git a/test/test_gridfs.py b/test/test_gridfs.py index 0d05e0b78..1827944c5 100644 --- a/test/test_gridfs.py +++ b/test/test_gridfs.py @@ -16,21 +16,23 @@ """Tests for the gridfs package. """ -import sys -sys.path[0:0] = [""] import datetime +import sys import threading import time -import gridfs + +from io import BytesIO + +sys.path[0:0] = [""] from bson.binary import Binary -from bson.py3compat import StringIO, string_type from pymongo.mongo_client import MongoClient from pymongo.errors import (ConfigurationError, NotMasterError, ServerSelectionTimeoutError) from pymongo.read_preferences import ReadPreference +import gridfs from gridfs.errors import CorruptGridFile, FileExists, NoFile from test import (client_context, unittest, @@ -156,7 +158,7 @@ class TestGridfs(IntegrationTest): self.assertEqual(oid, raw["_id"]) self.assertTrue(isinstance(raw["uploadDate"], datetime.datetime)) self.assertEqual(255 * 1024, raw["chunkSize"]) - self.assertTrue(isinstance(raw["md5"], string_type)) + self.assertTrue(isinstance(raw["md5"], str)) def test_corrupt_chunk(self): files_id = self.fs.put(b'foobar') @@ -311,23 +313,38 @@ class TestGridfs(IntegrationTest): time.sleep(0.01) three = self.fs.put(b"baz", filename="test", author="author2") - self.assertEqual(b"foo", self.fs.get_version(filename="test", author="author1", version=-2).read()) - self.assertEqual(b"bar", self.fs.get_version(filename="test", author="author1", version=-1).read()) - self.assertEqual(b"foo", self.fs.get_version(filename="test", author="author1", version=0).read()) - self.assertEqual(b"bar", self.fs.get_version(filename="test", author="author1", version=1).read()) - self.assertEqual(b"baz", self.fs.get_version(filename="test", author="author2", version=0).read()) - self.assertEqual(b"baz", self.fs.get_version(filename="test", version=-1).read()) - self.assertEqual(b"baz", self.fs.get_version(filename="test", version=2).read()) + self.assertEqual( + b"foo", + self.fs.get_version( + filename="test", author="author1", version=-2).read()) + self.assertEqual( + b"bar", self.fs.get_version( + filename="test", author="author1", version=-1).read()) + self.assertEqual( + b"foo", self.fs.get_version( + filename="test", author="author1", version=0).read()) + self.assertEqual( + b"bar", self.fs.get_version( + filename="test", author="author1", version=1).read()) + self.assertEqual( + b"baz", self.fs.get_version( + filename="test", author="author2", version=0).read()) + self.assertEqual( + b"baz", self.fs.get_version(filename="test", version=-1).read()) + self.assertEqual( + b"baz", self.fs.get_version(filename="test", version=2).read()) - self.assertRaises(NoFile, self.fs.get_version, filename="test", author="author3") - self.assertRaises(NoFile, self.fs.get_version, filename="test", author="author1", version=2) + self.assertRaises( + NoFile, self.fs.get_version, filename="test", author="author3") + self.assertRaises( + NoFile, self.fs.get_version, filename="test", author="author1", version=2) self.fs.delete(one) self.fs.delete(two) self.fs.delete(three) def test_put_filelike(self): - oid = self.fs.put(StringIO(b"hello world"), chunk_size=1) + oid = self.fs.put(BytesIO(b"hello world"), chunk_size=1) self.assertEqual(11, self.db.fs.chunks.count_documents({})) self.assertEqual(b"hello world", self.fs.get(oid).read()) diff --git a/test/test_gridfs_bucket.py b/test/test_gridfs_bucket.py index 17b5364bb..98478bf7b 100644 --- a/test/test_gridfs_bucket.py +++ b/test/test_gridfs_bucket.py @@ -21,13 +21,13 @@ import itertools import threading import time -import gridfs +from io import BytesIO from bson.binary import Binary from bson.int64 import Int64 from bson.objectid import ObjectId -from bson.py3compat import StringIO, string_type from bson.son import SON +import gridfs from gridfs.errors import NoFile, CorruptGridFile from pymongo.errors import (ConfigurationError, NotMasterError, @@ -131,7 +131,7 @@ class TestGridfs(IntegrationTest): self.assertEqual(oid, raw["_id"]) self.assertTrue(isinstance(raw["uploadDate"], datetime.datetime)) self.assertEqual(255 * 1024, raw["chunkSize"]) - self.assertTrue(isinstance(raw["md5"], string_type)) + self.assertTrue(isinstance(raw["md5"], str)) def test_corrupt_chunk(self): files_id = self.fs.upload_from_stream("test_filename", @@ -293,7 +293,7 @@ class TestGridfs(IntegrationTest): def test_upload_from_stream(self): oid = self.fs.upload_from_stream("test_file", - StringIO(b"hello world"), + BytesIO(b"hello world"), chunk_size_bytes=1) self.assertEqual(11, self.db.fs.chunks.count_documents({})) self.assertEqual(b"hello world", @@ -303,7 +303,7 @@ class TestGridfs(IntegrationTest): oid = ObjectId() self.fs.upload_from_stream_with_id(oid, "test_file_custom_id", - StringIO(b"custom id"), + BytesIO(b"custom id"), chunk_size_bytes=1) self.assertEqual(b"custom id", self.fs.open_download_stream(oid).read()) @@ -416,11 +416,11 @@ class TestGridfs(IntegrationTest): {"files_id": gin._id})) def test_download_to_stream(self): - file1 = StringIO(b"hello world") + file1 = BytesIO(b"hello world") # Test with one chunk. oid = self.fs.upload_from_stream("one_chunk", file1) self.assertEqual(1, self.db.fs.chunks.count_documents({})) - file2 = StringIO() + file2 = BytesIO() self.fs.download_to_stream(oid, file2) file1.seek(0) file2.seek(0) @@ -434,18 +434,18 @@ class TestGridfs(IntegrationTest): file1, chunk_size_bytes=1) self.assertEqual(11, self.db.fs.chunks.count_documents({})) - file2 = StringIO() + file2 = BytesIO() self.fs.download_to_stream(oid, file2) file1.seek(0) file2.seek(0) self.assertEqual(file1.read(), file2.read()) def test_download_to_stream_by_name(self): - file1 = StringIO(b"hello world") + file1 = BytesIO(b"hello world") # Test with one chunk. oid = self.fs.upload_from_stream("one_chunk", file1) self.assertEqual(1, self.db.fs.chunks.count_documents({})) - file2 = StringIO() + file2 = BytesIO() self.fs.download_to_stream_by_name("one_chunk", file2) file1.seek(0) file2.seek(0) @@ -458,7 +458,7 @@ class TestGridfs(IntegrationTest): self.fs.upload_from_stream("many_chunks", file1, chunk_size_bytes=1) self.assertEqual(11, self.db.fs.chunks.count_documents({})) - file2 = StringIO() + file2 = BytesIO() self.fs.download_to_stream_by_name("many_chunks", file2) file1.seek(0) file2.seek(0) diff --git a/test/test_gridfs_spec.py b/test/test_gridfs_spec.py index bda6f5281..e9e097568 100644 --- a/test/test_gridfs_spec.py +++ b/test/test_gridfs_spec.py @@ -13,22 +13,21 @@ # limitations under the License. """Test GridFSBucket class.""" + import copy import datetime import os -import sys import re +import sys from json import loads -import gridfs - sys.path[0:0] = [""] from bson import Binary from bson.int64 import Int64 from bson.json_util import object_hook -from bson.py3compat import bytes_from_hex +import gridfs from gridfs.errors import NoFile, CorruptGridFile from test import (unittest, IntegrationTest) @@ -210,7 +209,7 @@ def create_tests(): for key, val in jsn.items(): if key in ("data", "source", "result"): if "$hex" in val: - jsn[key] = Binary(bytes_from_hex(val['$hex'])) + jsn[key] = Binary(bytes.fromhex(val['$hex'])) if isinstance(jsn[key], dict): str2hex(jsn[key]) if isinstance(jsn[key], list): diff --git a/test/test_json_util.py b/test/test_json_util.py index 7906b276f..5b6efbc63 100644 --- a/test/test_json_util.py +++ b/test/test_json_util.py @@ -39,8 +39,6 @@ from bson.tz_util import FixedOffset, utc from test import unittest, IntegrationTest -PY3 = sys.version_info[0] == 3 - class TestJsonUtil(unittest.TestCase): def round_tripped(self, doc, **kwargs): @@ -336,10 +334,7 @@ class TestJsonUtil(unittest.TestCase): doc, json_util.loads(ext_json_str, json_options=options)) def test_binary(self): - if PY3: - bin_type_dict = {"bin": b"\x00\x01\x02\x03\x04"} - else: - bin_type_dict = {"bin": Binary(b"\x00\x01\x02\x03\x04")} + bin_type_dict = {"bin": b"\x00\x01\x02\x03\x04"} md5_type_dict = { "md5": Binary(b' n7\x18\xaf\t/\xd1\xd1/\x80\xca\xe7q\xcc\xac', MD5_SUBTYPE)} @@ -352,10 +347,7 @@ class TestJsonUtil(unittest.TestCase): # Binary with subtype 0 is decoded into bytes in Python 3. bin = json_util.loads( '{"bin": {"$binary": "AAECAwQ=", "$type": "00"}}')['bin'] - if PY3: - self.assertEqual(type(bin), bytes) - else: - self.assertEqual(type(bin), Binary) + self.assertEqual(type(bin), bytes) # PYTHON-443 ensure old type formats are supported json_bin_dump = json_util.dumps(bin_type_dict) diff --git a/test/test_legacy_api.py b/test/test_legacy_api.py index 1c909c904..6d7a6425b 100644 --- a/test/test_legacy_api.py +++ b/test/test_legacy_api.py @@ -19,7 +19,6 @@ import sys import threading import time import uuid -import warnings sys.path[0:0] = [""] @@ -27,14 +26,11 @@ from bson.binary import PYTHON_LEGACY, STANDARD from bson.code import Code from bson.codec_options import CodecOptions from bson.objectid import ObjectId -from bson.py3compat import string_type from bson.son import SON from pymongo import ASCENDING, DESCENDING, GEOHAYSTACK -from pymongo.database import Database from pymongo.common import partition_node from pymongo.errors import (BulkWriteError, ConfigurationError, - CursorNotFound, DocumentTooLarge, DuplicateKeyError, InvalidDocument, @@ -42,7 +38,6 @@ from pymongo.errors import (BulkWriteError, OperationFailure, WriteConcernError, WTimeoutError) -from pymongo.message import _CursorAddress from pymongo.operations import IndexModel from pymongo.son_manipulator import (AutoReference, NamespaceInjector, @@ -143,14 +138,8 @@ class TestLegacy(IntegrationTest): self.assertEqual(doc["_id"], _id) self.assertTrue(isinstance(_id, ObjectId)) - doc_class = dict - # Work around http://bugs.jython.org/issue1728 - if (sys.platform.startswith('java') and - sys.version_info[:3] >= (2, 5, 2)): - doc_class = SON - db = self.client.get_database( - db.name, codec_options=CodecOptions(document_class=doc_class)) + db.name, codec_options=CodecOptions(document_class=dict)) def remove_insert_find_one(doc): db.test.remove({}) @@ -2315,7 +2304,7 @@ class TestLegacyBulkWriteConcern(BulkTestBase): failed = result['writeConcernErrors'][0] self.assertEqual(64, failed['code']) - self.assertTrue(isinstance(failed['errmsg'], string_type)) + self.assertTrue(isinstance(failed['errmsg'], str)) self.coll.delete_many({}) self.coll.create_index('a', unique=True) @@ -2413,12 +2402,12 @@ class TestLegacyBulkWriteConcern(BulkTestBase): failed = result['writeErrors'][0] self.assertEqual(2, failed['index']) self.assertEqual(11000, failed['code']) - self.assertTrue(isinstance(failed['errmsg'], string_type)) + self.assertTrue(isinstance(failed['errmsg'], str)) self.assertEqual(1, failed['op']['a']) failed = result['writeConcernErrors'][0] self.assertEqual(64, failed['code']) - self.assertTrue(isinstance(failed['errmsg'], string_type)) + self.assertTrue(isinstance(failed['errmsg'], str)) upserts = result['upserted'] self.assertEqual(1, len(upserts)) diff --git a/test/test_monitoring.py b/test/test_monitoring.py index ba6d2a44f..f46ebe69b 100644 --- a/test/test_monitoring.py +++ b/test/test_monitoring.py @@ -21,7 +21,6 @@ import warnings sys.path[0:0] = [""] from bson.objectid import ObjectId -from bson.py3compat import text_type from bson.son import SON from pymongo import CursorType, monitoring, InsertOne, UpdateOne, DeleteOne from pymongo.command_cursor import CommandCursor @@ -37,10 +36,8 @@ from test import (client_context, unittest) from test.utils import (EventListener, get_pool, - ignore_deprecations, rs_or_single_client, - single_client, - wait_until) + single_client) class TestCommandMonitoring(PyMongoTestCase): @@ -821,7 +818,7 @@ class TestCommandMonitoring(PyMongoTestCase): error = errors[0] self.assertEqual(0, error.get('index')) self.assertIsInstance(error.get('code'), int) - self.assertIsInstance(error.get('errmsg'), text_type) + self.assertIsInstance(error.get('errmsg'), str) def test_legacy_writes(self): with warnings.catch_warnings(): diff --git a/test/test_objectid.py b/test/test_objectid.py index df80caf39..7b26e7da8 100644 --- a/test/test_objectid.py +++ b/test/test_objectid.py @@ -23,7 +23,6 @@ sys.path[0:0] = [""] from bson.errors import InvalidId from bson.objectid import ObjectId, _MAX_COUNTER_VALUE -from bson.py3compat import PY3, _unicode from bson.tz_util import (FixedOffset, utc) from test import SkipTest, unittest @@ -50,7 +49,7 @@ class TestObjectId(unittest.TestCase): def test_unicode(self): a = ObjectId() - self.assertEqual(a, ObjectId(_unicode(a))) + self.assertEqual(a, ObjectId(a)) self.assertEqual(ObjectId("123456789012123456789012"), ObjectId(u"123456789012123456789012")) self.assertRaises(InvalidId, ObjectId, u"hello") @@ -139,13 +138,9 @@ class TestObjectId(unittest.TestCase): b"object\np2\nNtp3\nRp4\n" b"S'M\\x9afV\\x13v\\xc0\\x0b\\x88\\x00\\x00\\x00'\np5\nb.") - if PY3: - # Have to load using 'latin-1' since these were pickled in python2.x. - oid_1_9 = pickle.loads(pickled_with_1_9, encoding='latin-1') - oid_1_10 = pickle.loads(pickled_with_1_10, encoding='latin-1') - else: - oid_1_9 = pickle.loads(pickled_with_1_9) - oid_1_10 = pickle.loads(pickled_with_1_10) + # Have to load using 'latin-1' since these were pickled in python2.x. + oid_1_9 = pickle.loads(pickled_with_1_9, encoding='latin-1') + oid_1_10 = pickle.loads(pickled_with_1_10, encoding='latin-1') self.assertEqual(oid_1_9, ObjectId("4d9a66561376c00b88000000")) self.assertEqual(oid_1_9, oid_1_10) diff --git a/test/test_read_preferences.py b/test/test_read_preferences.py index 2a8eb96e9..2bb1c57f2 100644 --- a/test/test_read_preferences.py +++ b/test/test_read_preferences.py @@ -23,7 +23,6 @@ import warnings sys.path[0:0] = [""] -from bson.py3compat import MAXSIZE from bson.son import SON from pymongo.errors import ConfigurationError, OperationFailure from pymongo.message import _maybe_add_read_preference @@ -430,7 +429,7 @@ class TestCommandAndReadPreference(IntegrationTest): # the collection already exists. self._test_primary_helper( lambda: self.c.pymongo_test.create_collection( - 'some_collection%s' % random.randint(0, MAXSIZE))) + 'some_collection%s' % random.randint(0, sys.maxsize))) @client_context.require_version_max(4, 1, 0, -1) def test_group(self): diff --git a/test/test_session.py b/test/test_session.py index 2e2038501..6a6ed12e0 100644 --- a/test/test_session.py +++ b/test/test_session.py @@ -18,8 +18,9 @@ import copy import os import sys +from io import BytesIO + from bson import DBRef -from bson.py3compat import StringIO from gridfs import GridFS, GridFSBucket from pymongo import ASCENDING, InsertOne, IndexModel, OFF, monitoring from pymongo.common import _MAX_END_SESSIONS @@ -465,7 +466,7 @@ class TestSession(IntegrationTest): for f in files: f.read() - sio = StringIO() + sio = BytesIO() self._test_ops( client, diff --git a/test/test_son.py b/test/test_son.py index 921f85d45..a8ac49060 100644 --- a/test/test_son.py +++ b/test/test_son.py @@ -21,9 +21,8 @@ import sys sys.path[0:0] = [""] -from bson.py3compat import b from bson.son import SON -from test import SkipTest, unittest +from test import unittest class TestSON(unittest.TestCase): @@ -107,11 +106,10 @@ class TestSON(unittest.TestCase): def test_pickle_backwards_compatability(self): # This string was generated by pickling a SON object in pymongo # version 2.1.1 - pickled_with_2_1_1 = b( + pickled_with_2_1_1 = ( "ccopy_reg\n_reconstructor\np0\n(cbson.son\nSON\np1\n" "c__builtin__\ndict\np2\n(dp3\ntp4\nRp5\n(dp6\n" - "S'_SON__keys'\np7\n(lp8\nsb." - ) + "S'_SON__keys'\np7\n(lp8\nsb.").encode('utf8') son_2_1_1 = pickle.loads(pickled_with_2_1_1) self.assertEqual(son_2_1_1, SON([])) diff --git a/test/test_ssl.py b/test/test_ssl.py index a5efcefdd..0edc6509c 100644 --- a/test/test_ssl.py +++ b/test/test_ssl.py @@ -450,9 +450,6 @@ class TestSSL(IntegrationTest): if sys.platform == "win32": raise SkipTest("Can't test system ca certs on Windows.") - if sys.version_info < (2, 7, 9): - raise SkipTest("Can't load system CA certificates.") - if (ssl.OPENSSL_VERSION.lower().startswith('libressl') and sys.platform == 'darwin' and not _ssl.IS_PYOPENSSL): raise SkipTest( diff --git a/test/test_topology.py b/test/test_topology.py index 1b3bfe5ab..9cd019c14 100644 --- a/test/test_topology.py +++ b/test/test_topology.py @@ -18,7 +18,6 @@ import sys sys.path[0:0] = [""] -from bson.py3compat import imap from pymongo import common from pymongo.read_preferences import ReadPreference, Secondary from pymongo.server_type import SERVER_TYPE @@ -52,7 +51,7 @@ def create_mock_topology( seeds=None, replica_set_name=None, monitor_class=DummyMonitor): - partitioned_seeds = list(imap(common.partition_node, seeds or ['a'])) + partitioned_seeds = list(map(common.partition_node, seeds or ['a'])) topology_settings = TopologySettings( partitioned_seeds, replica_set_name=replica_set_name, diff --git a/test/test_transactions.py b/test/test_transactions.py index a114db6e2..859c8f3e2 100644 --- a/test/test_transactions.py +++ b/test/test_transactions.py @@ -17,9 +17,9 @@ import os import sys -sys.path[0:0] = [""] +from io import BytesIO -from bson.py3compat import StringIO +sys.path[0:0] = [""] from pymongo import client_session, WriteConcern from pymongo.client_session import TransactionOptions @@ -270,8 +270,8 @@ class TestTransactions(TransactionsBase): (gfs.exists, ()), (gridfs_open_upload_stream, ('name',)), (bucket.upload_from_stream, ('name', b'data',)), - (bucket.download_to_stream, (1, StringIO(),)), - (bucket.download_to_stream_by_name, ('name', StringIO(),)), + (bucket.download_to_stream, (1, BytesIO(),)), + (bucket.download_to_stream_by_name, ('name', BytesIO(),)), (bucket.delete, (1,)), (bucket.find, ()), (bucket.open_download_stream, (1,)), diff --git a/test/test_uri_parser.py b/test/test_uri_parser.py index 8d1f55a0d..6e09f6a4d 100644 --- a/test/test_uri_parser.py +++ b/test/test_uri_parser.py @@ -26,7 +26,6 @@ except ImportError: sys.path[0:0] = [""] from bson.binary import JAVA_LEGACY -from bson.py3compat import string_type, _unicode from pymongo import ReadPreference from pymongo.errors import ConfigurationError, InvalidURI from pymongo.uri_parser import (parse_userinfo, @@ -169,7 +168,7 @@ class TestURI(unittest.TestCase): split_options('connectTimeoutMS=0.1')) self.assertTrue(split_options('connectTimeoutMS=300')) self.assertTrue(isinstance(split_options('w=5')['w'], int)) - self.assertTrue(isinstance(split_options('w=5.5')['w'], string_type)) + self.assertTrue(isinstance(split_options('w=5.5')['w'], str)) self.assertTrue(split_options('w=foo')) self.assertTrue(split_options('w=majority')) self.assertTrue(split_options('wtimeoutms=500')) diff --git a/test/unicode/test_utf8.py b/test/unicode/test_utf8.py index d5cbdb7bb..65738d5c0 100644 --- a/test/unicode/test_utf8.py +++ b/test/unicode/test_utf8.py @@ -4,7 +4,6 @@ sys.path[0:0] = [""] from bson import encode from bson.errors import InvalidStringData -from bson.py3compat import PY3 from test import unittest class TestUTF8(unittest.TestCase): @@ -26,51 +25,5 @@ class TestUTF8(unittest.TestCase): self.assertEqual(py_is_legal, bson_is_legal, data) - @unittest.skipIf(PY3, "python3 has strong separation between bytes/unicode") - def test_legal_utf8_full_coverage(self): - # This test takes 400 seconds. Which is too long to run each time. - # However it is the only one which covers all possible bit combinations - # in the 244 space. - b1 = chr(0xf4) - - for b2 in map(chr, range(255)): - m2 = b1 + b2 - self._assert_same_utf8_validation(m2) - - for b3 in map(chr, range(255)): - m3 = m2 + b3 - self._assert_same_utf8_validation(m3) - - for b4 in map(chr, range(255)): - m4 = m3 + b4 - self._assert_same_utf8_validation(m4) - - # In python3: - # - 'bytes' are not checked with isLegalutf - # - 'unicode' We cannot create unicode objects with invalid utf8, since it - # would result in non valid code-points. - @unittest.skipIf(PY3, "python3 has strong separation between bytes/unicode") - def test_legal_utf8_few_samples(self): - good_samples = [ - '\xf4\x80\x80\x80', - '\xf4\x8a\x80\x80', - '\xf4\x8e\x80\x80', - '\xf4\x81\x80\x80', - ] - - for data in good_samples: - self._assert_same_utf8_validation(data) - - bad_samples = [ - '\xf4\x00\x80\x80', - '\xf4\x3a\x80\x80', - '\xf4\x7f\x80\x80', - '\xf4\x90\x80\x80', - '\xf4\xff\x80\x80', - ] - - for data in bad_samples: - self._assert_same_utf8_validation(data) - if __name__ == "__main__": unittest.main() diff --git a/test/unified_format.py b/test/unified_format.py index 9c3fdd732..fc23a193b 100644 --- a/test/unified_format.py +++ b/test/unified_format.py @@ -25,10 +25,11 @@ import re import sys import types +from collections import abc + from bson import json_util, Code, Decimal128, DBRef, SON, Int64, MaxKey, MinKey from bson.binary import Binary from bson.objectid import ObjectId -from bson.py3compat import abc, integer_types, iteritems, text_type, PY3 from bson.regex import Regex, RE_TYPE from gridfs import GridFSBucket @@ -182,7 +183,7 @@ class EntityMapUtil(object): item,)) def __setitem__(self, key, value): - if not isinstance(key, text_type): + if not isinstance(key, str): self._test_class.fail( 'Expected entity name of type str, got %s' % (type(key))) @@ -197,7 +198,7 @@ class EntityMapUtil(object): "Entity spec %s did not contain exactly one top-level key" % ( entity_spec,)) - entity_type, spec = next(iteritems(entity_spec)) + entity_type, spec = next(iter(entity_spec.items())) if entity_type == 'client': kwargs = {} observe_events = spec.get('observeEvents', []) @@ -298,21 +299,16 @@ class EntityMapUtil(object): return self._session_lsids[session_name] -if not PY3: - binary_types = (Binary,) - long_types = (Int64, long) - unicode_type = unicode -else: - binary_types = (Binary, bytes) - long_types = (Int64,) - unicode_type = str +binary_types = (Binary, bytes) +long_types = (Int64,) +unicode_type = str BSON_TYPE_ALIAS_MAP = { # https://docs.mongodb.com/manual/reference/operator/query/type/ # https://pymongo.readthedocs.io/en/stable/api/bson/index.html 'double': (float,), - 'string': (text_type,), + 'string': (str,), 'object': (abc.Mapping,), 'array': (abc.MutableSequence,), 'binData': binary_types, @@ -421,11 +417,11 @@ class MatchEvaluatorUtil(object): else: nested = expectation[key_to_compare] if isinstance(nested, abc.Mapping) and len(nested) == 1: - opname, spec = next(iteritems(nested)) + opname, spec = next(iter(nested.items())) if opname.startswith('$$'): is_special_op = True elif len(expectation) == 1: - opname, spec = next(iteritems(expectation)) + opname, spec = next(iter(expectation.items())) if opname.startswith('$$'): is_special_op = True key_to_compare = None @@ -445,7 +441,7 @@ class MatchEvaluatorUtil(object): return self._test_class.assertIsInstance(actual, abc.Mapping) - for key, value in iteritems(expectation): + for key, value in expectation.items(): if self._evaluate_if_special_operation(expectation, actual, key): continue @@ -473,7 +469,7 @@ class MatchEvaluatorUtil(object): return # account for flexible numerics in element-wise comparison - if (isinstance(expectation, integer_types) or + if (isinstance(expectation, int) or isinstance(expectation, float)): self._test_class.assertEqual(expectation, actual) else: @@ -481,7 +477,7 @@ class MatchEvaluatorUtil(object): self._test_class.assertEqual(expectation, actual) def match_event(self, expectation, actual): - event_type, spec = next(iteritems(expectation)) + event_type, spec = next(iter(expectation.items())) # every event type has the commandName field command_name = spec.get('commandName') diff --git a/test/utils.py b/test/utils.py index ff301540c..be5be176b 100644 --- a/test/utils.py +++ b/test/utils.py @@ -30,18 +30,16 @@ import warnings from collections import defaultdict from functools import partial -from bson import json_util, py3compat +from bson import json_util from bson.objectid import ObjectId -from bson.py3compat import iteritems, string_type from bson.son import SON from pymongo import (MongoClient, monitoring, operations, read_preferences) from pymongo.collection import ReturnDocument from pymongo.errors import ConfigurationError, OperationFailure -from pymongo.monitoring import _SENSITIVE_COMMANDS, ConnectionPoolListener -from pymongo.pool import (_CancellationContext, - PoolOptions) +from pymongo.monitoring import _SENSITIVE_COMMANDS +from pymongo.pool import _CancellationContext from pymongo.read_concern import ReadConcern from pymongo.read_preferences import ReadPreference from pymongo.server_selectors import (any_server_selector, @@ -53,12 +51,6 @@ from test import (client_context, db_user, db_pwd) -if sys.version_info[0] < 3: - # Python 2.7, use our backport. - from test.barrier import Barrier -else: - from threading import Barrier - IMPOSSIBLE_WRITE_CONCERN = WriteConcern(w=50) @@ -289,7 +281,7 @@ class ScenarioDict(dict): def convert(v): if isinstance(v, collections.Mapping): return ScenarioDict(v) - if isinstance(v, (py3compat.string_type, bytes)): + if isinstance(v, (str, bytes)): return v if isinstance(v, collections.Sequence): return [convert(item) for item in v] @@ -974,7 +966,8 @@ def assertion_context(msg): yield except AssertionError as exc: msg = '%s (%s)' % (exc, msg) - py3compat.reraise(type(exc), msg, sys.exc_info()[2]) + exc_type, exc_val, exc_tb = sys.exc_info() + raise exc_type(exc_val).with_traceback(exc_tb) def parse_spec_options(opts): @@ -998,8 +991,8 @@ def parse_spec_options(opts): if 'hint' in opts: hint = opts.pop('hint') - if not isinstance(hint, string_type): - hint = list(iteritems(hint)) + if not isinstance(hint, str): + hint = list(hint.items()) opts['hint'] = hint # Properly format 'hint' arguments for the Bulk API tests. @@ -1011,17 +1004,17 @@ def parse_spec_options(opts): args = req.pop('arguments', {}) if 'hint' in args: hint = args.pop('hint') - if not isinstance(hint, string_type): - hint = list(iteritems(hint)) + if not isinstance(hint, str): + hint = list(hint.items()) args['hint'] = hint req['arguments'] = args else: # Unified test format - bulk_model, spec = next(iteritems(req)) + bulk_model, spec = next(iter(req.items())) if 'hint' in spec: hint = spec.pop('hint') - if not isinstance(hint, string_type): - hint = list(iteritems(hint)) + if not isinstance(hint, str): + hint = list(hint.items()) spec['hint'] = hint opts['requests'] = reqs @@ -1035,7 +1028,7 @@ def prepare_spec_arguments(spec, arguments, opname, entity_map, # PyMongo accepts sort as list of tuples. if arg_name == "sort": sort_dict = arguments[arg_name] - arguments[arg_name] = list(iteritems(sort_dict)) + arguments[arg_name] = list(sort_dict.items()) # Named "key" instead not fieldName. if arg_name == "fieldName": arguments["key"] = arguments.pop(arg_name) @@ -1057,7 +1050,7 @@ def prepare_spec_arguments(spec, arguments, opname, entity_map, bulk_arguments = camel_to_snake_args(request["arguments"]) else: # Unified test format - bulk_model, spec = next(iteritems(request)) + bulk_model, spec = next(iter(request.items())) bulk_class = getattr(operations, camel_to_upper_camel(bulk_model)) bulk_arguments = camel_to_snake_args(spec) requests.append(bulk_class(**dict(bulk_arguments))) diff --git a/test/utils_spec_runner.py b/test/utils_spec_runner.py index 2e2318591..17175884d 100644 --- a/test/utils_spec_runner.py +++ b/test/utils_spec_runner.py @@ -14,23 +14,20 @@ """Utilities for testing driver specs.""" -import copy import functools import threading +from collections import abc from bson import decode, encode from bson.binary import Binary, STANDARD from bson.codec_options import CodecOptions from bson.int64 import Int64 -from bson.py3compat import iteritems, abc, string_type, text_type from bson.son import SON from gridfs import GridFSBucket -from pymongo import (client_session, - helpers, - operations) +from pymongo import client_session from pymongo.command_cursor import CommandCursor from pymongo.cursor import Cursor from pymongo.errors import (BulkWriteError, @@ -43,20 +40,16 @@ from pymongo.write_concern import WriteConcern from test import (client_context, client_knobs, - IntegrationTest, - unittest) + IntegrationTest) from test.utils import (camel_to_snake, camel_to_snake_args, - camel_to_upper_camel, CompareType, CMAPListener, OvertCommandListener, parse_spec_options, - parse_read_preference, prepare_spec_arguments, rs_client, - ServerAndTopologyEventListener, - HeartbeatEventListener) + ServerAndTopologyEventListener) class SpecRunnerThread(threading.Thread): @@ -594,7 +587,7 @@ def expect_any_error(op): def expect_error_message(expected_result): if isinstance(expected_result, dict): - return isinstance(expected_result['errorContains'], text_type) + return isinstance(expected_result['errorContains'], str) return False