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 2009/11/13 16:33:05 UTC
svn commit: r835879 - in /qpid/trunk/qpid: cpp/src/tests/cluster_tests.py
java/testkit/testkit.py python/qpid/address.py python/qpid/brokertest.py
python/qpid/tests/address.py python/qpid/tests/messaging.py
Author: rhs
Date: Fri Nov 13 15:33:04 2009
New Revision: 835879
URL: http://svn.apache.org/viewvc?rev=835879&view=rev
Log:
changed address syntax to permit more complex subjects, added escaping, improved error reporting
Modified:
qpid/trunk/qpid/cpp/src/tests/cluster_tests.py
qpid/trunk/qpid/java/testkit/testkit.py
qpid/trunk/qpid/python/qpid/address.py
qpid/trunk/qpid/python/qpid/brokertest.py
qpid/trunk/qpid/python/qpid/tests/address.py
qpid/trunk/qpid/python/qpid/tests/messaging.py
Modified: qpid/trunk/qpid/cpp/src/tests/cluster_tests.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/tests/cluster_tests.py?rev=835879&r1=835878&r2=835879&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/tests/cluster_tests.py (original)
+++ qpid/trunk/qpid/cpp/src/tests/cluster_tests.py Fri Nov 13 15:33:04 2009
@@ -34,8 +34,8 @@
# Start a cluster, send some messages to member 0.
cluster = self.cluster(2)
s0 = cluster[0].connect().session()
- s0.sender("q {create:always}").send(messaging.Message("x"))
- s0.sender("q {create:always}").send(messaging.Message("y"))
+ s0.sender("q; {create:always}").send(messaging.Message("x"))
+ s0.sender("q; {create:always}").send(messaging.Message("y"))
s0.connection.close()
# Verify messages available on member 1.
Modified: qpid/trunk/qpid/java/testkit/testkit.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/java/testkit/testkit.py?rev=835879&r1=835878&r2=835879&view=diff
==============================================================================
--- qpid/trunk/qpid/java/testkit/testkit.py (original)
+++ qpid/trunk/qpid/java/testkit/testkit.py Fri Nov 13 15:33:04 2009
@@ -42,7 +42,7 @@
# temp hack: just creating the queue here and closing it.
def start_error_watcher(self,broker=None):
ssn = broker.connect().session()
- err_watcher = ssn.receiver("control {create:always}", capacity=1)
+ err_watcher = ssn.receiver("control; {create:always}", capacity=1)
ssn.close()
def client(self,**options):
@@ -76,7 +76,7 @@
# temp hack: just creating a receiver and closing session soon after.
def monitor_clients(self,broker=None,run_time=600,error_ck_freq=60):
ssn = broker.connect().session()
- err_watcher = ssn.receiver("control {create:always}", capacity=1)
+ err_watcher = ssn.receiver("control; {create:always}", capacity=1)
i = run_time/error_ck_freq
for j in range(i):
try:
Modified: qpid/trunk/qpid/python/qpid/address.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid/address.py?rev=835879&r1=835878&r2=835879&view=diff
==============================================================================
--- qpid/trunk/qpid/python/qpid/address.py (original)
+++ qpid/trunk/qpid/python/qpid/address.py Fri Nov 13 15:33:04 2009
@@ -34,22 +34,33 @@
LBRACE = Type("LBRACE", r"\{")
RBRACE = Type("RBRACE", r"\}")
COLON = Type("COLON", r":")
-COMMA = Type("COMMA", r",")
+SEMI = Type("SEMI", r";")
SLASH = Type("SLASH", r"/")
-ID = Type("ID", r'[a-zA-Z_][a-zA-Z0-9_.#*-]*')
+COMMA = Type("COMMA", r",")
NUMBER = Type("NUMBER", r'[+-]?[0-9]*\.?[0-9]+')
+ID = Type("ID", r'[a-zA-Z_][a-zA-Z0-9_]*')
STRING = Type("STRING", r""""(?:[^\\"]|\\.)*"|'(?:[^\\']|\\.)*'""")
+ESC = Type("ESC", r"\\[^ux]|\\x[0-9][0-9]|\\u[0-9][0-9][0-9][0-0]")
+SYM = Type("SYM", r"[.#*%@$^!+-]")
WSPACE = Type("WSPACE", r"[ \n\r\t]+")
EOF = Type("EOF")
class Token:
- def __init__(self, type, value):
+ def __init__(self, type, value, input, position):
self.type = type
self.value = value
+ self.input = input
+ self.position = position
+
+ def line_info(self):
+ return line_info(self.input, self.position)
def __repr__(self):
- return "%s: %r" % (self.type, self.value)
+ if self.value is None:
+ return repr(self.type)
+ else:
+ return "%s(%r)" % (self.type, self.value)
joined = "|".join(["(%s)" % t.pattern for t in TYPES])
LEXER = re.compile(joined)
@@ -83,15 +94,51 @@
m = LEXER.match(st, pos)
if m is None:
line, ln, col = line_info(st, pos)
- raise LexError("unrecognized character in <string>:%s,%s: %s" % (ln, col, line))
+ raise LexError("unrecognized characters line:%s,%s: %s" % (ln, col, line))
else:
idx = m.lastindex
- t = Token(TYPES[idx - 1], m.group(idx))
+ t = Token(TYPES[idx - 1], m.group(idx), st, pos)
yield t
pos = m.end()
- yield Token(EOF, None)
+ yield Token(EOF, None, st, pos)
-class ParseError(Exception): pass
+def tok2str(tok):
+ if tok.type is STRING:
+ return eval(tok.value)
+ elif tok.type is ESC:
+ if tok.value[1] in ("x", "u"):
+ return eval('"%s"' % tok.value)
+ else:
+ return tok.value[1]
+ else:
+ return tok.value
+
+def tok2obj(tok):
+ if tok.type in (STRING, NUMBER):
+ return eval(tok.value)
+ else:
+ return tok.value
+
+def toks2str(toks):
+ if toks:
+ return "".join(map(tok2str, toks))
+ else:
+ return None
+
+class ParseError(Exception):
+
+ def __init__(self, token, *expected):
+ line, ln, col = token.line_info()
+ exp = ", ".join(map(str, expected))
+ if len(expected) > 1:
+ exp = "(%s)" % exp
+ if expected:
+ msg = "expecting %s, got %s line:%s,%s:%s" % (exp, token, ln, col, line)
+ else:
+ msg = "unexpected token %s line:%s,%s:%s" % (token, ln, col, line)
+ Exception.__init__(self, msg)
+ self.token = token
+ self.expected = expected
class Parser:
@@ -107,46 +154,62 @@
def eat(self, *types):
if types and not self.matches(*types):
- raise ParseError("expecting %s -- got %s" % (", ".join(map(str, types)), self.next()))
+ raise ParseError(self.next(), *types)
else:
t = self.next()
self.idx += 1
return t
+ def eat_until(self, *types):
+ result = []
+ while not self.matches(*types):
+ result.append(self.eat())
+ return result
+
def parse(self):
result = self.address()
self.eat(EOF)
return result
def address(self):
- name = self.eat(ID).value
- subject = None
- options = None
+ name = toks2str(self.eat_until(SLASH, SEMI, EOF))
+
+ if name is None:
+ raise ParseError(self.next())
+
if self.matches(SLASH):
self.eat(SLASH)
- if self.matches(ID):
- subject = self.eat(ID).value
- else:
- subject = ""
- elif self.matches(LBRACE):
+ subject = toks2str(self.eat_until(SEMI, EOF))
+ else:
+ subject = None
+
+ if self.matches(SEMI):
+ self.eat(SEMI)
options = self.map()
+ else:
+ options = None
return name, subject, options
def map(self):
self.eat(LBRACE)
+
result = {}
while True:
- if self.matches(RBRACE):
- self.eat(RBRACE)
- break
- else:
- if self.matches(ID):
- n, v = self.nameval()
- result[n] = v
- elif self.matches(COMMA):
+ if self.matches(ID):
+ n, v = self.nameval()
+ result[n] = v
+ if self.matches(COMMA):
self.eat(COMMA)
+ elif self.matches(RBRACE):
+ break
else:
- raise ParseError("expecting (ID, COMMA), got %s" % self.next())
+ raise ParseError(self.next(), COMMA, RBRACE)
+ elif self.matches(RBRACE):
+ break
+ else:
+ raise ParseError(self.next(), ID, RBRACE)
+
+ self.eat(RBRACE)
return result
def nameval(self):
@@ -156,16 +219,14 @@
return (name, val)
def value(self):
- if self.matches(NUMBER, STRING):
- return eval(self.eat().value)
- elif self.matches(ID):
- return self.eat().value
+ if self.matches(NUMBER, STRING, ID):
+ return tok2obj(self.eat())
elif self.matches(LBRACE):
return self.map()
else:
- raise ParseError("expecting (NUMBER, STRING, LBRACE) got %s" % self.next())
+ raise ParseError(self.next(), NUMBER, STRING, ID, LBRACE)
def parse(addr):
return Parser(lex(addr)).parse()
-__all__ = ["parse"]
+__all__ = ["parse", "ParseError"]
Modified: qpid/trunk/qpid/python/qpid/brokertest.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid/brokertest.py?rev=835879&r1=835878&r2=835879&view=diff
==============================================================================
--- qpid/trunk/qpid/python/qpid/brokertest.py (original)
+++ qpid/trunk/qpid/python/qpid/brokertest.py Fri Nov 13 15:33:04 2009
@@ -176,25 +176,25 @@
def send_message(self, queue, message):
s = self.connect().session()
- s.sender(queue+" {create:always}").send(message)
+ s.sender(queue+"; {create:always}").send(message)
s.connection.close()
def send_messages(self, queue, messages):
s = self.connect().session()
- sender = s.sender(queue+" {create:always}")
+ sender = s.sender(queue+"; {create:always}")
for m in messages: sender.send(m)
s.connection.close()
def get_message(self, queue):
s = self.connect().session()
- m = s.receiver(queue+" {create:always}", capacity=1).fetch(timeout=1)
+ m = s.receiver(queue+"; {create:always}", capacity=1).fetch(timeout=1)
s.acknowledge()
s.connection.close()
return m
def get_messages(self, queue, n):
s = self.connect().session()
- receiver = s.receiver(queue+" {create:always}", capacity=n)
+ receiver = s.receiver(queue+"; {create:always}", capacity=n)
m = [receiver.fetch(timeout=1) for i in range(n)]
s.acknowledge()
s.connection.close()
Modified: qpid/trunk/qpid/python/qpid/tests/address.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid/tests/address.py?rev=835879&r1=835878&r2=835879&view=diff
==============================================================================
--- qpid/trunk/qpid/python/qpid/tests/address.py (original)
+++ qpid/trunk/qpid/python/qpid/tests/address.py Fri Nov 13 15:33:04 2009
@@ -18,18 +18,101 @@
#
from qpid.tests import Test
-from qpid.address import parse
+from qpid.address import parse, ParseError
class AddressTests(Test):
+ def valid(self, addr, name=None, subject=None, options=None):
+ expected = (name, subject, options)
+ got = parse(addr)
+ assert expected == got, "expected %s, got %s" % (expected, got)
+
+ def invalid(self, addr, error=None):
+ try:
+ p = parse(addr)
+ assert False, "invalid address parsed: %s" % p
+ except ParseError, e:
+ assert error == str(e), "expected %r, got %r" % (error, str(e))
+
def testHash(self):
- name, subject, options = parse("foo/bar.#")
- assert name == "foo", name
- assert subject == "bar.#", bar
- assert options == None
+ self.valid("foo/bar.#", "foo", "bar.#")
def testStar(self):
- name, subject, options = parse("foo/bar.*")
- assert name == "foo", name
- assert subject == "bar.*", bar
- assert options == None
+ self.valid("foo/bar.*", "foo", "bar.*")
+
+ def testColon(self):
+ self.valid("foo.bar/baz.qux:moo:arf", "foo.bar", "baz.qux:moo:arf")
+
+ def testOptions(self):
+ self.valid("foo.bar/baz.qux:moo:arf; {key: value}",
+ "foo.bar", "baz.qux:moo:arf", {"key": "value"})
+
+ def testOptionsTrailingComma(self):
+ self.valid("name/subject; {key: value,}", "name", "subject", {"key": "value"})
+
+ def testSemiSubject(self):
+ self.valid("foo.bar/'baz.qux;moo:arf'; {key: value}",
+ "foo.bar", "baz.qux;moo:arf", {"key": "value"})
+
+ def testCommaSubject(self):
+ self.valid("foo.bar/baz.qux.{moo,arf}", "foo.bar", "baz.qux.{moo,arf}")
+
+ def testCommaSubjectOptions(self):
+ self.valid("foo.bar/baz.qux.{moo,arf}; {key: value}", "foo.bar",
+ "baz.qux.{moo,arf}", {"key": "value"})
+
+ def testUnbalanced(self):
+ self.valid("foo.bar/baz.qux.{moo,arf; {key: value}", "foo.bar",
+ "baz.qux.{moo,arf", {"key": "value"})
+
+ def testSlashQuote(self):
+ self.valid("foo.bar\\/baz.qux.{moo,arf; {key: value}", "foo.bar/baz.qux.{moo,arf",
+ None, {"key": "value"})
+
+ def testSlashEsc(self):
+ self.valid("foo.bar\\x00baz.qux.{moo,arf; {key: value}", "foo.bar\x00baz.qux.{moo,arf",
+ None, {"key": "value"})
+
+ def testNoName(self):
+ self.invalid("; {key: value}", "unexpected token SEMI(';') line:1,0:; {key: value}")
+
+ def testEmpty(self):
+ self.invalid("", "unexpected token EOF line:1,0:")
+
+ def testNoNameSlash(self):
+ self.invalid("/asdf; {key: value}",
+ "unexpected token SLASH('/') line:1,0:/asdf; {key: value}")
+
+ def testBadOptions1(self):
+ self.invalid("name/subject; {",
+ "expecting (ID, RBRACE), got EOF line:1,15:name/subject; {")
+
+ def testBadOptions2(self):
+ self.invalid("name/subject; { 3",
+ "expecting (ID, RBRACE), got NUMBER('3') "
+ "line:1,16:name/subject; { 3")
+
+ def testBadOptions3(self):
+ self.invalid("name/subject; { key:",
+ "expecting (NUMBER, STRING, ID, LBRACE), got EOF "
+ "line:1,20:name/subject; { key:")
+
+ def testBadOptions4(self):
+ self.invalid("name/subject; { key: value",
+ "expecting (COMMA, RBRACE), got EOF "
+ "line:1,26:name/subject; { key: value")
+
+ def testBadOptions5(self):
+ self.invalid("name/subject; { key: value asdf",
+ "expecting (COMMA, RBRACE), got ID('asdf') "
+ "line:1,27:name/subject; { key: value asdf")
+
+ def testBadOptions6(self):
+ self.invalid("name/subject; { key: value,",
+ "expecting (ID, RBRACE), got EOF "
+ "line:1,27:name/subject; { key: value,")
+
+ def testBadOptions7(self):
+ self.invalid("name/subject; { key: value } asdf",
+ "expecting EOF, got ID('asdf') "
+ "line:1,29:name/subject; { key: value } asdf")
Modified: qpid/trunk/qpid/python/qpid/tests/messaging.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid/tests/messaging.py?rev=835879&r1=835878&r2=835879&view=diff
==============================================================================
--- qpid/trunk/qpid/python/qpid/tests/messaging.py (original)
+++ qpid/trunk/qpid/python/qpid/tests/messaging.py Fri Nov 13 15:33:04 2009
@@ -66,7 +66,7 @@
return "%s[%s, %s]" % (base, count, self.test_id)
def ping(self, ssn):
- PING_Q = 'ping-queue {create: always}'
+ PING_Q = 'ping-queue; {create: always}'
# send a message
sender = ssn.sender(PING_Q, durable=self.durable())
content = self.content("ping")
@@ -190,7 +190,7 @@
self.conn.close()
assert not self.conn.connected()
-ACK_Q = 'test-ack-queue {create: always}'
+ACK_Q = 'test-ack-queue; {create: always}'
class SessionTests(Base):
@@ -202,7 +202,7 @@
return self.conn.session()
def testSender(self):
- snd = self.ssn.sender('test-snd-queue {create: always}',
+ snd = self.ssn.sender('test-snd-queue; {create: always}',
durable=self.durable())
snd2 = self.ssn.sender(snd.target, durable=self.durable())
assert snd is not snd2
@@ -216,7 +216,7 @@
self.ssn.acknowledge(msg)
def testReceiver(self):
- rcv = self.ssn.receiver('test-rcv-queue {create: always}')
+ rcv = self.ssn.receiver('test-rcv-queue; {create: always}')
rcv2 = self.ssn.receiver(rcv.source)
assert rcv is not rcv2
rcv2.close()
@@ -229,7 +229,7 @@
self.ssn.acknowledge(msg)
def testNextReceiver(self):
- ADDR = 'test-next-rcv-queue {create: always}'
+ ADDR = 'test-next-rcv-queue; {create: always}'
rcv1 = self.ssn.receiver(ADDR, capacity=UNLIMITED)
rcv2 = self.ssn.receiver(ADDR, capacity=UNLIMITED)
rcv3 = self.ssn.receiver(ADDR, capacity=UNLIMITED)
@@ -258,7 +258,7 @@
self.ssn.acknowledge()
def testStart(self):
- START_Q = 'test-start-queue {create: always}'
+ START_Q = 'test-start-queue; {create: always}'
rcv = self.ssn.receiver(START_Q)
assert not rcv.started
self.ssn.start()
@@ -267,7 +267,7 @@
assert rcv.started
def testStop(self):
- STOP_Q = 'test-stop-queue {create: always}'
+ STOP_Q = 'test-stop-queue; {create: always}'
self.ssn.start()
rcv = self.ssn.receiver(STOP_Q)
assert rcv.started
@@ -345,8 +345,8 @@
return contents
def txTest(self, commit):
- TX_Q = 'test-tx-queue {create: always}'
- TX_Q_COPY = 'test-tx-queue-copy {create: always}'
+ TX_Q = 'test-tx-queue; {create: always}'
+ TX_Q_COPY = 'test-tx-queue-copy; {create: always}'
txssn = self.conn.session(transactional=True)
contents = self.send(self.ssn, TX_Q, "txTest", 3)
txrcv = txssn.receiver(TX_Q)
@@ -376,7 +376,7 @@
self.txTest(False)
def txTestSend(self, commit):
- TX_SEND_Q = 'test-tx-send-queue {create: always}'
+ TX_SEND_Q = 'test-tx-send-queue; {create: always}'
txssn = self.conn.session(transactional=True)
contents = self.send(txssn, TX_SEND_Q, "txTestSend", 3)
rcv = self.ssn.receiver(TX_SEND_Q)
@@ -399,7 +399,7 @@
self.txTestSend(False)
def txTestAck(self, commit):
- TX_ACK_Q = 'test-tx-ack-queue {create: always}'
+ TX_ACK_Q = 'test-tx-ack-queue; {create: always}'
txssn = self.conn.session(transactional=True)
txrcv = txssn.receiver(TX_ACK_Q)
self.assertEmpty(txrcv)
@@ -444,7 +444,7 @@
except Disconnected:
pass
-RECEIVER_Q = 'test-receiver-queue {create: always}'
+RECEIVER_Q = 'test-receiver-queue; {create: always}'
class ReceiverTests(Base):
@@ -581,7 +581,7 @@
# XXX: need testClose
NOSUCH_Q = "this-queue-should-not-exist"
-UNPARSEABLE_ADDR = "{bad address}"
+UNPARSEABLE_ADDR = "name/subject; {bad options"
UNLEXABLE_ADDR = "\0x0\0x1\0x2\0x3"
class AddressErrorTests(Base):
@@ -630,24 +630,24 @@
def testUnparseableTarget(self):
# XXX: should have specific exception for this
self.sendErrorTest(UNPARSEABLE_ADDR, SendError,
- lambda e: "expecting ID" in str(e))
+ lambda e: "expecting COLON" in str(e))
def testUnparseableSource(self):
# XXX: should have specific exception for this
self.fetchErrorTest(UNPARSEABLE_ADDR, ReceiveError,
- lambda e: "expecting ID" in str(e))
+ lambda e: "expecting COLON" in str(e))
def testUnlexableTarget(self):
# XXX: should have specific exception for this
self.sendErrorTest(UNLEXABLE_ADDR, SendError,
- lambda e: "unrecognized character" in str(e))
+ lambda e: "unrecognized characters" in str(e))
def testUnlexableSource(self):
# XXX: should have specific exception for this
self.fetchErrorTest(UNLEXABLE_ADDR, ReceiveError,
- lambda e: "unrecognized character" in str(e))
+ lambda e: "unrecognized characters" in str(e))
-SENDER_Q = 'test-sender-q {create: always}'
+SENDER_Q = 'test-sender-q; {create: always}'
class SenderTests(Base):
@@ -756,7 +756,7 @@
m.content = u"<html/>"
assert m.content_type == "text/html; charset=utf8"
-ECHO_Q = 'test-message-echo-queue {create: always}'
+ECHO_Q = 'test-message-echo-queue; {create: always}'
class MessageEchoTests(Base):
---------------------------------------------------------------------
Apache Qpid - AMQP Messaging Implementation
Project: http://qpid.apache.org
Use/Interact: mailto:commits-subscribe@qpid.apache.org