Skip to content

Commit a20fe64

Browse files
authored
ContentsHandler return 404 rather than raise exc (#1357)
1 parent 9579862 commit a20fe64

File tree

2 files changed

+59
-34
lines changed

2 files changed

+59
-34
lines changed

jupyter_server/services/contents/handlers.py

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
# Copyright (c) Jupyter Development Team.
66
# Distributed under the terms of the Modified BSD License.
77
import json
8+
from http import HTTPStatus
89

910
try:
1011
from jupyter_client.jsonutil import json_default
@@ -91,6 +92,12 @@ def _finish_model(self, model, location=True):
9192
self.set_header("Content-Type", "application/json")
9293
self.finish(json.dumps(model, default=json_default))
9394

95+
async def _finish_error(self, code, message):
96+
"""Finish a JSON request with an error code and descriptive message"""
97+
self.set_status(code)
98+
self.write(message)
99+
await self.finish()
100+
94101
@web.authenticated
95102
@authorized
96103
async def get(self, path=""):
@@ -116,18 +123,27 @@ async def get(self, path=""):
116123
content = int(content_str or "")
117124

118125
if not cm.allow_hidden and await ensure_async(cm.is_hidden(path)):
119-
raise web.HTTPError(404, f"file or directory {path!r} does not exist")
120-
121-
model = await ensure_async(
122-
self.contents_manager.get(
123-
path=path,
124-
type=type,
125-
format=format,
126-
content=content,
126+
await self._finish_error(
127+
HTTPStatus.NOT_FOUND, f"file or directory {path!r} does not exist"
127128
)
128-
)
129-
validate_model(model, expect_content=content)
130-
self._finish_model(model, location=False)
129+
try:
130+
model = await ensure_async(
131+
self.contents_manager.get(
132+
path=path,
133+
type=type,
134+
format=format,
135+
content=content,
136+
)
137+
)
138+
validate_model(model, expect_content=content)
139+
self._finish_model(model, location=False)
140+
except web.HTTPError as exc:
141+
# 404 is okay in this context, catch exception and return 404 code to prevent stack trace on client
142+
if exc.status_code == HTTPStatus.NOT_FOUND:
143+
await self._finish_error(
144+
HTTPStatus.NOT_FOUND, f"file or directory {path!r} does not exist"
145+
)
146+
raise
131147

132148
@web.authenticated
133149
@authorized

tests/services/contents/test_manager.py

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -406,35 +406,44 @@ async def test_400(jp_file_contents_manager_class, tmp_path): # noqa
406406

407407

408408
async def test_404(jp_file_contents_manager_class, tmp_path):
409+
# setup
410+
td = str(tmp_path)
411+
cm = jp_file_contents_manager_class(root_dir=td)
412+
409413
# Test visible file in hidden folder
410-
with pytest.raises(HTTPError) as excinfo:
411-
td = str(tmp_path)
412-
cm = jp_file_contents_manager_class(root_dir=td)
413-
hidden_dir = ".hidden"
414-
file_in_hidden_path = os.path.join(hidden_dir, "visible.txt")
415-
_make_dir(cm, hidden_dir)
416-
model = await ensure_async(cm.new(path=file_in_hidden_path))
417-
os_path = cm._get_os_path(model["path"])
414+
cm.allow_hidden = True
415+
hidden_dir = ".hidden"
416+
file_in_hidden_path = os.path.join(hidden_dir, "visible.txt")
417+
_make_dir(cm, hidden_dir)
418+
model = await ensure_async(cm.new(path=file_in_hidden_path))
419+
os_path = cm._get_os_path(model["path"])
420+
cm.allow_hidden = False
418421

419-
try:
420-
result = await ensure_async(cm.get(os_path, "w"))
421-
except HTTPError as e:
422-
assert e.status_code == 404
422+
with pytest.raises(HTTPError) as excinfo:
423+
await ensure_async(cm.get(os_path))
424+
assert excinfo.value.status_code == 404
423425

424426
# Test hidden file in visible folder
427+
cm.allow_hidden = True
428+
hidden_dir = "visible"
429+
file_in_hidden_path = os.path.join(hidden_dir, ".hidden.txt")
430+
_make_dir(cm, hidden_dir)
431+
model = await ensure_async(cm.new(path=file_in_hidden_path))
432+
os_path = cm._get_os_path(model["path"])
433+
cm.allow_hidden = False
434+
425435
with pytest.raises(HTTPError) as excinfo:
426-
td = str(tmp_path)
427-
cm = jp_file_contents_manager_class(root_dir=td)
428-
hidden_dir = "visible"
429-
file_in_hidden_path = os.path.join(hidden_dir, ".hidden.txt")
430-
_make_dir(cm, hidden_dir)
431-
model = await ensure_async(cm.new(path=file_in_hidden_path))
432-
os_path = cm._get_os_path(model["path"])
436+
await ensure_async(cm.get(os_path))
437+
assert excinfo.value.status_code == 404
433438

434-
try:
435-
result = await ensure_async(cm.get(os_path, "w"))
436-
except HTTPError as e:
437-
assert e.status_code == 404
439+
# Test file not found
440+
td = str(tmp_path)
441+
cm = jp_file_contents_manager_class(root_dir=td)
442+
not_a_file = "foo.bar"
443+
444+
with pytest.raises(HTTPError) as excinfo:
445+
await ensure_async(cm.get(not_a_file))
446+
assert excinfo.value.status_code == 404
438447

439448

440449
async def test_escape_root(jp_file_contents_manager_class, tmp_path):

0 commit comments

Comments
 (0)