Add a tick event for addons and scripts, and use it for race-free reload

This commit is contained in:
Aldo Cortesi 2016-07-14 18:46:07 +12:00
parent 5b2d1c044a
commit a4127fb6d5
3 changed files with 24 additions and 14 deletions

View File

@ -4,8 +4,8 @@ import contextlib
import os import os
import shlex import shlex
import sys import sys
import threading
import traceback import traceback
import copy
from mitmproxy import exceptions from mitmproxy import exceptions
from mitmproxy import controller from mitmproxy import controller
@ -76,15 +76,14 @@ def load_script(path, args):
class ReloadHandler(watchdog.events.FileSystemEventHandler): class ReloadHandler(watchdog.events.FileSystemEventHandler):
def __init__(self, callback, master, options): def __init__(self, callback):
self.callback = callback self.callback = callback
self.master, self.options = master, options
def on_modified(self, event): def on_modified(self, event):
self.callback(self.master, self.options) self.callback()
def on_created(self, event): def on_created(self, event):
self.callback(self.master, self.options) self.callback()
class Script: class Script:
@ -99,7 +98,10 @@ class Script:
self.ns = None self.ns = None
self.observer = None self.observer = None
for i in controller.Events: self.last_options = None
self.should_reload = threading.Event()
for i in controller.Events - set(["tick"]):
def mkprox(): def mkprox():
evt = i evt = i
@ -117,21 +119,25 @@ class Script:
with scriptenv(self.path, self.args): with scriptenv(self.path, self.args):
func(*args, **kwargs) func(*args, **kwargs)
def reload(self, master, options): def reload(self):
with master.handlecontext(): self.should_reload.set()
def tick(self):
if self.should_reload.is_set():
self.should_reload.clear()
self.ns = None self.ns = None
self.configure(options) ctx.log.info("Reloading script: %s" % self.name)
self.configure(self.last_options)
else:
self.run("tick")
def configure(self, options): def configure(self, options):
self.last_options = options
if not self.observer: if not self.observer:
self.observer = Observer() self.observer = Observer()
# Bind the handler to the real underlying master object # Bind the handler to the real underlying master object
self.observer.schedule( self.observer.schedule(
ReloadHandler( ReloadHandler(self.reload),
self.reload,
ctx.master,
copy.copy(options),
),
os.path.dirname(self.path) or "." os.path.dirname(self.path) or "."
) )
self.observer.start() self.observer.start()

View File

@ -33,6 +33,7 @@ Events = frozenset([
"error", "error",
"log", "log",
"done", "done",
"tick",
"script_change", "script_change",
]) ])
@ -108,6 +109,8 @@ class Master(object):
self.shutdown() self.shutdown()
def tick(self, timeout): def tick(self, timeout):
with self.handlecontext():
self.addons("tick")
changed = False changed = False
try: try:
mtype, obj = self.event_queue.get(timeout=timeout) mtype, obj = self.event_queue.get(timeout=timeout)

View File

@ -78,6 +78,7 @@ class TestScript(mastertest.MasterTest):
for _ in range(100): for _ in range(100):
with open("foo.py", "a") as f: with open("foo.py", "a") as f:
f.write(".") f.write(".")
m.addons.invoke_with_context(sc, "tick")
time.sleep(0.1) time.sleep(0.1)
if m.event_log: if m.event_log:
return return