diff options
Diffstat (limited to 'lib/sqlalchemy/engine/url.py')
-rw-r--r-- | lib/sqlalchemy/engine/url.py | 48 |
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: |