Skip to content
This repository

Multi-Mechanize tests #355

Merged
merged 4 commits into from about 1 year ago

2 participants

Ira Hanson Jason Grout
Ira Hanson
Collaborator

No description provided.

Jason Grout
Owner

I really like your idea to make the session a context!

Ira Hanson
Collaborator

Can this pull request be finally merged? It contains (among other things) a change to the API which is assumed in the the documentation in the wiki.

I just rebased it on top of the current master.

Jason Grout jasongrout commented on the diff
handlers.py
@@ -100,19 +100,19 @@ def post(self):
100 100
         logger.info("Starting session: %s"%timer)
101 101
         kernel_id = yield gen.Task(km.new_session_async)
102 102
         data = {"ws_url": ws_url, "kernel_id": kernel_id}
103  
-        if self.request.headers["Accept"] == "application/json":
  103
+        if "frame" not in self.request.headers:
1
Jason Grout Owner

why is this change is needed? (that's an honest question---I'm really curious)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Ira Hanson
Collaborator

At one point I changed the protocol from using a URL parameter to using an HTTP header to indicate whether to load a file using AJAX or iframes (used where CORS is needed for a POST request but not available). This turned out to problematic in several ways. For one, the sendRequest function is designed to be able to request files of any type, not just JSON, so checking for a requested type of application/json is not really appropriate. It also makes the API unnecessarily complicated; it may not be clear to someone using the API that this specific header is needed even when requesting plain JSON. I decided here to revert back to the old method of using URL parameters to specify the special cases of both JSONP and iframes.

Jason Grout
Owner

Thanks. I'm glad we have that info now in a comment here

Jason Grout jasongrout merged commit e40d6c4 into from
Jason Grout jasongrout closed this
Ira Hanson ihanson deleted the branch
Jason Grout jasongrout referenced this pull request
Closed

Too many open files #396

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
5  .gitignore
... ...
@@ -1,10 +1,13 @@
1 1
 *.pyc
2 2
 *~
3 3
 doc/_build/*
4  
-static/jmol
  4
+tests/multimechanize/results/
  5
+sqlite.db
5 6
 config.py
6 7
 
7 8
 # generated web files
  9
+submodules/jsmin-bin
  10
+static/jmol
8 11
 static/all*
9 12
 static/jquery.min.js
10 13
 static/sockjs.js
19  handlers.py
@@ -100,19 +100,19 @@ def post(self):
100 100
         logger.info("Starting session: %s"%timer)
101 101
         kernel_id = yield gen.Task(km.new_session_async)
102 102
         data = {"ws_url": ws_url, "kernel_id": kernel_id}
103  
-        if self.request.headers["Accept"] == "application/json":
  103
+        if "frame" not in self.request.headers:
104 104
             self.set_header("Access-Control-Allow-Origin", "*");
105 105
         else:
106 106
             data = '<script>parent.postMessage(%s,"*");</script>' % (json.dumps(data),)
107 107
             self.set_header("Content-Type", "text/html")
108 108
         self.write(data)
109 109
         self.finish()
110  
-    get = post
111 110
 
112 111
 class KernelConnection(sockjs.tornado.SockJSConnection):
113 112
     def __init__(self, session):
114 113
         self.session = session
115 114
         super(KernelConnection, self).__init__(session)
  115
+
116 116
     def on_open(self, request):
117 117
         self.channels = {}
118 118
 
@@ -128,6 +128,11 @@ def on_message(self, message):
128 128
             self.channels[kernel]["iopub"].open(kernel)
129 129
         self.channels[kernel][channel].on_message(message)
130 130
 
  131
+    def on_close(self):
  132
+        for channel in self.channels.itervalues():
  133
+            channel["shell"].on_close()
  134
+            channel["iopub"].on_close()
  135
+
131 136
 KernelRouter = sockjs.tornado.SockJSRouter(KernelConnection, "/sockjs")
132 137
 
133 138
 class SageCellHandler(tornado.web.RequestHandler):
@@ -172,7 +177,7 @@ def post(self):
172 177
                     retval["query"] = db.new_exec_msg(message)
173 178
             except:
174 179
                 pass
175  
-        if self.request.headers["Accept"] == "application/json":
  180
+        if "frame" not in self.request.headers:
176 181
             self.set_header("Access-Control-Allow-Origin", "*");
177 182
         else:
178 183
             retval = '<script>parent.postMessage(%s,"*");</script>' % (json.dumps(retval),)
@@ -252,9 +257,6 @@ def finish_request(self):
252 257
         self.set_header("Access-Control-Allow-Origin", "*")
253 258
         self.write(retval)
254 259
         self.finish()
255  
-        
256  
-    get = post
257  
-    
258 260
 
259 261
 class ZMQStreamHandler(object):
260 262
     """
@@ -301,6 +303,9 @@ def _on_zmq_reply(self, msg_list):
301 303
     def _output_message(self, message):
302 304
         raise NotImplementedError
303 305
 
  306
+    def on_close(self):
  307
+        self.km.end_session(self.kernel_id)
  308
+
304 309
 class ShellHandler(ZMQStreamHandler):
305 310
     """
306 311
     This handles the websocket-ZMQ bridge for the shell
@@ -344,6 +349,7 @@ def on_message(self, message):
344 349
     def on_close(self):
345 350
         if self.shell_stream is not None and not self.shell_stream.closed():
346 351
             self.shell_stream.close()
  352
+        super(ShellHandler, self).on_close()
347 353
 
348 354
 class IOPubHandler(ZMQStreamHandler):
349 355
     """
@@ -377,6 +383,7 @@ def on_close(self):
377 383
             self.iopub_stream.close()
378 384
         if self.hb_stream is not None and not self.hb_stream.closed():
379 385
             self.stop_hb()
  386
+        super(IOPubHandler, self).on_close()
380 387
 
381 388
     def start_hb(self, callback):
382 389
         """
5  static/embedded_sagecell.js
@@ -191,7 +191,7 @@ processEnvironments: false}\n\
191 191
                 // many prerequisites that have been smashed together into all.min.js
192 192
                 load({"src": sagecell.URLs.root + "static/all.min.js"})
193 193
             });
194  
-        }, undefined, "text/html");
  194
+        }, undefined);
195 195
 };
196 196
 
197 197
 sagecell.sagecell_dependencies_callback = function () {
@@ -512,7 +512,7 @@ sagecell.initCell = (function (sagecellInfo, k) {
512 512
     return sagecellInfo;
513 513
 });
514 514
 
515  
-sagecell.sendRequest = function (method, url, data, callback, files, accept) {
  515
+sagecell.sendRequest = function (method, url, data, callback, files) {
516 516
     method = method.toUpperCase();
517 517
     var hasFiles = false;
518 518
     if (files === undefined) {
@@ -559,7 +559,6 @@ sagecell.sendRequest = function (method, url, data, callback, files, accept) {
559 559
     if (window.FormData || !(isXDomain || hasFiles)) {
560 560
         // If an XMLHttpRequest is possible, use it
561 561
         xhr.open(method, url, true);
562  
-        xhr.setRequestHeader("Accept", accept || "application/json");
563 562
         xhr.onreadystatechange = function () {
564 563
             if (xhr.readyState === 4 /* DONE */) {
565 564
                 callback(xhr.responseText);
12  tests/multimechanize/config.cfg
... ...
@@ -0,0 +1,12 @@
  1
+[global]
  2
+run_time = 300
  3
+rampup = 10
  4
+results_ts_interval = 3
  5
+
  6
+[user_group-1]
  7
+threads = 350
  8
+script = simple_session.py
  9
+
  10
+[user_group-2]
  11
+threads = 150
  12
+script = interact_session.py
76  tests/multimechanize/test_scripts/client.py
... ...
@@ -0,0 +1,76 @@
  1
+import urllib2
  2
+import websocket
  3
+import json
  4
+import uuid
  5
+
  6
+root = "http://localhost:8888"
  7
+
  8
+class SageCellSession(object):
  9
+    def __init__(self):
  10
+        f = urllib2.urlopen("%s/kernel" % (root,), "")
  11
+        data = json.loads(f.read())
  12
+        f.close()
  13
+        self.kernel_id = data["kernel_id"]
  14
+        self.ws_url = data["ws_url"]
  15
+        self.iopub = websocket.create_connection("%skernel/%s/iopub" % (self.ws_url, self.kernel_id))
  16
+        self.shell = websocket.create_connection("%skernel/%s/shell" % (self.ws_url, self.kernel_id))
  17
+        self.session_id = str(uuid.uuid4())
  18
+
  19
+    def __del__(self):
  20
+        self.close()
  21
+
  22
+    def __enter__(self):
  23
+        return self
  24
+
  25
+    def __exit__(self, etype, value, traceback):
  26
+        self.close()
  27
+
  28
+    def execute(self, code):
  29
+        content = {"code": code,
  30
+                   "silent": False,
  31
+                   "user_variables": [],
  32
+                   "user_expressions": {"_sagecell_files": "sys._sage_.new_files()"},
  33
+                   "allow_stdin": False}
  34
+        self.send_msg("execute_request", content)
  35
+
  36
+    def update_interact(self, interact_id, values):
  37
+        self.execute("sys._sage_.update_interact(%r, %r)" % (interact_id, values))
  38
+
  39
+    def send_msg(self, msg_type, content):
  40
+        msg = {"header": {"msg_id": str(uuid.uuid4()),
  41
+                          "username": "username",
  42
+                          "session": self.session_id,
  43
+                          "msg_type": msg_type
  44
+                         },
  45
+               "metadata": {},
  46
+               "content": content,
  47
+               "parent_header":{}
  48
+              }
  49
+        self.shell.send(json.dumps(msg))
  50
+
  51
+    def close(self):
  52
+        self.iopub.close()
  53
+        self.shell.close()
  54
+
  55
+    def iopub_recv(self):
  56
+        return json.loads(self.iopub.recv())
  57
+
  58
+    def shell_recv(self):
  59
+        return json.loads(self.shell.recv())
  60
+
  61
+def load_root():
  62
+    resources = ["/", "/static/root.css", "/static/jquery.min.js",
  63
+                 "/static/embedded_sagecell.js",
  64
+                 "/static/jquery-ui/css/sagecell/jquery-ui-1.8.21.custom.css",
  65
+                 "/static/colorpicker/css/colorpicker.css",
  66
+                 "/static/all.min.css", "/static/mathjax/MathJax.js",
  67
+                 "/static/sagelogo.png", "/static/spinner.gif",
  68
+                 "/sagecell.html", "/static/all.min.js",
  69
+                 "/static/mathjax/config/TeX-AMS-MML_HTMLorMML.js",
  70
+                 "/static/mathjax/images/MenuArrow-15.png",
  71
+                 "/static/jquery-ui/css/sagecell/images/ui-bg_highlight-hard_60_99bbff_1x100.png",
  72
+                 "/static/mathjax/extensions/jsMath2jax.js",
  73
+                 "/static/jquery-ui/css/sagecell/images/ui-bg_highlight-hard_90_99bbff_1x100.png"]
  74
+    for r in resources:
  75
+        f = urllib2.urlopen(root + r)
  76
+        assert f.code == 200, "Bad response: HTTP %d" % (f.code,)
61  tests/multimechanize/test_scripts/interact_session.py
... ...
@@ -0,0 +1,61 @@
  1
+#! /usr/bin/env python
  2
+
  3
+import client
  4
+import time
  5
+import random
  6
+
  7
+computation = """@interact
  8
+def f(x=(1, 100, 1)):
  9
+    print x^2"""
  10
+
  11
+class Transaction(object):
  12
+    """
  13
+    A transaction that simulates loading the page
  14
+    and manipulating an interact
  15
+    """
  16
+
  17
+    def __init__(self):
  18
+        self.custom_timers = {}
  19
+
  20
+    def run(self):
  21
+        t  = time.time()
  22
+        client.load_root()
  23
+        self.custom_timers["root load"] = time.time() - t
  24
+        time.sleep(5)
  25
+        t = time.time()
  26
+        with client.SageCellSession() as s:
  27
+            self.custom_timers["initial connection"] = time.time() - t
  28
+            t = time.time()
  29
+            s.execute(computation)
  30
+            output = ""
  31
+            while True:
  32
+                msg = s.iopub_recv()
  33
+                if msg["header"]["msg_type"] == "status" and msg["content"]["execution_state"] == "idle":
  34
+                    break
  35
+                elif msg["header"]["msg_type"] == "display_data" and "application/sage-interact" in msg["content"]["data"]:
  36
+                    interact_id = msg["content"]["data"]["application/sage-interact"]["new_interact_id"]
  37
+                elif msg["header"]["msg_type"] == "stream" and msg["content"]["name"] == "stdout" and msg["metadata"]["interact_id"] == interact_id:
  38
+                    output += msg["content"]["data"]
  39
+            assert output == "1\n", "Incorrect output: %r" % (output,)
  40
+            times = []
  41
+            self.custom_timers["initial computation"] = time.time() - t
  42
+            for i in xrange(10):
  43
+                time.sleep(1)
  44
+                num = random.randint(1, 100)
  45
+                t = time.time()
  46
+                s.update_interact(interact_id, {"x": num})
  47
+                output = ""
  48
+                while True:
  49
+                    msg = s.iopub_recv()
  50
+                    if msg["header"]["msg_type"] == "status" and msg["content"]["execution_state"] == "idle":
  51
+                        break
  52
+                    elif msg["header"]["msg_type"] == "stream" and msg["content"]["name"] == "stdout" and msg["metadata"]["interact_id"] == interact_id:
  53
+                        output += msg["content"]["data"]
  54
+                assert int(output.strip()) == num * num, "Incorrect output: %r" % (output,)
  55
+                times.append(time.time() - t)
  56
+            self.custom_timers["interact update (average of 10)"] = sum(times) / len(times)
  57
+
  58
+if __name__ == "__main__":
  59
+    t = Transaction()
  60
+    t.run()
  61
+    print t.custom_timers
41  tests/multimechanize/test_scripts/simple_session.py
... ...
@@ -0,0 +1,41 @@
  1
+#! /usr/bin/env python
  2
+
  3
+import client
  4
+import time
  5
+import random
  6
+
  7
+class Transaction(object):
  8
+    """
  9
+    A transaction that simulates loading the page
  10
+    and performing a simple addition
  11
+    """
  12
+
  13
+    def __init__(self):
  14
+        self.custom_timers = {}
  15
+
  16
+    def run(self):
  17
+        t  = time.time()
  18
+        client.load_root()
  19
+        self.custom_timers["root load"] = time.time() - t
  20
+        time.sleep(5)
  21
+        t = time.time()
  22
+        with client.SageCellSession() as s:
  23
+            self.custom_timers["initial connection"] = time.time() - t
  24
+            t = time.time()
  25
+            num1 = random.randint(1, 10 ** 20)
  26
+            num2 = random.randint(1, 10 ** 20)
  27
+            s.execute("print %d + %d" % (num1, num2))
  28
+            output = ""
  29
+            while True:
  30
+                msg = s.iopub_recv()
  31
+                if msg["header"]["msg_type"] == "status" and msg["content"]["execution_state"] == "idle":
  32
+                    break
  33
+                elif msg["header"]["msg_type"] == "stream" and msg["content"]["name"] == "stdout":
  34
+                    output += msg["content"]["data"]
  35
+            assert int(output.strip()) == num1 + num2, "Incorrect output: %r" % (output,)
  36
+        self.custom_timers["computation"] = time.time() - t
  37
+
  38
+if __name__ == "__main__":
  39
+    t = Transaction()
  40
+    t.run()
  41
+    print t.custom_timers
2  trusted_kernel_manager.py
@@ -288,6 +288,8 @@ def end_session(self, kernel_id):
288 288
 
289 289
         :arg str kernel_id: the id of the kernel you want to kill
290 290
         """
  291
+        if not kernel_id in self._kernels:
  292
+            return
291 293
         comp_id = self._kernels[kernel_id]["comp_id"]
292 294
         def cb(reply):
293 295
             if (reply["type"] == "error"):
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.