Skip to content
This repository was archived by the owner on Mar 18, 2019. It is now read-only.

Commit df52bb3

Browse files
committed
Tweak DownloadCodec tempfile behavior slightly
1 parent 55ad1ae commit df52bb3

File tree

3 files changed

+34
-16
lines changed

3 files changed

+34
-16
lines changed

coreapi/codecs/download.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55
import mimetypes
66
import os
77
import posixpath
8-
import shutil
98
import tempfile
109

1110

1211
class DownloadedFile(tempfile._TemporaryFileWrapper):
12+
basename = None
13+
1314
def __repr__(self):
1415
state = "closed" if self.close_called else "open"
1516
mode = "" if self.close_called else " '%s'" % self.file.mode
@@ -98,6 +99,8 @@ def _get_filename(base_url=None, content_type=None, content_disposition=None):
9899
filename = _get_filename_from_content_disposition(content_disposition)
99100
if base_url and not filename:
100101
filename = _get_filename_from_url(base_url, content_type)
102+
if not filename:
103+
return None # Ensure empty filenames return as `None` for consistency.
101104
return filename
102105

103106

@@ -111,17 +114,11 @@ def __init__(self, download_dir=None):
111114
"""
112115
`download_dir` - The path to use for file downloads.
113116
"""
114-
self._temporary = download_dir is None
117+
self._delete_on_close = download_dir is None
115118
self._download_dir = download_dir
116119

117-
def __del__(self):
118-
if self._temporary and self._download_dir:
119-
shutil.rmtree(self._download_dir)
120-
121120
@property
122121
def download_dir(self):
123-
if self._download_dir is None:
124-
self._download_dir = tempfile.mkdtemp(prefix='temp-coreapi-download-')
125122
return self._download_dir
126123

127124
def decode(self, bytestring, **options):
@@ -137,15 +134,21 @@ def decode(self, bytestring, **options):
137134

138135
# Determine the output filename.
139136
output_filename = _get_filename(base_url, content_type, content_disposition)
140-
if not output_filename:
141-
# Fallback if no output filename could be determined.
137+
if output_filename is None:
142138
output_filename = os.path.basename(temp_path)
143139

140+
# Determine the output directory.
141+
output_dir = self._download_dir
142+
if output_dir is None:
143+
output_dir = os.path.dirname(temp_path)
144+
144145
# Determine the full output path.
145-
output_path = os.path.join(self.download_dir, output_filename)
146+
output_path = os.path.join(output_dir, output_filename)
146147
output_path = _unique_output_path(output_path)
147148

148149
# Move the temporary download file to the final location.
149150
os.rename(temp_path, output_path)
150151
output_file = open(output_path, 'rb')
151-
return DownloadedFile(output_file, output_path, delete=self._temporary)
152+
downloaded = DownloadedFile(output_file, output_path, delete=self._delete_on_close)
153+
downloaded.basename = output_filename
154+
return downloaded

coreapi/transports/http.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ def _get_params(method, encoding, fields, params=None):
8888
if errors:
8989
raise exceptions.ValidationError(errors)
9090

91+
# Move any files from 'data' into 'files'.
9192
if isinstance(data, dict):
9293
for key, value in list(data.items()):
9394
if is_file(data[key]):

docs/api-guide/codecs.md

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,18 +129,31 @@ Example:
129129

130130
### DownloadCodec
131131

132-
Supports decoding arbitrary media as a download file. Returns a temporary file
132+
Supports decoding arbitrary media as a download file. Returns a [temporary file][tempfile]
133133
that will be deleted once it goes out of scope.
134134

135135
**.media_type**: `*/*`
136136

137137
Example:
138138

139-
>>> from coreapi import codecs
140139
>>> codec = codecs.DownloadCodec()
141-
>>> data = codec.decode(b'...')
142-
>>> print(data)
140+
>>> download = codec.decode(b'abc...xyz')
141+
>>> print(download)
143142
<DownloadedFile '.../tmpYbxNXT.download', open 'rb'>
143+
>>> content = download.read()
144+
>>> print(content)
145+
abc...xyz
146+
147+
The download filename will be determined by either the `Content-Disposition`
148+
header, or based on the request URL and the `Content-Type` header. Download
149+
collisions are avoided by using incrementing filenames where required.
150+
The original name used for the download file can be inspected using `.basename`.
151+
152+
>>> download = codec.decode(b'abc...xyz', content_type='image/png', base_url='http://example.com/download/')
153+
>>> download.name
154+
'/var/folders/2k/qjf3np5s28zf2f58963pz2k40000gn/T/download.png'
155+
>>> download.basename
156+
'download.png'
144157

145158
#### Instantiation
146159

@@ -233,6 +246,7 @@ A codec for the [HAL][hal] hypermedia format. Installable [from PyPI][hal-pypi]
233246

234247
[content-disposition-filename]: https://tools.ietf.org/html/draft-ietf-httpbis-content-disp-00#section-3.3
235248
[click-ansi]: http://click.pocoo.org/5/utils/#ansi-colors
249+
[tempfile]: https://docs.python.org/3/library/tempfile.html#tempfile.TemporaryFile
236250

237251
[openapi]: https://openapis.org/specification
238252
[openapi-pypi]: https://pypi.python.org/pypi/openapi-codec

0 commit comments

Comments
 (0)