We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Handle print() statements for inline scripts
print() statements in inline scripts should be suppressed, and produce into ctx.log.warn() calls instead. 


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mitmproxy/addons/script.py`
Content:
```
1 import contextlib
2 import os
3 import shlex
4 import sys
5 import threading
6 import traceback
7 import types
8 
9 from mitmproxy import exceptions
10 from mitmproxy import ctx
11 from mitmproxy import eventsequence
12 
13 
14 import watchdog.events
15 from watchdog.observers import polling
16 
17 
18 def parse_command(command):
19     """
20         Returns a (path, args) tuple.
21     """
22     if not command or not command.strip():
23         raise ValueError("Empty script command.")
24     # Windows: escape all backslashes in the path.
25     if os.name == "nt":  # pragma: no cover
26         backslashes = shlex.split(command, posix=False)[0].count("\\")
27         command = command.replace("\\", "\\\\", backslashes)
28     args = shlex.split(command)  # pragma: no cover
29     args[0] = os.path.expanduser(args[0])
30     if not os.path.exists(args[0]):
31         raise ValueError(
32             ("Script file not found: %s.\r\n"
33              "If your script path contains spaces, "
34              "make sure to wrap it in additional quotes, e.g. -s \"'./foo bar/baz.py' --args\".") %
35             args[0])
36     elif os.path.isdir(args[0]):
37         raise ValueError("Not a file: %s" % args[0])
38     return args[0], args[1:]
39 
40 
41 def cut_traceback(tb, func_name):
42     """
43     Cut off a traceback at the function with the given name.
44     The func_name's frame is excluded.
45 
46     Args:
47         tb: traceback object, as returned by sys.exc_info()[2]
48         func_name: function name
49 
50     Returns:
51         Reduced traceback.
52     """
53     tb_orig = tb
54 
55     for _, _, fname, _ in traceback.extract_tb(tb):
56         tb = tb.tb_next
57         if fname == func_name:
58             break
59 
60     if tb is None:
61         # We could not find the method, take the full stack trace.
62         # This may happen on some Python interpreters/flavors (e.g. PyInstaller).
63         return tb_orig
64     else:
65         return tb
66 
67 
68 @contextlib.contextmanager
69 def scriptenv(path, args):
70     oldargs = sys.argv
71     sys.argv = [path] + args
72     script_dir = os.path.dirname(os.path.abspath(path))
73     sys.path.append(script_dir)
74     try:
75         yield
76     except SystemExit as v:
77         ctx.log.error("Script exited with code %s" % v.code)
78     except Exception:
79         etype, value, tb = sys.exc_info()
80         tb = cut_traceback(tb, "scriptenv").tb_next
81         ctx.log.error(
82             "Script error: %s" % "".join(
83                 traceback.format_exception(etype, value, tb)
84             )
85         )
86     finally:
87         sys.argv = oldargs
88         sys.path.pop()
89 
90 
91 def load_script(path, args):
92     with open(path, "rb") as f:
93         try:
94             code = compile(f.read(), path, 'exec')
95         except SyntaxError as e:
96             ctx.log.error(
97                 "Script error: %s line %s: %s" % (
98                     e.filename, e.lineno, e.msg
99                 )
100             )
101             return
102     ns = {'__file__': os.path.abspath(path)}
103     with scriptenv(path, args):
104         exec(code, ns)
105     return types.SimpleNamespace(**ns)
106 
107 
108 class ReloadHandler(watchdog.events.FileSystemEventHandler):
109     def __init__(self, callback):
110         self.callback = callback
111 
112     def filter(self, event):
113         """
114             Returns True only when .py file is changed
115         """
116         if event.is_directory:
117             return False
118         if os.path.basename(event.src_path).startswith("."):
119             return False
120         if event.src_path.endswith(".py"):
121             return True
122         return False
123 
124     def on_modified(self, event):
125         if self.filter(event):
126             self.callback()
127 
128     def on_created(self, event):
129         if self.filter(event):
130             self.callback()
131 
132 
133 class Script:
134     """
135         An addon that manages a single script.
136     """
137     def __init__(self, command):
138         self.name = command
139 
140         self.command = command
141         self.path, self.args = parse_command(command)
142         self.ns = None
143         self.observer = None
144         self.dead = False
145 
146         self.last_options = None
147         self.should_reload = threading.Event()
148 
149         for i in eventsequence.Events:
150             if not hasattr(self, i):
151                 def mkprox():
152                     evt = i
153 
154                     def prox(*args, **kwargs):
155                         self.run(evt, *args, **kwargs)
156                     return prox
157                 setattr(self, i, mkprox())
158 
159     def run(self, name, *args, **kwargs):
160         # It's possible for ns to be un-initialised if we failed during
161         # configure
162         if self.ns is not None and not self.dead:
163             func = getattr(self.ns, name, None)
164             if func:
165                 with scriptenv(self.path, self.args):
166                     return func(*args, **kwargs)
167 
168     def reload(self):
169         self.should_reload.set()
170 
171     def load_script(self):
172         self.ns = load_script(self.path, self.args)
173         ret = self.run("start", self.last_options)
174         if ret:
175             self.ns = ret
176             self.run("start", self.last_options)
177 
178     def tick(self):
179         if self.should_reload.is_set():
180             self.should_reload.clear()
181             ctx.log.info("Reloading script: %s" % self.name)
182             self.ns = load_script(self.path, self.args)
183             self.start(self.last_options)
184             self.configure(self.last_options, self.last_options.keys())
185         else:
186             self.run("tick")
187 
188     def start(self, opts):
189         self.last_options = opts
190         self.load_script()
191 
192     def configure(self, options, updated):
193         self.last_options = options
194         if not self.observer:
195             self.observer = polling.PollingObserver()
196             # Bind the handler to the real underlying master object
197             self.observer.schedule(
198                 ReloadHandler(self.reload),
199                 os.path.dirname(self.path) or "."
200             )
201             self.observer.start()
202         self.run("configure", options, updated)
203 
204     def done(self):
205         self.run("done")
206         self.dead = True
207 
208 
209 class ScriptLoader:
210     """
211         An addon that manages loading scripts from options.
212     """
213     def __init__(self):
214         self.is_running = False
215 
216     def running(self):
217         self.is_running = True
218 
219     def run_once(self, command, flows):
220         try:
221             sc = Script(command)
222         except ValueError as e:
223             raise ValueError(str(e))
224         sc.load_script()
225         for f in flows:
226             for evt, o in eventsequence.iterate(f):
227                 sc.run(evt, o)
228         sc.done()
229         return sc
230 
231     def configure(self, options, updated):
232         if "scripts" in updated:
233             for s in options.scripts:
234                 if options.scripts.count(s) > 1:
235                     raise exceptions.OptionsError("Duplicate script: %s" % s)
236 
237             for a in ctx.master.addons.chain[:]:
238                 if isinstance(a, Script) and a.name not in options.scripts:
239                     ctx.log.info("Un-loading script: %s" % a.name)
240                     ctx.master.addons.remove(a)
241 
242             # The machinations below are to ensure that:
243             #   - Scripts remain in the same order
244             #   - Scripts are listed directly after the script addon. This is
245             #   needed to ensure that interactions with, for instance, flow
246             #   serialization remains correct.
247             #   - Scripts are not initialized un-necessarily. If only a
248             #   script's order in the script list has changed, it should simply
249             #   be moved.
250 
251             current = {}
252             for a in ctx.master.addons.chain[:]:
253                 if isinstance(a, Script):
254                     current[a.name] = a
255                     ctx.master.addons.chain.remove(a)
256 
257             ordered = []
258             newscripts = []
259             for s in options.scripts:
260                 if s in current:
261                     ordered.append(current[s])
262                 else:
263                     ctx.log.info("Loading script: %s" % s)
264                     try:
265                         sc = Script(s)
266                     except ValueError as e:
267                         raise exceptions.OptionsError(str(e))
268                     ordered.append(sc)
269                     newscripts.append(sc)
270 
271             ochain = ctx.master.addons.chain
272             pos = ochain.index(self)
273             ctx.master.addons.chain = ochain[:pos + 1] + ordered + ochain[pos + 1:]
274 
275             for s in newscripts:
276                 ctx.master.addons.invoke_addon(s, "start", options)
277                 if self.is_running:
278                     # If we're already running, we configure and tell the addon
279                     # we're up and running.
280                     ctx.master.addons.invoke_addon(
281                         s, "configure", options, options.keys()
282                     )
283                     ctx.master.addons.invoke_addon(s, "running")
284
```
--- 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/mitmproxy/addons/script.py b/mitmproxy/addons/script.py
--- a/mitmproxy/addons/script.py
+++ b/mitmproxy/addons/script.py
@@ -65,14 +65,28 @@
         return tb
 
 
+class StreamLog:
+    """
+        A class for redirecting output using contextlib.
+    """
+    def __init__(self, log):
+        self.log = log
+
+    def write(self, buf):
+        if buf.strip():
+            self.log(buf)
+
+
 @contextlib.contextmanager
 def scriptenv(path, args):
     oldargs = sys.argv
     sys.argv = [path] + args
     script_dir = os.path.dirname(os.path.abspath(path))
     sys.path.append(script_dir)
+    stdout_replacement = StreamLog(ctx.log.warn)
     try:
-        yield
+        with contextlib.redirect_stdout(stdout_replacement):
+            yield
     except SystemExit as v:
         ctx.log.error("Script exited with code %s" % v.code)
     except Exception:

{"golden_diff": "diff --git a/mitmproxy/addons/script.py b/mitmproxy/addons/script.py\n--- a/mitmproxy/addons/script.py\n+++ b/mitmproxy/addons/script.py\n@@ -65,14 +65,28 @@\n         return tb\n \n \n+class StreamLog:\n+    \"\"\"\n+        A class for redirecting output using contextlib.\n+    \"\"\"\n+    def __init__(self, log):\n+        self.log = log\n+\n+    def write(self, buf):\n+        if buf.strip():\n+            self.log(buf)\n+\n+\n @contextlib.contextmanager\n def scriptenv(path, args):\n     oldargs = sys.argv\n     sys.argv = [path] + args\n     script_dir = os.path.dirname(os.path.abspath(path))\n     sys.path.append(script_dir)\n+    stdout_replacement = StreamLog(ctx.log.warn)\n     try:\n-        yield\n+        with contextlib.redirect_stdout(stdout_replacement):\n+            yield\n     except SystemExit as v:\n         ctx.log.error(\"Script exited with code %s\" % v.code)\n     except Exception:\n", "issue": "Handle print() statements for inline scripts\nprint() statements in inline scripts should be suppressed, and produce into ctx.log.warn() calls instead. \n\n", "before_files": [{"content": "import contextlib\nimport os\nimport shlex\nimport sys\nimport threading\nimport traceback\nimport types\n\nfrom mitmproxy import exceptions\nfrom mitmproxy import ctx\nfrom mitmproxy import eventsequence\n\n\nimport watchdog.events\nfrom watchdog.observers import polling\n\n\ndef parse_command(command):\n    \"\"\"\n        Returns a (path, args) tuple.\n    \"\"\"\n    if not command or not command.strip():\n        raise ValueError(\"Empty script command.\")\n    # Windows: escape all backslashes in the path.\n    if os.name == \"nt\":  # pragma: no cover\n        backslashes = shlex.split(command, posix=False)[0].count(\"\\\\\")\n        command = command.replace(\"\\\\\", \"\\\\\\\\\", backslashes)\n    args = shlex.split(command)  # pragma: no cover\n    args[0] = os.path.expanduser(args[0])\n    if not os.path.exists(args[0]):\n        raise ValueError(\n            (\"Script file not found: %s.\\r\\n\"\n             \"If your script path contains spaces, \"\n             \"make sure to wrap it in additional quotes, e.g. -s \\\"'./foo bar/baz.py' --args\\\".\") %\n            args[0])\n    elif os.path.isdir(args[0]):\n        raise ValueError(\"Not a file: %s\" % args[0])\n    return args[0], args[1:]\n\n\ndef cut_traceback(tb, func_name):\n    \"\"\"\n    Cut off a traceback at the function with the given name.\n    The func_name's frame is excluded.\n\n    Args:\n        tb: traceback object, as returned by sys.exc_info()[2]\n        func_name: function name\n\n    Returns:\n        Reduced traceback.\n    \"\"\"\n    tb_orig = tb\n\n    for _, _, fname, _ in traceback.extract_tb(tb):\n        tb = tb.tb_next\n        if fname == func_name:\n            break\n\n    if tb is None:\n        # We could not find the method, take the full stack trace.\n        # This may happen on some Python interpreters/flavors (e.g. PyInstaller).\n        return tb_orig\n    else:\n        return tb\n\n\n@contextlib.contextmanager\ndef scriptenv(path, args):\n    oldargs = sys.argv\n    sys.argv = [path] + args\n    script_dir = os.path.dirname(os.path.abspath(path))\n    sys.path.append(script_dir)\n    try:\n        yield\n    except SystemExit as v:\n        ctx.log.error(\"Script exited with code %s\" % v.code)\n    except Exception:\n        etype, value, tb = sys.exc_info()\n        tb = cut_traceback(tb, \"scriptenv\").tb_next\n        ctx.log.error(\n            \"Script error: %s\" % \"\".join(\n                traceback.format_exception(etype, value, tb)\n            )\n        )\n    finally:\n        sys.argv = oldargs\n        sys.path.pop()\n\n\ndef load_script(path, args):\n    with open(path, \"rb\") as f:\n        try:\n            code = compile(f.read(), path, 'exec')\n        except SyntaxError as e:\n            ctx.log.error(\n                \"Script error: %s line %s: %s\" % (\n                    e.filename, e.lineno, e.msg\n                )\n            )\n            return\n    ns = {'__file__': os.path.abspath(path)}\n    with scriptenv(path, args):\n        exec(code, ns)\n    return types.SimpleNamespace(**ns)\n\n\nclass ReloadHandler(watchdog.events.FileSystemEventHandler):\n    def __init__(self, callback):\n        self.callback = callback\n\n    def filter(self, event):\n        \"\"\"\n            Returns True only when .py file is changed\n        \"\"\"\n        if event.is_directory:\n            return False\n        if os.path.basename(event.src_path).startswith(\".\"):\n            return False\n        if event.src_path.endswith(\".py\"):\n            return True\n        return False\n\n    def on_modified(self, event):\n        if self.filter(event):\n            self.callback()\n\n    def on_created(self, event):\n        if self.filter(event):\n            self.callback()\n\n\nclass Script:\n    \"\"\"\n        An addon that manages a single script.\n    \"\"\"\n    def __init__(self, command):\n        self.name = command\n\n        self.command = command\n        self.path, self.args = parse_command(command)\n        self.ns = None\n        self.observer = None\n        self.dead = False\n\n        self.last_options = None\n        self.should_reload = threading.Event()\n\n        for i in eventsequence.Events:\n            if not hasattr(self, i):\n                def mkprox():\n                    evt = i\n\n                    def prox(*args, **kwargs):\n                        self.run(evt, *args, **kwargs)\n                    return prox\n                setattr(self, i, mkprox())\n\n    def run(self, name, *args, **kwargs):\n        # It's possible for ns to be un-initialised if we failed during\n        # configure\n        if self.ns is not None and not self.dead:\n            func = getattr(self.ns, name, None)\n            if func:\n                with scriptenv(self.path, self.args):\n                    return func(*args, **kwargs)\n\n    def reload(self):\n        self.should_reload.set()\n\n    def load_script(self):\n        self.ns = load_script(self.path, self.args)\n        ret = self.run(\"start\", self.last_options)\n        if ret:\n            self.ns = ret\n            self.run(\"start\", self.last_options)\n\n    def tick(self):\n        if self.should_reload.is_set():\n            self.should_reload.clear()\n            ctx.log.info(\"Reloading script: %s\" % self.name)\n            self.ns = load_script(self.path, self.args)\n            self.start(self.last_options)\n            self.configure(self.last_options, self.last_options.keys())\n        else:\n            self.run(\"tick\")\n\n    def start(self, opts):\n        self.last_options = opts\n        self.load_script()\n\n    def configure(self, options, updated):\n        self.last_options = options\n        if not self.observer:\n            self.observer = polling.PollingObserver()\n            # Bind the handler to the real underlying master object\n            self.observer.schedule(\n                ReloadHandler(self.reload),\n                os.path.dirname(self.path) or \".\"\n            )\n            self.observer.start()\n        self.run(\"configure\", options, updated)\n\n    def done(self):\n        self.run(\"done\")\n        self.dead = True\n\n\nclass ScriptLoader:\n    \"\"\"\n        An addon that manages loading scripts from options.\n    \"\"\"\n    def __init__(self):\n        self.is_running = False\n\n    def running(self):\n        self.is_running = True\n\n    def run_once(self, command, flows):\n        try:\n            sc = Script(command)\n        except ValueError as e:\n            raise ValueError(str(e))\n        sc.load_script()\n        for f in flows:\n            for evt, o in eventsequence.iterate(f):\n                sc.run(evt, o)\n        sc.done()\n        return sc\n\n    def configure(self, options, updated):\n        if \"scripts\" in updated:\n            for s in options.scripts:\n                if options.scripts.count(s) > 1:\n                    raise exceptions.OptionsError(\"Duplicate script: %s\" % s)\n\n            for a in ctx.master.addons.chain[:]:\n                if isinstance(a, Script) and a.name not in options.scripts:\n                    ctx.log.info(\"Un-loading script: %s\" % a.name)\n                    ctx.master.addons.remove(a)\n\n            # The machinations below are to ensure that:\n            #   - Scripts remain in the same order\n            #   - Scripts are listed directly after the script addon. This is\n            #   needed to ensure that interactions with, for instance, flow\n            #   serialization remains correct.\n            #   - Scripts are not initialized un-necessarily. If only a\n            #   script's order in the script list has changed, it should simply\n            #   be moved.\n\n            current = {}\n            for a in ctx.master.addons.chain[:]:\n                if isinstance(a, Script):\n                    current[a.name] = a\n                    ctx.master.addons.chain.remove(a)\n\n            ordered = []\n            newscripts = []\n            for s in options.scripts:\n                if s in current:\n                    ordered.append(current[s])\n                else:\n                    ctx.log.info(\"Loading script: %s\" % s)\n                    try:\n                        sc = Script(s)\n                    except ValueError as e:\n                        raise exceptions.OptionsError(str(e))\n                    ordered.append(sc)\n                    newscripts.append(sc)\n\n            ochain = ctx.master.addons.chain\n            pos = ochain.index(self)\n            ctx.master.addons.chain = ochain[:pos + 1] + ordered + ochain[pos + 1:]\n\n            for s in newscripts:\n                ctx.master.addons.invoke_addon(s, \"start\", options)\n                if self.is_running:\n                    # If we're already running, we configure and tell the addon\n                    # we're up and running.\n                    ctx.master.addons.invoke_addon(\n                        s, \"configure\", options, options.keys()\n                    )\n                    ctx.master.addons.invoke_addon(s, \"running\")\n", "path": "mitmproxy/addons/script.py"}], "after_files": [{"content": "import contextlib\nimport os\nimport shlex\nimport sys\nimport threading\nimport traceback\nimport types\n\nfrom mitmproxy import exceptions\nfrom mitmproxy import ctx\nfrom mitmproxy import eventsequence\n\n\nimport watchdog.events\nfrom watchdog.observers import polling\n\n\ndef parse_command(command):\n    \"\"\"\n        Returns a (path, args) tuple.\n    \"\"\"\n    if not command or not command.strip():\n        raise ValueError(\"Empty script command.\")\n    # Windows: escape all backslashes in the path.\n    if os.name == \"nt\":  # pragma: no cover\n        backslashes = shlex.split(command, posix=False)[0].count(\"\\\\\")\n        command = command.replace(\"\\\\\", \"\\\\\\\\\", backslashes)\n    args = shlex.split(command)  # pragma: no cover\n    args[0] = os.path.expanduser(args[0])\n    if not os.path.exists(args[0]):\n        raise ValueError(\n            (\"Script file not found: %s.\\r\\n\"\n             \"If your script path contains spaces, \"\n             \"make sure to wrap it in additional quotes, e.g. -s \\\"'./foo bar/baz.py' --args\\\".\") %\n            args[0])\n    elif os.path.isdir(args[0]):\n        raise ValueError(\"Not a file: %s\" % args[0])\n    return args[0], args[1:]\n\n\ndef cut_traceback(tb, func_name):\n    \"\"\"\n    Cut off a traceback at the function with the given name.\n    The func_name's frame is excluded.\n\n    Args:\n        tb: traceback object, as returned by sys.exc_info()[2]\n        func_name: function name\n\n    Returns:\n        Reduced traceback.\n    \"\"\"\n    tb_orig = tb\n\n    for _, _, fname, _ in traceback.extract_tb(tb):\n        tb = tb.tb_next\n        if fname == func_name:\n            break\n\n    if tb is None:\n        # We could not find the method, take the full stack trace.\n        # This may happen on some Python interpreters/flavors (e.g. PyInstaller).\n        return tb_orig\n    else:\n        return tb\n\n\nclass StreamLog:\n    \"\"\"\n        A class for redirecting output using contextlib.\n    \"\"\"\n    def __init__(self, log):\n        self.log = log\n\n    def write(self, buf):\n        if buf.strip():\n            self.log(buf)\n\n\n@contextlib.contextmanager\ndef scriptenv(path, args):\n    oldargs = sys.argv\n    sys.argv = [path] + args\n    script_dir = os.path.dirname(os.path.abspath(path))\n    sys.path.append(script_dir)\n    stdout_replacement = StreamLog(ctx.log.warn)\n    try:\n        with contextlib.redirect_stdout(stdout_replacement):\n            yield\n    except SystemExit as v:\n        ctx.log.error(\"Script exited with code %s\" % v.code)\n    except Exception:\n        etype, value, tb = sys.exc_info()\n        tb = cut_traceback(tb, \"scriptenv\").tb_next\n        ctx.log.error(\n            \"Script error: %s\" % \"\".join(\n                traceback.format_exception(etype, value, tb)\n            )\n        )\n    finally:\n        sys.argv = oldargs\n        sys.path.pop()\n\n\ndef load_script(path, args):\n    with open(path, \"rb\") as f:\n        try:\n            code = compile(f.read(), path, 'exec')\n        except SyntaxError as e:\n            ctx.log.error(\n                \"Script error: %s line %s: %s\" % (\n                    e.filename, e.lineno, e.msg\n                )\n            )\n            return\n    ns = {'__file__': os.path.abspath(path)}\n    with scriptenv(path, args):\n        exec(code, ns)\n    return types.SimpleNamespace(**ns)\n\n\nclass ReloadHandler(watchdog.events.FileSystemEventHandler):\n    def __init__(self, callback):\n        self.callback = callback\n\n    def filter(self, event):\n        \"\"\"\n            Returns True only when .py file is changed\n        \"\"\"\n        if event.is_directory:\n            return False\n        if os.path.basename(event.src_path).startswith(\".\"):\n            return False\n        if event.src_path.endswith(\".py\"):\n            return True\n        return False\n\n    def on_modified(self, event):\n        if self.filter(event):\n            self.callback()\n\n    def on_created(self, event):\n        if self.filter(event):\n            self.callback()\n\n\nclass Script:\n    \"\"\"\n        An addon that manages a single script.\n    \"\"\"\n    def __init__(self, command):\n        self.name = command\n\n        self.command = command\n        self.path, self.args = parse_command(command)\n        self.ns = None\n        self.observer = None\n        self.dead = False\n\n        self.last_options = None\n        self.should_reload = threading.Event()\n\n        for i in eventsequence.Events:\n            if not hasattr(self, i):\n                def mkprox():\n                    evt = i\n\n                    def prox(*args, **kwargs):\n                        self.run(evt, *args, **kwargs)\n                    return prox\n                setattr(self, i, mkprox())\n\n    def run(self, name, *args, **kwargs):\n        # It's possible for ns to be un-initialised if we failed during\n        # configure\n        if self.ns is not None and not self.dead:\n            func = getattr(self.ns, name, None)\n            if func:\n                with scriptenv(self.path, self.args):\n                    return func(*args, **kwargs)\n\n    def reload(self):\n        self.should_reload.set()\n\n    def load_script(self):\n        self.ns = load_script(self.path, self.args)\n        ret = self.run(\"start\", self.last_options)\n        if ret:\n            self.ns = ret\n            self.run(\"start\", self.last_options)\n\n    def tick(self):\n        if self.should_reload.is_set():\n            self.should_reload.clear()\n            ctx.log.info(\"Reloading script: %s\" % self.name)\n            self.ns = load_script(self.path, self.args)\n            self.start(self.last_options)\n            self.configure(self.last_options, self.last_options.keys())\n        else:\n            self.run(\"tick\")\n\n    def start(self, opts):\n        self.last_options = opts\n        self.load_script()\n\n    def configure(self, options, updated):\n        self.last_options = options\n        if not self.observer:\n            self.observer = polling.PollingObserver()\n            # Bind the handler to the real underlying master object\n            self.observer.schedule(\n                ReloadHandler(self.reload),\n                os.path.dirname(self.path) or \".\"\n            )\n            self.observer.start()\n        self.run(\"configure\", options, updated)\n\n    def done(self):\n        self.run(\"done\")\n        self.dead = True\n\n\nclass ScriptLoader:\n    \"\"\"\n        An addon that manages loading scripts from options.\n    \"\"\"\n    def __init__(self):\n        self.is_running = False\n\n    def running(self):\n        self.is_running = True\n\n    def run_once(self, command, flows):\n        try:\n            sc = Script(command)\n        except ValueError as e:\n            raise ValueError(str(e))\n        sc.load_script()\n        for f in flows:\n            for evt, o in eventsequence.iterate(f):\n                sc.run(evt, o)\n        sc.done()\n        return sc\n\n    def configure(self, options, updated):\n        if \"scripts\" in updated:\n            for s in options.scripts:\n                if options.scripts.count(s) > 1:\n                    raise exceptions.OptionsError(\"Duplicate script: %s\" % s)\n\n            for a in ctx.master.addons.chain[:]:\n                if isinstance(a, Script) and a.name not in options.scripts:\n                    ctx.log.info(\"Un-loading script: %s\" % a.name)\n                    ctx.master.addons.remove(a)\n\n            # The machinations below are to ensure that:\n            #   - Scripts remain in the same order\n            #   - Scripts are listed directly after the script addon. This is\n            #   needed to ensure that interactions with, for instance, flow\n            #   serialization remains correct.\n            #   - Scripts are not initialized un-necessarily. If only a\n            #   script's order in the script list has changed, it should simply\n            #   be moved.\n\n            current = {}\n            for a in ctx.master.addons.chain[:]:\n                if isinstance(a, Script):\n                    current[a.name] = a\n                    ctx.master.addons.chain.remove(a)\n\n            ordered = []\n            newscripts = []\n            for s in options.scripts:\n                if s in current:\n                    ordered.append(current[s])\n                else:\n                    ctx.log.info(\"Loading script: %s\" % s)\n                    try:\n                        sc = Script(s)\n                    except ValueError as e:\n                        raise exceptions.OptionsError(str(e))\n                    ordered.append(sc)\n                    newscripts.append(sc)\n\n            ochain = ctx.master.addons.chain\n            pos = ochain.index(self)\n            ctx.master.addons.chain = ochain[:pos + 1] + ordered + ochain[pos + 1:]\n\n            for s in newscripts:\n                ctx.master.addons.invoke_addon(s, \"start\", options)\n                if self.is_running:\n                    # If we're already running, we configure and tell the addon\n                    # we're up and running.\n                    ctx.master.addons.invoke_addon(\n                        s, \"configure\", options, options.keys()\n                    )\n                    ctx.master.addons.invoke_addon(s, \"running\")\n", "path": "mitmproxy/addons/script.py"}]}