Repository: joke2k/django-environ
Pull request #73 (2016-01-18 14:35:28)

Dependencies free (six and django)


Patch:
diff --git a/environ/compat.py b/environ/compat.py
new file mode 100644
index 0000000..9b39edc
--- /dev/null
+++ b/environ/compat.py
@@ -0,0 +1,69 @@
+# -*- coding: utf-8 -*-
+
+"""
+environ.compat
+~~~~~~~~~~~~~~~
+This module handles import compatibility issues between Python 2 and
+Python 3.
+"""
+
+import sys
+import pkgutil
+
+# -------
+# Pythons
+# -------
+
+# Syntax sugar.
+_ver = sys.version_info
+
+#: Python 2.x?
+is_py2 = (_ver[0] == 2)
+
+#: Python 3.x?
+is_py3 = (_ver[0] == 3)
+
+if is_py2:
+    import urlparse as urlparselib
+    from urllib import quote, unquote_plus
+
+    basestring = basestring
+
+elif is_py3:
+    import urllib.parse as urlparselib
+    quote = urlparselib.quote
+    unquote_plus = urlparselib.unquote_plus
+
+    basestring = str
+
+urlparse = urlparselib.urlparse
+urlunparse = urlparselib.urlunparse
+ParseResult = urlparselib.ParseResult
+parse_qs = urlparselib.parse_qs
+
+if pkgutil.find_loader('simplejson'):
+    import simplejson as json
+else:
+    import json
+
+if pkgutil.find_loader('django'):
+    from django import VERSION as DJANGO_VERSION
+    from django.core.exceptions import ImproperlyConfigured
+else:
+    DJANGO_VERSION = None
+
+    class ImproperlyConfigured(Exception):
+        pass
+
+# back compatibility with django postgresql package
+if DJANGO_VERSION is not None and DJANGO_VERSION < (2, 0):
+    DJANGO_POSTGRES = 'django.db.backends.postgresql_psycopg2'
+else:
+    # https://docs.djangoproject.com/en/2.0/releases/2.0/#id1
+    DJANGO_POSTGRES = 'django.db.backends.postgresql'
+
+# back compatibility with redis_cache package
+if pkgutil.find_loader('redis_cache'):
+    REDIS_DRIVER = 'redis_cache.RedisCache'
+else:
+    REDIS_DRIVER = 'django_redis.cache.RedisCache'
diff --git a/environ/environ.py b/environ/environ.py
index 560272f..d1c764f 100644
--- a/environ/environ.py
+++ b/environ/environ.py
@@ -2,24 +2,14 @@
 Django-environ allows you to utilize 12factor inspired environment
 variables to configure your Django application.
 """
-import json
 import logging
 import os
 import re
 import sys
 import warnings
-
-try:
-    from django import VERSION as DJANGO_VERSION
-    from django.core.exceptions import ImproperlyConfigured
-except ImportError:
-    DJANGO_VERSION = None
-
-    class ImproperlyConfigured(Exception):
-        pass
-
-from six.moves import urllib
-from six import string_types
+from .compat import (
+    json, urlparselib, urlparse, urlunparse, ParseResult, parse_qs,
+    unquote_plus, DJANGO_POSTGRES, REDIS_DRIVER, ImproperlyConfigured, basestring)
 
 logger = logging.getLogger(__name__)
 
@@ -33,19 +23,9 @@ __version__ = tuple(VERSION.split('.'))
 def _cast_int(v):
     return int(v) if hasattr(v, 'isdigit') and v.isdigit() else v
 
-def _cast_urlstr(v):
-    return urllib.parse.unquote_plus(v) if isinstance(v, str) else v
-
-# back compatibility with redis_cache package
-DJANGO_REDIS_DRIVER = 'django_redis.cache.RedisCache'
-DJANGO_REDIS_CACHE_DRIVER = 'redis_cache.RedisCache'
 
-REDIS_DRIVER = DJANGO_REDIS_DRIVER
-try:
-    import redis_cache
-    REDIS_DRIVER = DJANGO_REDIS_CACHE_DRIVER
-except:
-    pass
+def _cast_urlstr(v):
+    return unquote_plus(v) if isinstance(v, str) else v
 
 
 class NoValue(object):
@@ -69,9 +49,13 @@ class Env(object):
     ENVIRON = os.environ
     NOTSET = NoValue()
     BOOLEAN_TRUE_STRINGS = ('true', 'on', 'ok', 'y', 'yes', '1')
-    URL_CLASS = urllib.parse.ParseResult
+    URL_CLASS = ParseResult
     DEFAULT_DATABASE_ENV = 'DATABASE_URL'
     DB_SCHEMES = {
+        'postgres': DJANGO_POSTGRES,
+        'postgresql': DJANGO_POSTGRES,
+        'psql': DJANGO_POSTGRES,
+        'pgsql': DJANGO_POSTGRES,
         'postgis': 'django.contrib.gis.db.backends.postgis',
         'mysql': 'django.db.backends.mysql',
         'mysql2': 'django.db.backends.mysql',
@@ -85,17 +69,6 @@ class Env(object):
         'sqlite': 'django.db.backends.sqlite3',
         'ldap': 'ldapdb.backends.ldap',
     }
-    if DJANGO_VERSION is not None and DJANGO_VERSION < (2, 0):
-        DB_SCHEMES['postgres'] = 'django.db.backends.postgresql_psycopg2'
-        DB_SCHEMES['postgresql'] = 'django.db.backends.postgresql_psycopg2'
-        DB_SCHEMES['psql'] = 'django.db.backends.postgresql_psycopg2'
-        DB_SCHEMES['pgsql'] = 'django.db.backends.postgresql_psycopg2'
-    else:
-        # https://docs.djangoproject.com/en/2.0/releases/2.0/#id1
-        DB_SCHEMES['postgres'] = 'django.db.backends.postgresql'
-        DB_SCHEMES['postgresql'] = 'django.db.backends.postgresql'
-        DB_SCHEMES['psql'] = 'django.db.backends.postgresql'
-        DB_SCHEMES['pgsql'] = 'django.db.backends.postgresql'
     _DB_BASE_OPTIONS = ['CONN_MAX_AGE', 'ATOMIC_REQUESTS', 'AUTOCOMMIT']
 
     DEFAULT_CACHE_ENV = 'CACHE_URL'
@@ -212,7 +185,7 @@ class Env(object):
         """
         :rtype: urlparse.ParseResult
         """
-        return self.get_value(var, cast=urllib.parse.urlparse, default=default, parse_default=True)
+        return self.get_value(var, cast=urlparse, default=default, parse_default=True)
 
     def db_url(self, var=DEFAULT_DATABASE_ENV, default=NOTSET, engine=None):
         """Returns a config dictionary, defaulting to DATABASE_URL.
@@ -387,13 +360,13 @@ class Env(object):
                     'NAME': ':memory:'
                 }
                 # note: no other settings are required for sqlite
-            url = urllib.parse.urlparse(url)
+            url = urlparse(url)
 
         config = {}
 
         # Remove query strings.
         path = url.path[1:]
-        path = urllib.parse.unquote_plus(path.split('?', 2)[0])
+        path = unquote_plus(path.split('?', 2)[0])
 
         if url.scheme == 'sqlite':
             if path == '':
@@ -430,7 +403,7 @@ class Env(object):
 
         if url.query:
             config_options = {}
-            for k, v in urllib.parse.parse_qs(url.query).items():
+            for k, v in parse_qs(url.query).items():
                 if k.upper() in cls._DB_BASE_OPTIONS:
                     config.update({k.upper(): _cast_int(v[0])})
                 else:
@@ -459,7 +432,7 @@ class Env(object):
         :param backend:
         :return:
         """
-        url = urllib.parse.urlparse(url) if not isinstance(url, cls.URL_CLASS) else url
+        url = urlparse(url) if not isinstance(url, cls.URL_CLASS) else url
 
         location = url.netloc.split(',')
         if len(location) == 1:
@@ -490,7 +463,7 @@ class Env(object):
 
         if url.query:
             config_options = {}
-            for k, v in urllib.parse.parse_qs(url.query).items():
+            for k, v in parse_qs(url.query).items():
                 opt = {k.upper(): _cast_int(v[0])}
                 if k.upper() in cls._CACHE_BASE_OPTIONS:
                     config.update(opt)
@@ -509,11 +482,11 @@ class Env(object):
 
         config = {}
 
-        url = urllib.parse.urlparse(url) if not isinstance(url, cls.URL_CLASS) else url
+        url = urlparse(url) if not isinstance(url, cls.URL_CLASS) else url
 
         # Remove query strings
         path = url.path[1:]
-        path = urllib.parse.unquote_plus(path.split('?', 2)[0])
+        path = unquote_plus(path.split('?', 2)[0])
 
         # Update with environment configuration
         config.update({
@@ -538,7 +511,7 @@ class Env(object):
 
         if url.query:
             config_options = {}
-            for k, v in urllib.parse.parse_qs(url.query).items():
+            for k, v in parse_qs(url.query).items():
                 opt = {k.upper(): _cast_int(v[0])}
                 if k.upper() in cls._EMAIL_BASE_OPTIONS:
                     config.update(opt)
@@ -552,11 +525,11 @@ class Env(object):
     def search_url_config(cls, url, engine=None):
         config = {}
 
-        url = urllib.parse.urlparse(url) if not isinstance(url, cls.URL_CLASS) else url
+        url = urlparse(url) if not isinstance(url, cls.URL_CLASS) else url
 
         # Remove query strings.
         path = url.path[1:]
-        path = urllib.parse.unquote_plus(path.split('?', 2)[0])
+        path = unquote_plus(path.split('?', 2)[0])
 
         if url.scheme not in cls.SEARCH_SCHEMES:
             raise ImproperlyConfigured('Invalid search schema %s' % url.scheme)
@@ -565,7 +538,7 @@ class Env(object):
         # check commons params
         params = {}
         if url.query:
-            params = urllib.parse.parse_qs(url.query)
+            params = parse_qs(url.query)
             if 'EXCLUDED_INDEXES' in params.keys():
                 config['EXCLUDED_INDEXES'] = params['EXCLUDED_INDEXES'][0].split(',')
             if 'INCLUDE_SPELLING' in params.keys():
@@ -584,7 +557,7 @@ class Env(object):
             path = path[:-1]
 
         if url.scheme == 'solr':
-            config['URL'] = urllib.parse.urlunparse(('http',) + url[1:2] + (path,) + ('', '', ''))
+            config['URL'] = urlunparse(('http',) + url[1:2] + (path,) + ('', '', ''))
             if 'TIMEOUT' in params.keys():
                 config['TIMEOUT'] = cls.parse_value(params['TIMEOUT'][0], int)
             return config
@@ -600,7 +573,7 @@ class Env(object):
                 path = ""
                 index = split[0]
 
-            config['URL'] = urllib.parse.urlunparse(('http',) + url[1:2] + (path,) + ('', '', ''))
+            config['URL'] = urlunparse(('http',) + url[1:2] + (path,) + ('', '', ''))
             if 'TIMEOUT' in params.keys():
                 config['TIMEOUT'] = cls.parse_value(params['TIMEOUT'][0], int)
             config['INDEX_NAME'] = index
@@ -643,7 +616,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(
@@ -750,7 +723,7 @@ class Path(object):
     def __sub__(self, other):
         if isinstance(other, int):
             return self.path('../' * other)
-        elif isinstance(other, string_types):
+        elif isinstance(other, basestring):
             if self.__root__.endswith(other):
                 return Path(self.__root__.rstrip(other))
         raise TypeError(
@@ -800,9 +773,9 @@ class Path(object):
 
 
 def register_scheme(scheme):
-    for method in dir(urllib.parse):
+    for method in dir(urlparselib):
         if method.startswith('uses_'):
-            getattr(urllib.parse, method).append(scheme)
+            getattr(urlparselib, method).append(scheme)
 
 
 def register_schemes(schemes):
diff --git a/setup.py b/setup.py
index e745a99..8e26fe6 100644
--- a/setup.py
+++ b/setup.py
@@ -12,7 +12,6 @@ version = '0.4.4'
 author = 'joke2k'
 description = "Django-environ allows you to utilize 12factor inspired environment " \
               "variables to configure your Django application."
-install_requires = ['django', 'six']
 
 setup(name='django-environ',
       version=version,
@@ -50,6 +49,5 @@ setup(name='django-environ',
       platforms=["any"],
       include_package_data=True,
       test_suite='environ.test.load_suite',
-      zip_safe=False,
-      install_requires=install_requires,
+      zip_safe=False
       )


Test patch:
diff --git a/environ/test.py b/environ/test.py
index c6414b4..f642857 100644
--- a/environ/test.py
+++ b/environ/test.py
@@ -1,14 +1,14 @@
 from __future__ import print_function
-import json
 import os
 import sys
 import unittest
 import warnings
 
-from django import VERSION as DJANGO_VERSION
-from django.core.exceptions import ImproperlyConfigured
+from .compat import (
+    json, DJANGO_POSTGRES, ImproperlyConfigured, REDIS_DRIVER, quote
+)
 
-from environ import Env, Path, REDIS_DRIVER
+from environ import Env, Path
 
 
 class BaseTests(unittest.TestCase):
@@ -126,7 +126,7 @@ 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.assertEqual('bar', self.env('PROXIED_VAR'))
 
     def test_int_list(self):
         self.assertTypeAndValue(list, [42, 33], self.env('INT_LIST', cast=[int]))
@@ -168,18 +168,14 @@ class EnvTests(BaseTests):
         self.assertEqual(None, self.env.url('OTHER_URL', default=None))
 
     def test_url_encoded_parts(self):
-        from six.moves import urllib
         password_with_unquoted_characters = "#password"
-        encoded_url = "mysql://user:%s@127.0.0.1:3306/dbname" % urllib.parse.quote(password_with_unquoted_characters)
+        encoded_url = "mysql://user:%s@127.0.0.1:3306/dbname" % quote(password_with_unquoted_characters)
         parsed_url = self.env.db_url_config(encoded_url)
         self.assertEqual(parsed_url['PASSWORD'], password_with_unquoted_characters)
 
     def test_db_url_value(self):
         pg_config = self.env.db()
-        if DJANGO_VERSION < (2, 0):
-            self.assertEqual(pg_config['ENGINE'], 'django.db.backends.postgresql_psycopg2')
-        else:
-            self.assertEqual(pg_config['ENGINE'], 'django.db.backends.postgresql')
+        self.assertEqual(pg_config['ENGINE'], DJANGO_POSTGRES)
         self.assertEqual(pg_config['NAME'], 'd8r82722')
         self.assertEqual(pg_config['HOST'], 'ec2-107-21-253-135.compute-1.amazonaws.com')
         self.assertEqual(pg_config['USER'], 'uf07k1')
@@ -304,8 +300,8 @@ class SchemaEnvTests(BaseTests):
         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.assertEqual('bar', env('STR_VAR'))
+        self.assertEqual('foo', env('NOT_PRESENT2', default='foo'))
 
         self.assertTypeAndValue(list, [42, 33], env('INT_LIST'))
         self.assertTypeAndValue(list, [2], env('DEFAULT_LIST'))
@@ -320,10 +316,7 @@ class DatabaseTestSuite(unittest.TestCase):
         url = 'postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn'
         url = Env.db_url_config(url)
 
-        if DJANGO_VERSION < (2, 0):
-            self.assertEqual(url['ENGINE'], 'django.db.backends.postgresql_psycopg2')
-        else:
-            self.assertEqual(url['ENGINE'], 'django.db.backends.postgresql')
+        self.assertEqual(url['ENGINE'], DJANGO_POSTGRES)
         self.assertEqual(url['NAME'], 'd8r82722r2kuvn')
         self.assertEqual(url['HOST'], 'ec2-107-21-253-135.compute-1.amazonaws.com')
         self.assertEqual(url['USER'], 'uf07k1i6d8ia0v')