From 58cb514ddb39ad69524bc6240bf7fcf8b5fc8443 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Fri, 28 Oct 2022 13:59:00 -0400 Subject: [PATCH 01/16] update actions to mostly reuse CLI deploy_app --- rsconnect/actions.py | 167 +++++++++++++++++++------------------------ 1 file changed, 72 insertions(+), 95 deletions(-) diff --git a/rsconnect/actions.py b/rsconnect/actions.py index 80135385..b88a5391 100644 --- a/rsconnect/actions.py +++ b/rsconnect/actions.py @@ -18,6 +18,7 @@ from .exception import RSConnectException from . import api from .bundle import ( + create_python_environment, make_api_bundle, make_api_manifest, make_html_bundle, @@ -794,6 +795,71 @@ def fake_module_file_from_directory(directory: str): return join(directory, app_name + ".py") +def deploy_app( + name: str = None, + server: str = None, + api_key: str = None, + insecure: bool = None, + cacert: typing.IO = None, + ca_data: str = None, + entry_point: str = None, + excludes: typing.List[str] = None, + new: bool = False, + app_id: str = None, + title: str = None, + python: str = None, + conda_mode: bool = False, + force_generate: bool = False, + verbose: bool = None, + directory: str = None, + extra_files: typing.List[str] = None, + env_vars: typing.Dict[str, str] = None, + image: str = None, + account: str = None, + token: str = None, + secret: str = None, + app_mode: str = None, + connect_server: api.RSConnectServer = None, + **kws, +): + if connect_server: + server = connect_server.url + api_key = connect_server.api_key + insecure = connect_server.insecure + ca_data = connect_server.ca_data + kwargs = locals() + print(kwargs) + + kwargs["entry_point"] = entry_point = validate_entry_point(entry_point, directory) + kwargs["extra_files"] = extra_files = validate_extra_files(directory, extra_files) + environment = create_python_environment( + directory, + force_generate, + python, + conda_mode, + ) + + ce = RSConnectExecutor(**kwargs) + ( + ce.validate_server() + .validate_app_mode(app_mode=app_mode) + .check_server_capabilities([are_apis_supported_on_server]) + .make_bundle( + make_api_bundle, + directory, + entry_point, + app_mode, + environment, + extra_files, + excludes, + image=image, + ) + .deploy_bundle() + .save_deployed_info() + .emit_task_log() + ) + + def deploy_python_api( connect_server: api.RSConnectServer, directory: str, @@ -835,22 +901,7 @@ def deploy_python_api( :return: the ultimate URL where the deployed app may be accessed and the sequence of log lines. The log lines value will be None if a log callback was provided. """ - return _deploy_by_python_framework( - connect_server, - directory, - extra_files, - excludes, - entry_point, - gather_basic_deployment_info_for_api, - image, - new, - app_id, - title, - python, - conda_mode, - force_generate, - log_callback, - ) + return deploy_app(app_mode=AppModes.PYTHON_API, **locals()) def deploy_python_fastapi( @@ -894,22 +945,7 @@ def deploy_python_fastapi( :return: the ultimate URL where the deployed app may be accessed and the sequence of log lines. The log lines value will be None if a log callback was provided. """ - return _deploy_by_python_framework( - connect_server, - directory, - extra_files, - excludes, - entry_point, - gather_basic_deployment_info_for_fastapi, - image, - new, - app_id, - title, - python, - conda_mode, - force_generate, - log_callback, - ) + return deploy_app(app_mode=AppModes.PYTHON_FASTAPI, **locals()) def deploy_python_shiny( @@ -951,21 +987,7 @@ def deploy_python_shiny( :return: the ultimate URL where the deployed app may be accessed and the sequence of log lines. The log lines value will be None if a log callback was provided. """ - return _deploy_by_python_framework( - connect_server, - directory, - extra_files, - excludes, - entry_point, - gather_basic_deployment_info_for_shiny, - new, - app_id, - title, - python, - conda_mode, - force_generate, - log_callback, - ) + return deploy_app(app_mode=AppModes.PYTHON_SHINY, **locals()) def deploy_dash_app( @@ -1009,22 +1031,7 @@ def deploy_dash_app( :return: the ultimate URL where the deployed app may be accessed and the sequence of log lines. The log lines value will be None if a log callback was provided. """ - return _deploy_by_python_framework( - connect_server, - directory, - extra_files, - excludes, - entry_point, - gather_basic_deployment_info_for_dash, - image, - new, - app_id, - title, - python, - conda_mode, - force_generate, - log_callback, - ) + return deploy_app(app_mode=AppModes.DASH_APP, **locals()) def deploy_streamlit_app( @@ -1068,22 +1075,7 @@ def deploy_streamlit_app( :return: the ultimate URL where the deployed app may be accessed and the sequence of log lines. The log lines value will be None if a log callback was provided. """ - return _deploy_by_python_framework( - connect_server, - directory, - extra_files, - excludes, - entry_point, - gather_basic_deployment_info_for_streamlit, - image, - new, - app_id, - title, - python, - conda_mode, - force_generate, - log_callback, - ) + return deploy_app(app_mode=AppModes.STREAMLIT_APP, **locals()) def deploy_bokeh_app( @@ -1127,22 +1119,7 @@ def deploy_bokeh_app( :return: the ultimate URL where the deployed app may be accessed and the sequence of log lines. The log lines value will be None if a log callback was provided. """ - return _deploy_by_python_framework( - connect_server, - directory, - extra_files, - excludes, - entry_point, - gather_basic_deployment_info_for_bokeh, - image, - new, - app_id, - title, - python, - conda_mode, - force_generate, - log_callback, - ) + return deploy_app(app_mode=AppModes.BOKEH_APP, **locals()) def _deploy_by_python_framework( From da35e98c8b7d77c584e73de1491846b79c90e83c Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Fri, 28 Oct 2022 14:00:26 -0400 Subject: [PATCH 02/16] deprecate _deploy_by_python_framework --- rsconnect/actions.py | 76 -------------------------------------------- 1 file changed, 76 deletions(-) diff --git a/rsconnect/actions.py b/rsconnect/actions.py index b88a5391..2cd61ac6 100644 --- a/rsconnect/actions.py +++ b/rsconnect/actions.py @@ -1122,82 +1122,6 @@ def deploy_bokeh_app( return deploy_app(app_mode=AppModes.BOKEH_APP, **locals()) -def _deploy_by_python_framework( - connect_server: api.RSConnectServer, - directory: str, - extra_files: typing.List[str], - excludes: typing.List[str], - entry_point: str, - gatherer: typing.Callable, - image: str, - new: bool, - app_id: int, - title: str, - python: str, - conda_mode: bool, - force_generate: bool, - log_callback: typing.Callable, -) -> typing.Tuple[str, typing.Union[list, None]]: - """ - A function to deploy a Python WSGi API module to Connect. Depending on the files involved - and network latency, this may take a bit of time. - - :param connect_server: the Connect server information. - :param directory: the app directory to deploy. - :param extra_files: any extra files that should be included in the deploy. - :param excludes: a sequence of glob patterns that will exclude matched files. - :param entry_point: the module/executable object for the WSGi framework. - :param gatherer: the function to use to gather basic information. - :param image: the docker image to be specified for off-host execution. Use None if not specified. - :param new: a flag to force this as a new deploy. Previous default = False. - :param app_id: the ID of an existing application to deploy new files for. Previous default = None. - :param title: an optional title for the deploy. If this is not provided, one will - be generated. Previous default = None. - :param python: the optional name of a Python executable. Previous default = None. - :param conda_mode: use conda to build an environment.yml instead of conda, when - conda is not supported on RStudio Connect (version<=1.8.0). Previous default = False - :param force_generate: force generating "requirements.txt" or "environment.yml", - even if it already exists. Previous default = False - :param log_callback: the callback to use to write the log to. If this is None - (the default) the lines from the deployment log will be returned as a sequence. - If a log callback is provided, then None will be returned for the log lines part - of the return tuple. Previous default = None. - :return: the ultimate URL where the deployed app may be accessed and the sequence - of log lines. The log lines value will be None if a log callback was provided. - """ - module_file = fake_module_file_from_directory(directory) - app_store = AppStore(module_file) - ( - entry_point, - app_id, - deployment_name, - deployment_title, - default_title, - app_mode, - ) = gatherer(connect_server, app_store, directory, entry_point, new, app_id, title) - _, environment = get_python_env_info( - directory, - python, - conda_mode=conda_mode, - force_generate=force_generate, - ) - bundle = create_api_deployment_bundle( - directory, extra_files, excludes, entry_point, app_mode, environment, True, image - ) - return _finalize_deploy( - connect_server, - app_store, - directory, - app_id, - app_mode, - deployment_name, - deployment_title, - default_title, - bundle, - log_callback, - ) - - def deploy_by_manifest( connect_server: api.RSConnectServer, manifest_file_name: str, From 7a734eaf455ad412024fb48e459c17e568a11074 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Fri, 28 Oct 2022 14:08:56 -0400 Subject: [PATCH 03/16] update deploy_by_manifest --- rsconnect/actions.py | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/rsconnect/actions.py b/rsconnect/actions.py index 2cd61ac6..84b2eb6e 100644 --- a/rsconnect/actions.py +++ b/rsconnect/actions.py @@ -19,6 +19,7 @@ from . import api from .bundle import ( create_python_environment, + default_title_from_manifest, make_api_bundle, make_api_manifest, make_html_bundle, @@ -30,6 +31,7 @@ make_source_manifest, manifest_add_buffer, manifest_add_file, + read_manifest_app_mode, read_manifest_file, ) from .environment import Environment, MakeEnvironment, EnvironmentException @@ -1147,28 +1149,27 @@ def deploy_by_manifest( :return: the ultimate URL where the deployed app may be accessed and the sequence of log lines. The log lines value will be None if a log callback was provided. """ - app_store = AppStore(manifest_file_name) + if connect_server: + server = connect_server.url + api_key = connect_server.api_key + insecure = connect_server.insecure + ca_data = connect_server.ca_data + kwargs = locals() + kwargs["manifest_file_name"] = manifest_file_name = validate_manifest_file(manifest_file_name) + app_mode = read_manifest_app_mode(manifest_file_name) + kwargs["title"] = title or default_title_from_manifest(manifest_file_name) + + ce = RSConnectExecutor(**kwargs) ( - app_id, - deployment_name, - deployment_title, - default_title, - app_mode, - _, - _, - ) = gather_basic_deployment_info_from_manifest(connect_server, app_store, manifest_file_name, new, app_id, title) - bundle = make_manifest_bundle(manifest_file_name) - return _finalize_deploy( - connect_server, - app_store, - manifest_file_name, - app_id, - app_mode, - deployment_name, - deployment_title, - default_title, - bundle, - log_callback, + ce.validate_server() + .validate_app_mode(app_mode=app_mode) + .make_bundle( + make_manifest_bundle, + manifest_file_name, + ) + .deploy_bundle() + .save_deployed_info() + .emit_task_log() ) From c45900e1ca767530dc604987683a5f20073bf489 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Fri, 28 Oct 2022 14:15:00 -0400 Subject: [PATCH 04/16] remove debug line --- rsconnect/actions.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/rsconnect/actions.py b/rsconnect/actions.py index 84b2eb6e..d62797eb 100644 --- a/rsconnect/actions.py +++ b/rsconnect/actions.py @@ -830,8 +830,6 @@ def deploy_app( insecure = connect_server.insecure ca_data = connect_server.ca_data kwargs = locals() - print(kwargs) - kwargs["entry_point"] = entry_point = validate_entry_point(entry_point, directory) kwargs["extra_files"] = extra_files = validate_extra_files(directory, extra_files) environment = create_python_environment( From 8ed097577579aae19fb239dc0eed61208b897741 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Fri, 28 Oct 2022 14:22:57 -0400 Subject: [PATCH 05/16] update deploy_jupyter_notebook --- rsconnect/actions.py | 78 ++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/rsconnect/actions.py b/rsconnect/actions.py index d62797eb..ab296d36 100644 --- a/rsconnect/actions.py +++ b/rsconnect/actions.py @@ -18,6 +18,10 @@ from .exception import RSConnectException from . import api from .bundle import ( + _warn_if_environment_directory, + _warn_if_no_requirements_file, + _warn_on_ignored_manifest, + _warn_on_ignored_requirements, create_python_environment, default_title_from_manifest, make_api_bundle, @@ -696,45 +700,41 @@ def deploy_jupyter_notebook( :return: the ultimate URL where the deployed app may be accessed and the sequence of log lines. The log lines value will be None if a log callback was provided. """ - app_store = AppStore(file_name) - (app_id, deployment_name, deployment_title, default_title, app_mode,) = gather_basic_deployment_info_for_notebook( - connect_server, - app_store, - file_name, - new, - app_id, - title, - static, - ) - python, environment = get_python_env_info( - file_name, - python, - conda_mode=conda_mode, - force_generate=force_generate, - ) - bundle = create_notebook_deployment_bundle( - file_name, - extra_files, - app_mode, - python, - environment, - True, - hide_all_input=hide_all_input, - hide_tagged_input=hide_tagged_input, - image=image, - ) - return _finalize_deploy( - connect_server, - app_store, - file_name, - app_id, - app_mode, - deployment_name, - deployment_title, - default_title, - bundle, - log_callback, - ) + kwargs = locals() + kwargs["extra_files"] = extra_files = validate_extra_files(dirname(file_name), extra_files) + app_mode = AppModes.JUPYTER_NOTEBOOK if not static else AppModes.STATIC + + base_dir = dirname(file_name) + _warn_on_ignored_manifest(base_dir) + _warn_if_no_requirements_file(base_dir) + _warn_if_environment_directory(base_dir) + python, environment = get_python_env_info(file_name, python, conda_mode, force_generate) + + if force_generate: + _warn_on_ignored_requirements(base_dir, environment.filename) + + ce = RSConnectExecutor(**kwargs) + ce.validate_server().validate_app_mode(app_mode=app_mode) + if app_mode == AppModes.STATIC: + ce.make_bundle( + make_notebook_html_bundle, + file_name, + python, + hide_all_input, + hide_tagged_input, + image=image, + ) + else: + ce.make_bundle( + make_notebook_source_bundle, + file_name, + environment, + extra_files, + hide_all_input, + hide_tagged_input, + image=image, + ) + ce.deploy_bundle().save_deployed_info().emit_task_log() def _finalize_deploy( From 62a1be98c4cbf465066c8c77f1a9d732325f8d57 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Fri, 28 Oct 2022 14:32:35 -0400 Subject: [PATCH 06/16] deprecate _finalize_deploy --- rsconnect/actions.py | 46 -------------------------------------------- 1 file changed, 46 deletions(-) diff --git a/rsconnect/actions.py b/rsconnect/actions.py index ab296d36..a54db8a8 100644 --- a/rsconnect/actions.py +++ b/rsconnect/actions.py @@ -737,52 +737,6 @@ def deploy_jupyter_notebook( ce.deploy_bundle().save_deployed_info().emit_task_log() -def _finalize_deploy( - connect_server: api.RSConnectServer, - app_store: AppStore, - file_name: str, - app_id: int, - app_mode: AppMode, - deployment_name: str, - title: str, - title_is_default: bool, - bundle: typing.IO[bytes], - log_callback: typing.Callable, -) -> typing.Tuple[str, typing.Union[list, None]]: - """ - A common function to finish up the deploy process once all the data (bundle - included) has been resolved. - - :param connect_server: the Connect server information. - :param app_store: the store for the specified file - :param file_name: the primary file or directory being deployed. - :param app_id: the ID of an existing application to deploy new files for. - :param app_mode: the app mode to use. - :param deployment_name: the name to use for the deploy. - :param title: the title to use for the deploy. - :param title_is_default: a flag noting whether the title carries a defaulted value. - :param bundle: the bundle to deploy. - :param log_callback: the callback to use to write the log to. If this is None - (the default) the lines from the deployment log will be returned as a sequence. - If a log callback is provided, then None will be returned for the log lines part - of the return tuple. - :return: the ultimate URL where the deployed app may be accessed and the sequence - of log lines. The log lines value will be None if a log callback was provided. - """ - app = deploy_bundle(connect_server, app_id, deployment_name, title, title_is_default, bundle, None) - app_url, log_lines, _ = spool_deployment_log(connect_server, app, log_callback) - app_store.set( - connect_server.url, - abspath(file_name), - app_url, - app["app_id"], - app["app_guid"], - title, - app_mode, - ) - return app_url, log_lines - - def fake_module_file_from_directory(directory: str): """ Takes a directory and invents a properly named file that though possibly fake, From 4642fec7ef688760a1058534fd7159f83badbe53 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Fri, 28 Oct 2022 15:01:35 -0400 Subject: [PATCH 07/16] add in server init for deploy_jupyter_notebook --- rsconnect/actions.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/rsconnect/actions.py b/rsconnect/actions.py index a54db8a8..a7f8bd9e 100644 --- a/rsconnect/actions.py +++ b/rsconnect/actions.py @@ -656,7 +656,7 @@ def deploy_html( def deploy_jupyter_notebook( - connect_server: api.RSConnectServer, + connect_server: api.TargetableServer, file_name: str, extra_files: typing.List[str], new: bool, @@ -704,6 +704,26 @@ def deploy_jupyter_notebook( kwargs["extra_files"] = extra_files = validate_extra_files(dirname(file_name), extra_files) app_mode = AppModes.JUPYTER_NOTEBOOK if not static else AppModes.STATIC + if isinstance(connect_server, api.RSConnectServer): + ce = RSConnectExecutor( + url=connect_server.url, + api_key=connect_server.api_key, + insecure=connect_server.insecure, + ca_data=connect_server.ca_data, + cookies=connect_server.cookie_jar, + **kwargs, + ) + elif isinstance(connect_server, api.ShinyappsServer) or isinstance(connect_server, api.CloudServer): + ce = RSConnectExecutor( + url=connect_server.url, + account=connect_server.account_name, + token=connect_server.token, + secret=connect_server.secret, + **kwargs, + ) + else: + raise RSConnectException("Unable to infer Connect client.") + base_dir = dirname(file_name) _warn_on_ignored_manifest(base_dir) _warn_if_no_requirements_file(base_dir) @@ -713,7 +733,6 @@ def deploy_jupyter_notebook( if force_generate: _warn_on_ignored_requirements(base_dir, environment.filename) - ce = RSConnectExecutor(**kwargs) ce.validate_server().validate_app_mode(app_mode=app_mode) if app_mode == AppModes.STATIC: ce.make_bundle( From e66a7481199e286e862735921342aec64bcf6234 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Fri, 28 Oct 2022 15:03:50 -0400 Subject: [PATCH 08/16] add in consistent remote server check & init --- rsconnect/actions.py | 67 +++++++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 19 deletions(-) diff --git a/rsconnect/actions.py b/rsconnect/actions.py index a7f8bd9e..402e62b5 100644 --- a/rsconnect/actions.py +++ b/rsconnect/actions.py @@ -794,17 +794,33 @@ def deploy_app( token: str = None, secret: str = None, app_mode: str = None, - connect_server: api.RSConnectServer = None, + remote_server: api.TargetableServer = None, **kws, ): - if connect_server: - server = connect_server.url - api_key = connect_server.api_key - insecure = connect_server.insecure - ca_data = connect_server.ca_data kwargs = locals() kwargs["entry_point"] = entry_point = validate_entry_point(entry_point, directory) kwargs["extra_files"] = extra_files = validate_extra_files(directory, extra_files) + + if isinstance(remote_server, api.RSConnectServer): + ce = RSConnectExecutor( + url=remote_server.url, + api_key=remote_server.api_key, + insecure=remote_server.insecure, + ca_data=remote_server.ca_data, + cookies=remote_server.cookie_jar, + **kwargs, + ) + elif isinstance(remote_server, api.ShinyappsServer) or isinstance(remote_server, api.CloudServer): + ce = RSConnectExecutor( + url=remote_server.url, + account=remote_server.account_name, + token=remote_server.token, + secret=remote_server.secret, + **kwargs, + ) + else: + raise RSConnectException("Unable to infer Connect client.") + environment = create_python_environment( directory, force_generate, @@ -812,7 +828,6 @@ def deploy_app( conda_mode, ) - ce = RSConnectExecutor(**kwargs) ( ce.validate_server() .validate_app_mode(app_mode=app_mode) @@ -834,7 +849,7 @@ def deploy_app( def deploy_python_api( - connect_server: api.RSConnectServer, + connect_server: api.TargetableServer, directory: str, extra_files: typing.List[str], excludes: typing.List[str], @@ -878,7 +893,7 @@ def deploy_python_api( def deploy_python_fastapi( - connect_server: api.RSConnectServer, + connect_server: api.TargetableServer, directory: str, extra_files: typing.List[str], excludes: typing.List[str], @@ -964,7 +979,7 @@ def deploy_python_shiny( def deploy_dash_app( - connect_server: api.RSConnectServer, + connect_server: api.TargetableServer, directory: str, extra_files: typing.List[str], excludes: typing.List[str], @@ -1008,7 +1023,7 @@ def deploy_dash_app( def deploy_streamlit_app( - connect_server: api.RSConnectServer, + connect_server: api.TargetableServer, directory: str, extra_files: typing.List[str], excludes: typing.List[str], @@ -1052,7 +1067,7 @@ def deploy_streamlit_app( def deploy_bokeh_app( - connect_server: api.RSConnectServer, + connect_server: api.TargetableServer, directory: str, extra_files: typing.List[str], excludes: typing.List[str], @@ -1096,7 +1111,7 @@ def deploy_bokeh_app( def deploy_by_manifest( - connect_server: api.RSConnectServer, + connect_server: api.TargetableServer, manifest_file_name: str, new: bool, app_id: int, @@ -1120,17 +1135,31 @@ def deploy_by_manifest( :return: the ultimate URL where the deployed app may be accessed and the sequence of log lines. The log lines value will be None if a log callback was provided. """ - if connect_server: - server = connect_server.url - api_key = connect_server.api_key - insecure = connect_server.insecure - ca_data = connect_server.ca_data kwargs = locals() kwargs["manifest_file_name"] = manifest_file_name = validate_manifest_file(manifest_file_name) app_mode = read_manifest_app_mode(manifest_file_name) kwargs["title"] = title or default_title_from_manifest(manifest_file_name) - ce = RSConnectExecutor(**kwargs) + if isinstance(connect_server, api.RSConnectServer): + ce = RSConnectExecutor( + url=connect_server.url, + api_key=connect_server.api_key, + insecure=connect_server.insecure, + ca_data=connect_server.ca_data, + cookies=connect_server.cookie_jar, + **kwargs, + ) + elif isinstance(connect_server, api.ShinyappsServer) or isinstance(connect_server, api.CloudServer): + ce = RSConnectExecutor( + url=connect_server.url, + account=connect_server.account_name, + token=connect_server.token, + secret=connect_server.secret, + **kwargs, + ) + else: + raise RSConnectException("Unable to infer Connect client.") + ( ce.validate_server() .validate_app_mode(app_mode=app_mode) From 661186fa3c82ad15ac11ba0e479bf8736af90cfc Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Fri, 28 Oct 2022 15:18:46 -0400 Subject: [PATCH 09/16] update type hints and syntax --- rsconnect/actions.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rsconnect/actions.py b/rsconnect/actions.py index 402e62b5..7d7fd421 100644 --- a/rsconnect/actions.py +++ b/rsconnect/actions.py @@ -630,7 +630,7 @@ def deploy_html( api_key: str = None, insecure: bool = False, cacert: IO = None, -): +) -> None: kwargs = locals() ce = None if connect_server: @@ -670,7 +670,7 @@ def deploy_jupyter_notebook( hide_all_input: bool, hide_tagged_input: bool, image: str = None, -) -> typing.Tuple[typing.Any, typing.List]: +) -> None: """ A function to deploy a Jupyter notebook to Connect. Depending on the files involved and network latency, this may take a bit of time. @@ -793,9 +793,9 @@ def deploy_app( account: str = None, token: str = None, secret: str = None, - app_mode: str = None, + app_mode: typing.Optional[AppMode] = None, remote_server: api.TargetableServer = None, - **kws, + **kws ): kwargs = locals() kwargs["entry_point"] = entry_point = validate_entry_point(entry_point, directory) @@ -1117,7 +1117,7 @@ def deploy_by_manifest( app_id: int, title: str, log_callback: typing.Callable, -) -> typing.Tuple[str, typing.Union[list, None]]: +) -> None: """ A function to deploy a Jupyter notebook to Connect. Depending on the files involved and network latency, this may take a bit of time. From b6ec4df31b362b0deefd8937328a108b6dda7603 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Fri, 28 Oct 2022 15:26:34 -0400 Subject: [PATCH 10/16] use get_python_env_info from bundle --- rsconnect/actions.py | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/rsconnect/actions.py b/rsconnect/actions.py index 7d7fd421..6e11ffc4 100644 --- a/rsconnect/actions.py +++ b/rsconnect/actions.py @@ -24,6 +24,7 @@ _warn_on_ignored_requirements, create_python_environment, default_title_from_manifest, + get_python_env_info, make_api_bundle, make_api_manifest, make_html_bundle, @@ -1519,30 +1520,6 @@ def _gather_basic_deployment_info_for_framework( ) -def get_python_env_info(file_name, python, conda_mode=False, force_generate=False): - """ - Gathers the python and environment information relating to the specified file - with an eye to deploy it. - - :param file_name: the primary file being deployed. - :param python: the optional name of a Python executable. - :param conda_mode: inspect the environment assuming Conda - :param force_generate: force generating "requirements.txt" or "environment.yml", - even if it already exists. - :return: information about the version of Python in use plus some environmental - stuff. - """ - python = which_python(python) - logger.debug("Python: %s" % python) - environment = inspect_environment(python, dirname(file_name), conda_mode=conda_mode, force_generate=force_generate) - if environment.error: - raise RSConnectException(environment.error) - logger.debug("Python: %s" % python) - logger.debug("Environment: %s" % pformat(environment._asdict())) - - return python, environment - - def create_notebook_deployment_bundle( file_name: str, extra_files: typing.List[str], From a7e44d87df473cfffb83d4977ca3abcb851821c8 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Fri, 28 Oct 2022 15:46:05 -0400 Subject: [PATCH 11/16] deprecate 'gather x' functions --- rsconnect/actions.py | 346 ------------------------------------------- 1 file changed, 346 deletions(-) diff --git a/rsconnect/actions.py b/rsconnect/actions.py index 6e11ffc4..1173e253 100644 --- a/rsconnect/actions.py +++ b/rsconnect/actions.py @@ -1174,352 +1174,6 @@ def deploy_by_manifest( ) -def gather_basic_deployment_info_for_notebook( - connect_server: api.RSConnectServer, - app_store: AppStore, - file_name: str, - new: bool, - app_id: int, - title: str, - static: bool, -) -> typing.Tuple[int, str, str, bool, AppMode]: - """ - Helps to gather the necessary info for performing a deployment. - - :param connect_server: the Connect server information. - :param app_store: the store for the specified file - :param file_name: the primary file being deployed. - :param new: a flag noting whether we should force a new deployment. - :param app_id: the ID of the app to redeploy. - :param title: an optional title. If this isn't specified, a default title will - be generated. - :param static: a flag to note whether a static document should be deployed. - :return: the app ID, name, title information and mode for the deployment. - """ - validate_file_is_notebook(file_name) - _validate_title(title) - - if new and app_id: - raise RSConnectException("Specify either a new deploy or an app ID but not both.") - - if static: - app_mode = AppModes.STATIC - else: - app_mode = AppModes.JUPYTER_NOTEBOOK - - existing_app_mode = None - if not new: - if app_id is None: - # Possible redeployment - check for saved metadata. - # Use the saved app information unless overridden by the user. - app_id, existing_app_mode = app_store.resolve(connect_server.url, app_id, app_mode) - logger.debug("Using app mode from app %s: %s" % (app_id, app_mode)) - elif app_id is not None: - # Don't read app metadata if app-id is specified. Instead, we need - # to get this from Connect. - app = api.get_app_info(connect_server, app_id) - existing_app_mode = AppModes.get_by_ordinal(app.get("app_mode", 0), True) - if existing_app_mode and app_mode != existing_app_mode: - msg = ( - "Deploying with mode '%s',\n" - + "but the existing deployment has mode '%s'.\n" - + "Use the --new option to create a new deployment of the desired type." - ) % (app_mode.desc(), existing_app_mode.desc()) - raise RSConnectException(msg) - - default_title = not bool(title) - title = title or _default_title(file_name) - - return ( - app_id, - _make_deployment_name(connect_server, title, app_id is None), - title, - default_title, - app_mode, - ) - - -def gather_basic_deployment_info_for_html( - connect_server: api.RSConnectServer, - app_store: AppStore, - path: str, - new: bool, - app_id: int, - title: str, -) -> typing.Tuple[int, str, str, bool, AppMode]: - """ - Helps to gather the necessary info for performing a static html (re)deployment. - - :param connect_server: the Connect server information. - :param app_store: the store for the specified file - :param path: the primary file or directory being deployed. - :param new: a flag noting whether we should force a new deployment. - :param app_id: the ID of the app to redeploy. - :param title: an optional title. If this isn't specified, a default title will - be generated. - :return: the app ID, name, title information and mode for the deployment. - """ - - if new and app_id: - raise RSConnectException("Specify either a new deploy or an app ID but not both.") - - app_mode = AppModes.STATIC - existing_app_mode = None - if not new: - if app_id is None: - # Possible redeployment - check for saved metadata. - # Use the saved app information unless overridden by the user. - app_id, existing_app_mode = app_store.resolve(connect_server.url, app_id, app_mode) - logger.debug("Using app mode from app %s: %s" % (app_id, app_mode)) - elif app_id is not None: - # Don't read app metadata if app-id is specified. Instead, we need - # to get this from Connect. - app = api.get_app_info(connect_server, app_id) - existing_app_mode = AppModes.get_by_ordinal(app.get("app_mode", 0), True) - if existing_app_mode and app_mode != existing_app_mode: - msg = ( - "Deploying with mode '%s',\n" - + "but the existing deployment has mode '%s'.\n" - + "Use the --new option to create a new deployment of the desired type." - ) % (app_mode.desc(), existing_app_mode.desc()) - raise RSConnectException(msg) - - default_title = not bool(title) - title = title or _default_title(path) - - return ( - app_id, - _make_deployment_name(connect_server, title, app_id is None), - title, - default_title, - app_mode, - ) - - -def gather_basic_deployment_info_from_manifest( - connect_server: api.RSConnectServer, - app_store: AppStore, - file_name: str, - new: bool, - app_id: int, - title: str, -) -> typing.Tuple[int, str, str, bool, AppMode, str, str]: - """ - Helps to gather the necessary info for performing a deployment. - - :param connect_server: the Connect server information. - :param app_store: the store for the specified file - :param file_name: the manifest file being deployed. - :param new: a flag noting whether we should force a new deployment. - :param app_id: the ID of the app to redeploy. - :param title: an optional title. If this isn't specified, a default title will - be generated. - :return: the app ID, name, title information, mode, package manager and image for the - deployment. - """ - file_name = validate_manifest_file(file_name) - - _validate_title(title) - - if new and app_id: - raise RSConnectException("Specify either a new deploy or an app ID but not both.") - - source_manifest, _ = read_manifest_file(file_name) - # noinspection SpellCheckingInspection - app_mode = AppModes.get_by_name(source_manifest["metadata"]["appmode"]) - - if not new and app_id is None: - # Possible redeployment - check for saved metadata. - # Use the saved app information unless overridden by the user. - app_id, app_mode = app_store.resolve(connect_server.url, app_id, app_mode) - - package_manager = source_manifest.get("python", {}).get("package_manager", {}).get("name", None) - default_title = not bool(title) - title = title or _default_title_from_manifest(source_manifest, file_name) - image = source_manifest.get("Environment", {}).get("image", None) - - return ( - app_id, - _make_deployment_name(connect_server, title, app_id is None), - title, - default_title, - app_mode, - package_manager, - image, - ) - - -def gather_basic_deployment_info_for_quarto( - connect_server: api.RSConnectServer, - app_store: AppStore, - file_or_directory: str, - new: bool, - app_id: int, - title: str, -) -> typing.Tuple[int, str, str, bool, AppMode]: - """ - Helps to gather the necessary info for performing a deployment. - - :param connect_server: The Connect server information. - :param app_store: The store for the specified Quarto project directory. - :param file_or_directory: The Quarto document or directory containing the Quarto project. - :param new: A flag to force a new deployment. - :param app_id: The identifier of the content to redeploy. - :param title: The content title (optional). A default title is generated when one is not provided. - """ - _validate_title(title) - - if new and app_id: - raise RSConnectException("Specify either a new deploy or an app ID but not both.") - - app_mode = AppModes.STATIC_QUARTO - - existing_app_mode = None - if not new: - if app_id is None: - # Possible redeployment - check for saved metadata. - # Use the saved app information unless overridden by the user. - app_id, existing_app_mode = app_store.resolve(connect_server.url, app_id, app_mode) - logger.debug("Using app mode from app %s: %s" % (app_id, app_mode)) - elif app_id is not None: - # Don't read app metadata if app-id is specified. Instead, we need - # to get this from Connect. - app = api.get_app_info(connect_server, app_id) - existing_app_mode = AppModes.get_by_ordinal(app.get("app_mode", 0), True) - if existing_app_mode and app_mode != existing_app_mode: - msg = ( - "Deploying with mode '%s',\n" - + "but the existing deployment has mode '%s'.\n" - + "Use the --new option to create a new deployment of the desired type." - ) % (app_mode.desc(), existing_app_mode.desc()) - raise RSConnectException(msg) - - if file_or_directory[-1] == "/": - file_or_directory = file_or_directory[:-1] - - default_title = not bool(title) - title = title or _default_title(file_or_directory) - - return ( - app_id, - _make_deployment_name(connect_server, title, app_id is None), - title, - default_title, - app_mode, - ) - - -def _generate_gather_basic_deployment_info_for_python(app_mode: AppMode) -> typing.Callable: - """ - Generates function to gather the necessary info for performing a deployment by app mode - """ - - def gatherer( - remote_server: api.TargetableServer, - app_store: AppStore, - directory: str, - entry_point: str, - new: bool, - app_id: int, - title: str, - ) -> typing.Tuple[str, int, str, str, bool, AppMode]: - return _gather_basic_deployment_info_for_framework( - remote_server, - app_store, - directory, - entry_point, - new, - app_id, - app_mode, - title, - ) - - return gatherer - - -gather_basic_deployment_info_for_api = _generate_gather_basic_deployment_info_for_python(AppModes.PYTHON_API) -gather_basic_deployment_info_for_fastapi = _generate_gather_basic_deployment_info_for_python(AppModes.PYTHON_FASTAPI) -gather_basic_deployment_info_for_dash = _generate_gather_basic_deployment_info_for_python(AppModes.DASH_APP) -gather_basic_deployment_info_for_streamlit = _generate_gather_basic_deployment_info_for_python(AppModes.STREAMLIT_APP) -gather_basic_deployment_info_for_bokeh = _generate_gather_basic_deployment_info_for_python(AppModes.BOKEH_APP) -gather_basic_deployment_info_for_shiny = _generate_gather_basic_deployment_info_for_python(AppModes.PYTHON_SHINY) - - -def _gather_basic_deployment_info_for_framework( - remote_server: api.TargetableServer, - app_store: AppStore, - directory: str, - entry_point: str, - new: bool, - app_id: int, - app_mode: AppMode, - title: str, -) -> typing.Tuple[str, int, str, str, bool, AppMode]: - """ - Helps to gather the necessary info for performing a deployment. - - :param remote_server: the server information. - :param app_store: the store for the specified directory. - :param directory: the primary file being deployed. - :param entry_point: the entry point for the API in ': format. if - the object name is omitted, it defaults to the module name. If nothing is specified, - it defaults to 'app'. - :param new: a flag noting whether we should force a new deployment. - :param app_id: the ID of the app to redeploy. - :param app_mode: the app mode to use. - :param title: an optional title. If this isn't specified, a default title will - be generated. - :return: the entry point, app ID, name, title, and mode for the deployment. - """ - entry_point = validate_entry_point(entry_point, directory) - - _validate_title(title) - - if new and app_id: - raise RSConnectException("Specify either a new deploy or an app ID but not both.") - - existing_app_mode = None - if not new: - if app_id is None: - # Possible redeployment - check for saved metadata. - # Use the saved app information unless overridden by the user. - app_id, existing_app_mode = app_store.resolve(remote_server.url, app_id, app_mode) - logger.debug("Using app mode from app %s: %s" % (app_id, app_mode)) - elif app_id is not None: - # Don't read app metadata if app-id is specified. Instead, we need - # to get this from Connect. - if isinstance(remote_server, api.RSConnectServer): - app = api.get_app_info(remote_server, app_id) - existing_app_mode = AppModes.get_by_ordinal(app.get("app_mode", 0), True) - elif isinstance(remote_server, api.RStudioServer): - app = api.get_rstudio_app_info(remote_server, app_id) - existing_app_mode = AppModes.get_by_cloud_name(app.json_data["mode"]) - else: - raise RSConnectException("Unable to infer Connect client.") - if existing_app_mode and app_mode != existing_app_mode: - msg = ( - "Deploying with mode '%s',\n" - + "but the existing deployment has mode '%s'.\n" - + "Use the --new option to create a new deployment of the desired type." - ) % (app_mode.desc(), existing_app_mode.desc()) - raise RSConnectException(msg) - - if directory[-1] == "/": - directory = directory[:-1] - - default_title = not bool(title) - title = title or _default_title(directory) - - return ( - entry_point, - app_id, - _make_deployment_name(remote_server, title, app_id is None), - title, - default_title, - app_mode, - ) - - def create_notebook_deployment_bundle( file_name: str, extra_files: typing.List[str], From 4d98bd4d0cbdb60892ab355c0f2b967a0e36084c Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Fri, 28 Oct 2022 15:47:59 -0400 Subject: [PATCH 12/16] move some tests from actions to bundle --- tests/test_actions.py | 122 +----------------------------------------- tests/test_bundle.py | 114 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 111 insertions(+), 125 deletions(-) diff --git a/tests/test_actions.py b/tests/test_actions.py index 42a034fa..bb41bd41 100644 --- a/tests/test_actions.py +++ b/tests/test_actions.py @@ -1,20 +1,13 @@ import os -import subprocess -import sys try: import typing except ImportError: typing = None -from os.path import basename, join +from os.path import join from unittest import TestCase -import pytest - -import rsconnect.actions - - from rsconnect.actions import ( _verify_server, are_apis_supported_on_server, @@ -25,8 +18,6 @@ deploy_python_api, deploy_streamlit_app, deploy_bokeh_app, - gather_basic_deployment_info_for_api, - get_python_env_info, is_conda_supported_on_server, ) from rsconnect.api import RSConnectServer @@ -116,14 +107,6 @@ def test_deploy_streamlit_app_docs(self): def test_deploy_bokeh_app_docs(self): self.assertTrue("Bokeh app" in deploy_bokeh_app.__doc__) - def test_gather_basic_deployment_info_for_api_validates(self): - directory = get_api_path("flask") - server = RSConnectServer("https://www.bogus.com", "bogus") - with self.assertRaises(RSConnectException): - gather_basic_deployment_info_for_api(server, None, directory, "bogus:bogus:bogus", False, 0, "bogus") - with self.assertRaises(RSConnectException): - gather_basic_deployment_info_for_api(server, None, directory, "app:app", False, 0, "") - def test_create_notebook_deployment_bundle_validates(self): file_name = get_dir(join("pip1", "requirements.txt")) with self.assertRaises(RSConnectException): @@ -142,106 +125,3 @@ def test_create_api_deployment_bundle_validates(self): create_api_deployment_bundle(directory, [], [], "bogus:bogus:bogus", None, None, None, None) with self.assertRaises(RSConnectException): create_api_deployment_bundle(directory, ["bogus"], [], "app:app", MakeEnvironment(), None, True, None) - - -@pytest.mark.parametrize( - ( - "file_name", - "python", - "conda_mode", - "force_generate", - "expected_python", - "expected_environment", - ), - [ - pytest.param( - "path/to/file.py", - sys.executable, - False, - False, - sys.executable, - MakeEnvironment( - conda=None, - filename="requirements.txt", - locale="en_US.UTF-8", - package_manager="pip", - source="pip_freeze", - ), - id="basic", - ), - pytest.param( - "another/file.py", - basename(sys.executable), - False, - False, - sys.executable, - MakeEnvironment( - conda=None, - filename="requirements.txt", - locale="en_US.UTF-8", - package_manager="pip", - source="pip_freeze", - ), - id="which_python", - ), - pytest.param( - "even/moar/file.py", - "whython", - True, - True, - "/very/serious/whython", - MakeEnvironment( - conda="/opt/Conda/bin/conda", - filename="requirements.txt", - locale="en_US.UTF-8", - package_manager="pip", - source="pip_freeze", - ), - id="conda_ish", - ), - pytest.param( - "will/the/files/never/stop.py", - "argh.py", - False, - True, - "unused", - MakeEnvironment(error="Could not even do things"), - id="exploding", - ), - ], -) -def test_get_python_env_info( - monkeypatch, - file_name, - python, - conda_mode, - force_generate, - expected_python, - expected_environment, -): - def fake_which_python(python, env=os.environ): - return expected_python - - def fake_inspect_environment( - python, - directory, - conda_mode=False, - force_generate=False, - check_output=subprocess.check_output, - ): - return expected_environment - - monkeypatch.setattr(rsconnect.actions, "inspect_environment", fake_inspect_environment) - - monkeypatch.setattr(rsconnect.actions, "which_python", fake_which_python) - - if expected_environment.error is not None: - with pytest.raises(RSConnectException): - _, _ = get_python_env_info(file_name, python, conda_mode=conda_mode, force_generate=force_generate) - else: - python, environment = get_python_env_info( - file_name, python, conda_mode=conda_mode, force_generate=force_generate - ) - - assert python == expected_python - assert environment == expected_environment diff --git a/tests/test_bundle.py b/tests/test_bundle.py index d1e5868b..16e6db94 100644 --- a/tests/test_bundle.py +++ b/tests/test_bundle.py @@ -1,18 +1,20 @@ # -*- coding: utf-8 -*- import json +import os import shutil +import subprocess import sys import tarfile import tempfile - +import pytest from unittest import TestCase -from os.path import dirname, join +from os.path import dirname, join, basename -from rsconnect.environment import detect_environment from rsconnect.bundle import ( _default_title, _default_title_from_manifest, _validate_title, + get_python_env_info, inspect_environment, list_files, make_manifest_bundle, @@ -27,9 +29,10 @@ validate_extra_files, which_python, ) +import rsconnect.bundle from rsconnect.exception import RSConnectException from rsconnect.models import AppModes -from rsconnect.environment import Environment +from rsconnect.environment import MakeEnvironment, detect_environment, Environment from .utils import get_dir, get_manifest_path @@ -676,3 +679,106 @@ def test_inspect_environment(self): environment = inspect_environment(sys.executable, get_dir("pip1")) assert environment is not None assert environment.python != "" + + +@pytest.mark.parametrize( + ( + "file_name", + "python", + "conda_mode", + "force_generate", + "expected_python", + "expected_environment", + ), + [ + pytest.param( + "path/to/file.py", + sys.executable, + False, + False, + sys.executable, + MakeEnvironment( + conda=None, + filename="requirements.txt", + locale="en_US.UTF-8", + package_manager="pip", + source="pip_freeze", + ), + id="basic", + ), + pytest.param( + "another/file.py", + basename(sys.executable), + False, + False, + sys.executable, + MakeEnvironment( + conda=None, + filename="requirements.txt", + locale="en_US.UTF-8", + package_manager="pip", + source="pip_freeze", + ), + id="which_python", + ), + pytest.param( + "even/moar/file.py", + "whython", + True, + True, + "/very/serious/whython", + MakeEnvironment( + conda="/opt/Conda/bin/conda", + filename="requirements.txt", + locale="en_US.UTF-8", + package_manager="pip", + source="pip_freeze", + ), + id="conda_ish", + ), + pytest.param( + "will/the/files/never/stop.py", + "argh.py", + False, + True, + "unused", + MakeEnvironment(error="Could not even do things"), + id="exploding", + ), + ], +) +def test_get_python_env_info( + monkeypatch, + file_name, + python, + conda_mode, + force_generate, + expected_python, + expected_environment, +): + def fake_which_python(python, env=os.environ): + return expected_python + + def fake_inspect_environment( + python, + directory, + conda_mode=False, + force_generate=False, + check_output=subprocess.check_output, + ): + return expected_environment + + monkeypatch.setattr(rsconnect.bundle, "inspect_environment", fake_inspect_environment) + + monkeypatch.setattr(rsconnect.bundle, "which_python", fake_which_python) + + if expected_environment.error is not None: + with pytest.raises(RSConnectException): + _, _ = get_python_env_info(file_name, python, conda_mode=conda_mode, force_generate=force_generate) + else: + python, environment = get_python_env_info( + file_name, python, conda_mode=conda_mode, force_generate=force_generate + ) + + assert python == expected_python + assert environment == expected_environment From 5533e12038510fc6d14cefe1f6bb1c0b1f411f55 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Fri, 28 Oct 2022 15:48:19 -0400 Subject: [PATCH 13/16] clean imports and add deprecation warning --- rsconnect/actions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rsconnect/actions.py b/rsconnect/actions.py index 1173e253..4df7bad2 100644 --- a/rsconnect/actions.py +++ b/rsconnect/actions.py @@ -14,7 +14,6 @@ from typing import IO from warnings import warn from os.path import abspath, basename, dirname, exists, isdir, join, relpath, splitext -from pprint import pformat from .exception import RSConnectException from . import api from .bundle import ( @@ -143,6 +142,7 @@ def inspect_environment( Returns a dictionary of information about the environment, or containing an "error" field if an error occurred. """ + warn("This method has been moved and will be deprecated.", DeprecationWarning, stacklevel=2) flags = [] if conda_mode: flags.append("c") From 742d64a6d01d59d28475887ac1049580c9232359 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Fri, 28 Oct 2022 15:50:18 -0400 Subject: [PATCH 14/16] clean up unused import --- rsconnect/actions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/rsconnect/actions.py b/rsconnect/actions.py index 4df7bad2..a79155a3 100644 --- a/rsconnect/actions.py +++ b/rsconnect/actions.py @@ -40,7 +40,6 @@ ) from .environment import Environment, MakeEnvironment, EnvironmentException from .log import logger -from .metadata import AppStore from .models import AppModes, AppMode from .api import RSConnectExecutor, filter_out_server_info From f8df2b5ab17b6b25ea333a8004fb1b1bb2810401 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Fri, 28 Oct 2022 16:22:50 -0400 Subject: [PATCH 15/16] update server check & init --- rsconnect/actions.py | 93 ++++++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 42 deletions(-) diff --git a/rsconnect/actions.py b/rsconnect/actions.py index a79155a3..0f8f6d39 100644 --- a/rsconnect/actions.py +++ b/rsconnect/actions.py @@ -705,21 +705,23 @@ def deploy_jupyter_notebook( app_mode = AppModes.JUPYTER_NOTEBOOK if not static else AppModes.STATIC if isinstance(connect_server, api.RSConnectServer): - ce = RSConnectExecutor( - url=connect_server.url, - api_key=connect_server.api_key, - insecure=connect_server.insecure, - ca_data=connect_server.ca_data, - cookies=connect_server.cookie_jar, - **kwargs, + kwargs.update( + dict( + url=connect_server.url, + api_key=connect_server.api_key, + insecure=connect_server.insecure, + ca_data=connect_server.ca_data, + cookies=connect_server.cookie_jar, + ) ) elif isinstance(connect_server, api.ShinyappsServer) or isinstance(connect_server, api.CloudServer): - ce = RSConnectExecutor( - url=connect_server.url, - account=connect_server.account_name, - token=connect_server.token, - secret=connect_server.secret, - **kwargs, + kwargs.update( + dict( + url=connect_server.url, + account=connect_server.account_name, + token=connect_server.token, + secret=connect_server.secret, + ) ) else: raise RSConnectException("Unable to infer Connect client.") @@ -733,6 +735,7 @@ def deploy_jupyter_notebook( if force_generate: _warn_on_ignored_requirements(base_dir, environment.filename) + ce = RSConnectExecutor(**kwargs) ce.validate_server().validate_app_mode(app_mode=app_mode) if app_mode == AppModes.STATIC: ce.make_bundle( @@ -794,29 +797,31 @@ def deploy_app( token: str = None, secret: str = None, app_mode: typing.Optional[AppMode] = None, - remote_server: api.TargetableServer = None, + connect_server: api.TargetableServer = None, **kws ): kwargs = locals() kwargs["entry_point"] = entry_point = validate_entry_point(entry_point, directory) kwargs["extra_files"] = extra_files = validate_extra_files(directory, extra_files) - if isinstance(remote_server, api.RSConnectServer): - ce = RSConnectExecutor( - url=remote_server.url, - api_key=remote_server.api_key, - insecure=remote_server.insecure, - ca_data=remote_server.ca_data, - cookies=remote_server.cookie_jar, - **kwargs, + if isinstance(connect_server, api.RSConnectServer): + kwargs.update( + dict( + url=connect_server.url, + api_key=connect_server.api_key, + insecure=connect_server.insecure, + ca_data=connect_server.ca_data, + cookies=connect_server.cookie_jar, + ) ) - elif isinstance(remote_server, api.ShinyappsServer) or isinstance(remote_server, api.CloudServer): - ce = RSConnectExecutor( - url=remote_server.url, - account=remote_server.account_name, - token=remote_server.token, - secret=remote_server.secret, - **kwargs, + elif isinstance(connect_server, api.ShinyappsServer) or isinstance(connect_server, api.CloudServer): + kwargs.update( + dict( + url=connect_server.url, + account=connect_server.account_name, + token=connect_server.token, + secret=connect_server.secret, + ) ) else: raise RSConnectException("Unable to infer Connect client.") @@ -828,6 +833,7 @@ def deploy_app( conda_mode, ) + ce = RSConnectExecutor(**kwargs) ( ce.validate_server() .validate_app_mode(app_mode=app_mode) @@ -1141,25 +1147,28 @@ def deploy_by_manifest( kwargs["title"] = title or default_title_from_manifest(manifest_file_name) if isinstance(connect_server, api.RSConnectServer): - ce = RSConnectExecutor( - url=connect_server.url, - api_key=connect_server.api_key, - insecure=connect_server.insecure, - ca_data=connect_server.ca_data, - cookies=connect_server.cookie_jar, - **kwargs, + kwargs.update( + dict( + url=connect_server.url, + api_key=connect_server.api_key, + insecure=connect_server.insecure, + ca_data=connect_server.ca_data, + cookies=connect_server.cookie_jar, + ) ) elif isinstance(connect_server, api.ShinyappsServer) or isinstance(connect_server, api.CloudServer): - ce = RSConnectExecutor( - url=connect_server.url, - account=connect_server.account_name, - token=connect_server.token, - secret=connect_server.secret, - **kwargs, + kwargs.update( + dict( + url=connect_server.url, + account=connect_server.account_name, + token=connect_server.token, + secret=connect_server.secret, + ) ) else: raise RSConnectException("Unable to infer Connect client.") + ce = RSConnectExecutor(**kwargs) ( ce.validate_server() .validate_app_mode(app_mode=app_mode) From bdaf75c81da57e47973c76e0fa53a7cbf0ced64c Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Fri, 28 Oct 2022 16:34:37 -0400 Subject: [PATCH 16/16] remove a check in case function is used directly --- rsconnect/actions.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/rsconnect/actions.py b/rsconnect/actions.py index 0f8f6d39..60bc060c 100644 --- a/rsconnect/actions.py +++ b/rsconnect/actions.py @@ -823,8 +823,6 @@ def deploy_app( secret=connect_server.secret, ) ) - else: - raise RSConnectException("Unable to infer Connect client.") environment = create_python_environment( directory,