create_engine doesn't parse dburi's correctly

Issue #157 resolved
Former user created an issue

engine.create_engine doesn't work as advertised

The docstring claims: In the second, the "name" argument is a URL in the form <enginename>://opt1=val1&opt2=val2. Where <enginename> is the name as above, and the contents of the option dictionary are spelled out as a URL encoded string. The "opts" argument is not used.

However, the code doesn't decode the string:

This is a problem for oracle, where:

from cx_Oracle import makedsn

dsn = makedsn( "localhost", 1521, "vis" ) # host, port, sid

# this works ...
opts = {'dsn': dsn, 'user': 'scott', 'password': 'tiger'}
engine1 = create_engine( 'oracle', opts=opts )

# ... and these don't
engine2 = create_engine( 'oracle://' + '&'.join( [ "%s=%s" for opt,val in opts.iteritems() ] ) ) # fails because the regexp being used chokes on the ='s in the dsn
from urllib import urlencode
engine3 = create_engine( 'oracle://' + urlencode( opts ) ) # fails because nothing is decoded

Current (installed from 0.1.4 egg) code:

__all__ = ['engine_descriptors']('create_engine',)

def create_engine(name, opts=None,**kwargs):
    """
    __doc__
    """
    m = re.match(r'(\w+)://(.*)', name)
    if m is not None:
        (name, args) = m.group(1, 2)
        opts = {}
        def assign(m):
            opts[m.group(1)](m.group(1)) = m.group(2)
        re.sub(r'([^&](^&)+)=([^&](^&)*)', assign, args)
    module = getattr(__import__('sqlalchemy.databases.%s' % name).databases, name)
    return module.engine(opts, **kwargs)

The above can be replaced with the python doc-recommended url decoder, cgi.parse_qsl from the standard library. This has the benefit of getting rid of a regexp and avoiding wheel-reinvention. Suggested code:

from cgi import parse_qsl

__all__ = [ 'create_engine', 'engine_descriptors', 'uri_re' ]

# store the compiled regexp, for re-use
uri_re = re.compile( r'(\w+)://(.*)' )
def create_engine(name, opts=None,**kwargs):
    """
    __doc__
    """
    global uri_re
    if not uri_re:
        uri_re = re.compile( r'(\w+)://(.*)' )
    m = uri_re.match( name )
    if m is not None:
        (name, args) = m.group(1, 2)
        opts = dict( parse_qsl( args ) )
    module = getattr(__import__('sqlalchemy.databases.%s' % name).databases, name)
    return module.engine(opts, **kwargs)

Note: I don't have a checkout of the svn repository, but I can generate the Unix patch format diff if necessary (bug accepted).

Comments (3)

  1. Former user Account Deleted

    Sorry, wrong code in the example. It should be:

    engine2 = create_engine( 'oracle://' + '&'.join( [ "%s=%s" % pair for pair in opts.iteritems() ] ) )
    
  2. Mike Bayer repo owner

    didnt try out the test since i dont have an Oracle install handy at the moment, but taking your word for it; checked this in changeset:1282....but keep in mind rfc-1738 style urls is where we're going for SA 0.2....but ill keep this in also

  3. Log in to comment