Initial revision

This commit is contained in:
Guido van Rossum 1995-04-26 22:57:11 +00:00
parent 107c747009
commit 5f07b84796
5 changed files with 839 additions and 0 deletions

285
Demo/pdist/RCSProxy.py Executable file
View File

@ -0,0 +1,285 @@
#! /usr/local/bin/python
"""RCS Proxy.
Provide a simplified interface on RCS files, locally or remotely.
The functionality is geared towards implementing some sort of
remote CVS like utility. It is modeled after the similar module
FSProxy.
The module defines three classes:
RCSProxyLocal -- used for local access
RCSProxyServer -- used on the server side of remote access
RCSProxyClient -- 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
import string
import tempfile
okchars = string.letters + string.digits + '-_=+.'
class RCSProxyLocal:
def __init__(self):
self._dirstack = []
def _close(self):
while self._dirstack:
self.back()
def pwd(self):
return os.getcwd()
def cd(self, name):
save = os.getcwd()
os.chdir(name)
self._dirstack.append(save)
def back(self):
if not self._dirstack:
raise os.error, "empty directory stack"
dir = self._dirstack[-1]
os.chdir(dir)
del self._dirstack[-1]
def _filter(self, files, pat = None):
if pat:
def keep(name, pat = pat):
return fnmatch.fnmatch(name, pat)
files = filter(keep, files)
files.sort()
return files
def isfile(self, name):
namev = name + ',v'
return os.path.isfile(namev) or \
os.path.isfile(os.path.join('RCS', namev))
def _unmangle(self, name):
if type(name) == type(''):
rev = ''
else:
name, rev = name
return name, rev
def checkfile(self, name):
name, rev = self._unmangle(name)
if not self.isfile(name):
raise os.error, 'not an rcs file %s' % `name`
for c in rev:
if c not in okchars:
raise ValueError, "bad char in rev"
return name, rev
def listfiles(self, pat = None):
def isrcs(name): return name[-2:] == ',v'
def striprcs(name): return name[:-2]
files = os.listdir(os.curdir)
files = filter(isrcs, files)
if os.path.isdir('RCS'):
files2 = os.listdir('RCS')
files2 = filter(isrcs, files2)
files = files + files2
files = map(striprcs, 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 isdir(self, name):
return os.path.isdir(name)
def _open(self, name, cmd = 'co -p'):
name, rev = self.checkfile(name)
namev = name + ',v'
if rev:
cmd = cmd + ' -r' + rev
return os.popen('%s %s' % (cmd, `namev`))
def _closepipe(self, f):
sts = f.close()
if sts:
raise IOError, "Exit status %d" % sts
def _remove(self, fn):
try:
os.unlink(fn)
except os.error:
pass
def sum(self, name):
f = self._open(name)
BUFFERSIZE = 1024*8
sum = md5.new()
while 1:
buffer = f.read(BUFFERSIZE)
if not buffer:
break
sum.update(buffer)
self._closepipe(f)
return sum.digest()
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 _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 get(self, name):
f = self._open(name)
data = f.read()
self._closepipe(f)
return data
def info(self, name):
f = self._open(name, 'rlog -h')
dict = {}
while 1:
line = f.readline()
if not line: break
if line[0] == '\t':
continue # XXX lock details, later
i = string.find(line, ':')
if i > 0:
key, value = line[:i], string.strip(line[i+1:])
dict[key] = value
self._closepipe(f)
return dict
def head(self, name):
dict = self.info(name)
return dict['head']
def log(self, name, flags = ''):
f = self._open(name, 'rlog %s 2>&1' % flags)
log = f.read()
self._closepipe(f)
return log
def put(self, fullname, data, message = ""):
if message and message[-1] != '\n':
message = message + '\n'
name, rev = self._unmangle(fullname)
new = not self.isfile(name)
if new:
for c in name:
if c not in okchars:
raise ValueError, "bad char in name"
else:
self._remove(name)
f = open(name, 'w')
f.write(data)
f.close()
tf = tempfile.mktemp()
try:
if not new:
cmd = "rcs -l%s %s >>%s 2>&1" % (rev, name, tf)
sts = os.system(cmd)
if sts:
raise IOError, "rcs -l exit status %d" % sts
cmd = "ci -r%s %s >>%s 2>&1" % (rev, name, tf)
p = os.popen(cmd, 'w')
p.write(message)
sts = p.close()
if sts:
raise IOError, "ci exit status %d" % sts
messages = open(tf).read()
return messages or None
finally:
self._remove(tf)
def mkdir(self, name):
os.mkdir(name, 0777)
def rmdir(self, name):
os.rmdir(name)
class RCSProxyServer(RCSProxyLocal, server.Server):
def __init__(self, address, verbose = server.VERBOSE):
RCSProxyLocal.__init__(self)
server.Server.__init__(self, address, verbose)
def _close(self):
server.Server._close(self)
RCSProxyLocal._close(self)
def _serve(self):
server.Server._serve(self)
# Retreat into start directory
while self._dirstack: self.back()
class RCSProxyClient(client.Client):
def __init__(self, address, verbose = client.VERBOSE):
client.Client.__init__(self, address, verbose)
def test_server():
import string
import sys
if sys.argv[1:]:
port = string.atoi(sys.argv[1])
else:
port = 4127
proxy = RCSProxyServer(('', port))
proxy._serverloop()
def test():
import sys
if not sys.argv[1:] or sys.argv[1] and sys.argv[1][0] in '0123456789':
test_server()
sys.exit(0)
proxy = RCSProxyLocal()
what = sys.argv[1]
if hasattr(proxy, what):
attr = getattr(proxy, what)
if callable(attr):
print apply(attr, tuple(sys.argv[2:]))
else:
print `attr`
else:
print "%s: no such attribute" % what
sys.exit(2)
if __name__ == '__main__':
test()

2
Demo/pdist/README Normal file
View File

@ -0,0 +1,2 @@
This directory contains various modules and classes that support
remote file system operations

186
Demo/pdist/cvslib.py Executable file
View File

@ -0,0 +1,186 @@
"""Utilities to read and write CVS admin files (esp. CVS/Entries)"""
import string
import os
import time
class Entry:
"""Class representing one (parsed) line from CVS/Entries"""
def __init__(self, line):
words = string.splitfields(line, '/')
self.file = words[1]
self.rev = words[2]
dates = words[3] # ctime, mtime
if dates[:7] == 'Initial':
self.ctime = None
self.mtime = None
self.new = 1
else:
self.ctime = unctime(dates[:24])
self.mtime = unctime(dates[25:])
self.new = 0
self.extra = words[4]
self.sum = None
def unparse(self):
if self.new:
dates = "Initial %s" % self.file
else:
dates = gmctime(self.ctime) + ' ' + gmctime(self.mtime)
return "/%s/%s/%s/%s/\n" % (
self.file,
self.rev,
dates,
self.extra)
def setsum(self, sum):
self.sum = sum
def getsum(self):
return self.sum
def sethexsum(self, hexsum):
self.setsum(unhexify(hexsum))
def gethexsum(self):
if self.sum:
return hexify(self.sum)
else:
return None
class CVS:
"""Class representing the contents of CVS/Entries (and CVS/Sums)"""
def __init__(self):
self.readentries()
def readentries(self):
self.entries = {}
f = self.cvsopen("Entries")
while 1:
line = f.readline()
if not line: break
e = Entry(line)
self.entries[e.file] = e
f.close()
def readsums(self):
try:
f = self.cvsopen("Sums")
except IOError:
return
while 1:
line = f.readline()
if not line: break
words = string.split(line)
[file, rev, hexsum] = words
e = self.entries[file]
if e.rev == rev:
e.sethexsum(hexsum)
f.close()
def writeentries(self):
f = self.cvsopen("Entries", 'w')
for file in self.keys():
f.write(self.entries[file].unparse())
f.close()
def writesums(self):
if self.cvsexists("Sums"):
f = self.cvsopen("Sums", 'w')
else:
f = None
for file in self.keys():
e = self.entries[file]
hexsum = e.gethexsum()
if hexsum:
if not f:
f = self.cvsopen("Sums", 'w')
f.write("%s %s %s\n" % (file, e.rev, hexsum))
if f:
f.close()
def keys(self):
keys = self.entries.keys()
keys.sort()
return keys
def cvsexists(self, file):
file = os.path.join("CVS", file)
return os.path.exists(file)
def cvsopen(self, file, mode = 'r'):
file = os.path.join("CVS", file)
if 'r' not in mode:
self.backup(file)
return open(file, mode)
def backup(self, file):
if os.path.isfile(file):
bfile = file + '~'
os.rename(file, bfile)
hexify_format = '%02x' * 16
def hexify(sum):
"Return a hex representation of a 16-byte string (e.g. an MD5 digest)"
return hexify_format % tuple(map(ord, sum))
def unhexify(hexsum):
"Return the original from a hexified string"
sum = ''
for i in range(0, len(hexsum), 2):
sum = sum + chr(string.atoi(hexsum[i:i+2], 16))
return sum
unctime_monthmap = {}
def unctime(date):
if not unctime_monthmap:
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
i = 0
for m in months:
i = i+1
unctime_monthmap[m] = i
words = string.split(date) # Day Mon DD HH:MM:SS YEAR
year = string.atoi(words[4])
month = unctime_monthmap[words[1]]
day = string.atoi(words[2])
[hh, mm, ss] = map(string.atoi, string.splitfields(words[3], ':'))
ss = ss - time.timezone
return time.mktime((year, month, day, hh, mm, ss, 0, 0, 0))
def gmctime(t):
return time.asctime(time.gmtime(t))
def test_unctime():
now = int(time.time())
t = time.gmtime(now)
at = time.asctime(t)
print 'GMT', now, at
print 'timezone', time.timezone
print 'local', time.ctime(now)
u = unctime(at)
print 'unctime()', u
gu = time.gmtime(u)
print '->', gu
print time.asctime(gu)
def test():
x = CVS()
keys = x.entries.keys()
keys.sort()
for file in keys:
e = x.entries[file]
print file, e.rev, gmctime(e.ctime), gmctime(e.mtime), e.extra,
print e.gethexsum()
if __name__ == "__main__":
test()

192
Demo/pdist/rcvs.py Executable file
View File

@ -0,0 +1,192 @@
from cvslib import CVS, Entry
import RCSProxy
import client
import md5
import os
import string
import sys
import time
import fnmatch
ignored_patterns = ['*.pyc', '.*', '*~', '@*']
def ignored(file):
if os.path.isdir(file): return 1
for pat in ignored_patterns:
if fnmatch.fnmatch(file, pat): return 1
return 0
class PCVS(CVS):
def __init__(self, proxy):
CVS.__init__(self)
self.proxy = proxy
self.readsums()
self.calcsums()
def calcsums(self):
for file in self.keys():
e = self.entries[file]
if not e.new and e.sum is None:
sum = self.proxy.sum((file, e.rev))
e.setsum(sum)
def fullcheck(self):
ok = 1
for file in self.keys():
e = self.entries[file]
if e.new:
if self.proxy.isfile(file):
print "%s: created by someone else!"
ok = 0
continue
rrev = self.proxy.head(file)
if rrev != e.rev:
print "%s: out of date (%s vs. %s)" % \
(file, e.rev, rrev)
ok = 0
return ok
def update(self):
for file in self.keys():
e = self.entries[file]
if e.new:
print 'A', file
continue
rrev = self.proxy.head(file)
lsum = sumfile(file)
if rrev == e.rev:
if lsum == e.sum:
print '=', file
else:
print 'M', file
continue
if e.sum != lsum:
print "%s: conflict -- not updated" % file
continue
print "%s: getting ..." % file
data = self.proxy.get(file)
f = open(file, 'w')
f.write(data)
f.close()
nsum = md5.new(data).digest()
e.setsum(nsum)
e.rev = rrev
print 'U', file
self.writeentries()
self.writesums()
def commit(self):
if not self.fullcheck():
print "correct above errors first"
return
needed = []
for file in self.keys():
e = self.entries[file]
if e.new:
needed.append(file)
continue
lsum = sumfile(file)
if lsum != e.sum:
needed.append(file)
continue
if not needed:
print "no changes need committing"
return
message = raw_input("One-liner: ")
for file in needed:
print "%s: putting ..." % file
e = self.entries[file]
data = open(file).read()
self.proxy.put(file, data, message)
e.rev = self.proxy.head(file)
e.setsum(self.proxy.sum(file))
# XXX get it?
mtime, ctime = os.stat(file)[-2:]
e.mtime = mtime
e.ctime = ctime
self.writeentries()
self.writesums()
def report(self):
keys = self.keys()
files = os.listdir(os.curdir)
allfiles = files
for file in keys:
if file not in allfiles:
allfiles.append(file)
allfiles.sort()
for file in allfiles:
if file not in keys:
if not ignored(file):
print '?', file
continue
if file not in files:
print file, ': lost'
continue
e = self.entries[file]
if not os.path.exists(file):
print "%s: lost" % file
continue
if e.new:
print 'A', file
continue
lsum = sumfile(file)
rrev = self.proxy.head(file)
if rrev == e.rev:
if lsum == e.sum:
print '=', file
else:
print 'M', file
else:
if lsum == e.sum:
print 'U', file
else:
print 'C', file
def add(self, file):
if self.entries.has_key(file):
print "%s: already known"
else:
self.entries[file] = Entry('/%s/0/Initial %s//\n' %
(file, file))
def sumfile(file):
return md5.new(open(file).read()).digest()
def test():
proxy = RCSProxy.RCSProxyClient(('voorn.cwi.nl', 4127))
proxy.cd('/ufs/guido/voorn/python-RCS/Demo/pdist')
x = PCVS(proxy)
args = sys.argv[1:]
if args:
cmd = args[0]
files = args[1:]
if cmd == 'add':
if not files:
print "add needs at least one file argument"
else:
for file in files:
x.add(file)
x.writeentries()
elif cmd in ('update', 'up'):
if files:
print "updates wants no file arguments"
else:
x.update()
elif cmd in ('commit', 'com'):
if files:
print "commit wants no file arguments"
else:
x.commit()
else:
print "Unknown command", cmd
else:
x.report()
if sys.argv[1:]: x.writesums()
if __name__ == "__main__":
test()

174
Demo/pdist/rrcs.py Executable file
View File

@ -0,0 +1,174 @@
#! /usr/local/bin/python
import sys
import os
import getopt
import string
import md5
import tempfile
def main():
sys.stdout = sys.stderr
try:
opts, rest = getopt.getopt(sys.argv[1:], 'h:p:qv')
if not rest:
raise getopt.error, "missing command"
cmd, rest = rest[0], rest[1:]
if not commands.has_key(cmd):
raise getopt.error, "unknown command"
coptset, func = commands[cmd]
copts, files = getopt.getopt(rest, coptset)
except getopt.error, msg:
print msg
print "usage: rrcs [options] command [options] [file] ..."
print "where command can be:"
print " ci|put # checkin the given files"
print " co|get # checkout"
print " info # print header info"
print " head # print revision of head branch"
print " list # list filename if valid"
print " log # print full log"
print " diff # diff rcs file and work file"
print "if no files are given, all remote rcs files are assumed"
sys.exit(2)
x = openclient(opts)
if not files:
files = x.listfiles()
for fn in files:
try:
func(x, copts, fn)
except (IOError, os.error), msg:
print "%s: %s" % (fn, msg)
def openclient(opts):
import client
import RCSProxy
host = 'spam'
port = 4127
verbose = client.VERBOSE
for o, a in opts:
if o == '-h':
host = a
if ':' in host:
i = string.find(host, ':')
host, p = host[:i], host[i+1:]
if p:
port = string.atoi(p)
if o == '-p':
port = string.atoi(a)
if o == '-v':
verbose = verbose + 1
if o == '-q':
verbose = 0
address = (host, port)
x = RCSProxy.RCSProxyClient(address, verbose)
return x
def checkin(x, copts, fn):
f = open(fn)
data = f.read()
f.close()
new = not x.isfile(fn)
if not new and same(x, copts, fn, data):
print "%s: unchanged since last checkin" % fn
return
message = asklogmessage(new)
messages = x.put(fn, data, message)
if messages:
print messages
def checkout(x, copts, fn):
data = x.get(fn)
f = open(fn, 'w')
f.write(data)
f.close()
def info(x, copts, fn):
dict = x.info(fn)
keys = dict.keys()
keys.sort()
for key in keys:
print key + ':', dict[key]
print '='*70
def head(x, copts, fn):
head = x.head(fn)
print fn, head
def list(x, copts, fn):
if x.isfile(fn):
print fn
def log(x, copts, fn):
flags = ''
for o, a in copts:
flags = flags + ' ' + o + a
flags = flags[1:]
messages = x.log(fn, flags)
print messages
def diff(x, copts, fn):
if same(x, copts, fn):
return
flags = ''
for o, a in copts:
flags = flags + ' ' + o + a
flags = flags[1:]
data = x.get(fn)
tfn = tempfile.mktemp()
try:
tf = open(tfn, 'w')
tf.write(data)
tf.close()
print 'diff %s -r%s %s' % (flags, x.head(fn), fn)
sts = os.system('diff %s %s %s' % (flags, tfn, fn))
if sts:
print '='*70
finally:
remove(tfn)
def same(x, copts, fn, data = None):
if data is None:
f = open(fn)
data = f.read()
f.close()
lsum = md5.new(data).digest()
rsum = x.sum(fn)
return lsum == rsum
def asklogmessage(new):
if new:
print "enter description,",
else:
print "enter log message,",
print "terminate with single '.' or end of file:"
if new:
print "NOTE: This is NOT the log message!"
message = ""
while 1:
sys.stderr.write(">> ")
sys.stderr.flush()
line = sys.stdin.readline()
if not line or line == '.\n': break
message = message + line
return message
def remove(fn):
try:
os.unlink(fn)
except os.error:
pass
commands = {
'ci': ('', checkin),
'put': ('', checkin),
'co': ('', checkout),
'get': ('', checkout),
'info': ('', info),
'head': ('', head),
'list': ('', list),
'log': ('bhLRtd:l:r:s:w:V:', log),
'diff': ('c', diff),
}
main()