You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by gs...@apache.org on 2014/10/10 14:54:43 UTC
svn commit: r1630834 - in /qpid/proton/branches/examples:
proton-c/bindings/python/cproton.i proton-c/bindings/python/proton.py
tests/python/proton_tests/__init__.py tests/python/proton_tests/url.py
Author: gsim
Date: Fri Oct 10 12:54:43 2014
New Revision: 1630834
URL: http://svn.apache.org/r1630834
Log:
PROTON-693: Python Url class to wrap C function pni_parse_url
Also added unit tests.
Added:
qpid/proton/branches/examples/tests/python/proton_tests/url.py
Modified:
qpid/proton/branches/examples/proton-c/bindings/python/cproton.i
qpid/proton/branches/examples/proton-c/bindings/python/proton.py
qpid/proton/branches/examples/tests/python/proton_tests/__init__.py
Modified: qpid/proton/branches/examples/proton-c/bindings/python/cproton.i
URL: http://svn.apache.org/viewvc/qpid/proton/branches/examples/proton-c/bindings/python/cproton.i?rev=1630834&r1=1630833&r2=1630834&view=diff
==============================================================================
--- qpid/proton/branches/examples/proton-c/bindings/python/cproton.i (original)
+++ qpid/proton/branches/examples/proton-c/bindings/python/cproton.i Fri Oct 10 12:54:43 2014
@@ -280,4 +280,41 @@ int pn_ssl_get_peer_hostname(pn_ssl_t *s
}
%}
+
+/**
+ pni_parse_url(char* url, char **scheme, char **user, char **pass, char **host, char **port, char **path)
+ The following type maps convert this into a python function that taks a URL string argument
+ and returns a list of strings [scheme, user, pass, host, port, path]
+ This probably could be done more neatly.
+*/
+
+// Typemap to copy the url string as it will be modified by parse_url
+%typemap(in,noblock=1,fragment="SWIG_AsCharPtrAndSize") char *url (int res, char *t = 0, size_t n = 0, int alloc = 0) {
+ res = SWIG_AsCharPtrAndSize($input, &t, &n, &alloc);
+ if (!SWIG_IsOK(res)) {
+ %argument_fail(res, "char *url", $symname, $argnum);
+ }
+ $1 = %new_array(n, $*1_ltype);
+ memcpy($1,t,sizeof(char)*n);
+ if (alloc == SWIG_NEWOBJ) %delete_array(t);
+ $1[n-1] = 0;
+}
+%typemap(freearg,match="in") char *url "free($1);";
+%typemap(argout) char *url "";
+
+// Typemap for char** return strings. Don't free them.
+%typemap(in,numinputs=0) char **OUTSTR($*1_ltype temp = 0) "$1 = &temp;";
+%typemap(freearg,match="in") char **OUTSTR "";
+%typemap(argout,noblock=1,fragment="SWIG_FromCharPtr") char **OUTSTR {
+ %append_output(SWIG_FromCharPtr(*$1));
+}
+
+// Typemap to initialize result as empty list
+%typemap(out) void "$result = PyList_New(0);";
+
+
+%apply char** OUTSTR {char **scheme, char **user, char **pass, char **host, char **port, char **path};
+void pni_parse_url(char* url, char **scheme, char **user, char **pass, char **host, char **port, char **path);
+%ignore pni_parse_url;
+
%include "proton/cproton.i"
Modified: qpid/proton/branches/examples/proton-c/bindings/python/proton.py
URL: http://svn.apache.org/viewvc/qpid/proton/branches/examples/proton-c/bindings/python/proton.py?rev=1630834&r1=1630833&r2=1630834&view=diff
==============================================================================
--- qpid/proton/branches/examples/proton-c/bindings/python/proton.py (original)
+++ qpid/proton/branches/examples/proton-c/bindings/python/proton.py Fri Oct 10 12:54:43 2014
@@ -3654,3 +3654,117 @@ __all__ = [
"timestamp",
"ulong"
]
+
+
+class Url(object):
+ """
+ Simple URL parser/constructor, handles URLs of the form:
+
+ <scheme>://<user>:<password>@<host>:<port>/<path>
+
+ All components can be None if not specifeid in the URL string.
+
+ The port can be specified as a service name, e.g. 'amqp' in the
+ URL string but Url.port always gives the integer value.
+
+ @ivar scheme: Url scheme e.g. 'amqp' or 'amqps'
+ @ivar user: Username
+ @ivar password: Password
+ @ivar host: Host name, ipv6 literal or ipv4 dotted quad.
+ @ivar port: Integer port.
+ @ivar host_port: Returns host:port
+ """
+
+ AMQPS = "amqps"
+ AMQP = "amqp"
+
+ class Port(int):
+ """An integer port number that can also have an associated service name string"""
+
+ def __new__(cls, value):
+ port = super(Url.Port, cls).__new__(cls, cls.port_int(value))
+ setattr(port, 'name', str(value))
+ return port
+
+ def __eq__(self, x): return str(self) == x or int(self) == x
+ def __ne__(self, x): return not self == x
+ def __str__(self): return str(self.name)
+
+ @staticmethod
+ def port_int(value):
+ """Convert service, an integer or a service name, into an integer port number."""
+ try:
+ return int(value)
+ except ValueError:
+ try:
+ return socket.getservbyname(value)
+ except socket.error:
+ raise ValueError("Not a valid port number or service name: '%s'" % value)
+
+ def __init__(self, url=None, **kwargs):
+ """
+ @param url: String or Url instance to parse or copy.
+ @param kwargs: URL fields: scheme, user, password, host, port, path.
+ If specified, replaces corresponding component in url.
+ """
+
+ fields = ['scheme', 'user', 'password', 'host', 'port', 'path']
+
+ for f in fields: setattr(self, f, None)
+ for k in kwargs: getattr(self, k) # Check for invalid kwargs
+
+ if isinstance(url, Url): # Copy from another Url instance.
+ self.__dict__.update(url.__dict__)
+ elif url is not None: # Parse from url
+ parts = pni_parse_url(str(url))
+ if not filter(None, parts): raise ValueError("Invalid AMQP URL: '%s'" % url)
+ self.scheme, self.user, self.password, self.host, port, self.path = parts
+ if not self.host: self.host = None
+ self.port = port and self.Port(port)
+
+ # Let kwargs override values previously set from url
+ for field in fields:
+ setattr(self, field, kwargs.get(field, getattr(self, field)))
+
+ def __repr__(self):
+ return "Url(%r)" % str(self)
+
+ def __str__(self):
+ s = ""
+ if self.scheme:
+ s += "%s://" % self.scheme
+ if self.user:
+ s += self.user
+ if self.password:
+ s += ":%s" % self.password
+ if self.user or self.password:
+ s += '@'
+ if self.host and ':' in self.host:
+ s += "[%s]" % self.host
+ elif self.host:
+ s += self.host
+ if self.port:
+ s += ":%s" % self.port
+ if self.path:
+ s += "/%s" % self.path
+ return s
+
+ def __eq__(self, url):
+ return \
+ self.scheme == url.scheme and \
+ self.user == url.user and self.password == url.password and \
+ self.host == url.host and self.port == url.port and \
+ self.path == url.path
+
+ def __ne__(self, url):
+ return not self.__eq__(url)
+
+ def defaults(self):
+ """
+ Fill in missing values with defaults
+ @return: self
+ """
+ self.scheme = self.scheme or self.AMQP
+ self.host = self.host or '0.0.0.0'
+ self.port = self.port or self.Port(self.scheme)
+ return self
Modified: qpid/proton/branches/examples/tests/python/proton_tests/__init__.py
URL: http://svn.apache.org/viewvc/qpid/proton/branches/examples/tests/python/proton_tests/__init__.py?rev=1630834&r1=1630833&r2=1630834&view=diff
==============================================================================
--- qpid/proton/branches/examples/tests/python/proton_tests/__init__.py (original)
+++ qpid/proton/branches/examples/tests/python/proton_tests/__init__.py Fri Oct 10 12:54:43 2014
@@ -26,4 +26,4 @@ import proton_tests.transport
import proton_tests.ssl
import proton_tests.interop
import proton_tests.soak
-
+import proton_tests.url
Added: qpid/proton/branches/examples/tests/python/proton_tests/url.py
URL: http://svn.apache.org/viewvc/qpid/proton/branches/examples/tests/python/proton_tests/url.py?rev=1630834&view=auto
==============================================================================
--- qpid/proton/branches/examples/tests/python/proton_tests/url.py (added)
+++ qpid/proton/branches/examples/tests/python/proton_tests/url.py Fri Oct 10 12:54:43 2014
@@ -0,0 +1,117 @@
+#
+# 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 common
+from proton import Url
+
+class UrlTest(common.Test):
+ def assertEqual(self, a, b):
+ assert a == b, "%s != %s" % (a, b)
+
+ def assertNotEqual(self, a, b):
+ assert a != b, "%s == %s" % (a, b)
+
+ def assertUrl(self, u, scheme, user, password, host, port, path):
+ self.assertEqual((u.scheme, u.user, u.password, u.host, u.port, u.path),
+ (scheme, user, password, host, port, path))
+
+ def testUrl(self):
+ url = Url('amqp://me:secret@myhost:1234/foobar')
+ self.assertEqual(str(url), "amqp://me:secret@myhost:1234/foobar")
+ self.assertUrl(url, 'amqp', 'me', 'secret', 'myhost', 1234, 'foobar')
+ self.assertEqual(str(url), "amqp://me:secret@myhost:1234/foobar")
+
+ def testDefaults(self):
+ # Check that we allow None for scheme, port
+ url = Url(user='me', password='secret', host='myhost', path='foobar')
+ self.assertEqual(str(url), "me:secret@myhost/foobar")
+ self.assertUrl(url, None, 'me', 'secret', 'myhost', None, 'foobar')
+
+ # Scheme defaults
+ self.assertEqual(str(Url("me:secret@myhost/foobar").defaults()),
+ "amqp://me:secret@myhost:amqp/foobar")
+ # Correct port for amqps vs. amqps
+ self.assertEqual(str(Url("amqps://me:secret@myhost/foobar").defaults()),
+ "amqps://me:secret@myhost:amqps/foobar")
+ self.assertEqual(str(Url("amqp://me:secret@myhost/foobar").defaults()),
+ "amqp://me:secret@myhost:amqp/foobar")
+
+ # Empty string vs. None for path
+ self.assertEqual(Url("myhost/").path, "")
+ assert Url("myhost").path is None
+
+ def assertPort(self, port, portint, portstr):
+ self.assertEqual((port, port), (portint, portstr))
+ self.assertEqual((int(port), str(port)), (portint, portstr))
+
+ def testPort(self):
+ self.assertPort(Url.Port('amqp'), 5672, 'amqp')
+ self.assertPort(Url.Port(5672), 5672, '5672')
+ self.assertPort(Url.Port('amqps'), 5671, 'amqps')
+ self.assertPort(Url.Port(5671), 5671, '5671')
+ self.assertEqual(Url.Port(5671)+1, 5672) # Treat as int
+ self.assertEqual(str(Url.Port(5672)), '5672')
+
+ self.assertPort(Url.Port(Url.Port('amqp')), 5672, 'amqp')
+ self.assertPort(Url.Port(Url.Port(5672)), 5672, '5672')
+
+ try:
+ Url.Port('xxx')
+ assert False, "Expected ValueError"
+ except ValueError: pass
+
+ self.assertEqual(str(Url("host:amqp")), "host:amqp")
+ self.assertEqual(Url("host:amqp").port, 5672)
+ self.assertEqual(str(Url("host:amqps")), "host:amqps")
+ self.assertEqual(Url("host:amqps").port, 5671)
+
+ def testArgs(self):
+ u = Url("amqp://u:p@host:amqp/path", scheme='foo', host='bar', port=1234, path='garden')
+ self.assertUrl(u, 'foo', 'u', 'p', 'bar', 1234, 'garden')
+ u = Url()
+ self.assertUrl(u, None, None, None, None, None, None)
+
+ def assertRaises(self, exception, function, *args, **kwargs):
+ try:
+ function(*args, **kwargs)
+ assert False, "Expected exception %s" % exception.__name__
+ except exception: pass
+
+ def testMissing(self):
+ self.assertUrl(Url(), None, None, None, None, None, None)
+ self.assertUrl(Url('amqp://'), 'amqp', None, None, None, None, None)
+ self.assertUrl(Url('user@'), None, 'user', None, None, None, None)
+ self.assertUrl(Url(':pass@'), None, '', 'pass', None, None, None)
+ self.assertUrl(Url('host'), None, None, None, 'host', None, None)
+ self.assertUrl(Url(':1234'), None, None, None, None, 1234, None)
+ self.assertUrl(Url('/path'), None, None, None, None, None, 'path')
+
+ for s in ['amqp://', 'user@', ':pass@', ':1234', '/path']:
+ self.assertEqual(s, str(Url(s)))
+
+ for s, full in [
+ ('amqp://', 'amqp://0.0.0.0:amqp'),
+ ('user@', 'amqp://user@0.0.0.0:amqp'),
+ (':pass@', 'amqp://:pass@0.0.0.0:amqp'),
+ (':1234', 'amqp://0.0.0.0:1234'),
+ ('/path', 'amqp://0.0.0.0:amqp/path')]:
+ self.assertEqual(str(Url(s).defaults()), full)
+
+ self.assertRaises(ValueError, Url, '')
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org