summaryrefslogtreecommitdiffstats
path: root/Demo/pdist
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>1995-04-10 11:40:52 (GMT)
committerGuido van Rossum <guido@python.org>1995-04-10 11:40:52 (GMT)
commit07a272d9dec67f8eb4f2bf1396a2f13671fdaba2 (patch)
tree5222965ba7973a2dd6848ea4d4a4cf5594e406db /Demo/pdist
parent21bc15b73da1e3e5ab60ff9b93a95bfd390fea3f (diff)
downloadcpython-07a272d9dec67f8eb4f2bf1396a2f13671fdaba2.zip
cpython-07a272d9dec67f8eb4f2bf1396a2f13671fdaba2.tar.gz
cpython-07a272d9dec67f8eb4f2bf1396a2f13671fdaba2.tar.bz2
commit -- why not
Diffstat (limited to 'Demo/pdist')
-rwxr-xr-xDemo/pdist/FSProxy.py322
-rwxr-xr-xDemo/pdist/client.py133
-rwxr-xr-xDemo/pdist/cmptree.py185
-rwxr-xr-xDemo/pdist/server.py111
-rwxr-xr-xDemo/pdist/sumtree.py24
5 files changed, 775 insertions, 0 deletions
diff --git a/Demo/pdist/FSProxy.py b/Demo/pdist/FSProxy.py
new file mode 100755
index 0000000..7510d1e
--- /dev/null
+++ b/Demo/pdist/FSProxy.py
@@ -0,0 +1,322 @@
+"""File System Proxy.
+
+Provide an OS-neutral view on a file system, locally or remotely.
+The functionality is geared towards implementing some sort of
+rdist-like utility between a Mac and a UNIX system.
+
+The module defines three classes:
+
+FSProxyLocal -- used for local access
+FSProxyServer -- used on the server side of remote access
+FSProxyClient -- used on the client side of remote access
+
+The remote classes are instantiated with an IP address and an optional
+verbosity flag.
+"""
+
+import server
+import client
+import md5
+import os
+import fnmatch
+from stat import *
+import time
+import fnmatch
+
+if os.name == 'mac':
+ import macfs
+ maxnamelen = 31
+else:
+ macfs = None
+ maxnamelen = 255
+
+skipnames = (os.curdir, os.pardir)
+
+
+class FSProxyLocal:
+
+ def __init__(self):
+ self._dirstack = []
+ self._ignore = ['*.pyc'] + self._readignore()
+
+ def _close(self):
+ while self._dirstack:
+ self.back()
+
+ def _readignore(self):
+ file = self._hide('ignore')
+ try:
+ f = open(file)
+ except IOError:
+ file = self._hide('synctree.ignorefiles')
+ try:
+ f = open(file)
+ except IOError:
+ return []
+ ignore = []
+ while 1:
+ line = f.readline()
+ if not line: break
+ if line[-1] == '\n': line = line[:-1]
+ ignore.append(line)
+ f.close()
+ return ignore
+
+ def _hidden(self, name):
+ if os.name == 'mac':
+ return name[0] == '(' and name[-1] == ')'
+ else:
+ return name[0] == '.'
+
+ def _hide(self, name):
+ if os.name == 'mac':
+ return '(%s)' % name
+ else:
+ return '.%s' % name
+
+ def visible(self, name):
+ if len(name) > maxnamelen: return 0
+ if name[-1] == '~': return 0
+ if name in skipnames: return 0
+ if self._hidden(name): return 0
+ head, tail = os.path.split(name)
+ if head or not tail: return 0
+ if macfs:
+ if os.path.exists(name) and not os.path.isdir(name):
+ try:
+ fs = macfs.FSSpec(name)
+ c, t = fs.GetCreatorType()
+ if t != 'TEXT': return 0
+ except macfs.error, msg:
+ print "***", name, msg
+ return 0
+ else:
+ if os.path.islink(name): return 0
+ if '\0' in open(name, 'rb').read(512): return 0
+ for ign in self._ignore:
+ if fnmatch.fnmatch(name, ign): return 0
+ return 1
+
+ def check(self, name):
+ if not self.visible(name):
+ raise os.error, "protected name %s" % repr(name)
+
+ def checkfile(self, name):
+ self.check(name)
+ if not os.path.isfile(name):
+ raise os.error, "not a plain file %s" % repr(name)
+
+ def pwd(self):
+ return os.getcwd()
+
+ def cd(self, name):
+ self.check(name)
+ save = os.getcwd(), self._ignore
+ os.chdir(name)
+ self._dirstack.append(save)
+ self._ignore = self._ignore + self._readignore()
+
+ def back(self):
+ if not self._dirstack:
+ raise os.error, "empty directory stack"
+ dir, ignore = self._dirstack[-1]
+ os.chdir(dir)
+ del self._dirstack[-1]
+ self._ignore = ignore
+
+ def _filter(self, files, pat = None):
+ if pat:
+ def keep(name, pat = pat):
+ return fnmatch.fnmatch(name, pat)
+ files = filter(keep, files)
+ files = filter(self.visible, files)
+ files.sort()
+ return files
+
+ def list(self, pat = None):
+ files = os.listdir(os.curdir)
+ return self._filter(files, pat)
+
+ def listfiles(self, pat = None):
+ files = os.listdir(os.curdir)
+ files = filter(os.path.isfile, files)
+ return self._filter(files, pat)
+
+ def listsubdirs(self, pat = None):
+ files = os.listdir(os.curdir)
+ files = filter(os.path.isdir, files)
+ return self._filter(files, pat)
+
+ def exists(self, name):
+ return self.visible(name) and os.path.exists(name)
+
+ def isdir(self, name):
+ return self.visible(name) and os.path.isdir(name)
+
+ def islink(self, name):
+ return self.visible(name) and os.path.islink(name)
+
+ def isfile(self, name):
+ return self.visible(name) and os.path.isfile(name)
+
+ def sum(self, name):
+ self.checkfile(name)
+ BUFFERSIZE = 1024*8
+ f = open(name)
+ sum = md5.new()
+ while 1:
+ buffer = f.read(BUFFERSIZE)
+ if not buffer:
+ break
+ sum.update(buffer)
+ return sum.digest()
+
+ def size(self, name):
+ self.checkfile(name)
+ return os.stat(name)[ST_SIZE]
+
+ def mtime(self, name):
+ self.checkfile(name)
+ return time.localtime(os.stat(name)[ST_MTIME])
+
+ def stat(self, name):
+ self.checkfile(name)
+ size = os.stat(name)[ST_SIZE]
+ mtime = time.localtime(os.stat(name)[ST_MTIME])
+ return size, mtime
+
+ def info(self, name):
+ sum = self.sum(name)
+ size = os.stat(name)[ST_SIZE]
+ mtime = time.localtime(os.stat(name)[ST_MTIME])
+ return sum, size, mtime
+
+ def _list(self, function, list):
+ if list is None:
+ list = self.listfiles()
+ res = []
+ for name in list:
+ try:
+ res.append((name, function(name)))
+ except (os.error, IOError):
+ res.append((name, None))
+ return res
+
+ def sumlist(self, list = None):
+ return self._list(self.sum, list)
+
+ def statlist(self, list = None):
+ return self._list(self.stat, list)
+
+ def mtimelist(self, list = None):
+ return self._list(self.mtime, list)
+
+ def sizelist(self, list = None):
+ return self._list(self.size, list)
+
+ def infolist(self, list = None):
+ return self._list(self.info, list)
+
+ def _dict(self, function, list):
+ if list is None:
+ list = self.listfiles()
+ dict = {}
+ for name in list:
+ try:
+ dict[name] = function(name)
+ except (os.error, IOError):
+ pass
+ return dict
+
+ def sumdict(self, list = None):
+ return self.dict(self.sum, list)
+
+ def sizedict(self, list = None):
+ return self.dict(self.size, list)
+
+ def mtimedict(self, list = None):
+ return self.dict(self.mtime, list)
+
+ def statdict(self, list = None):
+ return self.dict(self.stat, list)
+
+ def infodict(self, list = None):
+ return self._dict(self.info, list)
+
+ def read(self, name, offset = 0, length = -1):
+ self.checkfile(name)
+ f = open(name)
+ f.seek(offset)
+ if length == 0:
+ data = ''
+ elif length < 0:
+ data = f.read()
+ else:
+ data = f.read(length)
+ f.close()
+ return data
+
+ def create(self, name):
+ self.check(name)
+ if os.path.exists(name):
+ self.checkfile(name)
+ bname = name + '~'
+ try:
+ os.unlink(bname)
+ except os.error:
+ pass
+ os.rename(name, bname)
+ f = open(name, 'w')
+ f.close()
+
+ def write(self, name, data, offset = 0):
+ self.checkfile(name)
+ f = open(name, 'r+')
+ f.seek(offset)
+ f.write(data)
+ f.close()
+
+ def mkdir(self, name):
+ self.check(name)
+ os.mkdir(name, 0777)
+
+ def rmdir(self, name):
+ self.check(name)
+ os.rmdir(name)
+
+
+class FSProxyServer(FSProxyLocal, server.Server):
+
+ def __init__(self, address, verbose = server.VERBOSE):
+ FSProxyLocal.__init__(self)
+ server.Server.__init__(self, address, verbose)
+
+ def _close(self):
+ server.Server._close(self)
+ FSProxyLocal._close(self)
+
+ def _serve(self):
+ server.Server._serve(self)
+ # Retreat into start directory
+ while self._dirstack: self.back()
+
+
+class FSProxyClient(client.Client):
+
+ def __init__(self, address, verbose = client.VERBOSE):
+ client.Client.__init__(self, address, verbose)
+
+
+def test():
+ import string
+ import sys
+ if sys.argv[1:]:
+ port = string.atoi(sys.argv[1])
+ else:
+ port = 4127
+ proxy = FSProxyServer(('', port))
+ proxy._serverloop()
+
+
+if __name__ == '__main__':
+ test()
diff --git a/Demo/pdist/client.py b/Demo/pdist/client.py
new file mode 100755
index 0000000..4b5cfc5
--- /dev/null
+++ b/Demo/pdist/client.py
@@ -0,0 +1,133 @@
+"""RPC Client module."""
+
+import sys
+import socket
+import pickle
+import __builtin__
+import os
+
+
+# Default verbosity (0 = silent, 1 = print connections, 2 = print requests too)
+VERBOSE = 1
+
+
+class Client:
+
+ """RPC Client class. No need to derive a class -- it's fully generic."""
+
+ def __init__(self, address, verbose = VERBOSE):
+ if type(address) == type(0):
+ address = ('', address)
+ self._address = address
+ self._verbose = verbose
+ if self._verbose: print "Connecting to %s ..." % repr(address)
+ self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self._socket.connect(address)
+ if self._verbose: print "Connected."
+ self._lastid = 0 # Last id for which a reply has been received
+ self._nextid = 1 # Id of next request
+ self._replies = {} # Unprocessed replies
+ self._rf = self._socket.makefile('r')
+ self._wf = self._socket.makefile('w')
+ self._methods = self._call('.methods')
+
+ def __del__(self):
+ self._close()
+
+ def _close(self):
+ if self._rf: self._rf.close()
+ self._rf = None
+ if self._wf: self._wf.close()
+ self._wf = None
+ if self._socket: self._socket.close()
+ self._socket = None
+
+ def __getattr__(self, name):
+ if name in self._methods:
+ method = _stub(self, name)
+ setattr(self, name, method) # XXX circular reference
+ return method
+ raise AttributeError, name
+
+ def _setverbose(self, verbose):
+ self._verbose = verbose
+
+ def _call(self, name, *args):
+ return self._vcall(name, args)
+
+ def _vcall(self, name, args):
+ return self._recv(self._vsend(name, args))
+
+ def _send(self, name, *args):
+ return self._vsend(name, args)
+
+ def _send_noreply(self, name, *args):
+ return self._vsend(name, args, 0)
+
+ def _vsend_noreply(self, name, args):
+ return self._vsend(name, args, 0)
+
+ def _vsend(self, name, args, wantreply = 1):
+ id = self._nextid
+ self._nextid = id+1
+ if not wantreply: id = -id
+ request = (name, args, id)
+ if self._verbose > 1: print "sending request: %s" % repr(request)
+ wp = pickle.Pickler(self._wf)
+ wp.dump(request)
+ return id
+
+ def _recv(self, id):
+ exception, value, rid = self._vrecv(id)
+ if rid != id:
+ raise RuntimeError, "request/reply id mismatch: %d/%d" % (id, rid)
+ if exception is None:
+ return value
+ x = exception
+ if hasattr(__builtin__, exception):
+ x = getattr(__builtin__, exception)
+ elif exception in ('posix.error', 'mac.error'):
+ x = os.error
+ if x == exception:
+ exception = x
+ raise exception, value
+
+ def _vrecv(self, id):
+ self._flush()
+ if self._replies.has_key(id):
+ if self._verbose > 1: print "retrieving previous reply, id = %d" % id
+ reply = self._replies[id]
+ del self._replies[id]
+ return reply
+ aid = abs(id)
+ while 1:
+ if self._verbose > 1: print "waiting for reply, id = %d" % id
+ rp = pickle.Unpickler(self._rf)
+ reply = rp.load()
+ del rp
+ if self._verbose > 1: print "got reply: %s" % repr(reply)
+ rid = reply[2]
+ arid = abs(rid)
+ if arid == aid:
+ if self._verbose > 1: print "got it"
+ return reply
+ self._replies[rid] = reply
+ if arid > aid:
+ if self._verbose > 1: print "got higher id, assume all ok"
+ return (None, None, id)
+
+ def _flush(self):
+ self._wf.flush()
+
+
+class _stub:
+
+ """Helper class for Client -- each instance serves as a method of the client."""
+
+ def __init__(self, client, name):
+ self._client = client
+ self._name = name
+
+ def __call__(self, *args):
+ return self._client._vcall(self._name, args)
+
diff --git a/Demo/pdist/cmptree.py b/Demo/pdist/cmptree.py
new file mode 100755
index 0000000..84ed3f0
--- /dev/null
+++ b/Demo/pdist/cmptree.py
@@ -0,0 +1,185 @@
+"""Compare local and remote dictionaries and transfer differing files -- like rdist."""
+
+import sys
+from repr import repr
+import FSProxy
+import time
+import os
+
+def main():
+ pwd = os.getcwd()
+ s = raw_input("chdir [%s] " % pwd)
+ if s:
+ os.chdir(s)
+ pwd = os.getcwd()
+ host = ask("host", 'voorn.cwi.nl')
+ port = 4127
+ verbose = 1
+ mode = ''
+ print """\
+Mode should be a string of characters, indicating what to do with differences.
+r - read different files to local file system
+w - write different files to remote file system
+c - create new files, either remote or local
+d - delete disappearing files, either remote or local
+"""
+ s = raw_input("mode [%s] " % mode)
+ if s: mode = s
+ address = (host, port)
+ t1 = time.time()
+ local = FSProxy.FSProxyLocal()
+ remote = FSProxy.FSProxyClient(address, verbose)
+ compare(local, remote, mode)
+ remote._close()
+ local._close()
+ t2 = time.time()
+ dt = t2-t1
+ mins, secs = divmod(dt, 60)
+ print mins, "minutes and", secs, "seconds"
+ raw_input("[Return to exit] ")
+
+def ask(prompt, default):
+ s = raw_input("%s [%s] " % (prompt, default))
+ return s or default
+
+def askint(prompt, default):
+ s = raw_input("%s [%s] " % (prompt, str(default)))
+ if s: return string.atoi(s)
+ return default
+
+def compare(local, remote, mode):
+ print
+ print "PWD =", `os.getcwd()`
+ sums_id = remote._send('sumlist')
+ subdirs_id = remote._send('listsubdirs')
+ remote._flush()
+ print "calculating local sums ..."
+ lsumdict = {}
+ for name, info in local.sumlist():
+ lsumdict[name] = info
+ print "getting remote sums ..."
+ sums = remote._recv(sums_id)
+ print "got", len(sums)
+ rsumdict = {}
+ for name, rsum in sums:
+ rsumdict[name] = rsum
+ if not lsumdict.has_key(name):
+ print `name`, "only remote"
+ if 'r' in mode and 'c' in mode:
+ recvfile(local, remote, name)
+ else:
+ lsum = lsumdict[name]
+ if lsum != rsum:
+ print `name`,
+ rmtime = remote.mtime(name)
+ lmtime = local.mtime(name)
+ if rmtime > lmtime:
+ print "remote newer",
+ if 'r' in mode:
+ recvfile(local, remote, name)
+ elif lmtime > rmtime:
+ print "local newer",
+ if 'w' in mode:
+ sendfile(local, remote, name)
+ else:
+ print "same mtime but different sum?!?!",
+ print
+ for name in lsumdict.keys():
+ if not rsumdict.keys():
+ print `name`, "only locally",
+ fl()
+ if 'w' in mode and 'c' in mode:
+ sendfile(local, remote, name)
+ elif 'r' in mode and 'd' in mode:
+ os.unlink(name)
+ print "removed."
+ print
+ print "gettin subdirs ..."
+ subdirs = remote._recv(subdirs_id)
+ common = []
+ for name in subdirs:
+ if local.isdir(name):
+ print "Common subdirectory", repr(name)
+ common.append(name)
+ else:
+ print "Remote subdirectory", repr(name), "not found locally"
+ lsubdirs = local.listsubdirs()
+ for name in lsubdirs:
+ if name not in subdirs:
+ print "Local subdirectory", repr(name), "not found remotely"
+ for name in common:
+ print "Entering subdirectory", repr(name)
+ local.cd(name)
+ remote.cd(name)
+ compare(local, remote, mode)
+ remote.back()
+ local.back()
+
+def sendfile(local, remote, name):
+ try:
+ remote.create(name)
+ except (IOError, os.error), msg:
+ print "cannot create:", msg
+ return
+
+ print "sending ...",
+ fl()
+
+ data = open(name).read()
+
+ t1 = time.time()
+
+ remote._send_noreply('write', name, data)
+ remote._flush()
+
+ t2 = time.time()
+
+ dt = t2-t1
+ print len(data), "bytes in", t2-t1, "seconds",
+ if dt:
+ print "i.e.", len(data)/dt, "bytes/sec",
+ print
+
+def recvfile(local, remote, name):
+ try:
+ local.create(name)
+ except (IOError, os.error), msg:
+ print "cannot create:", msg
+ return
+
+ print "receiving ...",
+ fl()
+
+ f = open(name, 'w')
+ t1 = time.time()
+
+ length = 4*1024
+ offset = 0
+ id = remote._send('read', name, offset, length)
+ remote._flush()
+ while 1:
+ newoffset = offset + length
+ newid = remote._send('read', name, newoffset, length)
+ data = remote._recv(id)
+ id = newid
+ if not data: break
+ f.seek(offset)
+ f.write(data)
+ offset = newoffset
+ size = f.tell()
+
+ t2 = time.time()
+ f.close()
+
+ dt = t2-t1
+ print size, "bytes in", dt, "seconds",
+ if dt:
+ print "i.e.", int(size/dt), "bytes/sec",
+ print
+ remote._recv(id) # ignored
+
+def fl():
+ sys.stdout.flush()
+
+if __name__ == '__main__':
+ main()
diff --git a/Demo/pdist/server.py b/Demo/pdist/server.py
new file mode 100755
index 0000000..5d42abc
--- /dev/null
+++ b/Demo/pdist/server.py
@@ -0,0 +1,111 @@
+"""RPC Server module."""
+
+import sys
+import socket
+import pickle
+from fnmatch import fnmatch
+from repr import repr
+
+
+# Default verbosity (0 = silent, 1 = print connections, 2 = print requests too)
+VERBOSE = 1
+
+
+class Server:
+
+ """RPC Server class. Derive a class to implement a particular service."""
+
+ def __init__(self, address, verbose = VERBOSE):
+ if type(address) == type(0):
+ address = ('', address)
+ self._address = address
+ self._verbose = verbose
+ self._socket = None
+ self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self._socket.bind(address)
+ self._socket.listen(1)
+ self._listening = 1
+
+ def _setverbose(self, verbose):
+ self._verbose = verbose
+
+ def __del__(self):
+ self._close()
+
+ def _close(self):
+ self._listening = 0
+ if self._socket:
+ self._socket.close()
+ self._socket = None
+
+ def _serverloop(self):
+ while self._listening:
+ self._serve()
+
+ def _serve(self):
+ if self._verbose: print "Wait for connection ..."
+ conn, address = self._socket.accept()
+ if self._verbose: print "Accepted connection from %s" % repr(address)
+ if not self._verify(conn, address):
+ print "*** Connection from %s refused" % repr(address)
+ conn.close()
+ return
+ rf = conn.makefile('r')
+ wf = conn.makefile('w')
+ ok = 1
+ while ok:
+ wf.flush()
+ if self._verbose > 1: print "Wait for next request ..."
+ ok = self._dorequest(rf, wf)
+
+ _valid = ['192.16.201.*', '192.16.197.*']
+
+ def _verify(self, conn, address):
+ host, port = address
+ for pat in self._valid:
+ if fnmatch(host, pat): return 1
+ return 0
+
+ def _dorequest(self, rf, wf):
+ rp = pickle.Unpickler(rf)
+ try:
+ request = rp.load()
+ except EOFError:
+ return 0
+ if self._verbose > 1: print "Got request: %s" % repr(request)
+ try:
+ methodname, args, id = request
+ if '.' in methodname:
+ reply = (None, self._special(methodname, args), id)
+ elif methodname[0] == '_':
+ raise NameError, "illegal method name %s" % repr(methodname)
+ else:
+ method = getattr(self, methodname)
+ reply = (None, apply(method, args), id)
+ except:
+ reply = (sys.exc_type, sys.exc_value, id)
+ if id < 0 and reply[:2] == (None, None):
+ if self._verbose > 1: print "Suppress reply"
+ return 1
+ if self._verbose > 1: print "Send reply: %s" % repr(reply)
+ wp = pickle.Pickler(wf)
+ wp.dump(reply)
+ return 1
+
+ def _special(self, methodname, args):
+ if methodname == '.methods':
+ if not hasattr(self, '_methods'):
+ self._methods = tuple(self._listmethods())
+ return self._methods
+ raise NameError, "unrecognized special method name %s" % repr(methodname)
+
+ def _listmethods(self, cl=None):
+ if not cl: cl = self.__class__
+ names = cl.__dict__.keys()
+ names = filter(lambda x: x[0] != '_', names)
+ names.sort()
+ for base in cl.__bases__:
+ basenames = self._listmethods(base)
+ basenames = filter(lambda x, names=names: x not in names, basenames)
+ names[len(names):] = basenames
+ return names
diff --git a/Demo/pdist/sumtree.py b/Demo/pdist/sumtree.py
new file mode 100755
index 0000000..92c1fd0
--- /dev/null
+++ b/Demo/pdist/sumtree.py
@@ -0,0 +1,24 @@
+import time
+import FSProxy
+
+def main():
+ t1 = time.time()
+ #proxy = FSProxy.FSProxyClient(('voorn.cwi.nl', 4127))
+ proxy = FSProxy.FSProxyLocal()
+ sumtree(proxy)
+ proxy._close()
+ t2 = time.time()
+ print t2-t1, "seconds"
+ raw_input("[Return to exit] ")
+
+def sumtree(proxy):
+ print "PWD =", proxy.pwd()
+ files = proxy.listfiles()
+ proxy.infolist(files)
+ subdirs = proxy.listsubdirs()
+ for name in subdirs:
+ proxy.cd(name)
+ sumtree(proxy)
+ proxy.back()
+
+main()