Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

### Fixed
Expand All @@ -26,10 +27,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
recognized filename patterns, the file patterns `app-*.py`, `app_*.py`, `*-app.py`,
and `*_app.py` are now considered. However, if the directory contains more than
one file matching these new patterns, you must provide rsconnect-python with an
explicit `--entrypoint` argument.
explicit `--entrypoint` argument.\
- Added a new verbose logging level. Specifying `-v` on the command line uses this
new level. Currently this will cause filenames to be logged as they are added to
a bundle. To enable maximum verbosity (debug level), use `-vv`.
- Added the `deploy flask` command.
- Added the `write-manifest flask` command.

### Changed
- Removing experimental support for Conda. Connect does not support restoring Conda environments.
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,9 @@ rsconnect write-manifest notebook my-notebook.ipynb
You can deploy a variety of APIs and applications using sub-commands of the
`rsconnect deploy` command.

* `api`: WSGI-compliant APIs such as Flask and packages based on Flask
* `fastapi`: ASGI-compliant APIs (FastAPI, Quart, Sanic, and Falcon)
* `api`: WSGI-compliant APIs (e.g., `bottle`, `falcon`, `flask`, `flask-restx`, `flasgger`, `pycnic`).
* `flask`: Flask APIs (_Note: `flask` is an alias of `api`._).
* `fastapi`: ASGI-compliant APIs (e.g, `fastapi`, `quart`, `sanic`, `starlette`)
* `dash`: Python Dash apps
* `streamlit`: Streamlit apps
* `bokeh`: Bokeh server apps
Expand Down
53 changes: 31 additions & 22 deletions rsconnect/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import click
from os.path import abspath, dirname, exists, isdir, join
from functools import wraps
from typing import Optional

from rsconnect.certificates import read_certificate_file

Expand Down Expand Up @@ -65,6 +66,7 @@
from .log import logger, LogOutputFormat
from .metadata import ServerStore, AppStore
from .models import (
AppMode,
AppModes,
BuildStatus,
ContentGuidWithBundleParamType,
Expand Down Expand Up @@ -1254,18 +1256,22 @@ def deploy_html(
)


def generate_deploy_python(app_mode, alias, min_version):
def generate_deploy_python(app_mode: AppMode, alias: str, min_version: str, desc: Optional[str] = None):

if desc is None:
desc = app_mode.desc()

# noinspection SpellCheckingInspection
@deploy.command(
name=alias,
short_help="Deploy a {desc} to Posit Connect [v{version}+], Posit Cloud, or shinyapps.io.".format(
desc=app_mode.desc(),
desc=desc,
version=min_version,
),
help=(
"Deploy a {desc} module to Posit Connect, Posit Cloud, or shinyapps.io (if supported by the platform). "
'The "directory" argument must refer to an existing directory that contains the application code.'
).format(desc=app_mode.desc()),
).format(desc=desc),
no_args_is_help=True,
)
@server_args
Expand All @@ -1277,7 +1283,7 @@ def generate_deploy_python(app_mode, alias, min_version):
"-e",
help=(
"The module and executable object which serves as the entry point for the {desc} (defaults to app)"
).format(desc=app_mode.desc()),
).format(desc=desc),
)
@click.option(
"--exclude",
Expand Down Expand Up @@ -1372,14 +1378,13 @@ def deploy_app(
return deploy_app


deploy_api = generate_deploy_python(app_mode=AppModes.PYTHON_API, alias="api", min_version="1.8.2")
# TODO: set fastapi min_version correctly
# deploy_fastapi = generate_deploy_python(app_mode=AppModes.PYTHON_FASTAPI, alias="fastapi", min_version="2021.08.0")
deploy_fastapi = generate_deploy_python(app_mode=AppModes.PYTHON_FASTAPI, alias="fastapi", min_version="2021.08.0")
deploy_dash_app = generate_deploy_python(app_mode=AppModes.DASH_APP, alias="dash", min_version="1.8.2")
deploy_streamlit_app = generate_deploy_python(app_mode=AppModes.STREAMLIT_APP, alias="streamlit", min_version="1.8.4")
deploy_bokeh_app = generate_deploy_python(app_mode=AppModes.BOKEH_APP, alias="bokeh", min_version="1.8.4")
deploy_shiny = generate_deploy_python(app_mode=AppModes.PYTHON_SHINY, alias="shiny", min_version="2022.07.0")
generate_deploy_python(app_mode=AppModes.PYTHON_API, alias="api", min_version="1.8.2")
generate_deploy_python(app_mode=AppModes.PYTHON_API, alias="flask", min_version="1.8.2", desc="Flask API")
generate_deploy_python(app_mode=AppModes.PYTHON_FASTAPI, alias="fastapi", min_version="2021.08.0")
generate_deploy_python(app_mode=AppModes.DASH_APP, alias="dash", min_version="1.8.2")
generate_deploy_python(app_mode=AppModes.STREAMLIT_APP, alias="streamlit", min_version="1.8.4")
generate_deploy_python(app_mode=AppModes.BOKEH_APP, alias="bokeh", min_version="1.8.4")
generate_deploy_python(app_mode=AppModes.PYTHON_SHINY, alias="shiny", min_version="2022.07.0")


@deploy.command(
Expand Down Expand Up @@ -1711,24 +1716,27 @@ def write_manifest_quarto(
)


def generate_write_manifest_python(app_mode, alias):
def generate_write_manifest_python(app_mode, alias, desc: Optional[str] = None):
if desc is None:
desc = app_mode.desc()

# noinspection SpellCheckingInspection
@write_manifest.command(
name=alias,
short_help="Create a manifest.json file for a {desc}.".format(desc=app_mode.desc()),
short_help="Create a manifest.json file for a {desc}.".format(desc=desc),
help=(
"Create a manifest.json file for a {desc} for later deployment. This will create an "
'environment file ("requirements.txt") if one does not exist. All files '
"are created in the same directory as the API code."
).format(desc=app_mode.desc()),
).format(desc=desc),
)
@click.option("--overwrite", "-o", is_flag=True, help="Overwrite manifest.json, if it exists.")
@click.option(
"--entrypoint",
"-e",
help=(
"The module and executable object which serves as the entry point for the {desc} (defaults to app)"
).format(desc=app_mode.desc()),
).format(desc=desc),
)
@click.option(
"--exclude",
Expand Down Expand Up @@ -1793,12 +1801,13 @@ def manifest_writer(
return manifest_writer


write_manifest_api = generate_write_manifest_python(AppModes.PYTHON_API, alias="api")
write_manifest_fastapi = generate_write_manifest_python(AppModes.PYTHON_FASTAPI, alias="fastapi")
write_manifest_dash = generate_write_manifest_python(AppModes.DASH_APP, alias="dash")
write_manifest_streamlit = generate_write_manifest_python(AppModes.STREAMLIT_APP, alias="streamlit")
write_manifest_bokeh = generate_write_manifest_python(AppModes.BOKEH_APP, alias="bokeh")
write_manifest_shiny = generate_write_manifest_python(AppModes.PYTHON_SHINY, alias="shiny")
generate_write_manifest_python(AppModes.BOKEH_APP, alias="bokeh")
generate_write_manifest_python(AppModes.DASH_APP, alias="dash")
generate_write_manifest_python(AppModes.PYTHON_API, alias="api")
generate_write_manifest_python(AppModes.PYTHON_API, alias="flask", desc="Flask API")
generate_write_manifest_python(AppModes.PYTHON_FASTAPI, alias="fastapi")
generate_write_manifest_python(AppModes.PYTHON_SHINY, alias="shiny")
generate_write_manifest_python(AppModes.STREAMLIT_APP, alias="streamlit")


# noinspection SpellCheckingInspection
Expand Down