Create a separate bson package PYTHON-60
Many of the pymongo modules have been moved into the bson package. Aliases for those modules have been added to the pymongo package, without deprecation warnings for now. Application developers should begin to use the bson namespace, as deprecation of moved modules will probably begin in the next release.
This commit is contained in:
parent
021136e797
commit
9c182809f3
@ -12,28 +12,27 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Tools for dealing with Mongo's BSON data representation.
|
||||
|
||||
Generally not needed to be used by application developers."""
|
||||
"""BSON (Binary JSON) encoding and decoding.
|
||||
"""
|
||||
|
||||
import struct
|
||||
import re
|
||||
import datetime
|
||||
import calendar
|
||||
|
||||
from pymongo.binary import Binary
|
||||
from pymongo.code import Code
|
||||
from pymongo.dbref import DBRef
|
||||
from pymongo.errors import (InvalidBSON,
|
||||
InvalidDocument,
|
||||
InvalidName,
|
||||
InvalidStringData)
|
||||
from pymongo.max_key import MaxKey
|
||||
from pymongo.min_key import MinKey
|
||||
from pymongo.objectid import ObjectId
|
||||
from pymongo.son import SON
|
||||
from pymongo.timestamp import Timestamp
|
||||
from pymongo.tz_util import utc
|
||||
from bson.binary import Binary
|
||||
from bson.code import Code
|
||||
from bson.dbref import DBRef
|
||||
from bson.errors import (InvalidBSON,
|
||||
InvalidDocument,
|
||||
InvalidName,
|
||||
InvalidStringData)
|
||||
from bson.max_key import MaxKey
|
||||
from bson.min_key import MinKey
|
||||
from bson.objectid import ObjectId
|
||||
from bson.son import SON
|
||||
from bson.timestamp import Timestamp
|
||||
from bson.tz_util import utc
|
||||
|
||||
|
||||
try:
|
||||
@ -300,7 +299,7 @@ def _element_to_bson(key, value, check_keys):
|
||||
if isinstance(value, (int, long)):
|
||||
# TODO this is a really ugly way to check for this...
|
||||
if value > 2 ** 64 / 2 - 1 or value < -2 ** 64 / 2:
|
||||
raise OverflowError("MongoDB can only handle up to 8-byte ints")
|
||||
raise OverflowError("BSON can only handle up to 8-byte ints")
|
||||
if value > 2 ** 32 / 2 - 1 or value < -2 ** 32 / 2:
|
||||
return "\x12" + name + struct.pack("<q", value)
|
||||
return "\x10" + name + struct.pack("<i", value)
|
||||
@ -416,9 +415,7 @@ def is_valid(bson):
|
||||
|
||||
|
||||
class BSON(str):
|
||||
"""BSON data.
|
||||
|
||||
Represents binary data storable in and retrievable from Mongo.
|
||||
"""BSON (Binary JSON) data.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
@ -428,14 +425,14 @@ class BSON(str):
|
||||
|
||||
Raises :class:`TypeError` if `dct` is not a mapping type, or
|
||||
contains keys that are not instances of :class:`basestring`.
|
||||
Raises :class:`~pymongo.errors.InvalidDocument` if `dct`
|
||||
cannot be converted to :class:`BSON`.
|
||||
Raises :class:`~bson.errors.InvalidDocument` if `dct` cannot
|
||||
be converted to :class:`BSON`.
|
||||
|
||||
:Parameters:
|
||||
- `dct`: mapping type representing a document
|
||||
- `check_keys` (optional): check if keys start with '$' or
|
||||
contain '.', raising :class:`~pymongo.errors.InvalidName`
|
||||
in either case
|
||||
contain '.', raising :class:`~bson.errors.InvalidName` in
|
||||
either case
|
||||
"""
|
||||
return cls(_dict_to_bson(dct, check_keys))
|
||||
|
||||
@ -448,7 +445,7 @@ class BSON(str):
|
||||
If `tz_aware` is ``True`` (default), any
|
||||
:class:`~datetime.datetime` instances returned will be
|
||||
timezone-aware, with their timezone set to
|
||||
:attr:`pymongo.tz_util.utc`. Otherwise, all
|
||||
:attr:`bson.tz_util.utc`. Otherwise, all
|
||||
:class:`~datetime.datetime` instances will be naive (but
|
||||
contain UTC) - this was the default behavior in PyMongo
|
||||
versions **<= 1.7**.
|
||||
112
bson/binary.py
Normal file
112
bson/binary.py
Normal file
@ -0,0 +1,112 @@
|
||||
# Copyright 2009-2010 10gen, 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.
|
||||
|
||||
"""Tools for representing BSON binary data.
|
||||
"""
|
||||
|
||||
BINARY_SUBTYPE = 0
|
||||
"""BSON binary subtype for binary data.
|
||||
|
||||
This is becomming the default subtype and should be the most commonly
|
||||
used.
|
||||
|
||||
.. versionadded:: 1.5
|
||||
"""
|
||||
|
||||
FUNCTION_SUBTYPE = 1
|
||||
"""BSON binary subtype for functions.
|
||||
|
||||
.. versionadded:: 1.5
|
||||
"""
|
||||
|
||||
OLD_BINARY_SUBTYPE = 2
|
||||
"""Old BSON binary subtype for binary data.
|
||||
|
||||
This is still the default subtype, but that is changing to
|
||||
:data:`BINARY_SUBTYPE`.
|
||||
|
||||
.. versionadded:: 1.7
|
||||
"""
|
||||
|
||||
UUID_SUBTYPE = 3
|
||||
"""BSON binary subtype for a UUID.
|
||||
|
||||
:class:`uuid.UUID` instances will automatically be encoded
|
||||
by :mod:`bson` using this subtype.
|
||||
|
||||
.. versionadded:: 1.5
|
||||
"""
|
||||
|
||||
MD5_SUBTYPE = 5
|
||||
"""BSON binary subtype for an MD5 hash.
|
||||
|
||||
.. versionadded:: 1.5
|
||||
"""
|
||||
|
||||
USER_DEFINED_SUBTYPE = 128
|
||||
"""BSON binary subtype for any user defined structure.
|
||||
|
||||
.. versionadded:: 1.5
|
||||
"""
|
||||
|
||||
|
||||
class Binary(str):
|
||||
"""Representation of BSON binary data.
|
||||
|
||||
This is necessary because we want to represent Python strings as
|
||||
the BSON string type. We need to wrap binary data so we can tell
|
||||
the difference between what should be considered binary data and
|
||||
what should be considered a string when we encode to BSON.
|
||||
|
||||
Raises TypeError if `data` is not an instance of str or `subtype`
|
||||
is not an instance of int. Raises ValueError if `subtype` is not
|
||||
in [0, 256).
|
||||
|
||||
:Parameters:
|
||||
- `data`: the binary data to represent
|
||||
- `subtype` (optional): the `binary subtype
|
||||
<http://bsonspec.org/#/specification>`_
|
||||
to use
|
||||
"""
|
||||
|
||||
def __new__(cls, data, subtype=OLD_BINARY_SUBTYPE):
|
||||
if not isinstance(data, str):
|
||||
raise TypeError("data must be an instance of str")
|
||||
if not isinstance(subtype, int):
|
||||
raise TypeError("subtype must be an instance of int")
|
||||
if subtype >= 256 or subtype < 0:
|
||||
raise ValueError("subtype must be contained in [0, 256)")
|
||||
self = str.__new__(cls, data)
|
||||
self.__subtype = subtype
|
||||
return self
|
||||
|
||||
@property
|
||||
def subtype(self):
|
||||
"""Subtype of this binary data.
|
||||
"""
|
||||
return self.__subtype
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, Binary):
|
||||
return (self.__subtype, str(self)) == (other.subtype, str(other))
|
||||
# We don't return NotImplemented here because if we did then
|
||||
# Binary("foo") == "foo" would return True, since Binary is a
|
||||
# subclass of str...
|
||||
return False
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __repr__(self):
|
||||
return "Binary(%s, %s)" % (str.__repr__(self), self.__subtype)
|
||||
78
bson/code.py
Normal file
78
bson/code.py
Normal file
@ -0,0 +1,78 @@
|
||||
# Copyright 2009-2010 10gen, 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.
|
||||
|
||||
"""Tools for representing JavaScript code in BSON.
|
||||
"""
|
||||
|
||||
|
||||
class Code(str):
|
||||
"""BSON's JavaScript code type.
|
||||
|
||||
Raises :class:`TypeError` if `code` is not an instance of
|
||||
:class:`basestring` or `scope` is not ``None`` or an instance of
|
||||
:class:`dict`.
|
||||
|
||||
Scope variables can be set by passing a dictionary as the `scope`
|
||||
argument or by using keyword arguments. If a variable is set as a
|
||||
keyword argument it will override any setting for that variable in
|
||||
the `scope` dictionary.
|
||||
|
||||
:Parameters:
|
||||
- `code`: string containing JavaScript code to be evaluated
|
||||
- `scope` (optional): dictionary representing the scope in which
|
||||
`code` should be evaluated - a mapping from identifiers (as
|
||||
strings) to values
|
||||
- `**kwargs` (optional): scope variables can also be passed as
|
||||
keyword arguments
|
||||
|
||||
.. versionadded:: 1.8.1+
|
||||
Ability to pass scope values using keyword arguments.
|
||||
"""
|
||||
|
||||
def __new__(cls, code, scope=None, **kwargs):
|
||||
if not isinstance(code, basestring):
|
||||
raise TypeError("code must be an instance of basestring")
|
||||
|
||||
self = str.__new__(cls, code)
|
||||
|
||||
try:
|
||||
self.__scope = code.scope
|
||||
except AttributeError:
|
||||
self.__scope = {}
|
||||
|
||||
if scope is not None:
|
||||
if not isinstance(scope, dict):
|
||||
raise TypeError("scope must be an instance of dict")
|
||||
self.__scope.update(scope)
|
||||
|
||||
self.__scope.update(kwargs)
|
||||
|
||||
return self
|
||||
|
||||
@property
|
||||
def scope(self):
|
||||
"""Scope dictionary for this instance.
|
||||
"""
|
||||
return self.__scope
|
||||
|
||||
def __repr__(self):
|
||||
return "Code(%s, %r)" % (str.__repr__(self), self.__scope)
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, Code):
|
||||
return (self.__scope, str(self)) == (other.__scope, str(other))
|
||||
return False
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
117
bson/dbref.py
Normal file
117
bson/dbref.py
Normal file
@ -0,0 +1,117 @@
|
||||
# Copyright 2009-2010 10gen, 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.
|
||||
|
||||
"""Tools for manipulating DBRefs (references to MongoDB documents)."""
|
||||
|
||||
from bson.son import SON
|
||||
|
||||
|
||||
class DBRef(object):
|
||||
"""A reference to a document stored in MongoDB.
|
||||
"""
|
||||
|
||||
def __init__(self, collection, id, database=None, _extra={}, **kwargs):
|
||||
"""Initialize a new :class:`DBRef`.
|
||||
|
||||
Raises :class:`TypeError` if `collection` or `database` is not
|
||||
an instance of :class:`basestring`. `database` is optional and
|
||||
allows references to documents to work across databases. Any
|
||||
additional keyword arguments will create additional fields in
|
||||
the resultant embedded document.
|
||||
|
||||
:Parameters:
|
||||
- `collection`: name of the collection the document is stored in
|
||||
- `id`: the value of the document's ``"_id"`` field
|
||||
- `database` (optional): name of the database to reference
|
||||
- `**kwargs` (optional): additional keyword arguments will
|
||||
create additional, custom fields
|
||||
|
||||
.. versionchanged:: 1.8
|
||||
Now takes keyword arguments to specify additional fields.
|
||||
.. versionadded:: 1.1.1
|
||||
The `database` parameter.
|
||||
|
||||
.. mongodoc:: dbrefs
|
||||
"""
|
||||
if not isinstance(collection, basestring):
|
||||
raise TypeError("collection must be an instance of basestring")
|
||||
if database is not None and not isinstance(database, basestring):
|
||||
raise TypeError("database must be an instance of basestring")
|
||||
|
||||
self.__collection = collection
|
||||
self.__id = id
|
||||
self.__database = database
|
||||
kwargs.update(_extra)
|
||||
self.__kwargs = kwargs
|
||||
|
||||
@property
|
||||
def collection(self):
|
||||
"""Get the name of this DBRef's collection as unicode.
|
||||
"""
|
||||
return self.__collection
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
"""Get this DBRef's _id.
|
||||
"""
|
||||
return self.__id
|
||||
|
||||
@property
|
||||
def database(self):
|
||||
"""Get the name of this DBRef's database.
|
||||
|
||||
Returns None if this DBRef doesn't specify a database.
|
||||
|
||||
.. versionadded:: 1.1.1
|
||||
"""
|
||||
return self.__database
|
||||
|
||||
def __getattr__(self, key):
|
||||
return self.__kwargs[key]
|
||||
|
||||
def as_doc(self):
|
||||
"""Get the SON document representation of this DBRef.
|
||||
|
||||
Generally not needed by application developers
|
||||
"""
|
||||
doc = SON([("$ref", self.collection),
|
||||
("$id", self.id)])
|
||||
if self.database is not None:
|
||||
doc["$db"] = self.database
|
||||
doc.update(self.__kwargs)
|
||||
return doc
|
||||
|
||||
def __repr__(self):
|
||||
extra = "".join([", %s=%r" % (k, v)
|
||||
for k, v in self.__kwargs.iteritems()])
|
||||
if self.database is None:
|
||||
return "DBRef(%r, %r%s)" % (self.collection, self.id, extra)
|
||||
return "DBRef(%r, %r, %r%s)" % (self.collection, self.id,
|
||||
self.database, extra)
|
||||
|
||||
def __cmp__(self, other):
|
||||
if isinstance(other, DBRef):
|
||||
return cmp([self.__database, self.__collection,
|
||||
self.__id, self.__kwargs],
|
||||
[other.__database, other.__collection,
|
||||
other.__id, other.__kwargs])
|
||||
return NotImplemented
|
||||
|
||||
def __hash__(self):
|
||||
"""Get a hash value for this :class:`DBRef`.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
"""
|
||||
return hash((self.__collection, self.__id,
|
||||
self.__database, self.__kwargs))
|
||||
45
bson/errors.py
Normal file
45
bson/errors.py
Normal file
@ -0,0 +1,45 @@
|
||||
# Copyright 2009-2010 10gen, 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.
|
||||
|
||||
"""Exceptions raised by the BSON package."""
|
||||
|
||||
|
||||
class BSONError(Exception):
|
||||
"""Base class for all BSON exceptions.
|
||||
"""
|
||||
|
||||
|
||||
class InvalidName(BSONError):
|
||||
"""Raised when an invalid name is used.
|
||||
"""
|
||||
|
||||
|
||||
class InvalidBSON(BSONError):
|
||||
"""Raised when trying to create a BSON object from invalid data.
|
||||
"""
|
||||
|
||||
|
||||
class InvalidStringData(BSONError):
|
||||
"""Raised when trying to encode a string containing non-UTF8 data.
|
||||
"""
|
||||
|
||||
|
||||
class InvalidDocument(BSONError):
|
||||
"""Raised when trying to create a BSON object from an invalid document.
|
||||
"""
|
||||
|
||||
|
||||
class InvalidId(BSONError):
|
||||
"""Raised when trying to create an ObjectId from invalid data.
|
||||
"""
|
||||
32
bson/max_key.py
Normal file
32
bson/max_key.py
Normal file
@ -0,0 +1,32 @@
|
||||
# Copyright 2010 10gen, 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.
|
||||
|
||||
"""Representation for the MongoDB internal MaxKey type.
|
||||
"""
|
||||
|
||||
|
||||
class MaxKey(object):
|
||||
"""MongoDB internal MaxKey type.
|
||||
"""
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, MaxKey):
|
||||
return True
|
||||
return NotImplemented
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __repr__(self):
|
||||
return "MaxKey()"
|
||||
32
bson/min_key.py
Normal file
32
bson/min_key.py
Normal file
@ -0,0 +1,32 @@
|
||||
# Copyright 2010 10gen, 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.
|
||||
|
||||
"""Representation for the MongoDB internal MinKey type.
|
||||
"""
|
||||
|
||||
|
||||
class MinKey(object):
|
||||
"""MongoDB internal MinKey type.
|
||||
"""
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, MinKey):
|
||||
return True
|
||||
return NotImplemented
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __repr__(self):
|
||||
return "MinKey()"
|
||||
204
bson/objectid.py
Normal file
204
bson/objectid.py
Normal file
@ -0,0 +1,204 @@
|
||||
# Copyright 2009-2010 10gen, 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.
|
||||
|
||||
"""Tools for working with MongoDB `ObjectIds
|
||||
<http://dochub.mongodb.org/core/objectids>`_.
|
||||
"""
|
||||
|
||||
import calendar
|
||||
import datetime
|
||||
try:
|
||||
import hashlib
|
||||
_md5func = hashlib.md5
|
||||
except ImportError: # for Python < 2.5
|
||||
import md5
|
||||
_md5func = md5.new
|
||||
import os
|
||||
import socket
|
||||
import struct
|
||||
import threading
|
||||
import time
|
||||
|
||||
from bson.errors import InvalidId
|
||||
from bson.tz_util import utc
|
||||
|
||||
|
||||
def _machine_bytes():
|
||||
"""Get the machine portion of an ObjectId.
|
||||
"""
|
||||
machine_hash = _md5func()
|
||||
machine_hash.update(socket.gethostname())
|
||||
return machine_hash.digest()[0:3]
|
||||
|
||||
|
||||
class ObjectId(object):
|
||||
"""A MongoDB ObjectId.
|
||||
"""
|
||||
|
||||
_inc = 0
|
||||
_inc_lock = threading.Lock()
|
||||
|
||||
_machine_bytes = _machine_bytes()
|
||||
|
||||
def __init__(self, oid=None):
|
||||
"""Initialize a new ObjectId.
|
||||
|
||||
If `oid` is ``None``, create a new (unique) ObjectId. If `oid`
|
||||
is an instance of (``basestring``, :class:`ObjectId`) validate
|
||||
it and use that. Otherwise, a :class:`TypeError` is
|
||||
raised. If `oid` is invalid,
|
||||
:class:`~bson.errors.InvalidId` is raised.
|
||||
|
||||
:Parameters:
|
||||
- `oid` (optional): a valid ObjectId (12 byte binary or 24 character
|
||||
hex string)
|
||||
|
||||
.. versionadded:: 1.2.1
|
||||
The `oid` parameter can be a ``unicode`` instance (that contains
|
||||
only hexadecimal digits).
|
||||
|
||||
.. mongodoc:: objectids
|
||||
"""
|
||||
if oid is None:
|
||||
self.__generate()
|
||||
else:
|
||||
self.__validate(oid)
|
||||
|
||||
@classmethod
|
||||
def from_datetime(cls, generation_time):
|
||||
"""Create a dummy ObjectId instance with a specific generation time.
|
||||
|
||||
This method is useful for doing range queries on a field
|
||||
containing :class:`ObjectId` instances.
|
||||
|
||||
.. warning::
|
||||
It is not safe to insert a document containing an ObjectId
|
||||
generated using this method. This method deliberately
|
||||
eliminates the uniqueness guarantee that ObjectIds
|
||||
generally provide. ObjectIds generated with this method
|
||||
should be used exclusively in queries.
|
||||
|
||||
`generation_time` will be converted to UTC. Naive datetime
|
||||
instances will be treated as though they already contain UTC.
|
||||
|
||||
An example using this helper to get documents where ``"_id"``
|
||||
was generated before January 1, 2010 would be:
|
||||
|
||||
>>> gen_time = datetime.datetime(2010, 1, 1)
|
||||
>>> dummy_id = ObjectId.from_datetime(gen_time)
|
||||
>>> result = collection.find({"_id": {"$lt": dummy_id}})
|
||||
|
||||
:Parameters:
|
||||
- `generation_time`: :class:`~datetime.datetime` to be used
|
||||
as the generation time for the resulting ObjectId.
|
||||
|
||||
.. versionchanged:: 1.8
|
||||
Properly handle timezone aware values for
|
||||
`generation_time`.
|
||||
|
||||
.. versionadded:: 1.6
|
||||
"""
|
||||
if generation_time.utcoffset() is not None:
|
||||
generation_time = generation_time - generation_time.utcoffset()
|
||||
ts = calendar.timegm(generation_time.timetuple())
|
||||
oid = struct.pack(">i", int(ts)) + "\x00" * 8
|
||||
return cls(oid)
|
||||
|
||||
def __generate(self):
|
||||
"""Generate a new value for this ObjectId.
|
||||
"""
|
||||
oid = ""
|
||||
|
||||
# 4 bytes current time
|
||||
oid += struct.pack(">i", int(time.time()))
|
||||
|
||||
# 3 bytes machine
|
||||
oid += ObjectId._machine_bytes
|
||||
|
||||
# 2 bytes pid
|
||||
oid += struct.pack(">H", os.getpid() % 0xFFFF)
|
||||
|
||||
# 3 bytes inc
|
||||
ObjectId._inc_lock.acquire()
|
||||
oid += struct.pack(">i", ObjectId._inc)[1:4]
|
||||
ObjectId._inc = (ObjectId._inc + 1) % 0xFFFFFF
|
||||
ObjectId._inc_lock.release()
|
||||
|
||||
self.__id = oid
|
||||
|
||||
def __validate(self, oid):
|
||||
"""Validate and use the given id for this ObjectId.
|
||||
|
||||
Raises TypeError if id is not an instance of (str, ObjectId) and
|
||||
InvalidId if it is not a valid ObjectId.
|
||||
|
||||
:Parameters:
|
||||
- `oid`: a valid ObjectId
|
||||
"""
|
||||
if isinstance(oid, ObjectId):
|
||||
self.__id = oid.__id
|
||||
elif isinstance(oid, basestring):
|
||||
if len(oid) == 12:
|
||||
self.__id = oid
|
||||
elif len(oid) == 24:
|
||||
try:
|
||||
self.__id = oid.decode("hex")
|
||||
except TypeError:
|
||||
raise InvalidId("%s is not a valid ObjectId" % oid)
|
||||
else:
|
||||
raise InvalidId("%s is not a valid ObjectId" % oid)
|
||||
else:
|
||||
raise TypeError("id must be an instance of (str, ObjectId), "
|
||||
"not %s" % type(oid))
|
||||
|
||||
@property
|
||||
def binary(self):
|
||||
"""12-byte binary representation of this ObjectId.
|
||||
"""
|
||||
return self.__id
|
||||
|
||||
@property
|
||||
def generation_time(self):
|
||||
"""A :class:`datetime.datetime` instance representing the time of
|
||||
generation for this :class:`ObjectId`.
|
||||
|
||||
The :class:`datetime.datetime` is timezone aware, and
|
||||
represents the generation time in UTC. It is precise to the
|
||||
second.
|
||||
|
||||
.. versionchanged:: 1.8
|
||||
Now return an aware datetime instead of a naive one.
|
||||
|
||||
.. versionadded:: 1.2
|
||||
"""
|
||||
t = struct.unpack(">i", self.__id[0:4])[0]
|
||||
return datetime.datetime.fromtimestamp(t, utc)
|
||||
|
||||
def __str__(self):
|
||||
return self.__id.encode("hex")
|
||||
|
||||
def __repr__(self):
|
||||
return "ObjectId('%s')" % self.__id.encode("hex")
|
||||
|
||||
def __cmp__(self, other):
|
||||
if isinstance(other, ObjectId):
|
||||
return cmp(self.__id, other.__id)
|
||||
return NotImplemented
|
||||
|
||||
def __hash__(self):
|
||||
"""Get a hash value for this :class:`ObjectId`.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
"""
|
||||
return hash(self.__id)
|
||||
204
bson/son.py
Normal file
204
bson/son.py
Normal file
@ -0,0 +1,204 @@
|
||||
# Copyright 2009-2010 10gen, 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.
|
||||
|
||||
"""Tools for creating and manipulating SON, the Serialized Ocument Notation.
|
||||
|
||||
Regular dictionaries can be used instead of SON objects, but not when the order
|
||||
of keys is important. A SON object can be used just like a normal Python
|
||||
dictionary."""
|
||||
|
||||
|
||||
class SON(dict):
|
||||
"""SON data.
|
||||
|
||||
A subclass of dict that maintains ordering of keys and provides a
|
||||
few extra niceties for dealing with SON. SON objects can be
|
||||
converted to and from BSON.
|
||||
|
||||
The mapping from Python types to BSON types is as follows:
|
||||
|
||||
=================================== ============= ===================
|
||||
Python Type BSON Type Supported Direction
|
||||
=================================== ============= ===================
|
||||
None null both
|
||||
bool boolean both
|
||||
int number (int) both
|
||||
float number (real) both
|
||||
string string py -> bson
|
||||
unicode string both
|
||||
list array both
|
||||
dict / `SON` object both
|
||||
datetime.datetime [#dt]_ [#dt2]_ date both
|
||||
compiled re regex both
|
||||
`bson.binary.Binary` binary both
|
||||
`bson.objectid.ObjectId` oid both
|
||||
`bson.dbref.DBRef` dbref both
|
||||
None undefined bson -> py
|
||||
unicode code bson -> py
|
||||
`bson.code.Code` code py -> bson
|
||||
unicode symbol bson -> py
|
||||
=================================== ============= ===================
|
||||
|
||||
Note that to save binary data it must be wrapped as an instance of
|
||||
`bson.binary.Binary`. Otherwise it will be saved as a BSON string
|
||||
and retrieved as unicode.
|
||||
|
||||
.. [#dt] datetime.datetime instances will be rounded to the nearest
|
||||
millisecond when saved
|
||||
.. [#dt2] all datetime.datetime instances are treated as *naive*. clients
|
||||
should always use UTC.
|
||||
"""
|
||||
|
||||
def __init__(self, data=None, **kwargs):
|
||||
self.__keys = []
|
||||
dict.__init__(self)
|
||||
self.update(data)
|
||||
self.update(kwargs)
|
||||
|
||||
def __repr__(self):
|
||||
result = []
|
||||
for key in self.__keys:
|
||||
result.append("(%r, %r)" % (key, self[key]))
|
||||
return "SON([%s])" % ", ".join(result)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
if key not in self:
|
||||
self.__keys.append(key)
|
||||
dict.__setitem__(self, key, value)
|
||||
|
||||
def __delitem__(self, key):
|
||||
self.__keys.remove(key)
|
||||
dict.__delitem__(self, key)
|
||||
|
||||
def keys(self):
|
||||
return list(self.__keys)
|
||||
|
||||
def copy(self):
|
||||
other = SON()
|
||||
other.update(self)
|
||||
return other
|
||||
|
||||
# TODO this is all from UserDict.DictMixin. it could probably be made more
|
||||
# efficient.
|
||||
# second level definitions support higher levels
|
||||
def __iter__(self):
|
||||
for k in self.keys():
|
||||
yield k
|
||||
|
||||
def has_key(self, key):
|
||||
return key in self.keys()
|
||||
|
||||
def __contains__(self, key):
|
||||
return key in self.keys()
|
||||
|
||||
# third level takes advantage of second level definitions
|
||||
def iteritems(self):
|
||||
for k in self:
|
||||
yield (k, self[k])
|
||||
|
||||
def iterkeys(self):
|
||||
return self.__iter__()
|
||||
|
||||
# fourth level uses definitions from lower levels
|
||||
def itervalues(self):
|
||||
for _, v in self.iteritems():
|
||||
yield v
|
||||
|
||||
def values(self):
|
||||
return [v for _, v in self.iteritems()]
|
||||
|
||||
def items(self):
|
||||
return list(self.iteritems())
|
||||
|
||||
def clear(self):
|
||||
for key in self.keys():
|
||||
del self[key]
|
||||
|
||||
def setdefault(self, key, default=None):
|
||||
try:
|
||||
return self[key]
|
||||
except KeyError:
|
||||
self[key] = default
|
||||
return default
|
||||
|
||||
def pop(self, key, *args):
|
||||
if len(args) > 1:
|
||||
raise TypeError("pop expected at most 2 arguments, got "\
|
||||
+ repr(1 + len(args)))
|
||||
try:
|
||||
value = self[key]
|
||||
except KeyError:
|
||||
if args:
|
||||
return args[0]
|
||||
raise
|
||||
del self[key]
|
||||
return value
|
||||
|
||||
def popitem(self):
|
||||
try:
|
||||
k, v = self.iteritems().next()
|
||||
except StopIteration:
|
||||
raise KeyError('container is empty')
|
||||
del self[k]
|
||||
return (k, v)
|
||||
|
||||
def update(self, other=None, **kwargs):
|
||||
# Make progressively weaker assumptions about "other"
|
||||
if other is None:
|
||||
pass
|
||||
elif hasattr(other, 'iteritems'): # iteritems saves memory and lookups
|
||||
for k, v in other.iteritems():
|
||||
self[k] = v
|
||||
elif hasattr(other, 'keys'):
|
||||
for k in other.keys():
|
||||
self[k] = other[k]
|
||||
else:
|
||||
for k, v in other:
|
||||
self[k] = v
|
||||
if kwargs:
|
||||
self.update(kwargs)
|
||||
|
||||
def get(self, key, default=None):
|
||||
try:
|
||||
return self[key]
|
||||
except KeyError:
|
||||
return default
|
||||
|
||||
def __cmp__(self, other):
|
||||
if isinstance(other, SON):
|
||||
return cmp((dict(self.iteritems()), self.keys()),
|
||||
(dict(other.iteritems()), other.keys()))
|
||||
return cmp(dict(self.iteritems()), other)
|
||||
|
||||
def __len__(self):
|
||||
return len(self.keys())
|
||||
|
||||
def to_dict(self):
|
||||
"""Convert a SON document to a normal Python dictionary instance.
|
||||
|
||||
This is trickier than just *dict(...)* because it needs to be
|
||||
recursive.
|
||||
"""
|
||||
|
||||
def transform_value(value):
|
||||
if isinstance(value, list):
|
||||
return [transform_value(v) for v in value]
|
||||
if isinstance(value, SON):
|
||||
value = dict(value)
|
||||
if isinstance(value, dict):
|
||||
for k, v in value.iteritems():
|
||||
value[k] = transform_value(v)
|
||||
return value
|
||||
|
||||
return transform_value(dict(self))
|
||||
96
bson/timestamp.py
Normal file
96
bson/timestamp.py
Normal file
@ -0,0 +1,96 @@
|
||||
# Copyright 2010 10gen, 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.
|
||||
|
||||
"""Tools for representing MongoDB internal Timestamps.
|
||||
"""
|
||||
|
||||
import calendar
|
||||
import datetime
|
||||
|
||||
from bson.tz_util import utc
|
||||
|
||||
|
||||
class Timestamp(object):
|
||||
"""MongoDB internal timestamps used in the opLog.
|
||||
"""
|
||||
|
||||
def __init__(self, time, inc):
|
||||
"""Create a new :class:`Timestamp`.
|
||||
|
||||
This class is only for use with the MongoDB opLog. If you need
|
||||
to store a regular timestamp, please use a
|
||||
:class:`~datetime.datetime`.
|
||||
|
||||
Raises :class:`TypeError` if `time` is not an instance of
|
||||
:class: `int` or :class:`~datetime.datetime`, or `inc` is not
|
||||
an instance of :class:`int`. Raises :class:`ValueError` if
|
||||
`time` or `inc` is not in [0, 2**32).
|
||||
|
||||
:Parameters:
|
||||
- `time`: time in seconds since epoch UTC, or a naive UTC
|
||||
:class:`~datetime.datetime`, or an aware
|
||||
:class:`~datetime.datetime`
|
||||
- `inc`: the incrementing counter
|
||||
|
||||
.. versionchanged:: 1.7
|
||||
`time` can now be a :class:`~datetime.datetime` instance.
|
||||
"""
|
||||
if isinstance(time, datetime.datetime):
|
||||
if time.utcoffset() is not None:
|
||||
time = time - time.utcoffset()
|
||||
time = int(calendar.timegm(time.timetuple()))
|
||||
if not isinstance(time, (int, long)):
|
||||
raise TypeError("time must be an instance of int")
|
||||
if not isinstance(inc, (int, long)):
|
||||
raise TypeError("inc must be an instance of int")
|
||||
if not 0 <= time < 2 ** 32:
|
||||
raise ValueError("time must be contained in [0, 2**32)")
|
||||
if not 0 <= inc < 2 ** 32:
|
||||
raise ValueError("inc must be contained in [0, 2**32)")
|
||||
|
||||
self.__time = time
|
||||
self.__inc = inc
|
||||
|
||||
@property
|
||||
def time(self):
|
||||
"""Get the time portion of this :class:`Timestamp`.
|
||||
"""
|
||||
return self.__time
|
||||
|
||||
@property
|
||||
def inc(self):
|
||||
"""Get the inc portion of this :class:`Timestamp`.
|
||||
"""
|
||||
return self.__inc
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, Timestamp):
|
||||
return (self.__time == other.time and self.__inc == other.inc)
|
||||
else:
|
||||
return NotImplemented
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __repr__(self):
|
||||
return "Timestamp(%s, %s)" % (self.__time, self.__inc)
|
||||
|
||||
def as_datetime(self):
|
||||
"""Return a :class:`~datetime.datetime` instance corresponding
|
||||
to the time portion of this :class:`Timestamp`.
|
||||
|
||||
.. versionchanged:: 1.8
|
||||
The returned datetime is now timezone aware.
|
||||
"""
|
||||
return datetime.datetime.fromtimestamp(self.__time, utc)
|
||||
45
bson/tz_util.py
Normal file
45
bson/tz_util.py
Normal file
@ -0,0 +1,45 @@
|
||||
# Copyright 2010 10gen, 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.
|
||||
|
||||
"""Timezone related utilities for BSON."""
|
||||
|
||||
from datetime import (timedelta,
|
||||
tzinfo)
|
||||
|
||||
ZERO = timedelta(0)
|
||||
|
||||
|
||||
class FixedOffset(tzinfo):
|
||||
"""Fixed offset timezone, in minutes east from UTC.
|
||||
|
||||
Implementation from the Python `standard library documentation
|
||||
<http://docs.python.org/library/datetime.html#tzinfo-objects>`_.
|
||||
"""
|
||||
|
||||
def __init__(self, offset, name):
|
||||
self.__offset = timedelta(minutes=offset)
|
||||
self.__name = name
|
||||
|
||||
def utcoffset(self, dt):
|
||||
return self.__offset
|
||||
|
||||
def tzname(self, dt):
|
||||
return self.__name
|
||||
|
||||
def dst(self, dt):
|
||||
return ZERO
|
||||
|
||||
|
||||
"""UTC"""
|
||||
utc = FixedOffset(0, "UTC")
|
||||
@ -22,15 +22,15 @@ try:
|
||||
except ImportError:
|
||||
from StringIO import StringIO
|
||||
|
||||
from bson.binary import Binary
|
||||
from bson.objectid import ObjectId
|
||||
from gridfs.errors import (CorruptGridFile,
|
||||
FileExists,
|
||||
NoFile,
|
||||
UnsupportedAPI)
|
||||
from pymongo import ASCENDING
|
||||
from pymongo.binary import Binary
|
||||
from pymongo.collection import Collection
|
||||
from pymongo.errors import DuplicateKeyError
|
||||
from pymongo.objectid import ObjectId
|
||||
|
||||
try:
|
||||
_SEEK_SET = os.SEEK_SET
|
||||
@ -95,7 +95,7 @@ class GridIn(object):
|
||||
arguments include:
|
||||
|
||||
- ``"_id"``: unique ID for this file (default:
|
||||
:class:`~pymongo.objectid.ObjectId`) - this ``"_id"`` must
|
||||
:class:`~bson.objectid.ObjectId`) - this ``"_id"`` must
|
||||
not have already been used for another file
|
||||
|
||||
- ``"filename"``: human name for the file
|
||||
|
||||
@ -233,12 +233,12 @@ static int write_string(bson_buffer* buffer, PyObject* py_string) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Get an error class from the pymongo.errors module.
|
||||
/* Get an error class from the bson.errors module.
|
||||
*
|
||||
* Returns a new ref */
|
||||
static PyObject* _error(char* name) {
|
||||
PyObject* error;
|
||||
PyObject* errors = PyImport_ImportModule("pymongo.errors");
|
||||
PyObject* errors = PyImport_ImportModule("bson.errors");
|
||||
if (!errors) {
|
||||
return NULL;
|
||||
}
|
||||
@ -268,14 +268,14 @@ static int _reload_object(PyObject** object, char* module_name, char* object_nam
|
||||
*
|
||||
* Returns non-zero on failure. */
|
||||
static int _reload_python_objects(void) {
|
||||
if (_reload_object(&Binary, "pymongo.binary", "Binary") ||
|
||||
_reload_object(&Code, "pymongo.code", "Code") ||
|
||||
_reload_object(&ObjectId, "pymongo.objectid", "ObjectId") ||
|
||||
_reload_object(&DBRef, "pymongo.dbref", "DBRef") ||
|
||||
_reload_object(&Timestamp, "pymongo.timestamp", "Timestamp") ||
|
||||
_reload_object(&MinKey, "pymongo.min_key", "MinKey") ||
|
||||
_reload_object(&MaxKey, "pymongo.max_key", "MaxKey") ||
|
||||
_reload_object(&UTC, "pymongo.tz_util", "utc") ||
|
||||
if (_reload_object(&Binary, "bson.binary", "Binary") ||
|
||||
_reload_object(&Code, "bson.code", "Code") ||
|
||||
_reload_object(&ObjectId, "bson.objectid", "ObjectId") ||
|
||||
_reload_object(&DBRef, "bson.dbref", "DBRef") ||
|
||||
_reload_object(&Timestamp, "bson.timestamp", "Timestamp") ||
|
||||
_reload_object(&MinKey, "bson.min_key", "MinKey") ||
|
||||
_reload_object(&MaxKey, "bson.max_key", "MaxKey") ||
|
||||
_reload_object(&UTC, "bson.tz_util", "utc") ||
|
||||
_reload_object(&RECompile, "re", "compile")) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -12,101 +12,4 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Tools for representing binary data to be stored in MongoDB.
|
||||
"""
|
||||
|
||||
BINARY_SUBTYPE = 0
|
||||
"""BSON binary subtype for binary data.
|
||||
|
||||
This is becomming the default subtype and should be the most commonly
|
||||
used.
|
||||
|
||||
.. versionadded:: 1.5
|
||||
"""
|
||||
|
||||
FUNCTION_SUBTYPE = 1
|
||||
"""BSON binary subtype for functions.
|
||||
|
||||
.. versionadded:: 1.5
|
||||
"""
|
||||
|
||||
OLD_BINARY_SUBTYPE = 2
|
||||
"""Old BSON binary subtype for binary data.
|
||||
|
||||
This is still the default subtype, but that is changing to
|
||||
:data:`BINARY_SUBTYPE`.
|
||||
|
||||
.. versionadded:: 1.7
|
||||
"""
|
||||
|
||||
UUID_SUBTYPE = 3
|
||||
"""BSON binary subtype for a UUID.
|
||||
|
||||
:class:`uuid.UUID` instances will automatically be encoded
|
||||
by :mod:`~pymongo.bson` using this subtype.
|
||||
|
||||
.. versionadded:: 1.5
|
||||
"""
|
||||
|
||||
MD5_SUBTYPE = 5
|
||||
"""BSON binary subtype for an MD5 hash.
|
||||
|
||||
.. versionadded:: 1.5
|
||||
"""
|
||||
|
||||
USER_DEFINED_SUBTYPE = 128
|
||||
"""BSON binary subtype for any user defined structure.
|
||||
|
||||
.. versionadded:: 1.5
|
||||
"""
|
||||
|
||||
|
||||
class Binary(str):
|
||||
"""Representation of binary data to be stored in or retrieved from MongoDB.
|
||||
|
||||
This is necessary because we want to store Python strings as the
|
||||
BSON string type. We need to wrap binary data so we can tell the
|
||||
difference between what should be considered binary data and what
|
||||
should be considered a string when we encode to BSON.
|
||||
|
||||
Raises TypeError if `data` is not an instance of str or `subtype`
|
||||
is not an instance of int. Raises ValueError if `subtype` is not
|
||||
in [0, 256).
|
||||
|
||||
:Parameters:
|
||||
- `data`: the binary data to represent
|
||||
- `subtype` (optional): the `binary subtype
|
||||
<http://bsonspec.org/#/specification>`_
|
||||
to use
|
||||
"""
|
||||
|
||||
def __new__(cls, data, subtype=OLD_BINARY_SUBTYPE):
|
||||
if not isinstance(data, str):
|
||||
raise TypeError("data must be an instance of str")
|
||||
if not isinstance(subtype, int):
|
||||
raise TypeError("subtype must be an instance of int")
|
||||
if subtype >= 256 or subtype < 0:
|
||||
raise ValueError("subtype must be contained in [0, 256)")
|
||||
self = str.__new__(cls, data)
|
||||
self.__subtype = subtype
|
||||
return self
|
||||
|
||||
@property
|
||||
def subtype(self):
|
||||
"""Subtype of this binary data.
|
||||
"""
|
||||
return self.__subtype
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, Binary):
|
||||
return (self.__subtype, str(self)) == (other.subtype, str(other))
|
||||
# We don't return NotImplemented here because if we did then
|
||||
# Binary("foo") == "foo" would return True, since Binary is a
|
||||
# subclass of str...
|
||||
return False
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __repr__(self):
|
||||
return "Binary(%s, %s)" % (str.__repr__(self), self.__subtype)
|
||||
from bson.binary import *
|
||||
|
||||
@ -12,67 +12,4 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Tools for representing JavaScript code to be evaluated by MongoDB.
|
||||
"""
|
||||
|
||||
|
||||
class Code(str):
|
||||
"""JavaScript code to be evaluated by MongoDB.
|
||||
|
||||
Raises :class:`TypeError` if `code` is not an instance of
|
||||
:class:`basestring` or `scope` is not ``None`` or an instance of
|
||||
:class:`dict`.
|
||||
|
||||
Scope variables can be set by passing a dictionary as the `scope`
|
||||
argument or by using keyword arguments. If a variable is set as a
|
||||
keyword argument it will override any setting for that variable in
|
||||
the `scope` dictionary.
|
||||
|
||||
:Parameters:
|
||||
- `code`: string containing JavaScript code to be evaluated
|
||||
- `scope` (optional): dictionary representing the scope in which
|
||||
`code` should be evaluated - a mapping from identifiers (as
|
||||
strings) to values
|
||||
- `**kwargs` (optional): scope variables can also be passed as
|
||||
keyword arguments
|
||||
|
||||
.. versionadded:: 1.8.1+
|
||||
Ability to pass scope values using keyword arguments.
|
||||
"""
|
||||
|
||||
def __new__(cls, code, scope=None, **kwargs):
|
||||
if not isinstance(code, basestring):
|
||||
raise TypeError("code must be an instance of basestring")
|
||||
|
||||
self = str.__new__(cls, code)
|
||||
|
||||
try:
|
||||
self.__scope = code.scope
|
||||
except AttributeError:
|
||||
self.__scope = {}
|
||||
|
||||
if scope is not None:
|
||||
if not isinstance(scope, dict):
|
||||
raise TypeError("scope must be an instance of dict")
|
||||
self.__scope.update(scope)
|
||||
|
||||
self.__scope.update(kwargs)
|
||||
|
||||
return self
|
||||
|
||||
@property
|
||||
def scope(self):
|
||||
"""Scope dictionary for this instance.
|
||||
"""
|
||||
return self.__scope
|
||||
|
||||
def __repr__(self):
|
||||
return "Code(%s, %r)" % (str.__repr__(self), self.__scope)
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, Code):
|
||||
return (self.__scope, str(self)) == (other.__scope, str(other))
|
||||
return False
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
from bson.code import *
|
||||
|
||||
@ -16,12 +16,12 @@
|
||||
|
||||
import warnings
|
||||
|
||||
from bson.code import Code
|
||||
from bson.errors import InvalidName
|
||||
from bson.son import SON
|
||||
from pymongo import (helpers,
|
||||
message)
|
||||
from pymongo.code import Code
|
||||
from pymongo.cursor import Cursor
|
||||
from pymongo.errors import InvalidName
|
||||
from pymongo.son import SON
|
||||
|
||||
_ZERO = "\x00\x00\x00\x00"
|
||||
|
||||
@ -410,10 +410,9 @@ class Collection(object):
|
||||
|
||||
.. versionadded:: 1.8
|
||||
Support for passing `getLastError` options as keyword arguments.
|
||||
.. versionchanged:: 1.7
|
||||
Accept any type other than a ``dict`` instance for removal
|
||||
by ``"_id"``, not just :class:`~pymongo.objectid.ObjectId`
|
||||
instances.
|
||||
.. versionchanged:: 1.7 Accept any type other than a ``dict``
|
||||
instance for removal by ``"_id"``, not just
|
||||
:class:`~bson.objectid.ObjectId` instances.
|
||||
.. versionchanged:: 1.4
|
||||
Return the response to *lastError* if `safe` is ``True``.
|
||||
.. versionchanged:: 1.2
|
||||
@ -460,10 +459,9 @@ class Collection(object):
|
||||
Allow passing any of the arguments that are valid for
|
||||
:meth:`find`.
|
||||
|
||||
.. versionchanged:: 1.7
|
||||
Accept any type other than a ``dict`` instance as an
|
||||
``"_id"`` query, not just
|
||||
:class:`~pymongo.objectid.ObjectId` instances.
|
||||
.. versionchanged:: 1.7 Accept any type other than a ``dict``
|
||||
instance as an ``"_id"`` query, not just
|
||||
:class:`~bson.objectid.ObjectId` instances.
|
||||
"""
|
||||
if spec_or_id is not None and not isinstance(spec_or_id, dict):
|
||||
spec_or_id = {"_id": spec_or_id}
|
||||
@ -820,9 +818,9 @@ class Collection(object):
|
||||
|
||||
- ``None`` to use the entire document as a key.
|
||||
- A :class:`list` of keys (each a :class:`basestring`) to group by.
|
||||
- A :class:`basestring` or :class:`~pymongo.code.Code` instance
|
||||
containing a JavaScript function to be applied to each document,
|
||||
returning the key to group by.
|
||||
- A :class:`basestring` or :class:`~bson.code.Code` instance
|
||||
containing a JavaScript function to be applied to each
|
||||
document, returning the key to group by.
|
||||
|
||||
:Parameters:
|
||||
- `key`: fields to group by (see above description)
|
||||
|
||||
@ -14,12 +14,12 @@
|
||||
|
||||
"""Cursor class to iterate over Mongo query results."""
|
||||
|
||||
from bson.code import Code
|
||||
from bson.son import SON
|
||||
from pymongo import (helpers,
|
||||
message)
|
||||
from pymongo.code import Code
|
||||
from pymongo.errors import (InvalidOperation,
|
||||
AutoReconnect)
|
||||
from pymongo.son import SON
|
||||
|
||||
_QUERY_OPTIONS = {
|
||||
"tailable_cursor": 2,
|
||||
@ -485,7 +485,7 @@ class Cursor(object):
|
||||
"""Adds a $where clause to this query.
|
||||
|
||||
The `code` argument must be an instance of :class:`basestring`
|
||||
or :class:`~pymongo.code.Code` containing a JavaScript
|
||||
or :class:`~bson.code.Code` containing a JavaScript
|
||||
expression. This expression will be evaluated for each
|
||||
document scanned. Only those documents for which the
|
||||
expression evaluates to *true* will be returned as
|
||||
|
||||
@ -16,14 +16,14 @@
|
||||
|
||||
import warnings
|
||||
|
||||
from bson.code import Code
|
||||
from bson.dbref import DBRef
|
||||
from bson.errors import InvalidName
|
||||
from bson.son import SON
|
||||
from pymongo import helpers
|
||||
from pymongo.code import Code
|
||||
from pymongo.collection import Collection
|
||||
from pymongo.dbref import DBRef
|
||||
from pymongo.errors import (CollectionInvalid,
|
||||
InvalidName,
|
||||
OperationFailure)
|
||||
from pymongo.son import SON
|
||||
from pymongo.son_manipulator import ObjectIdInjector
|
||||
|
||||
|
||||
|
||||
104
pymongo/dbref.py
104
pymongo/dbref.py
@ -12,106 +12,4 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Tools for manipulating DBRefs (references to MongoDB documents)."""
|
||||
|
||||
from pymongo.son import SON
|
||||
|
||||
|
||||
class DBRef(object):
|
||||
"""A reference to a document stored in a Mongo database.
|
||||
"""
|
||||
|
||||
def __init__(self, collection, id, database=None, _extra={}, **kwargs):
|
||||
"""Initialize a new :class:`DBRef`.
|
||||
|
||||
Raises :class:`TypeError` if `collection` or `database` is not
|
||||
an instance of :class:`basestring`. `database` is optional and
|
||||
allows references to documents to work across databases. Any
|
||||
additional keyword arguments will create additional fields in
|
||||
the resultant embedded document.
|
||||
|
||||
:Parameters:
|
||||
- `collection`: name of the collection the document is stored in
|
||||
- `id`: the value of the document's ``"_id"`` field
|
||||
- `database` (optional): name of the database to reference
|
||||
- `**kwargs` (optional): additional keyword arguments will
|
||||
create additional, custom fields
|
||||
|
||||
.. versionchanged:: 1.8
|
||||
Now takes keyword arguments to specify additional fields.
|
||||
.. versionadded:: 1.1.1
|
||||
The `database` parameter.
|
||||
|
||||
.. mongodoc:: dbrefs
|
||||
"""
|
||||
if not isinstance(collection, basestring):
|
||||
raise TypeError("collection must be an instance of basestring")
|
||||
if database is not None and not isinstance(database, basestring):
|
||||
raise TypeError("database must be an instance of basestring")
|
||||
|
||||
self.__collection = collection
|
||||
self.__id = id
|
||||
self.__database = database
|
||||
kwargs.update(_extra)
|
||||
self.__kwargs = kwargs
|
||||
|
||||
@property
|
||||
def collection(self):
|
||||
"""Get the name of this DBRef's collection as unicode.
|
||||
"""
|
||||
return self.__collection
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
"""Get this DBRef's _id.
|
||||
"""
|
||||
return self.__id
|
||||
|
||||
@property
|
||||
def database(self):
|
||||
"""Get the name of this DBRef's database.
|
||||
|
||||
Returns None if this DBRef doesn't specify a database.
|
||||
|
||||
.. versionadded:: 1.1.1
|
||||
"""
|
||||
return self.__database
|
||||
|
||||
def __getattr__(self, key):
|
||||
return self.__kwargs[key]
|
||||
|
||||
def as_doc(self):
|
||||
"""Get the SON document representation of this DBRef.
|
||||
|
||||
Generally not needed by application developers
|
||||
"""
|
||||
doc = SON([("$ref", self.collection),
|
||||
("$id", self.id)])
|
||||
if self.database is not None:
|
||||
doc["$db"] = self.database
|
||||
doc.update(self.__kwargs)
|
||||
return doc
|
||||
|
||||
def __repr__(self):
|
||||
extra = "".join([", %s=%r" % (k, v)
|
||||
for k, v in self.__kwargs.iteritems()])
|
||||
if self.database is None:
|
||||
return "DBRef(%r, %r%s)" % (self.collection, self.id, extra)
|
||||
return "DBRef(%r, %r, %r%s)" % (self.collection, self.id,
|
||||
self.database, extra)
|
||||
|
||||
def __cmp__(self, other):
|
||||
if isinstance(other, DBRef):
|
||||
return cmp([self.__database, self.__collection,
|
||||
self.__id, self.__kwargs],
|
||||
[other.__database, other.__collection,
|
||||
other.__id, other.__kwargs])
|
||||
return NotImplemented
|
||||
|
||||
def __hash__(self):
|
||||
"""Get a hash value for this :class:`DBRef`.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
"""
|
||||
return hash((self.__collection, self.__id,
|
||||
self.__database, self.__kwargs))
|
||||
from bson.dbref import *
|
||||
|
||||
@ -14,6 +14,8 @@
|
||||
|
||||
"""Exceptions raised by PyMongo."""
|
||||
|
||||
from bson.errors import *
|
||||
|
||||
|
||||
class PyMongoError(Exception):
|
||||
"""Base class for all PyMongo exceptions.
|
||||
@ -82,31 +84,6 @@ class CollectionInvalid(PyMongoError):
|
||||
"""
|
||||
|
||||
|
||||
class InvalidName(PyMongoError):
|
||||
"""Raised when an invalid name is used.
|
||||
"""
|
||||
|
||||
|
||||
class InvalidBSON(PyMongoError):
|
||||
"""Raised when trying to create a BSON object from invalid data.
|
||||
"""
|
||||
|
||||
|
||||
class InvalidStringData(PyMongoError):
|
||||
"""Raised when trying to encode a string containing non-UTF8 data.
|
||||
"""
|
||||
|
||||
|
||||
class InvalidDocument(PyMongoError):
|
||||
"""Raised when trying to create a BSON object from an invalid document.
|
||||
"""
|
||||
|
||||
|
||||
class InvalidId(PyMongoError):
|
||||
"""Raised when trying to create an ObjectId from invalid data.
|
||||
"""
|
||||
|
||||
|
||||
class InvalidURI(ConfigurationError):
|
||||
"""Raised when trying to parse an invalid mongodb URI.
|
||||
|
||||
|
||||
@ -22,8 +22,8 @@ except: # for Python < 2.5
|
||||
_md5func = md5.new
|
||||
import struct
|
||||
|
||||
import bson
|
||||
import pymongo
|
||||
from pymongo import bson
|
||||
from pymongo.errors import (AutoReconnect,
|
||||
OperationFailure,
|
||||
TimeoutError)
|
||||
|
||||
@ -31,8 +31,7 @@ Example usage (deserialization)::
|
||||
>>> json.loads(..., object_hook=json_util.object_hook)
|
||||
|
||||
Currently this does not handle special encoding and decoding for
|
||||
:class:`~pymongo.binary.Binary` and :class:`~pymongo.code.Code`
|
||||
instances.
|
||||
:class:`~bson.binary.Binary` and :class:`~bson.code.Code` instances.
|
||||
|
||||
.. versionchanged:: 1.8.1+
|
||||
Handle :class:`uuid.UUID` instances, whenever possible.
|
||||
@ -60,12 +59,12 @@ try:
|
||||
except ImportError:
|
||||
_use_uuid = False
|
||||
|
||||
from pymongo.dbref import DBRef
|
||||
from pymongo.max_key import MaxKey
|
||||
from pymongo.min_key import MinKey
|
||||
from pymongo.objectid import ObjectId
|
||||
from pymongo.timestamp import Timestamp
|
||||
from pymongo.tz_util import utc
|
||||
from bson.dbref import DBRef
|
||||
from bson.max_key import MaxKey
|
||||
from bson.min_key import MinKey
|
||||
from bson.objectid import ObjectId
|
||||
from bson.timestamp import Timestamp
|
||||
from bson.tz_util import utc
|
||||
|
||||
# TODO support Binary and Code
|
||||
# Binary and Code are tricky because they subclass str so json thinks it can
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# Copyright 2010 10gen, Inc.
|
||||
# Copyright 2009-2010 10gen, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@ -12,21 +12,4 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Representation for the MongoDB internal MaxKey type.
|
||||
"""
|
||||
|
||||
|
||||
class MaxKey(object):
|
||||
"""MongoDB internal MaxKey type.
|
||||
"""
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, MaxKey):
|
||||
return True
|
||||
return NotImplemented
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __repr__(self):
|
||||
return "MaxKey()"
|
||||
from bson.max_key import *
|
||||
|
||||
@ -25,7 +25,7 @@ MongoDB.
|
||||
import random
|
||||
import struct
|
||||
|
||||
from pymongo import bson
|
||||
import bson
|
||||
from pymongo.son import SON
|
||||
try:
|
||||
from pymongo import _cbson
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# Copyright 2010 10gen, Inc.
|
||||
# Copyright 2009-2010 10gen, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@ -12,21 +12,4 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Representation for the MongoDB internal MinKey type.
|
||||
"""
|
||||
|
||||
|
||||
class MinKey(object):
|
||||
"""MongoDB internal MinKey type.
|
||||
"""
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, MinKey):
|
||||
return True
|
||||
return NotImplemented
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __repr__(self):
|
||||
return "MinKey()"
|
||||
from bson.min_key import *
|
||||
|
||||
@ -12,193 +12,4 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Tools for working with MongoDB `ObjectIds
|
||||
<http://dochub.mongodb.org/core/objectids>`_.
|
||||
"""
|
||||
|
||||
import calendar
|
||||
import datetime
|
||||
try:
|
||||
import hashlib
|
||||
_md5func = hashlib.md5
|
||||
except ImportError: # for Python < 2.5
|
||||
import md5
|
||||
_md5func = md5.new
|
||||
import os
|
||||
import socket
|
||||
import struct
|
||||
import threading
|
||||
import time
|
||||
|
||||
from pymongo.errors import InvalidId
|
||||
from pymongo.tz_util import utc
|
||||
|
||||
|
||||
def _machine_bytes():
|
||||
"""Get the machine portion of an ObjectId.
|
||||
"""
|
||||
machine_hash = _md5func()
|
||||
machine_hash.update(socket.gethostname())
|
||||
return machine_hash.digest()[0:3]
|
||||
|
||||
|
||||
class ObjectId(object):
|
||||
"""A Mongo ObjectId.
|
||||
"""
|
||||
|
||||
_inc = 0
|
||||
_inc_lock = threading.Lock()
|
||||
|
||||
_machine_bytes = _machine_bytes()
|
||||
|
||||
def __init__(self, oid=None):
|
||||
"""Initialize a new ObjectId.
|
||||
|
||||
If `oid` is ``None``, create a new (unique) ObjectId. If `oid`
|
||||
is an instance of (``basestring``, :class:`ObjectId`) validate
|
||||
it and use that. Otherwise, a :class:`TypeError` is
|
||||
raised. If `oid` is invalid,
|
||||
:class:`~pymongo.errors.InvalidId` is raised.
|
||||
|
||||
:Parameters:
|
||||
- `oid` (optional): a valid ObjectId (12 byte binary or 24 character
|
||||
hex string)
|
||||
|
||||
.. versionadded:: 1.2.1
|
||||
The `oid` parameter can be a ``unicode`` instance (that contains
|
||||
only hexadecimal digits).
|
||||
|
||||
.. mongodoc:: objectids
|
||||
"""
|
||||
if oid is None:
|
||||
self.__generate()
|
||||
else:
|
||||
self.__validate(oid)
|
||||
|
||||
@classmethod
|
||||
def from_datetime(cls, generation_time):
|
||||
"""Create a dummy ObjectId instance with a specific generation time.
|
||||
|
||||
This method is useful for doing range queries on a field
|
||||
containing :class:`ObjectId` instances.
|
||||
|
||||
.. warning::
|
||||
It is not safe to insert a document containing an ObjectId
|
||||
generated using this method. This method deliberately
|
||||
eliminates the uniqueness guarantee that ObjectIds
|
||||
generally provide. ObjectIds generated with this method
|
||||
should be used exclusively in queries.
|
||||
|
||||
`generation_time` will be converted to UTC. Naive datetime
|
||||
instances will be treated as though they already contain UTC.
|
||||
|
||||
An example using this helper to get documents where ``"_id"``
|
||||
was generated before January 1, 2010 would be:
|
||||
|
||||
>>> gen_time = datetime.datetime(2010, 1, 1)
|
||||
>>> dummy_id = ObjectId.from_datetime(gen_time)
|
||||
>>> result = collection.find({"_id": {"$lt": dummy_id}})
|
||||
|
||||
:Parameters:
|
||||
- `generation_time`: :class:`~datetime.datetime` to be used
|
||||
as the generation time for the resulting ObjectId.
|
||||
|
||||
.. versionchanged:: 1.8
|
||||
Properly handle timezone aware values for
|
||||
`generation_time`.
|
||||
|
||||
.. versionadded:: 1.6
|
||||
"""
|
||||
if generation_time.utcoffset() is not None:
|
||||
generation_time = generation_time - generation_time.utcoffset()
|
||||
ts = calendar.timegm(generation_time.timetuple())
|
||||
oid = struct.pack(">i", int(ts)) + "\x00" * 8
|
||||
return cls(oid)
|
||||
|
||||
def __generate(self):
|
||||
"""Generate a new value for this ObjectId.
|
||||
"""
|
||||
oid = ""
|
||||
|
||||
# 4 bytes current time
|
||||
oid += struct.pack(">i", int(time.time()))
|
||||
|
||||
# 3 bytes machine
|
||||
oid += ObjectId._machine_bytes
|
||||
|
||||
# 2 bytes pid
|
||||
oid += struct.pack(">H", os.getpid() % 0xFFFF)
|
||||
|
||||
# 3 bytes inc
|
||||
ObjectId._inc_lock.acquire()
|
||||
oid += struct.pack(">i", ObjectId._inc)[1:4]
|
||||
ObjectId._inc = (ObjectId._inc + 1) % 0xFFFFFF
|
||||
ObjectId._inc_lock.release()
|
||||
|
||||
self.__id = oid
|
||||
|
||||
def __validate(self, oid):
|
||||
"""Validate and use the given id for this ObjectId.
|
||||
|
||||
Raises TypeError if id is not an instance of (str, ObjectId) and
|
||||
InvalidId if it is not a valid ObjectId.
|
||||
|
||||
:Parameters:
|
||||
- `oid`: a valid ObjectId
|
||||
"""
|
||||
if isinstance(oid, ObjectId):
|
||||
self.__id = oid.__id
|
||||
elif isinstance(oid, basestring):
|
||||
if len(oid) == 12:
|
||||
self.__id = oid
|
||||
elif len(oid) == 24:
|
||||
try:
|
||||
self.__id = oid.decode("hex")
|
||||
except TypeError:
|
||||
raise InvalidId("%s is not a valid ObjectId" % oid)
|
||||
else:
|
||||
raise InvalidId("%s is not a valid ObjectId" % oid)
|
||||
else:
|
||||
raise TypeError("id must be an instance of (str, ObjectId), "
|
||||
"not %s" % type(oid))
|
||||
|
||||
@property
|
||||
def binary(self):
|
||||
"""12-byte binary representation of this ObjectId.
|
||||
"""
|
||||
return self.__id
|
||||
|
||||
@property
|
||||
def generation_time(self):
|
||||
"""A :class:`datetime.datetime` instance representing the time of
|
||||
generation for this :class:`ObjectId`.
|
||||
|
||||
The :class:`datetime.datetime` is timezone aware, and
|
||||
represents the generation time in UTC. It is precise to the
|
||||
second.
|
||||
|
||||
.. versionchanged:: 1.8
|
||||
Now return an aware datetime instead of a naive one.
|
||||
|
||||
.. versionadded:: 1.2
|
||||
"""
|
||||
t = struct.unpack(">i", self.__id[0:4])[0]
|
||||
return datetime.datetime.fromtimestamp(t, utc)
|
||||
|
||||
def __str__(self):
|
||||
return self.__id.encode("hex")
|
||||
|
||||
def __repr__(self):
|
||||
return "ObjectId('%s')" % self.__id.encode("hex")
|
||||
|
||||
def __cmp__(self, other):
|
||||
if isinstance(other, ObjectId):
|
||||
return cmp(self.__id, other.__id)
|
||||
return NotImplemented
|
||||
|
||||
def __hash__(self):
|
||||
"""Get a hash value for this :class:`ObjectId`.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
"""
|
||||
return hash(self.__id)
|
||||
from bson.objectid import *
|
||||
|
||||
191
pymongo/son.py
191
pymongo/son.py
@ -12,193 +12,4 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Tools for creating and manipulating SON, the Serialized Ocument Notation.
|
||||
|
||||
Regular dictionaries can be used instead of SON objects, but not when the order
|
||||
of keys is important. A SON object can be used just like a normal Python
|
||||
dictionary."""
|
||||
|
||||
|
||||
class SON(dict):
|
||||
"""SON data.
|
||||
|
||||
A subclass of dict that maintains ordering of keys and provides a few extra
|
||||
niceties for dealing with SON. SON objects can be saved and retrieved from
|
||||
Mongo.
|
||||
|
||||
The mapping from Python types to Mongo types is as follows:
|
||||
|
||||
=================================== ============= ===================
|
||||
Python Type Mongo Type Supported Direction
|
||||
=================================== ============= ===================
|
||||
None null both
|
||||
bool boolean both
|
||||
int number (int) both
|
||||
float number (real) both
|
||||
string string py -> mongo
|
||||
unicode string both
|
||||
list array both
|
||||
dict / `SON` object both
|
||||
datetime.datetime [#dt]_ [#dt2]_ date both
|
||||
compiled re regex both
|
||||
`pymongo.binary.Binary` binary both
|
||||
`pymongo.objectid.ObjectId` oid both
|
||||
`pymongo.dbref.DBRef` dbref both
|
||||
None undefined mongo -> py
|
||||
unicode code mongo -> py
|
||||
`pymongo.code.Code` code py -> mongo
|
||||
unicode symbol mongo -> py
|
||||
=================================== ============= ===================
|
||||
|
||||
Note that to save binary data it must be wrapped as an instance of
|
||||
`pymongo.binary.Binary`. Otherwise it will be saved as a Mongo string and
|
||||
retrieved as unicode.
|
||||
|
||||
.. [#dt] datetime.datetime instances will be rounded to the nearest
|
||||
millisecond when saved
|
||||
.. [#dt2] all datetime.datetime instances are treated as *naive*. clients
|
||||
should always use UTC.
|
||||
"""
|
||||
|
||||
def __init__(self, data=None, **kwargs):
|
||||
self.__keys = []
|
||||
dict.__init__(self)
|
||||
self.update(data)
|
||||
self.update(kwargs)
|
||||
|
||||
def __repr__(self):
|
||||
result = []
|
||||
for key in self.__keys:
|
||||
result.append("(%r, %r)" % (key, self[key]))
|
||||
return "SON([%s])" % ", ".join(result)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
if key not in self:
|
||||
self.__keys.append(key)
|
||||
dict.__setitem__(self, key, value)
|
||||
|
||||
def __delitem__(self, key):
|
||||
self.__keys.remove(key)
|
||||
dict.__delitem__(self, key)
|
||||
|
||||
def keys(self):
|
||||
return list(self.__keys)
|
||||
|
||||
def copy(self):
|
||||
other = SON()
|
||||
other.update(self)
|
||||
return other
|
||||
|
||||
# TODO this is all from UserDict.DictMixin. it could probably be made more
|
||||
# efficient.
|
||||
# second level definitions support higher levels
|
||||
def __iter__(self):
|
||||
for k in self.keys():
|
||||
yield k
|
||||
|
||||
def has_key(self, key):
|
||||
return key in self.keys()
|
||||
|
||||
def __contains__(self, key):
|
||||
return key in self.keys()
|
||||
|
||||
# third level takes advantage of second level definitions
|
||||
def iteritems(self):
|
||||
for k in self:
|
||||
yield (k, self[k])
|
||||
|
||||
def iterkeys(self):
|
||||
return self.__iter__()
|
||||
|
||||
# fourth level uses definitions from lower levels
|
||||
def itervalues(self):
|
||||
for _, v in self.iteritems():
|
||||
yield v
|
||||
|
||||
def values(self):
|
||||
return [v for _, v in self.iteritems()]
|
||||
|
||||
def items(self):
|
||||
return list(self.iteritems())
|
||||
|
||||
def clear(self):
|
||||
for key in self.keys():
|
||||
del self[key]
|
||||
|
||||
def setdefault(self, key, default=None):
|
||||
try:
|
||||
return self[key]
|
||||
except KeyError:
|
||||
self[key] = default
|
||||
return default
|
||||
|
||||
def pop(self, key, *args):
|
||||
if len(args) > 1:
|
||||
raise TypeError("pop expected at most 2 arguments, got "\
|
||||
+ repr(1 + len(args)))
|
||||
try:
|
||||
value = self[key]
|
||||
except KeyError:
|
||||
if args:
|
||||
return args[0]
|
||||
raise
|
||||
del self[key]
|
||||
return value
|
||||
|
||||
def popitem(self):
|
||||
try:
|
||||
k, v = self.iteritems().next()
|
||||
except StopIteration:
|
||||
raise KeyError('container is empty')
|
||||
del self[k]
|
||||
return (k, v)
|
||||
|
||||
def update(self, other=None, **kwargs):
|
||||
# Make progressively weaker assumptions about "other"
|
||||
if other is None:
|
||||
pass
|
||||
elif hasattr(other, 'iteritems'): # iteritems saves memory and lookups
|
||||
for k, v in other.iteritems():
|
||||
self[k] = v
|
||||
elif hasattr(other, 'keys'):
|
||||
for k in other.keys():
|
||||
self[k] = other[k]
|
||||
else:
|
||||
for k, v in other:
|
||||
self[k] = v
|
||||
if kwargs:
|
||||
self.update(kwargs)
|
||||
|
||||
def get(self, key, default=None):
|
||||
try:
|
||||
return self[key]
|
||||
except KeyError:
|
||||
return default
|
||||
|
||||
def __cmp__(self, other):
|
||||
if isinstance(other, SON):
|
||||
return cmp((dict(self.iteritems()), self.keys()),
|
||||
(dict(other.iteritems()), other.keys()))
|
||||
return cmp(dict(self.iteritems()), other)
|
||||
|
||||
def __len__(self):
|
||||
return len(self.keys())
|
||||
|
||||
def to_dict(self):
|
||||
"""Convert a SON document to a normal Python dictionary instance.
|
||||
|
||||
This is trickier than just *dict(...)* because it needs to be
|
||||
recursive.
|
||||
"""
|
||||
|
||||
def transform_value(value):
|
||||
if isinstance(value, list):
|
||||
return [transform_value(v) for v in value]
|
||||
if isinstance(value, SON):
|
||||
value = dict(value)
|
||||
if isinstance(value, dict):
|
||||
for k, v in value.iteritems():
|
||||
value[k] = transform_value(v)
|
||||
return value
|
||||
|
||||
return transform_value(dict(self))
|
||||
from bson.son import *
|
||||
|
||||
@ -18,9 +18,9 @@ New manipulators should be defined as subclasses of SONManipulator and can be
|
||||
installed on a database by calling
|
||||
`pymongo.database.Database.add_son_manipulator`."""
|
||||
|
||||
from pymongo.dbref import DBRef
|
||||
from pymongo.objectid import ObjectId
|
||||
from pymongo.son import SON
|
||||
from bson.dbref import DBRef
|
||||
from bson.objectid import ObjectId
|
||||
from bson.son import SON
|
||||
|
||||
|
||||
class SONManipulator(object):
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# Copyright 2010 10gen, Inc.
|
||||
# Copyright 2009-2010 10gen, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@ -12,85 +12,4 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Tools for representing MongoDB internal Timestamps.
|
||||
"""
|
||||
|
||||
import calendar
|
||||
import datetime
|
||||
|
||||
from pymongo.tz_util import utc
|
||||
|
||||
|
||||
class Timestamp(object):
|
||||
"""MongoDB internal timestamps used in the opLog.
|
||||
"""
|
||||
|
||||
def __init__(self, time, inc):
|
||||
"""Create a new :class:`Timestamp`.
|
||||
|
||||
This class is only for use with the MongoDB opLog. If you need
|
||||
to store a regular timestamp, please use a
|
||||
:class:`~datetime.datetime`.
|
||||
|
||||
Raises :class:`TypeError` if `time` is not an instance of
|
||||
:class: `int` or :class:`~datetime.datetime`, or `inc` is not
|
||||
an instance of :class:`int`. Raises :class:`ValueError` if
|
||||
`time` or `inc` is not in [0, 2**32).
|
||||
|
||||
:Parameters:
|
||||
- `time`: time in seconds since epoch UTC, or a naive UTC
|
||||
:class:`~datetime.datetime`, or an aware
|
||||
:class:`~datetime.datetime`
|
||||
- `inc`: the incrementing counter
|
||||
|
||||
.. versionchanged:: 1.7
|
||||
`time` can now be a :class:`~datetime.datetime` instance.
|
||||
"""
|
||||
if isinstance(time, datetime.datetime):
|
||||
if time.utcoffset() is not None:
|
||||
time = time - time.utcoffset()
|
||||
time = int(calendar.timegm(time.timetuple()))
|
||||
if not isinstance(time, (int, long)):
|
||||
raise TypeError("time must be an instance of int")
|
||||
if not isinstance(inc, (int, long)):
|
||||
raise TypeError("inc must be an instance of int")
|
||||
if not 0 <= time < 2 ** 32:
|
||||
raise ValueError("time must be contained in [0, 2**32)")
|
||||
if not 0 <= inc < 2 ** 32:
|
||||
raise ValueError("inc must be contained in [0, 2**32)")
|
||||
|
||||
self.__time = time
|
||||
self.__inc = inc
|
||||
|
||||
@property
|
||||
def time(self):
|
||||
"""Get the time portion of this :class:`Timestamp`.
|
||||
"""
|
||||
return self.__time
|
||||
|
||||
@property
|
||||
def inc(self):
|
||||
"""Get the inc portion of this :class:`Timestamp`.
|
||||
"""
|
||||
return self.__inc
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, Timestamp):
|
||||
return (self.__time == other.time and self.__inc == other.inc)
|
||||
else:
|
||||
return NotImplemented
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __repr__(self):
|
||||
return "Timestamp(%s, %s)" % (self.__time, self.__inc)
|
||||
|
||||
def as_datetime(self):
|
||||
"""Return a :class:`~datetime.datetime` instance corresponding
|
||||
to the time portion of this :class:`Timestamp`.
|
||||
|
||||
.. versionchanged:: 1.8
|
||||
The returned datetime is now timezone aware.
|
||||
"""
|
||||
return datetime.datetime.fromtimestamp(self.__time, utc)
|
||||
from bson.timestamp import *
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# Copyright 2010 10gen, Inc.
|
||||
# Copyright 2009-2010 10gen, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@ -12,34 +12,4 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Timezone related utilities for PyMongo."""
|
||||
|
||||
from datetime import (timedelta,
|
||||
tzinfo)
|
||||
|
||||
ZERO = timedelta(0)
|
||||
|
||||
|
||||
class FixedOffset(tzinfo):
|
||||
"""Fixed offset timezone, in minutes east from UTC.
|
||||
|
||||
Implementation from the Python `standard library documentation
|
||||
<http://docs.python.org/library/datetime.html#tzinfo-objects>`_.
|
||||
"""
|
||||
|
||||
def __init__(self, offset, name):
|
||||
self.__offset = timedelta(minutes=offset)
|
||||
self.__name = name
|
||||
|
||||
def utcoffset(self, dt):
|
||||
return self.__offset
|
||||
|
||||
def tzname(self, dt):
|
||||
return self.__name
|
||||
|
||||
def dst(self, dt):
|
||||
return ZERO
|
||||
|
||||
|
||||
"""UTC"""
|
||||
utc = FixedOffset(0, "UTC")
|
||||
from bson.tz_util import *
|
||||
|
||||
@ -19,10 +19,10 @@ import re
|
||||
import sys
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
from pymongo.binary import Binary
|
||||
from pymongo.objectid import ObjectId
|
||||
from pymongo.dbref import DBRef
|
||||
from pymongo.son import SON
|
||||
from bson.binary import Binary
|
||||
from bson.dbref import DBRef
|
||||
from bson.objectid import ObjectId
|
||||
from bson.son import SON
|
||||
|
||||
gen_target = 100
|
||||
reduction_attempts = 10
|
||||
|
||||
@ -18,7 +18,7 @@ import unittest
|
||||
import sys
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
from pymongo.binary import Binary
|
||||
from bson.binary import Binary
|
||||
|
||||
|
||||
class TestBinary(unittest.TestCase):
|
||||
|
||||
@ -29,19 +29,20 @@ sys.path[0:0] = [""]
|
||||
|
||||
from nose.plugins.skip import SkipTest
|
||||
|
||||
from bson import BSON, is_valid, _to_dicts
|
||||
from bson.binary import Binary
|
||||
from bson.code import Code
|
||||
from bson.objectid import ObjectId
|
||||
from bson.dbref import DBRef
|
||||
from bson.son import SON
|
||||
from bson.timestamp import Timestamp
|
||||
from bson.errors import (InvalidDocument,
|
||||
InvalidStringData)
|
||||
from bson.max_key import MaxKey
|
||||
from bson.min_key import MinKey
|
||||
from bson.tz_util import (FixedOffset,
|
||||
utc)
|
||||
import pymongo
|
||||
from pymongo.binary import Binary
|
||||
from pymongo.code import Code
|
||||
from pymongo.objectid import ObjectId
|
||||
from pymongo.dbref import DBRef
|
||||
from pymongo.son import SON
|
||||
from pymongo.timestamp import Timestamp
|
||||
from pymongo.bson import BSON, is_valid, _to_dicts
|
||||
from pymongo.errors import InvalidDocument, InvalidStringData
|
||||
from pymongo.max_key import MaxKey
|
||||
from pymongo.min_key import MinKey
|
||||
from pymongo.tz_util import (FixedOffset,
|
||||
utc)
|
||||
import qcheck
|
||||
|
||||
class TestBSON(unittest.TestCase):
|
||||
|
||||
@ -18,7 +18,7 @@ import unittest
|
||||
import sys
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
from pymongo.code import Code
|
||||
from bson.code import Code
|
||||
|
||||
|
||||
class TestCode(unittest.TestCase):
|
||||
|
||||
@ -27,9 +27,11 @@ from nose.plugins.skip import SkipTest
|
||||
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
from bson.binary import Binary
|
||||
from bson.code import Code
|
||||
from bson.objectid import ObjectId
|
||||
from bson.son import SON
|
||||
from pymongo import ASCENDING, DESCENDING
|
||||
from pymongo.binary import Binary
|
||||
from pymongo.code import Code
|
||||
from pymongo.collection import Collection
|
||||
from pymongo.errors import (DuplicateKeyError,
|
||||
InvalidDocument,
|
||||
@ -37,8 +39,6 @@ from pymongo.errors import (DuplicateKeyError,
|
||||
InvalidOperation,
|
||||
OperationFailure,
|
||||
TimeoutError)
|
||||
from pymongo.objectid import ObjectId
|
||||
from pymongo.son import SON
|
||||
from test.test_connection import get_connection
|
||||
from test import (qcheck,
|
||||
version)
|
||||
|
||||
@ -22,11 +22,13 @@ sys.path[0:0] = [""]
|
||||
|
||||
from nose.plugins.skip import SkipTest
|
||||
|
||||
from pymongo.errors import InvalidOperation, OperationFailure
|
||||
from bson.code import Code
|
||||
from pymongo import (ASCENDING,
|
||||
DESCENDING)
|
||||
from pymongo.cursor import Cursor
|
||||
from pymongo.database import Database
|
||||
from pymongo.code import Code
|
||||
from pymongo import ASCENDING, DESCENDING
|
||||
from pymongo.errors import (InvalidOperation,
|
||||
OperationFailure)
|
||||
from test_connection import get_connection
|
||||
import version
|
||||
|
||||
|
||||
@ -20,24 +20,25 @@ import sys
|
||||
sys.path[0:0] = [""]
|
||||
import unittest
|
||||
|
||||
from bson.code import Code
|
||||
from bson.dbref import DBRef
|
||||
from bson.errors import InvalidName
|
||||
from bson.objectid import ObjectId
|
||||
from bson.son import SON
|
||||
from pymongo import (ALL,
|
||||
ASCENDING,
|
||||
DESCENDING,
|
||||
helpers,
|
||||
OFF,
|
||||
SLOW_ONLY)
|
||||
from pymongo.code import Code
|
||||
from pymongo.collection import Collection
|
||||
from pymongo.connection import Connection
|
||||
from pymongo.database import Database
|
||||
from pymongo.dbref import DBRef
|
||||
from pymongo.errors import (CollectionInvalid,
|
||||
InvalidName,
|
||||
InvalidOperation,
|
||||
OperationFailure)
|
||||
from pymongo.objectid import ObjectId
|
||||
from pymongo.son import SON
|
||||
from pymongo.son_manipulator import AutoReference, NamespaceInjector
|
||||
from pymongo.son_manipulator import (AutoReference,
|
||||
NamespaceInjector)
|
||||
from test import version
|
||||
from test.test_connection import get_connection
|
||||
|
||||
|
||||
@ -18,8 +18,8 @@ import unittest
|
||||
import sys
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
from pymongo.objectid import ObjectId
|
||||
from pymongo.dbref import DBRef
|
||||
from bson.objectid import ObjectId
|
||||
from bson.dbref import DBRef
|
||||
|
||||
|
||||
class TestDBRef(unittest.TestCase):
|
||||
|
||||
@ -27,6 +27,7 @@ import sys
|
||||
import unittest
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
from bson.objectid import ObjectId
|
||||
from gridfs.grid_file import (_SEEK_CUR,
|
||||
_SEEK_END,
|
||||
GridIn,
|
||||
@ -34,7 +35,6 @@ from gridfs.grid_file import (_SEEK_CUR,
|
||||
GridOut)
|
||||
from gridfs.errors import (NoFile,
|
||||
UnsupportedAPI)
|
||||
from pymongo.objectid import ObjectId
|
||||
from test_connection import get_connection
|
||||
import qcheck
|
||||
|
||||
|
||||
@ -36,13 +36,13 @@ from nose.plugins.skip import SkipTest
|
||||
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
from bson.objectid import ObjectId
|
||||
from bson.dbref import DBRef
|
||||
from bson.min_key import MinKey
|
||||
from bson.max_key import MaxKey
|
||||
from bson.timestamp import Timestamp
|
||||
from bson.tz_util import utc
|
||||
from pymongo.json_util import default, object_hook
|
||||
from pymongo.objectid import ObjectId
|
||||
from pymongo.dbref import DBRef
|
||||
from pymongo.min_key import MinKey
|
||||
from pymongo.max_key import MaxKey
|
||||
from pymongo.timestamp import Timestamp
|
||||
from pymongo.tz_util import utc
|
||||
|
||||
class TestJsonUtil(unittest.TestCase):
|
||||
|
||||
|
||||
@ -23,10 +23,10 @@ sys.path[0:0] = [""]
|
||||
|
||||
from nose.plugins.skip import SkipTest
|
||||
|
||||
from pymongo.errors import InvalidId
|
||||
from pymongo.objectid import ObjectId
|
||||
from pymongo.tz_util import (FixedOffset,
|
||||
utc)
|
||||
from bson.errors import InvalidId
|
||||
from bson.objectid import ObjectId
|
||||
from bson.tz_util import (FixedOffset,
|
||||
utc)
|
||||
|
||||
def oid(x):
|
||||
return ObjectId()
|
||||
|
||||
@ -19,13 +19,15 @@ import unittest
|
||||
import sys
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
import qcheck
|
||||
from pymongo.objectid import ObjectId
|
||||
from pymongo.son import SON
|
||||
from pymongo.son_manipulator import SONManipulator, ObjectIdInjector
|
||||
from pymongo.son_manipulator import NamespaceInjector, ObjectIdShuffler
|
||||
from bson.objectid import ObjectId
|
||||
from bson.son import SON
|
||||
from pymongo.database import Database
|
||||
from pymongo.son_manipulator import (NamespaceInjector,
|
||||
ObjectIdInjector,
|
||||
ObjectIdShuffler,
|
||||
SONManipulator)
|
||||
from test_connection import get_connection
|
||||
import qcheck
|
||||
|
||||
|
||||
class TestSONManipulator(unittest.TestCase):
|
||||
|
||||
Loading…
Reference in New Issue
Block a user