diff --git a/elasticsearch/client/__init__.py b/elasticsearch/client/__init__.py
index c735b70a..488cf8be 100644
--- a/elasticsearch/client/__init__.py
+++ b/elasticsearch/client/__init__.py
@@ -3,7 +3,7 @@ import logging
 
 from ..transport import Transport
 from ..exceptions import TransportError
-from ..compat import string_types, urlparse
+from ..compat import string_types, urlparse, unquote
 from .indices import IndicesClient
 from .ingest import IngestClient
 from .cluster import ClusterClient
@@ -49,7 +49,8 @@ def _normalize_hosts(hosts):
                 h['scheme'] = parsed_url.scheme
 
             if parsed_url.username or parsed_url.password:
-                h['http_auth'] = '%s:%s' % (parsed_url.username, parsed_url.password)
+                h['http_auth'] = '%s:%s' % (unquote(parsed_url.username),
+                                            unquote(parsed_url.password))
 
             if parsed_url.path and parsed_url.path != '/':
                 h['url_prefix'] = parsed_url.path
diff --git a/elasticsearch/compat.py b/elasticsearch/compat.py
index deee3c52..a5b615d2 100644
--- a/elasticsearch/compat.py
+++ b/elasticsearch/compat.py
@@ -4,10 +4,10 @@ PY2 = sys.version_info[0] == 2
 
 if PY2:
     string_types = basestring,
-    from urllib import quote_plus, urlencode
+    from urllib import quote_plus, urlencode, unquote
     from urlparse import  urlparse
     from itertools import imap as map
 else:
     string_types = str, bytes
-    from urllib.parse import quote_plus, urlencode, urlparse
+    from urllib.parse import quote_plus, urlencode, urlparse, unquote
     map = map

user:password in hosts are not url decoded
Hello,

If I have a character like "]" in my username or password I must urlencode it or `urlparse` will interpret the host as IPv6. Unfortunately, `urlparse` does not urldecode fragments : 
```
The components are not broken up in smaller parts (for example, the network location is a single string), and % escapes are not expanded.
```
(https://docs.python.org/3/library/urllib.parse.html)

`_normalize_hosts` should urldecode explicitely, which I will propose in a PR.
diff --git a/test_elasticsearch/test_client/__init__.py b/test_elasticsearch/test_client/__init__.py
index 4bf2978c..ec01145e 100644
--- a/test_elasticsearch/test_client/__init__.py
+++ b/test_elasticsearch/test_client/__init__.py
@@ -13,8 +13,8 @@ class TestNormalizeHosts(TestCase):
 
     def test_strings_are_parsed_for_port_and_user(self):
         self.assertEquals(
-            [{"host": "elastic.co", "port": 42}, {"host": "elastic.co", "http_auth": "user:secret"}],
-            _normalize_hosts(["elastic.co:42", "user:secret@elastic.co"])
+            [{"host": "elastic.co", "port": 42}, {"host": "elastic.co", "http_auth": "user:secre]"}],
+            _normalize_hosts(["elastic.co:42", "user:secre%5D@elastic.co"])
         )
 
     def test_strings_are_parsed_for_scheme(self):

pip install -e .
parse_log_pytest
requirements.txt
pytest pytest-cov pytest-xdist pytest-mock pytest-asyncio
apt-get update apt-get install -y gcc
3.9
requirements/base.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
coverage==7.8.0
-e git+https://github.com/elastic/elasticsearch-py.git@fe897ebe0d2167e91ea19fa9a81f448a861d58d1#egg=elasticsearch
exceptiongroup==1.2.2
execnet==2.1.1
iniconfig==2.1.0
packaging==24.2
pluggy==1.5.0
pytest==8.3.5
pytest-asyncio==0.26.0
pytest-cov==6.0.0
pytest-mock==3.14.0
pytest-xdist==3.6.1
tomli==2.2.1
typing_extensions==4.13.0
urllib3==1.26.20

name: elasticsearch-py
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - coverage==7.8.0
      - exceptiongroup==1.2.2
      - execnet==2.1.1
      - iniconfig==2.1.0
      - packaging==24.2
      - pluggy==1.5.0
      - pytest==8.3.5
      - pytest-asyncio==0.26.0
      - pytest-cov==6.0.0
      - pytest-mock==3.14.0
      - pytest-xdist==3.6.1
      - tomli==2.2.1
      - typing-extensions==4.13.0
      - urllib3==1.26.20
prefix: /opt/conda/envs/elasticsearch-py

test_elasticsearch/test_client/__init__.py::TestNormalizeHosts::test_strings_are_parsed_for_port_and_user
test_elasticsearch/test_client/__init__.py::TestNormalizeHosts::test_dicts_are_left_unchanged test_elasticsearch/test_client/__init__.py::TestNormalizeHosts::test_none_uses_defaults test_elasticsearch/test_client/__init__.py::TestNormalizeHosts::test_single_string_is_wrapped_in_list test_elasticsearch/test_client/__init__.py::TestNormalizeHosts::test_strings_are_parsed_for_scheme test_elasticsearch/test_client/__init__.py::TestNormalizeHosts::test_strings_are_used_as_hostnames test_elasticsearch/test_client/__init__.py::TestClient::test_from_in_search test_elasticsearch/test_client/__init__.py::TestClient::test_index_uses_post_if_id_is_empty test_elasticsearch/test_client/__init__.py::TestClient::test_index_uses_put_if_id_is_not_empty test_elasticsearch/test_client/__init__.py::TestClient::test_params_is_copied_when test_elasticsearch/test_client/__init__.py::TestClient::test_repr_contains_hosts test_elasticsearch/test_client/__init__.py::TestClient::test_repr_contains_hosts_passed_in test_elasticsearch/test_client/__init__.py::TestClient::test_repr_truncates_host_to_10 test_elasticsearch/test_client/__init__.py::TestClient::test_request_timeout_is_passed_through_unescaped
elasticsearch/compat.py elasticsearch/client/__init__.py
elasticsearch/compat.py elasticsearch/client/__init__.py