Skip to content

proxystore.endpoint.serve

Endpoint serving.

create_app()

create_app(
    endpoint: Endpoint,
    max_content_length: int | None = None,
    body_timeout: int = 300,
) -> quart.Quart

Create quart app for endpoint and registers routes.

Parameters:

  • endpoint (Endpoint) –

    Initialized endpoint to forward quart routes to.

  • max_content_length (int | None) –

    Max request body size in bytes.

  • body_timeout (int) –

    Number of seconds to wait for the body to be completely received.

Returns:

Source code in proxystore/endpoint/serve.py
def create_app(
    endpoint: Endpoint,
    max_content_length: int | None = None,
    body_timeout: int = 300,
) -> quart.Quart:
    """Create quart app for endpoint and registers routes.

    Args:
        endpoint: Initialized endpoint to forward quart routes to.
        max_content_length: Max request body size in bytes.
        body_timeout: Number of seconds to wait for the body to be
            completely received.

    Returns:
        Quart app.
    """
    app = quart.Quart(__name__)

    app.config['endpoint'] = endpoint

    app.register_blueprint(routes_blueprint, url_prefix='')

    logger.info(
        'Quart routes registered to endpoint '
        f'{endpoint.uuid} ({endpoint.name})',
    )

    app.config['MAX_CONTENT_LENGTH'] = max_content_length
    app.config['BODY_TIMEOUT'] = body_timeout

    return app

serve()

serve(
    config: EndpointConfig,
    *,
    log_level: int | str = logging.INFO,
    log_file: str | None = None,
    use_uvloop: bool = True
) -> None

Initialize endpoint and serve Quart app.

Warning

This function does not return until the Quart app is terminated.

Parameters:

  • config (EndpointConfig) –

    Configuration object.

  • log_level (int | str) –

    Logging level of endpoint.

  • log_file (str | None) –

    Optional file path to append log to.

  • use_uvloop (bool) –

    Install uvloop as the default event loop implementation.

Source code in proxystore/endpoint/serve.py
def serve(
    config: EndpointConfig,
    *,
    log_level: int | str = logging.INFO,
    log_file: str | None = None,
    use_uvloop: bool = True,
) -> None:
    """Initialize endpoint and serve Quart app.

    Warning:
        This function does not return until the Quart app is terminated.

    Args:
        config: Configuration object.
        log_level: Logging level of endpoint.
        log_file: Optional file path to append log to.
        use_uvloop: Install uvloop as the default event loop implementation.
    """
    if config.host is None:
        raise ValueError('EndpointConfig has NoneType as host.')

    if log_file is not None:
        parent_dir = os.path.dirname(log_file)
        if not os.path.isdir(parent_dir):
            os.makedirs(parent_dir, exist_ok=True)
        logging.getLogger().handlers.append(logging.FileHandler(log_file))

    for handler in logging.getLogger().handlers:
        handler.setFormatter(
            logging.Formatter(
                '[%(asctime)s.%(msecs)03d] %(levelname)-5s (%(name)s) :: '
                '%(message)s',
                datefmt='%Y-%m-%d %H:%M:%S',
            ),
        )
    logging.getLogger().setLevel(log_level)

    kwargs = dataclasses.asdict(config)
    # These are the only two EndpointConfig attributes not passed to the
    # Endpoint constructor
    kwargs.pop('host', None)
    kwargs.pop('port', None)

    database_path = kwargs.pop('database_path', None)
    storage: SQLiteStorage | None
    if database_path is not None:
        logger.info(
            f'Using SQLite database for storage (path: {database_path})',
        )
        storage = SQLiteStorage(database_path)
    else:
        logger.warning(
            'Database path not provided. Data will not be persisted',
        )
        storage = None

    endpoint = Endpoint(**kwargs, storage=storage)
    app = create_app(endpoint)

    if use_uvloop:  # pragma: no cover
        logger.info('Installing uvloop as default event loop')
        uvloop.install()
    else:
        logger.warning(
            'Not installing uvloop. Uvicorn may override and install anyways',
        )

    server_config = uvicorn.Config(
        app,
        host=config.host,
        port=config.port,
        log_config=None,
        log_level=logger.level,
        access_log=False,
    )
    server = uvicorn.Server(server_config)

    logger.info(
        f'Serving endpoint {endpoint.uuid} ({endpoint.name}) on '
        f'{config.host}:{config.port}',
    )
    logger.info(f'Config: {config}')
    asyncio.run(server.serve())