Skip to content

Commit 20b87ab

Browse files
committed
Merge pull request #743 from johnnoone/pathlib
Handle paths with pathlib
2 parents 613181e + 8d65873 commit 20b87ab

File tree

3 files changed

+30
-16
lines changed

3 files changed

+30
-16
lines changed

aiohttp/helpers.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import re
1010
from urllib.parse import quote, urlencode
1111
from collections import namedtuple
12+
from pathlib import Path
1213

1314
from . import hdrs, multidict
1415
from .errors import InvalidURL
@@ -204,7 +205,7 @@ def str_to_bytes(s, encoding='utf-8'):
204205
def guess_filename(obj, default=None):
205206
name = getattr(obj, 'name', None)
206207
if name and name[0] != '<' and name[-1] != '>':
207-
return os.path.split(name)[-1]
208+
return Path(name).name
208209
return default
209210

210211

aiohttp/multipart.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import zlib
1212
from urllib.parse import quote, unquote, urlencode, parse_qsl
1313
from collections import deque, Mapping, Sequence
14+
from pathlib import Path
1415

1516
from .helpers import parse_mimetype
1617
from .multidict import CIMultiDict
@@ -652,7 +653,7 @@ def _guess_filename(self, obj):
652653
if isinstance(obj, io.IOBase):
653654
name = getattr(obj, 'name', None)
654655
if name is not None:
655-
return os.path.basename(name)
656+
return Path(name).name
656657

657658
def serialize(self):
658659
"""Yields byte chunks for body part."""

aiohttp/web_urldispatcher.py

+26-14
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import inspect
1111

1212
from collections.abc import Sized, Iterable, Container
13+
from pathlib import Path
1314
from urllib.parse import urlencode, unquote
1415
from types import MappingProxyType
1516

@@ -159,14 +160,17 @@ def __init__(self, name, prefix, directory, *,
159160
'GET', self.handle, name, expect_handler=expect_handler)
160161
self._prefix = prefix
161162
self._prefix_len = len(self._prefix)
162-
self._directory = os.path.abspath(directory) + os.sep
163+
try:
164+
directory = Path(directory).resolve()
165+
if not directory.is_dir():
166+
raise ValueError('Not a directory')
167+
except (FileNotFoundError, ValueError) as error:
168+
raise ValueError(
169+
"No directory exists at '{}'".format(directory)) from error
170+
self._directory = directory
163171
self._chunk_size = chunk_size
164172
self._response_factory = response_factory
165173

166-
if not os.path.isdir(self._directory):
167-
raise ValueError(
168-
"No directory exists at '{}'".format(self._directory))
169-
170174
if bool(os.environ.get("AIOHTTP_NOSENDFILE")):
171175
self._sendfile = self._sendfile_fallback
172176

@@ -176,6 +180,8 @@ def match(self, path):
176180
return {'filename': path[self._prefix_len:]}
177181

178182
def url(self, *, filename, query=None):
183+
if isinstance(filename, Path):
184+
filename = str(getattr(filename, 'path', filename))
179185
while filename.startswith('/'):
180186
filename = filename[1:]
181187
url = self._prefix + filename
@@ -266,19 +272,25 @@ def _sendfile_fallback(self, req, resp, fobj, count):
266272
@asyncio.coroutine
267273
def handle(self, request):
268274
filename = request.match_info['filename']
269-
filepath = os.path.abspath(os.path.join(self._directory, filename))
270-
if not filepath.startswith(self._directory):
271-
raise HTTPNotFound()
272-
if not os.path.exists(filepath) or not os.path.isfile(filepath):
273-
raise HTTPNotFound()
274-
275-
st = os.stat(filepath)
275+
try:
276+
filepath = self._directory.joinpath(filename).resolve()
277+
filepath.relative_to(self._directory)
278+
except (ValueError, FileNotFoundError) as error:
279+
# relatively safe
280+
raise HTTPNotFound() from error
281+
except Exception as error:
282+
# perm error or other kind!
283+
request.logger.exception(error)
284+
raise HTTPNotFound() from error
285+
286+
st = filepath.stat()
276287

277288
modsince = request.if_modified_since
278289
if modsince is not None and st.st_mtime <= modsince.timestamp():
279290
raise HTTPNotModified()
280291

281-
ct, encoding = mimetypes.guess_type(filepath)
292+
path = str(getattr(filepath, 'path', filename))
293+
ct, encoding = mimetypes.guess_type(path)
282294
if not ct:
283295
ct = 'application/octet-stream'
284296

@@ -295,7 +307,7 @@ def handle(self, request):
295307
try:
296308
yield from resp.prepare(request)
297309

298-
with open(filepath, 'rb') as f:
310+
with filepath.open('rb') as f:
299311
yield from self._sendfile(request, resp, f, file_size)
300312

301313
finally:

0 commit comments

Comments
 (0)