]> git.ipfire.org Git - thirdparty/starlette.git/commitdiff
Add `typing.Optional` on missing annotations (#1549)
authorMarcelo Trylesinski <marcelotryle@gmail.com>
Thu, 21 Apr 2022 20:12:27 +0000 (22:12 +0200)
committerGitHub <noreply@github.com>
Thu, 21 Apr 2022 20:12:27 +0000 (22:12 +0200)
* Add `typing.Optional` on missing annotations

* Add `no_implicit_optional` on mypy settings

17 files changed:
setup.cfg
starlette/applications.py
starlette/authentication.py
starlette/background.py
starlette/config.py
starlette/exceptions.py
starlette/middleware/authentication.py
starlette/middleware/base.py
starlette/middleware/cors.py
starlette/middleware/errors.py
starlette/middleware/trustedhost.py
starlette/requests.py
starlette/responses.py
starlette/routing.py
starlette/staticfiles.py
starlette/templating.py
starlette/websockets.py

index f1a5c9929ef6a27a8b784fd6d64230e32f2f5a73..a5e6e0e146868249eda62745ae99f4180f9a3103 100644 (file)
--- a/setup.cfg
+++ b/setup.cfg
@@ -5,8 +5,12 @@ max-line-length = 88
 [mypy]
 disallow_untyped_defs = True
 ignore_missing_imports = True
+no_implicit_optional = True
 show_error_codes = True
 
+[mypy-starlette.testclient]
+no_implicit_optional = False
+
 [mypy-tests.*]
 disallow_untyped_defs = False
 check_untyped_defs = True
index d83f70d2f4df75fa463ebf686502e04c35e71215..8c5154449b488dc085fb976991258d2bbea6977d 100644 (file)
@@ -41,17 +41,22 @@ class Starlette:
     def __init__(
         self,
         debug: bool = False,
-        routes: typing.Sequence[BaseRoute] = None,
-        middleware: typing.Sequence[Middleware] = None,
-        exception_handlers: typing.Mapping[
-            typing.Any,
-            typing.Callable[
-                [Request, Exception], typing.Union[Response, typing.Awaitable[Response]]
-            ],
+        routes: typing.Optional[typing.Sequence[BaseRoute]] = None,
+        middleware: typing.Optional[typing.Sequence[Middleware]] = None,
+        exception_handlers: typing.Optional[
+            typing.Mapping[
+                typing.Any,
+                typing.Callable[
+                    [Request, Exception],
+                    typing.Union[Response, typing.Awaitable[Response]],
+                ],
+            ]
+        ] = None,
+        on_startup: typing.Optional[typing.Sequence[typing.Callable]] = None,
+        on_shutdown: typing.Optional[typing.Sequence[typing.Callable]] = None,
+        lifespan: typing.Optional[
+            typing.Callable[["Starlette"], typing.AsyncContextManager]
         ] = None,
-        on_startup: typing.Sequence[typing.Callable] = None,
-        on_shutdown: typing.Sequence[typing.Callable] = None,
-        lifespan: typing.Callable[["Starlette"], typing.AsyncContextManager] = None,
     ) -> None:
         # The lifespan context function is a newer style that replaces
         # on_startup / on_shutdown handlers. Use one or the other, not both.
@@ -124,7 +129,7 @@ class Starlette:
         return self.router.on_event(event_type)
 
     def mount(
-        self, path: str, app: ASGIApp, name: str = None
+        self, path: str, app: ASGIApp, name: typing.Optional[str] = None
     ) -> None:  # pragma: nocover
         """
         We no longer document this API, and its usage is discouraged.
@@ -141,7 +146,7 @@ class Starlette:
         self.router.mount(path, app=app, name=name)
 
     def host(
-        self, host: str, app: ASGIApp, name: str = None
+        self, host: str, app: ASGIApp, name: typing.Optional[str] = None
     ) -> None:  # pragma: no cover
         """
         We no longer document this API, and its usage is discouraged.
@@ -180,8 +185,8 @@ class Starlette:
         self,
         path: str,
         route: typing.Callable,
-        methods: typing.List[str] = None,
-        name: str = None,
+        methods: typing.Optional[typing.List[str]] = None,
+        name: typing.Optional[str] = None,
         include_in_schema: bool = True,
     ) -> None:  # pragma: no cover
         self.router.add_route(
@@ -189,7 +194,7 @@ class Starlette:
         )
 
     def add_websocket_route(
-        self, path: str, route: typing.Callable, name: str = None
+        self, path: str, route: typing.Callable, name: typing.Optional[str] = None
     ) -> None:  # pragma: no cover
         self.router.add_websocket_route(path, route, name=name)
 
@@ -205,8 +210,8 @@ class Starlette:
     def route(
         self,
         path: str,
-        methods: typing.List[str] = None,
-        name: str = None,
+        methods: typing.Optional[typing.List[str]] = None,
+        name: typing.Optional[str] = None,
         include_in_schema: bool = True,
     ) -> typing.Callable:  # pragma: nocover
         """
@@ -234,7 +239,7 @@ class Starlette:
         return decorator
 
     def websocket_route(
-        self, path: str, name: str = None
+        self, path: str, name: typing.Optional[str] = None
     ) -> typing.Callable:  # pragma: nocover
         """
         We no longer document this decorator style API, and its usage is discouraged.
index 1a4cba377980220e87cd5a01dff21a440a42c1e8..17f4a5eada706e0a26f7a8dcaaf774c49de67edd 100644 (file)
@@ -20,7 +20,7 @@ def has_required_scope(conn: HTTPConnection, scopes: typing.Sequence[str]) -> bo
 def requires(
     scopes: typing.Union[str, typing.Sequence[str]],
     status_code: int = 403,
-    redirect: str = None,
+    redirect: typing.Optional[str] = None,
 ) -> typing.Callable:
     scopes_list = [scopes] if isinstance(scopes, str) else list(scopes)
 
@@ -110,7 +110,7 @@ class AuthenticationBackend:
 
 
 class AuthCredentials:
-    def __init__(self, scopes: typing.Sequence[str] = None):
+    def __init__(self, scopes: typing.Optional[typing.Sequence[str]] = None):
         self.scopes = [] if scopes is None else list(scopes)
 
 
index 14a4e9e1a711aa1e1f39ca9ad3d0dd1d31409c98..145324e3fffaff33fe8b3b2f4a702f2ac96a0eae 100644 (file)
@@ -29,7 +29,7 @@ class BackgroundTask:
 
 
 class BackgroundTasks(BackgroundTask):
-    def __init__(self, tasks: typing.Sequence[BackgroundTask] = None):
+    def __init__(self, tasks: typing.Optional[typing.Sequence[BackgroundTask]] = None):
         self.tasks = list(tasks) if tasks else []
 
     def add_task(
index bd809afb4ce5981c90e893dcc4d032ad02a38a37..e9e809c735f42c75302e8a7d4c4a6b9b4e546155 100644 (file)
@@ -52,7 +52,7 @@ T = typing.TypeVar("T")
 class Config:
     def __init__(
         self,
-        env_file: typing.Union[str, Path] = None,
+        env_file: typing.Optional[typing.Union[str, Path]] = None,
         environ: typing.Mapping[str, str] = environ,
     ) -> None:
         self.environ = environ
@@ -88,12 +88,18 @@ class Config:
         ...
 
     def __call__(
-        self, key: str, cast: typing.Callable = None, default: typing.Any = undefined
+        self,
+        key: str,
+        cast: typing.Optional[typing.Callable] = None,
+        default: typing.Any = undefined,
     ) -> typing.Any:
         return self.get(key, cast, default)
 
     def get(
-        self, key: str, cast: typing.Callable = None, default: typing.Any = undefined
+        self,
+        key: str,
+        cast: typing.Optional[typing.Callable] = None,
+        default: typing.Any = undefined,
     ) -> typing.Any:
         if key in self.environ:
             value = self.environ[key]
@@ -118,7 +124,7 @@ class Config:
         return file_values
 
     def _perform_cast(
-        self, key: str, value: typing.Any, cast: typing.Callable = None
+        self, key: str, value: typing.Any, cast: typing.Optional[typing.Callable] = None
     ) -> typing.Any:
         if cast is None or value is None:
             return value
index 8f28b6e2df88c87e4cd5beed1b11812dddf022c9..61039c59801fc40c8717e866413325f7db55dcd1 100644 (file)
@@ -10,7 +10,10 @@ from starlette.types import ASGIApp, Message, Receive, Scope, Send
 
 class HTTPException(Exception):
     def __init__(
-        self, status_code: int, detail: str = None, headers: dict = None
+        self,
+        status_code: int,
+        detail: typing.Optional[str] = None,
+        headers: typing.Optional[dict] = None,
     ) -> None:
         if detail is None:
             detail = http.HTTPStatus(status_code).phrase
@@ -27,8 +30,8 @@ class ExceptionMiddleware:
     def __init__(
         self,
         app: ASGIApp,
-        handlers: typing.Mapping[
-            typing.Any, typing.Callable[[Request, Exception], Response]
+        handlers: typing.Optional[
+            typing.Mapping[typing.Any, typing.Callable[[Request, Exception], Response]]
         ] = None,
         debug: bool = False,
     ) -> None:
index 6e2d2dade36560ea29544e3d4e46ca162a43aa12..76e4a246d2c0190627e388d43d28c5202d7c556b 100644 (file)
@@ -16,8 +16,8 @@ class AuthenticationMiddleware:
         self,
         app: ASGIApp,
         backend: AuthenticationBackend,
-        on_error: typing.Callable[
-            [HTTPConnection, AuthenticationError], Response
+        on_error: typing.Optional[
+            typing.Callable[[HTTPConnection, AuthenticationError], Response]
         ] = None,
     ) -> None:
         self.app = app
index bfb4a54a48db9f9aba15a200ad936834f7f05748..ca9deb7de5b3673d407a112c705098a5a2e460e7 100644 (file)
@@ -13,7 +13,9 @@ DispatchFunction = typing.Callable[
 
 
 class BaseHTTPMiddleware:
-    def __init__(self, app: ASGIApp, dispatch: DispatchFunction = None) -> None:
+    def __init__(
+        self, app: ASGIApp, dispatch: typing.Optional[DispatchFunction] = None
+    ) -> None:
         self.app = app
         self.dispatch_func = self.dispatch if dispatch is None else dispatch
 
index c850579c8066815622006f89ae907de228531716..b36d155f5e0849d7378affc22f2ef58d39c93558 100644 (file)
@@ -18,7 +18,7 @@ class CORSMiddleware:
         allow_methods: typing.Sequence[str] = ("GET",),
         allow_headers: typing.Sequence[str] = (),
         allow_credentials: bool = False,
-        allow_origin_regex: str = None,
+        allow_origin_regex: typing.Optional[str] = None,
         expose_headers: typing.Sequence[str] = (),
         max_age: int = 600,
     ) -> None:
index 474c9afc0b62609b70b4fff378c7330b2dc09881..acb1930f33ffeb70f7f2693e07f0fdde4a5dcc6f 100644 (file)
@@ -135,7 +135,10 @@ class ServerErrorMiddleware:
     """
 
     def __init__(
-        self, app: ASGIApp, handler: typing.Callable = None, debug: bool = False
+        self,
+        app: ASGIApp,
+        handler: typing.Optional[typing.Callable] = None,
+        debug: bool = False,
     ) -> None:
         self.app = app
         self.handler = handler
index 6bc4d2b5e6f704ddbb6434b52424421eec6020c5..e84e6876a03a53c546c3d54d0799575da2caeac8 100644 (file)
@@ -11,7 +11,7 @@ class TrustedHostMiddleware:
     def __init__(
         self,
         app: ASGIApp,
-        allowed_hosts: typing.Sequence[str] = None,
+        allowed_hosts: typing.Optional[typing.Sequence[str]] = None,
         www_redirect: bool = True,
     ) -> None:
         if allowed_hosts is None:
index e3c91e284150e8cd4ae1b0597b55de832d02c55e..c738ebaa40841a3fef896b0b6a06eb43044f02a3 100644 (file)
@@ -65,7 +65,7 @@ class HTTPConnection(Mapping):
     any functionality that is common to both `Request` and `WebSocket`.
     """
 
-    def __init__(self, scope: Scope, receive: Receive = None) -> None:
+    def __init__(self, scope: Scope, receive: typing.Optional[Receive] = None) -> None:
         assert scope["type"] in ("http", "websocket")
         self.scope = scope
 
index b33bdd7132114d27a645acdfeb5ff2fd25307305..bc73cb1561b03dacd6e7b7257d2bbd3f71dba809 100644 (file)
@@ -38,9 +38,9 @@ class Response:
         self,
         content: typing.Any = None,
         status_code: int = 200,
-        headers: typing.Mapping[str, str] = None,
-        media_type: str = None,
-        background: BackgroundTask = None,
+        headers: typing.Optional[typing.Mapping[str, str]] = None,
+        media_type: typing.Optional[str] = None,
+        background: typing.Optional[BackgroundTask] = None,
     ) -> None:
         self.status_code = status_code
         if media_type is not None:
@@ -56,7 +56,9 @@ class Response:
             return content
         return content.encode(self.charset)
 
-    def init_headers(self, headers: typing.Mapping[str, str] = None) -> None:
+    def init_headers(
+        self, headers: typing.Optional[typing.Mapping[str, str]] = None
+    ) -> None:
         if headers is None:
             raw_headers: typing.List[typing.Tuple[bytes, bytes]] = []
             populate_content_length = True
@@ -97,10 +99,10 @@ class Response:
         self,
         key: str,
         value: str = "",
-        max_age: int = None,
-        expires: int = None,
+        max_age: typing.Optional[int] = None,
+        expires: typing.Optional[int] = None,
         path: str = "/",
-        domain: str = None,
+        domain: typing.Optional[str] = None,
         secure: bool = False,
         httponly: bool = False,
         samesite: str = "lax",
@@ -133,7 +135,7 @@ class Response:
         self,
         key: str,
         path: str = "/",
-        domain: str = None,
+        domain: typing.Optional[str] = None,
         secure: bool = False,
         httponly: bool = False,
         samesite: str = "lax",
@@ -178,9 +180,9 @@ class JSONResponse(Response):
         self,
         content: typing.Any,
         status_code: int = 200,
-        headers: dict = None,
-        media_type: str = None,
-        background: BackgroundTask = None,
+        headers: typing.Optional[dict] = None,
+        media_type: typing.Optional[str] = None,
+        background: typing.Optional[BackgroundTask] = None,
     ) -> None:
         super().__init__(content, status_code, headers, media_type, background)
 
@@ -199,8 +201,8 @@ class RedirectResponse(Response):
         self,
         url: typing.Union[str, URL],
         status_code: int = 307,
-        headers: typing.Mapping[str, str] = None,
-        background: BackgroundTask = None,
+        headers: typing.Optional[typing.Mapping[str, str]] = None,
+        background: typing.Optional[BackgroundTask] = None,
     ) -> None:
         super().__init__(
             content=b"", status_code=status_code, headers=headers, background=background
@@ -213,9 +215,9 @@ class StreamingResponse(Response):
         self,
         content: typing.Any,
         status_code: int = 200,
-        headers: typing.Mapping[str, str] = None,
-        media_type: str = None,
-        background: BackgroundTask = None,
+        headers: typing.Optional[typing.Mapping[str, str]] = None,
+        media_type: typing.Optional[str] = None,
+        background: typing.Optional[BackgroundTask] = None,
     ) -> None:
         if isinstance(content, typing.AsyncIterable):
             self.body_iterator = content
@@ -268,12 +270,12 @@ class FileResponse(Response):
         self,
         path: typing.Union[str, "os.PathLike[str]"],
         status_code: int = 200,
-        headers: typing.Mapping[str, str] = None,
-        media_type: str = None,
-        background: BackgroundTask = None,
-        filename: str = None,
-        stat_result: os.stat_result = None,
-        method: str = None,
+        headers: typing.Optional[typing.Mapping[str, str]] = None,
+        media_type: typing.Optional[str] = None,
+        background: typing.Optional[BackgroundTask] = None,
+        filename: typing.Optional[str] = None,
+        stat_result: typing.Optional[os.stat_result] = None,
+        method: typing.Optional[str] = None,
         content_disposition_type: str = "attachment",
     ) -> None:
         self.path = path
index ea6ec211715a39084c893dae3ee73cf4c02e671d..7b7dc9b749415ecc9b66a9e1281c065f660a0f9f 100644 (file)
@@ -192,8 +192,8 @@ class Route(BaseRoute):
         path: str,
         endpoint: typing.Callable,
         *,
-        methods: typing.List[str] = None,
-        name: str = None,
+        methods: typing.Optional[typing.List[str]] = None,
+        name: typing.Optional[str] = None,
         include_in_schema: bool = True,
     ) -> None:
         assert path.startswith("/"), "Routed paths must start with '/'"
@@ -276,7 +276,7 @@ class Route(BaseRoute):
 
 class WebSocketRoute(BaseRoute):
     def __init__(
-        self, path: str, endpoint: typing.Callable, *, name: str = None
+        self, path: str, endpoint: typing.Callable, *, name: typing.Optional[str] = None
     ) -> None:
         assert path.startswith("/"), "Routed paths must start with '/'"
         self.path = path
@@ -336,9 +336,9 @@ class Mount(BaseRoute):
     def __init__(
         self,
         path: str,
-        app: ASGIApp = None,
-        routes: typing.Sequence[BaseRoute] = None,
-        name: str = None,
+        app: typing.Optional[ASGIApp] = None,
+        routes: typing.Optional[typing.Sequence[BaseRoute]] = None,
+        name: typing.Optional[str] = None,
     ) -> None:
         assert path == "" or path.startswith("/"), "Routed paths must start with '/'"
         assert (
@@ -426,7 +426,9 @@ class Mount(BaseRoute):
 
 
 class Host(BaseRoute):
-    def __init__(self, host: str, app: ASGIApp, name: str = None) -> None:
+    def __init__(
+        self, host: str, app: ASGIApp, name: typing.Optional[str] = None
+    ) -> None:
         self.host = host
         self.app = app
         self.name = name
@@ -537,12 +539,14 @@ class _DefaultLifespan:
 class Router:
     def __init__(
         self,
-        routes: typing.Sequence[BaseRoute] = None,
+        routes: typing.Optional[typing.Sequence[BaseRoute]] = None,
         redirect_slashes: bool = True,
-        default: ASGIApp = None,
-        on_startup: typing.Sequence[typing.Callable] = None,
-        on_shutdown: typing.Sequence[typing.Callable] = None,
-        lifespan: typing.Callable[[typing.Any], typing.AsyncContextManager] = None,
+        default: typing.Optional[ASGIApp] = None,
+        on_startup: typing.Optional[typing.Sequence[typing.Callable]] = None,
+        on_shutdown: typing.Optional[typing.Sequence[typing.Callable]] = None,
+        lifespan: typing.Optional[
+            typing.Callable[[typing.Any], typing.AsyncContextManager]
+        ] = None,
     ) -> None:
         self.routes = [] if routes is None else list(routes)
         self.redirect_slashes = redirect_slashes
@@ -700,7 +704,7 @@ class Router:
     # The following usages are now discouraged in favour of configuration
     #  during Router.__init__(...)
     def mount(
-        self, path: str, app: ASGIApp, name: str = None
+        self, path: str, app: ASGIApp, name: typing.Optional[str] = None
     ) -> None:  # pragma: nocover
         """
         We no longer document this API, and its usage is discouraged.
@@ -718,7 +722,7 @@ class Router:
         self.routes.append(route)
 
     def host(
-        self, host: str, app: ASGIApp, name: str = None
+        self, host: str, app: ASGIApp, name: typing.Optional[str] = None
     ) -> None:  # pragma: no cover
         """
         We no longer document this API, and its usage is discouraged.
@@ -739,8 +743,8 @@ class Router:
         self,
         path: str,
         endpoint: typing.Callable,
-        methods: typing.List[str] = None,
-        name: str = None,
+        methods: typing.Optional[typing.List[str]] = None,
+        name: typing.Optional[str] = None,
         include_in_schema: bool = True,
     ) -> None:  # pragma: nocover
         route = Route(
@@ -753,7 +757,7 @@ class Router:
         self.routes.append(route)
 
     def add_websocket_route(
-        self, path: str, endpoint: typing.Callable, name: str = None
+        self, path: str, endpoint: typing.Callable, name: typing.Optional[str] = None
     ) -> None:  # pragma: no cover
         route = WebSocketRoute(path, endpoint=endpoint, name=name)
         self.routes.append(route)
@@ -761,8 +765,8 @@ class Router:
     def route(
         self,
         path: str,
-        methods: typing.List[str] = None,
-        name: str = None,
+        methods: typing.Optional[typing.List[str]] = None,
+        name: typing.Optional[str] = None,
         include_in_schema: bool = True,
     ) -> typing.Callable:  # pragma: nocover
         """
@@ -790,7 +794,7 @@ class Router:
         return decorator
 
     def websocket_route(
-        self, path: str, name: str = None
+        self, path: str, name: typing.Optional[str] = None
     ) -> typing.Callable:  # pragma: nocover
         """
         We no longer document this decorator style API, and its usage is discouraged.
index bd4d8bced1274f677a6276243a13ae3665d13674..d09630f35b85bd72efc8febed9ac816c551744a6 100644 (file)
@@ -39,8 +39,10 @@ class StaticFiles:
     def __init__(
         self,
         *,
-        directory: PathLike = None,
-        packages: typing.List[typing.Union[str, typing.Tuple[str, str]]] = None,
+        directory: typing.Optional[PathLike] = None,
+        packages: typing.Optional[
+            typing.List[typing.Union[str, typing.Tuple[str, str]]]
+        ] = None,
         html: bool = False,
         check_dir: bool = True,
     ) -> None:
@@ -54,8 +56,10 @@ class StaticFiles:
 
     def get_directories(
         self,
-        directory: PathLike = None,
-        packages: typing.List[typing.Union[str, typing.Tuple[str, str]]] = None,
+        directory: typing.Optional[PathLike] = None,
+        packages: typing.Optional[
+            typing.List[typing.Union[str, typing.Tuple[str, str]]]
+        ] = None,
     ) -> typing.List[PathLike]:
         """
         Given `directory` and `packages` arguments, return a list of all the
index 27939c95e00ee5895e82246ecf4cdd4771c2e7a5..99035837f54cc76a88df7ba4910905cdaf4d64f7 100644 (file)
@@ -28,9 +28,9 @@ class _TemplateResponse(Response):
         template: typing.Any,
         context: dict,
         status_code: int = 200,
-        headers: typing.Mapping[str, str] = None,
-        media_type: str = None,
-        background: BackgroundTask = None,
+        headers: typing.Optional[typing.Mapping[str, str]] = None,
+        media_type: typing.Optional[str] = None,
+        background: typing.Optional[BackgroundTask] = None,
     ):
         self.template = template
         self.context = context
@@ -88,9 +88,9 @@ class Jinja2Templates:
         name: str,
         context: dict,
         status_code: int = 200,
-        headers: typing.Mapping[str, str] = None,
-        media_type: str = None,
-        background: BackgroundTask = None,
+        headers: typing.Optional[typing.Mapping[str, str]] = None,
+        media_type: typing.Optional[str] = None,
+        background: typing.Optional[BackgroundTask] = None,
     ) -> _TemplateResponse:
         if "request" not in context:
             raise ValueError('context must include a "request" key')
index 03ed19972aada03a2accd8eff70bc631184aa409..afcbde7fc5a8c128cddeed861043caea1e439a95 100644 (file)
@@ -13,7 +13,7 @@ class WebSocketState(enum.Enum):
 
 
 class WebSocketDisconnect(Exception):
-    def __init__(self, code: int = 1000, reason: str = None) -> None:
+    def __init__(self, code: int = 1000, reason: typing.Optional[str] = None) -> None:
         self.code = code
         self.reason = reason or ""
 
@@ -88,8 +88,8 @@ class WebSocket(HTTPConnection):
 
     async def accept(
         self,
-        subprotocol: str = None,
-        headers: typing.Iterable[typing.Tuple[bytes, bytes]] = None,
+        subprotocol: typing.Optional[str] = None,
+        headers: typing.Optional[typing.Iterable[typing.Tuple[bytes, bytes]]] = None,
     ) -> None:
         headers = headers or []
 
@@ -174,14 +174,16 @@ class WebSocket(HTTPConnection):
         else:
             await self.send({"type": "websocket.send", "bytes": text.encode("utf-8")})
 
-    async def close(self, code: int = 1000, reason: str = None) -> None:
+    async def close(
+        self, code: int = 1000, reason: typing.Optional[str] = None
+    ) -> None:
         await self.send(
             {"type": "websocket.close", "code": code, "reason": reason or ""}
         )
 
 
 class WebSocketClose:
-    def __init__(self, code: int = 1000, reason: str = None) -> None:
+    def __init__(self, code: int = 1000, reason: typing.Optional[str] = None) -> None:
         self.code = code
         self.reason = reason or ""