Require stricter object names (#2146)

This commit is contained in:
Adam Hopkins 2021-05-30 15:37:44 +03:00 committed by GitHub
parent 72a745bfd5
commit ba374139f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 80 additions and 15 deletions

View File

@ -135,13 +135,8 @@ class Sanic(BaseSanic):
register: Optional[bool] = None, register: Optional[bool] = None,
dumps: Optional[Callable[..., str]] = None, dumps: Optional[Callable[..., str]] = None,
) -> None: ) -> None:
super().__init__() super().__init__(name=name)
if name is None:
raise SanicException(
"Sanic instance cannot be unnamed. "
"Please use Sanic(name='your_application_name') instead.",
)
# logging # logging
if configure_logging: if configure_logging:
logging.config.dictConfig(log_config or LOGGING_CONFIG_DEFAULTS) logging.config.dictConfig(log_config or LOGGING_CONFIG_DEFAULTS)
@ -161,7 +156,6 @@ class Sanic(BaseSanic):
self.is_running = False self.is_running = False
self.is_stopping = False self.is_stopping = False
self.listeners: Dict[str, List[ListenerType]] = defaultdict(list) self.listeners: Dict[str, List[ListenerType]] = defaultdict(list)
self.name = name
self.named_request_middleware: Dict[str, Deque[MiddlewareType]] = {} self.named_request_middleware: Dict[str, Deque[MiddlewareType]] = {}
self.named_response_middleware: Dict[str, Deque[MiddlewareType]] = {} self.named_response_middleware: Dict[str, Deque[MiddlewareType]] = {}
self.request_class = request_class self.request_class = request_class

View File

@ -1,6 +1,9 @@
import re
from typing import Any, Tuple from typing import Any, Tuple
from warnings import warn from warnings import warn
from sanic.exceptions import SanicException
from sanic.mixins.exceptions import ExceptionMixin from sanic.mixins.exceptions import ExceptionMixin
from sanic.mixins.listeners import ListenerMixin from sanic.mixins.listeners import ListenerMixin
from sanic.mixins.middleware import MiddlewareMixin from sanic.mixins.middleware import MiddlewareMixin
@ -8,6 +11,9 @@ from sanic.mixins.routes import RouteMixin
from sanic.mixins.signals import SignalMixin from sanic.mixins.signals import SignalMixin
VALID_NAME = re.compile(r"^[a-zA-Z][a-zA-Z0-9_\-]*$")
class BaseSanic( class BaseSanic(
RouteMixin, RouteMixin,
MiddlewareMixin, MiddlewareMixin,
@ -17,7 +23,25 @@ class BaseSanic(
): ):
__fake_slots__: Tuple[str, ...] __fake_slots__: Tuple[str, ...]
def __init__(self, *args, **kwargs) -> None: def __init__(self, name: str = None, *args, **kwargs) -> None:
class_name = self.__class__.__name__
if name is None:
raise SanicException(
f"{class_name} instance cannot be unnamed. "
"Please use Sanic(name='your_application_name') instead.",
)
if not VALID_NAME.match(name):
warn(
f"{class_name} instance named '{name}' uses a format that is"
f"deprecated. Starting in version 21.12, {class_name} objects "
"be named only using alphanumeric characters, _, or -.",
DeprecationWarning,
)
self.name = name
for base in BaseSanic.__bases__: for base in BaseSanic.__bases__:
base.__init__(self, *args, **kwargs) # type: ignore base.__init__(self, *args, **kwargs) # type: ignore
@ -36,6 +60,7 @@ class BaseSanic(
f"Setting variables on {self.__class__.__name__} instances is " f"Setting variables on {self.__class__.__name__} instances is "
"deprecated and will be removed in version 21.9. You should " "deprecated and will be removed in version 21.9. You should "
f"change your {self.__class__.__name__} instance to use " f"change your {self.__class__.__name__} instance to use "
f"instance.ctx.{name} instead." f"instance.ctx.{name} instead.",
DeprecationWarning,
) )
super().__setattr__(name, value) super().__setattr__(name, value)

View File

@ -62,19 +62,20 @@ class Blueprint(BaseSanic):
"strict_slashes", "strict_slashes",
"url_prefix", "url_prefix",
"version", "version",
"version_prefix",
"websocket_routes", "websocket_routes",
) )
def __init__( def __init__(
self, self,
name: str, name: str = None,
url_prefix: Optional[str] = None, url_prefix: Optional[str] = None,
host: Optional[str] = None, host: Optional[str] = None,
version: Optional[Union[int, str, float]] = None, version: Optional[Union[int, str, float]] = None,
strict_slashes: Optional[bool] = None, strict_slashes: Optional[bool] = None,
version_prefix: str = "/v", version_prefix: str = "/v",
): ):
super().__init__() super().__init__(name=name)
self._apps: Set[Sanic] = set() self._apps: Set[Sanic] = set()
self.ctx = SimpleNamespace() self.ctx = SimpleNamespace()
@ -82,7 +83,6 @@ class Blueprint(BaseSanic):
self.host = host self.host = host
self.listeners: Dict[str, List[ListenerType]] = {} self.listeners: Dict[str, List[ListenerType]] = {}
self.middlewares: List[MiddlewareType] = [] self.middlewares: List[MiddlewareType] = []
self.name = name
self.routes: List[Route] = [] self.routes: List[Route] = []
self.statics: List[RouteHandler] = [] self.statics: List[RouteHandler] = []
self.strict_slashes = strict_slashes self.strict_slashes = strict_slashes

View File

@ -26,10 +26,11 @@ from sanic.views import CompositionView
class RouteMixin: class RouteMixin:
name: str
def __init__(self, *args, **kwargs) -> None: def __init__(self, *args, **kwargs) -> None:
self._future_routes: Set[FutureRoute] = set() self._future_routes: Set[FutureRoute] = set()
self._future_statics: Set[FutureStatic] = set() self._future_statics: Set[FutureStatic] = set()
self.name = ""
self.strict_slashes: Optional[bool] = False self.strict_slashes: Optional[bool] = False
def _apply_route(self, route: FutureRoute) -> List[Route]: def _apply_route(self, route: FutureRoute) -> List[Route]:

View File

@ -389,7 +389,7 @@ def test_app_no_registry_env():
def test_app_set_attribute_warning(app): def test_app_set_attribute_warning(app):
with pytest.warns(UserWarning) as record: with pytest.warns(DeprecationWarning) as record:
app.foo = 1 app.foo = 1
assert len(record) == 1 assert len(record) == 1

View File

@ -41,3 +41,48 @@ def test_bp_repr_with_values(bp):
'Blueprint(name="my_bp", url_prefix="/foo", host="example.com", ' 'Blueprint(name="my_bp", url_prefix="/foo", host="example.com", '
"version=3, strict_slashes=True)" "version=3, strict_slashes=True)"
) )
@pytest.mark.parametrize(
"name",
(
"something",
"some-thing",
"some_thing",
"Something",
"SomeThing",
"Some-Thing",
"Some_Thing",
"SomeThing123",
"something123",
"some-thing123",
"some_thing123",
"some-Thing123",
"some_Thing123",
),
)
def test_names_okay(name):
app = Sanic(name)
bp = Blueprint(name)
assert app.name == name
assert bp.name == name
@pytest.mark.parametrize(
"name",
(
"123something",
"some thing",
"something!",
),
)
def test_names_not_okay(name):
with pytest.warns(DeprecationWarning):
app = Sanic(name)
with pytest.warns(DeprecationWarning):
bp = Blueprint(name)
assert app.name == name
assert bp.name == name

View File

@ -1028,7 +1028,7 @@ def test_blueprint_registered_multiple_apps():
def test_bp_set_attribute_warning(): def test_bp_set_attribute_warning():
bp = Blueprint("bp") bp = Blueprint("bp")
with pytest.warns(UserWarning) as record: with pytest.warns(DeprecationWarning) as record:
bp.foo = 1 bp.foo = 1
assert len(record) == 1 assert len(record) == 1