Repository: joke2k/django-environ
Pull request #321 (2021-09-05 08:27:45)

Add Elasticsearch5 to search scheme
Replaces https://github.com/joke2k/django-environ/pull/297


Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index ca1ce30..11ecc37 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -11,6 +11,8 @@ Added
 +++++
   - Added support for negative float strings
     (`#160 <https://github.com/joke2k/django-environ/issues/160>`_)
+  - Added Elasticsearch5 to search scheme
+    (`#297 <https://github.com/joke2k/django-environ/pull/297>`_)
 
 Fixed
 +++++
diff --git a/docs/types.rst b/docs/types.rst
index b2144d0..6bf1241 100644
--- a/docs/types.rst
+++ b/docs/types.rst
@@ -41,6 +41,8 @@ Supported types
 * ``search_url``
 
   * Elasticsearch: ``elasticsearch://``
+  * Elasticsearch2: ``elasticsearch2://``
+  * Elasticsearch5: ``elasticsearch5://``
   * Solr: ``solr://``
   * Whoosh: ``whoosh://``
   * Xapian: ``xapian://``
diff --git a/environ/environ.py b/environ/environ.py
index 310b748..5d897d5 100644
--- a/environ/environ.py
+++ b/environ/environ.py
@@ -30,11 +30,11 @@ from .compat import DJANGO_POSTGRES, ImproperlyConfigured, json, REDIS_DRIVER
 
 try:
     from os import PathLike
+
     Openable = (str, PathLike)
 except ImportError:
     Openable = (str,)
 
-
 logger = logging.getLogger(__name__)
 
 
@@ -64,7 +64,6 @@ class NoValue:
 
 
 class Env:
-
     """Provide scheme-based lookups of environment variables so that each
     caller doesn't have to pass in `cast` and `default` parameters.
 
@@ -79,9 +78,11 @@ class Env:
     NOTSET = NoValue()
     BOOLEAN_TRUE_STRINGS = ('true', 'on', 'ok', 'y', 'yes', '1')
     URL_CLASS = ParseResult
-    DEFAULT_DATABASE_ENV = 'DATABASE_URL'
 
     POSTGRES_FAMILY = ['postgres', 'postgresql', 'psql', 'pgsql', 'postgis']
+    ELASTICSEARCH_FAMILY = ['elasticsearch' + x for x in ['', '2', '5']]
+
+    DEFAULT_DATABASE_ENV = 'DATABASE_URL'
     DB_SCHEMES = {
         'postgres': DJANGO_POSTGRES,
         'postgresql': DJANGO_POSTGRES,
@@ -146,6 +147,8 @@ class Env:
                          "ElasticsearchSearchEngine",
         "elasticsearch2": "haystack.backends.elasticsearch2_backend."
                           "Elasticsearch2SearchEngine",
+        "elasticsearch5": "haystack.backends.elasticsearch5_backend."
+                          "Elasticsearch5SearchEngine",
         "solr": "haystack.backends.solr_backend.SolrEngine",
         "whoosh": "haystack.backends.whoosh_backend.WhooshEngine",
         "xapian": "haystack.backends.xapian_backend.XapianEngine",
@@ -262,6 +265,7 @@ class Env:
             self.get_value(var, default=default),
             engine=engine
         )
+
     db = db_url
 
     def cache_url(self, var=DEFAULT_CACHE_ENV, default=NOTSET, backend=None):
@@ -275,6 +279,7 @@ class Env:
             self.url(var, default=default),
             backend=backend
         )
+
     cache = cache_url
 
     def email_url(self, var=DEFAULT_EMAIL_ENV, default=NOTSET, backend=None):
@@ -288,6 +293,7 @@ class Env:
             self.url(var, default=default),
             backend=backend
         )
+
     email = email_url
 
     def search_url(self, var=DEFAULT_SEARCH_ENV, default=NOTSET, engine=None):
@@ -492,7 +498,7 @@ class Env:
         if url.scheme == 'oracle':
             # Django oracle/base.py strips port and fails on non-string value
             if not config['PORT']:
-                del(config['PORT'])
+                del (config['PORT'])
             else:
                 config['PORT'] = str(config['PORT'])
 
@@ -646,7 +652,7 @@ class Env:
         config["ENGINE"] = cls.SEARCH_SCHEMES[url.scheme]
 
         # check commons params
-        params = {}
+        params = {}  # type: dict
         if url.query:
             params = parse_qs(url.query)
             if 'EXCLUDED_INDEXES' in params.keys():
@@ -665,7 +671,7 @@ class Env:
 
         if url.scheme == 'simple':
             return config
-        elif url.scheme in ['solr', 'elasticsearch', 'elasticsearch2']:
+        elif url.scheme in ['solr'] + cls.ELASTICSEARCH_FAMILY:
             if 'KWARGS' in params.keys():
                 config['KWARGS'] = params['KWARGS'][0]
 
@@ -681,8 +687,7 @@ class Env:
                 config['TIMEOUT'] = cls.parse_value(params['TIMEOUT'][0], int)
             return config
 
-        if url.scheme in ['elasticsearch', 'elasticsearch2']:
-
+        if url.scheme in cls.ELASTICSEARCH_FAMILY:
             split = path.rsplit("/", 1)
 
             if len(split) > 1:
@@ -776,7 +781,6 @@ class Env:
 
 
 class Path:
-
     """Inspired to Django Two-scoops, handling File Paths in Settings."""
 
     def path(self, *paths, **kwargs):


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
index 32a19cd..38550f7 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -19,12 +19,6 @@ def solr_url():
     return 'solr://127.0.0.1:8983/solr'
 
 
-@pytest.fixture
-def elasticsearch_url():
-    """Return Elasticsearch URL."""
-    return 'elasticsearch://127.0.0.1:9200/index'
-
-
 @pytest.fixture
 def whoosh_url():
     """Return Whoosh URL."""
diff --git a/tests/test_search.py b/tests/test_search.py
index 99666d5..6bb1117 100644
--- a/tests/test_search.py
+++ b/tests/test_search.py
@@ -32,14 +32,24 @@ def test_solr_multicore_parsing(solr_url):
     assert 'PATH' not in url
 
 
-def test_elasticsearch_parsing(elasticsearch_url):
+@pytest.mark.parametrize(
+    'url,engine',
+    [
+        ('elasticsearch://127.0.0.1:9200/index',
+         'elasticsearch_backend.ElasticsearchSearchEngine'),
+        ('elasticsearch2://127.0.0.1:9200/index',
+         'elasticsearch2_backend.Elasticsearch2SearchEngine'),
+        ('elasticsearch5://127.0.0.1:9200/index',
+         'elasticsearch5_backend.Elasticsearch5SearchEngine'),
+    ]
+)
+def test_elasticsearch_parsing(url, engine):
+    """Ensure all supported Elasticsearch engines are recognized."""
     timeout = 360
-    url = '{}?TIMEOUT={}'.format(elasticsearch_url, timeout)
+    url = '{}?TIMEOUT={}'.format(url, timeout)
     url = Env.search_url_config(url)
 
-    assert url['ENGINE'] == (
-        'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine'
-    )
+    assert url['ENGINE'] == 'haystack.backends.{}'.format(engine)
     assert 'INDEX_NAME' in url.keys()
     assert url['INDEX_NAME'] == 'index'
     assert 'TIMEOUT' in url.keys()