You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by rh...@apache.org on 2008/03/04 21:03:12 UTC
svn commit: r633610 - in /incubator/qpid/trunk/qpid/python: ./ qpid/ tests/
Author: rhs
Date: Tue Mar 4 12:03:09 2008
New Revision: 633610
URL: http://svn.apache.org/viewvc?rev=633610&view=rev
Log:
import of in-process 0-10 final python client
Added:
incubator/qpid/trunk/qpid/python/hello-010-world (with props)
incubator/qpid/trunk/qpid/python/qpid/assembler.py (with props)
incubator/qpid/trunk/qpid/python/qpid/codec010.py (with props)
incubator/qpid/trunk/qpid/python/qpid/connection010.py (with props)
incubator/qpid/trunk/qpid/python/qpid/datatypes.py (with props)
incubator/qpid/trunk/qpid/python/qpid/delegates.py (with props)
incubator/qpid/trunk/qpid/python/qpid/framer.py (with props)
incubator/qpid/trunk/qpid/python/qpid/invoker.py (with props)
incubator/qpid/trunk/qpid/python/qpid/packer.py (with props)
incubator/qpid/trunk/qpid/python/qpid/session.py (with props)
incubator/qpid/trunk/qpid/python/qpid/spec010.py (with props)
incubator/qpid/trunk/qpid/python/qpid/util.py (with props)
incubator/qpid/trunk/qpid/python/server010 (with props)
incubator/qpid/trunk/qpid/python/tests/assembler.py (with props)
incubator/qpid/trunk/qpid/python/tests/connection010.py (with props)
incubator/qpid/trunk/qpid/python/tests/datatypes.py (with props)
incubator/qpid/trunk/qpid/python/tests/framer.py (with props)
incubator/qpid/trunk/qpid/python/tests/spec010.py (with props)
Modified:
incubator/qpid/trunk/qpid/python/tests/__init__.py
Added: incubator/qpid/trunk/qpid/python/hello-010-world
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/python/hello-010-world?rev=633610&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/python/hello-010-world (added)
+++ incubator/qpid/trunk/qpid/python/hello-010-world Tue Mar 4 12:03:09 2008
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+
+from qpid.connection010 import Connection
+from qpid.spec010 import load
+from qpid.util import connect
+from qpid.datatypes import Message
+
+spec = load("../specs/amqp.0-10.xml")
+conn = Connection(connect("0.0.0.0", spec.port), spec)
+conn.start(timeout=10)
+
+ssn = conn.session("my-session")
+
+ssn.queue_declare("asdf")
+
+ssn.message_transfer("this", None, None, Message("testing..."))
+ssn.message_transfer("is")
+ssn.message_transfer("a")
+ssn.message_transfer("test")
+
+print ssn.queue_query("testing")
+
+ssn.close(timeout=10)
+conn.close(timeout=10)
Propchange: incubator/qpid/trunk/qpid/python/hello-010-world
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/qpid/trunk/qpid/python/qpid/assembler.py
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/python/qpid/assembler.py?rev=633610&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/python/qpid/assembler.py (added)
+++ incubator/qpid/trunk/qpid/python/qpid/assembler.py Tue Mar 4 12:03:09 2008
@@ -0,0 +1,110 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from codec010 import StringCodec
+from framer import *
+
+class Segment:
+
+ def __init__(self, first, last, type, track, channel, payload):
+ self.id = None
+ self.offset = None
+ self.first = first
+ self.last = last
+ self.type = type
+ self.track = track
+ self.channel = channel
+ self.payload = payload
+
+ def decode(self, spec):
+ segs = spec["segment_type"]
+ choice = segs.choices[self.type]
+ return getattr(self, "decode_%s" % choice.name)(spec)
+
+ def decode_control(self, spec):
+ sc = StringCodec(spec, self.payload)
+ return sc.read_control()
+
+ def decode_command(self, spec):
+ sc = StringCodec(spec, self.payload)
+ return sc.read_command()
+
+ def decode_header(self, spec):
+ sc = StringCodec(spec, self.payload)
+ values = []
+ while len(sc.encoded) > 0:
+ values.append(sc.read_struct32())
+ return values
+
+ def decode_body(self, spec):
+ return self
+
+ def __str__(self):
+ return "%s%s %s %s %s %r" % (int(self.first), int(self.last), self.type,
+ self.track, self.channel, self.payload)
+
+ def __repr__(self):
+ return str(self)
+
+class Assembler(Framer):
+
+ def __init__(self, sock, max_payload = Frame.MAX_PAYLOAD):
+ Framer.__init__(self, sock)
+ self.max_payload = max_payload
+ self.fragments = {}
+
+ def read_segment(self):
+ while True:
+ frame = self.read_frame()
+
+ key = (frame.channel, frame.track)
+ seg = self.fragments.get(key)
+ if seg == None:
+ seg = Segment(frame.isFirstSegment(), frame.isLastSegment(),
+ frame.type, frame.track, frame.channel, "")
+ self.fragments[key] = seg
+
+ seg.payload += frame.payload
+
+ if frame.isLastFrame():
+ self.fragments.pop(key)
+ return seg
+
+ def write_segment(self, segment):
+ remaining = segment.payload
+
+ first = True
+ while remaining:
+ payload = remaining[:self.max_payload]
+ remaining = remaining[self.max_payload:]
+
+ flags = 0
+ if first:
+ flags |= FIRST_FRM
+ first = False
+ if not remaining:
+ flags |= LAST_FRM
+ if segment.first:
+ flags |= FIRST_SEG
+ if segment.last:
+ flags |= LAST_SEG
+
+ frame = Frame(flags, segment.type, segment.track, segment.channel,
+ payload)
+ self.write_frame(frame)
Propchange: incubator/qpid/trunk/qpid/python/qpid/assembler.py
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/qpid/trunk/qpid/python/qpid/codec010.py
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/python/qpid/codec010.py?rev=633610&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/python/qpid/codec010.py (added)
+++ incubator/qpid/trunk/qpid/python/qpid/codec010.py Tue Mar 4 12:03:09 2008
@@ -0,0 +1,186 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from packer import Packer
+
+class Codec(Packer):
+
+ def __init__(self, spec):
+ self.spec = spec
+
+ def write_bit(self, b):
+ if not b: raise ValueError(b)
+ def read_bit(self):
+ return True
+
+ def read_uint8(self):
+ return self.unpack("!B")
+ def write_uint8(self, n):
+ return self.pack("!B", n)
+
+ def read_int8(self):
+ return self.unpack("!b")
+ def write_int8(self, n):
+ self.pack("!b", n)
+
+ def read_char(self):
+ return self.unpack("!c")
+ def write_char(self, c):
+ self.pack("!c", c)
+
+ def read_boolean(self):
+ return self.read_uint8() != 0
+ def write_boolean(self, b):
+ if b: n = 1
+ else: n = 0
+ self.write_uint8(n)
+
+
+ def read_uint16(self):
+ return self.unpack("!H")
+ def write_uint16(self, n):
+ self.pack("!H", n)
+
+ def read_int16(self):
+ return self.unpack("!h")
+ def write_int16(self, n):
+ return self.unpack("!h", n)
+
+
+ def read_uint32(self):
+ return self.unpack("!L")
+ def write_uint32(self, n):
+ self.pack("!L", n)
+
+ def read_int32(self):
+ return self.unpack("!l")
+ def write_int32(self, n):
+ self.pack("!l", n)
+
+ def read_float(self):
+ return self.unpack("!f")
+ def write_float(self, f):
+ self.pack("!f", f)
+
+ def read_sequence_no(self):
+ return self.read_uint32()
+ def write_sequence_no(self, n):
+ self.write_uint32(n)
+
+
+ def read_uint64(self):
+ return self.unpack("!Q")
+ def write_uint64(self, n):
+ self.pack("!Q", n)
+
+ def read_int64(self):
+ return self.unpack("!q")
+ def write_int64(self, n):
+ self.pack("!q", n)
+
+ def read_double(self):
+ return self.unpack("!d")
+ def write_double(self, d):
+ self.pack("!d", d)
+
+
+ def read_vbin8(self):
+ return self.read(self.read_uint8())
+ def write_vbin8(self, b):
+ self.write_uint8(len(b))
+ self.write(b)
+
+ def read_str8(self):
+ return self.read_vbin8().decode("utf8")
+ def write_str8(self, s):
+ self.write_vbin8(s.encode("utf8"))
+
+
+ def read_vbin16(self):
+ return self.read(self.read_uint16())
+ def write_vbin16(self, b):
+ self.write_uint16(len(b))
+ self.write(b)
+
+ def read_vbin32(self):
+ return self.read(self.read_uint32())
+ def write_vbin32(self, b):
+ self.write_uint32(len(b))
+ self.write(b)
+
+ def write_map(self, m):
+ pass
+ def read_map(self):
+ pass
+
+ def write_array(self, a):
+ pass
+ def read_array(self):
+ pass
+
+ def read_struct32(self):
+ size = self.read_uint32()
+ code = self.read_uint16()
+ struct = self.spec.structs[code]
+ return struct.decode_fields(self)
+ def write_struct32(self, value):
+ sc = StringCodec(self.spec)
+ sc.write_uint16(value.type.code)
+ value.type.encode_fields(sc, value)
+ self.write_vbin32(sc.encoded)
+
+ def read_control(self):
+ cntrl = self.spec.controls[self.read_uint16()]
+ return cntrl.decode(self)
+ def write_control(self, type, ctrl):
+ self.write_uint16(type.code)
+ type.encode(self, ctrl)
+
+ def read_command(self):
+ cmd = self.spec.commands[self.read_uint16()]
+ return cmd.decode(self)
+ def write_command(self, type, cmd):
+ self.write_uint16(type.code)
+ type.encode(self, cmd)
+
+ def read_size(self, width):
+ if width > 0:
+ attr = "read_uint%d" % (width*8)
+ return getattr(self, attr)()
+
+ def write_size(self, width, n):
+ if width > 0:
+ attr = "write_uint%d" % (width*8)
+ getattr(self, attr)(n)
+
+
+
+class StringCodec(Codec):
+
+ def __init__(self, spec, encoded = ""):
+ Codec.__init__(self, spec)
+ self.encoded = encoded
+
+ def write(self, s):
+ self.encoded += s
+
+ def read(self, n):
+ result = self.encoded[:n]
+ self.encoded = self.encoded[n:]
+ return result
Propchange: incubator/qpid/trunk/qpid/python/qpid/codec010.py
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/qpid/trunk/qpid/python/qpid/connection010.py
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/python/qpid/connection010.py?rev=633610&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/python/qpid/connection010.py (added)
+++ incubator/qpid/trunk/qpid/python/qpid/connection010.py Tue Mar 4 12:03:09 2008
@@ -0,0 +1,172 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import datatypes, session
+from threading import Thread, Event, RLock
+from framer import Closed
+from assembler import Assembler, Segment
+from codec010 import StringCodec
+from session import Session
+from invoker import Invoker
+from spec010 import Control, Command
+import delegates
+
+class Timeout(Exception): pass
+
+class ChannelBusy(Exception): pass
+
+class ChannelsBusy(Exception): pass
+
+class SessionBusy(Exception): pass
+
+def client(*args):
+ return delegates.Client(*args)
+
+def server(*args):
+ return delegates.Server(*args)
+
+class Connection(Assembler):
+
+ def __init__(self, sock, spec, delegate=client):
+ Assembler.__init__(self, sock)
+ self.spec = spec
+ self.track = self.spec["track"]
+ self.delegate = delegate(self)
+ self.attached = {}
+ self.sessions = {}
+ self.lock = RLock()
+ self.thread = Thread(target=self.run)
+ self.thread.setDaemon(True)
+ self.opened = Event()
+ self.closed = Event()
+ self.channel_max = 65535
+
+ def attach(self, name, ch, delegate, force=False):
+ self.lock.acquire()
+ try:
+ ssn = self.attached.get(ch.id)
+ if ssn is not None:
+ if ssn.name != name:
+ raise ChannelBusy(ch, ssn)
+ else:
+ ssn = self.sessions.get(name)
+ if ssn is None:
+ ssn = Session(name, self.spec, delegate=delegate)
+ self.sessions[name] = ssn
+ elif ssn.channel is not None:
+ if force:
+ del self.attached[ssn.channel.id]
+ ssn.channel = None
+ else:
+ raise SessionBusy(ssn)
+ self.attached[ch.id] = ssn
+ ssn.channel = ch
+ ch.session = ssn
+ return ssn
+ finally:
+ self.lock.release()
+
+ def detach(self, name, ch):
+ self.lock.acquire()
+ try:
+ self.attached.pop(ch.id, None)
+ ssn = self.sessions.pop(name, None)
+ if ssn is not None:
+ ssn.channel = None
+ return ssn
+ finally:
+ self.lock.release()
+
+ def __channel(self):
+ # XXX: ch 0?
+ for i in xrange(self.channel_max):
+ if not self.attached.has_key(i):
+ return i
+ else:
+ raise ChannelsBusy()
+
+ def session(self, name, timeout=None, delegate=session.client):
+ self.lock.acquire()
+ try:
+ ssn = self.attach(name, Channel(self, self.__channel()), delegate)
+ ssn.channel.session_attach(name)
+ ssn.opened.wait(timeout)
+ if ssn.opened.isSet():
+ return ssn
+ else:
+ raise Timeout()
+ finally:
+ self.lock.release()
+
+ def start(self, timeout=None):
+ self.delegate.start()
+ self.thread.start()
+ self.opened.wait(timeout=timeout)
+ if not self.opened.isSet():
+ raise Timeout()
+
+ def run(self):
+ # XXX: we don't really have a good way to exit this loop without
+ # getting the other end to kill the socket
+ while True:
+ try:
+ seg = self.read_segment()
+ except Closed:
+ break
+ self.delegate.received(seg)
+
+ def close(self, timeout=None):
+ Channel(self, 0).connection_close()
+ self.closed.wait(timeout=timeout)
+ if not self.closed.isSet():
+ raise Timeout()
+ self.thread.join(timeout=timeout)
+
+ def __str__(self):
+ return "%s:%s" % self.sock.getsockname()
+
+ def __repr__(self):
+ return str(self)
+
+class Channel(Invoker):
+
+ def __init__(self, connection, id):
+ self.connection = connection
+ self.id = id
+ self.session = None
+
+ def resolve_method(self, name):
+ inst = self.connection.spec.instructions.get(name)
+ if inst is not None and isinstance(inst, Control):
+ return inst
+ else:
+ return None
+
+ def invoke(self, type, args, kwargs):
+ cntrl = type.new(args, kwargs)
+ sc = StringCodec(self.connection.spec)
+ sc.write_control(type, cntrl)
+ self.connection.write_segment(Segment(True, True, type.segment_type,
+ type.track, self.id, sc.encoded))
+
+ def __str__(self):
+ return "%s[%s]" % (self.connection, self.id)
+
+ def __repr__(self):
+ return str(self)
Propchange: incubator/qpid/trunk/qpid/python/qpid/connection010.py
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/qpid/trunk/qpid/python/qpid/datatypes.py
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/python/qpid/datatypes.py?rev=633610&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/python/qpid/datatypes.py (added)
+++ incubator/qpid/trunk/qpid/python/qpid/datatypes.py Tue Mar 4 12:03:09 2008
@@ -0,0 +1,112 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import threading
+
+class Struct:
+
+ def __init__(self, fields):
+ self.__dict__ = fields
+
+ def __repr__(self):
+ return "Struct(%s)" % ", ".join(["%s=%r" % (k, v)
+ for k, v in self.__dict__.items()])
+
+ def fields(self):
+ return self.__dict__
+
+class Message:
+
+ def __init__(self, body):
+ self.headers = None
+ self.body = body
+
+class Range:
+
+ def __init__(self, lower, upper):
+ self.lower = lower
+ self.upper = upper
+
+ def __contains__(self, n):
+ return self.lower <= n and n <= self.upper
+
+ def touches(self, r):
+ return (self.lower - 1 in r or
+ self.upper + 1 in r or
+ r.lower - 1 in self or
+ r.upper + 1 in self)
+
+ def span(self, r):
+ return Range(min(self.lower, r.lower), max(self.upper, r.upper))
+
+ def __str__(self):
+ return "Range(%s, %s)" % (self.lower, self.upper)
+
+ def __repr__(self):
+ return str(self)
+
+class RangeSet:
+
+ def __init__(self):
+ self.ranges = []
+
+ def __contains__(self, n):
+ for r in self.ranges:
+ if n in r:
+ return True
+ return False
+
+ def add_range(self, range):
+ idx = 0
+ while idx < len(self.ranges):
+ r = self.ranges[idx]
+ if range.touches(r):
+ del self.ranges[idx]
+ range = range.span(r)
+ elif range.upper < r.lower:
+ self.ranges.insert(idx, range)
+ return
+ else:
+ idx += 1
+ self.ranges.append(range)
+
+ def add(self, n):
+ self.add_range(Range(n, n))
+
+ def __str__(self):
+ return "RangeSet(%s)" % str(self.ranges)
+
+ def __repr__(self):
+ return str(self)
+
+class Future:
+ def __init__(self, initial=None):
+ self.value = initial
+ self._set = threading.Event()
+
+ def set(self, value):
+ self.value = value
+ self._set.set()
+
+ def get(self, timeout=None):
+ self._set.wait(timeout)
+ return self.value
+
+ def is_set(self):
+ return self._set.isSet()
Propchange: incubator/qpid/trunk/qpid/python/qpid/datatypes.py
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/qpid/trunk/qpid/python/qpid/delegates.py
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/python/qpid/delegates.py?rev=633610&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/python/qpid/delegates.py (added)
+++ incubator/qpid/trunk/qpid/python/qpid/delegates.py Tue Mar 4 12:03:09 2008
@@ -0,0 +1,111 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import connection010
+import session
+
+class Delegate:
+
+ def __init__(self, connection, delegate=session.client):
+ self.connection = connection
+ self.spec = connection.spec
+ self.delegate = delegate
+ self.control = self.spec["track.control"].value
+
+ def received(self, seg):
+ ssn = self.connection.attached.get(seg.channel)
+ if ssn is None:
+ ch = connection010.Channel(self.connection, seg.channel)
+ else:
+ ch = ssn.channel
+
+ if seg.track == self.control:
+ cntrl = seg.decode(self.spec)
+ attr = cntrl.type.qname.replace(".", "_")
+ getattr(self, attr)(ch, cntrl)
+ elif ssn is None:
+ ch.session_detached()
+ else:
+ ssn.received(seg)
+
+ def connection_close(self, ch, close):
+ ch.connection_close_ok()
+ self.connection.sock.close()
+
+ def connection_close_ok(self, ch, close_ok):
+ self.connection.closed.set()
+
+ def session_attach(self, ch, a):
+ try:
+ self.connection.attach(a.name, ch, self.delegate, a.force)
+ ch.session_attached(a.name)
+ except connection010.ChannelBusy:
+ ch.session_detached(a.name)
+ except connection010.SessionBusy:
+ ch.session_detached(a.name)
+
+ def session_attached(self, ch, a):
+ ch.session.opened.set()
+
+ def session_detach(self, ch, d):
+ self.connection.detach(d.name, ch)
+ ch.session_detached(d.name)
+
+ def session_detached(self, ch, d):
+ ssn = self.connection.detach(d.name, ch)
+ if ssn is not None:
+ ssn.closed.set()
+
+ def session_command_point(self, ch, cp):
+ ssn = ch.session
+ ssn.receiver.next_id = cp.command_id
+ ssn.receiver.next_offset = cp.command_offset
+
+class Server(Delegate):
+
+ def start(self):
+ self.connection.read_header()
+ self.connection.write_header(self.spec.major, self.spec.minor)
+ connection010.Channel(self.connection, 0).connection_start()
+
+ def connection_start_ok(self, ch, start_ok):
+ ch.connection_tune()
+
+ def connection_tune_ok(self, ch, tune_ok):
+ pass
+
+ def connection_open(self, ch, open):
+ self.connection.opened.set()
+ ch.connection_open_ok()
+
+class Client(Delegate):
+
+ def start(self):
+ self.connection.write_header(self.spec.major, self.spec.minor)
+ self.connection.read_header()
+
+ def connection_start(self, ch, start):
+ ch.connection_start_ok()
+
+ def connection_tune(self, ch, tune):
+ ch.connection_tune_ok()
+ ch.connection_open()
+
+ def connection_open_ok(self, ch, open_ok):
+ self.connection.opened.set()
Propchange: incubator/qpid/trunk/qpid/python/qpid/delegates.py
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/qpid/trunk/qpid/python/qpid/framer.py
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/python/qpid/framer.py?rev=633610&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/python/qpid/framer.py (added)
+++ incubator/qpid/trunk/qpid/python/qpid/framer.py Tue Mar 4 12:03:09 2008
@@ -0,0 +1,124 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import struct, socket
+from packer import Packer
+
+FIRST_SEG = 0x08
+LAST_SEG = 0x04
+FIRST_FRM = 0x02
+LAST_FRM = 0x01
+
+class Frame:
+
+ HEADER = "!2BHxBH4x"
+ MAX_PAYLOAD = 65535 - struct.calcsize(HEADER)
+
+ def __init__(self, flags, type, track, channel, payload):
+ if len(payload) > Frame.MAX_PAYLOAD:
+ raise ValueError("max payload size exceeded: %s" % len(payload))
+ self.flags = flags
+ self.type = type
+ self.track = track
+ self.channel = channel
+ self.payload = payload
+
+ def isFirstSegment(self):
+ return bool(FIRST_SEG & self.flags)
+
+ def isLastSegment(self):
+ return bool(LAST_SEG & self.flags)
+
+ def isFirstFrame(self):
+ return bool(FIRST_FRM & self.flags)
+
+ def isLastFrame(self):
+ return bool(LAST_FRM & self.flags)
+
+ def __str__(self):
+ return "%s%s%s%s %s %s %s %r" % (int(self.isFirstSegment()),
+ int(self.isLastSegment()),
+ int(self.isFirstFrame()),
+ int(self.isLastFrame()),
+ self.type,
+ self.track,
+ self.channel,
+ self.payload)
+
+class Closed(Exception): pass
+
+class Framer(Packer):
+
+ HEADER="!4s4B"
+
+ def __init__(self, sock):
+ self.sock = sock
+
+ def aborted(self):
+ return False
+
+ def write(self, buf):
+# print "OUT: %r" % buf
+ while buf:
+ try:
+ n = self.sock.send(buf)
+ except socket.timeout:
+ if self.aborted():
+ raise Closed()
+ else:
+ continue
+ buf = buf[n:]
+
+ def read(self, n):
+ data = ""
+ while len(data) < n:
+ try:
+ s = self.sock.recv(n - len(data))
+ except socket.timeout:
+ if self.aborted():
+ raise Closed()
+ else:
+ continue
+ except socket.error, e:
+ if data != "":
+ raise e
+ else:
+ raise Closed()
+ if len(s) == 0:
+ raise Closed()
+# print "IN: %r" % s
+ data += s
+ return data
+
+ def read_header(self):
+ return self.unpack(Framer.HEADER)
+
+ def write_header(self, major, minor):
+ self.pack(Framer.HEADER, "AMQP", 1, 1, major, minor)
+
+ def write_frame(self, frame):
+ size = len(frame.payload) + struct.calcsize(Frame.HEADER)
+ track = frame.track & 0x0F
+ self.pack(Frame.HEADER, frame.flags, frame.type, size, track, frame.channel)
+ self.write(frame.payload)
+
+ def read_frame(self):
+ flags, type, size, track, channel = self.unpack(Frame.HEADER)
+ payload = self.read(size - struct.calcsize(Frame.HEADER))
+ return Frame(flags, type, track, channel, payload)
Propchange: incubator/qpid/trunk/qpid/python/qpid/framer.py
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/qpid/trunk/qpid/python/qpid/invoker.py
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/python/qpid/invoker.py?rev=633610&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/python/qpid/invoker.py (added)
+++ incubator/qpid/trunk/qpid/python/qpid/invoker.py Tue Mar 4 12:03:09 2008
@@ -0,0 +1,32 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+class Invoker:
+
+ def resolve_method(self, name):
+ pass
+
+ def __getattr__(self, name):
+ resolved = self.resolve_method(name)
+ if resolved == None:
+ raise AttributeError("%s instance has no attribute '%s'" %
+ (self.__class__.__name__, name))
+ method = lambda *args, **kwargs: self.invoke(resolved, args, kwargs)
+ self.__dict__[name] = method
+ return method
Propchange: incubator/qpid/trunk/qpid/python/qpid/invoker.py
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/qpid/trunk/qpid/python/qpid/packer.py
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/python/qpid/packer.py?rev=633610&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/python/qpid/packer.py (added)
+++ incubator/qpid/trunk/qpid/python/qpid/packer.py Tue Mar 4 12:03:09 2008
@@ -0,0 +1,36 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import struct
+
+class Packer:
+
+ def read(self, n): abstract
+
+ def write(self, s): abstract
+
+ def unpack(self, fmt):
+ values = struct.unpack(fmt, self.read(struct.calcsize(fmt)))
+ if len(values) == 1:
+ return values[0]
+ else:
+ return values
+
+ def pack(self, fmt, *args):
+ self.write(struct.pack(fmt, *args))
Propchange: incubator/qpid/trunk/qpid/python/qpid/packer.py
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/qpid/trunk/qpid/python/qpid/session.py
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/python/qpid/session.py?rev=633610&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/python/qpid/session.py (added)
+++ incubator/qpid/trunk/qpid/python/qpid/session.py Tue Mar 4 12:03:09 2008
@@ -0,0 +1,208 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from threading import Event
+from invoker import Invoker
+from datatypes import RangeSet, Struct, Future
+from codec010 import StringCodec
+from assembler import Segment
+
+class SessionDetached(Exception): pass
+
+def client(*args):
+ return Client(*args)
+
+def server(*args):
+ return Server(*args)
+
+class Session(Invoker):
+
+ def __init__(self, name, spec, sync=True, timeout=10, delegate=client):
+ self.name = name
+ self.spec = spec
+ self.sync = sync
+ self.timeout = timeout
+ self.channel = None
+ self.opened = Event()
+ self.closed = Event()
+ self.receiver = Receiver(self)
+ self.sender = Sender(self)
+ self.delegate = delegate(self)
+ self.send_id = True
+ self.results = {}
+
+ def close(self, timeout=None):
+ self.channel.session_detach(self.name)
+ self.closed.wait(timeout=timeout)
+
+ def resolve_method(self, name):
+ cmd = self.spec.instructions.get(name)
+ if cmd is not None and cmd.track == self.spec["track.command"].value:
+ return cmd
+ else:
+ return None
+
+ def invoke(self, type, args, kwargs):
+ if self.channel == None:
+ raise SessionDetached()
+
+ if type.segments:
+ if len(args) == len(type.fields) + 1:
+ message = args[-1]
+ args = args[:-1]
+ else:
+ message = kwargs.pop("message", None)
+ else:
+ message = None
+
+ cmd = type.new(args, kwargs)
+ sc = StringCodec(self.spec)
+ sc.write_command(type, cmd)
+
+ seg = Segment(True, (message == None or
+ (message.headers == None and message.body == None)),
+ type.segment_type, type.track, self.channel.id, sc.encoded)
+
+ if type.result:
+ result = Future()
+ self.results[self.sender.next_id] = result
+
+ self.send(seg)
+
+ if message != None:
+ if message.headers != None:
+ sc = StringCodec(self.spec)
+ for st in message.headers:
+ sc.write_struct32(st.type, st)
+ seg = Segment(False, message.body == None, self.spec["segment_type.header"].value,
+ type.track, self.channel.id, sc.encoded)
+ self.send(seg)
+ if message.body != None:
+ seg = Segment(False, True, self.spec["segment_type.body"].value,
+ type.track, self.channel.id, message.body)
+ self.send(seg)
+
+ if type.result:
+ if self.sync:
+ return result.get(self.timeout)
+ else:
+ return result
+
+ def received(self, seg):
+ self.receiver.received(seg)
+ if seg.type == self.spec["segment_type.command"].value:
+ cmd = seg.decode(self.spec)
+ attr = cmd.type.qname.replace(".", "_")
+ result = getattr(self.delegate, attr)(cmd)
+ if cmd.type.result:
+ self.execution_result(seg.id, result)
+ elif seg.type == self.spec["segment_type.header"].value:
+ self.delegate.header(seg.decode(self.spec))
+ elif seg.type == self.spec["segment_type.body"].value:
+ self.delegate.body(seg.decode(self.spec))
+ else:
+ raise ValueError("unknown segment type: %s" % seg.type)
+ self.receiver.completed(seg)
+
+ def send(self, seg):
+ self.sender.send(seg)
+
+ def __str__(self):
+ return '<Session: %s, %s>' % (self.name, self.channel)
+
+ def __repr__(self):
+ return str(self)
+
+class Receiver:
+
+ def __init__(self, session):
+ self.session = session
+ self.next_id = None
+ self.next_offset = None
+ self._completed = RangeSet()
+
+ def received(self, seg):
+ if self.next_id == None or self.next_offset == None:
+ raise Exception("todo")
+ seg.id = self.next_id
+ seg.offset = self.next_offset
+ if seg.last:
+ self.next_id += 1
+ self.next_offset = 0
+ else:
+ self.next_offset += len(seg.payload)
+
+ def completed(self, seg):
+ if seg.id == None:
+ raise ValueError("cannot complete unidentified segment")
+ if seg.last:
+ self._completed.add(seg.id)
+
+class Sender:
+
+ def __init__(self, session):
+ self.session = session
+ self.next_id = 0
+ self.next_offset = 0
+ self.segments = []
+
+ def send(self, seg):
+ seg.id = self.next_id
+ seg.offset = self.next_offset
+ if seg.last:
+ self.next_id += 1
+ self.next_offset = 0
+ else:
+ self.next_offset += len(seg.payload)
+ self.segments.append(seg)
+ if self.session.send_id:
+ self.session.send_id = False
+ self.session.channel.session_command_point(seg.id, seg.offset)
+ self.session.channel.connection.write_segment(seg)
+
+ def completed(self, commands):
+ idx = 0
+ while idx < len(self.segments):
+ seg = self.segments[idx]
+ if seg.id in commands:
+ del self.segments[idx]
+ else:
+ idx += 1
+
+from queue import Queue, Closed, Empty
+
+class Delegate:
+
+ def __init__(self, session):
+ self.session = session
+
+ def execution_result(self, er):
+ future = self.session.results[er.command_id]
+ future.set(er.value)
+
+class Client(Delegate):
+
+ def message_transfer(self, cmd):
+ print "TRANSFER:", cmd
+
+ def header(self, hdr):
+ print "HEADER:", hdr
+
+ def body(self, seg):
+ print "BODY:", seg
Propchange: incubator/qpid/trunk/qpid/python/qpid/session.py
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/qpid/trunk/qpid/python/qpid/spec010.py
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/python/qpid/spec010.py?rev=633610&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/python/qpid/spec010.py (added)
+++ incubator/qpid/trunk/qpid/python/qpid/spec010.py Tue Mar 4 12:03:09 2008
@@ -0,0 +1,617 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import datatypes
+from codec010 import StringCodec
+
+class Node:
+
+ def __init__(self, children):
+ self.children = children
+ self.named = {}
+ self.docs = []
+ self.rules = []
+
+ def register(self):
+ for ch in self.children:
+ ch.register(self)
+
+ def resolve(self):
+ for ch in self.children:
+ ch.resolve()
+
+ def __getitem__(self, name):
+ path = name.split(".", 1)
+ nd = self.named
+ for step in path:
+ nd = nd[step]
+ return nd
+
+ def __iter__(self):
+ return iter(self.children)
+
+class Anonymous:
+
+ def __init__(self, children):
+ self.children = children
+
+ def register(self, node):
+ for ch in self.children:
+ ch.register(node)
+
+ def resolve(self):
+ for ch in self.children:
+ ch.resolve()
+
+class Named:
+
+ def __init__(self, name):
+ self.name = name
+ self.qname = None
+
+ def register(self, node):
+ self.spec = node.spec
+ self.klass = node.klass
+ node.named[self.name] = self
+ if node.qname:
+ self.qname = "%s.%s" % (node.qname, self.name)
+ else:
+ self.qname = self.name
+
+ def __str__(self):
+ return self.qname
+
+ def __repr__(self):
+ return str(self)
+
+class Lookup:
+
+ def lookup(self, name):
+ value = None
+ if self.klass:
+ try:
+ value = self.klass[name]
+ except KeyError:
+ pass
+ if not value:
+ value = self.spec[name]
+ return value
+
+class Coded:
+
+ def __init__(self, code):
+ self.code = code
+
+class Constant(Named, Node):
+
+ def __init__(self, name, value, children):
+ Named.__init__(self, name)
+ Node.__init__(self, children)
+ self.value = value
+
+ def register(self, node):
+ Named.register(self, node)
+ node.constants.append(self)
+ Node.register(self)
+
+class Type(Named, Node):
+
+ def __init__(self, name, children):
+ Named.__init__(self, name)
+ Node.__init__(self, children)
+
+ def is_present(self, value):
+ return value != None
+
+ def register(self, node):
+ Named.register(self, node)
+ node.types.append(self)
+ Node.register(self)
+
+class Primitive(Coded, Type):
+
+ def __init__(self, name, code, fixed, variable, children):
+ Coded.__init__(self, code)
+ Type.__init__(self, name, children)
+ self.fixed = fixed
+ self.variable = variable
+
+ def is_present(self, value):
+ if self.fixed == 0:
+ return value
+ else:
+ return Type.is_present(self, value)
+
+ def encode(self, codec, value):
+ getattr(codec, "write_%s" % self.name)(value)
+
+ def decode(self, codec):
+ return getattr(codec, "read_%s" % self.name)()
+
+class Domain(Type, Lookup):
+
+ def __init__(self, name, type, children):
+ Type.__init__(self, name, children)
+ self.type = type
+ self.choices = {}
+
+ def resolve(self):
+ self.type = self.lookup(self.type)
+ Node.resolve(self)
+
+ def encode(self, codec, value):
+ self.type.encode(codec, value)
+
+ def decode(self, codec):
+ return self.type.decode(codec)
+
+class Choice(Named, Node):
+
+ def __init__(self, name, value, children):
+ Named.__init__(self, name)
+ Node.__init__(self, children)
+ self.value = value
+
+ def register(self, node):
+ Named.register(self, node)
+ node.choices[self.value] = self
+ Node.register(self)
+
+class Composite(Type, Coded):
+
+ def __init__(self, name, code, size, pack, children):
+ Coded.__init__(self, code)
+ Type.__init__(self, name, children)
+ self.fields = []
+ self.size = size
+ self.pack = pack
+
+ def new(self, args, kwargs):
+ if len(args) > len(self.fields):
+ raise TypeError("%s takes at most %s arguments (%s given)" %
+ (self.name, len(self.fields), len(self.args)))
+
+ result = {"type": self}
+
+ for a, f, in zip(args, self.fields):
+ result[f.name] = a
+
+ for k, v in kwargs.items():
+ f = self.named.get(k, None)
+ if f == None:
+ raise TypeError("%s got an unexpected keyword argument '%s'" %
+ (self.name, k))
+ result[f.name] = v
+
+ return datatypes.Struct(result)
+
+ def decode(self, codec):
+ codec.read_size(self.size)
+ return self.decode_fields(codec)
+
+ def decode_fields(self, codec):
+ flags = 0
+ for i in range(self.pack):
+ flags |= (codec.read_uint8() << 8*i)
+
+ result = {"type": self}
+
+ for i in range(len(self.fields)):
+ f = self.fields[i]
+ if flags & (0x1 << i):
+ result[f.name] = f.type.decode(codec)
+ else:
+ result[f.name] = None
+ return datatypes.Struct(result)
+
+ def encode(self, codec, value):
+ sc = StringCodec(self.spec)
+ self.encode_fields(sc, value)
+ codec.write_size(self.size, len(sc.encoded))
+ codec.write(sc.encoded)
+
+ def encode_fields(self, codec, value):
+ values = value.__dict__
+ flags = 0
+ for i in range(len(self.fields)):
+ f = self.fields[i]
+ if f.type.is_present(values.get(f.name, None)):
+ flags |= (0x1 << i)
+ for i in range(self.pack):
+ codec.write_uint8((flags >> 8*i) & 0xFF)
+ for i in range(len(self.fields)):
+ f = self.fields[i]
+ if flags & (0x1 << i):
+ f.type.encode(codec, values[f.name])
+
+class Field(Named, Node, Lookup):
+
+ def __init__(self, name, type, children):
+ Named.__init__(self, name)
+ Node.__init__(self, children)
+ self.type = type
+ self.exceptions = []
+
+ def register(self, node):
+ Named.register(self, node)
+ node.fields.append(self)
+ Node.register(self)
+
+ def resolve(self):
+ self.type = self.lookup(self.type)
+ Node.resolve(self)
+
+ def __str__(self):
+ return "%s: %s" % (self.qname, self.type.qname)
+
+class Struct(Composite):
+
+ def register(self, node):
+ Composite.register(self, node)
+ self.spec.structs[self.code] = self
+
+ def __str__(self):
+ fields = ",\n ".join(["%s: %s" % (f.name, f.type.qname)
+ for f in self.fields])
+ return "%s {\n %s\n}" % (self.qname, fields)
+
+class Segment(Node):
+
+ def register(self, node):
+ self.spec = node.spec
+ self.klass = node.klass
+ node.segments.append(self)
+ Node.register(self)
+
+class Instruction(Composite, Segment):
+
+ def __init__(self, name, code, children):
+ Composite.__init__(self, name, code, 0, 2, children)
+ self.segment_type = None
+ self.track = None
+ self.handlers = []
+
+ def __str__(self):
+ return "%s(%s)" % (self.qname, ", ".join(["%s: %s" % (f.name, f.type.qname)
+ for f in self.fields]))
+
+ def register(self, node):
+ Composite.register(self, node)
+ self.spec.instructions[self.qname.replace(".", "_")] = self
+
+class Control(Instruction):
+
+ def __init__(self, name, code, children):
+ Instruction.__init__(self, name, code, children)
+ self.response = None
+
+ def register(self, node):
+ Instruction.register(self, node)
+ node.controls.append(self)
+ self.spec.controls[self.code] = self
+ self.segment_type = self.spec["segment_type.control"].value
+ self.track = self.spec["track.control"].value
+
+class Command(Instruction):
+
+ def __init__(self, name, code, children):
+ Instruction.__init__(self, name, code, children)
+ self.result = None
+ self.exceptions = []
+ self.segments = []
+
+ def register(self, node):
+ Instruction.register(self, node)
+ node.commands.append(self)
+ self.header = self.spec["session.header"]
+ self.spec.commands[self.code] = self
+ self.segment_type = self.spec["segment_type.command"].value
+ self.track = self.spec["track.command"].value
+
+ def decode(self, codec):
+ hdr = self.header.decode(codec)
+ args = Instruction.decode(self, codec)
+ result = {}
+ result.update(hdr.fields())
+ result.update(args.fields())
+ return datatypes.Struct(result)
+
+ def encode(self, codec, cmd):
+ self.header.encode(codec, cmd)
+ Instruction.encode(self, codec, cmd)
+
+class Header(Segment):
+
+ def __init__(self, children):
+ self.entries = []
+ Segment.__init__(self, children)
+
+class Entry(Lookup):
+
+ def __init__(self, type):
+ self.type = type
+
+ def register(self, node):
+ self.spec = node.spec
+ self.klass = node.klass
+ node.entries.append(self)
+
+ def resolve(self):
+ self.type = self.lookup(self.type)
+
+class Body(Segment):
+
+ def resolve(self): pass
+
+class Class(Named, Coded, Node):
+
+ def __init__(self, name, code, children):
+ Named.__init__(self, name)
+ Coded.__init__(self, code)
+ Node.__init__(self, children)
+ self.types = []
+ self.controls = []
+ self.commands = []
+
+ def register(self, node):
+ Named.register(self, node)
+ self.klass = self
+ node.classes.append(self)
+ Node.register(self)
+
+class Doc:
+
+ def __init__(self, type, title, text):
+ self.type = type
+ self.title = title
+ self.text = text
+
+ def register(self, node):
+ node.docs.append(self)
+
+ def resolve(self): pass
+
+class Role(Named, Node):
+
+ def __init__(self, name, children):
+ Named.__init__(self, name)
+ Node.__init__(self, children)
+
+ def register(self, node):
+ Named.register(self, node)
+ Node.register(self)
+
+class Rule(Named, Node):
+
+ def __init__(self, name, children):
+ Named.__init__(self, name)
+ Node.__init__(self, children)
+
+ def register(self, node):
+ Named.register(self, node)
+ node.rules.append(self)
+ Node.register(self)
+
+class Exception(Named, Node):
+
+ def __init__(self, name, error_code, children):
+ Named.__init__(self, name)
+ Node.__init__(self, children)
+ self.error_code = error_code
+
+ def register(self, node):
+ Named.register(self, node)
+ node.exceptions.append(self)
+ Node.register(self)
+
+class Spec(Node):
+
+ def __init__(self, major, minor, port, children):
+ Node.__init__(self, children)
+ self.major = major
+ self.minor = minor
+ self.port = port
+ self.constants = []
+ self.classes = []
+ self.types = []
+ self.qname = None
+ self.spec = self
+ self.klass = None
+ self.instructions = {}
+ self.controls = {}
+ self.commands = {}
+ self.structs = {}
+
+class Implement:
+
+ def __init__(self, handle):
+ self.handle = handle
+
+ def register(self, node):
+ node.handlers.append(self.handle)
+
+ def resolve(self): pass
+
+class Response(Node):
+
+ def __init__(self, name, children):
+ Node.__init__(self, children)
+ self.name = name
+
+ def register(self, node):
+ Node.register(self)
+
+class Result(Node, Lookup):
+
+ def __init__(self, type, children):
+ self.type = type
+ Node.__init__(self, children)
+
+ def register(self, node):
+ node.result = self
+ self.qname = node.qname
+ self.klass = node.klass
+ self.spec = node.spec
+ Node.register(self)
+
+ def resolve(self):
+ self.type = self.lookup(self.type)
+ Node.resolve(self)
+
+import mllib
+
+def num(s):
+ if s: return int(s, 0)
+
+REPLACE = {" ": "_", "-": "_"}
+KEYWORDS = {"global": "global_",
+ "return": "return_"}
+
+def id(name):
+ name = str(name)
+ for key, val in REPLACE.items():
+ name = name.replace(key, val)
+ try:
+ name = KEYWORDS[name]
+ except KeyError:
+ pass
+ return name
+
+class Loader:
+
+ def __init__(self):
+ self.class_code = 0
+
+ def code(self, nd):
+ c = num(nd["@code"])
+ if c is None:
+ return None
+ else:
+ return c | (self.class_code << 8)
+
+ def list(self, q):
+ result = []
+ for nd in q:
+ result.append(nd.dispatch(self))
+ return result
+
+ def children(self, n):
+ return self.list(n.query["#tag"])
+
+ def data(self, d):
+ return d.data
+
+ def do_amqp(self, a):
+ return Spec(num(a["@major"]), num(a["@minor"]), num(a["@port"]),
+ self.children(a))
+
+ def do_type(self, t):
+ return Primitive(id(t["@name"]), self.code(t), num(t["@fixed-width"]),
+ num(t["@variable-width"]), self.children(t))
+
+ def do_constant(self, c):
+ return Constant(id(c["@name"]), num(c["@value"]), self.children(c))
+
+ def do_domain(self, d):
+ return Domain(id(d["@name"]), id(d["@type"]), self.children(d))
+
+ def do_enum(self, e):
+ return Anonymous(self.children(e))
+
+ def do_choice(self, c):
+ return Choice(id(c["@name"]), num(c["@value"]), self.children(c))
+
+ def do_class(self, c):
+ code = num(c["@code"])
+ self.class_code = code
+ children = self.children(c)
+ children += self.list(c.query["command/result/struct"])
+ self.class_code = 0
+ return Class(id(c["@name"]), code, children)
+
+ def do_doc(self, doc):
+ text = reduce(lambda x, y: x + y, self.list(doc.children))
+ return Doc(doc["@type"], doc["@title"], text)
+
+ def do_xref(self, x):
+ return x["@ref"]
+
+ def do_role(self, r):
+ return Role(id(r["@name"]), self.children(r))
+
+ def do_control(self, c):
+ return Control(id(c["@name"]), self.code(c), self.children(c))
+
+ def do_rule(self, r):
+ return Rule(id(r["@name"]), self.children(r))
+
+ def do_implement(self, i):
+ return Implement(id(i["@handle"]))
+
+ def do_response(self, r):
+ return Response(id(r["@name"]), self.children(r))
+
+ def do_field(self, f):
+ return Field(id(f["@name"]), id(f["@type"]), self.children(f))
+
+ def do_struct(self, s):
+ return Struct(id(s["@name"]), self.code(s), num(s["@size"]),
+ num(s["@pack"]), self.children(s))
+
+ def do_command(self, c):
+ return Command(id(c["@name"]), self.code(c), self.children(c))
+
+ def do_segments(self, s):
+ return Anonymous(self.children(s))
+
+ def do_header(self, h):
+ return Header(self.children(h))
+
+ def do_entry(self, e):
+ return Entry(id(e["@type"]))
+
+ def do_body(self, b):
+ return Body(self.children(b))
+
+ def do_result(self, r):
+ type = r["@type"]
+ if not type:
+ type = r["struct/@name"]
+ return Result(id(type), self.list(r.query["#tag", lambda x: x.name != "struct"]))
+
+ def do_exception(self, e):
+ return Exception(id(e["@name"]), id(e["@error-code"]), self.children(e))
+
+import os, cPickle
+
+def load(xml):
+ fname = xml + ".pcl"
+ if os.path.exists(fname):
+ file = open(fname, "r")
+ s = cPickle.load(file)
+ file.close()
+ else:
+ doc = mllib.xml_parse(xml)
+ s = doc["amqp"].dispatch(Loader())
+ s.register()
+ s.resolve()
+ file = open(fname, "w")
+ cPickle.dump(s, file)
+ file.close()
+ return s
Propchange: incubator/qpid/trunk/qpid/python/qpid/spec010.py
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/qpid/trunk/qpid/python/qpid/util.py
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/python/qpid/util.py?rev=633610&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/python/qpid/util.py (added)
+++ incubator/qpid/trunk/qpid/python/qpid/util.py Tue Mar 4 12:03:09 2008
@@ -0,0 +1,39 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import socket
+
+def connect(host, port):
+ sock = socket.socket()
+ sock.connect((host, port))
+ sock.setblocking(1)
+ # XXX: we could use this on read, but we'd have to put write in a
+ # loop as well
+ # sock.settimeout(1)
+ return sock
+
+def listen(host, port, predicate = lambda: True, bound = lambda: None):
+ sock = socket.socket()
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sock.bind((host, port))
+ bound()
+ sock.listen(5)
+ while predicate():
+ s, a = sock.accept()
+ yield s
Propchange: incubator/qpid/trunk/qpid/python/qpid/util.py
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/qpid/trunk/qpid/python/server010
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/python/server010?rev=633610&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/python/server010 (added)
+++ incubator/qpid/trunk/qpid/python/server010 Tue Mar 4 12:03:09 2008
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+
+from qpid import delegates
+from qpid.connection010 import Connection
+from qpid.util import connect, listen
+from qpid.spec010 import load
+from qpid.session import Client
+
+spec = load("../specs/amqp.0-10.xml")
+
+class Server:
+
+ def connection(self, connection):
+ return delegates.Server(connection, self.session)
+
+ def session(self, session):
+ return SessionDelegate(session)
+
+class SessionDelegate(Client):
+
+ def __init__(self, session):
+ self.session = session
+
+ def queue_declare(self, qd):
+ print "Queue %s declared..." % qd.queue
+
+ def queue_query(self, qq):
+ return qq.type.result.type.new((qq.queue,), {})
+
+server = Server()
+
+for s in listen("0.0.0.0", spec.port):
+ conn = Connection(s, spec, server.connection)
+ conn.start(5)
Propchange: incubator/qpid/trunk/qpid/python/server010
------------------------------------------------------------------------------
svn:executable = *
Modified: incubator/qpid/trunk/qpid/python/tests/__init__.py
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/python/tests/__init__.py?rev=633610&r1=633609&r2=633610&view=diff
==============================================================================
--- incubator/qpid/trunk/qpid/python/tests/__init__.py (original)
+++ incubator/qpid/trunk/qpid/python/tests/__init__.py Tue Mar 4 12:03:09 2008
@@ -22,3 +22,8 @@
from codec import *
from queue import *
from spec import *
+from framer import *
+from assembler import *
+from datatypes import *
+from connection010 import *
+from spec010 import *
Added: incubator/qpid/trunk/qpid/python/tests/assembler.py
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/python/tests/assembler.py?rev=633610&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/python/tests/assembler.py (added)
+++ incubator/qpid/trunk/qpid/python/tests/assembler.py Tue Mar 4 12:03:09 2008
@@ -0,0 +1,77 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from threading import *
+from unittest import TestCase
+from qpid.util import connect, listen
+from qpid.assembler import *
+
+PORT = 1234
+
+class AssemblerTest(TestCase):
+
+ def setUp(self):
+ started = Event()
+ self.running = True
+
+ def run():
+ running = True
+ for s in listen("0.0.0.0", PORT, lambda: self.running, lambda: started.set()):
+ asm = Assembler(s)
+ try:
+ asm.write_header(*asm.read_header()[-2:])
+ while True:
+ seg = asm.read_segment()
+ asm.write_segment(seg)
+ except Closed:
+ pass
+
+ self.server = Thread(target=run)
+ self.server.setDaemon(True)
+ self.server.start()
+
+ started.wait(3)
+
+ def tearDown(self):
+ self.running = False
+ self.server.join()
+
+ def test(self):
+ asm = Assembler(connect("0.0.0.0", PORT), max_payload = 1)
+ asm.write_header(0, 10)
+ asm.write_segment(Segment(True, False, 1, 2, 3, "TEST"))
+ asm.write_segment(Segment(False, True, 1, 2, 3, "ING"))
+
+ assert asm.read_header() == ("AMQP", 1, 1, 0, 10)
+
+ seg = asm.read_segment()
+ assert seg.first == True
+ assert seg.last == False
+ assert seg.type == 1
+ assert seg.track == 2
+ assert seg.channel == 3
+ assert seg.payload == "TEST"
+
+ seg = asm.read_segment()
+ assert seg.first == False
+ assert seg.last == True
+ assert seg.type == 1
+ assert seg.track == 2
+ assert seg.channel == 3
+ assert seg.payload == "ING"
Propchange: incubator/qpid/trunk/qpid/python/tests/assembler.py
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/qpid/trunk/qpid/python/tests/connection010.py
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/python/tests/connection010.py?rev=633610&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/python/tests/connection010.py (added)
+++ incubator/qpid/trunk/qpid/python/tests/connection010.py Tue Mar 4 12:03:09 2008
@@ -0,0 +1,137 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from threading import *
+from unittest import TestCase
+from qpid.util import connect, listen
+from qpid.connection010 import *
+from qpid.datatypes import Message
+from qpid.testlib import testrunner
+from qpid.delegates import Server
+from qpid.queue import Queue
+from qpid.spec010 import load
+from qpid.session import Delegate
+
+PORT = 1234
+
+class TestServer:
+
+ def __init__(self, queue):
+ self.queue = queue
+
+ def connection(self, connection):
+ return Server(connection, delegate=self.session)
+
+ def session(self, session):
+ return TestSession(session, self.queue)
+
+class TestSession(Delegate):
+
+ def __init__(self, session, queue):
+ self.session = session
+ self.queue = queue
+
+ def queue_query(self, qq):
+ return qq.type.result.type.new((qq.queue,), {})
+
+ def message_transfer(self, cmd):
+ self.queue.put(cmd)
+
+ def body(self, body):
+ self.queue.put(body)
+
+class ConnectionTest(TestCase):
+
+ def setUp(self):
+ self.spec = load(testrunner.get_spec_file("amqp.0-10.xml"))
+ self.queue = Queue()
+ self.running = True
+ started = Event()
+
+ def run():
+ ts = TestServer(self.queue)
+ for s in listen("0.0.0.0", PORT, lambda: self.running, lambda: started.set()):
+ conn = Connection(s, self.spec, ts.connection)
+ try:
+ conn.start(5)
+ except Closed:
+ pass
+
+ self.server = Thread(target=run)
+ self.server.setDaemon(True)
+ self.server.start()
+
+ started.wait(3)
+
+ def tearDown(self):
+ self.running = False
+ connect("0.0.0.0", PORT).close()
+ self.server.join(3)
+
+ def test(self):
+ c = Connection(connect("0.0.0.0", PORT), self.spec)
+ c.start(10)
+
+ ssn1 = c.session("test1")
+ ssn2 = c.session("test2")
+
+ assert ssn1 == c.sessions["test1"]
+ assert ssn2 == c.sessions["test2"]
+ assert ssn1.channel != None
+ assert ssn2.channel != None
+ assert ssn1 in c.attached.values()
+ assert ssn2 in c.attached.values()
+
+ ssn1.close(5)
+
+ assert ssn1.channel == None
+ assert ssn1 not in c.attached.values()
+ assert ssn2 in c.sessions.values()
+
+ ssn2.close(5)
+
+ assert ssn2.channel == None
+ assert ssn2 not in c.attached.values()
+ assert ssn2 not in c.sessions.values()
+
+ ssn = c.session("session")
+
+ assert ssn.channel != None
+ assert ssn in c.sessions.values()
+
+ destinations = ("one", "two", "three")
+
+ for d in destinations:
+ ssn.message_transfer(d)
+
+ for d in destinations:
+ cmd = self.queue.get(10)
+ assert cmd.destination == d
+
+ msg = Message("this is a test")
+ ssn.message_transfer("four", message=msg)
+ cmd = self.queue.get(10)
+ assert cmd.destination == "four"
+ body = self.queue.get(10)
+ assert body.payload == msg.body
+ assert body.last
+
+ qq = ssn.queue_query("asdf")
+ assert qq.queue == "asdf"
+ c.close(5)
Propchange: incubator/qpid/trunk/qpid/python/tests/connection010.py
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/qpid/trunk/qpid/python/tests/datatypes.py
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/python/tests/datatypes.py?rev=633610&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/python/tests/datatypes.py (added)
+++ incubator/qpid/trunk/qpid/python/tests/datatypes.py Tue Mar 4 12:03:09 2008
@@ -0,0 +1,91 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from unittest import TestCase
+from qpid.datatypes import *
+
+class RangeSetTest(TestCase):
+
+ def check(self, ranges):
+ posts = []
+ for range in ranges:
+ posts.append(range.lower)
+ posts.append(range.upper)
+
+ sorted = posts[:]
+ sorted.sort()
+
+ assert posts == sorted
+
+ idx = 1
+ while idx + 1 < len(posts):
+ assert posts[idx] + 1 != posts[idx+1]
+ idx += 2
+
+ def test(self):
+ rs = RangeSet()
+
+ self.check(rs.ranges)
+
+ rs.add(1)
+
+ assert 1 in rs
+ assert 2 not in rs
+ assert 0 not in rs
+ self.check(rs.ranges)
+
+ rs.add(2)
+
+ assert 0 not in rs
+ assert 1 in rs
+ assert 2 in rs
+ assert 3 not in rs
+ self.check(rs.ranges)
+
+ rs.add(0)
+
+ assert -1 not in rs
+ assert 0 in rs
+ assert 1 in rs
+ assert 2 in rs
+ assert 3 not in rs
+ self.check(rs.ranges)
+
+ rs.add(37)
+
+ assert -1 not in rs
+ assert 0 in rs
+ assert 1 in rs
+ assert 2 in rs
+ assert 3 not in rs
+ assert 36 not in rs
+ assert 37 in rs
+ assert 38 not in rs
+ self.check(rs.ranges)
+
+ rs.add(-1)
+ self.check(rs.ranges)
+
+ rs.add(-3)
+ self.check(rs.ranges)
+
+ rs.add_range(Range(1, 20))
+ assert 21 not in rs
+ assert 20 in rs
+ self.check(rs.ranges)
Propchange: incubator/qpid/trunk/qpid/python/tests/datatypes.py
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/qpid/trunk/qpid/python/tests/framer.py
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/python/tests/framer.py?rev=633610&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/python/tests/framer.py (added)
+++ incubator/qpid/trunk/qpid/python/tests/framer.py Tue Mar 4 12:03:09 2008
@@ -0,0 +1,92 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from threading import *
+from unittest import TestCase
+from qpid.util import connect, listen
+from qpid.framer import *
+
+PORT = 1234
+
+class FramerTest(TestCase):
+
+ def setUp(self):
+ self.running = True
+ started = Event()
+ def run():
+ for s in listen("0.0.0.0", PORT, lambda: self.running, lambda: started.set()):
+ conn = Framer(s)
+ try:
+ conn.write_header(*conn.read_header()[-2:])
+ while True:
+ frame = conn.read_frame()
+ conn.write_frame(frame)
+ except Closed:
+ pass
+
+ self.server = Thread(target=run)
+ self.server.setDaemon(True)
+ self.server.start()
+
+ started.wait(3)
+
+ def tearDown(self):
+ self.running = False
+ self.server.join(3)
+
+ def test(self):
+ c = Framer(connect("0.0.0.0", PORT))
+
+ c.write_header(0, 10)
+ assert c.read_header() == ("AMQP", 1, 1, 0, 10)
+
+ c.write_frame(Frame(FIRST_FRM, 1, 2, 3, "THIS"))
+ c.write_frame(Frame(0, 1, 2, 3, "IS"))
+ c.write_frame(Frame(0, 1, 2, 3, "A"))
+ c.write_frame(Frame(LAST_FRM, 1, 2, 3, "TEST"))
+
+ f = c.read_frame()
+ assert f.flags & FIRST_FRM
+ assert not (f.flags & LAST_FRM)
+ assert f.type == 1
+ assert f.track == 2
+ assert f.channel == 3
+ assert f.payload == "THIS"
+
+ f = c.read_frame()
+ assert f.flags == 0
+ assert f.type == 1
+ assert f.track == 2
+ assert f.channel == 3
+ assert f.payload == "IS"
+
+ f = c.read_frame()
+ assert f.flags == 0
+ assert f.type == 1
+ assert f.track == 2
+ assert f.channel == 3
+ assert f.payload == "A"
+
+ f = c.read_frame()
+ assert f.flags & LAST_FRM
+ assert not (f.flags & FIRST_FRM)
+ assert f.type == 1
+ assert f.track == 2
+ assert f.channel == 3
+ assert f.payload == "TEST"
Propchange: incubator/qpid/trunk/qpid/python/tests/framer.py
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/qpid/trunk/qpid/python/tests/spec010.py
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/python/tests/spec010.py?rev=633610&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/python/tests/spec010.py (added)
+++ incubator/qpid/trunk/qpid/python/tests/spec010.py Tue Mar 4 12:03:09 2008
@@ -0,0 +1,60 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from unittest import TestCase
+from qpid.spec010 import load
+from qpid.codec010 import Codec, StringCodec
+from qpid.testlib import testrunner
+from qpid.datatypes import Struct
+
+class SpecTest(TestCase):
+
+ def setUp(self):
+ self.spec = load(testrunner.get_spec_file("amqp.0-10.xml"))
+
+ def testSessionHeader(self):
+ hdr = self.spec["session.header"]
+ sc = StringCodec(self.spec)
+ hdr.encode(sc, Struct({"sync": True}))
+ assert sc.encoded == "\x01\x01"
+
+ sc = StringCodec(self.spec)
+ hdr.encode(sc, Struct({"sync": False}))
+ assert sc.encoded == "\x01\x00"
+
+ def encdec(self, type, value):
+ sc = StringCodec(self.spec)
+ type.encode(sc, value)
+ decoded = type.decode(sc)
+ return decoded
+
+ def testMessageProperties(self):
+ props = Struct({"content_length": 0xDEADBEEF,
+ "reply_to":
+ Struct({"exchange": "the exchange name", "routing_key": "the routing key"})})
+ dec = self.encdec(self.spec["message.message_properties"], props)
+ assert props.content_length == dec.content_length
+ assert props.reply_to.exchange == dec.reply_to.exchange
+ assert props.reply_to.routing_key == dec.reply_to.routing_key
+
+ def testMessageSubscribe(self):
+ cmd = Struct({"exclusive": True, "destination": "this is a test"})
+ dec = self.encdec(self.spec["message.subscribe"], cmd)
+ assert cmd.exclusive == dec.exclusive
+ assert cmd.destination == dec.destination
Propchange: incubator/qpid/trunk/qpid/python/tests/spec010.py
------------------------------------------------------------------------------
svn:eol-style = native