pull in additional changes from 2.0 branch.
[monitor.git] / monitor / Rpyc / Demo / demo-6.py
diff --git a/monitor/Rpyc/Demo/demo-6.py b/monitor/Rpyc/Demo/demo-6.py
new file mode 100644 (file)
index 0000000..1c34039
--- /dev/null
@@ -0,0 +1,130 @@
+# as you can see - the import line now requires even less typing!\r
+from Rpyc import *\r
+c = SocketConnection("localhost")\r
+\r
+#------------------------------------------------------------------------------ \r
+# this demo shows the new `execute` and `namespace` features of rpyc\r
+#------------------------------------------------------------------------------ \r
+\r
+\r
+# the code below will run AT THE OTHER SIDE OF THE CONNECTION... so you'll see\r
+# 'hello world' on the server's console\r
+c.execute("print 'hello world'")\r
+\r
+import sys\r
+c.modules.sys.stdout = sys.stdout\r
+\r
+# and this time, on our console\r
+c.execute("print 'brave new world'")\r
+\r
+# restore that\r
+c.modules.sys.stdout = c.modules.sys.__stdout__\r
+\r
+# anyway, the `execute` method runs the given code at the other side of the connection\r
+# and works in the `namespace` dict. what?\r
+c.execute("x = [1,2,3]")\r
+print c.namespace.x\r
+\r
+# now it makes sense, doesn't it? the 'namespace' attribute is something i called \r
+# AttrFrontend -- it wraps a dict with the attribute protocol, so you can access\r
+# it with the dot notation, instead of the braces notation (more intuitive).\r
+# this namespace works both ways -- executing code affects the namespace, while\r
+# altering the namespace directly also affects it:\r
+c.namespace.x.append(4)\r
+c.execute("x.append(5)")\r
+print c.namespace.x\r
+\r
+# but you should not assign complex objects (not int/float/str, etc) to this namespace\r
+# directy, or NetProxies will be created. there's nothing wrong with that, but keep\r
+# in mind it might cause blocking (and even deadlocks), as i'll explain later.\r
+\r
+# another cool thing i want to show is the second, optional parameter to execute: mode.\r
+# the mode controls how the code is compiled. the default mode is "exec", which means \r
+# it executes the code as a module. the other option is "eval" which returns a value.\r
+# so if you want to _do_ something, like printing of assigning a variable, you do it \r
+# with "exec", and if you want to evaluate something, you do it with "eval"\r
+# for example:\r
+\r
+# this will print None\r
+print c.execute("1+2")\r
+\r
+# while this will print 3\r
+print c.execute("1+2", "eval")\r
+\r
+# but there's a time in a man's life when he asks himself, why the heck? you can, as i \r
+# showed in other places, just do this:\r
+#     c.modules.__builtin__.eval("1+2")\r
+# so what's the point? \r
+#\r
+# well, i've been waiting for this question. the rationale behind this seemingly useless \r
+# feature is for times you NEED to have the code executing remotely, but writing a \r
+# dedicated module for it is overdoing it:\r
+#  * more files to update ==> more chance that you'll forget to update\r
+#  * distributing the module to all of the machines\r
+#  * making a mess on the file system\r
+#  * it's really not a module... it's just some code that logically belongs to one single \r
+#    module, but technical difficulties prevent it\r
+#\r
+# and to show you what i mean -- i want to start a thread on the server, like it did in \r
+# several places over the demos. this thread will send me an event every second. what i \r
+# used to do was, creating another module, like testmodule.py to define the thread \r
+# function, so it will exist on the server, and i could call it.\r
+# if i defined thread_func at the client side, then the thread will block when trying \r
+# to execute the code, because the client holds it. so this new mechanism lets you \r
+# distribute code in a volatile fashion:\r
+#  * when the connection is closed, everything you defined is gone\r
+#  * no file-system mess\r
+#  * no need to distribute files across the network\r
+#  * only one place to maintain\r
+\r
+c.execute("""\r
+my_thread_active = True\r
+\r
+def my_thread_func(callback):\r
+    import time\r
+    from Rpyc import Async\r
+\r
+    callback = Async(callback)\r
+    while my_thread_active:\r
+        callback(time.time())\r
+        time.sleep(1)\r
+    print "the thread says goodbye"\r
+""")\r
+\r
+def callback(timestamp):\r
+    print "the timestamp is", timestamp\r
+\r
+c.modules.thread.start_new_thread(c.namespace.my_thread_func, (callback,))\r
+c.modules.time.sleep(5)\r
+c.namespace.my_thread_active = False\r
+c.close()\r
+\r
+# it's not only for threads of course. there are many times when you NEED the code/objects \r
+# on the remote side. for example:\r
+#  * situations that would block (like having the thread func on the client)\r
+#  * code that check the type of the object (type or isinstance), and a NetProxy would make\r
+#    it cry. DONT CHECK THE TYPE OF OBJECTS, PEOPLE, JUST USE THEM! that's why they invented \r
+#    duck-typing. argh.\r
+#  * other places i didnt think of as of yet. i want to sleep. leave me alone ;) zzzZZZ\r
+#\r
+# so enjoy!\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r