From 48dd05aa65fe28ec7465da7ce23af43e9f6dc119 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Wed, 9 Jun 2021 17:17:21 -0400 Subject: [PATCH 01/26] Initial commit adding option to hide code cells when rendering Jupyter notebooks --- rsconnect/actions.py | 15 +++++++++------ rsconnect/bundle.py | 10 +++++++++- rsconnect/main.py | 9 ++++++--- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/rsconnect/actions.py b/rsconnect/actions.py index f8f1258d..064135bc 100644 --- a/rsconnect/actions.py +++ b/rsconnect/actions.py @@ -455,6 +455,7 @@ def deploy_jupyter_notebook( conda_mode=False, force_generate=False, log_callback=None, + noinput=None, ): """ A function to deploy a Jupyter notebook to Connect. Depending on the files involved @@ -478,6 +479,7 @@ def deploy_jupyter_notebook( (the default) the lines from the deployment log will be returned as a sequence. If a log callback is provided, then None will be returned for the log lines part of the return tuple. + :param noinput: if True, will hide notebook code cells when rendering to output :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. """ @@ -486,7 +488,7 @@ def deploy_jupyter_notebook( connect_server, app_store, file_name, new, app_id, title, static ) python, environment = get_python_env_info(file_name, python, conda_mode=conda_mode, force_generate=force_generate,) - bundle = create_notebook_deployment_bundle(file_name, extra_files, app_mode, python, environment) + bundle = create_notebook_deployment_bundle(file_name, extra_files, app_mode, python, environment, noinput) return _finalize_deploy( connect_server, app_store, @@ -1079,6 +1081,7 @@ def get_python_env_info(file_name, python, conda_mode=False, force_generate=Fals def create_notebook_deployment_bundle( file_name, extra_files, app_mode, python, environment, extra_files_need_validating=True, + noinput=None, ): """ Create an in-memory bundle, ready to deploy. @@ -1101,13 +1104,13 @@ def create_notebook_deployment_bundle( if app_mode == AppModes.STATIC: try: - return make_notebook_html_bundle(file_name, python) + return make_notebook_html_bundle(file_name, python, noinput) except subprocess.CalledProcessError as exc: # Jupyter rendering failures are often due to # user code failing, vs. an internal failure of rsconnect-python. raise api.RSConnectException(str(exc)) else: - return make_notebook_source_bundle(file_name, environment, extra_files) + return make_notebook_source_bundle(file_name, environment, extra_files, noinput) def create_api_deployment_bundle( @@ -1172,7 +1175,7 @@ def spool_deployment_log(connect_server, app, log_callback): def create_notebook_manifest_and_environment_file( - entry_point_file, environment, app_mode=None, extra_files=None, force=True + entry_point_file, environment, app_mode=None, extra_files=None, force=True, noinput=None ): """ Creates and writes a manifest.json file for the given notebook entry point file. @@ -1190,11 +1193,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, noinput) 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, extra_files, noinput): """ 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 diff --git a/rsconnect/bundle.py b/rsconnect/bundle.py index 1b7316d0..b716febb 100644 --- a/rsconnect/bundle.py +++ b/rsconnect/bundle.py @@ -136,7 +136,7 @@ def bundle_add_buffer(bundle, filename, contents): bundle.addfile(file_info, buf) -def write_manifest(relative_dir, nb_name, environment, output_dir): +def write_manifest(relative_dir, nb_name, environment, output_dir, noinput=None): # type: (str, str, Environment, str) -> typing.Tuple[list, list] """Create a manifest for source publishing the specified notebook. @@ -147,6 +147,8 @@ def write_manifest(relative_dir, nb_name, environment, output_dir): """ manifest_filename = "manifest.json" manifest = make_source_manifest(nb_name, environment, AppModes.JUPYTER_NOTEBOOK) + if noinput: + manifest['jupyter'] = {'noinput': noinput} manifest_file = join(output_dir, manifest_filename) created = [] skipped = [] @@ -204,6 +206,7 @@ def make_notebook_source_bundle( file, # type: str environment, # type: Environment extra_files=None, # type: typing.Optional[typing.List[str]] + noinput=None, ): # type: (...) -> typing.IO[bytes] """Create a bundle containing the specified notebook and python environment. @@ -216,6 +219,8 @@ def make_notebook_source_bundle( nb_name = basename(file) manifest = make_source_manifest(nb_name, environment, AppModes.JUPYTER_NOTEBOOK) + if noinput: + manifest['jupyter'] = {'noinput': noinput} manifest_add_file(manifest, nb_name, base_dir) manifest_add_buffer(manifest, environment.filename, environment.contents) @@ -256,6 +261,7 @@ def make_notebook_html_bundle( filename, # type: str python, # type: str check_output=subprocess.check_output, # type: typing.Callable + noinput=None, ): # type: (...) -> typing.IO[bytes] # noinspection SpellCheckingInspection @@ -270,6 +276,8 @@ def make_notebook_html_bundle( "--to=html", filename, ] + if noinput: + cmd.append('--no-input') try: output = check_output(cmd) except subprocess.CalledProcessError: diff --git a/rsconnect/main.py b/rsconnect/main.py index 363cceba..80fa0bf8 100644 --- a/rsconnect/main.py +++ b/rsconnect/main.py @@ -520,6 +520,7 @@ def _deploy_bundle( "--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.option("--noinput", is_flag=True, default=None, help="Hide code cells when rendering output") @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), @@ -540,6 +541,7 @@ def deploy_notebook( verbose, file, extra_files, + noinput, ): set_verbosity(verbose) @@ -568,7 +570,7 @@ 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, noinput) _deploy_bundle( connect_server, app_store, file, app_id, app_mode, deployment_name, title, default_title, bundle, @@ -934,12 +936,13 @@ def write_manifest(): @click.option( "--force-generate", "-g", is_flag=True, help='Force generating "requirements.txt", even if it already exists.', ) +@click.option("--noinput", help="Hide code cells when rendering output") @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), ) -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, noinput=None): set_verbosity(verbose) with cli_feedback("Checking arguments"): validate_file_is_notebook(file) @@ -958,7 +961,7 @@ def write_manifest_notebook(overwrite, python, conda, force_generate, verbose, f with cli_feedback("Creating manifest.json"): environment_file_exists = write_notebook_manifest_json( - file, environment, AppModes.JUPYTER_NOTEBOOK, extra_files + file, environment, AppModes.JUPYTER_NOTEBOOK, extra_files, noinput, ) if environment_file_exists and not force_generate: From ea19966a7edc17faf988d3d6fafc6cbb95289676 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Thu, 10 Jun 2021 13:21:30 -0400 Subject: [PATCH 02/26] add types-six to requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 660c124e..80fffe6b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,3 +17,4 @@ six>=1.14.0 toml twine wheel +types-six From dd98b852c81de145b057ca8a168f5b3cf1f013e5 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Thu, 10 Jun 2021 13:32:11 -0400 Subject: [PATCH 03/26] update types --- rsconnect/bundle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rsconnect/bundle.py b/rsconnect/bundle.py index b716febb..0eb777bf 100644 --- a/rsconnect/bundle.py +++ b/rsconnect/bundle.py @@ -137,7 +137,7 @@ def bundle_add_buffer(bundle, filename, contents): def write_manifest(relative_dir, nb_name, environment, output_dir, noinput=None): - # type: (str, str, Environment, str) -> typing.Tuple[list, list] + # type: (str, str, Environment, str, noinput) -> typing.Tuple[list, list] """Create a manifest for source publishing the specified notebook. The manifest will be written to `manifest.json` in the output directory.. From 8be15a211aa039a3eb5a85e934c8067b5326c1a2 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Thu, 10 Jun 2021 13:43:47 -0400 Subject: [PATCH 04/26] skip parameter type check --- rsconnect/bundle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rsconnect/bundle.py b/rsconnect/bundle.py index 0eb777bf..6367a59f 100644 --- a/rsconnect/bundle.py +++ b/rsconnect/bundle.py @@ -137,7 +137,7 @@ def bundle_add_buffer(bundle, filename, contents): def write_manifest(relative_dir, nb_name, environment, output_dir, noinput=None): - # type: (str, str, Environment, str, noinput) -> typing.Tuple[list, list] + # type: (...) -> typing.Tuple[list, list] """Create a manifest for source publishing the specified notebook. The manifest will be written to `manifest.json` in the output directory.. From a06a30c74c56fcbd174c10a583bf44c0379349fa Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Thu, 10 Jun 2021 14:11:28 -0400 Subject: [PATCH 05/26] add types-click to requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 80fffe6b..47ebe32b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,3 +18,4 @@ toml twine wheel types-six +types-click From 8267f391cf220f6ee78b38a5262ccc39ad1368b6 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Thu, 10 Jun 2021 14:16:01 -0400 Subject: [PATCH 06/26] Add types-Flask to requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 47ebe32b..ae2c711d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,3 +19,4 @@ twine wheel types-six types-click +types-Flask From 3448524147f79cdb8eaf242b19ed313145536a63 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Wed, 23 Jun 2021 11:49:58 -0400 Subject: [PATCH 07/26] rename noinput --- rsconnect/actions.py | 18 +++++++++--------- rsconnect/bundle.py | 16 ++++++++-------- rsconnect/main.py | 12 ++++++------ 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/rsconnect/actions.py b/rsconnect/actions.py index 064135bc..daec37f6 100644 --- a/rsconnect/actions.py +++ b/rsconnect/actions.py @@ -455,7 +455,7 @@ def deploy_jupyter_notebook( conda_mode=False, force_generate=False, log_callback=None, - noinput=None, + no_input=None, ): """ A function to deploy a Jupyter notebook to Connect. Depending on the files involved @@ -479,7 +479,7 @@ def deploy_jupyter_notebook( (the default) the lines from the deployment log will be returned as a sequence. If a log callback is provided, then None will be returned for the log lines part of the return tuple. - :param noinput: if True, will hide notebook code cells when rendering to output + :param no_input: if True, will hide notebook code cells when rendering to output :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. """ @@ -488,7 +488,7 @@ def deploy_jupyter_notebook( connect_server, app_store, file_name, new, app_id, title, static ) python, environment = get_python_env_info(file_name, python, conda_mode=conda_mode, force_generate=force_generate,) - bundle = create_notebook_deployment_bundle(file_name, extra_files, app_mode, python, environment, noinput) + bundle = create_notebook_deployment_bundle(file_name, extra_files, app_mode, python, environment, no_input) return _finalize_deploy( connect_server, app_store, @@ -1081,7 +1081,7 @@ def get_python_env_info(file_name, python, conda_mode=False, force_generate=Fals def create_notebook_deployment_bundle( file_name, extra_files, app_mode, python, environment, extra_files_need_validating=True, - noinput=None, + no_input=None, ): """ Create an in-memory bundle, ready to deploy. @@ -1104,13 +1104,13 @@ def create_notebook_deployment_bundle( if app_mode == AppModes.STATIC: try: - return make_notebook_html_bundle(file_name, python, noinput) + return make_notebook_html_bundle(file_name, python, no_input) except subprocess.CalledProcessError as exc: # Jupyter rendering failures are often due to # user code failing, vs. an internal failure of rsconnect-python. raise api.RSConnectException(str(exc)) else: - return make_notebook_source_bundle(file_name, environment, extra_files, noinput) + return make_notebook_source_bundle(file_name, environment, extra_files, no_input) def create_api_deployment_bundle( @@ -1175,7 +1175,7 @@ def spool_deployment_log(connect_server, app, log_callback): def create_notebook_manifest_and_environment_file( - entry_point_file, environment, app_mode=None, extra_files=None, force=True, noinput=None + entry_point_file, environment, app_mode=None, extra_files=None, force=True, no_input=None ): """ Creates and writes a manifest.json file for the given notebook entry point file. @@ -1193,11 +1193,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, noinput) or force: + if not write_notebook_manifest_json(entry_point_file, environment, app_mode, extra_files, no_input) or force: write_environment_file(environment, dirname(entry_point_file)) -def write_notebook_manifest_json(entry_point_file, environment, app_mode, extra_files, noinput): +def write_notebook_manifest_json(entry_point_file, environment, app_mode, extra_files, no_input): """ 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 diff --git a/rsconnect/bundle.py b/rsconnect/bundle.py index 6367a59f..47e215ca 100644 --- a/rsconnect/bundle.py +++ b/rsconnect/bundle.py @@ -136,7 +136,7 @@ def bundle_add_buffer(bundle, filename, contents): bundle.addfile(file_info, buf) -def write_manifest(relative_dir, nb_name, environment, output_dir, noinput=None): +def write_manifest(relative_dir, nb_name, environment, output_dir, no_input=None): # type: (...) -> typing.Tuple[list, list] """Create a manifest for source publishing the specified notebook. @@ -147,8 +147,8 @@ def write_manifest(relative_dir, nb_name, environment, output_dir, noinput=None) """ manifest_filename = "manifest.json" manifest = make_source_manifest(nb_name, environment, AppModes.JUPYTER_NOTEBOOK) - if noinput: - manifest['jupyter'] = {'noinput': noinput} + if no_input: + manifest['jupyter'] = {'no_input': no_input} manifest_file = join(output_dir, manifest_filename) created = [] skipped = [] @@ -206,7 +206,7 @@ def make_notebook_source_bundle( file, # type: str environment, # type: Environment extra_files=None, # type: typing.Optional[typing.List[str]] - noinput=None, + no_input=None, ): # type: (...) -> typing.IO[bytes] """Create a bundle containing the specified notebook and python environment. @@ -219,8 +219,8 @@ def make_notebook_source_bundle( nb_name = basename(file) manifest = make_source_manifest(nb_name, environment, AppModes.JUPYTER_NOTEBOOK) - if noinput: - manifest['jupyter'] = {'noinput': noinput} + if no_input: + manifest['jupyter'] = {'no_input': no_input} manifest_add_file(manifest, nb_name, base_dir) manifest_add_buffer(manifest, environment.filename, environment.contents) @@ -260,8 +260,8 @@ def make_html_manifest(filename): def make_notebook_html_bundle( filename, # type: str python, # type: str + no_input=None, check_output=subprocess.check_output, # type: typing.Callable - noinput=None, ): # type: (...) -> typing.IO[bytes] # noinspection SpellCheckingInspection @@ -276,7 +276,7 @@ def make_notebook_html_bundle( "--to=html", filename, ] - if noinput: + if no_input: cmd.append('--no-input') try: output = check_output(cmd) diff --git a/rsconnect/main.py b/rsconnect/main.py index 80fa0bf8..3e236fae 100644 --- a/rsconnect/main.py +++ b/rsconnect/main.py @@ -520,7 +520,7 @@ def _deploy_bundle( "--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.option("--noinput", is_flag=True, default=None, help="Hide code cells when rendering output") +@click.option("--no-input", is_flag=True, default=None, help="Hide code cells when rendering output") @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), @@ -541,7 +541,7 @@ def deploy_notebook( verbose, file, extra_files, - noinput, + no_input, ): set_verbosity(verbose) @@ -570,7 +570,7 @@ 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, noinput) + bundle = create_notebook_deployment_bundle(file, extra_files, app_mode, python, environment, False, no_input) _deploy_bundle( connect_server, app_store, file, app_id, app_mode, deployment_name, title, default_title, bundle, @@ -936,13 +936,13 @@ def write_manifest(): @click.option( "--force-generate", "-g", is_flag=True, help='Force generating "requirements.txt", even if it already exists.', ) -@click.option("--noinput", help="Hide code cells when rendering output") +@click.option("--no-input", help="Hide code cells when rendering output") @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), ) -def write_manifest_notebook(overwrite, python, conda, force_generate, verbose, file, extra_files, noinput=None): +def write_manifest_notebook(overwrite, python, conda, force_generate, verbose, file, extra_files, no_input=None): set_verbosity(verbose) with cli_feedback("Checking arguments"): validate_file_is_notebook(file) @@ -961,7 +961,7 @@ def write_manifest_notebook(overwrite, python, conda, force_generate, verbose, f with cli_feedback("Creating manifest.json"): environment_file_exists = write_notebook_manifest_json( - file, environment, AppModes.JUPYTER_NOTEBOOK, extra_files, noinput, + file, environment, AppModes.JUPYTER_NOTEBOOK, extra_files, no_input, ) if environment_file_exists and not force_generate: From 2d3b32c8cff47fa626bd657f0bc3fda6e9d18730 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Mon, 28 Jun 2021 17:24:09 -0400 Subject: [PATCH 08/26] add feature to hide input cells tagged with 'remove_input' --- rsconnect/actions.py | 26 ++++++++++++++++++++------ rsconnect/bundle.py | 12 ++++++++++-- rsconnect/main.py | 19 +++++++++++++++---- 3 files changed, 45 insertions(+), 12 deletions(-) diff --git a/rsconnect/actions.py b/rsconnect/actions.py index daec37f6..fa3d24d7 100644 --- a/rsconnect/actions.py +++ b/rsconnect/actions.py @@ -456,6 +456,7 @@ def deploy_jupyter_notebook( force_generate=False, log_callback=None, no_input=None, + no_tag_input=None, ): """ A function to deploy a Jupyter notebook to Connect. Depending on the files involved @@ -480,6 +481,7 @@ def deploy_jupyter_notebook( If a log callback is provided, then None will be returned for the log lines part of the return tuple. :param no_input: if True, will hide notebook code cells when rendering to output + :param no_tag_input: If True, hides the input of cells tagged with "remove_input" :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. """ @@ -488,7 +490,9 @@ def deploy_jupyter_notebook( connect_server, app_store, file_name, new, app_id, title, static ) python, environment = get_python_env_info(file_name, python, conda_mode=conda_mode, force_generate=force_generate,) - bundle = create_notebook_deployment_bundle(file_name, extra_files, app_mode, python, environment, no_input) + bundle = create_notebook_deployment_bundle( + file_name, extra_files, app_mode, python, environment, no_input, no_tag_input + ) return _finalize_deploy( connect_server, app_store, @@ -1082,6 +1086,7 @@ def get_python_env_info(file_name, python, conda_mode=False, force_generate=Fals def create_notebook_deployment_bundle( file_name, extra_files, app_mode, python, environment, extra_files_need_validating=True, no_input=None, + no_tag_input=None, ): """ Create an in-memory bundle, ready to deploy. @@ -1104,13 +1109,13 @@ def create_notebook_deployment_bundle( if app_mode == AppModes.STATIC: try: - return make_notebook_html_bundle(file_name, python, no_input) + return make_notebook_html_bundle(file_name, python, no_input, no_tag_input) except subprocess.CalledProcessError as exc: # Jupyter rendering failures are often due to # user code failing, vs. an internal failure of rsconnect-python. raise api.RSConnectException(str(exc)) else: - return make_notebook_source_bundle(file_name, environment, extra_files, no_input) + return make_notebook_source_bundle(file_name, environment, extra_files, no_input, no_tag_input) def create_api_deployment_bundle( @@ -1175,7 +1180,13 @@ def spool_deployment_log(connect_server, app, log_callback): def create_notebook_manifest_and_environment_file( - entry_point_file, environment, app_mode=None, extra_files=None, force=True, no_input=None + entry_point_file, + environment, + app_mode=None, + extra_files=None, + force=True, + no_input=None, + no_tag_input=None, ): """ Creates and writes a manifest.json file for the given notebook entry point file. @@ -1193,11 +1204,14 @@ def create_notebook_manifest_and_environment_file( already exists. :return: """ - if not write_notebook_manifest_json(entry_point_file, environment, app_mode, extra_files, no_input) or force: + if ( + not write_notebook_manifest_json(entry_point_file, environment, app_mode, extra_files, no_input, no_tag_input) + or force + ): write_environment_file(environment, dirname(entry_point_file)) -def write_notebook_manifest_json(entry_point_file, environment, app_mode, extra_files, no_input): +def write_notebook_manifest_json(entry_point_file, environment, app_mode, extra_files, no_input, no_tag_input): """ 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 diff --git a/rsconnect/bundle.py b/rsconnect/bundle.py index 47e215ca..45eeb1d0 100644 --- a/rsconnect/bundle.py +++ b/rsconnect/bundle.py @@ -136,7 +136,7 @@ def bundle_add_buffer(bundle, filename, contents): bundle.addfile(file_info, buf) -def write_manifest(relative_dir, nb_name, environment, output_dir, no_input=None): +def write_manifest(relative_dir, nb_name, environment, output_dir, no_input=None, no_tag_input=None): # type: (...) -> typing.Tuple[list, list] """Create a manifest for source publishing the specified notebook. @@ -149,6 +149,8 @@ def write_manifest(relative_dir, nb_name, environment, output_dir, no_input=None manifest = make_source_manifest(nb_name, environment, AppModes.JUPYTER_NOTEBOOK) if no_input: manifest['jupyter'] = {'no_input': no_input} + if no_tag_input: + manifest['jupyter'] = {'no_tag_input': no_tag_input} manifest_file = join(output_dir, manifest_filename) created = [] skipped = [] @@ -207,6 +209,7 @@ def make_notebook_source_bundle( environment, # type: Environment extra_files=None, # type: typing.Optional[typing.List[str]] no_input=None, + no_tag_input=None, ): # type: (...) -> typing.IO[bytes] """Create a bundle containing the specified notebook and python environment. @@ -221,6 +224,8 @@ def make_notebook_source_bundle( manifest = make_source_manifest(nb_name, environment, AppModes.JUPYTER_NOTEBOOK) if no_input: manifest['jupyter'] = {'no_input': no_input} + if no_tag_input: + manifest['jupyter'] = {'no_tag_input': no_tag_input} manifest_add_file(manifest, nb_name, base_dir) manifest_add_buffer(manifest, environment.filename, environment.contents) @@ -261,6 +266,7 @@ def make_notebook_html_bundle( filename, # type: str python, # type: str no_input=None, + no_tag_input=None, check_output=subprocess.check_output, # type: typing.Callable ): # type: (...) -> typing.IO[bytes] @@ -277,7 +283,9 @@ def make_notebook_html_bundle( filename, ] if no_input: - cmd.append('--no-input') + cmd.append('--no-input') + if no_tag_input: + cmd.append('--TagRemovePreprocessor.remove_input_tags=remove_input') try: output = check_output(cmd) except subprocess.CalledProcessError: diff --git a/rsconnect/main.py b/rsconnect/main.py index 3e236fae..8fe0996c 100644 --- a/rsconnect/main.py +++ b/rsconnect/main.py @@ -521,6 +521,7 @@ def _deploy_bundle( ) @click.option("--verbose", "-v", is_flag=True, help="Print detailed messages.") @click.option("--no-input", is_flag=True, default=None, help="Hide code cells when rendering output") +@click.option("--no-tag-input", is_flag=True, default=None, help="Hide input of cells with the 'remove_input' tag") @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), @@ -542,6 +543,7 @@ def deploy_notebook( file, extra_files, no_input, + no_tag_input, ): set_verbosity(verbose) @@ -570,8 +572,9 @@ 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, no_input) - + bundle = create_notebook_deployment_bundle( + file, extra_files, app_mode, python, environment, False, no_input, no_tag_input + ) _deploy_bundle( connect_server, app_store, file, app_id, app_mode, deployment_name, title, default_title, bundle, ) @@ -937,12 +940,15 @@ def write_manifest(): "--force-generate", "-g", is_flag=True, help='Force generating "requirements.txt", even if it already exists.', ) @click.option("--no-input", help="Hide code cells when rendering output") +@click.option("--no-tag-input", is_flag=True, default=None, help="Hide input of cells with the 'remove_input' tag") @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), ) -def write_manifest_notebook(overwrite, python, conda, force_generate, verbose, file, extra_files, no_input=None): +def write_manifest_notebook( + overwrite, python, conda, force_generate, verbose, file, extra_files, no_input=None, no_tag_input=None +): set_verbosity(verbose) with cli_feedback("Checking arguments"): validate_file_is_notebook(file) @@ -961,7 +967,12 @@ def write_manifest_notebook(overwrite, python, conda, force_generate, verbose, f with cli_feedback("Creating manifest.json"): environment_file_exists = write_notebook_manifest_json( - file, environment, AppModes.JUPYTER_NOTEBOOK, extra_files, no_input, + file, + environment, + AppModes.JUPYTER_NOTEBOOK, + extra_files, + no_input, + no_tag_input, ) if environment_file_exists and not force_generate: From 12e5781dc52c6e11c8ecfb60ed2d8feb7851250d Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Mon, 12 Jul 2021 16:33:41 -0400 Subject: [PATCH 09/26] rename no_input to hide_all_input; rename no_tag_input to hide_tagged_input --- rsconnect/actions.py | 32 +++++++++++++++++++------------- rsconnect/bundle.py | 30 +++++++++++++++--------------- rsconnect/main.py | 8 ++++---- 3 files changed, 38 insertions(+), 32 deletions(-) diff --git a/rsconnect/actions.py b/rsconnect/actions.py index fa3d24d7..6133c2c2 100644 --- a/rsconnect/actions.py +++ b/rsconnect/actions.py @@ -455,8 +455,8 @@ def deploy_jupyter_notebook( conda_mode=False, force_generate=False, log_callback=None, - no_input=None, - no_tag_input=None, + hide_all_input=None, + hide_tagged_input=None, ): """ A function to deploy a Jupyter notebook to Connect. Depending on the files involved @@ -480,8 +480,8 @@ def deploy_jupyter_notebook( (the default) the lines from the deployment log will be returned as a sequence. If a log callback is provided, then None will be returned for the log lines part of the return tuple. - :param no_input: if True, will hide notebook code cells when rendering to output - :param no_tag_input: If True, hides the input of cells tagged with "remove_input" + :param hide_all_input: if True, will hide all input cells when rendering output + :param hide_tagged_input: If True, will hide input code cells with the 'hide_input' tag when rendering output :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. """ @@ -491,7 +491,7 @@ def deploy_jupyter_notebook( ) python, environment = get_python_env_info(file_name, python, conda_mode=conda_mode, force_generate=force_generate,) bundle = create_notebook_deployment_bundle( - file_name, extra_files, app_mode, python, environment, no_input, no_tag_input + file_name, extra_files, app_mode, python, environment, hide_all_input, hide_tagged_input ) return _finalize_deploy( connect_server, @@ -1085,8 +1085,8 @@ def get_python_env_info(file_name, python, conda_mode=False, force_generate=Fals def create_notebook_deployment_bundle( file_name, extra_files, app_mode, python, environment, extra_files_need_validating=True, - no_input=None, - no_tag_input=None, + hide_all_input=None, + hide_tagged_input=None, ): """ Create an in-memory bundle, ready to deploy. @@ -1097,6 +1097,8 @@ def create_notebook_deployment_bundle( :param python: information about the version of Python being used. :param environment: environmental information. :param extra_files_need_validating: a flag indicating whether the list of extra + :param hide_all_input: if True, will hide all input cells when rendering output + :param hide_tagged_input: If True, will hide input code cells with the 'hide_input' tag when rendering output files should be validated or not. Part of validating includes qualifying each with the parent directory of the notebook file. If you provide False here, make sure the names are properly qualified first. @@ -1109,13 +1111,13 @@ def create_notebook_deployment_bundle( if app_mode == AppModes.STATIC: try: - return make_notebook_html_bundle(file_name, python, no_input, no_tag_input) + return make_notebook_html_bundle(file_name, python, hide_all_input, hide_tagged_input) except subprocess.CalledProcessError as exc: # Jupyter rendering failures are often due to # user code failing, vs. an internal failure of rsconnect-python. raise api.RSConnectException(str(exc)) else: - return make_notebook_source_bundle(file_name, environment, extra_files, no_input, no_tag_input) + return make_notebook_source_bundle(file_name, environment, extra_files, hide_all_input, hide_tagged_input) def create_api_deployment_bundle( @@ -1185,8 +1187,8 @@ def create_notebook_manifest_and_environment_file( app_mode=None, extra_files=None, force=True, - no_input=None, - no_tag_input=None, + hide_all_input=None, + hide_tagged_input=None, ): """ Creates and writes a manifest.json file for the given notebook entry point file. @@ -1202,16 +1204,18 @@ def create_notebook_manifest_and_environment_file( :param extra_files: any extra files that should be included in the manifest. :param force: if True, forces the environment file to be written. even if it already exists. + :param hide_all_input: if True, will hide all input cells when rendering output + :param hide_tagged_input: If True, will hide input code cells with the 'hide_input' tag when rendering output :return: """ if ( - not write_notebook_manifest_json(entry_point_file, environment, app_mode, extra_files, no_input, no_tag_input) + not write_notebook_manifest_json(entry_point_file, environment, app_mode, extra_files, hide_all_input, hide_tagged_input) or force ): write_environment_file(environment, dirname(entry_point_file)) -def write_notebook_manifest_json(entry_point_file, environment, app_mode, extra_files, no_input, no_tag_input): +def write_notebook_manifest_json(entry_point_file, environment, app_mode, extra_files, hide_all_input, hide_tagged_input): """ 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 @@ -1224,6 +1228,8 @@ def write_notebook_manifest_json(entry_point_file, environment, app_mode, extra_ :param app_mode: the application mode to assume. If this is None, the extension portion of the entry point file name will be used to derive one. :param extra_files: any extra files that should be included in the manifest. + :param hide_all_input: if True, will hide all input cells when rendering output + :param hide_tagged_input: If True, will hide input code cells with the 'hide_input' tag when rendering output :return: whether or not the environment file (requirements.txt, environment.yml, etc.) that goes along with the manifest exists. """ diff --git a/rsconnect/bundle.py b/rsconnect/bundle.py index db905c01..0d8ae3a9 100644 --- a/rsconnect/bundle.py +++ b/rsconnect/bundle.py @@ -136,7 +136,7 @@ def bundle_add_buffer(bundle, filename, contents): bundle.addfile(file_info, buf) -def write_manifest(relative_dir, nb_name, environment, output_dir, no_input=None, no_tag_input=None): +def write_manifest(relative_dir, nb_name, environment, output_dir, hide_all_input=False, hide_tagged_input=False): # type: (...) -> typing.Tuple[list, list] """Create a manifest for source publishing the specified notebook. @@ -147,10 +147,10 @@ def write_manifest(relative_dir, nb_name, environment, output_dir, no_input=None """ manifest_filename = "manifest.json" manifest = make_source_manifest(nb_name, environment, AppModes.JUPYTER_NOTEBOOK) - if no_input: - manifest['jupyter'] = {'no_input': no_input} - if no_tag_input: - manifest['jupyter'] = {'no_tag_input': no_tag_input} + if hide_all_input: + manifest['jupyter'] = {'hide_all_input': hide_all_input} + if hide_tagged_input: + manifest['jupyter'] = {'hide_tagged_input': hide_tagged_input} manifest_file = join(output_dir, manifest_filename) created = [] skipped = [] @@ -208,8 +208,8 @@ def make_notebook_source_bundle( file, # type: str environment, # type: Environment extra_files=None, # type: typing.Optional[typing.List[str]] - no_input=None, - no_tag_input=None, + hide_all_input=False, + hide_tagged_input=False, ): # type: (...) -> typing.IO[bytes] """Create a bundle containing the specified notebook and python environment. @@ -222,10 +222,10 @@ def make_notebook_source_bundle( nb_name = basename(file) manifest = make_source_manifest(nb_name, environment, AppModes.JUPYTER_NOTEBOOK) - if no_input: - manifest['jupyter'] = {'no_input': no_input} - if no_tag_input: - manifest['jupyter'] = {'no_tag_input': no_tag_input} + if hide_all_input: + manifest['jupyter'] = {'hide_all_input': hide_all_input} + if hide_tagged_input: + manifest['jupyter'] = {'hide_tagged_input': hide_tagged_input} manifest_add_file(manifest, nb_name, base_dir) manifest_add_buffer(manifest, environment.filename, environment.contents) @@ -268,8 +268,8 @@ def make_html_manifest(filename): def make_notebook_html_bundle( filename, # type: str python, # type: str - no_input=None, - no_tag_input=None, + hide_all_input=False, + hide_tagged_input=False, check_output=subprocess.check_output, # type: typing.Callable ): # type: (...) -> typing.IO[bytes] @@ -285,9 +285,9 @@ def make_notebook_html_bundle( "--to=html", filename, ] - if no_input: + if hide_all_input: cmd.append('--no-input') - if no_tag_input: + if hide_tagged_input: cmd.append('--TagRemovePreprocessor.remove_input_tags=remove_input') try: output = check_output(cmd) diff --git a/rsconnect/main.py b/rsconnect/main.py index 8fe0996c..3c6b9f66 100644 --- a/rsconnect/main.py +++ b/rsconnect/main.py @@ -520,8 +520,8 @@ def _deploy_bundle( "--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.option("--no-input", is_flag=True, default=None, help="Hide code cells when rendering output") -@click.option("--no-tag-input", is_flag=True, default=None, help="Hide input of cells with the 'remove_input' tag") +@click.option("--hide-all-input", is_flag=True, default=None, help="Hide all input cells when rendering output") +@click.option("--hide-tagged-input", is_flag=True, default=None, help="Hide input code cells with the 'hide_input' tag") @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), @@ -939,8 +939,8 @@ def write_manifest(): @click.option( "--force-generate", "-g", is_flag=True, help='Force generating "requirements.txt", even if it already exists.', ) -@click.option("--no-input", help="Hide code cells when rendering output") -@click.option("--no-tag-input", is_flag=True, default=None, help="Hide input of cells with the 'remove_input' tag") +@click.option("--hide-all-input", help="Hide all input cells when rendering output") +@click.option("--hide-tagged-input", is_flag=True, default=None, help="Hide input code cells with the 'hide_input' tag") @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( From 1e84f0639a4328455f71718675473acc2733fcf6 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Mon, 12 Jul 2021 16:47:03 -0400 Subject: [PATCH 10/26] Update changelog for hide Jupyter code cell API --- CHANGELOG.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8177d740..c548a549 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,32 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Added +- Ability to hide code cells when rendering Jupyter notebooks. + +After setting up Connect and rsconnect-python, the user can render a Jupyter notebook without its corresponding code cells by passing the ' hide-all-input' flag through the rsconnect cli: + +``` +rsconnect deploy notebook \ + -n server \ + -k APIKey \ + --hide-all-input \ + hello_world.ipynb +``` + +To selectively hide the input of cells, the user can add a tag call 'hide_input' to the cell, then pass the ' hide-tagged-input' flag through the rsconnect cli: + +``` +rsconnect deploy notebook \ + -n server \ + -k APIKey \ + --hide-tagged-input \ + hello_world.ipynb +``` + + ## [1.5.3] - 2021-05-06 ### Added From db19964bfb455ef7f23b7ff23c4a690ba03f6361 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Wed, 14 Jul 2021 16:13:52 -0400 Subject: [PATCH 11/26] update parameters to match click --- rsconnect/main.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rsconnect/main.py b/rsconnect/main.py index 3c6b9f66..8fa9552b 100644 --- a/rsconnect/main.py +++ b/rsconnect/main.py @@ -542,8 +542,8 @@ def deploy_notebook( verbose, file, extra_files, - no_input, - no_tag_input, + hide_all_input, + hide_tagged_input, ): set_verbosity(verbose) @@ -573,7 +573,7 @@ def deploy_notebook( with cli_feedback("Creating deployment bundle"): bundle = create_notebook_deployment_bundle( - file, extra_files, app_mode, python, environment, False, no_input, no_tag_input + file, extra_files, app_mode, python, environment, False, hide_all_input, hide_tagged_input ) _deploy_bundle( connect_server, app_store, file, app_id, app_mode, deployment_name, title, default_title, bundle, @@ -947,7 +947,7 @@ def write_manifest(): "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, no_input=None, no_tag_input=None + overwrite, python, conda, force_generate, verbose, file, extra_files, hide_all_input=None, hide_tagged_input=None ): set_verbosity(verbose) with cli_feedback("Checking arguments"): @@ -971,8 +971,8 @@ def write_manifest_notebook( environment, AppModes.JUPYTER_NOTEBOOK, extra_files, - no_input, - no_tag_input, + hide_all_input, + hide_tagged_input, ) if environment_file_exists and not force_generate: From 948a32bb8e2eaa5e298c2eb5cf1bbeb97c85d259 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Fri, 16 Jul 2021 14:27:40 -0400 Subject: [PATCH 12/26] fix hide tagged input not working for static mode --- rsconnect/bundle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rsconnect/bundle.py b/rsconnect/bundle.py index 0d8ae3a9..baf78554 100644 --- a/rsconnect/bundle.py +++ b/rsconnect/bundle.py @@ -288,7 +288,7 @@ def make_notebook_html_bundle( if hide_all_input: cmd.append('--no-input') if hide_tagged_input: - cmd.append('--TagRemovePreprocessor.remove_input_tags=remove_input') + cmd.append('--TagRemovePreprocessor.remove_input_tags=hide_input') try: output = check_output(cmd) except subprocess.CalledProcessError: From 8e7e6878c1fd10fc3c31f6d4ee1d71d4c9e6c360 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Mon, 19 Jul 2021 13:20:57 -0400 Subject: [PATCH 13/26] hide_all_input will take precedence if both hide_all_input and hide_tagged_input are selected --- rsconnect/bundle.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rsconnect/bundle.py b/rsconnect/bundle.py index baf78554..0d9b0e25 100644 --- a/rsconnect/bundle.py +++ b/rsconnect/bundle.py @@ -147,9 +147,9 @@ def write_manifest(relative_dir, nb_name, environment, output_dir, hide_all_inpu """ manifest_filename = "manifest.json" manifest = make_source_manifest(nb_name, environment, AppModes.JUPYTER_NOTEBOOK) - if hide_all_input: + if hide_all_input and hide_tagged_input or hide_all_input: manifest['jupyter'] = {'hide_all_input': hide_all_input} - if hide_tagged_input: + elif hide_tagged_input: manifest['jupyter'] = {'hide_tagged_input': hide_tagged_input} manifest_file = join(output_dir, manifest_filename) created = [] From 226f379246740b3ebad567a8f36c868b622dc025 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Mon, 19 Jul 2021 13:26:14 -0400 Subject: [PATCH 14/26] Revert "hide_all_input will take precedence if both hide_all_input and hide_tagged_input are selected" This reverts commit 8e7e6878c1fd10fc3c31f6d4ee1d71d4c9e6c360. --- rsconnect/bundle.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rsconnect/bundle.py b/rsconnect/bundle.py index 0d9b0e25..baf78554 100644 --- a/rsconnect/bundle.py +++ b/rsconnect/bundle.py @@ -147,9 +147,9 @@ def write_manifest(relative_dir, nb_name, environment, output_dir, hide_all_inpu """ manifest_filename = "manifest.json" manifest = make_source_manifest(nb_name, environment, AppModes.JUPYTER_NOTEBOOK) - if hide_all_input and hide_tagged_input or hide_all_input: + if hide_all_input: manifest['jupyter'] = {'hide_all_input': hide_all_input} - elif hide_tagged_input: + if hide_tagged_input: manifest['jupyter'] = {'hide_tagged_input': hide_tagged_input} manifest_file = join(output_dir, manifest_filename) created = [] From cb05102b0531995afc3b1158791c18c6a2f91788 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Mon, 19 Jul 2021 13:55:22 -0400 Subject: [PATCH 15/26] hide_all_input will take precedence when both hide_all_input and hide_tagged_input are selected --- rsconnect/bundle.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rsconnect/bundle.py b/rsconnect/bundle.py index baf78554..e8024b28 100644 --- a/rsconnect/bundle.py +++ b/rsconnect/bundle.py @@ -285,9 +285,9 @@ def make_notebook_html_bundle( "--to=html", filename, ] - if hide_all_input: + if hide_all_input and hide_tagged_input or hide_all_input: cmd.append('--no-input') - if hide_tagged_input: + elif hide_tagged_input: cmd.append('--TagRemovePreprocessor.remove_input_tags=hide_input') try: output = check_output(cmd) From a7cc3f5cceda8be05cd65ad835091f892e2da593 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Tue, 20 Jul 2021 11:00:46 -0400 Subject: [PATCH 16/26] update manifest creation so hide input options don't overwrite one another --- rsconnect/bundle.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rsconnect/bundle.py b/rsconnect/bundle.py index e8024b28..7ba4b690 100644 --- a/rsconnect/bundle.py +++ b/rsconnect/bundle.py @@ -148,9 +148,9 @@ def write_manifest(relative_dir, nb_name, environment, output_dir, hide_all_inpu manifest_filename = "manifest.json" manifest = make_source_manifest(nb_name, environment, AppModes.JUPYTER_NOTEBOOK) if hide_all_input: - manifest['jupyter'] = {'hide_all_input': hide_all_input} + manifest['jupyter'].update({'hide_all_input': hide_all_input}) if hide_tagged_input: - manifest['jupyter'] = {'hide_tagged_input': hide_tagged_input} + manifest['jupyter'].update({'hide_tagged_input': hide_tagged_input}) manifest_file = join(output_dir, manifest_filename) created = [] skipped = [] @@ -223,9 +223,9 @@ def make_notebook_source_bundle( manifest = make_source_manifest(nb_name, environment, AppModes.JUPYTER_NOTEBOOK) if hide_all_input: - manifest['jupyter'] = {'hide_all_input': hide_all_input} + manifest['jupyter'].update({'hide_all_input': hide_all_input}) if hide_tagged_input: - manifest['jupyter'] = {'hide_tagged_input': hide_tagged_input} + manifest['jupyter'].update({'hide_tagged_input': hide_tagged_input}) manifest_add_file(manifest, nb_name, base_dir) manifest_add_buffer(manifest, environment.filename, environment.contents) From 1987836da30a50c7984d2497a070d1b6f4dd1231 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Tue, 20 Jul 2021 11:01:14 -0400 Subject: [PATCH 17/26] update defaults --- rsconnect/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rsconnect/main.py b/rsconnect/main.py index 699df649..272b8f0a 100644 --- a/rsconnect/main.py +++ b/rsconnect/main.py @@ -520,8 +520,8 @@ def _deploy_bundle( "--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.option("--hide-all-input", is_flag=True, default=None, help="Hide all input cells when rendering output") -@click.option("--hide-tagged-input", is_flag=True, default=None, help="Hide input code cells with the 'hide_input' tag") +@click.option("--hide-all-input", is_flag=True, default=False, help="Hide all input cells when rendering output") +@click.option("--hide-tagged-input", is_flag=True, default=False, help="Hide input code cells with the 'hide_input' tag") @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), From 6040f1bbe45f3fa979b4a925784433eab89db4a2 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Tue, 20 Jul 2021 11:15:16 -0400 Subject: [PATCH 18/26] add jupyter manifest handling without changing default manifest format --- rsconnect/bundle.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rsconnect/bundle.py b/rsconnect/bundle.py index 7ba4b690..4b7230f7 100644 --- a/rsconnect/bundle.py +++ b/rsconnect/bundle.py @@ -148,8 +148,10 @@ def write_manifest(relative_dir, nb_name, environment, output_dir, hide_all_inpu manifest_filename = "manifest.json" manifest = make_source_manifest(nb_name, environment, AppModes.JUPYTER_NOTEBOOK) if hide_all_input: + if 'jupyter' not in manifest: manifest['jupyter']= {} manifest['jupyter'].update({'hide_all_input': hide_all_input}) if hide_tagged_input: + if 'jupyter' not in manifest: manifest['jupyter']= {} manifest['jupyter'].update({'hide_tagged_input': hide_tagged_input}) manifest_file = join(output_dir, manifest_filename) created = [] @@ -223,8 +225,10 @@ def make_notebook_source_bundle( manifest = make_source_manifest(nb_name, environment, AppModes.JUPYTER_NOTEBOOK) if hide_all_input: + if 'jupyter' not in manifest: manifest['jupyter']= {} manifest['jupyter'].update({'hide_all_input': hide_all_input}) if hide_tagged_input: + if 'jupyter' not in manifest: manifest['jupyter']= {} manifest['jupyter'].update({'hide_tagged_input': hide_tagged_input}) manifest_add_file(manifest, nb_name, base_dir) manifest_add_buffer(manifest, environment.filename, environment.contents) From 4ff75fb3c5cedbaa27314394896d373b1baceb0b Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Tue, 20 Jul 2021 11:27:02 -0400 Subject: [PATCH 19/26] update defaults --- rsconnect/actions.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rsconnect/actions.py b/rsconnect/actions.py index 31c72643..6d061552 100644 --- a/rsconnect/actions.py +++ b/rsconnect/actions.py @@ -473,8 +473,8 @@ def deploy_jupyter_notebook( conda_mode=False, force_generate=False, log_callback=None, - hide_all_input=None, - hide_tagged_input=None, + hide_all_input=False, + hide_tagged_input=False, ): """ A function to deploy a Jupyter notebook to Connect. Depending on the files involved @@ -1205,8 +1205,8 @@ def create_notebook_manifest_and_environment_file( app_mode=None, extra_files=None, force=True, - hide_all_input=None, - hide_tagged_input=None, + hide_all_input=False, + hide_tagged_input=False, ): """ Creates and writes a manifest.json file for the given notebook entry point file. From 9ff59bac19778933aca7f73be8bf7334b0d4e795 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Fri, 23 Jul 2021 10:34:54 -0400 Subject: [PATCH 20/26] add Python 2 compatibility for hide cell feature --- rsconnect/bundle.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rsconnect/bundle.py b/rsconnect/bundle.py index 4b7230f7..1f84a946 100644 --- a/rsconnect/bundle.py +++ b/rsconnect/bundle.py @@ -289,10 +289,14 @@ def make_notebook_html_bundle( "--to=html", filename, ] + version = check_output([python, '--version']).decode("utf-8") if hide_all_input and hide_tagged_input or hide_all_input: cmd.append('--no-input') elif hide_tagged_input: - cmd.append('--TagRemovePreprocessor.remove_input_tags=hide_input') + if version >= 'Python 3': + cmd.append('--TagRemovePreprocessor.remove_input_tags=hide_input') + else: + cmd.append("--TagRemovePreprocessor.remove_input_tags={'hide_input'}") try: output = check_output(cmd) except subprocess.CalledProcessError: From 2922c7422b0502564d4614ee72e69155ab9e2475 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Mon, 26 Jul 2021 12:17:35 -0400 Subject: [PATCH 21/26] Update README with Hide Jupyter Notebook Input Code Cells --- README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/README.md b/README.md index 99e90330..dda261bf 100644 --- a/README.md +++ b/README.md @@ -503,3 +503,27 @@ directory specified above.
Generated from rsconnect-python {{ rsconnect_python.version }}
+ +### Hide Jupyter Notebook Input Code Cells + +The user can render a Jupyter notebook without its corresponding input code cells by passing the '--hide-all-input' flag through the cli: + +``` +rsconnect deploy notebook \ + --server https://connect.example.org:3939 \ + --api-key my-api-key \ + --hide-all-input \ + my-notebook.ipynb +``` + +To selectively hide input cells in a Jupyter notebook, the user needs to follow a two step process: +1. tag cells with the 'hide_input' tag, +2. then pass the ' --hide-tagged-input' flag through the cli: + +``` +rsconnect deploy notebook \ + --server https://connect.example.org:3939 \ + --api-key my-api-key \ + --hide-tagged-input \ + my-notebook.ipynb +``` From 7bdbb8f9af315aee9869176c085d55d4c9472186 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Mon, 26 Jul 2021 13:30:43 -0400 Subject: [PATCH 22/26] move version checking --- rsconnect/bundle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rsconnect/bundle.py b/rsconnect/bundle.py index 1f84a946..2368ad46 100644 --- a/rsconnect/bundle.py +++ b/rsconnect/bundle.py @@ -289,10 +289,10 @@ def make_notebook_html_bundle( "--to=html", filename, ] - version = check_output([python, '--version']).decode("utf-8") if hide_all_input and hide_tagged_input or hide_all_input: cmd.append('--no-input') elif hide_tagged_input: + version = check_output([python, '--version']).decode("utf-8") if version >= 'Python 3': cmd.append('--TagRemovePreprocessor.remove_input_tags=hide_input') else: From ff6c7fd1622c2ddf0583e5215ec92b92e0dff0ff Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Mon, 26 Jul 2021 14:16:33 -0400 Subject: [PATCH 23/26] update readme wrt hide input cell Jupyter dependencies --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index dda261bf..9738cf32 100644 --- a/README.md +++ b/README.md @@ -527,3 +527,11 @@ rsconnect deploy notebook \ --hide-tagged-input \ my-notebook.ipynb ``` + +By default, rsconnect-python does not install Jupyter notebook related depenencies. These dependencies are installed via rsconnect-jupyter. When the user is using the hide input features in rsconnect-python by itself without rsconnect-jupyter, he/she needs to install the following package depenecies: + +``` +notebook +nbformat +nbconvert>=5.6.1 +``` From dddd5d5fcb2e0222e678f03c5f79f079de360129 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Mon, 26 Jul 2021 15:08:38 -0400 Subject: [PATCH 24/26] update TagRemovePreprocessor command for Python 2 --- rsconnect/bundle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rsconnect/bundle.py b/rsconnect/bundle.py index 2368ad46..2a690433 100644 --- a/rsconnect/bundle.py +++ b/rsconnect/bundle.py @@ -296,7 +296,7 @@ def make_notebook_html_bundle( if version >= 'Python 3': cmd.append('--TagRemovePreprocessor.remove_input_tags=hide_input') else: - cmd.append("--TagRemovePreprocessor.remove_input_tags={'hide_input'}") + cmd.append('''--TagRemovePreprocessor.remove_input_tags="['hide_input']"''') try: output = check_output(cmd) except subprocess.CalledProcessError: From 428426e17e3fec35e79ab48798c26528ccf2bd67 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Mon, 26 Jul 2021 15:28:16 -0400 Subject: [PATCH 25/26] update TagRemovePreprocessor for Python 2 --- rsconnect/bundle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rsconnect/bundle.py b/rsconnect/bundle.py index 2a690433..6b1daf9b 100644 --- a/rsconnect/bundle.py +++ b/rsconnect/bundle.py @@ -296,7 +296,7 @@ def make_notebook_html_bundle( if version >= 'Python 3': cmd.append('--TagRemovePreprocessor.remove_input_tags=hide_input') else: - cmd.append('''--TagRemovePreprocessor.remove_input_tags="['hide_input']"''') + cmd.append("--TagRemovePreprocessor.remove_input_tags=set(['hide_input'])") try: output = check_output(cmd) except subprocess.CalledProcessError: From e1bd07cfa25b728ea9fde163b088d0047c5d27f9 Mon Sep 17 00:00:00 2001 From: Bincheng Wu Date: Tue, 27 Jul 2021 17:11:54 -0400 Subject: [PATCH 26/26] update TagRemovePreprocessor to remove quotes for jupyter cli parsing --- rsconnect/bundle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rsconnect/bundle.py b/rsconnect/bundle.py index 6b1daf9b..39d1e9a1 100644 --- a/rsconnect/bundle.py +++ b/rsconnect/bundle.py @@ -296,7 +296,7 @@ def make_notebook_html_bundle( if version >= 'Python 3': cmd.append('--TagRemovePreprocessor.remove_input_tags=hide_input') else: - cmd.append("--TagRemovePreprocessor.remove_input_tags=set(['hide_input'])") + cmd.append("--TagRemovePreprocessor.remove_input_tags=['hide_input']") try: output = check_output(cmd) except subprocess.CalledProcessError: