Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4

Expand All @@ -21,7 +21,7 @@ jobs:
python-version: ${{ matrix.python-version }}

- name: Run tests
run: uv run --with python-dateutil --with six test.py
run: uv run --with python-dateutil --with pytest pytest bson/tests test.py

# https://github.com/marketplace/actions/alls-green#why used for branch protection checks
check:
Expand Down
37 changes: 16 additions & 21 deletions bson/codec.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@
from dateutil.tz import tzutc
from binascii import b2a_hex

from six import integer_types, iterkeys, text_type, PY3
from six.moves import xrange


utc = tzutc()

class MissingClassDefinition(ValueError):
Expand Down Expand Up @@ -132,7 +128,7 @@ def encode_string(value):

def encode_cstring(value):
if not isinstance(value, bytes):
value = text_type(value).encode("utf-8")
value = str(value).encode("utf-8")
if b"\x00" in value:
raise ValueError("Element names may not include NUL bytes.")
# A NUL byte is used to delimit our string, accepting one would cause
Expand Down Expand Up @@ -172,24 +168,26 @@ def encode_double_element(name, value):
def encode_string_element(name, value):
return b"\x02" + encode_cstring(name) + encode_string(value)


# any need for this at all with py3??
def _is_string(value):
if isinstance(value, text_type):
if isinstance(value, str):
return True
elif isinstance(value, str) or isinstance(value, bytes):
try:
unicode(value, errors='strict')
return True
except:
pass
return False
# this never worked in py3
# elif isinstance(value, str) or isinstance(value, bytes):
# try:
# unicode(value, errors='strict')
# return True
# except:
# pass
else:
return False


def encode_value(name, value, buf, traversal_stack,
generator_func, on_unknown=None):
if isinstance(value, bool):
buf.write(encode_boolean_element(name, value))
elif isinstance(value, integer_types):
elif isinstance(value, int):
if value < -0x80000000 or 0x7FFFFFFFFFFFFFFF >= value > 0x7fffffff:
buf.write(encode_int64_element(name, value))
elif value > 0x7FFFFFFFFFFFFFFF:
Expand Down Expand Up @@ -238,7 +236,7 @@ def encode_value(name, value, buf, traversal_stack,
def encode_document(obj, traversal_stack, traversal_parent=None,
generator_func=None, on_unknown=None):
buf = StringIO()
key_iter = iterkeys(obj)
key_iter = iter(obj)
if generator_func is not None:
key_iter = generator_func(obj, traversal_stack)
for name in key_iter:
Expand All @@ -256,7 +254,7 @@ def encode_document(obj, traversal_stack, traversal_parent=None,
def encode_array(array, traversal_stack, traversal_parent=None,
generator_func=None, on_unknown=None):
buf = StringIO()
for i in xrange(0, len(array)):
for i in range(0, len(array)):
value = array[i]
traversal_stack.append(TraversalStep(traversal_parent or array, i))
encode_value(str(i), value, buf, traversal_stack,
Expand Down Expand Up @@ -295,10 +293,7 @@ def decode_document(data, base, as_array=False):

element_type = char_struct.unpack(data[base:base + 1])[0]

if PY3:
ll = data.index(0, base + 1) + 1
else:
ll = data.index("\x00", base + 1) + 1
ll = data.index(0, base + 1) + 1
if decode_name:
name = data[base + 1:ll - 1]
try:
Expand Down
9 changes: 5 additions & 4 deletions bson/network.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python
from struct import unpack

from six import BytesIO, b
from io import BytesIO

from . import dumps, loads

Expand Down Expand Up @@ -55,9 +55,10 @@ def recvbytes(self, bytes_needed, sock_buf = None):
chunk = self.recv(min(bytes_needed - bytes_count, 32768))
part_count = len(chunk)

if type(chunk) == str:
chunk = b(chunk)

if type(chunk) == str: # this probably never occurs ...
# chunk = b(chunk)
# six.b() encoded to latin-1
chunk = chunk.encode("latin-1")
if part_count < 1:
return None

Expand Down
22 changes: 9 additions & 13 deletions bson/objectid.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,14 @@
import threading
import time

from bson.py3compat import PY3, bytes_from_hex, string_type, text_type
# from bson.py3compat import bytes_from_hex #string_type
from bson.tz_util import utc


# fnv_1a_24 adaptation taken from MongoDB Python Driver at https://github.com/mongodb/mongo-python-driver/commit/61850357a0e0eeec1a30e1adc0bbf7ebee807358
if PY3:
_ord = lambda x: x
else:
_ord = ord
# is this needed at all for Py3?
_ord = lambda x: x

# http://isthe.com/chongo/tech/comp/fnv/index.html#FNV-1a
def _fnv_1a_24(data, _ord=_ord):
"""FNV-1a 24 bit hash"""
Expand Down Expand Up @@ -212,18 +211,17 @@ def __validate(self, oid):
"""
if isinstance(oid, ObjectId):
self.__id = oid.binary
# bytes or unicode in python 2, str in python 3
elif isinstance(oid, string_type):
elif isinstance(oid, str):
if len(oid) == 24:
try:
self.__id = bytes_from_hex(oid)
self.__id = bytes.fromhex(oid)
except (TypeError, ValueError):
_raise_invalid_id(oid)
else:
_raise_invalid_id(oid)
else:
raise TypeError("id must be an instance of (bytes, %s, ObjectId), "
"not %s" % (text_type.__name__, type(oid)))
"not %s" % ('str', type(oid)))

@property
def binary(self):
Expand Down Expand Up @@ -261,15 +259,13 @@ def __setstate__(self, value):
# ObjectIds pickled in python 2.x used `str` for __id.
# In python 3.x this has to be converted to `bytes`
# by encoding latin-1.
if PY3 and isinstance(oid, text_type):
if isinstance(oid, str):
self.__id = oid.encode('latin-1')
else:
self.__id = oid

def __str__(self):
if PY3:
return binascii.hexlify(self.__id).decode()
return binascii.hexlify(self.__id)
return binascii.hexlify(self.__id).decode()

def __repr__(self):
return "ObjectId('%s')" % (str(self),)
Expand Down
88 changes: 0 additions & 88 deletions bson/py3compat.py

This file was deleted.

3 changes: 1 addition & 2 deletions bson/tests/test_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from unittest import TestCase

from bson import dumps, loads
from six import PY3


class TestArray(TestCase):
Expand Down Expand Up @@ -74,5 +73,5 @@ def test_long_array(self):

def test_encoded_order(self):
serialized = dumps(self.doc)
expected = repr(serialized)[1:] if PY3 else repr(serialized)
expected = repr(serialized)[1:]
self.assertEqual(expected, '\'\\xea\\x08\\x00\\x00\\x04lyrics\\x00\\xdd\\x08\\x00\\x00\\x020\\x00\\x14\\x00\\x00\\x00Viva La Vida lyrics\\x00\\x021\\x00\\x01\\x00\\x00\\x00\\x00\\x022\\x00!\\x00\\x00\\x00 I used to rule the world\\x00\\x023\\x00-\\x00\\x00\\x00 Seas would rise when I gave the word\\x00\\x024\\x00)\\x00\\x00\\x00 Now in the morning I sleep alone\\x00\\x025\\x00(\\x00\\x00\\x00 Sweep the streets I used to own\\x00\\x026\\x00\\x01\\x00\\x00\\x00\\x00\\x027\\x00 \\x00\\x00\\x00 I used to roll the dice\\x00\\x028\\x00)\\x00\\x00\\x00 Feel the fear in my enemy\\\'s eyes\\x00\\x029\\x00\\\'\\x00\\x00\\x00 Listen as the crowd would sing\\x00\\x0210\\x008\\x00\\x00\\x00 "Now the old king is dead! Long live the king!"\\x00\\x0211\\x00\\x01\\x00\\x00\\x00\\x00\\x0212\\x00"\\x00\\x00\\x00 One minute I held the key\\x00\\x0213\\x00)\\x00\\x00\\x00 Next the walls were closed on me\\x00\\x0214\\x00/\\x00\\x00\\x00 And I discovered that my castles stand\\x00\\x0215\\x001\\x00\\x00\\x00 Upon pillars of salt and pillars of sand\\x00\\x0216\\x00\\x01\\x00\\x00\\x00\\x00\\x0217\\x00)\\x00\\x00\\x00 I hear Jerusalem bells a ringing\\x00\\x0218\\x00)\\x00\\x00\\x00 Roman Cavalry choirs are singing\\x00\\x0219\\x00*\\x00\\x00\\x00 Be my mirror, my sword and shield\\x00\\x0220\\x00+\\x00\\x00\\x00 My missionaries in a foreign field\\x00\\x0221\\x00\\x01\\x00\\x00\\x00\\x00\\x0222\\x00(\\x00\\x00\\x00 For some reason I can\\\'t explain\\x00\\x0223\\x00$\\x00\\x00\\x00 Once you go there was never\\x00\\x0224\\x00\\x1d\\x00\\x00\\x00 Never an honest word\\x00\\x0225\\x00,\\x00\\x00\\x00 And that was when I ruled the world\\x00\\x0226\\x00\\x01\\x00\\x00\\x00\\x00\\x0227\\x00(\\x00\\x00\\x00 It was the wicked and wild wind\\x00\\x0228\\x00)\\x00\\x00\\x00 Blew down the doors to let me in\\x00\\x0229\\x001\\x00\\x00\\x00 Shattered windows and the sound of drums\\x00\\x0230\\x000\\x00\\x00\\x00 People couldn\\\'t believe what I\\\'d become\\x00\\x0231\\x00\\x01\\x00\\x00\\x00\\x00\\x0232\\x00\\x1d\\x00\\x00\\x00 Revolutionaries wait\\x00\\x0233\\x00&\\x00\\x00\\x00 For my head on a silver plate\\x00\\x0234\\x00)\\x00\\x00\\x00 Just a puppet on a lonely string\\x00\\x0235\\x00+\\x00\\x00\\x00 Oh who would ever want to be king?\\x00\\x0236\\x00\\x01\\x00\\x00\\x00\\x00\\x0237\\x00)\\x00\\x00\\x00 I hear Jerusalem bells a ringing\\x00\\x0238\\x00)\\x00\\x00\\x00 Roman Cavalry choirs are singing\\x00\\x0239\\x00*\\x00\\x00\\x00 Be my mirror, my sword and shield\\x00\\x0240\\x00+\\x00\\x00\\x00 My missionaries in a foreign field\\x00\\x0241\\x00\\x01\\x00\\x00\\x00\\x00\\x0242\\x00(\\x00\\x00\\x00 For some reason I can\\\'t explain\\x00\\x0243\\x00.\\x00\\x00\\x00 I know Saint Peter won\\\'t call my name\\x00\\x0244\\x00\\x1d\\x00\\x00\\x00 Never an honest word\\x00\\x0245\\x00,\\x00\\x00\\x00 But that was when I ruled the world\\x00\\x0246\\x00\\x01\\x00\\x00\\x00\\x00\\x0247\\x00)\\x00\\x00\\x00 I hear Jerusalem bells a ringing\\x00\\x0248\\x00)\\x00\\x00\\x00 Roman Cavalry choirs are singing\\x00\\x0249\\x00*\\x00\\x00\\x00 Be my mirror, my sword and shield\\x00\\x0250\\x00+\\x00\\x00\\x00 My missionaries in a foreign field\\x00\\x0251\\x00\\x01\\x00\\x00\\x00\\x00\\x0252\\x00(\\x00\\x00\\x00 For some reason I can\\\'t explain\\x00\\x0253\\x00.\\x00\\x00\\x00 I know Saint Peter won\\\'t call my name\\x00\\x0254\\x00\\x1d\\x00\\x00\\x00 Never an honest word\\x00\\x0255\\x00,\\x00\\x00\\x00 But that was when I ruled the world\\x00\\x00\\x00\'')
12 changes: 6 additions & 6 deletions bson/tests/test_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

from bson import BSONCoding, dumps, loads, import_class


class TestData(BSONCoding):
# named with underscore so as not to confuse pytest
class _TestData(BSONCoding):
Comment on lines +6 to +7
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

def __init__(self, *args):
self.args = list(args)
self.nested = None
Expand All @@ -17,7 +17,7 @@ def bson_init(self, raw_values):
self.nested = raw_values["nested"]

def __eq__(self, other):
if not isinstance(other, TestData):
if not isinstance(other, _TestData):
return NotImplemented
if self.args != other.args:
return False
Expand All @@ -31,12 +31,12 @@ def __ne__(self, other):

class TestObjectCoding(TestCase):
def test_codec(self):
import_class(TestData)
data = TestData(u"Lorem ipsum dolor sit amet",
import_class(_TestData)
data = _TestData(u"Lorem ipsum dolor sit amet",
"consectetur adipisicing elit",
42)

data2 = TestData(u"She's got both hands in her pockets",
data2 = _TestData(u"She's got both hands in her pockets",
"and she won't look at you won't look at you eh",
66,
23.54,
Expand Down
20 changes: 8 additions & 12 deletions bson/tests/test_objectid.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@

from bson.objectid import ObjectId, _fnv_1a_24
from bson.objectid import InvalidId
from bson.py3compat import PY3, _unicode
from bson.tz_util import (FixedOffset,
utc)

Expand Down Expand Up @@ -467,7 +466,7 @@ def test_fnv_1a_24(self):

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")
Expand Down Expand Up @@ -511,18 +510,19 @@ def test_pid(self):
self.assertTrue(oid_generated_on_client(ObjectId()))

def test_generation_time(self):
d1 = datetime.datetime.utcnow()
d1 = datetime.datetime.now(utc)
d2 = ObjectId().generation_time

self.assertEqual(utc, d2.tzinfo)
d2 = d2.replace(tzinfo=None)
self.assertTrue(d2 - d1 < datetime.timedelta(seconds=2))

def test_from_datetime(self):
if 'PyPy 1.8.0' in sys.version:
# See https://bugs.pypy.org/issue1092
raise SkipTest("datetime.timedelta is broken in pypy 1.8.0")
d = datetime.datetime.utcnow()
# note: could use tz aware datetimes here?
d = datetime.datetime.now(utc).replace(tzinfo=None)

d = d - datetime.timedelta(microseconds=d.microsecond)
oid = ObjectId.from_datetime(d)
self.assertEqual(d, oid.generation_time.replace(tzinfo=None))
Expand Down Expand Up @@ -559,13 +559,9 @@ def test_pickle_backwards_compatability(self):
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)
Expand Down
Loading
Loading