From 0f11e53372e25565691b0f4d45484f0a42bbc8e0 Mon Sep 17 00:00:00 2001 From: Burhan Khalid Date: Sun, 12 Jul 2015 22:22:59 +0300 Subject: [PATCH 1/9] adding rfind and find methods to Path --- environ/environ.py | 57 ++++++++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/environ/environ.py b/environ/environ.py index f4d11773..f8f63bf3 100644 --- a/environ/environ.py +++ b/environ/environ.py @@ -38,17 +38,20 @@ class ImproperlyConfigured(Exception): # return int if possible -_cast_int = lambda v: int(v) if isinstance(v, basestring) 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 class NoValue(object): + def __repr__(self): return '<{0}>'.format(self.__class__.__name__) class Env(object): + """Provide schema-based lookups of environment variables so that each caller doesn't have to pass in `cast` and `default` parameters. @@ -110,8 +113,6 @@ class Env(object): "simple": "haystack.backends.simple_backend.SimpleEngine", } - - def __init__(self, **schema): self.schema = schema @@ -180,7 +181,7 @@ def db_url(self, var=DEFAULT_DATABASE_ENV, default=NOTSET, engine=None): :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. @@ -188,7 +189,7 @@ def cache_url(self, var=DEFAULT_CACHE_ENV, default=NOTSET, backend=None): :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. @@ -196,7 +197,7 @@ def email_url(self, var=DEFAULT_EMAIL_ENV, default=NOTSET, backend=None): :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. @@ -222,7 +223,8 @@ def get_value(self, var, cast=None, default=NOTSET, parse_default=False): :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.schema: var_info = self.schema[var] @@ -289,18 +291,20 @@ def parse_value(cls, value, cast): 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': + # 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 float: # clean string float_str = re.sub(r'[^\d,\.]', '', value) - # split for avoid thousand separator and different locale comma/dot symbol + # split for avoid thousand separator and different locale comma/dot + # symbol parts = re.split(r'[,\.]', float_str) if len(parts) == 1: float_str = parts[0] @@ -350,7 +354,8 @@ def db_url_config(cls, url, engine=None): if url.scheme == 'sqlite' and path == '': path = ':memory:' if url.scheme == 'ldap': - path = '{scheme}://{hostname}'.format(scheme=_cast_str(url.scheme), hostname=_cast_str(url.hostname)) + path = '{scheme}://{hostname}'.format( + scheme=_cast_str(url.scheme), hostname=_cast_str(url.hostname)) if url.port: path += ':{port}'.format(port=_cast_str(url.port)) @@ -391,7 +396,8 @@ def cache_url_config(cls, url, backend=None): :param overrides: :return: """ - url = urlparse.urlparse(url) if not isinstance(url, cls.URL_CLASS) else url + url = urlparse.urlparse(url) if not isinstance( + url, cls.URL_CLASS) else url location = url.netloc.split(',') if len(location) == 1: @@ -433,7 +439,8 @@ def email_url_config(cls, url, backend=None): config = {} - url = urlparse.urlparse(url) if not isinstance(url, cls.URL_CLASS) else url + url = urlparse.urlparse(url) if not isinstance( + url, cls.URL_CLASS) else url # Remove query strings path = url.path[1:] @@ -474,7 +481,8 @@ def email_url_config(cls, url, backend=None): def search_url_config(cls, url, engine=None): config = {} - url = urlparse.urlparse(url) if not isinstance(url, cls.URL_CLASS) else url + url = urlparse.urlparse(url) if not isinstance( + url, cls.URL_CLASS) else url # Remove query strings. path = url.path[1:] @@ -498,7 +506,7 @@ def search_url_config(cls, url, engine=None): config.update({ "URL": urlparse.urlunparse(("http",) + url[1:2] + (path,) + url[3:]), "INDEX_NAME": index, - }) + }) if path: config.update({ @@ -523,7 +531,8 @@ def read_env(env_file=None, **overrides): """ if env_file is None: frame = sys._getframe() - env_file = os.path.join(os.path.dirname(frame.f_back.f_code.co_filename), '.env') + env_file = os.path.join( + os.path.dirname(frame.f_back.f_code.co_filename), '.env') if not os.path.exists(env_file): warnings.warn("not reading %s - it doesn't exist." % env_file) return @@ -555,6 +564,7 @@ def read_env(env_file=None, **overrides): class Path(object): + """Inspired to Django Two-scoops, handling File Paths in Settings. >>> from environ import Path @@ -635,7 +645,8 @@ def __sub__(self, other): return self.path('../' * other) 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('..') @@ -655,17 +666,25 @@ def __str__(self): def __unicode__(self): return self.__str__() + 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 filter(lambda s: s.startswith('uses_'), dir(urlparse)): getattr(urlparse, method).append(scheme) # Register database and cache schemes in URLs. -for schema in list(Env.DB_SCHEMES.keys()) + list(Env.CACHE_SCHEMES.keys()) + list(Env.SEARCH_SCHEMES.keys()) +list(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) From 59a9f3df45fdfc42d48c3598c6f726b07389027a Mon Sep 17 00:00:00 2001 From: Burhan Khalid Date: Sun, 12 Jul 2015 22:43:43 +0300 Subject: [PATCH 2/9] updating test coverage --- environ/test.py | 146 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 98 insertions(+), 48 deletions(-) diff --git a/environ/test.py b/environ/test.py index 9631325f..cafdd573 100644 --- a/environ/test.py +++ b/environ/test.py @@ -78,31 +78,40 @@ def test_int(self): self.assertTypeAndValue(int, 42, self.env.int('INT_VAR')) def test_int_with_none_default(self): - self.assertTrue(self.env('NOT_PRESENT_VAR', cast=int, default=None) is None) + self.assertTrue( + self.env('NOT_PRESENT_VAR', cast=int, default=None) is None) def test_float(self): self.assertTypeAndValue(float, 33.3, self.env('FLOAT_VAR', cast=float)) self.assertTypeAndValue(float, 33.3, self.env.float('FLOAT_VAR')) - self.assertTypeAndValue(float, 33.3, self.env('FLOAT_COMMA_VAR', cast=float)) - self.assertTypeAndValue(float, 123420333.3, self.env('FLOAT_STRANGE_VAR1', cast=float)) - self.assertTypeAndValue(float, 123420333.3, self.env('FLOAT_STRANGE_VAR2', cast=float)) + self.assertTypeAndValue( + float, 33.3, self.env('FLOAT_COMMA_VAR', cast=float)) + self.assertTypeAndValue( + float, 123420333.3, self.env('FLOAT_STRANGE_VAR1', cast=float)) + self.assertTypeAndValue( + float, 123420333.3, self.env('FLOAT_STRANGE_VAR2', cast=float)) def test_bool_true(self): - self.assertTypeAndValue(bool, True, self.env('BOOL_TRUE_VAR', cast=bool)) - self.assertTypeAndValue(bool, True, self.env('BOOL_TRUE_VAR2', cast=bool)) + self.assertTypeAndValue( + bool, True, self.env('BOOL_TRUE_VAR', cast=bool)) + self.assertTypeAndValue( + bool, True, self.env('BOOL_TRUE_VAR2', cast=bool)) self.assertTypeAndValue(bool, True, self.env.bool('BOOL_TRUE_VAR')) def test_bool_false(self): - self.assertTypeAndValue(bool, False, self.env('BOOL_FALSE_VAR', cast=bool)) - self.assertTypeAndValue(bool, False, self.env('BOOL_FALSE_VAR2', cast=bool)) + self.assertTypeAndValue( + bool, False, self.env('BOOL_FALSE_VAR', cast=bool)) + self.assertTypeAndValue( + bool, False, self.env('BOOL_FALSE_VAR2', cast=bool)) self.assertTypeAndValue(bool, False, self.env.bool('BOOL_FALSE_VAR')) def test_proxied_value(self): 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('INT_LIST', cast=[int])) self.assertTypeAndValue(list, [42, 33], self.env.list('INT_LIST', int)) def test_str_list_with_spaces(self): @@ -120,9 +129,12 @@ def test_dict_value(self): def test_dict_parsing(self): 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=[text_type]))) - self.assertEqual({'a': [1, 2, 3]}, self.env.parse_value('a=1,2,3', dict(value=[int]))) + 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=[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])))) @@ -133,9 +145,11 @@ def test_url_value(self): 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['ENGINE'], 'django.db.backends.postgresql_psycopg2') self.assertEqual(pg_config['NAME'], 'd8r82722r2kuvn') - self.assertEqual(pg_config['HOST'], 'ec2-107-21-253-135.compute-1.amazonaws.com') + self.assertEqual( + pg_config['HOST'], 'ec2-107-21-253-135.compute-1.amazonaws.com') self.assertEqual(pg_config['USER'], 'uf07k1i6d8ia0v') self.assertEqual(pg_config['PASSWORD'], 'wegauwhgeuioweg') self.assertEqual(pg_config['PORT'], 5431) @@ -147,9 +161,10 @@ def test_db_url_value(self): 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['ENGINE'], 'django.contrib.gis.db.backends.mysql') self.assertEqual(mysql_gis_config['NAME'], 'some_database') self.assertEqual(mysql_gis_config['HOST'], '127.0.0.1') self.assertEqual(mysql_gis_config['USER'], 'user') @@ -158,16 +173,19 @@ def test_db_url_value(self): sqlite_config = self.env.db('DATABASE_SQLITE_URL') self.assertEqual(sqlite_config['ENGINE'], 'django.db.backends.sqlite3') - self.assertEqual(sqlite_config['NAME'], '/full/path/to/your/database/file.sqlite') + self.assertEqual( + sqlite_config['NAME'], '/full/path/to/your/database/file.sqlite') def test_cache_url_value(self): cache_config = self.env.cache_url() - self.assertEqual(cache_config['BACKEND'], 'django.core.cache.backends.memcached.MemcachedCache') + self.assertEqual( + cache_config['BACKEND'], 'django.core.cache.backends.memcached.MemcachedCache') self.assertEqual(cache_config['LOCATION'], '127.0.0.1:11211') redis_config = self.env.cache_url('CACHE_REDIS') - self.assertEqual(redis_config['BACKEND'], 'redis_cache.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': 'redis_cache.client.DefaultClient', @@ -178,14 +196,13 @@ def test_email_url_value(self): email_config = self.env.email_url() self.assertEqual(email_config['EMAIL_BACKEND'], - 'django.core.mail.backends.smtp.EmailBackend') + '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')) @@ -201,7 +218,8 @@ def setUp(self): self._orig_environ = os.environ os.environ = {} file_path = Path(__file__, is_file=True)('test_env.txt') - self.env.read_env(file_path, PATH_VAR=Path(__file__, is_file=True).__root__) + self.env.read_env( + file_path, PATH_VAR=Path(__file__, is_file=True).__root__) class SchemaEnvTests(BaseTests): @@ -214,13 +232,15 @@ def test_schema(self): self.assertTypeAndValue(float, 33.3, env('NOT_PRESENT_VAR')) self.assertTypeAndValue(text_type, 'bar', env('STR_VAR')) - self.assertTypeAndValue(text_type, 'foo', env('NOT_PRESENT2', default='foo')) + 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(text_type, '42', env('INT_VAR', cast=text_type)) + self.assertTypeAndValue( + text_type, '42', env('INT_VAR', cast=text_type)) class DatabaseTestSuite(unittest.TestCase): @@ -229,9 +249,11 @@ def test_postgres_parsing(self): url = 'postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn' url = Env.db_url_config(url) - self.assertEqual(url['ENGINE'], 'django.db.backends.postgresql_psycopg2') + self.assertEqual( + url['ENGINE'], 'django.db.backends.postgresql_psycopg2') self.assertEqual(url['NAME'], 'd8r82722r2kuvn') - self.assertEqual(url['HOST'], 'ec2-107-21-253-135.compute-1.amazonaws.com') + self.assertEqual( + url['HOST'], 'ec2-107-21-253-135.compute-1.amazonaws.com') self.assertEqual(url['USER'], 'uf07k1i6d8ia0v') self.assertEqual(url['PASSWORD'], 'wegauwhgeuioweg') self.assertEqual(url['PORT'], 5431) @@ -240,9 +262,11 @@ def test_postgis_parsing(self): url = 'postgis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn' url = Env.db_url_config(url) - self.assertEqual(url['ENGINE'], 'django.contrib.gis.db.backends.postgis') + self.assertEqual( + url['ENGINE'], 'django.contrib.gis.db.backends.postgis') self.assertEqual(url['NAME'], 'd8r82722r2kuvn') - self.assertEqual(url['HOST'], 'ec2-107-21-253-135.compute-1.amazonaws.com') + self.assertEqual( + url['HOST'], 'ec2-107-21-253-135.compute-1.amazonaws.com') self.assertEqual(url['USER'], 'uf07k1i6d8ia0v') self.assertEqual(url['PASSWORD'], 'wegauwhgeuioweg') self.assertEqual(url['PORT'], 5431) @@ -253,7 +277,8 @@ def test_mysql_gis_parsing(self): self.assertEqual(url['ENGINE'], 'django.contrib.gis.db.backends.mysql') self.assertEqual(url['NAME'], 'd8r82722r2kuvn') - self.assertEqual(url['HOST'], 'ec2-107-21-253-135.compute-1.amazonaws.com') + self.assertEqual( + url['HOST'], 'ec2-107-21-253-135.compute-1.amazonaws.com') self.assertEqual(url['USER'], 'uf07k1i6d8ia0v') self.assertEqual(url['PASSWORD'], 'wegauwhgeuioweg') self.assertEqual(url['PORT'], 5431) @@ -305,76 +330,88 @@ def test_database_ldap_url(self): 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): url = 'memcache://127.0.0.1:11211' url = Env.cache_url_config(url) - self.assertEqual(url['BACKEND'], 'django.core.cache.backends.memcached.MemcachedCache') + self.assertEqual( + url['BACKEND'], 'django.core.cache.backends.memcached.MemcachedCache') self.assertEqual(url['LOCATION'], '127.0.0.1:11211') def test_memcache_pylib_parsing(self): url = 'pymemcache://127.0.0.1:11211' url = Env.cache_url_config(url) - self.assertEqual(url['BACKEND'], 'django.core.cache.backends.memcached.PyLibMCCache') + self.assertEqual( + url['BACKEND'], 'django.core.cache.backends.memcached.PyLibMCCache') self.assertEqual(url['LOCATION'], '127.0.0.1:11211') def test_memcache_multiple_parsing(self): url = 'memcache://172.19.26.240:11211,172.19.26.242:11212' url = Env.cache_url_config(url) - self.assertEqual(url['BACKEND'], 'django.core.cache.backends.memcached.MemcachedCache') - self.assertEqual(url['LOCATION'], ['172.19.26.240:11211', '172.19.26.242:11212']) + self.assertEqual( + url['BACKEND'], 'django.core.cache.backends.memcached.MemcachedCache') + self.assertEqual( + url['LOCATION'], ['172.19.26.240:11211', '172.19.26.242:11212']) def test_memcache_socket_parsing(self): url = 'memcache:///tmp/memcached.sock' url = Env.cache_url_config(url) - self.assertEqual(url['BACKEND'], 'django.core.cache.backends.memcached.MemcachedCache') + self.assertEqual( + url['BACKEND'], 'django.core.cache.backends.memcached.MemcachedCache') self.assertEqual(url['LOCATION'], 'unix:/tmp/memcached.sock') def test_dbcache_parsing(self): url = 'dbcache://my_cache_table' url = Env.cache_url_config(url) - self.assertEqual(url['BACKEND'], 'django.core.cache.backends.db.DatabaseCache') + self.assertEqual( + url['BACKEND'], 'django.core.cache.backends.db.DatabaseCache') self.assertEqual(url['LOCATION'], 'my_cache_table') def test_filecache_parsing(self): url = 'filecache:///var/tmp/django_cache' url = Env.cache_url_config(url) - self.assertEqual(url['BACKEND'], 'django.core.cache.backends.filebased.FileBasedCache') + self.assertEqual( + url['BACKEND'], 'django.core.cache.backends.filebased.FileBasedCache') self.assertEqual(url['LOCATION'], '/var/tmp/django_cache') def test_filecache_windows_parsing(self): url = 'filecache://C:/foo/bar' url = Env.cache_url_config(url) - self.assertEqual(url['BACKEND'], 'django.core.cache.backends.filebased.FileBasedCache') + self.assertEqual( + url['BACKEND'], 'django.core.cache.backends.filebased.FileBasedCache') self.assertEqual(url['LOCATION'], 'C:/foo/bar') def test_locmem_parsing(self): url = 'locmemcache://' url = Env.cache_url_config(url) - self.assertEqual(url['BACKEND'], 'django.core.cache.backends.locmem.LocMemCache') + self.assertEqual( + url['BACKEND'], 'django.core.cache.backends.locmem.LocMemCache') self.assertEqual(url['LOCATION'], '') def test_locmem_named_parsing(self): url = 'locmemcache://unique-snowflake' url = Env.cache_url_config(url) - self.assertEqual(url['BACKEND'], 'django.core.cache.backends.locmem.LocMemCache') + self.assertEqual( + url['BACKEND'], 'django.core.cache.backends.locmem.LocMemCache') self.assertEqual(url['LOCATION'], 'unique-snowflake') def test_dummycache_parsing(self): url = 'dummycache://' url = Env.cache_url_config(url) - self.assertEqual(url['BACKEND'], 'django.core.cache.backends.dummy.DummyCache') + self.assertEqual( + url['BACKEND'], 'django.core.cache.backends.dummy.DummyCache') self.assertEqual(url['LOCATION'], '') def test_redis_parsing(self): @@ -398,7 +435,8 @@ def test_options_parsing(self): url = 'filecache:///var/tmp/django_cache?timeout=60&max_entries=1000&cull_frequency=0' url = Env.cache_url_config(url) - self.assertEqual(url['BACKEND'], 'django.core.cache.backends.filebased.FileBasedCache') + self.assertEqual( + url['BACKEND'], 'django.core.cache.backends.filebased.FileBasedCache') self.assertEqual(url['LOCATION'], '/var/tmp/django_cache') self.assertEqual(url['TIMEOUT'], 60) self.assertEqual(url['OPTIONS'], { @@ -426,7 +464,7 @@ def test_smtp_parsing(self): url = Env.email_url_config(url) self.assertEqual(url['EMAIL_BACKEND'], - 'django.core.mail.backends.smtp.EmailBackend') + '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') @@ -439,7 +477,8 @@ class PathTests(unittest.TestCase): def test_path_class(self): root = Path(__file__, '..', is_file=True) - root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '../')) + root_path = os.path.abspath( + os.path.join(os.path.dirname(__file__), '../')) self.assertEqual(root(), root_path) self.assertEqual(root.__root__, root_path) @@ -449,8 +488,11 @@ def test_path_class(self): def test_required_path(self): - self.assertRaises(ImproperlyConfigured, Path, '/not/existing/path/', required=True) - self.assertRaises(ImproperlyConfigured, Path(__file__), 'not_existing_path', required=True) + self.assertRaises( + ImproperlyConfigured, Path, '/not/existing/path/', required=True) + self.assertRaises( + ImproperlyConfigured, Path(__file__), 'not_existing_path', + required=True) def test_comparison(self): @@ -460,11 +502,18 @@ def test_comparison(self): self.assertTrue(Path('/home') == Path('/home')) self.assertTrue(Path('/home') != Path('/home/dev')) + self.assertTrue(Path('/home/foo/').rfind('/') + == str(Path('/home/foo')).rfind('/')) + + self.assertTrue(Path('/home/foo/').find('/home') + == str(Path('/home/foo/')).find('/home')) + 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')) + self.assertEqual( + Path('/home/dev/public') - 'public', Path('/home/dev')) self.assertRaises(TypeError, lambda _: Path('/home/dev/') - 'not int') @@ -472,7 +521,8 @@ def test_comparison(self): def load_suite(): test_suite = unittest.TestSuite() - for case in [EnvTests, FileEnvTests, SchemaEnvTests, PathTests, DatabaseTestSuite, CacheTestSuite, EmailTests]: + for case in [EnvTests, FileEnvTests, SchemaEnvTests, PathTests, + DatabaseTestSuite, CacheTestSuite, EmailTests]: test_suite.addTest(unittest.makeSuite(case)) return test_suite From f982bbe147ea505d2ebe3fe119ced523ae6ad929 Mon Sep 17 00:00:00 2001 From: Burhan Khalid Date: Sun, 12 Jul 2015 22:48:50 +0300 Subject: [PATCH 3/9] Better tests --- environ/test.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/environ/test.py b/environ/test.py index cafdd573..c0a4c5c6 100644 --- a/environ/test.py +++ b/environ/test.py @@ -502,11 +502,11 @@ def test_comparison(self): self.assertTrue(Path('/home') == Path('/home')) self.assertTrue(Path('/home') != Path('/home/dev')) - self.assertTrue(Path('/home/foo/').rfind('/') - == str(Path('/home/foo')).rfind('/')) + self.assertEqual(Path('/home/foo/').rfind('/'), + str(Path('/home/foo')).rfind('/')) - self.assertTrue(Path('/home/foo/').find('/home') - == str(Path('/home/foo/')).find('/home')) + self.assertEqual(Path('/home/foo/').find('/home'), + str(Path('/home/foo/')).find('/home')) self.assertEqual(~Path('/home'), Path('/')) self.assertEqual(Path('/') + 'home', Path('/home')) From 9efdade0aa16630553e17655ad9c7e294ba9721d Mon Sep 17 00:00:00 2001 From: Burhan Khalid Date: Tue, 14 Jul 2015 01:30:07 +0300 Subject: [PATCH 4/9] adding __getitem__ --- environ/environ.py | 3 +++ environ/test.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/environ/environ.py b/environ/environ.py index f8f63bf3..f78ef6d9 100644 --- a/environ/environ.py +++ b/environ/environ.py @@ -666,6 +666,9 @@ def __str__(self): 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) diff --git a/environ/test.py b/environ/test.py index c0a4c5c6..ce5fb1b0 100644 --- a/environ/test.py +++ b/environ/test.py @@ -508,6 +508,9 @@ def test_comparison(self): 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')) From 0f4e3c02464a4626fec33b128c7dea0af42cbbcc Mon Sep 17 00:00:00 2001 From: Mikhail Sokolov Date: Tue, 11 Aug 2015 18:49:06 +0700 Subject: [PATCH 5/9] Add tuple support --- README.rst | 3 +++ environ/environ.py | 14 +++++++++++++- environ/test.py | 8 +++++++- environ/test_env.txt | 1 + 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 0fc900ad..89f372e3 100644 --- a/README.rst +++ b/README.rst @@ -173,6 +173,7 @@ Supported Types - float - json - list (FOO=a,b,c) +- tuple (FOO=(a,b,c)) - dict (BAR=key=val,foo=bar) - url - path (environ.Path) @@ -217,6 +218,8 @@ Tests Changelog --------- +=== 0.3.2 (2015-08-11) + * Add tuple support === 0.3.1 (2014-09-03) * Add LDAP url support for database (django-ldapdb) diff --git a/environ/environ.py b/environ/environ.py index f4d11773..261fd47e 100644 --- a/environ/environ.py +++ b/environ/environ.py @@ -34,7 +34,7 @@ class ImproperlyConfigured(Exception): __author__ = 'joke2k' -__version__ = (0, 3, 1) +__version__ = (0, 3, 2) # return int if possible @@ -162,6 +162,12 @@ def list(self, var, cast=None, default=NOTSET): """ 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 @@ -284,6 +290,9 @@ def parse_value(cls, value, cast): 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', text_type) @@ -297,6 +306,9 @@ def parse_value(cls, value, cast): 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) diff --git a/environ/test.py b/environ/test.py index 9631325f..82ad8a46 100644 --- a/environ/test.py +++ b/environ/test.py @@ -33,6 +33,7 @@ def generateData(cls): 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', @@ -105,6 +106,11 @@ 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=[text_type])) @@ -147,7 +153,7 @@ def test_db_url_value(self): 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') diff --git a/environ/test_env.txt b/environ/test_env.txt index eb8e01d2..8f5c29be 100644 --- a/environ/test_env.txt +++ b/environ/test_env.txt @@ -23,3 +23,4 @@ INT_VAR=42 STR_LIST_WITH_SPACES= foo, bar STR_VAR=bar INT_LIST=42,33 +INT_TUPLE=(42,33) From c85540ffc8c6266a151b3973d3ad57002edcb911 Mon Sep 17 00:00:00 2001 From: joke2k Date: Wed, 23 Sep 2015 11:13:00 +0200 Subject: [PATCH 6/9] remove casting for empty default values FIX #44 --- environ/environ.py | 2 +- environ/test.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/environ/environ.py b/environ/environ.py index e847cf98..4fe95f62 100644 --- a/environ/environ.py +++ b/environ/environ.py @@ -251,7 +251,7 @@ def get_value(self, var, cast=None, default=NOTSET, parse_default=False): value = value.lstrip('$') value = self.get_value(value, cast=cast, default=default) - if value != default or parse_default: + if value != default or (parse_default and value): value = self.parse_value(value, cast) return value diff --git a/environ/test.py b/environ/test.py index 80470ae4..d41b0dc3 100644 --- a/environ/test.py +++ b/environ/test.py @@ -134,6 +134,7 @@ def test_url_value(self): 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() From 1d48d5b9abf0f0f9c4a0646bbc1bab016df5f2b8 Mon Sep 17 00:00:00 2001 From: joke2k Date: Wed, 23 Sep 2015 11:31:43 +0200 Subject: [PATCH 7/9] Update travisCI configuration to speed up the tests --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index a604ab75..78c3377a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ language: python +sudo: false python: - "2.6" - "2.7" From bb9b1e7c4af97c137d4d39224cf9a06fee37a189 Mon Sep 17 00:00:00 2001 From: joke2k Date: Wed, 23 Sep 2015 17:51:01 +0200 Subject: [PATCH 8/9] update readme and license files --- LICENSE.txt | 2 +- README.rst | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/LICENSE.txt b/LICENSE.txt index 80d813f9..59010057 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2013 Daniele Faraglia +Copyright (c) 2013-2015, 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 d060bd6d..9a4faa24 100644 --- a/README.rst +++ b/README.rst @@ -125,7 +125,7 @@ How to install How to use ========== -There are only classes, Env and Path +There are only two classes, `environ.Env` and `environ.Path` :: @@ -190,6 +190,7 @@ Supported Types - ElasticSearch: elasticsearch:// - Solr: solr:// - Whoosh: whoosh:// + - Xapian: xapian:// - Simple cache: simple:// - email_url - SMTP: smtp:// @@ -213,36 +214,34 @@ Tests License ======= -Django-environ is licensed under the MIT License - see the LICENSE.rst file for details +Django-environ is licensed under the MIT License - see the `LICENSE`_ file for details Changelog ========= -**0.4.0 (2015-09-19)** +**`0.4.0 - 23-September-2015 `__ ** - 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 - -**0.3.1 (2014-09-03)** - Add LDAP url support for database (django-ldapdb) - Fix psql/pgsql url -**0.3 (2014-06-03)** +**`0.3 - 03-June-2014 `__ ** - Add cache url support - Add email url support - Add search url support - Rewriting README.rst -**0.2.1 (2013-04-19)** +**0.2.1 19-April-2013** - environ/environ.py: Env.__call__ now uses Env.get_value instance method -**0.2 (2013-04-16)** +**0.2 16-April-2013** - 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)** +**0.1 02-April-2013** - initial release Credits @@ -313,3 +312,5 @@ Credits .. |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 From 97fa7a144c80da15d45209edd74915b95c371851 Mon Sep 17 00:00:00 2001 From: joke2k Date: Wed, 23 Sep 2015 17:58:56 +0200 Subject: [PATCH 9/9] fix readme rst --- README.rst | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 9a4faa24..151f8d7c 100644 --- a/README.rst +++ b/README.rst @@ -219,7 +219,8 @@ Django-environ is licensed under the MIT License - see the `LICENSE`_ file for d Changelog ========= -**`0.4.0 - 23-September-2015 `__ ** +`0.4.0 - 23-September-2015 `__ +------------------------------------------------------------------------------------------- - 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 @@ -227,21 +228,25 @@ Changelog - Add LDAP url support for database (django-ldapdb) - Fix psql/pgsql url -**`0.3 - 03-June-2014 `__ ** +`0.3 - 03-June-2014 `__ +-------------------------------------------------------------------------------------- - Add cache url support - Add email url support - Add search url support - Rewriting README.rst -**0.2.1 19-April-2013** +0.2.1 19-April-2013 +------------------- - environ/environ.py: Env.__call__ now uses Env.get_value instance method -**0.2 16-April-2013** +0.2 16-April-2013 +----------------- - 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** +0.1 02-April-2013 +----------------- - initial release Credits