121

I'm using SQLalchemy for a Python project, and I want to have a tidy connection string to access my database. So for example:

engine = create_engine('postgresql://user:pass@host/database')

The problem is my password contains a sequence of special characters that get interpreted as delimiters when I try to connect.

I realize that I could just use engine.URL.create() and then pass my credentials like this:

import sqlalchemy as sa

connection_url = sa.engine.URL.create(
    drivername="postgresql",
    username="user",
    password="p@ss",
    host="host",
    database="database",
)
print(connection_url)
# postgresql://user:p%40ss@host/database

But I'd much rather use a connection string if this is possible.

So to be clear, is it possible to encode my connection string, or the password part of the connection string - so that it can be properly parsed?

5
  • Can you give an example of this special char that cannot be escaped by a backslash?
    – tuergeist
    Commented Sep 14, 2009 at 20:55
  • missing "=" after "X6~k9?q" in connection info Commented Sep 14, 2009 at 21:11
  • 1
    that happens whether I escape it or not Commented Sep 14, 2009 at 21:12
  • @KeyboardInterrupt were you able to resolve this ? Thanks
    – Tanu
    Commented Dec 5, 2022 at 4:45
  • 3
    "I realize that I could just use engine.URL.create() and then pass my credentials" - Future readers should not gloss over the fact that this really is the preferred method of creating a connection URL. Commented Dec 30, 2022 at 20:40

3 Answers 3

179

You need to URL-encode the password portion of the connect string:

from urllib.parse import quote_plus
from sqlalchemy.engine import create_engine
engine = create_engine("postgres://user:%s@host/database" % quote_plus("p@ss"))

If you look at the implementation of the class used in SQLAlchemy to represent database connection URLs (in sqlalchemy/engine/url.py), you can see that they use the same method to escape passwords when converting the URL instances into strings.

3
  • 1
    The part about using unquote_plus was apparently a bug and changed in v0.9.0 (ref) - Dec 2013. Interestingly, they still quote with quote_plus but unquote with unquote so there may still be some bug lingering in there (src).
    – wim
    Commented Nov 15, 2022 at 22:43
  • quote_plus will cause issues if there are spaces in the password (it will encode a space as + which is a valid character. it should be encoded as %20). better use quote Commented Mar 26 at 14:31
  • Tank you! This literally saved me inordinate amounts of time editing passwords on all our environments.
    – James
    Commented Apr 4 at 9:10
47

In Python 3.x, you need to import urllib.parse.quote:

The urllib module has been split into parts and renamed in Python 3 to urllib.request, urllib.parse, and urllib.error.

When you are trying to connect database MySQL with password which contains sequence of special characters and your python version is Python3

user_name is your userid for database
database is your database name
your_password password with special characters

 from urllib.parse import quote  
 from sqlalchemy.engine import create_engine
 engine = create_engine('mysql+mysqlconnector://user_name:%s@localhost:3306/database' % quote('your_password'))
2
  • 1
    does not work if you have an @ in the password.
    – Tanu
    Commented Dec 5, 2022 at 3:14
  • Tanu, the quoting will change "@" to '%40'. Commented Oct 6, 2023 at 18:19
14

METHOD 1:

The password contains "@", you can escape the "@" character using "%40" instead.

BEFORE:

# mssql+pymssql://username:password@databaseserver/database

mssql+pymssql://admin:[email protected]/dbtest

AFTER:

mssql+pymssql://admin:admin%[email protected]/dbtest

METHOD 2:

Encode the password using urllib.parse.quote_plus.

DATABASE_PASSWORD = "admin@123"

# to elimate the error, if the password contains special characters like '@' 
DATABASE_PASSWORD_UPDATED = urllib.parse.quote_plus(DATABASE_PASSWORD)

Here is the complete code snippet :

import os, sys, click, urllib
from flask import Flask, jsonify, request
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import text

# Make sure to replace below data with your DB values
DATABASE_HOST = "10.10.10.110"
DATABASE_NAME = "dbtest"
DATABASE_USERNAME = "admin" 
DATABASE_PASSWORD = "admin@123"

app = Flask(__name__)

# to elimate the error, if the password contains special characters like '@'
# replace the DATABASE_PASSWORD with DATABASE_PASSWORD_UPDATED. 

DATABASE_PASSWORD_UPDATED = urllib.parse.quote_plus(DATABASE_PASSWORD)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mssql+pymssql://'+DATABASE_USERNAME+':'+DATABASE_PASSWORD_UPDATED+'@'+DATABASE_HOST+'/'+DATABASE_NAME
app.config['SQLALCHEMY_ECHO'] = True


db = SQLAlchemy(app)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000, debug=True)

2
  • 1
    Your answer helped me a lot, but I had to pay a little attention. I believe the password in the example METHOD 1 > BEFORE would be "admin@123". As it is, it seems like the "%40" came from nowhere. Commented May 9 at 14:19
  • %40 saved my life!
    – auciomar
    Commented Jun 25 at 10:59

Not the answer you're looking for? Browse other questions tagged or ask your own question.