From: "Russ Cox" <rsc@plan9.bell-labs.com>
To: 9fans@cse.psu.edu
Subject: Re: [9fans] Serving 9p in python - anyone started that?
Date: Wed, 13 Nov 2002 14:18:51 -0500 [thread overview]
Message-ID: <cbe90192194055d2b334055aab40baeb@plan9.bell-labs.com> (raw)
yep. here's a minimal 9p2000 test scaffolding.
it predates the python dial, hence the weird exec of aux/trampoline
in connetwork.
all i wrote was version testing. but the fcall module
is complete. and the arg parser is nice.
# To unbundle, run this file
echo 9ptest
sed 's/.//' >9ptest <<'//GO.SYSIN DD 9ptest'
-#!/bin/python
-
-from arg import ArgParser
-from fcall import *
-from plan9 import *
-from plan9 import _exec
-import plan9, sys
-
-t,r = None, None
-fd = None
-Tag = 1
-chatty = 0
-
-def chat(s):
- if chatty:
- sys.stderr.write(s+'\n')
- sys.stderr.flush()
-
-def conservice(name):
- return plan9.open(name, plan9.ORDWR)
-
-def connetwork(addr):
- return concommand('aux/trampoline '+addr)
-
-def concommand(cmd):
- p = pipe()
- pid = rfork(RFFDG|RFPROC|RFNOWAIT)
- if pid == 0:
- try:
- close(p[0])
- dup(p[1], 0)
- dup(p[1], 1)
- _exec('/bin/rc', ['rc', '-c', cmd])
- except Exception, msg:
- sys.stderr.write('cmd exec: '+str(msg)+'\n')
- sys.stderr.flush()
- sys.exit(1)
- else:
- close(p[1])
- return p[0]
-
-def tx(_t):
- global t, r
- t = _t
- pkt = t.unload()
- chat('-> %s' % (t,))
- n = write(fd, pkt)
- try:
- pkt = read9pmsg(fd)
- r = Fcall()
- r.load(pkt)
- except Exception, e:
- sys.stdout.write('sent: '+str(t)+'\n')
- sys.stdout.write('error receiving response: '+str(e)+'\n')
-# sys.exit(1)
- raise
- chat('<- %s' % (r,))
- return r
-
-def note(s):
- sys.stdout.write('sent: '+str(t)+'\n')
- sys.stdout.write('recv: '+str(r)+'\n')
- sys.stdout.write(s+'\n')
- sys.stdout.flush()
-
-def testversion():
- r = tx(mkfcall(Tflush, Tag, 42))
- if r.type != Terror:
- note('server should reject messages before first Tversion is sent')
-
- # test various msizes and versions
- def test(msize, vers, expectvers='9P2000'):
- r = tx(mkfcall(Tversion, Tag, msize, vers))
- if r.type != Rversion:
- note('response to Tversion should always be Rversion')
- return
- if r.msize > msize:
- note('response msize must be <= request msize')
- if r.version != expectvers:
- note('response version must be '+expectvers)
-
- test(8192+IOHDRSZ, '9P2000') # common case
- test(512, '9P2000') # small msize
- test(8192+IOHDRSZ, '9P2005') # future protocol
- test(16384, '9P2000') # large msize
- test(8192+IOHDRSZ, '9P2000.1') # subprotocol
- test(8192+IOHDRSZ, '9P2001.2') # future subprotocol
- test(8192+IOHDRSZ, '8P2000', 'unknown')
-
-def main():
- global fd, chatty
- arg = ArgParser('usage: 9ptest [-c|-n] service\n')
- con = conservice
- for o in arg:
- if o=='-c':
- con = concommand
- elif o=='-n':
- con = connetwork
- elif o=='-v':
- chatty += 1
- else:
- arg.error('unknown option '+o)
- if len(arg.argv) != 1:
- arg.error()
-
- fd = con(arg.argv[0])
-
- testversion()
-
-
-if __name__=='__main__':
- main()
//GO.SYSIN DD 9ptest
echo fcall.py
sed 's/.//' >fcall.py <<'//GO.SYSIN DD fcall.py'
-"""Pack and unpack 9P2000 messages."""
-
-from __future__ import nested_scopes, generators
-import sys, struct
-
-class FcallError(Exception):
- def __init__(self, msg=None):
- if msg:
- self.msg = msg
-
-IOHDRSZ = 24
-
-Tversion, \
-Rversion, \
-Tauth, \
-Rauth, \
-Tattach, \
-Rattach, \
-Terror, \
-Rerror, \
-Tflush, \
-Rflush, \
-Twalk, \
-Rwalk, \
-Topen, \
-Ropen, \
-Tcreate, \
-Rcreate, \
-Tread, \
-Rread, \
-Twrite, \
-Rwrite, \
-Tclunk, \
-Rclunk, \
-Tremove, \
-Rremove, \
-Tstat, \
-Rstat, \
-Twstat, \
-Rwstat = range(100, 128)
-
-fcalls = {
- Tversion: ('LS', ['msize', 'version']),
- Rversion: ('LS', ['msize', 'version']),
-
- Tauth: ('LSS', ['afid', 'uname', 'aname']),
- Rauth: ('q', ['aqid']),
-
- Rerror: ('S', ['ename']),
-
- Tflush: ('H', ['oldtag']),
- Rflush: ('', []),
-
- Tattach: ('LLSS', ['fid', 'afid', 'uname', 'aname']),
- Rattach: ('Q', ['qid']),
-
- Twalk: ('FF#S', ['fid', 'newfid', 'wname']),
- Rwalk: ('#Q', ['qid']),
-
- Topen: ('LB', ['fid', 'mode']),
- Ropen: ('qL', ['qid', 'iounit']),
-
- Tcreate: ('LSLB', ['fid', 'name', 'perm', 'mode']),
- Rcreate: ('qL', ['qid', 'iounit']),
-
- Tread: ('LQL', ['fid', 'offset', 'count']),
- Rread: ('D', ['data']),
-
- Twrite: ('LQD', ['fid', 'offset', 'data']),
- Rwrite: ('L', ['count']),
-
- Tclunk: ('L', ['fid']),
- Rclunk: ('', []),
-
- Tremove: ('L', ['fid']),
- Rremove: ('', []),
-
- Tstat: ('L', ['fid']),
- Rstat: ('D', ['stat']),
-
- Twstat: ('LD', ['fid', 'stat']),
- Rwstat: ('', [])
-}
-
-fcallnames = {
- Tversion: 'Tversion',
- Rversion: 'Rversion',
-
- Tauth: 'Tauth',
- Rauth: 'Rauth',
-
- Rerror: 'Rerror',
-
- Tflush: 'Tflush',
- Rflush: 'Rflush',
-
- Tattach: 'Tattach',
- Rattach: 'Rattach',
-
- Twalk: 'Twalk',
- Rwalk: 'Rwalk',
-
- Topen: 'Topen',
- Ropen: 'Ropen',
-
- Tcreate: 'Tcreate',
- Rcreate: 'Rcreate',
-
- Tread: 'Tread',
- Rread: 'Rread',
-
- Twrite: 'Twrite',
- Rwrite: 'Rwrite',
-
- Tclunk: 'Tclunk',
- Rclunk: 'Rclunk',
-
- Tremove: 'Tremove',
- Rremove: 'Rremove',
-
- Tstat: 'Tstat',
- Rstat: 'Rstat',
-
- Twstat: 'Twstat',
- Rwstat: 'Rwstat',
-}
-
-def pack(fmt, vals):
- pkt = ''
- while fmt:
- c = fmt[0]
- fmt = fmt[1:]
- v = vals[0]
- vals = vals[1:]
- if c in 'BHLQ':
- pkt += struct.pack(c, v)
- elif c == 'S':
- pkt += pack('H', (len(v),))
- pkt += v
- elif c == 'q':
- pkt += pack('VLB', v.path, v.vers, v.type)
- elif c == 'D':
- pkt += pack('L', (len(v),))
- pkt += v
- elif c == '#':
- c = fmt[0]
- fmt = fmt[1:]
- pkt += pack(c*len(v), v)
- else:
- raise Exception('unknown format character \''+c+'\'')
- return pkt
-
-def unpack(fmt, pkt):
- l = []
- while fmt:
- c = fmt[0]
- fmt = fmt[1:]
- if c == 'B':
- v, = struct.unpack('<B', pkt[:1])
- pkt = pkt[1:]
- elif c == 'H':
- v, = struct.unpack('<H', pkt[:2])
- pkt = pkt[2:]
- elif c == 'L':
- v, = struct.unpack('<L', pkt[:4])
- pkt = pkt[4:]
- elif c == 'Q':
- v, = struct.unpack('<Q', pkt[:8])
- pkt = pkt[8:]
- elif c == 'S':
- n, = struct.unpack('<H', pkt[:2])
- v = pkt[2:2+n]
- pkt = pkt[2+n:]
- elif c == 'q':
- q, pkt = unpack('VLB', pkt[:8+4+1])
- v = plan9.Qid().fromtuple(q)
- elif c == 'D':
- n, = struct.unpack('<L', pkt[:4])
- v = pkt[4:4+n]
- pkt = pkt[4+n:]
- elif c == '#':
- n, = struct.unpack('<H', pkt[:2])
- c = fmt[0]
- fmt = fmt[1:]
- v, pkt = unpack(c*n, pkt[2:])
- else:
- raise Exception('unknown format character \''+c+'\'')
- l.append(v)
- return l, pkt
-
-def mkfcall(*args):
- f = Fcall()
- f.loadtuple(*args)
- return f
-
-class Fcall:
- def __init__(self, type=None, args=None):
- if type==None:
- return
- if not fcalls.has_key(type):
- raise Exception('bad fcall type')
- self.type = type
- if args != None:
- self.fromtuple(args)
- return
-
- def loadtuple(self, *args):
- self.type = args[0]
- self.tag = args[1]
- names = fcalls[self.type][1]
- off = 2
- for n in names:
- self.__dict__[n] = args[off]
- off += 1
- return self
-
- def load(self, pkt):
- (l, self.type, self.tag), npkt = unpack('LBH', pkt)
- if l != len(pkt):
- raise Exception('packet length field is a lie')
- pkt = npkt
- if not fcalls.has_key(self.type):
- raise Exception('bad packet type %d' % (self.type,))
- fmt, names = fcalls[self.type]
- vals, pkt = unpack(fmt, pkt)
- if pkt:
- raise Exception('packet too large')
- for n,v in zip(names, vals):
- self.__dict__[n] = v
- return self
-
- def unload(self):
- hdr = pack('BH', (self.type, self.tag))
- fmt, names = fcalls[self.type]
- dat = pack(fmt, map(lambda x: self.__dict__[x], names))
- l = pack('L', (4+len(hdr)+len(dat),))
- return l+hdr+dat
-
- def __repr__(self):
- try:
- s = fcallnames[self.type]
- for n in fcalls[self.type][1]:
- s += ' '+str(self.__dict__[n])
- return s
- except Exception:
- return '<malformed or uninitialized Fcall>'
-
//GO.SYSIN DD fcall.py
echo arg.py
sed 's/.//' >arg.py <<'//GO.SYSIN DD arg.py'
-from __future__ import generators
-import sys, copy
-
-"""Simple command-line argument iterator.
-
-A typical example looks like:
-
- arg = ArgParser('usage: example [-d] [-r root] database\n')
- for o in arg:
- if o=='-d':
- debug=1
- elif o=='-r':
- root = arg.nextarg()
- else:
- arg.error('unknown option '+o)
- if len(arg.argv) != 1:
- arg.error()
-
-This illustrates all the important points:
-
- * arg is a generator that returns the next argument.
- >>>The behavior of the generator depends on the behavior
- of the body of your for loop.<<< This is why you don't have
- to tell the ArgParser about your options a priori. If an option
- has an argument, you call arg.nextarg() to fetch it. (If you
- want an option to have two arguments, call arg.nextarg twice.)
-
- * After the loop has finished, arg.argv contains the
- remaining command-line arguments.
-
- * Calling arg.error prints the optional passed string,
- then prints the usage message, then exits.
-
-A few other points:
-
- * Consistent with typical Unix conventions, option parsing ends
- after seeing '--', before seeing '-', or before any command-line argument
- not beginning with a - (that wasn't gobbled as an option argument).
- Assuming that -c does not take an argument but -f does, the following
- set of command lines and parsings illustrates the rules:
- foo -c -- -a
- opts: -c
- args: -a
- foo -c - -a
- opts: -c
- args: - -a
- foo -c bar baz -a
- opts: -c
- args: bar baz -a
- foo -cfbar baz -a
- opts: -c -f
- (-f took bar)
- args: baz -a
- foo -cf bar baz -a
- opts: -c -f
- (-f took bar)
- args: baz -a
- foo -fc bar baz -a
- opts: -f
- (-f took c)
- args: bar baz -a
-
- * Single character short options begin with - and take arguments from the
- rest of the current argument or from the next argument. For example,
- foo -cbar
- foo -c bar
- are equivalent. There is no support for optional arguments, since that
- introduces command-line parsing ambiguities.
-
- * Long options begin with -- and take arguments from an optional
- ``=ARG'' suffix. For example,
- foo --long=bar
- is the only way to specify an argument to the --long option.
- If the handler for '--long' does not fetch the argument with arg.nextarg,
- the parser will call arg.error automatically. There is support for optional
- arguments to long options, since that does not introduce any ambiguities.
- To fetch an optional argument call arg.nextarg(allownone=1). If there
- is no argument, None will be returned.
-
-"""
-
-class ArgError(Exception):
- def __init__(self, msg=None):
- if msg:
- self.msg = msg
-
-class ArgParser:
- def __init__(self, usage, argv=sys.argv):
- self.argv0 = argv[0]
- self.argv = argv[1:]
- self.usage = usage
- self.waitingarg = ''
-
- def __iter__(self):
- # this assumes the "
- while self.argv:
- if self.argv[0]=='-' or self.argv[0][0]!='-':
- break
- a = self.argv.pop(0)
- if a=='--':
- break
- if a[0:2]=='--':
- i = a.find('=')
- if i==-1:
- self.waitingarg=None
- self.option = a
- yield self.option
- self.option = None
- else:
- self.waitingarg = a[i+1:]
- self.option = a[0:i]
- yield self.option
- if self.waitingarg: # wasn't fetched using optarg
- self.error(self.option+' does not take an argument')
- self.option = None
- continue
- self.waitingarg = a[1:]
- while self.waitingarg:
- a = self.waitingarg[0:1]
- self.waitingarg = self.waitingarg[1:]
- self.option = '-'+a
- yield self.option
- self.option = None
-
- def nextarg(self, allownone=0):
- if self.waitingarg==None:
- if allownone:
- return None
- self.error(self.option+' requires an argument')
- elif self.waitingarg:
- ret = self.waitingarg
- self.waitingarg=''
- else:
- try:
- ret = self.argv.pop(0)
- except IndexError:
- self.error(self.option+' requires an argument')
- return ret
-
- def error(self, msg=None):
- if msg:
- sys.stderr.write('argument error: '+msg+'\n')
- sys.stderr.write(self.usage)
- sys.stderr.flush()
- sys.exit(1)
-
//GO.SYSIN DD arg.py
next reply other threads:[~2002-11-13 19:18 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2002-11-13 19:18 Russ Cox [this message]
-- strict thread matches above, loose matches on Subject: below --
2002-11-14 20:56 markp
2002-11-14 21:02 ` matt
2002-11-14 18:54 Russ Cox
2002-11-15 11:52 ` Boyd Roberts
[not found] <rsc@plan9.bell-labs.com>
2002-11-14 17:33 ` Russ Cox
2002-11-14 18:28 ` Scott Schwartz
2002-11-14 18:46 ` matt
2002-11-14 19:19 ` Dan Cross
2002-11-14 19:00 ` William Josephson
2002-11-14 9:49 Fco.J.Ballesteros
2002-11-14 10:28 ` matt
2002-11-14 9:42 okamoto
2002-11-14 8:53 Russ Cox
2002-11-14 17:05 ` Ronald G. Minnich
2002-11-14 18:24 ` Scott Schwartz
2002-11-14 8:34 Fco.J.Ballesteros
2002-11-14 17:37 ` John E. Barham
2002-11-13 17:22 matt
2002-11-13 19:33 ` John E. Barham
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=cbe90192194055d2b334055aab40baeb@plan9.bell-labs.com \
--to=rsc@plan9.bell-labs.com \
--cc=9fans@cse.psu.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).