|
10 | 10 | import re
|
11 | 11 | import sys
|
12 | 12 | import traceback
|
| 13 | +import types |
| 14 | +import warnings |
13 | 15 | try:
|
14 | 16 | # py3
|
15 | 17 | from http.client import responses
|
@@ -450,6 +452,34 @@ def prepare(self):
|
450 | 452 | raise web.HTTPError(404)
|
451 | 453 | return super(APIHandler, self).prepare()
|
452 | 454 |
|
| 455 | + def write_error(self, status_code, **kwargs): |
| 456 | + """APIHandler errors are JSON, not human pages""" |
| 457 | + self.set_header('Content-Type', 'application/json') |
| 458 | + message = responses.get(status_code, 'Unknown HTTP Error') |
| 459 | + reply = { |
| 460 | + 'message': message, |
| 461 | + } |
| 462 | + exc_info = kwargs.get('exc_info') |
| 463 | + if exc_info: |
| 464 | + e = exc_info[1] |
| 465 | + if isinstance(e, HTTPError): |
| 466 | + reply['message'] = e.log_message or message |
| 467 | + else: |
| 468 | + reply['message'] = 'Unhandled error' |
| 469 | + reply['traceback'] = ''.join(traceback.format_exception(*exc_info)) |
| 470 | + self.log.warning(reply['message']) |
| 471 | + self.finish(json.dumps(reply)) |
| 472 | + |
| 473 | + def get_current_user(self): |
| 474 | + """Raise 403 on API handlers instead of redirecting to human login page""" |
| 475 | + # preserve _user_cache so we don't raise more than once |
| 476 | + if hasattr(self, '_user_cache'): |
| 477 | + return self._user_cache |
| 478 | + self._user_cache = user = super(APIHandler, self).get_current_user() |
| 479 | + if user is None: |
| 480 | + raise web.HTTPError(403) |
| 481 | + return user |
| 482 | + |
453 | 483 | @property
|
454 | 484 | def content_security_policy(self):
|
455 | 485 | csp = '; '.join([
|
@@ -547,32 +577,14 @@ def json_errors(method):
|
547 | 577 | 2. Create and return a JSON body with a message field describing
|
548 | 578 | the error in a human readable form.
|
549 | 579 | """
|
| 580 | + warnings.warn('@json_errors is deprecated in notebook 5.2.0. Subclass APIHandler instead.', |
| 581 | + DeprecationWarning, |
| 582 | + stacklevel=2, |
| 583 | + ) |
550 | 584 | @functools.wraps(method)
|
551 |
| - @gen.coroutine |
552 | 585 | def wrapper(self, *args, **kwargs):
|
553 |
| - try: |
554 |
| - result = yield gen.maybe_future(method(self, *args, **kwargs)) |
555 |
| - except web.HTTPError as e: |
556 |
| - self.set_header('Content-Type', 'application/json') |
557 |
| - status = e.status_code |
558 |
| - message = e.log_message |
559 |
| - self.log.warning(message) |
560 |
| - self.set_status(e.status_code) |
561 |
| - reply = dict(message=message, reason=e.reason) |
562 |
| - self.finish(json.dumps(reply)) |
563 |
| - except Exception: |
564 |
| - self.set_header('Content-Type', 'application/json') |
565 |
| - self.log.error("Unhandled error in API request", exc_info=True) |
566 |
| - status = 500 |
567 |
| - message = "Unknown server error" |
568 |
| - t, value, tb = sys.exc_info() |
569 |
| - self.set_status(status) |
570 |
| - tb_text = ''.join(traceback.format_exception(t, value, tb)) |
571 |
| - reply = dict(message=message, reason=None, traceback=tb_text) |
572 |
| - self.finish(json.dumps(reply)) |
573 |
| - else: |
574 |
| - # FIXME: can use regular return in generators in py3 |
575 |
| - raise gen.Return(result) |
| 586 | + self.write_error = types.MethodType(APIHandler.write_error, self) |
| 587 | + return method(self, *args, **kwargs) |
576 | 588 | return wrapper
|
577 | 589 |
|
578 | 590 |
|
@@ -643,7 +655,6 @@ def validate_absolute_path(self, root, absolute_path):
|
643 | 655 |
|
644 | 656 | class APIVersionHandler(APIHandler):
|
645 | 657 |
|
646 |
| - @json_errors |
647 | 658 | def get(self):
|
648 | 659 | # not authenticated, so give as few info as possible
|
649 | 660 | self.finish(json.dumps({"version":notebook.__version__}))
|
|
0 commit comments