We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Information about javascript timeouts not printed to the console
At https://github.com/common-workflow-language/cwltool/blob/f454efb1e980b2a9374afba4a8b793f73f4d891a/cwltool/sandboxjs.py#L297-L301, if `debug` is false, no information about the snippet of javascript that caused the timeout is printed.
Information about javascript timeouts not printed to the console
At https://github.com/common-workflow-language/cwltool/blob/f454efb1e980b2a9374afba4a8b793f73f4d891a/cwltool/sandboxjs.py#L297-L301, if `debug` is false, no information about the snippet of javascript that caused the timeout is printed.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `cwltool/sandboxjs.py`
Content:
```
1 from __future__ import absolute_import
2 import errno
3 import json
4 import logging
5 import os
6 import re
7 import select
8 import subprocess
9 import threading
10 import sys
11 from io import BytesIO
12 from typing import Any, Dict, List, Mapping, Text, Tuple, Union
13 from .utils import onWindows
14 from pkg_resources import resource_stream
15 
16 import six
17 
18 try:
19     import queue  # type: ignore
20 except ImportError:
21     import Queue as queue  # type: ignore
22 
23 class JavascriptException(Exception):
24     pass
25 
26 
27 _logger = logging.getLogger("cwltool")
28 
29 JSON = Union[Dict[Text, Any], List[Any], Text, int, float, bool, None]
30 
31 localdata = threading.local()
32 
33 have_node_slim = False
34 # minimum acceptable version of nodejs engine
35 minimum_node_version_str = '0.10.26'
36 
37 def check_js_threshold_version(working_alias):
38     # type: (str) -> bool
39 
40     """Checks if the nodeJS engine version on the system
41     with the allowed minimum version.
42     https://github.com/nodejs/node/blob/master/CHANGELOG.md#nodejs-changelog
43     """
44     # parse nodejs version into int Tuple: 'v4.2.6\n' -> [4, 2, 6]
45     current_version_str = subprocess.check_output(
46         [working_alias, "-v"]).decode('utf-8')
47 
48     current_version = [int(v) for v in current_version_str.strip().strip('v').split('.')]
49     minimum_node_version = [int(v) for v in minimum_node_version_str.split('.')]
50 
51     if current_version >= minimum_node_version:
52         return True
53     else:
54         return False
55 
56 
57 def new_js_proc(force_docker_pull=False, js_console=False):
58     # type: (bool, bool) -> subprocess.Popen
59     
60     cwl_node_engine_js = 'cwlNodeEngine.js'
61     if js_console:
62         cwl_node_engine_js = 'cwlNodeEngineJSConsole.js'
63         _logger.warn("Running with support for javascript console in expressions (DO NOT USE IN PRODUCTION)")
64 
65     res = resource_stream(__name__, cwl_node_engine_js)
66     nodecode = res.read().decode('utf-8')
67 
68     required_node_version, docker = (False,)*2
69     nodejs = None
70     trynodes = ("nodejs", "node")
71     for n in trynodes:
72         try:
73             if subprocess.check_output([n, "--eval", "process.stdout.write('t')"]).decode('utf-8') != "t":
74                 continue
75             else:
76                 nodejs = subprocess.Popen([n, "--eval", nodecode],
77                                           stdin=subprocess.PIPE,
78                                           stdout=subprocess.PIPE,
79                                           stderr=subprocess.PIPE)
80 
81                 required_node_version = check_js_threshold_version(n)
82                 break
83         except subprocess.CalledProcessError:
84             pass
85         except OSError as e:
86             if e.errno == errno.ENOENT:
87                 pass
88             else:
89                 raise
90 
91     if nodejs is None or nodejs is not None and required_node_version is False:
92         try:
93             nodeimg = "node:slim"
94             global have_node_slim
95 
96             if not have_node_slim:
97                 dockerimgs = subprocess.check_output(["docker", "images", "-q", nodeimg]).decode('utf-8')
98                 # if output is an empty string
99                 if (len(dockerimgs.split("\n")) <= 1) or force_docker_pull:
100                     # pull node:slim docker container
101                     nodejsimg = subprocess.check_output(["docker", "pull", nodeimg]).decode('utf-8')
102                     _logger.info("Pulled Docker image %s %s", nodeimg, nodejsimg)
103                 have_node_slim = True
104             nodejs = subprocess.Popen(["docker", "run",
105                                        "--attach=STDIN", "--attach=STDOUT", "--attach=STDERR",
106                                        "--sig-proxy=true", "--interactive",
107                                        "--rm", nodeimg, "node", "--eval", nodecode],
108                                       stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
109             docker = True
110         except OSError as e:
111             if e.errno == errno.ENOENT:
112                 pass
113             else:
114                 raise
115         except subprocess.CalledProcessError:
116             pass
117 
118     # docker failed and nodejs not on system
119     if nodejs is None:
120         raise JavascriptException(
121             u"cwltool requires Node.js engine to evaluate Javascript "
122             "expressions, but couldn't find it.  Tried %s, docker run "
123             "node:slim" % u", ".join(trynodes))
124 
125     # docker failed, but nodejs is installed on system but the version is below the required version
126     if docker is False and required_node_version is False:
127         raise JavascriptException(
128             u'cwltool requires minimum v{} version of Node.js engine.'.format(minimum_node_version_str),
129             u'Try updating: https://docs.npmjs.com/getting-started/installing-node')
130 
131     return nodejs
132 
133 
134 def execjs(js, jslib, timeout=None, force_docker_pull=False, debug=False, js_console=False):  # type: (Union[Mapping, Text], Any, int, bool, bool, bool) -> JSON
135 
136     if not hasattr(localdata, "proc") or localdata.proc.poll() is not None or onWindows():
137         localdata.proc = new_js_proc(force_docker_pull=force_docker_pull, js_console=js_console)
138 
139     nodejs = localdata.proc
140 
141     fn = u"\"use strict\";\n%s\n(function()%s)()" %\
142          (jslib, js if isinstance(js, six.string_types) and len(js) > 1 and js[0] == '{' else ("{return (%s);}" % js))
143 
144     killed = []
145 
146     """ Kill the node process if it exceeds timeout limit"""
147     def terminate():
148         try:
149             killed.append(True)
150             nodejs.kill()
151         except OSError:
152             pass
153 
154     if timeout is None:
155         timeout = 20
156 
157     tm = threading.Timer(timeout, terminate)
158     tm.start()
159 
160     stdin_buf = BytesIO((json.dumps(fn) + "\n").encode('utf-8'))
161     stdout_buf = BytesIO()
162     stderr_buf = BytesIO()
163 
164     rselect = [nodejs.stdout, nodejs.stderr]  # type: List[BytesIO]
165     wselect = [nodejs.stdin]  # type: List[BytesIO]
166 
167     PROCESS_FINISHED_STR = "r1cepzbhUTxtykz5XTC4\n"
168 
169     def process_finished(): # type: () -> bool
170         return stdout_buf.getvalue().decode().endswith(PROCESS_FINISHED_STR) and \
171             stderr_buf.getvalue().decode().endswith(PROCESS_FINISHED_STR)
172 
173     # On windows system standard input/output are not handled properly by select module
174     # (modules like  pywin32, msvcrt, gevent don't work either)
175     if sys.platform=='win32':
176         READ_BYTES_SIZE = 512
177 
178         # creating queue for reading from a thread to queue
179         input_queue = queue.Queue()
180         output_queue = queue.Queue()
181         error_queue = queue.Queue()
182 
183         # To tell threads that output has ended and threads can safely exit
184         no_more_output = threading.Lock()
185         no_more_output.acquire()
186         no_more_error = threading.Lock()
187         no_more_error.acquire()
188 
189         # put constructed command to input queue which then will be passed to nodejs's stdin
190         def put_input(input_queue):
191             while True:
192                 b = stdin_buf.read(READ_BYTES_SIZE)
193                 if b:
194                     input_queue.put(b)
195                 else:
196                     break
197 
198         # get the output from nodejs's stdout and continue till otuput ends
199         def get_output(output_queue):
200             while not no_more_output.acquire(False):
201                 b=os.read(nodejs.stdout.fileno(), READ_BYTES_SIZE)
202                 if b:
203                     output_queue.put(b)
204 
205         # get the output from nodejs's stderr and continue till error output ends
206         def get_error(error_queue):
207             while not no_more_error.acquire(False):
208                 b = os.read(nodejs.stderr.fileno(), READ_BYTES_SIZE)
209                 if b:
210                     error_queue.put(b)
211 
212         # Threads managing nodejs.stdin, nodejs.stdout and nodejs.stderr respectively
213         input_thread = threading.Thread(target=put_input, args=(input_queue,))
214         input_thread.daemon=True
215         input_thread.start()
216         output_thread = threading.Thread(target=get_output, args=(output_queue,))
217         output_thread.daemon=True
218         output_thread.start()
219         error_thread = threading.Thread(target=get_error, args=(error_queue,))
220         error_thread.daemon=True
221         error_thread.start()
222 
223         finished = False
224 
225         while not finished and tm.is_alive():
226             try:
227                 if nodejs.stdin in wselect:
228                     if not input_queue.empty():
229                         os.write(nodejs.stdin.fileno(), input_queue.get())
230                     elif not input_thread.is_alive():
231                         wselect = []
232                 if nodejs.stdout in rselect:
233                     if not output_queue.empty():
234                         stdout_buf.write(output_queue.get())
235 
236                 if nodejs.stderr in rselect:
237                     if not error_queue.empty():
238                         stderr_buf.write(error_queue.get())                
239 
240                 if process_finished() and error_queue.empty() and output_queue.empty():
241                     finished = True
242                     no_more_output.release()
243                     no_more_error.release()
244             except OSError as e:
245                 break
246 
247     else:
248         while not process_finished() and tm.is_alive():
249             rready, wready, _ = select.select(rselect, wselect, [])
250             try:
251                 if nodejs.stdin in wready:
252                     b = stdin_buf.read(select.PIPE_BUF)
253                     if b:
254                         os.write(nodejs.stdin.fileno(), b)
255                 for pipes in ((nodejs.stdout, stdout_buf), (nodejs.stderr, stderr_buf)):
256                     if pipes[0] in rready:
257                         b = os.read(pipes[0].fileno(), select.PIPE_BUF)
258                         if b:
259                             pipes[1].write(b)
260             except OSError as e:
261                 break
262     tm.cancel()
263 
264     stdin_buf.close()
265     stdoutdata = stdout_buf.getvalue()[:-len(PROCESS_FINISHED_STR) - 1]
266     stderrdata = stderr_buf.getvalue()[:-len(PROCESS_FINISHED_STR) - 1]
267 
268     def fn_linenum():  # type: () -> Text
269         lines = fn.splitlines()
270         ofs = 0
271         maxlines = 99
272         if len(lines) > maxlines:
273             ofs = len(lines) - maxlines
274             lines = lines[-maxlines:]
275         return u"\n".join(u"%02i %s" % (i + ofs + 1, b) for i, b in enumerate(lines))
276 
277     def stdfmt(data):  # type: (Text) -> Text
278         if "\n" in data:
279             return "\n" + data.strip()
280         return data
281 
282     nodejs.poll()
283 
284     if js_console:
285         if len(stderrdata) > 0:
286             _logger.info("Javascript console output:")
287             _logger.info("----------------------------------------")
288             _logger.info('\n'.join(re.findall(r'^[[](?:log|err)[]].*$', stderrdata.decode('utf-8'), flags=re.MULTILINE)))
289             _logger.info("----------------------------------------")
290 
291     if debug:
292         info = u"returncode was: %s\nscript was:\n%s\nstdout was: %s\nstderr was: %s\n" %\
293                (nodejs.returncode, fn_linenum(), stdfmt(stdoutdata.decode('utf-8')), stdfmt(stderrdata.decode('utf-8')))
294     else:
295         info = stdfmt(stderrdata.decode('utf-8'))
296 
297     if nodejs.poll() not in (None, 0):
298         if killed:
299             raise JavascriptException(u"Long-running script killed after %s seconds: %s" % (timeout, info))
300         else:
301             raise JavascriptException(info)
302     else:
303         try:
304             # On windows currently a new instance of nodejs process is used due to problem with blocking on read operation on windows
305             if onWindows():
306                 nodejs.kill()
307             return json.loads(stdoutdata.decode('utf-8'))
308         except ValueError as e:
309             raise JavascriptException(u"%s\nscript was:\n%s\nstdout was: '%s'\nstderr was: '%s'\n" %
310                                       (e, fn_linenum(), stdoutdata, stderrdata))
311
```
--- 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/cwltool/sandboxjs.py b/cwltool/sandboxjs.py
--- a/cwltool/sandboxjs.py
+++ b/cwltool/sandboxjs.py
@@ -292,7 +292,8 @@
         info = u"returncode was: %s\nscript was:\n%s\nstdout was: %s\nstderr was: %s\n" %\
                (nodejs.returncode, fn_linenum(), stdfmt(stdoutdata.decode('utf-8')), stdfmt(stderrdata.decode('utf-8')))
     else:
-        info = stdfmt(stderrdata.decode('utf-8'))
+        info = u"Javascript expression was: %s\nstdout was: %s\nstderr was: %s" %\
+               (js, stdfmt(stdoutdata.decode('utf-8')), stdfmt(stderrdata.decode('utf-8')))
 
     if nodejs.poll() not in (None, 0):
         if killed:

{"golden_diff": "diff --git a/cwltool/sandboxjs.py b/cwltool/sandboxjs.py\n--- a/cwltool/sandboxjs.py\n+++ b/cwltool/sandboxjs.py\n@@ -292,7 +292,8 @@\n         info = u\"returncode was: %s\\nscript was:\\n%s\\nstdout was: %s\\nstderr was: %s\\n\" %\\\n                (nodejs.returncode, fn_linenum(), stdfmt(stdoutdata.decode('utf-8')), stdfmt(stderrdata.decode('utf-8')))\n     else:\n-        info = stdfmt(stderrdata.decode('utf-8'))\n+        info = u\"Javascript expression was: %s\\nstdout was: %s\\nstderr was: %s\" %\\\n+               (js, stdfmt(stdoutdata.decode('utf-8')), stdfmt(stderrdata.decode('utf-8')))\n \n     if nodejs.poll() not in (None, 0):\n         if killed:\n", "issue": "Information about javascript timeouts not printed to the console\nAt https://github.com/common-workflow-language/cwltool/blob/f454efb1e980b2a9374afba4a8b793f73f4d891a/cwltool/sandboxjs.py#L297-L301, if `debug` is false, no information about the snippet of javascript that caused the timeout is printed.\nInformation about javascript timeouts not printed to the console\nAt https://github.com/common-workflow-language/cwltool/blob/f454efb1e980b2a9374afba4a8b793f73f4d891a/cwltool/sandboxjs.py#L297-L301, if `debug` is false, no information about the snippet of javascript that caused the timeout is printed.\n", "before_files": [{"content": "from __future__ import absolute_import\nimport errno\nimport json\nimport logging\nimport os\nimport re\nimport select\nimport subprocess\nimport threading\nimport sys\nfrom io import BytesIO\nfrom typing import Any, Dict, List, Mapping, Text, Tuple, Union\nfrom .utils import onWindows\nfrom pkg_resources import resource_stream\n\nimport six\n\ntry:\n    import queue  # type: ignore\nexcept ImportError:\n    import Queue as queue  # type: ignore\n\nclass JavascriptException(Exception):\n    pass\n\n\n_logger = logging.getLogger(\"cwltool\")\n\nJSON = Union[Dict[Text, Any], List[Any], Text, int, float, bool, None]\n\nlocaldata = threading.local()\n\nhave_node_slim = False\n# minimum acceptable version of nodejs engine\nminimum_node_version_str = '0.10.26'\n\ndef check_js_threshold_version(working_alias):\n    # type: (str) -> bool\n\n    \"\"\"Checks if the nodeJS engine version on the system\n    with the allowed minimum version.\n    https://github.com/nodejs/node/blob/master/CHANGELOG.md#nodejs-changelog\n    \"\"\"\n    # parse nodejs version into int Tuple: 'v4.2.6\\n' -> [4, 2, 6]\n    current_version_str = subprocess.check_output(\n        [working_alias, \"-v\"]).decode('utf-8')\n\n    current_version = [int(v) for v in current_version_str.strip().strip('v').split('.')]\n    minimum_node_version = [int(v) for v in minimum_node_version_str.split('.')]\n\n    if current_version >= minimum_node_version:\n        return True\n    else:\n        return False\n\n\ndef new_js_proc(force_docker_pull=False, js_console=False):\n    # type: (bool, bool) -> subprocess.Popen\n    \n    cwl_node_engine_js = 'cwlNodeEngine.js'\n    if js_console:\n        cwl_node_engine_js = 'cwlNodeEngineJSConsole.js'\n        _logger.warn(\"Running with support for javascript console in expressions (DO NOT USE IN PRODUCTION)\")\n\n    res = resource_stream(__name__, cwl_node_engine_js)\n    nodecode = res.read().decode('utf-8')\n\n    required_node_version, docker = (False,)*2\n    nodejs = None\n    trynodes = (\"nodejs\", \"node\")\n    for n in trynodes:\n        try:\n            if subprocess.check_output([n, \"--eval\", \"process.stdout.write('t')\"]).decode('utf-8') != \"t\":\n                continue\n            else:\n                nodejs = subprocess.Popen([n, \"--eval\", nodecode],\n                                          stdin=subprocess.PIPE,\n                                          stdout=subprocess.PIPE,\n                                          stderr=subprocess.PIPE)\n\n                required_node_version = check_js_threshold_version(n)\n                break\n        except subprocess.CalledProcessError:\n            pass\n        except OSError as e:\n            if e.errno == errno.ENOENT:\n                pass\n            else:\n                raise\n\n    if nodejs is None or nodejs is not None and required_node_version is False:\n        try:\n            nodeimg = \"node:slim\"\n            global have_node_slim\n\n            if not have_node_slim:\n                dockerimgs = subprocess.check_output([\"docker\", \"images\", \"-q\", nodeimg]).decode('utf-8')\n                # if output is an empty string\n                if (len(dockerimgs.split(\"\\n\")) <= 1) or force_docker_pull:\n                    # pull node:slim docker container\n                    nodejsimg = subprocess.check_output([\"docker\", \"pull\", nodeimg]).decode('utf-8')\n                    _logger.info(\"Pulled Docker image %s %s\", nodeimg, nodejsimg)\n                have_node_slim = True\n            nodejs = subprocess.Popen([\"docker\", \"run\",\n                                       \"--attach=STDIN\", \"--attach=STDOUT\", \"--attach=STDERR\",\n                                       \"--sig-proxy=true\", \"--interactive\",\n                                       \"--rm\", nodeimg, \"node\", \"--eval\", nodecode],\n                                      stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n            docker = True\n        except OSError as e:\n            if e.errno == errno.ENOENT:\n                pass\n            else:\n                raise\n        except subprocess.CalledProcessError:\n            pass\n\n    # docker failed and nodejs not on system\n    if nodejs is None:\n        raise JavascriptException(\n            u\"cwltool requires Node.js engine to evaluate Javascript \"\n            \"expressions, but couldn't find it.  Tried %s, docker run \"\n            \"node:slim\" % u\", \".join(trynodes))\n\n    # docker failed, but nodejs is installed on system but the version is below the required version\n    if docker is False and required_node_version is False:\n        raise JavascriptException(\n            u'cwltool requires minimum v{} version of Node.js engine.'.format(minimum_node_version_str),\n            u'Try updating: https://docs.npmjs.com/getting-started/installing-node')\n\n    return nodejs\n\n\ndef execjs(js, jslib, timeout=None, force_docker_pull=False, debug=False, js_console=False):  # type: (Union[Mapping, Text], Any, int, bool, bool, bool) -> JSON\n\n    if not hasattr(localdata, \"proc\") or localdata.proc.poll() is not None or onWindows():\n        localdata.proc = new_js_proc(force_docker_pull=force_docker_pull, js_console=js_console)\n\n    nodejs = localdata.proc\n\n    fn = u\"\\\"use strict\\\";\\n%s\\n(function()%s)()\" %\\\n         (jslib, js if isinstance(js, six.string_types) and len(js) > 1 and js[0] == '{' else (\"{return (%s);}\" % js))\n\n    killed = []\n\n    \"\"\" Kill the node process if it exceeds timeout limit\"\"\"\n    def terminate():\n        try:\n            killed.append(True)\n            nodejs.kill()\n        except OSError:\n            pass\n\n    if timeout is None:\n        timeout = 20\n\n    tm = threading.Timer(timeout, terminate)\n    tm.start()\n\n    stdin_buf = BytesIO((json.dumps(fn) + \"\\n\").encode('utf-8'))\n    stdout_buf = BytesIO()\n    stderr_buf = BytesIO()\n\n    rselect = [nodejs.stdout, nodejs.stderr]  # type: List[BytesIO]\n    wselect = [nodejs.stdin]  # type: List[BytesIO]\n\n    PROCESS_FINISHED_STR = \"r1cepzbhUTxtykz5XTC4\\n\"\n\n    def process_finished(): # type: () -> bool\n        return stdout_buf.getvalue().decode().endswith(PROCESS_FINISHED_STR) and \\\n            stderr_buf.getvalue().decode().endswith(PROCESS_FINISHED_STR)\n\n    # On windows system standard input/output are not handled properly by select module\n    # (modules like  pywin32, msvcrt, gevent don't work either)\n    if sys.platform=='win32':\n        READ_BYTES_SIZE = 512\n\n        # creating queue for reading from a thread to queue\n        input_queue = queue.Queue()\n        output_queue = queue.Queue()\n        error_queue = queue.Queue()\n\n        # To tell threads that output has ended and threads can safely exit\n        no_more_output = threading.Lock()\n        no_more_output.acquire()\n        no_more_error = threading.Lock()\n        no_more_error.acquire()\n\n        # put constructed command to input queue which then will be passed to nodejs's stdin\n        def put_input(input_queue):\n            while True:\n                b = stdin_buf.read(READ_BYTES_SIZE)\n                if b:\n                    input_queue.put(b)\n                else:\n                    break\n\n        # get the output from nodejs's stdout and continue till otuput ends\n        def get_output(output_queue):\n            while not no_more_output.acquire(False):\n                b=os.read(nodejs.stdout.fileno(), READ_BYTES_SIZE)\n                if b:\n                    output_queue.put(b)\n\n        # get the output from nodejs's stderr and continue till error output ends\n        def get_error(error_queue):\n            while not no_more_error.acquire(False):\n                b = os.read(nodejs.stderr.fileno(), READ_BYTES_SIZE)\n                if b:\n                    error_queue.put(b)\n\n        # Threads managing nodejs.stdin, nodejs.stdout and nodejs.stderr respectively\n        input_thread = threading.Thread(target=put_input, args=(input_queue,))\n        input_thread.daemon=True\n        input_thread.start()\n        output_thread = threading.Thread(target=get_output, args=(output_queue,))\n        output_thread.daemon=True\n        output_thread.start()\n        error_thread = threading.Thread(target=get_error, args=(error_queue,))\n        error_thread.daemon=True\n        error_thread.start()\n\n        finished = False\n\n        while not finished and tm.is_alive():\n            try:\n                if nodejs.stdin in wselect:\n                    if not input_queue.empty():\n                        os.write(nodejs.stdin.fileno(), input_queue.get())\n                    elif not input_thread.is_alive():\n                        wselect = []\n                if nodejs.stdout in rselect:\n                    if not output_queue.empty():\n                        stdout_buf.write(output_queue.get())\n\n                if nodejs.stderr in rselect:\n                    if not error_queue.empty():\n                        stderr_buf.write(error_queue.get())                \n\n                if process_finished() and error_queue.empty() and output_queue.empty():\n                    finished = True\n                    no_more_output.release()\n                    no_more_error.release()\n            except OSError as e:\n                break\n\n    else:\n        while not process_finished() and tm.is_alive():\n            rready, wready, _ = select.select(rselect, wselect, [])\n            try:\n                if nodejs.stdin in wready:\n                    b = stdin_buf.read(select.PIPE_BUF)\n                    if b:\n                        os.write(nodejs.stdin.fileno(), b)\n                for pipes in ((nodejs.stdout, stdout_buf), (nodejs.stderr, stderr_buf)):\n                    if pipes[0] in rready:\n                        b = os.read(pipes[0].fileno(), select.PIPE_BUF)\n                        if b:\n                            pipes[1].write(b)\n            except OSError as e:\n                break\n    tm.cancel()\n\n    stdin_buf.close()\n    stdoutdata = stdout_buf.getvalue()[:-len(PROCESS_FINISHED_STR) - 1]\n    stderrdata = stderr_buf.getvalue()[:-len(PROCESS_FINISHED_STR) - 1]\n\n    def fn_linenum():  # type: () -> Text\n        lines = fn.splitlines()\n        ofs = 0\n        maxlines = 99\n        if len(lines) > maxlines:\n            ofs = len(lines) - maxlines\n            lines = lines[-maxlines:]\n        return u\"\\n\".join(u\"%02i %s\" % (i + ofs + 1, b) for i, b in enumerate(lines))\n\n    def stdfmt(data):  # type: (Text) -> Text\n        if \"\\n\" in data:\n            return \"\\n\" + data.strip()\n        return data\n\n    nodejs.poll()\n\n    if js_console:\n        if len(stderrdata) > 0:\n            _logger.info(\"Javascript console output:\")\n            _logger.info(\"----------------------------------------\")\n            _logger.info('\\n'.join(re.findall(r'^[[](?:log|err)[]].*$', stderrdata.decode('utf-8'), flags=re.MULTILINE)))\n            _logger.info(\"----------------------------------------\")\n\n    if debug:\n        info = u\"returncode was: %s\\nscript was:\\n%s\\nstdout was: %s\\nstderr was: %s\\n\" %\\\n               (nodejs.returncode, fn_linenum(), stdfmt(stdoutdata.decode('utf-8')), stdfmt(stderrdata.decode('utf-8')))\n    else:\n        info = stdfmt(stderrdata.decode('utf-8'))\n\n    if nodejs.poll() not in (None, 0):\n        if killed:\n            raise JavascriptException(u\"Long-running script killed after %s seconds: %s\" % (timeout, info))\n        else:\n            raise JavascriptException(info)\n    else:\n        try:\n            # On windows currently a new instance of nodejs process is used due to problem with blocking on read operation on windows\n            if onWindows():\n                nodejs.kill()\n            return json.loads(stdoutdata.decode('utf-8'))\n        except ValueError as e:\n            raise JavascriptException(u\"%s\\nscript was:\\n%s\\nstdout was: '%s'\\nstderr was: '%s'\\n\" %\n                                      (e, fn_linenum(), stdoutdata, stderrdata))\n", "path": "cwltool/sandboxjs.py"}], "after_files": [{"content": "from __future__ import absolute_import\nimport errno\nimport json\nimport logging\nimport os\nimport re\nimport select\nimport subprocess\nimport threading\nimport sys\nfrom io import BytesIO\nfrom typing import Any, Dict, List, Mapping, Text, Tuple, Union\nfrom .utils import onWindows\nfrom pkg_resources import resource_stream\n\nimport six\n\ntry:\n    import queue  # type: ignore\nexcept ImportError:\n    import Queue as queue  # type: ignore\n\nclass JavascriptException(Exception):\n    pass\n\n\n_logger = logging.getLogger(\"cwltool\")\n\nJSON = Union[Dict[Text, Any], List[Any], Text, int, float, bool, None]\n\nlocaldata = threading.local()\n\nhave_node_slim = False\n# minimum acceptable version of nodejs engine\nminimum_node_version_str = '0.10.26'\n\ndef check_js_threshold_version(working_alias):\n    # type: (str) -> bool\n\n    \"\"\"Checks if the nodeJS engine version on the system\n    with the allowed minimum version.\n    https://github.com/nodejs/node/blob/master/CHANGELOG.md#nodejs-changelog\n    \"\"\"\n    # parse nodejs version into int Tuple: 'v4.2.6\\n' -> [4, 2, 6]\n    current_version_str = subprocess.check_output(\n        [working_alias, \"-v\"]).decode('utf-8')\n\n    current_version = [int(v) for v in current_version_str.strip().strip('v').split('.')]\n    minimum_node_version = [int(v) for v in minimum_node_version_str.split('.')]\n\n    if current_version >= minimum_node_version:\n        return True\n    else:\n        return False\n\n\ndef new_js_proc(force_docker_pull=False, js_console=False):\n    # type: (bool, bool) -> subprocess.Popen\n    \n    cwl_node_engine_js = 'cwlNodeEngine.js'\n    if js_console:\n        cwl_node_engine_js = 'cwlNodeEngineJSConsole.js'\n        _logger.warn(\"Running with support for javascript console in expressions (DO NOT USE IN PRODUCTION)\")\n\n    res = resource_stream(__name__, cwl_node_engine_js)\n    nodecode = res.read().decode('utf-8')\n\n    required_node_version, docker = (False,)*2\n    nodejs = None\n    trynodes = (\"nodejs\", \"node\")\n    for n in trynodes:\n        try:\n            if subprocess.check_output([n, \"--eval\", \"process.stdout.write('t')\"]).decode('utf-8') != \"t\":\n                continue\n            else:\n                nodejs = subprocess.Popen([n, \"--eval\", nodecode],\n                                          stdin=subprocess.PIPE,\n                                          stdout=subprocess.PIPE,\n                                          stderr=subprocess.PIPE)\n\n                required_node_version = check_js_threshold_version(n)\n                break\n        except subprocess.CalledProcessError:\n            pass\n        except OSError as e:\n            if e.errno == errno.ENOENT:\n                pass\n            else:\n                raise\n\n    if nodejs is None or nodejs is not None and required_node_version is False:\n        try:\n            nodeimg = \"node:slim\"\n            global have_node_slim\n\n            if not have_node_slim:\n                dockerimgs = subprocess.check_output([\"docker\", \"images\", \"-q\", nodeimg]).decode('utf-8')\n                # if output is an empty string\n                if (len(dockerimgs.split(\"\\n\")) <= 1) or force_docker_pull:\n                    # pull node:slim docker container\n                    nodejsimg = subprocess.check_output([\"docker\", \"pull\", nodeimg]).decode('utf-8')\n                    _logger.info(\"Pulled Docker image %s %s\", nodeimg, nodejsimg)\n                have_node_slim = True\n            nodejs = subprocess.Popen([\"docker\", \"run\",\n                                       \"--attach=STDIN\", \"--attach=STDOUT\", \"--attach=STDERR\",\n                                       \"--sig-proxy=true\", \"--interactive\",\n                                       \"--rm\", nodeimg, \"node\", \"--eval\", nodecode],\n                                      stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n            docker = True\n        except OSError as e:\n            if e.errno == errno.ENOENT:\n                pass\n            else:\n                raise\n        except subprocess.CalledProcessError:\n            pass\n\n    # docker failed and nodejs not on system\n    if nodejs is None:\n        raise JavascriptException(\n            u\"cwltool requires Node.js engine to evaluate Javascript \"\n            \"expressions, but couldn't find it.  Tried %s, docker run \"\n            \"node:slim\" % u\", \".join(trynodes))\n\n    # docker failed, but nodejs is installed on system but the version is below the required version\n    if docker is False and required_node_version is False:\n        raise JavascriptException(\n            u'cwltool requires minimum v{} version of Node.js engine.'.format(minimum_node_version_str),\n            u'Try updating: https://docs.npmjs.com/getting-started/installing-node')\n\n    return nodejs\n\n\ndef execjs(js, jslib, timeout=None, force_docker_pull=False, debug=False, js_console=False):  # type: (Union[Mapping, Text], Any, int, bool, bool, bool) -> JSON\n\n    if not hasattr(localdata, \"proc\") or localdata.proc.poll() is not None or onWindows():\n        localdata.proc = new_js_proc(force_docker_pull=force_docker_pull, js_console=js_console)\n\n    nodejs = localdata.proc\n\n    fn = u\"\\\"use strict\\\";\\n%s\\n(function()%s)()\" %\\\n         (jslib, js if isinstance(js, six.string_types) and len(js) > 1 and js[0] == '{' else (\"{return (%s);}\" % js))\n\n    killed = []\n\n    \"\"\" Kill the node process if it exceeds timeout limit\"\"\"\n    def terminate():\n        try:\n            killed.append(True)\n            nodejs.kill()\n        except OSError:\n            pass\n\n    if timeout is None:\n        timeout = 20\n\n    tm = threading.Timer(timeout, terminate)\n    tm.start()\n\n    stdin_buf = BytesIO((json.dumps(fn) + \"\\n\").encode('utf-8'))\n    stdout_buf = BytesIO()\n    stderr_buf = BytesIO()\n\n    rselect = [nodejs.stdout, nodejs.stderr]  # type: List[BytesIO]\n    wselect = [nodejs.stdin]  # type: List[BytesIO]\n\n    PROCESS_FINISHED_STR = \"r1cepzbhUTxtykz5XTC4\\n\"\n\n    def process_finished(): # type: () -> bool\n        return stdout_buf.getvalue().decode().endswith(PROCESS_FINISHED_STR) and \\\n            stderr_buf.getvalue().decode().endswith(PROCESS_FINISHED_STR)\n\n    # On windows system standard input/output are not handled properly by select module\n    # (modules like  pywin32, msvcrt, gevent don't work either)\n    if sys.platform=='win32':\n        READ_BYTES_SIZE = 512\n\n        # creating queue for reading from a thread to queue\n        input_queue = queue.Queue()\n        output_queue = queue.Queue()\n        error_queue = queue.Queue()\n\n        # To tell threads that output has ended and threads can safely exit\n        no_more_output = threading.Lock()\n        no_more_output.acquire()\n        no_more_error = threading.Lock()\n        no_more_error.acquire()\n\n        # put constructed command to input queue which then will be passed to nodejs's stdin\n        def put_input(input_queue):\n            while True:\n                b = stdin_buf.read(READ_BYTES_SIZE)\n                if b:\n                    input_queue.put(b)\n                else:\n                    break\n\n        # get the output from nodejs's stdout and continue till otuput ends\n        def get_output(output_queue):\n            while not no_more_output.acquire(False):\n                b=os.read(nodejs.stdout.fileno(), READ_BYTES_SIZE)\n                if b:\n                    output_queue.put(b)\n\n        # get the output from nodejs's stderr and continue till error output ends\n        def get_error(error_queue):\n            while not no_more_error.acquire(False):\n                b = os.read(nodejs.stderr.fileno(), READ_BYTES_SIZE)\n                if b:\n                    error_queue.put(b)\n\n        # Threads managing nodejs.stdin, nodejs.stdout and nodejs.stderr respectively\n        input_thread = threading.Thread(target=put_input, args=(input_queue,))\n        input_thread.daemon=True\n        input_thread.start()\n        output_thread = threading.Thread(target=get_output, args=(output_queue,))\n        output_thread.daemon=True\n        output_thread.start()\n        error_thread = threading.Thread(target=get_error, args=(error_queue,))\n        error_thread.daemon=True\n        error_thread.start()\n\n        finished = False\n\n        while not finished and tm.is_alive():\n            try:\n                if nodejs.stdin in wselect:\n                    if not input_queue.empty():\n                        os.write(nodejs.stdin.fileno(), input_queue.get())\n                    elif not input_thread.is_alive():\n                        wselect = []\n                if nodejs.stdout in rselect:\n                    if not output_queue.empty():\n                        stdout_buf.write(output_queue.get())\n\n                if nodejs.stderr in rselect:\n                    if not error_queue.empty():\n                        stderr_buf.write(error_queue.get())                \n\n                if process_finished() and error_queue.empty() and output_queue.empty():\n                    finished = True\n                    no_more_output.release()\n                    no_more_error.release()\n            except OSError as e:\n                break\n\n    else:\n        while not process_finished() and tm.is_alive():\n            rready, wready, _ = select.select(rselect, wselect, [])\n            try:\n                if nodejs.stdin in wready:\n                    b = stdin_buf.read(select.PIPE_BUF)\n                    if b:\n                        os.write(nodejs.stdin.fileno(), b)\n                for pipes in ((nodejs.stdout, stdout_buf), (nodejs.stderr, stderr_buf)):\n                    if pipes[0] in rready:\n                        b = os.read(pipes[0].fileno(), select.PIPE_BUF)\n                        if b:\n                            pipes[1].write(b)\n            except OSError as e:\n                break\n    tm.cancel()\n\n    stdin_buf.close()\n    stdoutdata = stdout_buf.getvalue()[:-len(PROCESS_FINISHED_STR) - 1]\n    stderrdata = stderr_buf.getvalue()[:-len(PROCESS_FINISHED_STR) - 1]\n\n    def fn_linenum():  # type: () -> Text\n        lines = fn.splitlines()\n        ofs = 0\n        maxlines = 99\n        if len(lines) > maxlines:\n            ofs = len(lines) - maxlines\n            lines = lines[-maxlines:]\n        return u\"\\n\".join(u\"%02i %s\" % (i + ofs + 1, b) for i, b in enumerate(lines))\n\n    def stdfmt(data):  # type: (Text) -> Text\n        if \"\\n\" in data:\n            return \"\\n\" + data.strip()\n        return data\n\n    nodejs.poll()\n\n    if js_console:\n        if len(stderrdata) > 0:\n            _logger.info(\"Javascript console output:\")\n            _logger.info(\"----------------------------------------\")\n            _logger.info('\\n'.join(re.findall(r'^[[](?:log|err)[]].*$', stderrdata.decode('utf-8'), flags=re.MULTILINE)))\n            _logger.info(\"----------------------------------------\")\n\n    if debug:\n        info = u\"returncode was: %s\\nscript was:\\n%s\\nstdout was: %s\\nstderr was: %s\\n\" %\\\n               (nodejs.returncode, fn_linenum(), stdfmt(stdoutdata.decode('utf-8')), stdfmt(stderrdata.decode('utf-8')))\n    else:\n        info = u\"Javascript expression was: %s\\nstdout was: %s\\nstderr was: %s\" %\\\n               (js, stdfmt(stdoutdata.decode('utf-8')), stdfmt(stderrdata.decode('utf-8')))\n\n    if nodejs.poll() not in (None, 0):\n        if killed:\n            raise JavascriptException(u\"Long-running script killed after %s seconds: %s\" % (timeout, info))\n        else:\n            raise JavascriptException(info)\n    else:\n        try:\n            # On windows currently a new instance of nodejs process is used due to problem with blocking on read operation on windows\n            if onWindows():\n                nodejs.kill()\n            return json.loads(stdoutdata.decode('utf-8'))\n        except ValueError as e:\n            raise JavascriptException(u\"%s\\nscript was:\\n%s\\nstdout was: '%s'\\nstderr was: '%s'\\n\" %\n                                      (e, fn_linenum(), stdoutdata, stderrdata))\n", "path": "cwltool/sandboxjs.py"}]}