summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/engine/url.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy/engine/url.py')
-rw-r--r--lib/sqlalchemy/engine/url.py48
1 files changed, 36 insertions, 12 deletions
diff --git a/lib/sqlalchemy/engine/url.py b/lib/sqlalchemy/engine/url.py
index ed5729eea..78ac06187 100644
--- a/lib/sqlalchemy/engine/url.py
+++ b/lib/sqlalchemy/engine/url.py
@@ -1,5 +1,5 @@
# engine/url.py
-# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
+# Copyright (C) 2005-2014 the SQLAlchemy authors and contributors <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -16,6 +16,7 @@ be used directly and is also accepted directly by ``create_engine()``.
import re
from .. import exc, util
from . import Dialect
+from ..dialects import registry
class URL(object):
@@ -23,8 +24,8 @@ class URL(object):
Represent the components of a URL used to connect to a database.
This object is suitable to be passed directly to a
- ``create_engine()`` call. The fields of the URL are parsed from a
- string by the ``module-level make_url()`` function. the string
+ :func:`~sqlalchemy.create_engine` call. The fields of the URL are parsed from a
+ string by the :func:`.make_url` function. the string
format of the URL is an RFC-1738-style string.
All initialization parameters are available as public attributes.
@@ -61,15 +62,19 @@ class URL(object):
self.database = database
self.query = query or {}
- def __str__(self):
+ def __to_string__(self, hide_password=True):
s = self.drivername + "://"
if self.username is not None:
- s += self.username
+ s += _rfc_1738_quote(self.username)
if self.password is not None:
- s += ':' + util.quote_plus(self.password)
+ s += ':' + ('***' if hide_password
+ else _rfc_1738_quote(self.password))
s += "@"
if self.host is not None:
- s += self.host
+ if ':' in self.host:
+ s += "[%s]" % self.host
+ else:
+ s += self.host
if self.port is not None:
s += ':' + str(self.port)
if self.database is not None:
@@ -80,6 +85,12 @@ class URL(object):
s += '?' + "&".join("%s=%s" % (k, self.query[k]) for k in keys)
return s
+ def __str__(self):
+ return self.__to_string__(hide_password=False)
+
+ def __repr__(self):
+ return self.__to_string__()
+
def __hash__(self):
return hash(str(self))
@@ -102,7 +113,6 @@ class URL(object):
name = self.drivername
else:
name = self.drivername.replace('+', '.')
- from sqlalchemy.dialects import registry
cls = registry.load(name)
# check for legacy dialects that
# would return a module with 'dialect' as the
@@ -160,10 +170,13 @@ def _parse_rfc1738_args(name):
(?P<name>[\w\+]+)://
(?:
(?P<username>[^:/]*)
- (?::(?P<password>[^/]*))?
+ (?::(?P<password>.*))?
@)?
(?:
- (?P<host>[^/:]*)
+ (?:
+ \[(?P<ipv6host>[^/]+)\] |
+ (?P<ipv4host>[^/:]+)
+ )?
(?::(?P<port>[^/]*))?
)?
(?:/(?P<database>.*))?
@@ -182,10 +195,15 @@ def _parse_rfc1738_args(name):
query = None
components['query'] = query
+ if components['username'] is not None:
+ components['username'] = _rfc_1738_unquote(components['username'])
+
if components['password'] is not None:
- components['password'] = \
- util.unquote_plus(components['password'])
+ components['password'] = _rfc_1738_unquote(components['password'])
+ ipv4host = components.pop('ipv4host')
+ ipv6host = components.pop('ipv6host')
+ components['host'] = ipv4host or ipv6host
name = components.pop('name')
return URL(name, **components)
else:
@@ -193,6 +211,12 @@ def _parse_rfc1738_args(name):
"Could not parse rfc1738 URL from string '%s'" % name)
+def _rfc_1738_quote(text):
+ return re.sub(r'[:@/]', lambda m: "%%%X" % ord(m.group(0)), text)
+
+def _rfc_1738_unquote(text):
+ return util.unquote(text)
+
def _parse_keyvalue_args(name):
m = re.match(r'(\w+)://(.*)', name)
if m is not None: