PROTON-1853: [Python] Rework Url so it doesn't use proton-c
- Improve the str output format to be more RFC compliant
- Tests for new string format


Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/55a4a192
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/55a4a192
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/55a4a192

Branch: refs/heads/go1
Commit: 55a4a192bf6e76fc1cf9dbacc90623b0fe46735b
Parents: a04a964
Author: Andrew Stitcher <astitc...@apache.org>
Authored: Fri May 25 18:18:27 2018 -0400
Committer: Andrew Stitcher <astitc...@apache.org>
Committed: Tue Jun 5 15:52:18 2018 +0100

----------------------------------------------------------------------
 python/proton/_compat.py         |   6 ++
 python/proton/_url.py            | 127 ++++++++++++++++++++++------------
 tests/python/proton_tests/url.py |  16 +++--
 3 files changed, 101 insertions(+), 48 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/55a4a192/python/proton/_compat.py
----------------------------------------------------------------------
diff --git a/python/proton/_compat.py b/python/proton/_compat.py
index eae4c84..79bd2c1 100644
--- a/python/proton/_compat.py
+++ b/python/proton/_compat.py
@@ -29,6 +29,12 @@ try:
 except ImportError:
     import queue
 
+try:
+    from urlparse import urlparse, urlunparse
+    from urllib import quote, unquote
+except ImportError:
+    from urllib.parse import urlparse, urlunparse, quote, unquote
+
 PY3 = sys.version_info[0] == 3
 
 if PY3:

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/55a4a192/python/proton/_url.py
----------------------------------------------------------------------
diff --git a/python/proton/_url.py b/python/proton/_url.py
index b4a9a6a..39cf480 100644
--- a/python/proton/_url.py
+++ b/python/proton/_url.py
@@ -20,12 +20,7 @@
 from __future__ import absolute_import
 
 import socket
-
-from cproton import pn_url, pn_url_free, pn_url_parse, pn_url_str, 
pn_url_get_port, pn_url_get_scheme, \
-    pn_url_get_host, pn_url_get_username, pn_url_get_password, 
pn_url_get_path, pn_url_set_scheme, pn_url_set_host, \
-    pn_url_set_username, pn_url_set_password, pn_url_set_port, pn_url_set_path
-
-from ._common import unicode2utf8
+from ._compat import urlparse, urlunparse, quote, unquote
 
 
 class Url(object):
@@ -97,48 +92,98 @@ class Url(object):
         @param kwargs: scheme, user, password, host, port, path.
           If specified, replaces corresponding part in url string.
         """
-        if url:
-            self._url = pn_url_parse(unicode2utf8(str(url)))
-            if not self._url: raise ValueError("Invalid URL '%s'" % url)
+        if isinstance(url, Url):
+            self.scheme = url.scheme
+            self.username = url.username
+            self.password = url.password
+            self.host = url.host
+            self._port = url._port
+            self._path = url._path
+            self._params = url._params
+            self._query = url._query
+            self._fragment = url._fragment
+        elif url:
+            if not '://' in url:
+                url = '//' + url
+            u = urlparse(url)
+            if not u: raise ValueError("Invalid URL '%s'" % url)
+            self.scheme = None if not u.scheme else u.scheme
+            self.username = u.username and unquote(u.username)
+            self.password = u.password and unquote(u.password)
+            self.host = None if not u.hostname else u.hostname
+            self._port = self._parse_port(u.netloc)
+            self._path = None if not u.path else u.path
+            self._params = u.params
+            self._query = u.query
+            self._fragment = u.fragment
         else:
-            self._url = pn_url()
+            self.scheme = None
+            self.username = None
+            self.password = None
+            self.host = None
+            self._port = None
+            self._path = None
+            self._params = None
+            self._query = None
+            self._fragment = None
         for k in kwargs:  # Let kwargs override values parsed from url
             getattr(self, k)  # Check for invalid kwargs
             setattr(self, k, kwargs[k])
         if defaults: self.defaults()
 
-    class PartDescriptor(object):
-        def __init__(self, part):
-            self.getter = globals()["pn_url_get_%s" % part]
-            self.setter = globals()["pn_url_set_%s" % part]
-
-        def __get__(self, obj, type=None): return self.getter(obj._url)
-
-        def __set__(self, obj, value): return self.setter(obj._url, str(value))
-
-    scheme = PartDescriptor('scheme')
-    username = PartDescriptor('username')
-    password = PartDescriptor('password')
-    host = PartDescriptor('host')
-    path = PartDescriptor('path')
-
-    def _get_port(self):
-        portstr = pn_url_get_port(self._url)
-        return portstr and Url.Port(portstr)
-
-    def _set_port(self, value):
-        if value is None:
-            pn_url_set_port(self._url, None)
-        else:
-            pn_url_set_port(self._url, str(Url.Port(value)))
-
-    port = property(_get_port, _set_port)
+    @staticmethod
+    def _parse_port(nl):
+        netloc = nl.split('@')[-1].split(']')[-1]
+        if ':' in netloc:
+            port = netloc.split(':')[1]
+            if port:
+                return port
+        return None
+
+    @property
+    def path(self):
+        return self._path if not self._path or self._path[0] != '/' else 
self._path[1:]
+
+    @path.setter
+    def path(self, p):
+        self._path = p if p[0] == '/' else '/' + p
+
+    @property
+    def port(self):
+        return self._port and Url.Port(self._port)
+
+    @port.setter
+    def port(self, p):
+        self._port = p
+
+    @property
+    def _netloc(self):
+        hostport = ''
+        if self.host:
+            hostport = self.host
+        if self._port:
+            hostport += ':'
+            hostport += str(self._port)
+        userpart = ''
+        if self.username:
+            userpart += quote(self.username)
+        if self.password:
+            userpart += ':'
+            userpart += quote(self.password)
+        if self.username or self.password:
+            userpart += '@'
+        return userpart + hostport
 
     def __str__(self):
-        return pn_url_str(self._url)
+        if self.scheme \
+                and not self._netloc and not self._path \
+                and not self._params and not self._query and not 
self._fragment:
+            return self.scheme + '://'
+        return urlunparse((self.scheme or '', self._netloc or '', self._path 
or '',
+                           self._params or '', self._query or '', 
self._fragment or ''))
 
     def __repr__(self):
-        return "Url(%s://%s/%s)" % (self.scheme, self.host, self.path)
+        return "Url('%s')" % self
 
     def __eq__(self, x):
         return str(self) == str(x)
@@ -146,10 +191,6 @@ class Url(object):
     def __ne__(self, x):
         return not self == x
 
-    def __del__(self):
-        pn_url_free(self._url)
-        del self._url
-
     def defaults(self):
         """
         Fill in missing values (scheme, host or port) with defaults
@@ -157,5 +198,5 @@ class Url(object):
         """
         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)
+        self._port = self._port or self.Port(self.scheme)
         return self

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/55a4a192/tests/python/proton_tests/url.py
----------------------------------------------------------------------
diff --git a/tests/python/proton_tests/url.py b/tests/python/proton_tests/url.py
index 40a257c..5b259c6 100644
--- a/tests/python/proton_tests/url.py
+++ b/tests/python/proton_tests/url.py
@@ -42,7 +42,7 @@ class UrlTest(common.Test):
     def testDefaults(self):
         # Check that we allow None for scheme, port
         url = Url(username='me', password='secret', host='myhost', 
path='foobar', defaults=False)
-        self.assertEqual(str(url), "me:secret@myhost/foobar")
+        self.assertEqual(str(url), "//me:secret@myhost/foobar")
         self.assertUrl(url, None, 'me', 'secret', 'myhost', None, 'foobar')
 
         self.assertEqual(str(Url("amqp://me:secret@myhost/foobar")),
@@ -79,7 +79,7 @@ class UrlTest(common.Test):
             assert False, "Expected ValueError"
         except ValueError: pass
 
-        self.assertEqual(str(Url("host:amqp", defaults=False)), "host:amqp")
+        self.assertEqual(str(Url("host:amqp", defaults=False)), "//host:amqp")
         self.assertEqual(Url("host:amqp", defaults=False).port, 5672)
 
     def testArgs(self):
@@ -103,8 +103,14 @@ class UrlTest(common.Test):
         self.assertUrl(Url(':1234', defaults=False), None, None, None, None, 
1234, None)
         self.assertUrl(Url('/path', defaults=False), None, None, None, None, 
None, 'path')
 
-        for s in ['amqp://', 'username@', ':pass@', ':1234', '/path']:
-            self.assertEqual(s, str(Url(s, defaults=False)))
+        for s, full in [
+            ('amqp://', 'amqp://'),
+            ('username@','//username@'),
+            (':pass@', '//:pass@'),
+            (':1234', '//:1234'),
+            ('/path','/path')
+        ]:
+            self.assertEqual(str(Url(s, defaults=False)), full)
 
         for s, full in [
                 ('amqp://', 'amqp://0.0.0.0:amqp'),
@@ -126,7 +132,7 @@ class UrlTest(common.Test):
                          "amqps://me:secret@myhost:amqps/foobar")
 
         self.assertPort(Url.Port('amqps'), 5671, 'amqps')
-        self.assertEqual(str(Url("host:amqps", defaults=False)), "host:amqps")
+        self.assertEqual(str(Url("host:amqps", defaults=False)), 
"//host:amqps")
         self.assertEqual(Url("host:amqps", defaults=False).port, 5671)
 
     def testEqual(self):


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@qpid.apache.org
For additional commands, e-mail: commits-h...@qpid.apache.org

Reply via email to