diff --git a/repo2docker/buildpacks/conda/__init__.py b/repo2docker/buildpacks/conda/__init__.py index 7ec8eda5..e277cc40 100644 --- a/repo2docker/buildpacks/conda/__init__.py +++ b/repo2docker/buildpacks/conda/__init__.py @@ -1,4 +1,5 @@ """BuildPack for conda environments""" + import os import re import warnings @@ -13,10 +14,10 @@ from .._r_base import rstudio_base_scripts from ..base import BaseImage from .matlab import ( - matlab_requirements_scripts, matlab_installation_scripts, - matlab_python_engine_installation_scripts, matlab_proxy_installation_scripts, + matlab_python_engine_installation_scripts, + matlab_requirements_scripts, ) # pattern for parsing conda dependency line @@ -483,7 +484,7 @@ def _get_assemble_scripts_for_r(self): installR_path = self.binder_path("install.R") if not os.path.exists(installR_path): return [] - repo_url = 'https://cloud.r-project.org/' + repo_url = "https://cloud.r-project.org/" return [ ( "${NB_USER}", @@ -512,8 +513,12 @@ def _get_assemble_scripts_for_matlab(self): raise ValueError("mpm.yml must contain a 'release' field") scripts = matlab_requirements_scripts(config["release"], self.base_image) matlab_dir = "/opt/matlab" - scripts += matlab_installation_scripts(config["release"], config.get("products", []), matlab_dir) - scripts += matlab_python_engine_installation_scripts(config["release"], matlab_dir) + scripts += matlab_installation_scripts( + config["release"], config.get("products", []), matlab_dir + ) + scripts += matlab_python_engine_installation_scripts( + config["release"], matlab_dir + ) scripts += matlab_proxy_installation_scripts() return scripts @@ -524,18 +529,23 @@ def _get_env_for_matlab(self): return [("MW_CONTEXT_TAGS", "MATLAB_PROXY:JUPYTER:MPM:V1")] def _get_jlab_extension_script( - self, grdm_jlab_release_tag, grdm_jlab_filename_body, jupyter_resource_usage_tag, + self, + grdm_jlab_release_tag, + grdm_jlab_filename_body, + jupyter_resource_usage_tag, perform_labextension_install=True, perform_nbextension_install=True, perform_jlpm_cache_clean=True, perform_npm_cache_clean=True, ): - grdm_jlab_release_url = ( - f"https://github.com/RCOSDP/CS-jupyterlab-grdm/releases/download/{grdm_jlab_release_tag}" + grdm_jlab_release_url = f"https://github.com/RCOSDP/CS-jupyterlab-grdm/releases/download/{grdm_jlab_release_tag}" + jupyter_resource_usage_release_url = ( + "https://github.com/RCOSDP/CS-jupyter-resource-usage" ) - jupyter_resource_usage_release_url = "https://github.com/RCOSDP/CS-jupyter-resource-usage" grdm_jlab_filename_tar_gz = f"{grdm_jlab_filename_body}.tar.gz" - grdm_jlab_filename_tgz = "{}.tgz".format(grdm_jlab_filename_body.replace("_", "-")) + grdm_jlab_filename_tgz = "{}.tgz".format( + grdm_jlab_filename_body.replace("_", "-") + ) jlpm_cache_clean = "" if perform_jlpm_cache_clean: jlpm_cache_clean = "jlpm cache clean" @@ -578,16 +588,24 @@ def _get_jlab_extension_script( def get_custom_extension_script(self, post): grdm_jlab3_release_tag = "0.2.0" - grdm_jlab3_filename_body = f"rdm_binderhub_jlabextension-refs.tags.{grdm_jlab3_release_tag}" + grdm_jlab3_filename_body = ( + f"rdm_binderhub_jlabextension-refs.tags.{grdm_jlab3_release_tag}" + ) grdm_jlab4_release_tag = "0.3.0" - grdm_jlab4_filename_body = f"rdm_binderhub_jlabextension-{grdm_jlab4_release_tag}" + grdm_jlab4_filename_body = ( + f"rdm_binderhub_jlabextension-{grdm_jlab4_release_tag}" + ) jlab3_ext_script = self._get_jlab_extension_script( - grdm_jlab3_release_tag, grdm_jlab3_filename_body, 'v2023.07', + grdm_jlab3_release_tag, + grdm_jlab3_filename_body, + "v2023.07", ) jlab4_ext_script = self._get_jlab_extension_script( - grdm_jlab4_release_tag, grdm_jlab4_filename_body, 'v2024.04', + grdm_jlab4_release_tag, + grdm_jlab4_filename_body, + "v2024.04", perform_labextension_install=False, perform_nbextension_install=False, perform_jlpm_cache_clean=False, @@ -595,9 +613,14 @@ def get_custom_extension_script(self, post): ) if post: - install_grdm = 'options(repos = c(CRAN = \\"https://cloud.r-project.org/\\")); ' \ - + 'if (!(\\"remotes\\" %in% rownames(installed.packages()))) install.packages(\\"remotes\\"); ' \ - + 'remotes::install_github(\\"RCOSDP/CS-rstudio-grdm\\", type = \\"source\\")' + # install from the GitHub archive tarball; install_github queries + # api.github.com, which is rate-limited for unauthenticated builds + install_grdm = ( + 'options(repos = c(CRAN = \\"https://cloud.r-project.org/\\")); ' + + 'if (!(\\"remotes\\" %in% rownames(installed.packages()))) install.packages(\\"remotes\\"); ' + + 'remotes::install_url(\\"https://github.com/RCOSDP/CS-rstudio-grdm/archive/refs/heads/master.tar.gz\\"); ' + + 'stopifnot(\\"rdmaddins\\" %in% rownames(installed.packages()))' + ) bash_scripts = f""" if [ -x \\"$(command -v R)\\" ]; then R -e '{install_grdm}'; fi """ diff --git a/repo2docker/buildpacks/r.py b/repo2docker/buildpacks/r.py index ddc18ed3..4c77e591 100644 --- a/repo2docker/buildpacks/r.py +++ b/repo2docker/buildpacks/r.py @@ -350,11 +350,11 @@ def get_build_scripts(self): ), ( "${NB_USER}", - # Install a pinned version of devtools, IRKernel and shiny + # Install a pinned version of remotes, devtools, IRKernel and shiny rf""" export EXPANDED_CRAN_MIRROR_URL="$(. /etc/os-release && echo {cran_mirror_url} | envsubst)" && \ - R --quiet -e "install.packages(c('devtools', 'IRkernel', 'shiny'), repos=Sys.getenv(\"EXPANDED_CRAN_MIRROR_URL\"))" && \ - R --quiet -e "IRkernel::installspec(prefix=Sys.getenv(\"NB_PYTHON_PREFIX\"))" + R --no-save --no-restore --no-init-file --no-environ --quiet -e "install.packages(c('remotes', 'devtools', 'IRkernel', 'shiny'), repos=Sys.getenv(\"EXPANDED_CRAN_MIRROR_URL\"))" && \ + R --no-save --no-restore --no-init-file --no-environ --quiet -e "IRkernel::installspec(prefix=Sys.getenv(\"NB_PYTHON_PREFIX\"))" """, ), ] @@ -390,7 +390,7 @@ def get_preassemble_scripts(self): "${NB_USER}", # Delete /tmp/downloaded_packages only if install.R fails, as the second # invocation of install.R might be able to reuse them - f"Rscript {installR_path} && touch /tmp/.preassembled || true && rm -rf /tmp/downloaded_packages", + f"Rscript --no-save --no-restore --no-init-file --no-environ {installR_path} && touch /tmp/.preassembled || true && rm -rf /tmp/downloaded_packages", ) ] @@ -408,14 +408,17 @@ def get_assemble_scripts(self): "${NB_USER}", # only run install.R if the pre-assembly failed # Delete any downloaded packages in /tmp, as they aren't reused by R - f"""if [ ! -f /tmp/.preassembled ]; then Rscript {installR_path}; rm -rf /tmp/downloaded_packages; fi""", + f"""if [ ! -f /tmp/.preassembled ]; then Rscript --no-save --no-restore --no-init-file --no-environ {installR_path}; rm -rf /tmp/downloaded_packages; fi""", ) ] description_R = "DESCRIPTION" if not self.binder_dir and os.path.exists(description_R): assemble_scripts += [ - ("${NB_USER}", 'R --quiet -e "devtools::install_local(getwd())"') + ( + "${NB_USER}", + 'R --no-save --no-restore --no-init-file --no-environ --quiet -e "remotes::install_local()"', + ) ] return assemble_scripts diff --git a/repo2docker/contentproviders/figshare.py b/repo2docker/contentproviders/figshare.py index 6c7b26c8..c2331b78 100644 --- a/repo2docker/contentproviders/figshare.py +++ b/repo2docker/contentproviders/figshare.py @@ -41,7 +41,9 @@ def __init__(self): # We may need to add other item types in future, see # https://github.com/jupyterhub/repo2docker/pull/1001#issuecomment-760107436 # for a list - url_regex = re.compile(r"(.*)/articles/(code/|dataset/)?([^/]+)/(\d+)(/)?(\d+)?") + url_regex = re.compile( + r"(.*)/articles/(code/|dataset/|software/)?([^/]+)/(\d+)(/)?(\d+)?" + ) def detect(self, doi, ref=None, extra_args=None): """Trigger this provider for things that resolve to a Figshare article""" diff --git a/repo2docker/utils.py b/repo2docker/utils.py index 9c2769e1..05ba1e53 100644 --- a/repo2docker/utils.py +++ b/repo2docker/utils.py @@ -8,7 +8,7 @@ from functools import partial from shutil import copy2, copystat -import chardet +import charset_normalizer from traitlets import Integer, TraitError @@ -94,21 +94,13 @@ def chdir(path): def open_guess_encoding(path): """ Open a file in text mode, specifying its encoding, - that we guess using chardet. + that we guess using charset_normalizer. """ - detector = chardet.universaldetector.UniversalDetector() with open(path, "rb") as f: - for line in f.readlines(): - detector.feed(line) - if detector.done: - break - detector.close() + detector = charset_normalizer.from_fp(f) - file = open(path, encoding=detector.result["encoding"]) - try: + with open(path, encoding=detector.best().encoding) as file: yield file - finally: - file.close() def validate_and_generate_port_mapping(port_mappings): diff --git a/setup.py b/setup.py index d629d2e2..754dc59b 100644 --- a/setup.py +++ b/setup.py @@ -49,7 +49,7 @@ def get_identifier(json): name="jupyter-repo2docker", version=versioneer.get_version(), install_requires=[ - "chardet", + "charset-normalizer", "docker!=5.0.0", "entrypoints", "escapism",