Repository: joke2k/django-environ
Pull request #48 (2015-10-15 11:00:36)

URL decode usernames and passwords in urls
Amazon SES credentials have slashes in them sometimes. The `urlparse` function doesn't see the username and password if it does, and the `password` isn't decoded if encoded. This ensures it is.



Patch:
diff --git a/.coveragerc b/.coveragerc
deleted file mode 100644
index 6db2067..0000000
--- a/.coveragerc
+++ /dev/null
@@ -1,3 +0,0 @@
-[paths]
-source = environ/
-omit = environ/test.py
diff --git a/.travis.yml b/.travis.yml
index 6b99c26..10d1e14 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,22 +1,17 @@
 language: python
-sudo: false
 python:
  - "2.6"
  - "2.7"
  - "3.3"
- - "3.4"
- - "3.5"
- - "nightly"
 env:
   matrix:
    - DJANGO=Django==1.4
    - DJANGO=Django==1.5
    - DJANGO=Django==1.6
    - DJANGO=Django==1.7
-   - DJANGO=Django==1.8
 install:
- - pip install $DJANGO
- - pip install .
+ - pip install $DJANGO --use-mirrors
+ - pip install . --use-mirrors
  - pip install coverage
 branches:
  only:
@@ -25,22 +20,8 @@ branches:
 script: coverage run --source=environ setup.py test
 matrix:
   exclude:
-    - python: "2.6"
-      env: DJANGO=Django==1.7
-    - python: "2.6"
-      env: DJANGO=Django==1.8
     - python: "3.3"
       env: DJANGO=Django==1.4
-    - python: "3.3"
-      env: DJANGO=Django==1.5
-    - python: "3.4"
-      env: DJANGO=Django==1.4
-    - python: "3.4"
-      env: DJANGO=Django==1.5
-    - python: "3.5"
-      env: DJANGO=Django==1.4
-    - python: "3.5"
-      env: DJANGO=Django==1.5
 after_success:
   - coverage report
   - pip install --quiet python-coveralls
diff --git a/LICENSE.txt b/LICENSE.txt
index 5901005..80d813f 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,4 +1,4 @@
-Copyright (c) 2013-2015, Daniele Faraglia
+Copyright (c) 2013 Daniele Faraglia
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/README.rst b/README.rst
index 151f8d7..0fc900a 100644
--- a/README.rst
+++ b/README.rst
@@ -1,11 +1,18 @@
-==============
 Django-environ
 ==============
 
 Django-environ allows you to utilize 12factor inspired environment variables to configure your Django application.
 
-|pypi| |unix_build| |windows_build| |coverage| |downloads| |license|
-
+.. image:: https://travis-ci.org/joke2k/django-environ.svg?branch=master
+  :target: https://travis-ci.org/joke2k/django-environ
+.. image:: https://ci.appveyor.com/api/projects/status/o6p7l2o0xoatqcin
+  :target: https://ci.appveyor.com/project/joke2k/django-environ
+.. image:: https://coveralls.io/repos/joke2k/django-environ/badge.png?branch=master
+  :target: https://coveralls.io/r/joke2k/django-environ?branch=master
+.. image:: https://badge.fury.io/py/django-environ.png
+  :target: http://badge.fury.io/py/django-environ
+.. image:: https://pypip.in/d/django-environ/badge.png
+  :target: https://crate.io/packages/django-environ
 
 This module is a merge of:
 
@@ -62,10 +69,10 @@ This is your `settings.py` file before you have installed **django-environ**
             ]
         },
         'redis': {
-            'BACKEND': 'django_redis.cache.RedisCache',
+            'BACKEND': 'redis_cache.cache.RedisCache',
             'LOCATION': '127.0.0.1:6379:1',
             'OPTIONS': {
-                'CLIENT_CLASS': 'django_redis.client.DefaultClient',
+                'CLIENT_CLASS': 'redis_cache.client.DefaultClient',
                 'PASSWORD': 'redis-githubbed-password',
             }
         }
@@ -111,11 +118,11 @@ Create a `.env` file::
     DATABASE_URL=psql://urser:un-githubbedpassword@127.0.0.1:8458/database
     # SQLITE_URL=sqlite:///my-local-sqlite.db
     CACHE_URL=memcache://127.0.0.1:11211,127.0.0.1:11212,127.0.0.1:11213
-    REDIS_URL=rediscache://127.0.0.1:6379:1?client_class=django_redis.client.DefaultClient&password=redis-un-githubbed-password
+    REDIS_URL=rediscache://127.0.0.1:6379:1?client_class=redis_cache.client.DefaultClient&password=redis-un-githubbed-password
 
 
 How to install
-==============
+--------------
 
 ::
 
@@ -123,9 +130,9 @@ How to install
 
 
 How to use
-==========
+----------
 
-There are only two classes, `environ.Env` and `environ.Path`
+There are only classes, Env and Path
 
 ::
 
@@ -158,7 +165,7 @@ There are only two classes, `environ.Env` and `environ.Path`
 
 
 Supported Types
-===============
+---------------
 
 - str
 - bool
@@ -166,7 +173,6 @@ Supported Types
 - float
 - json
 - list (FOO=a,b,c)
-- tuple (FOO=(a,b,c))
 - dict (BAR=key=val,foo=bar)
 - url
 - path (environ.Path)
@@ -190,19 +196,17 @@ Supported Types
     - ElasticSearch: elasticsearch://
     - Solr: solr://
     - Whoosh: whoosh://
-    - Xapian: xapian://
     - Simple cache: simple://
 - email_url
     - SMTP: smtp://
-    - SMTP+SSL: smtp+ssl://
-    - SMTP+TLS: smtp+tls://
+    - SMTPS: smtps://
     - Console mail: consolemail://
     - File mail: filemail://
     - LocMem mail: memorymail://
     - Dummy mail: dummymail://
 
 Tests
-=====
+-----
 
 ::
 
@@ -211,46 +215,37 @@ Tests
     $ python setup.py test
 
 
-License
-=======
+Changelog
+---------
 
-Django-environ is licensed under the MIT License - see the `LICENSE`_ file for details
+=== 0.3.1 (2014-09-03)
+  * Add LDAP url support for database (django-ldapdb)
+  * Fix psql/pgsql url
 
-Changelog
-=========
-
-`0.4.0 - 23-September-2015 <http://github.com/joke2k/django-environ/compare/v0.3...v0.4>`__
--------------------------------------------------------------------------------------------
-  - Fix non-ascii values (broken in Python 2.x)
-  - New email schemes - smtp+ssl and smtp+tls (smtps would be deprecated)
-  - redis_cache replaced by django_redis
-  - Add tuple support. Thanks to @anonymouzz
-  - Add LDAP url support for database (django-ldapdb)
-  - Fix psql/pgsql url
-
-`0.3 - 03-June-2014 <http://github.com/joke2k/django-environ/compare/v0.2.1...v0.3>`__
---------------------------------------------------------------------------------------
-  - Add cache url support
-  - Add email url support
-  - Add search url support
-  - Rewriting README.rst
-
-0.2.1 19-April-2013
--------------------
-  - environ/environ.py: Env.__call__ now uses Env.get_value instance method
-
-0.2 16-April-2013
------------------
-  - environ/environ.py, environ/test.py, environ/test_env.txt: add advanced
+=== 0.3 (2014-06-03) ===
+
+  * Add cache url support
+  * Add email url support
+  * Add search url support
+  * Rewriting README.rst
+
+=== 0.2.1 (2013-04-19) ===
+
+  * environ/environ.py: Env.__call__ now uses Env.get_value instance method
+
+=== 0.2 (2013-04-16) ===
+
+  * environ/environ.py, environ/test.py, environ/test_env.txt: add advanced
     float parsing (comma and dot symbols to separate thousands and decimals)
-  - README.rst, docs/index.rst: fix TYPO in documentation
 
-0.1 02-April-2013
------------------
-  - initial release
+  * README.rst, docs/index.rst: fix TYPO in documentation
+
+=== 0.1 (2013-04-02) ===
+
+  * initial release
 
 Credits
-=======
+-------
 
 - `12factor`_
 - `12factor-django`_
@@ -293,29 +288,3 @@ Credits
 
 .. _Distribute: http://pypi.python.org/pypi/distribute
 .. _`modern-package-template`: http://pypi.python.org/pypi/modern-package-template
-
-.. |pypi| image:: https://img.shields.io/pypi/v/django-environ.svg?style=flat-square&label=version
-    :target: https://pypi.python.org/pypi/django-environ
-    :alt: Latest version released on PyPi
-
-.. |coverage| image:: https://img.shields.io/coveralls/joke2k/django-environ/master.svg?style=flat-square
-    :target: https://coveralls.io/r/joke2k/django-environ?branch=master
-    :alt: Test coverage
-
-.. |unix_build| image:: https://img.shields.io/travis/joke2k/django-environ/master.svg?style=flat-square&label=unix%20build
-    :target: http://travis-ci.org/joke2k/django-environ
-    :alt: Build status of the master branch on Mac/Linux
-
-.. |windows_build|  image:: https://img.shields.io/appveyor/ci/joke2k/django-environ.svg?style=flat-square&label=windows%20build
-    :target: https://ci.appveyor.com/project/joke2k/django-environ
-    :alt: Build status of the master branch on Windows
-
-.. |downloads| image:: https://img.shields.io/pypi/dm/django-environ.svg?style=flat-square
-    :target: https://pypi.python.org/pypi/django-environ
-    :alt: Monthly downloads
-
-.. |license| image:: https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square
-    :target: https://raw.githubusercontent.com/joke2k/django-environ/master/LICENSE.txt
-    :alt: Package license
-
-.. _LICENSE: https://github.com/joke2k/django-environ/blob/master/LICENSE.txt
diff --git a/docs/conf.py b/docs/conf.py
index 6298bf5..1da5784 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -18,6 +18,7 @@ import os
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use os.path.abspath to make it absolute, like shown here.
 sys.path.insert(0, os.path.dirname(os.path.abspath('.')))
+
 # -- General configuration -----------------------------------------------------
 
 # If your documentation needs a minimal Sphinx version, state it here.
@@ -41,16 +42,16 @@ master_doc = 'index'
 
 # General information about the project.
 project = u'Django-environ'
-copyright = u'2013-2015, Daniele Faraglia and other contributors'
+copyright = u'2013, joke2k'
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
 #
 # The short X.Y version.
-version = '0.4'
+version = '0.1'
 # The full version, including alpha/beta/rc tags.
-release = '0.4.0'
+release = '0.1'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
@@ -228,8 +229,7 @@ man_pages = [
 #  dir menu entry, description, category)
 texinfo_documents = [
   ('index', 'Django-environ', u'Django-environ Documentation',
-   u'joke2k', 'Django-environ', "Django-environ allows you to utilize 12factor inspired environment "
-                                "variables to configure your Django application.",
+   u'joke2k', 'Django-environ', 'One line description of project.',
    'Miscellaneous'),
 ]
 
diff --git a/docs/index.rst b/docs/index.rst
index 9d113eb..6ea7cd6 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -1,12 +1,211 @@
 Welcome to Django-environ's documentation!
 ==========================================
 
-.. automodule:: environ.environ
+..  automodule:: environ.environ
+
+This module is a merge of:
+
+* `envparse`_
+* `honcho`_
+* `dj-database-url`_
+* `dj-search-url`_
+* `dj-config-url`_
+* `django-cache-url`_
+
+and inspired by:
+
+* http://www.12factor.net/
+* http://www.wellfireinteractive.com/blog/easier-12-factor-django/
+* https://django.2scoops.org (book)
+
+This is your `settings.py` file before you have installed **django-environ**
+
+::
+
+    import os
+    SITE_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
+
+    DEBUG = True
+    TEMPLATE_DEBUG = DEBUG
+
+    DATABASES = {
+        'default': {
+            'ENGINE': 'django.db.backends.postgresql_psycopg2',
+            'NAME': 'database',
+            'USER': 'user',
+            'PASSWORD': 'githubbedpassword',
+            'HOST': '127.0.0.1',
+            'PORT': '8458',
+        }
+        'extra': {
+            'ENGINE': 'django.db.backends.sqlite3',
+            'NAME': os.path.join(SITE_ROOT, 'database.sqlite')
+        }
+    }
+
+    MEDIA_ROOT = os.path.join(SITE_ROOT, 'assets')
+    MEDIA_URL = 'media/'
+    STATIC_ROOT = os.path.join(SITE_ROOT, 'static')
+    STATIC_URL = 'static/'
+
+    SECRET_KEY = '...im incredibly still here...'
+
+    CACHES = {
+        'default': {
+            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
+            'LOCATION': [
+                '127.0.0.1:11211', '127.0.0.1:11212', '127.0.0.1:11213',
+            ]
+        },
+        'redis': {
+            'BACKEND': 'redis_cache.cache.RedisCache',
+            'LOCATION': '127.0.0.1:6379:1',
+            'OPTIONS': {
+                'CLIENT_CLASS': 'redis_cache.client.DefaultClient',
+                'PASSWORD': 'redis-githubbed-password',
+            }
+        }
+    }
+
+After::
+
+    import environ
+    root = environ.Path(__file__) - 3 # three folder back (/a/b/c/ - 3 = /)
+    env = environ.Env(DEBUG=(bool, False),) # set default values and casting
+    environ.Env.read_env() # reading .env file
+
+    SITE_ROOT = root()
+
+    DEBUG = env('DEBUG') # False if not in os.environ
+    TEMPLATE_DEBUG = DEBUG
+
+    DATABASES = {
+        'default': env.db(), # Raises ImproperlyConfigured exception if DATABASE_URL not in os.environ
+        'extra': env.db('SQLITE_URL', default='sqlite:////tmp/my-tmp-sqlite.db')
+    }
+
+    public_root = root.path('public/')
+
+    MEDIA_ROOT = public_root('media')
+    MEDIA_URL = 'media/'
+    STATIC_ROOT = public_root('static')
+    STATIC_URL = 'static/'
+
+    SECRET_KEY = env('SECRET_KEY') # Raises ImproperlyConfigured exception if SECRET_KEY not in os.environ
+
+    CACHES = {
+        'default': env.cache(),
+        'redis': env.cache('REDIS_URL')
+    }
+
+You can also pass `read_env()` an explicit path to the .env file.
+Create a `.env` file::
+
+    DEBUG=on
+    # DJANGO_SETTINGS_MODULE=myapp.settings.dev
+    SECRET_KEY=your-secret-key
+    DATABASE_URL=psql://urser:un-githubbedpassword@127.0.0.1:8458/database
+    # SQLITE_URL=sqlite:///my-local-sqlite.db
+    CACHE_URL=memcache://127.0.0.1:11211,127.0.0.1:11212,127.0.0.1:11213
+    REDIS_URL=rediscache://127.0.0.1:6379:1?client_class=redis_cache.client.DefaultClient&password=redis-un-githubbed-password
+
+
+How to install
+--------------
+
+::
+
+    $ pip install django-environ
+
+
+How to use
+----------
+
+There are only classes, Env and Path
+
+::
+
+    >>> import environ
+    >>> env = environ.Env(
+            DEBUG=(bool, False),
+        )
+    >>> env('DEBUG')
+    False
+    >>> env('DEBUG', default=True)
+    True
+
+    >>> open('.myenv', 'a').write('DEBUG=on')
+    >>> environ.Env.read_env('.`myenv') # or env.read_env('.myenv')
+    >>> env('DEBUG')
+    True
+
+    >>> open('.myenv', 'a').write('\nINT_VAR=1010')
+    >>> env.int('INT_VAR'), env.str('INT_VAR')
+    1010, '1010'
+
+    >>> open('.myenv', 'a').write('\nDATABASE_URL=sqlite:///my-local-sqlite.db')
+    >>> env.read_env('.myenv')
+    >>> env.db()
+    {'ENGINE': 'django.db.backends.sqlite3', 'NAME': 'my-local-sqlite.db', 'HOST': '', 'USER': '', 'PASSWORD': '', 'PORT': ''}
+
+    >>> root = env.path('/home/myproject/')
+    >>> root('static')
+    '/home/myproject/static'
+
+
+Supported Types
+---------------
+
+- str
+- bool
+- int
+- float
+- json
+- list (FOO=a,b,c)
+- dict (BAR=key=val;foo=bar)
+- url
+- path (environ.Path)
+- db_url
+    -  PostgreSQL: postgres://, pgsql:// or postgresql://
+    -  PostGIS: postgis://
+    -  MySQL: mysql:// or mysql2://
+    -  MySQL for GeoDjango: mysqlgis://
+    -  SQLITE: sqlite://
+    -  SQLITE with SPATIALITE for GeoDjango: spatialite://
+
+- cache_url
+    -  Database: dbcache://
+    -  Dummy: dummycache://
+    -  File: filecache://
+    -  Memory: locmemcache://
+    -  Memcached: memcache://
+    -  Python memory: pymemcache://
+    -  Redis: rediscache://
+- search_url
+    - ElasticSearch: elasticsearch://
+    - Solr: solr://
+    - Whoosh: whoosh://
+    - Simple cache: simple://
+- email_url
+    - SMTP: smtp://
+    - SMTPS: smtps://
+    - Console mail: consolemail://
+    - File mail: filemail://
+    - LocMem mail: memorymail://
+    - Dummy mail: dummymail://
+
+Tests
+-----
+
+::
+
+    $ git clone git@github.com:joke2k/django-environ.git
+    $ cd django-environ/
+    $ python setup.py test
 
-.. include:: ../README.rst
 
 environ.Env
-===========
+-----------
 
 ..  autoclass:: environ.environ.Env
 
@@ -45,7 +244,7 @@ environ.Env
 
 
 environ.Path
-============
+------------
 
 ..  autoclass:: environ.environ.Path
 
@@ -54,6 +253,78 @@ environ.Path
     ..  automethod:: path
     ..  automethod:: file
 
+Changelog
+---------
+
+=== 0.3 (2014-06-03) ===
+
+  * Add cache url support
+  * Add email url support
+  * Add search url support
+  * Rewriting README.rst
+
+
+=== 0.2.1 (2013-04-19) ===
+
+  * environ/environ.py: Env.__call__ now uses Env.get_value instance method
+
+=== 0.2 (2013-04-16) ===
+
+  * environ/environ.py, environ/test.py, environ/test_env.txt: add advanced
+    float parsing (comma and dot symbols to separate thousands and decimals)
+
+  * README.rst, docs/index.rst: fix TYPO in documentation
+
+=== 0.1 (2013-04-02) ===
+
+  * initial release
+
+Credits
+-------
+
+- `12factor`_
+- `12factor-django`_
+- `Two Scoops of Django`_
+- `rconradharris`_ / `envparse`_
+- `kennethreitz`_ / `dj-database-url`_
+- `migonzalvar`_ / `dj-email-url`_
+- `ghickman`_ / `django-cache-url`_
+- `dstufft`_ / `dj-search-url`_
+- `julianwachholz`_ / `dj-config-url`_
+- `nickstenning`_ / `honcho`_
+- `envparse`_
+- `Distribute`_
+- `modern-package-template`_
+
+.. _rconradharris: https://github.com/rconradharris
+.. _envparse: https://github.com/rconradharris/envparse
+
+.. _kennethreitz: https://github.com/kennethreitz
+.. _dj-database-url: https://github.com/kennethreitz/dj-database-url
+
+.. _migonzalvar: https://github.com/migonzalvar
+.. _dj-email-url: https://github.com/migonzalvar/dj-email-url
+
+.. _ghickman: https://github.com/ghickman
+.. _django-cache-url: https://github.com/ghickman/django-cache-url
+
+.. _julianwachholz: https://github.com/julianwachholz
+.. _dj-config-url: https://github.com/julianwachholz/dj-config-url
+
+.. _dstufft: https://github.com/dstufft
+.. _dj-search-url: https://github.com/dstufft/dj-search-url
+
+.. _nickstenning: https://github.com/nickstenning
+.. _honcho: https://github.com/nickstenning/honcho
+
+.. _12factor: http://www.12factor.net/
+.. _12factor-django: http://www.wellfireinteractive.com/blog/easier-12-factor-django/
+.. _`Two Scoops of Django`: https://django.2scoops.org (book)
+
+
+.. _Distribute: http://pypi.python.org/pypi/distribute
+.. _`modern-package-template`: http://pypi.python.org/pypi/modern-package-template
+
 
 Indices and tables
 ==================
diff --git a/environ/environ.py b/environ/environ.py
index ca07f93..8a67490 100644
--- a/environ/environ.py
+++ b/environ/environ.py
@@ -2,45 +2,56 @@
 Django-environ allows you to utilize 12factor inspired environment
 variables to configure your Django application.
 """
-import json
-import logging
+from __future__ import unicode_literals
 import os
-import re
 import sys
+import re
+import json
 import warnings
 
+import logging
+
+logger = logging.getLogger(__file__)
+
 try:
     from django.core.exceptions import ImproperlyConfigured
 except ImportError:
     class ImproperlyConfigured(Exception):
         pass
 
-from six.moves import urllib_parse as urlparse
-from six import string_types
+try:
+    import urllib.parse as urlparse
+except ImportError:
+    # Python <= 2.6
+    import urlparse
+import urllib
 
 
-logger = logging.getLogger(__file__)
+if sys.version < '3':
+    text_type = unicode
+else:
+    text_type = str
+    basestring = str
 
 
-VERSION = '0.4.0'
 __author__ = 'joke2k'
-__version__ = tuple(VERSION.split('.'))
+__version__ = (0, 3, 1)
 
 
 # return int if possible
-def _cast_int(v):
-    return int(v) if hasattr(v, 'isdigit') and v.isdigit() else v
-
+_cast_int = lambda v: int(v) if isinstance(v, basestring) and v.isdigit() else v
+# return str if possibile
+_cast_str = lambda v: str(v) if isinstance(v, basestring) else v
+# return str if possible, and possibly urlencoded
+_cast_urlstr = lambda v: urllib.unquote_plus(str(v)) if isinstance(v, basestring) else v
 
 class NoValue(object):
-
     def __repr__(self):
         return '<{0}>'.format(self.__class__.__name__)
 
 
 class Env(object):
-
-    """Provide scheme-based lookups of environment variables so that each
+    """Provide schema-based lookups of environment variables so that each
     caller doesn't have to pass in `cast` and `default` parameters.
 
     Usage:::
@@ -77,8 +88,8 @@ class Env(object):
         'locmemcache': 'django.core.cache.backends.locmem.LocMemCache',
         'memcache': 'django.core.cache.backends.memcached.MemcachedCache',
         'pymemcache': 'django.core.cache.backends.memcached.PyLibMCCache',
-        'rediscache': 'django_redis.cache.RedisCache',
-        'redis': 'django_redis.cache.RedisCache',
+        'rediscache': 'redis_cache.cache.RedisCache',
+        'redis': 'redis_cache.cache.RedisCache',
     }
     _CACHE_BASE_OPTIONS = ['TIMEOUT', 'KEY_PREFIX', 'VERSION', 'KEY_FUNCTION']
 
@@ -86,26 +97,25 @@ class Env(object):
     EMAIL_SCHEMES = {
         'smtp': 'django.core.mail.backends.smtp.EmailBackend',
         'smtps': 'django.core.mail.backends.smtp.EmailBackend',
-        'smtp+tls': 'django.core.mail.backends.smtp.EmailBackend',
-        'smtp+ssl': 'django.core.mail.backends.smtp.EmailBackend',
         'consolemail': 'django.core.mail.backends.console.EmailBackend',
         'filemail': 'django.core.mail.backends.filebased.EmailBackend',
         'memorymail': 'django.core.mail.backends.locmem.EmailBackend',
         'dummymail': 'django.core.mail.backends.dummy.EmailBackend'
     }
-    _EMAIL_BASE_OPTIONS = ['EMAIL_USE_TLS', 'EMAIL_USE_SSL']
+    _EMAIL_BASE_OPTIONS = ['EMAIL_USE_TLS', ]
 
     DEFAULT_SEARCH_ENV = 'SEARCH_URL'
     SEARCH_SCHEMES = {
         "elasticsearch": "haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine",
         "solr": "haystack.backends.solr_backend.SolrEngine",
         "whoosh": "haystack.backends.whoosh_backend.WhooshEngine",
-        "xapian": "haystack.backends.xapian_backend.XapianEngine",
         "simple": "haystack.backends.simple_backend.SimpleEngine",
     }
 
-    def __init__(self, **scheme):
-        self.scheme = scheme
+
+
+    def __init__(self, **schema):
+        self.schema = schema
 
     def __call__(self, var, cast=None, default=NOTSET, parse_default=False):
         return self.get_value(var, cast=cast, default=default, parse_default=parse_default)
@@ -122,7 +132,7 @@ class Env(object):
         """Helper for python2
         :rtype: unicode
         """
-        return self.get_value(var, cast=str, default=default)
+        return self.get_value(var, cast=text_type, default=default)
 
     def bool(self, var, default=NOTSET):
         """
@@ -154,12 +164,6 @@ class Env(object):
         """
         return self.get_value(var, cast=list if not cast else [cast], default=default)
 
-    def tuple(self, var, cast=None, default=NOTSET):
-        """
-        :rtype: tuple
-        """
-        return self.get_value(var, cast=tuple if not cast else (cast,), default=default)
-
     def dict(self, var, cast=dict, default=NOTSET):
         """
         :rtype: dict
@@ -178,7 +182,7 @@ class Env(object):
         :rtype: dict
         """
         return self.db_url_config(self.get_value(var, default=default), engine=engine)
-    db = db_url
+    db=db_url
 
     def cache_url(self, var=DEFAULT_CACHE_ENV, default=NOTSET, backend=None):
         """Returns a config dictionary, defaulting to CACHE_URL.
@@ -186,7 +190,7 @@ class Env(object):
         :rtype: dict
         """
         return self.cache_url_config(self.url(var, default=default), backend=backend)
-    cache = cache_url
+    cache=cache_url
 
     def email_url(self, var=DEFAULT_EMAIL_ENV, default=NOTSET, backend=None):
         """Returns a config dictionary, defaulting to EMAIL_URL.
@@ -194,7 +198,7 @@ class Env(object):
         :rtype: dict
         """
         return self.email_url_config(self.url(var, default=default), backend=backend)
-    email = email_url
+    email=email_url
 
     def search_url(self, var=DEFAULT_SEARCH_ENV, default=NOTSET, engine=None):
         """Returns a config dictionary, defaulting to SEARCH_URL.
@@ -220,12 +224,10 @@ class Env(object):
         :returns: Value from environment or default (if set)
         """
 
-        logger.debug("get '{0}' casted as '{1}' with default '{2}'".format(
-            var, cast, default
-        ))
+        logger.debug("get '{0}' casted as '{1}' with default '{2}'".format(var, cast, default))
 
-        if var in self.scheme:
-            var_info = self.scheme[var]
+        if var in self.schema:
+            var_info = self.schema[var]
 
             try:
                 has_default = len(var_info) == 2
@@ -259,7 +261,7 @@ class Env(object):
             value = value.lstrip('$')
             value = self.get_value(value, cast=cast, default=default)
 
-        if value != default or (parse_default and value):
+        if value != default or parse_default:
             value = self.parse_value(value, cast)
 
         return value
@@ -284,27 +286,19 @@ class Env(object):
                 value = value.lower() in cls.BOOLEAN_TRUE_STRINGS
         elif isinstance(cast, list):
             value = list(map(cast[0], [x for x in value.split(',') if x]))
-        elif isinstance(cast, tuple):
-            val = value.strip('(').strip(')').split(',')
-            value = tuple(map(cast[0], [x for x in val if x]))
         elif isinstance(cast, dict):
             key_cast = cast.get('key', str)
-            value_cast = cast.get('value', str)
+            value_cast = cast.get('value', text_type)
             value_cast_by_key = cast.get('cast', dict())
             value = dict(map(
-                lambda kv: (
-                    key_cast(kv[0]),
-                    cls.parse_value(kv[1], value_cast_by_key.get(kv[0], value_cast))
-                ),
+                lambda kv: (key_cast(kv[0]), cls.parse_value(kv[1], value_cast_by_key.get(kv[0], value_cast))),
                 [val.split('=') for val in value.split(';') if val]
             ))
         elif cast is dict:
+        #elif hasattr(cast, '__name__') and cast.__name__ == 'dict':
             value = dict([val.split('=') for val in value.split(',') if val])
         elif cast is list:
             value = [x for x in value.split(',') if x]
-        elif cast is tuple:
-            val = value.strip('(').strip(')').split(',')
-            value = tuple([x for x in val if x])
         elif cast is float:
             # clean string
             float_str = re.sub(r'[^\d,\.]', '', value)
@@ -358,16 +352,16 @@ class Env(object):
         if url.scheme == 'sqlite' and path == '':
             path = ':memory:'
         if url.scheme == 'ldap':
-            path = '{scheme}://{hostname}'.format(scheme=url.scheme, hostname=url.hostname)
+            path = '{scheme}://{hostname}'.format(scheme=_cast_str(url.scheme), hostname=_cast_str(url.hostname))
             if url.port:
-                path += ':{port}'.format(port=url.port)
+                path += ':{port}'.format(port=_cast_str(url.port))
 
         # Update with environment configuration.
         config.update({
             'NAME': path,
-            'USER': url.username,
-            'PASSWORD': url.password,
-            'HOST': url.hostname,
+            'USER': _cast_urlstr(url.username),
+            'PASSWORD': _cast_urlstr(url.password),
+            'HOST': _cast_str(url.hostname),
             'PORT': _cast_int(url.port),
         })
 
@@ -396,7 +390,7 @@ class Env(object):
         """Pulled from DJ-Cache-URL, parse an arbitrary Cache URL.
 
         :param url:
-        :param backend:
+        :param overrides:
         :return:
         """
         url = urlparse.urlparse(url) if not isinstance(url, cls.URL_CLASS) else url
@@ -412,7 +406,7 @@ class Env(object):
 
         if url.scheme == 'filecache':
             config.update({
-                'LOCATION': url.netloc + url.path,
+                'LOCATION': _cast_str(url.netloc + url.path),
             })
 
         if url.path and url.scheme in ['memcache', 'pymemcache', 'rediscache']:
@@ -450,23 +444,21 @@ class Env(object):
         # Update with environment configuration
         config.update({
             'EMAIL_FILE_PATH': path,
-            'EMAIL_HOST_USER': url.username,
-            'EMAIL_HOST_PASSWORD': url.password,
-            'EMAIL_HOST': url.hostname,
+            'EMAIL_HOST_USER': _cast_urlstr(url.username),
+            'EMAIL_HOST_PASSWORD': _cast_urlstr(url.password),
+            'EMAIL_HOST': _cast_str(url.hostname),
             'EMAIL_PORT': _cast_int(url.port),
         })
 
         if backend:
             config['EMAIL_BACKEND'] = backend
-        elif url.scheme not in cls.EMAIL_SCHEMES:
-            raise ImproperlyConfigured('Invalid email schema %s' % url.scheme)
         elif url.scheme in cls.EMAIL_SCHEMES:
             config['EMAIL_BACKEND'] = cls.EMAIL_SCHEMES[url.scheme]
 
-        if url.scheme in ('smtps', 'smtp+tls'):
+        if url.scheme == 'smtps':
             config['EMAIL_USE_TLS'] = True
-        elif url.scheme == 'smtp+ssl':
-            config['EMAIL_USE_SSL'] = True
+        else:
+            config['EMAIL_USE_TLS'] = False
 
         if url.query:
             config_options = {}
@@ -490,64 +482,30 @@ class Env(object):
         path = url.path[1:]
         path = path.split('?', 2)[0]
 
-        if url.scheme not in cls.SEARCH_SCHEMES:
-            raise ImproperlyConfigured('Invalid search schema %s' % url.scheme)
-        config["ENGINE"] = cls.SEARCH_SCHEMES[url.scheme]
+        if url.scheme in cls.SEARCH_SCHEMES:
+            config["ENGINE"] = cls.SEARCH_SCHEMES[url.scheme]
 
-        # check commons params
-        params = {}
-        if url.query:
-            params = urlparse.parse_qs(url.query)
-            if 'EXCLUDED_INDEXES' in params.keys():
-                config['EXCLUDED_INDEXES'] = params['EXCLUDED_INDEXES'][0].split(',')
-            if 'INCLUDE_SPELLING' in params.keys():
-                config['INCLUDE_SPELLING'] = cls.parse_value(params['INCLUDE_SPELLING'][0], bool)
-            if 'BATCH_SIZE' in params.keys():
-                config['BATCH_SIZE'] = cls.parse_value(params['BATCH_SIZE'][0], int)
-
-        if url.scheme == 'simple':
-            return config
-        elif url.scheme in ['solr', 'elasticsearch']:
-            if 'KWARGS' in params.keys():
-                config['KWARGS'] = params['KWARGS'][0]
-
-        # remove trailing slash
         if path.endswith("/"):
             path = path[:-1]
 
-        if url.scheme == 'solr':
-            config['URL'] = urlparse.urlunparse(('http',) + url[1:2] + (path,) + ('', '', ''))
-            if 'TIMEOUT' in params.keys():
-                config['TIMEOUT'] = cls.parse_value(params['TIMEOUT'][0], int)
-            return config
+        split = path.rsplit("/", 1)
 
-        if url.scheme == 'elasticsearch':
+        if len(split) > 1:
+            path = split[:-1]
+            index = split[-1]
+        else:
+            path = ""
+            index = split[0]
 
-            split = path.rsplit("/", 1)
+        config.update({
+            "URL": urlparse.urlunparse(("http",) + url[1:2] + (path,) + url[3:]),
+            "INDEX_NAME": index,
+            })
 
-            if len(split) > 1:
-                path = "/".join(split[:-1])
-                index = split[-1]
-            else:
-                path = ""
-                index = split[0]
-
-            config['URL'] = urlparse.urlunparse(('http',) + url[1:2] + (path,) + ('', '', ''))
-            if 'TIMEOUT' in params.keys():
-                config['TIMEOUT'] = cls.parse_value(params['TIMEOUT'][0], int)
-            config['INDEX_NAME'] = index
-            return config
-
-        config['PATH'] = '/' + path
-
-        if url.scheme == 'whoosh':
-            if 'STORAGE' in params.keys():
-                config['STORAGE'] = params['STORAGE'][0]
-            if 'POST_LIMIT' in params.keys():
-                config['POST_LIMIT'] = cls.parse_value(params['POST_LIMIT'][0], int)
-        elif url.scheme == 'xapian':
-            if 'FLAGS' in params.keys():
-                config['FLAGS'] = params['FLAGS'][0]
+        if path:
+            config.update({
+                "PATH": path,
+            })
 
         if engine:
             config['ENGINE'] = engine
@@ -573,7 +531,7 @@ class Env(object):
                 return
 
         try:
-            with open(env_file) if isinstance(env_file, string_types) else env_file as f:
+            with open(env_file) if isinstance(env_file, basestring) else env_file as f:
                 content = f.read()
         except IOError:
             warnings.warn("not reading %s - it doesn't exist." % env_file)
@@ -591,7 +549,7 @@ class Env(object):
                 m3 = re.match(r'\A"(.*)"\Z', val)
                 if m3:
                     val = re.sub(r'\\(.)', r'\1', m3.group(1))
-                os.environ.setdefault(key, str(val))
+                os.environ.setdefault(key, text_type(val))
 
         # set defaults
         for key, value in overrides.items():
@@ -599,7 +557,6 @@ class Env(object):
 
 
 class Path(object):
-
     """Inspired to Django Two-scoops, handling File Paths in Settings.
 
         >>> from environ import Path
@@ -678,10 +635,9 @@ class Path(object):
     def __sub__(self, other):
         if isinstance(other, int):
             return self.path('../' * other)
-        elif isinstance(other, string_types):
+        elif isinstance(other, (str, text_type)):
             return Path(self.__root__.rstrip(other))
-        raise TypeError(
-            "unsupported operand type(s) for -: '{0}' and '{1}'".format(self, type(other)))
+        raise TypeError("unsupported operand type(s) for -: '{0}' and '{1}'".format(self, type(other)))
 
     def __invert__(self):
         return self.path('..')
@@ -701,37 +657,17 @@ class Path(object):
     def __unicode__(self):
         return self.__str__()
 
-    def __getitem__(self, *args, **kwargs):
-        return self.__str__().__getitem__(*args, **kwargs)
-
-    def rfind(self, *args, **kwargs):
-        return self.__str__().rfind(*args, **kwargs)
-
-    def find(self, *args, **kwargs):
-        return self.__str__().find(*args, **kwargs)
-
     @staticmethod
     def _absolute_join(base, *paths, **kwargs):
         absolute_path = os.path.abspath(os.path.join(base, *paths))
         if kwargs.get('required', False) and not os.path.exists(absolute_path):
-            raise ImproperlyConfigured(
-                "Create required path: {0}".format(absolute_path))
+            raise ImproperlyConfigured("Create required path: {0}".format(absolute_path))
         return absolute_path
 
-
 def register_scheme(scheme):
-    for method in dir(urlparse):
-        if method.startswith('uses_'):
-            getattr(urlparse, method).append(scheme)
-
-
-def register_schemes(schemes):
-    for scheme in schemes:
-        register_scheme(scheme)
-
+    for method in filter(lambda s: s.startswith('uses_'), dir(urlparse)):
+        getattr(urlparse, method).append(scheme)
 
 # Register database and cache schemes in URLs.
-register_schemes(Env.DB_SCHEMES.keys())
-register_schemes(Env.CACHE_SCHEMES.keys())
-register_schemes(Env.SEARCH_SCHEMES.keys())
-register_schemes(Env.EMAIL_SCHEMES.keys())
+for schema in list(Env.DB_SCHEMES.keys()) + list(Env.CACHE_SCHEMES.keys()) + list(Env.SEARCH_SCHEMES.keys()) +list(Env.EMAIL_SCHEMES.keys()):
+    register_scheme(schema)
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 9026215..0000000
--- a/setup.cfg
+++ /dev/null
@@ -1,8 +0,0 @@
-[bdist_wheel]
-universal = 1
-
-[bumpversion]
-current_version = 0.4.0
-commit = True
-tag = True
-files = setup.py environ/environ.py docs/conf.py
diff --git a/setup.py b/setup.py
index 1f27233..eb8cdeb 100644
--- a/setup.py
+++ b/setup.py
@@ -1,16 +1,16 @@
 from __future__ import unicode_literals
 from setuptools import setup, find_packages
-import io
-import os
 
-here = os.path.abspath(os.path.dirname(__file__))
-README = io.open(os.path.join(here, 'README.rst'), encoding="utf8").read()
+from environ import environ
 
-version = '0.4.0'
-author = 'joke2k'
-description = "Django-environ allows you to utilize 12factor inspired environment " \
-              "variables to configure your Django application."
-install_requires = ['django', 'six']
+
+here = environ.Path(__file__, is_file=True)
+README = open(here('README.rst')).read()
+
+version = ".".join(map(str, environ.__version__))
+author = environ.__author__
+description = environ.__doc__
+install_requires = [] # 'django'
 
 setup(name='django-environ',
       version=version,
@@ -34,9 +34,10 @@ setup(name='django-environ',
       url='http://github.com/joke2k/django-environ',
       license='MIT License',
       packages=find_packages(),
-      platforms=["any"],
       include_package_data=True,
+      #py_modules=['environ'],
       test_suite='environ.test.load_suite',
       zip_safe=False,
       install_requires=install_requires,
-      )
+)
+


Test patch:
diff --git a/environ/test.py b/environ/test.py
index 7f66c34..9631325 100644
--- a/environ/test.py
+++ b/environ/test.py
@@ -1,23 +1,19 @@
 from __future__ import print_function
-import json
-import os
-import sys
+from __future__ import unicode_literals
 import unittest
 
-from django.core.exceptions import ImproperlyConfigured
-
-from environ import Env, Path
+from environ import *
 
 
 class BaseTests(unittest.TestCase):
 
     URL = 'http://www.google.com/'
-    POSTGRES = 'postgres://uf07k1:wegauwhg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722'
-    MYSQL = 'mysql://bea6eb0:69772142@us-cdbr-east.cleardb.com/heroku_97681?reconnect=true'
+    POSTGRES = 'postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn'
+    MYSQL = 'mysql://bea6eb025ca0d8:69772142@us-cdbr-east.cleardb.com/heroku_97681db3eff7580?reconnect=true'
     MYSQLGIS = 'mysqlgis://user:password@127.0.0.1/some_database'
     SQLITE = 'sqlite:////full/path/to/your/database/file.sqlite'
     MEMCACHE = 'memcache://127.0.0.1:11211'
-    REDIS = 'rediscache://127.0.0.1:6379:1?client_class=django_redis.client.DefaultClient&password=secret'
+    REDIS = 'rediscache://127.0.0.1:6379:1?client_class=redis_cache.client.DefaultClient&password=secret'
     EMAIL = 'smtps://user@domain.com:password@smtp.example.com:587'
     JSON = dict(one='bar', two=2, three=33.44)
     DICT = dict(foo='bar', test='on')
@@ -37,7 +33,6 @@ class BaseTests(unittest.TestCase):
                     BOOL_FALSE_VAR2='False',
                     PROXIED_VAR='$STR_VAR',
                     INT_LIST='42,33',
-                    INT_TUPLE='(42,33)',
                     STR_LIST_WITH_SPACES=' foo,  bar',
                     EMPTY_LIST='',
                     DICT_VAR='foo=bar,test=on',
@@ -75,8 +70,8 @@ class EnvTests(BaseTests):
         self.assertRaises(ImproperlyConfigured, self.env, 'not_present')
 
     def test_str(self):
-        self.assertTypeAndValue(str, 'bar', self.env('STR_VAR'))
-        self.assertTypeAndValue(str, 'bar', self.env.str('STR_VAR'))
+        self.assertTypeAndValue(text_type, 'bar', self.env('STR_VAR'))
+        self.assertTypeAndValue(text_type, 'bar', self.env.str('STR_VAR'))
 
     def test_int(self):
         self.assertTypeAndValue(int, 42, self.env('INT_VAR', cast=int))
@@ -104,20 +99,15 @@ class EnvTests(BaseTests):
         self.assertTypeAndValue(bool, False, self.env.bool('BOOL_FALSE_VAR'))
 
     def test_proxied_value(self):
-        self.assertTypeAndValue(str, 'bar', self.env('PROXIED_VAR'))
+        self.assertTypeAndValue(text_type, 'bar', self.env('PROXIED_VAR'))
 
     def test_int_list(self):
         self.assertTypeAndValue(list, [42, 33], self.env('INT_LIST', cast=[int]))
         self.assertTypeAndValue(list, [42, 33], self.env.list('INT_LIST', int))
 
-    def test_int_tuple(self):
-        self.assertTypeAndValue(tuple, (42, 33), self.env('INT_LIST', cast=(int,)))
-        self.assertTypeAndValue(tuple, (42, 33), self.env.tuple('INT_LIST', int))
-        self.assertTypeAndValue(tuple, ('42', '33'), self.env.tuple('INT_LIST'))
-
     def test_str_list_with_spaces(self):
         self.assertTypeAndValue(list, [' foo', '  bar'],
-                                self.env('STR_LIST_WITH_SPACES', cast=[str]))
+                                self.env('STR_LIST_WITH_SPACES', cast=[text_type]))
         self.assertTypeAndValue(list, [' foo', '  bar'],
                                 self.env.list('STR_LIST_WITH_SPACES'))
 
@@ -131,7 +121,7 @@ class EnvTests(BaseTests):
 
         self.assertEqual({'a': '1'}, self.env.parse_value('a=1', dict))
         self.assertEqual({'a': 1}, self.env.parse_value('a=1', dict(value=int)))
-        self.assertEqual({'a': ['1', '2', '3']}, self.env.parse_value('a=1,2,3', dict(value=[str])))
+        self.assertEqual({'a': ['1', '2', '3']}, self.env.parse_value('a=1,2,3', dict(value=[text_type])))
         self.assertEqual({'a': [1, 2, 3]}, self.env.parse_value('a=1,2,3', dict(value=[int])))
         self.assertEqual({'a': 1, 'b': [1.1, 2.2], 'c': 3},
                          self.env.parse_value('a=1;b=1.1,2.2;c=3', dict(value=int, cast=dict(b=[float]))))
@@ -140,25 +130,24 @@ class EnvTests(BaseTests):
         url = self.env.url('URL_VAR')
         self.assertEqual(url.__class__, self.env.URL_CLASS)
         self.assertEqual(url.geturl(), self.URL)
-        self.assertEqual(None, self.env.url('OTHER_URL', default=None))
 
     def test_db_url_value(self):
         pg_config = self.env.db()
         self.assertEqual(pg_config['ENGINE'], 'django.db.backends.postgresql_psycopg2')
-        self.assertEqual(pg_config['NAME'], 'd8r82722')
+        self.assertEqual(pg_config['NAME'], 'd8r82722r2kuvn')
         self.assertEqual(pg_config['HOST'], 'ec2-107-21-253-135.compute-1.amazonaws.com')
-        self.assertEqual(pg_config['USER'], 'uf07k1')
-        self.assertEqual(pg_config['PASSWORD'], 'wegauwhg')
+        self.assertEqual(pg_config['USER'], 'uf07k1i6d8ia0v')
+        self.assertEqual(pg_config['PASSWORD'], 'wegauwhgeuioweg')
         self.assertEqual(pg_config['PORT'], 5431)
 
         mysql_config = self.env.db('DATABASE_MYSQL_URL')
         self.assertEqual(mysql_config['ENGINE'], 'django.db.backends.mysql')
-        self.assertEqual(mysql_config['NAME'], 'heroku_97681')
+        self.assertEqual(mysql_config['NAME'], 'heroku_97681db3eff7580')
         self.assertEqual(mysql_config['HOST'], 'us-cdbr-east.cleardb.com')
-        self.assertEqual(mysql_config['USER'], 'bea6eb0')
+        self.assertEqual(mysql_config['USER'], 'bea6eb025ca0d8')
         self.assertEqual(mysql_config['PASSWORD'], '69772142')
         self.assertEqual(mysql_config['PORT'], None)
-
+        
         mysql_gis_config = self.env.db('DATABASE_MYSQL_GIS_URL')
         self.assertEqual(mysql_gis_config['ENGINE'], 'django.contrib.gis.db.backends.mysql')
         self.assertEqual(mysql_gis_config['NAME'], 'some_database')
@@ -178,23 +167,25 @@ class EnvTests(BaseTests):
         self.assertEqual(cache_config['LOCATION'], '127.0.0.1:11211')
 
         redis_config = self.env.cache_url('CACHE_REDIS')
-        self.assertEqual(redis_config['BACKEND'], 'django_redis.cache.RedisCache')
+        self.assertEqual(redis_config['BACKEND'], 'redis_cache.cache.RedisCache')
         self.assertEqual(redis_config['LOCATION'], '127.0.0.1:6379:1')
         self.assertEqual(redis_config['OPTIONS'], {
-            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
+            'CLIENT_CLASS': 'redis_cache.client.DefaultClient',
             'PASSWORD': 'secret',
         })
 
     def test_email_url_value(self):
 
         email_config = self.env.email_url()
-        self.assertEqual(email_config['EMAIL_BACKEND'], 'django.core.mail.backends.smtp.EmailBackend')
+        self.assertEqual(email_config['EMAIL_BACKEND'],
+                'django.core.mail.backends.smtp.EmailBackend')
         self.assertEqual(email_config['EMAIL_HOST'], 'smtp.example.com')
         self.assertEqual(email_config['EMAIL_HOST_PASSWORD'], 'password')
         self.assertEqual(email_config['EMAIL_HOST_USER'], 'user@domain.com')
         self.assertEqual(email_config['EMAIL_PORT'], 587)
         self.assertEqual(email_config['EMAIL_USE_TLS'], True)
 
+
     def test_json_value(self):
         self.assertEqual(self.JSON, self.env.json('JSON_VAR'))
 
@@ -216,20 +207,20 @@ class FileEnvTests(EnvTests):
 class SchemaEnvTests(BaseTests):
 
     def test_schema(self):
-        env = Env(INT_VAR=int, NOT_PRESENT_VAR=(float, 33.3), STR_VAR=str,
+        env = Env(INT_VAR=int, NOT_PRESENT_VAR=(float, 33.3), STR_VAR=text_type,
                   INT_LIST=[int], DEFAULT_LIST=([int], [2]))
 
         self.assertTypeAndValue(int, 42, env('INT_VAR'))
         self.assertTypeAndValue(float, 33.3, env('NOT_PRESENT_VAR'))
 
-        self.assertTypeAndValue(str, 'bar', env('STR_VAR'))
-        self.assertTypeAndValue(str, 'foo', env('NOT_PRESENT2', default='foo'))
+        self.assertTypeAndValue(text_type, 'bar', env('STR_VAR'))
+        self.assertTypeAndValue(text_type, 'foo', env('NOT_PRESENT2', default='foo'))
 
         self.assertTypeAndValue(list, [42, 33], env('INT_LIST'))
         self.assertTypeAndValue(list, [2], env('DEFAULT_LIST'))
 
         # Override schema in this one case
-        self.assertTypeAndValue(str, '42', env('INT_VAR', cast=str))
+        self.assertTypeAndValue(text_type, '42', env('INT_VAR', cast=text_type))
 
 
 class DatabaseTestSuite(unittest.TestCase):
@@ -314,7 +305,6 @@ class DatabaseTestSuite(unittest.TestCase):
         self.assertEqual(url['USER'], 'cn=admin,dc=nodomain,dc=org')
         self.assertEqual(url['PASSWORD'], 'some_secret_password')
 
-
 class CacheTestSuite(unittest.TestCase):
 
     def test_memcache_parsing(self):
@@ -388,20 +378,20 @@ class CacheTestSuite(unittest.TestCase):
         self.assertEqual(url['LOCATION'], '')
 
     def test_redis_parsing(self):
-        url = 'rediscache://127.0.0.1:6379:1?client_class=django_redis.client.DefaultClient&password=secret'
+        url = 'rediscache://127.0.0.1:6379:1?client_class=redis_cache.client.DefaultClient&password=secret'
         url = Env.cache_url_config(url)
 
-        self.assertEqual(url['BACKEND'], 'django_redis.cache.RedisCache')
+        self.assertEqual(url['BACKEND'], 'redis_cache.cache.RedisCache')
         self.assertEqual(url['LOCATION'], '127.0.0.1:6379:1')
         self.assertEqual(url['OPTIONS'], {
-            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
+            'CLIENT_CLASS': 'redis_cache.client.DefaultClient',
             'PASSWORD': 'secret',
         })
 
     def test_redis_socket_parsing(self):
         url = 'rediscache:///path/to/socket:1'
         url = Env.cache_url_config(url)
-        self.assertEqual(url['BACKEND'], 'django_redis.cache.RedisCache')
+        self.assertEqual(url['BACKEND'], 'redis_cache.cache.RedisCache')
         self.assertEqual(url['LOCATION'], 'unix:/path/to/socket:1')
 
     def test_options_parsing(self):
@@ -418,7 +408,7 @@ class CacheTestSuite(unittest.TestCase):
 
     def test_custom_backend(self):
         url = 'memcache://127.0.0.1:5400?foo=option&bars=9001'
-        backend = 'django_redis.cache.RedisCache'
+        backend = 'redis_cache.cache.RedisCache'
         url = Env.cache_url_config(url, backend)
 
         self.assertEqual(url['BACKEND'], backend)
@@ -429,113 +419,14 @@ class CacheTestSuite(unittest.TestCase):
         })
 
 
-class SearchTestSuite(unittest.TestCase):
-
-    solr_url = 'solr://127.0.0.1:8983/solr'
-    elasticsearch_url = 'elasticsearch://127.0.0.1:9200/index'
-    whoosh_url = 'whoosh:///home/search/whoosh_index'
-    xapian_url = 'xapian:///home/search/xapian_index'
-    simple_url = 'simple:///'
-
-    def test_solr_parsing(self):
-        url = Env.search_url_config(self.solr_url)
-
-        self.assertEqual(url['ENGINE'], 'haystack.backends.solr_backend.SolrEngine')
-        self.assertEqual(url['URL'], 'http://127.0.0.1:8983/solr')
-
-    def test_solr_multicore_parsing(self):
-        timeout = 360
-        index = 'solr_index'
-        url = '%s/%s?TIMEOUT=%s' % (self.solr_url, index, timeout)
-        url = Env.search_url_config(url)
-
-        self.assertEqual(url['ENGINE'], 'haystack.backends.solr_backend.SolrEngine')
-        self.assertEqual(url['URL'], 'http://127.0.0.1:8983/solr/solr_index')
-        self.assertEqual(url['TIMEOUT'], timeout)
-        self.assertTrue('INDEX_NAME' not in url)
-        self.assertTrue('PATH' not in url)
-
-    def test_elasticsearch_parsing(self):
-        timeout = 360
-        url = '%s?TIMEOUT=%s' % (self.elasticsearch_url, timeout)
-        url = Env.search_url_config(url)
-
-        self.assertEqual(url['ENGINE'], 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine')
-        self.assertTrue('INDEX_NAME' in url.keys())
-        self.assertEqual(url['INDEX_NAME'], 'index')
-        self.assertTrue('TIMEOUT' in url.keys())
-        self.assertEqual(url['TIMEOUT'], timeout)
-        self.assertTrue('PATH' not in url)
-
-    def test_whoosh_parsing(self):
-        storage = 'file'  # or ram
-        post_limit = 128 * 1024 * 1024
-        url = '%s?STORAGE=%s&POST_LIMIT=%s' % (self.whoosh_url, storage, post_limit)
-        url = Env.search_url_config(url)
-
-        self.assertEqual(url['ENGINE'], 'haystack.backends.whoosh_backend.WhooshEngine')
-        self.assertTrue('PATH' in url.keys())
-        self.assertEqual(url['PATH'], '/home/search/whoosh_index')
-        self.assertTrue('STORAGE' in url.keys())
-        self.assertEqual(url['STORAGE'], storage)
-        self.assertTrue('POST_LIMIT' in url.keys())
-        self.assertEqual(url['POST_LIMIT'], post_limit)
-        self.assertTrue('INDEX_NAME' not in url)
-
-    def test_xapian_parsing(self):
-        flags = 'myflags'
-        url = '%s?FLAGS=%s' % (self.xapian_url, flags)
-        url = Env.search_url_config(url)
-
-        self.assertEqual(url['ENGINE'], 'haystack.backends.xapian_backend.XapianEngine')
-        self.assertTrue('PATH' in url.keys())
-        self.assertEqual(url['PATH'], '/home/search/xapian_index')
-        self.assertTrue('FLAGS' in url.keys())
-        self.assertEqual(url['FLAGS'], flags)
-        self.assertTrue('INDEX_NAME' not in url)
-
-    def test_simple_parsing(self):
-        url = Env.search_url_config(self.simple_url)
-
-        self.assertEqual(url['ENGINE'], 'haystack.backends.simple_backend.SimpleEngine')
-        self.assertTrue('INDEX_NAME' not in url)
-        self.assertTrue('PATH' not in url)
-
-    def test_common_args_parsing(self):
-        excluded_indexes = 'myapp.indexes.A,myapp.indexes.B'
-        include_spelling = 1
-        batch_size = 100
-        params = 'EXCLUDED_INDEXES=%s&INCLUDE_SPELLING=%s&BATCH_SIZE=%s' % (
-            excluded_indexes,
-            include_spelling,
-            batch_size
-        )
-        for url in [
-            self.solr_url,
-            self.elasticsearch_url,
-            self.whoosh_url,
-            self.xapian_url,
-            self.simple_url,
-        ]:
-            url = '?'.join([url, params])
-            url = Env.search_url_config(url)
-
-            self.assertTrue('EXCLUDED_INDEXES' in url.keys())
-            self.assertTrue('myapp.indexes.A' in url['EXCLUDED_INDEXES'])
-            self.assertTrue('myapp.indexes.B' in url['EXCLUDED_INDEXES'])
-            self.assertTrue('INCLUDE_SPELLING'in url.keys())
-            self.assertTrue(url['INCLUDE_SPELLING'])
-            self.assertTrue('BATCH_SIZE' in url.keys())
-            self.assertEqual(url['BATCH_SIZE'], 100)
-
-
 class EmailTests(unittest.TestCase):
 
     def test_smtp_parsing(self):
         url = 'smtps://user@domain.com:password@smtp.example.com:587'
         url = Env.email_url_config(url)
 
-        self.assertEqual(url['EMAIL_BACKEND'], 'django.core.mail.backends.smtp.EmailBackend')
+        self.assertEqual(url['EMAIL_BACKEND'],
+                'django.core.mail.backends.smtp.EmailBackend')
         self.assertEqual(url['EMAIL_HOST'], 'smtp.example.com')
         self.assertEqual(url['EMAIL_HOST_PASSWORD'], 'password')
         self.assertEqual(url['EMAIL_HOST_USER'], 'user@domain.com')
@@ -569,13 +460,9 @@ class PathTests(unittest.TestCase):
         self.assertTrue(Path('/home') == Path('/home'))
         self.assertTrue(Path('/home') != Path('/home/dev'))
 
-        self.assertEqual(Path('/home/foo/').rfind('/'), str(Path('/home/foo')).rfind('/'))
-        self.assertEqual(Path('/home/foo/').find('/home'), str(Path('/home/foo/')).find('/home'))
-        self.assertEqual(Path('/home/foo/')[1], str(Path('/home/foo/'))[1])
-
         self.assertEqual(~Path('/home'), Path('/'))
         self.assertEqual(Path('/') + 'home', Path('/home'))
-        self.assertEqual(Path('/') + '/home/public', Path('/home/public'))
+        self.assertEqual(Path('/')+ '/home/public', Path('/home/public'))
         self.assertEqual(Path('/home/dev/public') - 2, Path('/home'))
         self.assertEqual(Path('/home/dev/public') - 'public', Path('/home/dev'))
 
@@ -585,10 +472,7 @@ class PathTests(unittest.TestCase):
 def load_suite():
 
     test_suite = unittest.TestSuite()
-    for case in [
-        EnvTests, FileEnvTests, SchemaEnvTests, PathTests, DatabaseTestSuite,
-        CacheTestSuite, EmailTests, SearchTestSuite
-    ]:
+    for case in [EnvTests, FileEnvTests, SchemaEnvTests, PathTests, DatabaseTestSuite, CacheTestSuite, EmailTests]:
         test_suite.addTest(unittest.makeSuite(case))
     return test_suite
 
diff --git a/environ/test_env.txt b/environ/test_env.txt
index 112ca3e..eb8e01d 100644
--- a/environ/test_env.txt
+++ b/environ/test_env.txt
@@ -1,9 +1,9 @@
 DICT_VAR=foo=bar,test=on
 BOOL_FALSE_VAR2=False
-DATABASE_MYSQL_URL=mysql://bea6eb0:69772142@us-cdbr-east.cleardb.com/heroku_97681?reconnect=true
+DATABASE_MYSQL_URL=mysql://bea6eb025ca0d8:69772142@us-cdbr-east.cleardb.com/heroku_97681db3eff7580?reconnect=true
 DATABASE_MYSQL_GIS_URL=mysqlgis://user:password@127.0.0.1/some_database
 CACHE_URL=memcache://127.0.0.1:11211
-CACHE_REDIS=rediscache://127.0.0.1:6379:1?client_class=django_redis.client.DefaultClient&password=secret
+CACHE_REDIS=rediscache://127.0.0.1:6379:1?client_class=redis_cache.client.DefaultClient&password=secret
 EMAIL_URL=smtps://user@domain.com:password@smtp.example.com:587
 URL_VAR=http://www.google.com/
 PATH_VAR=/home/dev
@@ -12,7 +12,7 @@ BOOL_TRUE_VAR2=True
 DATABASE_SQLITE_URL=sqlite:////full/path/to/your/database/file.sqlite
 JSON_VAR={"three": 33.44, "two": 2, "one": "bar"}
 BOOL_TRUE_VAR=1
-DATABASE_URL=postgres://uf07k1:wegauwhg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722
+DATABASE_URL=postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn
 FLOAT_VAR=33.3
 FLOAT_COMMA_VAR=33,3
 FLOAT_STRANGE_VAR1=123,420,333.3
@@ -23,5 +23,3 @@ INT_VAR=42
 STR_LIST_WITH_SPACES= foo,  bar
 STR_VAR=bar
 INT_LIST=42,33
-CYRILLIC_VAR=фуубар
-INT_TUPLE=(42,33)