refactor: consistent exception naming (#2420)

Co-authored-by: Adam Hopkins <adam@amhopkins.com>
This commit is contained in:
Néstor Pérez 2022-05-12 19:39:35 +02:00 committed by GitHub
parent 2bfa65e0de
commit 86ae5f981c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 82 additions and 57 deletions

View File

@ -58,7 +58,7 @@ from sanic.blueprints import Blueprint
from sanic.compat import OS_IS_WINDOWS, enable_windows_color_support from sanic.compat import OS_IS_WINDOWS, enable_windows_color_support
from sanic.config import SANIC_PREFIX, Config from sanic.config import SANIC_PREFIX, Config
from sanic.exceptions import ( from sanic.exceptions import (
InvalidUsage, BadRequest,
SanicException, SanicException,
ServerError, ServerError,
URLBuildError, URLBuildError,
@ -281,7 +281,7 @@ class Sanic(BaseSanic, RunnerMixin, metaclass=TouchUpMeta):
valid = ", ".join( valid = ", ".join(
map(lambda x: x.lower(), ListenerEvent.__members__.keys()) map(lambda x: x.lower(), ListenerEvent.__members__.keys())
) )
raise InvalidUsage(f"Invalid event: {event}. Use one of: {valid}") raise BadRequest(f"Invalid event: {event}. Use one of: {valid}")
if "." in _event: if "." in _event:
self.signal(_event.value)( self.signal(_event.value)(

View File

@ -19,7 +19,7 @@ import typing as t
from functools import partial from functools import partial
from traceback import extract_tb from traceback import extract_tb
from sanic.exceptions import InvalidUsage, SanicException from sanic.exceptions import BadRequest, SanicException
from sanic.helpers import STATUS_CODES from sanic.helpers import STATUS_CODES
from sanic.request import Request from sanic.request import Request
from sanic.response import HTTPResponse, html, json, text from sanic.response import HTTPResponse, html, json, text
@ -506,7 +506,7 @@ def exception_response(
# $ curl localhost:8000 -d '{"foo": "bar"}' # $ curl localhost:8000 -d '{"foo": "bar"}'
# And provide them with JSONRenderer # And provide them with JSONRenderer
renderer = JSONRenderer if request.json else base renderer = JSONRenderer if request.json else base
except InvalidUsage: except BadRequest:
renderer = base renderer = base
else: else:
renderer = RENDERERS_BY_CONFIG.get(render_format, renderer) renderer = RENDERERS_BY_CONFIG.get(render_format, renderer)

View File

@ -42,7 +42,7 @@ class NotFound(SanicException):
quiet = True quiet = True
class InvalidUsage(SanicException): class BadRequest(SanicException):
""" """
**Status**: 400 Bad Request **Status**: 400 Bad Request
""" """
@ -51,11 +51,14 @@ class InvalidUsage(SanicException):
quiet = True quiet = True
class BadURL(InvalidUsage): InvalidUsage = BadRequest
class BadURL(BadRequest):
... ...
class MethodNotSupported(SanicException): class MethodNotAllowed(SanicException):
""" """
**Status**: 405 Method Not Allowed **Status**: 405 Method Not Allowed
""" """
@ -68,6 +71,9 @@ class MethodNotSupported(SanicException):
self.headers = {"Allow": ", ".join(allowed_methods)} self.headers = {"Allow": ", ".join(allowed_methods)}
MethodNotSupported = MethodNotAllowed
class ServerError(SanicException): class ServerError(SanicException):
""" """
**Status**: 500 Internal Server Error **Status**: 500 Internal Server Error
@ -129,19 +135,19 @@ class PayloadTooLarge(SanicException):
quiet = True quiet = True
class HeaderNotFound(InvalidUsage): class HeaderNotFound(BadRequest):
""" """
**Status**: 400 Bad Request **Status**: 400 Bad Request
""" """
class InvalidHeader(InvalidUsage): class InvalidHeader(BadRequest):
""" """
**Status**: 400 Bad Request **Status**: 400 Bad Request
""" """
class ContentRangeError(SanicException): class RangeNotSatisfiable(SanicException):
""" """
**Status**: 416 Range Not Satisfiable **Status**: 416 Range Not Satisfiable
""" """
@ -154,7 +160,10 @@ class ContentRangeError(SanicException):
self.headers = {"Content-Range": f"bytes */{content_range.total}"} self.headers = {"Content-Range": f"bytes */{content_range.total}"}
class HeaderExpectationFailed(SanicException): ContentRangeError = RangeNotSatisfiable
class ExpectationFailed(SanicException):
""" """
**Status**: 417 Expectation Failed **Status**: 417 Expectation Failed
""" """
@ -163,6 +172,9 @@ class HeaderExpectationFailed(SanicException):
quiet = True quiet = True
HeaderExpectationFailed = ExpectationFailed
class Forbidden(SanicException): class Forbidden(SanicException):
""" """
**Status**: 403 Forbidden **Status**: 403 Forbidden
@ -172,7 +184,7 @@ class Forbidden(SanicException):
quiet = True quiet = True
class InvalidRangeType(ContentRangeError): class InvalidRangeType(RangeNotSatisfiable):
""" """
**Status**: 416 Range Not Satisfiable **Status**: 416 Range Not Satisfiable
""" """

View File

@ -10,9 +10,9 @@ from sanic.errorpages import (
exception_response, exception_response,
) )
from sanic.exceptions import ( from sanic.exceptions import (
ContentRangeError,
HeaderNotFound, HeaderNotFound,
InvalidRangeType, InvalidRangeType,
RangeNotSatisfiable,
SanicException, SanicException,
) )
from sanic.helpers import Default, _default from sanic.helpers import Default, _default
@ -296,18 +296,18 @@ class ContentRangeHandler:
try: try:
self.start = int(start_b) if start_b else None self.start = int(start_b) if start_b else None
except ValueError: except ValueError:
raise ContentRangeError( raise RangeNotSatisfiable(
"'%s' is invalid for Content Range" % (start_b,), self "'%s' is invalid for Content Range" % (start_b,), self
) )
try: try:
self.end = int(end_b) if end_b else None self.end = int(end_b) if end_b else None
except ValueError: except ValueError:
raise ContentRangeError( raise RangeNotSatisfiable(
"'%s' is invalid for Content Range" % (end_b,), self "'%s' is invalid for Content Range" % (end_b,), self
) )
if self.end is None: if self.end is None:
if self.start is None: if self.start is None:
raise ContentRangeError( raise RangeNotSatisfiable(
"Invalid for Content Range parameters", self "Invalid for Content Range parameters", self
) )
else: else:
@ -319,7 +319,7 @@ class ContentRangeHandler:
self.start = self.total - self.end self.start = self.total - self.end
self.end = self.total - 1 self.end = self.total - 1
if self.start >= self.end: if self.start >= self.end:
raise ContentRangeError( raise RangeNotSatisfiable(
"Invalid for Content Range parameters", self "Invalid for Content Range parameters", self
) )
self.size = self.end - self.start + 1 self.size = self.end - self.start + 1

View File

@ -12,8 +12,8 @@ from enum import Enum
from sanic.compat import Header from sanic.compat import Header
from sanic.exceptions import ( from sanic.exceptions import (
HeaderExpectationFailed, BadRequest,
InvalidUsage, ExpectationFailed,
PayloadTooLarge, PayloadTooLarge,
ServerError, ServerError,
ServiceUnavailable, ServiceUnavailable,
@ -53,14 +53,14 @@ class Http(metaclass=TouchUpMeta):
:raises ServerError: :raises ServerError:
:raises PayloadTooLarge: :raises PayloadTooLarge:
:raises Exception: :raises Exception:
:raises InvalidUsage: :raises BadRequest:
:raises HeaderExpectationFailed: :raises ExpectationFailed:
:raises RuntimeError: :raises RuntimeError:
:raises ServerError: :raises ServerError:
:raises ServerError: :raises ServerError:
:raises InvalidUsage: :raises BadRequest:
:raises InvalidUsage: :raises BadRequest:
:raises InvalidUsage: :raises BadRequest:
:raises PayloadTooLarge: :raises PayloadTooLarge:
:raises RuntimeError: :raises RuntimeError:
""" """
@ -248,7 +248,7 @@ class Http(metaclass=TouchUpMeta):
headers.append(h) headers.append(h)
except Exception: except Exception:
raise InvalidUsage("Bad Request") raise BadRequest("Bad Request")
headers_instance = Header(headers) headers_instance = Header(headers)
self.upgrade_websocket = ( self.upgrade_websocket = (
@ -281,7 +281,7 @@ class Http(metaclass=TouchUpMeta):
if expect.lower() == "100-continue": if expect.lower() == "100-continue":
self.expecting_continue = True self.expecting_continue = True
else: else:
raise HeaderExpectationFailed(f"Unknown Expect: {expect}") raise ExpectationFailed(f"Unknown Expect: {expect}")
if headers.getone("transfer-encoding", None) == "chunked": if headers.getone("transfer-encoding", None) == "chunked":
self.request_body = "chunked" self.request_body = "chunked"
@ -510,7 +510,7 @@ class Http(metaclass=TouchUpMeta):
if len(buf) > 64: if len(buf) > 64:
self.keep_alive = False self.keep_alive = False
raise InvalidUsage("Bad chunked encoding") raise BadRequest("Bad chunked encoding")
await self._receive_more() await self._receive_more()
@ -518,14 +518,14 @@ class Http(metaclass=TouchUpMeta):
size = int(buf[2:pos].split(b";", 1)[0].decode(), 16) size = int(buf[2:pos].split(b";", 1)[0].decode(), 16)
except Exception: except Exception:
self.keep_alive = False self.keep_alive = False
raise InvalidUsage("Bad chunked encoding") raise BadRequest("Bad chunked encoding")
if size <= 0: if size <= 0:
self.request_body = None self.request_body = None
if size < 0: if size < 0:
self.keep_alive = False self.keep_alive = False
raise InvalidUsage("Bad chunked encoding") raise BadRequest("Bad chunked encoding")
# Consume CRLF, chunk size 0 and the two CRLF that follow # Consume CRLF, chunk size 0 and the two CRLF that follow
pos += 4 pos += 4

View File

@ -3,7 +3,7 @@ from functools import partial
from typing import Callable, List, Optional, Union, overload from typing import Callable, List, Optional, Union, overload
from sanic.base.meta import SanicMeta from sanic.base.meta import SanicMeta
from sanic.exceptions import InvalidUsage from sanic.exceptions import BadRequest
from sanic.models.futures import FutureListener from sanic.models.futures import FutureListener
from sanic.models.handler_types import ListenerType, Sanic from sanic.models.handler_types import ListenerType, Sanic
@ -86,7 +86,7 @@ class ListenerMixin(metaclass=SanicMeta):
if callable(listener_or_event): if callable(listener_or_event):
if event_or_none is None: if event_or_none is None:
raise InvalidUsage( raise BadRequest(
"Invalid event registration: Missing event name." "Invalid event registration: Missing event name."
) )
return register_listener(listener_or_event, event_or_none) return register_listener(listener_or_event, event_or_none)

View File

@ -18,10 +18,10 @@ from sanic.compat import stat_async
from sanic.constants import DEFAULT_HTTP_CONTENT_TYPE, HTTP_METHODS from sanic.constants import DEFAULT_HTTP_CONTENT_TYPE, HTTP_METHODS
from sanic.errorpages import RESPONSE_MAPPING from sanic.errorpages import RESPONSE_MAPPING
from sanic.exceptions import ( from sanic.exceptions import (
ContentRangeError, BadRequest,
FileNotFound, FileNotFound,
HeaderNotFound, HeaderNotFound,
InvalidUsage, RangeNotSatisfiable,
) )
from sanic.handlers import ContentRangeHandler from sanic.handlers import ContentRangeHandler
from sanic.log import deprecation, error_logger from sanic.log import deprecation, error_logger
@ -778,7 +778,7 @@ class RouteMixin(metaclass=SanicMeta):
# Using this to determine if the URL is trying to break out of the path # Using this to determine if the URL is trying to break out of the path
# served. os.path.realpath seems to be very slow # served. os.path.realpath seems to be very slow
if __file_uri__ and "../" in __file_uri__: if __file_uri__ and "../" in __file_uri__:
raise InvalidUsage("Invalid URL") raise BadRequest("Invalid URL")
# Merge served directory and requested file if provided # Merge served directory and requested file if provided
# Strip all / that in the beginning of the URL to help prevent python # Strip all / that in the beginning of the URL to help prevent python
# from herping a derp and treating the uri as an absolute path # from herping a derp and treating the uri as an absolute path
@ -865,7 +865,7 @@ class RouteMixin(metaclass=SanicMeta):
file_path, headers=headers, _range=_range file_path, headers=headers, _range=_range
) )
return await file(file_path, headers=headers, _range=_range) return await file(file_path, headers=headers, _range=_range)
except ContentRangeError: except RangeNotSatisfiable:
raise raise
except FileNotFoundError: except FileNotFoundError:
raise FileNotFound( raise FileNotFound(

View File

@ -3,7 +3,7 @@ import sys
from typing import Any, Awaitable, Callable, MutableMapping, Optional, Union from typing import Any, Awaitable, Callable, MutableMapping, Optional, Union
from sanic.exceptions import InvalidUsage from sanic.exceptions import BadRequest
from sanic.server.websockets.connection import WebSocketConnection from sanic.server.websockets.connection import WebSocketConnection
@ -84,7 +84,7 @@ class MockTransport: # no cov
try: try:
return self._websocket_connection return self._websocket_connection
except AttributeError: except AttributeError:
raise InvalidUsage("Improper websocket connection.") raise BadRequest("Improper websocket connection.")
def create_websocket_connection( def create_websocket_connection(
self, send: ASGISend, receive: ASGIReceive self, send: ASGISend, receive: ASGIReceive

View File

@ -35,7 +35,7 @@ from httptools.parser.errors import HttpParserInvalidURLError # type: ignore
from sanic.compat import CancelledErrors, Header from sanic.compat import CancelledErrors, Header
from sanic.constants import DEFAULT_HTTP_CONTENT_TYPE from sanic.constants import DEFAULT_HTTP_CONTENT_TYPE
from sanic.exceptions import BadURL, InvalidUsage, ServerError from sanic.exceptions import BadRequest, BadURL, ServerError
from sanic.headers import ( from sanic.headers import (
AcceptContainer, AcceptContainer,
Options, Options,
@ -379,7 +379,7 @@ class Request:
except Exception: except Exception:
if not self.body: if not self.body:
return None return None
raise InvalidUsage("Failed when parsing body as json") raise BadRequest("Failed when parsing body as json")
return self.parsed_json return self.parsed_json

View File

@ -14,7 +14,7 @@ from sanic_routing.route import Route # type: ignore
from sanic.constants import HTTP_METHODS from sanic.constants import HTTP_METHODS
from sanic.errorpages import check_error_format from sanic.errorpages import check_error_format
from sanic.exceptions import MethodNotSupported, NotFound, SanicException from sanic.exceptions import MethodNotAllowed, NotFound, SanicException
from sanic.models.handler_types import RouteHandler from sanic.models.handler_types import RouteHandler
@ -43,7 +43,7 @@ class Router(BaseRouter):
except RoutingNotFound as e: except RoutingNotFound as e:
raise NotFound("Requested URL {} not found".format(e.path)) raise NotFound("Requested URL {} not found".format(e.path))
except NoMethod as e: except NoMethod as e:
raise MethodNotSupported( raise MethodNotAllowed(
"Method {} not allowed for URL {}".format(method, path), "Method {} not allowed for URL {}".format(method, path),
method=method, method=method,
allowed_methods=e.allowed_methods, allowed_methods=e.allowed_methods,

View File

@ -9,7 +9,7 @@ import uvicorn
from sanic import Sanic from sanic import Sanic
from sanic.application.state import Mode from sanic.application.state import Mode
from sanic.asgi import MockTransport from sanic.asgi import MockTransport
from sanic.exceptions import Forbidden, InvalidUsage, ServiceUnavailable from sanic.exceptions import Forbidden, BadRequest, ServiceUnavailable
from sanic.request import Request from sanic.request import Request
from sanic.response import json, text from sanic.response import json, text
from sanic.server.websockets.connection import WebSocketConnection from sanic.server.websockets.connection import WebSocketConnection
@ -392,7 +392,7 @@ async def test_websocket_accept_with_multiple_subprotocols(
def test_improper_websocket_connection(transport, send, receive): def test_improper_websocket_connection(transport, send, receive):
with pytest.raises(InvalidUsage): with pytest.raises(BadRequest):
transport.get_websocket_connection() transport.get_websocket_connection()
transport.create_websocket_connection(send, receive) transport.create_websocket_connection(send, receive)

View File

@ -5,7 +5,7 @@ from sanic.blueprint_group import BlueprintGroup
from sanic.blueprints import Blueprint from sanic.blueprints import Blueprint
from sanic.exceptions import ( from sanic.exceptions import (
Forbidden, Forbidden,
InvalidUsage, BadRequest,
SanicException, SanicException,
ServerError, ServerError,
) )
@ -104,7 +104,7 @@ def test_bp_group(app: Sanic):
@blueprint_1.route("/invalid") @blueprint_1.route("/invalid")
def blueprint_1_error(request: Request): def blueprint_1_error(request: Request):
raise InvalidUsage("Invalid") raise BadRequest("Invalid")
@blueprint_2.route("/") @blueprint_2.route("/")
def blueprint_2_default_route(request): def blueprint_2_default_route(request):
@ -120,7 +120,7 @@ def test_bp_group(app: Sanic):
blueprint_3 = Blueprint("blueprint_3", url_prefix="/bp3") blueprint_3 = Blueprint("blueprint_3", url_prefix="/bp3")
@blueprint_group_1.exception(InvalidUsage) @blueprint_group_1.exception(BadRequest)
def handle_group_exception(request, exception): def handle_group_exception(request, exception):
return text("BP1_ERR_OK") return text("BP1_ERR_OK")

View File

@ -8,7 +8,7 @@ from sanic.app import Sanic
from sanic.blueprints import Blueprint from sanic.blueprints import Blueprint
from sanic.constants import HTTP_METHODS from sanic.constants import HTTP_METHODS
from sanic.exceptions import ( from sanic.exceptions import (
InvalidUsage, BadRequest,
NotFound, NotFound,
SanicException, SanicException,
ServerError, ServerError,
@ -448,7 +448,7 @@ def test_bp_exception_handler(app):
@blueprint.route("/1") @blueprint.route("/1")
def handler_1(request): def handler_1(request):
raise InvalidUsage("OK") raise BadRequest("OK")
@blueprint.route("/2") @blueprint.route("/2")
def handler_2(request): def handler_2(request):

View File

@ -6,9 +6,16 @@ from bs4 import BeautifulSoup
from sanic import Sanic from sanic import Sanic
from sanic.exceptions import ( from sanic.exceptions import (
BadRequest,
ContentRangeError,
ExpectationFailed,
Forbidden, Forbidden,
HeaderExpectationFailed,
InvalidUsage, InvalidUsage,
MethodNotAllowed,
MethodNotSupported,
NotFound, NotFound,
RangeNotSatisfiable,
SanicException, SanicException,
ServerError, ServerError,
Unauthorized, Unauthorized,
@ -77,7 +84,7 @@ def exception_app():
@app.route("/invalid") @app.route("/invalid")
def handler_invalid(request): def handler_invalid(request):
raise InvalidUsage("OK") raise BadRequest("OK")
@app.route("/abort/401") @app.route("/abort/401")
def handler_401_error(request): def handler_401_error(request):
@ -136,7 +143,7 @@ def test_server_error_exception(exception_app):
def test_invalid_usage_exception(exception_app): def test_invalid_usage_exception(exception_app):
"""Test the built-in InvalidUsage exception works""" """Test the built-in BadRequest exception works"""
request, response = exception_app.test_client.get("/invalid") request, response = exception_app.test_client.get("/invalid")
assert response.status == 400 assert response.status == 400
@ -375,3 +382,10 @@ def test_contextual_exception_functional_message(override):
assert response.status == 418 assert response.status == 418
assert response.json["message"] == error_message assert response.json["message"] == error_message
assert response.json["context"] == {"foo": "bar"} assert response.json["context"] == {"foo": "bar"}
def test_exception_aliases():
assert InvalidUsage is BadRequest
assert MethodNotSupported is MethodNotAllowed
assert ContentRangeError is RangeNotSatisfiable
assert HeaderExpectationFailed is ExpectationFailed

View File

@ -10,7 +10,7 @@ from bs4 import BeautifulSoup
from pytest import LogCaptureFixture, MonkeyPatch from pytest import LogCaptureFixture, MonkeyPatch
from sanic import Sanic, handlers from sanic import Sanic, handlers
from sanic.exceptions import Forbidden, InvalidUsage, NotFound, ServerError from sanic.exceptions import Forbidden, BadRequest, NotFound, ServerError
from sanic.handlers import ErrorHandler from sanic.handlers import ErrorHandler
from sanic.request import Request from sanic.request import Request
from sanic.response import stream, text from sanic.response import stream, text
@ -32,7 +32,7 @@ def exception_handler_app():
@exception_handler_app.route("/1", error_format="html") @exception_handler_app.route("/1", error_format="html")
def handler_1(request): def handler_1(request):
raise InvalidUsage("OK") raise BadRequest("OK")
@exception_handler_app.route("/2", error_format="html") @exception_handler_app.route("/2", error_format="html")
def handler_2(request): def handler_2(request):

View File

@ -8,7 +8,7 @@ import pytest
from sanic_testing.testing import HOST, PORT from sanic_testing.testing import HOST, PORT
from sanic.exceptions import InvalidUsage, SanicException from sanic.exceptions import BadRequest, SanicException
AVAILABLE_LISTENERS = [ AVAILABLE_LISTENERS = [
@ -137,7 +137,7 @@ async def test_trigger_before_events_create_server_missing_event(app):
class MySanicDb: class MySanicDb:
pass pass
with pytest.raises(InvalidUsage): with pytest.raises(BadRequest):
@app.listener @app.listener
async def init_db(app, loop): async def init_db(app, loop):

View File

@ -11,7 +11,7 @@ import pytest
from sanic_testing.testing import HOST, PORT from sanic_testing.testing import HOST, PORT
from sanic.compat import ctrlc_workaround_for_windows from sanic.compat import ctrlc_workaround_for_windows
from sanic.exceptions import InvalidUsage from sanic.exceptions import BadRequest
from sanic.response import HTTPResponse from sanic.response import HTTPResponse
@ -122,6 +122,6 @@ def test_signals_with_invalid_invocation(app):
return HTTPResponse() return HTTPResponse()
with pytest.raises( with pytest.raises(
InvalidUsage, match="Invalid event registration: Missing event name" BadRequest, match="Invalid event registration: Missing event name"
): ):
app.listener(stop) app.listener(stop)

View File

@ -2,7 +2,6 @@ import pytest
from sanic.blueprints import Blueprint from sanic.blueprints import Blueprint
from sanic.constants import HTTP_METHODS from sanic.constants import HTTP_METHODS
from sanic.exceptions import InvalidUsage
from sanic.request import Request from sanic.request import Request
from sanic.response import HTTPResponse, text from sanic.response import HTTPResponse, text
from sanic.views import HTTPMethodView from sanic.views import HTTPMethodView