Repository: redis/redis-py

Issue:
Support Database URLs
A trend in platform as a service providers is to use Database URLs to supp. They're quickly becoming a standard.

SQLAlchemy and RedisToGo, for example, uses them for database connections.

I'd love to implement this at the redis-py level, if appropriate. 

That would turn this code:

``` python
    import os
    import urlparse
    from redis import Redis

    redis_url = os.getenv('REDISTOGO_URL')

    url = urlparse.urlparse(redis_url)
    r = redis.Redis(
        host=url.hostname, 
        port=url.port, 
        db=0, 
        password=url.password)
```

Into this:

``` python
    import os
    from redis.utils import from_databaseurl

    redis_url = os.getenv('REDISTOGO_URL')
    r = from_databaseurl(redis_url)
```



Discussion:
When 2.4 came out, we talked about doing this with the internal refactoring of how connections worked in redis-py. Ultimately, ended up punting on the idea for several reasons. Most notably, there's a number of options when creating a redis client instance that aren't able to be elegantly represented in a URL, e.g. ConnectionClass and unix_socket_path.

However, I do see the value in what you're proposing, especially for services like Heroku/RedisToGo. Your suggestion of using a factory-like function might make a lot of my initial concern go away.

I agree with your concern, which is why I think it should be a helper function instead of a first-class member of the API.

So, you're :+1: to this? I can send a pull request if you'd like :)

Send away!

@andymccurdy excellent :) 

Do you think the function should exist at the root namespace (e.g. `redis.from_url` or `redis.utils.from_url`)?

Root namespace. It's where you create a client instance the "normal way".


Resolving patch:
diff --git a/redis/__init__.py b/redis/__init__.py
--- a/redis/__init__.py
+++ b/redis/__init__.py
@@ -1,5 +1,6 @@
 # legacy imports
 from redis.client import Redis, ConnectionPool
+from redis.utils import from_url
 from redis.exceptions import (
     AuthenticationError,
     ConnectionError,
@@ -11,10 +12,11 @@
     )
 
 
+
 __version__ = '2.2.4'
 
 __all__ = [
     'Redis', 'ConnectionPool',
     'RedisError', 'ConnectionError', 'ResponseError', 'AuthenticationError',
-    'InvalidResponse', 'DataError', 'PubSubError',
+    'InvalidResponse', 'DataError', 'PubSubError', 'from_url'
     ]
diff --git a/redis/utils.py b/redis/utils.py
new file mode 100644
--- /dev/null
+++ b/redis/utils.py
@@ -0,0 +1,31 @@
+from urlparse import urlparse
+
+from .client import Redis
+
+DEFAULT_DATABASE_ID = 0
+
+def from_url(url, db=None):
+    """Returns an active Redis client generated from the given database URL.
+
+    Will attempt to extract the database id from the path url fragment, if
+    none is provided.
+    """
+
+    url = urlparse(url)
+
+    # Make sure it's a redis database.
+    if url.scheme:
+        assert url.scheme == 'redis'
+
+    # Attempt to resolve database id.
+    if db is None:
+        try:
+            db = int(url.path.replace('/', ''))
+        except (AttributeError, ValueError):
+            db = DEFAULT_DATABASE_ID
+
+    return Redis(
+        host=url.hostname,
+        port=url.port,
+        db=db,
+        password=url.password)
\ No newline at end of file


Test patch: