11import json
2+ import logging
23import os
34import subprocess
45from os import PathLike
56from types import TracebackType
67from typing import Any
78
9+ logger = logging .getLogger (__name__ )
810
911class InterpreterError (RuntimeError ):
1012 pass
@@ -37,8 +39,9 @@ def __init__(
3739 """
3840 Args:
3941 deno_command: command list to launch Deno.
40- enable_read_paths: Files or directories to allow reading from in the sandbox.
41- enable_write_paths: Files or directories to allow writing to in the sandbox.
42+ enable_read_paths: Files or directories to allow reading from in the sandbox.
43+ enable_write_paths: Files or directories to allow writing to in the sandbox.
44+ All write paths will also be able to be read from for mounting.
4245 enable_env_vars: Environment variable names to allow in the sandbox.
4346 enable_network_access: Domains or IPs to allow network access in the sandbox.
4447 sync_files: If set, syncs changes within the sandbox back to original files after execution.
@@ -56,7 +59,22 @@ def __init__(
5659 if deno_command :
5760 self .deno_command = list (deno_command )
5861 else :
59- args = ["deno" , "run" , "--allow-read" ]
62+ args = ["deno" , "run" ]
63+
64+ # Allow reading runner.js and explicitly enabled paths
65+ allowed_read_paths = [self ._get_runner_path ()]
66+
67+ # Also allow reading Deno's cache directory so Pyodide can load its files
68+ deno_dir = self ._get_deno_dir ()
69+ if deno_dir :
70+ allowed_read_paths .append (deno_dir )
71+
72+ if self .enable_read_paths :
73+ allowed_read_paths .extend (str (p ) for p in self .enable_read_paths )
74+ if self .enable_write_paths :
75+ allowed_read_paths .extend (str (p ) for p in self .enable_write_paths )
76+ args .append (f"--allow-read={ ',' .join (allowed_read_paths )} " )
77+
6078 self ._env_arg = ""
6179 if self .enable_env_vars :
6280 user_vars = [str (v ).strip () for v in self .enable_env_vars ]
@@ -77,6 +95,36 @@ def __init__(
7795 self .deno_process = None
7896 self ._mounted_files = False
7997
98+ _deno_dir_cache = None
99+
100+ @classmethod
101+ def _get_deno_dir (cls ) -> str | None :
102+ if cls ._deno_dir_cache :
103+ return cls ._deno_dir_cache
104+
105+ if "DENO_DIR" in os .environ :
106+ cls ._deno_dir_cache = os .environ ["DENO_DIR" ]
107+ return cls ._deno_dir_cache
108+
109+ try :
110+ # Attempt to find deno in path or use just "deno"
111+ # We can't easily know which 'deno' will be used if not absolute, but 'deno' is a safe bet
112+ result = subprocess .run (
113+ ["deno" , "info" , "--json" ],
114+ capture_output = True ,
115+ text = True ,
116+ check = False
117+ )
118+ if result .returncode == 0 :
119+ info = json .loads (result .stdout )
120+ cls ._deno_dir_cache = info .get ("denoDir" )
121+ return cls ._deno_dir_cache
122+ except Exception :
123+ logger .warning ("Unable to find the Deno cache dir." )
124+ pass
125+
126+ return None
127+
80128 def _get_runner_path (self ) -> str :
81129 current_dir = os .path .dirname (os .path .abspath (__file__ ))
82130 return os .path .join (current_dir , "runner.js" )
0 commit comments