We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Avoid unquoting weirdness of Windows for  `language: r`
### search you tried in the issue tracker

never, r, found

### describe your issue

Multiple reports in https://github.com/lorenzwalthert/precommit (https://github.com/lorenzwalthert/precommit/issues/441, https://github.com/lorenzwalthert/precommit/issues/473) were raised and describe a problem with (un)quoting the long string that runs when `language: r` is setup in `Rscript -e 'xxx'` where `'xxx'` contains [multiple levels of quotes](https://github.com/pre-commit/pre-commit/blob/6896025288691aafd015a4681c59dc105e61b614/pre_commit/languages/r.py#L101). For the readers convenience, the output looks like:
```
[INFO] Installing environment for https://github.com/lorenzwalthert/precommit.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Restored changes from C:\Users\USER\.cache\pre-commit\patch1678401203-36472.
An unexpected error has occurred: CalledProcessError: command: ('C:/PROGRA~1/R/R-41~1.0\\bin\\Rscript.exe', '--vanilla', '-e', '    options(install.packages.compile.from.source = "never", pkgType = "binary")\n            prefix_dir <- \'C:\\\\Users\\\\USER\\\\.cache\\\\pre-commit\\\\repovawmpj_r\'\n        options(\n            repos = c(CRAN = "https://cran.rstudio.com"),\n            renv.consent = TRUE\n        )\n        source("renv/activate.R")\n        renv::restore()\n        activate_statement <- paste0(\n          \'suppressWarnings({\',\n          \'old <- setwd("\', getwd(), \'"); \',\n          \'source("renv/activate.R"); \',\n          \'setwd(old); \',\n          \'renv::load("\', getwd(), \'");})\'\n        )\n        writeLines(activate_statement, \'activate.R\')\n        is_package <- tryCatch(\n          {\n              path_desc <- file.path(prefix_dir, \'DESCRIPTION\')\n              suppressWarnings(desc <- read.dcf(path_desc))\n              "Package" %in% colnames(desc)\n          },\n          error = function(...) FALSE\n        )\n        if (is_package) {\n            renv::install(prefix_dir)\n        }\n        \n    ')
return code: 1
stdout: (none)
stderr:
    During startup - Warning messages:
    1: Setting LC_COLLATE=en_US.UTF-8 failed 
    2: Setting LC_CTYPE=en_US.UTF-8 failed 
    3: Setting LC_MONETARY=en_US.UTF-8 failed 
    4: Setting LC_TIME=en_US.UTF-8 failed 
    Error in options(install.packages.compile.from.source = never, pkgType = binary) : 
      object 'never' not found
    Execution halted
Check the log at C:\Users\USER\.cache\pre-commit\pre-commit.log
```


The solution described by @asottile in https://github.com/lorenzwalthert/precommit/issues/473#issuecomment-1511498032 is to probably write the contents to a temporary file and avoid unquoting within the expression (i.e. the term after `-e`). This should be quite straight forward.

Question is if we can create a good test first to reproduce the offending behavior and whether or not there are tools already in pre-commit to deal with temp files etc. that we could use.



### pre-commit --version

precommit 3.1.1

### .pre-commit-config.yaml

```yaml
repos:
-   repo: https://github.com/lorenzwalthert/precommit
    rev: v0.3.2.9007
    hooks:
    -   id: style-files
```


### ~/.cache/pre-commit/pre-commit.log (if present)

_No response_

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `pre_commit/languages/r.py`
Content:
```
1 from __future__ import annotations
2 
3 import contextlib
4 import os
5 import shlex
6 import shutil
7 from typing import Generator
8 from typing import Sequence
9 
10 from pre_commit import lang_base
11 from pre_commit.envcontext import envcontext
12 from pre_commit.envcontext import PatchesT
13 from pre_commit.envcontext import UNSET
14 from pre_commit.prefix import Prefix
15 from pre_commit.util import cmd_output_b
16 from pre_commit.util import win_exe
17 
18 ENVIRONMENT_DIR = 'renv'
19 RSCRIPT_OPTS = ('--no-save', '--no-restore', '--no-site-file', '--no-environ')
20 get_default_version = lang_base.basic_get_default_version
21 health_check = lang_base.basic_health_check
22 
23 
24 def get_env_patch(venv: str) -> PatchesT:
25     return (
26         ('R_PROFILE_USER', os.path.join(venv, 'activate.R')),
27         ('RENV_PROJECT', UNSET),
28     )
29 
30 
31 @contextlib.contextmanager
32 def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]:
33     envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
34     with envcontext(get_env_patch(envdir)):
35         yield
36 
37 
38 def _prefix_if_file_entry(
39         entry: list[str],
40         prefix: Prefix,
41         *,
42         is_local: bool,
43 ) -> Sequence[str]:
44     if entry[1] == '-e' or is_local:
45         return entry[1:]
46     else:
47         return (prefix.path(entry[1]),)
48 
49 
50 def _rscript_exec() -> str:
51     r_home = os.environ.get('R_HOME')
52     if r_home is None:
53         return 'Rscript'
54     else:
55         return os.path.join(r_home, 'bin', win_exe('Rscript'))
56 
57 
58 def _entry_validate(entry: list[str]) -> None:
59     """
60     Allowed entries:
61     # Rscript -e expr
62     # Rscript path/to/file
63     """
64     if entry[0] != 'Rscript':
65         raise ValueError('entry must start with `Rscript`.')
66 
67     if entry[1] == '-e':
68         if len(entry) > 3:
69             raise ValueError('You can supply at most one expression.')
70     elif len(entry) > 2:
71         raise ValueError(
72             'The only valid syntax is `Rscript -e {expr}`'
73             'or `Rscript path/to/hook/script`',
74         )
75 
76 
77 def _cmd_from_hook(
78         prefix: Prefix,
79         entry: str,
80         args: Sequence[str],
81         *,
82         is_local: bool,
83 ) -> tuple[str, ...]:
84     cmd = shlex.split(entry)
85     _entry_validate(cmd)
86 
87     cmd_part = _prefix_if_file_entry(cmd, prefix, is_local=is_local)
88     return (cmd[0], *RSCRIPT_OPTS, *cmd_part, *args)
89 
90 
91 def install_environment(
92         prefix: Prefix,
93         version: str,
94         additional_dependencies: Sequence[str],
95 ) -> None:
96     lang_base.assert_version_default('r', version)
97 
98     env_dir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
99     os.makedirs(env_dir, exist_ok=True)
100     shutil.copy(prefix.path('renv.lock'), env_dir)
101     shutil.copytree(prefix.path('renv'), os.path.join(env_dir, 'renv'))
102 
103     r_code_inst_environment = f"""\
104         prefix_dir <- {prefix.prefix_dir!r}
105         options(
106             repos = c(CRAN = "https://cran.rstudio.com"),
107             renv.consent = TRUE
108         )
109         source("renv/activate.R")
110         renv::restore()
111         activate_statement <- paste0(
112           'suppressWarnings({{',
113           'old <- setwd("', getwd(), '"); ',
114           'source("renv/activate.R"); ',
115           'setwd(old); ',
116           'renv::load("', getwd(), '");}})'
117         )
118         writeLines(activate_statement, 'activate.R')
119         is_package <- tryCatch(
120           {{
121               path_desc <- file.path(prefix_dir, 'DESCRIPTION')
122               suppressWarnings(desc <- read.dcf(path_desc))
123               "Package" %in% colnames(desc)
124           }},
125           error = function(...) FALSE
126         )
127         if (is_package) {{
128             renv::install(prefix_dir)
129         }}
130         """
131 
132     cmd_output_b(
133         _rscript_exec(), '--vanilla', '-e',
134         _inline_r_setup(r_code_inst_environment),
135         cwd=env_dir,
136     )
137     if additional_dependencies:
138         r_code_inst_add = 'renv::install(commandArgs(trailingOnly = TRUE))'
139         with in_env(prefix, version):
140             cmd_output_b(
141                 _rscript_exec(), *RSCRIPT_OPTS, '-e',
142                 _inline_r_setup(r_code_inst_add),
143                 *additional_dependencies,
144                 cwd=env_dir,
145             )
146 
147 
148 def _inline_r_setup(code: str) -> str:
149     """
150     Some behaviour of R cannot be configured via env variables, but can
151     only be configured via R options once R has started. These are set here.
152     """
153     with_option = f"""\
154     options(install.packages.compile.from.source = "never", pkgType = "binary")
155     {code}
156     """
157     return with_option
158 
159 
160 def run_hook(
161         prefix: Prefix,
162         entry: str,
163         args: Sequence[str],
164         file_args: Sequence[str],
165         *,
166         is_local: bool,
167         require_serial: bool,
168         color: bool,
169 ) -> tuple[int, bytes]:
170     cmd = _cmd_from_hook(prefix, entry, args, is_local=is_local)
171     return lang_base.run_xargs(
172         cmd,
173         file_args,
174         require_serial=require_serial,
175         color=color,
176     )
177
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/pre_commit/languages/r.py b/pre_commit/languages/r.py
--- a/pre_commit/languages/r.py
+++ b/pre_commit/languages/r.py
@@ -4,6 +4,8 @@
 import os
 import shlex
 import shutil
+import tempfile
+import textwrap
 from typing import Generator
 from typing import Sequence
 
@@ -21,6 +23,19 @@
 health_check = lang_base.basic_health_check
 
 
+@contextlib.contextmanager
+def _r_code_in_tempfile(code: str) -> Generator[str, None, None]:
+    """
+    To avoid quoting and escaping issues, avoid `Rscript [options] -e {expr}`
+    but use `Rscript [options] path/to/file_with_expr.R`
+    """
+    with tempfile.TemporaryDirectory() as tmpdir:
+        fname = os.path.join(tmpdir, 'script.R')
+        with open(fname, 'w') as f:
+            f.write(_inline_r_setup(textwrap.dedent(code)))
+        yield fname
+
+
 def get_env_patch(venv: str) -> PatchesT:
     return (
         ('R_PROFILE_USER', os.path.join(venv, 'activate.R')),
@@ -129,20 +144,19 @@
         }}
         """
 
-    cmd_output_b(
-        _rscript_exec(), '--vanilla', '-e',
-        _inline_r_setup(r_code_inst_environment),
-        cwd=env_dir,
-    )
+    with _r_code_in_tempfile(r_code_inst_environment) as f:
+        cmd_output_b(_rscript_exec(), '--vanilla', f, cwd=env_dir)
+
     if additional_dependencies:
         r_code_inst_add = 'renv::install(commandArgs(trailingOnly = TRUE))'
         with in_env(prefix, version):
-            cmd_output_b(
-                _rscript_exec(), *RSCRIPT_OPTS, '-e',
-                _inline_r_setup(r_code_inst_add),
-                *additional_dependencies,
-                cwd=env_dir,
-            )
+            with _r_code_in_tempfile(r_code_inst_add) as f:
+                cmd_output_b(
+                    _rscript_exec(), *RSCRIPT_OPTS,
+                    f,
+                    *additional_dependencies,
+                    cwd=env_dir,
+                )
 
 
 def _inline_r_setup(code: str) -> str:
@@ -150,11 +164,16 @@
     Some behaviour of R cannot be configured via env variables, but can
     only be configured via R options once R has started. These are set here.
     """
-    with_option = f"""\
-    options(install.packages.compile.from.source = "never", pkgType = "binary")
-    {code}
-    """
-    return with_option
+    with_option = [
+        textwrap.dedent("""\
+        options(
+            install.packages.compile.from.source = "never",
+            pkgType = "binary"
+        )
+        """),
+        code,
+    ]
+    return '\n'.join(with_option)
 
 
 def run_hook(

{"golden_diff": "diff --git a/pre_commit/languages/r.py b/pre_commit/languages/r.py\n--- a/pre_commit/languages/r.py\n+++ b/pre_commit/languages/r.py\n@@ -4,6 +4,8 @@\n import os\n import shlex\n import shutil\n+import tempfile\n+import textwrap\n from typing import Generator\n from typing import Sequence\n \n@@ -21,6 +23,19 @@\n health_check = lang_base.basic_health_check\n \n \n+@contextlib.contextmanager\n+def _r_code_in_tempfile(code: str) -> Generator[str, None, None]:\n+    \"\"\"\n+    To avoid quoting and escaping issues, avoid `Rscript [options] -e {expr}`\n+    but use `Rscript [options] path/to/file_with_expr.R`\n+    \"\"\"\n+    with tempfile.TemporaryDirectory() as tmpdir:\n+        fname = os.path.join(tmpdir, 'script.R')\n+        with open(fname, 'w') as f:\n+            f.write(_inline_r_setup(textwrap.dedent(code)))\n+        yield fname\n+\n+\n def get_env_patch(venv: str) -> PatchesT:\n     return (\n         ('R_PROFILE_USER', os.path.join(venv, 'activate.R')),\n@@ -129,20 +144,19 @@\n         }}\n         \"\"\"\n \n-    cmd_output_b(\n-        _rscript_exec(), '--vanilla', '-e',\n-        _inline_r_setup(r_code_inst_environment),\n-        cwd=env_dir,\n-    )\n+    with _r_code_in_tempfile(r_code_inst_environment) as f:\n+        cmd_output_b(_rscript_exec(), '--vanilla', f, cwd=env_dir)\n+\n     if additional_dependencies:\n         r_code_inst_add = 'renv::install(commandArgs(trailingOnly = TRUE))'\n         with in_env(prefix, version):\n-            cmd_output_b(\n-                _rscript_exec(), *RSCRIPT_OPTS, '-e',\n-                _inline_r_setup(r_code_inst_add),\n-                *additional_dependencies,\n-                cwd=env_dir,\n-            )\n+            with _r_code_in_tempfile(r_code_inst_add) as f:\n+                cmd_output_b(\n+                    _rscript_exec(), *RSCRIPT_OPTS,\n+                    f,\n+                    *additional_dependencies,\n+                    cwd=env_dir,\n+                )\n \n \n def _inline_r_setup(code: str) -> str:\n@@ -150,11 +164,16 @@\n     Some behaviour of R cannot be configured via env variables, but can\n     only be configured via R options once R has started. These are set here.\n     \"\"\"\n-    with_option = f\"\"\"\\\n-    options(install.packages.compile.from.source = \"never\", pkgType = \"binary\")\n-    {code}\n-    \"\"\"\n-    return with_option\n+    with_option = [\n+        textwrap.dedent(\"\"\"\\\n+        options(\n+            install.packages.compile.from.source = \"never\",\n+            pkgType = \"binary\"\n+        )\n+        \"\"\"),\n+        code,\n+    ]\n+    return '\\n'.join(with_option)\n \n \n def run_hook(\n", "issue": "Avoid unquoting weirdness of Windows for  `language: r`\n### search you tried in the issue tracker\n\nnever, r, found\n\n### describe your issue\n\nMultiple reports in https://github.com/lorenzwalthert/precommit (https://github.com/lorenzwalthert/precommit/issues/441, https://github.com/lorenzwalthert/precommit/issues/473) were raised and describe a problem with (un)quoting the long string that runs when `language: r` is setup in `Rscript -e 'xxx'` where `'xxx'` contains [multiple levels of quotes](https://github.com/pre-commit/pre-commit/blob/6896025288691aafd015a4681c59dc105e61b614/pre_commit/languages/r.py#L101). For the readers convenience, the output looks like:\r\n```\r\n[INFO] Installing environment for https://github.com/lorenzwalthert/precommit.\r\n[INFO] Once installed this environment will be reused.\r\n[INFO] This may take a few minutes...\r\n[INFO] Restored changes from C:\\Users\\USER\\.cache\\pre-commit\\patch1678401203-36472.\r\nAn unexpected error has occurred: CalledProcessError: command: ('C:/PROGRA~1/R/R-41~1.0\\\\bin\\\\Rscript.exe', '--vanilla', '-e', '    options(install.packages.compile.from.source = \"never\", pkgType = \"binary\")\\n            prefix_dir <- \\'C:\\\\\\\\Users\\\\\\\\USER\\\\\\\\.cache\\\\\\\\pre-commit\\\\\\\\repovawmpj_r\\'\\n        options(\\n            repos = c(CRAN = \"https://cran.rstudio.com\"),\\n            renv.consent = TRUE\\n        )\\n        source(\"renv/activate.R\")\\n        renv::restore()\\n        activate_statement <- paste0(\\n          \\'suppressWarnings({\\',\\n          \\'old <- setwd(\"\\', getwd(), \\'\"); \\',\\n          \\'source(\"renv/activate.R\"); \\',\\n          \\'setwd(old); \\',\\n          \\'renv::load(\"\\', getwd(), \\'\");})\\'\\n        )\\n        writeLines(activate_statement, \\'activate.R\\')\\n        is_package <- tryCatch(\\n          {\\n              path_desc <- file.path(prefix_dir, \\'DESCRIPTION\\')\\n              suppressWarnings(desc <- read.dcf(path_desc))\\n              \"Package\" %in% colnames(desc)\\n          },\\n          error = function(...) FALSE\\n        )\\n        if (is_package) {\\n            renv::install(prefix_dir)\\n        }\\n        \\n    ')\r\nreturn code: 1\r\nstdout: (none)\r\nstderr:\r\n    During startup - Warning messages:\r\n    1: Setting LC_COLLATE=en_US.UTF-8 failed \r\n    2: Setting LC_CTYPE=en_US.UTF-8 failed \r\n    3: Setting LC_MONETARY=en_US.UTF-8 failed \r\n    4: Setting LC_TIME=en_US.UTF-8 failed \r\n    Error in options(install.packages.compile.from.source = never, pkgType = binary) : \r\n      object 'never' not found\r\n    Execution halted\r\nCheck the log at C:\\Users\\USER\\.cache\\pre-commit\\pre-commit.log\r\n```\r\n\r\n\r\nThe solution described by @asottile in https://github.com/lorenzwalthert/precommit/issues/473#issuecomment-1511498032 is to probably write the contents to a temporary file and avoid unquoting within the expression (i.e. the term after `-e`). This should be quite straight forward.\r\n\r\nQuestion is if we can create a good test first to reproduce the offending behavior and whether or not there are tools already in pre-commit to deal with temp files etc. that we could use.\r\n\r\n\n\n### pre-commit --version\n\nprecommit 3.1.1\n\n### .pre-commit-config.yaml\n\n```yaml\nrepos:\r\n-   repo: https://github.com/lorenzwalthert/precommit\r\n    rev: v0.3.2.9007\r\n    hooks:\r\n    -   id: style-files\n```\n\n\n### ~/.cache/pre-commit/pre-commit.log (if present)\n\n_No response_\n", "before_files": [{"content": "from __future__ import annotations\n\nimport contextlib\nimport os\nimport shlex\nimport shutil\nfrom typing import Generator\nfrom typing import Sequence\n\nfrom pre_commit import lang_base\nfrom pre_commit.envcontext import envcontext\nfrom pre_commit.envcontext import PatchesT\nfrom pre_commit.envcontext import UNSET\nfrom pre_commit.prefix import Prefix\nfrom pre_commit.util import cmd_output_b\nfrom pre_commit.util import win_exe\n\nENVIRONMENT_DIR = 'renv'\nRSCRIPT_OPTS = ('--no-save', '--no-restore', '--no-site-file', '--no-environ')\nget_default_version = lang_base.basic_get_default_version\nhealth_check = lang_base.basic_health_check\n\n\ndef get_env_patch(venv: str) -> PatchesT:\n    return (\n        ('R_PROFILE_USER', os.path.join(venv, 'activate.R')),\n        ('RENV_PROJECT', UNSET),\n    )\n\n\n@contextlib.contextmanager\ndef in_env(prefix: Prefix, version: str) -> Generator[None, None, None]:\n    envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)\n    with envcontext(get_env_patch(envdir)):\n        yield\n\n\ndef _prefix_if_file_entry(\n        entry: list[str],\n        prefix: Prefix,\n        *,\n        is_local: bool,\n) -> Sequence[str]:\n    if entry[1] == '-e' or is_local:\n        return entry[1:]\n    else:\n        return (prefix.path(entry[1]),)\n\n\ndef _rscript_exec() -> str:\n    r_home = os.environ.get('R_HOME')\n    if r_home is None:\n        return 'Rscript'\n    else:\n        return os.path.join(r_home, 'bin', win_exe('Rscript'))\n\n\ndef _entry_validate(entry: list[str]) -> None:\n    \"\"\"\n    Allowed entries:\n    # Rscript -e expr\n    # Rscript path/to/file\n    \"\"\"\n    if entry[0] != 'Rscript':\n        raise ValueError('entry must start with `Rscript`.')\n\n    if entry[1] == '-e':\n        if len(entry) > 3:\n            raise ValueError('You can supply at most one expression.')\n    elif len(entry) > 2:\n        raise ValueError(\n            'The only valid syntax is `Rscript -e {expr}`'\n            'or `Rscript path/to/hook/script`',\n        )\n\n\ndef _cmd_from_hook(\n        prefix: Prefix,\n        entry: str,\n        args: Sequence[str],\n        *,\n        is_local: bool,\n) -> tuple[str, ...]:\n    cmd = shlex.split(entry)\n    _entry_validate(cmd)\n\n    cmd_part = _prefix_if_file_entry(cmd, prefix, is_local=is_local)\n    return (cmd[0], *RSCRIPT_OPTS, *cmd_part, *args)\n\n\ndef install_environment(\n        prefix: Prefix,\n        version: str,\n        additional_dependencies: Sequence[str],\n) -> None:\n    lang_base.assert_version_default('r', version)\n\n    env_dir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)\n    os.makedirs(env_dir, exist_ok=True)\n    shutil.copy(prefix.path('renv.lock'), env_dir)\n    shutil.copytree(prefix.path('renv'), os.path.join(env_dir, 'renv'))\n\n    r_code_inst_environment = f\"\"\"\\\n        prefix_dir <- {prefix.prefix_dir!r}\n        options(\n            repos = c(CRAN = \"https://cran.rstudio.com\"),\n            renv.consent = TRUE\n        )\n        source(\"renv/activate.R\")\n        renv::restore()\n        activate_statement <- paste0(\n          'suppressWarnings({{',\n          'old <- setwd(\"', getwd(), '\"); ',\n          'source(\"renv/activate.R\"); ',\n          'setwd(old); ',\n          'renv::load(\"', getwd(), '\");}})'\n        )\n        writeLines(activate_statement, 'activate.R')\n        is_package <- tryCatch(\n          {{\n              path_desc <- file.path(prefix_dir, 'DESCRIPTION')\n              suppressWarnings(desc <- read.dcf(path_desc))\n              \"Package\" %in% colnames(desc)\n          }},\n          error = function(...) FALSE\n        )\n        if (is_package) {{\n            renv::install(prefix_dir)\n        }}\n        \"\"\"\n\n    cmd_output_b(\n        _rscript_exec(), '--vanilla', '-e',\n        _inline_r_setup(r_code_inst_environment),\n        cwd=env_dir,\n    )\n    if additional_dependencies:\n        r_code_inst_add = 'renv::install(commandArgs(trailingOnly = TRUE))'\n        with in_env(prefix, version):\n            cmd_output_b(\n                _rscript_exec(), *RSCRIPT_OPTS, '-e',\n                _inline_r_setup(r_code_inst_add),\n                *additional_dependencies,\n                cwd=env_dir,\n            )\n\n\ndef _inline_r_setup(code: str) -> str:\n    \"\"\"\n    Some behaviour of R cannot be configured via env variables, but can\n    only be configured via R options once R has started. These are set here.\n    \"\"\"\n    with_option = f\"\"\"\\\n    options(install.packages.compile.from.source = \"never\", pkgType = \"binary\")\n    {code}\n    \"\"\"\n    return with_option\n\n\ndef run_hook(\n        prefix: Prefix,\n        entry: str,\n        args: Sequence[str],\n        file_args: Sequence[str],\n        *,\n        is_local: bool,\n        require_serial: bool,\n        color: bool,\n) -> tuple[int, bytes]:\n    cmd = _cmd_from_hook(prefix, entry, args, is_local=is_local)\n    return lang_base.run_xargs(\n        cmd,\n        file_args,\n        require_serial=require_serial,\n        color=color,\n    )\n", "path": "pre_commit/languages/r.py"}], "after_files": [{"content": "from __future__ import annotations\n\nimport contextlib\nimport os\nimport shlex\nimport shutil\nimport tempfile\nimport textwrap\nfrom typing import Generator\nfrom typing import Sequence\n\nfrom pre_commit import lang_base\nfrom pre_commit.envcontext import envcontext\nfrom pre_commit.envcontext import PatchesT\nfrom pre_commit.envcontext import UNSET\nfrom pre_commit.prefix import Prefix\nfrom pre_commit.util import cmd_output_b\nfrom pre_commit.util import win_exe\n\nENVIRONMENT_DIR = 'renv'\nRSCRIPT_OPTS = ('--no-save', '--no-restore', '--no-site-file', '--no-environ')\nget_default_version = lang_base.basic_get_default_version\nhealth_check = lang_base.basic_health_check\n\n\n@contextlib.contextmanager\ndef _r_code_in_tempfile(code: str) -> Generator[str, None, None]:\n    \"\"\"\n    To avoid quoting and escaping issues, avoid `Rscript [options] -e {expr}`\n    but use `Rscript [options] path/to/file_with_expr.R`\n    \"\"\"\n    with tempfile.TemporaryDirectory() as tmpdir:\n        fname = os.path.join(tmpdir, 'script.R')\n        with open(fname, 'w') as f:\n            f.write(_inline_r_setup(textwrap.dedent(code)))\n        yield fname\n\n\ndef get_env_patch(venv: str) -> PatchesT:\n    return (\n        ('R_PROFILE_USER', os.path.join(venv, 'activate.R')),\n        ('RENV_PROJECT', UNSET),\n    )\n\n\n@contextlib.contextmanager\ndef in_env(prefix: Prefix, version: str) -> Generator[None, None, None]:\n    envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)\n    with envcontext(get_env_patch(envdir)):\n        yield\n\n\ndef _prefix_if_file_entry(\n        entry: list[str],\n        prefix: Prefix,\n        *,\n        is_local: bool,\n) -> Sequence[str]:\n    if entry[1] == '-e' or is_local:\n        return entry[1:]\n    else:\n        return (prefix.path(entry[1]),)\n\n\ndef _rscript_exec() -> str:\n    r_home = os.environ.get('R_HOME')\n    if r_home is None:\n        return 'Rscript'\n    else:\n        return os.path.join(r_home, 'bin', win_exe('Rscript'))\n\n\ndef _entry_validate(entry: list[str]) -> None:\n    \"\"\"\n    Allowed entries:\n    # Rscript -e expr\n    # Rscript path/to/file\n    \"\"\"\n    if entry[0] != 'Rscript':\n        raise ValueError('entry must start with `Rscript`.')\n\n    if entry[1] == '-e':\n        if len(entry) > 3:\n            raise ValueError('You can supply at most one expression.')\n    elif len(entry) > 2:\n        raise ValueError(\n            'The only valid syntax is `Rscript -e {expr}`'\n            'or `Rscript path/to/hook/script`',\n        )\n\n\ndef _cmd_from_hook(\n        prefix: Prefix,\n        entry: str,\n        args: Sequence[str],\n        *,\n        is_local: bool,\n) -> tuple[str, ...]:\n    cmd = shlex.split(entry)\n    _entry_validate(cmd)\n\n    cmd_part = _prefix_if_file_entry(cmd, prefix, is_local=is_local)\n    return (cmd[0], *RSCRIPT_OPTS, *cmd_part, *args)\n\n\ndef install_environment(\n        prefix: Prefix,\n        version: str,\n        additional_dependencies: Sequence[str],\n) -> None:\n    lang_base.assert_version_default('r', version)\n\n    env_dir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)\n    os.makedirs(env_dir, exist_ok=True)\n    shutil.copy(prefix.path('renv.lock'), env_dir)\n    shutil.copytree(prefix.path('renv'), os.path.join(env_dir, 'renv'))\n\n    r_code_inst_environment = f\"\"\"\\\n        prefix_dir <- {prefix.prefix_dir!r}\n        options(\n            repos = c(CRAN = \"https://cran.rstudio.com\"),\n            renv.consent = TRUE\n        )\n        source(\"renv/activate.R\")\n        renv::restore()\n        activate_statement <- paste0(\n          'suppressWarnings({{',\n          'old <- setwd(\"', getwd(), '\"); ',\n          'source(\"renv/activate.R\"); ',\n          'setwd(old); ',\n          'renv::load(\"', getwd(), '\");}})'\n        )\n        writeLines(activate_statement, 'activate.R')\n        is_package <- tryCatch(\n          {{\n              path_desc <- file.path(prefix_dir, 'DESCRIPTION')\n              suppressWarnings(desc <- read.dcf(path_desc))\n              \"Package\" %in% colnames(desc)\n          }},\n          error = function(...) FALSE\n        )\n        if (is_package) {{\n            renv::install(prefix_dir)\n        }}\n        \"\"\"\n\n    with _r_code_in_tempfile(r_code_inst_environment) as f:\n        cmd_output_b(_rscript_exec(), '--vanilla', f, cwd=env_dir)\n\n    if additional_dependencies:\n        r_code_inst_add = 'renv::install(commandArgs(trailingOnly = TRUE))'\n        with in_env(prefix, version):\n            with _r_code_in_tempfile(r_code_inst_add) as f:\n                cmd_output_b(\n                    _rscript_exec(), *RSCRIPT_OPTS,\n                    f,\n                    *additional_dependencies,\n                    cwd=env_dir,\n                )\n\n\ndef _inline_r_setup(code: str) -> str:\n    \"\"\"\n    Some behaviour of R cannot be configured via env variables, but can\n    only be configured via R options once R has started. These are set here.\n    \"\"\"\n    with_option = [\n        textwrap.dedent(\"\"\"\\\n        options(\n            install.packages.compile.from.source = \"never\",\n            pkgType = \"binary\"\n        )\n        \"\"\"),\n        code,\n    ]\n    return '\\n'.join(with_option)\n\n\ndef run_hook(\n        prefix: Prefix,\n        entry: str,\n        args: Sequence[str],\n        file_args: Sequence[str],\n        *,\n        is_local: bool,\n        require_serial: bool,\n        color: bool,\n) -> tuple[int, bytes]:\n    cmd = _cmd_from_hook(prefix, entry, args, is_local=is_local)\n    return lang_base.run_xargs(\n        cmd,\n        file_args,\n        require_serial=require_serial,\n        color=color,\n    )\n", "path": "pre_commit/languages/r.py"}]}