1"""
2Support for downloading objects from the object service, following best
3practices for that service.
4
5Downloaded data is written to a "writer" provided by a "writer factory".  A
6writer has a `write` method which writes the entire passed buffer to storage.
7A writer factory is a callable which returns a fresh writer, ready to write the
8first byte of the object.  When downloads are retried, the writer factory may
9be called more than once.
10
11This module provides several pre-defined writers and writer factories for
12common cases.
13"""
14import functools
15import six
16
17if six.PY2:
18    raise ImportError("download is only supported in Python 3")
19
20from .aio import download as aio_download
21from .aio.asyncutils import ensureCoro, runAsync
22
23
24def downloadToBuf(**kwargs):
25    """
26    Convenience method to download data to an in-memory buffer and return the
27    downloaded data.  Arguments are the same as `download`, except that
28    `writerFactory` should not be supplied.  Returns a tuple (buffer, contentType).
29    """
30    return runAsync(aio_download.downloadToBuf(**kwargs))
31
32
33def downloadToFile(file, **kwargs):
34    """
35    Convenience method to download data to a file object.  The file must be
36    writeable, in binary mode, seekable (`f.seek`), and truncatable
37    (`f.truncate`) to support retries.  Arguments are the same as `download`,
38    except that `writerFactory` should not be supplied.  Returns the content-type.
39    """
40    return runAsync(aio_download.downloadToFile(file=file, **kwargs))
41
42
43def download(*, writerFactory, **kwargs):
44    """
45    Download the named object from the object service, using a writer returned
46    from `writerFactory` to write the data.  The `maxRetries` parameter has
47    the same meaning as for service clients.  The `objectService` parameter is
48    an instance of the Object class, configured with credentials for the
49    upload.  Returns the content-type.
50    """
51    wrappedWriterFactory = _wrapSyncWriterFactory(writerFactory)
52    return runAsync(aio_download.download(writerFactory=wrappedWriterFactory, **kwargs))
53
54
55def downloadArtifactToBuf(**kwargs):
56    """
57    Convenience method to download an artifact to an in-memory buffer and return the
58    downloaded data.  Arguments are the same as `downloadArtifact`, except that
59    `writerFactory` should not be supplied.  Returns a tuple (buffer, contentType).
60    """
61    return runAsync(aio_download.downloadArtifactToBuf(**kwargs))
62
63
64def downloadArtifactToFile(file, **kwargs):
65    """
66    Convenience method to download an artifact to a file object.  The file must be
67    writeable, in binary mode, seekable (`f.seek`), and truncatable
68    (`f.truncate`) to support retries.  Arguments are the same as `downloadArtifac`,
69    except that `writerFactory` should not be supplied.  Returns the content-type.
70    """
71    return runAsync(aio_download.downloadArtifactToFile(file=file, **kwargs))
72
73
74def downloadArtifact(*, writerFactory, **kwargs):
75    """
76    Download the named artifact with the appropriate storageType, using a writer returned
77    from `writerFactory` to write the data.  The `maxRetries` parameter has
78    the same meaning as for service clients.  The `queueService` parameter is
79    an instance of the Queue class, configured with credentials for the
80    download.  Returns the content-type.
81    """
82    wrappedWriterFactory = _wrapSyncWriterFactory(writerFactory)
83    return runAsync(aio_download.downloadArtifact(writerFactory=wrappedWriterFactory, **kwargs))
84
85
86def _wrapSyncWriterFactory(writerFactory):
87    """Modify the reader returned by readerFactory to have an async read."""
88    @functools.wraps(writerFactory)
89    async def wrappedFactory():
90        writer = writerFactory()
91        writer.write = ensureCoro(writer.write)
92        return writer
93
94    return wrappedFactory
95