Ejemplo n.º 1
0
class AgnosticGridIn(AgnosticBase):
    __delegate_class__ = gridfs.GridIn
    __motor_class_name__ = 'MotorGridIn'

    abort = AsyncCommand()
    chunk_size = ReadOnlyProperty()
    closed = ReadOnlyProperty()
    close = AsyncCommand()
    content_type = ReadOnlyProperty()
    filename = ReadOnlyProperty()
    length = ReadOnlyProperty()
    md5 = ReadOnlyProperty()
    name = ReadOnlyProperty()
    read = DelegateMethod()
    readable = DelegateMethod()
    seekable = DelegateMethod()
    upload_date = ReadOnlyProperty()
    write = AsyncCommand().unwrap('MotorGridOut')
    writeable = DelegateMethod()
    writelines = AsyncCommand().unwrap('MotorGridOut')

    async def __aenter__(self):
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        await self.delegate.close()

    def __getattr__(self, item):
        return getattr(self.delegate, item)

    def __setattr__(self, item, value):
        return getattr(self.delegate, item, value)
Ejemplo n.º 2
0
class AgnosticGridOut(AgnosticBase):
    __delegate_class__ = gridfs.GridOut
    __motor_class_name__ = 'MotorGridOut'

    chunk_size = ReadOnlyProperty()
    close = ReadOnlyProperty()
    content_type = ReadOnlyProperty()
    filename = ReadOnlyProperty()
    length = ReadOnlyProperty()
    md5 = ReadOnlyProperty()
    metadata = ReadOnlyProperty()
    name = ReadOnlyProperty()
    read = AsyncRead()
    readable = DelegateMethod()
    readchunk = AsyncRead()
    readline = AsyncRead()
    seek = DelegateMethod()
    seekable = DelegateMethod()
    tell = DelegateMethod()
    upload_date = ReadOnlyProperty()
    write = DelegateMethod()

    def __aiter__(self):
        return self

    async def __anext__(self):
        return next(self.delegate)

    def __getattr__(self, item):
        return getattr(self.delegate, item)
Ejemplo n.º 3
0
class AgnosticClient(AgnosticBase):
    __motor_class_name__ = 'MotorClient'
    __delegate_class__ = mongomock.MongoClient

    def __init__(self, *args, **kwargs):
        self._extract_io_loop(kwargs)
        super(AgnosticBase,
              self).__init__(mongomock.MongoClient(*args, **kwargs))

    close = DelegateMethod()
    drop_database = AsyncCommand().unwrap('MotorDatabase')
    get_database = DelegateMethod().wrap(mongomock.Database)
    get_default_database = DelegateMethod().wrap(mongomock.Database)
    list_database_names = AsyncRead()
    server_info = AsyncRead()
Ejemplo n.º 4
0
class AgnosticCollection(AgnosticBase):
    __motor_class_name__ = 'MotorCollection'
    __delegate_class__ = mongomock.Collection

    bulk_write = AsyncCommand()
    count_documents = AsyncRead()
    create_index = AsyncCommand()
    create_indexes = AsyncCommand()
    delete_many = AsyncCommand()
    delete_one = AsyncCommand()
    distinct = AsyncRead()
    drop = AsyncCommand()
    drop_index = AsyncCommand()
    drop_indexes = AsyncCommand()
    estimated_document_count = AsyncCommand()
    find_one = AsyncRead()
    find_one_and_delete = AsyncCommand()
    find_one_and_replace = AsyncCommand()
    find_one_and_update = AsyncCommand()
    full_name = ReadOnlyProperty()
    insert_many = AsyncWrite()
    insert_one = AsyncCommand()
    map_reduce = AsyncCommand().wrap(mongomock.Collection)
    name = ReadOnlyProperty()
    reindex = AsyncCommand()
    rename = AsyncCommand()
    replace_one = AsyncCommand()
    update_many = AsyncCommand()
    update_one = AsyncCommand()
    with_options = DelegateMethod().wrap(mongomock.Collection)

    def find(self, *args, **kwargs):
        return AsyncIOMotorCursor(self.delegate.find(*args, **kwargs))
Ejemplo n.º 5
0
class AgnosticGridFSBucket(AgnosticBase):
    __delegate_class__ = gridfs.GridFSBucket
    __motor_class_name__ = 'MotorGridFSBucket'

    delete = AsyncCommand()
    download_to_stream = AsyncCommand()
    download_to_stream_by_name = AsyncCommand()
    open_download_stream = AsyncCommand().wrap(gridfs.GridOut)
    open_download_stream_by_name = AsyncCommand().wrap(gridfs.GridOut)
    open_upload_stream = DelegateMethod().wrap(gridfs.GridIn)
    open_upload_stream_with_id = DelegateMethod().wrap(gridfs.GridIn)
    rename = AsyncCommand()
    upload_from_stream = AsyncCommand()
    upload_from_stream_with_id = AsyncCommand()

    def __init__(self, db, *args, **kwargs):
        self._extract_io_loop(kwargs)
        super(AgnosticBase,
              self).__init__(gridfs.GridFSBucket(db.delegate, *args, **kwargs))
Ejemplo n.º 6
0
class AgnosticDatabase(AgnosticBase):
    __motor_class_name__ = 'MotorDatabase'
    __delegate_class__ = mongomock.Database

    command = AsyncCommand()
    create_collection = AsyncCommand().wrap(mongomock.Collection)
    dereference = AsyncRead()
    drop_collection = AsyncCommand().unwrap('MotorCollection')
    get_collection = DelegateMethod().wrap(mongomock.Collection)
    list_collection_names = AsyncRead()
    with_options = DelegateMethod().wrap(mongomock.Database)

    def __getattr__(self, name):
        if name.startswith('_'):
            raise AttributeError("%s has no attribute %r. To access the %s"
                                 " collection, use database['%s']." %
                                 (self.__class__.__name__, name, name, name))

        return self[name]

    def __getitem__(self, name):
        return self.wrap(self.delegate[name])
Ejemplo n.º 7
0
class AgnosticGridFSBucket(_GFSBase):
    __motor_class_name__ = 'MotorGridFSBucket'
    __delegate_class__ = gridfs.GridFSBucket

    delete                       = AsyncCommand()
    download_to_stream           = AsyncCommand()
    download_to_stream_by_name   = AsyncCommand()
    open_download_stream         = AsyncCommand().wrap(gridfs.GridOut)
    open_download_stream_by_name = AsyncCommand().wrap(gridfs.GridOut)
    open_upload_stream           = DelegateMethod().wrap(gridfs.GridIn)
    open_upload_stream_with_id   = DelegateMethod().wrap(gridfs.GridIn)
    rename                       = AsyncCommand()
    upload_from_stream           = AsyncCommand()
    upload_from_stream_with_id   = AsyncCommand()

    def __init__(self, database, collection="fs"):
        """Create a handle to a GridFS bucket.

        Raises :exc:`~pymongo.errors.ConfigurationError` if `write_concern`
        is not acknowledged.

        This class is a replacement for :class:`.MotorGridFS`; it conforms to the
        `GridFS API Spec <https://github.com/mongodb/specifications/blob/master/source/gridfs/gridfs-spec.rst>`_
        for MongoDB drivers.

        :Parameters:
          - `database`: database to use.
          - `bucket_name` (optional): The name of the bucket. Defaults to 'fs'.
          - `chunk_size_bytes` (optional): The chunk size in bytes. Defaults
            to 255KB.
          - `write_concern` (optional): The
            :class:`~pymongo.write_concern.WriteConcern` to use. If ``None``
            (the default) db.write_concern is used.
          - `read_preference` (optional): The read preference to use. If
            ``None`` (the default) db.read_preference is used.

        .. versionadded:: 1.0

        .. mongodoc:: gridfs
        """
        super(self.__class__, self).__init__(database, collection)

    def find(self, *args, **kwargs):
        """Find and return the files collection documents that match ``filter``.

        Returns a cursor that iterates across files matching
        arbitrary queries on the files collection. Can be combined
        with other modifiers for additional control.

        For example::

          cursor = bucket.find({"filename": "lisa.txt"}, no_cursor_timeout=True)
          while (yield cursor.fetch_next):
              grid_out = cursor.next_object()
              data = yield grid_out.read()

        This iterates through all versions of "lisa.txt" stored in GridFS.
        Note that setting no_cursor_timeout to True may be important to
        prevent the cursor from timing out during long multi-file processing
        work.

        As another example, the call::

          most_recent_three = fs.find().sort("uploadDate", -1).limit(3)

        would return a cursor to the three most recently uploaded files
        in GridFS.

        Follows a similar interface to
        :meth:`~motor.MotorCollection.find`
        in :class:`~motor.MotorCollection`.

        :Parameters:
          - `filter`: Search query.
          - `batch_size` (optional): The number of documents to return per
            batch.
          - `limit` (optional): The maximum number of documents to return.
          - `no_cursor_timeout` (optional): The server normally times out idle
            cursors after an inactivity period (10 minutes) to prevent excess
            memory use. Set this option to True prevent that.
          - `skip` (optional): The number of documents to skip before
            returning.
          - `sort` (optional): The order by which to sort results. Defaults to
            None.
        """
        cursor = self.delegate.find(*args, **kwargs)
        grid_out_cursor = create_class_with_framework(
            AgnosticGridOutCursor, self._framework, self.__module__)

        return grid_out_cursor(cursor, self.collection)
Ejemplo n.º 8
0
class AgnosticGridIn(object):
    __motor_class_name__ = 'MotorGridIn'
    __delegate_class__ = gridfs.GridIn

    __getattr__  = DelegateMethod()
    abort        = AsyncCommand()
    closed       = ReadOnlyProperty()
    close        = AsyncCommand()
    write        = AsyncCommand().unwrap('MotorGridOut')
    writelines   = AsyncCommand().unwrap('MotorGridOut')
    _id          = ReadOnlyProperty()
    md5          = ReadOnlyProperty()
    filename     = ReadOnlyProperty()
    name         = ReadOnlyProperty()
    content_type = ReadOnlyProperty()
    length       = ReadOnlyProperty()
    chunk_size   = ReadOnlyProperty()
    upload_date  = ReadOnlyProperty()
    set          = AsyncCommand(attr_name='__setattr__', doc="""
Set an arbitrary metadata attribute on the file. Stores value on the server
as a key-value pair within the file document once the file is closed. If
the file is already closed, calling :meth:`set` will immediately update the file
document on the server.

Metadata set on the file appears as attributes on a
:class:`~motor.MotorGridOut` object created from the file.

:Parameters:
  - `name`: Name of the attribute, will be stored as a key in the file
    document on the server
  - `value`: Value of the attribute
""")

    def __init__(self, root_collection, delegate=None, **kwargs):
        """
        Class to write data to GridFS. Application developers should not
        generally need to instantiate this class - see
        :meth:`~motor.MotorGridFSBucket.open_upload_stream`.

        Any of the file level options specified in the `GridFS Spec
        <http://dochub.mongodb.org/core/gridfs>`_ may be passed as
        keyword arguments. Any additional keyword arguments will be
        set as additional fields on the file document. Valid keyword
        arguments include:

          - ``"_id"``: unique ID for this file (default:
            :class:`~bson.objectid.ObjectId`) - this ``"_id"`` must
            not have already been used for another file

          - ``"filename"``: human name for the file

          - ``"contentType"`` or ``"content_type"``: valid mime-type
            for the file

          - ``"chunkSize"`` or ``"chunk_size"``: size of each of the
            chunks, in bytes (default: 256 kb)

          - ``"encoding"``: encoding used for this file. In Python 2,
            any :class:`unicode` that is written to the file will be
            converted to a :class:`str`. In Python 3, any :class:`str`
            that is written to the file will be converted to
            :class:`bytes`.

        :Parameters:
          - `root_collection`: A :class:`~motor.MotorCollection`, the root
             collection to write to
          - `**kwargs` (optional): file level options (see above)

        .. versionchanged:: 0.2
           ``open`` method removed, no longer needed.
        """
        collection_class = create_class_with_framework(
            AgnosticCollection, self._framework, self.__module__)

        if not isinstance(root_collection, collection_class):
            raise TypeError(
                "First argument to MotorGridIn must be "
                "MotorCollection, not %r" % root_collection)

        self.io_loop = root_collection.get_io_loop()
        if delegate:
            # Short cut.
            self.delegate = delegate
        else:
            self.delegate = self.__delegate_class__(
                root_collection.delegate,
                **kwargs)

    if PY35:
        # Support "async with fs.new_file() as f:"
        exec(textwrap.dedent("""
        async def __aenter__(self):
            return self

        async def __aexit__(self, exc_type, exc_val, exc_tb):
            await self.close()
        """), globals(), locals())

    def get_io_loop(self):
        return self.io_loop
Ejemplo n.º 9
0
class AgnosticGridOut(object):
    """Class to read data out of GridFS.

    MotorGridOut supports the same attributes as PyMongo's
    :class:`~gridfs.grid_file.GridOut`, such as ``_id``, ``content_type``,
    etc.

    You don't need to instantiate this class directly - use the
    methods provided by :class:`~motor.MotorGridFSBucket`. If it **is**
    instantiated directly, call :meth:`open`, :meth:`read`, or
    :meth:`readline` before accessing its attributes.
    """
    __motor_class_name__ = 'MotorGridOut'
    __delegate_class__ = gridfs.GridOut

    _ensure_file = AsyncCommand()
    _id          = MotorGridOutProperty()
    aliases      = MotorGridOutProperty()
    chunk_size   = MotorGridOutProperty()
    close        = MotorGridOutProperty()
    content_type = MotorGridOutProperty()
    filename     = MotorGridOutProperty()
    length       = MotorGridOutProperty()
    md5          = MotorGridOutProperty()
    metadata     = MotorGridOutProperty()
    name         = MotorGridOutProperty()
    read         = AsyncRead()
    readchunk    = AsyncRead()
    readline     = AsyncRead()
    seek         = DelegateMethod()
    tell         = DelegateMethod()
    upload_date  = MotorGridOutProperty()

    def __init__(
        self,
        root_collection,
        file_id=None,
        file_document=None,
        delegate=None,
    ):
        collection_class = create_class_with_framework(
            AgnosticCollection, self._framework, self.__module__)

        if not isinstance(root_collection, collection_class):
            raise TypeError(
                "First argument to MotorGridOut must be "
                "MotorCollection, not %r" % root_collection)

        if delegate:
            self.delegate = delegate
        else:
            self.delegate = self.__delegate_class__(
                root_collection.delegate,
                file_id,
                file_document)

        self.io_loop = root_collection.get_io_loop()

    # python.org/dev/peps/pep-0492/#api-design-and-implementation-revisions
    if PY352:
        exec(textwrap.dedent("""
        def __aiter__(self):
            return self

        async def __anext__(self):
            chunk = await self.readchunk()
            if chunk:
                return chunk
            raise StopAsyncIteration()
        """), globals(), locals())

    elif PY35:
        # In Python 3.5.0 and 3.5.1, __aiter__ is a coroutine.
        exec(textwrap.dedent("""
        async def __aiter__(self):
            return self

        async def __anext__(self):
            chunk = await self.readchunk()
            if chunk:
                return chunk
            raise StopAsyncIteration()
        """), globals(), locals())

    def __getattr__(self, item):
        if not self.delegate._file:
            raise pymongo.errors.InvalidOperation(
                "You must call MotorGridOut.open() before accessing "
                "the %s property" % item)

        return getattr(self.delegate, item)

    @coroutine_annotation
    def open(self, callback=None):
        """Retrieve this file's attributes from the server.

        Takes an optional callback, or returns a Future.

        :Parameters:
         - `callback`: Optional function taking parameters (self, error)

        .. versionchanged:: 0.2
           :class:`~motor.MotorGridOut` now opens itself on demand, calling
           ``open`` explicitly is rarely needed.
        """
        return self._framework.future_or_callback(self._ensure_file(),
                                                  callback,
                                                  self.get_io_loop(),
                                                  self)

    def get_io_loop(self):
        return self.io_loop

    @motor_coroutine
    def stream_to_handler(self, request_handler):
        """Write the contents of this file to a
        :class:`tornado.web.RequestHandler`. This method calls
        :meth:`~tornado.web.RequestHandler.flush` on
        the RequestHandler, so ensure all headers have already been set.
        For a more complete example see the implementation of
        :class:`~motor.web.GridFSHandler`.

        .. code-block:: python

            class FileHandler(tornado.web.RequestHandler):
                @tornado.web.asynchronous
                @gen.coroutine
                def get(self, filename):
                    db = self.settings['db']
                    fs = yield motor.MotorGridFSBucket(db())
                    try:
                        gridout = yield fs.open_download_stream_by_name(filename)
                    except gridfs.NoFile:
                        raise tornado.web.HTTPError(404)

                    self.set_header("Content-Type", gridout.content_type)
                    self.set_header("Content-Length", gridout.length)
                    yield gridout.stream_to_handler(self)
                    self.finish()

        .. seealso:: Tornado `RequestHandler <http://tornadoweb.org/en/stable/web.html#request-handlers>`_
        """
        written = 0
        while written < self.length:
            # Reading chunk_size at a time minimizes buffering.
            f = self._framework.yieldable(self.read(self.chunk_size))
            yield f
            chunk = f.result()

            # write() simply appends the output to a list; flush() sends it
            # over the network and minimizes buffering in the handler.
            request_handler.write(chunk)
            request_handler.flush()
            written += len(chunk)
Ejemplo n.º 10
0
class AgnosticGridFSBucket(object):
    __motor_class_name__ = 'MotorGridFSBucket'
    __delegate_class__ = gridfs.GridFSBucket

    delete = AsyncCommand()
    download_to_stream = AsyncCommand()
    download_to_stream_by_name = AsyncCommand()
    open_download_stream = AsyncCommand().wrap(gridfs.GridOut)
    open_download_stream_by_name = AsyncCommand().wrap(gridfs.GridOut)
    open_upload_stream = DelegateMethod().wrap(gridfs.GridIn)
    open_upload_stream_with_id = DelegateMethod().wrap(gridfs.GridIn)
    rename = AsyncCommand()
    upload_from_stream = AsyncCommand()
    upload_from_stream_with_id = AsyncCommand()

    def __init__(self,
                 database,
                 bucket_name="fs",
                 disable_md5=False,
                 chunk_size_bytes=DEFAULT_CHUNK_SIZE,
                 write_concern=None,
                 read_preference=None,
                 collection=None):
        """Create a handle to a GridFS bucket.

        Raises :exc:`~pymongo.errors.ConfigurationError` if `write_concern`
        is not acknowledged.

        This class conforms to the `GridFS API Spec
        <https://github.com/mongodb/specifications/blob/master/source/gridfs/gridfs-spec.rst>`_
        for MongoDB drivers.

        :Parameters:
          - `database`: database to use.
          - `bucket_name` (optional): The name of the bucket. Defaults to 'fs'.
          - `chunk_size_bytes` (optional): The chunk size in bytes. Defaults
            to 255KB.
          - `write_concern` (optional): The
            :class:`~pymongo.write_concern.WriteConcern` to use. If ``None``
            (the default) db.write_concern is used.
          - `read_preference` (optional): The read preference to use. If
            ``None`` (the default) db.read_preference is used.
          - `disable_md5` (optional): When True, MD5 checksums will not be
            computed for uploaded files. Useful in environments where MD5
            cannot be used for regulatory or other reasons. Defaults to False.
          - `collection` (optional): Deprecated, an alias for `bucket_name`
            that exists solely to provide backwards compatibility.

        .. versionchanged:: 2.1
           Added support for the `bucket_name`, `chunk_size_bytes`,
           `write_concern`, and `read_preference` parameters.
           Deprecated the `collection` parameter which is now an alias to
           `bucket_name` (to match the GridFSBucket class in PyMongo).
        .. versionadded:: 1.0

        .. mongodoc:: gridfs
        """
        # Preserve backwards compatibility of "collection" parameter
        if collection is not None:
            warnings.warn(
                'the "collection" parameter is deprecated, use '
                '"bucket_name" instead',
                DeprecationWarning,
                stacklevel=2)
            bucket_name = collection

        db_class = create_class_with_framework(AgnosticDatabase,
                                               self._framework,
                                               self.__module__)

        if not isinstance(database, db_class):
            raise TypeError(
                "First argument to %s must be  MotorDatabase, not %r" %
                (self.__class__, database))

        self.io_loop = database.get_io_loop()
        self.collection = database.get_collection(
            bucket_name,
            write_concern=write_concern,
            read_preference=read_preference)
        self.delegate = self.__delegate_class__(
            database.delegate,
            bucket_name,
            chunk_size_bytes=chunk_size_bytes,
            write_concern=write_concern,
            read_preference=read_preference,
            disable_md5=disable_md5)

    def get_io_loop(self):
        return self.io_loop

    def wrap(self, obj):
        if obj.__class__ is grid_file.GridIn:
            grid_in_class = create_class_with_framework(
                AgnosticGridIn, self._framework, self.__module__)

            return grid_in_class(root_collection=self.collection, delegate=obj)

        elif obj.__class__ is grid_file.GridOut:
            grid_out_class = create_class_with_framework(
                AgnosticGridOut, self._framework, self.__module__)

            return grid_out_class(root_collection=self.collection,
                                  delegate=obj)

        elif obj.__class__ is gridfs.GridOutCursor:
            grid_out_class = create_class_with_framework(
                AgnosticGridOutCursor, self._framework, self.__module__)

            return grid_out_class(cursor=obj, collection=self.collection)

    def find(self, *args, **kwargs):
        """Find and return the files collection documents that match ``filter``.

        Returns a cursor that iterates across files matching
        arbitrary queries on the files collection. Can be combined
        with other modifiers for additional control.

        For example::

          cursor = bucket.find({"filename": "lisa.txt"}, no_cursor_timeout=True)
          while (yield cursor.fetch_next):
              grid_out = cursor.next_object()
              data = yield grid_out.read()

        This iterates through all versions of "lisa.txt" stored in GridFS.
        Note that setting no_cursor_timeout to True may be important to
        prevent the cursor from timing out during long multi-file processing
        work.

        As another example, the call::

          most_recent_three = fs.find().sort("uploadDate", -1).limit(3)

        would return a cursor to the three most recently uploaded files
        in GridFS.

        Follows a similar interface to
        :meth:`~motor.MotorCollection.find`
        in :class:`~motor.MotorCollection`.

        :Parameters:
          - `filter`: Search query.
          - `batch_size` (optional): The number of documents to return per
            batch.
          - `limit` (optional): The maximum number of documents to return.
          - `no_cursor_timeout` (optional): The server normally times out idle
            cursors after an inactivity period (10 minutes) to prevent excess
            memory use. Set this option to True prevent that.
          - `skip` (optional): The number of documents to skip before
            returning.
          - `sort` (optional): The order by which to sort results. Defaults to
            None.
          - `session` (optional): a
            :class:`~pymongo.client_session.ClientSession`, created with
            :meth:`~MotorClient.start_session`.

        If a :class:`~pymongo.client_session.ClientSession` is passed to
        :meth:`find`, all returned :class:`MotorGridOut` instances
        are associated with that session.

        .. versionchanged:: 1.2
           Added session parameter.
        """
        cursor = self.delegate.find(*args, **kwargs)
        grid_out_cursor = create_class_with_framework(AgnosticGridOutCursor,
                                                      self._framework,
                                                      self.__module__)

        return grid_out_cursor(cursor, self.collection)
Ejemplo n.º 11
0
class AgnosticGridOut(object):
    """Class to read data out of GridFS.

    MotorGridOut supports the same attributes as PyMongo's
    :class:`~gridfs.grid_file.GridOut`, such as ``_id``, ``content_type``,
    etc.

    You don't need to instantiate this class directly - use the
    methods provided by :class:`~motor.MotorGridFS`. If it **is**
    instantiated directly, call :meth:`open`, :meth:`read`, or
    :meth:`readline` before accessing its attributes.
    """
    __motor_class_name__ = 'MotorGridOut'
    __delegate_class__ = gridfs.GridOut

    tell         = DelegateMethod()
    seek         = DelegateMethod()
    read         = AsyncRead()
    readchunk    = AsyncRead()
    readline     = AsyncRead()
    _ensure_file = AsyncCommand()

    def __init__(
        self,
        root_collection,
        file_id=None,
        file_document=None,
        delegate=None,
    ):
        collection_class = create_class_with_framework(
            AgnosticCollection, self._framework)

        if not isinstance(root_collection, collection_class):
            raise TypeError(
                "First argument to MotorGridOut must be "
                "MotorCollection, not %r" % root_collection)

        if delegate:
            self.delegate = delegate
        else:
            self.delegate = self.__delegate_class__(
                root_collection.delegate,
                file_id,
                file_document,
                _connect=False)

        self.io_loop = root_collection.get_io_loop()

    def __getattr__(self, item):
        if not self.delegate._file:
            raise pymongo.errors.InvalidOperation(
                "You must call MotorGridOut.open() before accessing "
                "the %s property" % item)

        return getattr(self.delegate, item)

    @motor_coroutine 
    def open(self):
        """Retrieve this file's attributes from the server.

        Takes an optional callback, or returns a Future.

        :Parameters:
         - `callback`: Optional function taking parameters (self, error)

        .. versionchanged:: 0.2
           :class:`~motor.MotorGridOut` now opens itself on demand, calling
           ``open`` explicitly is rarely needed.
        """
        yield self._framework.yieldable(self._ensure_file())
        raise self._framework.return_value(self)

    def get_io_loop(self):
        return self.io_loop

    @motor_coroutine 
    def stream_to_handler(self, request_handler):
        """Write the contents of this file to a
        :class:`tornado.web.RequestHandler`. This method calls `flush` on
        the RequestHandler, so ensure all headers have already been set.
        For a more complete example see the implementation of
        :class:`~motor.web.GridFSHandler`.

        Takes an optional callback, or returns a Future.

        :Parameters:
         - `callback`: Optional function taking parameters (self, error)

        .. code-block:: python

            class FileHandler(tornado.web.RequestHandler):
                @tornado.web.asynchronous
                @gen.coroutine
                def get(self, filename):
                    db = self.settings['db']
                    fs = yield motor.MotorGridFS(db()).open()
                    try:
                        gridout = yield fs.get_last_version(filename)
                    except gridfs.NoFile:
                        raise tornado.web.HTTPError(404)

                    self.set_header("Content-Type", gridout.content_type)
                    self.set_header("Content-Length", gridout.length)
                    yield gridout.stream_to_handler(self)
                    self.finish()

        .. seealso:: Tornado `RequestHandler <http://tornadoweb.org/en/stable/web.html#request-handlers>`_
        """
        written = 0
        while written < self.length:
            # Reading chunk_size at a time minimizes buffering.
            chunk = yield self._framework.yieldable(self.read(self.chunk_size))

            # write() simply appends the output to a list; flush() sends it
            # over the network and minimizes buffering in the handler.
            request_handler.write(chunk)
            request_handler.flush()
            written += len(chunk)