diff --git a/.flake8 b/.flake8 index 683b5d46..197aeeff 100644 --- a/.flake8 +++ b/.flake8 @@ -1,5 +1,5 @@ [flake8] -max_line_length = 88 +max_line_length = 120 show_source = true exclude = .git,.venv,.venv2,.venv3,__pycache__,.cache @@ -12,8 +12,6 @@ exclude = .git,.venv,.venv2,.venv3,__pycache__,.cache # ref: # https://pycodestyle.readthedocs.io/en/latest/intro.html#error-codes # -# The ignored E501 is intended to be _temporary_ and should be removed once all -# of the long lines can be addressed. -extend_ignore = E203,E231,E302,E501 +extend_ignore = E203,E231,E302 # vim:filetype=dosini diff --git a/mock_connect/mock_connect/http_helpers.py b/mock_connect/mock_connect/http_helpers.py index f0363358..12b0d30e 100644 --- a/mock_connect/mock_connect/http_helpers.py +++ b/mock_connect/mock_connect/http_helpers.py @@ -43,10 +43,7 @@ def _make_json_ready(thing): def endpoint( - authenticated: bool = False, - auth_optional: bool = False, - cls=None, - writes_json: bool = False, + authenticated: bool = False, auth_optional: bool = False, cls=None, writes_json: bool = False, ): def decorator(function): @wraps(function) @@ -69,9 +66,7 @@ def wrapper(object_id=None, *args, **kwargs): object_id = int(object_id) item = cls.get_object(object_id) if item is None: - result = error( - 404, "%s with ID %s not found." % (cls.__name__, object_id) - ) + result = error(404, "%s with ID %s not found." % (cls.__name__, object_id)) else: result = _make_json_ready(function(item, *args, **kwargs)) diff --git a/mock_connect/mock_connect/main.py b/mock_connect/mock_connect/main.py index 4cd85e3f..656c02f3 100644 --- a/mock_connect/mock_connect/main.py +++ b/mock_connect/mock_connect/main.py @@ -97,14 +97,10 @@ def upload(connect_app): def deploy(connect_app): bundle_id = request.get_json(force=True).get("bundle") if bundle_id is None: - return error( - 400, "bundle_id is required" - ) # message and status code probably wrong + return error(400, "bundle_id is required") # message and status code probably wrong bundle = Bundle.get_object(bundle_id) if bundle is None: - return error( - 404, "bundle %s not found" % bundle_id - ) # message and status code probably wrong + return error(404, "bundle %s not found" % bundle_id) # message and status code probably wrong manifest = bundle.get_manifest() old_app_mode = connect_app.app_mode @@ -112,9 +108,7 @@ def deploy(connect_app): new_app_mode = AppMode.value_of(manifest["metadata"]["appmode"]) if old_app_mode is not None and old_app_mode != new_app_mode: - return error( - 400, "Cannot change app mode once deployed" - ) # message and status code probably wrong + return error(400, "Cannot change app mode once deployed") # message and status code probably wrong connect_app.bundle_deployed(bundle, new_app_mode) @@ -161,9 +155,7 @@ def python_settings(): def content(connect_app): bundle = connect_app.get_bundle() if bundle is None: - return error( - 400, "The content has not been deployed." - ) # message and status code probably wrong + return error(400, "The content has not been deployed.") # message and status code probably wrong return bundle.get_rendered_content() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..632eb802 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[tool.black] +line-length = 120 +target-version = ['py27', 'py35', 'py36', 'py37', 'py38'] diff --git a/rsconnect/actions.py b/rsconnect/actions.py index ee838dc0..f5e9aa96 100644 --- a/rsconnect/actions.py +++ b/rsconnect/actions.py @@ -96,9 +96,7 @@ def which_python(python, env=os.environ): """ if python: if not (exists(python) and os.access(python, os.X_OK)): - raise api.RSConnectException( - 'The file, "%s", does not exist or is not executable.' % python - ) + raise api.RSConnectException('The file, "%s", does not exist or is not executable.' % python) return python if "RETICULATE_PYTHON" in env: @@ -108,11 +106,7 @@ def which_python(python, env=os.environ): def inspect_environment( - python, - directory, - conda_mode=False, - force_generate=False, - check_output=subprocess.check_output, + python, directory, conda_mode=False, force_generate=False, check_output=subprocess.check_output, ): """Run the environment inspector using the specified python binary. @@ -230,20 +224,12 @@ 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 - } + 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, + "api_enabled": python_settings["api_enabled"] if "api_enabled" in python_settings else False, "versions": python_versions, }, "conda": conda_settings, @@ -273,9 +259,7 @@ def is_conda_supported_on_server(connect_details): return connect_details.get("conda", {}).get("supported", False) -def check_server_capabilities( - connect_server, capability_functions, details_source=gather_server_details -): +def check_server_capabilities(connect_server, capability_functions, details_source=gather_server_details): """ Uses a sequence of functions that check for capabilities in a Connect server. The server settings data is retrieved by the gather_server_details() function. @@ -299,10 +283,7 @@ def check_server_capabilities( if index >= 0: message = function.__doc__[index + 7 :].strip() else: - message = ( - "The server does not satisfy the %s capability check." - % function.__name__ - ) + message = "The server does not satisfy the %s capability check." % function.__name__ raise api.RSConnectException(message) @@ -343,9 +324,7 @@ def _validate_title(title): """ if title: if not (3 <= len(title) <= 1024): - raise api.RSConnectException( - "A title must be between 3-1024 characters long." - ) + raise api.RSConnectException("A title must be between 3-1024 characters long.") def _default_title(file_name): @@ -372,11 +351,7 @@ def _default_title_from_manifest(the_manifest, manifest_file): metadata = the_manifest.get("metadata") if metadata: # noinspection SpellCheckingInspection - filename = ( - metadata.get("entrypoint") - or metadata.get("primary_rmd") - or metadata.get("primary_html") - ) + filename = metadata.get("entrypoint") or metadata.get("primary_rmd") or metadata.get("primary_html") # If the manifest is for an API, revert to using the parent directory. if filename and _module_pattern.match(filename): filename = None @@ -392,9 +367,7 @@ def validate_file_is_notebook(file_name): """ file_suffix = splitext(file_name)[1].lower() if file_suffix != ".ipynb" or not exists(file_name): - raise api.RSConnectException( - "A Jupyter notebook (.ipynb) file is required here." - ) + raise api.RSConnectException("A Jupyter notebook (.ipynb) file is required here.") def validate_extra_files(directory, extra_files): @@ -414,13 +387,9 @@ def validate_extra_files(directory, extra_files): # It's an error if we have to leave the given dir to get to the extra # file. if extra_file.startswith("../"): - raise api.RSConnectException( - "%s must be under %s." % (extra_file, directory) - ) + raise api.RSConnectException("%s must be under %s." % (extra_file, directory)) if not exists(join(directory, extra_file)): - raise api.RSConnectException( - "Could not find file %s under %s" % (extra, directory) - ) + raise api.RSConnectException("Could not find file %s under %s" % (extra, directory)) result.append(extra_file) return result @@ -436,9 +405,7 @@ def validate_manifest_file(file_or_directory): if isdir(file_or_directory): file_or_directory = join(file_or_directory, "manifest.json") if basename(file_or_directory) != "manifest.json" or not exists(file_or_directory): - raise api.RSConnectException( - "A manifest.json file or a directory containing one is required here." - ) + raise api.RSConnectException("A manifest.json file or a directory containing one is required here.") return file_or_directory @@ -501,21 +468,11 @@ def deploy_jupyter_notebook( of log lines. The log lines value will be None if a log callback was provided. """ app_store = AppStore(file_name) - ( - app_id, - deployment_name, - deployment_title, - default_title, - app_mode, - ) = gather_basic_deployment_info_for_notebook( + (app_id, deployment_name, deployment_title, default_title, app_mode,) = gather_basic_deployment_info_for_notebook( connect_server, app_store, file_name, new, app_id, title, static ) - python, environment = get_python_env_info( - file_name, python, compatibility_mode, force_generate - ) - bundle = create_notebook_deployment_bundle( - file_name, extra_files, app_mode, python, environment - ) + python, environment = get_python_env_info(file_name, python, compatibility_mode, force_generate) + bundle = create_notebook_deployment_bundle(file_name, extra_files, app_mode, python, environment) return _finalize_deploy( connect_server, app_store, @@ -531,16 +488,7 @@ def deploy_jupyter_notebook( def _finalize_deploy( - connect_server, - app_store, - file_name, - app_id, - app_mode, - name, - title, - title_is_default, - bundle, - log_callback, + connect_server, app_store, file_name, app_id, app_mode, name, title, title_is_default, bundle, log_callback, ): """ A common function to finish up the deploy process once all the data (bundle @@ -565,13 +513,7 @@ def _finalize_deploy( app = deploy_bundle(connect_server, app_id, name, title, title_is_default, bundle) app_url, log_lines = spool_deployment_log(connect_server, app, log_callback) app_store.set( - connect_server.url, - abspath(file_name), - app_url, - app["app_id"], - app["app_guid"], - title, - app_mode, + connect_server.url, abspath(file_name), app_url, app["app_id"], app["app_guid"], title, app_mode, ) return app_url, log_lines @@ -585,9 +527,7 @@ def fake_module_file_from_directory(directory): :return: the directory plus the (potentially) fake module file. """ app_name = abspath(directory) - app_name = ( - dirname(app_name) if app_name.endswith(os.path.sep) else basename(app_name) - ) + app_name = dirname(app_name) if app_name.endswith(os.path.sep) else basename(app_name) return join(directory, app_name + ".py") @@ -746,20 +686,11 @@ def _deploy_by_python_framework( """ module_file = fake_module_file_from_directory(directory) app_store = AppStore(module_file) - ( - entry_point, - app_id, - deployment_name, - deployment_title, - default_title, - app_mode, - ) = gatherer(connect_server, app_store, directory, entry_point, new, app_id, title) - _, environment = get_python_env_info( - directory, python, compatibility_mode, force_generate - ) - bundle = create_api_deployment_bundle( - directory, extra_files, excludes, entry_point, app_mode, environment + (entry_point, app_id, deployment_name, deployment_title, default_title, app_mode,) = gatherer( + connect_server, app_store, directory, entry_point, new, app_id, title ) + _, environment = get_python_env_info(directory, python, compatibility_mode, force_generate) + bundle = create_api_deployment_bundle(directory, extra_files, excludes, entry_point, app_mode, environment) return _finalize_deploy( connect_server, app_store, @@ -775,12 +706,7 @@ def _deploy_by_python_framework( def deploy_by_manifest( - connect_server, - manifest_file_name, - new=False, - app_id=None, - title=None, - log_callback=None, + connect_server, manifest_file_name, new=False, app_id=None, title=None, log_callback=None, ): """ A function to deploy a Jupyter notebook to Connect. Depending on the files involved @@ -807,9 +733,7 @@ def deploy_by_manifest( default_title, app_mode, package_manager, - ) = gather_basic_deployment_info_from_manifest( - connect_server, app_store, manifest_file_name, new, app_id, title - ) + ) = gather_basic_deployment_info_from_manifest(connect_server, app_store, manifest_file_name, new, app_id, title) bundle = make_manifest_bundle(manifest_file_name) return _finalize_deploy( connect_server, @@ -825,9 +749,7 @@ def deploy_by_manifest( ) -def gather_basic_deployment_info_for_notebook( - connect_server, app_store, file_name, new, app_id, title, static -): +def gather_basic_deployment_info_for_notebook(connect_server, app_store, file_name, new, app_id, title, static): """ Helps to gather the necessary info for performing a deployment. @@ -845,9 +767,7 @@ def gather_basic_deployment_info_for_notebook( _validate_title(title) if new and app_id: - raise api.RSConnectException( - "Specify either a new deploy or an app ID but not both." - ) + raise api.RSConnectException("Specify either a new deploy or an app ID but not both.") if app_id is not None: # Don't read app metadata if app-id is specified. Instead, we need @@ -867,8 +787,7 @@ def gather_basic_deployment_info_for_notebook( app_id, app_mode = app_store.resolve(connect_server.url, app_id, app_mode) if static and app_mode != AppModes.STATIC: raise api.RSConnectException( - 'Cannot change app mode to "static" once deployed. ' - "Use --new to create a new deployment." + 'Cannot change app mode to "static" once deployed. ' "Use --new to create a new deployment." ) default_title = not bool(title) @@ -883,9 +802,7 @@ def gather_basic_deployment_info_for_notebook( ) -def gather_basic_deployment_info_from_manifest( - connect_server, app_store, file_name, new, app_id, title -): +def gather_basic_deployment_info_from_manifest(connect_server, app_store, file_name, new, app_id, title): """ Helps to gather the necessary info for performing a deployment. @@ -904,9 +821,7 @@ def gather_basic_deployment_info_from_manifest( _validate_title(title) if new and app_id: - raise api.RSConnectException( - "Specify either a new deploy or an app ID but not both." - ) + raise api.RSConnectException("Specify either a new deploy or an app ID but not both.") source_manifest, _ = read_manifest_file(file_name) # noinspection SpellCheckingInspection @@ -917,9 +832,7 @@ def gather_basic_deployment_info_from_manifest( # Use the saved app information unless overridden by the user. app_id, app_mode = app_store.resolve(connect_server.url, app_id, app_mode) - package_manager = ( - source_manifest.get("python", {}).get("package_manager", {}).get("name", None) - ) + package_manager = source_manifest.get("python", {}).get("package_manager", {}).get("name", None) default_title = not bool(title) title = title or _default_title_from_manifest(source_manifest, file_name) @@ -933,9 +846,7 @@ def gather_basic_deployment_info_from_manifest( ) -def gather_basic_deployment_info_for_api( - connect_server, app_store, directory, entry_point, new, app_id, title -): +def gather_basic_deployment_info_for_api(connect_server, app_store, directory, entry_point, new, app_id, title): """ Helps to gather the necessary info for performing a deployment. @@ -952,20 +863,11 @@ def gather_basic_deployment_info_for_api( :return: the entry point, app ID, name, title and mode for the deployment. """ return _gather_basic_deployment_info_for_framework( - connect_server, - app_store, - directory, - entry_point, - new, - app_id, - AppModes.PYTHON_API, - title, + connect_server, app_store, directory, entry_point, new, app_id, AppModes.PYTHON_API, title, ) -def gather_basic_deployment_info_for_dash( - connect_server, app_store, directory, entry_point, new, app_id, title -): +def gather_basic_deployment_info_for_dash(connect_server, app_store, directory, entry_point, new, app_id, title): """ Helps to gather the necessary info for performing a deployment. @@ -982,14 +884,7 @@ def gather_basic_deployment_info_for_dash( :return: the entry point, app ID, name, title and mode for the deployment. """ return _gather_basic_deployment_info_for_framework( - connect_server, - app_store, - directory, - entry_point, - new, - app_id, - AppModes.DASH_APP, - title, + connect_server, app_store, directory, entry_point, new, app_id, AppModes.DASH_APP, title, ) @@ -1017,9 +912,7 @@ def _gather_basic_deployment_info_for_framework( _validate_title(title) if new and app_id: - raise api.RSConnectException( - "Specify either a new deploy or an app ID but not both." - ) + raise api.RSConnectException("Specify either a new deploy or an app ID but not both.") if app_id is not None: # Don't read app metadata if app-id is specified. Instead, we need @@ -1065,9 +958,7 @@ def get_python_env_info(file_name, python, conda_mode, force_generate): """ 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), conda_mode=conda_mode, force_generate=force_generate) if "error" in environment: raise api.RSConnectException(environment["error"]) logger.debug("Python: %s" % python) @@ -1077,12 +968,7 @@ def get_python_env_info(file_name, python, conda_mode, force_generate): def create_notebook_deployment_bundle( - file_name, - extra_files, - app_mode, - python, - environment, - extra_files_need_validating=True, + file_name, extra_files, app_mode, python, environment, extra_files_need_validating=True, ): """ Create an in-memory bundle, ready to deploy. @@ -1115,13 +1001,7 @@ def create_notebook_deployment_bundle( def create_api_deployment_bundle( - directory, - extra_files, - excludes, - entry_point, - app_mode, - environment, - extra_files_need_validating=True, + directory, extra_files, excludes, entry_point, app_mode, environment, extra_files_need_validating=True, ): """ Create an in-memory bundle, ready to deploy. @@ -1146,9 +1026,7 @@ 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 - ) + return make_api_bundle(directory, entry_point, app_mode, environment, extra_files, excludes) def deploy_bundle(connect_server, app_id, name, title, title_is_default, bundle): @@ -1164,9 +1042,7 @@ def deploy_bundle(connect_server, app_id, name, title, title_is_default, bundle) :return: application information about the deploy. This includes the ID of the task that may be queried for deployment progress. """ - return api.do_bundle_deploy( - connect_server, app_id, name, title, title_is_default, bundle - ) + return api.do_bundle_deploy(connect_server, app_id, name, title, title_is_default, bundle) def spool_deployment_log(connect_server, app, log_callback): @@ -1182,9 +1058,7 @@ def spool_deployment_log(connect_server, app, log_callback): :return: the ultimate URL where the deployed app may be accessed and the sequence of log lines. The log lines value will be None if a log callback was provided. """ - return api.emit_task_log( - connect_server, app["app_id"], app["task_id"], log_callback - ) + return api.emit_task_log(connect_server, app["app_id"], app["task_id"], log_callback) def create_notebook_manifest_and_environment_file( @@ -1206,18 +1080,11 @@ def create_notebook_manifest_and_environment_file( already exists. :return: """ - if ( - not write_notebook_manifest_json( - entry_point_file, environment, app_mode, extra_files - ) - or force - ): + if not write_notebook_manifest_json(entry_point_file, environment, app_mode, extra_files) or force: write_environment_file(environment, dirname(entry_point_file)) -def write_notebook_manifest_json( - entry_point_file, environment, app_mode=None, extra_files=None -): +def write_notebook_manifest_json(entry_point_file, environment, app_mode=None, extra_files=None): """ Creates and writes a manifest.json file for the given entry point file. If the application mode is not provided, an attempt will be made to resolve one @@ -1242,10 +1109,7 @@ def write_notebook_manifest_json( _, extension = splitext(file_name) app_mode = AppModes.get_by_extension(extension, True) if app_mode == AppModes.UNKNOWN: - raise api.RSConnectException( - 'Could not determine the app mode from "%s"; please specify one.' - % extension - ) + raise api.RSConnectException('Could not determine the app mode from "%s"; please specify one.' % extension) manifest_data = make_source_manifest(file_name, environment, app_mode) manifest_add_file(manifest_data, file_name, directory) @@ -1261,13 +1125,7 @@ def write_notebook_manifest_json( def create_api_manifest_and_environment_file( - directory, - entry_point, - environment, - app_mode=AppModes.PYTHON_API, - extra_files=None, - excludes=None, - force=True, + directory, entry_point, environment, app_mode=AppModes.PYTHON_API, extra_files=None, excludes=None, force=True, ): """ Creates and writes a manifest.json file for the given Python API entry point. If @@ -1285,22 +1143,12 @@ def create_api_manifest_and_environment_file( already exists. :return: """ - if ( - not write_api_manifest_json( - directory, entry_point, environment, app_mode, extra_files, excludes - ) - or force - ): + if not write_api_manifest_json(directory, entry_point, environment, app_mode, extra_files, excludes) or force: write_environment_file(environment, directory) def write_api_manifest_json( - directory, - entry_point, - environment, - app_mode=AppModes.PYTHON_API, - extra_files=None, - excludes=None, + directory, entry_point, environment, app_mode=AppModes.PYTHON_API, extra_files=None, excludes=None, ): """ Creates and writes a manifest.json file for the given entry point file. If @@ -1318,9 +1166,7 @@ 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 - ) + manifest, _ = make_api_manifest(directory, entry_point, app_mode, environment, extra_files, excludes) manifest_path = join(directory, "manifest.json") with open(manifest_path, "w") as f: diff --git a/rsconnect/api.py b/rsconnect/api.py index f562b7b0..3d74b450 100644 --- a/rsconnect/api.py +++ b/rsconnect/api.py @@ -34,10 +34,7 @@ def __init__(self, url, api_key, insecure=False, ca_data=None): def handle_bad_response(self, response): if isinstance(response, HTTPResponse): if response.exception: - raise RSConnectException( - "Exception trying to connect to %s - %s" - % (self.url, response.exception) - ) + raise RSConnectException("Exception trying to connect to %s - %s" % (self.url, response.exception)) # Sometimes an ISP will respond to an unknown server name by returning a friendly # search page so trap that since we know we're expecting JSON from Connect. This # also catches all error conditions which we will report as "not running Connect". @@ -47,14 +44,10 @@ def handle_bad_response(self, response): if code in _error_map: error = _error_map[code] else: - error = ( - "The Connect server reported an error: %s" - % response.json_data["error"] - ) + error = "The Connect server reported an error: %s" % response.json_data["error"] raise RSConnectException(error) raise RSConnectException( - "Received and unexpected response from RStudio Connect: %s %s" - % (response.status, response.reason) + "Received and unexpected response from RStudio Connect: %s %s" % (response.status, response.reason) ) @@ -63,11 +56,7 @@ def __init__(self, server, cookies=None, timeout=30): if cookies is None: cookies = server.cookie_jar super(RSConnect, self).__init__( - append_to_path(server.url, "__api__"), - server.insecure, - server.ca_data, - cookies, - timeout, + append_to_path(server.url, "__api__"), server.insecure, server.ca_data, cookies, timeout, ) self._server = server @@ -75,11 +64,7 @@ def __init__(self, server, cookies=None, timeout=30): self.key_authorization(server.api_key) def _tweak_response(self, response): - return ( - response.json_data - if response.status and response.status == 200 and response.json_data - else response - ) + return response.json_data if response.status and response.status == 200 and response.json_data else response def me(self): return self.get("me") @@ -109,10 +94,7 @@ def app_deploy(self, app_id, bundle_id=None): return self.post("applications/%s/deploy" % app_id, body={"bundle": bundle_id}) def app_publish(self, app_id, access): - return self.post( - "applications/%s" % app_id, - body={"access_type": access, "id": app_id, "needs_config": False}, - ) + return self.post("applications/%s" % app_id, body={"access_type": access, "id": app_id, "needs_config": False},) def app_config(self, app_id): return self.get("applications/%s/config" % app_id) @@ -138,9 +120,7 @@ def deploy(self, app_id, app_name, app_title, title_is_default, tarball): self._server.handle_bad_response(app) if app["title"] != app_title and not title_is_default: - self._server.handle_bad_response( - self.app_update(app_id, {"title": app_title}) - ) + self._server.handle_bad_response(self.app_update(app_id, {"title": app_title})) app["title"] = app_title app_bundle = self.app_upload(app_id, tarball) @@ -223,9 +203,7 @@ def verify_server(connect_server): connect_server.handle_bad_response(result) return result except SSLError as ssl_error: - raise RSConnectException( - "There is an SSL/TLS configuration problem: %s" % ssl_error - ) + raise RSConnectException("There is an SSL/TLS configuration problem: %s" % ssl_error) def verify_api_key(connect_server): @@ -239,15 +217,9 @@ def verify_api_key(connect_server): with RSConnect(connect_server) as client: result = client.me() if isinstance(result, HTTPResponse): - if ( - result.json_data - and "code" in result.json_data - and result.json_data["code"] == 30 - ): + if result.json_data and "code" in result.json_data and result.json_data["code"] == 30: raise RSConnectException("The specified API key is not valid.") - raise RSConnectException( - "Could not verify the API key: %s %s" % (result.status, result.reason) - ) + raise RSConnectException("Could not verify the API key: %s %s" % (result.status, result.reason)) return result["username"] @@ -334,9 +306,7 @@ def emit_task_log(connect_server, app_id, task_id, log_callback, timeout=None): return result -def retrieve_matching_apps( - connect_server, filters=None, limit=None, mapping_function=None -): +def retrieve_matching_apps(connect_server, filters=None, limit=None, mapping_function=None): """ Retrieves all the app names that start with the given default name. The main point for this function is that it handles all the necessary paging logic. @@ -467,9 +437,7 @@ def mapping_filter(client, app): if mode in (AppModes.STATIC, AppModes.JUPYTER_NOTEBOOK): apps.append(map_app(app, get_app_config(connect_server, app_id))) except RSConnectException: - logger.debug( - 'Error getting info for previous app_id "%s", skipping.', app_id - ) + logger.debug('Error getting info for previous app_id "%s", skipping.', app_id) return apps @@ -484,9 +452,7 @@ def find_unique_name(connect_server, name): :return: the name, potentially with a suffixed number to guarantee uniqueness. """ existing_names = retrieve_matching_apps( - connect_server, - filters={"search": name}, - mapping_function=lambda client, app: app["name"], + connect_server, filters={"search": name}, mapping_function=lambda client, app: app["name"], ) if name in existing_names: diff --git a/rsconnect/bundle.py b/rsconnect/bundle.py index cd1cc7bb..c67fe0eb 100644 --- a/rsconnect/bundle.py +++ b/rsconnect/bundle.py @@ -333,9 +333,7 @@ def create_glob_set(directory, excludes): return GlobSet(work) -def _create_api_file_list( - directory, requirements_file_name, extra_files=None, excludes=None -): +def _create_api_file_list(directory, requirements_file_name, extra_files=None, excludes=None): """ Builds a full list of files under the given directory that should be included in a manifest or bundle. Extra files and excludes are relative to the given @@ -366,9 +364,7 @@ def _create_api_file_list( abs_path = os.path.join(subdir, file) rel_path = os.path.relpath(abs_path, directory) - if keep_manifest_specified_file(rel_path) and ( - rel_path in extra_files or not glob_set.matches(abs_path) - ): + if keep_manifest_specified_file(rel_path) and (rel_path in extra_files or not glob_set.matches(abs_path)): file_list.append(rel_path) # Don't add extra files more than once. if rel_path in extra_files: @@ -380,9 +376,7 @@ def _create_api_file_list( return sorted(file_list) -def make_api_manifest( - directory, entry_point, app_mode, environment, extra_files=None, excludes=None -): +def make_api_manifest(directory, entry_point, app_mode, environment, extra_files=None, excludes=None): """ Makes a manifest for an API. @@ -394,9 +388,7 @@ def make_api_manifest( :param excludes: a sequence of glob patterns that will exclude matched files. :return: the manifest and a list of the files involved. """ - relevant_files = _create_api_file_list( - directory, environment["filename"], extra_files, excludes - ) + relevant_files = _create_api_file_list(directory, environment["filename"], extra_files, excludes) manifest = make_source_manifest(entry_point, environment, app_mode) manifest_add_buffer(manifest, environment["filename"], environment["contents"]) @@ -407,9 +399,7 @@ def make_api_manifest( return manifest, relevant_files -def make_api_bundle( - directory, entry_point, app_mode, environment, extra_files=None, excludes=None -): +def make_api_bundle(directory, entry_point, app_mode, environment, extra_files=None, excludes=None): """ Create an API bundle, given a directory path and a manifest. @@ -421,9 +411,7 @@ def make_api_bundle( :param excludes: a sequence of glob patterns that will exclude matched files. :return: a file-like object containing the bundle tarball. """ - manifest, relevant_files = make_api_manifest( - directory, entry_point, app_mode, environment, extra_files, excludes - ) + manifest, relevant_files = make_api_manifest(directory, entry_point, app_mode, environment, extra_files, excludes) bundle_file = tempfile.TemporaryFile(prefix="rsc_bundle") with tarfile.open(mode="w:gz", fileobj=bundle_file) as bundle: diff --git a/rsconnect/environment.py b/rsconnect/environment.py index 6e3a5b23..4935c4ff 100644 --- a/rsconnect/environment.py +++ b/rsconnect/environment.py @@ -35,9 +35,7 @@ def detect_environment(dirname, force_generate=False, conda_mode=False, conda=No if force_generate: result = conda_env_export(conda) else: - result = output_file( - dirname, "environment.yml", "conda" - ) or conda_env_export(conda) + result = output_file(dirname, "environment.yml", "conda") or conda_env_export(conda) else: if force_generate: result = pip_freeze() @@ -89,19 +87,15 @@ def get_python_version(data): def get_conda_version(conda): try: args = [conda, "-V"] - proc = subprocess.Popen( - args, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True, - ) + 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) + 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: @@ -116,26 +110,16 @@ def get_default_locale(locale_source=locale.getdefaultlocale): def get_version(module): try: args = [sys.executable, "-m", module, "--version"] - proc = subprocess.Popen( - args, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True, - ) + proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True,) stdout, stderr = proc.communicate() match = version_re.search(stdout) if match: return match.group() - msg = "Failed to get version of '%s' from the output of: %s" % ( - module, - " ".join(args), - ) + msg = "Failed to get version of '%s' from the output of: %s" % (module, " ".join(args),) raise EnvironmentException(msg) except Exception as exception: - raise EnvironmentException( - "Error getting '%s' version: %s" % (module, str(exception)) - ) + raise EnvironmentException("Error getting '%s' version: %s" % (module, str(exception))) def output_file(dirname, filename, package_manager): @@ -189,15 +173,10 @@ def pip_freeze(): msg = pip_stderr or ("exited with code %d" % pip_status) raise EnvironmentException("Error during pip freeze: %s" % msg) - pip_stdout = "\n".join( - [line for line in pip_stdout.split("\n") if "rsconnect" not in line] - ) + pip_stdout = "\n".join([line for line in pip_stdout.split("\n") if "rsconnect" not in line]) pip_stdout = ( - "# requirements.txt generated by rsconnect-python on " - + str(datetime.datetime.utcnow()) - + "\n" - + pip_stdout + "# requirements.txt generated by rsconnect-python on " + str(datetime.datetime.utcnow()) + "\n" + pip_stdout ) return { @@ -215,10 +194,7 @@ def conda_env_export(conda): """ try: proc = subprocess.Popen( - [conda, "env", "export"], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True, + [conda, "env", "export"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, ) conda_stdout, conda_stderr = proc.communicate() conda_status = proc.returncode diff --git a/rsconnect/http_support.py b/rsconnect/http_support.py index b8a18623..7fd98147 100644 --- a/rsconnect/http_support.py +++ b/rsconnect/http_support.py @@ -30,9 +30,7 @@ def _create_plain_connection(host_name, port, disable_tls_check, ca_data, timeou :param timeout: the timeout value to use for socket operations. :return: a plain HTTP connection. """ - return http.HTTPConnection( - host_name, port=(port or http.HTTP_PORT), timeout=timeout - ) + return http.HTTPConnection(host_name, port=(port or http.HTTP_PORT), timeout=timeout) # noinspection PyUnresolvedReferences @@ -48,9 +46,7 @@ def _create_ssl_connection(host_name, port, disable_tls_check, ca_data, timeout) :return: a TLS HTTPS connection. """ if ca_data is not None and disable_tls_check: - raise ValueError( - "Cannot both disable TLS checking and provide a custom certificate" - ) + raise ValueError("Cannot both disable TLS checking and provide a custom certificate") if ca_data is not None: return http.HTTPSConnection( host_name, @@ -61,15 +57,10 @@ def _create_ssl_connection(host_name, port, disable_tls_check, ca_data, timeout) elif disable_tls_check: # noinspection PyProtectedMember return http.HTTPSConnection( - host_name, - port=(port or http.HTTPS_PORT), - timeout=timeout, - context=ssl._create_unverified_context(), + host_name, port=(port or http.HTTPS_PORT), timeout=timeout, context=ssl._create_unverified_context(), ) else: - return http.HTTPSConnection( - host_name, port=(port or http.HTTPS_PORT), timeout=timeout - ) + return http.HTTPSConnection(host_name, port=(port or http.HTTPS_PORT), timeout=timeout) def append_to_path(uri, path): @@ -117,11 +108,7 @@ def __init__(self, full_uri, response=None, body=None, exception=None): self.reason = response.reason self.content_type = response.getheader("Content-Type") - if ( - self.content_type - and self.content_type.startswith("application/json") - and len(self.response_body) > 0 - ): + if self.content_type and self.content_type.startswith("application/json") and len(self.response_body) > 0: self.json_data = json.loads(self.response_body) @@ -131,9 +118,7 @@ class HTTPServer(object): server. """ - def __init__( - self, url, disable_tls_check=False, ca_data=None, cookies=None, timeout=30 - ): + def __init__(self, url, disable_tls_check=False, ca_data=None, cookies=None, timeout=30): """ Constructs an HTTPServer object. @@ -172,13 +157,7 @@ def _get_full_path(self, path): def __enter__(self): factory = _connection_factory[self._url.scheme] - self._conn = factory( - self._url.hostname, - self._url.port, - self._disable_tls_check, - self._ca_data, - self._timeout, - ) + self._conn = factory(self._url.hostname, self._url.port, self._disable_tls_check, self._ca_data, self._timeout,) return self def __exit__(self, *args): @@ -198,13 +177,9 @@ def request(self, method, path, query_params=None, body=None, maximum_redirects= if isinstance(body, dict): body = json.dumps(body).encode("utf-8") extra_headers = {"Content-Type": "application/json; charset=utf-8"} - return self._do_request( - method, path, query_params, body, maximum_redirects, extra_headers - ) + return self._do_request(method, path, query_params, body, maximum_redirects, extra_headers) - def _do_request( - self, method, path, query_params, body, maximum_redirects, extra_headers=None - ): + def _do_request(self, method, path, query_params, body, maximum_redirects, extra_headers=None): full_uri = path if query_params is not None: full_uri = "%s?%s" % (path, urlencode(query_params)) @@ -252,20 +227,11 @@ def _do_request( logger.debug("--> Redirected to: %s" % next_url) - return self._do_request( - method, - next_url, - query_params, - body, - maximum_redirects - 1, - extra_headers, - ) + return self._do_request(method, next_url, query_params, body, maximum_redirects - 1, extra_headers,) self._handle_set_cookie(response) - return self._tweak_response( - HTTPResponse(full_uri, response=response, body=response_body) - ) + return self._tweak_response(HTTPResponse(full_uri, response=response, body=response_body)) except ( http.HTTPException, ssl.CertificateError, @@ -276,9 +242,7 @@ def _do_request( socket.gaierror, socket.timeout, ) as exception: - logger.debug( - "An exception occurred processing the HTTP request.", exc_info=True - ) + logger.debug("An exception occurred processing the HTTP request.", exc_info=True) return HTTPResponse(full_uri, exception=exception) # noinspection PyMethodMayBeStatic @@ -332,12 +296,7 @@ def store_cookies(self, response): logger.debug("CookieJar contents: %s\n%s" % (self._keys, self._content)) def get_cookie_header_value(self): - result = "; ".join( - [ - "%s=%s" % (key, self._reference.value_encode(self._content[key])[1]) - for key in self._keys - ] - ) + result = "; ".join(["%s=%s" % (key, self._reference.value_encode(self._content[key])[1]) for key in self._keys]) logger.debug("Cookie: %s" % result) return result diff --git a/rsconnect/main.py b/rsconnect/main.py index 80afff30..16a1a3f0 100644 --- a/rsconnect/main.py +++ b/rsconnect/main.py @@ -47,9 +47,7 @@ @click.group(no_args_is_help=True) -@click.option( - "--future", "-u", is_flag=True, hidden=True, help="Enables future functionality." -) +@click.option("--future", "-u", is_flag=True, hidden=True, help="Enables future functionality.") def cli(future): """ This command line tool may be used to deploy Jupyter notebooks to RStudio @@ -90,9 +88,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, None, insecure, ca_data)) real_server.api_key = api_key @@ -110,24 +106,13 @@ def _test_server_and_api(server, api_key, insecure, ca_cert): "Specifying an existing nickname will cause its stored information to be replaced by what is given " "on the command line.", ) +@click.option("--name", "-n", required=True, help="The nickname to associate with the server.") +@click.option("--server", "-s", required=True, help="The URL for the RStudio Connect server.") @click.option( - "--name", "-n", required=True, help="The nickname to associate with the server." -) -@click.option( - "--server", "-s", required=True, help="The URL for the RStudio Connect server." -) -@click.option( - "--api-key", - "-k", - required=True, - help="The API key to use to authenticate with RStudio Connect.", -) -@click.option( - "--insecure", "-i", is_flag=True, help="Disable TLS certification/host validation." -) -@click.option( - "--cacert", "-c", type=click.File(), help="The path to trusted TLS CA certificates." + "--api-key", "-k", required=True, help="The API key to use to authenticate with RStudio Connect.", ) +@click.option("--insecure", "-i", is_flag=True, help="Disable TLS certification/host validation.") +@click.option("--cacert", "-c", type=click.File(), help="The path to trusted TLS CA certificates.") @click.option("--verbose", "-v", is_flag=True, help="Print detailed messages.") def add(name, server, api_key, insecure, cacert, verbose): set_verbosity(verbose) @@ -138,11 +123,7 @@ def add(name, server, api_key, insecure, cacert, verbose): real_server, _ = _test_server_and_api(server, api_key, insecure, cacert) server_store.set( - name, - real_server.url, - real_server.api_key, - real_server.insecure, - real_server.ca_data, + name, real_server.url, real_server.api_key, real_server.insecure, real_server.ca_data, ) if old_server: @@ -165,9 +146,7 @@ def list_servers(verbose): click.echo("Server information from %s" % server_store.get_path()) if not servers: - click.echo( - "No servers are saved. To add a server, see `rsconnect add --help`." - ) + click.echo("No servers are saved. To add a server, see `rsconnect add --help`.") else: click.echo() for server in servers: @@ -175,9 +154,7 @@ def list_servers(verbose): click.echo(" URL: %s" % server["url"]) click.echo(" API key is saved") if server["insecure"]: - click.echo( - " Insecure mode (TLS host/certificate validation disabled)" - ) + click.echo(" Insecure mode (TLS host/certificate validation disabled)") if server["ca_cert"]: click.echo(" Client TLS certificate data provided") click.echo() @@ -192,28 +169,16 @@ def list_servers(verbose): "information stored as a nickname is still valid.", ) @click.option( - "--name", - "-n", - help="The nickname of the RStudio Connect server to get details for.", + "--name", "-n", help="The nickname of the RStudio Connect server to get details for.", ) @click.option( - "--server", - "-s", - envvar="CONNECT_SERVER", - help="The URL for the RStudio Connect server to get details for.", + "--server", "-s", envvar="CONNECT_SERVER", help="The URL for the RStudio Connect server to get details for.", ) @click.option( - "--api-key", - "-k", - envvar="CONNECT_API_KEY", - help="The API key to use to authenticate with RStudio Connect.", + "--api-key", "-k", envvar="CONNECT_API_KEY", help="The API key to use to authenticate with RStudio Connect.", ) @click.option( - "--insecure", - "-i", - envvar="CONNECT_INSECURE", - is_flag=True, - help="Disable TLS certification/host validation.", + "--insecure", "-i", envvar="CONNECT_INSECURE", is_flag=True, help="Disable TLS certification/host validation.", ) @click.option( "--cacert", @@ -227,9 +192,7 @@ def details(name, server, api_key, insecure, cacert, verbose): set_verbosity(verbose) with cli_feedback("Checking arguments"): - connect_server = _validate_deploy_to_args( - name, server, api_key, insecure, cacert, api_key_is_required=False - ) + connect_server = _validate_deploy_to_args(name, server, api_key, insecure, cacert, api_key_is_required=False) click.echo(" RStudio Connect URL: %s" % connect_server.url) @@ -244,10 +207,7 @@ def details(name, server, api_key, insecure, cacert, verbose): python_versions = server_details["python"]["versions"] conda_details = server_details["conda"] - click.echo( - " RStudio Connect version: %s" - % ("" if len(connect_version) == 0 else connect_version) - ) + click.echo(" RStudio Connect version: %s" % ("" if len(connect_version) == 0 else connect_version)) if len(python_versions) == 0: click.echo(" No versions of Python are installed.") @@ -259,9 +219,7 @@ 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 ") - ) + click.echo(" Conda: %ssupported" % ("" if conda_details["supported"] else "not ")) @cli.command( @@ -269,9 +227,7 @@ def details(name, server, api_key, insecure, cacert, verbose): help="Remove the information about an RStudio Connect server by nickname or URL. " "One of --name or --server is required.", ) -@click.option( - "--name", "-n", help="The nickname of the RStudio Connect server to remove." -) +@click.option("--name", "-n", help="The nickname of the RStudio Connect server to remove.") @click.option("--server", "-s", help="The URL of the RStudio Connect server to remove.") @click.option("--verbose", "-v", is_flag=True, help="Print detailed messages.") def remove(name, server, verbose): @@ -281,14 +237,10 @@ def remove(name, server, verbose): with cli_feedback("Checking arguments"): if name and server: - raise api.RSConnectException( - "You must specify only one of -n/--name or -s/--server." - ) + raise api.RSConnectException("You must specify only one of -n/--name or -s/--server.") if not (name or server): - raise api.RSConnectException( - "You must specify one of -n/--name or -s/--server." - ) + raise api.RSConnectException("You must specify one of -n/--name or -s/--server.") if name: if server_store.remove_by_name(name): @@ -339,9 +291,7 @@ def info(file): break if len(deployments) > 0: - click.echo( - "Loaded deployment information from %s" % abspath(app_store.get_path()) - ) + click.echo("Loaded deployment information from %s" % abspath(app_store.get_path())) for deployment in deployments: # If this deployment was via a manifest, this will get us extra stuff about that. @@ -349,10 +299,7 @@ def info(file): entry_point, primary_document = describe_manifest(file_name) label = "Directory:" if isdir(file_name) else "Filename: " click.echo() - click.echo( - "Server URL: %s" - % click.style(deployment.get("server_url"), fg="white") - ) + click.echo("Server URL: %s" % click.style(deployment.get("server_url"), fg="white")) click.echo(" App URL: %s" % deployment.get("app_url")) click.echo(" App ID: %s" % deployment.get("app_id")) click.echo(" App GUID: %s" % deployment.get("app_guid")) @@ -362,10 +309,7 @@ def info(file): click.echo(" Entry point: %s" % entry_point) if primary_document: click.echo(" Primary doc: %s" % primary_document) - click.echo( - " Type: %s" - % AppModes.get_by_name(deployment.get("app_mode"), True).desc() - ) + click.echo(" Type: %s" % AppModes.get_by_name(deployment.get("app_mode"), True).desc()) else: click.echo("No saved deployment information was found for %s." % file) @@ -375,9 +319,7 @@ def deploy(): pass -def _validate_deploy_to_args( - name, url, api_key, insecure, ca_cert, api_key_is_required=True -): +def _validate_deploy_to_args(name, url, api_key, insecure, ca_cert, api_key_is_required=True): """ Validate that the user gave us enough information to talk to a Connect server. @@ -393,20 +335,14 @@ def _validate_deploy_to_args( ca_data = ca_cert and text_type(ca_cert.read()) if name and url: - raise api.RSConnectException( - "You must specify only one of -n/--name or -s/--server, not both." - ) + raise api.RSConnectException("You must specify only one of -n/--name or -s/--server, not both.") - real_server, api_key, insecure, ca_data, from_store = server_store.resolve( - name, url, api_key, insecure, ca_data - ) + real_server, api_key, insecure, ca_data, from_store = server_store.resolve(name, url, api_key, insecure, ca_data) # This can happen if the user specifies neither --name or --server and there's not # a single default to go with. if not real_server: - raise api.RSConnectException( - "You must specify one of -n/--name or -s/--server." - ) + raise api.RSConnectException("You must specify one of -n/--name or -s/--server.") connect_server = api.RSConnectServer(real_server, None, insecure, ca_data) @@ -418,9 +354,7 @@ def _validate_deploy_to_args( if not connect_server.api_key: if api_key_is_required: - raise api.RSConnectException( - 'An API key must be specified for "%s".' % connect_server.url - ) + raise api.RSConnectException('An API key must be specified for "%s".' % connect_server.url) return connect_server # If our info came from the command line, make sure the key really works. @@ -439,8 +373,7 @@ def _warn_on_ignored_manifest(directory): """ if exists(join(directory, "manifest.json")): click.secho( - " Warning: the existing manifest.json file will not be used or considered.", - fg="yellow", + " Warning: the existing manifest.json file will not be used or considered.", fg="yellow", ) @@ -452,11 +385,7 @@ def _warn_on_ignored_conda_env(environment): :param environment: The Python environment that was discovered. """ - if ( - future_enabled - and environment["package_manager"] != "conda" - and "conda" in environment - ): + if future_enabled and environment["package_manager"] != "conda" and "conda" in environment: click.echo( " Using %s for package management; the current Conda environment will be ignored." % environment["package_manager"] @@ -473,22 +402,12 @@ def _warn_on_ignored_requirements(directory, requirements_file_name): """ if exists(join(directory, requirements_file_name)): click.secho( - " Warning: the existing %s file will not be used or considered." - % requirements_file_name, - fg="yellow", + " Warning: the existing %s file will not be used or considered." % requirements_file_name, fg="yellow", ) def _deploy_bundle( - connect_server, - app_store, - primary_path, - app_id, - app_mode, - name, - title, - title_is_default, - bundle, + connect_server, app_store, primary_path, app_id, app_mode, name, title, title_is_default, bundle, ): """ Does the work of uploading a prepared bundle. @@ -504,19 +423,11 @@ def _deploy_bundle( :param bundle: the bundle to deploy. """ with cli_feedback("Uploading bundle"): - app = deploy_bundle( - connect_server, app_id, name, title, title_is_default, bundle - ) + app = deploy_bundle(connect_server, app_id, name, title, title_is_default, bundle) with cli_feedback("Saving deployment data"): app_store.set( - connect_server.url, - abspath(primary_path), - app["app_url"], - app["app_id"], - app["app_guid"], - title, - app_mode, + connect_server.url, abspath(primary_path), app["app_url"], app["app_id"], app["app_guid"], title, app_mode, ) with cli_feedback(""): @@ -529,13 +440,7 @@ def _deploy_bundle( # save the config URL, replacing the old app URL we got during deployment # (which is the Open Solo URL). app_store.set( - connect_server.url, - abspath(primary_path), - app_url, - app["app_id"], - app["app_guid"], - app["title"], - app_mode, + connect_server.url, abspath(primary_path), app_url, app["app_id"], app["app_guid"], app["title"], app_mode, ) @@ -547,27 +452,15 @@ def _deploy_bundle( "page. If the notebook is deployed as a static HTML page (--static), it cannot be scheduled or " "rerun on the Connect server.", ) +@click.option("--name", "-n", help="The nickname of the RStudio Connect server to deploy to.") @click.option( - "--name", "-n", help="The nickname of the RStudio Connect server to deploy to." + "--server", "-s", envvar="CONNECT_SERVER", help="The URL for the RStudio Connect server to deploy to.", ) @click.option( - "--server", - "-s", - envvar="CONNECT_SERVER", - help="The URL for the RStudio Connect server to deploy to.", + "--api-key", "-k", envvar="CONNECT_API_KEY", help="The API key to use to authenticate with RStudio Connect.", ) @click.option( - "--api-key", - "-k", - envvar="CONNECT_API_KEY", - help="The API key to use to authenticate with RStudio Connect.", -) -@click.option( - "--insecure", - "-i", - envvar="CONNECT_INSECURE", - is_flag=True, - help="Disable TLS certification/host validation.", + "--insecure", "-i", envvar="CONNECT_INSECURE", is_flag=True, help="Disable TLS certification/host validation.", ) @click.option( "--cacert", @@ -596,13 +489,9 @@ def _deploy_bundle( ), ) @click.option( - "--app-id", - "-a", - help="Existing app ID or GUID to replace. Cannot be used with --new.", -) -@click.option( - "--title", "-t", help="Title of the content (default is the same as the filename)." + "--app-id", "-a", help="Existing app ID or GUID to replace. Cannot be used with --new.", ) +@click.option("--title", "-t", help="Title of the content (default is the same as the filename).") @click.option( "--python", "-p", @@ -611,24 +500,15 @@ def _deploy_bundle( "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)", + "--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", - is_flag=True, - help='Force generating "requirements.txt", even if it already exists.', + "--force-generate", "-g", is_flag=True, help='Force generating "requirements.txt", even if it already exists.', ) @click.option("--verbose", "-v", is_flag=True, help="Print detailed messages.") @click.argument("file", type=click.Path(exists=True, dir_okay=False, file_okay=True)) @click.argument( - "extra_files", - nargs=-1, - type=click.Path(exists=True, dir_okay=False, file_okay=True), + "extra_files", nargs=-1, type=click.Path(exists=True, dir_okay=False, file_okay=True), ) def deploy_notebook( name, @@ -651,23 +531,13 @@ def deploy_notebook( with cli_feedback("Checking arguments"): app_store = AppStore(file) - connect_server = _validate_deploy_to_args( - name, server, api_key, insecure, cacert - ) + connect_server = _validate_deploy_to_args(name, server, api_key, insecure, cacert) extra_files = validate_extra_files(dirname(file), extra_files) - ( - app_id, - deployment_name, - title, - default_title, - app_mode, - ) = gather_basic_deployment_info_for_notebook( + (app_id, deployment_name, title, default_title, app_mode,) = gather_basic_deployment_info_for_notebook( connect_server, app_store, file, new, app_id, title, static ) - click.secho( - ' Deploying %s to server "%s"' % (file, connect_server.url), fg="white" - ) + click.secho(' Deploying %s to server "%s"' % (file, connect_server.url), fg="white") _warn_on_ignored_manifest(dirname(file)) @@ -684,20 +554,10 @@ def deploy_notebook( _warn_on_ignored_requirements(dirname(file), environment["filename"]) with cli_feedback("Creating deployment bundle"): - bundle = create_notebook_deployment_bundle( - file, extra_files, app_mode, python, environment, False - ) + bundle = create_notebook_deployment_bundle(file, extra_files, app_mode, python, environment, False) _deploy_bundle( - connect_server, - app_store, - file, - app_id, - app_mode, - deployment_name, - title, - default_title, - bundle, + connect_server, app_store, file, app_id, app_mode, deployment_name, title, default_title, bundle, ) @@ -711,27 +571,15 @@ def deploy_notebook( 'refer to a directory that contains a file named "manifest.json".' ), ) +@click.option("--name", "-n", help="The nickname of the RStudio Connect server to deploy to.") @click.option( - "--name", "-n", help="The nickname of the RStudio Connect server to deploy to." + "--server", "-s", envvar="CONNECT_SERVER", help="The URL for the RStudio Connect server to deploy to.", ) @click.option( - "--server", - "-s", - envvar="CONNECT_SERVER", - help="The URL for the RStudio Connect server to deploy to.", + "--api-key", "-k", envvar="CONNECT_API_KEY", help="The API key to use to authenticate with RStudio Connect.", ) @click.option( - "--api-key", - "-k", - envvar="CONNECT_API_KEY", - help="The API key to use to authenticate with RStudio Connect.", -) -@click.option( - "--insecure", - "-i", - envvar="CONNECT_INSECURE", - is_flag=True, - help="Disable TLS certification/host validation.", + "--insecure", "-i", envvar="CONNECT_INSECURE", is_flag=True, help="Disable TLS certification/host validation.", ) @click.option( "--cacert", @@ -750,24 +598,16 @@ def deploy_notebook( ), ) @click.option( - "--app-id", - "-a", - help="Existing app ID or GUID to replace. Cannot be used with --new.", -) -@click.option( - "--title", "-t", help="Title of the content (default is the same as the filename)." + "--app-id", "-a", help="Existing app ID or GUID to replace. Cannot be used with --new.", ) +@click.option("--title", "-t", help="Title of the content (default is the same as the filename).") @click.option("--verbose", "-v", is_flag=True, help="Print detailed messages.") @click.argument("file", type=click.Path(exists=True, dir_okay=True, file_okay=True)) -def deploy_manifest( - name, server, api_key, insecure, cacert, new, app_id, title, verbose, file -): +def deploy_manifest(name, server, api_key, insecure, cacert, new, app_id, title, verbose, file): set_verbosity(verbose) with cli_feedback("Checking arguments"): - connect_server = _validate_deploy_to_args( - name, server, api_key, insecure, cacert - ) + connect_server = _validate_deploy_to_args(name, server, api_key, insecure, cacert) file = validate_manifest_file(file) app_store = AppStore(file) @@ -778,13 +618,9 @@ def deploy_manifest( default_title, app_mode, package_manager, - ) = gather_basic_deployment_info_from_manifest( - connect_server, app_store, file, new, app_id, title - ) + ) = gather_basic_deployment_info_from_manifest(connect_server, app_store, file, new, app_id, title) - click.secho( - ' Deploying %s to server "%s"' % (file, connect_server.url), fg="white" - ) + click.secho(' Deploying %s to server "%s"' % (file, connect_server.url), fg="white") if package_manager == "conda": with cli_feedback("Ensuring Conda is supported"): @@ -794,10 +630,7 @@ def deploy_manifest( try: bundle = make_manifest_bundle(file) except IOError as error: - msg = "Unable to include the file %s in the bundle: %s" % ( - error.filename, - error.args[1], - ) + msg = "Unable to include the file %s in the bundle: %s" % (error.filename, error.args[1],) if error.args[0] == errno.ENOENT: msg = "\n".join( [ @@ -811,15 +644,7 @@ def deploy_manifest( raise api.RSConnectException(msg) _deploy_bundle( - connect_server, - app_store, - file, - app_id, - app_mode, - deployment_name, - title, - default_title, - bundle, + connect_server, app_store, file, app_id, app_mode, deployment_name, title, default_title, bundle, ) @@ -832,27 +657,15 @@ def deploy_manifest( "argument must refer to an existing directory that contains the API code." ), ) +@click.option("--name", "-n", help="The nickname of the RStudio Connect server to deploy to.") @click.option( - "--name", "-n", help="The nickname of the RStudio Connect server to deploy to." -) -@click.option( - "--server", - "-s", - envvar="CONNECT_SERVER", - help="The URL for the RStudio Connect server to deploy to.", + "--server", "-s", envvar="CONNECT_SERVER", help="The URL for the RStudio Connect server to deploy to.", ) @click.option( - "--api-key", - "-k", - envvar="CONNECT_API_KEY", - help="The API key to use to authenticate with RStudio Connect.", + "--api-key", "-k", envvar="CONNECT_API_KEY", help="The API key to use to authenticate with RStudio Connect.", ) @click.option( - "--insecure", - "-i", - envvar="CONNECT_INSECURE", - is_flag=True, - help="Disable TLS certification/host validation.", + "--insecure", "-i", envvar="CONNECT_INSECURE", is_flag=True, help="Disable TLS certification/host validation.", ) @click.option( "--cacert", @@ -888,13 +701,9 @@ def deploy_manifest( ), ) @click.option( - "--app-id", - "-a", - help="Existing app ID or GUID to replace. Cannot be used with --new.", -) -@click.option( - "--title", "-t", help="Title of the content (default is the same as the directory)." + "--app-id", "-a", help="Existing app ID or GUID to replace. Cannot be used with --new.", ) +@click.option("--title", "-t", help="Title of the content (default is the same as the directory).") @click.option( "--python", "-p", @@ -903,26 +712,15 @@ def deploy_manifest( "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)", + "--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", - is_flag=True, - help='Force generating "requirements.txt", even if it already exists.', + "--force-generate", "-g", is_flag=True, help='Force generating "requirements.txt", even if it already exists.', ) @click.option("--verbose", "-v", is_flag=True, help="Print detailed messages.") +@click.argument("directory", type=click.Path(exists=True, dir_okay=True, file_okay=False)) @click.argument( - "directory", type=click.Path(exists=True, dir_okay=True, file_okay=False) -) -@click.argument( - "extra_files", - nargs=-1, - type=click.Path(exists=True, dir_okay=False, file_okay=True), + "extra_files", nargs=-1, type=click.Path(exists=True, dir_okay=False, file_okay=True), ) def deploy_api( name, @@ -972,27 +770,15 @@ def deploy_api( "argument must refer to an existing directory that contains the API code." ), ) +@click.option("--name", "-n", help="The nickname of the RStudio Connect server to deploy to.") @click.option( - "--name", "-n", help="The nickname of the RStudio Connect server to deploy to." + "--server", "-s", envvar="CONNECT_SERVER", help="The URL for the RStudio Connect server to deploy to.", ) @click.option( - "--server", - "-s", - envvar="CONNECT_SERVER", - help="The URL for the RStudio Connect server to deploy to.", + "--api-key", "-k", envvar="CONNECT_API_KEY", help="The API key to use to authenticate with RStudio Connect.", ) @click.option( - "--api-key", - "-k", - envvar="CONNECT_API_KEY", - help="The API key to use to authenticate with RStudio Connect.", -) -@click.option( - "--insecure", - "-i", - envvar="CONNECT_INSECURE", - is_flag=True, - help="Disable TLS certification/host validation.", + "--insecure", "-i", envvar="CONNECT_INSECURE", is_flag=True, help="Disable TLS certification/host validation.", ) @click.option( "--cacert", @@ -1028,13 +814,9 @@ def deploy_api( ), ) @click.option( - "--app-id", - "-a", - help="Existing app ID or GUID to replace. Cannot be used with --new.", -) -@click.option( - "--title", "-t", help="Title of the content (default is the same as the directory)." + "--app-id", "-a", help="Existing app ID or GUID to replace. Cannot be used with --new.", ) +@click.option("--title", "-t", help="Title of the content (default is the same as the directory).") @click.option( "--python", "-p", @@ -1043,26 +825,15 @@ def deploy_api( "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)", + "--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", - is_flag=True, - help='Force generating "requirements.txt", even if it already exists.', + "--force-generate", "-g", is_flag=True, help='Force generating "requirements.txt", even if it already exists.', ) @click.option("--verbose", "-v", is_flag=True, help="Print detailed messages.") +@click.argument("directory", type=click.Path(exists=True, dir_okay=True, file_okay=False)) @click.argument( - "directory", type=click.Path(exists=True, dir_okay=True, file_okay=False) -) -@click.argument( - "extra_files", - nargs=-1, - type=click.Path(exists=True, dir_okay=False, file_okay=True), + "extra_files", nargs=-1, type=click.Path(exists=True, dir_okay=False, file_okay=True), ) def deploy_dash_app( name, @@ -1149,9 +920,7 @@ def _deploy_by_framework( set_verbosity(verbose) with cli_feedback("Checking arguments"): - connect_server = _validate_deploy_to_args( - name, server, api_key, insecure, cacert - ) + connect_server = _validate_deploy_to_args(name, server, api_key, insecure, cacert) module_file = fake_module_file_from_directory(directory) extra_files = validate_extra_files(directory, extra_files) app_store = AppStore(module_file) @@ -1159,9 +928,7 @@ def _deploy_by_framework( connect_server, app_store, directory, entrypoint, new, app_id, title ) - click.secho( - ' Deploying %s to server "%s"' % (directory, connect_server.url), fg="white" - ) + click.secho(' Deploying %s to server "%s"' % (directory, connect_server.url), fg="white") _warn_on_ignored_manifest(directory) @@ -1180,20 +947,10 @@ def _deploy_by_framework( _warn_on_ignored_requirements(directory, environment["filename"]) with cli_feedback("Creating deployment bundle"): - bundle = create_api_deployment_bundle( - directory, extra_files, exclude, entrypoint, app_mode, environment, False - ) + bundle = create_api_deployment_bundle(directory, extra_files, exclude, entrypoint, app_mode, environment, False) _deploy_bundle( - connect_server, - app_store, - directory, - app_id, - app_mode, - deployment_name, - title, - default_title, - bundle, + connect_server, app_store, directory, app_id, app_mode, deployment_name, title, default_title, bundle, ) @@ -1211,9 +968,7 @@ def deploy_help(): ) click.echo("\n".join(textwrap.wrap(text, 79))) click.echo() - click.echo( - " rsconnect deploy manifest [-n |-s -k ] " - ) + click.echo(" rsconnect deploy manifest [-n |-s -k ] ") click.echo() @@ -1240,9 +995,7 @@ def write_manifest(): "not exist. All files are created in the same directory as the notebook file." ), ) -@click.option( - "--overwrite", "-o", is_flag=True, help="Overwrite manifest.json, if it exists." -) +@click.option("--overwrite", "-o", is_flag=True, help="Overwrite manifest.json, if it exists.") @click.option( "--python", "-p", @@ -1251,30 +1004,17 @@ def write_manifest(): + "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", - is_flag=True, - help='Force generating "requirements.txt", even if it already exists.', + "--conda", "-C", is_flag=True, hidden=True, help="Use Conda to deploy (requires Connect version 1.8.2 or later)", ) @click.option( - "--verbose", "-v", "verbose", is_flag=True, help="Print detailed messages" + "--force-generate", "-g", is_flag=True, help='Force generating "requirements.txt", even if it already exists.', ) +@click.option("--verbose", "-v", "verbose", is_flag=True, help="Print detailed messages") @click.argument("file", type=click.Path(exists=True, dir_okay=False, file_okay=True)) @click.argument( - "extra_files", - nargs=-1, - type=click.Path(exists=True, dir_okay=False, file_okay=True), + "extra_files", nargs=-1, type=click.Path(exists=True, dir_okay=False, file_okay=True), ) -def write_manifest_notebook( - overwrite, python, conda, force_generate, verbose, file, extra_files -): +def write_manifest_notebook(overwrite, python, conda, force_generate, verbose, file, extra_files): set_verbosity(verbose) with cli_feedback("Checking arguments"): validate_file_is_notebook(file) @@ -1284,9 +1024,7 @@ def write_manifest_notebook( manifest_path = join(base_dir, "manifest.json") if exists(manifest_path) and not overwrite: - raise api.RSConnectException( - "manifest.json already exists. Use --overwrite to overwrite." - ) + raise api.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) @@ -1300,9 +1038,7 @@ def write_manifest_notebook( if environment_file_exists and not force_generate: click.secho( - " Warning: %s already exists and will not be overwritten." - % environment["filename"], - fg="yellow", + " Warning: %s already exists and will not be overwritten." % environment["filename"], fg="yellow", ) else: with cli_feedback("Creating %s" % environment["filename"]): @@ -1319,9 +1055,7 @@ def write_manifest_notebook( "not exist. All files are created in the same directory as the API code." ), ) -@click.option( - "--overwrite", "-o", is_flag=True, help="Overwrite manifest.json, if it exists." -) +@click.option("--overwrite", "-o", is_flag=True, help="Overwrite manifest.json, if it exists.") @click.option( "--entrypoint", "-e", @@ -1347,39 +1081,18 @@ def write_manifest_notebook( + "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", - is_flag=True, - help='Force generating "requirements.txt", even if it already exists.', + "--conda", "-C", is_flag=True, hidden=True, help="Use Conda to deploy (requires Connect version 1.8.2 or later)", ) @click.option( - "--verbose", "-v", "verbose", is_flag=True, help="Print detailed messages" -) -@click.argument( - "directory", type=click.Path(exists=True, dir_okay=True, file_okay=False) + "--force-generate", "-g", is_flag=True, help='Force generating "requirements.txt", even if it already exists.', ) +@click.option("--verbose", "-v", "verbose", is_flag=True, help="Print detailed messages") +@click.argument("directory", type=click.Path(exists=True, dir_okay=True, file_okay=False)) @click.argument( - "extra_files", - nargs=-1, - type=click.Path(exists=True, dir_okay=False, file_okay=True), + "extra_files", nargs=-1, type=click.Path(exists=True, dir_okay=False, file_okay=True), ) def write_manifest_api( - overwrite, - entrypoint, - exclude, - python, - conda, - force_generate, - verbose, - directory, - extra_files, + overwrite, entrypoint, exclude, python, conda, force_generate, verbose, directory, extra_files, ): _write_framework_manifest( overwrite, @@ -1405,9 +1118,7 @@ def write_manifest_api( "does not exist. All files are created in the same directory as the API code." ), ) -@click.option( - "--overwrite", "-o", is_flag=True, help="Overwrite manifest.json, if it exists." -) +@click.option("--overwrite", "-o", is_flag=True, help="Overwrite manifest.json, if it exists.") @click.option( "--entrypoint", "-e", @@ -1433,39 +1144,18 @@ def write_manifest_api( + "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", - is_flag=True, - help='Force generating "requirements.txt", even if it already exists.', + "--conda", "-C", is_flag=True, hidden=True, help="Use Conda to deploy (requires Connect version 1.8.2 or later)", ) @click.option( - "--verbose", "-v", "verbose", is_flag=True, help="Print detailed messages" -) -@click.argument( - "directory", type=click.Path(exists=True, dir_okay=True, file_okay=False) + "--force-generate", "-g", is_flag=True, help='Force generating "requirements.txt", even if it already exists.', ) +@click.option("--verbose", "-v", "verbose", is_flag=True, help="Print detailed messages") +@click.argument("directory", type=click.Path(exists=True, dir_okay=True, file_okay=False)) @click.argument( - "extra_files", - nargs=-1, - type=click.Path(exists=True, dir_okay=False, file_okay=True), + "extra_files", nargs=-1, type=click.Path(exists=True, dir_okay=False, file_okay=True), ) def write_manifest_dash( - overwrite, - entrypoint, - exclude, - python, - conda, - force_generate, - verbose, - directory, - extra_files, + overwrite, entrypoint, exclude, python, conda, force_generate, verbose, directory, extra_files, ): _write_framework_manifest( overwrite, @@ -1483,16 +1173,7 @@ def write_manifest_dash( # noinspection SpellCheckingInspection def _write_framework_manifest( - overwrite, - entrypoint, - exclude, - python, - conda, - force_generate, - verbose, - directory, - extra_files, - app_mode, + overwrite, entrypoint, exclude, python, conda, force_generate, verbose, directory, extra_files, app_mode, ): """ A common function for writing manifests for APIs, Dash apps, etc. @@ -1518,9 +1199,7 @@ def _write_framework_manifest( manifest_path = join(directory, "manifest.json") if exists(manifest_path) and not overwrite: - raise api.RSConnectException( - "manifest.json already exists. Use --overwrite to overwrite." - ) + raise api.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) @@ -1534,9 +1213,7 @@ def _write_framework_manifest( if environment_file_exists and not force_generate: click.secho( - " Warning: %s already exists and will not be overwritten." - % environment["filename"], - fg="yellow", + " Warning: %s already exists and will not be overwritten." % environment["filename"], fg="yellow", ) else: with cli_feedback("Creating %s" % environment["filename"]): diff --git a/rsconnect/metadata.py b/rsconnect/metadata.py index 0f8db496..6438e3a1 100644 --- a/rsconnect/metadata.py +++ b/rsconnect/metadata.py @@ -249,10 +249,7 @@ def set(self, name, url, api_key, insecure=False, ca_data=None): :param ca_data: client side certificate data to use for TLS. """ self._set( - name, - dict( - name=name, url=url, api_key=api_key, insecure=insecure, ca_cert=ca_data, - ), + name, dict(name=name, url=url, api_key=api_key, insecure=insecure, ca_cert=ca_data,), ) def remove_by_name(self, name): @@ -298,9 +295,7 @@ def resolve(self, name, url, api_key, insecure, ca_data): if name: entry = self.get_by_name(name) if not entry: - raise api.RSConnectException( - 'The nickname, "%s", does not exist.' % name - ) + raise api.RSConnectException('The nickname, "%s", does not exist.' % name) elif url: entry = self.get_by_url(url) else: @@ -402,9 +397,7 @@ def set(self, server_url, filename, app_url, app_id, app_guid, title, app_mode): def resolve(self, server, app_id, app_mode): metadata = self.get(server) if metadata is None: - logger.debug( - "No previous deployment to this server was found; this will be a new deployment." - ) + logger.debug("No previous deployment to this server was found; this will be a new deployment.") return app_id, app_mode logger.debug("Found previous deployment data in %s" % self.get_path()) diff --git a/rsconnect/models.py b/rsconnect/models.py index 4d373c08..194d0e01 100644 --- a/rsconnect/models.py +++ b/rsconnect/models.py @@ -61,17 +61,11 @@ class AppModes(object): @classmethod def get_by_ordinal(cls, ordinal, return_unknown=False): - return cls._find_by( - lambda mode: mode.ordinal() == ordinal, - "with ordinal %s" % ordinal, - return_unknown, - ) + return cls._find_by(lambda mode: mode.ordinal() == ordinal, "with ordinal %s" % ordinal, return_unknown,) @classmethod def get_by_name(cls, name, return_unknown=False): - return cls._find_by( - lambda mode: mode.name() == name, "named %s" % name, return_unknown - ) + return cls._find_by(lambda mode: mode.name() == name, "named %s" % name, return_unknown) @classmethod def get_by_extension(cls, extension, return_unknown=False): @@ -82,9 +76,7 @@ def get_by_extension(cls, extension, return_unknown=False): raise ValueError("No app mode with extension %s" % extension) return cls._find_by( - lambda mode: mode.extension() == extension, - "with extension: %s" % extension, - return_unknown, + lambda mode: mode.extension() == extension, "with extension: %s" % extension, return_unknown, ) @classmethod @@ -131,9 +123,7 @@ def _to_parts_list(pattern): for index, name in enumerate(parts): if name == "**": if depth_wildcard_index is not None: - raise ValueError( - 'Only one occurrence of the "**" pattern is allowed.' - ) + raise ValueError('Only one occurrence of the "**" pattern is allowed.') depth_wildcard_index = index elif any(ch in name for ch in "*?["): parts[index] = re.compile(r"\A" + fnmatch.translate(name)) @@ -152,11 +142,7 @@ def items_match(i1, i2): return self._pattern_parts[i1] == parts[i2] return self._pattern_parts[i1].match(parts[i2]) is not None - wildcard_index = ( - len(self._pattern_parts) - if self._wildcard_index is None - else self._wildcard_index - ) + wildcard_index = len(self._pattern_parts) if self._wildcard_index is None else self._wildcard_index # Top-down... for index in range(wildcard_index): diff --git a/rsconnect/tests/test_actions.py b/rsconnect/tests/test_actions.py index 63c35195..a611e7bb 100644 --- a/rsconnect/tests/test_actions.py +++ b/rsconnect/tests/test_actions.py @@ -41,9 +41,7 @@ def test_which_python(self): self.assertEqual(which_python(sys.executable), sys.executable) self.assertEqual(which_python(None), sys.executable) - self.assertEqual( - which_python(None, {"RETICULATE_PYTHON": "fake-python"}), "fake-python" - ) + self.assertEqual(which_python(None, {"RETICULATE_PYTHON": "fake-python"}), "fake-python") def test_verify_server(self): with self.assertRaises(RSConnectException): @@ -67,45 +65,32 @@ def test_check_server_capabilities(self): api_support = {"python": {"api_enabled": True}} with self.assertRaises(api.RSConnectException) as context: - check_server_capabilities( - None, (are_apis_supported_on_server,), lambda x: no_api_support - ) + check_server_capabilities(None, (are_apis_supported_on_server,), lambda x: no_api_support) self.assertEqual( - str(context.exception), - "The RStudio Connect server does not allow for Python APIs.", + str(context.exception), "The RStudio Connect server does not allow for Python APIs.", ) - check_server_capabilities( - None, (are_apis_supported_on_server,), lambda x: api_support - ) + check_server_capabilities(None, (are_apis_supported_on_server,), lambda x: api_support) no_conda = api_support conda_not_supported = {"conda": {"supported": False}} conda_supported = {"conda": {"supported": True}} with self.assertRaises(api.RSConnectException) as context: - check_server_capabilities( - None, (is_conda_supported_on_server,), lambda x: no_conda - ) + check_server_capabilities(None, (is_conda_supported_on_server,), lambda x: no_conda) self.assertEqual( str(context.exception), - "Conda is not supported on the target server. " - + "Try deploying without requesting Conda.", + "Conda is not supported on the target server. " + "Try deploying without requesting Conda.", ) with self.assertRaises(api.RSConnectException) as context: - check_server_capabilities( - None, (is_conda_supported_on_server,), lambda x: conda_not_supported - ) + check_server_capabilities(None, (is_conda_supported_on_server,), lambda x: conda_not_supported) self.assertEqual( str(context.exception), - "Conda is not supported on the target server. " - + "Try deploying without requesting Conda.", + "Conda is not supported on the target server. " + "Try deploying without requesting Conda.", ) - check_server_capabilities( - None, (is_conda_supported_on_server,), lambda x: conda_supported - ) + check_server_capabilities(None, (is_conda_supported_on_server,), lambda x: conda_supported) # noinspection PyUnusedLocal def fake_cap(details): @@ -119,15 +104,13 @@ def fake_cap_with_doc(details): with self.assertRaises(api.RSConnectException) as context: check_server_capabilities(None, (fake_cap,), lambda x: None) self.assertEqual( - str(context.exception), - "The server does not satisfy the fake_cap capability check.", + str(context.exception), "The server does not satisfy the fake_cap capability check.", ) with self.assertRaises(api.RSConnectException) as context: check_server_capabilities(None, (fake_cap_with_doc,), lambda x: None) self.assertEqual( - str(context.exception), - "The server does not satisfy the fake_cap_with_doc capability check.", + str(context.exception), "The server does not satisfy the fake_cap_with_doc capability check.", ) def test_validate_title(self): @@ -156,9 +139,7 @@ def test_make_deployment_name(self): self.assertEqual(_make_deployment_name(None, "My _ Title", False), "my_title") self.assertEqual(_make_deployment_name(None, "My-Title", False), "my-title") # noinspection SpellCheckingInspection - self.assertEqual( - _make_deployment_name(None, u"M\ry\n \tT\u2103itle", False), "my_title" - ) + self.assertEqual(_make_deployment_name(None, u"M\ry\n \tT\u2103itle", False), "my_title") self.assertEqual(_make_deployment_name(None, u"\r\n\t\u2103", False), "___") self.assertEqual(_make_deployment_name(None, u"\r\n\tR\u2103", False), "__r") @@ -169,22 +150,14 @@ def test_default_title(self): self.assertEqual(_default_title("%s.ext" % ("n" * 2048)), "n" * 1024) def test_default_title_from_manifest(self): - self.assertEqual( - _default_title_from_manifest({}, "dir/to/manifest.json"), "0to" - ) + self.assertEqual(_default_title_from_manifest({}, "dir/to/manifest.json"), "0to") # noinspection SpellCheckingInspection m = {"metadata": {"entrypoint": "point"}} - self.assertEqual( - _default_title_from_manifest(m, "dir/to/manifest.json"), "point" - ) + self.assertEqual(_default_title_from_manifest(m, "dir/to/manifest.json"), "point") m = {"metadata": {"primary_rmd": "file.Rmd"}} - self.assertEqual( - _default_title_from_manifest(m, "dir/to/manifest.json"), "file" - ) + self.assertEqual(_default_title_from_manifest(m, "dir/to/manifest.json"), "file") m = {"metadata": {"primary_html": "page.html"}} - self.assertEqual( - _default_title_from_manifest(m, "dir/to/manifest.json"), "page" - ) + self.assertEqual(_default_title_from_manifest(m, "dir/to/manifest.json"), "page") m = {"metadata": {"primary_wat?": "my-cool-thing.wat"}} self.assertEqual(_default_title_from_manifest(m, "dir/to/manifest.json"), "0to") # noinspection SpellCheckingInspection @@ -204,8 +177,7 @@ def test_validate_extra_files(self): self.assertEqual(validate_extra_files(directory, None), []) self.assertEqual(validate_extra_files(directory, []), []) self.assertEqual( - validate_extra_files(directory, [join(directory, "index.htm")]), - ["index.htm"], + validate_extra_files(directory, [join(directory, "index.htm")]), ["index.htm"], ) def test_deploy_python_api_validates(self): @@ -218,13 +190,9 @@ def test_gather_basic_deployment_info_for_api_validates(self): directory = get_api_path("flask") server = RSConnectServer("https://www.bogus.com", "bogus") with self.assertRaises(RSConnectException): - gather_basic_deployment_info_for_api( - server, None, directory, "bogus:bogus:bogus", False, 0, "bogus" - ) + gather_basic_deployment_info_for_api(server, None, directory, "bogus:bogus:bogus", False, 0, "bogus") with self.assertRaises(RSConnectException): - gather_basic_deployment_info_for_api( - server, None, directory, "app:app", False, 0, "" - ) + gather_basic_deployment_info_for_api(server, None, directory, "app:app", False, 0, "") def test_create_notebook_deployment_bundle_validates(self): file_name = get_dir(join("pip1", "requirements.txt")) @@ -237,10 +205,6 @@ def test_create_notebook_deployment_bundle_validates(self): def test_create_api_deployment_bundle_validates(self): directory = get_api_path("flask") with self.assertRaises(RSConnectException): - create_api_deployment_bundle( - directory, [], [], "bogus:bogus:bogus", None, None - ) + create_api_deployment_bundle(directory, [], [], "bogus:bogus:bogus", None, None) with self.assertRaises(RSConnectException): - create_api_deployment_bundle( - directory, ["bogus"], [], "app:app", None, None - ) + create_api_deployment_bundle(directory, ["bogus"], [], "app:app", None, None) diff --git a/rsconnect/tests/test_api.py b/rsconnect/tests/test_api.py index be8e7720..50514cec 100644 --- a/rsconnect/tests/test_api.py +++ b/rsconnect/tests/test_api.py @@ -20,7 +20,5 @@ def test_output_task_log(self): task_status["code"] = 0 self.assertEqual(len(output), 0) - self.assertEqual( - RSConnect.output_task_log(task_status, "0", output.append), "last" - ) + self.assertEqual(RSConnect.output_task_log(task_status, "0", output.append), "last") self.assertEqual(lines, output) diff --git a/rsconnect/tests/test_bundle.py b/rsconnect/tests/test_bundle.py index edc0fbad..c56513d8 100644 --- a/rsconnect/tests/test_bundle.py +++ b/rsconnect/tests/test_bundle.py @@ -36,16 +36,12 @@ def test_source_bundle1(self): ) as tar: names = sorted(tar.getnames()) - self.assertEqual( - names, ["dummy.ipynb", "manifest.json", "requirements.txt",] - ) + self.assertEqual(names, ["dummy.ipynb", "manifest.json", "requirements.txt",]) reqs = tar.extractfile("requirements.txt").read() self.assertEqual(reqs, b"numpy\npandas\nmatplotlib\n") - manifest = json.loads( - tar.extractfile("manifest.json").read().decode("utf-8") - ) + manifest = json.loads(tar.extractfile("manifest.json").read().decode("utf-8")) # don't check locale value, just require it be present del manifest["locale"] @@ -61,22 +57,14 @@ def test_source_bundle1(self): manifest, { u"version": 1, - u"metadata": { - u"appmode": u"jupyter-static", - u"entrypoint": u"dummy.ipynb", - }, + u"metadata": {u"appmode": u"jupyter-static", u"entrypoint": u"dummy.ipynb",}, u"python": { u"version": self.python_version(), - u"package_manager": { - u"name": u"pip", - u"package_file": u"requirements.txt", - }, + u"package_manager": {u"name": u"pip", u"package_file": u"requirements.txt",}, }, u"files": { u"dummy.ipynb": {u"checksum": ipynb_hash,}, - u"requirements.txt": { - u"checksum": u"5f2a5e862fe7afe3def4a57bb5cfb214" - }, + u"requirements.txt": {u"checksum": u"5f2a5e862fe7afe3def4a57bb5cfb214"}, }, }, ) @@ -92,23 +80,19 @@ def test_source_bundle2(self): # the kernel environment and not the notebook server environment. environment = detect_environment(directory) - with make_notebook_source_bundle( - nb_path, environment, extra_files=["data.csv"] - ) as bundle, tarfile.open(mode="r:gz", fileobj=bundle) as tar: + with make_notebook_source_bundle(nb_path, environment, extra_files=["data.csv"]) as bundle, tarfile.open( + mode="r:gz", fileobj=bundle + ) as tar: names = sorted(tar.getnames()) - self.assertEqual( - names, ["data.csv", "dummy.ipynb", "manifest.json", "requirements.txt",] - ) + self.assertEqual(names, ["data.csv", "dummy.ipynb", "manifest.json", "requirements.txt",]) reqs = tar.extractfile("requirements.txt").read() # these are the dependencies declared in our setup.py self.assertIn(b"six", reqs) - manifest = json.loads( - tar.extractfile("manifest.json").read().decode("utf-8") - ) + manifest = json.loads(tar.extractfile("manifest.json").read().decode("utf-8")) # don't check requirements.txt since we don't know the checksum del manifest["files"]["requirements.txt"] @@ -127,16 +111,10 @@ def test_source_bundle2(self): manifest, { u"version": 1, - u"metadata": { - u"appmode": u"jupyter-static", - u"entrypoint": u"dummy.ipynb", - }, + u"metadata": {u"appmode": u"jupyter-static", u"entrypoint": u"dummy.ipynb",}, u"python": { u"version": self.python_version(), - u"package_manager": { - u"name": u"pip", - u"package_file": u"requirements.txt", - }, + u"package_manager": {u"name": u"pip", u"package_file": u"requirements.txt",}, }, u"files": { u"dummy.ipynb": {u"checksum": ipynb_hash,}, @@ -198,20 +176,11 @@ def do_test_html_bundle(self, directory): names = sorted(tar.getnames()) self.assertEqual(names, ["dummy.html", "manifest.json",]) - manifest = json.loads( - tar.extractfile("manifest.json").read().decode("utf-8") - ) + manifest = json.loads(tar.extractfile("manifest.json").read().decode("utf-8")) # noinspection SpellCheckingInspection self.assertEqual( - manifest, - { - u"version": 1, - u"metadata": { - u"appmode": u"static", - u"primary_html": u"dummy.html", - }, - }, + manifest, {u"version": 1, u"metadata": {u"appmode": u"static", u"primary_html": u"dummy.html",},}, ) finally: tar.close() @@ -231,18 +200,10 @@ def test_keep_manifest_specified_file(self): def test_manifest_bundle(self): self.maxDiff = 5000 # noinspection SpellCheckingInspection - manifest_path = join( - dirname(__file__), "testdata", "R", "shinyapp", "manifest.json" - ) + manifest_path = join(dirname(__file__), "testdata", "R", "shinyapp", "manifest.json") - with make_manifest_bundle(manifest_path) as bundle, tarfile.open( - mode="r:gz", fileobj=bundle - ) as tar: + with make_manifest_bundle(manifest_path) as bundle, tarfile.open(mode="r:gz", fileobj=bundle) as tar: tar_names = sorted(tar.getnames()) - manifest = json.loads( - tar.extractfile("manifest.json").read().decode("utf-8") - ) - manifest_names = sorted( - filter(keep_manifest_specified_file, manifest["files"].keys()) - ) + manifest = json.loads(tar.extractfile("manifest.json").read().decode("utf-8")) + manifest_names = sorted(filter(keep_manifest_specified_file, manifest["files"].keys())) self.assertEqual(tar_names, manifest_names) diff --git a/rsconnect/tests/test_environment.py b/rsconnect/tests/test_environment.py index dae2e261..21013a46 100644 --- a/rsconnect/tests/test_environment.py +++ b/rsconnect/tests/test_environment.py @@ -21,9 +21,7 @@ def python_version(): return ".".join(map(str, sys.version_info[:3])) def test_get_python_version(self): - self.assertEqual( - get_python_version({"package_manager": "pip"}), self.python_version() - ) + self.assertEqual(get_python_version({"package_manager": "pip"}), self.python_version()) def test_get_default_locale(self): self.assertEqual(get_default_locale(lambda: ("en_US", "UTF-8")), "en_US.UTF-8") @@ -79,9 +77,7 @@ def test_pip_freeze(self): def test_conda_env_export(self): fake_conda = join(dirname(__file__), "testdata", "fake_conda.sh") - result = detect_environment( - get_dir("conda1"), conda_mode=True, force_generate=True, conda=fake_conda - ) + result = detect_environment(get_dir("conda1"), conda_mode=True, force_generate=True, conda=fake_conda) self.assertEqual(result["source"], "conda_env_export") self.assertEqual(result["conda"], "1.0.0") self.assertEqual(result["contents"], "this is a conda environment\n") diff --git a/rsconnect/tests/test_http_support.py b/rsconnect/tests/test_http_support.py index 83f6344a..677fc875 100644 --- a/rsconnect/tests/test_http_support.py +++ b/rsconnect/tests/test_http_support.py @@ -62,14 +62,9 @@ def getheaders(self): class TestCookieJar(TestCase): def test_basic_stuff(self): jar = CookieJar() - jar.store_cookies( - FakeSetCookieResponse( - ["my-cookie=my-value", "my-2nd-cookie=my-other-value"] - ) - ) + jar.store_cookies(FakeSetCookieResponse(["my-cookie=my-value", "my-2nd-cookie=my-other-value"])) self.assertEqual( - jar.get_cookie_header_value(), - "my-cookie=my-value; my-2nd-cookie=my-other-value", + jar.get_cookie_header_value(), "my-cookie=my-value; my-2nd-cookie=my-other-value", ) def test_from_dict(self): @@ -93,11 +88,7 @@ def test_from_dict_errors(self): def test_as_dict(self): jar = CookieJar() - jar.store_cookies( - FakeSetCookieResponse( - ["my-cookie=my-value", "my-2nd-cookie=my-other-value"] - ) - ) + jar.store_cookies(FakeSetCookieResponse(["my-cookie=my-value", "my-2nd-cookie=my-other-value"])) self.assertEqual( jar.as_dict(), { diff --git a/rsconnect/tests/test_metadata.py b/rsconnect/tests/test_metadata.py index 46a14ec8..b8569c98 100644 --- a/rsconnect/tests/test_metadata.py +++ b/rsconnect/tests/test_metadata.py @@ -9,12 +9,8 @@ class TestServerMetadata(TestCase): def setUp(self): self.server_store = ServerStore() - self.server_store.set( - "foo", "http://connect.local", "notReallyAnApiKey", ca_data="/certs/connect" - ) - self.server_store.set( - "bar", "http://connect.remote", "differentApiKey", insecure=True - ) + self.server_store.set("foo", "http://connect.local", "notReallyAnApiKey", ca_data="/certs/connect") + self.server_store.set("bar", "http://connect.remote", "differentApiKey", insecure=True) def test_add(self): self.assertEqual( @@ -30,13 +26,7 @@ def test_add(self): self.assertEqual( self.server_store.get_by_name("bar"), - dict( - name="bar", - url="http://connect.remote", - api_key="differentApiKey", - insecure=True, - ca_cert=None, - ), + dict(name="bar", url="http://connect.remote", api_key="differentApiKey", insecure=True, ca_cert=None,), ) def test_remove_by_name(self): @@ -67,9 +57,7 @@ def test_list(self): self.assertEqual(servers[1]["name"], "foo") self.assertEqual(servers[1]["url"], "http://connect.local") - def check_resolve_call( - self, name, server, api_key, insecure, ca_cert, should_be_from_store - ): + def check_resolve_call(self, name, server, api_key, insecure, ca_cert, should_be_from_store): server, api_key, insecure, ca_cert, from_store = self.server_store.resolve( name, server, api_key, insecure, ca_cert ) @@ -89,9 +77,7 @@ def test_resolve_by_url(self): def test_resolve_by_default(self): # with multiple entries, server None will not resolve by default name, server, api_key, insecure, ca_cert = None, None, None, None, None - server, api_key, insecure, ca_cert, _ = self.server_store.resolve( - name, server, api_key, insecure, ca_cert - ) + server, api_key, insecure, ca_cert, _ = self.server_store.resolve(name, server, api_key, insecure, ca_cert) self.assertEqual(server, None) # with only a single entry, server None will resolve to that entry @@ -123,9 +109,7 @@ def test_save_and_load(self): self.assertFalse(exists(path)) - server_store.set( - "foo", "http://connect.local", "notReallyAnApiKey", ca_data="/certs/connect" - ) + server_store.set("foo", "http://connect.local", "notReallyAnApiKey", ca_data="/certs/connect") self.assertTrue(exists(path)) @@ -138,9 +122,7 @@ def test_save_and_load(self): self.assertIn("/certs/connect", data) server_store2 = ServerStore(base_dir=temp) - self.assertEqual( - server_store.get_all_servers(), server_store2.get_all_servers() - ) + self.assertEqual(server_store.get_all_servers(), server_store2.get_all_servers()) def test_get_path(self): self.assertIn("rsconnect-python", self.server_store.get_path()) @@ -156,13 +138,7 @@ def setUp(self): self.app_store = AppStore(self.nb_path) self.app_store.set( - "http://dev", - "/path/to/file", - "http://dev/apps/123", - 123, - "shouldBeAGuid", - "Important Title", - "static", + "http://dev", "/path/to/file", "http://dev/apps/123", 123, "shouldBeAGuid", "Important Title", "static", ) self.app_store.set( "http://prod", @@ -230,10 +206,7 @@ def test_local_save_load(self): def test_global_save_load(self): def mock_open(path_to_open, mode, *args, **kw): if path_to_open.startswith(self.tempdir) and "w" in mode: - raise OSError( - "Mock: path %s in directory %s is not writable" - % (path_to_open, self.tempdir) - ) + raise OSError("Mock: path %s in directory %s is not writable" % (path_to_open, self.tempdir)) return open(path_to_open, mode, *args, **kw) path = join(self.tempdir, "rsconnect-python", "notebook.ipynb") diff --git a/rsconnect/tests/test_models.py b/rsconnect/tests/test_models.py index d9d3fea4..6ed8950a 100644 --- a/rsconnect/tests/test_models.py +++ b/rsconnect/tests/test_models.py @@ -104,12 +104,7 @@ def test_glob_matcher(self): for case in cases: matcher = GlobMatcher(case[0]) - msg = "Pattern: %s, Path: %s, expected: %s, got: %s" % ( - case[0], - case[1], - case[2], - not case[2], - ) + msg = "Pattern: %s, Path: %s, expected: %s, got: %s" % (case[0], case[1], case[2], not case[2],) self.assertEqual(matcher.matches(case[1]), case[2], msg) with self.assertRaises(ValueError):