pull in additional changes from 2.0 branch.
[monitor.git] / monitor / Rpyc / Demo / demo-1.py
diff --git a/monitor/Rpyc/Demo/demo-1.py b/monitor/Rpyc/Demo/demo-1.py
new file mode 100644 (file)
index 0000000..ea1c5a1
--- /dev/null
@@ -0,0 +1,156 @@
+#\r
+# welcome to RPyC. this demo serves as an introduction. i believe in learning through\r
+# showcases, and that's why this package comes with a demo subpackage, instead of\r
+# documentation\r
+# \r
+# so, the first thing we're gonna do is import the SocketConnection. this is a factory\r
+# function that returns us a new Connection object over a socket stream. we dont need\r
+# to get into details here.\r
+#\r
+from Rpyc.Factories import SocketConnection\r
+\r
+#\r
+# next, we'll get all the helpful utilities. the utilities include wrappers for builtin\r
+# functions, like dir(), so they'd work as expected with netproxies. \r
+#\r
+from Rpyc.Utils import *\r
+\r
+#\r
+# by now you should have an rpyc server running. if you dont, go to the Servers directory\r
+# and choose your favorite version of a socket server. for unixes i'd recommend the \r
+# forking server; for windows -- the threaded server.\r
+#\r
+# so let's connect to the server\r
+#\r
+c = SocketConnection("localhost", 22222)\r
+\r
+#\r
+# now it's time to explain a little about how rpyc works. it's quite simple really. the\r
+# magic comes from a concept called NetProxy. a NetProxy object delivers all of the\r
+# operations performed on it to the remote object. so if you get a list from your host,\r
+# what you're are really getting is a NetProxy to that list. it looks and works just\r
+# like a real list -- but everytime you do something on it, it actually performs a \r
+# request on the list object stored on the host. this is called boxing. this means\r
+# you can change the object you get locally, and the remote object changes, etc.\r
+#\r
+# however, for efficiency and other reason, not all objects you get are NetProxies.\r
+# all immutable and pickle-able objects pass by value (through pickle). these types\r
+# of objects include ints, longs, strings, and some other types. all other types are\r
+# passed by boxing.\r
+#\r
+# this boxing mechanism works on everything -- objects, functions, classes, and modules,\r
+# which is why rpyc is considered transparent. your code looks just as if it was meant \r
+# to run locally.\r
+#\r
+\r
+#\r
+# let's start with something simple -- getting a remote module.  accessing the remote \r
+# namespace always starts with the `modules` attribute, then the module (or package) \r
+# name, and then the attribute you want to get.\r
+#\r
+\r
+print c.modules.sys\r
+print c.modules.sys.path \r
+c.modules.sys.path.append("lucy")\r
+print c.modules.sys.path[-1]\r
+\r
+#\r
+# these remote objects are first class objects, like all others in python. this means\r
+# you can store them in variables, pass them as parameters, etc.\r
+#\r
+rsys = c.modules.sys\r
+rpath = rsys.path\r
+rpath.pop(-1)\r
+\r
+#\r
+# and as you might expect, netproxies also look like the real objects\r
+#\r
+print dir(rpath)\r
+\r
+#\r
+# but there are a couple of issues with netproxies. the type(), isinstance(), and \r
+# issubclass() classes dont work on them... as they query the underlying object, not\r
+# the remote one. so:\r
+#\r
+print type(rsys.maxint) # <int> -- because it's a simple type which is passed by value)\r
+print type(rsys.path)   # <SyncNetProxy> -- because, after all, it's a netproxy, not a list\r
+\r
+#\r
+# now for a demo of packages\r
+# (which looks very much like 'from xml.dom.minidom import parseString')\r
+#\r
+parseString = c.modules.xml.dom.minidom.parseString\r
+x = parseString("<a>lala</a>")\r
+print x\r
+x.toxml()\r
+print x.firstChild.nodeName\r
+\r
+#\r
+# however, there's a catch when working with packages like that. the way it works is\r
+# trying to find an attribute with that name, and if not found, trying to import a sub-\r
+# module. \r
+#\r
+# now in english:\r
+# c.module.xml is the xml module of the server. when you do c.module.xml.dom, rpyc looks\r
+# for an attribute named 'dom' inside the xml module. since there's no such attribute,\r
+# it tries to import a subpackage called xml.dom, which succeeds. then it does the same\r
+# for xml.dom.minidom, and xml.dom.minidom.parseString.\r
+#\r
+# but there are times when that's ambiguous. this mean that the module has both a sub-\r
+# module called 'X', and an attribute called 'X'. according to rpyc's algorithm, the\r
+# attribute 'X' is returned, not the sub-module.\r
+#\r
+# but if you need to be explicit, you can, and it works like this:\r
+#\r
+\r
+c.modules["xml.dom.minidom"].parseString("<a></a>")\r
+\r
+#\r
+# this will make sure the module 'xml.dom.minidom' is returned, and not an attribute.\r
+# in general, it's better to use this form, unless you know there are no such conflicts.\r
+# remeber that "Explicit is better than implicit", although it requires four more key\r
+# strokes. perhaps in a later version it will raise an exception if there's a conflict.\r
+#\r
+\r
+#\r
+# and now for a little demo of working with files (a common task)\r
+#\r
+f = c.modules.__builtin__.open("lala.txt", "w")\r
+f.write("lucy")\r
+f.close()\r
+c.modules.os.remove("lala.txt")\r
+\r
+#\r
+# now to a bitter part of life: exceptions. as you could expect, they work just like\r
+# regular exceptions\r
+#\r
+try:\r
+    a = c.modules.sys.nonexistent_attribute\r
+except AttributeError:\r
+    pass\r
+else:\r
+    assert False\r
+\r
+try:\r
+    a = c.modules.__builtin__.open("**\\//##~~..::!@#$%^&*()_+\n <,>?")\r
+except IOError:\r
+    pass\r
+else:\r
+    assert False\r
+\r
+print "goodbye"\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r