├── .gitignore ├── LICENSE.txt ├── README.md ├── client.js ├── index.html ├── methods.py ├── server.py └── style.css /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | 21 | # Installer logs 22 | pip-log.txt 23 | 24 | # Unit test / coverage reports 25 | .coverage 26 | .tox 27 | nosetests.xml 28 | 29 | # Translations 30 | *.mo 31 | 32 | # Mr Developer 33 | .mr.developer.cfg 34 | .project 35 | .pydevproject 36 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2013 Patrick Fuller, patrick-fuller.com 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Python-Javascript Communication 2 | =============================== 3 | 4 | An example of using the [Tornado Web Server](http://www.tornadoweb.org/en/stable/) 5 | enable running Python (as a "server") through a website (the "client") with 6 | Javascript. 7 | 8 | This example uses [websockets](http://en.wikipedia.org/wiki/WebSocket), a way 9 | to create a "bridge" between the Python server and Javascript client. It has 10 | multiple advantages over HTTP-based communication, and is especially preferable 11 | in situations where you can tell your users to not use Internet Explorer. 12 | 13 | This code provides enough infrastructure to write your own methods by appending 14 | files. Any function added to `methods.py` can be called from the client by 15 | making a corresponding function in `static/client.js`, as well as some basic 16 | logic to handle the server response. Follow the setup of the "count" function 17 | to get this running. 18 | 19 | There is also basic error handling - stack traces from the server are sent back 20 | to the client and displayed in an alert window. 21 | 22 | Dependencies 23 | ------------ 24 | 25 | This program uses Tornado to handle the mess of communication. 26 | 27 | ``` 28 | pip install tornado 29 | ``` 30 | 31 | Usage 32 | ----- 33 | 34 | Once the dependencies are met, just run 35 | 36 | ``` 37 | python server.py 38 | ``` 39 | 40 | and then open http://localhost:8000/ in a browser. The example uses server-side 41 | python to count numbers. 42 | -------------------------------------------------------------------------------- /client.js: -------------------------------------------------------------------------------- 1 | /*global WebSocket, JSON, $, window, console, alert*/ 2 | "use strict"; 3 | /** 4 | * Function calls across the background TCP socket. Uses JSON RPC + a queue. 5 | * (I've added this extra logic to simplify expanding this) 6 | */ 7 | var client = { 8 | queue: {}, 9 | 10 | // Connects to Python through the websocket 11 | connect: function (port) { 12 | var self = this; 13 | this.socket = new WebSocket("ws://" + window.location.hostname + ":" + port + "/websocket"); 14 | 15 | this.socket.onopen = function () { 16 | console.log("Connected!"); 17 | }; 18 | 19 | this.socket.onmessage = function (messageEvent) { 20 | var router, current, updated, jsonRpc; 21 | 22 | jsonRpc = JSON.parse(messageEvent.data); 23 | router = self.queue[jsonRpc.id]; 24 | delete self.queue[jsonRpc.id]; 25 | self.result = jsonRpc.result; 26 | 27 | // If there's an error, display it in an alert window 28 | if (jsonRpc.error) { 29 | alert(jsonRpc.result); 30 | 31 | // If the response is from "count", do stuff 32 | } else if (router === "count") { 33 | current = $(".answer").html(); 34 | if (current.length === 0) { 35 | updated = jsonRpc.result; 36 | } else if (current.length > 100) { 37 | updated = current.substring(current.length - 100) + ", " + jsonRpc.result; 38 | } else { 39 | updated = current + ", " + jsonRpc.result; 40 | } 41 | $(".answer").html(updated); 42 | $(".number").val(jsonRpc.result); 43 | 44 | // If the response is from anything else, it's currently unsupported 45 | } else { 46 | alert("Unsupported function: " + router); 47 | } 48 | }; 49 | }, 50 | 51 | // Generates a unique identifier for request ids 52 | // Code from http://stackoverflow.com/questions/105034/ 53 | // how-to-create-a-guid-uuid-in-javascript/2117523#2117523 54 | uuid: function () { 55 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { 56 | var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); 57 | return v.toString(16); 58 | }); 59 | }, 60 | 61 | // Placeholder function. It adds one to things. 62 | count: function (data) { 63 | var uuid = this.uuid(); 64 | this.socket.send(JSON.stringify({method: "count", id: uuid, params: {number: data}})); 65 | this.queue[uuid] = "count"; 66 | } 67 | }; 68 | 69 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |Counting is hard. Let Python do the heavy lifting for you.
12 | 13 | 14 |