~crunch.io/ubuntu/precise/pymongo/unstable

« back to all changes in this revision

Viewing changes to gridfs/grid_file.py

  • Committer: Joseph Tate
  • Date: 2013-01-31 08:00:57 UTC
  • mfrom: (1.1.12)
  • Revision ID: jtate@dragonstrider.com-20130131080057-y7lv17xi6x8c1j5x
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
import datetime
18
18
import math
19
19
import os
20
 
import sys
21
 
 
22
 
if sys.version_info[0] == 3:
23
 
    from io import BytesIO as StringIO
24
 
else:
25
 
    # 2to3 will turn cStringIO into io. That's okay
26
 
    # since we'll never get here under python3.
27
 
    from cStringIO import StringIO
28
20
 
29
21
from bson.binary import Binary
30
22
from bson.objectid import ObjectId
31
 
from bson.py3compat import b, binary_type, string_types, text_type
 
23
from bson.py3compat import b, binary_type, string_types, text_type, StringIO
32
24
from gridfs.errors import (CorruptGridFile,
33
25
                           FileExists,
34
26
                           NoFile,
70
62
    def setter(self, value):
71
63
        if self._closed:
72
64
            self._coll.files.update({"_id": self._file["_id"]},
73
 
                                    {"$set": {field_name: value}}, safe=True)
 
65
                                    {"$set": {field_name: value}},
 
66
                                    **self._coll._get_wc_override())
74
67
        self._file[field_name] = value
75
68
 
76
69
    if read_only:
122
115
            that is written to the file will be converted to
123
116
            :class:`bytes`.
124
117
 
 
118
        If you turn off write-acknowledgment for performance reasons, it is
 
119
        critical to wrap calls to :meth:`write` and :meth:`close` within a
 
120
        single request:
 
121
 
 
122
           >>> from pymongo import MongoClient
 
123
           >>> from gridfs import GridFS
 
124
           >>> client = MongoClient(w=0) # turn off write acknowledgment
 
125
           >>> fs = GridFS(client)
 
126
           >>> gridin = fs.new_file()
 
127
           >>> request = client.start_request()
 
128
           >>> try:
 
129
           ...     for i in range(10):
 
130
           ...         gridin.write('foo')
 
131
           ...     gridin.close()
 
132
           ... finally:
 
133
           ...     request.end()
 
134
 
 
135
        In Python 2.5 and later this code can be simplified with a
 
136
        with-statement, see :doc:`/examples/requests` for more information.
 
137
 
125
138
        :Parameters:
126
139
          - `root_collection`: root collection to write to
127
140
          - `**kwargs` (optional): file level options (see above)
160
173
    _id = _create_property("_id", "The ``'_id'`` value for this file.",
161
174
                            read_only=True)
162
175
    filename = _create_property("filename", "Name of this file.")
 
176
    name = _create_property("filename", "Alias for `filename`.")
163
177
    content_type = _create_property("contentType", "Mime-type for this file.")
164
178
    length = _create_property("length", "Length (in bytes) of this file.",
165
179
                               closed_only=True)
178
192
        raise AttributeError("GridIn object has no attribute '%s'" % name)
179
193
 
180
194
    def __setattr__(self, name, value):
181
 
        object.__setattr__(self, name, value)
182
 
        if self._closed:
183
 
            self._coll.files.update({"_id": self._file["_id"]},
184
 
                                    {"$set": {name: value}}, safe=True)
 
195
        # For properties of this instance like _buffer, or descriptors set on
 
196
        # the class like filename, use regular __setattr__
 
197
        if name in self.__dict__ or name in self.__class__.__dict__:
 
198
            object.__setattr__(self, name, value)
 
199
        else:
 
200
            # All other attributes are part of the document in db.fs.files.
 
201
            # Store them to be sent to server on close() or if closed, send
 
202
            # them now.
 
203
            self._file[name] = value
 
204
            if self._closed:
 
205
                self._coll.files.update({"_id": self._file["_id"]},
 
206
                                        {"$set": {name: value}},
 
207
                                        **self._coll._get_wc_override())
185
208
 
186
209
    def __flush_data(self, data):
187
210
        """Flush `data` to a chunk.
194
217
                 "n": self._chunk_number,
195
218
                 "data": Binary(data)}
196
219
 
197
 
        self._chunks.insert(chunk)
 
220
        try:
 
221
            self._chunks.insert(chunk)
 
222
        except DuplicateKeyError:
 
223
            self._raise_file_exists(self._file['_id'])
198
224
        self._chunk_number += 1
199
225
        self._position += len(data)
200
226
 
208
234
    def __flush(self):
209
235
        """Flush the file to the database.
210
236
        """
211
 
        self.__flush_buffer()
212
 
 
213
 
        md5 = self._coll.database.command("filemd5", self._id,
214
 
                                          root=self._coll.name)["md5"]
215
 
 
216
 
        self._file["md5"] = md5
217
 
        self._file["length"] = self._position
218
 
        self._file["uploadDate"] = datetime.datetime.utcnow()
219
 
 
220
237
        try:
221
 
            return self._coll.files.insert(self._file, safe=True)
 
238
            self.__flush_buffer()
 
239
 
 
240
            db = self._coll.database
 
241
 
 
242
            # See PYTHON-417, "Sharded GridFS fails with exception: chunks out
 
243
            # of order." Inserts via mongos, even if they use a single
 
244
            # connection, can succeed out-of-order due to the writebackListener.
 
245
            # We mustn't call "filemd5" until all inserts are complete, which
 
246
            # we ensure by calling getLastError (and ignoring the result).
 
247
            db.error()
 
248
 
 
249
            md5 = db.command(
 
250
                "filemd5", self._id, root=self._coll.name)["md5"]
 
251
 
 
252
            self._file["md5"] = md5
 
253
            self._file["length"] = self._position
 
254
            self._file["uploadDate"] = datetime.datetime.utcnow()
 
255
 
 
256
            return self._coll.files.insert(self._file,
 
257
                                           **self._coll._get_wc_override())
222
258
        except DuplicateKeyError:
223
 
            raise FileExists("file with _id %r already exists" % self._id)
 
259
            self._raise_file_exists(self._id)
 
260
 
 
261
    def _raise_file_exists(self, file_id):
 
262
        """Raise a FileExists exception for the given file_id."""
 
263
        raise FileExists("file with _id %r already exists" % file_id)
224
264
 
225
265
    def close(self):
226
266
        """Flush the file and close it.
307
347
    def __exit__(self, exc_type, exc_val, exc_tb):
308
348
        """Support for the context manager protocol.
309
349
 
310
 
        Close the file and allow exceptions to propogate.
 
350
        Close the file and allow exceptions to propagate.
311
351
        """
312
352
        self.close()
313
353
 
314
 
        # propogate exceptions
 
354
        # propagate exceptions
315
355
        return False
316
356
 
317
357
 
355
395
        self.__position = 0
356
396
 
357
397
    _id = _create_property("_id", "The ``'_id'`` value for this file.", True)
358
 
    name = _create_property("filename", "Name of this file.", True)
 
398
    filename = _create_property("filename", "Name of this file.", True)
 
399
    name = _create_property("filename", "Alias for `filename`.", True)
359
400
    content_type = _create_property("contentType", "Mime-type for this file.",
360
401
                                     True)
361
402
    length = _create_property("length", "Length (in bytes) of this file.",