Пример #1
0
    def finalize(
        cls,
        error_handler: ErrorHandler,
        config: Config,
        fallback: Optional[str] = None,
    ):
        if fallback:
            deprecation(
                "Setting the ErrorHandler fallback value via finalize() "
                "is deprecated and no longer supported. This feature will "
                "be removed in v22.6. Instead, use "
                "app.config.FALLBACK_ERROR_FORMAT.",
                22.6,
            )

        if not fallback:
            fallback = config.FALLBACK_ERROR_FORMAT

        if fallback != DEFAULT_FORMAT:
            if error_handler._fallback is not _default:
                error_logger.warning(
                    f"Setting the fallback value to {fallback}. This changes "
                    "the current non-default value "
                    f"'{error_handler._fallback}'.")
            error_handler._fallback = fallback

        if not isinstance(error_handler, cls):
            error_logger.warning(
                f"Error handler is non-conforming: {type(error_handler)}")
Пример #2
0
    def _precheck(self):
        if self.args.debug and self.main_process:
            error_logger.warning(
                "Starting in v22.3, --debug will no "
                "longer automatically run the auto-reloader.\n  Switch to "
                "--dev to continue using that functionality."
            )

        # # Custom TLS mismatch handling for better diagnostics
        if self.main_process and (
            # one of cert/key missing
            bool(self.args.cert) != bool(self.args.key)
            # new and old style self.args used together
            or self.args.tls
            and self.args.cert
            # strict host checking without certs would always fail
            or self.args.tlshost
            and not self.args.tls
            and not self.args.cert
        ):
            self.parser.print_usage(sys.stderr)
            message = (
                "TLS certificates must be specified by either of:\n"
                "  --cert certdir/fullchain.pem --key certdir/privkey.pem\n"
                "  --tls certdir  (equivalent to the above)"
            )
            error_logger.error(message)
            sys.exit(1)
Пример #3
0
async def get_courier(request: Request,
                      courier_id: int) -> response.HTTPResponse:
    courier_status = await app.db.courier_status(courier_id)
    if not courier_status:
        error_logger.warning("Courier id=%s not found", courier_id)
        return response.HTTPResponse(status=404)

    # TODO: move it to thread_pool_executor
    completed_orders_ids = {
        status.order_id
        for status in courier_status.statuses
        if status.completed_time is not None
    }

    json = courier_status.courier.external()

    if not completed_orders_ids:
        return response.json(json, indent=4)

    completed_orders = [
        order for order in courier_status.orders
        if order.order_id in completed_orders_ids
    ]
    completed_orders.sort(key=lambda order: order.completed_time)

    return response.json(json, indent=4)
Пример #4
0
 def FALLBACK_ERROR_FORMAT(self, value):
     self._check_error_format(value)
     if (self._FALLBACK_ERROR_FORMAT is not _default
             and value != self._FALLBACK_ERROR_FORMAT):
         error_logger.warning(
             "Setting config.FALLBACK_ERROR_FORMAT on an already "
             "configured value may have unintended consequences.")
     self._FALLBACK_ERROR_FORMAT = value
Пример #5
0
    def _get_fallback_value(cls, error_handler: ErrorHandler, config: Config):
        if error_handler._fallback is not _default:
            if config._FALLBACK_ERROR_FORMAT is _default:
                return error_handler.fallback

            error_logger.warning(
                "Conflicting error fallback values were found in the "
                "error handler and in the app.config while handling an "
                "exception. Using the value from app.config.")
        return config.FALLBACK_ERROR_FORMAT
Пример #6
0
 def register_type(self, converter: Callable[[str], Any]) -> None:
     """
     Allows for adding custom function to cast from a string value to any
     other type. The function should raise ValueError if it is not the
     correct type.
     """
     if converter in self._converters:
         error_logger.warning(
             f"Configuration value converter '{converter.__name__}' has "
             "already been registered")
         return
     self._converters.append(converter)
Пример #7
0
    def finalize(
        cls,
        error_handler: ErrorHandler,
        fallback: Optional[str] = None,
        config: Optional[Config] = None,
    ):
        if fallback:
            deprecation(
                "Setting the ErrorHandler fallback value via finalize() "
                "is deprecated and no longer supported. This feature will "
                "be removed in v22.6. Instead, use "
                "app.config.FALLBACK_ERROR_FORMAT.",
                22.6,
            )

        if config is None:
            deprecation(
                "Starting in v22.3, config will be a required argument "
                "for ErrorHandler.finalize().",
                22.3,
            )

        if fallback and fallback != DEFAULT_FORMAT:
            if error_handler._fallback is not _default:
                error_logger.warning(
                    f"Setting the fallback value to {fallback}. This changes "
                    "the current non-default value "
                    f"'{error_handler._fallback}'."
                )
            error_handler._fallback = fallback

        if not isinstance(error_handler, cls):
            error_logger.warning(
                f"Error handler is non-conforming: {type(error_handler)}"
            )

        sig = signature(error_handler.lookup)
        if len(sig.parameters) == 1:
            deprecation(
                "You are using a deprecated error handler. The lookup "
                "method should accept two positional parameters: "
                "(exception, route_name: Optional[str]). "
                "Until you upgrade your ErrorHandler.lookup, Blueprint "
                "specific exceptions will not work properly. Beginning "
                "in v22.3, the legacy style lookup method will not "
                "work at all.",
                22.3,
            )
            legacy_lookup = error_handler._legacy_lookup
            error_handler._lookup = legacy_lookup  # type: ignore
Пример #8
0
    async def _dispatch(
        self,
        event: str,
        context: Optional[Dict[str, Any]] = None,
        condition: Optional[Dict[str, str]] = None,
        fail_not_found: bool = True,
        reverse: bool = False,
    ) -> Any:
        try:
            group, handlers, params = self.get(event, condition=condition)
        except NotFound as e:
            is_reserved = event.split(".", 1)[0] in RESERVED_NAMESPACES
            if fail_not_found and (not is_reserved or self.allow_fail_builtin):
                raise e
            else:
                if self.ctx.app.debug and self.ctx.app.state.verbosity >= 1:
                    error_logger.warning(str(e))
                return None

        events = [signal.ctx.event for signal in group]
        for signal_event in events:
            signal_event.set()
        if context:
            params.update(context)

        signals = group.routes
        if not reverse:
            signals = signals[::-1]
        try:
            for signal in signals:
                params.pop("__trigger__", None)
                if ((condition is None and signal.ctx.exclusive is False) or
                    (condition is None and not signal.handler.__requirements__)
                        or
                    (condition == signal.handler.__requirements__)) and (
                        signal.ctx.trigger or event == signal.ctx.definition):
                    maybe_coroutine = signal.handler(**params)
                    if isawaitable(maybe_coroutine):
                        retval = await maybe_coroutine
                        if retval:
                            return retval
                    elif maybe_coroutine:
                        return maybe_coroutine
            return None
        finally:
            for signal_event in events:
                signal_event.clear()
Пример #9
0
    def finalize(cls, error_handler):
        if not isinstance(error_handler, cls):
            error_logger.warning(
                f"Error handler is non-conforming: {type(error_handler)}")

        sig = signature(error_handler.lookup)
        if len(sig.parameters) == 1:
            error_logger.warning(
                DeprecationWarning(
                    "You are using a deprecated error handler. The lookup "
                    "method should accept two positional parameters: "
                    "(exception, route_name: Optional[str]). "
                    "Until you upgrade your ErrorHandler.lookup, Blueprint "
                    "specific exceptions will not work properly. Beginning "
                    "in v22.3, the legacy style lookup method will not "
                    "work at all."), )
            error_handler._lookup = error_handler._legacy_lookup
Пример #10
0
    async def keepalive_ping(self) -> None:
        """
        Send a Ping frame and wait for a Pong frame at regular intervals.
        This coroutine exits when the connection terminates and one of the
        following happens:
        - :meth:`ping` raises :exc:`ConnectionClosed`, or
        - :meth:`auto_close_connection` cancels :attr:`keepalive_ping_task`.
        """
        if self.ping_interval is None:
            return

        try:
            while True:
                await asyncio.sleep(self.ping_interval)

                # ping() raises CancelledError if the connection is closed,
                # when auto_close_connection() cancels keepalive_ping_task.

                # ping() raises ConnectionClosed if the connection is lost,
                # when connection_lost() calls abort_pings().

                ping_waiter = await self.ping()

                if self.ping_timeout is not None:
                    try:
                        await asyncio.wait_for(ping_waiter, self.ping_timeout)
                    except asyncio.TimeoutError:
                        error_logger.warning(
                            "Websocket timed out waiting for pong"
                        )
                        self.fail_connection(1011)
                        break
        except asyncio.CancelledError:
            # It is expected for this task to be cancelled during during
            # normal operation, when the connection is closed.
            logger.debug("Websocket keepalive ping task was cancelled.")
        except (ConnectionClosed, WebsocketClosed):
            logger.debug("Websocket closed. Keepalive ping task exiting.")
        except Exception as e:
            error_logger.warning(
                "Unexpected exception in websocket keepalive ping task."
            )
            logger.debug(str(e))
Пример #11
0
async def add_couriers(request: Request) -> response.HTTPResponse:
    couriers, invalid_couriers_id = [], []
    for courier in request.json['data']:
        try:
            courier = CourierModel(**courier)
        except ValidationError as e:
            invalid_couriers_id += [courier.get('courier_id', -1)]
            error_logger.warning(e.json(indent=4))
        else:
            couriers += [courier]

    if invalid_couriers_id:
        error_logger.warning(
            "Request rejected, it contains invalid couriers (%s)",
            len(invalid_couriers_id))
        context = validation_error('couriers', invalid_couriers_id)
        return response.json(context, status=400)

    added_couriers = await app.db.add_couriers(couriers)

    return response.json(added_couriers, status=201)
Пример #12
0
    def _build_run_kwargs(self):
        ssl: Union[None, dict, str, list] = []
        if self.args.tlshost:
            ssl.append(None)
        if self.args.cert is not None or self.args.key is not None:
            ssl.append(dict(cert=self.args.cert, key=self.args.key))
        if self.args.tls:
            ssl += self.args.tls
        if not ssl:
            ssl = None
        elif len(ssl) == 1 and ssl[0] is not None:
            # Use only one cert, no TLSSelector.
            ssl = ssl[0]
        kwargs = {
            "access_log": self.args.access_log,
            "debug": self.args.debug,
            "fast": self.args.fast,
            "host": self.args.host,
            "motd": self.args.motd,
            "noisy_exceptions": self.args.noisy_exceptions,
            "port": self.args.port,
            "ssl": ssl,
            "unix": self.args.unix,
            "verbosity": self.args.verbosity or 0,
            "workers": self.args.workers,
        }

        if self.args.auto_reload:
            kwargs["auto_reload"] = True

        if self.args.path:
            if self.args.auto_reload or self.args.debug:
                kwargs["reload_dir"] = self.args.path
            else:
                error_logger.warning(
                    "Ignoring '--reload-dir' since auto reloading was not "
                    "enabled. If you would like to watch directories for "
                    "changes, consider using --debug or --auto-reload."
                )
        return kwargs
Пример #13
0
 def __init__(
     self,
     *args,
     websocket_timeout: float = 10.0,
     websocket_max_size: Optional[int] = None,
     websocket_max_queue: Optional[int] = None,  # max_queue is deprecated
     websocket_read_limit: Optional[int] = None,  # read_limit is deprecated
     websocket_write_limit: Optional[int] = None,  # write_limit deprecated
     websocket_ping_interval: Optional[float] = 20.0,
     websocket_ping_timeout: Optional[float] = 20.0,
     **kwargs,
 ):
     super().__init__(*args, **kwargs)
     self.websocket: Optional[WebsocketImplProtocol] = None
     self.websocket_timeout = websocket_timeout
     self.websocket_max_size = websocket_max_size
     if websocket_max_queue is not None and websocket_max_queue > 0:
         # TODO: Reminder remove this warning in v22.3
         error_logger.warning(
             DeprecationWarning(
                 "Websocket no longer uses queueing, so websocket_max_queue"
                 " is no longer required."))
     if websocket_read_limit is not None and websocket_read_limit > 0:
         # TODO: Reminder remove this warning in v22.3
         error_logger.warning(
             DeprecationWarning("Websocket no longer uses read buffers, so "
                                "websocket_read_limit is not required."))
     if websocket_write_limit is not None and websocket_write_limit > 0:
         # TODO: Reminder remove this warning in v22.3
         error_logger.warning(
             DeprecationWarning(
                 "Websocket no longer uses write buffers, so "
                 "websocket_write_limit is not required."))
     self.websocket_ping_interval = websocket_ping_interval
     self.websocket_ping_timeout = websocket_ping_timeout
Пример #14
0
    async def _dispatch(
        self,
        event: str,
        context: Optional[Dict[str, Any]] = None,
        condition: Optional[Dict[str, str]] = None,
        fail_not_found: bool = True,
        reverse: bool = False,
    ) -> Any:
        try:
            group, handlers, params = self.get(event, condition=condition)
        except NotFound as e:
            if fail_not_found:
                raise e
            else:
                if self.ctx.app.debug:
                    error_logger.warning(str(e))
                return None

        events = [signal.ctx.event for signal in group]
        for signal_event in events:
            signal_event.set()
        if context:
            params.update(context)

        if not reverse:
            handlers = handlers[::-1]
        try:
            for handler in handlers:
                if condition is None or condition == handler.__requirements__:
                    maybe_coroutine = handler(**params)
                    if isawaitable(maybe_coroutine):
                        retval = await maybe_coroutine
                        if retval:
                            return retval
                    elif maybe_coroutine:
                        return maybe_coroutine
            return None
        finally:
            for signal_event in events:
                signal_event.clear()
Пример #15
0
def try_use_uvloop() -> None:
    """
    Use uvloop instead of the default asyncio loop.
    """
    if OS_IS_WINDOWS:
        error_logger.warning(
            "You are trying to use uvloop, but uvloop is not compatible "
            "with your system. You can disable uvloop completely by setting "
            "the 'USE_UVLOOP' configuration value to false, or simply not "
            "defining it and letting Sanic handle it for you. Sanic will now "
            "continue to run using the default event loop.")
        return

    try:
        import uvloop  # type: ignore
    except ImportError:
        error_logger.warning(
            "You are trying to use uvloop, but uvloop is not "
            "installed in your system. In order to use uvloop "
            "you must first install it. Otherwise, you can disable "
            "uvloop completely by setting the 'USE_UVLOOP' "
            "configuration value to false. Sanic will now continue "
            "to run with the default event loop.")
        return

    uvloop_install_removed = strtobool(getenv("SANIC_NO_UVLOOP", "no"))
    if uvloop_install_removed:
        error_logger.info(
            "You are requesting to run Sanic using uvloop, but the "
            "install-time 'SANIC_NO_UVLOOP' environment variable (used to "
            "opt-out of installing uvloop with Sanic) is set to true. If "
            "you want to prevent Sanic from overriding the event loop policy "
            "during runtime, set the 'USE_UVLOOP' configuration value to "
            "false.")

    if not isinstance(asyncio.get_event_loop_policy(), uvloop.EventLoopPolicy):
        asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
Пример #16
0
async def complete(request: Request) -> response.HTTPResponse:
    try:
        complete = CompleteModel(**request.json)
    except ValidationError as e:
        error_logger.warning(e.json(indent=4))
        return response.HTTPResponse(status=400)

    courier_status = await app.db.courier_status(complete.courier_id)

    if courier_status is None:
        error_logger.warning("Courier id=%s not found", complete.courier_id)
        return response.HTTPResponse(status=400)

    has_the_courier_order = any(order.order_id == complete.order_id
                                for order in courier_status.orders)
    if not has_the_courier_order:
        error_logger.warning("Courier id=%s has no order id=%s",
                             complete.courier_id, complete.order_id)
        return response.HTTPResponse(status=400)

    await app.db.complete_order(complete.order_id, complete.complete_time)

    return response.json({"order_id": complete.order_id})
Пример #17
0
async def update_courier(request: Request,
                         courier_id: int) -> response.HTTPResponse:
    if invalid_fields := is_json_patching_courier_valid(request.json):
        error_logger.warning("Only %s might be updated, but %s found",
                             PATCHABLE_FIELDS, invalid_fields)
        return response.HTTPResponse(status=400)
Пример #18
0
    async def auto_close_connection(self) -> None:
        """
        Close the WebSocket Connection
        When the opening handshake succeeds, :meth:`connection_open` starts
        this coroutine in a task. It waits for the data transfer phase to
        complete then it closes the TCP connection cleanly.
        When the opening handshake fails, :meth:`fail_connection` does the
        same. There's no data transfer phase in that case.
        """
        try:
            # Wait for the data transfer phase to complete.
            if self.data_finished_fut:
                try:
                    await self.data_finished_fut
                    logger.debug(
                        "Websocket task finished. Closing the connection."
                    )
                except asyncio.CancelledError:
                    # Cancelled error is called when data phase is cancelled
                    # if an error occurred or the client closed the connection
                    logger.debug(
                        "Websocket handler cancelled. Closing the connection."
                    )

            # Cancel the keepalive ping task.
            if self.keepalive_ping_task:
                self.keepalive_ping_task.cancel()
                self.keepalive_ping_task = None

            # Half-close the TCP connection if possible (when there's no TLS).
            if (
                self.io_proto
                and self.io_proto.transport
                and self.io_proto.transport.can_write_eof()
            ):
                logger.debug("Websocket half-closing TCP connection")
                self.io_proto.transport.write_eof()
                if self.connection_lost_waiter:
                    if await self.wait_for_connection_lost(timeout=0):
                        return
        except asyncio.CancelledError:
            ...
        finally:
            # The try/finally ensures that the transport never remains open,
            # even if this coroutine is cancelled (for example).
            if (not self.io_proto) or (not self.io_proto.transport):
                # we were never open, or done. Can't do any finalization.
                return
            elif (
                self.connection_lost_waiter
                and self.connection_lost_waiter.done()
            ):
                # connection confirmed closed already, proceed to abort waiter
                ...
            elif self.io_proto.transport.is_closing():
                # Connection is already closing (due to half-close above)
                # proceed to abort waiter
                ...
            else:
                self.io_proto.transport.close()
            if not self.connection_lost_waiter:
                # Our connection monitor task isn't running.
                try:
                    await asyncio.sleep(self.close_timeout)
                except asyncio.CancelledError:
                    ...
                if self.io_proto and self.io_proto.transport:
                    self.io_proto.transport.abort()
            else:
                if await self.wait_for_connection_lost(
                    timeout=self.close_timeout
                ):
                    # Connection aborted before the timeout expired.
                    return
                error_logger.warning(
                    "Timeout waiting for TCP connection to close. Aborting"
                )
                if self.io_proto and self.io_proto.transport:
                    self.io_proto.transport.abort()
Пример #19
0
    async def create_server(
        self,
        host: Optional[str] = None,
        port: Optional[int] = None,
        *,
        debug: bool = False,
        ssl: Union[None, SSLContext, dict, str, list, tuple] = None,
        sock: Optional[socket] = None,
        protocol: Type[Protocol] = None,
        backlog: int = 100,
        access_log: Optional[bool] = None,
        unix: Optional[str] = None,
        return_asyncio_server: bool = False,
        asyncio_server_kwargs: Dict[str, Any] = None,
        noisy_exceptions: Optional[bool] = None,
    ) -> Optional[AsyncioServer]:
        """
        Asynchronous version of :func:`run`.

        This method will take care of the operations necessary to invoke
        the *before_start* events via :func:`trigger_events` method invocation
        before starting the *sanic* app in Async mode.

        .. note::
            This does not support multiprocessing and is not the preferred
            way to run a :class:`Sanic` application.

        :param host: Address to host on
        :type host: str
        :param port: Port to host on
        :type port: int
        :param debug: Enables debug output (slows server)
        :type debug: bool
        :param ssl: SSLContext, or location of certificate and key
                    for SSL encryption of worker(s)
        :type ssl: SSLContext or dict
        :param sock: Socket for the server to accept connections from
        :type sock: socket
        :param protocol: Subclass of asyncio Protocol class
        :type protocol: type[Protocol]
        :param backlog: a number of unaccepted connections that the system
                        will allow before refusing new connections
        :type backlog: int
        :param access_log: Enables writing access logs (slows server)
        :type access_log: bool
        :param return_asyncio_server: flag that defines whether there's a need
                                      to return asyncio.Server or
                                      start it serving right away
        :type return_asyncio_server: bool
        :param asyncio_server_kwargs: key-value arguments for
                                      asyncio/uvloop create_server method
        :type asyncio_server_kwargs: dict
        :param noisy_exceptions: Log exceptions that are normally considered
                                 to be quiet/silent
        :type noisy_exceptions: bool
        :return: AsyncioServer if return_asyncio_server is true, else Nothing
        """

        if sock is None:
            host, port = host or "127.0.0.1", port or 8000

        if protocol is None:
            protocol = (
                WebSocketProtocol if self.websocket_enabled else HttpProtocol
            )

        # Set explicitly passed configuration values
        for attribute, value in {
            "ACCESS_LOG": access_log,
            "NOISY_EXCEPTIONS": noisy_exceptions,
        }.items():
            if value is not None:
                setattr(self.config, attribute, value)

        server_settings = self._helper(
            host=host,
            port=port,
            debug=debug,
            ssl=ssl,
            sock=sock,
            unix=unix,
            loop=get_event_loop(),
            protocol=protocol,
            backlog=backlog,
            run_async=return_asyncio_server,
        )

        if self.config.USE_UVLOOP is not _default:
            error_logger.warning(
                "You are trying to change the uvloop configuration, but "
                "this is only effective when using the run(...) method. "
                "When using the create_server(...) method Sanic will use "
                "the already existing loop."
            )

        main_start = server_settings.pop("main_start", None)
        main_stop = server_settings.pop("main_stop", None)
        if main_start or main_stop:
            logger.warning(
                "Listener events for the main process are not available "
                "with create_server()"
            )

        return await serve(
            asyncio_server_kwargs=asyncio_server_kwargs, **server_settings
        )
Пример #20
0
def main():
    parser = SanicArgumentParser(
        prog="sanic",
        description=BASE_LOGO,
        formatter_class=lambda prog: RawTextHelpFormatter(
            prog, max_help_position=33),
    )
    parser.add_argument(
        "-v",
        "--version",
        action="version",
        version=f"Sanic {__version__}; Routing {__routing_version__}",
    )
    parser.add_argument(
        "--factory",
        action="store_true",
        help=("Treat app as an application factory, "
              "i.e. a () -> <Sanic app> callable"),
    )
    parser.add_argument(
        "-s",
        "--simple",
        dest="simple",
        action="store_true",
        help="Run Sanic as a Simple Server (module arg should be a path)\n ",
    )
    parser.add_argument(
        "-H",
        "--host",
        dest="host",
        type=str,
        default="127.0.0.1",
        help="Host address [default 127.0.0.1]",
    )
    parser.add_argument(
        "-p",
        "--port",
        dest="port",
        type=int,
        default=8000,
        help="Port to serve on [default 8000]",
    )
    parser.add_argument(
        "-u",
        "--unix",
        dest="unix",
        type=str,
        default="",
        help="location of unix socket\n ",
    )
    parser.add_argument(
        "--cert",
        dest="cert",
        type=str,
        help="Location of fullchain.pem, bundle.crt or equivalent",
    )
    parser.add_argument(
        "--key",
        dest="key",
        type=str,
        help="Location of privkey.pem or equivalent .key file",
    )
    parser.add_argument(
        "--tls",
        metavar="DIR",
        type=str,
        action="append",
        help="TLS certificate folder with fullchain.pem and privkey.pem\n"
        "May be specified multiple times to choose of multiple certificates",
    )
    parser.add_argument(
        "--tls-strict-host",
        dest="tlshost",
        action="store_true",
        help="Only allow clients that send an SNI matching server certs\n ",
    )
    parser.add_bool_arguments("--access-logs",
                              dest="access_log",
                              help="display access logs")
    parser.add_argument(
        "-w",
        "--workers",
        dest="workers",
        type=int,
        default=1,
        help="number of worker processes [default 1]\n ",
    )
    parser.add_argument("-d", "--debug", dest="debug", action="store_true")
    parser.add_bool_arguments(
        "--noisy-exceptions",
        dest="noisy_exceptions",
        help="print stack traces for all exceptions",
    )
    parser.add_argument(
        "-r",
        "--reload",
        "--auto-reload",
        dest="auto_reload",
        action="store_true",
        help="Watch source directory for file changes and reload on changes",
    )
    parser.add_argument(
        "-R",
        "--reload-dir",
        dest="path",
        action="append",
        help="Extra directories to watch and reload on changes\n ",
    )
    parser.add_argument(
        "module",
        help=("Path to your Sanic app. Example: path.to.server:app\n"
              "If running a Simple Server, path to directory to serve. "
              "Example: ./\n"),
    )
    args = parser.parse_args()

    # Custom TLS mismatch handling for better diagnostics
    if (
            # one of cert/key missing
            bool(args.cert) != bool(args.key)
            # new and old style args used together
            or args.tls and args.cert
            # strict host checking without certs would always fail
            or args.tlshost and not args.tls and not args.cert):
        parser.print_usage(sys.stderr)
        error_logger.error(
            "sanic: error: TLS certificates must be specified by either of:\n"
            "  --cert certdir/fullchain.pem --key certdir/privkey.pem\n"
            "  --tls certdir  (equivalent to the above)")
        sys.exit(1)

    try:
        module_path = os.path.abspath(os.getcwd())
        if module_path not in sys.path:
            sys.path.append(module_path)

        if args.simple:
            path = Path(args.module)
            app = create_simple_server(path)
        else:
            delimiter = ":" if ":" in args.module else "."
            module_name, app_name = args.module.rsplit(delimiter, 1)

            if app_name.endswith("()"):
                args.factory = True
                app_name = app_name[:-2]

            module = import_module(module_name)
            app = getattr(module, app_name, None)
            if args.factory:
                app = app()

            app_type_name = type(app).__name__

            if not isinstance(app, Sanic):
                raise ValueError(
                    f"Module is not a Sanic app, it is a {app_type_name}.  "
                    f"Perhaps you meant {args.module}.app?")

        ssl: Union[None, dict, str, list] = []
        if args.tlshost:
            ssl.append(None)
        if args.cert is not None or args.key is not None:
            ssl.append(dict(cert=args.cert, key=args.key))
        if args.tls:
            ssl += args.tls
        if not ssl:
            ssl = None
        elif len(ssl) == 1 and ssl[0] is not None:
            # Use only one cert, no TLSSelector.
            ssl = ssl[0]
        kwargs = {
            "host": args.host,
            "port": args.port,
            "unix": args.unix,
            "workers": args.workers,
            "debug": args.debug,
            "access_log": args.access_log,
            "ssl": ssl,
            "noisy_exceptions": args.noisy_exceptions,
        }

        if args.auto_reload:
            kwargs["auto_reload"] = True

        if args.path:
            if args.auto_reload or args.debug:
                kwargs["reload_dir"] = args.path
            else:
                error_logger.warning(
                    "Ignoring '--reload-dir' since auto reloading was not "
                    "enabled. If you would like to watch directories for "
                    "changes, consider using --debug or --auto-reload.")

        app.run(**kwargs)
    except ImportError as e:
        if module_name.startswith(e.name):
            error_logger.error(
                f"No module named {e.name} found.\n"
                "  Example File: project/sanic_server.py -> app\n"
                "  Example Module: project.sanic_server.app")
        else:
            raise e
    except ValueError:
        error_logger.exception("Failed to run app")
Пример #21
0
    def _helper(
        self,
        host: Optional[str] = None,
        port: Optional[int] = None,
        debug: bool = False,
        ssl: Union[None, SSLContext, dict, str, list, tuple] = None,
        sock: Optional[socket] = None,
        unix: Optional[str] = None,
        workers: int = 1,
        loop: AbstractEventLoop = None,
        protocol: Type[Protocol] = HttpProtocol,
        backlog: int = 100,
        register_sys_signals: bool = True,
        run_async: bool = False,
    ) -> Dict[str, Any]:
        """Helper function used by `run` and `create_server`."""
        if self.config.PROXIES_COUNT and self.config.PROXIES_COUNT < 0:
            raise ValueError(
                "PROXIES_COUNT cannot be negative. "
                "https://sanic.readthedocs.io/en/latest/sanic/config.html"
                "#proxy-configuration"
            )

        ssl = process_to_context(ssl)

        if not self.state.is_debug:
            self.state.mode = Mode.DEBUG if debug else Mode.PRODUCTION

        self.state.host = host or ""
        self.state.port = port or 0
        self.state.workers = workers
        self.state.ssl = ssl
        self.state.unix = unix
        self.state.sock = sock

        server_settings = {
            "protocol": protocol,
            "host": host,
            "port": port,
            "sock": sock,
            "unix": unix,
            "ssl": ssl,
            "app": self,
            "signal": ServerSignal(),
            "loop": loop,
            "register_sys_signals": register_sys_signals,
            "backlog": backlog,
        }

        self.motd(self.serve_location)

        if sys.stdout.isatty() and not self.state.is_debug:
            error_logger.warning(
                f"{Colors.YELLOW}Sanic is running in PRODUCTION mode. "
                "Consider using '--debug' or '--dev' while actively "
                f"developing your application.{Colors.END}"
            )

        # Register start/stop events
        for event_name, settings_name, reverse in (
            ("main_process_start", "main_start", False),
            ("main_process_stop", "main_stop", True),
        ):
            listeners = self.listeners[event_name].copy()
            if reverse:
                listeners.reverse()
            # Prepend sanic to the arguments when listeners are triggered
            listeners = [partial(listener, self) for listener in listeners]
            server_settings[settings_name] = listeners  # type: ignore

        if run_async:
            server_settings["run_async"] = True

        return server_settings
Пример #22
0
    async def _start_servers(
        self,
        primary: Sanic,
        _,
        apps: List[Sanic],
    ) -> None:
        for app in apps:
            if (
                app.name is not primary.name
                and app.state.workers != primary.state.workers
                and app.state.server_info
            ):
                message = (
                    f"The primary application {repr(primary)} is running "
                    f"with {primary.state.workers} worker(s). All "
                    "application instances will run with the same number. "
                    f"You requested {repr(app)} to run with "
                    f"{app.state.workers} worker(s), which will be ignored "
                    "in favor of the primary application."
                )
                if sys.stdout.isatty():
                    message = "".join(
                        [
                            Colors.YELLOW,
                            message,
                            Colors.END,
                        ]
                    )
                error_logger.warning(message, exc_info=True)
            for server_info in app.state.server_info:
                if server_info.stage is not ServerStage.SERVING:
                    app.state.primary = False
                    handlers = [
                        *server_info.settings.pop("main_start", []),
                        *server_info.settings.pop("main_stop", []),
                    ]
                    if handlers:
                        error_logger.warning(
                            f"Sanic found {len(handlers)} listener(s) on "
                            "secondary applications attached to the main "
                            "process. These will be ignored since main "
                            "process listeners can only be attached to your "
                            "primary application: "
                            f"{repr(primary)}"
                        )

                    if not server_info.settings["loop"]:
                        server_info.settings["loop"] = get_running_loop()

                    try:
                        server_info.server = await serve(
                            **server_info.settings,
                            run_async=True,
                            reuse_port=bool(primary.state.workers - 1),
                        )
                    except OSError as e:  # no cov
                        first_message = (
                            "An OSError was detected on startup. "
                            "The encountered error was: "
                        )
                        second_message = str(e)
                        if sys.stdout.isatty():
                            message_parts = [
                                Colors.YELLOW,
                                first_message,
                                Colors.RED,
                                second_message,
                                Colors.END,
                            ]
                        else:
                            message_parts = [first_message, second_message]
                        message = "".join(message_parts)
                        error_logger.warning(message, exc_info=True)
                        continue
                    primary.add_task(
                        self._run_server(app, server_info), name="RunServer"
                    )
Пример #23
0
def main():
    parser = SanicArgumentParser(
        prog="sanic",
        description=BASE_LOGO,
        formatter_class=lambda prog: RawTextHelpFormatter(
            prog, max_help_position=33),
    )
    parser.add_argument(
        "-v",
        "--version",
        action="version",
        version=f"Sanic {__version__}; Routing {__routing_version__}",
    )
    parser.add_argument(
        "--factory",
        action="store_true",
        help=("Treat app as an application factory, "
              "i.e. a () -> <Sanic app> callable"),
    )
    parser.add_argument(
        "-s",
        "--simple",
        dest="simple",
        action="store_true",
        help="Run Sanic as a Simple Server (module arg should be a path)\n ",
    )
    parser.add_argument(
        "-H",
        "--host",
        dest="host",
        type=str,
        default="127.0.0.1",
        help="Host address [default 127.0.0.1]",
    )
    parser.add_argument(
        "-p",
        "--port",
        dest="port",
        type=int,
        default=8000,
        help="Port to serve on [default 8000]",
    )
    parser.add_argument(
        "-u",
        "--unix",
        dest="unix",
        type=str,
        default="",
        help="location of unix socket\n ",
    )
    parser.add_argument("--cert",
                        dest="cert",
                        type=str,
                        help="Location of certificate for SSL")
    parser.add_argument("--key",
                        dest="key",
                        type=str,
                        help="location of keyfile for SSL\n ")
    parser.add_bool_arguments("--access-logs",
                              dest="access_log",
                              help="display access logs")
    parser.add_argument(
        "-w",
        "--workers",
        dest="workers",
        type=int,
        default=1,
        help="number of worker processes [default 1]\n ",
    )
    parser.add_argument("-d", "--debug", dest="debug", action="store_true")
    parser.add_argument(
        "-r",
        "--reload",
        "--auto-reload",
        dest="auto_reload",
        action="store_true",
        help="Watch source directory for file changes and reload on changes",
    )
    parser.add_argument(
        "-R",
        "--reload-dir",
        dest="path",
        action="append",
        help="Extra directories to watch and reload on changes\n ",
    )
    parser.add_argument(
        "module",
        help=("Path to your Sanic app. Example: path.to.server:app\n"
              "If running a Simple Server, path to directory to serve. "
              "Example: ./\n"),
    )
    args = parser.parse_args()

    try:
        module_path = os.path.abspath(os.getcwd())
        if module_path not in sys.path:
            sys.path.append(module_path)

        if args.simple:
            path = Path(args.module)
            app = create_simple_server(path)
        else:
            delimiter = ":" if ":" in args.module else "."
            module_name, app_name = args.module.rsplit(delimiter, 1)

            if app_name.endswith("()"):
                args.factory = True
                app_name = app_name[:-2]

            module = import_module(module_name)
            app = getattr(module, app_name, None)
            if args.factory:
                app = app()

            app_type_name = type(app).__name__

            if not isinstance(app, Sanic):
                raise ValueError(
                    f"Module is not a Sanic app, it is a {app_type_name}.  "
                    f"Perhaps you meant {args.module}.app?")
        if args.cert is not None or args.key is not None:
            ssl: Optional[Dict[str, Any]] = {
                "cert": args.cert,
                "key": args.key,
            }
        else:
            ssl = None

        kwargs = {
            "host": args.host,
            "port": args.port,
            "unix": args.unix,
            "workers": args.workers,
            "debug": args.debug,
            "access_log": args.access_log,
            "ssl": ssl,
        }
        if args.auto_reload:
            kwargs["auto_reload"] = True

        if args.path:
            if args.auto_reload or args.debug:
                kwargs["reload_dir"] = args.path
            else:
                error_logger.warning(
                    "Ignoring '--reload-dir' since auto reloading was not "
                    "enabled. If you would like to watch directories for "
                    "changes, consider using --debug or --auto-reload.")

        app.run(**kwargs)
    except ImportError as e:
        if module_name.startswith(e.name):
            error_logger.error(
                f"No module named {e.name} found.\n"
                "  Example File: project/sanic_server.py -> app\n"
                "  Example Module: project.sanic_server.app")
        else:
            raise e
    except ValueError:
        error_logger.exception("Failed to run app")
Пример #24
0
}),
              required=True,
              location="body",
              content_type="application/json")
@doc.response(200, CourierModel.schema(), description="Courier updated")
@doc.response(404, None, description="Courier not found")
@doc.response(400, None, description="Bad request")
async def update_courier(request: Request,
                         courier_id: int) -> response.HTTPResponse:
    if invalid_fields := is_json_patching_courier_valid(request.json):
        error_logger.warning("Only %s might be updated, but %s found",
                             PATCHABLE_FIELDS, invalid_fields)
        return response.HTTPResponse(status=400)

    if (courier := await app.db.get_courier(courier_id)) is None:
        error_logger.warning("Courier id=%s not found", courier_id)
        return response.HTTPResponse(status=404)

    courier = CourierModel(**courier.external())

    try:
        courier_data = {**courier.dict(), **request.json}
        updated_courier = CourierModel(**courier_data)
    except ValidationError as e:
        error_logger.warning(e.json(indent=4))
        return response.HTTPResponse(status=400)

    await app.db.update_courier(courier_id=courier.courier_id, **request.json)

    return response.json(updated_courier.dict(), indent=4)
Пример #25
0
async def assign(request: Request) -> response.HTTPResponse:
    courier_id = request.json.get('courier_id', -1)

    if (courier := await app.db.get_courier(courier_id)) is None:
        error_logger.warning("Courier id=%s not found", courier_id)
        return response.HTTPResponse(status=400)