From 848dfe66f525429494f15177e6d218cf21f5a5c2 Mon Sep 17 00:00:00 2001 From: Sergey Talash Date: Tue, 12 Sep 2023 14:37:54 +0600 Subject: [PATCH 01/43] Update http_support.py with NO_PROXY support --- rsconnect/http_support.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rsconnect/http_support.py b/rsconnect/http_support.py index 261b2e52..0661f4b2 100644 --- a/rsconnect/http_support.py +++ b/rsconnect/http_support.py @@ -75,7 +75,10 @@ def _create_ssl_connection(host_name, port, disable_tls_check, ca_data): """ if ca_data is not None and disable_tls_check: raise ValueError("Cannot both disable TLS checking and provide a custom certificate") - _, _, proxyHost, proxyPort = _get_proxy() + if any([host_name.endswith(host) for host in os.environ.get('NO_PROXY', '#').split(',')]): + proxyHost, proxyPort = None, None + else: + _, _, proxyHost, proxyPort = _get_proxy() headers = _get_proxy_headers() timeout = get_request_timeout() logger.debug(f"The HTTPSConnection timeout is set to '{timeout}' seconds") From 5f193afcb0b73ead9aa1722e37bd6512e7fb3e60 Mon Sep 17 00:00:00 2001 From: Michael Marchetti Date: Tue, 3 Oct 2023 11:52:43 -0400 Subject: [PATCH 02/43] allow lowercase env variable names --- rsconnect/http_support.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rsconnect/http_support.py b/rsconnect/http_support.py index 0661f4b2..833d3d42 100644 --- a/rsconnect/http_support.py +++ b/rsconnect/http_support.py @@ -37,7 +37,7 @@ def _create_plain_connection(host_name, port, disable_tls_check, ca_data): def _get_proxy(): - proxyURL = os.getenv("HTTPS_PROXY") + proxyURL = os.getenv("https_proxy", os.getenv("HTTPS_PROXY")) if not proxyURL: return None, None, None, None parsed = urlparse(proxyURL) @@ -75,7 +75,9 @@ def _create_ssl_connection(host_name, port, disable_tls_check, ca_data): """ if ca_data is not None and disable_tls_check: raise ValueError("Cannot both disable TLS checking and provide a custom certificate") - if any([host_name.endswith(host) for host in os.environ.get('NO_PROXY', '#').split(',')]): + + no_proxy = os.environ.get("no_proxy", os.environ.get("NO_PROXY", "#")) + if any([host_name.endswith(host) for host in no_proxy.split(",")]): proxyHost, proxyPort = None, None else: _, _, proxyHost, proxyPort = _get_proxy() From 3391353c3560abab1a4713f349d5e7dc28089860 Mon Sep 17 00:00:00 2001 From: Michael Marchetti Date: Tue, 3 Oct 2023 11:56:23 -0400 Subject: [PATCH 03/43] update changelog --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a47c2b2..00d3ee39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,17 @@ 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 +- The `https_proxy` environment variable is recognized as a synonym for + `HTTPS_PROXY`. + +### Added +- Added support for the `no_proxy` or `NO_PROXY` environment variables to specify + hosts that should not be accessed via proxy server. It's a comma-separated list + of host or domain suffixes. For example, specifying `example.com` will + bypass the proxy for example.com, host.example.com, etc. ## [1.20.0] - 2023-09-11 From feb3b0742574c8d3466769c85b462831ad0ab575 Mon Sep 17 00:00:00 2001 From: Joe Cheng Date: Tue, 10 Oct 2023 18:21:31 -0700 Subject: [PATCH 04/43] Infer additional entrypoints These new app patterns are accepted: app-*.py app_*.py *-app.py *_app.py --- rsconnect/bundle.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/rsconnect/bundle.py b/rsconnect/bundle.py index 074427fb..5f182676 100644 --- a/rsconnect/bundle.py +++ b/rsconnect/bundle.py @@ -1404,6 +1404,10 @@ def validate_manifest_file(file_or_directory): return file_or_directory +re_app_prefix = re.compile(r"^app[-_].+\.py$") +re_app_suffix = re.compile(r".+[-_]app\.py$") + + def get_default_entrypoint(directory): candidates = ["app", "application", "main", "api"] files = set(os.listdir(directory)) @@ -1418,6 +1422,12 @@ def get_default_entrypoint(directory): if len(python_files) == 1: return python_files[0][:-3] + # try app-*.py, app_*.py, *-app.py, *_app.py + app_files = list(filter(lambda s: re_app_prefix.match(s) or re_app_suffix.match(s), python_files)) + if len(app_files) == 1: + # In these cases, the app should be in the "app" attribute + return app_files[0][:-3] + ":app" + logger.warning("Can't determine entrypoint; defaulting to 'app'") return "app" From b33f5bfce969341144647d4b1cbeff4845f352b2 Mon Sep 17 00:00:00 2001 From: Joe Cheng Date: Tue, 10 Oct 2023 18:49:00 -0700 Subject: [PATCH 05/43] Fail if no entrypoint inferred; add unit tests --- rsconnect/bundle.py | 3 +-- tests/test_bundle.py | 56 +++++++++++++++++++++++++++++--------------- 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/rsconnect/bundle.py b/rsconnect/bundle.py index 5f182676..b19f4833 100644 --- a/rsconnect/bundle.py +++ b/rsconnect/bundle.py @@ -1428,8 +1428,7 @@ def get_default_entrypoint(directory): # In these cases, the app should be in the "app" attribute return app_files[0][:-3] + ":app" - logger.warning("Can't determine entrypoint; defaulting to 'app'") - return "app" + raise RSConnectException(f"Could not determine default entrypoint file in directory '{directory}'") def validate_entry_point(entry_point, directory): diff --git a/tests/test_bundle.py b/tests/test_bundle.py index d87ff20a..c872eaf0 100644 --- a/tests/test_bundle.py +++ b/tests/test_bundle.py @@ -7,6 +7,7 @@ import sys import tarfile import tempfile +from pathlib import Path from os.path import dirname, join, basename, abspath from unittest import mock, TestCase @@ -1081,25 +1082,42 @@ def test_validate_title(self): _validate_title("1" * 1024) def test_validate_entry_point(self): - directory = tempfile.mkdtemp() - - try: - self.assertEqual(validate_entry_point(None, directory), "app") - self.assertEqual(validate_entry_point("app", directory), "app") - self.assertEqual(validate_entry_point("app:app", directory), "app:app") - - with self.assertRaises(RSConnectException): - validate_entry_point("x:y:z", directory) - - with open(join(directory, "onlysource.py"), "w") as f: - f.close() - self.assertEqual(validate_entry_point(None, directory), "onlysource") - - with open(join(directory, "main.py"), "w") as f: - f.close() - self.assertEqual(validate_entry_point(None, directory), "main") - finally: - shutil.rmtree(directory) + # Simple cases + for case in ["app", "application", "main", "api"]: + self._entry_point_case(["helper.py", f"{case}.py"], None, case) + + # app patterns; these differ because they come back with ":app" + for case in ["app-example", "app_example", "example-app", "example_app"]: + self._entry_point_case(["helper.py", f"{case}.py"], None, f"{case}:app") + + # only one Python file means we assume it's the entrypoint + self._entry_point_case(["onlysource.py"], None, "onlysource") + + # Explicit entrypoint specifiers, no need to infer + self._entry_point_case(["helper.py", "app.py"], "app", "app") + self._entry_point_case(["helper.py", "app.py"], "app:app", "app:app") + self._entry_point_case(["helper.py", "app.py"], "foo:bar", "foo:bar") + + def test_validate_entry_point_failure(self): + # Invalid entrypoint specifier + self._entry_point_case(["app.py"], "x:y:z", False) + # Nothing relevant found + self._entry_point_case(["one.py", "two.py"], "x:y:z", False) + # Too many app-*.py files + self._entry_point_case(["app-one.py", "app-two.py"], "x:y:z", False) + + def _entry_point_case(self, files, entry_point, expected): + with tempfile.TemporaryDirectory() as directory: + dir = Path(directory) + + for file in files: + (dir / file).touch() + + if expected is False: + with self.assertRaises(RSConnectException): + validate_entry_point(entry_point, directory) + else: + self.assertEqual(validate_entry_point(entry_point, directory), expected) def test_default_title(self): self.assertEqual(_default_title("testing.txt"), "testing") From 2b58b94687ee54eaae4482ed32bb90fff0e2f149 Mon Sep 17 00:00:00 2001 From: Joe Cheng Date: Tue, 10 Oct 2023 18:49:30 -0700 Subject: [PATCH 06/43] Deprecated actions.get_default_entry should delegate to new impl --- rsconnect/actions.py | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/rsconnect/actions.py b/rsconnect/actions.py index 4af40e60..d0449403 100644 --- a/rsconnect/actions.py +++ b/rsconnect/actions.py @@ -16,6 +16,7 @@ from os.path import abspath, basename, dirname, exists, isdir, join, relpath, splitext from .exception import RSConnectException from . import api +from . import bundle from .bundle import ( _warn_if_environment_directory, _warn_if_no_requirements_file, @@ -415,21 +416,7 @@ def validate_manifest_file(file_or_directory): def get_default_entrypoint(directory): warn("This method has been moved and will be deprecated.", DeprecationWarning, stacklevel=2) - candidates = ["app", "application", "main", "api"] - files = set(os.listdir(directory)) - - for candidate in candidates: - filename = candidate + ".py" - if filename in files: - return candidate - - # if only one python source file, use it - python_files = list(filter(lambda s: s.endswith(".py"), files)) - if len(python_files) == 1: - return python_files[0][:-3] - - logger.warning("Can't determine entrypoint; defaulting to 'app'") - return "app" + return bundle.get_default_entrypoint(directory) def validate_entry_point(entry_point, directory): From 4ed0f0b41d5c7fb6cd818b3ee84d26461c9023ca Mon Sep 17 00:00:00 2001 From: Joe Cheng Date: Tue, 10 Oct 2023 19:09:19 -0700 Subject: [PATCH 07/43] Add changelog entry --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00d3ee39..8ce5225c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 hosts that should not be accessed via proxy server. It's a comma-separated list of host or domain suffixes. For example, specifying `example.com` will bypass the proxy for example.com, host.example.com, etc. +- If an entrypoint is not specified with `--entrypoint`, rsconnect-python will try + harder than before to choose an entrypoint file. In addition to the previously + 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. ## [1.20.0] - 2023-09-11 From bf61f27317dac7bfa5d5ab2a04c64b49fd73b40a Mon Sep 17 00:00:00 2001 From: Joe Cheng Date: Tue, 10 Oct 2023 19:10:09 -0700 Subject: [PATCH 08/43] Remove unused import --- tests/test_bundle.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_bundle.py b/tests/test_bundle.py index c872eaf0..c3a7ce04 100644 --- a/tests/test_bundle.py +++ b/tests/test_bundle.py @@ -2,7 +2,6 @@ import json import os import pytest -import shutil import subprocess import sys import tarfile From 02437280239332025e5c5352cfe8cdb57c4f24e4 Mon Sep 17 00:00:00 2001 From: Joe Cheng Date: Wed, 11 Oct 2023 13:20:01 -0700 Subject: [PATCH 09/43] Simplify logic and test, update docstring --- rsconnect/bundle.py | 7 ++++--- tests/test_bundle.py | 6 +----- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/rsconnect/bundle.py b/rsconnect/bundle.py index b19f4833..cd0a5093 100644 --- a/rsconnect/bundle.py +++ b/rsconnect/bundle.py @@ -1426,7 +1426,7 @@ def get_default_entrypoint(directory): app_files = list(filter(lambda s: re_app_prefix.match(s) or re_app_suffix.match(s), python_files)) if len(app_files) == 1: # In these cases, the app should be in the "app" attribute - return app_files[0][:-3] + ":app" + return app_files[0][:-3] raise RSConnectException(f"Could not determine default entrypoint file in directory '{directory}'") @@ -1435,10 +1435,11 @@ def validate_entry_point(entry_point, directory): """ Validates the entry point specified by the user, expanding as necessary. If the user specifies nothing, a module of "app" is assumed. If the user specifies a - module only, the object is assumed to be the same name as the module. + module only, at runtime the following object names will be tried in order: `app`, + `application`, `create_app`, and `make_app`. :param entry_point: the entry point as specified by the user. - :return: the fully expanded and validated entry point and the module file name.. + :return: An entry point, in the form of "module" or "module:app". """ if not entry_point: entry_point = get_default_entrypoint(directory) diff --git a/tests/test_bundle.py b/tests/test_bundle.py index c3a7ce04..663796bf 100644 --- a/tests/test_bundle.py +++ b/tests/test_bundle.py @@ -1082,13 +1082,9 @@ def test_validate_title(self): def test_validate_entry_point(self): # Simple cases - for case in ["app", "application", "main", "api"]: + for case in ["app", "application", "main", "api", "app-example", "app_example", "example-app", "example_app"]: self._entry_point_case(["helper.py", f"{case}.py"], None, case) - # app patterns; these differ because they come back with ":app" - for case in ["app-example", "app_example", "example-app", "example_app"]: - self._entry_point_case(["helper.py", f"{case}.py"], None, f"{case}:app") - # only one Python file means we assume it's the entrypoint self._entry_point_case(["onlysource.py"], None, "onlysource") From 4515cdcd45abe5cec785c75fe6a0c8f967009b87 Mon Sep 17 00:00:00 2001 From: Neal Richardson Date: Thu, 27 Jul 2023 10:18:30 -0400 Subject: [PATCH 10/43] Remove warning about python 3.7 --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 5dd558bc..cf4da9df 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,5 @@ # The rsconnect-python CLI -> **Warning** -> As of version 1.14.0, rsconnect-python requires Python version 3.7 or higher. - This package provides a CLI (command-line interface) for interacting with and deploying to Posit Connect. This is also used by the [`rsconnect-jupyter`](https://github.com/rstudio/rsconnect-jupyter) package to deploy From 44d76b3f956b0192d62ac2fde8dbe6eb25cea1f3 Mon Sep 17 00:00:00 2001 From: Michael Marchetti Date: Mon, 16 Oct 2023 08:11:51 -0400 Subject: [PATCH 11/43] Include APIkey with initial server check --- rsconnect/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rsconnect/main.py b/rsconnect/main.py index 5af3128f..e3673d61 100644 --- a/rsconnect/main.py +++ b/rsconnect/main.py @@ -351,7 +351,7 @@ def _test_server_and_api(server, api_key, insecure, ca_cert): me = None with cli_feedback("Checking %s" % server): - real_server, _ = test_server(api.RSConnectServer(server, None, insecure, ca_data)) + real_server, _ = test_server(api.RSConnectServer(server, api_key, insecure, ca_data)) real_server.api_key = api_key From dc8094f3d809f27c563a885736be9ec44931f626 Mon Sep 17 00:00:00 2001 From: Michael Marchetti Date: Mon, 16 Oct 2023 08:18:31 -0400 Subject: [PATCH 12/43] update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ce5225c..e20ba3f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - The `https_proxy` environment variable is recognized as a synonym for `HTTPS_PROXY`. +- When adding a new server, the initial request now includes an + Authorization header containing the API key. This is needed + for Connect installations behind a proxy that only passes + authenticated requests. ### Added - Added support for the `no_proxy` or `NO_PROXY` environment variables to specify From c56de4bb107a16ccf1437af7ec0824359e9cbee7 Mon Sep 17 00:00:00 2001 From: Michael Marchetti Date: Mon, 16 Oct 2023 08:23:08 -0400 Subject: [PATCH 13/43] Don't exclude environment directories by name --- rsconnect/bundle.py | 4 ---- tests/test_bundle.py | 4 ---- 2 files changed, 8 deletions(-) diff --git a/rsconnect/bundle.py b/rsconnect/bundle.py index cd0a5093..2bb9cdfd 100644 --- a/rsconnect/bundle.py +++ b/rsconnect/bundle.py @@ -39,17 +39,13 @@ # noinspection SpellCheckingInspection directories_ignore_list = [ ".Rproj.user/", - ".env/", ".git/", ".svn/", - ".venv/", "__pycache__/", - "env/", "packrat/", "renv/", "rsconnect-python/", "rsconnect/", - "venv/", ] directories_to_ignore = {Path(d) for d in directories_ignore_list} diff --git a/tests/test_bundle.py b/tests/test_bundle.py index 663796bf..145b4037 100644 --- a/tests/test_bundle.py +++ b/tests/test_bundle.py @@ -537,10 +537,6 @@ def test_keep_manifest_specified_file(self): self.assertFalse(keep_manifest_specified_file("rsconnect-python")) self.assertFalse(keep_manifest_specified_file("rsconnect-python/bogus.file")) self.assertFalse(keep_manifest_specified_file(".svn/bogus.file")) - self.assertFalse(keep_manifest_specified_file(".env/share/jupyter/kernels/python3/kernel.json")) - self.assertFalse(keep_manifest_specified_file(".venv/bin/activate")) - self.assertFalse(keep_manifest_specified_file("env/pyvenv.cfg")) - self.assertFalse(keep_manifest_specified_file("venv/lib/python3.8/site-packages/wheel/__init__.py")) # noinspection SpellCheckingInspection self.assertFalse(keep_manifest_specified_file(".Rproj.user/bogus.file")) From 749a839b853317a14b5d625d8cb0ed6a3705733d Mon Sep 17 00:00:00 2001 From: Michael Marchetti Date: Mon, 16 Oct 2023 08:25:06 -0400 Subject: [PATCH 14/43] udpate changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ce5225c..45dff38c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - The `https_proxy` environment variable is recognized as a synonym for `HTTPS_PROXY`. +- Common environment directories (`env, venv, .env, .venv`) are no longer + excluded by name. Environments are detected by the presence of a python + executable in `bin` or `Scripts` and excluded. ### Added - Added support for the `no_proxy` or `NO_PROXY` environment variables to specify From 81155b4d41af3eaf2a6cbb0e87f929a44d0c8189 Mon Sep 17 00:00:00 2001 From: Michael Marchetti Date: Mon, 16 Oct 2023 09:05:37 -0400 Subject: [PATCH 15/43] add new verbose logging level --- rsconnect/actions.py | 12 +++++++----- rsconnect/bundle.py | 8 +++++--- rsconnect/log.py | 2 ++ rsconnect/main.py | 29 ++++++++++++++++------------- 4 files changed, 30 insertions(+), 21 deletions(-) diff --git a/rsconnect/actions.py b/rsconnect/actions.py index d0449403..5b9f2541 100644 --- a/rsconnect/actions.py +++ b/rsconnect/actions.py @@ -40,7 +40,7 @@ read_manifest_file, ) from .environment import Environment, MakeEnvironment, EnvironmentException -from .log import logger +from .log import logger, VERBOSE from .models import AppModes, AppMode from .api import RSConnectExecutor, filter_out_server_info @@ -98,15 +98,17 @@ def failed(err): logger.set_in_feedback(False) -def set_verbosity(verbose): +def set_verbosity(verbose: int): """Set the verbosity level based on a passed flag :param verbose: boolean specifying verbose or not """ - if verbose: - logger.setLevel(logging.DEBUG) - else: + if verbose == 0: logger.setLevel(logging.INFO) + elif verbose == 1: + logger.setLevel(VERBOSE) + else: + logger.setLevel(logging.DEBUG) def which_python(python, env=os.environ): diff --git a/rsconnect/bundle.py b/rsconnect/bundle.py index cd0a5093..c2e6048d 100644 --- a/rsconnect/bundle.py +++ b/rsconnect/bundle.py @@ -28,7 +28,7 @@ from os.path import basename, dirname, exists, isdir, join, relpath, splitext, isfile, abspath -from .log import logger +from .log import logger, VERBOSE from .models import AppMode, AppModes, GlobSet from .environment import Environment, MakeEnvironment from .exception import RSConnectException @@ -281,11 +281,13 @@ def to_file(self, flatten_to_deploy_dir=True): if Path(fp).name in self.buffer: continue rel_path = Path(fp).relative_to(self.deploy_dir) if flatten_to_deploy_dir else None + logger.log(VERBOSE, "Adding file: %s", fp) bundle.add(fp, arcname=rel_path) for k, v in self.buffer.items(): buf = io.BytesIO(to_bytes(v)) file_info = tarfile.TarInfo(k) file_info.size = len(buf.getvalue()) + logger.log(VERBOSE, "Adding file: %s", k) bundle.addfile(file_info, buf) bundle_file.seek(0) return bundle_file @@ -428,7 +430,7 @@ def bundle_add_file(bundle, rel_path, base_dir): The file path is relative to the notebook directory. """ path = join(base_dir, rel_path) if os.path.isdir(base_dir) else rel_path - logger.debug("adding file: %s", path) + logger.log(VERBOSE, "Adding file: %s", path) bundle.add(path, arcname=rel_path) @@ -437,7 +439,7 @@ def bundle_add_buffer(bundle, filename, contents): `contents` may be a string or bytes object """ - logger.debug("adding file: %s", filename) + logger.log(VERBOSE, "Adding file: %s", filename) buf = io.BytesIO(to_bytes(contents)) file_info = tarfile.TarInfo(filename) file_info.size = len(buf.getvalue()) diff --git a/rsconnect/log.py b/rsconnect/log.py index 1bfce807..8dfade66 100644 --- a/rsconnect/log.py +++ b/rsconnect/log.py @@ -9,6 +9,8 @@ _DATE_FORMAT = "%Y-%m-%dT%H:%M:%S%z" +VERBOSE = int((logging.INFO + logging.DEBUG) / 2) + class LogOutputFormat(object): TEXT = "text" diff --git a/rsconnect/main.py b/rsconnect/main.py index 5af3128f..9e8bea7c 100644 --- a/rsconnect/main.py +++ b/rsconnect/main.py @@ -135,7 +135,7 @@ def server_args(func): type=click.Path(exists=True, file_okay=True, dir_okay=False), help="The path to trusted TLS CA certificates.", ) - @click.option("--verbose", "-v", is_flag=True, help="Print detailed messages.") + @click.option("--verbose", "-v", count=True, help="Enable verbose output. Use -vv for very verbose (debug) output.") @functools.wraps(func) def wrapper(*args, **kwargs): return func(*args, **kwargs) @@ -243,6 +243,7 @@ def wrapper(*args, **kwargs): return wrapper + # This callback handles the "shorthand" --disable-env-management option. # If the shorthand flag is provided, then it takes precendence over the R and Python flags. # This callback also inverts the --disable-env-management-r and @@ -399,7 +400,7 @@ def _test_rstudio_creds(server: api.PositServer): help="The path to the file containing the private key used to sign the JWT.", ) @click.option("--raw", "-r", is_flag=True, help="Return the API key as raw output rather than a JSON object") -@click.option("--verbose", "-v", is_flag=True, help="Enable verbose output") +@click.option("--verbose", "-v", count=True, help="Enable verbose output. Use -vv for very verbose (debug) output.") @cli_exception_handler def bootstrap( server, @@ -482,11 +483,10 @@ def bootstrap( type=click.Path(exists=True, file_okay=True, dir_okay=False), help="The path to trusted TLS CA certificates.", ) -@click.option("--verbose", "-v", is_flag=True, help="Print detailed messages.") +@click.option("--verbose", "-v", count=True, help="Enable verbose output. Use -vv for very verbose (debug) output.") @cloud_shinyapps_args @click.pass_context def add(ctx, name, server, api_key, insecure, cacert, account, token, secret, verbose): - set_verbosity(verbose) if click.__version__ >= "8.0.0" and sys.version_info >= (3, 7): click.echo("Detected the following inputs:") @@ -550,7 +550,7 @@ def add(ctx, name, server, api_key, insecure, cacert, account, token, secret, ve short_help="List the known Posit Connect servers.", help="Show the stored information about each known server nickname.", ) -@click.option("--verbose", "-v", is_flag=True, help="Print detailed messages.") +@click.option("--verbose", "-v", count=True, help="Enable verbose output. Use -vv for very verbose (debug) output.") def list_servers(verbose): set_verbosity(verbose) with cli_feedback(""): @@ -630,7 +630,7 @@ def details(name, server, api_key, insecure, cacert, verbose): ) @click.option("--name", "-n", help="The nickname of the Posit Connect server to remove.") @click.option("--server", "-s", help="The URL of the Posit Connect server to remove.") -@click.option("--verbose", "-v", is_flag=True, help="Print detailed messages.") +@click.option("--verbose", "-v", count=True, help="Enable verbose output. Use -vv for very verbose (debug) output.") def remove(name, server, verbose): set_verbosity(verbose) @@ -866,7 +866,7 @@ def deploy_notebook( python, conda, force_generate, - verbose: bool, + verbose: int, file: str, extra_files, hide_all_input: bool, @@ -986,7 +986,7 @@ def deploy_voila( env_management_r: bool = None, title: str = None, env_vars: typing.Dict[str, str] = None, - verbose: bool = False, + verbose: int = 0, new: bool = False, app_id: str = None, name: str = None, @@ -1050,7 +1050,7 @@ def deploy_manifest( new: bool, app_id: str, title: str, - verbose: bool, + verbose: int, file: str, env_vars: typing.Dict[str, str], visibility: typing.Optional[str], @@ -1143,7 +1143,7 @@ def deploy_quarto( quarto, python, force_generate: bool, - verbose: bool, + verbose: int, file_or_directory, extra_files, env_vars: typing.Dict[str, str], @@ -1245,7 +1245,7 @@ def deploy_html( exclude=None, title: str = None, env_vars: typing.Dict[str, str] = None, - verbose: bool = False, + verbose: int = 0, new: bool = False, app_id: str = None, name: str = None, @@ -1258,6 +1258,8 @@ def deploy_html( secret: str = None, ): kwargs = locals() + set_verbosity(verbose) + ce = None if connect_server: kwargs = filter_out_server_info(**kwargs) @@ -1360,7 +1362,7 @@ def deploy_app( python, conda, force_generate: bool, - verbose: bool, + verbose: int, directory, extra_files, visibility: typing.Optional[str], @@ -1373,6 +1375,7 @@ def deploy_app( token: str = None, secret: str = None, ): + set_verbosity(verbose) kwargs = locals() kwargs["entrypoint"] = entrypoint = validate_entry_point(entrypoint, directory) kwargs["extra_files"] = extra_files = validate_extra_files(directory, extra_files) @@ -2155,7 +2158,7 @@ def remove_content_build(name, server, api_key, insecure, cacert, guid, all, pur metavar="TEXT", help="Check the local build state of a specific content item. This flag can be passed multiple times.", ) -@click.option("--verbose", "-v", is_flag=True, help="Print detailed messages.") +@click.option("--verbose", "-v", count=True, help="Enable verbose output. Use -vv for very verbose (debug) output.") # todo: --format option (json, text) def list_content_build(name, server, api_key, insecure, cacert, status, guid, verbose): set_verbosity(verbose) From d287c3a95f62aedf4d75792e81cf52c1b64e0ba6 Mon Sep 17 00:00:00 2001 From: Michael Marchetti Date: Mon, 16 Oct 2023 09:07:01 -0400 Subject: [PATCH 16/43] udpate changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ce5225c..34709f9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 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. +- 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`. ## [1.20.0] - 2023-09-11 From 12cbd91706a9c07d379620c082c342ae41405b00 Mon Sep 17 00:00:00 2001 From: Michael Marchetti Date: Mon, 16 Oct 2023 09:13:23 -0400 Subject: [PATCH 17/43] define level name --- rsconnect/log.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rsconnect/log.py b/rsconnect/log.py index 8dfade66..49676cf2 100644 --- a/rsconnect/log.py +++ b/rsconnect/log.py @@ -10,6 +10,7 @@ _DATE_FORMAT = "%Y-%m-%dT%H:%M:%S%z" VERBOSE = int((logging.INFO + logging.DEBUG) / 2) +logging.addLevelName(VERBOSE, "VERBOSE") class LogOutputFormat(object): From c23553b7c3631086a8ffa64489f51b222b67d7af Mon Sep 17 00:00:00 2001 From: Chris Tierney Date: Tue, 17 Oct 2023 12:42:02 -0400 Subject: [PATCH 18/43] enable tmate debugging --- .github/workflows/snyk.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/snyk.yml b/.github/workflows/snyk.yml index 58817cb5..6c8bb0ae 100644 --- a/.github/workflows/snyk.yml +++ b/.github/workflows/snyk.yml @@ -16,6 +16,12 @@ jobs: with: fetch-depth: 0 + - uses: snyk/actions/setup@master + - name: Setup tmate session + uses: mxschmitt/action-tmate@v3 + with: + limit-access-to-actor: true + - name: Run Snyk (setup.py) uses: snyk/actions/python@master with: From a196b798e378f984c7968f8981a6124f19514656 Mon Sep 17 00:00:00 2001 From: Chris Tierney Date: Tue, 17 Oct 2023 13:06:29 -0400 Subject: [PATCH 19/43] switch from snyk/actions/python to snyk/actions/setup --- .github/workflows/snyk.yml | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/.github/workflows/snyk.yml b/.github/workflows/snyk.yml index 6c8bb0ae..afcbb3d7 100644 --- a/.github/workflows/snyk.yml +++ b/.github/workflows/snyk.yml @@ -16,20 +16,34 @@ jobs: with: fetch-depth: 0 - - uses: snyk/actions/setup@master - - name: Setup tmate session - uses: mxschmitt/action-tmate@v3 + # - name: Run Snyk (setup.py) + # uses: snyk/actions/python@master + # with: + # command: monitor + # args: --file=setup.py --package-manager=pip --project-name=setup.py --org=${{ env.SNYK_ORG }} + + # - name: Run Snyk (requirements.txt) + # uses: snyk/actions/python@master + # with: + # command: monitor + # args: --file=requirements.txt --package-manager=pip --project-name=requirements.txt --org=${{ env.SNYK_ORG }} + + # On Oct 2 2023, the steps using snyk/actions/python@master started failing with "undefined". + # Nothing obvious changed in our code or in the Snyk action or Docker image. + # Setting up and running snyk generically seems to work, so we'll go with that. + - name: Set up Python + uses: actions/setup-python@v4 with: - limit-access-to-actor: true + python-version: '3.11' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + + - uses: snyk/actions/setup@master - name: Run Snyk (setup.py) - uses: snyk/actions/python@master - with: - command: monitor - args: --file=setup.py --package-manager=pip --project-name=setup.py --org=${{ env.SNYK_ORG }} + run: snyk monitor --file="setup.py" --package-manager=pip --project-name="setup.py" --org=${{ env.SNYK_ORG }} - name: Run Snyk (requirements.txt) - uses: snyk/actions/python@master - with: - command: monitor - args: --file=requirements.txt --package-manager=pip --project-name=requirements.txt --org=${{ env.SNYK_ORG }} + run: snyk monitor --file="requirements.txt" --package-manager=pip --project-name="requirements.txt" --org=${{ env.SNYK_ORG }} From 917b738cf1eca4baf807a0e7e35694098c4ad242 Mon Sep 17 00:00:00 2001 From: Bill Sager Date: Tue, 17 Oct 2023 13:22:28 -0700 Subject: [PATCH 20/43] remove conda support from codebase --- CHANGELOG.md | 3 + CONTRIBUTING.md | 4 +- conftest.py | 6 +- docs/patch_admonitions.py | 4 +- mock_connect/mock_connect/main.py | 1 - my-shiny-app/app.py | 4 +- rsconnect/actions.py | 98 ++++---- rsconnect/api.py | 34 +-- rsconnect/bundle.py | 108 ++++++--- rsconnect/environment.py | 117 +--------- rsconnect/main.py | 85 +------ tests/test_api.py | 25 +- tests/test_bundle.py | 214 ++++++------------ tests/test_main.py | 13 +- tests/test_main_system_caches.py | 1 + tests/test_timeouts.py | 6 +- tests/testdata/fake_conda.sh | 9 - tests/testdata/stock-api-fastapi/main.py | 4 +- tests/testdata/stock-api-flask/app.py | 4 +- tests/testdata/stock-dashboard-python/app2.py | 23 +- .../testdata/top-5-income-share-bokeh/app3.py | 10 +- 21 files changed, 271 insertions(+), 502 deletions(-) delete mode 100755 tests/testdata/fake_conda.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 45dff38c..77b67fca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 one file matching these new patterns, you must provide rsconnect-python with an explicit `--entrypoint` argument. +### Changed +- Removing experimental support for Conda. Connect does not support restoring Conda environments. + ## [1.20.0] - 2023-09-11 ### Fixed diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 832c5251..921a71f4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,7 +22,9 @@ cd rsconnect-python python3 -m venv .venv # Activate the virtual environment source .venv/bin/activate -# install our requirements into the virtual environment +# install our requirements into the virtual environment based on pyproject.toml +pip install . +# install our requirements into the virtual environment based pip install -r requirements.txt # install rsconnect-python with a symbolic link to the locations repository, # meaning any changes to code in there will automatically be reflected diff --git a/conftest.py b/conftest.py index 9d3d1562..032363e1 100644 --- a/conftest.py +++ b/conftest.py @@ -9,13 +9,13 @@ def pytest_addoption(parser): - parser.addoption( - "--vetiver", action="store_true", default=False, help="run vetiver tests" - ) + parser.addoption("--vetiver", action="store_true", default=False, help="run vetiver tests") + def pytest_configure(config): config.addinivalue_line("markers", "vetiver: test for vetiver interaction") + def pytest_collection_modifyitems(config, items): if config.getoption("--vetiver"): return diff --git a/docs/patch_admonitions.py b/docs/patch_admonitions.py index 043dba36..72a6c02f 100755 --- a/docs/patch_admonitions.py +++ b/docs/patch_admonitions.py @@ -26,6 +26,7 @@ import sys + def rewrite(gh_admonition, mkdocs_admonition, lines): for i in range(len(lines)): line = lines[i] @@ -35,7 +36,7 @@ def rewrite(gh_admonition, mkdocs_admonition, lines): # The start of the GitHub admonition MUST be on its own line. if gh_admonition == line.rstrip(): lines[i] = f"!!! { mkdocs_admonition }\n" - for j in range(i+1, len(lines)): + for j in range(i + 1, len(lines)): if lines[j].startswith("> "): text = lines[j][2:] lines[j] = f" { text }" @@ -44,6 +45,7 @@ def rewrite(gh_admonition, mkdocs_admonition, lines): break return lines + lines = sys.stdin.readlines() lines = rewrite("> **Note**", "note", lines) diff --git a/mock_connect/mock_connect/main.py b/mock_connect/mock_connect/main.py index 2b57475c..c6778bdf 100644 --- a/mock_connect/mock_connect/main.py +++ b/mock_connect/mock_connect/main.py @@ -155,7 +155,6 @@ def python_settings(): return { "installations": [{"version": v}], "api_enabled": True, - "conda_enabled": False, } diff --git a/my-shiny-app/app.py b/my-shiny-app/app.py index 79bb9d0c..f9b5f96b 100644 --- a/my-shiny-app/app.py +++ b/my-shiny-app/app.py @@ -5,10 +5,12 @@ ui.output_text_verbatim("txt", placeholder=True), ) + def server(input, output, session): @output() @render_text() def txt(): return f"n*2 is {input.n() * 2}" -app = App(app_ui, server) \ No newline at end of file + +app = App(app_ui, server) diff --git a/rsconnect/actions.py b/rsconnect/actions.py index d0449403..90b695d0 100644 --- a/rsconnect/actions.py +++ b/rsconnect/actions.py @@ -128,7 +128,6 @@ def which_python(python, env=os.environ): def inspect_environment( python, # type: str directory, # type: str - conda_mode=False, # type: bool force_generate=False, # type: bool check_output=subprocess.check_output, # type: typing.Callable ): @@ -140,8 +139,6 @@ def inspect_environment( """ warn("This method has been moved and will be deprecated.", DeprecationWarning, stacklevel=2) flags = [] - if conda_mode: - flags.append("c") if force_generate: flags.append("f") args = [python, "-m", "rsconnect.environment"] @@ -248,10 +245,9 @@ def gather_server_details(connect_server): and the versions of Python installed there. :param connect_server: the Connect server information. - :return: a three-entry dictionary. The key 'connect' will refer to the version + :return: a two-entry dictionary. The key 'connect' will refer to the version of Connect that was found. The key `python` will refer to a sequence of version - strings for all the versions of Python that are installed. The key `conda` will - refer to data about whether Connect is configured to support Conda environments. + strings for all the versions of Python that are installed. """ warn("This method has been moved and will be deprecated.", DeprecationWarning, stacklevel=2) @@ -262,28 +258,15 @@ def _to_sort_key(text): server_settings = api.verify_server(connect_server) python_settings = api.get_python_info(connect_server) python_versions = sorted([item["version"] for item in python_settings["installations"]], key=_to_sort_key) - conda_settings = {"supported": python_settings["conda_enabled"] if "conda_enabled" in python_settings else False} return { "connect": server_settings["version"], "python": { "api_enabled": python_settings["api_enabled"] if "api_enabled" in python_settings else False, "versions": python_versions, }, - "conda": conda_settings, } -def is_conda_supported_on_server(connect_details): - """ - Returns whether or not conda is supported on a Connect server. - - :param connect_details: details about a Connect server as returned by gather_server_details() - :return: boolean True if supported, False otherwise - :error: Conda is not supported on the target server. Try deploying without requesting Conda. - """ - return connect_details.get("conda", {}).get("supported", False) - - def _make_deployment_name(remote_server: api.TargetableServer, title: str, force_unique: bool) -> str: """ Produce a name for a deployment based on its title. It is assumed that the @@ -613,7 +596,6 @@ def deploy_jupyter_notebook( title: str, static: bool, python: str, - conda_mode: bool, force_generate: bool, log_callback: typing.Callable, hide_all_input: bool, @@ -636,8 +618,6 @@ def deploy_jupyter_notebook( :param static: a flag noting whether the notebook should be deployed as a static HTML page or as a render-able document with sources. Previous default = False. :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 Posit 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 @@ -685,7 +665,7 @@ def deploy_jupyter_notebook( _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) + python, environment = get_python_env_info(file_name, python, force_generate) if force_generate: _warn_on_ignored_requirements(base_dir, environment.filename) @@ -745,7 +725,6 @@ def deploy_app( app_id: str = None, title: str = None, python: str = None, - conda_mode: bool = False, force_generate: bool = False, verbose: bool = None, directory: str = None, @@ -789,7 +768,6 @@ def deploy_app( directory, force_generate, python, - conda_mode, ) ce = RSConnectExecutor(**kwargs) @@ -824,7 +802,6 @@ def deploy_python_api( app_id: int, title: str, python: str, - conda_mode: bool, force_generate: bool, log_callback: typing.Callable, image: str = None, @@ -845,8 +822,6 @@ def deploy_python_api( :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 Posit 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 @@ -874,7 +849,6 @@ def deploy_python_fastapi( app_id: int, title: str, python: str, - conda_mode: bool, force_generate: bool, log_callback: typing.Callable, image: str = None, @@ -895,8 +869,6 @@ def deploy_python_fastapi( :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 Posit 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 @@ -924,7 +896,6 @@ def deploy_python_shiny( app_id=None, title=None, python=None, - conda_mode=False, force_generate=False, log_callback=None, ): @@ -942,8 +913,6 @@ def deploy_python_shiny( :param title: an optional title for the deploy. If this is not provided, ne will be generated. :param python: the optional name of a Python executable. - :param conda_mode: use conda to build an environment.yml - instead of conda, when conda is not supported on Posit Connect (version<=1.8.0). :param force_generate: force generating "requirements.txt" or "environment.yml", even if it already exists. :param log_callback: the callback to use to write the log to. If this is None @@ -966,7 +935,6 @@ def deploy_dash_app( app_id: int, title: str, python: str, - conda_mode: bool, force_generate: bool, log_callback: typing.Callable, image: str = None, @@ -987,8 +955,6 @@ def deploy_dash_app( :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 Posit 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 @@ -1016,7 +982,6 @@ def deploy_streamlit_app( app_id: int, title: str, python: str, - conda_mode: bool, force_generate: bool, log_callback: typing.Callable, image: str = None, @@ -1037,8 +1002,6 @@ def deploy_streamlit_app( :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 Posit 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 @@ -1066,7 +1029,6 @@ def deploy_bokeh_app( app_id: int, title: str, python: str, - conda_mode: bool, force_generate: bool, log_callback: typing.Callable, image: str = None, @@ -1087,8 +1049,6 @@ def deploy_bokeh_app( :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 Posit 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 @@ -1282,8 +1242,9 @@ def create_api_deployment_bundle( if app_mode is None: app_mode = AppModes.PYTHON_API - return make_api_bundle(directory, entry_point, app_mode, environment, extra_files, excludes, - image, env_management_py, env_management_r) + return make_api_bundle( + directory, entry_point, app_mode, environment, extra_files, excludes, image, env_management_py, env_management_r + ) def create_quarto_deployment_bundle( @@ -1320,8 +1281,17 @@ def create_quarto_deployment_bundle( if app_mode is None: app_mode = AppModes.STATIC_QUARTO - return make_quarto_source_bundle(file_or_directory, inspect, app_mode, environment, extra_files, excludes, - image, env_management_py, env_management_r) + return make_quarto_source_bundle( + file_or_directory, + inspect, + app_mode, + environment, + extra_files, + excludes, + image, + env_management_py, + env_management_r, + ) def deploy_bundle( @@ -1429,8 +1399,15 @@ def create_notebook_manifest_and_environment_file( warn("This method has been moved and will be deprecated.", DeprecationWarning, stacklevel=2) if ( not write_notebook_manifest_json( - entry_point_file, environment, app_mode, extra_files, hide_all_input, hide_tagged_input, - image, env_management_py, env_management_r, + entry_point_file, + environment, + app_mode, + extra_files, + hide_all_input, + hide_tagged_input, + image, + env_management_py, + env_management_r, ) or force ): @@ -1483,8 +1460,9 @@ def write_notebook_manifest_json( if app_mode == AppModes.UNKNOWN: raise RSConnectException('Could not determine the app mode from "%s"; please specify one.' % extension) - manifest_data = make_source_manifest(app_mode, environment, file_name, None, - image, env_management_py, env_management_r) + manifest_data = make_source_manifest( + app_mode, environment, file_name, None, image, env_management_py, env_management_r + ) manifest_add_file(manifest_data, file_name, directory) manifest_add_buffer(manifest_data, environment.filename, environment.contents) @@ -1531,8 +1509,17 @@ def create_api_manifest_and_environment_file( """ warn("This method has been moved and will be deprecated.", DeprecationWarning, stacklevel=2) if ( - not write_api_manifest_json(directory, entry_point, environment, app_mode, extra_files, excludes, - image, env_management_py, env_management_r) + not write_api_manifest_json( + directory, + entry_point, + environment, + app_mode, + extra_files, + excludes, + image, + env_management_py, + env_management_r, + ) or force ): write_environment_file(environment, directory) @@ -1571,8 +1558,9 @@ def write_api_manifest_json( """ warn("This method has been moved and will be deprecated.", DeprecationWarning, stacklevel=2) extra_files = validate_extra_files(directory, extra_files) - manifest, _ = make_api_manifest(directory, entry_point, app_mode, environment, extra_files, excludes, - image, env_management_py, env_management_r) + manifest, _ = make_api_manifest( + directory, entry_point, app_mode, environment, extra_files, excludes, image, env_management_py, env_management_r + ) manifest_path = join(directory, "manifest.json") write_manifest_json(manifest_path, manifest) diff --git a/rsconnect/api.py b/rsconnect/api.py index c004958d..9fd55962 100644 --- a/rsconnect/api.py +++ b/rsconnect/api.py @@ -921,10 +921,9 @@ def server_details(self): Builds a dictionary containing the version of Posit Connect that is running and the versions of Python installed there. - :return: a three-entry dictionary. The key 'connect' will refer to the version + :return: a two-entry dictionary. The key 'connect' will refer to the version of Connect that was found. The key `python` will refer to a sequence of version - strings for all the versions of Python that are installed. The key `conda` will - refer to data about whether Connect is configured to support Conda environments. + strings for all the versions of Python that are installed. """ def _to_sort_key(text): @@ -934,16 +933,12 @@ def _to_sort_key(text): server_settings = self.server_settings python_settings = self.python_info python_versions = sorted([item["version"] for item in python_settings["installations"]], key=_to_sort_key) - conda_settings = { - "supported": python_settings["conda_enabled"] if "conda_enabled" in python_settings else False - } return { "connect": server_settings["version"], "python": { "api_enabled": python_settings["api_enabled"] if "api_enabled" in python_settings else False, "versions": python_versions, }, - "conda": conda_settings, } def make_deployment_name(self, title, force_unique): @@ -1121,14 +1116,9 @@ def create_application(self, account_id, application_name): return response def create_output(self, name: str, application_type: str, project_id=None, space_id=None, render_by=None): - data = { - "name": name, - "space": space_id, - "project": project_id, - "application_type": application_type - } + data = {"name": name, "space": space_id, "project": project_id, "application_type": application_type} if render_by: - data['render_by'] = render_by + data["render_by"] = render_by response = self.post("/v1/outputs/", body=data) self._server.handle_bad_response(response) return response @@ -1342,9 +1332,7 @@ def prepare_deploy( app_store_version: typing.Optional[int], ) -> PrepareDeployOutputResult: - application_type = "static" if app_mode in [ - AppModes.STATIC, - AppModes.STATIC_QUARTO] else "connect" + application_type = "static" if app_mode in [AppModes.STATIC, AppModes.STATIC_QUARTO] else "connect" logger.debug(f"application_type: {application_type}") render_by = "server" if app_mode == AppModes.STATIC_QUARTO else None @@ -1362,11 +1350,13 @@ def prepare_deploy( space_id = None # create the new output and associate it with the current Posit Cloud project and space - output = self._rstudio_client.create_output(name=app_name, - application_type=application_type, - project_id=project_id, - space_id=space_id, - render_by=render_by) + output = self._rstudio_client.create_output( + name=app_name, + application_type=application_type, + project_id=project_id, + space_id=space_id, + render_by=render_by, + ) app_id_int = output["source_id"] else: # this is a redeployment of an existing output diff --git a/rsconnect/bundle.py b/rsconnect/bundle.py index 2bb9cdfd..22a14be4 100644 --- a/rsconnect/bundle.py +++ b/rsconnect/bundle.py @@ -116,7 +116,6 @@ def __init__( }, } - if image or env_management_py is not None or env_management_r is not None: self.data["environment"] = {} if image: @@ -459,8 +458,9 @@ def write_manifest( Returns the list of filenames written. """ manifest_filename = "manifest.json" - manifest = make_source_manifest(AppModes.JUPYTER_NOTEBOOK, environment, nb_name, None, - image, env_management_py, env_management_r) + manifest = make_source_manifest( + AppModes.JUPYTER_NOTEBOOK, environment, nb_name, None, image, env_management_py, env_management_r + ) if hide_all_input: if "jupyter" not in manifest: manifest["jupyter"] = {} @@ -541,8 +541,9 @@ def make_notebook_source_bundle( base_dir = dirname(file) nb_name = basename(file) - manifest = make_source_manifest(AppModes.JUPYTER_NOTEBOOK, environment, nb_name, None, - image, env_management_py, env_management_r) + manifest = make_source_manifest( + AppModes.JUPYTER_NOTEBOOK, environment, nb_name, None, image, env_management_py, env_management_r + ) if hide_all_input: if "jupyter" not in manifest: manifest["jupyter"] = {} @@ -595,8 +596,15 @@ def make_quarto_source_bundle( Returns a file-like object containing the bundle tarball. """ manifest, relevant_files = make_quarto_manifest( - file_or_directory, inspect, app_mode, environment, extra_files, excludes, - image, env_management_py, env_management_r, + file_or_directory, + inspect, + app_mode, + environment, + extra_files, + excludes, + image, + env_management_py, + env_management_r, ) bundle_file = tempfile.TemporaryFile(prefix="rsc_bundle") @@ -873,8 +881,9 @@ def make_api_manifest( excludes.extend(list_environment_dirs(directory)) relevant_files = create_file_list(directory, extra_files, excludes) - manifest = make_source_manifest(app_mode, environment, entry_point, None, - image, env_management_py, env_management_r) + manifest = make_source_manifest( + app_mode, environment, entry_point, None, image, env_management_py, env_management_r + ) manifest_add_buffer(manifest, environment.filename, environment.contents) @@ -938,8 +947,14 @@ def create_html_manifest( excludes.extend(["manifest.json"]) excludes.extend(list_environment_dirs(deploy_dir)) - manifest = Manifest(app_mode=AppModes.STATIC, entrypoint=entrypoint, primary_html=entrypoint, - image=image, env_management_py=env_management_py, env_management_r=env_management_r) + manifest = Manifest( + app_mode=AppModes.STATIC, + entrypoint=entrypoint, + primary_html=entrypoint, + image=image, + env_management_py=env_management_py, + env_management_r=env_management_r, + ) manifest.deploy_dir = deploy_dir file_list = create_file_list(path, extra_files, excludes, use_abspath=True) @@ -1196,8 +1211,15 @@ def make_api_bundle( :return: a file-like object containing the bundle tarball. """ manifest, relevant_files = make_api_manifest( - directory, entry_point, app_mode, environment, extra_files, excludes, - image, env_management_py, env_management_r, + directory, + entry_point, + app_mode, + environment, + extra_files, + excludes, + image, + env_management_py, + env_management_r, ) bundle_file = tempfile.TemporaryFile(prefix="rsc_bundle") @@ -1550,7 +1572,6 @@ def which_python(python: typing.Optional[str] = None): def inspect_environment( python, # type: str directory, # type: str - conda_mode=False, # type: bool force_generate=False, # type: bool check_output=subprocess.check_output, # type: typing.Callable ): @@ -1561,8 +1582,6 @@ def inspect_environment( or containing an "error" field if an error occurred. """ flags = [] - if conda_mode: - flags.append("c") if force_generate: flags.append("f") args = [python, "-m", "rsconnect.environment"] @@ -1576,14 +1595,13 @@ def inspect_environment( return MakeEnvironment(**json.loads(environment_json)) # type: ignore -def get_python_env_info(file_name, python, conda_mode=False, force_generate=False): +def get_python_env_info(file_name, python, 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 @@ -1591,7 +1609,7 @@ def get_python_env_info(file_name, python, conda_mode=False, force_generate=Fals """ python = which_python(python) logger.debug("Python: %s" % python) - environment = inspect_environment(python, dirname(file_name), conda_mode=conda_mode, force_generate=force_generate) + environment = inspect_environment(python, dirname(file_name), force_generate=force_generate) if environment.error: raise RSConnectException(environment.error) logger.debug("Python: %s" % python) @@ -1638,8 +1656,15 @@ def create_notebook_manifest_and_environment_file( """ if ( not write_notebook_manifest_json( - entry_point_file, environment, app_mode, extra_files, hide_all_input, hide_tagged_input, - image, env_management_py, env_management_r, + entry_point_file, + environment, + app_mode, + extra_files, + hide_all_input, + hide_tagged_input, + image, + env_management_py, + env_management_r, ) or force ): @@ -1691,8 +1716,9 @@ def write_notebook_manifest_json( if app_mode == AppModes.UNKNOWN: raise RSConnectException('Could not determine the app mode from "%s"; please specify one.' % extension) - manifest_data = make_source_manifest(app_mode, environment, file_name, None, - image, env_management_py, env_management_r) + manifest_data = make_source_manifest( + app_mode, environment, file_name, None, image, env_management_py, env_management_r + ) if hide_all_input or hide_tagged_input: if "jupyter" not in manifest_data: manifest_data["jupyter"] = dict() @@ -1789,8 +1815,14 @@ def create_voila_manifest( if isfile(voila_json_path): extra_files.append(voila_json_path) - manifest = Manifest(app_mode=AppModes.JUPYTER_VOILA, environment=environment, entrypoint=entrypoint, - image=image, env_management_py=env_management_py, env_management_r=env_management_r) + manifest = Manifest( + app_mode=AppModes.JUPYTER_VOILA, + environment=environment, + entrypoint=entrypoint, + image=image, + env_management_py=env_management_py, + env_management_r=env_management_r, + ) manifest.deploy_dir = deploy_dir if entrypoint and isfile(entrypoint): validate_file_is_notebook(entrypoint) @@ -1880,8 +1912,17 @@ def create_api_manifest_and_environment_file( :return: """ if ( - not write_api_manifest_json(directory, entry_point, environment, app_mode, extra_files, excludes, - image, env_management_py, env_management_r) + not write_api_manifest_json( + directory, + entry_point, + environment, + app_mode, + extra_files, + excludes, + image, + env_management_py, + env_management_r, + ) or force ): write_environment_file(environment, directory) @@ -1919,8 +1960,9 @@ def write_api_manifest_json( etc.) that goes along with the manifest exists. """ extra_files = validate_extra_files(directory, extra_files) - manifest, _ = make_api_manifest(directory, entry_point, app_mode, environment, extra_files, excludes, - image, env_management_py, env_management_r) + manifest, _ = make_api_manifest( + directory, entry_point, app_mode, environment, extra_files, excludes, image, env_management_py, env_management_r + ) manifest_path = join(directory, "manifest.json") write_manifest_json(manifest_path, manifest) @@ -1997,8 +2039,9 @@ def write_quarto_manifest_json( """ extra_files = validate_extra_files(directory, extra_files) - manifest, _ = make_quarto_manifest(directory, inspect, app_mode, environment, extra_files, excludes, - image, env_management_py, env_management_r) + manifest, _ = make_quarto_manifest( + directory, inspect, app_mode, environment, extra_files, excludes, image, env_management_py, env_management_r + ) manifest_path = join(directory, "manifest.json") write_manifest_json(manifest_path, manifest) @@ -2017,7 +2060,6 @@ def create_python_environment( directory: str = None, force_generate: bool = False, python: str = None, - conda: bool = False, ): module_file = fake_module_file_from_directory(directory) @@ -2028,7 +2070,7 @@ def create_python_environment( _warn_if_environment_directory(directory) # with cli_feedback("Inspecting Python environment"): - _, environment = get_python_env_info(module_file, python, conda, force_generate) + _, environment = get_python_env_info(module_file, python, force_generate) if force_generate: _warn_on_ignored_requirements(directory, environment.filename) diff --git a/rsconnect/environment.py b/rsconnect/environment.py index 9639af12..71148fa0 100644 --- a/rsconnect/environment.py +++ b/rsconnect/environment.py @@ -22,14 +22,12 @@ typing = None version_re = re.compile(r"\d+\.\d+(\.\d+)?") -conda_version_re = re.compile(r"^(?:\s*-\s*)?python=(\d+\.\d+(?:\.\d+)?)", re.MULTILINE) exec_dir = os.path.dirname(sys.executable) Environment = collections.namedtuple( "Environment", ( - "conda", "contents", "error", "filename", @@ -43,7 +41,6 @@ def MakeEnvironment( - conda: Optional[str] = None, contents: Optional[str] = None, error: Optional[str] = None, filename: Optional[str] = None, @@ -53,107 +50,44 @@ def MakeEnvironment( python: Optional[str] = None, source: Optional[str] = None, ): - return Environment(conda, contents, error, filename, locale, package_manager, pip, python, source) + return Environment(contents, error, filename, locale, package_manager, pip, python, source) class EnvironmentException(Exception): pass -def detect_environment(dirname, force_generate=False, conda_mode=False, conda=None): - # type: (str, bool, bool, typing.Optional[str]) -> Environment +def detect_environment(dirname, force_generate=False): + # type: (str, bool) -> Environment """Determine the python dependencies in the environment. - `pip freeze` will be used to introspect the environment unless `conda_mode` is - set to `True`. In that case, an attempt will be made to use Conda to introspect - the environment. + `pip freeze` will be used to introspect the environment. :param: dirname Directory name :param: force_generate Force the generation of an environment - :param: conda_mode inspect the environment assuming Conda :return: a dictionary containing the package spec filename and contents if successful, or a dictionary containing `error` on failure. """ - conda = get_conda(conda) - if conda_mode and conda: - if force_generate: - result = conda_env_export(conda) - else: - result = output_file(dirname, "environment.yml", "conda") or conda_env_export(conda) + if force_generate: + result = pip_freeze() else: - if force_generate: - result = pip_freeze() - else: - result = output_file(dirname, "requirements.txt", "pip") or pip_freeze() + result = output_file(dirname, "requirements.txt", "pip") or pip_freeze() if result is not None: - if conda_mode and result["package_manager"] != "conda": - return MakeEnvironment( - error=( - 'Conda was requested but no activated Conda environment was found. See "conda activate ' - '--help" for more information.' - ) - ) - result["python"] = get_python_version(MakeEnvironment(**result)) result["pip"] = get_version("pip") - if conda: - result["conda"] = get_conda_version(conda) result["locale"] = get_default_locale() return MakeEnvironment(**result) -def get_conda(conda=None): - """get_conda tries to find the conda executable if we're in - a conda environment. If not, or if we cannot find the executable, - return None. - :returns: conda string path to conda or None. - """ - if os.environ.get("CONDA_PREFIX", None) is None and conda is None: - return None - else: - return conda or os.environ.get("CONDA_EXE", None) - - def get_python_version(environment): # type: (Environment) -> str - if environment.package_manager == "conda": - versions = conda_version_re.findall(environment.contents) - if len(versions) > 0: - version = versions[0] - if version.count(".") == 1: - version = version + ".0" - return version - v = sys.version_info return "%d.%d.%d" % (v[0], v[1], v[2]) -def get_conda_version(conda): - try: - args = [conda, "-V"] - proc = subprocess.Popen( - args, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True, - ) - stdout, stderr = proc.communicate() - match = version_re.search(stdout or stderr) - if match: - return match.group() - msg = "Failed to get version of conda from the output of: %s - standard output: %s; standard error: %s" % ( - " ".join(args), - stdout, - stderr, - ) - raise EnvironmentException(msg) - except Exception as exception: - raise EnvironmentException("Error getting conda version: %s" % str(exception)) - - def get_default_locale(locale_source=locale.getlocale): result = ".".join([item or "" for item in locale_source()]) return "" if result == "." else result @@ -256,35 +190,6 @@ def exclude(line): return line and line.startswith("setuptools") and "post" in line -def conda_env_export(conda): - """Inspect the environment using `conda env export` - :param: conda path to the `conda` tool - :return: dictionary containing the key "environment.yml" and the data inside - """ - try: - proc = subprocess.Popen( - [conda, "env", "export"], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True, - ) - conda_stdout, conda_stderr = proc.communicate() - conda_status = proc.returncode - except Exception as exception: - raise EnvironmentException("Error during conda env export: %s" % str(exception)) - - if conda_status != 0: - msg = conda_stderr or ("exited with code %d" % conda_status) - raise EnvironmentException("Error during conda env export: %s" % msg) - - return { - "filename": "environment.yml", - "contents": conda_stdout, - "source": "conda_env_export", - "package_manager": "conda", - } - - def main(): """ Run `detect_environment` and dump the result as JSON. @@ -296,18 +201,14 @@ def main(): directory = sys.argv[len(sys.argv) - 1] flags = "" force_generate = False - conda_mode = False if len(sys.argv) > 2: flags = sys.argv[1] if "f" in flags: force_generate = True - if "c" in flags: - conda_mode = True - envinfo = detect_environment(directory, force_generate, conda_mode)._asdict() + envinfo = detect_environment(directory, force_generate)._asdict() if "contents" in envinfo: keepers = list(map(strip_ref, envinfo["contents"].split("\n"))) - if not conda_mode: - keepers = [line for line in keepers if not exclude(line)] + keepers = [line for line in keepers if not exclude(line)] envinfo["contents"] = "\n".join(keepers) json.dump( diff --git a/rsconnect/main.py b/rsconnect/main.py index 5af3128f..f38e9292 100644 --- a/rsconnect/main.py +++ b/rsconnect/main.py @@ -243,6 +243,7 @@ def wrapper(*args, **kwargs): return wrapper + # This callback handles the "shorthand" --disable-env-management option. # If the shorthand flag is provided, then it takes precendence over the R and Python flags. # This callback also inverts the --disable-env-management-r and @@ -252,7 +253,7 @@ def wrapper(*args, **kwargs): # which is more consistent when writing these values to the manifest. def env_management_callback(ctx, param, value) -> typing.Optional[bool]: # eval the shorthand flag if it was provided - disable_env_management = ctx.params.get('disable_env_management') + disable_env_management = ctx.params.get("disable_env_management") if disable_env_management is not None: value = disable_env_management @@ -603,7 +604,6 @@ def details(name, server, api_key, insecure, cacert, verbose): connect_version = server_details["connect"] apis_allowed = server_details["python"]["api_enabled"] python_versions = server_details["python"]["versions"] - conda_details = server_details["conda"] click.echo(" Posit Connect version: %s" % ("" if len(connect_version) == 0 else connect_version)) @@ -616,9 +616,6 @@ def details(name, server, api_key, insecure, cacert, verbose): click.echo(" APIs: %sallowed" % ("" if apis_allowed else "not ")) - if future_enabled: - click.echo(" Conda: %ssupported" % ("" if conda_details["supported"] else "not ")) - @cli.command( short_help="Remove the information about a Posit Connect server.", @@ -766,21 +763,6 @@ def _warn_if_environment_directory(directory): ) -def _warn_on_ignored_conda_env(environment): - """ - Checks for a discovered Conda environment and produces a warning that it will be ignored when - Conda was not requested. The warning is only shown if we're in "future" mode since we don't - yet want to advertise Conda support. - - :param environment: The Python environment that was discovered. - """ - if future_enabled and environment.package_manager != "conda" and environment.conda is not None: - click.echo( - " Using %s for package management; the current Conda environment will be ignored." - % environment.package_manager - ) - - def _warn_on_ignored_requirements(directory, requirements_file_name): """ Checks for the existence of a file called manifest.json in the given directory. @@ -829,13 +811,6 @@ def _warn_on_ignored_requirements(directory, requirements_file_name): "The Python environment must have the rsconnect package installed." ), ) -@click.option( - "--conda", - "-C", - is_flag=True, - hidden=True, - help="Use Conda to deploy (requires Connect version 1.8.2 or later)", -) @click.option( "--force-generate", "-g", @@ -864,7 +839,6 @@ def deploy_notebook( app_id: str, title: str, python, - conda, force_generate, verbose: bool, file: str, @@ -887,7 +861,7 @@ def deploy_notebook( _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, python, conda, force_generate) + python, environment = get_python_env_info(file, python, force_generate) if force_generate: _warn_on_ignored_requirements(base_dir, environment.filename) @@ -1081,11 +1055,11 @@ def deploy_manifest( name="quarto", short_help="Deploy Quarto content to Posit Connect [v2021.08.0+] or Posit Cloud.", help=( - 'Deploy a Quarto document or project to Posit Connect or Posit Cloud. Should the content use the Quarto ' + "Deploy a Quarto document or project to Posit Connect or Posit Cloud. Should the content use the Quarto " 'Jupyter engine, an environment file ("requirements.txt") is created and included in the deployment if one ' - 'does not already exist. Requires Posit Connect 2021.08.0 or later.' - '\n\n' - 'FILE_OR_DIRECTORY is the path to a single-file Quarto document or the directory containing a Quarto project.' + "does not already exist. Requires Posit Connect 2021.08.0 or later." + "\n\n" + "FILE_OR_DIRECTORY is the path to a single-file Quarto document or the directory containing a Quarto project." ), no_args_is_help=True, ) @@ -1176,9 +1150,7 @@ def deploy_quarto( _warn_if_environment_directory(base_dir) with cli_feedback("Inspecting Python environment"): - python, environment = get_python_env_info(module_file, python, False, force_generate) - - _warn_on_ignored_conda_env(environment) + python, environment = get_python_env_info(module_file, python, force_generate) if force_generate: _warn_on_ignored_requirements(base_dir, environment.filename) @@ -1325,13 +1297,6 @@ def generate_deploy_python(app_mode, alias, min_version): "The Python environment must have the rsconnect package installed." ), ) - @click.option( - "--conda", - "-C", - is_flag=True, - hidden=True, - help="Use Conda to deploy (requires Connect version 1.8.2 or later)", - ) @click.option( "--force-generate", "-g", @@ -1358,7 +1323,6 @@ def deploy_app( app_id: str, title: str, python, - conda, force_generate: bool, verbose: bool, directory, @@ -1380,7 +1344,6 @@ def deploy_app( directory, force_generate, python, - conda, ) ce = RSConnectExecutor(**kwargs) @@ -1467,13 +1430,6 @@ def write_manifest(): help="Path to Python interpreter whose environment should be used. " + "The Python environment must have the rsconnect package installed.", ) -@click.option( - "--conda", - "-C", - is_flag=True, - hidden=True, - help="Use Conda to deploy (requires Connect version 1.8.2 or later)", -) @click.option( "--force-generate", "-g", @@ -1493,7 +1449,6 @@ def write_manifest(): def write_manifest_notebook( overwrite, python, - conda, force_generate, verbose, file, @@ -1516,9 +1471,7 @@ def write_manifest_notebook( raise RSConnectException("manifest.json already exists. Use --overwrite to overwrite.") with cli_feedback("Inspecting Python environment"): - python, environment = get_python_env_info(file, python, conda, force_generate) - - _warn_on_ignored_conda_env(environment) + python, environment = get_python_env_info(file, python, force_generate) with cli_feedback("Creating manifest.json"): environment_file_exists = write_notebook_manifest_json( @@ -1615,9 +1568,7 @@ def write_manifest_voila( raise RSConnectException("manifest.json already exists. Use --overwrite to overwrite.") with cli_feedback("Inspecting Python environment"): - python, environment = get_python_env_info(path, python, False, force_generate) - - _warn_on_ignored_conda_env(environment) + python, environment = get_python_env_info(path, python, force_generate) environment_file_exists = exists(join(base_dir, environment.filename)) @@ -1734,7 +1685,6 @@ def write_manifest_quarto( with cli_feedback("Inspecting Python environment"): python, environment = get_python_env_info(base_dir, python, False, force_generate) - _warn_on_ignored_conda_env(environment) environment_file_exists = exists(join(base_dir, environment.filename)) if environment_file_exists and not force_generate: click.secho( @@ -1795,13 +1745,6 @@ def generate_write_manifest_python(app_mode, alias): help="Path to Python interpreter whose environment should be used. " + "The Python environment must have the rsconnect-python package installed.", ) - @click.option( - "--conda", - "-C", - is_flag=True, - hidden=True, - help="Use Conda to deploy (requires Connect version 1.8.2 or later)", - ) @click.option( "--force-generate", "-g", @@ -1821,7 +1764,6 @@ def manifest_writer( entrypoint, exclude, python, - conda, force_generate, verbose, directory, @@ -1836,7 +1778,6 @@ def manifest_writer( entrypoint, exclude, python, - conda, force_generate, verbose, directory, @@ -1864,7 +1805,6 @@ def _write_framework_manifest( entrypoint, exclude, python, - conda, force_generate, verbose, directory, @@ -1882,7 +1822,6 @@ def _write_framework_manifest( :param exclude: a sequence of exclude glob patterns to exclude files from the deploy. :param python: a path to the Python executable to use. - :param conda: a flag to note whether Conda should be used/assumed.. :param force_generate: a flag to force the generation of manifest and requirements file. :param verbose: a flag to produce more (debugging) output. @@ -1905,9 +1844,7 @@ def _write_framework_manifest( raise RSConnectException("manifest.json already exists. Use --overwrite to overwrite.") with cli_feedback("Inspecting Python environment"): - _, environment = get_python_env_info(directory, python, conda, force_generate) - - _warn_on_ignored_conda_env(environment) + _, environment = get_python_env_info(directory, python, force_generate) with cli_feedback("Creating manifest.json"): environment_file_exists = write_api_manifest_json( diff --git a/tests/test_api.py b/tests/test_api.py index b37b475b..9b71a4b0 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -224,7 +224,6 @@ def test_deploy_existing_application_with_failure(self): class ShinyappsServiceTestCase(TestCase): - def setUp(self) -> None: super().setUp() self.cloud_client = Mock(spec=PositClient) @@ -236,11 +235,11 @@ def test_do_deploy(self): app_id = 2 task_id = 3 - self.cloud_client.deploy_application.return_value = {'id': task_id} + self.cloud_client.deploy_application.return_value = {"id": task_id} self.service.do_deploy(bundle_id, app_id) - self.cloud_client.set_bundle_status.assert_called_with(bundle_id, 'ready') + self.cloud_client.set_bundle_status.assert_called_with(bundle_id, "ready") self.cloud_client.deploy_application.assert_called_with(bundle_id, app_id) self.cloud_client.wait_until_task_is_successful.assert_called_with(task_id) @@ -250,9 +249,9 @@ def test_do_deploy_failure(self): task_id = 3 build_task_id = 4 - self.cloud_client.deploy_application.return_value = {'id': task_id} - self.cloud_client.wait_until_task_is_successful.side_effect = DeploymentFailedException('uh oh') - self.cloud_client.get_shinyapps_build_task.return_value = {'tasks': [{'id': build_task_id}]} + self.cloud_client.deploy_application.return_value = {"id": task_id} + self.cloud_client.wait_until_task_is_successful.side_effect = DeploymentFailedException("uh oh") + self.cloud_client.get_shinyapps_build_task.return_value = {"tasks": [{"id": build_task_id}]} task_logs_response = Mock() task_logs_response.response_body = "here's why it failed" self.cloud_client.get_task_logs.return_value = task_logs_response @@ -260,7 +259,7 @@ def test_do_deploy_failure(self): with pytest.raises(DeploymentFailedException): self.service.do_deploy(bundle_id, app_id) - self.cloud_client.set_bundle_status.assert_called_with(bundle_id, 'ready') + self.cloud_client.set_bundle_status.assert_called_with(bundle_id, "ready") self.cloud_client.deploy_application.assert_called_with(bundle_id, app_id) self.cloud_client.wait_until_task_is_successful.assert_called_with(task_id) self.cloud_client.get_shinyapps_build_task.assert_called_with(task_id) @@ -366,7 +365,7 @@ def test_prepare_new_deploy_static_quarto(self): cloud_client.get_application.assert_called_with(project_application_id) cloud_client.get_content.assert_called_with(2) cloud_client.create_output.assert_called_with( - name=app_name, application_type="static", project_id=2, space_id=1000, render_by='server' + name=app_name, application_type="static", project_id=2, space_id=1000, render_by="server" ) def test_prepare_redeploy(self): @@ -485,11 +484,11 @@ def test_do_deploy(self): app_id = 2 task_id = 3 - self.cloud_client.deploy_application.return_value = {'id': task_id} + self.cloud_client.deploy_application.return_value = {"id": task_id} self.cloud_service.do_deploy(bundle_id, app_id) - self.cloud_client.set_bundle_status.assert_called_with(bundle_id, 'ready') + self.cloud_client.set_bundle_status.assert_called_with(bundle_id, "ready") self.cloud_client.deploy_application.assert_called_with(bundle_id, app_id) self.cloud_client.wait_until_task_is_successful.assert_called_with(task_id) @@ -498,8 +497,8 @@ def test_do_deploy_failure(self): app_id = 2 task_id = 3 - self.cloud_client.deploy_application.return_value = {'id': task_id} - self.cloud_client.wait_until_task_is_successful.side_effect = DeploymentFailedException('uh oh') + self.cloud_client.deploy_application.return_value = {"id": task_id} + self.cloud_client.wait_until_task_is_successful.side_effect = DeploymentFailedException("uh oh") task_logs_response = Mock() task_logs_response.response_body = "here's why it failed" self.cloud_client.get_task_logs.return_value = task_logs_response @@ -507,7 +506,7 @@ def test_do_deploy_failure(self): with pytest.raises(DeploymentFailedException): self.cloud_service.do_deploy(bundle_id, app_id) - self.cloud_client.set_bundle_status.assert_called_with(bundle_id, 'ready') + self.cloud_client.set_bundle_status.assert_called_with(bundle_id, "ready") self.cloud_client.deploy_application.assert_called_with(bundle_id, app_id) self.cloud_client.wait_until_task_is_successful.assert_called_with(task_id) self.cloud_client.get_task_logs.assert_called_with(task_id) diff --git a/tests/test_bundle.py b/tests/test_bundle.py index 145b4037..cc8f5bcd 100644 --- a/tests/test_bundle.py +++ b/tests/test_bundle.py @@ -74,8 +74,14 @@ def test_make_notebook_source_bundle1(self): # the kernel environment and not the notebook server environment. environment = detect_environment(directory) with make_notebook_source_bundle( - nb_path, environment, None, hide_all_input=False, hide_tagged_input=False, - image=None, env_management_py=None, env_management_r=None, + nb_path, + environment, + None, + hide_all_input=False, + hide_tagged_input=False, + image=None, + env_management_py=None, + env_management_r=None, ) as bundle, tarfile.open(mode="r:gz", fileobj=bundle) as tar: names = sorted(tar.getnames()) self.assertEqual( @@ -204,7 +210,7 @@ def test_make_notebook_source_bundle2(self): "environment_management": { "python": False, "r": False, - } + }, }, "files": { "dummy.ipynb": { @@ -232,35 +238,27 @@ def test_make_quarto_source_bundle_from_project(self): fp = open(join(temp_proj, "_quarto.yml"), "w") fp.write("project:\n") fp.write(' title: "myquarto"\n') - fp.write('editor: visual\n') + fp.write("editor: visual\n") environment = detect_environment(temp_proj) # mock the result of running of `quarto inspect ` inspect = { - 'quarto': {'version': '1.3.433'}, - 'dir': temp_proj, - 'engines': ['jupyter'], - 'config': { - 'project': {'title': 'myquarto'}, - 'editor': 'visual', - 'language': {} + "quarto": {"version": "1.3.433"}, + "dir": temp_proj, + "engines": ["jupyter"], + "config": {"project": {"title": "myquarto"}, "editor": "visual", "language": {}}, + "files": { + "input": [temp_proj + "/myquarto.qmd"], + "resources": [], + "config": [temp_proj + "/_quarto.yml"], + "configResources": [], }, - 'files': { - 'input': [temp_proj + '/myquarto.qmd'], - 'resources': [], - 'config': [temp_proj + '/_quarto.yml'], - 'configResources': [] - } } - with make_quarto_source_bundle(temp_proj, - inspect, - AppModes.STATIC_QUARTO, - environment, - [], - [], - None) as bundle, tarfile.open(mode="r:gz", fileobj=bundle) as tar: + with make_quarto_source_bundle( + temp_proj, inspect, AppModes.STATIC_QUARTO, environment, [], [], None + ) as bundle, tarfile.open(mode="r:gz", fileobj=bundle) as tar: names = sorted(tar.getnames()) self.assertEqual( names, @@ -283,9 +281,7 @@ def test_make_quarto_source_bundle_from_project(self): { "version": 1, "locale": mock.ANY, - "metadata": { - "appmode": "quarto-static" - }, + "metadata": {"appmode": "quarto-static"}, "python": { "version": self.python_version(), "package_manager": { @@ -294,7 +290,7 @@ def test_make_quarto_source_bundle_from_project(self): "version": mock.ANY, }, }, - "quarto": {'engines': ['jupyter'], 'version': mock.ANY}, + "quarto": {"engines": ["jupyter"], "version": mock.ANY}, "files": { "_quarto.yml": {"checksum": mock.ANY}, "myquarto.qmd": {"checksum": mock.ANY}, @@ -320,7 +316,7 @@ def test_make_quarto_source_bundle_from_project_with_requirements(self): fp = open(join(temp_proj, "_quarto.yml"), "w") fp.write("project:\n") fp.write(' title: "myquarto"\n') - fp.write('editor: visual\n') + fp.write("editor: visual\n") fp = open(join(temp_proj, "requirements.txt"), "w") fp.write("dash\n") @@ -331,29 +327,21 @@ def test_make_quarto_source_bundle_from_project_with_requirements(self): # mock the result of running of `quarto inspect ` inspect = { - 'quarto': {'version': '1.3.433'}, - 'dir': temp_proj, - 'engines': ['jupyter'], - 'config': { - 'project': {'title': 'myquarto'}, - 'editor': 'visual', - 'language': {} + "quarto": {"version": "1.3.433"}, + "dir": temp_proj, + "engines": ["jupyter"], + "config": {"project": {"title": "myquarto"}, "editor": "visual", "language": {}}, + "files": { + "input": [temp_proj + "/myquarto.qmd"], + "resources": [], + "config": [temp_proj + "/_quarto.yml"], + "configResources": [], }, - 'files': { - 'input': [temp_proj + '/myquarto.qmd'], - 'resources': [], - 'config': [temp_proj + '/_quarto.yml'], - 'configResources': [] - } } - with make_quarto_source_bundle(temp_proj, - inspect, - AppModes.STATIC_QUARTO, - environment, - [], - [], - None) as bundle, tarfile.open(mode="r:gz", fileobj=bundle) as tar: + with make_quarto_source_bundle( + temp_proj, inspect, AppModes.STATIC_QUARTO, environment, [], [], None + ) as bundle, tarfile.open(mode="r:gz", fileobj=bundle) as tar: names = sorted(tar.getnames()) self.assertEqual( names, @@ -376,9 +364,7 @@ def test_make_quarto_source_bundle_from_project_with_requirements(self): { "version": 1, "locale": mock.ANY, - "metadata": { - "appmode": "quarto-static" - }, + "metadata": {"appmode": "quarto-static"}, "python": { "version": self.python_version(), "package_manager": { @@ -387,7 +373,7 @@ def test_make_quarto_source_bundle_from_project_with_requirements(self): "version": mock.ANY, }, }, - "quarto": {'engines': ['jupyter'], 'version': mock.ANY}, + "quarto": {"engines": ["jupyter"], "version": mock.ANY}, "files": { "_quarto.yml": {"checksum": mock.ANY}, "myquarto.qmd": {"checksum": mock.ANY}, @@ -410,17 +396,13 @@ def test_make_quarto_source_bundle_from_file(self): # mock the result of running of `quarto inspect ` inspect = { - 'quarto': {'version': '1.3.433'}, - 'engines': ['markdown'], + "quarto": {"version": "1.3.433"}, + "engines": ["markdown"], } - with make_quarto_source_bundle(temp_proj, - inspect, - AppModes.STATIC_QUARTO, - None, - [], - [], - None) as bundle, tarfile.open(mode="r:gz", fileobj=bundle) as tar: + with make_quarto_source_bundle( + temp_proj, inspect, AppModes.STATIC_QUARTO, None, [], [], None + ) as bundle, tarfile.open(mode="r:gz", fileobj=bundle) as tar: names = sorted(tar.getnames()) self.assertEqual( names, @@ -437,10 +419,8 @@ def test_make_quarto_source_bundle_from_file(self): manifest, { "version": 1, - "metadata": { - "appmode": "quarto-static" - }, - "quarto": {'engines': ['markdown'], 'version': mock.ANY}, + "metadata": {"appmode": "quarto-static"}, + "quarto": {"engines": ["markdown"], "version": mock.ANY}, "files": { "myquarto.qmd": {"checksum": mock.ANY}, }, @@ -566,8 +546,7 @@ def test_make_source_manifest(self): ) # include image parameter - manifest = make_source_manifest(AppModes.PYTHON_API, None, None, None, - image="rstudio/connect:bionic") + manifest = make_source_manifest(AppModes.PYTHON_API, None, None, None, image="rstudio/connect:bionic") self.assertEqual( manifest, { @@ -581,53 +560,47 @@ def test_make_source_manifest(self): ) # include env_management_py parameter - manifest = make_source_manifest(AppModes.PYTHON_API, None, None, None, - env_management_py=False) + manifest = make_source_manifest(AppModes.PYTHON_API, None, None, None, env_management_py=False) self.assertEqual( manifest, { "version": 1, "metadata": {"appmode": "python-api"}, - "environment": { - "environment_management": { - "python": False - } - }, + "environment": {"environment_management": {"python": False}}, "files": {}, }, ) # include env_management_r parameter - manifest = make_source_manifest(AppModes.PYTHON_API, None, None, None, - env_management_r=False) + manifest = make_source_manifest(AppModes.PYTHON_API, None, None, None, env_management_r=False) self.assertEqual( manifest, { "version": 1, "metadata": {"appmode": "python-api"}, - "environment": { - "environment_management": { - "r": False - } - }, + "environment": {"environment_management": {"r": False}}, "files": {}, }, ) # include all runtime environment parameters - manifest = make_source_manifest(AppModes.PYTHON_API, None, None, None, - image="rstudio/connect:bionic", env_management_py=False, env_management_r=False) + manifest = make_source_manifest( + AppModes.PYTHON_API, + None, + None, + None, + image="rstudio/connect:bionic", + env_management_py=False, + env_management_r=False, + ) self.assertEqual( manifest, { "version": 1, "metadata": {"appmode": "python-api"}, - "environment": { + "environment": { "image": "rstudio/connect:bionic", - "environment_management": { - "r": False, - "python": False - } + "environment_management": {"r": False, "python": False}, }, "files": {}, }, @@ -637,7 +610,6 @@ def test_make_source_manifest(self): manifest = make_source_manifest( AppModes.PYTHON_API, Environment( - conda=None, contents="", error=None, filename="requirements.txt", @@ -766,9 +738,7 @@ def test_make_quarto_manifest_doc_no_opt_params(self): "version": 1, "metadata": {"appmode": "quarto-static"}, "quarto": {"version": "0.9.16", "engines": ["jupyter"]}, - "files": { - basename(temp_doc): {'checksum': mock.ANY} - }, + "files": {basename(temp_doc): {"checksum": mock.ANY}}, }, ) @@ -819,7 +789,6 @@ def test_make_quarto_manifest_project_with_env(self): }, AppModes.SHINY_QUARTO, Environment( - conda=None, contents="", error=None, filename="requirements.txt", @@ -968,8 +937,7 @@ def test_make_html_manifest(self): ) # include image parameter - manifest = make_html_manifest("abc.html", - image="rstudio/connect:bionic") + manifest = make_html_manifest("abc.html", image="rstudio/connect:bionic") # print(manifest) self.assertEqual( manifest, @@ -986,8 +954,7 @@ def test_make_html_manifest(self): ) # include env_management_py parameter - manifest = make_html_manifest("abc.html", - env_management_py=False) + manifest = make_html_manifest("abc.html", env_management_py=False) # print(manifest) self.assertEqual( manifest, @@ -1006,8 +973,7 @@ def test_make_html_manifest(self): ) # include env_management_r parameter - manifest = make_html_manifest("abc.html", - env_management_r=False) + manifest = make_html_manifest("abc.html", env_management_r=False) # print(manifest) self.assertEqual( manifest, @@ -1026,10 +992,9 @@ def test_make_html_manifest(self): ) # include all runtime environment parameters - manifest = make_html_manifest("abc.html", - image="rstudio/connect:bionic", - env_management_py=False, - env_management_r=False) + manifest = make_html_manifest( + "abc.html", image="rstudio/connect:bionic", env_management_py=False, env_management_r=False + ) # print(manifest) self.assertEqual( manifest, @@ -1044,7 +1009,7 @@ def test_make_html_manifest(self): "environment_management": { "python": False, "r": False, - } + }, }, }, ) @@ -1141,7 +1106,6 @@ def test_inspect_environment(self): ( "file_name", "python", - "conda_mode", "force_generate", "expected_python", "expected_environment", @@ -1151,10 +1115,8 @@ def test_inspect_environment(self): "path/to/file.py", sys.executable, False, - False, sys.executable, MakeEnvironment( - conda=None, filename="requirements.txt", locale="en_US.UTF-8", package_manager="pip", @@ -1166,10 +1128,8 @@ def test_inspect_environment(self): "another/file.py", basename(sys.executable), False, - False, sys.executable, MakeEnvironment( - conda=None, filename="requirements.txt", locale="en_US.UTF-8", package_manager="pip", @@ -1177,26 +1137,10 @@ def test_inspect_environment(self): ), 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", @@ -1207,7 +1151,6 @@ def test_get_python_env_info( monkeypatch, file_name, python, - conda_mode, force_generate, expected_python, expected_environment, @@ -1218,7 +1161,6 @@ def fake_which_python(python, env=os.environ): def fake_inspect_environment( python, directory, - conda_mode=False, force_generate=False, check_output=subprocess.check_output, ): @@ -1230,11 +1172,9 @@ def fake_inspect_environment( 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) + _, _ = get_python_env_info(file_name, python, force_generate=force_generate) else: - python, environment = get_python_env_info( - file_name, python, conda_mode=conda_mode, force_generate=force_generate - ) + python, environment = get_python_env_info(file_name, python, force_generate=force_generate) assert python == expected_python assert environment == expected_environment @@ -1337,7 +1277,6 @@ def test_guess_deploy_dir(self): ) def test_create_voila_manifest_1(path, entrypoint): environment = Environment( - conda=None, contents="bqplot\n", error=None, filename="requirements.txt", @@ -1413,7 +1352,6 @@ def test_create_voila_manifest_1(path, entrypoint): ) def test_create_voila_manifest_2(path, entrypoint): environment = Environment( - conda=None, contents="numpy\nipywidgets\nbqplot\n", error=None, filename="requirements.txt", @@ -1461,7 +1399,6 @@ def test_create_voila_manifest_2(path, entrypoint): def test_create_voila_manifest_extra(): environment = Environment( - conda=None, contents="numpy\nipywidgets\nbqplot\n", error=None, filename="requirements.txt", @@ -1547,7 +1484,6 @@ def test_create_voila_manifest_extra(): ) def test_create_voila_manifest_multi_notebook(path, entrypoint): environment = Environment( - conda=None, contents="bqplot\n", error=None, filename="requirements.txt", @@ -1655,7 +1591,6 @@ def test_make_voila_bundle( entrypoint, ): environment = Environment( - conda=None, contents="bqplot", error=None, filename="requirements.txt", @@ -1763,7 +1698,6 @@ def test_make_voila_bundle_multi_notebook( entrypoint, ): environment = Environment( - conda=None, contents="bqplot", error=None, filename="requirements.txt", @@ -1853,7 +1787,6 @@ def test_make_voila_bundle_2( entrypoint, ): environment = Environment( - conda=None, contents="numpy\nipywidgets\nbqplot\n", error=None, filename="requirements.txt", @@ -1918,7 +1851,6 @@ def test_make_voila_bundle_extra(): requirements_hash = "d51994456975ff487749acc247ae6d63" environment = Environment( - conda=None, contents="numpy\nipywidgets\nbqplot\n", error=None, filename="requirements.txt", @@ -2031,7 +1963,7 @@ def test_create_html_manifest(): "environment_management": { "python": False, "r": False, - } + }, }, } manifest = create_html_manifest( diff --git a/tests/test_main.py b/tests/test_main.py index 8bad8096..b6f825d3 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -267,7 +267,7 @@ def post_deploy_callback(request, uri, response_headers): "--title", "myApp", "--visibility", - "private" + "private", ] try: result = runner.invoke(cli, args) @@ -443,7 +443,6 @@ def post_deploy_callback(request, uri, response_headers): if original_server_value: os.environ["CONNECT_SERVER"] = original_server_value - @httpretty.activate(verbose=True, allow_net_connect=False) @pytest.mark.parametrize( "project_application_id,project_id", @@ -624,7 +623,7 @@ def post_deploy_callback(request, uri, response_headers): "--title", "myApp", "--visibility", - "public" + "public", ] try: result = runner.invoke(cli, args) @@ -640,7 +639,6 @@ def post_deploy_callback(request, uri, response_headers): @httpretty.activate(verbose=True, allow_net_connect=False) @pytest.mark.parametrize( "command,arg", - [ [ "manifest", @@ -657,7 +655,7 @@ def test_deploy_static_cloud(self, command, arg): """ Verify that an app with app_mode as static can deploy to cloud. """ - shutil.rmtree(os.path.join(arg, 'rsconnect-python'), ignore_errors=True) + shutil.rmtree(os.path.join(arg, "rsconnect-python"), ignore_errors=True) original_api_key_value = os.environ.pop("CONNECT_API_KEY", None) original_server_value = os.environ.pop("CONNECT_SERVER", None) @@ -912,18 +910,17 @@ def test_add_shinyapps_missing_options(self): if original_server_value: os.environ["CONNECT_SERVER"] = original_server_value - def test_env_management_callback(self): ctx = click.Context(cli) # env_management is always False when --disable-env-management is True - ctx.params = {'disable_env_management': True} + ctx.params = {"disable_env_management": True} assert env_management_callback(ctx, None, None) is False assert env_management_callback(ctx, None, True) is False assert env_management_callback(ctx, None, False) is False # (env_management == not value) when --disable-env-management is None - ctx.params = {'disable_env_management': None} + ctx.params = {"disable_env_management": None} assert env_management_callback(ctx, None, None) is None assert env_management_callback(ctx, None, True) is False assert env_management_callback(ctx, None, False) is True diff --git a/tests/test_main_system_caches.py b/tests/test_main_system_caches.py index e14f1d02..cbb50778 100644 --- a/tests/test_main_system_caches.py +++ b/tests/test_main_system_caches.py @@ -16,6 +16,7 @@ CACHE_EXISTS_COMMAND = "docker-compose exec -u rstudio-connect -T rsconnect [ -d /data/python-environments/pip/1.2.3 ]" SERVICE_RUNNING_COMMAND = "docker-compose ps --services --filter 'status=running' | grep rsconnect" + def rsconnect_service_running(): exit_code = system(SERVICE_RUNNING_COMMAND) if exit_code == 0: diff --git a/tests/test_timeouts.py b/tests/test_timeouts.py index 89b637ed..148fd4cf 100644 --- a/tests/test_timeouts.py +++ b/tests/test_timeouts.py @@ -32,6 +32,7 @@ def test_get_negative_timeout_from_environment(self): with self.assertRaises(RSConnectException): get_request_timeout() + class GetTaskTimeoutTestCase(TestCase): def test_get_default_timeout(self): timeout = get_task_timeout() @@ -57,10 +58,13 @@ def test_get_negative_timeout_from_environment(self): with self.assertRaises(RSConnectException): get_task_timeout() + class GetTaskTimeoutHelpMessageTestCase(TestCase): def test_get_task_timeout_help_message(self): res = get_task_timeout_help_message(1) self.assertTrue("The task timed out after 1 seconds." in res) - self.assertTrue("You may try increasing the task timeout value using the CONNECT_TASK_TIMEOUT environment variable." in res) # noqa: E501 + self.assertTrue( + "You may try increasing the task timeout value using the CONNECT_TASK_TIMEOUT environment variable." in res + ) # noqa: E501 self.assertTrue("The default value is 86400 seconds." in res) self.assertTrue("The current value is 86400 seconds." in res) diff --git a/tests/testdata/fake_conda.sh b/tests/testdata/fake_conda.sh deleted file mode 100755 index f30aa2b8..00000000 --- a/tests/testdata/fake_conda.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -if [[ $1 = '-V' ]]; then - echo 'Conda 1.0.0' - exit 0 -fi - -echo "this is a conda environment" -exit 0 diff --git a/tests/testdata/stock-api-fastapi/main.py b/tests/testdata/stock-api-fastapi/main.py index f47c39ab..c69f2ca1 100644 --- a/tests/testdata/stock-api-fastapi/main.py +++ b/tests/testdata/stock-api-fastapi/main.py @@ -58,9 +58,7 @@ async def ticker(ticker: str): ticker_prices = prices[prices["ticker"] == ticker] current_price = ticker_prices["close"].last("1d").round(2) - current_volatility = np.log( - ticker_prices["adjusted"] / ticker_prices["adjusted"].shift(1) - ).var() + current_volatility = np.log(ticker_prices["adjusted"] / ticker_prices["adjusted"].shift(1)).var() return { "ticker": ticker, diff --git a/tests/testdata/stock-api-flask/app.py b/tests/testdata/stock-api-flask/app.py index bb8f83fa..5bb3278c 100644 --- a/tests/testdata/stock-api-flask/app.py +++ b/tests/testdata/stock-api-flask/app.py @@ -95,9 +95,7 @@ def get(self, ticker): ticker_prices = prices[prices["ticker"] == ticker] current_price = ticker_prices["close"].last("1d").round(2) - current_volatility = np.log( - ticker_prices["adjusted"] / ticker_prices["adjusted"].shift(1) - ).var() + current_volatility = np.log(ticker_prices["adjusted"] / ticker_prices["adjusted"].shift(1)).var() return { "ticker": ticker, diff --git a/tests/testdata/stock-dashboard-python/app2.py b/tests/testdata/stock-dashboard-python/app2.py index a200cd50..d1ad2f19 100644 --- a/tests/testdata/stock-dashboard-python/app2.py +++ b/tests/testdata/stock-dashboard-python/app2.py @@ -36,12 +36,8 @@ def custom_date_parser(date): tickers = prices["ticker"].unique() # Dataframe with top 5 volumes for each ticker -max_vol = ( - prices.set_index(["date"]).groupby("ticker")["volume"].nlargest(1).reset_index() -) -min_vol = ( - prices.set_index(["date"]).groupby("ticker")["volume"].nsmallest(1).reset_index() -) +max_vol = prices.set_index(["date"]).groupby("ticker")["volume"].nlargest(1).reset_index() +min_vol = prices.set_index(["date"]).groupby("ticker")["volume"].nsmallest(1).reset_index() extreme_vol = pd.concat([max_vol, min_vol]) extreme_vol.columns = ["Stock", "Date", "Top and Lowest Volumes"] @@ -144,8 +140,7 @@ def custom_date_parser(date): # price and volume graphs graphs = [ dbc.Alert( - "📊 Hover over the charts to highlight data points and show graph utilities. " - "All data is historical.", + "📊 Hover over the charts to highlight data points and show graph utilities. " "All data is historical.", color="info", ), dcc.Graph(id="stock-price-graph", animate=True), @@ -246,9 +241,7 @@ def filter_data_by_date(df, ticker, start_date, end_date): if end_date is None: end_date = MAX_DATE - filtered = df[ - (df["ticker"] == ticker) & (df["date"] >= start_date) & (df["date"] <= end_date) - ] + filtered = df[(df["ticker"] == ticker) & (df["date"] >= start_date) & (df["date"] <= end_date)] return filtered @@ -406,9 +399,7 @@ def update_scatter_plot(all_tickers, price): for stock in ["AAPL", "AMZN", "FB", "GOOG", "INTC", "MSFT"]: col_name = stock - date_final[col_name] = final.loc[final["ticker"] == stock][ - "daily ret" - ].values + date_final[col_name] = final.loc[final["ticker"] == stock]["daily ret"].values ret_list = date_final.columns[1:] fig = px.scatter_matrix(date_final, dimensions=ret_list) fig.update_traces(diagonal_visible=False) @@ -429,9 +420,7 @@ def update_scatter_plot(all_tickers, price): for stock in all_tickers: col_name = stock - date_final[col_name] = final.loc[final["ticker"] == stock][ - "daily ret" - ].values + date_final[col_name] = final.loc[final["ticker"] == stock]["daily ret"].values ret_list = date_final.columns[1:] fig = px.scatter_matrix(date_final, dimensions=ret_list) diff --git a/tests/testdata/top-5-income-share-bokeh/app3.py b/tests/testdata/top-5-income-share-bokeh/app3.py index 02cbdf1f..a36e3f85 100644 --- a/tests/testdata/top-5-income-share-bokeh/app3.py +++ b/tests/testdata/top-5-income-share-bokeh/app3.py @@ -80,14 +80,8 @@ def update(): selected_countries = countries_selector.value countries = [name for name, _ in grouped if name in selected_countries] years = [list(df["Year"]) for name, df in grouped if name in selected_countries] - percents = [ - list(df["Percent"]) for name, df in grouped if name in selected_countries - ] - span = [ - "%s - %s" % (df["Year"].min(), df["Year"].max()) - for name, df in grouped - if name in selected_countries - ] + percents = [list(df["Percent"]) for name, df in grouped if name in selected_countries] + span = ["%s - %s" % (df["Year"].min(), df["Year"].max()) for name, df in grouped if name in selected_countries] mean = [df["Percent"].mean() for name, df in grouped if name in selected_countries] color = [colors[name] for name, df in grouped if name in selected_countries] source.data = dict( From e8cce470df181a2b0d92ffdc742439b92ca94d0e Mon Sep 17 00:00:00 2001 From: Bill Sager Date: Tue, 17 Oct 2023 13:44:25 -0700 Subject: [PATCH 21/43] updated dependency for setuptools>=61 --- CONTRIBUTING.md | 2 - .../LICENSE.md | 361 ++++++ .../METADATA | 1128 +++++++++++++++++ .../entry_points.txt | 2 + .../top_level.txt | 1 + .../zip-safe | 1 + .../rsconnect_python.egg-info/PKG-INFO | 1128 +++++++++++++++++ .../rsconnect_python.egg-info/SOURCES.txt | 234 ++++ .../dependency_links.txt | 1 + .../entry_points.txt | 2 + .../rsconnect_python.egg-info/requires.txt | 5 + .../rsconnect_python.egg-info/top_level.txt | 1 + .../rsconnect_python.egg-info/zip-safe | 1 + pyproject.toml | 2 +- requirements.txt | 1 + setup.py | 2 +- 16 files changed, 2868 insertions(+), 4 deletions(-) create mode 100644 pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/LICENSE.md create mode 100644 pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/METADATA create mode 100644 pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/entry_points.txt create mode 100644 pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/top_level.txt create mode 100644 pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/zip-safe create mode 100644 pip-wheel-metadata/rsconnect_python.egg-info/PKG-INFO create mode 100644 pip-wheel-metadata/rsconnect_python.egg-info/SOURCES.txt create mode 100644 pip-wheel-metadata/rsconnect_python.egg-info/dependency_links.txt create mode 100644 pip-wheel-metadata/rsconnect_python.egg-info/entry_points.txt create mode 100644 pip-wheel-metadata/rsconnect_python.egg-info/requires.txt create mode 100644 pip-wheel-metadata/rsconnect_python.egg-info/top_level.txt create mode 100644 pip-wheel-metadata/rsconnect_python.egg-info/zip-safe diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 921a71f4..7a794756 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,8 +22,6 @@ cd rsconnect-python python3 -m venv .venv # Activate the virtual environment source .venv/bin/activate -# install our requirements into the virtual environment based on pyproject.toml -pip install . # install our requirements into the virtual environment based pip install -r requirements.txt # install rsconnect-python with a symbolic link to the locations repository, diff --git a/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/LICENSE.md b/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/LICENSE.md new file mode 100644 index 00000000..50e720ec --- /dev/null +++ b/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/LICENSE.md @@ -0,0 +1,361 @@ +### GNU GENERAL PUBLIC LICENSE + +Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +### Preamble + +The licenses for most software are designed to take away your freedom +to share and change it. By contrast, the GNU General Public License is +intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + +We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, +we want its recipients to know that what they have is not the +original, so that any problems introduced by others will not reflect +on the original authors' reputations. + +Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at +all. + +The precise terms and conditions for copying, distribution and +modification follow. + +### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +**0.** This License applies to any program or other work which +contains a notice placed by the copyright holder saying it may be +distributed under the terms of this General Public License. The +"Program", below, refers to any such program or work, and a "work +based on the Program" means either the Program or any derivative work +under copyright law: that is to say, a work containing the Program or +a portion of it, either verbatim or with modifications and/or +translated into another language. (Hereinafter, translation is +included without limitation in the term "modification".) Each licensee +is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the Program +(independent of having been made by running the Program). Whether that +is true depends on what the Program does. + +**1.** You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a +fee. + +**2.** You may modify your copy or copies of the Program or any +portion of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + +**a)** You must cause the modified files to carry prominent notices +stating that you changed the files and the date of any change. + + +**b)** You must cause any work that you distribute or publish, that in +whole or in part contains or is derived from the Program or any part +thereof, to be licensed as a whole at no charge to all third parties +under the terms of this License. + + +**c)** If the modified program normally reads commands interactively +when run, you must cause it, when started running for such interactive +use in the most ordinary way, to print or display an announcement +including an appropriate copyright notice and a notice that there is +no warranty (or else, saying that you provide a warranty) and that +users may redistribute the program under these conditions, and telling +the user how to view a copy of this License. (Exception: if the +Program itself is interactive but does not normally print such an +announcement, your work based on the Program is not required to print +an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +**3.** You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + +**a)** Accompany it with the complete corresponding machine-readable +source code, which must be distributed under the terms of Sections 1 +and 2 above on a medium customarily used for software interchange; or, + + +**b)** Accompany it with a written offer, valid for at least three +years, to give any third party, for a charge no more than your cost of +physically performing source distribution, a complete machine-readable +copy of the corresponding source code, to be distributed under the +terms of Sections 1 and 2 above on a medium customarily used for +software interchange; or, + + +**c)** Accompany it with the information you received as to the offer +to distribute corresponding source code. (This alternative is allowed +only for noncommercial distribution and only if you received the +program in object code or executable form with such an offer, in +accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + +**4.** You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt otherwise +to copy, modify, sublicense or distribute the Program is void, and +will automatically terminate your rights under this License. However, +parties who have received copies, or rights, from you under this +License will not have their licenses terminated so long as such +parties remain in full compliance. + +**5.** You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +**6.** Each time you redistribute the Program (or any work based on +the Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + +**7.** If, as a consequence of a court judgment or allegation of +patent infringement or for any other reason (not limited to patent +issues), conditions are imposed on you (whether by court order, +agreement or otherwise) that contradict the conditions of this +License, they do not excuse you from the conditions of this License. +If you cannot distribute so as to satisfy simultaneously your +obligations under this License and any other pertinent obligations, +then as a consequence you may not distribute the Program at all. For +example, if a patent license would not permit royalty-free +redistribution of the Program by all those who receive copies directly +or indirectly through you, then the only way you could satisfy both it +and this License would be to refrain entirely from distribution of the +Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +**8.** If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + +**9.** The Free Software Foundation may publish revised and/or new +versions of the General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Program does not specify a +version number of this License, you may choose any version ever +published by the Free Software Foundation. + +**10.** If you wish to incorporate parts of the Program into other +free programs whose distribution conditions are different, write to +the author to ask for permission. For software which is copyrighted by +the Free Software Foundation, write to the Free Software Foundation; +we sometimes make exceptions for this. Our decision will be guided by +the two goals of preserving the free status of all derivatives of our +free software and of promoting the sharing and reuse of software +generally. + +**NO WARRANTY** + +**11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +**12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + +### END OF TERMS AND CONDITIONS + +### How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + +To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + one line to give the program's name and an idea of what it does. + Copyright (C) yyyy name of author + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +Also add information on how to contact you by electronic and paper +mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details + type `show w'. This is free software, and you are welcome + to redistribute it under certain conditions; type `show c' + for details. + +The hypothetical commands \`show w' and \`show c' should show the +appropriate parts of the General Public License. Of course, the +commands you use may be called something other than \`show w' and +\`show c'; they could even be mouse-clicks or menu items--whatever +suits your program. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the program, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright + interest in the program `Gnomovision' + (which makes passes at compilers) written + by James Hacker. + + signature of Ty Coon, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, +you may consider it more useful to permit linking proprietary +applications with the library. If this is what you want to do, use the +[GNU Lesser General Public +License](https://www.gnu.org/licenses/lgpl.html) instead of this +License. diff --git a/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/METADATA b/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/METADATA new file mode 100644 index 00000000..479cc88f --- /dev/null +++ b/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/METADATA @@ -0,0 +1,1128 @@ +Metadata-Version: 2.1 +Name: rsconnect-python +Version: 1.20.1.dev23+g917b738.d20231017 +Summary: Python integration with Posit Connect +Home-page: http://github.com/rstudio/rsconnect-python +Author: Michael Marchetti +Author-email: mike@posit.co +License: GPL-2.0 +Project-URL: Documentation, https://docs.posit.co/rsconnect-python +Requires-Python: >=3.7 +Description-Content-Type: text/markdown +License-File: LICENSE.md +Requires-Dist: six >=1.14.0 +Requires-Dist: click >=7.0.0 +Requires-Dist: pip >=10.0.0 +Requires-Dist: semver <3.0.0,>=2.0.0 +Requires-Dist: pyjwt >=2.4.0 + +# The rsconnect-python CLI + +This package provides a CLI (command-line interface) for interacting +with and deploying to Posit Connect. This is also used by the +[`rsconnect-jupyter`](https://github.com/rstudio/rsconnect-jupyter) package to deploy +Jupyter notebooks via the Jupyter web console. Many types of content supported by Posit +Connect may be deployed by this package, including WSGI-style APIs, Dash, Streamlit, and +Bokeh applications. + +Content types not directly supported by the CLI may also be deployed if they include a +prepared `manifest.json` file. See ["Deploying R or Other +Content"](#deploying-r-or-other-content) for details. + + +## Deploying Python Content to Posit Connect + +Posit Connect supports the deployment of Jupyter notebooks, Python APIs (such as +those based on Flask or FastAPI) and apps (such as Dash, Streamlit, and Bokeh apps). +Much like deploying R +content to Posit Connect, there are some caveats to understand when replicating your +environment on the Posit Connect server: + +Posit Connect insists on matching `` versions of Python. For example, +a server with only Python 3.8 installed will fail to match content deployed with +Python 3.7. Your administrator may also enable exact Python version matching which +will be stricter and require matching major, minor, and patch versions. For more +information see the [Posit Connect Admin Guide chapter titled Python Version +Matching](https://docs.posit.co/connect/admin/python/#python-version-matching). + +### Installation + +To install `rsconnect-python` from PYPI, you may use any python package manager such as +pip: + +```bash +pip install rsconnect-python +``` + +You may also build and install a wheel directly from a repository clone: + +```bash +git clone https://github.com/rstudio/rsconnect-python.git +cd rsconnect-python +pip install pipenv +make deps dist +pip install ./dist/rsconnect_python-*.whl +``` + +### Using the rsconnect CLI + +Here's an example command that deploys a Jupyter notebook to Posit Connect. + +```bash +rsconnect deploy notebook \ + --server https://connect.example.org \ + --api-key my-api-key \ + my-notebook.ipynb +``` + +> **Note** +> The examples here use long command line options, but there are short +> options (`-s`, `-k`, etc.) available also. Run `rsconnect deploy notebook --help` +> for details. + +### Setting up `rsconnect` CLI auto-completion + +If you would like to use your shell's tab completion support with the `rsconnect` +command, use the command below for the shell you are using. + +#### `bash` + +If you are using the `bash` shell, use this to enable tab completion. + +```bash +#~/.bashrc +eval "$(_RSCONNECT_COMPLETE=source rsconnect)" +``` + +#### `zsh` + +If you are using the `zsh` shell, use this to enable tab completion. + +```zsh +#~/.zshrc +eval "$(_RSCONNECT_COMPLETE=source_zsh rsconnect)" +``` + +If you get `command not found: compdef`, you need to add the following lines to your +`.zshrc` before the completion setup: + +```zsh +#~/.zshrc +autoload -Uz compinit +compinit +``` + +### Managing Server Information + +The information used by the `rsconnect` command to communicate with a Posit Connect +server can be tedious to repeat on every command. To help, the CLI supports the idea +of saving this information, making it usable by a simple nickname. + +> **Warning** +> One item of information saved is the API key used to authenticate with +> Posit Connect. Although the file where this information is saved is marked as +> accessible by the owner only, it's important to remember that the key is present +> in the file as plain text so care must be taken to prevent any unauthorized access +> to the server information file. + +#### TLS Support and Posit Connect + +Usually, a Posit Connect server will be set up to be accessed in a secure manner, +using the `https` protocol rather than simple `http`. If Posit Connect is set up +with a self-signed certificate, you will need to include the `--insecure` flag on +all commands. If Posit Connect is set up to require a client-side certificate chain, +you will need to include the `--cacert` option that points to your certificate +authority (CA) trusted certificates file. Both of these options can be saved along +with the URL and API Key for a server. + +> **Note** +> When certificate information is saved for the server, the specified file +> is read and its _contents_ are saved under the server's nickname. If the CA file's +> contents are ever changed, you will need to add the server information again. + +See the [Network Options](#network-options) section for more details about these options. + +#### Remembering Server Information + +Use the `add` command to store information about a Posit Connect server: + +```bash +rsconnect add \ + --api-key my-api-key \ + --server https://connect.example.org \ + --name myserver +``` + +> **Note** +> The `rsconnect` CLI will verify that the serve URL and API key +> are valid. If either is found not to be, no information will be saved. + +If any of the access information for the server changes, simply rerun the +`add` command with the new information and it will replace the original +information. + +Once the server's information is saved, you can refer to it by its nickname: + +```bash +rsconnect deploy notebook --name myserver my-notebook.ipynb +``` + +If there is information for only one server saved, this will work too: + +```bash +rsconnect deploy notebook my-notebook.ipynb +``` + +#### Listing Server Information + +You can see the list of saved server information with: + +``` +rsconnect list +``` + +#### Removing Server Information + +You can remove information about a server with: + +``` +rsconnect remove --name myserver +``` + +Removing may be done by its nickname (`--name`) or URL (`--server`). + +### Verifying Server Information + +You can verify that a URL refers to a running instance of Posit Connect by using +the `details` command: + +```bash +rsconnect details --server https://connect.example.org +``` + +In this form, `rsconnect` will only tell you whether the URL given does, in fact, refer +to a running Posit Connect instance. If you include a valid API key: + +```bash +rsconnect details --server https://connect.example.org --api-key my-api-key +``` + +the tool will provide the version of Posit Connect (if the server is configured to +divulge that information) and environmental information including versions of Python +that are installed on the server. + +You can also use nicknames with the `details` command if you want to verify that the +stored information is still valid. + +### Notebook Deployment Options + +There are a variety of options available to you when deploying a Jupyter notebook to +Posit Connect. + +#### Including Extra Files + +You can include extra files in the deployment bundle to make them available when your +notebook is run by the Posit Connect server. Just specify them on the command line +after the notebook file: + +```bash +rsconnect deploy notebook my-notebook.ipynb data.csv +``` + +#### Package Dependencies + +If a `requirements.txt` file exists in the same directory as the notebook file, it will +be included in the bundle. It must specify the package dependencies needed to execute +the notebook. Posit Connect will reconstruct the Python environment using the +specified package list. + +If there is no `requirements.txt` file or the `--force-generate` option is specified, +the package dependencies will be determined from the current Python environment, or +from an alternative Python executable specified via the `--python` option: + +```bash +rsconnect deploy notebook --python /path/to/python my-notebook.ipynb +``` + +You can see the packages list that will be included by running `pip list --format=freeze` yourself, +ensuring that you use the same Python that you use to run your Jupyter Notebook: + +```bash +/path/to/python -m pip list --format=freeze +``` + +#### Static (Snapshot) Deployment + +By default, `rsconnect` deploys the original notebook with all its source code. This +enables the Posit Connect server to re-run the notebook upon request or on a schedule. + +If you just want to publish an HTML snapshot of the notebook, you can use the `--static` +option. This will cause `rsconnect` to execute your notebook locally to produce the HTML +file, then publish the HTML file to the Posit Connect server: + +```bash +rsconnect deploy notebook --static my-notebook.ipynb +``` + +### Creating a Manifest for Future Deployment + +You can create a `manifest.json` file for a Jupyter Notebook, then use that manifest +in a later deployment. Use the `write-manifest` command to do this. + +The `write-manifest` command will also create a `requirements.txt` file, if it does +not already exist or the `--force-generate` option is specified. It will contain the +package dependencies from the current Python environment, or from an alternative +Python executable specified in the `--python` option. + +Here is an example of the `write-manifest` command: + +```bash +rsconnect write-manifest notebook my-notebook.ipynb +``` + +> **Note** +> Manifests for static (pre-rendered) notebooks cannot be created. + +### API/Application Deployment Options + +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) +* `dash`: Python Dash apps +* `streamlit`: Streamlit apps +* `bokeh`: Bokeh server apps + +All options below apply equally to the `api`, `fastapi`, `dash`, `streamlit`, +and `bokeh` sub-commands. + +#### Including Extra Files + +You can include extra files in the deployment bundle to make them available when your +API or application is run by the Posit Connect server. Just specify them on the +command line after the API or application directory: + +```bash +rsconnect deploy api flask-api/ data.csv +``` + +Since deploying an API or application starts at a directory level, there will be times +when some files under that directory subtree should not be included in the deployment +or manifest. Use the `--exclude` option to specify files or directories to exclude. + +```bash +rsconnect deploy dash --exclude dash-app-venv --exclude TODO.txt dash-app/ +``` + +You can exclude a directory by naming it: +```bash +rsconnect deploy dash --exclude dash-app-venv --exclude output/ dash-app/ +``` + +The `--exclude` option may be repeated, and may include a glob pattern. +You should always quote a glob pattern so that it will be passed to `rsconnect` as-is +instead of letting the shell expand it. If a file is specifically listed as an extra +file that also matches an exclusion pattern, the file will still be included in the +deployment (i.e., extra files take precedence). + +```bash +rsconnect deploy dash --exclude dash-app-venv --exclude “*.txt” dash-app/ +``` + +The following shows an example of an extra file taking precedence: + +```bash +rsconnect deploy dash --exclude “*.csv” dash-app/ important_data.csv +``` + +Some directories are excluded by default, to prevent bundling and uploading files that are not needed or might interfere with the deployment process: + +``` +.Rproj.user +.env +.git +.svn +.venv +__pycache__ +env +packrat +renv +rsconnect-python +rsconnect +venv +``` + +Any directory that appears to be a Python virtual environment (by containing +`bin/python`) will also be excluded. + + +#### Package Dependencies + +If a `requirements.txt` file exists in the API/application directory, it will be +included in the bundle. It must specify the package dependencies needed to execute +the API or application. Posit Connect will reconstruct the Python environment using +the specified package list. + +If there is no `requirements.txt` file or the `--force-generate` option is specified, +the package dependencies will be determined from the current Python environment, or +from an alternative Python executable specified via the `--python` option: + +```bash +rsconnect deploy api --python /path/to/python my-api/ +``` + +You can see the packages list that will be included by running `pip list --format=freeze` yourself, +ensuring that you use the same Python that you use to run your API or application: + +```bash +/path/to/python -m pip list --format=freeze +``` + +### Creating a Manifest for Future Deployment + +You can create a `manifest.json` file for an API or application, then use that +manifest in a later deployment. Use the `write-manifest` command to do this. + +The `write-manifest` command will also create a `requirements.txt` file, if it does +not already exist or the `--force-generate` option is specified. It will contain +the package dependencies from the current Python environment, or from an alternative +Python executable specified in the `--python` option. + +Here is an example of the `write-manifest` command: + +```bash +rsconnect write-manifest api my-api/ +``` + +### Deploying R or Other Content + +You can deploy other content that has an existing Posit Connect `manifest.json` +file. For example, if you download and unpack a source bundle from Posit Connect, +you can deploy the resulting directory. The options are similar to notebook or +API/application deployment; see `rsconnect deploy manifest --help` for details. + +Here is an example of the `deploy manifest` command: + +```bash +rsconnect deploy manifest /path/to/manifest.json +``` + +> **Note** +> In this case, the existing content is deployed as-is. Python environment +> inspection and notebook pre-rendering, if needed, are assumed to be done already +> and represented in the manifest. + +The argument to `deploy manifest` may also be a directory so long as that directory +contains a `manifest.json` file. + +If you have R content but don't have a `manifest.json` file, you can use the RStudio +IDE to create the manifest. See the help for the `rsconnect::writeManifest` R function: + +```r +install.packages('rsconnect') +library(rsconnect) +?rsconnect::writeManifest +``` + +### Options for All Types of Deployments + +These options apply to any type of content deployment. + +#### Title + +The title of the deployed content is, by default, derived from the filename. For +example, if you deploy `my-notebook.ipynb`, the title will be `my-notebook`. To change +this, use the `--title` option: + +``` +rsconnect deploy notebook --title "My Notebook" my-notebook.ipynb +``` + +When using `rsconnect deploy api`, `rsconnect deploy fastapi`, `rsconnect deploy dash`, +`rsconnect deploy streamlit`, or `rsconnect deploy bokeh`, the title is derived from the directory +containing the API or application. + +When using `rsconnect deploy manifest`, the title is derived from the primary +filename referenced in the manifest. + +### Environment variables +You can set environment variables during deployment. Their names and values will be +passed to Posit Connect during deployment so you can use them in your code. Note that +if you are using `rsconnect` to deploy to shinyapps.io, environment variable management +is not supported on that platform. + +For example, if `notebook.ipynb` contains +```python +print(os.environ["MYVAR"]) +``` + +You can set the value of `MYVAR` that will be set when your code runs in Posit Connect +using the `-E/--environment` option: +```bash +rsconnect deploy notebook --environment MYVAR='hello world' notebook.ipynb +``` + +To avoid exposing sensitive values on the command line, you can specify +a variable without a value. In this case, it will use the value from the +environment in which rsconnect-python is running: +```bash +export SECRET_KEY=12345 + +rsconnect deploy notebook --environment SECRET_KEY notebook.ipynb +``` + +If you specify environment variables when updating an existing deployment, +new values will be set for the variables you provided. Other variables will +remain unchanged. If you don't specify any variables, all of the existing +variables will remain unchanged. + +Environment variables are set on the content item before the content bundle +is uploaded and deployed. If the deployment fails, the new environment variables +will still take effect. + +### Network Options + +When specifying information that `rsconnect` needs to be able to interact with Posit +Connect, you can tailor how transport layer security is performed. + +#### TLS/SSL Certificates + +Posit Connect servers can be configured to use TLS/SSL. If your server's certificate +is trusted by your Jupyter Notebook server, API client or user's browser, then you +don't need to do anything special. You can test this out with the `details` command: + +```bash +rsconnect details \ + --api-key my-api-key \ + --server https://connect.example.org:3939 +``` + +If this fails with a TLS Certificate Validation error, then you have two options. + +* Provide the Root CA certificate that is at the root of the signing chain for your + Posit Connect server. This will enable `rsconnect` to securely validate the + server's TLS certificate. + + ```bash + rsconnect details \ + --api-key my-api-key \ + --server https://connect.example.org \ + --cacert /path/to/certificate.pem + ``` + +* Posit Connect is in "insecure mode". This disables TLS certificate verification, + which results in a less secure connection. + + ```bash + rsconnect add \ + --api-key my-api-key \ + --server https://connect.example.org \ + --insecure + ``` + +Once you work out the combination of options that allow you to successfully work with +an instance of Posit Connect, you'll probably want to use the `add` command to have +`rsconnect` remember those options and allow you to just use a nickname. + +### Updating a Deployment + +If you deploy a file again to the same server, `rsconnect` will update the previous +deployment. This means that you can keep running `rsconnect deploy notebook my-notebook.ipynb` +as you develop new versions of your notebook. The same applies to other Python content +types. + +#### Forcing a New Deployment + +To bypass this behavior and force a new deployment, use the `--new` option: + +```bash +rsconnect deploy dash --new my-app/ +``` + +#### Updating a Different Deployment + +If you want to update an existing deployment but don't have the saved deployment data, +you can provide the app's numeric ID or GUID on the command line: + +```bash +rsconnect deploy notebook --app-id 123456 my-notebook.ipynb +``` + +You must be the owner of the target deployment, or a collaborator with permission to +change the content. The type of content (static notebook, notebook with source code, +API, or application) must match the existing deployment. + +> **Note** +> There is no confirmation required to update a deployment. If you do so +> accidentally, use the "Source Versions" dialog in the Posit Connect dashboard to +> activate the previous version and remove the erroneous one. + +##### Finding the App ID + +The App ID associated with a piece of content you have previously deployed from the +`rsconnect` command line interface can be found easily by querying the deployment +information using the `info` command. For more information, see the +[Showing the Deployment Information](#showing-the-deployment-information) section. + +If the content was deployed elsewhere or `info` does not return the correct App ID, +but you can open the content on Posit Connect, find the content and open it in a +browser. The URL in your browser's location bar will contain `#/apps/NNN` where `NNN` +is your App ID. The GUID identifier for the app may be found on the **Info** tab for +the content in the Posit Connect UI. + +#### Showing the Deployment Information + +You can see the information that the `rsconnect` command has saved for the most recent +deployment with the `info` command: + +```bash +rsconnect info my-notebook.ipynb +``` + +If you have deployed to multiple servers, the most recent deployment information for +each server will be shown. This command also displays the path to the file where the +deployment data is stored. + +## Stored Information Files + +Stored information files are stored in a platform-specific directory: + +| Platform | Location | +| -------- | ------------------------------------------------------------------ | +| Mac | `$HOME/Library/Application Support/rsconnect-python/` | +| Linux | `$HOME/.rsconnect-python/` or `$XDG_CONFIG_HOME/rsconnect-python/` | +| Windows | `$APPDATA/rsconnect-python` | + +Remembered server information is stored in the `servers.json` file in that directory. + +### Deployment Data + +After a deployment is completed, information about the deployment is saved +to enable later redeployment. This data is stored alongside the deployed file, +in an `rsconnect-python` subdirectory, if possible. If that location is not writable +during deployment, then the deployment data will be stored in the global configuration +directory specified above. + +
+Generated from rsconnect-python {{ rsconnect_python.version }} +
+ +### Hide Jupyter Notebook Input Code Cells + +The user can render a Jupyter notebook without its corresponding input code cells by passing the '--hide-all-input' flag through the cli: + +```bash +rsconnect deploy notebook \ + --server https://connect.example.org \ + --api-key my-api-key \ + --hide-all-input \ + my-notebook.ipynb +``` + +To selectively hide input cells in a Jupyter notebook, the user needs to follow a two step process: +1. tag cells with the 'hide_input' tag, +2. then pass the ' --hide-tagged-input' flag through the cli: + +```bash +rsconnect deploy notebook \ + --server https://connect.example.org \ + --api-key my-api-key \ + --hide-tagged-input \ + my-notebook.ipynb +``` + +By default, rsconnect-python does not install Jupyter notebook related depenencies. These dependencies are installed via rsconnect-jupyter. When the user is using the hide input features in rsconnect-python by itself without rsconnect-jupyter, he/she needs to install the following package depenecies: + +``` +notebook +nbformat +nbconvert>=5.6.1 +``` + +## Content subcommands + +rsconnect-python supports multiple options for interacting with Posit Connect's +`/v1/content` API. Both administrators and publishers can use the content subcommands +to search, download, and rebuild content on Posit Connect without needing to access the +dashboard from a browser. + +> **Note** +> The `rsconnect content` CLI subcommands are intended to be easily scriptable. +> The default output format is `JSON` so that the results can be easily piped into +> other command line utilities like [`jq`](https://stedolan.github.io/jq/) for further post-processing. + +```bash +rsconnect content --help +# Usage: rsconnect content [OPTIONS] COMMAND [ARGS]... + +# Interact with Posit Connect's content API. + +# Options: +# --help Show this message and exit. + +# Commands: +# build Build content on Posit Connect. +# describe Describe a content item on Posit Connect. +# download-bundle Download a content item's source bundle. +# search Search for content on Posit Connect. +``` + +### Content Search + +The `rsconnect content search` subcommands can be used by administrators and publishers +to find specific content on a given Posit Connect server. The search returns +metadata for each content item that meets the search criteria. + +```bash +rsconnect content search --help +# Usage: rsconnect content search [OPTIONS] + +# Options: +# -n, --name TEXT The nickname of the Posit Connect server. +# -s, --server TEXT The URL for the Posit Connect server. +# -k, --api-key TEXT The API key to use to authenticate with +# Posit Connect. + +# -i, --insecure Disable TLS certification/host validation. +# -c, --cacert FILENAME The path to trusted TLS CA certificates. +# --published Search only published content. +# --unpublished Search only unpublished content. +# --content-type [unknown|shiny|rmd-static|rmd-shiny|static|api|tensorflow-saved-model|jupyter-static|python-api|python-dash|python-streamlit|python-bokeh|python-fastapi|quarto-shiny|quarto-static] +# Filter content results by content type. +# --r-version VERSIONSEARCHFILTER +# Filter content results by R version. +# --py-version VERSIONSEARCHFILTER +# Filter content results by Python version. +# --title-contains TEXT Filter content results by title. +# --order-by [created|last_deployed] +# Order content results. +# -v, --verbose Print detailed messages. +# --help Show this message and exit. + +rsconnect content search +# [ +# { +# "max_conns_per_process": null, +# "content_category": "", +# "load_factor": null, +# "cluster_name": "Local", +# "description": "", +# "bundle_id": "142", +# "image_name": null, +# "r_version": null, +# "content_url": "https://connect.example.org:3939/content/4ffc819c-065c-420c-88eb-332db1133317/", +# "connection_timeout": null, +# "min_processes": null, +# "last_deployed_time": "2021-12-02T18:09:11Z", +# "name": "logs-api-python", +# "title": "logs-api-python", +# "created_time": "2021-07-19T19:17:32Z", +# "read_timeout": null, +# "guid": "4ffc819c-065c-420c-88eb-332db1133317", +# "parameterized": false, +# "run_as": null, +# "py_version": "3.8.2", +# "idle_timeout": null, +# "app_role": "owner", +# "access_type": "acl", +# "app_mode": "python-api", +# "init_timeout": null, +# "id": "18", +# "quarto_version": null, +# "dashboard_url": "https://connect.example.org:3939/connect/#/apps/4ffc819c-065c-420c-88eb-332db1133317", +# "run_as_current_user": false, +# "owner_guid": "edf26318-0027-4d9d-bbbb-54703ebb1855", +# "max_processes": null +# }, +# ... +# ] +``` + +See [this section](#searching-for-content) for more comprehensive usage examples +of the available search flags. + + +### Content Build + +> **Note** +> The `rsconnect content build` subcommand requires Posit Connect >= 2021.11.1 + +Posit Connect caches R and Python packages in the configured +[`Server.DataDir`](https://docs.posit.co/connect/admin/appendix/configuration/#Server.DataDir). +Under certain circumstances (examples below), these package caches can become stale +and need to be rebuilt. This refresh automatically occurs when a Posit Connect +user visits the content. You may wish to refresh some content before it is visited +because it is high priority or is not visited frequently (API content, emailed reports). +In these cases, it is possible to preemptively build specific content items using +the `rsconnect content build` subcommands. This way the user does not have to pay +the build cost when the content is accessed next. + +The following are some common scenarios where performing a content build might be necessary: + +- OS upgrade +- changes to gcc or libc libraries +- changes to Python or R installations +- switching from source to binary package repositories or vice versa + +> **Note** +> The `content build` command is non-destructive, meaning that it does nothing to purge +> existing packrat/python package caches before a build. If you have an +> existing cache, it should be cleared prior to starting a content build. +> See the [migration documentation](https://docs.posit.co/connect/admin/appendix/cli/#migration) for details. + +> **Note** +> You may use the [`rsconnect content search`](#content-search) subcommand to help +> identify high priority content items to build. + +```bash +rsconnect content build --help +Usage: rsconnect content build [OPTIONS] COMMAND [ARGS]... + + Build content on Posit Connect. Requires Connect >= 2021.11.1 + +Options: + --help Show this message and exit. + +Commands: + add Mark a content item for build. Use `build run` to invoke the build + on the Connect server. + + history Get the build history for a content item. + logs Print the logs for a content build. + ls List the content items that are being tracked for build on a given + Connect server. + + rm Remove a content item from the list of content that are tracked for + build. Use `build ls` to view the tracked content. + + run Start building content on a given Connect server. +``` + +To build a specific content item, first `add` it to the list of content that is +"tracked" for building using its GUID. + +> **Note** +> Metadata for "tracked" content items is stored in a local directory called +> `rsconnect-build` which will be automatically created in your current working directory. +> You may set the environment variable `CONNECT_CONTENT_BUILD_DIR` to override this directory location. + +```bash +rsconnect content build add --guid 4ffc819c-065c-420c-88eb-332db1133317 +``` + +> **Note** +> See [this section](#add-to-build-from-search-results) for +> an example of how to add multiple content items in bulk, from the results +> of a `rsconnect content search` command. + +To view all currently "tracked" content items, use the `rsconnect content build ls` subcommand. + +```bash +rsconnect content build ls +``` + +To view only the "tracked" content items that have not yet been built, use the `--status NEEDS_BUILD` flag. + +```bash +rsconnect content build ls --status NEEDS_BUILD +``` + +Once the content items have been added, you may initiate a build +using the `rsconnect content build run` subcommand. This command will attempt to +build all "tracked" content that has the status `NEEDS_BUILD`. + +```bash +rsconnect content build run +# [INFO] 2021-12-14T13:02:45-0500 Initializing ContentBuildStore for https://connect.example.org:3939 +# [INFO] 2021-12-14T13:02:45-0500 Starting content build (https://connect.example.org:3939)... +# [INFO] 2021-12-14T13:02:45-0500 Starting build: 4ffc819c-065c-420c-88eb-332db1133317 +# [INFO] 2021-12-14T13:02:50-0500 Running = 1, Pending = 0, Success = 0, Error = 0 +# [INFO] 2021-12-14T13:02:50-0500 Build succeeded: 4ffc819c-065c-420c-88eb-332db1133317 +# [INFO] 2021-12-14T13:02:55-0500 Running = 0, Pending = 0, Success = 1, Error = 0 +# [INFO] 2021-12-14T13:02:55-0500 1/1 content builds completed in 0:00:10 +# [INFO] 2021-12-14T13:02:55-0500 Success = 1, Error = 0 +# [INFO] 2021-12-14T13:02:55-0500 Content build complete. +``` + +Sometimes content builds will fail and require debugging by the publisher or administrator. +Use the `rsconnect content build ls` to identify content builds that resulted in errors +and inspect the build logs with the `rsconnect content build logs` subcommand. + +```bash +rsconnect content build ls --status ERROR +# [INFO] 2021-12-14T13:07:32-0500 Initializing ContentBuildStore for https://connect.example.org:3939 +# [ +# { +# "rsconnect_build_status": "ERROR", +# "last_deployed_time": "2021-12-02T18:09:11Z", +# "owner_guid": "edf26318-0027-4d9d-bbbb-54703ebb1855", +# "rsconnect_last_build_log": "/Users/david/code/posit/rsconnect-python/rsconnect-build/logs/connect_example_org_3939/4ffc819c-065c-420c-88eb-332db1133317/pZoqfBoi6BgpKde5.log", +# "guid": "4ffc819c-065c-420c-88eb-332db1133317", +# "rsconnect_build_task_result": { +# "user_id": 1, +# "error": "Cannot find compatible environment: no compatible Local environment with Python version 3.9.5", +# "code": 1, +# "finished": true, +# "result": { +# "data": "An error occurred while building the content", +# "type": "build-failed-error" +# }, +# "id": "pZoqfBoi6BgpKde5" +# }, +# "dashboard_url": "https://connect.example.org:3939/connect/#/apps/4ffc819c-065c-420c-88eb-332db1133317", +# "name": "logs-api-python", +# "title": "logs-api-python", +# "content_url": "https://connect.example.org:3939/content/4ffc819c-065c-420c-88eb-332db1133317/", +# "bundle_id": "141", +# "rsconnect_last_build_time": "2021-12-14T18:07:16Z", +# "created_time": "2021-07-19T19:17:32Z", +# "app_mode": "python-api" +# } +# ] + +rsconnect content build logs --guid 4ffc819c-065c-420c-88eb-332db1133317 +# [INFO] 2021-12-14T13:09:27-0500 Initializing ContentBuildStore for https://connect.example.org:3939 +# Building Python API... +# Cannot find compatible environment: no compatible Local environment with Python version 3.9.5 +# Task failed. Task exited with status 1. +``` + +## Common Usage Examples + +### Searching for content + +The following are some examples of how publishers might use the +`rsconnect content search` subcommand to find content on Posit Connect. +By default, the `rsconnect content search` command will return metadata for ALL +of the content on a Posit Connect server, both published and unpublished content. + +> **Note** +> When using the `--r-version` and `--py-version` flags, users should +> make sure to quote the arguments to avoid conflicting with your shell. For +> example, bash would interpret `--py-version >3.0.0` as a shell redirect because of the +> unquoted `>` character. + +```bash +# return only published content +rsconnect content search --published + +# return only unpublished content +rsconnect content search --unpublished + +# return published content where the python version is at least 3.9.0 +rsconnect content search --published --py-version ">=3.9.0" + +# return published content where the R version is exactly 3.6.3 +rsconnect content search --published --r-version "==3.6.3" + +# return published content where the content type is a static RMD +rsconnect content search --content-type rmd-static + +# return published content where the content type is either shiny OR fast-api +rsconnect content search --content-type shiny --content-type python-fastapi + +# return all content, published or unpublished, where the title contains the +# text "Stock Report" +rsconnect content search --title-contains "Stock Report" + +# return published content, results are ordered by when the content was last +# deployed +rsconnect content search --published --order-by last_deployed + +# return published content, results are ordered by when the content was +# created +rsconnect content search --published --order-by created +``` + +### Finding r and python versions + +One common use for the `search` command might be to find the versions of +r and python that are currently in use on your Posit Connect server before a migration. + +```bash +# search for all published content and print the unique r and python version +# combinations +rsconnect content search --published | jq -c '.[] | {py_version,r_version}' | sort | +uniq +# {"py_version":"3.8.2","r_version":"3.5.3"} +# {"py_version":"3.8.2","r_version":"3.6.3"} +# {"py_version":"3.8.2","r_version":null} +# {"py_version":null,"r_version":"3.5.3"} +# {"py_version":null,"r_version":"3.6.3"} +# {"py_version":null,"r_version":null} +``` + +### Finding recently deployed content + +```bash +# return only the 10 most recently deployed content items +rsconnect content search \ + --order-by last_deployed \ + --published | jq -c 'limit(10; .[]) | { guid, last_deployed_time }' +# {"guid":"4ffc819c-065c-420c-88eb-332db1133317","last_deployed_time":"2021-12-02T18:09:11Z"} +# {"guid":"aa2603f8-1988-484f-a335-193f2c57e6c4","last_deployed_time":"2021-12-01T20:56:07Z"} +# {"guid":"051252f0-4f70-438f-9be1-d818a3b5f8d9","last_deployed_time":"2021-12-01T20:37:01Z"} +# {"guid":"015143da-b75f-407c-81b1-99c4a724341e","last_deployed_time":"2021-11-30T16:56:21Z"} +# {"guid":"bcc74209-3a81-4b9c-acd5-d24a597c256c","last_deployed_time":"2021-11-30T15:51:07Z"} +# {"guid":"f21d7767-c99e-4dd4-9b00-ff8ec9ae2f53","last_deployed_time":"2021-11-23T18:46:28Z"} +# {"guid":"da4f709c-c383-4fbc-89e2-f032b2d7e91d","last_deployed_time":"2021-11-23T18:46:28Z"} +# {"guid":"9180809d-38fd-4730-a0e0-8568c45d87b7","last_deployed_time":"2021-11-23T15:16:19Z"} +# {"guid":"2b1d2ab8-927d-4956-bbf9-29798d039bc5","last_deployed_time":"2021-11-22T18:33:17Z"} +# {"guid":"c96db3f3-87a1-4df5-9f58-eb109c397718","last_deployed_time":"2021-11-19T20:25:33Z"} +``` + +### Add to build from search results + +One common use case might be to `rsconnect content build add` content for build +based on the results of a `rsconnect content search`. For example: + +```bash +# search for all API type content, then +# for each guid, add it to the "tracked" content items +for guid in $(rsconnect content search \ + --published \ + --content-type python-api \ + --content-type api | jq -r '.[].guid'); do + rsconnect content build add --guid $guid +done +``` + +Adding content items one at a time can be a slow operation. This is because +`rsconnect content build add` must fetch metadata for each content item before it +is added to the "tracked" content items. By providing multiple `--guid` arguments +to the `rsconnect content build add` subcommand, we can fetch metadata for multiple content items +in a single api call, which speeds up the operation significantly. + +```bash +# write the guid of every published content item to a file called guids.txt +rsconnect content search --published | jq '.[].guid' > guids.txt + +# bulk-add from the guids.txt with a single `rsconnect content build add` command +xargs printf -- '-g %s\n' < guids.txt | xargs rsconnect content build add +``` +## Programmatic Provisioning + +Posit Connect supports the programmatic bootstrapping of an administrator API key +for scripted provisioning tasks. This process is supported by the `rsconnect bootstrap` command, +which uses a JSON Web Token to request an initial API key from a fresh Connect instance. + +> **Warning** +> This feature **requires Python version 3.6 or higher**. + +```bash +rsconnect bootstrap \ + --server https://connect.example.org:3939 \ + --jwt-keypath /path/to/secret.key +``` + +A full description on how to use `rsconnect bootstrap` in a provisioning workflow is provided in the Connect administrator guide's +[programmatic provisioning](https://docs.posit.co/connect/admin/programmatic-provisioning) documentation. + +## Server Administration Tasks + +Starting with the 2023.05 edition of Posit Connect, `rsconnect-python` can be +used to perform certain server administration tasks, such as instance managing +runtime caches. For more information on runtime caches in Posit Connect, see the +Connect Admin Guide's section on [runtime +caches](https://docs.posit.co/connect/admin/server-management/runtime-caches/). + +Examples in this section will use `--name myserver` to stand in for your Connect +server information. See [Managing Server +Information](#managing-server-information) above for more details. + +### Enumerate Runtime Caches +*New in Connect 2023.05* + +Use the command below to enumerate runtime caches on a Connect server. The +command will output a JSON object containing a list of runtime caches . Each +cache entry will contain the following information: + +- `language`: The language of content that uses the cache, either R or Python. +- `version`: The language version of the content that uses the cache. +- `image_name`: The execution environment of the cache. The string `Local` + denotes native execution. For Connect instances that use off-host execution, + the name of the image that uses the cache will be displayed. + +```bash +rsconnect system caches list --name myserver +# { +# "caches": [ +# { +# "language": "R", +# "version": "3.6.3", +# "image_name": "Local" +# }, +# { +# "language": "Python", +# "version": "3.9.5", +# "image_name": "Local" +# }, +# { +# "language": "R", +# "version": "3.6.3", +# "image_name": "rstudio/content-base:r3.6.3-py3.9.5-bionic" +# }, +# { +# "language": "Python", +# "version": "3.9.5", +# "image_name": "rstudio/content-base:r3.6.3-py3.9.5-bionic" +# } +# ] +# } +``` + +> **Note** +> The `image_name` field returned by the server will use sanitized versions +> of names. + +### Delete Runtime Caches +*New in Connect 2023.05* + +When Connect's execution environment changes, runtime caches may be invalidated. +In these cases, you will need to delete the affected runtime caches using the +`system caches delete` command. + +> **Warning** +> After deleting a cache, the first time affected content is visited, Connect +> will need to reconstruct its environment. This can take a long time. To +> mitigate this, you can use the [`content build`](#content-build) command to +> rebuild affected content ahead of time. You may want to do this just for +> high-priority content, or for all content. + +To delete a runtime cache, call the `system caches delete` command, specifying a +Connect server, as well as the language (`-l, --language`), version (`-V, +--version`), and image name (`-I, --image-name`) for the cache you wish to +delete. Deleting a large cache might take a while. The command will wait for +Connect to finish the task. + +Use the following parameters specify the target cache: + +- `language` (required) must name `R` or `Python`. It is case-insensitive. +- `version` (required) must be a three-part version number, e.g. `3.8.12`. +- `image-name` (optional) defaults to `Local`, which targets caches used for + natively-executed content. Off-host images can be specified using either the + literal image name or the sanitized name returned by the `list` command. + +Use the dry run flag (`-d, --dry-run`) to surface any errors ahead of +deletion. + +```bash +rsconnect system caches delete \ + --name myserver \ + --language Python \ + --version 3.9.5 \ + --image-name rstudio/content-base:r3.6.3-py3.9.5-bionic \ + --dry-run +# Dry run finished + +rsconnect system caches delete \ + --name myserver \ + --language Python \ + --version 3.9.5 \ + --image-name rstudio/content-base:r3.6.3-py3.9.5-bionic +# Deleting runtime cache... +# Successfully deleted runtime cache +``` + +You should run these commands for each cache you wish to delete. diff --git a/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/entry_points.txt b/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/entry_points.txt new file mode 100644 index 00000000..a6874c3b --- /dev/null +++ b/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +rsconnect = rsconnect.main:cli diff --git a/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/top_level.txt b/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/top_level.txt new file mode 100644 index 00000000..e9a412e1 --- /dev/null +++ b/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/top_level.txt @@ -0,0 +1 @@ +rsconnect diff --git a/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/zip-safe b/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/zip-safe new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/zip-safe @@ -0,0 +1 @@ + diff --git a/pip-wheel-metadata/rsconnect_python.egg-info/PKG-INFO b/pip-wheel-metadata/rsconnect_python.egg-info/PKG-INFO new file mode 100644 index 00000000..9e2d7a37 --- /dev/null +++ b/pip-wheel-metadata/rsconnect_python.egg-info/PKG-INFO @@ -0,0 +1,1128 @@ +Metadata-Version: 2.1 +Name: rsconnect-python +Version: 1.20.1.dev23+g917b738.d20231017 +Summary: Python integration with Posit Connect +Home-page: http://github.com/rstudio/rsconnect-python +Author: Michael Marchetti +Author-email: mike@posit.co +License: GPL-2.0 +Project-URL: Documentation, https://docs.posit.co/rsconnect-python +Requires-Python: >=3.7 +Description-Content-Type: text/markdown +License-File: LICENSE.md +Requires-Dist: six>=1.14.0 +Requires-Dist: click>=7.0.0 +Requires-Dist: pip>=10.0.0 +Requires-Dist: semver<3.0.0,>=2.0.0 +Requires-Dist: pyjwt>=2.4.0 + +# The rsconnect-python CLI + +This package provides a CLI (command-line interface) for interacting +with and deploying to Posit Connect. This is also used by the +[`rsconnect-jupyter`](https://github.com/rstudio/rsconnect-jupyter) package to deploy +Jupyter notebooks via the Jupyter web console. Many types of content supported by Posit +Connect may be deployed by this package, including WSGI-style APIs, Dash, Streamlit, and +Bokeh applications. + +Content types not directly supported by the CLI may also be deployed if they include a +prepared `manifest.json` file. See ["Deploying R or Other +Content"](#deploying-r-or-other-content) for details. + + +## Deploying Python Content to Posit Connect + +Posit Connect supports the deployment of Jupyter notebooks, Python APIs (such as +those based on Flask or FastAPI) and apps (such as Dash, Streamlit, and Bokeh apps). +Much like deploying R +content to Posit Connect, there are some caveats to understand when replicating your +environment on the Posit Connect server: + +Posit Connect insists on matching `` versions of Python. For example, +a server with only Python 3.8 installed will fail to match content deployed with +Python 3.7. Your administrator may also enable exact Python version matching which +will be stricter and require matching major, minor, and patch versions. For more +information see the [Posit Connect Admin Guide chapter titled Python Version +Matching](https://docs.posit.co/connect/admin/python/#python-version-matching). + +### Installation + +To install `rsconnect-python` from PYPI, you may use any python package manager such as +pip: + +```bash +pip install rsconnect-python +``` + +You may also build and install a wheel directly from a repository clone: + +```bash +git clone https://github.com/rstudio/rsconnect-python.git +cd rsconnect-python +pip install pipenv +make deps dist +pip install ./dist/rsconnect_python-*.whl +``` + +### Using the rsconnect CLI + +Here's an example command that deploys a Jupyter notebook to Posit Connect. + +```bash +rsconnect deploy notebook \ + --server https://connect.example.org \ + --api-key my-api-key \ + my-notebook.ipynb +``` + +> **Note** +> The examples here use long command line options, but there are short +> options (`-s`, `-k`, etc.) available also. Run `rsconnect deploy notebook --help` +> for details. + +### Setting up `rsconnect` CLI auto-completion + +If you would like to use your shell's tab completion support with the `rsconnect` +command, use the command below for the shell you are using. + +#### `bash` + +If you are using the `bash` shell, use this to enable tab completion. + +```bash +#~/.bashrc +eval "$(_RSCONNECT_COMPLETE=source rsconnect)" +``` + +#### `zsh` + +If you are using the `zsh` shell, use this to enable tab completion. + +```zsh +#~/.zshrc +eval "$(_RSCONNECT_COMPLETE=source_zsh rsconnect)" +``` + +If you get `command not found: compdef`, you need to add the following lines to your +`.zshrc` before the completion setup: + +```zsh +#~/.zshrc +autoload -Uz compinit +compinit +``` + +### Managing Server Information + +The information used by the `rsconnect` command to communicate with a Posit Connect +server can be tedious to repeat on every command. To help, the CLI supports the idea +of saving this information, making it usable by a simple nickname. + +> **Warning** +> One item of information saved is the API key used to authenticate with +> Posit Connect. Although the file where this information is saved is marked as +> accessible by the owner only, it's important to remember that the key is present +> in the file as plain text so care must be taken to prevent any unauthorized access +> to the server information file. + +#### TLS Support and Posit Connect + +Usually, a Posit Connect server will be set up to be accessed in a secure manner, +using the `https` protocol rather than simple `http`. If Posit Connect is set up +with a self-signed certificate, you will need to include the `--insecure` flag on +all commands. If Posit Connect is set up to require a client-side certificate chain, +you will need to include the `--cacert` option that points to your certificate +authority (CA) trusted certificates file. Both of these options can be saved along +with the URL and API Key for a server. + +> **Note** +> When certificate information is saved for the server, the specified file +> is read and its _contents_ are saved under the server's nickname. If the CA file's +> contents are ever changed, you will need to add the server information again. + +See the [Network Options](#network-options) section for more details about these options. + +#### Remembering Server Information + +Use the `add` command to store information about a Posit Connect server: + +```bash +rsconnect add \ + --api-key my-api-key \ + --server https://connect.example.org \ + --name myserver +``` + +> **Note** +> The `rsconnect` CLI will verify that the serve URL and API key +> are valid. If either is found not to be, no information will be saved. + +If any of the access information for the server changes, simply rerun the +`add` command with the new information and it will replace the original +information. + +Once the server's information is saved, you can refer to it by its nickname: + +```bash +rsconnect deploy notebook --name myserver my-notebook.ipynb +``` + +If there is information for only one server saved, this will work too: + +```bash +rsconnect deploy notebook my-notebook.ipynb +``` + +#### Listing Server Information + +You can see the list of saved server information with: + +``` +rsconnect list +``` + +#### Removing Server Information + +You can remove information about a server with: + +``` +rsconnect remove --name myserver +``` + +Removing may be done by its nickname (`--name`) or URL (`--server`). + +### Verifying Server Information + +You can verify that a URL refers to a running instance of Posit Connect by using +the `details` command: + +```bash +rsconnect details --server https://connect.example.org +``` + +In this form, `rsconnect` will only tell you whether the URL given does, in fact, refer +to a running Posit Connect instance. If you include a valid API key: + +```bash +rsconnect details --server https://connect.example.org --api-key my-api-key +``` + +the tool will provide the version of Posit Connect (if the server is configured to +divulge that information) and environmental information including versions of Python +that are installed on the server. + +You can also use nicknames with the `details` command if you want to verify that the +stored information is still valid. + +### Notebook Deployment Options + +There are a variety of options available to you when deploying a Jupyter notebook to +Posit Connect. + +#### Including Extra Files + +You can include extra files in the deployment bundle to make them available when your +notebook is run by the Posit Connect server. Just specify them on the command line +after the notebook file: + +```bash +rsconnect deploy notebook my-notebook.ipynb data.csv +``` + +#### Package Dependencies + +If a `requirements.txt` file exists in the same directory as the notebook file, it will +be included in the bundle. It must specify the package dependencies needed to execute +the notebook. Posit Connect will reconstruct the Python environment using the +specified package list. + +If there is no `requirements.txt` file or the `--force-generate` option is specified, +the package dependencies will be determined from the current Python environment, or +from an alternative Python executable specified via the `--python` option: + +```bash +rsconnect deploy notebook --python /path/to/python my-notebook.ipynb +``` + +You can see the packages list that will be included by running `pip list --format=freeze` yourself, +ensuring that you use the same Python that you use to run your Jupyter Notebook: + +```bash +/path/to/python -m pip list --format=freeze +``` + +#### Static (Snapshot) Deployment + +By default, `rsconnect` deploys the original notebook with all its source code. This +enables the Posit Connect server to re-run the notebook upon request or on a schedule. + +If you just want to publish an HTML snapshot of the notebook, you can use the `--static` +option. This will cause `rsconnect` to execute your notebook locally to produce the HTML +file, then publish the HTML file to the Posit Connect server: + +```bash +rsconnect deploy notebook --static my-notebook.ipynb +``` + +### Creating a Manifest for Future Deployment + +You can create a `manifest.json` file for a Jupyter Notebook, then use that manifest +in a later deployment. Use the `write-manifest` command to do this. + +The `write-manifest` command will also create a `requirements.txt` file, if it does +not already exist or the `--force-generate` option is specified. It will contain the +package dependencies from the current Python environment, or from an alternative +Python executable specified in the `--python` option. + +Here is an example of the `write-manifest` command: + +```bash +rsconnect write-manifest notebook my-notebook.ipynb +``` + +> **Note** +> Manifests for static (pre-rendered) notebooks cannot be created. + +### API/Application Deployment Options + +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) +* `dash`: Python Dash apps +* `streamlit`: Streamlit apps +* `bokeh`: Bokeh server apps + +All options below apply equally to the `api`, `fastapi`, `dash`, `streamlit`, +and `bokeh` sub-commands. + +#### Including Extra Files + +You can include extra files in the deployment bundle to make them available when your +API or application is run by the Posit Connect server. Just specify them on the +command line after the API or application directory: + +```bash +rsconnect deploy api flask-api/ data.csv +``` + +Since deploying an API or application starts at a directory level, there will be times +when some files under that directory subtree should not be included in the deployment +or manifest. Use the `--exclude` option to specify files or directories to exclude. + +```bash +rsconnect deploy dash --exclude dash-app-venv --exclude TODO.txt dash-app/ +``` + +You can exclude a directory by naming it: +```bash +rsconnect deploy dash --exclude dash-app-venv --exclude output/ dash-app/ +``` + +The `--exclude` option may be repeated, and may include a glob pattern. +You should always quote a glob pattern so that it will be passed to `rsconnect` as-is +instead of letting the shell expand it. If a file is specifically listed as an extra +file that also matches an exclusion pattern, the file will still be included in the +deployment (i.e., extra files take precedence). + +```bash +rsconnect deploy dash --exclude dash-app-venv --exclude “*.txt” dash-app/ +``` + +The following shows an example of an extra file taking precedence: + +```bash +rsconnect deploy dash --exclude “*.csv” dash-app/ important_data.csv +``` + +Some directories are excluded by default, to prevent bundling and uploading files that are not needed or might interfere with the deployment process: + +``` +.Rproj.user +.env +.git +.svn +.venv +__pycache__ +env +packrat +renv +rsconnect-python +rsconnect +venv +``` + +Any directory that appears to be a Python virtual environment (by containing +`bin/python`) will also be excluded. + + +#### Package Dependencies + +If a `requirements.txt` file exists in the API/application directory, it will be +included in the bundle. It must specify the package dependencies needed to execute +the API or application. Posit Connect will reconstruct the Python environment using +the specified package list. + +If there is no `requirements.txt` file or the `--force-generate` option is specified, +the package dependencies will be determined from the current Python environment, or +from an alternative Python executable specified via the `--python` option: + +```bash +rsconnect deploy api --python /path/to/python my-api/ +``` + +You can see the packages list that will be included by running `pip list --format=freeze` yourself, +ensuring that you use the same Python that you use to run your API or application: + +```bash +/path/to/python -m pip list --format=freeze +``` + +### Creating a Manifest for Future Deployment + +You can create a `manifest.json` file for an API or application, then use that +manifest in a later deployment. Use the `write-manifest` command to do this. + +The `write-manifest` command will also create a `requirements.txt` file, if it does +not already exist or the `--force-generate` option is specified. It will contain +the package dependencies from the current Python environment, or from an alternative +Python executable specified in the `--python` option. + +Here is an example of the `write-manifest` command: + +```bash +rsconnect write-manifest api my-api/ +``` + +### Deploying R or Other Content + +You can deploy other content that has an existing Posit Connect `manifest.json` +file. For example, if you download and unpack a source bundle from Posit Connect, +you can deploy the resulting directory. The options are similar to notebook or +API/application deployment; see `rsconnect deploy manifest --help` for details. + +Here is an example of the `deploy manifest` command: + +```bash +rsconnect deploy manifest /path/to/manifest.json +``` + +> **Note** +> In this case, the existing content is deployed as-is. Python environment +> inspection and notebook pre-rendering, if needed, are assumed to be done already +> and represented in the manifest. + +The argument to `deploy manifest` may also be a directory so long as that directory +contains a `manifest.json` file. + +If you have R content but don't have a `manifest.json` file, you can use the RStudio +IDE to create the manifest. See the help for the `rsconnect::writeManifest` R function: + +```r +install.packages('rsconnect') +library(rsconnect) +?rsconnect::writeManifest +``` + +### Options for All Types of Deployments + +These options apply to any type of content deployment. + +#### Title + +The title of the deployed content is, by default, derived from the filename. For +example, if you deploy `my-notebook.ipynb`, the title will be `my-notebook`. To change +this, use the `--title` option: + +``` +rsconnect deploy notebook --title "My Notebook" my-notebook.ipynb +``` + +When using `rsconnect deploy api`, `rsconnect deploy fastapi`, `rsconnect deploy dash`, +`rsconnect deploy streamlit`, or `rsconnect deploy bokeh`, the title is derived from the directory +containing the API or application. + +When using `rsconnect deploy manifest`, the title is derived from the primary +filename referenced in the manifest. + +### Environment variables +You can set environment variables during deployment. Their names and values will be +passed to Posit Connect during deployment so you can use them in your code. Note that +if you are using `rsconnect` to deploy to shinyapps.io, environment variable management +is not supported on that platform. + +For example, if `notebook.ipynb` contains +```python +print(os.environ["MYVAR"]) +``` + +You can set the value of `MYVAR` that will be set when your code runs in Posit Connect +using the `-E/--environment` option: +```bash +rsconnect deploy notebook --environment MYVAR='hello world' notebook.ipynb +``` + +To avoid exposing sensitive values on the command line, you can specify +a variable without a value. In this case, it will use the value from the +environment in which rsconnect-python is running: +```bash +export SECRET_KEY=12345 + +rsconnect deploy notebook --environment SECRET_KEY notebook.ipynb +``` + +If you specify environment variables when updating an existing deployment, +new values will be set for the variables you provided. Other variables will +remain unchanged. If you don't specify any variables, all of the existing +variables will remain unchanged. + +Environment variables are set on the content item before the content bundle +is uploaded and deployed. If the deployment fails, the new environment variables +will still take effect. + +### Network Options + +When specifying information that `rsconnect` needs to be able to interact with Posit +Connect, you can tailor how transport layer security is performed. + +#### TLS/SSL Certificates + +Posit Connect servers can be configured to use TLS/SSL. If your server's certificate +is trusted by your Jupyter Notebook server, API client or user's browser, then you +don't need to do anything special. You can test this out with the `details` command: + +```bash +rsconnect details \ + --api-key my-api-key \ + --server https://connect.example.org:3939 +``` + +If this fails with a TLS Certificate Validation error, then you have two options. + +* Provide the Root CA certificate that is at the root of the signing chain for your + Posit Connect server. This will enable `rsconnect` to securely validate the + server's TLS certificate. + + ```bash + rsconnect details \ + --api-key my-api-key \ + --server https://connect.example.org \ + --cacert /path/to/certificate.pem + ``` + +* Posit Connect is in "insecure mode". This disables TLS certificate verification, + which results in a less secure connection. + + ```bash + rsconnect add \ + --api-key my-api-key \ + --server https://connect.example.org \ + --insecure + ``` + +Once you work out the combination of options that allow you to successfully work with +an instance of Posit Connect, you'll probably want to use the `add` command to have +`rsconnect` remember those options and allow you to just use a nickname. + +### Updating a Deployment + +If you deploy a file again to the same server, `rsconnect` will update the previous +deployment. This means that you can keep running `rsconnect deploy notebook my-notebook.ipynb` +as you develop new versions of your notebook. The same applies to other Python content +types. + +#### Forcing a New Deployment + +To bypass this behavior and force a new deployment, use the `--new` option: + +```bash +rsconnect deploy dash --new my-app/ +``` + +#### Updating a Different Deployment + +If you want to update an existing deployment but don't have the saved deployment data, +you can provide the app's numeric ID or GUID on the command line: + +```bash +rsconnect deploy notebook --app-id 123456 my-notebook.ipynb +``` + +You must be the owner of the target deployment, or a collaborator with permission to +change the content. The type of content (static notebook, notebook with source code, +API, or application) must match the existing deployment. + +> **Note** +> There is no confirmation required to update a deployment. If you do so +> accidentally, use the "Source Versions" dialog in the Posit Connect dashboard to +> activate the previous version and remove the erroneous one. + +##### Finding the App ID + +The App ID associated with a piece of content you have previously deployed from the +`rsconnect` command line interface can be found easily by querying the deployment +information using the `info` command. For more information, see the +[Showing the Deployment Information](#showing-the-deployment-information) section. + +If the content was deployed elsewhere or `info` does not return the correct App ID, +but you can open the content on Posit Connect, find the content and open it in a +browser. The URL in your browser's location bar will contain `#/apps/NNN` where `NNN` +is your App ID. The GUID identifier for the app may be found on the **Info** tab for +the content in the Posit Connect UI. + +#### Showing the Deployment Information + +You can see the information that the `rsconnect` command has saved for the most recent +deployment with the `info` command: + +```bash +rsconnect info my-notebook.ipynb +``` + +If you have deployed to multiple servers, the most recent deployment information for +each server will be shown. This command also displays the path to the file where the +deployment data is stored. + +## Stored Information Files + +Stored information files are stored in a platform-specific directory: + +| Platform | Location | +| -------- | ------------------------------------------------------------------ | +| Mac | `$HOME/Library/Application Support/rsconnect-python/` | +| Linux | `$HOME/.rsconnect-python/` or `$XDG_CONFIG_HOME/rsconnect-python/` | +| Windows | `$APPDATA/rsconnect-python` | + +Remembered server information is stored in the `servers.json` file in that directory. + +### Deployment Data + +After a deployment is completed, information about the deployment is saved +to enable later redeployment. This data is stored alongside the deployed file, +in an `rsconnect-python` subdirectory, if possible. If that location is not writable +during deployment, then the deployment data will be stored in the global configuration +directory specified above. + +
+Generated from rsconnect-python {{ rsconnect_python.version }} +
+ +### Hide Jupyter Notebook Input Code Cells + +The user can render a Jupyter notebook without its corresponding input code cells by passing the '--hide-all-input' flag through the cli: + +```bash +rsconnect deploy notebook \ + --server https://connect.example.org \ + --api-key my-api-key \ + --hide-all-input \ + my-notebook.ipynb +``` + +To selectively hide input cells in a Jupyter notebook, the user needs to follow a two step process: +1. tag cells with the 'hide_input' tag, +2. then pass the ' --hide-tagged-input' flag through the cli: + +```bash +rsconnect deploy notebook \ + --server https://connect.example.org \ + --api-key my-api-key \ + --hide-tagged-input \ + my-notebook.ipynb +``` + +By default, rsconnect-python does not install Jupyter notebook related depenencies. These dependencies are installed via rsconnect-jupyter. When the user is using the hide input features in rsconnect-python by itself without rsconnect-jupyter, he/she needs to install the following package depenecies: + +``` +notebook +nbformat +nbconvert>=5.6.1 +``` + +## Content subcommands + +rsconnect-python supports multiple options for interacting with Posit Connect's +`/v1/content` API. Both administrators and publishers can use the content subcommands +to search, download, and rebuild content on Posit Connect without needing to access the +dashboard from a browser. + +> **Note** +> The `rsconnect content` CLI subcommands are intended to be easily scriptable. +> The default output format is `JSON` so that the results can be easily piped into +> other command line utilities like [`jq`](https://stedolan.github.io/jq/) for further post-processing. + +```bash +rsconnect content --help +# Usage: rsconnect content [OPTIONS] COMMAND [ARGS]... + +# Interact with Posit Connect's content API. + +# Options: +# --help Show this message and exit. + +# Commands: +# build Build content on Posit Connect. +# describe Describe a content item on Posit Connect. +# download-bundle Download a content item's source bundle. +# search Search for content on Posit Connect. +``` + +### Content Search + +The `rsconnect content search` subcommands can be used by administrators and publishers +to find specific content on a given Posit Connect server. The search returns +metadata for each content item that meets the search criteria. + +```bash +rsconnect content search --help +# Usage: rsconnect content search [OPTIONS] + +# Options: +# -n, --name TEXT The nickname of the Posit Connect server. +# -s, --server TEXT The URL for the Posit Connect server. +# -k, --api-key TEXT The API key to use to authenticate with +# Posit Connect. + +# -i, --insecure Disable TLS certification/host validation. +# -c, --cacert FILENAME The path to trusted TLS CA certificates. +# --published Search only published content. +# --unpublished Search only unpublished content. +# --content-type [unknown|shiny|rmd-static|rmd-shiny|static|api|tensorflow-saved-model|jupyter-static|python-api|python-dash|python-streamlit|python-bokeh|python-fastapi|quarto-shiny|quarto-static] +# Filter content results by content type. +# --r-version VERSIONSEARCHFILTER +# Filter content results by R version. +# --py-version VERSIONSEARCHFILTER +# Filter content results by Python version. +# --title-contains TEXT Filter content results by title. +# --order-by [created|last_deployed] +# Order content results. +# -v, --verbose Print detailed messages. +# --help Show this message and exit. + +rsconnect content search +# [ +# { +# "max_conns_per_process": null, +# "content_category": "", +# "load_factor": null, +# "cluster_name": "Local", +# "description": "", +# "bundle_id": "142", +# "image_name": null, +# "r_version": null, +# "content_url": "https://connect.example.org:3939/content/4ffc819c-065c-420c-88eb-332db1133317/", +# "connection_timeout": null, +# "min_processes": null, +# "last_deployed_time": "2021-12-02T18:09:11Z", +# "name": "logs-api-python", +# "title": "logs-api-python", +# "created_time": "2021-07-19T19:17:32Z", +# "read_timeout": null, +# "guid": "4ffc819c-065c-420c-88eb-332db1133317", +# "parameterized": false, +# "run_as": null, +# "py_version": "3.8.2", +# "idle_timeout": null, +# "app_role": "owner", +# "access_type": "acl", +# "app_mode": "python-api", +# "init_timeout": null, +# "id": "18", +# "quarto_version": null, +# "dashboard_url": "https://connect.example.org:3939/connect/#/apps/4ffc819c-065c-420c-88eb-332db1133317", +# "run_as_current_user": false, +# "owner_guid": "edf26318-0027-4d9d-bbbb-54703ebb1855", +# "max_processes": null +# }, +# ... +# ] +``` + +See [this section](#searching-for-content) for more comprehensive usage examples +of the available search flags. + + +### Content Build + +> **Note** +> The `rsconnect content build` subcommand requires Posit Connect >= 2021.11.1 + +Posit Connect caches R and Python packages in the configured +[`Server.DataDir`](https://docs.posit.co/connect/admin/appendix/configuration/#Server.DataDir). +Under certain circumstances (examples below), these package caches can become stale +and need to be rebuilt. This refresh automatically occurs when a Posit Connect +user visits the content. You may wish to refresh some content before it is visited +because it is high priority or is not visited frequently (API content, emailed reports). +In these cases, it is possible to preemptively build specific content items using +the `rsconnect content build` subcommands. This way the user does not have to pay +the build cost when the content is accessed next. + +The following are some common scenarios where performing a content build might be necessary: + +- OS upgrade +- changes to gcc or libc libraries +- changes to Python or R installations +- switching from source to binary package repositories or vice versa + +> **Note** +> The `content build` command is non-destructive, meaning that it does nothing to purge +> existing packrat/python package caches before a build. If you have an +> existing cache, it should be cleared prior to starting a content build. +> See the [migration documentation](https://docs.posit.co/connect/admin/appendix/cli/#migration) for details. + +> **Note** +> You may use the [`rsconnect content search`](#content-search) subcommand to help +> identify high priority content items to build. + +```bash +rsconnect content build --help +Usage: rsconnect content build [OPTIONS] COMMAND [ARGS]... + + Build content on Posit Connect. Requires Connect >= 2021.11.1 + +Options: + --help Show this message and exit. + +Commands: + add Mark a content item for build. Use `build run` to invoke the build + on the Connect server. + + history Get the build history for a content item. + logs Print the logs for a content build. + ls List the content items that are being tracked for build on a given + Connect server. + + rm Remove a content item from the list of content that are tracked for + build. Use `build ls` to view the tracked content. + + run Start building content on a given Connect server. +``` + +To build a specific content item, first `add` it to the list of content that is +"tracked" for building using its GUID. + +> **Note** +> Metadata for "tracked" content items is stored in a local directory called +> `rsconnect-build` which will be automatically created in your current working directory. +> You may set the environment variable `CONNECT_CONTENT_BUILD_DIR` to override this directory location. + +```bash +rsconnect content build add --guid 4ffc819c-065c-420c-88eb-332db1133317 +``` + +> **Note** +> See [this section](#add-to-build-from-search-results) for +> an example of how to add multiple content items in bulk, from the results +> of a `rsconnect content search` command. + +To view all currently "tracked" content items, use the `rsconnect content build ls` subcommand. + +```bash +rsconnect content build ls +``` + +To view only the "tracked" content items that have not yet been built, use the `--status NEEDS_BUILD` flag. + +```bash +rsconnect content build ls --status NEEDS_BUILD +``` + +Once the content items have been added, you may initiate a build +using the `rsconnect content build run` subcommand. This command will attempt to +build all "tracked" content that has the status `NEEDS_BUILD`. + +```bash +rsconnect content build run +# [INFO] 2021-12-14T13:02:45-0500 Initializing ContentBuildStore for https://connect.example.org:3939 +# [INFO] 2021-12-14T13:02:45-0500 Starting content build (https://connect.example.org:3939)... +# [INFO] 2021-12-14T13:02:45-0500 Starting build: 4ffc819c-065c-420c-88eb-332db1133317 +# [INFO] 2021-12-14T13:02:50-0500 Running = 1, Pending = 0, Success = 0, Error = 0 +# [INFO] 2021-12-14T13:02:50-0500 Build succeeded: 4ffc819c-065c-420c-88eb-332db1133317 +# [INFO] 2021-12-14T13:02:55-0500 Running = 0, Pending = 0, Success = 1, Error = 0 +# [INFO] 2021-12-14T13:02:55-0500 1/1 content builds completed in 0:00:10 +# [INFO] 2021-12-14T13:02:55-0500 Success = 1, Error = 0 +# [INFO] 2021-12-14T13:02:55-0500 Content build complete. +``` + +Sometimes content builds will fail and require debugging by the publisher or administrator. +Use the `rsconnect content build ls` to identify content builds that resulted in errors +and inspect the build logs with the `rsconnect content build logs` subcommand. + +```bash +rsconnect content build ls --status ERROR +# [INFO] 2021-12-14T13:07:32-0500 Initializing ContentBuildStore for https://connect.example.org:3939 +# [ +# { +# "rsconnect_build_status": "ERROR", +# "last_deployed_time": "2021-12-02T18:09:11Z", +# "owner_guid": "edf26318-0027-4d9d-bbbb-54703ebb1855", +# "rsconnect_last_build_log": "/Users/david/code/posit/rsconnect-python/rsconnect-build/logs/connect_example_org_3939/4ffc819c-065c-420c-88eb-332db1133317/pZoqfBoi6BgpKde5.log", +# "guid": "4ffc819c-065c-420c-88eb-332db1133317", +# "rsconnect_build_task_result": { +# "user_id": 1, +# "error": "Cannot find compatible environment: no compatible Local environment with Python version 3.9.5", +# "code": 1, +# "finished": true, +# "result": { +# "data": "An error occurred while building the content", +# "type": "build-failed-error" +# }, +# "id": "pZoqfBoi6BgpKde5" +# }, +# "dashboard_url": "https://connect.example.org:3939/connect/#/apps/4ffc819c-065c-420c-88eb-332db1133317", +# "name": "logs-api-python", +# "title": "logs-api-python", +# "content_url": "https://connect.example.org:3939/content/4ffc819c-065c-420c-88eb-332db1133317/", +# "bundle_id": "141", +# "rsconnect_last_build_time": "2021-12-14T18:07:16Z", +# "created_time": "2021-07-19T19:17:32Z", +# "app_mode": "python-api" +# } +# ] + +rsconnect content build logs --guid 4ffc819c-065c-420c-88eb-332db1133317 +# [INFO] 2021-12-14T13:09:27-0500 Initializing ContentBuildStore for https://connect.example.org:3939 +# Building Python API... +# Cannot find compatible environment: no compatible Local environment with Python version 3.9.5 +# Task failed. Task exited with status 1. +``` + +## Common Usage Examples + +### Searching for content + +The following are some examples of how publishers might use the +`rsconnect content search` subcommand to find content on Posit Connect. +By default, the `rsconnect content search` command will return metadata for ALL +of the content on a Posit Connect server, both published and unpublished content. + +> **Note** +> When using the `--r-version` and `--py-version` flags, users should +> make sure to quote the arguments to avoid conflicting with your shell. For +> example, bash would interpret `--py-version >3.0.0` as a shell redirect because of the +> unquoted `>` character. + +```bash +# return only published content +rsconnect content search --published + +# return only unpublished content +rsconnect content search --unpublished + +# return published content where the python version is at least 3.9.0 +rsconnect content search --published --py-version ">=3.9.0" + +# return published content where the R version is exactly 3.6.3 +rsconnect content search --published --r-version "==3.6.3" + +# return published content where the content type is a static RMD +rsconnect content search --content-type rmd-static + +# return published content where the content type is either shiny OR fast-api +rsconnect content search --content-type shiny --content-type python-fastapi + +# return all content, published or unpublished, where the title contains the +# text "Stock Report" +rsconnect content search --title-contains "Stock Report" + +# return published content, results are ordered by when the content was last +# deployed +rsconnect content search --published --order-by last_deployed + +# return published content, results are ordered by when the content was +# created +rsconnect content search --published --order-by created +``` + +### Finding r and python versions + +One common use for the `search` command might be to find the versions of +r and python that are currently in use on your Posit Connect server before a migration. + +```bash +# search for all published content and print the unique r and python version +# combinations +rsconnect content search --published | jq -c '.[] | {py_version,r_version}' | sort | +uniq +# {"py_version":"3.8.2","r_version":"3.5.3"} +# {"py_version":"3.8.2","r_version":"3.6.3"} +# {"py_version":"3.8.2","r_version":null} +# {"py_version":null,"r_version":"3.5.3"} +# {"py_version":null,"r_version":"3.6.3"} +# {"py_version":null,"r_version":null} +``` + +### Finding recently deployed content + +```bash +# return only the 10 most recently deployed content items +rsconnect content search \ + --order-by last_deployed \ + --published | jq -c 'limit(10; .[]) | { guid, last_deployed_time }' +# {"guid":"4ffc819c-065c-420c-88eb-332db1133317","last_deployed_time":"2021-12-02T18:09:11Z"} +# {"guid":"aa2603f8-1988-484f-a335-193f2c57e6c4","last_deployed_time":"2021-12-01T20:56:07Z"} +# {"guid":"051252f0-4f70-438f-9be1-d818a3b5f8d9","last_deployed_time":"2021-12-01T20:37:01Z"} +# {"guid":"015143da-b75f-407c-81b1-99c4a724341e","last_deployed_time":"2021-11-30T16:56:21Z"} +# {"guid":"bcc74209-3a81-4b9c-acd5-d24a597c256c","last_deployed_time":"2021-11-30T15:51:07Z"} +# {"guid":"f21d7767-c99e-4dd4-9b00-ff8ec9ae2f53","last_deployed_time":"2021-11-23T18:46:28Z"} +# {"guid":"da4f709c-c383-4fbc-89e2-f032b2d7e91d","last_deployed_time":"2021-11-23T18:46:28Z"} +# {"guid":"9180809d-38fd-4730-a0e0-8568c45d87b7","last_deployed_time":"2021-11-23T15:16:19Z"} +# {"guid":"2b1d2ab8-927d-4956-bbf9-29798d039bc5","last_deployed_time":"2021-11-22T18:33:17Z"} +# {"guid":"c96db3f3-87a1-4df5-9f58-eb109c397718","last_deployed_time":"2021-11-19T20:25:33Z"} +``` + +### Add to build from search results + +One common use case might be to `rsconnect content build add` content for build +based on the results of a `rsconnect content search`. For example: + +```bash +# search for all API type content, then +# for each guid, add it to the "tracked" content items +for guid in $(rsconnect content search \ + --published \ + --content-type python-api \ + --content-type api | jq -r '.[].guid'); do + rsconnect content build add --guid $guid +done +``` + +Adding content items one at a time can be a slow operation. This is because +`rsconnect content build add` must fetch metadata for each content item before it +is added to the "tracked" content items. By providing multiple `--guid` arguments +to the `rsconnect content build add` subcommand, we can fetch metadata for multiple content items +in a single api call, which speeds up the operation significantly. + +```bash +# write the guid of every published content item to a file called guids.txt +rsconnect content search --published | jq '.[].guid' > guids.txt + +# bulk-add from the guids.txt with a single `rsconnect content build add` command +xargs printf -- '-g %s\n' < guids.txt | xargs rsconnect content build add +``` +## Programmatic Provisioning + +Posit Connect supports the programmatic bootstrapping of an administrator API key +for scripted provisioning tasks. This process is supported by the `rsconnect bootstrap` command, +which uses a JSON Web Token to request an initial API key from a fresh Connect instance. + +> **Warning** +> This feature **requires Python version 3.6 or higher**. + +```bash +rsconnect bootstrap \ + --server https://connect.example.org:3939 \ + --jwt-keypath /path/to/secret.key +``` + +A full description on how to use `rsconnect bootstrap` in a provisioning workflow is provided in the Connect administrator guide's +[programmatic provisioning](https://docs.posit.co/connect/admin/programmatic-provisioning) documentation. + +## Server Administration Tasks + +Starting with the 2023.05 edition of Posit Connect, `rsconnect-python` can be +used to perform certain server administration tasks, such as instance managing +runtime caches. For more information on runtime caches in Posit Connect, see the +Connect Admin Guide's section on [runtime +caches](https://docs.posit.co/connect/admin/server-management/runtime-caches/). + +Examples in this section will use `--name myserver` to stand in for your Connect +server information. See [Managing Server +Information](#managing-server-information) above for more details. + +### Enumerate Runtime Caches +*New in Connect 2023.05* + +Use the command below to enumerate runtime caches on a Connect server. The +command will output a JSON object containing a list of runtime caches . Each +cache entry will contain the following information: + +- `language`: The language of content that uses the cache, either R or Python. +- `version`: The language version of the content that uses the cache. +- `image_name`: The execution environment of the cache. The string `Local` + denotes native execution. For Connect instances that use off-host execution, + the name of the image that uses the cache will be displayed. + +```bash +rsconnect system caches list --name myserver +# { +# "caches": [ +# { +# "language": "R", +# "version": "3.6.3", +# "image_name": "Local" +# }, +# { +# "language": "Python", +# "version": "3.9.5", +# "image_name": "Local" +# }, +# { +# "language": "R", +# "version": "3.6.3", +# "image_name": "rstudio/content-base:r3.6.3-py3.9.5-bionic" +# }, +# { +# "language": "Python", +# "version": "3.9.5", +# "image_name": "rstudio/content-base:r3.6.3-py3.9.5-bionic" +# } +# ] +# } +``` + +> **Note** +> The `image_name` field returned by the server will use sanitized versions +> of names. + +### Delete Runtime Caches +*New in Connect 2023.05* + +When Connect's execution environment changes, runtime caches may be invalidated. +In these cases, you will need to delete the affected runtime caches using the +`system caches delete` command. + +> **Warning** +> After deleting a cache, the first time affected content is visited, Connect +> will need to reconstruct its environment. This can take a long time. To +> mitigate this, you can use the [`content build`](#content-build) command to +> rebuild affected content ahead of time. You may want to do this just for +> high-priority content, or for all content. + +To delete a runtime cache, call the `system caches delete` command, specifying a +Connect server, as well as the language (`-l, --language`), version (`-V, +--version`), and image name (`-I, --image-name`) for the cache you wish to +delete. Deleting a large cache might take a while. The command will wait for +Connect to finish the task. + +Use the following parameters specify the target cache: + +- `language` (required) must name `R` or `Python`. It is case-insensitive. +- `version` (required) must be a three-part version number, e.g. `3.8.12`. +- `image-name` (optional) defaults to `Local`, which targets caches used for + natively-executed content. Off-host images can be specified using either the + literal image name or the sanitized name returned by the `list` command. + +Use the dry run flag (`-d, --dry-run`) to surface any errors ahead of +deletion. + +```bash +rsconnect system caches delete \ + --name myserver \ + --language Python \ + --version 3.9.5 \ + --image-name rstudio/content-base:r3.6.3-py3.9.5-bionic \ + --dry-run +# Dry run finished + +rsconnect system caches delete \ + --name myserver \ + --language Python \ + --version 3.9.5 \ + --image-name rstudio/content-base:r3.6.3-py3.9.5-bionic +# Deleting runtime cache... +# Successfully deleted runtime cache +``` + +You should run these commands for each cache you wish to delete. diff --git a/pip-wheel-metadata/rsconnect_python.egg-info/SOURCES.txt b/pip-wheel-metadata/rsconnect_python.egg-info/SOURCES.txt new file mode 100644 index 00000000..a07ba8be --- /dev/null +++ b/pip-wheel-metadata/rsconnect_python.egg-info/SOURCES.txt @@ -0,0 +1,234 @@ +.flake8 +.gitignore +CHANGELOG.md +CONTRIBUTING.md +Dockerfile +LICENSE.md +Makefile +README.md +conftest.py +docker-compose.yml +mypy.ini +pyproject.toml +requirements.txt +setup.cfg +setup.py +.github/pull_request_template.md +.github/workflows/main.yml +.github/workflows/snyk.yml +/Users/billsager/dev/rsconnect-python/pip-wheel-metadata/rsconnect_python.egg-info/PKG-INFO +/Users/billsager/dev/rsconnect-python/pip-wheel-metadata/rsconnect_python.egg-info/SOURCES.txt +/Users/billsager/dev/rsconnect-python/pip-wheel-metadata/rsconnect_python.egg-info/dependency_links.txt +/Users/billsager/dev/rsconnect-python/pip-wheel-metadata/rsconnect_python.egg-info/entry_points.txt +/Users/billsager/dev/rsconnect-python/pip-wheel-metadata/rsconnect_python.egg-info/requires.txt +/Users/billsager/dev/rsconnect-python/pip-wheel-metadata/rsconnect_python.egg-info/top_level.txt +/Users/billsager/dev/rsconnect-python/pip-wheel-metadata/rsconnect_python.egg-info/zip-safe +docs/.gitignore +docs/Dockerfile +docs/Makefile +docs/README.md +docs/mkdocs.yml +docs/patch_admonitions.py +docs/requirements.txt +docs/docs/css/custom.css +docs/docs/images/favicon.ico +docs/docs/images/iconPositConnect.svg +docs/docs/images/positLogoBlack.svg +docs/docs/images/positLogoWhite.svg +docs/docs/images/rstudio-logo.png +docs/overrides/partials/footer.html +docs/overrides/partials/header.html +docs/overrides/partials/integrations/analytics.html +integration/.gitignore +integration/__init__.py +integration/jwt_testbed.py +integration-testing/.gitignore +integration-testing/cypress.config.js +integration-testing/docker-compose.yml +integration-testing/justfile +integration-testing/content/notebook/README.md +integration-testing/content/notebook/quandl-wiki-tsla.json.gz +integration-testing/content/notebook/requirements.txt +integration-testing/content/notebook/stock-report-jupyter.ipynb +integration-testing/content/notebook/thumbnail.jpg +integration-testing/content/voila/index.ipynb +integration-testing/content/voila/requirements.txt +integration-testing/cypress/e2e/jupyter.cy.js +integration-testing/cypress/e2e/voila.cy.js +integration-testing/cypress/plugins/index.js +integration-testing/cypress/support/commands.js +integration-testing/cypress/support/e2e.js +integration-testing/docker/client.Dockerfile +integration-testing/docker/cypress.Dockerfile +integration-testing/docker/requirements.txt +integration-testing/docker/rstudio-connect.gcfg +mock_connect/.gitignore +mock_connect/Dockerfile +mock_connect/Makefile +mock_connect/README.md +mock_connect/data.json +mock_connect/mock_connect/__init__.py +mock_connect/mock_connect/data.py +mock_connect/mock_connect/http_helpers.py +mock_connect/mock_connect/main.py +my-shiny-app/app.py +my-shiny-app/manifest.json +my-shiny-app/requirements.txt +rsconnect/__init__.py +rsconnect/actions.py +rsconnect/actions_content.py +rsconnect/api.py +rsconnect/bundle.py +rsconnect/certificates.py +rsconnect/environment.py +rsconnect/exception.py +rsconnect/http_support.py +rsconnect/json_web_token.py +rsconnect/log.py +rsconnect/main.py +rsconnect/metadata.py +rsconnect/models.py +rsconnect/timeouts.py +rsconnect/validation.py +rsconnect/version.py +scripts/build-image +scripts/runtests +tests/__init__.py +tests/test_Manifest.py +tests/test_actions.py +tests/test_api.py +tests/test_bundle.py +tests/test_certificates.py +tests/test_environment.py +tests/test_http_support.py +tests/test_json_web_token.py +tests/test_main.py +tests/test_main_content.py +tests/test_main_system_caches.py +tests/test_metadata.py +tests/test_models.py +tests/test_timeouts.py +tests/test_utils.py +tests/test_vetiver_pins.py +tests/utils.py +tests/rsconnect-build-test/connect_remote_6443.json +tests/testdata/bundle.tar.gz +tests/testdata/fake_broken_conda.sh +tests/testdata/Manifest/html_manifest.json +tests/testdata/Manifest_data/empty_manifest.json +tests/testdata/Manifest_data/missing_file_manifest.json +tests/testdata/R/shinyapp/app.R +tests/testdata/R/shinyapp/index.htm +tests/testdata/R/shinyapp/manifest.json +tests/testdata/R/shinyapp/packrat/packrat.lock +tests/testdata/R/shinyapp/packrat/desc/BH +tests/testdata/R/shinyapp/packrat/desc/R6 +tests/testdata/R/shinyapp/packrat/desc/Rcpp +tests/testdata/R/shinyapp/packrat/desc/crayon +tests/testdata/R/shinyapp/packrat/desc/digest +tests/testdata/R/shinyapp/packrat/desc/htmltools +tests/testdata/R/shinyapp/packrat/desc/httpuv +tests/testdata/R/shinyapp/packrat/desc/jsonlite +tests/testdata/R/shinyapp/packrat/desc/later +tests/testdata/R/shinyapp/packrat/desc/magrittr +tests/testdata/R/shinyapp/packrat/desc/mime +tests/testdata/R/shinyapp/packrat/desc/packrat +tests/testdata/R/shinyapp/packrat/desc/promises +tests/testdata/R/shinyapp/packrat/desc/rlang +tests/testdata/R/shinyapp/packrat/desc/shiny +tests/testdata/R/shinyapp/packrat/desc/sourcetools +tests/testdata/R/shinyapp/packrat/desc/xtable +tests/testdata/api/flask/app.py +tests/testdata/api/flask/requirements.txt +tests/testdata/certificates/localhost.ca-bundle +tests/testdata/certificates/localhost.cer +tests/testdata/certificates/localhost.crt +tests/testdata/certificates/localhost.csr +tests/testdata/certificates/localhost.der +tests/testdata/certificates/localhost.key +tests/testdata/certificates/localhost.pem +tests/testdata/html_tests/multi_file_index/index.html +tests/testdata/html_tests/multi_file_index/main.html +tests/testdata/html_tests/multi_file_nonindex/a.html +tests/testdata/html_tests/multi_file_nonindex/b.html +tests/testdata/html_tests/single_file_index/index.html +tests/testdata/html_tests/single_file_index/test1.txt +tests/testdata/html_tests/single_file_index/test_folder1/testfoldertext1.txt +tests/testdata/html_tests/single_file_nonindex/main.html +tests/testdata/html_tests/single_file_nonindex/test1.txt +tests/testdata/html_tests/single_file_nonindex/test_folder1/testfoldertext1.txt +tests/testdata/initial-admin-responses/forbidden_error.json +tests/testdata/initial-admin-responses/not_found_error.json +tests/testdata/initial-admin-responses/other_error.json +tests/testdata/initial-admin-responses/success.json +tests/testdata/initial-admin-responses/unauthorized_error.json +tests/testdata/jwt/empty_secret.key +tests/testdata/jwt/invalid_secret.key +tests/testdata/jwt/secret.key +tests/testdata/py3/conda1/.keep +tests/testdata/py3/data_file/data.csv +tests/testdata/py3/data_file/data_file.ipynb +tests/testdata/py3/minimal/minimal.ipynb +tests/testdata/py3/numpy/numpy.ipynb +tests/testdata/py3/numpy_requirements/numpy.ipynb +tests/testdata/py3/numpy_requirements/requirements.txt +tests/testdata/py3/pip1/dummy.ipynb +tests/testdata/py3/pip1/requirements.txt +tests/testdata/py3/pip2/data.csv +tests/testdata/py3/pip2/dummy.ipynb +tests/testdata/py3/static/index.html +tests/testdata/py3/static/manifest.json +tests/testdata/pyshiny_with_manifest/README.md +tests/testdata/pyshiny_with_manifest/app5.py +tests/testdata/pyshiny_with_manifest/data.csv +tests/testdata/pyshiny_with_manifest/manifest.json +tests/testdata/pyshiny_with_manifest/requirements.txt +tests/testdata/rstudio-responses/application.json +tests/testdata/rstudio-responses/create-bundle.json +tests/testdata/rstudio-responses/create-output.json +tests/testdata/rstudio-responses/get-accounts.json +tests/testdata/rstudio-responses/get-applications.json +tests/testdata/rstudio-responses/get-bundle.json +tests/testdata/rstudio-responses/get-content.json +tests/testdata/rstudio-responses/get-output-application.json +tests/testdata/rstudio-responses/get-project-application.json +tests/testdata/rstudio-responses/get-task.json +tests/testdata/rstudio-responses/get-user.json +tests/testdata/rstudio-responses/post-deploy.json +tests/testdata/stock-api-fastapi/README.md +tests/testdata/stock-api-fastapi/main.py +tests/testdata/stock-api-fastapi/prices.csv +tests/testdata/stock-api-fastapi/requirements.txt +tests/testdata/stock-api-flask/README.md +tests/testdata/stock-api-flask/app.py +tests/testdata/stock-api-flask/prices.csv +tests/testdata/stock-api-flask/requirements.txt +tests/testdata/stock-dashboard-python/README.md +tests/testdata/stock-dashboard-python/app2.py +tests/testdata/stock-dashboard-python/prices.csv +tests/testdata/stock-dashboard-python/requirements.txt +tests/testdata/top-5-income-share-bokeh/README.md +tests/testdata/top-5-income-share-bokeh/app3.py +tests/testdata/top-5-income-share-bokeh/data.csv +tests/testdata/top-5-income-share-bokeh/requirements.txt +tests/testdata/top-5-income-share-shiny/README.md +tests/testdata/top-5-income-share-shiny/app4.py +tests/testdata/top-5-income-share-shiny/data.csv +tests/testdata/top-5-income-share-shiny/requirements.txt +tests/testdata/top-5-income-share-streamlit/README.md +tests/testdata/top-5-income-share-streamlit/app1.py +tests/testdata/top-5-income-share-streamlit/data.csv +tests/testdata/top-5-income-share-streamlit/requirements.txt +tests/testdata/voila/bqplot/bqplot.ipynb +tests/testdata/voila/bqplot/requirements.txt +tests/testdata/voila/dashboard/bqplot.ipynb +tests/testdata/voila/dashboard/dashboard.ipynb +tests/testdata/voila/dashboard/requirements.txt +tests/testdata/voila/multi-voila/requirements.txt +tests/testdata/voila/multi-voila/bqplot/bqplot.ipynb +tests/testdata/voila/multi-voila/dashboard/dashboard.ipynb +vetiver-testing/vetiver-requirements.txt +vetiver-testing/setup-rsconnect/add-users.sh +vetiver-testing/setup-rsconnect/dump_api_keys.py +vetiver-testing/setup-rsconnect/rstudio-connect.gcfg +vetiver-testing/setup-rsconnect/users.txt \ No newline at end of file diff --git a/pip-wheel-metadata/rsconnect_python.egg-info/dependency_links.txt b/pip-wheel-metadata/rsconnect_python.egg-info/dependency_links.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/pip-wheel-metadata/rsconnect_python.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/pip-wheel-metadata/rsconnect_python.egg-info/entry_points.txt b/pip-wheel-metadata/rsconnect_python.egg-info/entry_points.txt new file mode 100644 index 00000000..a6874c3b --- /dev/null +++ b/pip-wheel-metadata/rsconnect_python.egg-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +rsconnect = rsconnect.main:cli diff --git a/pip-wheel-metadata/rsconnect_python.egg-info/requires.txt b/pip-wheel-metadata/rsconnect_python.egg-info/requires.txt new file mode 100644 index 00000000..163a44f5 --- /dev/null +++ b/pip-wheel-metadata/rsconnect_python.egg-info/requires.txt @@ -0,0 +1,5 @@ +six>=1.14.0 +click>=7.0.0 +pip>=10.0.0 +semver<3.0.0,>=2.0.0 +pyjwt>=2.4.0 diff --git a/pip-wheel-metadata/rsconnect_python.egg-info/top_level.txt b/pip-wheel-metadata/rsconnect_python.egg-info/top_level.txt new file mode 100644 index 00000000..e9a412e1 --- /dev/null +++ b/pip-wheel-metadata/rsconnect_python.egg-info/top_level.txt @@ -0,0 +1 @@ +rsconnect diff --git a/pip-wheel-metadata/rsconnect_python.egg-info/zip-safe b/pip-wheel-metadata/rsconnect_python.egg-info/zip-safe new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/pip-wheel-metadata/rsconnect_python.egg-info/zip-safe @@ -0,0 +1 @@ + diff --git a/pyproject.toml b/pyproject.toml index c0939a29..8b6ec5ef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools>=42", "wheel", "setuptools_scm[toml]>=3.4"] +requires = ["setuptools>=61", "wheel", "setuptools_scm[toml]>=3.4"] [tool.black] line-length = 120 diff --git a/requirements.txt b/requirements.txt index 75c7e18b..f7ec3ba2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,6 +15,7 @@ pytest pytest-cov pytest-mypy semver>=2.0.0,<3.0.0 +setuptools>=61 setuptools_scm six>=1.14.0 toml diff --git a/setup.py b/setup.py index 912eb394..29ad8d75 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ "pyjwt>=2.4.0", ], setup_requires=[ - "setuptools", + "setuptools>=61", "setuptools_scm>=3.4", "toml", "wheel", From 30413b10cc402929b884de9aabadd8f44dbd34f7 Mon Sep 17 00:00:00 2001 From: Bill Sager Date: Tue, 17 Oct 2023 14:20:26 -0700 Subject: [PATCH 22/43] Adding conda param back for vetiver compatibility --- rsconnect/actions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rsconnect/actions.py b/rsconnect/actions.py index 90b695d0..2a073dcd 100644 --- a/rsconnect/actions.py +++ b/rsconnect/actions.py @@ -849,6 +849,7 @@ def deploy_python_fastapi( app_id: int, title: str, python: str, + conda_mode: bool, force_generate: bool, log_callback: typing.Callable, image: str = None, @@ -869,6 +870,7 @@ def deploy_python_fastapi( :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: depricated parameter, included for compatibility. Ignored. :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 From 50a2da114224fe5bcd0be92cea81b1f716555206 Mon Sep 17 00:00:00 2001 From: Bill Sager Date: Tue, 17 Oct 2023 16:52:26 -0700 Subject: [PATCH 23/43] Clean up pip-wheel-metadata directory and CONTRIBUTING.md --- CONTRIBUTING.md | 2 +- .../LICENSE.md | 361 ------ .../METADATA | 1128 ----------------- .../entry_points.txt | 2 - .../top_level.txt | 1 - .../zip-safe | 1 - .../rsconnect_python.egg-info/PKG-INFO | 1128 ----------------- .../rsconnect_python.egg-info/SOURCES.txt | 234 ---- .../dependency_links.txt | 1 - .../entry_points.txt | 2 - .../rsconnect_python.egg-info/requires.txt | 5 - .../rsconnect_python.egg-info/top_level.txt | 1 - .../rsconnect_python.egg-info/zip-safe | 1 - 13 files changed, 1 insertion(+), 2866 deletions(-) delete mode 100644 pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/LICENSE.md delete mode 100644 pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/METADATA delete mode 100644 pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/entry_points.txt delete mode 100644 pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/top_level.txt delete mode 100644 pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/zip-safe delete mode 100644 pip-wheel-metadata/rsconnect_python.egg-info/PKG-INFO delete mode 100644 pip-wheel-metadata/rsconnect_python.egg-info/SOURCES.txt delete mode 100644 pip-wheel-metadata/rsconnect_python.egg-info/dependency_links.txt delete mode 100644 pip-wheel-metadata/rsconnect_python.egg-info/entry_points.txt delete mode 100644 pip-wheel-metadata/rsconnect_python.egg-info/requires.txt delete mode 100644 pip-wheel-metadata/rsconnect_python.egg-info/top_level.txt delete mode 100644 pip-wheel-metadata/rsconnect_python.egg-info/zip-safe diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7a794756..832c5251 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,7 +22,7 @@ cd rsconnect-python python3 -m venv .venv # Activate the virtual environment source .venv/bin/activate -# install our requirements into the virtual environment based +# install our requirements into the virtual environment pip install -r requirements.txt # install rsconnect-python with a symbolic link to the locations repository, # meaning any changes to code in there will automatically be reflected diff --git a/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/LICENSE.md b/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/LICENSE.md deleted file mode 100644 index 50e720ec..00000000 --- a/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/LICENSE.md +++ /dev/null @@ -1,361 +0,0 @@ -### GNU GENERAL PUBLIC LICENSE - -Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -### Preamble - -The licenses for most software are designed to take away your freedom -to share and change it. By contrast, the GNU General Public License is -intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - -When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - -To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if -you distribute copies of the software, or if you modify it. - -For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - -We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - -Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, -we want its recipients to know that what they have is not the -original, so that any problems introduced by others will not reflect -on the original authors' reputations. - -Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at -all. - -The precise terms and conditions for copying, distribution and -modification follow. - -### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - -**0.** This License applies to any program or other work which -contains a notice placed by the copyright holder saying it may be -distributed under the terms of this General Public License. The -"Program", below, refers to any such program or work, and a "work -based on the Program" means either the Program or any derivative work -under copyright law: that is to say, a work containing the Program or -a portion of it, either verbatim or with modifications and/or -translated into another language. (Hereinafter, translation is -included without limitation in the term "modification".) Each licensee -is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the Program -(independent of having been made by running the Program). Whether that -is true depends on what the Program does. - -**1.** You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a -fee. - -**2.** You may modify your copy or copies of the Program or any -portion of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - -**a)** You must cause the modified files to carry prominent notices -stating that you changed the files and the date of any change. - - -**b)** You must cause any work that you distribute or publish, that in -whole or in part contains or is derived from the Program or any part -thereof, to be licensed as a whole at no charge to all third parties -under the terms of this License. - - -**c)** If the modified program normally reads commands interactively -when run, you must cause it, when started running for such interactive -use in the most ordinary way, to print or display an announcement -including an appropriate copyright notice and a notice that there is -no warranty (or else, saying that you provide a warranty) and that -users may redistribute the program under these conditions, and telling -the user how to view a copy of this License. (Exception: if the -Program itself is interactive but does not normally print such an -announcement, your work based on the Program is not required to print -an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - -**3.** You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - -**a)** Accompany it with the complete corresponding machine-readable -source code, which must be distributed under the terms of Sections 1 -and 2 above on a medium customarily used for software interchange; or, - - -**b)** Accompany it with a written offer, valid for at least three -years, to give any third party, for a charge no more than your cost of -physically performing source distribution, a complete machine-readable -copy of the corresponding source code, to be distributed under the -terms of Sections 1 and 2 above on a medium customarily used for -software interchange; or, - - -**c)** Accompany it with the information you received as to the offer -to distribute corresponding source code. (This alternative is allowed -only for noncommercial distribution and only if you received the -program in object code or executable form with such an offer, in -accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - -**4.** You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt otherwise -to copy, modify, sublicense or distribute the Program is void, and -will automatically terminate your rights under this License. However, -parties who have received copies, or rights, from you under this -License will not have their licenses terminated so long as such -parties remain in full compliance. - -**5.** You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - -**6.** Each time you redistribute the Program (or any work based on -the Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - -**7.** If, as a consequence of a court judgment or allegation of -patent infringement or for any other reason (not limited to patent -issues), conditions are imposed on you (whether by court order, -agreement or otherwise) that contradict the conditions of this -License, they do not excuse you from the conditions of this License. -If you cannot distribute so as to satisfy simultaneously your -obligations under this License and any other pertinent obligations, -then as a consequence you may not distribute the Program at all. For -example, if a patent license would not permit royalty-free -redistribution of the Program by all those who receive copies directly -or indirectly through you, then the only way you could satisfy both it -and this License would be to refrain entirely from distribution of the -Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - -**8.** If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - -**9.** The Free Software Foundation may publish revised and/or new -versions of the General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Program does not specify a -version number of this License, you may choose any version ever -published by the Free Software Foundation. - -**10.** If you wish to incorporate parts of the Program into other -free programs whose distribution conditions are different, write to -the author to ask for permission. For software which is copyrighted by -the Free Software Foundation, write to the Free Software Foundation; -we sometimes make exceptions for this. Our decision will be guided by -the two goals of preserving the free status of all derivatives of our -free software and of promoting the sharing and reuse of software -generally. - -**NO WARRANTY** - -**11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - -**12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - -### END OF TERMS AND CONDITIONS - -### How to Apply These Terms to Your New Programs - -If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these -terms. - -To do so, attach the following notices to the program. It is safest to -attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - one line to give the program's name and an idea of what it does. - Copyright (C) yyyy name of author - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -Also add information on how to contact you by electronic and paper -mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details - type `show w'. This is free software, and you are welcome - to redistribute it under certain conditions; type `show c' - for details. - -The hypothetical commands \`show w' and \`show c' should show the -appropriate parts of the General Public License. Of course, the -commands you use may be called something other than \`show w' and -\`show c'; they could even be mouse-clicks or menu items--whatever -suits your program. - -You should also get your employer (if you work as a programmer) or -your school, if any, to sign a "copyright disclaimer" for the program, -if necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright - interest in the program `Gnomovision' - (which makes passes at compilers) written - by James Hacker. - - signature of Ty Coon, 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, -you may consider it more useful to permit linking proprietary -applications with the library. If this is what you want to do, use the -[GNU Lesser General Public -License](https://www.gnu.org/licenses/lgpl.html) instead of this -License. diff --git a/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/METADATA b/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/METADATA deleted file mode 100644 index 479cc88f..00000000 --- a/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/METADATA +++ /dev/null @@ -1,1128 +0,0 @@ -Metadata-Version: 2.1 -Name: rsconnect-python -Version: 1.20.1.dev23+g917b738.d20231017 -Summary: Python integration with Posit Connect -Home-page: http://github.com/rstudio/rsconnect-python -Author: Michael Marchetti -Author-email: mike@posit.co -License: GPL-2.0 -Project-URL: Documentation, https://docs.posit.co/rsconnect-python -Requires-Python: >=3.7 -Description-Content-Type: text/markdown -License-File: LICENSE.md -Requires-Dist: six >=1.14.0 -Requires-Dist: click >=7.0.0 -Requires-Dist: pip >=10.0.0 -Requires-Dist: semver <3.0.0,>=2.0.0 -Requires-Dist: pyjwt >=2.4.0 - -# The rsconnect-python CLI - -This package provides a CLI (command-line interface) for interacting -with and deploying to Posit Connect. This is also used by the -[`rsconnect-jupyter`](https://github.com/rstudio/rsconnect-jupyter) package to deploy -Jupyter notebooks via the Jupyter web console. Many types of content supported by Posit -Connect may be deployed by this package, including WSGI-style APIs, Dash, Streamlit, and -Bokeh applications. - -Content types not directly supported by the CLI may also be deployed if they include a -prepared `manifest.json` file. See ["Deploying R or Other -Content"](#deploying-r-or-other-content) for details. - - -## Deploying Python Content to Posit Connect - -Posit Connect supports the deployment of Jupyter notebooks, Python APIs (such as -those based on Flask or FastAPI) and apps (such as Dash, Streamlit, and Bokeh apps). -Much like deploying R -content to Posit Connect, there are some caveats to understand when replicating your -environment on the Posit Connect server: - -Posit Connect insists on matching `` versions of Python. For example, -a server with only Python 3.8 installed will fail to match content deployed with -Python 3.7. Your administrator may also enable exact Python version matching which -will be stricter and require matching major, minor, and patch versions. For more -information see the [Posit Connect Admin Guide chapter titled Python Version -Matching](https://docs.posit.co/connect/admin/python/#python-version-matching). - -### Installation - -To install `rsconnect-python` from PYPI, you may use any python package manager such as -pip: - -```bash -pip install rsconnect-python -``` - -You may also build and install a wheel directly from a repository clone: - -```bash -git clone https://github.com/rstudio/rsconnect-python.git -cd rsconnect-python -pip install pipenv -make deps dist -pip install ./dist/rsconnect_python-*.whl -``` - -### Using the rsconnect CLI - -Here's an example command that deploys a Jupyter notebook to Posit Connect. - -```bash -rsconnect deploy notebook \ - --server https://connect.example.org \ - --api-key my-api-key \ - my-notebook.ipynb -``` - -> **Note** -> The examples here use long command line options, but there are short -> options (`-s`, `-k`, etc.) available also. Run `rsconnect deploy notebook --help` -> for details. - -### Setting up `rsconnect` CLI auto-completion - -If you would like to use your shell's tab completion support with the `rsconnect` -command, use the command below for the shell you are using. - -#### `bash` - -If you are using the `bash` shell, use this to enable tab completion. - -```bash -#~/.bashrc -eval "$(_RSCONNECT_COMPLETE=source rsconnect)" -``` - -#### `zsh` - -If you are using the `zsh` shell, use this to enable tab completion. - -```zsh -#~/.zshrc -eval "$(_RSCONNECT_COMPLETE=source_zsh rsconnect)" -``` - -If you get `command not found: compdef`, you need to add the following lines to your -`.zshrc` before the completion setup: - -```zsh -#~/.zshrc -autoload -Uz compinit -compinit -``` - -### Managing Server Information - -The information used by the `rsconnect` command to communicate with a Posit Connect -server can be tedious to repeat on every command. To help, the CLI supports the idea -of saving this information, making it usable by a simple nickname. - -> **Warning** -> One item of information saved is the API key used to authenticate with -> Posit Connect. Although the file where this information is saved is marked as -> accessible by the owner only, it's important to remember that the key is present -> in the file as plain text so care must be taken to prevent any unauthorized access -> to the server information file. - -#### TLS Support and Posit Connect - -Usually, a Posit Connect server will be set up to be accessed in a secure manner, -using the `https` protocol rather than simple `http`. If Posit Connect is set up -with a self-signed certificate, you will need to include the `--insecure` flag on -all commands. If Posit Connect is set up to require a client-side certificate chain, -you will need to include the `--cacert` option that points to your certificate -authority (CA) trusted certificates file. Both of these options can be saved along -with the URL and API Key for a server. - -> **Note** -> When certificate information is saved for the server, the specified file -> is read and its _contents_ are saved under the server's nickname. If the CA file's -> contents are ever changed, you will need to add the server information again. - -See the [Network Options](#network-options) section for more details about these options. - -#### Remembering Server Information - -Use the `add` command to store information about a Posit Connect server: - -```bash -rsconnect add \ - --api-key my-api-key \ - --server https://connect.example.org \ - --name myserver -``` - -> **Note** -> The `rsconnect` CLI will verify that the serve URL and API key -> are valid. If either is found not to be, no information will be saved. - -If any of the access information for the server changes, simply rerun the -`add` command with the new information and it will replace the original -information. - -Once the server's information is saved, you can refer to it by its nickname: - -```bash -rsconnect deploy notebook --name myserver my-notebook.ipynb -``` - -If there is information for only one server saved, this will work too: - -```bash -rsconnect deploy notebook my-notebook.ipynb -``` - -#### Listing Server Information - -You can see the list of saved server information with: - -``` -rsconnect list -``` - -#### Removing Server Information - -You can remove information about a server with: - -``` -rsconnect remove --name myserver -``` - -Removing may be done by its nickname (`--name`) or URL (`--server`). - -### Verifying Server Information - -You can verify that a URL refers to a running instance of Posit Connect by using -the `details` command: - -```bash -rsconnect details --server https://connect.example.org -``` - -In this form, `rsconnect` will only tell you whether the URL given does, in fact, refer -to a running Posit Connect instance. If you include a valid API key: - -```bash -rsconnect details --server https://connect.example.org --api-key my-api-key -``` - -the tool will provide the version of Posit Connect (if the server is configured to -divulge that information) and environmental information including versions of Python -that are installed on the server. - -You can also use nicknames with the `details` command if you want to verify that the -stored information is still valid. - -### Notebook Deployment Options - -There are a variety of options available to you when deploying a Jupyter notebook to -Posit Connect. - -#### Including Extra Files - -You can include extra files in the deployment bundle to make them available when your -notebook is run by the Posit Connect server. Just specify them on the command line -after the notebook file: - -```bash -rsconnect deploy notebook my-notebook.ipynb data.csv -``` - -#### Package Dependencies - -If a `requirements.txt` file exists in the same directory as the notebook file, it will -be included in the bundle. It must specify the package dependencies needed to execute -the notebook. Posit Connect will reconstruct the Python environment using the -specified package list. - -If there is no `requirements.txt` file or the `--force-generate` option is specified, -the package dependencies will be determined from the current Python environment, or -from an alternative Python executable specified via the `--python` option: - -```bash -rsconnect deploy notebook --python /path/to/python my-notebook.ipynb -``` - -You can see the packages list that will be included by running `pip list --format=freeze` yourself, -ensuring that you use the same Python that you use to run your Jupyter Notebook: - -```bash -/path/to/python -m pip list --format=freeze -``` - -#### Static (Snapshot) Deployment - -By default, `rsconnect` deploys the original notebook with all its source code. This -enables the Posit Connect server to re-run the notebook upon request or on a schedule. - -If you just want to publish an HTML snapshot of the notebook, you can use the `--static` -option. This will cause `rsconnect` to execute your notebook locally to produce the HTML -file, then publish the HTML file to the Posit Connect server: - -```bash -rsconnect deploy notebook --static my-notebook.ipynb -``` - -### Creating a Manifest for Future Deployment - -You can create a `manifest.json` file for a Jupyter Notebook, then use that manifest -in a later deployment. Use the `write-manifest` command to do this. - -The `write-manifest` command will also create a `requirements.txt` file, if it does -not already exist or the `--force-generate` option is specified. It will contain the -package dependencies from the current Python environment, or from an alternative -Python executable specified in the `--python` option. - -Here is an example of the `write-manifest` command: - -```bash -rsconnect write-manifest notebook my-notebook.ipynb -``` - -> **Note** -> Manifests for static (pre-rendered) notebooks cannot be created. - -### API/Application Deployment Options - -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) -* `dash`: Python Dash apps -* `streamlit`: Streamlit apps -* `bokeh`: Bokeh server apps - -All options below apply equally to the `api`, `fastapi`, `dash`, `streamlit`, -and `bokeh` sub-commands. - -#### Including Extra Files - -You can include extra files in the deployment bundle to make them available when your -API or application is run by the Posit Connect server. Just specify them on the -command line after the API or application directory: - -```bash -rsconnect deploy api flask-api/ data.csv -``` - -Since deploying an API or application starts at a directory level, there will be times -when some files under that directory subtree should not be included in the deployment -or manifest. Use the `--exclude` option to specify files or directories to exclude. - -```bash -rsconnect deploy dash --exclude dash-app-venv --exclude TODO.txt dash-app/ -``` - -You can exclude a directory by naming it: -```bash -rsconnect deploy dash --exclude dash-app-venv --exclude output/ dash-app/ -``` - -The `--exclude` option may be repeated, and may include a glob pattern. -You should always quote a glob pattern so that it will be passed to `rsconnect` as-is -instead of letting the shell expand it. If a file is specifically listed as an extra -file that also matches an exclusion pattern, the file will still be included in the -deployment (i.e., extra files take precedence). - -```bash -rsconnect deploy dash --exclude dash-app-venv --exclude “*.txt” dash-app/ -``` - -The following shows an example of an extra file taking precedence: - -```bash -rsconnect deploy dash --exclude “*.csv” dash-app/ important_data.csv -``` - -Some directories are excluded by default, to prevent bundling and uploading files that are not needed or might interfere with the deployment process: - -``` -.Rproj.user -.env -.git -.svn -.venv -__pycache__ -env -packrat -renv -rsconnect-python -rsconnect -venv -``` - -Any directory that appears to be a Python virtual environment (by containing -`bin/python`) will also be excluded. - - -#### Package Dependencies - -If a `requirements.txt` file exists in the API/application directory, it will be -included in the bundle. It must specify the package dependencies needed to execute -the API or application. Posit Connect will reconstruct the Python environment using -the specified package list. - -If there is no `requirements.txt` file or the `--force-generate` option is specified, -the package dependencies will be determined from the current Python environment, or -from an alternative Python executable specified via the `--python` option: - -```bash -rsconnect deploy api --python /path/to/python my-api/ -``` - -You can see the packages list that will be included by running `pip list --format=freeze` yourself, -ensuring that you use the same Python that you use to run your API or application: - -```bash -/path/to/python -m pip list --format=freeze -``` - -### Creating a Manifest for Future Deployment - -You can create a `manifest.json` file for an API or application, then use that -manifest in a later deployment. Use the `write-manifest` command to do this. - -The `write-manifest` command will also create a `requirements.txt` file, if it does -not already exist or the `--force-generate` option is specified. It will contain -the package dependencies from the current Python environment, or from an alternative -Python executable specified in the `--python` option. - -Here is an example of the `write-manifest` command: - -```bash -rsconnect write-manifest api my-api/ -``` - -### Deploying R or Other Content - -You can deploy other content that has an existing Posit Connect `manifest.json` -file. For example, if you download and unpack a source bundle from Posit Connect, -you can deploy the resulting directory. The options are similar to notebook or -API/application deployment; see `rsconnect deploy manifest --help` for details. - -Here is an example of the `deploy manifest` command: - -```bash -rsconnect deploy manifest /path/to/manifest.json -``` - -> **Note** -> In this case, the existing content is deployed as-is. Python environment -> inspection and notebook pre-rendering, if needed, are assumed to be done already -> and represented in the manifest. - -The argument to `deploy manifest` may also be a directory so long as that directory -contains a `manifest.json` file. - -If you have R content but don't have a `manifest.json` file, you can use the RStudio -IDE to create the manifest. See the help for the `rsconnect::writeManifest` R function: - -```r -install.packages('rsconnect') -library(rsconnect) -?rsconnect::writeManifest -``` - -### Options for All Types of Deployments - -These options apply to any type of content deployment. - -#### Title - -The title of the deployed content is, by default, derived from the filename. For -example, if you deploy `my-notebook.ipynb`, the title will be `my-notebook`. To change -this, use the `--title` option: - -``` -rsconnect deploy notebook --title "My Notebook" my-notebook.ipynb -``` - -When using `rsconnect deploy api`, `rsconnect deploy fastapi`, `rsconnect deploy dash`, -`rsconnect deploy streamlit`, or `rsconnect deploy bokeh`, the title is derived from the directory -containing the API or application. - -When using `rsconnect deploy manifest`, the title is derived from the primary -filename referenced in the manifest. - -### Environment variables -You can set environment variables during deployment. Their names and values will be -passed to Posit Connect during deployment so you can use them in your code. Note that -if you are using `rsconnect` to deploy to shinyapps.io, environment variable management -is not supported on that platform. - -For example, if `notebook.ipynb` contains -```python -print(os.environ["MYVAR"]) -``` - -You can set the value of `MYVAR` that will be set when your code runs in Posit Connect -using the `-E/--environment` option: -```bash -rsconnect deploy notebook --environment MYVAR='hello world' notebook.ipynb -``` - -To avoid exposing sensitive values on the command line, you can specify -a variable without a value. In this case, it will use the value from the -environment in which rsconnect-python is running: -```bash -export SECRET_KEY=12345 - -rsconnect deploy notebook --environment SECRET_KEY notebook.ipynb -``` - -If you specify environment variables when updating an existing deployment, -new values will be set for the variables you provided. Other variables will -remain unchanged. If you don't specify any variables, all of the existing -variables will remain unchanged. - -Environment variables are set on the content item before the content bundle -is uploaded and deployed. If the deployment fails, the new environment variables -will still take effect. - -### Network Options - -When specifying information that `rsconnect` needs to be able to interact with Posit -Connect, you can tailor how transport layer security is performed. - -#### TLS/SSL Certificates - -Posit Connect servers can be configured to use TLS/SSL. If your server's certificate -is trusted by your Jupyter Notebook server, API client or user's browser, then you -don't need to do anything special. You can test this out with the `details` command: - -```bash -rsconnect details \ - --api-key my-api-key \ - --server https://connect.example.org:3939 -``` - -If this fails with a TLS Certificate Validation error, then you have two options. - -* Provide the Root CA certificate that is at the root of the signing chain for your - Posit Connect server. This will enable `rsconnect` to securely validate the - server's TLS certificate. - - ```bash - rsconnect details \ - --api-key my-api-key \ - --server https://connect.example.org \ - --cacert /path/to/certificate.pem - ``` - -* Posit Connect is in "insecure mode". This disables TLS certificate verification, - which results in a less secure connection. - - ```bash - rsconnect add \ - --api-key my-api-key \ - --server https://connect.example.org \ - --insecure - ``` - -Once you work out the combination of options that allow you to successfully work with -an instance of Posit Connect, you'll probably want to use the `add` command to have -`rsconnect` remember those options and allow you to just use a nickname. - -### Updating a Deployment - -If you deploy a file again to the same server, `rsconnect` will update the previous -deployment. This means that you can keep running `rsconnect deploy notebook my-notebook.ipynb` -as you develop new versions of your notebook. The same applies to other Python content -types. - -#### Forcing a New Deployment - -To bypass this behavior and force a new deployment, use the `--new` option: - -```bash -rsconnect deploy dash --new my-app/ -``` - -#### Updating a Different Deployment - -If you want to update an existing deployment but don't have the saved deployment data, -you can provide the app's numeric ID or GUID on the command line: - -```bash -rsconnect deploy notebook --app-id 123456 my-notebook.ipynb -``` - -You must be the owner of the target deployment, or a collaborator with permission to -change the content. The type of content (static notebook, notebook with source code, -API, or application) must match the existing deployment. - -> **Note** -> There is no confirmation required to update a deployment. If you do so -> accidentally, use the "Source Versions" dialog in the Posit Connect dashboard to -> activate the previous version and remove the erroneous one. - -##### Finding the App ID - -The App ID associated with a piece of content you have previously deployed from the -`rsconnect` command line interface can be found easily by querying the deployment -information using the `info` command. For more information, see the -[Showing the Deployment Information](#showing-the-deployment-information) section. - -If the content was deployed elsewhere or `info` does not return the correct App ID, -but you can open the content on Posit Connect, find the content and open it in a -browser. The URL in your browser's location bar will contain `#/apps/NNN` where `NNN` -is your App ID. The GUID identifier for the app may be found on the **Info** tab for -the content in the Posit Connect UI. - -#### Showing the Deployment Information - -You can see the information that the `rsconnect` command has saved for the most recent -deployment with the `info` command: - -```bash -rsconnect info my-notebook.ipynb -``` - -If you have deployed to multiple servers, the most recent deployment information for -each server will be shown. This command also displays the path to the file where the -deployment data is stored. - -## Stored Information Files - -Stored information files are stored in a platform-specific directory: - -| Platform | Location | -| -------- | ------------------------------------------------------------------ | -| Mac | `$HOME/Library/Application Support/rsconnect-python/` | -| Linux | `$HOME/.rsconnect-python/` or `$XDG_CONFIG_HOME/rsconnect-python/` | -| Windows | `$APPDATA/rsconnect-python` | - -Remembered server information is stored in the `servers.json` file in that directory. - -### Deployment Data - -After a deployment is completed, information about the deployment is saved -to enable later redeployment. This data is stored alongside the deployed file, -in an `rsconnect-python` subdirectory, if possible. If that location is not writable -during deployment, then the deployment data will be stored in the global configuration -directory specified above. - -
-Generated from rsconnect-python {{ rsconnect_python.version }} -
- -### Hide Jupyter Notebook Input Code Cells - -The user can render a Jupyter notebook without its corresponding input code cells by passing the '--hide-all-input' flag through the cli: - -```bash -rsconnect deploy notebook \ - --server https://connect.example.org \ - --api-key my-api-key \ - --hide-all-input \ - my-notebook.ipynb -``` - -To selectively hide input cells in a Jupyter notebook, the user needs to follow a two step process: -1. tag cells with the 'hide_input' tag, -2. then pass the ' --hide-tagged-input' flag through the cli: - -```bash -rsconnect deploy notebook \ - --server https://connect.example.org \ - --api-key my-api-key \ - --hide-tagged-input \ - my-notebook.ipynb -``` - -By default, rsconnect-python does not install Jupyter notebook related depenencies. These dependencies are installed via rsconnect-jupyter. When the user is using the hide input features in rsconnect-python by itself without rsconnect-jupyter, he/she needs to install the following package depenecies: - -``` -notebook -nbformat -nbconvert>=5.6.1 -``` - -## Content subcommands - -rsconnect-python supports multiple options for interacting with Posit Connect's -`/v1/content` API. Both administrators and publishers can use the content subcommands -to search, download, and rebuild content on Posit Connect without needing to access the -dashboard from a browser. - -> **Note** -> The `rsconnect content` CLI subcommands are intended to be easily scriptable. -> The default output format is `JSON` so that the results can be easily piped into -> other command line utilities like [`jq`](https://stedolan.github.io/jq/) for further post-processing. - -```bash -rsconnect content --help -# Usage: rsconnect content [OPTIONS] COMMAND [ARGS]... - -# Interact with Posit Connect's content API. - -# Options: -# --help Show this message and exit. - -# Commands: -# build Build content on Posit Connect. -# describe Describe a content item on Posit Connect. -# download-bundle Download a content item's source bundle. -# search Search for content on Posit Connect. -``` - -### Content Search - -The `rsconnect content search` subcommands can be used by administrators and publishers -to find specific content on a given Posit Connect server. The search returns -metadata for each content item that meets the search criteria. - -```bash -rsconnect content search --help -# Usage: rsconnect content search [OPTIONS] - -# Options: -# -n, --name TEXT The nickname of the Posit Connect server. -# -s, --server TEXT The URL for the Posit Connect server. -# -k, --api-key TEXT The API key to use to authenticate with -# Posit Connect. - -# -i, --insecure Disable TLS certification/host validation. -# -c, --cacert FILENAME The path to trusted TLS CA certificates. -# --published Search only published content. -# --unpublished Search only unpublished content. -# --content-type [unknown|shiny|rmd-static|rmd-shiny|static|api|tensorflow-saved-model|jupyter-static|python-api|python-dash|python-streamlit|python-bokeh|python-fastapi|quarto-shiny|quarto-static] -# Filter content results by content type. -# --r-version VERSIONSEARCHFILTER -# Filter content results by R version. -# --py-version VERSIONSEARCHFILTER -# Filter content results by Python version. -# --title-contains TEXT Filter content results by title. -# --order-by [created|last_deployed] -# Order content results. -# -v, --verbose Print detailed messages. -# --help Show this message and exit. - -rsconnect content search -# [ -# { -# "max_conns_per_process": null, -# "content_category": "", -# "load_factor": null, -# "cluster_name": "Local", -# "description": "", -# "bundle_id": "142", -# "image_name": null, -# "r_version": null, -# "content_url": "https://connect.example.org:3939/content/4ffc819c-065c-420c-88eb-332db1133317/", -# "connection_timeout": null, -# "min_processes": null, -# "last_deployed_time": "2021-12-02T18:09:11Z", -# "name": "logs-api-python", -# "title": "logs-api-python", -# "created_time": "2021-07-19T19:17:32Z", -# "read_timeout": null, -# "guid": "4ffc819c-065c-420c-88eb-332db1133317", -# "parameterized": false, -# "run_as": null, -# "py_version": "3.8.2", -# "idle_timeout": null, -# "app_role": "owner", -# "access_type": "acl", -# "app_mode": "python-api", -# "init_timeout": null, -# "id": "18", -# "quarto_version": null, -# "dashboard_url": "https://connect.example.org:3939/connect/#/apps/4ffc819c-065c-420c-88eb-332db1133317", -# "run_as_current_user": false, -# "owner_guid": "edf26318-0027-4d9d-bbbb-54703ebb1855", -# "max_processes": null -# }, -# ... -# ] -``` - -See [this section](#searching-for-content) for more comprehensive usage examples -of the available search flags. - - -### Content Build - -> **Note** -> The `rsconnect content build` subcommand requires Posit Connect >= 2021.11.1 - -Posit Connect caches R and Python packages in the configured -[`Server.DataDir`](https://docs.posit.co/connect/admin/appendix/configuration/#Server.DataDir). -Under certain circumstances (examples below), these package caches can become stale -and need to be rebuilt. This refresh automatically occurs when a Posit Connect -user visits the content. You may wish to refresh some content before it is visited -because it is high priority or is not visited frequently (API content, emailed reports). -In these cases, it is possible to preemptively build specific content items using -the `rsconnect content build` subcommands. This way the user does not have to pay -the build cost when the content is accessed next. - -The following are some common scenarios where performing a content build might be necessary: - -- OS upgrade -- changes to gcc or libc libraries -- changes to Python or R installations -- switching from source to binary package repositories or vice versa - -> **Note** -> The `content build` command is non-destructive, meaning that it does nothing to purge -> existing packrat/python package caches before a build. If you have an -> existing cache, it should be cleared prior to starting a content build. -> See the [migration documentation](https://docs.posit.co/connect/admin/appendix/cli/#migration) for details. - -> **Note** -> You may use the [`rsconnect content search`](#content-search) subcommand to help -> identify high priority content items to build. - -```bash -rsconnect content build --help -Usage: rsconnect content build [OPTIONS] COMMAND [ARGS]... - - Build content on Posit Connect. Requires Connect >= 2021.11.1 - -Options: - --help Show this message and exit. - -Commands: - add Mark a content item for build. Use `build run` to invoke the build - on the Connect server. - - history Get the build history for a content item. - logs Print the logs for a content build. - ls List the content items that are being tracked for build on a given - Connect server. - - rm Remove a content item from the list of content that are tracked for - build. Use `build ls` to view the tracked content. - - run Start building content on a given Connect server. -``` - -To build a specific content item, first `add` it to the list of content that is -"tracked" for building using its GUID. - -> **Note** -> Metadata for "tracked" content items is stored in a local directory called -> `rsconnect-build` which will be automatically created in your current working directory. -> You may set the environment variable `CONNECT_CONTENT_BUILD_DIR` to override this directory location. - -```bash -rsconnect content build add --guid 4ffc819c-065c-420c-88eb-332db1133317 -``` - -> **Note** -> See [this section](#add-to-build-from-search-results) for -> an example of how to add multiple content items in bulk, from the results -> of a `rsconnect content search` command. - -To view all currently "tracked" content items, use the `rsconnect content build ls` subcommand. - -```bash -rsconnect content build ls -``` - -To view only the "tracked" content items that have not yet been built, use the `--status NEEDS_BUILD` flag. - -```bash -rsconnect content build ls --status NEEDS_BUILD -``` - -Once the content items have been added, you may initiate a build -using the `rsconnect content build run` subcommand. This command will attempt to -build all "tracked" content that has the status `NEEDS_BUILD`. - -```bash -rsconnect content build run -# [INFO] 2021-12-14T13:02:45-0500 Initializing ContentBuildStore for https://connect.example.org:3939 -# [INFO] 2021-12-14T13:02:45-0500 Starting content build (https://connect.example.org:3939)... -# [INFO] 2021-12-14T13:02:45-0500 Starting build: 4ffc819c-065c-420c-88eb-332db1133317 -# [INFO] 2021-12-14T13:02:50-0500 Running = 1, Pending = 0, Success = 0, Error = 0 -# [INFO] 2021-12-14T13:02:50-0500 Build succeeded: 4ffc819c-065c-420c-88eb-332db1133317 -# [INFO] 2021-12-14T13:02:55-0500 Running = 0, Pending = 0, Success = 1, Error = 0 -# [INFO] 2021-12-14T13:02:55-0500 1/1 content builds completed in 0:00:10 -# [INFO] 2021-12-14T13:02:55-0500 Success = 1, Error = 0 -# [INFO] 2021-12-14T13:02:55-0500 Content build complete. -``` - -Sometimes content builds will fail and require debugging by the publisher or administrator. -Use the `rsconnect content build ls` to identify content builds that resulted in errors -and inspect the build logs with the `rsconnect content build logs` subcommand. - -```bash -rsconnect content build ls --status ERROR -# [INFO] 2021-12-14T13:07:32-0500 Initializing ContentBuildStore for https://connect.example.org:3939 -# [ -# { -# "rsconnect_build_status": "ERROR", -# "last_deployed_time": "2021-12-02T18:09:11Z", -# "owner_guid": "edf26318-0027-4d9d-bbbb-54703ebb1855", -# "rsconnect_last_build_log": "/Users/david/code/posit/rsconnect-python/rsconnect-build/logs/connect_example_org_3939/4ffc819c-065c-420c-88eb-332db1133317/pZoqfBoi6BgpKde5.log", -# "guid": "4ffc819c-065c-420c-88eb-332db1133317", -# "rsconnect_build_task_result": { -# "user_id": 1, -# "error": "Cannot find compatible environment: no compatible Local environment with Python version 3.9.5", -# "code": 1, -# "finished": true, -# "result": { -# "data": "An error occurred while building the content", -# "type": "build-failed-error" -# }, -# "id": "pZoqfBoi6BgpKde5" -# }, -# "dashboard_url": "https://connect.example.org:3939/connect/#/apps/4ffc819c-065c-420c-88eb-332db1133317", -# "name": "logs-api-python", -# "title": "logs-api-python", -# "content_url": "https://connect.example.org:3939/content/4ffc819c-065c-420c-88eb-332db1133317/", -# "bundle_id": "141", -# "rsconnect_last_build_time": "2021-12-14T18:07:16Z", -# "created_time": "2021-07-19T19:17:32Z", -# "app_mode": "python-api" -# } -# ] - -rsconnect content build logs --guid 4ffc819c-065c-420c-88eb-332db1133317 -# [INFO] 2021-12-14T13:09:27-0500 Initializing ContentBuildStore for https://connect.example.org:3939 -# Building Python API... -# Cannot find compatible environment: no compatible Local environment with Python version 3.9.5 -# Task failed. Task exited with status 1. -``` - -## Common Usage Examples - -### Searching for content - -The following are some examples of how publishers might use the -`rsconnect content search` subcommand to find content on Posit Connect. -By default, the `rsconnect content search` command will return metadata for ALL -of the content on a Posit Connect server, both published and unpublished content. - -> **Note** -> When using the `--r-version` and `--py-version` flags, users should -> make sure to quote the arguments to avoid conflicting with your shell. For -> example, bash would interpret `--py-version >3.0.0` as a shell redirect because of the -> unquoted `>` character. - -```bash -# return only published content -rsconnect content search --published - -# return only unpublished content -rsconnect content search --unpublished - -# return published content where the python version is at least 3.9.0 -rsconnect content search --published --py-version ">=3.9.0" - -# return published content where the R version is exactly 3.6.3 -rsconnect content search --published --r-version "==3.6.3" - -# return published content where the content type is a static RMD -rsconnect content search --content-type rmd-static - -# return published content where the content type is either shiny OR fast-api -rsconnect content search --content-type shiny --content-type python-fastapi - -# return all content, published or unpublished, where the title contains the -# text "Stock Report" -rsconnect content search --title-contains "Stock Report" - -# return published content, results are ordered by when the content was last -# deployed -rsconnect content search --published --order-by last_deployed - -# return published content, results are ordered by when the content was -# created -rsconnect content search --published --order-by created -``` - -### Finding r and python versions - -One common use for the `search` command might be to find the versions of -r and python that are currently in use on your Posit Connect server before a migration. - -```bash -# search for all published content and print the unique r and python version -# combinations -rsconnect content search --published | jq -c '.[] | {py_version,r_version}' | sort | -uniq -# {"py_version":"3.8.2","r_version":"3.5.3"} -# {"py_version":"3.8.2","r_version":"3.6.3"} -# {"py_version":"3.8.2","r_version":null} -# {"py_version":null,"r_version":"3.5.3"} -# {"py_version":null,"r_version":"3.6.3"} -# {"py_version":null,"r_version":null} -``` - -### Finding recently deployed content - -```bash -# return only the 10 most recently deployed content items -rsconnect content search \ - --order-by last_deployed \ - --published | jq -c 'limit(10; .[]) | { guid, last_deployed_time }' -# {"guid":"4ffc819c-065c-420c-88eb-332db1133317","last_deployed_time":"2021-12-02T18:09:11Z"} -# {"guid":"aa2603f8-1988-484f-a335-193f2c57e6c4","last_deployed_time":"2021-12-01T20:56:07Z"} -# {"guid":"051252f0-4f70-438f-9be1-d818a3b5f8d9","last_deployed_time":"2021-12-01T20:37:01Z"} -# {"guid":"015143da-b75f-407c-81b1-99c4a724341e","last_deployed_time":"2021-11-30T16:56:21Z"} -# {"guid":"bcc74209-3a81-4b9c-acd5-d24a597c256c","last_deployed_time":"2021-11-30T15:51:07Z"} -# {"guid":"f21d7767-c99e-4dd4-9b00-ff8ec9ae2f53","last_deployed_time":"2021-11-23T18:46:28Z"} -# {"guid":"da4f709c-c383-4fbc-89e2-f032b2d7e91d","last_deployed_time":"2021-11-23T18:46:28Z"} -# {"guid":"9180809d-38fd-4730-a0e0-8568c45d87b7","last_deployed_time":"2021-11-23T15:16:19Z"} -# {"guid":"2b1d2ab8-927d-4956-bbf9-29798d039bc5","last_deployed_time":"2021-11-22T18:33:17Z"} -# {"guid":"c96db3f3-87a1-4df5-9f58-eb109c397718","last_deployed_time":"2021-11-19T20:25:33Z"} -``` - -### Add to build from search results - -One common use case might be to `rsconnect content build add` content for build -based on the results of a `rsconnect content search`. For example: - -```bash -# search for all API type content, then -# for each guid, add it to the "tracked" content items -for guid in $(rsconnect content search \ - --published \ - --content-type python-api \ - --content-type api | jq -r '.[].guid'); do - rsconnect content build add --guid $guid -done -``` - -Adding content items one at a time can be a slow operation. This is because -`rsconnect content build add` must fetch metadata for each content item before it -is added to the "tracked" content items. By providing multiple `--guid` arguments -to the `rsconnect content build add` subcommand, we can fetch metadata for multiple content items -in a single api call, which speeds up the operation significantly. - -```bash -# write the guid of every published content item to a file called guids.txt -rsconnect content search --published | jq '.[].guid' > guids.txt - -# bulk-add from the guids.txt with a single `rsconnect content build add` command -xargs printf -- '-g %s\n' < guids.txt | xargs rsconnect content build add -``` -## Programmatic Provisioning - -Posit Connect supports the programmatic bootstrapping of an administrator API key -for scripted provisioning tasks. This process is supported by the `rsconnect bootstrap` command, -which uses a JSON Web Token to request an initial API key from a fresh Connect instance. - -> **Warning** -> This feature **requires Python version 3.6 or higher**. - -```bash -rsconnect bootstrap \ - --server https://connect.example.org:3939 \ - --jwt-keypath /path/to/secret.key -``` - -A full description on how to use `rsconnect bootstrap` in a provisioning workflow is provided in the Connect administrator guide's -[programmatic provisioning](https://docs.posit.co/connect/admin/programmatic-provisioning) documentation. - -## Server Administration Tasks - -Starting with the 2023.05 edition of Posit Connect, `rsconnect-python` can be -used to perform certain server administration tasks, such as instance managing -runtime caches. For more information on runtime caches in Posit Connect, see the -Connect Admin Guide's section on [runtime -caches](https://docs.posit.co/connect/admin/server-management/runtime-caches/). - -Examples in this section will use `--name myserver` to stand in for your Connect -server information. See [Managing Server -Information](#managing-server-information) above for more details. - -### Enumerate Runtime Caches -*New in Connect 2023.05* - -Use the command below to enumerate runtime caches on a Connect server. The -command will output a JSON object containing a list of runtime caches . Each -cache entry will contain the following information: - -- `language`: The language of content that uses the cache, either R or Python. -- `version`: The language version of the content that uses the cache. -- `image_name`: The execution environment of the cache. The string `Local` - denotes native execution. For Connect instances that use off-host execution, - the name of the image that uses the cache will be displayed. - -```bash -rsconnect system caches list --name myserver -# { -# "caches": [ -# { -# "language": "R", -# "version": "3.6.3", -# "image_name": "Local" -# }, -# { -# "language": "Python", -# "version": "3.9.5", -# "image_name": "Local" -# }, -# { -# "language": "R", -# "version": "3.6.3", -# "image_name": "rstudio/content-base:r3.6.3-py3.9.5-bionic" -# }, -# { -# "language": "Python", -# "version": "3.9.5", -# "image_name": "rstudio/content-base:r3.6.3-py3.9.5-bionic" -# } -# ] -# } -``` - -> **Note** -> The `image_name` field returned by the server will use sanitized versions -> of names. - -### Delete Runtime Caches -*New in Connect 2023.05* - -When Connect's execution environment changes, runtime caches may be invalidated. -In these cases, you will need to delete the affected runtime caches using the -`system caches delete` command. - -> **Warning** -> After deleting a cache, the first time affected content is visited, Connect -> will need to reconstruct its environment. This can take a long time. To -> mitigate this, you can use the [`content build`](#content-build) command to -> rebuild affected content ahead of time. You may want to do this just for -> high-priority content, or for all content. - -To delete a runtime cache, call the `system caches delete` command, specifying a -Connect server, as well as the language (`-l, --language`), version (`-V, ---version`), and image name (`-I, --image-name`) for the cache you wish to -delete. Deleting a large cache might take a while. The command will wait for -Connect to finish the task. - -Use the following parameters specify the target cache: - -- `language` (required) must name `R` or `Python`. It is case-insensitive. -- `version` (required) must be a three-part version number, e.g. `3.8.12`. -- `image-name` (optional) defaults to `Local`, which targets caches used for - natively-executed content. Off-host images can be specified using either the - literal image name or the sanitized name returned by the `list` command. - -Use the dry run flag (`-d, --dry-run`) to surface any errors ahead of -deletion. - -```bash -rsconnect system caches delete \ - --name myserver \ - --language Python \ - --version 3.9.5 \ - --image-name rstudio/content-base:r3.6.3-py3.9.5-bionic \ - --dry-run -# Dry run finished - -rsconnect system caches delete \ - --name myserver \ - --language Python \ - --version 3.9.5 \ - --image-name rstudio/content-base:r3.6.3-py3.9.5-bionic -# Deleting runtime cache... -# Successfully deleted runtime cache -``` - -You should run these commands for each cache you wish to delete. diff --git a/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/entry_points.txt b/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/entry_points.txt deleted file mode 100644 index a6874c3b..00000000 --- a/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/entry_points.txt +++ /dev/null @@ -1,2 +0,0 @@ -[console_scripts] -rsconnect = rsconnect.main:cli diff --git a/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/top_level.txt b/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/top_level.txt deleted file mode 100644 index e9a412e1..00000000 --- a/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -rsconnect diff --git a/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/zip-safe b/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/zip-safe deleted file mode 100644 index 8b137891..00000000 --- a/pip-wheel-metadata/rsconnect_python-1.20.1.dev23+g917b738.d20231017.dist-info/zip-safe +++ /dev/null @@ -1 +0,0 @@ - diff --git a/pip-wheel-metadata/rsconnect_python.egg-info/PKG-INFO b/pip-wheel-metadata/rsconnect_python.egg-info/PKG-INFO deleted file mode 100644 index 9e2d7a37..00000000 --- a/pip-wheel-metadata/rsconnect_python.egg-info/PKG-INFO +++ /dev/null @@ -1,1128 +0,0 @@ -Metadata-Version: 2.1 -Name: rsconnect-python -Version: 1.20.1.dev23+g917b738.d20231017 -Summary: Python integration with Posit Connect -Home-page: http://github.com/rstudio/rsconnect-python -Author: Michael Marchetti -Author-email: mike@posit.co -License: GPL-2.0 -Project-URL: Documentation, https://docs.posit.co/rsconnect-python -Requires-Python: >=3.7 -Description-Content-Type: text/markdown -License-File: LICENSE.md -Requires-Dist: six>=1.14.0 -Requires-Dist: click>=7.0.0 -Requires-Dist: pip>=10.0.0 -Requires-Dist: semver<3.0.0,>=2.0.0 -Requires-Dist: pyjwt>=2.4.0 - -# The rsconnect-python CLI - -This package provides a CLI (command-line interface) for interacting -with and deploying to Posit Connect. This is also used by the -[`rsconnect-jupyter`](https://github.com/rstudio/rsconnect-jupyter) package to deploy -Jupyter notebooks via the Jupyter web console. Many types of content supported by Posit -Connect may be deployed by this package, including WSGI-style APIs, Dash, Streamlit, and -Bokeh applications. - -Content types not directly supported by the CLI may also be deployed if they include a -prepared `manifest.json` file. See ["Deploying R or Other -Content"](#deploying-r-or-other-content) for details. - - -## Deploying Python Content to Posit Connect - -Posit Connect supports the deployment of Jupyter notebooks, Python APIs (such as -those based on Flask or FastAPI) and apps (such as Dash, Streamlit, and Bokeh apps). -Much like deploying R -content to Posit Connect, there are some caveats to understand when replicating your -environment on the Posit Connect server: - -Posit Connect insists on matching `` versions of Python. For example, -a server with only Python 3.8 installed will fail to match content deployed with -Python 3.7. Your administrator may also enable exact Python version matching which -will be stricter and require matching major, minor, and patch versions. For more -information see the [Posit Connect Admin Guide chapter titled Python Version -Matching](https://docs.posit.co/connect/admin/python/#python-version-matching). - -### Installation - -To install `rsconnect-python` from PYPI, you may use any python package manager such as -pip: - -```bash -pip install rsconnect-python -``` - -You may also build and install a wheel directly from a repository clone: - -```bash -git clone https://github.com/rstudio/rsconnect-python.git -cd rsconnect-python -pip install pipenv -make deps dist -pip install ./dist/rsconnect_python-*.whl -``` - -### Using the rsconnect CLI - -Here's an example command that deploys a Jupyter notebook to Posit Connect. - -```bash -rsconnect deploy notebook \ - --server https://connect.example.org \ - --api-key my-api-key \ - my-notebook.ipynb -``` - -> **Note** -> The examples here use long command line options, but there are short -> options (`-s`, `-k`, etc.) available also. Run `rsconnect deploy notebook --help` -> for details. - -### Setting up `rsconnect` CLI auto-completion - -If you would like to use your shell's tab completion support with the `rsconnect` -command, use the command below for the shell you are using. - -#### `bash` - -If you are using the `bash` shell, use this to enable tab completion. - -```bash -#~/.bashrc -eval "$(_RSCONNECT_COMPLETE=source rsconnect)" -``` - -#### `zsh` - -If you are using the `zsh` shell, use this to enable tab completion. - -```zsh -#~/.zshrc -eval "$(_RSCONNECT_COMPLETE=source_zsh rsconnect)" -``` - -If you get `command not found: compdef`, you need to add the following lines to your -`.zshrc` before the completion setup: - -```zsh -#~/.zshrc -autoload -Uz compinit -compinit -``` - -### Managing Server Information - -The information used by the `rsconnect` command to communicate with a Posit Connect -server can be tedious to repeat on every command. To help, the CLI supports the idea -of saving this information, making it usable by a simple nickname. - -> **Warning** -> One item of information saved is the API key used to authenticate with -> Posit Connect. Although the file where this information is saved is marked as -> accessible by the owner only, it's important to remember that the key is present -> in the file as plain text so care must be taken to prevent any unauthorized access -> to the server information file. - -#### TLS Support and Posit Connect - -Usually, a Posit Connect server will be set up to be accessed in a secure manner, -using the `https` protocol rather than simple `http`. If Posit Connect is set up -with a self-signed certificate, you will need to include the `--insecure` flag on -all commands. If Posit Connect is set up to require a client-side certificate chain, -you will need to include the `--cacert` option that points to your certificate -authority (CA) trusted certificates file. Both of these options can be saved along -with the URL and API Key for a server. - -> **Note** -> When certificate information is saved for the server, the specified file -> is read and its _contents_ are saved under the server's nickname. If the CA file's -> contents are ever changed, you will need to add the server information again. - -See the [Network Options](#network-options) section for more details about these options. - -#### Remembering Server Information - -Use the `add` command to store information about a Posit Connect server: - -```bash -rsconnect add \ - --api-key my-api-key \ - --server https://connect.example.org \ - --name myserver -``` - -> **Note** -> The `rsconnect` CLI will verify that the serve URL and API key -> are valid. If either is found not to be, no information will be saved. - -If any of the access information for the server changes, simply rerun the -`add` command with the new information and it will replace the original -information. - -Once the server's information is saved, you can refer to it by its nickname: - -```bash -rsconnect deploy notebook --name myserver my-notebook.ipynb -``` - -If there is information for only one server saved, this will work too: - -```bash -rsconnect deploy notebook my-notebook.ipynb -``` - -#### Listing Server Information - -You can see the list of saved server information with: - -``` -rsconnect list -``` - -#### Removing Server Information - -You can remove information about a server with: - -``` -rsconnect remove --name myserver -``` - -Removing may be done by its nickname (`--name`) or URL (`--server`). - -### Verifying Server Information - -You can verify that a URL refers to a running instance of Posit Connect by using -the `details` command: - -```bash -rsconnect details --server https://connect.example.org -``` - -In this form, `rsconnect` will only tell you whether the URL given does, in fact, refer -to a running Posit Connect instance. If you include a valid API key: - -```bash -rsconnect details --server https://connect.example.org --api-key my-api-key -``` - -the tool will provide the version of Posit Connect (if the server is configured to -divulge that information) and environmental information including versions of Python -that are installed on the server. - -You can also use nicknames with the `details` command if you want to verify that the -stored information is still valid. - -### Notebook Deployment Options - -There are a variety of options available to you when deploying a Jupyter notebook to -Posit Connect. - -#### Including Extra Files - -You can include extra files in the deployment bundle to make them available when your -notebook is run by the Posit Connect server. Just specify them on the command line -after the notebook file: - -```bash -rsconnect deploy notebook my-notebook.ipynb data.csv -``` - -#### Package Dependencies - -If a `requirements.txt` file exists in the same directory as the notebook file, it will -be included in the bundle. It must specify the package dependencies needed to execute -the notebook. Posit Connect will reconstruct the Python environment using the -specified package list. - -If there is no `requirements.txt` file or the `--force-generate` option is specified, -the package dependencies will be determined from the current Python environment, or -from an alternative Python executable specified via the `--python` option: - -```bash -rsconnect deploy notebook --python /path/to/python my-notebook.ipynb -``` - -You can see the packages list that will be included by running `pip list --format=freeze` yourself, -ensuring that you use the same Python that you use to run your Jupyter Notebook: - -```bash -/path/to/python -m pip list --format=freeze -``` - -#### Static (Snapshot) Deployment - -By default, `rsconnect` deploys the original notebook with all its source code. This -enables the Posit Connect server to re-run the notebook upon request or on a schedule. - -If you just want to publish an HTML snapshot of the notebook, you can use the `--static` -option. This will cause `rsconnect` to execute your notebook locally to produce the HTML -file, then publish the HTML file to the Posit Connect server: - -```bash -rsconnect deploy notebook --static my-notebook.ipynb -``` - -### Creating a Manifest for Future Deployment - -You can create a `manifest.json` file for a Jupyter Notebook, then use that manifest -in a later deployment. Use the `write-manifest` command to do this. - -The `write-manifest` command will also create a `requirements.txt` file, if it does -not already exist or the `--force-generate` option is specified. It will contain the -package dependencies from the current Python environment, or from an alternative -Python executable specified in the `--python` option. - -Here is an example of the `write-manifest` command: - -```bash -rsconnect write-manifest notebook my-notebook.ipynb -``` - -> **Note** -> Manifests for static (pre-rendered) notebooks cannot be created. - -### API/Application Deployment Options - -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) -* `dash`: Python Dash apps -* `streamlit`: Streamlit apps -* `bokeh`: Bokeh server apps - -All options below apply equally to the `api`, `fastapi`, `dash`, `streamlit`, -and `bokeh` sub-commands. - -#### Including Extra Files - -You can include extra files in the deployment bundle to make them available when your -API or application is run by the Posit Connect server. Just specify them on the -command line after the API or application directory: - -```bash -rsconnect deploy api flask-api/ data.csv -``` - -Since deploying an API or application starts at a directory level, there will be times -when some files under that directory subtree should not be included in the deployment -or manifest. Use the `--exclude` option to specify files or directories to exclude. - -```bash -rsconnect deploy dash --exclude dash-app-venv --exclude TODO.txt dash-app/ -``` - -You can exclude a directory by naming it: -```bash -rsconnect deploy dash --exclude dash-app-venv --exclude output/ dash-app/ -``` - -The `--exclude` option may be repeated, and may include a glob pattern. -You should always quote a glob pattern so that it will be passed to `rsconnect` as-is -instead of letting the shell expand it. If a file is specifically listed as an extra -file that also matches an exclusion pattern, the file will still be included in the -deployment (i.e., extra files take precedence). - -```bash -rsconnect deploy dash --exclude dash-app-venv --exclude “*.txt” dash-app/ -``` - -The following shows an example of an extra file taking precedence: - -```bash -rsconnect deploy dash --exclude “*.csv” dash-app/ important_data.csv -``` - -Some directories are excluded by default, to prevent bundling and uploading files that are not needed or might interfere with the deployment process: - -``` -.Rproj.user -.env -.git -.svn -.venv -__pycache__ -env -packrat -renv -rsconnect-python -rsconnect -venv -``` - -Any directory that appears to be a Python virtual environment (by containing -`bin/python`) will also be excluded. - - -#### Package Dependencies - -If a `requirements.txt` file exists in the API/application directory, it will be -included in the bundle. It must specify the package dependencies needed to execute -the API or application. Posit Connect will reconstruct the Python environment using -the specified package list. - -If there is no `requirements.txt` file or the `--force-generate` option is specified, -the package dependencies will be determined from the current Python environment, or -from an alternative Python executable specified via the `--python` option: - -```bash -rsconnect deploy api --python /path/to/python my-api/ -``` - -You can see the packages list that will be included by running `pip list --format=freeze` yourself, -ensuring that you use the same Python that you use to run your API or application: - -```bash -/path/to/python -m pip list --format=freeze -``` - -### Creating a Manifest for Future Deployment - -You can create a `manifest.json` file for an API or application, then use that -manifest in a later deployment. Use the `write-manifest` command to do this. - -The `write-manifest` command will also create a `requirements.txt` file, if it does -not already exist or the `--force-generate` option is specified. It will contain -the package dependencies from the current Python environment, or from an alternative -Python executable specified in the `--python` option. - -Here is an example of the `write-manifest` command: - -```bash -rsconnect write-manifest api my-api/ -``` - -### Deploying R or Other Content - -You can deploy other content that has an existing Posit Connect `manifest.json` -file. For example, if you download and unpack a source bundle from Posit Connect, -you can deploy the resulting directory. The options are similar to notebook or -API/application deployment; see `rsconnect deploy manifest --help` for details. - -Here is an example of the `deploy manifest` command: - -```bash -rsconnect deploy manifest /path/to/manifest.json -``` - -> **Note** -> In this case, the existing content is deployed as-is. Python environment -> inspection and notebook pre-rendering, if needed, are assumed to be done already -> and represented in the manifest. - -The argument to `deploy manifest` may also be a directory so long as that directory -contains a `manifest.json` file. - -If you have R content but don't have a `manifest.json` file, you can use the RStudio -IDE to create the manifest. See the help for the `rsconnect::writeManifest` R function: - -```r -install.packages('rsconnect') -library(rsconnect) -?rsconnect::writeManifest -``` - -### Options for All Types of Deployments - -These options apply to any type of content deployment. - -#### Title - -The title of the deployed content is, by default, derived from the filename. For -example, if you deploy `my-notebook.ipynb`, the title will be `my-notebook`. To change -this, use the `--title` option: - -``` -rsconnect deploy notebook --title "My Notebook" my-notebook.ipynb -``` - -When using `rsconnect deploy api`, `rsconnect deploy fastapi`, `rsconnect deploy dash`, -`rsconnect deploy streamlit`, or `rsconnect deploy bokeh`, the title is derived from the directory -containing the API or application. - -When using `rsconnect deploy manifest`, the title is derived from the primary -filename referenced in the manifest. - -### Environment variables -You can set environment variables during deployment. Their names and values will be -passed to Posit Connect during deployment so you can use them in your code. Note that -if you are using `rsconnect` to deploy to shinyapps.io, environment variable management -is not supported on that platform. - -For example, if `notebook.ipynb` contains -```python -print(os.environ["MYVAR"]) -``` - -You can set the value of `MYVAR` that will be set when your code runs in Posit Connect -using the `-E/--environment` option: -```bash -rsconnect deploy notebook --environment MYVAR='hello world' notebook.ipynb -``` - -To avoid exposing sensitive values on the command line, you can specify -a variable without a value. In this case, it will use the value from the -environment in which rsconnect-python is running: -```bash -export SECRET_KEY=12345 - -rsconnect deploy notebook --environment SECRET_KEY notebook.ipynb -``` - -If you specify environment variables when updating an existing deployment, -new values will be set for the variables you provided. Other variables will -remain unchanged. If you don't specify any variables, all of the existing -variables will remain unchanged. - -Environment variables are set on the content item before the content bundle -is uploaded and deployed. If the deployment fails, the new environment variables -will still take effect. - -### Network Options - -When specifying information that `rsconnect` needs to be able to interact with Posit -Connect, you can tailor how transport layer security is performed. - -#### TLS/SSL Certificates - -Posit Connect servers can be configured to use TLS/SSL. If your server's certificate -is trusted by your Jupyter Notebook server, API client or user's browser, then you -don't need to do anything special. You can test this out with the `details` command: - -```bash -rsconnect details \ - --api-key my-api-key \ - --server https://connect.example.org:3939 -``` - -If this fails with a TLS Certificate Validation error, then you have two options. - -* Provide the Root CA certificate that is at the root of the signing chain for your - Posit Connect server. This will enable `rsconnect` to securely validate the - server's TLS certificate. - - ```bash - rsconnect details \ - --api-key my-api-key \ - --server https://connect.example.org \ - --cacert /path/to/certificate.pem - ``` - -* Posit Connect is in "insecure mode". This disables TLS certificate verification, - which results in a less secure connection. - - ```bash - rsconnect add \ - --api-key my-api-key \ - --server https://connect.example.org \ - --insecure - ``` - -Once you work out the combination of options that allow you to successfully work with -an instance of Posit Connect, you'll probably want to use the `add` command to have -`rsconnect` remember those options and allow you to just use a nickname. - -### Updating a Deployment - -If you deploy a file again to the same server, `rsconnect` will update the previous -deployment. This means that you can keep running `rsconnect deploy notebook my-notebook.ipynb` -as you develop new versions of your notebook. The same applies to other Python content -types. - -#### Forcing a New Deployment - -To bypass this behavior and force a new deployment, use the `--new` option: - -```bash -rsconnect deploy dash --new my-app/ -``` - -#### Updating a Different Deployment - -If you want to update an existing deployment but don't have the saved deployment data, -you can provide the app's numeric ID or GUID on the command line: - -```bash -rsconnect deploy notebook --app-id 123456 my-notebook.ipynb -``` - -You must be the owner of the target deployment, or a collaborator with permission to -change the content. The type of content (static notebook, notebook with source code, -API, or application) must match the existing deployment. - -> **Note** -> There is no confirmation required to update a deployment. If you do so -> accidentally, use the "Source Versions" dialog in the Posit Connect dashboard to -> activate the previous version and remove the erroneous one. - -##### Finding the App ID - -The App ID associated with a piece of content you have previously deployed from the -`rsconnect` command line interface can be found easily by querying the deployment -information using the `info` command. For more information, see the -[Showing the Deployment Information](#showing-the-deployment-information) section. - -If the content was deployed elsewhere or `info` does not return the correct App ID, -but you can open the content on Posit Connect, find the content and open it in a -browser. The URL in your browser's location bar will contain `#/apps/NNN` where `NNN` -is your App ID. The GUID identifier for the app may be found on the **Info** tab for -the content in the Posit Connect UI. - -#### Showing the Deployment Information - -You can see the information that the `rsconnect` command has saved for the most recent -deployment with the `info` command: - -```bash -rsconnect info my-notebook.ipynb -``` - -If you have deployed to multiple servers, the most recent deployment information for -each server will be shown. This command also displays the path to the file where the -deployment data is stored. - -## Stored Information Files - -Stored information files are stored in a platform-specific directory: - -| Platform | Location | -| -------- | ------------------------------------------------------------------ | -| Mac | `$HOME/Library/Application Support/rsconnect-python/` | -| Linux | `$HOME/.rsconnect-python/` or `$XDG_CONFIG_HOME/rsconnect-python/` | -| Windows | `$APPDATA/rsconnect-python` | - -Remembered server information is stored in the `servers.json` file in that directory. - -### Deployment Data - -After a deployment is completed, information about the deployment is saved -to enable later redeployment. This data is stored alongside the deployed file, -in an `rsconnect-python` subdirectory, if possible. If that location is not writable -during deployment, then the deployment data will be stored in the global configuration -directory specified above. - -
-Generated from rsconnect-python {{ rsconnect_python.version }} -
- -### Hide Jupyter Notebook Input Code Cells - -The user can render a Jupyter notebook without its corresponding input code cells by passing the '--hide-all-input' flag through the cli: - -```bash -rsconnect deploy notebook \ - --server https://connect.example.org \ - --api-key my-api-key \ - --hide-all-input \ - my-notebook.ipynb -``` - -To selectively hide input cells in a Jupyter notebook, the user needs to follow a two step process: -1. tag cells with the 'hide_input' tag, -2. then pass the ' --hide-tagged-input' flag through the cli: - -```bash -rsconnect deploy notebook \ - --server https://connect.example.org \ - --api-key my-api-key \ - --hide-tagged-input \ - my-notebook.ipynb -``` - -By default, rsconnect-python does not install Jupyter notebook related depenencies. These dependencies are installed via rsconnect-jupyter. When the user is using the hide input features in rsconnect-python by itself without rsconnect-jupyter, he/she needs to install the following package depenecies: - -``` -notebook -nbformat -nbconvert>=5.6.1 -``` - -## Content subcommands - -rsconnect-python supports multiple options for interacting with Posit Connect's -`/v1/content` API. Both administrators and publishers can use the content subcommands -to search, download, and rebuild content on Posit Connect without needing to access the -dashboard from a browser. - -> **Note** -> The `rsconnect content` CLI subcommands are intended to be easily scriptable. -> The default output format is `JSON` so that the results can be easily piped into -> other command line utilities like [`jq`](https://stedolan.github.io/jq/) for further post-processing. - -```bash -rsconnect content --help -# Usage: rsconnect content [OPTIONS] COMMAND [ARGS]... - -# Interact with Posit Connect's content API. - -# Options: -# --help Show this message and exit. - -# Commands: -# build Build content on Posit Connect. -# describe Describe a content item on Posit Connect. -# download-bundle Download a content item's source bundle. -# search Search for content on Posit Connect. -``` - -### Content Search - -The `rsconnect content search` subcommands can be used by administrators and publishers -to find specific content on a given Posit Connect server. The search returns -metadata for each content item that meets the search criteria. - -```bash -rsconnect content search --help -# Usage: rsconnect content search [OPTIONS] - -# Options: -# -n, --name TEXT The nickname of the Posit Connect server. -# -s, --server TEXT The URL for the Posit Connect server. -# -k, --api-key TEXT The API key to use to authenticate with -# Posit Connect. - -# -i, --insecure Disable TLS certification/host validation. -# -c, --cacert FILENAME The path to trusted TLS CA certificates. -# --published Search only published content. -# --unpublished Search only unpublished content. -# --content-type [unknown|shiny|rmd-static|rmd-shiny|static|api|tensorflow-saved-model|jupyter-static|python-api|python-dash|python-streamlit|python-bokeh|python-fastapi|quarto-shiny|quarto-static] -# Filter content results by content type. -# --r-version VERSIONSEARCHFILTER -# Filter content results by R version. -# --py-version VERSIONSEARCHFILTER -# Filter content results by Python version. -# --title-contains TEXT Filter content results by title. -# --order-by [created|last_deployed] -# Order content results. -# -v, --verbose Print detailed messages. -# --help Show this message and exit. - -rsconnect content search -# [ -# { -# "max_conns_per_process": null, -# "content_category": "", -# "load_factor": null, -# "cluster_name": "Local", -# "description": "", -# "bundle_id": "142", -# "image_name": null, -# "r_version": null, -# "content_url": "https://connect.example.org:3939/content/4ffc819c-065c-420c-88eb-332db1133317/", -# "connection_timeout": null, -# "min_processes": null, -# "last_deployed_time": "2021-12-02T18:09:11Z", -# "name": "logs-api-python", -# "title": "logs-api-python", -# "created_time": "2021-07-19T19:17:32Z", -# "read_timeout": null, -# "guid": "4ffc819c-065c-420c-88eb-332db1133317", -# "parameterized": false, -# "run_as": null, -# "py_version": "3.8.2", -# "idle_timeout": null, -# "app_role": "owner", -# "access_type": "acl", -# "app_mode": "python-api", -# "init_timeout": null, -# "id": "18", -# "quarto_version": null, -# "dashboard_url": "https://connect.example.org:3939/connect/#/apps/4ffc819c-065c-420c-88eb-332db1133317", -# "run_as_current_user": false, -# "owner_guid": "edf26318-0027-4d9d-bbbb-54703ebb1855", -# "max_processes": null -# }, -# ... -# ] -``` - -See [this section](#searching-for-content) for more comprehensive usage examples -of the available search flags. - - -### Content Build - -> **Note** -> The `rsconnect content build` subcommand requires Posit Connect >= 2021.11.1 - -Posit Connect caches R and Python packages in the configured -[`Server.DataDir`](https://docs.posit.co/connect/admin/appendix/configuration/#Server.DataDir). -Under certain circumstances (examples below), these package caches can become stale -and need to be rebuilt. This refresh automatically occurs when a Posit Connect -user visits the content. You may wish to refresh some content before it is visited -because it is high priority or is not visited frequently (API content, emailed reports). -In these cases, it is possible to preemptively build specific content items using -the `rsconnect content build` subcommands. This way the user does not have to pay -the build cost when the content is accessed next. - -The following are some common scenarios where performing a content build might be necessary: - -- OS upgrade -- changes to gcc or libc libraries -- changes to Python or R installations -- switching from source to binary package repositories or vice versa - -> **Note** -> The `content build` command is non-destructive, meaning that it does nothing to purge -> existing packrat/python package caches before a build. If you have an -> existing cache, it should be cleared prior to starting a content build. -> See the [migration documentation](https://docs.posit.co/connect/admin/appendix/cli/#migration) for details. - -> **Note** -> You may use the [`rsconnect content search`](#content-search) subcommand to help -> identify high priority content items to build. - -```bash -rsconnect content build --help -Usage: rsconnect content build [OPTIONS] COMMAND [ARGS]... - - Build content on Posit Connect. Requires Connect >= 2021.11.1 - -Options: - --help Show this message and exit. - -Commands: - add Mark a content item for build. Use `build run` to invoke the build - on the Connect server. - - history Get the build history for a content item. - logs Print the logs for a content build. - ls List the content items that are being tracked for build on a given - Connect server. - - rm Remove a content item from the list of content that are tracked for - build. Use `build ls` to view the tracked content. - - run Start building content on a given Connect server. -``` - -To build a specific content item, first `add` it to the list of content that is -"tracked" for building using its GUID. - -> **Note** -> Metadata for "tracked" content items is stored in a local directory called -> `rsconnect-build` which will be automatically created in your current working directory. -> You may set the environment variable `CONNECT_CONTENT_BUILD_DIR` to override this directory location. - -```bash -rsconnect content build add --guid 4ffc819c-065c-420c-88eb-332db1133317 -``` - -> **Note** -> See [this section](#add-to-build-from-search-results) for -> an example of how to add multiple content items in bulk, from the results -> of a `rsconnect content search` command. - -To view all currently "tracked" content items, use the `rsconnect content build ls` subcommand. - -```bash -rsconnect content build ls -``` - -To view only the "tracked" content items that have not yet been built, use the `--status NEEDS_BUILD` flag. - -```bash -rsconnect content build ls --status NEEDS_BUILD -``` - -Once the content items have been added, you may initiate a build -using the `rsconnect content build run` subcommand. This command will attempt to -build all "tracked" content that has the status `NEEDS_BUILD`. - -```bash -rsconnect content build run -# [INFO] 2021-12-14T13:02:45-0500 Initializing ContentBuildStore for https://connect.example.org:3939 -# [INFO] 2021-12-14T13:02:45-0500 Starting content build (https://connect.example.org:3939)... -# [INFO] 2021-12-14T13:02:45-0500 Starting build: 4ffc819c-065c-420c-88eb-332db1133317 -# [INFO] 2021-12-14T13:02:50-0500 Running = 1, Pending = 0, Success = 0, Error = 0 -# [INFO] 2021-12-14T13:02:50-0500 Build succeeded: 4ffc819c-065c-420c-88eb-332db1133317 -# [INFO] 2021-12-14T13:02:55-0500 Running = 0, Pending = 0, Success = 1, Error = 0 -# [INFO] 2021-12-14T13:02:55-0500 1/1 content builds completed in 0:00:10 -# [INFO] 2021-12-14T13:02:55-0500 Success = 1, Error = 0 -# [INFO] 2021-12-14T13:02:55-0500 Content build complete. -``` - -Sometimes content builds will fail and require debugging by the publisher or administrator. -Use the `rsconnect content build ls` to identify content builds that resulted in errors -and inspect the build logs with the `rsconnect content build logs` subcommand. - -```bash -rsconnect content build ls --status ERROR -# [INFO] 2021-12-14T13:07:32-0500 Initializing ContentBuildStore for https://connect.example.org:3939 -# [ -# { -# "rsconnect_build_status": "ERROR", -# "last_deployed_time": "2021-12-02T18:09:11Z", -# "owner_guid": "edf26318-0027-4d9d-bbbb-54703ebb1855", -# "rsconnect_last_build_log": "/Users/david/code/posit/rsconnect-python/rsconnect-build/logs/connect_example_org_3939/4ffc819c-065c-420c-88eb-332db1133317/pZoqfBoi6BgpKde5.log", -# "guid": "4ffc819c-065c-420c-88eb-332db1133317", -# "rsconnect_build_task_result": { -# "user_id": 1, -# "error": "Cannot find compatible environment: no compatible Local environment with Python version 3.9.5", -# "code": 1, -# "finished": true, -# "result": { -# "data": "An error occurred while building the content", -# "type": "build-failed-error" -# }, -# "id": "pZoqfBoi6BgpKde5" -# }, -# "dashboard_url": "https://connect.example.org:3939/connect/#/apps/4ffc819c-065c-420c-88eb-332db1133317", -# "name": "logs-api-python", -# "title": "logs-api-python", -# "content_url": "https://connect.example.org:3939/content/4ffc819c-065c-420c-88eb-332db1133317/", -# "bundle_id": "141", -# "rsconnect_last_build_time": "2021-12-14T18:07:16Z", -# "created_time": "2021-07-19T19:17:32Z", -# "app_mode": "python-api" -# } -# ] - -rsconnect content build logs --guid 4ffc819c-065c-420c-88eb-332db1133317 -# [INFO] 2021-12-14T13:09:27-0500 Initializing ContentBuildStore for https://connect.example.org:3939 -# Building Python API... -# Cannot find compatible environment: no compatible Local environment with Python version 3.9.5 -# Task failed. Task exited with status 1. -``` - -## Common Usage Examples - -### Searching for content - -The following are some examples of how publishers might use the -`rsconnect content search` subcommand to find content on Posit Connect. -By default, the `rsconnect content search` command will return metadata for ALL -of the content on a Posit Connect server, both published and unpublished content. - -> **Note** -> When using the `--r-version` and `--py-version` flags, users should -> make sure to quote the arguments to avoid conflicting with your shell. For -> example, bash would interpret `--py-version >3.0.0` as a shell redirect because of the -> unquoted `>` character. - -```bash -# return only published content -rsconnect content search --published - -# return only unpublished content -rsconnect content search --unpublished - -# return published content where the python version is at least 3.9.0 -rsconnect content search --published --py-version ">=3.9.0" - -# return published content where the R version is exactly 3.6.3 -rsconnect content search --published --r-version "==3.6.3" - -# return published content where the content type is a static RMD -rsconnect content search --content-type rmd-static - -# return published content where the content type is either shiny OR fast-api -rsconnect content search --content-type shiny --content-type python-fastapi - -# return all content, published or unpublished, where the title contains the -# text "Stock Report" -rsconnect content search --title-contains "Stock Report" - -# return published content, results are ordered by when the content was last -# deployed -rsconnect content search --published --order-by last_deployed - -# return published content, results are ordered by when the content was -# created -rsconnect content search --published --order-by created -``` - -### Finding r and python versions - -One common use for the `search` command might be to find the versions of -r and python that are currently in use on your Posit Connect server before a migration. - -```bash -# search for all published content and print the unique r and python version -# combinations -rsconnect content search --published | jq -c '.[] | {py_version,r_version}' | sort | -uniq -# {"py_version":"3.8.2","r_version":"3.5.3"} -# {"py_version":"3.8.2","r_version":"3.6.3"} -# {"py_version":"3.8.2","r_version":null} -# {"py_version":null,"r_version":"3.5.3"} -# {"py_version":null,"r_version":"3.6.3"} -# {"py_version":null,"r_version":null} -``` - -### Finding recently deployed content - -```bash -# return only the 10 most recently deployed content items -rsconnect content search \ - --order-by last_deployed \ - --published | jq -c 'limit(10; .[]) | { guid, last_deployed_time }' -# {"guid":"4ffc819c-065c-420c-88eb-332db1133317","last_deployed_time":"2021-12-02T18:09:11Z"} -# {"guid":"aa2603f8-1988-484f-a335-193f2c57e6c4","last_deployed_time":"2021-12-01T20:56:07Z"} -# {"guid":"051252f0-4f70-438f-9be1-d818a3b5f8d9","last_deployed_time":"2021-12-01T20:37:01Z"} -# {"guid":"015143da-b75f-407c-81b1-99c4a724341e","last_deployed_time":"2021-11-30T16:56:21Z"} -# {"guid":"bcc74209-3a81-4b9c-acd5-d24a597c256c","last_deployed_time":"2021-11-30T15:51:07Z"} -# {"guid":"f21d7767-c99e-4dd4-9b00-ff8ec9ae2f53","last_deployed_time":"2021-11-23T18:46:28Z"} -# {"guid":"da4f709c-c383-4fbc-89e2-f032b2d7e91d","last_deployed_time":"2021-11-23T18:46:28Z"} -# {"guid":"9180809d-38fd-4730-a0e0-8568c45d87b7","last_deployed_time":"2021-11-23T15:16:19Z"} -# {"guid":"2b1d2ab8-927d-4956-bbf9-29798d039bc5","last_deployed_time":"2021-11-22T18:33:17Z"} -# {"guid":"c96db3f3-87a1-4df5-9f58-eb109c397718","last_deployed_time":"2021-11-19T20:25:33Z"} -``` - -### Add to build from search results - -One common use case might be to `rsconnect content build add` content for build -based on the results of a `rsconnect content search`. For example: - -```bash -# search for all API type content, then -# for each guid, add it to the "tracked" content items -for guid in $(rsconnect content search \ - --published \ - --content-type python-api \ - --content-type api | jq -r '.[].guid'); do - rsconnect content build add --guid $guid -done -``` - -Adding content items one at a time can be a slow operation. This is because -`rsconnect content build add` must fetch metadata for each content item before it -is added to the "tracked" content items. By providing multiple `--guid` arguments -to the `rsconnect content build add` subcommand, we can fetch metadata for multiple content items -in a single api call, which speeds up the operation significantly. - -```bash -# write the guid of every published content item to a file called guids.txt -rsconnect content search --published | jq '.[].guid' > guids.txt - -# bulk-add from the guids.txt with a single `rsconnect content build add` command -xargs printf -- '-g %s\n' < guids.txt | xargs rsconnect content build add -``` -## Programmatic Provisioning - -Posit Connect supports the programmatic bootstrapping of an administrator API key -for scripted provisioning tasks. This process is supported by the `rsconnect bootstrap` command, -which uses a JSON Web Token to request an initial API key from a fresh Connect instance. - -> **Warning** -> This feature **requires Python version 3.6 or higher**. - -```bash -rsconnect bootstrap \ - --server https://connect.example.org:3939 \ - --jwt-keypath /path/to/secret.key -``` - -A full description on how to use `rsconnect bootstrap` in a provisioning workflow is provided in the Connect administrator guide's -[programmatic provisioning](https://docs.posit.co/connect/admin/programmatic-provisioning) documentation. - -## Server Administration Tasks - -Starting with the 2023.05 edition of Posit Connect, `rsconnect-python` can be -used to perform certain server administration tasks, such as instance managing -runtime caches. For more information on runtime caches in Posit Connect, see the -Connect Admin Guide's section on [runtime -caches](https://docs.posit.co/connect/admin/server-management/runtime-caches/). - -Examples in this section will use `--name myserver` to stand in for your Connect -server information. See [Managing Server -Information](#managing-server-information) above for more details. - -### Enumerate Runtime Caches -*New in Connect 2023.05* - -Use the command below to enumerate runtime caches on a Connect server. The -command will output a JSON object containing a list of runtime caches . Each -cache entry will contain the following information: - -- `language`: The language of content that uses the cache, either R or Python. -- `version`: The language version of the content that uses the cache. -- `image_name`: The execution environment of the cache. The string `Local` - denotes native execution. For Connect instances that use off-host execution, - the name of the image that uses the cache will be displayed. - -```bash -rsconnect system caches list --name myserver -# { -# "caches": [ -# { -# "language": "R", -# "version": "3.6.3", -# "image_name": "Local" -# }, -# { -# "language": "Python", -# "version": "3.9.5", -# "image_name": "Local" -# }, -# { -# "language": "R", -# "version": "3.6.3", -# "image_name": "rstudio/content-base:r3.6.3-py3.9.5-bionic" -# }, -# { -# "language": "Python", -# "version": "3.9.5", -# "image_name": "rstudio/content-base:r3.6.3-py3.9.5-bionic" -# } -# ] -# } -``` - -> **Note** -> The `image_name` field returned by the server will use sanitized versions -> of names. - -### Delete Runtime Caches -*New in Connect 2023.05* - -When Connect's execution environment changes, runtime caches may be invalidated. -In these cases, you will need to delete the affected runtime caches using the -`system caches delete` command. - -> **Warning** -> After deleting a cache, the first time affected content is visited, Connect -> will need to reconstruct its environment. This can take a long time. To -> mitigate this, you can use the [`content build`](#content-build) command to -> rebuild affected content ahead of time. You may want to do this just for -> high-priority content, or for all content. - -To delete a runtime cache, call the `system caches delete` command, specifying a -Connect server, as well as the language (`-l, --language`), version (`-V, ---version`), and image name (`-I, --image-name`) for the cache you wish to -delete. Deleting a large cache might take a while. The command will wait for -Connect to finish the task. - -Use the following parameters specify the target cache: - -- `language` (required) must name `R` or `Python`. It is case-insensitive. -- `version` (required) must be a three-part version number, e.g. `3.8.12`. -- `image-name` (optional) defaults to `Local`, which targets caches used for - natively-executed content. Off-host images can be specified using either the - literal image name or the sanitized name returned by the `list` command. - -Use the dry run flag (`-d, --dry-run`) to surface any errors ahead of -deletion. - -```bash -rsconnect system caches delete \ - --name myserver \ - --language Python \ - --version 3.9.5 \ - --image-name rstudio/content-base:r3.6.3-py3.9.5-bionic \ - --dry-run -# Dry run finished - -rsconnect system caches delete \ - --name myserver \ - --language Python \ - --version 3.9.5 \ - --image-name rstudio/content-base:r3.6.3-py3.9.5-bionic -# Deleting runtime cache... -# Successfully deleted runtime cache -``` - -You should run these commands for each cache you wish to delete. diff --git a/pip-wheel-metadata/rsconnect_python.egg-info/SOURCES.txt b/pip-wheel-metadata/rsconnect_python.egg-info/SOURCES.txt deleted file mode 100644 index a07ba8be..00000000 --- a/pip-wheel-metadata/rsconnect_python.egg-info/SOURCES.txt +++ /dev/null @@ -1,234 +0,0 @@ -.flake8 -.gitignore -CHANGELOG.md -CONTRIBUTING.md -Dockerfile -LICENSE.md -Makefile -README.md -conftest.py -docker-compose.yml -mypy.ini -pyproject.toml -requirements.txt -setup.cfg -setup.py -.github/pull_request_template.md -.github/workflows/main.yml -.github/workflows/snyk.yml -/Users/billsager/dev/rsconnect-python/pip-wheel-metadata/rsconnect_python.egg-info/PKG-INFO -/Users/billsager/dev/rsconnect-python/pip-wheel-metadata/rsconnect_python.egg-info/SOURCES.txt -/Users/billsager/dev/rsconnect-python/pip-wheel-metadata/rsconnect_python.egg-info/dependency_links.txt -/Users/billsager/dev/rsconnect-python/pip-wheel-metadata/rsconnect_python.egg-info/entry_points.txt -/Users/billsager/dev/rsconnect-python/pip-wheel-metadata/rsconnect_python.egg-info/requires.txt -/Users/billsager/dev/rsconnect-python/pip-wheel-metadata/rsconnect_python.egg-info/top_level.txt -/Users/billsager/dev/rsconnect-python/pip-wheel-metadata/rsconnect_python.egg-info/zip-safe -docs/.gitignore -docs/Dockerfile -docs/Makefile -docs/README.md -docs/mkdocs.yml -docs/patch_admonitions.py -docs/requirements.txt -docs/docs/css/custom.css -docs/docs/images/favicon.ico -docs/docs/images/iconPositConnect.svg -docs/docs/images/positLogoBlack.svg -docs/docs/images/positLogoWhite.svg -docs/docs/images/rstudio-logo.png -docs/overrides/partials/footer.html -docs/overrides/partials/header.html -docs/overrides/partials/integrations/analytics.html -integration/.gitignore -integration/__init__.py -integration/jwt_testbed.py -integration-testing/.gitignore -integration-testing/cypress.config.js -integration-testing/docker-compose.yml -integration-testing/justfile -integration-testing/content/notebook/README.md -integration-testing/content/notebook/quandl-wiki-tsla.json.gz -integration-testing/content/notebook/requirements.txt -integration-testing/content/notebook/stock-report-jupyter.ipynb -integration-testing/content/notebook/thumbnail.jpg -integration-testing/content/voila/index.ipynb -integration-testing/content/voila/requirements.txt -integration-testing/cypress/e2e/jupyter.cy.js -integration-testing/cypress/e2e/voila.cy.js -integration-testing/cypress/plugins/index.js -integration-testing/cypress/support/commands.js -integration-testing/cypress/support/e2e.js -integration-testing/docker/client.Dockerfile -integration-testing/docker/cypress.Dockerfile -integration-testing/docker/requirements.txt -integration-testing/docker/rstudio-connect.gcfg -mock_connect/.gitignore -mock_connect/Dockerfile -mock_connect/Makefile -mock_connect/README.md -mock_connect/data.json -mock_connect/mock_connect/__init__.py -mock_connect/mock_connect/data.py -mock_connect/mock_connect/http_helpers.py -mock_connect/mock_connect/main.py -my-shiny-app/app.py -my-shiny-app/manifest.json -my-shiny-app/requirements.txt -rsconnect/__init__.py -rsconnect/actions.py -rsconnect/actions_content.py -rsconnect/api.py -rsconnect/bundle.py -rsconnect/certificates.py -rsconnect/environment.py -rsconnect/exception.py -rsconnect/http_support.py -rsconnect/json_web_token.py -rsconnect/log.py -rsconnect/main.py -rsconnect/metadata.py -rsconnect/models.py -rsconnect/timeouts.py -rsconnect/validation.py -rsconnect/version.py -scripts/build-image -scripts/runtests -tests/__init__.py -tests/test_Manifest.py -tests/test_actions.py -tests/test_api.py -tests/test_bundle.py -tests/test_certificates.py -tests/test_environment.py -tests/test_http_support.py -tests/test_json_web_token.py -tests/test_main.py -tests/test_main_content.py -tests/test_main_system_caches.py -tests/test_metadata.py -tests/test_models.py -tests/test_timeouts.py -tests/test_utils.py -tests/test_vetiver_pins.py -tests/utils.py -tests/rsconnect-build-test/connect_remote_6443.json -tests/testdata/bundle.tar.gz -tests/testdata/fake_broken_conda.sh -tests/testdata/Manifest/html_manifest.json -tests/testdata/Manifest_data/empty_manifest.json -tests/testdata/Manifest_data/missing_file_manifest.json -tests/testdata/R/shinyapp/app.R -tests/testdata/R/shinyapp/index.htm -tests/testdata/R/shinyapp/manifest.json -tests/testdata/R/shinyapp/packrat/packrat.lock -tests/testdata/R/shinyapp/packrat/desc/BH -tests/testdata/R/shinyapp/packrat/desc/R6 -tests/testdata/R/shinyapp/packrat/desc/Rcpp -tests/testdata/R/shinyapp/packrat/desc/crayon -tests/testdata/R/shinyapp/packrat/desc/digest -tests/testdata/R/shinyapp/packrat/desc/htmltools -tests/testdata/R/shinyapp/packrat/desc/httpuv -tests/testdata/R/shinyapp/packrat/desc/jsonlite -tests/testdata/R/shinyapp/packrat/desc/later -tests/testdata/R/shinyapp/packrat/desc/magrittr -tests/testdata/R/shinyapp/packrat/desc/mime -tests/testdata/R/shinyapp/packrat/desc/packrat -tests/testdata/R/shinyapp/packrat/desc/promises -tests/testdata/R/shinyapp/packrat/desc/rlang -tests/testdata/R/shinyapp/packrat/desc/shiny -tests/testdata/R/shinyapp/packrat/desc/sourcetools -tests/testdata/R/shinyapp/packrat/desc/xtable -tests/testdata/api/flask/app.py -tests/testdata/api/flask/requirements.txt -tests/testdata/certificates/localhost.ca-bundle -tests/testdata/certificates/localhost.cer -tests/testdata/certificates/localhost.crt -tests/testdata/certificates/localhost.csr -tests/testdata/certificates/localhost.der -tests/testdata/certificates/localhost.key -tests/testdata/certificates/localhost.pem -tests/testdata/html_tests/multi_file_index/index.html -tests/testdata/html_tests/multi_file_index/main.html -tests/testdata/html_tests/multi_file_nonindex/a.html -tests/testdata/html_tests/multi_file_nonindex/b.html -tests/testdata/html_tests/single_file_index/index.html -tests/testdata/html_tests/single_file_index/test1.txt -tests/testdata/html_tests/single_file_index/test_folder1/testfoldertext1.txt -tests/testdata/html_tests/single_file_nonindex/main.html -tests/testdata/html_tests/single_file_nonindex/test1.txt -tests/testdata/html_tests/single_file_nonindex/test_folder1/testfoldertext1.txt -tests/testdata/initial-admin-responses/forbidden_error.json -tests/testdata/initial-admin-responses/not_found_error.json -tests/testdata/initial-admin-responses/other_error.json -tests/testdata/initial-admin-responses/success.json -tests/testdata/initial-admin-responses/unauthorized_error.json -tests/testdata/jwt/empty_secret.key -tests/testdata/jwt/invalid_secret.key -tests/testdata/jwt/secret.key -tests/testdata/py3/conda1/.keep -tests/testdata/py3/data_file/data.csv -tests/testdata/py3/data_file/data_file.ipynb -tests/testdata/py3/minimal/minimal.ipynb -tests/testdata/py3/numpy/numpy.ipynb -tests/testdata/py3/numpy_requirements/numpy.ipynb -tests/testdata/py3/numpy_requirements/requirements.txt -tests/testdata/py3/pip1/dummy.ipynb -tests/testdata/py3/pip1/requirements.txt -tests/testdata/py3/pip2/data.csv -tests/testdata/py3/pip2/dummy.ipynb -tests/testdata/py3/static/index.html -tests/testdata/py3/static/manifest.json -tests/testdata/pyshiny_with_manifest/README.md -tests/testdata/pyshiny_with_manifest/app5.py -tests/testdata/pyshiny_with_manifest/data.csv -tests/testdata/pyshiny_with_manifest/manifest.json -tests/testdata/pyshiny_with_manifest/requirements.txt -tests/testdata/rstudio-responses/application.json -tests/testdata/rstudio-responses/create-bundle.json -tests/testdata/rstudio-responses/create-output.json -tests/testdata/rstudio-responses/get-accounts.json -tests/testdata/rstudio-responses/get-applications.json -tests/testdata/rstudio-responses/get-bundle.json -tests/testdata/rstudio-responses/get-content.json -tests/testdata/rstudio-responses/get-output-application.json -tests/testdata/rstudio-responses/get-project-application.json -tests/testdata/rstudio-responses/get-task.json -tests/testdata/rstudio-responses/get-user.json -tests/testdata/rstudio-responses/post-deploy.json -tests/testdata/stock-api-fastapi/README.md -tests/testdata/stock-api-fastapi/main.py -tests/testdata/stock-api-fastapi/prices.csv -tests/testdata/stock-api-fastapi/requirements.txt -tests/testdata/stock-api-flask/README.md -tests/testdata/stock-api-flask/app.py -tests/testdata/stock-api-flask/prices.csv -tests/testdata/stock-api-flask/requirements.txt -tests/testdata/stock-dashboard-python/README.md -tests/testdata/stock-dashboard-python/app2.py -tests/testdata/stock-dashboard-python/prices.csv -tests/testdata/stock-dashboard-python/requirements.txt -tests/testdata/top-5-income-share-bokeh/README.md -tests/testdata/top-5-income-share-bokeh/app3.py -tests/testdata/top-5-income-share-bokeh/data.csv -tests/testdata/top-5-income-share-bokeh/requirements.txt -tests/testdata/top-5-income-share-shiny/README.md -tests/testdata/top-5-income-share-shiny/app4.py -tests/testdata/top-5-income-share-shiny/data.csv -tests/testdata/top-5-income-share-shiny/requirements.txt -tests/testdata/top-5-income-share-streamlit/README.md -tests/testdata/top-5-income-share-streamlit/app1.py -tests/testdata/top-5-income-share-streamlit/data.csv -tests/testdata/top-5-income-share-streamlit/requirements.txt -tests/testdata/voila/bqplot/bqplot.ipynb -tests/testdata/voila/bqplot/requirements.txt -tests/testdata/voila/dashboard/bqplot.ipynb -tests/testdata/voila/dashboard/dashboard.ipynb -tests/testdata/voila/dashboard/requirements.txt -tests/testdata/voila/multi-voila/requirements.txt -tests/testdata/voila/multi-voila/bqplot/bqplot.ipynb -tests/testdata/voila/multi-voila/dashboard/dashboard.ipynb -vetiver-testing/vetiver-requirements.txt -vetiver-testing/setup-rsconnect/add-users.sh -vetiver-testing/setup-rsconnect/dump_api_keys.py -vetiver-testing/setup-rsconnect/rstudio-connect.gcfg -vetiver-testing/setup-rsconnect/users.txt \ No newline at end of file diff --git a/pip-wheel-metadata/rsconnect_python.egg-info/dependency_links.txt b/pip-wheel-metadata/rsconnect_python.egg-info/dependency_links.txt deleted file mode 100644 index 8b137891..00000000 --- a/pip-wheel-metadata/rsconnect_python.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/pip-wheel-metadata/rsconnect_python.egg-info/entry_points.txt b/pip-wheel-metadata/rsconnect_python.egg-info/entry_points.txt deleted file mode 100644 index a6874c3b..00000000 --- a/pip-wheel-metadata/rsconnect_python.egg-info/entry_points.txt +++ /dev/null @@ -1,2 +0,0 @@ -[console_scripts] -rsconnect = rsconnect.main:cli diff --git a/pip-wheel-metadata/rsconnect_python.egg-info/requires.txt b/pip-wheel-metadata/rsconnect_python.egg-info/requires.txt deleted file mode 100644 index 163a44f5..00000000 --- a/pip-wheel-metadata/rsconnect_python.egg-info/requires.txt +++ /dev/null @@ -1,5 +0,0 @@ -six>=1.14.0 -click>=7.0.0 -pip>=10.0.0 -semver<3.0.0,>=2.0.0 -pyjwt>=2.4.0 diff --git a/pip-wheel-metadata/rsconnect_python.egg-info/top_level.txt b/pip-wheel-metadata/rsconnect_python.egg-info/top_level.txt deleted file mode 100644 index e9a412e1..00000000 --- a/pip-wheel-metadata/rsconnect_python.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -rsconnect diff --git a/pip-wheel-metadata/rsconnect_python.egg-info/zip-safe b/pip-wheel-metadata/rsconnect_python.egg-info/zip-safe deleted file mode 100644 index 8b137891..00000000 --- a/pip-wheel-metadata/rsconnect_python.egg-info/zip-safe +++ /dev/null @@ -1 +0,0 @@ - From 6c049f80b107fc771964b504d6128d32616a545d Mon Sep 17 00:00:00 2001 From: Bill Sager Date: Tue, 17 Oct 2023 16:58:12 -0700 Subject: [PATCH 24/43] ignore pip-wheel-metadata dir --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 04f4bf93..1c7d3572 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,4 @@ coverage.xml # Temporary dev files vetiver-testing/rsconnect_api_keys.json +/pip-wheel-metadata/ From 0eae10d0ba32b0938ca2ab8859d143c503a9e20a Mon Sep 17 00:00:00 2001 From: tdstein Date: Wed, 18 Oct 2023 12:25:41 -0400 Subject: [PATCH 25/43] Adds Python 3.12 to testing matrix --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6c3064ca..851d2cc2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,7 +17,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - python-version: ['3.7', '3.8', '3.9', '3.10', '3.11'] + python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12'] include: - os: macos-latest python-version: '3.9' From 43d323abf257a1b6a0707dff21c3724d74066674 Mon Sep 17 00:00:00 2001 From: Michael Marchetti Date: Wed, 18 Oct 2023 20:08:25 -0400 Subject: [PATCH 26/43] put all settings in pyproject.toml --- .flake8 | 20 ---------------- .gitignore | 1 + mypy.ini | 3 --- pyproject.toml | 59 ++++++++++++++++++++++++++++++++++++++++++++---- requirements.txt | 16 +------------ setup.cfg | 24 -------------------- setup.py | 18 +-------------- 7 files changed, 58 insertions(+), 83 deletions(-) delete mode 100644 .flake8 delete mode 100644 mypy.ini delete mode 100644 setup.cfg diff --git a/.flake8 b/.flake8 deleted file mode 100644 index 8dece52f..00000000 --- a/.flake8 +++ /dev/null @@ -1,20 +0,0 @@ -[flake8] -max_line_length = 120 -show_source = true -exclude = .git,.venv,.venv2,.venv3,__pycache__,.cache - -# The following codes are ignored so that `flake8` plays nicer with how `black` -# likes to format: -# - E203: whitespace before ':' -# - E231: missing whitespace after ',', ';', or ':' -# - E302: expected 2 blank lines, found 0 -# -# ref: -# https://pycodestyle.readthedocs.io/en/latest/intro.html#error-codes -# -extend_ignore = E203,E231,E302 - -# vim:filetype=dosini - -per-file-ignores = - tests/test_metadata.py: E501 diff --git a/.gitignore b/.gitignore index 1c7d3572..782b1122 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ /rsconnect-build-test /rsconnect/version.py /tests/testdata/**/rsconnect-python/ +env htmlcov test-home/ venv diff --git a/mypy.ini b/mypy.ini deleted file mode 100644 index c904427a..00000000 --- a/mypy.ini +++ /dev/null @@ -1,3 +0,0 @@ -[mypy] -ignore_missing_imports = True -strict_optional = False diff --git a/pyproject.toml b/pyproject.toml index 8b6ec5ef..1e0514d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,34 @@ +[project] +name = "rsconnect_python" +description = "Python integration with Posit Connect" + +authors = [{ name = "Michael Marchetti", email = "mike@posit.co" }] +license = { file = "LICENSE.md" } +readme = { file = "README.md", content-type = "text/markdown" } +requires-python = ">=3.7" + +dependencies = [ + "six>=1.14.0", + "click>=7.0.0", + "pip>=10.0.0", + "semver>=2.0.0,<3.0.0", + "pyjwt>=2.4.0", +] + +dynamic = ["version"] + +[project.scripts] +rsconnect = "rsconnect.main:cli" + +[project.urls] +Repository = "http://github.com/rstudio/rsconnect-python" +Documentation = "https://docs.posit.co/rsconnect-python" + [build-system] -requires = ["setuptools>=61", "wheel", "setuptools_scm[toml]>=3.4"] +requires = ["setuptools>=61", "setuptools_scm[toml]>=3.4", "wheel"] + +[tool.distutils.bdist_wheel] +universal = true [tool.black] line-length = 120 @@ -8,10 +37,32 @@ target-version = ['py35'] [tool.coverage.run] omit = ["tests/*"] +[tool.flake8] +max_line_length = 120 +show_source = true +exclude = [".git", ".venv", ".venv2", ".venv3", "__pycache__", ".cache"] + +# The following codes are ignored so that `flake8` plays nicer with how `black` +# likes to format: +# - E203: whitespace before ':' +# - E231: missing whitespace after ',', ';', or ':' +# - E302: expected 2 blank lines, found 0 +# +# ref: +# https://pycodestyle.readthedocs.io/en/latest/intro.html#error-codes +# +extend_ignore = ["E203", "E231", "E302"] +per-file-ignores = ["tests/test_metadata.py: E501"] + +[tool.mypy] +ignore_missing_imports = true +strict_optional = false + +[tool.setuptools] +packages = ["rsconnect"] + [tool.setuptools_scm] write_to = "rsconnect/version.py" [tool.pytest.ini_options] -markers = [ - "vetiver: tests for vetiver", -] +markers = ["vetiver: tests for vetiver"] diff --git a/requirements.txt b/requirements.txt index f7ec3ba2..da1949d4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,26 +1,12 @@ black==22.3.0 -click>=7.0.0 coverage flake8 -funcsigs -httpretty==1.1.4 -importlib-metadata -ipykernel -ipython -jupyter_client +flake8-pyproject mypy -nbconvert -pyjwt>=2.4.0 pytest pytest-cov pytest-mypy -semver>=2.0.0,<3.0.0 -setuptools>=61 -setuptools_scm -six>=1.14.0 -toml twine -wheel types-six types-click types-Flask diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index eae9e885..00000000 --- a/setup.cfg +++ /dev/null @@ -1,24 +0,0 @@ -[bdist_wheel] -universal = 1 - -[metadata] -author = Michael Marchetti -author_email = mike@posit.co -description = Python integration with Posit Connect -license = GPL-2.0 -license_files = LICENSE.md -long_description = file:README.md -long_description_content_type = text/markdown -name = rsconnect_python -url = http://github.com/rstudio/rsconnect-python -project_urls = - Documentation = https://docs.posit.co/rsconnect-python - -[options] -packages = rsconnect -python_requires = >=3.7 -zip_safe = true - -[options.entry_points] -console_scripts = - rsconnect = rsconnect.main:cli diff --git a/setup.py b/setup.py index 29ad8d75..60684932 100644 --- a/setup.py +++ b/setup.py @@ -1,19 +1,3 @@ from setuptools import setup -# Dependencies here so Snyk can see them -# https://github.com/snyk/snyk-python-plugin/issues/147 -setup( - install_requires=[ - "six>=1.14.0", - "click>=7.0.0", - "pip>=10.0.0", - "semver>=2.0.0,<3.0.0", - "pyjwt>=2.4.0", - ], - setup_requires=[ - "setuptools>=61", - "setuptools_scm>=3.4", - "toml", - "wheel", - ], -) +setup() From a402d04d6d05264d8aeec0b87044660f24f71202 Mon Sep 17 00:00:00 2001 From: Michael Marchetti Date: Wed, 18 Oct 2023 20:23:46 -0400 Subject: [PATCH 27/43] install deps for test --- .github/workflows/main.yml | 3 +++ pyproject.toml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 851d2cc2..a94e4a08 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -32,6 +32,7 @@ jobs: with: python-version: ${{ matrix.python-version }} - run: pip install -r requirements.txt + - run: pip install '.[test]' - run: pip freeze - run: make fmt - run: make lint @@ -53,6 +54,7 @@ jobs: with: python-version: 3.8.x - run: pip install --pre -r requirements.txt + - run: pip install '.[test]' - run: pip freeze - run: make fmt - run: make lint @@ -172,6 +174,7 @@ jobs: python -m pip install --upgrade pip python -m pip install -r requirements.txt python -m pip install -r vetiver-testing/vetiver-requirements.txt + python -m pip install '.[test]' - name: Run RStudio Connect run: | docker-compose up --build -d diff --git a/pyproject.toml b/pyproject.toml index 1e0514d5..d54f0ef2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,6 +20,9 @@ dynamic = ["version"] [project.scripts] rsconnect = "rsconnect.main:cli" +[project.optional-dependencies] +test = ["httpretty", "nbconvert", "ipykernel"] + [project.urls] Repository = "http://github.com/rstudio/rsconnect-python" Documentation = "https://docs.posit.co/rsconnect-python" From 77894560d7f40caea7ec362da3c288fdc1e456a6 Mon Sep 17 00:00:00 2001 From: Michael Marchetti Date: Wed, 18 Oct 2023 20:43:45 -0400 Subject: [PATCH 28/43] stop running setup.py --version --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a94e4a08..d871f9d9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -36,7 +36,7 @@ jobs: - run: pip freeze - run: make fmt - run: make lint - - run: python setup.py --version + - run: rsconnect version - run: make test-${{ matrix.python-version }} - if: github.event_name == 'pull_request' && matrix.python-version == '3.8' uses: orgoro/coverage@v3 @@ -58,7 +58,7 @@ jobs: - run: pip freeze - run: make fmt - run: make lint - - run: python setup.py --version + - run: rsconnect version - run: make mock-test-3.8 distributions: From ea906b9ec19ca933b2176d77e47f25fe21282549 Mon Sep 17 00:00:00 2001 From: Michael Marchetti Date: Wed, 18 Oct 2023 21:02:07 -0400 Subject: [PATCH 29/43] build wheels and determine versions without setup.py --- CONTRIBUTING.md | 11 ++++++----- Makefile | 4 ++-- tests/test_environment.py | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 832c5251..44885b3e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,9 +24,10 @@ python3 -m venv .venv source .venv/bin/activate # install our requirements into the virtual environment pip install -r requirements.txt -# install rsconnect-python with a symbolic link to the locations repository, +# install rsconnect-python with a symbolic link to the locations repository, # meaning any changes to code in there will automatically be reflected -pip install -e ./ +# also install test dependencies +pip install -e '.[test]' ``` ## Workflow @@ -46,8 +47,8 @@ make test make all-tests ``` -As another example, the [`test` job in the default GitHub Actions workflow](.github/workflows/main.yml) -uses some of these targets during the CI for building and testing. +As another example, the [`test` job in the default GitHub Actions workflow](.github/workflows/main.yml) +uses some of these targets during the CI for building and testing. ## Proposing Change @@ -82,7 +83,7 @@ a release will be created and published to the repository rsconnect-python exists on conda-forge as its own [feedstock](https://github.com/conda-forge/rsconnect-python-feedstock) -Updating the package requires a fork of the repository and a [push request](https://github.com/conda-forge/rsconnect-python-feedstock#updating-rsconnect-python-feedstock). +Updating the package requires a fork of the repository and a [push request](https://github.com/conda-forge/rsconnect-python-feedstock#updating-rsconnect-python-feedstock). - For new version/release, update the [meta.yaml](https://github.com/conda-forge/rsconnect-python-feedstock/blob/master/recipe/meta.yaml) file with the new version number, source url, and corresponding checksum. diff --git a/Makefile b/Makefile index d9a6f044..d8190081 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -VERSION := $(shell python setup.py --version) +VERSION := $(shell rsconnect version) HOSTNAME := $(shell hostname) S3_PREFIX := s3://rstudio-connect-downloads/connect/rsconnect-python @@ -133,7 +133,7 @@ version: # exported as a point of reference instead. .PHONY: dist dist: - python setup.py bdist_wheel + pip wheel --no-deps -w dist . twine check $(BDIST_WHEEL) rm -vf dist/*.egg @echo "::set-output name=whl::$(BDIST_WHEEL)" diff --git a/tests/test_environment.py b/tests/test_environment.py index c35c946a..ad4688cb 100644 --- a/tests/test_environment.py +++ b/tests/test_environment.py @@ -53,7 +53,7 @@ def test_file(self): def test_pip_freeze(self): result = detect_environment(get_dir("pip2")) - # these are the dependencies declared in our setup.py + # these are the dependencies declared in our pyproject.toml self.assertIn("six", result.contents) self.assertIn("click", result.contents.lower()) From 141fd8ba0b3bbbc807dad8aca60c5162e057b501 Mon Sep 17 00:00:00 2001 From: Michael Marchetti Date: Thu, 19 Oct 2023 08:32:50 -0400 Subject: [PATCH 30/43] get version from setuptools_scm --- Makefile | 2 +- requirements.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d8190081..8024d946 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -VERSION := $(shell rsconnect version) +VERSION := $(shell python -c 'from setuptools_scm import get_version; print(get_version())') HOSTNAME := $(shell hostname) S3_PREFIX := s3://rstudio-connect-downloads/connect/rsconnect-python diff --git a/requirements.txt b/requirements.txt index da1949d4..f6a7b8c4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,6 +6,7 @@ mypy pytest pytest-cov pytest-mypy +setuptools_scm[toml] twine types-six types-click From 105ec917cb4ff1c916ac867b3950df6c3cc07284 Mon Sep 17 00:00:00 2001 From: Michael Marchetti Date: Thu, 19 Oct 2023 08:33:43 -0400 Subject: [PATCH 31/43] simplify --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8024d946..21043736 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -VERSION := $(shell python -c 'from setuptools_scm import get_version; print(get_version())') +VERSION := $(shell python -m setuptools_scm) HOSTNAME := $(shell hostname) S3_PREFIX := s3://rstudio-connect-downloads/connect/rsconnect-python From 72379b1e59baa66d10878befe85e0b5295f928cc Mon Sep 17 00:00:00 2001 From: Michael Marchetti Date: Thu, 19 Oct 2023 09:56:41 -0400 Subject: [PATCH 32/43] make dist-install --- Makefile | 6 +++++- integration-testing/docker/client.Dockerfile | 11 +---------- integration-testing/justfile | 8 ++++---- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/Makefile b/Makefile index 21043736..f539226e 100644 --- a/Makefile +++ b/Makefile @@ -134,11 +134,15 @@ version: .PHONY: dist dist: pip wheel --no-deps -w dist . - twine check $(BDIST_WHEEL) + twine check dist/rsconnect_python*.whl rm -vf dist/*.egg @echo "::set-output name=whl::$(BDIST_WHEEL)" @echo "::set-output name=whl_basename::$(notdir $(BDIST_WHEEL))" +.PHONY: dist-install +dist-install: dist + pip install $(BDIST_WHEEL) + .PHONY: sync-to-s3 sync-to-s3: aws s3 cp --acl bucket-owner-full-control \ diff --git a/integration-testing/docker/client.Dockerfile b/integration-testing/docker/client.Dockerfile index a20c6ce9..9d7cad0b 100644 --- a/integration-testing/docker/client.Dockerfile +++ b/integration-testing/docker/client.Dockerfile @@ -18,18 +18,9 @@ RUN pip install rsconnect-jupyter --pre && \ pip install pipenv && \ jupyter-nbextension install --sys-prefix --py rsconnect_jupyter -# RUN git clone https://github.com/rstudio/rsconnect-jupyter.git && \ -# pip install twine && \ -# cd rsconnect-jupyter && \ -# make dist && \ -# pip install ./dist/rsconnect_jupyter-0.0.0-py2.py3-none-any.whl && \ -# pip install pipenv && \ -# jupyter-nbextension install --sys-prefix --py rsconnect_jupyter - CMD cd ../ && \ rm -rf ~/.jupyter/ && \ - make deps dist && \ - pip install ./dist/rsconnect_python-*.whl && \ + make deps dist-install && \ jupyter-nbextension enable --sys-prefix --py rsconnect_jupyter && \ jupyter-serverextension enable --sys-prefix --py rsconnect_jupyter && \ jupyter-notebook \ diff --git a/integration-testing/justfile b/integration-testing/justfile index 59c68712..e8d5c6c8 100644 --- a/integration-testing/justfile +++ b/integration-testing/justfile @@ -9,14 +9,14 @@ build: ADMIN_API_KEY=${ADMIN_API_KEY} \ docker compose build -up: +up: docker compose pull connect && \ docker compose up -d client && docker compose up -d connect up-cypress: docker compose up --exit-code-from cypress -# use this target if you want to run Cypress +# use this target if you want to run Cypress # without shutting down all containers after each run up-cypress-local: docker compose up cypress @@ -27,11 +27,11 @@ run-client: just setup-client setup-client: + #!/bin/bash python -m venv ./client-python/ && \ . ./client-python/bin/activate && \ cd ../ && \ - make deps dist && \ - pip install ./dist/rsconnect_python-*.whl && \ + make deps dist-install && \ jupyter-nbextension enable --sys-prefix --py rsconnect_jupyter && \ jupyter-serverextension enable --sys-prefix --py rsconnect_jupyter && \ jupyter-notebook \ From e087af25dd9e97006ae2d04af5ae2e49c501753c Mon Sep 17 00:00:00 2001 From: Michael Marchetti Date: Thu, 19 Oct 2023 10:05:07 -0400 Subject: [PATCH 33/43] unshallow so tags are visible --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d871f9d9..8103cfa0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -203,6 +203,7 @@ jobs: - uses: actions/checkout@v2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - run: git fetch --prune --unshallow - uses: extractions/setup-just@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From a7bd02e43eda01e4ea377197b5549bc2693d98b8 Mon Sep 17 00:00:00 2001 From: Michael Marchetti Date: Thu, 19 Oct 2023 10:58:13 -0400 Subject: [PATCH 34/43] echo version --- integration-testing/docker/client.Dockerfile | 2 +- integration-testing/justfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/integration-testing/docker/client.Dockerfile b/integration-testing/docker/client.Dockerfile index 9d7cad0b..eea60cee 100644 --- a/integration-testing/docker/client.Dockerfile +++ b/integration-testing/docker/client.Dockerfile @@ -20,7 +20,7 @@ RUN pip install rsconnect-jupyter --pre && \ CMD cd ../ && \ rm -rf ~/.jupyter/ && \ - make deps dist-install && \ + make deps version dist-install && \ jupyter-nbextension enable --sys-prefix --py rsconnect_jupyter && \ jupyter-serverextension enable --sys-prefix --py rsconnect_jupyter && \ jupyter-notebook \ diff --git a/integration-testing/justfile b/integration-testing/justfile index e8d5c6c8..44140bbf 100644 --- a/integration-testing/justfile +++ b/integration-testing/justfile @@ -31,7 +31,7 @@ setup-client: python -m venv ./client-python/ && \ . ./client-python/bin/activate && \ cd ../ && \ - make deps dist-install && \ + make deps version dist-install && \ jupyter-nbextension enable --sys-prefix --py rsconnect_jupyter && \ jupyter-serverextension enable --sys-prefix --py rsconnect_jupyter && \ jupyter-notebook \ From 715d500efa71811340b3fa11b567820c49430f6d Mon Sep 17 00:00:00 2001 From: Michael Marchetti Date: Thu, 19 Oct 2023 10:58:40 -0400 Subject: [PATCH 35/43] only check current wheel --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f539226e..835f8100 100644 --- a/Makefile +++ b/Makefile @@ -134,7 +134,7 @@ version: .PHONY: dist dist: pip wheel --no-deps -w dist . - twine check dist/rsconnect_python*.whl + twine check $(BDIST_WHEEL) rm -vf dist/*.egg @echo "::set-output name=whl::$(BDIST_WHEEL)" @echo "::set-output name=whl_basename::$(notdir $(BDIST_WHEEL))" From 093e65269e983be7398b8c2e4af46942fe989708 Mon Sep 17 00:00:00 2001 From: Michael Marchetti Date: Thu, 19 Oct 2023 10:58:50 -0400 Subject: [PATCH 36/43] check with depth 0 --- .github/workflows/main.yml | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8103cfa0..cd56effe 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -26,8 +26,11 @@ jobs: runs-on: ${{ matrix.os }} name: test (py${{ matrix.python-version }} ${{ matrix.os }}) steps: - - uses: actions/checkout@v2 - - run: git fetch --prune --unshallow + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} @@ -48,8 +51,11 @@ jobs: runs-on: ubuntu-latest continue-on-error: true steps: - - uses: actions/checkout@v2 - - run: git fetch --prune --unshallow + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - uses: actions/setup-python@v2 with: python-version: 3.8.x @@ -65,8 +71,11 @@ jobs: needs: test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - run: git fetch --prune --unshallow + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - uses: actions/setup-python@v2 with: python-version: 3.8.x @@ -119,8 +128,11 @@ jobs: needs: test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - run: git fetch --prune --unshallow + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - uses: actions/setup-python@v2 with: python-version: 3.8.x @@ -165,7 +177,7 @@ jobs: name: "Integration tests against latest Connect" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: actions/setup-python@v2 with: python-version: 3.8 @@ -200,10 +212,11 @@ jobs: CONNECT_LICENSE: ${{ secrets.RSC_LICENSE }} ADMIN_API_KEY: ${{ secrets.ADMIN_API_KEY }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + with: + fetch-depth: 0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - run: git fetch --prune --unshallow - uses: extractions/setup-just@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From d6aaafa9eba6c7610c43e3b6a198c5180a1259d6 Mon Sep 17 00:00:00 2001 From: Michael Marchetti Date: Thu, 19 Oct 2023 11:05:41 -0400 Subject: [PATCH 37/43] install --- integration-testing/justfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/integration-testing/justfile b/integration-testing/justfile index 44140bbf..ac66db23 100644 --- a/integration-testing/justfile +++ b/integration-testing/justfile @@ -31,7 +31,8 @@ setup-client: python -m venv ./client-python/ && \ . ./client-python/bin/activate && \ cd ../ && \ - make deps version dist-install && \ + make deps && \ + pip install . && \ jupyter-nbextension enable --sys-prefix --py rsconnect_jupyter && \ jupyter-serverextension enable --sys-prefix --py rsconnect_jupyter && \ jupyter-notebook \ From 691d62bef02f5f9a6181762697db8e2a7f20add3 Mon Sep 17 00:00:00 2001 From: Bill Sager Date: Thu, 19 Oct 2023 10:49:43 -0700 Subject: [PATCH 38/43] compatibility with previous saved environments --- rsconnect/bundle.py | 1 + rsconnect/environment.py | 1 + 2 files changed, 2 insertions(+) diff --git a/rsconnect/bundle.py b/rsconnect/bundle.py index af69b916..36c19bff 100644 --- a/rsconnect/bundle.py +++ b/rsconnect/bundle.py @@ -1590,6 +1590,7 @@ def inspect_environment( if len(flags) > 0: args.append("-" + "".join(flags)) args.append(directory) + try: environment_json = check_output(args, universal_newlines=True) except subprocess.CalledProcessError as e: diff --git a/rsconnect/environment.py b/rsconnect/environment.py index 71148fa0..1766f701 100644 --- a/rsconnect/environment.py +++ b/rsconnect/environment.py @@ -49,6 +49,7 @@ def MakeEnvironment( pip: Optional[str] = None, python: Optional[str] = None, source: Optional[str] = None, + conda: Optional[str] = None, # Deprecated, but included for compatibility with past environments ): return Environment(contents, error, filename, locale, package_manager, pip, python, source) From 8e166382d64d5bc3c08c4ed5661d2451697416d1 Mon Sep 17 00:00:00 2001 From: Bill Sager Date: Thu, 19 Oct 2023 10:57:22 -0700 Subject: [PATCH 39/43] switch to catchall to handle unknown args --- rsconnect/environment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rsconnect/environment.py b/rsconnect/environment.py index 1766f701..4c47f8ec 100644 --- a/rsconnect/environment.py +++ b/rsconnect/environment.py @@ -49,7 +49,7 @@ def MakeEnvironment( pip: Optional[str] = None, python: Optional[str] = None, source: Optional[str] = None, - conda: Optional[str] = None, # Deprecated, but included for compatibility with past environments + **kwargs, # provides compatibility where we no longer support some older properties ): return Environment(contents, error, filename, locale, package_manager, pip, python, source) From 9682fbd93b523b40dd74c9aedee345c60f054696 Mon Sep 17 00:00:00 2001 From: Michael Marchetti Date: Thu, 19 Oct 2023 14:46:37 -0400 Subject: [PATCH 40/43] pip install . in client test --- integration-testing/docker/client.Dockerfile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/integration-testing/docker/client.Dockerfile b/integration-testing/docker/client.Dockerfile index eea60cee..bf94233f 100644 --- a/integration-testing/docker/client.Dockerfile +++ b/integration-testing/docker/client.Dockerfile @@ -20,8 +20,9 @@ RUN pip install rsconnect-jupyter --pre && \ CMD cd ../ && \ rm -rf ~/.jupyter/ && \ - make deps version dist-install && \ + make deps && \ + pip install . && \ jupyter-nbextension enable --sys-prefix --py rsconnect_jupyter && \ jupyter-serverextension enable --sys-prefix --py rsconnect_jupyter && \ jupyter-notebook \ - -y --ip='0.0.0.0' --port=9999 --no-browser --NotebookApp.token='' --allow-root \ No newline at end of file + -y --ip='0.0.0.0' --port=9999 --no-browser --NotebookApp.token='' --allow-root From 7208664a279616c865a2bae7d3ae4bc58b6836f1 Mon Sep 17 00:00:00 2001 From: Michael Marchetti Date: Thu, 19 Oct 2023 15:59:22 -0400 Subject: [PATCH 41/43] move remaining dev dependencies to pyproject.toml --- pyproject.toml | 19 ++++++++++++++++++- requirements.txt | 13 ------------- 2 files changed, 18 insertions(+), 14 deletions(-) delete mode 100644 requirements.txt diff --git a/pyproject.toml b/pyproject.toml index d54f0ef2..2da6e227 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,24 @@ dynamic = ["version"] rsconnect = "rsconnect.main:cli" [project.optional-dependencies] -test = ["httpretty", "nbconvert", "ipykernel"] +test = [ + "black==22.3.0", + "coverage", + "flake8-pyproject", + "flake8", + "httpretty", + "ipykernel", + "mypy", + "nbconvert", + "pytest-cov", + "pytest-mypy", + "pytest", + "setuptools_scm[toml]", + "twine", + "types-click", + "types-Flask", + "types-six", +] [project.urls] Repository = "http://github.com/rstudio/rsconnect-python" diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index f6a7b8c4..00000000 --- a/requirements.txt +++ /dev/null @@ -1,13 +0,0 @@ -black==22.3.0 -coverage -flake8 -flake8-pyproject -mypy -pytest -pytest-cov -pytest-mypy -setuptools_scm[toml] -twine -types-six -types-click -types-Flask From 5fd3c2f1c6f20eb4d77f9c100040209c20f64224 Mon Sep 17 00:00:00 2001 From: Michael Marchetti Date: Thu, 19 Oct 2023 16:14:41 -0400 Subject: [PATCH 42/43] remove requirements.txt --- .github/workflows/main.yml | 7 +------ .github/workflows/snyk.yml | 2 +- CONTRIBUTING.md | 8 +++----- Dockerfile | 1 - Makefile | 11 ----------- README.md | 14 +++++++------- integration-testing/docker/client.Dockerfile | 1 - integration-testing/justfile | 1 - scripts/build-image | 2 +- 9 files changed, 13 insertions(+), 34 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cd56effe..4959137d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -34,7 +34,6 @@ jobs: - uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - - run: pip install -r requirements.txt - run: pip install '.[test]' - run: pip freeze - run: make fmt @@ -59,8 +58,7 @@ jobs: - uses: actions/setup-python@v2 with: python-version: 3.8.x - - run: pip install --pre -r requirements.txt - - run: pip install '.[test]' + - run: pip install --pre '.[test]' - run: pip freeze - run: make fmt - run: make lint @@ -79,7 +77,6 @@ jobs: - uses: actions/setup-python@v2 with: python-version: 3.8.x - - run: pip install -r requirements.txt - run: pip freeze - run: make dist id: create_dist @@ -136,7 +133,6 @@ jobs: - uses: actions/setup-python@v2 with: python-version: 3.8.x - - run: pip install -r requirements.txt - run: pip freeze - run: make docs - uses: actions/upload-artifact@v2 @@ -184,7 +180,6 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install -r requirements.txt python -m pip install -r vetiver-testing/vetiver-requirements.txt python -m pip install '.[test]' - name: Run RStudio Connect diff --git a/.github/workflows/snyk.yml b/.github/workflows/snyk.yml index afcbb3d7..d4c35d66 100644 --- a/.github/workflows/snyk.yml +++ b/.github/workflows/snyk.yml @@ -38,7 +38,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -r requirements.txt + pip install -e '.[test]' - uses: snyk/actions/setup@master diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 44885b3e..fa418b5b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,11 +22,9 @@ cd rsconnect-python python3 -m venv .venv # Activate the virtual environment source .venv/bin/activate -# install our requirements into the virtual environment -pip install -r requirements.txt -# install rsconnect-python with a symbolic link to the locations repository, -# meaning any changes to code in there will automatically be reflected -# also install test dependencies +# Install rsconnect-python with a symbolic link to the locations repository, +# meaning any changes to code in there will automatically be reflected. +# Also install dev/test dependencies. pip install -e '.[test]' ``` diff --git a/Dockerfile b/Dockerfile index aa14306d..8e8ca552 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,5 +3,4 @@ FROM ${BASE_IMAGE} WORKDIR /rsconnect COPY scripts/build-image build-image -COPY requirements.txt requirements.txt RUN bash build-image && rm -vf build-image diff --git a/Makefile b/Makefile index 835f8100..280ae9f9 100644 --- a/Makefile +++ b/Makefile @@ -67,13 +67,6 @@ fmt-%: @echo ERROR: This python version cannot run the fmting tools @exit 1 -.PHONY: deps-prerelease -deps-prerelease: - pip install --pre -r requirements.txt - -deps-%: - $(RUNNER) 'pip install --pre -r requirements.txt' - lint-%: $(RUNNER) 'black --check --diff rsconnect/' $(RUNNER) 'flake8 rsconnect/' @@ -112,10 +105,6 @@ test: test-3.8 lint: RUNNER = bash -c lint: lint-3.8 -.PHONY: deps -deps: RUNNER = bash -c -deps: deps-3.8 - .PHONY: fmt fmt: RUNNER = bash -c fmt: fmt-3.8 diff --git a/README.md b/README.md index cf4da9df..428fcdbe 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ You may also build and install a wheel directly from a repository clone: git clone https://github.com/rstudio/rsconnect-python.git cd rsconnect-python pip install pipenv -make deps dist +make dist pip install ./dist/rsconnect_python-*.whl ``` @@ -117,7 +117,7 @@ you will need to include the `--cacert` option that points to your certificate authority (CA) trusted certificates file. Both of these options can be saved along with the URL and API Key for a server. -> **Note** +> **Note** > When certificate information is saved for the server, the specified file > is read and its _contents_ are saved under the server's nickname. If the CA file's > contents are ever changed, you will need to add the server information again. @@ -135,7 +135,7 @@ rsconnect add \ --name myserver ``` -> **Note** +> **Note** > The `rsconnect` CLI will verify that the serve URL and API key > are valid. If either is found not to be, no information will be saved. @@ -430,7 +430,7 @@ filename referenced in the manifest. ### Environment variables You can set environment variables during deployment. Their names and values will be -passed to Posit Connect during deployment so you can use them in your code. Note that +passed to Posit Connect during deployment so you can use them in your code. Note that if you are using `rsconnect` to deploy to shinyapps.io, environment variable management is not supported on that platform. @@ -985,9 +985,9 @@ xargs printf -- '-g %s\n' < guids.txt | xargs rsconnect content build add ``` ## Programmatic Provisioning -Posit Connect supports the programmatic bootstrapping of an administrator API key +Posit Connect supports the programmatic bootstrapping of an administrator API key for scripted provisioning tasks. This process is supported by the `rsconnect bootstrap` command, -which uses a JSON Web Token to request an initial API key from a fresh Connect instance. +which uses a JSON Web Token to request an initial API key from a fresh Connect instance. > **Warning** > This feature **requires Python version 3.6 or higher**. @@ -998,7 +998,7 @@ rsconnect bootstrap \ --jwt-keypath /path/to/secret.key ``` -A full description on how to use `rsconnect bootstrap` in a provisioning workflow is provided in the Connect administrator guide's +A full description on how to use `rsconnect bootstrap` in a provisioning workflow is provided in the Connect administrator guide's [programmatic provisioning](https://docs.posit.co/connect/admin/programmatic-provisioning) documentation. ## Server Administration Tasks diff --git a/integration-testing/docker/client.Dockerfile b/integration-testing/docker/client.Dockerfile index bf94233f..2986a095 100644 --- a/integration-testing/docker/client.Dockerfile +++ b/integration-testing/docker/client.Dockerfile @@ -20,7 +20,6 @@ RUN pip install rsconnect-jupyter --pre && \ CMD cd ../ && \ rm -rf ~/.jupyter/ && \ - make deps && \ pip install . && \ jupyter-nbextension enable --sys-prefix --py rsconnect_jupyter && \ jupyter-serverextension enable --sys-prefix --py rsconnect_jupyter && \ diff --git a/integration-testing/justfile b/integration-testing/justfile index ac66db23..c4512232 100644 --- a/integration-testing/justfile +++ b/integration-testing/justfile @@ -31,7 +31,6 @@ setup-client: python -m venv ./client-python/ && \ . ./client-python/bin/activate && \ cd ../ && \ - make deps && \ pip install . && \ jupyter-nbextension enable --sys-prefix --py rsconnect_jupyter && \ jupyter-serverextension enable --sys-prefix --py rsconnect_jupyter && \ diff --git a/scripts/build-image b/scripts/build-image index a905dfca..a29e1c42 100755 --- a/scripts/build-image +++ b/scripts/build-image @@ -8,4 +8,4 @@ set -o xtrace exit 1 } python -m pip install --upgrade pip -pip install -r requirements.txt +pip install '.[test]' From 91fcc46f00054e2498ef2d3a59d12a61716453bf Mon Sep 17 00:00:00 2001 From: Michael Marchetti Date: Thu, 19 Oct 2023 16:23:13 -0400 Subject: [PATCH 43/43] install deps for make dist --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4959137d..7af2f42e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -77,6 +77,7 @@ jobs: - uses: actions/setup-python@v2 with: python-version: 3.8.x + - run: pip install -e '.[test]' - run: pip freeze - run: make dist id: create_dist